Cockatrice/webclient/src/store/server/server.selectors.ts
2026-04-15 21:48:03 -05:00

97 lines
3.6 KiB
TypeScript

import { createSelector } from '@reduxjs/toolkit';
import { App, Data } from '@app/types';
import { SortUtil } from '../common';
import { ServerState } from './server.interfaces';
interface State {
server: ServerState
}
const EMPTY_USERS: Data.ServerInfo_User[] = [];
const EMPTY_REPLAYS: Data.ServerInfo_ReplayMatch[] = [];
export const Selectors = {
getInitialized: ({ server }: State) => server.initialized,
getMessage: ({ server }: State) => server.info.message,
getName: ({ server }: State) => server.info.name,
getVersion: ({ server }: State) => server.info.version,
getDescription: ({ server }: State) => server.status.description,
getState: ({ server }: State) => server.status.state,
getConnectionAttemptMade: ({ server }: State) => server.status.connectionAttemptMade,
getUser: ({ server }: State) => server.user,
/** True when the server status has reached LOGGED_IN. */
getIsConnected: createSelector(
[({ server }: State) => server.status.state],
(state): boolean => state === App.StatusEnum.LOGGED_IN
),
/** True when the currently logged-in user has the IsModerator level flag. */
getIsUserModerator: createSelector(
[({ server }: State) => server.user],
(user): boolean => {
if (!user) {
return false;
}
const mask = Data.ServerInfo_User_UserLevelFlag.IsModerator;
return (user.userLevel & mask) === mask;
}
),
getLogs: ({ server }: State) => server.logs,
getBackendDecks: ({ server }: State) => server.backendDecks,
getRegistrationError: ({ server }: State) => server.registrationError,
getSortUsersBy: ({ server }: State) => server.sortUsersBy,
/** Raw keyed maps — use the sorted-view selectors below for display. */
getUsers: ({ server }: State) => server.users,
getBuddyList: ({ server }: State) => server.buddyList,
getIgnoreList: ({ server }: State) => server.ignoreList,
getReplays: ({ server }: State) => server.replays,
/**
* Sorted array views of the keyed maps. Memoized via `createSelector` so
* the array reference is stable until the underlying map or sort config
* actually changes — consumers using these in `useAppSelector` won't
* re-render unnecessarily.
*/
getSortedUsers: createSelector(
[(state: State) => state.server.users, (state: State) => state.server.sortUsersBy],
(users, sortBy): Data.ServerInfo_User[] => {
if (!users || Object.keys(users).length === 0) {
return EMPTY_USERS;
}
return SortUtil.sortedUsersByField(Object.values(users), sortBy);
}
),
getSortedBuddyList: createSelector(
[(state: State) => state.server.buddyList, (state: State) => state.server.sortUsersBy],
(buddyList, sortBy): Data.ServerInfo_User[] => {
if (!buddyList || Object.keys(buddyList).length === 0) {
return EMPTY_USERS;
}
return SortUtil.sortedUsersByField(Object.values(buddyList), sortBy);
}
),
getSortedIgnoreList: createSelector(
[(state: State) => state.server.ignoreList, (state: State) => state.server.sortUsersBy],
(ignoreList, sortBy): Data.ServerInfo_User[] => {
if (!ignoreList || Object.keys(ignoreList).length === 0) {
return EMPTY_USERS;
}
return SortUtil.sortedUsersByField(Object.values(ignoreList), sortBy);
}
),
/** Replay list as an array, ordered by gameId ascending for stable display. */
getReplaysList: createSelector(
[(state: State) => state.server.replays],
(replays): Data.ServerInfo_ReplayMatch[] => {
if (!replays || Object.keys(replays).length === 0) {
return EMPTY_REPLAYS;
}
return Object.values(replays).sort((a, b) => a.gameId - b.gameId);
}
),
}