feat: Add chat seperator and restore last viewed chat on restart
This commit is contained in:
@@ -2,7 +2,10 @@
|
||||
/* eslint-disable id-length */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars,, complexity */
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
NavigationEnd,
|
||||
Router
|
||||
} from '@angular/router';
|
||||
import {
|
||||
Actions,
|
||||
createEffect,
|
||||
@@ -15,7 +18,8 @@ import {
|
||||
import {
|
||||
of,
|
||||
from,
|
||||
EMPTY
|
||||
EMPTY,
|
||||
merge
|
||||
} from 'rxjs';
|
||||
import {
|
||||
map,
|
||||
@@ -38,7 +42,12 @@ import {
|
||||
selectSavedRooms
|
||||
} from './rooms.selectors';
|
||||
import { RealtimeSessionFacade } from '../../core/realtime';
|
||||
import { DatabaseService } from '../../infrastructure/persistence';
|
||||
import {
|
||||
clearLastViewedChatFromStorage,
|
||||
DatabaseService,
|
||||
loadLastViewedChatFromStorage,
|
||||
saveLastViewedChatToStorage
|
||||
} from '../../infrastructure/persistence';
|
||||
import {
|
||||
CLIENT_UPDATE_REQUIRED_MESSAGE,
|
||||
type ServerSourceSelector,
|
||||
@@ -137,6 +146,19 @@ function resolveRoomChannels(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveTextChannelId(
|
||||
channels: Room['channels'] | undefined,
|
||||
preferredChannelId?: string | null
|
||||
): string | null {
|
||||
const textChannels = (channels ?? []).filter((channel) => channel.type === 'text');
|
||||
|
||||
if (preferredChannelId && textChannels.some((channel) => channel.id === preferredChannelId)) {
|
||||
return preferredChannelId;
|
||||
}
|
||||
|
||||
return textChannels[0]?.id ?? null;
|
||||
}
|
||||
|
||||
interface RoomPresenceSignalingMessage {
|
||||
type: string;
|
||||
reason?: string;
|
||||
@@ -183,6 +205,51 @@ export class RoomsEffects {
|
||||
)
|
||||
);
|
||||
|
||||
/** Opens the routed room after login/refresh once saved rooms are available. */
|
||||
syncViewedRoomToRoute$ = createEffect(() =>
|
||||
merge(
|
||||
this.actions$.pipe(
|
||||
ofType(
|
||||
RoomsActions.loadRoomsSuccess,
|
||||
UsersActions.loadCurrentUserSuccess,
|
||||
UsersActions.setCurrentUser
|
||||
)
|
||||
),
|
||||
this.router.events.pipe(
|
||||
filter((event): event is NavigationEnd => event instanceof NavigationEnd)
|
||||
)
|
||||
).pipe(
|
||||
withLatestFrom(
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectCurrentRoom),
|
||||
this.store.select(selectSavedRooms)
|
||||
),
|
||||
mergeMap(([
|
||||
, currentUser,
|
||||
currentRoom,
|
||||
savedRooms
|
||||
]) => {
|
||||
if (!currentUser) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
const roomId = this.extractRoomIdFromUrl(this.router.url);
|
||||
|
||||
if (!roomId || currentRoom?.id === roomId) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
const room = savedRooms.find((savedRoom) => savedRoom.id === roomId) ?? null;
|
||||
|
||||
if (!room) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
return of(RoomsActions.viewServer({ room }));
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
/** Searches the server directory with debounced input. */
|
||||
searchServers$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
@@ -431,6 +498,106 @@ export class RoomsEffects {
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
/** Remembers the viewed room whenever a room becomes active. */
|
||||
persistLastViewedChatOnRoomActivation$ = createEffect(
|
||||
() =>
|
||||
this.actions$.pipe(
|
||||
ofType(RoomsActions.createRoomSuccess, RoomsActions.joinRoomSuccess, RoomsActions.viewServerSuccess),
|
||||
withLatestFrom(this.store.select(selectCurrentUser)),
|
||||
tap(([
|
||||
{ room },
|
||||
currentUser
|
||||
]) => {
|
||||
if (!currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const persisted = loadLastViewedChatFromStorage(currentUser.id);
|
||||
const channelId = persisted?.roomId === room.id
|
||||
? resolveTextChannelId(room.channels, persisted.channelId)
|
||||
: resolveTextChannelId(room.channels);
|
||||
|
||||
saveLastViewedChatToStorage({
|
||||
userId: currentUser.id,
|
||||
roomId: room.id,
|
||||
channelId
|
||||
});
|
||||
})
|
||||
),
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
/** Remembers the currently selected text channel for the active room. */
|
||||
persistLastViewedChatOnChannelSelection$ = createEffect(
|
||||
() =>
|
||||
this.actions$.pipe(
|
||||
ofType(RoomsActions.selectChannel),
|
||||
withLatestFrom(this.store.select(selectCurrentRoom), this.store.select(selectCurrentUser)),
|
||||
tap(([
|
||||
{ channelId },
|
||||
currentRoom,
|
||||
currentUser
|
||||
]) => {
|
||||
if (!currentRoom || !currentUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resolvedChannelId = resolveTextChannelId(currentRoom.channels, channelId);
|
||||
|
||||
if (!resolvedChannelId || resolvedChannelId !== channelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
saveLastViewedChatToStorage({
|
||||
userId: currentUser.id,
|
||||
roomId: currentRoom.id,
|
||||
channelId
|
||||
});
|
||||
})
|
||||
),
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
/** Restores the last viewed text channel once the active room's channels are known. */
|
||||
restoreLastViewedTextChannel$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(
|
||||
RoomsActions.createRoomSuccess,
|
||||
RoomsActions.joinRoomSuccess,
|
||||
RoomsActions.viewServerSuccess,
|
||||
RoomsActions.updateRoom
|
||||
),
|
||||
withLatestFrom(
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectCurrentRoom),
|
||||
this.store.select(selectActiveChannelId)
|
||||
),
|
||||
mergeMap(([
|
||||
, currentUser,
|
||||
currentRoom,
|
||||
activeChannelId
|
||||
]) => {
|
||||
if (!currentUser || !currentRoom) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
const persisted = loadLastViewedChatFromStorage(currentUser.id);
|
||||
|
||||
if (!persisted || persisted.roomId !== currentRoom.id) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
const channelId = resolveTextChannelId(currentRoom.channels, persisted.channelId);
|
||||
|
||||
if (!channelId || channelId === activeChannelId) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
return of(RoomsActions.selectChannel({ channelId }));
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
refreshServerOwnedRoomMetadata$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(RoomsActions.joinRoomSuccess, RoomsActions.viewServerSuccess),
|
||||
@@ -648,6 +815,22 @@ export class RoomsEffects {
|
||||
)
|
||||
);
|
||||
|
||||
/** Clears stale resume state when the remembered room is removed locally. */
|
||||
clearLastViewedChatOnRoomRemoval$ = createEffect(
|
||||
() =>
|
||||
this.actions$.pipe(
|
||||
ofType(RoomsActions.deleteRoomSuccess, RoomsActions.forgetRoomSuccess),
|
||||
tap(({ roomId }) => {
|
||||
const persisted = loadLastViewedChatFromStorage();
|
||||
|
||||
if (persisted?.roomId === roomId) {
|
||||
clearLastViewedChatFromStorage();
|
||||
}
|
||||
})
|
||||
),
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
/** Updates room settings (host/admin-only) and broadcasts changes to all peers. */
|
||||
updateRoomSettings$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
|
||||
Reference in New Issue
Block a user