Skip to content

Commit

Permalink
feat(admin): discord configuration (#2421)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrappachc authored Nov 7, 2023
1 parent b22a54e commit 6e4804c
Show file tree
Hide file tree
Showing 17 changed files with 450 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/app/admin/admin-item-list/admin-item-list.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
context: { $implicit: { link: 'voice-server', title: 'Voice server' } }
"
></ng-template>
<ng-template
*ngTemplateOutlet="
item;
context: { $implicit: { link: 'discord', title: 'Discord' } }
"
></ng-template>
<ng-template
*ngTemplateOutlet="
item;
Expand Down
11 changes: 11 additions & 0 deletions src/app/admin/admin-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import { GameConfigurationComponent } from './game-configuration/game-configurat
import { GameConfigurationResolver } from './game-configuration/game-configuration.resolver';
import { ConfigurationEditComponent } from './configuration-edit/configuration-edit.component';
import { ServemeTfConfigurationResolver } from './game-servers/serveme-tf-configuration/serveme-tf-configuration.resolver';
import { DiscordComponent } from './discord/discord.component';
import { DiscordConfigurationResolver } from './discord/discord-configuration.resolver';
import { GuildsResolver } from './discord/guilds.resolver';

const routes: Routes = [
{
Expand Down Expand Up @@ -171,6 +174,14 @@ const routes: Routes = [
animation: 'ConfigurationEditPage',
},
},
{
path: 'discord',
component: DiscordComponent,
resolve: {
configuration: DiscordConfigurationResolver,
guilds: GuildsResolver,
},
},
],
},
];
Expand Down
4 changes: 4 additions & 0 deletions src/app/admin/admin.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { ImportExportPlayerSkillComponent } from './import-export-player-skill/i
import { GameConfigurationComponent } from './game-configuration/game-configuration.component';
import { ConfigurationEditComponent } from './configuration-edit/configuration-edit.component';
import { NgxEditInlineModule } from 'ngx-edit-inline';
import { DiscordComponent } from './discord/discord.component';
import { DiscordGuildEditComponent } from './discord/discord-guild-edit/discord-guild-edit.component';

@NgModule({
imports: [
Expand Down Expand Up @@ -64,6 +66,8 @@ import { NgxEditInlineModule } from 'ngx-edit-inline';
ImportExportPlayerSkillComponent,
GameConfigurationComponent,
ConfigurationEditComponent,
DiscordComponent,
DiscordGuildEditComponent,
],
})
export class AdminModule {}
25 changes: 25 additions & 0 deletions src/app/admin/discord/discord-configuration.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { map, Observable } from 'rxjs';
import { ConfigurationService } from '@app/configuration/configuration.service';
import { DiscordConfiguration } from './models/discord-configuration';
import { GuildConfiguration } from './models/guild-configuration';

@Injectable({
providedIn: 'root',
})
export class DiscordConfigurationResolver
implements Resolve<DiscordConfiguration>
{
constructor(private readonly configurationService: ConfigurationService) {}

resolve(): Observable<DiscordConfiguration> {
return this.configurationService
.fetchValues<[GuildConfiguration[]]>('discord.guilds')
.pipe(
map(([guilds]) => ({
guilds: guilds.value,
})),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<div class="flex flex-col" [formGroup]="guildControl">
<div class="flex flex-row gap-2 items-center">
<input type="checkbox" name="id" formControlName="isEnabled" />
<span>{{ guildControl.get('name').value }}</span>
</div>

<div class="flex flex-col gap-2" *ngIf="guildControl.get('isEnabled').value">
<div
class="flex flex-row gap-2 items-center"
formGroupName="adminNotifications"
>
<span>Admin notifications channel:</span>
<select formControlName="channel">
<option [ngValue]="null" class="text-gray-600">disabled</option>
<optgroup
*ngFor="let textChannelGroup of textChannels | async | keyvalue"
label="{{ textChannelGroup.key }}"
>
<option
*ngFor="let textChannel of textChannelGroup.value"
[value]="textChannel.id"
>
{{ textChannel.name }}
</option>
</optgroup>
</select>
</div>

<div
class="flex flex-row gap-2 items-center"
formGroupName="substituteNotifications"
>
<span>Substitute notifications channel:</span>
<select formControlName="channel">
<option [ngValue]="null" class="text-gray-600">disabled</option>
<optgroup
*ngFor="let textChannelGroup of textChannels | async | keyvalue"
label="{{ textChannelGroup.key }}"
>
<option
*ngFor="let textChannel of textChannelGroup.value"
[value]="textChannel.id"
>
{{ textChannel.name }}
</option>
</optgroup>
</select>

<span>mention role:</span>
<select formControlName="role">
<option [ngValue]="null">disabled</option>
<option *ngFor="let role of roles | async" [value]="role.id">
{{ role.name }}
</option>
</select>
</div>

<div class="flex flex-row gap-2 items-center" formGroupName="queuePrompts">
<span>Queue prompts channel:</span>
<select formControlName="channel">
<option [ngValue]="null" class="text-gray-600">disabled</option>
<optgroup
*ngFor="let textChannelGroup of textChannels | async | keyvalue"
label="{{ textChannelGroup.key }}"
>
<option
*ngFor="let textChannel of textChannelGroup.value"
[value]="textChannel.id"
>
{{ textChannel.name }}
</option>
</optgroup>
</select>
</div>
</div>
</div>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
ChangeDetectionStrategy,
Component,
Input,
OnInit,
} from '@angular/core';
import { DiscordService } from '../discord.service';
import { ReplaySubject, distinctUntilChanged, map } from 'rxjs';
import { TextChannel } from '../models/text-channel';
import { FormControl, FormGroup } from '@angular/forms';
import { Role } from '../models/role';

@Component({
selector: 'app-discord-guild-edit',
templateUrl: './discord-guild-edit.component.html',
styleUrls: ['./discord-guild-edit.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DiscordGuildEditComponent implements OnInit {
@Input()
guildControl: FormGroup<{
id: FormControl<string>;
name: FormControl<string>;
isEnabled: FormControl<boolean>;
adminNotifications: FormGroup<{ channel: FormControl<string | null> }>;
substituteNotifications: FormGroup<{
channel: FormControl<string | null>;
role: FormControl<string | null>;
}>;
queuePrompts: FormGroup<{ channel: FormControl<string | null> }>;
}>;

textChannels = new ReplaySubject<Map<string, TextChannel[]>>(1);
roles = new ReplaySubject<Role[]>(1);

constructor(private readonly discordService: DiscordService) {}

ngOnInit() {
this.guildControl
.get('isEnabled')
.valueChanges.pipe(distinctUntilChanged())
.subscribe(isEnabled => {
if (isEnabled) {
this.loadTextChannels();
this.loadRoles();
}
});

if (this.guildControl.get('isEnabled').value) {
this.loadTextChannels();
this.loadRoles();
}
}

loadTextChannels() {
this.discordService
.fetchTextChannels(this.guildControl.get('id').value)
.pipe(
map(textChannels =>
textChannels.reduce((prev, curr) => {
if (!prev.has(curr.parent)) {
prev.set(curr.parent, []);
}

prev.get(curr.parent).push(curr);
return prev;
}, new Map<string, TextChannel[]>()),
),
map(groups => {
groups.forEach(value =>
value.sort((a, b) => a.position - b.position),
);
return groups;
}),
)
.subscribe(textChannels => this.textChannels.next(textChannels));
}

loadRoles() {
this.discordService
.fetchTextChannels(this.guildControl.get('id').value)
.pipe(map(roles => roles.sort((a, b) => a.name.localeCompare(b.name))))
.subscribe(roles => this.roles.next(roles));
}
}
19 changes: 19 additions & 0 deletions src/app/admin/discord/discord.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<form [formGroup]="form" (submit)="save()">
<app-edit-page-wrapper
title="Discord"
[saveDisabled]="(isSaving | async) || !form.valid || form.pristine"
>
<div class="surface surface--white py-4 my-4 flex flex-col gap-2">
<ng-container formArrayName="guilds">
<div
class="flex flex-row gap-2 items-center"
*ngFor="let guildControl of guilds.controls"
>
<app-discord-guild-edit
[guildControl]="guildControl | asFormGroup"
></app-discord-guild-edit>
</div>
</ng-container>
</div>
</app-edit-page-wrapper>
</form>
Empty file.
Loading

0 comments on commit 6e4804c

Please sign in to comment.