1.1.7 - #376 #375 #374 #377

Merged
edraft merged 24 commits from 1.1.7 into support 2023-09-28 18:12:58 +02:00
22 changed files with 225 additions and 46 deletions
Showing only changes of commit 8e8da46a54 - Show all commits

View File

@ -35,6 +35,7 @@ type Server implements TableWithHistoryQuery {
shortRoleNames(filter: ShortRoleNameFilter, page: Page, sort: Sort): [ShortRoleName]
config: ServerConfig
hasFeatureFlag(flag: String): Boolean
createdAt: String
modifiedAt: String

View File

@ -1,6 +1,9 @@
from cpl_core.configuration import ConfigurationABC
from cpl_core.database.context import DatabaseContextABC
from cpl_discord.service import DiscordBotServiceABC
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
from bot_core.configuration.feature_flags_settings import FeatureFlagsSettings
from bot_data.abc.achievement_repository_abc import AchievementRepositoryABC
from bot_data.abc.auto_role_repository_abc import AutoRoleRepositoryABC
from bot_data.abc.client_repository_abc import ClientRepositoryABC
@ -12,6 +15,7 @@ from bot_data.abc.user_joined_server_repository_abc import UserJoinedServerRepos
from bot_data.abc.user_joined_voice_channel_repository_abc import UserJoinedVoiceChannelRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.server import Server
from bot_data.model.server_config import ServerConfig
from bot_data.model.server_history import ServerHistory
from bot_graphql.abc.data_query_with_history_abc import DataQueryWithHistoryABC
from bot_graphql.filter.achievement_filter import AchievementFilter
@ -25,6 +29,7 @@ from bot_graphql.filter.user_filter import UserFilter
class ServerQuery(DataQueryWithHistoryABC):
def __init__(
self,
config: ConfigurationABC,
bot: DiscordBotServiceABC,
db: DatabaseContextABC,
auto_roles: AutoRoleRepositoryABC,
@ -40,6 +45,7 @@ class ServerQuery(DataQueryWithHistoryABC):
):
DataQueryWithHistoryABC.__init__(self, "Server", "ServersHistory", ServerHistory, db)
self._config = config
self._bot = bot
self._auto_roles = auto_roles
self._clients = clients
@ -73,6 +79,9 @@ class ServerQuery(DataQueryWithHistoryABC):
ShortRoleNameFilter,
)
self.set_field("config", lambda server, *_: server_configs.get_server_config_by_server(server.id))
self.set_field(
"hasFeatureFlag", lambda server, *_, **kwargs: self._resolve_has_feature_flag(server, *_, **kwargs)
)
@staticmethod
def resolve_id(server: Server, *_):
@ -89,3 +98,9 @@ class ServerQuery(DataQueryWithHistoryABC):
@staticmethod
def resolve_icon_url(server: Server, *_):
return server.icon_url
def _resolve_has_feature_flag(self, server: Server, *_, **kwargs):
settings: ServerConfig = self._config.get_configuration(f"ServerConfig_{server.discord_id}")
if "flag" not in kwargs:
return False
return FeatureFlagsSettings.get_flag_from_dict(settings.feature_flags, FeatureFlagsEnum(kwargs["flag"]))

View File

@ -3,6 +3,7 @@ import {User} from "./user.model";
import {Level} from "./level.model";
import {Client} from "./client.model";
import { AutoRole } from "./auto_role.model";
import { ServerConfig } from "../config/server-config.model";
export interface GameServer {
id?: number;
@ -22,6 +23,8 @@ export interface Server extends Data {
levels?: Level[];
userCount?: number;
users?: User[];
config?: ServerConfig;
hasFeatureFlag?: boolean;
}
export interface ServerFilter {

View File

@ -82,6 +82,14 @@ export class Queries {
}
`;
static hasServerFeatureFlag = `
query HasServerFeatureFlag($filter: ServerFilter, $flag: String) {
servers(filter: $filter) {
hasFeatureFlag(flag: $flag)
}
}
`;
static gameServerQuery = `
query GameServersList($serverId: ID) {
servers(filter: {id: $serverId}) {

View File

@ -64,3 +64,7 @@ export interface PossibleFeatureFlagsQuery {
possibleFeatureFlags: string[];
}
export interface HasServerFeatureFlagQuery {
hasFeatureFlag: boolean;
}

View File

@ -11,6 +11,7 @@ import { FormBuilder } from "@angular/forms";
import { TranslateService } from "@ngx-translate/core";
import { ActivatedRoute } from "@angular/router";
import { SidebarService } from "../../../../services/sidebar/sidebar.service";
import { ServerService } from "../../../../services/server.service";
@Component({
selector: "app-history-btn",
@ -37,12 +38,12 @@ export class HistoryBtnComponent implements OnInit {
private translate: TranslateService,
private data: DataService,
private route: ActivatedRoute,
private sidebar: SidebarService
private serverService: ServerService
) {
}
public ngOnInit(): void {
this.server = this.sidebar.server$.value ?? {};
this.server = this.serverService.server$.value ?? {};
}
private findVal(object: any, key: string) {

View File

@ -14,6 +14,7 @@ import { Page } from "../../../../../models/graphql/filter/page.model";
import { Sort } from "../../../../../models/graphql/filter/sort.model";
import { Query } from "../../../../../models/graphql/query.model";
import { SidebarService } from "../../../../../services/sidebar/sidebar.service";
import { ServerService } from "../../../../../services/server.service";
@Component({
selector: "app-dashboard",
@ -44,12 +45,9 @@ export class DashboardComponent implements OnInit, OnDestroy {
constructor(
private data: DataService,
private spinnerService: SpinnerService,
private toastService: ToastService,
private confirmDialog: ConfirmationDialogService,
private fb: FormBuilder,
private translate: TranslateService,
private router: Router,
private sidebar: SidebarService
private serverService: ServerService,
) {
}
@ -107,7 +105,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
}
selectServer(server: Server) {
this.sidebar.setServer(server);
this.serverService.setServer(server);
this.router.navigate(["/server", server.id]);
}

View File

@ -14,22 +14,22 @@
<div class="server-list">
<div class="server">
<div class="logo">
<img *ngIf="server.iconURL" [src]="server.iconURL">
<img *ngIf="server ? server.iconURL : ''" [src]="server ? server.iconURL : ''">
</div>
<div class="info">
<h3 class="name">
{{server.name}}
{{server ? server.name : ''}}
</h3>
<div class="data">
<i class="pi pi-users"></i>
{{server.userCount}}
{{server ? server.userCount : ''}}
{{'view.dashboard.server.member_count' | translate}}
</div>
<div class="client-data"
*ngFor="let client of server.clients">
*ngFor="let client of server?.clients">
<app-client class="client-component" [client]="client"></app-client>
</div>
</div>

View File

@ -4,6 +4,7 @@ import { Server } from "src/app/models/data/server.model";
import { DataService } from "src/app/services/data/data.service";
import { SpinnerService } from "src/app/services/spinner/spinner.service";
import { SidebarService } from "../../../../services/sidebar/sidebar.service";
import { ServerService } from "../../../../services/server.service";
@Component({
selector: "app-server-dashboard",
@ -20,7 +21,7 @@ export class ServerDashboardComponent implements OnInit {
private router: Router,
private data: DataService,
private spinner: SpinnerService,
private sidebar: SidebarService
private serverService: ServerService
) {
}
@ -29,7 +30,7 @@ export class ServerDashboardComponent implements OnInit {
this.server = server;
});
this.sidebar.server$.subscribe(server => {
this.serverService.server$.subscribe(server => {
if (!server) {
return;
}

View File

@ -17,8 +17,9 @@ const routes: Routes = [
data: { memberRole: MemberRoles.Moderator }
},
{ path: "levels", loadChildren: () => import("./levels/levels.module").then(m => m.LevelsModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } },
{ path: "achievements", loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule), data: { memberRole: MemberRoles.Moderator } },
{ path: "config", loadChildren: () => import("./config/config.module").then(m => m.ConfigModule), data: { memberRole: MemberRoles.Admin } }
{ path: "achievements", loadChildren: () => import("./achievements/achievements.module").then(m => m.AchievementsModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } },
{ path: "short-role-names", loadChildren: () => import("./short-role-name/short-role-name.module").then(m => m.ShortRoleNameModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Moderator } },
{ path: "config", loadChildren: () => import("./config/config.module").then(m => m.ConfigModule), canActivate: [AuthGuard], data: { memberRole: MemberRoles.Admin } }
];
@NgModule({

View File

@ -14,7 +14,7 @@ import { ClientComponent } from './server-dashboard/components/client/client.com
ServerDashboardComponent,
ProfileComponent,
MembersComponent,
ClientComponent
ClientComponent,
],
imports: [
CommonModule,

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ShortRoleNamesComponent } from './short-role-names.component';
describe('ShortRoleNamesComponent', () => {
let component: ShortRoleNamesComponent;
let fixture: ComponentFixture<ShortRoleNamesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ShortRoleNamesComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ShortRoleNamesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,39 @@
import { Component, OnInit } from "@angular/core";
import { AuthService } from "../../../../../../services/auth/auth.service";
import { SpinnerService } from "../../../../../../services/spinner/spinner.service";
import { ToastService } from "../../../../../../services/toast/toast.service";
import { ConfirmationDialogService } from "../../../../../../services/confirmation-dialog/confirmation-dialog.service";
import { FormBuilder } from "@angular/forms";
import { TranslateService } from "@ngx-translate/core";
import { DataService } from "../../../../../../services/data/data.service";
import { SidebarService } from "../../../../../../services/sidebar/sidebar.service";
import { ActivatedRoute } from "@angular/router";
import { Server } from "../../../../../../models/data/server.model";
@Component({
selector: 'app-short-role-names',
templateUrl: './short-role-names.component.html',
styleUrls: ['./short-role-names.component.scss']
})
export class ShortRoleNamesComponent implements OnInit {
private server: Server = {};
public constructor(
private authService: AuthService,
private spinner: SpinnerService,
private toastService: ToastService,
private confirmDialog: ConfirmationDialogService,
private fb: FormBuilder,
private translate: TranslateService,
private data: DataService,
private sidebar: SidebarService,
private route: ActivatedRoute
) {
}
public ngOnInit(): void {
this.data.getServerFromRoute(this.route).then(async server => {
this.server = server;
});
}
}

View File

@ -0,0 +1,14 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { ShortRoleNamesComponent } from "./components/short-role-names/short-role-names.component";
const routes: Routes = [
{ path: "", component: ShortRoleNamesComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ShortRoleNameRoutingModule {
}

View File

@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ShortRoleNamesComponent } from './components/short-role-names/short-role-names.component';
import { ShortRoleNameRoutingModule } from "./short-role-name-routing.module";
@NgModule({
declarations: [
ShortRoleNamesComponent,
],
imports: [
CommonModule,
ShortRoleNameRoutingModule
]
})
export class ShortRoleNameModule { }

View File

@ -7,11 +7,11 @@ import { ActivatedRoute, Router } from "@angular/router";
import { Server } from "../../models/data/server.model";
import { Queries } from "../../models/graphql/queries.model";
import { Query } from "../../models/graphql/query.model";
import { SidebarService } from "../sidebar/sidebar.service";
import { SpinnerService } from "../spinner/spinner.service";
import { GraphQLResult } from "../../models/graphql/result.model";
import { ToastService } from "../toast/toast.service";
import { TranslateService } from "@ngx-translate/core";
import { ServerService } from "../server.service";
@Injectable({
providedIn: "root"
@ -21,7 +21,7 @@ export class DataService {
constructor(
private appsettings: SettingsService,
private http: HttpClient,
private sidebar: SidebarService,
private server: ServerService,
private spinner: SpinnerService,
private router: Router,
private toast: ToastService,
@ -45,7 +45,7 @@ export class DataService {
return data.servers.length > 0 ? data.servers[0] : null;
}
).subscribe(server => {
this.sidebar.setServer(server);
this.server.setServer(server);
this.spinner.hideSpinner();
resolve(server);
});

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ServerService } from './server.service';
describe('ServerService', () => {
let service: ServerService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ServerService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,36 @@
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Server } from "../models/data/server.model";
import { NavigationEnd, Router } from "@angular/router";
@Injectable({
providedIn: "root"
})
export class ServerService {
server$ = new BehaviorSubject<Server | null>(null);
constructor(
private router: Router
) {
this.router.events.subscribe(event => {
if (!(event instanceof NavigationEnd)) {
return;
}
if (!event.url.startsWith("/server/") && this.server$.value) {
this.setServer(null);
}
});
}
setServer(server: Server | null) {
if (!server) {
this.server$.next(server);
return;
}
if (server.id != this.server$.value?.id) {
this.server$.next(server);
}
}
}

View File

@ -8,6 +8,7 @@ import { NavigationEnd, Router } from "@angular/router";
import { ThemeService } from "../theme/theme.service";
import { Server } from "../../models/data/server.model";
import { UserDTO } from "../../models/auth/auth-user.dto";
import { ServerService } from "../server.service";
@Injectable({
providedIn: "root"
@ -16,7 +17,7 @@ export class SidebarService {
isSidebarOpen: boolean = true;
menuItems$ = new BehaviorSubject<MenuItem[]>(new Array<MenuItem>());
server$ = new BehaviorSubject<Server | null>(null);
server!: Server | null;
dashboard: MenuItem = {};
serverDashboard: MenuItem = {};
@ -25,6 +26,7 @@ export class SidebarService {
serverAutoRoles: MenuItem = {};
serverLevels: MenuItem = {};
serverAchievements: MenuItem = {};
serverShortRoleNames: MenuItem = {};
serverConfig: MenuItem = {};
serverMenu: MenuItem = {};
adminConfig: MenuItem = {};
@ -35,7 +37,8 @@ export class SidebarService {
private themeService: ThemeService,
private authService: AuthService,
private translateService: TranslateService,
private router: Router
private router: Router,
private serverService: ServerService
) {
this.themeService.isSidebarOpen$.subscribe(value => {
this.isSidebarOpen = value;
@ -46,25 +49,14 @@ export class SidebarService {
this.setMenu(true);
});
this.router.events.subscribe(event => {
if (!(event instanceof NavigationEnd)) {
return;
}
if (!event.url.startsWith("/server/") && this.server$.value) {
this.setServer(null);
}
});
}
setServer(server: Server | null) {
if (server?.id != this.server$.value?.id) {
this.server$.next(server);
this.serverService.server$.subscribe(server => {
this.server = server;
if (server) {
this.setMenu(true);
} else {
this.setMenu(false);
}
}
});
}
async buildMenu(user: UserDTO | null, hasPermission: boolean, isTechnician: boolean = false) {
@ -76,54 +68,61 @@ export class SidebarService {
this.serverDashboard = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.dashboard") : "",
icon: "pi pi-th-large",
routerLink: `server/${this.server$.value?.id}`
routerLink: `server/${this.server?.id}`
};
this.serverProfile = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.profile") : "",
icon: "pi pi-id-card",
routerLink: `server/${this.server$.value?.id}/members/${user?.id}`
routerLink: `server/${this.server?.id}/members/${user?.id}`
};
this.serverMembers = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.members") : "",
icon: "pi pi-users",
visible: true,
routerLink: `server/${this.server$.value?.id}/members`
routerLink: `server/${this.server?.id}/members`
};
this.serverAutoRoles = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.auto_roles") : "",
icon: "pi pi-sitemap",
visible: true,
routerLink: `server/${this.server$.value?.id}/auto-roles`
routerLink: `server/${this.server?.id}/auto-roles`
};
this.serverLevels = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.levels") : "",
icon: "pi pi-book",
visible: true,
routerLink: `server/${this.server$.value?.id}/levels`
routerLink: `server/${this.server?.id}/levels`
};
this.serverAchievements = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.achievements") : "",
icon: "pi pi-angle-double-up",
visible: true,
routerLink: `server/${this.server$.value?.id}/achievements`
routerLink: `server/${this.server?.id}/achievements`
};
this.serverShortRoleNames = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.short_role_names") : "",
icon: "pi pi-list",
visible: true,
routerLink: `server/${this.server?.id}/short-role-names`
};
this.serverConfig = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.server.configuration") : "",
icon: "pi pi-cog",
visible: true,
routerLink: `server/${this.server$.value?.id}/config`
routerLink: `server/${this.server?.id}/config`
};
this.serverMenu = {
label: this.isSidebarOpen ? this.server$.value?.name : "",
label: this.isSidebarOpen ? this.server?.name : "",
icon: "pi pi-server",
visible: false,
expanded: true,
items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels, this.serverAchievements, this.serverConfig]
items: [this.serverDashboard, this.serverProfile, this.serverMembers, this.serverAutoRoles, this.serverLevels, this.serverAchievements, this.serverShortRoleNames, this.serverConfig]
};
this.adminConfig = {
label: this.isSidebarOpen ? this.translateService.instant("sidebar.config") : "",
@ -149,19 +148,20 @@ export class SidebarService {
setMenu(build: boolean = false) {
this.authService.hasUserPermission(AuthRoles.Admin).then(async hasPermission => {
let authUser = await this.authService.getLoggedInUser();
let user: UserDTO | null = authUser?.users?.find(u => u.server == this.server$.value?.id) ?? null;
let user: UserDTO | null = authUser?.users?.find(u => u.server == this.server?.id) ?? null;
let isTechnician = authUser?.users?.map(u => u.isTechnician).filter(u => u) ?? [];
if (build || this.menuItems$.value.length == 0) {
await this.buildMenu(user, hasPermission, isTechnician.length > 0);
}
if (this.server$.value) {
if (this.server) {
this.serverMenu.visible = true;
this.serverMembers.visible = !!user?.isModerator;
this.serverAutoRoles.visible = !!user?.isModerator;
this.serverLevels.visible = !!user?.isModerator;
this.serverAchievements.visible = !!user?.isModerator;
this.serverShortRoleNames.visible = !!user?.isAdmin;
this.serverConfig.visible = !!user?.isAdmin || isTechnician.length > 0;
} else {
this.serverMenu.visible = false;

View File

@ -310,6 +310,7 @@
"dashboard": "Dashboard",
"members": "Mitglieder",
"server": {
"short_role_names": "Rollen Kürzel",
"achievements": "Errungenschaften",
"auto_roles": "Auto Rollen",
"configuration": "Konfiguration",