feat: Add game activity status (Experimental)
All checks were successful
Queue Release Build / prepare (push) Successful in 21s
Deploy Web Apps / deploy (push) Successful in 5m14s
Queue Release Build / build-windows (push) Successful in 16m18s
Queue Release Build / build-linux (push) Successful in 29m20s
Queue Release Build / finalize (push) Successful in 36s
All checks were successful
Queue Release Build / prepare (push) Successful in 21s
Deploy Web Apps / deploy (push) Successful in 5m14s
Queue Release Build / build-windows (push) Successful in 16m18s
Queue Release Build / build-linux (push) Successful in 29m20s
Queue Release Build / finalize (push) Successful in 36s
This commit is contained in:
@@ -277,6 +277,29 @@
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm text-foreground truncate">{{ currentUser()?.displayName }}</p>
|
||||
@if (currentUser()?.gameActivity; as activity) {
|
||||
<p class="mt-0.5 flex items-center gap-1 truncate text-[10px] text-muted-foreground">
|
||||
<ng-icon
|
||||
name="lucideGamepad2"
|
||||
class="h-2.5 w-2.5 shrink-0"
|
||||
/>
|
||||
@if (activity.store?.url) {
|
||||
<button
|
||||
type="button"
|
||||
class="truncate text-left hover:text-foreground hover:underline"
|
||||
(click)="openGameStore($event, activity)"
|
||||
(dblclick)="$event.stopPropagation()"
|
||||
(keydown.enter)="$event.stopPropagation()"
|
||||
(keydown.space)="$event.stopPropagation()"
|
||||
>
|
||||
Playing {{ activity.name }}
|
||||
</button>
|
||||
} @else {
|
||||
<span class="truncate">Playing {{ activity.name }}</span>
|
||||
}
|
||||
<span class="shrink-0">{{ gameActivityElapsed(currentUser()) }}</span>
|
||||
</p>
|
||||
}
|
||||
<div class="flex items-center gap-2">
|
||||
@if (currentUser()?.voiceState?.isConnected) {
|
||||
<p class="text-[10px] text-muted-foreground flex items-center gap-1">
|
||||
@@ -340,6 +363,29 @@
|
||||
<span class="text-[10px] bg-green-500/20 text-green-400 px-1 py-0.5 rounded font-medium">Mod</span>
|
||||
}
|
||||
</div>
|
||||
@if (user.gameActivity; as activity) {
|
||||
<p class="mt-0.5 flex items-center gap-1 truncate text-[10px] text-muted-foreground">
|
||||
<ng-icon
|
||||
name="lucideGamepad2"
|
||||
class="h-2.5 w-2.5 shrink-0"
|
||||
/>
|
||||
@if (activity.store?.url) {
|
||||
<button
|
||||
type="button"
|
||||
class="truncate text-left hover:text-foreground hover:underline"
|
||||
(click)="openGameStore($event, activity)"
|
||||
(dblclick)="$event.stopPropagation()"
|
||||
(keydown.enter)="$event.stopPropagation()"
|
||||
(keydown.space)="$event.stopPropagation()"
|
||||
>
|
||||
Playing {{ activity.name }}
|
||||
</button>
|
||||
} @else {
|
||||
<span class="truncate">Playing {{ activity.name }}</span>
|
||||
}
|
||||
<span class="shrink-0">{{ gameActivityElapsed(user) }}</span>
|
||||
</p>
|
||||
}
|
||||
<div class="flex items-center gap-2">
|
||||
@if (user.voiceState?.isConnected) {
|
||||
<p class="text-[10px] text-muted-foreground flex items-center gap-1">
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
inject,
|
||||
computed,
|
||||
input,
|
||||
OnDestroy,
|
||||
signal
|
||||
} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
@@ -22,7 +23,8 @@ import {
|
||||
lucideHash,
|
||||
lucideUsers,
|
||||
lucidePlus,
|
||||
lucideVolumeX
|
||||
lucideVolumeX,
|
||||
lucideGamepad2
|
||||
} from '@ng-icons/lucide';
|
||||
import { selectOnlineUsers, selectCurrentUser } from '../../../store/users/users.selectors';
|
||||
import {
|
||||
@@ -46,6 +48,8 @@ import {
|
||||
import { VoiceSessionFacade, VoiceWorkspaceService } from '../../../domains/voice-session';
|
||||
import { DirectMessageService } from '../../../domains/direct-message';
|
||||
import { VoicePlaybackService } from '../../../domains/voice-connection';
|
||||
import { formatGameActivityElapsed } from '../../../domains/game-activity';
|
||||
import { ExternalLinkService } from '../../../core/platform/external-link.service';
|
||||
import { VoiceControlsComponent } from '../../../domains/voice-session/feature/voice-controls/voice-controls.component';
|
||||
import { isChannelNameTaken, normalizeChannelName } from '../../../store/rooms/room-channels.rules';
|
||||
import {
|
||||
@@ -64,6 +68,7 @@ import {
|
||||
import {
|
||||
Channel,
|
||||
ChatEvent,
|
||||
GameActivity,
|
||||
RoomMember,
|
||||
Room,
|
||||
User
|
||||
@@ -98,12 +103,13 @@ type PanelMode = 'channels' | 'users';
|
||||
lucideHash,
|
||||
lucideUsers,
|
||||
lucidePlus,
|
||||
lucideVolumeX
|
||||
lucideVolumeX,
|
||||
lucideGamepad2
|
||||
})
|
||||
],
|
||||
templateUrl: './rooms-side-panel.component.html'
|
||||
})
|
||||
export class RoomsSidePanelComponent {
|
||||
export class RoomsSidePanelComponent implements OnDestroy {
|
||||
private store = inject(Store);
|
||||
private router = inject(Router);
|
||||
private realtime = inject(RealtimeSessionFacade);
|
||||
@@ -115,9 +121,11 @@ export class RoomsSidePanelComponent {
|
||||
private voicePlayback = inject(VoicePlaybackService);
|
||||
private profileCard = inject(ProfileCardService);
|
||||
private directMessages = inject(DirectMessageService);
|
||||
private readonly externalLinks = inject(ExternalLinkService);
|
||||
private readonly voiceActivity = inject(VoiceActivityService);
|
||||
private readonly voiceConnectivity = inject(VoiceConnectivityHealthService);
|
||||
private profileCardOpenTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private readonly activityTimer = setInterval(() => this.activityNow.set(Date.now()), 1_000);
|
||||
|
||||
readonly panelMode = input<PanelMode>('channels');
|
||||
readonly showVoiceControls = input(true);
|
||||
@@ -198,6 +206,26 @@ export class RoomsSidePanelComponent {
|
||||
volumeMenuDisplayName = signal('');
|
||||
draggedVoiceUserId = signal<string | null>(null);
|
||||
dragTargetVoiceChannelId = signal<string | null>(null);
|
||||
activityNow = signal(Date.now());
|
||||
|
||||
ngOnDestroy(): void {
|
||||
clearInterval(this.activityTimer);
|
||||
this.cancelQueuedProfileCardOpen();
|
||||
}
|
||||
|
||||
gameActivityElapsed(user: User | null | undefined): string {
|
||||
const activity = user?.gameActivity;
|
||||
|
||||
return activity ? formatGameActivityElapsed(activity.startedAt, this.activityNow()) : '';
|
||||
}
|
||||
|
||||
openGameStore(event: Event, activity: GameActivity): void {
|
||||
event.stopPropagation();
|
||||
|
||||
if (activity.store?.url) {
|
||||
this.externalLinks.open(activity.store.url);
|
||||
}
|
||||
}
|
||||
|
||||
openProfileCard(event: Event, user: User, editable: boolean): void {
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
(keydown.space)="$event.stopPropagation()"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="settings-modal-title"
|
||||
tabindex="-1"
|
||||
>
|
||||
<!-- Side Navigation -->
|
||||
@@ -36,7 +37,12 @@
|
||||
class="flex w-56 flex-shrink-0 flex-col border-r border-border bg-card"
|
||||
>
|
||||
<div class="border-b border-border px-3 py-3">
|
||||
<h2 class="text-lg font-semibold text-foreground">Settings</h2>
|
||||
<h2
|
||||
id="settings-modal-title"
|
||||
class="text-lg font-semibold text-foreground"
|
||||
>
|
||||
Settings
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto py-2">
|
||||
|
||||
Reference in New Issue
Block a user