Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion mobile_app/hooks/useMessageNotifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,20 @@ export function useMessageNotifications(
if (appStateRef.current === 'active') {
onInApp(payload);
} else {
// OS notification path (lock screen / Notification Center) shows no
// identifying content — generic title + body keep sender and message
// out of any screen the user hasn't unlocked into the app for. The
// in-app banner above still surfaces sender when the app is active.
// Per-peer identifier collapses repeats; passive interruption stops
// re-presenting the banner on every follow-up.
Notifications.scheduleNotificationAsync({
content: { title: sender, body: 'new message', sound: true },
identifier: `anonmesh-msg-${srcHash}`,
content: {
title: 'anonmesh',
body: 'New message',
sound: true,
interruptionLevel: 'passive',
},
trigger: null,
Comment on lines 91 to 99
}).catch(() => {});
}
Expand Down
47 changes: 40 additions & 7 deletions mobile_app/hooks/usePeerCountNotification.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,63 @@
import { useEffect, useRef } from 'react';
import { Platform } from 'react-native';
import * as Notifications from 'expo-notifications';
import { useLxmfContext } from '@/context/LxmfContext';

const NOTIF_ID = 'anonmesh-peer-count';

export function usePeerCountNotification(enabled = true) {
const { peers } = useLxmfContext();
const prevOnlineRef = useRef(-1);
const prevTotalRef = useRef(-1);
const prevOnlineRef = useRef(-1);
const prevTotalRef = useRef(-1);
const prevHadPeersRef = useRef<boolean | null>(null);

useEffect(() => {
if (!enabled) {
Notifications.dismissNotificationAsync(NOTIF_ID).catch(() => {});
prevOnlineRef.current = -1;
prevTotalRef.current = -1;
prevOnlineRef.current = -1;
prevTotalRef.current = -1;
prevHadPeersRef.current = null;
return;
}

const online = peers.filter(p => p.online).length;
const total = peers.length;
const online = peers.filter(p => p.online).length;
const total = peers.length;
const hasPeers = total > 0;

// iOS: schedule only on 0↔peers transition. Notification Center has no
// silent persistent surface — every reschedule resurfaces the entry. One
// quiet "mesh active" pill is enough; the in-app peer indicator already
// shows live counts.
if (Platform.OS === 'ios') {
if (prevHadPeersRef.current === hasPeers) return;
prevHadPeersRef.current = hasPeers;

if (!hasPeers) {
Notifications.dismissNotificationAsync(NOTIF_ID).catch(() => {});
return;
}

Notifications.scheduleNotificationAsync({
identifier: NOTIF_ID,
content: {
title: 'anonmesh',
body: 'Mesh active',
sound: false,
sticky: true,
interruptionLevel: 'passive',
},
trigger: null,
}).catch(() => {});
return;
}

// Android: live counts on the LOW-importance 'peer-status' channel — the
// channel handles silence, so updates can flow without annoying the user.
if (online === prevOnlineRef.current && total === prevTotalRef.current) return;
prevOnlineRef.current = online;
prevTotalRef.current = total;

if (total === 0) {
if (!hasPeers) {
Notifications.dismissNotificationAsync(NOTIF_ID).catch(() => {});
return;
}
Expand Down
Loading