mirror of
https://github.com/layer-systems/website.git
synced 2026-09-12 21:53:40 +02:00
82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
import { useQuery } from '@tanstack/react-query';
|
|
import { useNostr } from '@nostrify/react';
|
|
import type { NostrEvent } from '@nostrify/nostrify';
|
|
import { useCurrentUser } from './useCurrentUser';
|
|
import { useLocalStorage } from './useLocalStorage';
|
|
|
|
/** Nostr event kinds that commonly represent activity directed at a person. */
|
|
const NOTIFICATION_KINDS = [1, 3, 6, 7, 16, 9735] as const;
|
|
|
|
export type NotificationKind = 'mention' | 'reply' | 'reaction' | 'repost' | 'follow' | 'zap';
|
|
|
|
export interface Notification {
|
|
event: NostrEvent;
|
|
kind: NotificationKind;
|
|
}
|
|
|
|
function notificationKind(event: NostrEvent): NotificationKind {
|
|
switch (event.kind) {
|
|
case 3:
|
|
return 'follow';
|
|
case 7:
|
|
return 'reaction';
|
|
case 6:
|
|
case 16:
|
|
return 'repost';
|
|
case 9735:
|
|
return 'zap';
|
|
case 1:
|
|
return event.tags.some(([name]) => name === 'e') ? 'reply' : 'mention';
|
|
default:
|
|
return 'mention';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches activity that explicitly tags the current user. Relays index the
|
|
* single-letter `p` tag, so this stays a narrow query instead of downloading a
|
|
* general timeline and filtering it in the browser.
|
|
*/
|
|
export function useNotifications() {
|
|
const { nostr } = useNostr();
|
|
const { user } = useCurrentUser();
|
|
|
|
return useQuery<Notification[]>({
|
|
queryKey: ['nostr', 'notifications', user?.pubkey ?? ''],
|
|
enabled: Boolean(user),
|
|
queryFn: async ({ signal }) => {
|
|
if (!user) return [];
|
|
const events = await nostr.query(
|
|
[{ kinds: [...NOTIFICATION_KINDS], '#p': [user.pubkey], limit: 100 }],
|
|
{
|
|
signal: AbortSignal.any([
|
|
signal,
|
|
AbortSignal.timeout(6000),
|
|
]),
|
|
},
|
|
);
|
|
|
|
const seen = new Set<string>();
|
|
return events
|
|
.filter((event) => event.pubkey !== user.pubkey && !seen.has(event.id) && seen.add(event.id))
|
|
.sort((left, right) => right.created_at - left.created_at)
|
|
.map((event) => ({ event, kind: notificationKind(event) }));
|
|
},
|
|
staleTime: 30_000,
|
|
});
|
|
}
|
|
|
|
/** Persists the most recent timestamp the account has explicitly acknowledged. */
|
|
export function useNotificationReadState() {
|
|
const { user } = useCurrentUser();
|
|
const [lastReadAt, setLastReadAt] = useLocalStorage<number>(
|
|
`nostr:notifications:last-read:${user?.pubkey ?? 'anonymous'}`,
|
|
0,
|
|
);
|
|
|
|
return {
|
|
lastReadAt,
|
|
markAllRead: () => setLastReadAt(Math.floor(Date.now() / 1000)),
|
|
};
|
|
}
|