Imrpove chat with gifs, videos, music player, redesigns and improved filesharing errors
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<div
|
||||
class="fixed inset-0 z-40 bg-black/30"
|
||||
(click)="cancelled.emit(undefined)"
|
||||
(keydown.enter)="cancelled.emit(undefined)"
|
||||
(keydown.space)="cancelled.emit(undefined)"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-label="Close dialog"
|
||||
></div>
|
||||
<div
|
||||
class="fixed left-1/2 top-1/2 z-50 w-[360px] max-w-[calc(100vw-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-lg border border-border bg-card shadow-lg"
|
||||
>
|
||||
<div class="space-y-3 p-4">
|
||||
<h4 class="font-semibold text-foreground">Leave Server?</h4>
|
||||
<div class="space-y-3 text-sm text-muted-foreground">
|
||||
<p>
|
||||
Leaving will remove
|
||||
<span class="font-medium text-foreground">{{ room().name }}</span>
|
||||
from your My Servers list.
|
||||
</p>
|
||||
|
||||
@if (isOwner()) {
|
||||
<div class="space-y-2 rounded-md border border-border/80 bg-secondary/20 p-3">
|
||||
<p class="text-foreground">You are the current owner of this server.</p>
|
||||
<p>You can optionally promote another member before leaving. If you skip this step, the server will continue without an owner.</p>
|
||||
|
||||
@if (ownerCandidates().length > 0) {
|
||||
<label class="block space-y-1">
|
||||
<span class="text-xs font-medium uppercase tracking-wide text-muted-foreground"> New owner </span>
|
||||
<select
|
||||
class="w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
||||
[ngModel]="selectedOwnerKey()"
|
||||
(ngModelChange)="selectedOwnerKey.set($event || '')"
|
||||
>
|
||||
<option value="">Skip owner transfer</option>
|
||||
@for (member of ownerCandidates(); track roomMemberKey(member)) {
|
||||
<option [value]="roomMemberKey(member)">{{ member.displayName }} - {{ roleLabel(member) }}</option>
|
||||
}
|
||||
</select>
|
||||
</label>
|
||||
} @else {
|
||||
<p>No other known members are available to promote right now.</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 border-t border-border p-3">
|
||||
<button
|
||||
(click)="cancelled.emit(undefined)"
|
||||
type="button"
|
||||
class="flex-1 rounded-lg bg-secondary px-3 py-2 text-sm text-foreground transition-colors hover:bg-secondary/80"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
(click)="confirmLeave()"
|
||||
type="button"
|
||||
class="flex-1 rounded-lg bg-destructive px-3 py-2 text-sm text-destructive-foreground transition-colors hover:bg-destructive/90"
|
||||
>
|
||||
Leave Server
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
Component,
|
||||
HostListener,
|
||||
computed,
|
||||
effect,
|
||||
input,
|
||||
output,
|
||||
signal
|
||||
} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import {
|
||||
Room,
|
||||
RoomMember,
|
||||
User
|
||||
} from '../../../core/models';
|
||||
|
||||
export interface LeaveServerDialogResult {
|
||||
nextOwnerKey?: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-leave-server-dialog',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
templateUrl: './leave-server-dialog.component.html'
|
||||
})
|
||||
export class LeaveServerDialogComponent {
|
||||
room = input.required<Room>();
|
||||
currentUser = input<User | null>(null);
|
||||
confirmed = output<LeaveServerDialogResult>();
|
||||
cancelled = output<undefined>();
|
||||
|
||||
selectedOwnerKey = signal('');
|
||||
|
||||
isOwner = computed(() => {
|
||||
const room = this.room();
|
||||
const user = this.currentUser();
|
||||
|
||||
if (!room || !user)
|
||||
return false;
|
||||
|
||||
return room.hostId === user.id || room.hostId === user.oderId;
|
||||
});
|
||||
|
||||
ownerCandidates = computed(() => {
|
||||
const room = this.room();
|
||||
const user = this.currentUser();
|
||||
const userIds = new Set([user?.id, user?.oderId].filter((value): value is string => !!value));
|
||||
|
||||
return (room.members ?? []).filter((member) => !userIds.has(member.id) && !userIds.has(member.oderId || ''));
|
||||
});
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
this.room();
|
||||
this.currentUser();
|
||||
this.selectedOwnerKey.set('');
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape')
|
||||
onEscape(): void {
|
||||
this.cancelled.emit(undefined);
|
||||
}
|
||||
|
||||
confirmLeave(): void {
|
||||
this.confirmed.emit(
|
||||
this.selectedOwnerKey()
|
||||
? { nextOwnerKey: this.selectedOwnerKey() }
|
||||
: {}
|
||||
);
|
||||
}
|
||||
|
||||
roomMemberKey(member: RoomMember): string {
|
||||
return member.oderId || member.id;
|
||||
}
|
||||
|
||||
roleLabel(member: RoomMember): string {
|
||||
switch (member.role) {
|
||||
case 'host':
|
||||
return 'Owner';
|
||||
case 'admin':
|
||||
return 'Admin';
|
||||
case 'moderator':
|
||||
return 'Moderator';
|
||||
default:
|
||||
return 'Member';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user