chore: enforce lint across codebase and ban "maybe" in identifiers

Remove member-ordering and complexity eslint-disable comments by reordering
class members and applying targeted fixes. Add metoyou/no-maybe-in-naming,
type-safe WebRTC e2e harness helpers, and resolve remaining lint errors so
npm run lint exits cleanly.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-11 11:08:26 +02:00
parent b630bacdc6
commit 79c6f91cd6
138 changed files with 4286 additions and 2310 deletions

View File

@@ -36,7 +36,6 @@ import {
INVENTORY_LIMIT,
CHUNK_SIZE,
FULL_SYNC_LIMIT,
type InventoryItem,
chunkArray,
buildInventoryItem,
buildLocalInventoryMap,
@@ -216,10 +215,10 @@ function handleSyncRequestIds(
return from(
(async () => {
const maybeMessages = await Promise.all(
const loadedMessages = await Promise.all(
(ids as string[]).map((id) => db.getMessageById(id))
);
const messages = maybeMessages.filter(
const messages = loadedMessages.filter(
(msg): msg is Message => !!msg && msg.roomId === roomId
);
const hydrated = await Promise.all(

View File

@@ -8,7 +8,6 @@
* Extracted from the monolithic MessagesEffects to keep each
* class focused on a single concern.
*/
/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import {
Actions,
@@ -49,17 +48,6 @@ import {
@Injectable()
export class MessagesSyncEffects {
private readonly actions$ = inject(Actions);
private readonly store = inject(Store);
private readonly db = inject(DatabaseService);
private readonly debugging = inject(DebuggingService);
private readonly webrtc = inject(RealtimeSessionFacade);
/** Tracks whether the last sync cycle found no new messages. */
private lastSyncClean = false;
/** Subject to reset the periodic sync timer. */
private readonly syncReset$ = new Subject<void>();
/**
* When a new peer connects, sends our dataset summary and an
@@ -244,4 +232,21 @@ export class MessagesSyncEffects {
),
{ dispatch: false }
);
private readonly actions$ = inject(Actions);
private readonly store = inject(Store);
private readonly db = inject(DatabaseService);
private readonly debugging = inject(DebuggingService);
private readonly webrtc = inject(RealtimeSessionFacade);
/** Tracks whether the last sync cycle found no new messages. */
private lastSyncClean = false;
/** Subject to reset the periodic sync timer. */
private readonly syncReset$ = new Subject<void>();
}

View File

@@ -8,7 +8,6 @@
* The giant `incomingMessages$` switch-case has been replaced by a
* handler registry in `messages-incoming.handlers.ts`.
*/
/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import {
Actions,
@@ -64,18 +63,6 @@ const PREFETCH_CONCURRENCY = 3;
@Injectable()
export class MessagesEffects {
private readonly actions$ = inject(Actions);
private readonly store = inject(Store);
private readonly db = inject(DatabaseService);
private readonly debugging = inject(DebuggingService);
private readonly attachments = inject(AttachmentFacade);
private readonly customEmoji = inject(CustomEmojiService);
private readonly webrtc = inject(RealtimeSessionFacade);
private readonly timeSync = inject(TimeSyncService);
private readonly linkMetadata = inject(LinkMetadataService);
private readonly platform = inject(PlatformService);
private readonly i18n = inject(AppI18nService);
private readonly messageRevisions = inject(MessageRevisionService);
/** Loads messages for a room from the local database, hydrating reactions. */
loadMessages$ = createEffect(() =>
@@ -146,29 +133,6 @@ export class MessagesEffects {
)
);
private async fetchRoomMessagesForPrefetch(roomId: string, targetRoom: Room | null) {
try {
const messages = await this.loadInitialMessages(roomId, targetRoom);
const hydrated = await hydrateMessages(messages, this.db);
for (const message of hydrated) {
this.attachments.rememberMessageRoom(message.id, message.roomId);
}
return MessagesActions.prefetchRoomMessagesSuccess({ messages: hydrated });
} catch (error) {
reportDebuggingError(
this.debugging,
'MessagesEffects.prefetchRoomMessages',
'Failed to prefetch room messages',
{ roomId },
error
);
return MessagesActions.prefetchRoomMessagesSuccess({ messages: [] });
}
}
/** Paginates older messages from the local DB for scroll-up history loading. */
loadOlderMessages$ = createEffect(() =>
this.actions$.pipe(
@@ -198,29 +162,6 @@ export class MessagesEffects {
)
);
private async loadInitialMessages(roomId: string, targetRoom: Room | null): Promise<Message[]> {
const textChannels = targetRoom?.id === roomId
? (targetRoom.channels ?? []).filter((channel) => channel.type === 'text')
: [];
if (textChannels.length <= 1) {
return this.db.getMessages(roomId, INITIAL_ROOM_MESSAGE_LIMIT, 0, textChannels[0]?.id);
}
const channelMessageSets = await Promise.all(
textChannels.map((channel) => this.db.getMessages(roomId, INITIAL_ROOM_MESSAGE_LIMIT, 0, channel.id))
);
const messagesById = new Map<string, Message>();
for (const messages of channelMessageSets) {
for (const message of messages) {
messagesById.set(message.id, message);
}
}
return [...messagesById.values()].sort((first, second) => first.timestamp - second.timestamp);
}
/** Constructs a new message, persists it locally, and broadcasts to all peers. */
sendMessage$ = createEffect(() =>
this.actions$.pipe(
@@ -746,7 +687,78 @@ export class MessagesEffects {
)
);
private readonly actions$ = inject(Actions);
private readonly store = inject(Store);
private readonly db = inject(DatabaseService);
private readonly debugging = inject(DebuggingService);
private readonly attachments = inject(AttachmentFacade);
private readonly customEmoji = inject(CustomEmojiService);
private readonly webrtc = inject(RealtimeSessionFacade);
private readonly timeSync = inject(TimeSyncService);
private readonly linkMetadata = inject(LinkMetadataService);
private readonly platform = inject(PlatformService);
private readonly i18n = inject(AppI18nService);
private readonly messageRevisions = inject(MessageRevisionService);
private async fetchRoomMessagesForPrefetch(roomId: string, targetRoom: Room | null) {
try {
const messages = await this.loadInitialMessages(roomId, targetRoom);
const hydrated = await hydrateMessages(messages, this.db);
for (const message of hydrated) {
this.attachments.rememberMessageRoom(message.id, message.roomId);
}
return MessagesActions.prefetchRoomMessagesSuccess({ messages: hydrated });
} catch (error) {
reportDebuggingError(
this.debugging,
'MessagesEffects.prefetchRoomMessages',
'Failed to prefetch room messages',
{ roomId },
error
);
return MessagesActions.prefetchRoomMessagesSuccess({ messages: [] });
}
}
private async loadInitialMessages(roomId: string, targetRoom: Room | null): Promise<Message[]> {
const textChannels = targetRoom?.id === roomId
? (targetRoom.channels ?? []).filter((channel) => channel.type === 'text')
: [];
if (textChannels.length <= 1) {
return this.db.getMessages(roomId, INITIAL_ROOM_MESSAGE_LIMIT, 0, textChannels[0]?.id);
}
const channelMessageSets = await Promise.all(
textChannels.map((channel) => this.db.getMessages(roomId, INITIAL_ROOM_MESSAGE_LIMIT, 0, channel.id))
);
const messagesById = new Map<string, Message>();
for (const messages of channelMessageSets) {
for (const message of messages) {
messagesById.set(message.id, message);
}
}
return [...messagesById.values()].sort((first, second) => first.timestamp - second.timestamp);
}
private trackBackgroundOperation(task: Promise<unknown> | unknown, message: string, payload: Record<string, unknown>): void {
trackDebuggingTaskFailure(task, this.debugging, 'messages', message, payload);
}
}

View File

@@ -0,0 +1,52 @@
import {
describe,
expect,
it
} from 'vitest';
import { resolveRoomMemberActorIdentity } from './room-member-identity.rules';
describe('resolveRoomMemberActorIdentity', () => {
it('uses the foreign-server actor id instead of the home registration id', () => {
const identity = resolveRoomMemberActorIdentity(
{ sourceUrl: 'http://primary.example.com', members: [] },
{ id: 'home-user-id', oderId: 'home-peer-id' },
(_serverUrl, fallback) => (fallback === 'home-peer-id' ? 'foreign-actor-id' : fallback),
() => new Set([
'home-user-id',
'home-peer-id',
'foreign-actor-id'
])
);
expect(identity.actorUserId).toBe('foreign-actor-id');
});
it('reuses an existing self member id when the roster already tracked a foreign alias', () => {
const identity = resolveRoomMemberActorIdentity(
{
sourceUrl: 'http://primary.example.com',
members: [
{
id: 'home-user-id',
oderId: 'home-peer-id',
username: 'mixed_user_7',
displayName: 'Mixed User 7',
role: 'member',
joinedAt: 1,
lastSeenAt: 1
}
]
},
{ id: 'home-user-id', oderId: 'home-peer-id' },
() => 'foreign-actor-id',
() => new Set([
'home-user-id',
'home-peer-id',
'foreign-actor-id'
])
);
expect(identity.actorUserId).toBe('foreign-actor-id');
expect(identity.existingMemberId).toBe('home-user-id');
});
});

View File

@@ -0,0 +1,31 @@
import type { Room, User } from '../../shared-kernel';
import { isSelfPresenceUserId } from '../../domains/authentication/domain/logic/self-presence-identity.rules';
import { findRoomMember } from './room-members.helpers';
export interface RoomMemberActorIdentity {
readonly actorUserId: string;
readonly existingMemberId?: string;
}
/** Resolve the per-server user id that should represent the local user in a room roster. */
export function resolveRoomMemberActorIdentity(
room: Pick<Room, 'sourceUrl' | 'members'>,
currentUser: Pick<User, 'id' | 'oderId'>,
resolveActorUserId: (serverUrl: string | undefined, fallbackUserId: string) => string,
resolveSelfPresenceUserIds: (
currentUser: Pick<User, 'id' | 'oderId'>,
roomSourceUrl: string | undefined
) => ReadonlySet<string>
): RoomMemberActorIdentity {
const homeUserKey = currentUser.oderId || currentUser.id;
const actorUserId = resolveActorUserId(room.sourceUrl, homeUserKey);
const selfIds = resolveSelfPresenceUserIds(currentUser, room.sourceUrl);
const existingMember = (room.members ?? []).find((member) =>
isSelfPresenceUserId(member.oderId, selfIds) || isSelfPresenceUserId(member.id, selfIds)
) ?? findRoomMember(room.members ?? [], actorUserId);
return {
actorUserId,
existingMemberId: existingMember?.id
};
}

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import {
Actions,
@@ -41,10 +40,6 @@ import { isSelfPresenceUserId } from '../../domains/authentication/domain/logic/
@Injectable()
export class RoomMembersSyncEffects {
private readonly actions$ = inject(Actions);
private readonly store = inject(Store);
private readonly webrtc = inject(RealtimeSessionFacade);
private readonly signalServerAuth = inject(SignalServerAuthService);
/** Ensure the local user is recorded in a room as soon as it becomes active. */
ensureCurrentMemberOnRoomEntry$ = createEffect(() =>
@@ -328,6 +323,14 @@ export class RoomMembersSyncEffects {
)
);
private readonly actions$ = inject(Actions);
private readonly store = inject(Store);
private readonly webrtc = inject(RealtimeSessionFacade);
private readonly signalServerAuth = inject(SignalServerAuthService);
private resolveRoom(roomId: string | undefined, currentRoom: Room | null, savedRooms: Room[]): Room | null {
if (!roomId)
return null;
@@ -666,4 +669,5 @@ export class RoomMembersSyncEffects {
return actions;
}
}

View File

@@ -0,0 +1,116 @@
import {
describe,
expect,
it
} from 'vitest';
import {
collapseSelfRoomMembers,
pruneRoomMembers,
removeRoomMembersMatchingIds
} from './room-members.helpers';
describe('removeRoomMembersMatchingIds', () => {
it('removes every alias id for the same local user', () => {
const now = Date.now();
const members = removeRoomMembersMatchingIds(
[
{
id: 'home-id',
oderId: 'home-peer',
username: 'mixed_user_7',
displayName: 'Mixed User 7',
role: 'member',
joinedAt: now,
lastSeenAt: now
},
{
id: 'foreign-id',
oderId: 'foreign-id',
username: 'mixed_user_7',
displayName: 'Mixed User 7',
role: 'member',
joinedAt: now,
lastSeenAt: now
}
],
new Set([
'home-id',
'home-peer',
'foreign-id'
])
);
expect(members).toEqual([]);
});
});
describe('pruneRoomMembers display-name deduplication', () => {
it('collapses foreign and home aliases that share the same display name', () => {
const now = Date.now();
const members = pruneRoomMembers(
[
{
id: 'home-id',
oderId: 'home-peer',
username: 'mixed_user_2',
displayName: 'Mixed User 2',
role: 'member',
joinedAt: now,
lastSeenAt: now
},
{
id: 'foreign-id',
oderId: 'foreign-id',
username: 'mixed_user_2',
displayName: 'Mixed User 2',
role: 'member',
joinedAt: now,
lastSeenAt: now + 1
}
],
now
);
expect(members).toHaveLength(1);
expect(members[0].id).toBe('foreign-id');
expect(members[0].oderId).toBe('foreign-id');
});
});
describe('collapseSelfRoomMembers', () => {
it('replaces stale home aliases with the canonical actor member', () => {
const now = Date.now();
const members = collapseSelfRoomMembers(
[
{
id: 'home-id',
oderId: 'home-peer',
username: 'mixed_user_7',
displayName: 'Mixed User 7',
role: 'member',
joinedAt: now,
lastSeenAt: now
}
],
new Set([
'home-id',
'home-peer',
'foreign-id'
]),
{
id: 'foreign-id',
oderId: 'foreign-id',
username: 'mixed_user_7',
displayName: 'Mixed User 7',
role: 'member',
joinedAt: now,
lastSeenAt: now
},
now
);
expect(members).toHaveLength(1);
expect(members[0].id).toBe('foreign-id');
expect(members[0].oderId).toBe('foreign-id');
});
});

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import {
Actions,
@@ -37,11 +36,6 @@ import { defaultChannels } from './room-channels.defaults';
*/
@Injectable()
export class RoomSettingsEffects {
private actions$ = inject(Actions);
private store = inject(Store);
private webrtc = inject(RealtimeSessionFacade);
private db = inject(DatabaseService);
private serverDirectory = inject(ServerDirectoryFacade);
/** Updates room settings (host/admin-only) and broadcasts changes to all peers. */
updateRoomSettings$ = createEffect(() =>
@@ -368,4 +362,15 @@ export class RoomSettingsEffects {
})
)
);
private actions$ = inject(Actions);
private store = inject(Store);
private webrtc = inject(RealtimeSessionFacade);
private db = inject(DatabaseService);
private serverDirectory = inject(ServerDirectoryFacade);
}

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import {
Actions,
@@ -73,29 +72,6 @@ const SERVER_ICON_SYNC_REQUEST_DELAYS_MS = [
*/
@Injectable()
export class RoomStateSyncEffects {
private actions$ = inject(Actions);
private store = inject(Store);
private webrtc = inject(RealtimeSessionFacade);
private db = inject(DatabaseService);
private audioService = inject(NotificationAudioService);
private voiceSessionService = inject(VoiceSessionFacade);
private voiceClientTakeoverService = inject(VoiceClientTakeoverService);
private signalServerAuth = inject(SignalServerAuthService);
private clientInstanceService = inject(ClientInstanceService);
/**
* Tracks user IDs we already know are in voice. Lives outside the
* NgRx store so it survives room switches and presence re-syncs,
* preventing false join/leave sounds during state refreshes.
*/
private knownVoiceUsers = new Set<string>();
private pendingServerIconRequestsByPeer = new Map<string, Set<string>>();
/**
* When a user leaves (e.g. socket drops), record the timestamp so
* that a rapid re-join (reconnect) does not trigger a false
* join/leave sound within {@link RECONNECT_SOUND_GRACE_MS}.
*/
private recentlyLeftVoiceTimestamps = new Map<string, number>();
// ── Signaling presence ─────────────────────────────────────────
@@ -478,6 +454,40 @@ export class RoomStateSyncEffects {
{ dispatch: false }
);
private actions$ = inject(Actions);
private store = inject(Store);
private webrtc = inject(RealtimeSessionFacade);
private db = inject(DatabaseService);
private audioService = inject(NotificationAudioService);
private voiceSessionService = inject(VoiceSessionFacade);
private voiceClientTakeoverService = inject(VoiceClientTakeoverService);
private signalServerAuth = inject(SignalServerAuthService);
private clientInstanceService = inject(ClientInstanceService);
/**
* Tracks user IDs we already know are in voice. Lives outside the
* NgRx store so it survives room switches and presence re-syncs,
* preventing false join/leave sounds during state refreshes.
*/
private knownVoiceUsers = new Set<string>();
private pendingServerIconRequestsByPeer = new Map<string, Set<string>>();
/**
* When a user leaves (e.g. socket drops), record the timestamp so
* that a rapid re-join (reconnect) does not trigger a false
* join/leave sound within {@link RECONNECT_SOUND_GRACE_MS}.
*/
private recentlyLeftVoiceTimestamps = new Map<string, number>();
// ── Voice / Screen / Camera handlers ───────────────────────────
private handleVoiceOrScreenState(event: ChatEvent, allUsers: User[], currentUser: User | null, kind: 'voice' | 'screen' | 'camera') {
@@ -1002,4 +1012,5 @@ export class RoomStateSyncEffects {
})
);
}
}

View File

@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import {
@@ -70,23 +68,6 @@ const VIEW_SERVER_LOAD_DELAY_MS = 0;
@Injectable()
export class RoomsEffects {
private actions$ = inject(Actions);
private store = inject(Store);
private router = inject(Router);
private db = inject(DatabaseService);
private webrtc = inject(RealtimeSessionFacade);
private serverDirectory = inject(ServerDirectoryFacade);
private readonly i18n = inject(AppI18nService);
private readonly signalServerAuthorize = inject(SignalServerAuthorizeService);
private readonly signalServerAuth = inject(SignalServerAuthService);
private readonly signalingConnection = new RoomSignalingConnection(
this.webrtc,
this.serverDirectory,
this.store,
this.signalServerAuthorize,
this.signalServerAuth
);
/** Loads all saved rooms from the local database. */
loadRooms$ = createEffect(() =>
@@ -882,6 +863,32 @@ export class RoomsEffects {
{ dispatch: false }
);
private actions$ = inject(Actions);
private store = inject(Store);
private router = inject(Router);
private db = inject(DatabaseService);
private webrtc = inject(RealtimeSessionFacade);
private serverDirectory = inject(ServerDirectoryFacade);
private readonly i18n = inject(AppI18nService);
private readonly signalServerAuthorize = inject(SignalServerAuthorizeService);
private readonly signalServerAuth = inject(SignalServerAuthService);
private readonly signalingConnection = new RoomSignalingConnection(
this.webrtc,
this.serverDirectory,
this.store,
this.signalServerAuthorize,
this.signalServerAuth
);
// ── Private helpers ────────────────────────────────────────────
private async getBlockedRoomAccessActions(
@@ -907,4 +914,5 @@ export class RoomsEffects {
private getPersistedCurrentUserId(): string | null {
return localStorage.getItem('metoyou_currentUserId');
}
}

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import {
Actions,
@@ -109,13 +108,6 @@ export function shouldApplyAvatarTransfer(
@Injectable()
export class UserAvatarEffects {
private readonly actions$ = inject(Actions);
private readonly store = inject(Store);
private readonly webrtc = inject(RealtimeSessionFacade);
private readonly db = inject(DatabaseService);
private readonly avatars = inject(ProfileAvatarFacade);
private readonly pendingTransfers = new Map<string, PendingAvatarTransfer>();
persistCurrentAvatar$ = createEffect(
() =>
@@ -267,6 +259,18 @@ export class UserAvatarEffects {
)
);
private readonly actions$ = inject(Actions);
private readonly store = inject(Store);
private readonly webrtc = inject(RealtimeSessionFacade);
private readonly db = inject(DatabaseService);
private readonly avatars = inject(ProfileAvatarFacade);
private readonly pendingTransfers = new Map<string, PendingAvatarTransfer>();
private buildAvatarSummary(user: Pick<User, 'oderId' | 'id' | 'avatarHash' | 'avatarUpdatedAt' | 'profileUpdatedAt'>): ChatEvent {
return {
type: 'user-avatar-summary',
@@ -535,4 +539,5 @@ export class UserAvatarEffects {
reader.readAsDataURL(blob);
});
}
}

View File

@@ -1,7 +1,6 @@
/**
* Users store effects (load, kick, ban, host election, profile persistence).
*/
/* eslint-disable @typescript-eslint/member-ordering */
import { Injectable, inject } from '@angular/core';
import { Router } from '@angular/router';
import {
@@ -68,16 +67,6 @@ type IncomingModerationAction =
@Injectable()
export class UsersEffects {
private actions$ = inject(Actions);
private store = inject(Store);
private db = inject(DatabaseService);
private serverDirectory = inject(ServerDirectoryFacade);
private webrtc = inject(RealtimeSessionFacade);
private readonly i18n = inject(AppI18nService);
private readonly authTokenStore = inject(AuthTokenStoreService);
private readonly signalServerAuthRetries = new Map<string, { count: number; windowStart: number }>();
private readonly signalServerAuth = inject(SignalServerAuthService);
private readonly router = inject(Router);
/** Prepares persisted state for a successful login before exposing the user in-memory. */
authenticateUser$ = createEffect(() =>
@@ -255,54 +244,6 @@ export class UsersEffects {
)
);
private clearStartupVoiceConnection(user: User): User {
const voiceState = user.voiceState;
if (!voiceState)
return user;
const hasStaleConnectionState =
voiceState.isConnected ||
voiceState.isSpeaking ||
voiceState.roomId !== undefined ||
voiceState.serverId !== undefined;
if (!hasStaleConnectionState)
return user;
return {
...user,
voiceState: {
...voiceState,
isConnected: false,
isSpeaking: false,
roomId: undefined,
serverId: undefined
}
};
}
private async prepareAuthenticatedUserStorage(
user: User,
loginResponse?: {
id: string;
username: string;
displayName: string;
token: string;
expiresAt: number;
}
): Promise<void> {
setStoredCurrentUserId(user.id);
await this.db.initialize();
await this.db.setCurrentUserId(user.id);
await this.db.saveUser(user);
if (user.homeSignalServerUrl && loginResponse) {
this.signalServerAuth.upsertCredentialFromLogin(user.homeSignalServerUrl, loginResponse, { provisioned: false });
await this.signalServerAuth.ensureHomeProvisionSecret(user);
}
}
/** Loads all users associated with a specific room from the local database. */
loadRoomUsers$ = createEffect(() =>
this.actions$.pipe(
@@ -664,6 +605,74 @@ export class UsersEffects {
{ dispatch: false }
);
private actions$ = inject(Actions);
private store = inject(Store);
private db = inject(DatabaseService);
private serverDirectory = inject(ServerDirectoryFacade);
private webrtc = inject(RealtimeSessionFacade);
private readonly i18n = inject(AppI18nService);
private readonly authTokenStore = inject(AuthTokenStoreService);
private readonly signalServerAuthRetries = new Map<string, { count: number; windowStart: number }>();
private readonly signalServerAuth = inject(SignalServerAuthService);
private readonly router = inject(Router);
private clearStartupVoiceConnection(user: User): User {
const voiceState = user.voiceState;
if (!voiceState)
return user;
const hasStaleConnectionState =
voiceState.isConnected ||
voiceState.isSpeaking ||
voiceState.roomId !== undefined ||
voiceState.serverId !== undefined;
if (!hasStaleConnectionState)
return user;
return {
...user,
voiceState: {
...voiceState,
isConnected: false,
isSpeaking: false,
roomId: undefined,
serverId: undefined
}
};
}
private async prepareAuthenticatedUserStorage(
user: User,
loginResponse?: {
id: string;
username: string;
displayName: string;
token: string;
expiresAt: number;
}
): Promise<void> {
setStoredCurrentUserId(user.id);
await this.db.initialize();
await this.db.setCurrentUserId(user.id);
await this.db.saveUser(user);
if (user.homeSignalServerUrl && loginResponse) {
this.signalServerAuth.upsertCredentialFromLogin(user.homeSignalServerUrl, loginResponse, { provisioned: false });
await this.signalServerAuth.ensureHomeProvisionSecret(user);
}
}
private resolveRoom(roomId: string | undefined, currentRoom: Room | null, savedRooms: Room[]): Room | null {
if (!roomId)
return currentRoom;
@@ -896,4 +905,5 @@ export class UsersEffects {
catchError(() => EMPTY)
);
}
}