Skip to content
Merged
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
62 changes: 45 additions & 17 deletions src/app/pages/client/BackgroundNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,13 @@ const startBackgroundClient = async (session: Session): Promise<MatrixClient> =>
timelineLimit: 1,
};

await startClient(mx, startOpts);
return mx;
try {
await startClient(mx, startOpts);
return mx;
} catch (error) {
stopClient(mx);
throw error;
}
};

/**
Expand Down Expand Up @@ -207,7 +212,9 @@ export function BackgroundNotifications() {
}

const { current } = clientsRef;
let disposed = false;
const activeIds = new Set(inactiveSessions.map((s) => s.userId));
const retryTimers: ReturnType<typeof setTimeout>[] = [];

async function sendNotification(opts: NotifyOptions): Promise<void> {
if (isDesktopTauri()) {
Expand Down Expand Up @@ -285,15 +292,27 @@ export function BackgroundNotifications() {
// Using a named function (vs. inline .then) lets the .catch() schedule a
// fresh retry referencing the latest session from inactiveSessionsRef.
const startSession = (session: Session, attempt = 0): void => {
if (disposed || current.has(session.userId)) return;

let sessionMx: MatrixClient | undefined;
startBackgroundClient(session)
.then(async (mx) => {
sessionMx = mx;
if (disposed) {
stopClient(mx);
return;
}
current.set(session.userId, mx);
Sentry.metrics.gauge('sable.background.client_count', current.size);

await waitForSync(mx);

if (disposed) return;
if (current.get(session.userId) !== mx) {
stopClient(mx);
return;
}

// Wait for m.direct account data to load. This is critical for DM detection.
// Without it, rooms in /direct/ won't be recognized as DMs, causing notifications to fail.
let mDirectsSet: Set<string> | undefined;
Expand All @@ -317,6 +336,12 @@ export function BackgroundNotifications() {
});
}

if (disposed) return;
if (current.get(session.userId) !== mx) {
stopClient(mx);
return;
}

const pushProcessor = mx.pushProcessor;

const handleAccountData = (event: MatrixEvent) => {
Expand Down Expand Up @@ -564,6 +589,7 @@ export function BackgroundNotifications() {
});
})
.catch((err) => {
if (disposed) return;
log.error('failed to start background client for', session.userId, err);
debugLog.error('notification', 'Failed to start background client', {
userId: session.userId,
Expand All @@ -585,14 +611,17 @@ export function BackgroundNotifications() {
// Retry with exponential backoff, up to 5 attempts (5s, 10s, 20s, 40s, 60s cap).
if (attempt < 5) {
const retryDelay = Math.min(5_000 * 2 ** attempt, 60_000);
setTimeout(() => {
const latestSession = inactiveSessionsRef.current.find(
(s) => s.userId === session.userId
);
if (latestSession && !current.has(session.userId)) {
startSession(latestSession, attempt + 1);
}
}, retryDelay);
retryTimers.push(
setTimeout(() => {
if (disposed) return;
const latestSession = inactiveSessionsRef.current.find(
(s) => s.userId === session.userId
);
if (latestSession && !current.has(session.userId)) {
startSession(latestSession, attempt + 1);
}
}, retryDelay)
);
}
});
};
Expand All @@ -610,16 +639,15 @@ export function BackgroundNotifications() {
});

const cleanupMap = clientCleanupRef.current;
const activeUserIds = new Set(inactiveSessions.map((s) => s.userId));
return () => {
disposed = true;
staggerTimers.forEach(clearTimeout);
retryTimers.forEach(clearTimeout);
current.forEach((mx, userId) => {
if (!activeUserIds.has(userId)) {
cleanupMap.get(userId)?.();
cleanupMap.delete(userId);
stopClient(mx);
current.delete(userId);
}
cleanupMap.get(userId)?.();
cleanupMap.delete(userId);
stopClient(mx);
current.delete(userId);
});
};
}, [
Expand Down
Loading