UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

229 lines (224 loc) • 7.55 kB
"use strict"; import { create } from 'zustand'; const initialState = { accounts: {}, accountOrder: [], accountsArray: [], loading: false, loadingSessionIds: new Set(), error: null }; // Helper: Build accounts array from accounts map and order const buildAccountsArray = (accounts, order) => { const result = []; for (const id of order) { const account = accounts[id]; if (account) result.push(account); } return result; }; // Helper: Create QuickAccount from user data const createQuickAccount = (sessionId, userData, existingAccount, oxyServices) => { const displayName = userData.name?.full || userData.name?.first || userData.username || 'Account'; const userId = userData.id || userData._id?.toString(); // Preserve existing avatarUrl if avatar hasn't changed (prevents image reload) let avatarUrl; if (existingAccount && existingAccount.avatar === userData.avatar && existingAccount.avatarUrl) { avatarUrl = existingAccount.avatarUrl; // Reuse existing URL } else if (userData.avatar && oxyServices) { avatarUrl = oxyServices.getFileDownloadUrl(userData.avatar, 'thumb'); } return { sessionId, userId, username: userData.username || '', displayName, avatar: userData.avatar, avatarUrl }; }; export const useAccountStore = create((set, get) => ({ ...initialState, setAccounts: accounts => set(state => { const accountMap = {}; const order = []; const seenSessionIds = new Set(); for (const account of accounts) { if (seenSessionIds.has(account.sessionId)) continue; seenSessionIds.add(account.sessionId); accountMap[account.sessionId] = account; order.push(account.sessionId); } const accountsArray = buildAccountsArray(accountMap, order); const sameOrder = order.length === state.accountOrder.length && order.every((id, i) => id === state.accountOrder[i]); const sameAccounts = sameOrder && order.every(id => { const existing = state.accounts[id]; const newAccount = accountMap[id]; return existing && existing.sessionId === newAccount.sessionId && existing.userId === newAccount.userId && existing.avatar === newAccount.avatar && existing.avatarUrl === newAccount.avatarUrl; }); if (sameAccounts) return {}; return { accounts: accountMap, accountOrder: order, accountsArray }; }), addAccount: account => set(state => { // Check if account with same sessionId exists if (state.accounts[account.sessionId]) { // Update existing const existing = state.accounts[account.sessionId]; if (existing.avatar === account.avatar && existing.avatarUrl === account.avatarUrl) { return {}; // No change } const newAccounts = { ...state.accounts, [account.sessionId]: account }; return { accounts: newAccounts, accountsArray: buildAccountsArray(newAccounts, state.accountOrder) }; } const newAccounts = { ...state.accounts, [account.sessionId]: account }; const newOrder = [account.sessionId, ...state.accountOrder]; return { accounts: newAccounts, accountOrder: newOrder, accountsArray: buildAccountsArray(newAccounts, newOrder) }; }), updateAccount: (sessionId, updates) => set(state => { const existing = state.accounts[sessionId]; if (!existing) return {}; const updated = { ...existing, ...updates }; if (existing.avatar === updated.avatar && existing.avatarUrl === updated.avatarUrl) { return {}; // No change } const newAccounts = { ...state.accounts, [sessionId]: updated }; return { accounts: newAccounts, accountsArray: buildAccountsArray(newAccounts, state.accountOrder) }; }), removeAccount: sessionId => set(state => { if (!state.accounts[sessionId]) return {}; const { [sessionId]: _removed, ...rest } = state.accounts; const newOrder = state.accountOrder.filter(id => id !== sessionId); return { accounts: rest, accountOrder: newOrder, accountsArray: buildAccountsArray(rest, newOrder) }; }), moveAccountToTop: sessionId => set(state => { if (!state.accounts[sessionId]) return {}; const filtered = state.accountOrder.filter(id => id !== sessionId); const newOrder = [sessionId, ...filtered]; return { accountOrder: newOrder, accountsArray: buildAccountsArray(state.accounts, newOrder) }; }), setLoading: loading => set({ loading }), setLoadingSession: (sessionId, loading) => set(state => { const newSet = new Set(state.loadingSessionIds); if (loading) { newSet.add(sessionId); } else { newSet.delete(sessionId); } return { loadingSessionIds: newSet }; }), setError: error => set({ error }), loadAccounts: async (sessionIds, oxyServices, existingAccounts = [], preserveOrder = true) => { const state = get(); const uniqueSessionIds = Array.from(new Set(sessionIds)); if (uniqueSessionIds.length === 0) { get().setAccounts([]); return; } const existingMap = new Map(existingAccounts.map(a => [a.sessionId, a])); for (const account of Object.values(state.accounts)) { existingMap.set(account.sessionId, account); } const missingSessionIds = uniqueSessionIds.filter(id => !existingMap.has(id)); if (missingSessionIds.length === 0) { const ordered = uniqueSessionIds.map(id => existingMap.get(id)).filter(acc => acc !== undefined); get().setAccounts(ordered); return; } if (state.loading) { return; } set({ loading: true, error: null }); try { const batchResults = await oxyServices.getUsersBySessions(missingSessionIds); const accountMap = new Map(); for (const { sessionId, user: userData } of batchResults) { if (userData && !accountMap.has(sessionId)) { const existing = existingMap.get(sessionId); accountMap.set(sessionId, createQuickAccount(sessionId, userData, existing, oxyServices)); } } for (const [sessionId, account] of accountMap) { existingMap.set(sessionId, account); } const orderToUse = preserveOrder ? uniqueSessionIds : [...uniqueSessionIds, ...state.accountOrder]; const seen = new Set(); const ordered = []; for (const sessionId of orderToUse) { if (seen.has(sessionId)) continue; seen.add(sessionId); const account = existingMap.get(sessionId); if (account) ordered.push(account); } get().setAccounts(ordered); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to load accounts'; if (__DEV__) { console.error('AccountStore: Failed to load accounts:', error); } set({ error: errorMessage }); } finally { set({ loading: false }); } }, reset: () => set(initialState) })); // Selectors for performance - return cached array to prevent infinite loops export const useAccounts = () => { return useAccountStore(state => state.accountsArray); }; export const useAccountLoading = () => useAccountStore(s => s.loading); export const useAccountError = () => useAccountStore(s => s.error); export const useAccountLoadingSession = sessionId => useAccountStore(s => s.loadingSessionIds.has(sessionId)); //# sourceMappingURL=accountStore.js.map