Move toju-app into own its folder

This commit is contained in:
2026-03-29 23:30:37 +02:00
parent 0467a7b612
commit 8162e0444a
287 changed files with 42 additions and 34 deletions

View File

@@ -0,0 +1,250 @@
import { createReducer, on } from '@ngrx/store';
import {
EntityState,
EntityAdapter,
createEntityAdapter
} from '@ngrx/entity';
import { User, BanEntry } from '../../shared-kernel';
import { UsersActions } from './users.actions';
export interface UsersState extends EntityState<User> {
currentUserId: string | null;
hostId: string | null;
loading: boolean;
error: string | null;
bans: BanEntry[];
}
export const usersAdapter: EntityAdapter<User> = createEntityAdapter<User>({
selectId: (user) => user.id,
sortComparer: (userA, userB) => userA.username.localeCompare(userB.username)
});
export const initialState: UsersState = usersAdapter.getInitialState({
currentUserId: null,
hostId: null,
loading: false,
error: null,
bans: []
});
export const usersReducer = createReducer(
initialState,
on(UsersActions.loadCurrentUser, (state) => ({
...state,
loading: true,
error: null
})),
on(UsersActions.loadCurrentUserSuccess, (state, { user }) =>
usersAdapter.upsertOne(user, {
...state,
currentUserId: user.id,
loading: false
})
),
on(UsersActions.loadCurrentUserFailure, (state, { error }) => ({
...state,
loading: false,
error
})),
on(UsersActions.setCurrentUser, (state, { user }) =>
usersAdapter.upsertOne(user, {
...state,
currentUserId: user.id
})
),
on(UsersActions.updateCurrentUser, (state, { updates }) => {
if (!state.currentUserId)
return state;
return usersAdapter.updateOne(
{
id: state.currentUserId,
changes: updates
},
state
);
}),
on(UsersActions.loadRoomUsers, (state) => ({
...state,
loading: true,
error: null
})),
on(UsersActions.loadRoomUsersSuccess, (state, { users }) =>
usersAdapter.upsertMany(users, {
...state,
loading: false
})
),
on(UsersActions.loadRoomUsersFailure, (state, { error }) => ({
...state,
loading: false,
error
})),
on(UsersActions.userJoined, (state, { user }) =>
usersAdapter.upsertOne(user, state)
),
on(UsersActions.userLeft, (state, { userId }) =>
usersAdapter.removeOne(userId, state)
),
on(UsersActions.updateUser, (state, { userId, updates }) =>
usersAdapter.updateOne(
{
id: userId,
changes: updates
},
state
)
),
on(UsersActions.updateUserRole, (state, { userId, role }) =>
usersAdapter.updateOne(
{
id: userId,
changes: { role }
},
state
)
),
on(UsersActions.kickUserSuccess, (state, { userId }) =>
usersAdapter.removeOne(userId, state)
),
on(UsersActions.banUserSuccess, (state, { userId, ban }) => {
const newState = usersAdapter.removeOne(userId, state);
return {
...newState,
bans: [...state.bans, ban]
};
}),
on(UsersActions.unbanUserSuccess, (state, { oderId }) => ({
...state,
bans: state.bans.filter((ban) => ban.oderId !== oderId)
})),
on(UsersActions.loadBansSuccess, (state, { bans }) => ({
...state,
bans
})),
on(UsersActions.adminMuteUser, (state, { userId }) =>
usersAdapter.updateOne(
{
id: userId,
changes: {
voiceState: {
...state.entities[userId]?.voiceState,
isConnected: state.entities[userId]?.voiceState?.isConnected ?? false,
isMuted: true,
isDeafened: state.entities[userId]?.voiceState?.isDeafened ?? false,
isSpeaking: false,
isMutedByAdmin: true
}
}
},
state
)
),
on(UsersActions.adminUnmuteUser, (state, { userId }) =>
usersAdapter.updateOne(
{
id: userId,
changes: {
voiceState: {
...state.entities[userId]?.voiceState,
isConnected: state.entities[userId]?.voiceState?.isConnected ?? false,
isMuted: state.entities[userId]?.voiceState?.isMuted ?? false,
isDeafened: state.entities[userId]?.voiceState?.isDeafened ?? false,
isSpeaking: state.entities[userId]?.voiceState?.isSpeaking ?? false,
isMutedByAdmin: false
}
}
},
state
)
),
on(UsersActions.updateVoiceState, (state, { userId, voiceState }) => {
const prev = state.entities[userId]?.voiceState || {
isConnected: false,
isMuted: false,
isDeafened: false,
isSpeaking: false
};
return usersAdapter.updateOne(
{
id: userId,
changes: {
voiceState: {
isConnected: voiceState.isConnected ?? prev.isConnected,
isMuted: voiceState.isMuted ?? prev.isMuted,
isDeafened: voiceState.isDeafened ?? prev.isDeafened,
isSpeaking: voiceState.isSpeaking ?? prev.isSpeaking,
isMutedByAdmin: voiceState.isMutedByAdmin ?? prev.isMutedByAdmin,
volume: voiceState.volume ?? prev.volume,
// Use explicit undefined check - if undefined is passed, clear the value
roomId: voiceState.roomId !== undefined ? voiceState.roomId : prev.roomId,
serverId: voiceState.serverId !== undefined ? voiceState.serverId : prev.serverId
}
}
},
state
);
}),
on(UsersActions.updateScreenShareState, (state, { userId, screenShareState }) => {
const prev = state.entities[userId]?.screenShareState || {
isSharing: false
};
return usersAdapter.updateOne(
{
id: userId,
changes: {
screenShareState: {
isSharing: screenShareState.isSharing ?? prev.isSharing,
streamId: screenShareState.streamId ?? prev.streamId,
sourceId: screenShareState.sourceId ?? prev.sourceId,
sourceName: screenShareState.sourceName ?? prev.sourceName
}
}
},
state
);
}),
on(UsersActions.syncUsers, (state, { users }) =>
usersAdapter.upsertMany(users, state)
),
on(UsersActions.clearUsers, (state) => {
const idsToRemove = Object.keys(state.entities).filter((id) => id !== state.currentUserId);
return usersAdapter.removeMany(idsToRemove, {
...state,
hostId: null
});
}),
on(UsersActions.updateHost, (state, { userId }) => {
let newState = state;
if (state.hostId && state.hostId !== userId) {
newState = usersAdapter.updateOne(
{
id: state.hostId,
changes: { role: 'member' }
},
state
);
}
return usersAdapter.updateOne(
{
id: userId,
changes: { role: 'host' }
},
{
...newState,
hostId: userId
}
);
})
);