@@ -100,7 +100,7 @@ function AnimatedCategoryButton({ category, selected, onPress, theme }: Animated
|
|||||||
toValue: selected ? 1.05 : 1,
|
toValue: selected ? 1.05 : 1,
|
||||||
duration: 150,
|
duration: 150,
|
||||||
easing: Easing.out(Easing.cubic),
|
easing: Easing.out(Easing.cubic),
|
||||||
useNativeDriver: true,
|
useNativeDriver: false,
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
Animated.timing(borderWidthAnim, {
|
Animated.timing(borderWidthAnim, {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AppState, AppStateStatus } from 'react-native';
|
||||||
import { database, collections } from './index';
|
import { database, collections } from './index';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { apiFetch, getAuthToken } from '@/services/auth';
|
import { apiFetch, getAuthToken } from '@/services/auth';
|
||||||
@@ -473,3 +474,74 @@ async function upsertFriendship(row: ServerRow, force: boolean): Promise<boolean
|
|||||||
});
|
});
|
||||||
return true;
|
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 React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { database, collections } from '../database';
|
import { database, collections } from '../database';
|
||||||
import { runSync } from '../database/sync';
|
import { runSync, watchForUpdates, stopWatchingForUpdates } from '../database/sync';
|
||||||
import { getAuthToken } from '../services/auth';
|
import { getAuthToken } from '../services/auth';
|
||||||
import { DEFAULT_CATEGORIES } from '../constants';
|
import { DEFAULT_CATEGORIES } from '../constants';
|
||||||
import Category from '../models/Category';
|
import Category from '../models/Category';
|
||||||
@@ -41,7 +41,13 @@ export function DatabaseProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
const token = await getAuthToken();
|
const token = await getAuthToken();
|
||||||
if (token) {
|
if (token) {
|
||||||
runSync().catch(() => {});
|
runSync()
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => {
|
||||||
|
watchForUpdates();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
stopWatchingForUpdates();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to initialize database:', error);
|
console.error('Failed to initialize database:', error);
|
||||||
@@ -51,6 +57,9 @@ export function DatabaseProvider({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initializeDatabase();
|
initializeDatabase();
|
||||||
|
return () => {
|
||||||
|
stopWatchingForUpdates();
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user