refactor: Clean lint errors and organise files
This commit is contained in:
@@ -182,15 +182,7 @@
|
||||
[name]="u.displayName"
|
||||
[avatarUrl]="u.avatarUrl"
|
||||
size="xs"
|
||||
[ringClass]="
|
||||
u.voiceState?.isDeafened
|
||||
? 'ring-2 ring-red-500'
|
||||
: u.voiceState?.isMuted
|
||||
? 'ring-2 ring-yellow-500'
|
||||
: voiceActivity.isSpeaking(u.oderId || u.id)()
|
||||
? 'ring-2 ring-green-400 shadow-[0_0_8px_2px_rgba(74,222,128,0.6)]'
|
||||
: 'ring-2 ring-green-500/40'
|
||||
"
|
||||
[ringClass]="getVoiceUserRingClass(u)"
|
||||
/>
|
||||
<span class="text-sm text-foreground/80 truncate flex-1">{{ u.displayName }}</span>
|
||||
<!-- Ping latency indicator -->
|
||||
@@ -246,7 +238,11 @@
|
||||
<h4 class="text-xs uppercase tracking-wide text-muted-foreground font-medium mb-2 px-1">You</h4>
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-md bg-secondary/60 px-3 py-2 hover:bg-secondary/80 transition-colors cursor-pointer"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
(click)="openProfileCard($event, currentUser()!, true); $event.stopPropagation()"
|
||||
(keydown.enter)="openProfileCard($event, currentUser()!, true); $event.stopPropagation()"
|
||||
(keydown.space)="openProfileCard($event, currentUser()!, true); $event.preventDefault(); $event.stopPropagation()"
|
||||
>
|
||||
<app-user-avatar
|
||||
[name]="currentUser()?.displayName || '?'"
|
||||
@@ -293,8 +289,12 @@
|
||||
@for (user of onlineRoomUsers(); track user.id) {
|
||||
<div
|
||||
class="group/user flex items-center gap-2 rounded-md px-3 py-2 transition-colors hover:bg-secondary/50 cursor-pointer"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
(contextmenu)="openUserContextMenu($event, user)"
|
||||
(click)="openProfileCard($event, user, false); $event.stopPropagation()"
|
||||
(keydown.enter)="openProfileCard($event, user, false); $event.stopPropagation()"
|
||||
(keydown.space)="openProfileCard($event, user, false); $event.preventDefault(); $event.stopPropagation()"
|
||||
>
|
||||
<app-user-avatar
|
||||
[name]="user.displayName"
|
||||
@@ -352,7 +352,11 @@
|
||||
@for (member of offlineRoomMembers(); track member.oderId || member.id) {
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-md px-3 py-2 opacity-80 hover:bg-secondary/30 transition-colors cursor-pointer"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
(click)="openProfileCardForMember($event, member); $event.stopPropagation()"
|
||||
(keydown.enter)="openProfileCardForMember($event, member); $event.stopPropagation()"
|
||||
(keydown.space)="openProfileCardForMember($event, member); $event.preventDefault(); $event.stopPropagation()"
|
||||
>
|
||||
<app-user-avatar
|
||||
[name]="member.displayName"
|
||||
|
||||
@@ -103,7 +103,7 @@ export class RoomsSidePanelComponent {
|
||||
private voiceWorkspace = inject(VoiceWorkspaceService);
|
||||
private voicePlayback = inject(VoicePlaybackService);
|
||||
private profileCard = inject(ProfileCardService);
|
||||
voiceActivity = inject(VoiceActivityService);
|
||||
private readonly voiceActivity = inject(VoiceActivityService);
|
||||
|
||||
readonly panelMode = input<PanelMode>('channels');
|
||||
readonly showVoiceControls = input(true);
|
||||
@@ -186,14 +186,14 @@ export class RoomsSidePanelComponent {
|
||||
draggedVoiceUserId = signal<string | null>(null);
|
||||
dragTargetVoiceChannelId = signal<string | null>(null);
|
||||
|
||||
openProfileCard(event: MouseEvent, user: User, editable: boolean): void {
|
||||
openProfileCard(event: Event, user: User, editable: boolean): void {
|
||||
event.stopPropagation();
|
||||
const el = event.currentTarget as HTMLElement;
|
||||
|
||||
this.profileCard.open(el, user, { placement: 'left', editable });
|
||||
}
|
||||
|
||||
openProfileCardForMember(event: MouseEvent, member: RoomMember): void {
|
||||
openProfileCardForMember(event: Event, member: RoomMember): void {
|
||||
const user: User = {
|
||||
id: member.id,
|
||||
oderId: member.oderId || member.id,
|
||||
@@ -886,6 +886,22 @@ export class RoomsSidePanelComponent {
|
||||
return this.isUserSharing(userId) || this.isUserOnCamera(userId);
|
||||
}
|
||||
|
||||
getVoiceUserRingClass(user: User): string {
|
||||
if (user.voiceState?.isDeafened) {
|
||||
return 'ring-2 ring-red-500';
|
||||
}
|
||||
|
||||
if (user.voiceState?.isMuted) {
|
||||
return 'ring-2 ring-yellow-500';
|
||||
}
|
||||
|
||||
if (this.isVoiceUserSpeaking(user)) {
|
||||
return 'ring-2 ring-green-400 shadow-[0_0_8px_2px_rgba(74,222,128,0.6)]';
|
||||
}
|
||||
|
||||
return 'ring-2 ring-green-500/40';
|
||||
}
|
||||
|
||||
getUserLiveIconName(userId: string): string {
|
||||
return this.isUserSharing(userId) ? 'lucideMonitor' : 'lucideVideo';
|
||||
}
|
||||
@@ -981,6 +997,12 @@ export class RoomsSidePanelComponent {
|
||||
return 'bg-red-500';
|
||||
}
|
||||
|
||||
private isVoiceUserSpeaking(user: User): boolean {
|
||||
const userKey = user.oderId || user.id;
|
||||
|
||||
return !!userKey && this.voiceActivity.speakingMap().get(userKey) === true;
|
||||
}
|
||||
|
||||
private findKnownUser(userId: string): User | null {
|
||||
const current = this.currentUser();
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ import {
|
||||
lucideVolumeX
|
||||
} from '@ng-icons/lucide';
|
||||
|
||||
import { UserAvatarComponent } from '../../../shared';
|
||||
import { VoiceWorkspacePlaybackService } from './voice-workspace-playback.service';
|
||||
import { VoiceWorkspaceStreamItem } from './voice-workspace.models';
|
||||
import { UserAvatarComponent } from '../../../../shared';
|
||||
import { VoiceWorkspacePlaybackService } from '../voice-workspace-playback.service';
|
||||
import { VoiceWorkspaceStreamItem } from '../voice-workspace.models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-voice-workspace-stream-tile',
|
||||
@@ -86,23 +86,20 @@ export class VoiceWorkspaceStreamTileComponent implements OnDestroy {
|
||||
void video.play().catch(() => {});
|
||||
});
|
||||
|
||||
effect(
|
||||
() => {
|
||||
this.workspacePlayback.settings();
|
||||
effect(() => {
|
||||
this.workspacePlayback.settings();
|
||||
|
||||
const item = this.item();
|
||||
const item = this.item();
|
||||
|
||||
if (item.isLocal || !item.hasAudio) {
|
||||
this.volume.set(0);
|
||||
this.muted.set(false);
|
||||
return;
|
||||
}
|
||||
if (item.isLocal || !item.hasAudio) {
|
||||
this.volume.set(0);
|
||||
this.muted.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.volume.set(this.workspacePlayback.getUserVolume(item.peerKey));
|
||||
this.muted.set(this.workspacePlayback.isUserMuted(item.peerKey));
|
||||
},
|
||||
{ allowSignalWrites: true }
|
||||
);
|
||||
this.volume.set(this.workspacePlayback.getUserVolume(item.peerKey));
|
||||
this.muted.set(this.workspacePlayback.isUserMuted(item.peerKey));
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
const ref = this.videoRef();
|
||||
@@ -48,7 +48,7 @@ import { UsersActions } from '../../../store/users/users.actions';
|
||||
import { selectCurrentUser, selectOnlineUsers } from '../../../store/users/users.selectors';
|
||||
import { ScreenShareQualityDialogComponent, UserAvatarComponent } from '../../../shared';
|
||||
import { VoiceWorkspacePlaybackService } from './voice-workspace-playback.service';
|
||||
import { VoiceWorkspaceStreamTileComponent } from './voice-workspace-stream-tile.component';
|
||||
import { VoiceWorkspaceStreamTileComponent } from './voice-workspace-stream-tile/voice-workspace-stream-tile.component';
|
||||
import { VoiceWorkspaceStreamItem } from './voice-workspace.models';
|
||||
import { ThemeNodeDirective } from '../../../domains/theme';
|
||||
|
||||
@@ -456,38 +456,35 @@ export class VoiceWorkspaceComponent {
|
||||
this.pruneObservedRemoteStreams(peerKeys);
|
||||
});
|
||||
|
||||
effect(
|
||||
() => {
|
||||
const isExpanded = this.showExpanded();
|
||||
const shouldAutoHideChrome = this.shouldAutoHideChrome();
|
||||
effect(() => {
|
||||
const isExpanded = this.showExpanded();
|
||||
const shouldAutoHideChrome = this.shouldAutoHideChrome();
|
||||
|
||||
if (!isExpanded) {
|
||||
this.clearHeaderHideTimeout();
|
||||
this.showWorkspaceHeader.set(true);
|
||||
this.wasExpanded = false;
|
||||
this.wasAutoHideChrome = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shouldAutoHideChrome) {
|
||||
this.clearHeaderHideTimeout();
|
||||
this.showWorkspaceHeader.set(true);
|
||||
this.wasExpanded = true;
|
||||
this.wasAutoHideChrome = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldRevealChrome = !this.wasExpanded || !this.wasAutoHideChrome;
|
||||
if (!isExpanded) {
|
||||
this.clearHeaderHideTimeout();
|
||||
this.showWorkspaceHeader.set(true);
|
||||
this.wasExpanded = false;
|
||||
this.wasAutoHideChrome = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shouldAutoHideChrome) {
|
||||
this.clearHeaderHideTimeout();
|
||||
this.showWorkspaceHeader.set(true);
|
||||
this.wasExpanded = true;
|
||||
this.wasAutoHideChrome = true;
|
||||
this.wasAutoHideChrome = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldRevealChrome) {
|
||||
this.revealWorkspaceChrome();
|
||||
}
|
||||
},
|
||||
{ allowSignalWrites: true }
|
||||
);
|
||||
const shouldRevealChrome = !this.wasExpanded || !this.wasAutoHideChrome;
|
||||
|
||||
this.wasExpanded = true;
|
||||
this.wasAutoHideChrome = true;
|
||||
|
||||
if (shouldRevealChrome) {
|
||||
this.revealWorkspaceChrome();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onWorkspacePointerMove(): void {
|
||||
|
||||
@@ -26,21 +26,21 @@ import {
|
||||
tap
|
||||
} from 'rxjs';
|
||||
|
||||
import { Room, User } from '../../shared-kernel';
|
||||
import { UserBarComponent } from '../../domains/authentication/feature/user-bar/user-bar.component';
|
||||
import { VoiceSessionFacade } from '../../domains/voice-session';
|
||||
import { selectSavedRooms, selectCurrentRoom } from '../../store/rooms/rooms.selectors';
|
||||
import { selectCurrentUser, selectOnlineUsers } from '../../store/users/users.selectors';
|
||||
import { RoomsActions } from '../../store/rooms/rooms.actions';
|
||||
import { DatabaseService } from '../../infrastructure/persistence';
|
||||
import { NotificationsFacade } from '../../domains/notifications';
|
||||
import { type ServerInfo, ServerDirectoryFacade } from '../../domains/server-directory';
|
||||
import { hasRoomBanForUser } from '../../domains/access-control';
|
||||
import { Room, User } from '../../../shared-kernel';
|
||||
import { UserBarComponent } from '../../../domains/authentication/feature/user-bar/user-bar.component';
|
||||
import { VoiceSessionFacade } from '../../../domains/voice-session';
|
||||
import { selectSavedRooms, selectCurrentRoom } from '../../../store/rooms/rooms.selectors';
|
||||
import { selectCurrentUser, selectOnlineUsers } from '../../../store/users/users.selectors';
|
||||
import { RoomsActions } from '../../../store/rooms/rooms.actions';
|
||||
import { DatabaseService } from '../../../infrastructure/persistence';
|
||||
import { NotificationsFacade } from '../../../domains/notifications';
|
||||
import { type ServerInfo, ServerDirectoryFacade } from '../../../domains/server-directory';
|
||||
import { hasRoomBanForUser } from '../../../domains/access-control';
|
||||
import {
|
||||
ConfirmDialogComponent,
|
||||
ContextMenuComponent,
|
||||
LeaveServerDialogComponent
|
||||
} from '../../shared';
|
||||
} from '../../../shared';
|
||||
|
||||
@Component({
|
||||
selector: 'app-servers-rail',
|
||||
@@ -81,8 +81,8 @@ export class ServersRailComponent {
|
||||
bannedRoomLookup = signal<Record<string, boolean>>({});
|
||||
isOnSearch = toSignal(
|
||||
this.router.events.pipe(
|
||||
filter((e): e is NavigationEnd => e instanceof NavigationEnd),
|
||||
map((e) => e.urlAfterRedirects.startsWith('/search'))
|
||||
filter((navigationEvent): navigationEvent is NavigationEnd => navigationEvent instanceof NavigationEnd),
|
||||
map((navigationEvent) => navigationEvent.urlAfterRedirects.startsWith('/search'))
|
||||
),
|
||||
{ initialValue: this.router.url.startsWith('/search') }
|
||||
);
|
||||
@@ -25,6 +25,7 @@
|
||||
<div class="flex items-center gap-1">
|
||||
@if (canKickMembers(member)) {
|
||||
<button
|
||||
type="button"
|
||||
(click)="kickMember(member)"
|
||||
class="grid h-8 w-8 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-destructive/20 hover:text-destructive"
|
||||
title="Kick"
|
||||
@@ -37,6 +38,7 @@
|
||||
}
|
||||
@if (canBanMembers(member)) {
|
||||
<button
|
||||
type="button"
|
||||
(click)="banMember(member)"
|
||||
class="grid h-8 w-8 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-destructive/20 hover:text-destructive"
|
||||
title="Ban"
|
||||
|
||||
@@ -32,7 +32,7 @@ import { RealtimeSessionFacade } from '../../../core/realtime';
|
||||
import { selectSavedRooms, selectCurrentRoom } from '../../../store/rooms/rooms.selectors';
|
||||
import { selectCurrentUser } from '../../../store/users/users.selectors';
|
||||
import { Room, UserRole } from '../../../shared-kernel';
|
||||
import { NotificationsSettingsComponent } from '../../../domains/notifications/feature/settings/notifications-settings.component';
|
||||
import { NotificationsSettingsComponent } from '../../../domains/notifications';
|
||||
import { resolveLegacyRole, resolveRoomPermission } from '../../../domains/access-control';
|
||||
|
||||
import { GeneralSettingsComponent } from './general-settings/general-settings.component';
|
||||
|
||||
@@ -9,7 +9,8 @@ export interface ThirdPartyLicense {
|
||||
}
|
||||
|
||||
const toLicenseText = (lines: readonly string[]): string => lines.join('\n');
|
||||
const GROUPED_LICENSE_NOTE = 'Grouped by the license declared in the installed package metadata for the packages below. Some upstream packages include their own copyright notices in addition to this standard license text.';
|
||||
const GROUPED_LICENSE_NOTE = 'Grouped by the license declared in the installed package metadata for the packages below. '
|
||||
+ 'Some upstream packages include their own copyright notices in addition to this standard license text.';
|
||||
const MIT_LICENSE_TEXT = toLicenseText([
|
||||
'MIT License',
|
||||
'',
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
inject,
|
||||
signal
|
||||
} from '@angular/core';
|
||||
import { ElectronBridgeService } from '../../core/platform/electron/electron-bridge.service';
|
||||
import { ContextMenuComponent } from '../../shared';
|
||||
import type { ContextMenuParams } from '../../core/platform/electron/electron-api.models';
|
||||
import { ElectronBridgeService } from '../../../core/platform/electron/electron-bridge.service';
|
||||
import { ContextMenuComponent } from '../../../shared';
|
||||
import type { ContextMenuParams } from '../../../core/platform/electron/electron-api.models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-native-context-menu',
|
||||
@@ -26,18 +26,18 @@ import {
|
||||
selectVoiceChannels,
|
||||
selectIsSignalServerReconnecting,
|
||||
selectSignalServerCompatibilityError
|
||||
} from '../../store/rooms/rooms.selectors';
|
||||
import { RoomsActions } from '../../store/rooms/rooms.actions';
|
||||
import { selectCurrentUser } from '../../store/users/users.selectors';
|
||||
import { ElectronBridgeService } from '../../core/platform/electron/electron-bridge.service';
|
||||
import { RealtimeSessionFacade } from '../../core/realtime';
|
||||
import { ServerDirectoryFacade } from '../../domains/server-directory';
|
||||
import { PlatformService } from '../../core/platform';
|
||||
import { STORAGE_KEY_CURRENT_USER_ID } from '../../core/constants';
|
||||
import { LeaveServerDialogComponent } from '../../shared';
|
||||
import { Room } from '../../shared-kernel';
|
||||
import { VoiceWorkspaceService } from '../../domains/voice-session';
|
||||
import { ThemeNodeDirective } from '../../domains/theme';
|
||||
} from '../../../store/rooms/rooms.selectors';
|
||||
import { RoomsActions } from '../../../store/rooms/rooms.actions';
|
||||
import { selectCurrentUser } from '../../../store/users/users.selectors';
|
||||
import { ElectronBridgeService } from '../../../core/platform/electron/electron-bridge.service';
|
||||
import { RealtimeSessionFacade } from '../../../core/realtime';
|
||||
import { ServerDirectoryFacade } from '../../../domains/server-directory';
|
||||
import { PlatformService } from '../../../core/platform';
|
||||
import { STORAGE_KEY_CURRENT_USER_ID } from '../../../core/constants';
|
||||
import { LeaveServerDialogComponent } from '../../../shared';
|
||||
import { Room } from '../../../shared-kernel';
|
||||
import { VoiceWorkspaceService } from '../../../domains/voice-session';
|
||||
import { ThemeNodeDirective } from '../../../domains/theme';
|
||||
|
||||
@Component({
|
||||
selector: 'app-title-bar',
|
||||
Reference in New Issue
Block a user