Online-Status (Phase 1+):
- UserAvatar mit 4 Size-Variants (sm/md/lg/xl) + integrierter Online-Dot
- OnlinePresenceProvider: Supabase-Channel + Following-Filter
- ChatHeaderStatus: "Online" neutral / "vor X min" offline
- useLastSeen + Heartbeat (60s interval + AppState-background ping)
- Privatsphäre-Toggle in profile/index
Sheets:
- FormSheet Android-keyboard-fix (Dimensions.get('screen'), kein
useWindowDimensions-Kollaps), useKeyboardHandler statt manual
Keyboard.addListener, state-reset on re-open
- PostCommentsSheet same Pattern + close-after-submit + drag bis under
app-header
- ConnectMailSheet form-view refactor: scrollable, AES-Banner als
footnote, field-order email→pw→label, fixed 0.85 über alle Steps
Chat:
- DmChatBackground iOS klecks fix (G transform statt nested Svg)
- ChatInput Lyra-1:1 (keyboardWillShow, surfaceElevated bubble,
arrow-up send, attachment links)
- dm/room/chat headers + conversation-list nutzen UserAvatar
- Foreign-Profile "Nachricht"-Button öffnet richtige DM
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
import { createContext, useContext, useMemo } from 'react';
|
|
import { OnlinePresenceContext, useOnlinePresenceNode } from '../hooks/useOnlineUsers';
|
|
import { useAuthStore } from '../stores/auth';
|
|
import { useLastSeenHeartbeat } from '../hooks/useLastSeenHeartbeat';
|
|
import { useFollowing } from '../hooks/useFollowing';
|
|
|
|
export type PresenceContextExtended = {
|
|
onlineUserIds: Set<string>;
|
|
isOnline: (userId: string) => boolean;
|
|
};
|
|
|
|
export const PresenceVisibilityContext = createContext<{
|
|
presenceVisible: boolean;
|
|
setPresenceVisible: (v: boolean) => void;
|
|
}>({
|
|
presenceVisible: true,
|
|
setPresenceVisible: () => {},
|
|
});
|
|
|
|
export function usePresenceVisibility() {
|
|
return useContext(PresenceVisibilityContext);
|
|
}
|
|
|
|
type Props = {
|
|
children: React.ReactNode;
|
|
};
|
|
|
|
export function OnlinePresenceProvider({ children }: Props) {
|
|
const user = useAuthStore((s) => s.user);
|
|
const ids = useOnlinePresenceNode(user?.id ?? null);
|
|
const following = useFollowing();
|
|
|
|
useLastSeenHeartbeat(!!user);
|
|
|
|
// Debug-Log nur bei tatsächlichen state-changes (size geändert) — sonst
|
|
// hängt's an jedem Re-Render und spammed Metro.
|
|
useMemo(() => {
|
|
console.log(
|
|
'[presence] state — self=%s, onlineGlobal=%d, following=%d',
|
|
user?.id ?? 'none',
|
|
ids.size,
|
|
following.size,
|
|
);
|
|
}, [ids.size, following.size, user?.id]);
|
|
|
|
const ctx = useMemo(
|
|
() => ({
|
|
onlineUserIds: ids,
|
|
isOnline: (userId: string) => {
|
|
if (!user?.id || userId === user.id) return false;
|
|
return ids.has(userId) && following.has(userId);
|
|
},
|
|
}),
|
|
[ids, following, user?.id],
|
|
);
|
|
|
|
return (
|
|
<OnlinePresenceContext.Provider value={ctx}>
|
|
{children}
|
|
</OnlinePresenceContext.Provider>
|
|
);
|
|
}
|