feat: Implement core Discord features including members list, direct messages, user presence, authentication, and chat UI.
Some checks failed
Build and Release / build-and-release (push) Has been cancelled
Some checks failed
Build and Release / build-and-release (push) Has been cancelled
This commit is contained in:
47
Frontend/Electron/src/contexts/PresenceContext.jsx
Normal file
47
Frontend/Electron/src/contexts/PresenceContext.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { createContext, useContext, useMemo } from 'react';
|
||||
import usePresence from '@convex-dev/presence/react';
|
||||
import { api } from '../../../../convex/_generated/api';
|
||||
|
||||
const PresenceContext = createContext({
|
||||
onlineUsers: new Set(),
|
||||
resolveStatus: (storedStatus, userId) => storedStatus || 'offline',
|
||||
});
|
||||
|
||||
export const useOnlineUsers = () => useContext(PresenceContext);
|
||||
|
||||
/**
|
||||
* Status resolution logic:
|
||||
* - If user is NOT connected (no heartbeat) → "offline"
|
||||
* - If user IS connected and chose "invisible" → "offline"
|
||||
* - If user IS connected → show their chosen status (online/idle/dnd)
|
||||
*/
|
||||
function resolveStatusFn(onlineUsers, storedStatus, userId) {
|
||||
if (!onlineUsers.has(userId)) return 'offline';
|
||||
if (storedStatus === 'invisible') return 'offline';
|
||||
return storedStatus || 'online';
|
||||
}
|
||||
|
||||
export const PresenceProvider = ({ userId, children }) => {
|
||||
const presenceState = usePresence(api.presence, 'global', userId);
|
||||
|
||||
const onlineUsers = useMemo(() => {
|
||||
const set = new Set();
|
||||
if (presenceState) {
|
||||
for (const p of presenceState) {
|
||||
if (p.online) set.add(p.userId);
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}, [presenceState]);
|
||||
|
||||
const value = useMemo(() => ({
|
||||
onlineUsers,
|
||||
resolveStatus: (storedStatus, uid) => resolveStatusFn(onlineUsers, storedStatus, uid),
|
||||
}), [onlineUsers]);
|
||||
|
||||
return (
|
||||
<PresenceContext.Provider value={value}>
|
||||
{children}
|
||||
</PresenceContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -23,9 +23,21 @@ export function ThemeProvider({ children }) {
|
||||
return localStorage.getItem(STORAGE_KEY) || THEMES.DARK;
|
||||
});
|
||||
|
||||
// On mount, check settings.json as fallback when localStorage is empty
|
||||
useEffect(() => {
|
||||
if (!localStorage.getItem(STORAGE_KEY) && window.appSettings) {
|
||||
window.appSettings.get('theme').then((saved) => {
|
||||
if (saved && Object.values(THEMES).includes(saved)) {
|
||||
setTheme(saved);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.className = theme;
|
||||
localStorage.setItem(STORAGE_KEY, theme);
|
||||
window.appSettings?.set('theme', theme);
|
||||
}, [theme]);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user