@@ -100,7 +100,7 @@ function AnimatedCategoryButton({ category, selected, onPress, theme }: Animated
|
||||
toValue: selected ? 1.05 : 1,
|
||||
duration: 150,
|
||||
easing: Easing.out(Easing.cubic),
|
||||
useNativeDriver: true,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
|
||||
Animated.timing(borderWidthAnim, {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AppState, AppStateStatus } from 'react-native';
|
||||
import { database, collections } from './index';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { apiFetch, getAuthToken } from '@/services/auth';
|
||||
@@ -473,3 +474,74 @@ async function upsertFriendship(row: ServerRow, force: boolean): Promise<boolean
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
export const DEFAULT_WATCH_INTERVAL_MS = 60_000;
|
||||
|
||||
export interface UpdateWatcher {
|
||||
stop: () => void;
|
||||
isRunning: () => boolean;
|
||||
}
|
||||
|
||||
let watcherStop: (() => void) | null = null;
|
||||
let watcherActive = false;
|
||||
let appInForeground = true;
|
||||
let syncInFlight = false;
|
||||
|
||||
export function watchForUpdates(intervalMs: number = DEFAULT_WATCH_INTERVAL_MS): UpdateWatcher {
|
||||
if (watcherStop) {
|
||||
return {
|
||||
stop: () => stopWatchingForUpdates(),
|
||||
isRunning: () => watcherActive,
|
||||
};
|
||||
}
|
||||
|
||||
appInForeground = AppState.currentState === 'active';
|
||||
|
||||
const appStateSubscription = AppState.addEventListener('change', (state: AppStateStatus) => {
|
||||
const next = state === 'active';
|
||||
const wentForeground = next && !appInForeground;
|
||||
appInForeground = next;
|
||||
if (wentForeground) {
|
||||
maybeSyncOnce().catch(() => {});
|
||||
}
|
||||
});
|
||||
|
||||
const timer = setInterval(() => {
|
||||
if (appInForeground && !syncInFlight) {
|
||||
maybeSyncOnce().catch(() => {});
|
||||
}
|
||||
}, intervalMs);
|
||||
|
||||
watcherActive = true;
|
||||
watcherStop = () => {
|
||||
clearInterval(timer);
|
||||
appStateSubscription.remove();
|
||||
watcherStop = null;
|
||||
watcherActive = false;
|
||||
};
|
||||
|
||||
return {
|
||||
stop: () => stopWatchingForUpdates(),
|
||||
isRunning: () => watcherActive,
|
||||
};
|
||||
}
|
||||
|
||||
async function maybeSyncOnce(): Promise<void> {
|
||||
if (syncInFlight) return;
|
||||
const token = await getAuthToken();
|
||||
if (!token) return;
|
||||
syncInFlight = true;
|
||||
try {
|
||||
await runSync();
|
||||
} finally {
|
||||
syncInFlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
export function stopWatchingForUpdates(): void {
|
||||
watcherStop?.();
|
||||
}
|
||||
|
||||
export function isWatchingForUpdates(): boolean {
|
||||
return watcherActive;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { database, collections } from '../database';
|
||||
import { runSync } from '../database/sync';
|
||||
import { runSync, watchForUpdates, stopWatchingForUpdates } from '../database/sync';
|
||||
import { getAuthToken } from '../services/auth';
|
||||
import { DEFAULT_CATEGORIES } from '../constants';
|
||||
import Category from '../models/Category';
|
||||
@@ -41,7 +41,13 @@ export function DatabaseProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const token = await getAuthToken();
|
||||
if (token) {
|
||||
runSync().catch(() => {});
|
||||
runSync()
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
watchForUpdates();
|
||||
});
|
||||
} else {
|
||||
stopWatchingForUpdates();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize database:', error);
|
||||
@@ -51,6 +57,9 @@ export function DatabaseProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
useEffect(() => {
|
||||
initializeDatabase();
|
||||
return () => {
|
||||
stopWatchingForUpdates();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user