Skip to content

Commit ac10b28

Browse files
authored
Merge pull request PapillonApp#536 from PapillonApp/feat-refreshafter5min
feat(Refresh): Ajout d'un refresh Automatique a la réouvrerture de l'application
2 parents 97a653d + 48c0115 commit ac10b28

File tree

1 file changed

+97
-16
lines changed

1 file changed

+97
-16
lines changed

App.tsx

Lines changed: 97 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
import Router from "@/router";
22
import { useFonts } from "expo-font";
3-
import { btoaPolyfill, atobPolyfill } from "js-base64";
43
import * as SplashScreen from "expo-splash-screen";
5-
import { LogBox } from "react-native";
6-
import React, { useEffect } from "react";
7-
import { expoGoWrapper } from "@/utils/native/expoGoAlert";
4+
import { LogBox, AppState, AppStateStatus } from "react-native";
5+
import React, { useEffect, useState, useRef, useCallback } from "react";
86
import AsyncStorage from "@react-native-async-storage/async-storage";
7+
import { useAccounts, useCurrentAccount } from "@/stores/account";
8+
import { AccountService } from "@/stores/account/types";
9+
import { log } from "@/utils/logger/logger";
10+
import { expoGoWrapper } from "@/utils/native/expoGoAlert";
11+
import { atobPolyfill, btoaPolyfill } from "js-base64";
912

1013
SplashScreen.preventAutoHideAsync();
1114

15+
const DEFAULT_BACKGROUND_TIME = 15 * 60 * 1000; // 15 minutes
16+
17+
const BACKGROUND_LIMITS: Partial<Record<AccountService, number>> = {
18+
[AccountService.EcoleDirecte]: 15 * 60 * 1000, // 15 minutes
19+
[AccountService.Pronote]: 5 * 60 * 1000, // 5 minutes
20+
[AccountService.Skolengo]: 12 * 60 * 60 * 1000, // 12 heures
21+
};
22+
1223
export default function App () {
24+
const [appState, setAppState] = useState<AppStateStatus>(AppState.currentState);
25+
const backgroundStartTime = useRef<number | null>(null);
26+
const switchTo = useCurrentAccount((store) => store.switchTo);
27+
const accounts = useAccounts((store) => store.accounts)
28+
.filter(account => !account.isExternal);
29+
1330
const [fontsLoaded, fontError] = useFonts({
1431
light: require("./assets/fonts/FixelText-Light.ttf"),
1532
regular: require("./assets/fonts/FixelText-Regular.ttf"),
@@ -18,17 +35,65 @@ export default function App () {
1835
bold: require("./assets/fonts/FixelText-Bold.ttf"),
1936
});
2037

21-
const applyGlobalPolyfills = () => {
22-
const encoding = require("text-encoding");
23-
Object.assign(global, {
24-
TextDecoder: encoding.TextDecoder,
25-
TextEncoder: encoding.TextEncoder,
26-
atob: atobPolyfill,
27-
btoa: btoaPolyfill
38+
const getBackgroundTimeLimit = useCallback((service: AccountService): number => {
39+
return BACKGROUND_LIMITS[service] ?? DEFAULT_BACKGROUND_TIME;
40+
}, []);
41+
42+
const handleBackgroundState = useCallback(async () => {
43+
try {
44+
if (!backgroundStartTime.current) return;
45+
46+
const timeInBackground = Date.now() - backgroundStartTime.current;
47+
await AsyncStorage.setItem("@background_timestamp", Date.now().toString());
48+
49+
for (const account of accounts) {
50+
const timeLimit = getBackgroundTimeLimit(account.service);
51+
const timeInBackgroundSeconds = Math.floor(timeInBackground / 1000);
52+
const serviceName = AccountService[account.service];
53+
54+
log(`Checking account ${account.studentName.first} ${account.studentName.last}:`, "RefreshToken");
55+
log(`Time in background: ${timeInBackgroundSeconds}s`, "RefreshToken");
56+
log(`Time limit: ${timeLimit / 1000}s`, "RefreshToken");
57+
log(`Account type: ${serviceName}`, "RefreshToken");
58+
log(`Using ${BACKGROUND_LIMITS[account.service] ? "specific" : "default"} time limit`, "RefreshToken");
59+
60+
if (timeInBackground >= timeLimit) {
61+
log(`⚠️ Refreshing account ${account.studentName.first} ${account.studentName.last} after ${timeInBackgroundSeconds}s in background`, "RefreshToken");
62+
63+
// Prevent React state updates during render
64+
setTimeout(() => {
65+
switchTo(account).catch((error) => {
66+
log(`Error during switchTo: ${error}`, "RefreshToken");
67+
});
68+
}, 0);
69+
70+
// Wait before processing next account
71+
await new Promise(resolve => setTimeout(resolve, 1000));
72+
}
73+
}
74+
} catch (error) {
75+
log(`Error handling background state: ${error}`, "RefreshToken");
76+
}
77+
}, [accounts, switchTo, getBackgroundTimeLimit]);
78+
79+
useEffect(() => {
80+
const subscription = AppState.addEventListener("change", async (nextAppState: AppStateStatus) => {
81+
if (appState === nextAppState) return;
82+
83+
if (nextAppState === "active") {
84+
log("🔄 App is active", "AppState");
85+
await handleBackgroundState();
86+
backgroundStartTime.current = null;
87+
} else if (nextAppState.match(/inactive|background/)) {
88+
log("App in background", "AppState");
89+
backgroundStartTime.current = Date.now();
90+
}
91+
92+
setAppState(nextAppState);
2893
});
29-
};
3094

31-
applyGlobalPolyfills();
95+
return () => subscription.remove();
96+
}, [appState, handleBackgroundState]);
3297

3398
useEffect(() => {
3499
LogBox.ignoreLogs([
@@ -37,13 +102,29 @@ export default function App () {
37102
"TNodeChildrenRenderer: Support for defaultProps"
38103
]);
39104

40-
// Register background tasks only if not running in the Expo Go app
41105
expoGoWrapper(async () => {
42106
const { registerBackgroundTasks } = await import("@/background/BackgroundTasks");
43107
registerBackgroundTasks();
44108
});
45109
}, []);
46110

47-
if (!fontsLoaded && !fontError) return null;
111+
const applyGlobalPolyfills = useCallback(() => {
112+
const encoding = require("text-encoding");
113+
Object.assign(global, {
114+
TextDecoder: encoding.TextDecoder,
115+
TextEncoder: encoding.TextEncoder,
116+
atob: atobPolyfill,
117+
btoa: btoaPolyfill
118+
});
119+
}, []);
120+
121+
useEffect(() => {
122+
applyGlobalPolyfills();
123+
}, [applyGlobalPolyfills]);
124+
125+
if (!fontsLoaded && !fontError) {
126+
return null;
127+
}
128+
48129
return <Router />;
49-
}
130+
}

0 commit comments

Comments
 (0)