Vendored
+3
@@ -12,6 +12,9 @@ declare global {
|
|||||||
fetchMetadata: (url: string) => Promise<LinkMetadata | null>;
|
fetchMetadata: (url: string) => Promise<LinkMetadata | null>;
|
||||||
openExternal: (url: string) => Promise<void>;
|
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 { useViewModeStore } from "@/stores/view-mode-store";
|
||||||
import { useStreamParticles } from "@/hooks/use-stream-particles";
|
import { useStreamParticles } from "@/hooks/use-stream-particles";
|
||||||
import { useStreamKeyboardNav } from "@/hooks/use-stream-keyboard-nav";
|
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).
|
* Route-level component for /:networkId (index).
|
||||||
@@ -24,6 +26,8 @@ export default function NetworkRoot() {
|
|||||||
const setViewMode = useViewModeStore((s) => s.setViewMode);
|
const setViewMode = useViewModeStore((s) => s.setViewMode);
|
||||||
|
|
||||||
const { streams } = useStreamParticles(path);
|
const { streams } = useStreamParticles(path);
|
||||||
|
const userId = useAuthStore((s) => s.user?.id);
|
||||||
|
useDockBadge(streams, userId);
|
||||||
const [composeActive, setComposeActive] = useState(false);
|
const [composeActive, setComposeActive] = useState(false);
|
||||||
|
|
||||||
const { selectedIndex } = useStreamKeyboardNav({
|
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) => {
|
ipcMain.handle('link:fetch-metadata', async (_event, url: string) => {
|
||||||
if (typeof url !== 'string') return null;
|
if (typeof url !== 'string') return null;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -13,3 +13,7 @@ contextBridge.exposeInMainWorld('electronLink', {
|
|||||||
fetchMetadata: (url: string) => ipcRenderer.invoke('link:fetch-metadata', url),
|
fetchMetadata: (url: string) => ipcRenderer.invoke('link:fetch-metadata', url),
|
||||||
openExternal: (url: string) => ipcRenderer.invoke('link:open-external', 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