Files
eranos/src/hooks/useNativeNotifications.ts
T
Alex Gleason 1597e7540b Disable user relays by default
User relays are no longer used until the user explicitly opts in via
Settings > Network. Adds a useUserRelays toggle alongside the existing
useAppRelays toggle in RelayListManager, defaulting to false. Fresh
installs and new accounts will only query app-default relays until the
user enables their personal NIP-65 list.

The user's relay list (kind 10002) is still synced from Nostr and
managed in the UI when logged in — the toggle only controls whether it
is included in the effective relay set used by NostrProvider's pool
and useNativeNotifications. The setting is persisted to AppConfig and
synced cross-device via NIP-78 encrypted settings.

getEffectiveRelays now takes both flags and short-circuits accordingly,
producing an empty list when both are off (instead of the previous
behavior of always returning user relays).
2026-05-03 09:00:43 -05:00

96 lines
3.5 KiB
TypeScript

import { useEffect, useMemo } from 'react';
import { Capacitor, registerPlugin } from '@capacitor/core';
import { LocalNotifications } from '@capacitor/local-notifications';
import { useCurrentUser } from './useCurrentUser';
import { useAppContext } from './useAppContext';
import { useEncryptedSettings } from './useEncryptedSettings';
import { useFollowList } from './useFollowActions';
import { getEffectiveRelays } from '@/lib/appRelays';
import { getEnabledNotificationKinds } from '@/lib/notificationKinds';
/** Interface for the native DittoNotification Capacitor plugin. */
interface DittoNotificationPlugin {
configure(options: { userPubkey?: string; relayUrls?: string[]; enabledKinds?: number[]; authors?: string[]; notificationStyle?: string }): Promise<void>;
}
const DittoNotification = registerPlugin<DittoNotificationPlugin>('DittoNotification');
/**
* Manages the native Android notification service via Capacitor.
*
* Passes user pubkey + relay URLs + enabled notification kinds + optional
* authors filter to the DittoNotification plugin so it can poll for events
* in the background. Respects the NIP-78 notificationsEnabled setting
* (defaults to on), per-type notification preferences, and the "only from
* people I follow" setting.
*
* Web Push (nostr-push) is handled separately by usePushNotifications +
* NotificationSettings — this hook is Capacitor-only.
*/
export function useNativeNotifications(): void {
const { user } = useCurrentUser();
const { config } = useAppContext();
const { settings } = useEncryptedSettings();
const { data: followData } = useFollowList();
const prefs = settings?.notificationPreferences;
const notificationsEnabled = settings?.notificationsEnabled ?? true;
const notificationStyle = settings?.notificationStyle ?? 'push';
const enabledKinds = useMemo(
() => getEnabledNotificationKinds(prefs),
[prefs],
);
// Authors filter: when onlyFollowing is set, restrict to followed pubkeys
const followedPubkeys = useMemo(
() => followData?.pubkeys ?? [],
[followData?.pubkeys],
);
const onlyFollowing = prefs?.onlyFollowing === true;
const authorsFilter = onlyFollowing && followedPubkeys.length > 0
? followedPubkeys
: undefined;
// Request native notification permission on first mount.
useEffect(() => {
if (!Capacitor.isNativePlatform()) return;
(async () => {
try {
const { display } = await LocalNotifications.checkPermissions();
if (display === 'prompt' || display === 'prompt-with-rationale') {
await LocalNotifications.requestPermissions();
}
} catch {
// Permission check failed — ignore
}
})();
}, []);
// Configure / deconfigure the native polling service.
useEffect(() => {
if (!Capacitor.isNativePlatform()) return;
if (!user || !notificationsEnabled) {
DittoNotification.configure({});
return;
}
const effectiveRelays = getEffectiveRelays(config.relayMetadata, config.useAppRelays, config.useUserRelays);
const relayUrls = effectiveRelays.relays
.filter((r) => r.read)
.map((r) => r.url);
if (relayUrls.length === 0) return;
DittoNotification.configure({
userPubkey: user.pubkey,
relayUrls,
enabledKinds,
notificationStyle,
...(authorsFilter ? { authors: authorsFilter } : {}),
});
}, [user, config.relayMetadata, config.useAppRelays, config.useUserRelays, notificationsEnabled, notificationStyle, enabledKinds, authorsFilter]);
}