Now formatted correctly with eslint
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
* Root state definition and barrel exports for the NgRx store.
|
||||
*
|
||||
* Three feature slices:
|
||||
* - **messages** – chat messages, reactions, sync state
|
||||
* - **users** – online users, bans, roles, voice state
|
||||
* - **rooms** – servers / rooms, channels, search results
|
||||
* - **messages** - chat messages, reactions, sync state
|
||||
* - **users** - online users, bans, roles, voice state
|
||||
* - **rooms** - servers / rooms, channels, search results
|
||||
*/
|
||||
import { isDevMode } from '@angular/core';
|
||||
import { ActionReducerMap, MetaReducer } from '@ngrx/store';
|
||||
|
||||
@@ -9,7 +9,12 @@
|
||||
* handlers, and `dispatchIncomingMessage()` is the single entry point
|
||||
* consumed by the `incomingMessages$` effect.
|
||||
*/
|
||||
import { Observable, of, from, EMPTY } from 'rxjs';
|
||||
import {
|
||||
Observable,
|
||||
of,
|
||||
from,
|
||||
EMPTY
|
||||
} from 'rxjs';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
import { Action } from '@ngrx/store';
|
||||
import { Message } from '../../core/models';
|
||||
@@ -264,6 +269,7 @@ function handleMessageEdited(
|
||||
content: event.content,
|
||||
editedAt: event.editedAt
|
||||
});
|
||||
|
||||
return of(
|
||||
MessagesActions.editMessageSuccess({
|
||||
messageId: event.messageId,
|
||||
|
||||
@@ -10,9 +10,19 @@
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import {
|
||||
Actions,
|
||||
createEffect,
|
||||
ofType
|
||||
} from '@ngrx/effects';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { of, from, timer, Subject, EMPTY } from 'rxjs';
|
||||
import {
|
||||
of,
|
||||
from,
|
||||
timer,
|
||||
Subject,
|
||||
EMPTY
|
||||
} from 'rxjs';
|
||||
import {
|
||||
map,
|
||||
mergeMap,
|
||||
@@ -78,6 +88,7 @@ export class MessagesSyncEffects {
|
||||
count,
|
||||
lastUpdated
|
||||
} as any);
|
||||
|
||||
this.webrtc.sendToPeer(peerId, {
|
||||
type: 'chat-inventory-request',
|
||||
roomId: room.id
|
||||
@@ -119,6 +130,7 @@ export class MessagesSyncEffects {
|
||||
count,
|
||||
lastUpdated
|
||||
} as any);
|
||||
|
||||
this.webrtc.sendToPeer(pid, {
|
||||
type: 'chat-inventory-request',
|
||||
roomId: activeRoom.id
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
* Action type strings follow the `[Messages] Event Name` convention and are
|
||||
* generated automatically by NgRx from the `source` and event key.
|
||||
*/
|
||||
import { createActionGroup, emptyProps, props } from '@ngrx/store';
|
||||
import {
|
||||
createActionGroup,
|
||||
emptyProps,
|
||||
props
|
||||
} from '@ngrx/store';
|
||||
import { Message, Reaction } from '../../core/models';
|
||||
|
||||
export const MessagesActions = createActionGroup({
|
||||
|
||||
@@ -10,10 +10,23 @@
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import {
|
||||
Actions,
|
||||
createEffect,
|
||||
ofType
|
||||
} from '@ngrx/effects';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { of, from, EMPTY } from 'rxjs';
|
||||
import { mergeMap, catchError, withLatestFrom, switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
of,
|
||||
from,
|
||||
EMPTY
|
||||
} from 'rxjs';
|
||||
import {
|
||||
mergeMap,
|
||||
catchError,
|
||||
withLatestFrom,
|
||||
switchMap
|
||||
} from 'rxjs/operators';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { MessagesActions } from './messages.actions';
|
||||
import { selectCurrentUser } from '../users/users.selectors';
|
||||
@@ -24,10 +37,7 @@ import { TimeSyncService } from '../../core/services/time-sync.service';
|
||||
import { AttachmentService } from '../../core/services/attachment.service';
|
||||
import { Message, Reaction } from '../../core/models';
|
||||
import { hydrateMessages } from './messages.helpers';
|
||||
import {
|
||||
dispatchIncomingMessage,
|
||||
IncomingMessageContext
|
||||
} from './messages-incoming.handlers';
|
||||
import { dispatchIncomingMessage, IncomingMessageContext } from './messages-incoming.handlers';
|
||||
|
||||
@Injectable()
|
||||
export class MessagesEffects {
|
||||
@@ -65,7 +75,11 @@ export class MessagesEffects {
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectCurrentRoom)
|
||||
),
|
||||
mergeMap(([{ content, replyToId, channelId }, currentUser, currentRoom]) => {
|
||||
mergeMap(([
|
||||
{ content, replyToId, channelId },
|
||||
currentUser,
|
||||
currentRoom
|
||||
]) => {
|
||||
if (!currentUser || !currentRoom) {
|
||||
return of(MessagesActions.sendMessageFailure({ error: 'Not connected to a room' }));
|
||||
}
|
||||
@@ -84,7 +98,8 @@ export class MessagesEffects {
|
||||
};
|
||||
|
||||
this.db.saveMessage(message);
|
||||
this.webrtc.broadcastMessage({ type: 'chat-message', message });
|
||||
this.webrtc.broadcastMessage({ type: 'chat-message',
|
||||
message });
|
||||
|
||||
return of(MessagesActions.sendMessageSuccess({ message }));
|
||||
}),
|
||||
@@ -116,10 +131,17 @@ export class MessagesEffects {
|
||||
|
||||
const editedAt = this.timeSync.now();
|
||||
|
||||
this.db.updateMessage(messageId, { content, editedAt });
|
||||
this.webrtc.broadcastMessage({ type: 'message-edited', messageId, content, editedAt });
|
||||
this.db.updateMessage(messageId, { content,
|
||||
editedAt });
|
||||
|
||||
return of(MessagesActions.editMessageSuccess({ messageId, content, editedAt }));
|
||||
this.webrtc.broadcastMessage({ type: 'message-edited',
|
||||
messageId,
|
||||
content,
|
||||
editedAt });
|
||||
|
||||
return of(MessagesActions.editMessageSuccess({ messageId,
|
||||
content,
|
||||
editedAt }));
|
||||
}),
|
||||
catchError((error) =>
|
||||
of(MessagesActions.editMessageFailure({ error: error.message }))
|
||||
@@ -150,7 +172,8 @@ export class MessagesEffects {
|
||||
}
|
||||
|
||||
this.db.updateMessage(messageId, { isDeleted: true });
|
||||
this.webrtc.broadcastMessage({ type: 'message-deleted', messageId });
|
||||
this.webrtc.broadcastMessage({ type: 'message-deleted',
|
||||
messageId });
|
||||
|
||||
return of(MessagesActions.deleteMessageSuccess({ messageId }));
|
||||
}),
|
||||
@@ -182,7 +205,9 @@ export class MessagesEffects {
|
||||
}
|
||||
|
||||
this.db.updateMessage(messageId, { isDeleted: true });
|
||||
this.webrtc.broadcastMessage({ type: 'message-deleted', messageId, deletedBy: currentUser.id });
|
||||
this.webrtc.broadcastMessage({ type: 'message-deleted',
|
||||
messageId,
|
||||
deletedBy: currentUser.id });
|
||||
|
||||
return of(MessagesActions.deleteMessageSuccess({ messageId }));
|
||||
}),
|
||||
@@ -211,7 +236,9 @@ export class MessagesEffects {
|
||||
};
|
||||
|
||||
this.db.saveReaction(reaction);
|
||||
this.webrtc.broadcastMessage({ type: 'reaction-added', messageId, reaction });
|
||||
this.webrtc.broadcastMessage({ type: 'reaction-added',
|
||||
messageId,
|
||||
reaction });
|
||||
|
||||
return of(MessagesActions.addReactionSuccess({ reaction }));
|
||||
})
|
||||
@@ -256,7 +283,11 @@ export class MessagesEffects {
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectCurrentRoom)
|
||||
),
|
||||
mergeMap(([event, currentUser, currentRoom]: [any, any, any]) => {
|
||||
mergeMap(([
|
||||
event,
|
||||
currentUser,
|
||||
currentRoom]: [any, any, any
|
||||
]) => {
|
||||
const ctx: IncomingMessageContext = {
|
||||
db: this.db,
|
||||
webrtc: this.webrtc,
|
||||
|
||||
@@ -56,7 +56,8 @@ export async function hydrateMessage(
|
||||
): Promise<Message> {
|
||||
const reactions = await db.getReactionsForMessage(msg.id);
|
||||
|
||||
return reactions.length > 0 ? { ...msg, reactions } : msg;
|
||||
return reactions.length > 0 ? { ...msg,
|
||||
reactions } : msg;
|
||||
}
|
||||
|
||||
/** Hydrates an array of messages with their reactions. */
|
||||
@@ -81,7 +82,9 @@ export async function buildInventoryItem(
|
||||
): Promise<InventoryItem> {
|
||||
const reactions = await db.getReactionsForMessage(msg.id);
|
||||
|
||||
return { id: msg.id, ts: getMessageTimestamp(msg), rc: reactions.length };
|
||||
return { id: msg.id,
|
||||
ts: getMessageTimestamp(msg),
|
||||
rc: reactions.length };
|
||||
}
|
||||
|
||||
/** Builds a local map of `{timestamp, reactionCount}` keyed by message ID. */
|
||||
@@ -95,9 +98,11 @@ export async function buildLocalInventoryMap(
|
||||
messages.map(async (msg) => {
|
||||
const reactions = await db.getReactionsForMessage(msg.id);
|
||||
|
||||
map.set(msg.id, { ts: getMessageTimestamp(msg), rc: reactions.length });
|
||||
map.set(msg.id, { ts: getMessageTimestamp(msg),
|
||||
rc: reactions.length });
|
||||
})
|
||||
);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -161,18 +166,22 @@ export async function mergeIncomingMessage(
|
||||
const baseMessage = isNewer ? incoming : existing;
|
||||
|
||||
if (!baseMessage) {
|
||||
return { message: incoming, changed };
|
||||
return { message: incoming,
|
||||
changed };
|
||||
}
|
||||
|
||||
return {
|
||||
message: { ...baseMessage, reactions },
|
||||
message: { ...baseMessage,
|
||||
reactions },
|
||||
changed
|
||||
};
|
||||
}
|
||||
|
||||
if (!existing) {
|
||||
return { message: incoming, changed: false };
|
||||
return { message: incoming,
|
||||
changed: false };
|
||||
}
|
||||
|
||||
return { message: existing, changed: false };
|
||||
return { message: existing,
|
||||
changed: false };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { createReducer, on } from '@ngrx/store';
|
||||
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
|
||||
import {
|
||||
EntityState,
|
||||
EntityAdapter,
|
||||
createEntityAdapter
|
||||
} from '@ngrx/entity';
|
||||
import { Message } from '../../core/models';
|
||||
import { MessagesActions } from './messages.actions';
|
||||
|
||||
@@ -30,7 +34,7 @@ export const initialState: MessagesState = messagesAdapter.getInitialState({
|
||||
export const messagesReducer = createReducer(
|
||||
initialState,
|
||||
|
||||
// Load messages — clear stale messages when switching to a different room
|
||||
// Load messages - clear stale messages when switching to a different room
|
||||
on(MessagesActions.loadMessages, (state, { roomId }) => {
|
||||
if (state.currentRoomId && state.currentRoomId !== roomId) {
|
||||
return messagesAdapter.removeAll({
|
||||
@@ -91,7 +95,8 @@ export const messagesReducer = createReducer(
|
||||
messagesAdapter.updateOne(
|
||||
{
|
||||
id: messageId,
|
||||
changes: { content, editedAt }
|
||||
changes: { content,
|
||||
editedAt }
|
||||
},
|
||||
state
|
||||
)
|
||||
@@ -102,7 +107,8 @@ export const messagesReducer = createReducer(
|
||||
messagesAdapter.updateOne(
|
||||
{
|
||||
id: messageId,
|
||||
changes: { isDeleted: true, content: '[Message deleted]' }
|
||||
changes: { isDeleted: true,
|
||||
content: '[Message deleted]' }
|
||||
},
|
||||
state
|
||||
)
|
||||
@@ -184,7 +190,8 @@ export const messagesReducer = createReducer(
|
||||
}
|
||||
}
|
||||
|
||||
return { ...message, reactions: combined };
|
||||
return { ...message,
|
||||
reactions: combined };
|
||||
}
|
||||
|
||||
return message;
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
/**
|
||||
* Rooms store actions using `createActionGroup`.
|
||||
*/
|
||||
import { createActionGroup, emptyProps, props } from '@ngrx/store';
|
||||
import { Room, RoomSettings, ServerInfo, RoomPermissions, Channel } from '../../core/models';
|
||||
import {
|
||||
createActionGroup,
|
||||
emptyProps,
|
||||
props
|
||||
} from '@ngrx/store';
|
||||
import {
|
||||
Room,
|
||||
RoomSettings,
|
||||
ServerInfo,
|
||||
RoomPermissions,
|
||||
Channel
|
||||
} from '../../core/models';
|
||||
|
||||
export const RoomsActions = createActionGroup({
|
||||
source: 'Rooms',
|
||||
|
||||
@@ -3,9 +3,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-non-null-assertion */
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import {
|
||||
Actions,
|
||||
createEffect,
|
||||
ofType
|
||||
} from '@ngrx/effects';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { of, from, EMPTY } from 'rxjs';
|
||||
import {
|
||||
of,
|
||||
from,
|
||||
EMPTY
|
||||
} from 'rxjs';
|
||||
import {
|
||||
map,
|
||||
mergeMap,
|
||||
@@ -25,7 +33,12 @@ import { selectCurrentRoom } from './rooms.selectors';
|
||||
import { DatabaseService } from '../../core/services/database.service';
|
||||
import { WebRTCService } from '../../core/services/webrtc.service';
|
||||
import { ServerDirectoryService } from '../../core/services/server-directory.service';
|
||||
import { Room, RoomSettings, RoomPermissions, VoiceState } from '../../core/models';
|
||||
import {
|
||||
Room,
|
||||
RoomSettings,
|
||||
RoomPermissions,
|
||||
VoiceState
|
||||
} from '../../core/models';
|
||||
import { NotificationAudioService, AppSound } from '../../core/services/notification-audio.service';
|
||||
|
||||
/** Build a minimal User object from signaling payload. */
|
||||
@@ -285,11 +298,16 @@ export class RoomsEffects {
|
||||
ofType(RoomsActions.deleteRoom),
|
||||
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectCurrentRoom)),
|
||||
filter(
|
||||
([, currentUser, currentRoom]) => !!currentUser && currentRoom?.hostId === currentUser.id
|
||||
([
|
||||
, currentUser,
|
||||
currentRoom
|
||||
]) => !!currentUser && currentRoom?.hostId === currentUser.id
|
||||
),
|
||||
switchMap(([{ roomId }]) => {
|
||||
this.db.deleteRoom(roomId);
|
||||
this.webrtc.broadcastMessage({ type: 'room-deleted', roomId });
|
||||
this.webrtc.broadcastMessage({ type: 'room-deleted',
|
||||
roomId });
|
||||
|
||||
this.webrtc.disconnectAll();
|
||||
return of(RoomsActions.deleteRoomSuccess({ roomId }));
|
||||
})
|
||||
@@ -318,7 +336,11 @@ export class RoomsEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(RoomsActions.updateRoomSettings),
|
||||
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectCurrentRoom)),
|
||||
mergeMap(([{ settings }, currentUser, currentRoom]) => {
|
||||
mergeMap(([
|
||||
{ settings },
|
||||
currentUser,
|
||||
currentRoom
|
||||
]) => {
|
||||
if (!currentUser || !currentRoom) {
|
||||
return of(RoomsActions.updateRoomSettingsFailure({ error: 'Not in a room' }));
|
||||
}
|
||||
@@ -377,15 +399,22 @@ export class RoomsEffects {
|
||||
ofType(RoomsActions.updateRoomPermissions),
|
||||
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectCurrentRoom)),
|
||||
filter(
|
||||
([{ roomId }, currentUser, currentRoom]) =>
|
||||
([
|
||||
{ roomId },
|
||||
currentUser,
|
||||
currentRoom
|
||||
]) =>
|
||||
!!currentUser &&
|
||||
!!currentRoom &&
|
||||
currentRoom.id === roomId &&
|
||||
currentRoom.hostId === currentUser.id
|
||||
),
|
||||
mergeMap(([{ roomId, permissions }, , currentRoom]) => {
|
||||
mergeMap(([
|
||||
{ roomId, permissions }, , currentRoom
|
||||
]) => {
|
||||
const updated: Partial<Room> = {
|
||||
permissions: { ...(currentRoom!.permissions || {}), ...permissions } as RoomPermissions
|
||||
permissions: { ...(currentRoom!.permissions || {}),
|
||||
...permissions } as RoomPermissions
|
||||
};
|
||||
|
||||
this.db.updateRoom(roomId, updated);
|
||||
@@ -394,7 +423,9 @@ export class RoomsEffects {
|
||||
type: 'room-permissions-update',
|
||||
permissions: updated.permissions
|
||||
} as any);
|
||||
return of(RoomsActions.updateRoom({ roomId, changes: updated }));
|
||||
|
||||
return of(RoomsActions.updateRoom({ roomId,
|
||||
changes: updated }));
|
||||
})
|
||||
)
|
||||
);
|
||||
@@ -404,7 +435,11 @@ export class RoomsEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(RoomsActions.updateServerIcon),
|
||||
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectCurrentRoom)),
|
||||
mergeMap(([{ roomId, icon }, currentUser, currentRoom]) => {
|
||||
mergeMap(([
|
||||
{ roomId, icon },
|
||||
currentUser,
|
||||
currentRoom
|
||||
]) => {
|
||||
if (!currentUser || !currentRoom || currentRoom.id !== roomId) {
|
||||
return of(RoomsActions.updateServerIconFailure({ error: 'Not in room' }));
|
||||
}
|
||||
@@ -421,7 +456,8 @@ export class RoomsEffects {
|
||||
}
|
||||
|
||||
const iconUpdatedAt = Date.now();
|
||||
const changes: Partial<Room> = { icon, iconUpdatedAt };
|
||||
const changes: Partial<Room> = { icon,
|
||||
iconUpdatedAt };
|
||||
|
||||
this.db.updateRoom(roomId, changes);
|
||||
// Broadcast to peers
|
||||
@@ -431,7 +467,10 @@ export class RoomsEffects {
|
||||
icon,
|
||||
iconUpdatedAt
|
||||
} as any);
|
||||
return of(RoomsActions.updateServerIconSuccess({ roomId, icon, iconUpdatedAt }));
|
||||
|
||||
return of(RoomsActions.updateServerIconSuccess({ roomId,
|
||||
icon,
|
||||
iconUpdatedAt }));
|
||||
})
|
||||
)
|
||||
);
|
||||
@@ -491,7 +530,11 @@ export class RoomsEffects {
|
||||
signalingMessages$ = createEffect(() =>
|
||||
this.webrtc.onSignalingMessage.pipe(
|
||||
withLatestFrom(this.store.select(selectCurrentUser), this.store.select(selectCurrentRoom)),
|
||||
mergeMap(([message, currentUser, currentRoom]: [any, any, any]) => {
|
||||
mergeMap(([
|
||||
message,
|
||||
currentUser,
|
||||
currentRoom]: [any, any, any
|
||||
]) => {
|
||||
const myId = currentUser?.oderId || currentUser?.id;
|
||||
const viewedServerId = currentRoom?.id;
|
||||
|
||||
@@ -533,7 +576,11 @@ export class RoomsEffects {
|
||||
this.webrtc.onMessageReceived.pipe(
|
||||
withLatestFrom(this.store.select(selectCurrentRoom), this.store.select(selectAllUsers)),
|
||||
filter(([, room]) => !!room),
|
||||
mergeMap(([event, currentRoom, allUsers]: [any, any, any[]]) => {
|
||||
mergeMap(([
|
||||
event,
|
||||
currentRoom,
|
||||
allUsers]: [any, any, any[]
|
||||
]) => {
|
||||
const room = currentRoom as Room;
|
||||
|
||||
switch (event.type) {
|
||||
@@ -590,7 +637,8 @@ export class RoomsEffects {
|
||||
return of(
|
||||
UsersActions.userJoined({
|
||||
user: buildSignalingUser(
|
||||
{ oderId: userId, displayName: event.displayName || 'User' },
|
||||
{ oderId: userId,
|
||||
displayName: event.displayName || 'User' },
|
||||
{
|
||||
voiceState: {
|
||||
isConnected: vs.isConnected ?? false,
|
||||
@@ -608,7 +656,8 @@ export class RoomsEffects {
|
||||
);
|
||||
}
|
||||
|
||||
return of(UsersActions.updateVoiceState({ userId, voiceState: vs }));
|
||||
return of(UsersActions.updateVoiceState({ userId,
|
||||
voiceState: vs }));
|
||||
}
|
||||
|
||||
// screen-state
|
||||
@@ -621,7 +670,8 @@ export class RoomsEffects {
|
||||
return of(
|
||||
UsersActions.userJoined({
|
||||
user: buildSignalingUser(
|
||||
{ oderId: userId, displayName: event.displayName || 'User' },
|
||||
{ oderId: userId,
|
||||
displayName: event.displayName || 'User' },
|
||||
{ screenShareState: { isSharing } }
|
||||
)
|
||||
})
|
||||
@@ -700,7 +750,8 @@ export class RoomsEffects {
|
||||
};
|
||||
|
||||
this.db.updateRoom(room.id, updates);
|
||||
return of(RoomsActions.updateRoom({ roomId: room.id, changes: updates }));
|
||||
return of(RoomsActions.updateRoom({ roomId: room.id,
|
||||
changes: updates }));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,31 @@
|
||||
import { createReducer, on } from '@ngrx/store';
|
||||
import { Room, ServerInfo, RoomSettings, Channel } from '../../core/models';
|
||||
import {
|
||||
Room,
|
||||
ServerInfo,
|
||||
RoomSettings,
|
||||
Channel
|
||||
} from '../../core/models';
|
||||
import { RoomsActions } from './rooms.actions';
|
||||
|
||||
/** Default channels for a new server */
|
||||
export function defaultChannels(): Channel[] {
|
||||
return [
|
||||
{ id: 'general', name: 'general', type: 'text', position: 0 },
|
||||
{ id: 'random', name: 'random', type: 'text', position: 1 },
|
||||
{ id: 'vc-general', name: 'General', type: 'voice', position: 0 },
|
||||
{ id: 'vc-afk', name: 'AFK', type: 'voice', position: 1 }
|
||||
{ id: 'general',
|
||||
name: 'general',
|
||||
type: 'text',
|
||||
position: 0 },
|
||||
{ id: 'random',
|
||||
name: 'random',
|
||||
type: 'text',
|
||||
position: 1 },
|
||||
{ id: 'vc-general',
|
||||
name: 'General',
|
||||
type: 'voice',
|
||||
position: 0 },
|
||||
{ id: 'vc-afk',
|
||||
name: 'AFK',
|
||||
type: 'voice',
|
||||
position: 1 }
|
||||
];
|
||||
}
|
||||
|
||||
@@ -123,7 +140,8 @@ export const roomsReducer = createReducer(
|
||||
})),
|
||||
|
||||
on(RoomsActions.createRoomSuccess, (state, { room }) => {
|
||||
const enriched = { ...room, channels: room.channels || defaultChannels() };
|
||||
const enriched = { ...room,
|
||||
channels: room.channels || defaultChannels() };
|
||||
|
||||
return {
|
||||
...state,
|
||||
@@ -149,7 +167,8 @@ export const roomsReducer = createReducer(
|
||||
})),
|
||||
|
||||
on(RoomsActions.joinRoomSuccess, (state, { room }) => {
|
||||
const enriched = { ...room, channels: room.channels || defaultChannels() };
|
||||
const enriched = { ...room,
|
||||
channels: room.channels || defaultChannels() };
|
||||
|
||||
return {
|
||||
...state,
|
||||
@@ -181,7 +200,7 @@ export const roomsReducer = createReducer(
|
||||
isConnected: false
|
||||
})),
|
||||
|
||||
// View server – just switch the viewed room, stay connected
|
||||
// View server - just switch the viewed room, stay connected
|
||||
on(RoomsActions.viewServer, (state) => ({
|
||||
...state,
|
||||
isConnecting: true,
|
||||
@@ -189,7 +208,8 @@ export const roomsReducer = createReducer(
|
||||
})),
|
||||
|
||||
on(RoomsActions.viewServerSuccess, (state, { room }) => {
|
||||
const enriched = { ...room, channels: room.channels || defaultChannels() };
|
||||
const enriched = { ...room,
|
||||
channels: room.channels || defaultChannels() };
|
||||
|
||||
return {
|
||||
...state,
|
||||
@@ -264,7 +284,8 @@ export const roomsReducer = createReducer(
|
||||
|
||||
return {
|
||||
...state,
|
||||
currentRoom: { ...state.currentRoom, ...changes }
|
||||
currentRoom: { ...state.currentRoom,
|
||||
...changes }
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -275,14 +296,17 @@ export const roomsReducer = createReducer(
|
||||
|
||||
return {
|
||||
...state,
|
||||
currentRoom: { ...state.currentRoom, icon, iconUpdatedAt }
|
||||
currentRoom: { ...state.currentRoom,
|
||||
icon,
|
||||
iconUpdatedAt }
|
||||
};
|
||||
}),
|
||||
|
||||
// Receive room update
|
||||
on(RoomsActions.receiveRoomUpdate, (state, { room }) => ({
|
||||
...state,
|
||||
currentRoom: state.currentRoom ? { ...state.currentRoom, ...room } : null
|
||||
currentRoom: state.currentRoom ? { ...state.currentRoom,
|
||||
...room } : null
|
||||
})),
|
||||
|
||||
// Clear search results
|
||||
@@ -309,7 +333,8 @@ export const roomsReducer = createReducer(
|
||||
|
||||
const existing = state.currentRoom.channels || defaultChannels();
|
||||
const updatedChannels = [...existing, channel];
|
||||
const updatedRoom = { ...state.currentRoom, channels: updatedChannels };
|
||||
const updatedRoom = { ...state.currentRoom,
|
||||
channels: updatedChannels };
|
||||
|
||||
return {
|
||||
...state,
|
||||
@@ -324,7 +349,8 @@ export const roomsReducer = createReducer(
|
||||
|
||||
const existing = state.currentRoom.channels || defaultChannels();
|
||||
const updatedChannels = existing.filter(channel => channel.id !== channelId);
|
||||
const updatedRoom = { ...state.currentRoom, channels: updatedChannels };
|
||||
const updatedRoom = { ...state.currentRoom,
|
||||
channels: updatedChannels };
|
||||
|
||||
return {
|
||||
...state,
|
||||
@@ -339,8 +365,10 @@ export const roomsReducer = createReducer(
|
||||
return state;
|
||||
|
||||
const existing = state.currentRoom.channels || defaultChannels();
|
||||
const updatedChannels = existing.map(channel => channel.id === channelId ? { ...channel, name } : channel);
|
||||
const updatedRoom = { ...state.currentRoom, channels: updatedChannels };
|
||||
const updatedChannels = existing.map(channel => channel.id === channelId ? { ...channel,
|
||||
name } : channel);
|
||||
const updatedRoom = { ...state.currentRoom,
|
||||
channels: updatedChannels };
|
||||
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
/**
|
||||
* Users store actions using `createActionGroup`.
|
||||
*/
|
||||
import { createActionGroup, emptyProps, props } from '@ngrx/store';
|
||||
import { User, BanEntry, VoiceState, ScreenShareState } from '../../core/models';
|
||||
import {
|
||||
createActionGroup,
|
||||
emptyProps,
|
||||
props
|
||||
} from '@ngrx/store';
|
||||
import {
|
||||
User,
|
||||
BanEntry,
|
||||
VoiceState,
|
||||
ScreenShareState
|
||||
} from '../../core/models';
|
||||
|
||||
export const UsersActions = createActionGroup({
|
||||
source: 'Users',
|
||||
|
||||
@@ -3,13 +3,32 @@
|
||||
*/
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import {
|
||||
Actions,
|
||||
createEffect,
|
||||
ofType
|
||||
} from '@ngrx/effects';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { of, from, EMPTY } from 'rxjs';
|
||||
import { map, mergeMap, catchError, withLatestFrom, tap, switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
of,
|
||||
from,
|
||||
EMPTY
|
||||
} from 'rxjs';
|
||||
import {
|
||||
map,
|
||||
mergeMap,
|
||||
catchError,
|
||||
withLatestFrom,
|
||||
tap,
|
||||
switchMap
|
||||
} from 'rxjs/operators';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { UsersActions } from './users.actions';
|
||||
import { selectCurrentUser, selectCurrentUserId, selectHostId } from './users.selectors';
|
||||
import {
|
||||
selectCurrentUser,
|
||||
selectCurrentUserId,
|
||||
selectHostId
|
||||
} from './users.selectors';
|
||||
import { selectCurrentRoom } from '../rooms/rooms.selectors';
|
||||
import { DatabaseService } from '../../core/services/database.service';
|
||||
import { WebRTCService } from '../../core/services/webrtc.service';
|
||||
@@ -67,7 +86,11 @@ export class UsersEffects {
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectCurrentRoom)
|
||||
),
|
||||
mergeMap(([{ userId }, currentUser, currentRoom]) => {
|
||||
mergeMap(([
|
||||
{ userId },
|
||||
currentUser,
|
||||
currentRoom
|
||||
]) => {
|
||||
if (!currentUser || !currentRoom)
|
||||
return EMPTY;
|
||||
|
||||
@@ -99,7 +122,11 @@ export class UsersEffects {
|
||||
this.store.select(selectCurrentUser),
|
||||
this.store.select(selectCurrentRoom)
|
||||
),
|
||||
mergeMap(([{ userId, reason, expiresAt }, currentUser, currentRoom]) => {
|
||||
mergeMap(([
|
||||
{ userId, reason, expiresAt },
|
||||
currentUser,
|
||||
currentRoom
|
||||
]) => {
|
||||
if (!currentUser || !currentRoom)
|
||||
return EMPTY;
|
||||
|
||||
@@ -127,7 +154,8 @@ export class UsersEffects {
|
||||
reason
|
||||
});
|
||||
|
||||
return of(UsersActions.banUserSuccess({ userId, ban }));
|
||||
return of(UsersActions.banUserSuccess({ userId,
|
||||
ban }));
|
||||
})
|
||||
)
|
||||
);
|
||||
@@ -171,7 +199,11 @@ export class UsersEffects {
|
||||
this.store.select(selectHostId),
|
||||
this.store.select(selectCurrentUserId)
|
||||
),
|
||||
mergeMap(([{ userId }, hostId, currentUserId]) =>
|
||||
mergeMap(([
|
||||
{ userId },
|
||||
hostId,
|
||||
currentUserId
|
||||
]) =>
|
||||
userId === hostId && currentUserId
|
||||
? of(UsersActions.updateHost({ userId: currentUserId }))
|
||||
: EMPTY
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { createReducer, on } from '@ngrx/store';
|
||||
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
|
||||
import {
|
||||
EntityState,
|
||||
EntityAdapter,
|
||||
createEntityAdapter
|
||||
} from '@ngrx/entity';
|
||||
import { User, BanEntry } from '../../core/models';
|
||||
import { UsersActions } from './users.actions';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user