Fix syncing issues
Some checks failed
Deploy Web Apps / deploy (push) Has been cancelled
Queue Release Build / prepare (push) Has been cancelled
Queue Release Build / build-linux (push) Has been cancelled
Queue Release Build / build-windows (push) Has been cancelled
Queue Release Build / finalize (push) Has been cancelled
Some checks failed
Deploy Web Apps / deploy (push) Has been cancelled
Queue Release Build / prepare (push) Has been cancelled
Queue Release Build / build-linux (push) Has been cancelled
Queue Release Build / build-windows (push) Has been cancelled
Queue Release Build / finalize (push) Has been cancelled
This commit is contained in:
@@ -5,10 +5,7 @@ import {
|
|||||||
signal,
|
signal,
|
||||||
effect
|
effect
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
NavigationEnd,
|
|
||||||
Router
|
|
||||||
} from '@angular/router';
|
|
||||||
import { take } from 'rxjs';
|
import { take } from 'rxjs';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { WebRTCService } from './webrtc.service';
|
import { WebRTCService } from './webrtc.service';
|
||||||
|
|||||||
@@ -218,7 +218,6 @@ export class DebuggingService {
|
|||||||
|
|
||||||
const rawMessage = args.map((arg) => this.stringifyPreview(arg)).join(' ')
|
const rawMessage = args.map((arg) => this.stringifyPreview(arg)).join(' ')
|
||||||
.trim() || '(empty console call)';
|
.trim() || '(empty console call)';
|
||||||
|
|
||||||
// Use only string args for label/message extraction so that
|
// Use only string args for label/message extraction so that
|
||||||
// stringified object payloads don't pollute the parsed message.
|
// stringified object payloads don't pollute the parsed message.
|
||||||
// Object payloads are captured separately via extractConsolePayload.
|
// Object payloads are captured separately via extractConsolePayload.
|
||||||
@@ -226,7 +225,6 @@ export class DebuggingService {
|
|||||||
.filter((arg): arg is string => typeof arg === 'string')
|
.filter((arg): arg is string => typeof arg === 'string')
|
||||||
.join(' ')
|
.join(' ')
|
||||||
.trim() || rawMessage;
|
.trim() || rawMessage;
|
||||||
|
|
||||||
const consoleMetadata = this.extractConsoleMetadata(metadataSource);
|
const consoleMetadata = this.extractConsoleMetadata(metadataSource);
|
||||||
const payload = this.extractConsolePayload(args);
|
const payload = this.extractConsolePayload(args);
|
||||||
const payloadText = payload === undefined
|
const payloadText = payload === undefined
|
||||||
|
|||||||
@@ -19,10 +19,7 @@ import {
|
|||||||
lucideRefreshCw
|
lucideRefreshCw
|
||||||
} from '@ng-icons/lucide';
|
} from '@ng-icons/lucide';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import {
|
import { selectCurrentRoom, selectIsSignalServerReconnecting } from '../../store/rooms/rooms.selectors';
|
||||||
selectCurrentRoom,
|
|
||||||
selectIsSignalServerReconnecting
|
|
||||||
} from '../../store/rooms/rooms.selectors';
|
|
||||||
import { RoomsActions } from '../../store/rooms/rooms.actions';
|
import { RoomsActions } from '../../store/rooms/rooms.actions';
|
||||||
import { selectCurrentUser } from '../../store/users/users.selectors';
|
import { selectCurrentUser } from '../../store/users/users.selectors';
|
||||||
import { ServerDirectoryService } from '../../core/services/server-directory.service';
|
import { ServerDirectoryService } from '../../core/services/server-directory.service';
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export class VoicePlaybackService {
|
|||||||
this.temporaryOutputDeviceId = this.webrtc.forceDefaultRemotePlaybackOutput()
|
this.temporaryOutputDeviceId = this.webrtc.forceDefaultRemotePlaybackOutput()
|
||||||
? 'default'
|
? 'default'
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
void this.applyEffectiveOutputDeviceToAllPipelines();
|
void this.applyEffectiveOutputDeviceToAllPipelines();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,17 @@ export class DebugConsoleToolbarComponent {
|
|||||||
|
|
||||||
readonly tabs: ('logs' | 'network')[] = ['logs', 'network'];
|
readonly tabs: ('logs' | 'network')[] = ['logs', 'network'];
|
||||||
|
|
||||||
|
@HostListener('document:click', ['$event'])
|
||||||
|
onDocumentClick(event: MouseEvent): void {
|
||||||
|
if (!this.exportMenuOpen())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const target = event.target as HTMLElement;
|
||||||
|
|
||||||
|
if (!target.closest('[data-export-menu]'))
|
||||||
|
this.closeExportMenu();
|
||||||
|
}
|
||||||
|
|
||||||
setActiveTab(tab: 'logs' | 'network'): void {
|
setActiveTab(tab: 'logs' | 'network'): void {
|
||||||
this.activeTabChange.emit(tab);
|
this.activeTabChange.emit(tab);
|
||||||
}
|
}
|
||||||
@@ -138,17 +149,6 @@ export class DebugConsoleToolbarComponent {
|
|||||||
this.closeExportMenu();
|
this.closeExportMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('document:click', ['$event'])
|
|
||||||
onDocumentClick(event: MouseEvent): void {
|
|
||||||
if (!this.exportMenuOpen())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const target = event.target as HTMLElement;
|
|
||||||
|
|
||||||
if (!target.closest('[data-export-menu]'))
|
|
||||||
this.closeExportMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
getDetachLabel(): string {
|
getDetachLabel(): string {
|
||||||
return this.detached() ? 'Dock' : 'Detach';
|
return this.detached() ? 'Dock' : 'Detach';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,16 @@
|
|||||||
<header class="border-b border-border p-5">
|
<header class="border-b border-border p-5">
|
||||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 id="screen-share-source-picker-title" class="text-lg font-semibold text-foreground">
|
<h2
|
||||||
|
id="screen-share-source-picker-title"
|
||||||
|
class="text-lg font-semibold text-foreground"
|
||||||
|
>
|
||||||
Choose what to share
|
Choose what to share
|
||||||
</h2>
|
</h2>
|
||||||
<p id="screen-share-source-picker-description" class="mt-1 text-sm text-muted-foreground">
|
<p
|
||||||
|
id="screen-share-source-picker-description"
|
||||||
|
class="mt-1 text-sm text-muted-foreground"
|
||||||
|
>
|
||||||
Select a screen or window to start sharing.
|
Select a screen or window to start sharing.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,7 +61,11 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 flex flex-wrap gap-2" role="tablist" aria-label="Share source type">
|
<div
|
||||||
|
class="mt-4 flex flex-wrap gap-2"
|
||||||
|
role="tablist"
|
||||||
|
aria-label="Share source type"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="inline-flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
class="inline-flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
@@ -129,7 +139,11 @@
|
|||||||
<div class="flex items-start justify-between gap-3">
|
<div class="flex items-start justify-between gap-3">
|
||||||
<div class="min-w-0 flex-1">
|
<div class="min-w-0 flex-1">
|
||||||
<span class="screen-share-source-picker__preview">
|
<span class="screen-share-source-picker__preview">
|
||||||
<img [ngSrc]="source.thumbnail" [alt]="source.name" fill />
|
<img
|
||||||
|
[ngSrc]="source.thumbnail"
|
||||||
|
[alt]="source.name"
|
||||||
|
fill
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<p class="mt-3 truncate font-medium">{{ source.name }}</p>
|
<p class="mt-3 truncate font-medium">{{ source.name }}</p>
|
||||||
@@ -156,13 +170,13 @@
|
|||||||
} @else {
|
} @else {
|
||||||
<div class="flex min-h-52 items-center justify-center px-5 py-8 text-center">
|
<div class="flex min-h-52 items-center justify-center px-5 py-8 text-center">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-sm font-medium text-foreground">
|
<p class="text-sm font-medium text-foreground">No {{ activeTab() === 'screen' ? 'screens' : 'windows' }} available</p>
|
||||||
No {{ activeTab() === 'screen' ? 'screens' : 'windows' }} available
|
|
||||||
</p>
|
|
||||||
<p class="mt-1 text-sm text-muted-foreground">
|
<p class="mt-1 text-sm text-muted-foreground">
|
||||||
{{ activeTab() === 'screen'
|
{{
|
||||||
|
activeTab() === 'screen'
|
||||||
? 'No displays were reported by Electron right now.'
|
? 'No displays were reported by Electron right now.'
|
||||||
: 'Restore the window you want to share and try again.' }}
|
: 'Restore the window you want to share and try again.'
|
||||||
|
}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Sync-lifecycle effects for the messages store slice.
|
* Sync-lifecycle effects for the messages store slice.
|
||||||
*
|
*
|
||||||
* These effects manage the periodic sync polling, peer-connect
|
* These effects manage the periodic sync polling, peer-connect
|
||||||
* handshakes, and join-room kickoff that keep message databases
|
* handshakes, and room-activation kickoff that keep message databases
|
||||||
* in sync across peers.
|
* in sync across peers.
|
||||||
*
|
*
|
||||||
* Extracted from the monolithic MessagesEffects to keep each
|
* Extracted from the monolithic MessagesEffects to keep each
|
||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
exhaustMap,
|
exhaustMap,
|
||||||
switchMap,
|
switchMap,
|
||||||
repeat,
|
repeat,
|
||||||
takeUntil
|
startWith
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
import { MessagesActions } from './messages.actions';
|
import { MessagesActions } from './messages.actions';
|
||||||
import { RoomsActions } from '../rooms/rooms.actions';
|
import { RoomsActions } from '../rooms/rooms.actions';
|
||||||
@@ -103,13 +103,13 @@ export class MessagesSyncEffects {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the user joins a room, sends a summary and inventory
|
* When the user joins or views a room, sends a summary and inventory
|
||||||
* request to every already-connected peer.
|
* request to every already-connected peer.
|
||||||
*/
|
*/
|
||||||
joinRoomSyncKickoff$ = createEffect(
|
roomActivationSyncKickoff$ = createEffect(
|
||||||
() =>
|
() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(RoomsActions.joinRoomSuccess),
|
ofType(RoomsActions.joinRoomSuccess, RoomsActions.viewServerSuccess),
|
||||||
withLatestFrom(this.store.select(selectCurrentRoom)),
|
withLatestFrom(this.store.select(selectCurrentRoom)),
|
||||||
mergeMap(([{ room }, currentRoom]) => {
|
mergeMap(([{ room }, currentRoom]) => {
|
||||||
const activeRoom = currentRoom || room;
|
const activeRoom = currentRoom || room;
|
||||||
@@ -152,11 +152,30 @@ export class MessagesSyncEffects {
|
|||||||
{ dispatch: false }
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the polling cadence when the active room changes so the next
|
||||||
|
* room does not inherit a stale slow-poll delay.
|
||||||
|
*/
|
||||||
|
resetPeriodicSyncOnRoomActivation$ = createEffect(
|
||||||
|
() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(RoomsActions.joinRoomSuccess, RoomsActions.viewServerSuccess),
|
||||||
|
tap(() => {
|
||||||
|
this.lastSyncClean = false;
|
||||||
|
this.syncReset$.next();
|
||||||
|
})
|
||||||
|
),
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alternates between fast (10 s) and slow (15 min) sync intervals.
|
* Alternates between fast (10 s) and slow (15 min) sync intervals.
|
||||||
* Sends inventory requests to all connected peers.
|
* Sends inventory requests to all connected peers for the active room.
|
||||||
*/
|
*/
|
||||||
periodicSyncPoll$ = createEffect(() =>
|
periodicSyncPoll$ = createEffect(() =>
|
||||||
|
this.syncReset$.pipe(
|
||||||
|
startWith(undefined),
|
||||||
|
switchMap(() =>
|
||||||
timer(SYNC_POLL_FAST_MS).pipe(
|
timer(SYNC_POLL_FAST_MS).pipe(
|
||||||
repeat({
|
repeat({
|
||||||
delay: () =>
|
delay: () =>
|
||||||
@@ -164,7 +183,6 @@ export class MessagesSyncEffects {
|
|||||||
this.lastSyncClean ? SYNC_POLL_SLOW_MS : SYNC_POLL_FAST_MS
|
this.lastSyncClean ? SYNC_POLL_SLOW_MS : SYNC_POLL_FAST_MS
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
takeUntil(this.syncReset$),
|
|
||||||
withLatestFrom(this.store.select(selectCurrentRoom)),
|
withLatestFrom(this.store.select(selectCurrentRoom)),
|
||||||
filter(
|
filter(
|
||||||
([, room]) =>
|
([, room]) =>
|
||||||
@@ -210,6 +228,8 @@ export class MessagesSyncEffects {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -348,7 +348,11 @@ export class RoomsEffects {
|
|||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(RoomsActions.createRoomSuccess, RoomsActions.joinRoomSuccess),
|
ofType(RoomsActions.createRoomSuccess, RoomsActions.joinRoomSuccess),
|
||||||
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectSavedRooms)),
|
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectSavedRooms)),
|
||||||
tap(([{ room }, user, savedRooms]) => {
|
tap(([
|
||||||
|
{ room },
|
||||||
|
user,
|
||||||
|
savedRooms
|
||||||
|
]) => {
|
||||||
this.connectToRoomSignaling(room, user ?? null, undefined, savedRooms);
|
this.connectToRoomSignaling(room, user ?? null, undefined, savedRooms);
|
||||||
|
|
||||||
this.router.navigate(['/room', room.id]);
|
this.router.navigate(['/room', room.id]);
|
||||||
@@ -362,7 +366,11 @@ export class RoomsEffects {
|
|||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(RoomsActions.viewServer),
|
ofType(RoomsActions.viewServer),
|
||||||
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectSavedRooms)),
|
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectSavedRooms)),
|
||||||
switchMap(([{ room }, user, savedRooms]) => {
|
switchMap(([
|
||||||
|
{ room },
|
||||||
|
user,
|
||||||
|
savedRooms
|
||||||
|
]) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return of(RoomsActions.joinRoomFailure({ error: 'Not logged in' }));
|
return of(RoomsActions.joinRoomFailure({ error: 'Not logged in' }));
|
||||||
}
|
}
|
||||||
@@ -573,7 +581,6 @@ export class RoomsEffects {
|
|||||||
hasPassword: nextHasPassword,
|
hasPassword: nextHasPassword,
|
||||||
maxUsers: settings.maxUsers ?? room.maxUsers
|
maxUsers: settings.maxUsers ?? room.maxUsers
|
||||||
};
|
};
|
||||||
|
|
||||||
const localRoomUpdates: Partial<Room> = {
|
const localRoomUpdates: Partial<Room> = {
|
||||||
...updatedSettings,
|
...updatedSettings,
|
||||||
password: hasPasswordUpdate ? (normalizedPassword || undefined) : room.password,
|
password: hasPasswordUpdate ? (normalizedPassword || undefined) : room.password,
|
||||||
@@ -837,10 +844,7 @@ export class RoomsEffects {
|
|||||||
return EMPTY;
|
return EMPTY;
|
||||||
|
|
||||||
this.knownVoiceUsers.delete(signalingMessage.oderId);
|
this.knownVoiceUsers.delete(signalingMessage.oderId);
|
||||||
return [
|
return [RoomsActions.setSignalServerReconnecting({ isReconnecting: false }), UsersActions.userLeft({ userId: signalingMessage.oderId })];
|
||||||
RoomsActions.setSignalServerReconnecting({ isReconnecting: false }),
|
|
||||||
UsersActions.userLeft({ userId: signalingMessage.oderId })
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'access_denied': {
|
case 'access_denied': {
|
||||||
|
|||||||
Reference in New Issue
Block a user