feat(sync): implement background sync and visibility handling for mobile
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
Spinner,
|
||||
Text,
|
||||
} from 'folds';
|
||||
import { HttpApiEvent, HttpApiEventHandlerMap, MatrixClient } from 'matrix-js-sdk';
|
||||
import { HttpApiEvent, HttpApiEventHandlerMap, MatrixClient, SyncState } from 'matrix-js-sdk';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import React, { MouseEventHandler, ReactNode, useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
@@ -36,6 +36,7 @@ import { SyncStatus } from './SyncStatus';
|
||||
import { AuthMetadataProvider } from '../../hooks/useAuthMetadata';
|
||||
import { getFallbackSession } from '../../state/sessions';
|
||||
import { CallProviderWrapper } from '../../features/call/CallProviderWrapper';
|
||||
import { isTauriMobile, startBackgroundSync, stopBackgroundSync } from '../../utils/tauri';
|
||||
|
||||
function ClientRootLoading() {
|
||||
return (
|
||||
@@ -139,6 +140,90 @@ const useLogoutListener = (mx?: MatrixClient) => {
|
||||
}, [mx]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to handle visibility changes and restart sync on mobile
|
||||
* When the app goes to background and comes back, sync may be stale/stopped
|
||||
*/
|
||||
const useVisibilitySync = (mx?: MatrixClient) => {
|
||||
useEffect(() => {
|
||||
if (!mx || !isTauriMobile()) return;
|
||||
|
||||
const handleVisibilityChange = () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
console.log('[useVisibilitySync] App became visible, checking sync state');
|
||||
const syncState = mx.getSyncState();
|
||||
console.log('[useVisibilitySync] Current sync state:', syncState);
|
||||
|
||||
// If sync is not running or errored, restart it
|
||||
if (!mx.clientRunning || syncState === SyncState.Error || syncState === SyncState.Stopped || syncState === null) {
|
||||
console.log('[useVisibilitySync] Restarting sync...');
|
||||
startClient(mx).catch((err) => {
|
||||
console.error('[useVisibilitySync] Failed to restart sync:', err);
|
||||
});
|
||||
} else {
|
||||
// Force a sync even if client is running (to catch up quickly)
|
||||
console.log('[useVisibilitySync] Triggering immediate sync');
|
||||
mx.retryImmediately();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
// Also handle resume event for mobile (if available)
|
||||
window.addEventListener('resume', handleVisibilityChange);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
window.removeEventListener('resume', handleVisibilityChange);
|
||||
};
|
||||
}, [mx]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to start/stop native Rust background sync on mobile
|
||||
* This allows notifications to work even when the app is backgrounded
|
||||
*/
|
||||
const useBackgroundSync = (mx?: MatrixClient) => {
|
||||
useEffect(() => {
|
||||
if (!mx || !isTauriMobile()) return;
|
||||
|
||||
// Start background sync with credentials from the Matrix client
|
||||
const startSync = async () => {
|
||||
try {
|
||||
const homeserverUrl = mx.getHomeserverUrl();
|
||||
const userId = mx.getUserId();
|
||||
const accessToken = mx.getAccessToken();
|
||||
const deviceId = mx.getDeviceId();
|
||||
|
||||
if (!homeserverUrl || !userId || !accessToken || !deviceId) {
|
||||
console.warn('[useBackgroundSync] Missing credentials, cannot start background sync');
|
||||
return;
|
||||
}
|
||||
|
||||
await startBackgroundSync({
|
||||
homeserverUrl,
|
||||
userId,
|
||||
accessToken,
|
||||
deviceId,
|
||||
});
|
||||
console.log('[useBackgroundSync] Background sync started');
|
||||
} catch (err) {
|
||||
console.error('[useBackgroundSync] Failed to start background sync:', err);
|
||||
}
|
||||
};
|
||||
|
||||
startSync();
|
||||
|
||||
// Stop background sync on cleanup
|
||||
return () => {
|
||||
stopBackgroundSync().catch((err) => {
|
||||
console.error('[useBackgroundSync] Failed to stop background sync:', err);
|
||||
});
|
||||
};
|
||||
}, [mx]);
|
||||
};
|
||||
|
||||
type ClientRootProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
@@ -161,6 +246,8 @@ export function ClientRoot({ children }: ClientRootProps) {
|
||||
);
|
||||
|
||||
useLogoutListener(mx);
|
||||
useVisibilitySync(mx);
|
||||
useBackgroundSync(mx);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadState.status === AsyncStatus.Idle) {
|
||||
|
||||
Reference in New Issue
Block a user