Move toju-app into own its folder
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering, */
|
||||
import {
|
||||
Injectable,
|
||||
signal,
|
||||
computed,
|
||||
inject
|
||||
} from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { RoomsActions } from '../../../store/rooms/rooms.actions';
|
||||
import { buildVoiceSessionRoom, isViewingVoiceSessionServer } from '../domain/voice-session.logic';
|
||||
import type { VoiceSessionInfo } from '../domain/voice-session.models';
|
||||
|
||||
/**
|
||||
* Tracks the user's current voice session across client-side
|
||||
* navigation so that floating voice controls remain visible when
|
||||
* the user is browsing a different server or view.
|
||||
*
|
||||
* This service is purely a UI-state tracker - actual WebRTC
|
||||
* voice management lives in {@link WebRTCService} and its managers.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class VoiceSessionFacade {
|
||||
private readonly store = inject(Store);
|
||||
|
||||
/** Current voice session metadata, or `null` when disconnected. */
|
||||
private readonly _voiceSession = signal<VoiceSessionInfo | null>(null);
|
||||
|
||||
/** Whether the user is currently viewing the voice-connected server. */
|
||||
private readonly _isViewingVoiceServer = signal<boolean>(true);
|
||||
|
||||
/** Reactive read-only voice session. */
|
||||
readonly voiceSession = computed(() => this._voiceSession());
|
||||
|
||||
/** Reactive flag: is the user's current view the voice server? */
|
||||
readonly isViewingVoiceServer = computed(() => this._isViewingVoiceServer());
|
||||
|
||||
/**
|
||||
* Whether the floating voice-controls overlay should be visible.
|
||||
* `true` when a voice session is active AND the user is viewing
|
||||
* a different server.
|
||||
*/
|
||||
readonly showFloatingControls = computed(
|
||||
() => this._voiceSession() !== null && !this._isViewingVoiceServer()
|
||||
);
|
||||
|
||||
/**
|
||||
* Begin tracking a voice session.
|
||||
* Called when the user joins a voice channel.
|
||||
*
|
||||
* @param sessionInfo - Metadata describing the voice-connected server/channel.
|
||||
*/
|
||||
startSession(sessionInfo: VoiceSessionInfo): void {
|
||||
this._voiceSession.set(sessionInfo);
|
||||
this._isViewingVoiceServer.set(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop tracking the voice session.
|
||||
* Called when the user disconnects from voice.
|
||||
*/
|
||||
endSession(): void {
|
||||
this._voiceSession.set(null);
|
||||
this._isViewingVoiceServer.set(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually flag whether the user is currently viewing the
|
||||
* voice-connected server.
|
||||
*
|
||||
* @param isViewing - `true` if the user's current view is the voice server.
|
||||
*/
|
||||
setViewingVoiceServer(isViewing: boolean): void {
|
||||
this._isViewingVoiceServer.set(isViewing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the given server ID to the voice session's server and
|
||||
* update the {@link isViewingVoiceServer} flag accordingly.
|
||||
*
|
||||
* @param currentServerId - ID of the server the user is currently viewing.
|
||||
*/
|
||||
checkCurrentRoute(currentServerId: string | null): void {
|
||||
this._isViewingVoiceServer.set(
|
||||
isViewingVoiceSessionServer(this._voiceSession(), currentServerId)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate the user back to the voice-connected server by
|
||||
* dispatching a `viewServer` action.
|
||||
*/
|
||||
navigateToVoiceServer(): void {
|
||||
const session = this._voiceSession();
|
||||
|
||||
if (!session)
|
||||
return;
|
||||
|
||||
this.store.dispatch(
|
||||
RoomsActions.viewServer({
|
||||
room: buildVoiceSessionRoom(session)
|
||||
})
|
||||
);
|
||||
|
||||
this._isViewingVoiceServer.set(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the server ID of the active voice session, or `null`
|
||||
* if the user is not in a voice channel.
|
||||
*/
|
||||
getVoiceServerId(): string | null {
|
||||
return this._voiceSession()?.serverId ?? null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Injectable,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
signal
|
||||
} from '@angular/core';
|
||||
|
||||
import { VoiceSessionFacade } from './voice-session.facade';
|
||||
|
||||
export type VoiceWorkspaceMode = 'hidden' | 'expanded' | 'minimized';
|
||||
|
||||
export interface VoiceWorkspacePosition {
|
||||
left: number;
|
||||
top: number;
|
||||
}
|
||||
|
||||
const DEFAULT_MINI_WINDOW_POSITION: VoiceWorkspacePosition = {
|
||||
left: 24,
|
||||
top: 24
|
||||
};
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class VoiceWorkspaceService {
|
||||
private readonly voiceSession = inject(VoiceSessionFacade);
|
||||
|
||||
private readonly _mode = signal<VoiceWorkspaceMode>('hidden');
|
||||
private readonly _focusedStreamId = signal<string | null>(null);
|
||||
private readonly _connectRemoteShares = signal(false);
|
||||
private readonly _miniWindowPosition = signal<VoiceWorkspacePosition>(
|
||||
DEFAULT_MINI_WINDOW_POSITION
|
||||
);
|
||||
private readonly _hasCustomMiniWindowPosition = signal(false);
|
||||
|
||||
readonly mode = computed<VoiceWorkspaceMode>(() => {
|
||||
if (!this.voiceSession.voiceSession() || !this.voiceSession.isViewingVoiceServer()) {
|
||||
return 'hidden';
|
||||
}
|
||||
|
||||
return this._mode();
|
||||
});
|
||||
|
||||
readonly isExpanded = computed(() => this.mode() === 'expanded');
|
||||
readonly isMinimized = computed(() => this.mode() === 'minimized');
|
||||
readonly isVisible = computed(() => this.mode() !== 'hidden');
|
||||
readonly focusedStreamId = computed(() => this._focusedStreamId());
|
||||
readonly shouldConnectRemoteShares = computed(
|
||||
() => this.isVisible() && this._connectRemoteShares()
|
||||
);
|
||||
readonly miniWindowPosition = computed(() => this._miniWindowPosition());
|
||||
readonly hasCustomMiniWindowPosition = computed(() => this._hasCustomMiniWindowPosition());
|
||||
|
||||
constructor() {
|
||||
effect(
|
||||
() => {
|
||||
if (this.voiceSession.voiceSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.reset();
|
||||
},
|
||||
{ allowSignalWrites: true }
|
||||
);
|
||||
}
|
||||
|
||||
open(
|
||||
focusedStreamId: string | null = null,
|
||||
options?: { connectRemoteShares?: boolean }
|
||||
): void {
|
||||
if (!this.voiceSession.voiceSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options && Object.prototype.hasOwnProperty.call(options, 'connectRemoteShares')) {
|
||||
this._connectRemoteShares.set(options.connectRemoteShares === true);
|
||||
}
|
||||
|
||||
this._focusedStreamId.set(focusedStreamId);
|
||||
this._mode.set('expanded');
|
||||
}
|
||||
|
||||
focusStream(streamId: string, options?: { connectRemoteShares?: boolean }): void {
|
||||
this.open(streamId, options);
|
||||
}
|
||||
|
||||
minimize(): void {
|
||||
if (!this.voiceSession.voiceSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._mode.set('minimized');
|
||||
}
|
||||
|
||||
restore(): void {
|
||||
this.open(this._focusedStreamId());
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this._mode.set('hidden');
|
||||
this._connectRemoteShares.set(false);
|
||||
}
|
||||
|
||||
showChat(): void {
|
||||
if (this._mode() === 'expanded') {
|
||||
this._mode.set('hidden');
|
||||
this._connectRemoteShares.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
clearFocusedStream(): void {
|
||||
this._focusedStreamId.set(null);
|
||||
}
|
||||
|
||||
setMiniWindowPosition(position: VoiceWorkspacePosition, markCustom = true): void {
|
||||
this._miniWindowPosition.set(position);
|
||||
this._hasCustomMiniWindowPosition.set(markCustom);
|
||||
}
|
||||
|
||||
resetMiniWindowPosition(): void {
|
||||
this._miniWindowPosition.set(DEFAULT_MINI_WINDOW_POSITION);
|
||||
this._hasCustomMiniWindowPosition.set(false);
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this._mode.set('hidden');
|
||||
this._focusedStreamId.set(null);
|
||||
this._connectRemoteShares.set(false);
|
||||
this.resetMiniWindowPosition();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user