Vendored
+3
@@ -12,6 +12,9 @@ declare global {
|
||||
fetchMetadata: (url: string) => Promise<LinkMetadata | null>;
|
||||
openExternal: (url: string) => Promise<void>;
|
||||
};
|
||||
electronApp: {
|
||||
setDockBadge: (count: number) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||
import { useViewModeStore } from "@/stores/view-mode-store";
|
||||
import { useStreamParticles } from "@/hooks/use-stream-particles";
|
||||
import { useStreamKeyboardNav } from "@/hooks/use-stream-keyboard-nav";
|
||||
import { useDockBadge } from "@/hooks/use-dock-badge";
|
||||
import { useAuthStore } from "@/stores/auth-store";
|
||||
|
||||
/**
|
||||
* Route-level component for /:networkId (index).
|
||||
@@ -24,6 +26,8 @@ export default function NetworkRoot() {
|
||||
const setViewMode = useViewModeStore((s) => s.setViewMode);
|
||||
|
||||
const { streams } = useStreamParticles(path);
|
||||
const userId = useAuthStore((s) => s.user?.id);
|
||||
useDockBadge(streams, userId);
|
||||
const [composeActive, setComposeActive] = useState(false);
|
||||
|
||||
const { selectedIndex } = useStreamKeyboardNav({
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import type { Particle, StreamProperties } from "@/api/types";
|
||||
|
||||
type StreamParticle = Particle & { type: "stream"; properties: StreamProperties };
|
||||
|
||||
/**
|
||||
* Syncs the macOS dock badge with the count of unseen streams.
|
||||
* Both last_child_created_at and playback_markers store the child particle's
|
||||
* created_at, so they're directly comparable.
|
||||
*/
|
||||
export function useDockBadge(streams: StreamParticle[], userId: string | undefined) {
|
||||
const unseenCount = useMemo(() => {
|
||||
if (!userId) return 0;
|
||||
return streams.filter((s) => {
|
||||
const lastActivity = s.last_child_created_at?.getTime();
|
||||
if (!lastActivity) return false;
|
||||
const marker = s.playback_markers?.[userId]?.getTime();
|
||||
if (marker === undefined) return false;
|
||||
return lastActivity > marker;
|
||||
}).length;
|
||||
}, [streams, userId]);
|
||||
|
||||
useEffect(() => {
|
||||
window.electronApp.setDockBadge(unseenCount);
|
||||
return () => window.electronApp.setDockBadge(0);
|
||||
}, [unseenCount]);
|
||||
}
|
||||
@@ -163,6 +163,14 @@ async function fetchLinkMetadata(url: string): Promise<LinkMetadata | null> {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Dock badge ---
|
||||
|
||||
ipcMain.on('app:set-dock-badge', (_event, count: number) => {
|
||||
if (process.platform === 'darwin') {
|
||||
app.dock?.setBadge(count > 0 ? String(count) : '');
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('link:fetch-metadata', async (_event, url: string) => {
|
||||
if (typeof url !== 'string') return null;
|
||||
try {
|
||||
|
||||
@@ -13,3 +13,7 @@ contextBridge.exposeInMainWorld('electronLink', {
|
||||
fetchMetadata: (url: string) => ipcRenderer.invoke('link:fetch-metadata', url),
|
||||
openExternal: (url: string) => ipcRenderer.invoke('link:open-external', url),
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('electronApp', {
|
||||
setDockBadge: (count: number) => ipcRenderer.send('app:set-dock-badge', count),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user