feat(sync): implement background sync and visibility handling for mobile

This commit is contained in:
2026-02-05 19:20:49 +11:00
parent 1a452f52ca
commit 4ca4af0e8b
3 changed files with 171 additions and 16 deletions

View File

@@ -9,26 +9,28 @@ export const openExternalUrl = async (url: string): Promise<void> => {
// Only run this in Tauri builds
if (typeof window !== 'undefined' && (window as any).__TAURI__) {
try {
console.log('[openExternalUrl] invoking open_external_url command...');
const { invoke } = await import('@tauri-apps/api/core');
await invoke('open_external_url', { url });
console.log('[openExternalUrl] command succeeded');
// First, try the plugin directly (works on both desktop and mobile)
console.log('[openExternalUrl] trying opener plugin...');
const { openUrl } = await import('@tauri-apps/plugin-opener');
await openUrl(url);
console.log('[openExternalUrl] plugin succeeded');
return;
} catch (err) {
console.error('[openExternalUrl] Tauri command failed:', err);
// Try the plugin as fallback (for mobile)
} catch (pluginErr) {
console.warn('[openExternalUrl] opener plugin failed:', pluginErr);
// Fallback: try the custom command (useful if plugin fails due to ACL)
try {
console.log('[openExternalUrl] trying opener plugin fallback...');
const { openUrl } = await import('@tauri-apps/plugin-opener');
await openUrl(url);
console.log('[openExternalUrl] plugin fallback succeeded');
console.log('[openExternalUrl] trying invoke command fallback...');
const { invoke } = await import('@tauri-apps/api/core');
await invoke('open_external_url', { url });
console.log('[openExternalUrl] command fallback succeeded');
return;
} catch (pluginErr) {
console.error('[openExternalUrl] plugin fallback also failed:', pluginErr);
} catch (invokeErr) {
console.error('[openExternalUrl] command fallback also failed:', invokeErr);
}
return;
}
}
console.log('[openExternalUrl] falling back to window.open');
window.open(url, '_blank');
};
@@ -269,3 +271,69 @@ export const readClipboardImage = async (): Promise<File | null> => {
return null;
}
};
/**
* Background Sync API for mobile platforms
* Starts a native Rust-based Matrix sync that runs even when the app is backgrounded
*/
export interface BackgroundSyncCredentials {
homeserverUrl: string;
userId: string;
accessToken: string;
deviceId: string;
}
/**
* Start background Matrix sync on mobile
* This runs the sync in native Rust code, allowing notifications even when the app is backgrounded
*/
export const startBackgroundSync = async (credentials: BackgroundSyncCredentials): Promise<void> => {
if (!isTauriMobile()) {
console.log('[BackgroundSync] Not on mobile, skipping');
return;
}
try {
const { invoke } = await import('@tauri-apps/api/core');
await invoke('start_background_sync', {
homeserverUrl: credentials.homeserverUrl,
userId: credentials.userId,
accessToken: credentials.accessToken,
deviceId: credentials.deviceId,
});
console.log('[BackgroundSync] Started successfully');
} catch (err) {
console.error('[BackgroundSync] Failed to start:', err);
throw err;
}
};
/**
* Stop background Matrix sync on mobile
*/
export const stopBackgroundSync = async (): Promise<void> => {
if (!isTauriMobile()) return;
try {
const { invoke } = await import('@tauri-apps/api/core');
await invoke('stop_background_sync');
console.log('[BackgroundSync] Stopped');
} catch (err) {
console.error('[BackgroundSync] Failed to stop:', err);
}
};
/**
* Get the current background sync state
*/
export const getBackgroundSyncState = async (): Promise<string> => {
if (!isTauriMobile()) return 'NotApplicable';
try {
const { invoke } = await import('@tauri-apps/api/core');
return await invoke<string>('get_background_sync_state');
} catch (err) {
console.error('[BackgroundSync] Failed to get state:', err);
return 'Error';
}
};