feat: Allow admin to create new text channels
This commit is contained in:
@@ -77,9 +77,12 @@
|
||||
#renameInput
|
||||
type="text"
|
||||
[value]="ch.name"
|
||||
[class.border-destructive]="renamingChannelId() === ch.id && !!channelNameError()"
|
||||
[title]="renamingChannelId() === ch.id ? (channelNameError() ?? '') : ''"
|
||||
(keydown.enter)="confirmRename($event)"
|
||||
(keydown.escape)="cancelRename()"
|
||||
(blur)="confirmRename($event)"
|
||||
(input)="clearChannelNameError()"
|
||||
class="flex-1 bg-secondary border border-border rounded px-1 py-0.5 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
(click)="$event.stopPropagation()"
|
||||
/>
|
||||
@@ -132,9 +135,12 @@
|
||||
#renameInput
|
||||
type="text"
|
||||
[value]="ch.name"
|
||||
[class.border-destructive]="renamingChannelId() === ch.id && !!channelNameError()"
|
||||
[title]="renamingChannelId() === ch.id ? (channelNameError() ?? '') : ''"
|
||||
(keydown.enter)="confirmRename($event)"
|
||||
(keydown.escape)="cancelRename()"
|
||||
(blur)="confirmRename($event)"
|
||||
(input)="clearChannelNameError()"
|
||||
class="flex-1 bg-secondary border border-border rounded px-1 py-0.5 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
(click)="$event.stopPropagation()"
|
||||
/>
|
||||
@@ -483,7 +489,12 @@
|
||||
[(ngModel)]="newChannelName"
|
||||
placeholder="Channel name"
|
||||
class="w-full px-3 py-2 bg-secondary rounded border border-border text-foreground focus:outline-none focus:ring-2 focus:ring-primary text-sm"
|
||||
[class.border-destructive]="!!channelNameError()"
|
||||
(ngModelChange)="clearChannelNameError()"
|
||||
(keydown.enter)="confirmCreateChannel()"
|
||||
/>
|
||||
@if (channelNameError()) {
|
||||
<p class="mt-2 text-sm text-destructive">{{ channelNameError() }}</p>
|
||||
}
|
||||
</app-confirm-dialog>
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ import { VoiceActivityService, VoiceConnectionFacade } from '../../../domains/vo
|
||||
import { VoiceSessionFacade, VoiceWorkspaceService } from '../../../domains/voice-session';
|
||||
import { VoicePlaybackService } from '../../../domains/voice-connection/application/voice-playback.service';
|
||||
import { VoiceControlsComponent } from '../../../domains/voice-session/feature/voice-controls/voice-controls.component';
|
||||
import {
|
||||
isChannelNameTaken,
|
||||
normalizeChannelName
|
||||
} from '../../../store/rooms/room-channels.rules';
|
||||
import {
|
||||
ContextMenuComponent,
|
||||
UserAvatarComponent,
|
||||
@@ -152,6 +156,7 @@ export class RoomsSidePanelComponent {
|
||||
contextChannel = signal<Channel | null>(null);
|
||||
|
||||
renamingChannelId = signal<string | null>(null);
|
||||
channelNameError = signal<string | null>(null);
|
||||
|
||||
showCreateChannelDialog = signal(false);
|
||||
createChannelType = signal<'text' | 'voice'>('text');
|
||||
@@ -243,6 +248,7 @@ export class RoomsSidePanelComponent {
|
||||
const ch = this.contextChannel();
|
||||
|
||||
this.closeChannelMenu();
|
||||
this.channelNameError.set(null);
|
||||
|
||||
if (ch) {
|
||||
this.renamingChannelId.set(ch.id);
|
||||
@@ -251,10 +257,29 @@ export class RoomsSidePanelComponent {
|
||||
|
||||
confirmRename(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const name = input.value.trim();
|
||||
const name = normalizeChannelName(input.value);
|
||||
const channelId = this.renamingChannelId();
|
||||
|
||||
if (channelId && name) {
|
||||
if (!channelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const validationError = this.getChannelNameError(name, channelId);
|
||||
|
||||
if (validationError) {
|
||||
this.channelNameError.set(validationError);
|
||||
requestAnimationFrame(() => {
|
||||
input.focus();
|
||||
input.select();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.channelNameError.set(null);
|
||||
|
||||
const currentName = this.currentRoom()?.channels?.find((channel) => channel.id === channelId)?.name;
|
||||
|
||||
if (currentName !== name) {
|
||||
this.store.dispatch(RoomsActions.renameChannel({ channelId, name }));
|
||||
}
|
||||
|
||||
@@ -262,6 +287,7 @@ export class RoomsSidePanelComponent {
|
||||
}
|
||||
|
||||
cancelRename() {
|
||||
this.channelNameError.set(null);
|
||||
this.renamingChannelId.set(null);
|
||||
}
|
||||
|
||||
@@ -300,14 +326,19 @@ export class RoomsSidePanelComponent {
|
||||
createChannel(type: 'text' | 'voice') {
|
||||
this.createChannelType.set(type);
|
||||
this.newChannelName = '';
|
||||
this.channelNameError.set(null);
|
||||
this.showCreateChannelDialog.set(true);
|
||||
}
|
||||
|
||||
confirmCreateChannel() {
|
||||
const name = this.newChannelName.trim();
|
||||
const name = normalizeChannelName(this.newChannelName);
|
||||
|
||||
if (!name)
|
||||
const validationError = this.getChannelNameError(name);
|
||||
|
||||
if (validationError) {
|
||||
this.channelNameError.set(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
const type = this.createChannelType();
|
||||
const existing = type === 'text' ? this.textChannels() : this.voiceChannels();
|
||||
@@ -319,13 +350,35 @@ export class RoomsSidePanelComponent {
|
||||
};
|
||||
|
||||
this.store.dispatch(RoomsActions.addChannel({ channel }));
|
||||
this.channelNameError.set(null);
|
||||
this.showCreateChannelDialog.set(false);
|
||||
}
|
||||
|
||||
cancelCreateChannel() {
|
||||
this.channelNameError.set(null);
|
||||
this.showCreateChannelDialog.set(false);
|
||||
}
|
||||
|
||||
clearChannelNameError(): void {
|
||||
if (this.channelNameError()) {
|
||||
this.channelNameError.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
private getChannelNameError(name: string, excludeChannelId?: string): string | null {
|
||||
if (!name) {
|
||||
return 'Channel name is required.';
|
||||
}
|
||||
|
||||
const channels = this.currentRoom()?.channels ?? [];
|
||||
|
||||
if (isChannelNameTaken(channels, name, excludeChannelId)) {
|
||||
return 'Channel names must be unique in a server.';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
openUserContextMenu(evt: MouseEvent, user: User) {
|
||||
evt.preventDefault();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user