1
1
import Router from "@/router" ;
2
2
import { useFonts } from "expo-font" ;
3
- import { btoaPolyfill , atobPolyfill } from "js-base64" ;
4
3
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" ;
8
6
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" ;
9
12
10
13
SplashScreen . preventAutoHideAsync ( ) ;
11
14
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
+
12
23
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
+
13
30
const [ fontsLoaded , fontError ] = useFonts ( {
14
31
light : require ( "./assets/fonts/FixelText-Light.ttf" ) ,
15
32
regular : require ( "./assets/fonts/FixelText-Regular.ttf" ) ,
@@ -18,17 +35,65 @@ export default function App () {
18
35
bold : require ( "./assets/fonts/FixelText-Bold.ttf" ) ,
19
36
} ) ;
20
37
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 ( / i n a c t i v e | b a c k g r o u n d / ) ) {
88
+ log ( "App in background" , "AppState" ) ;
89
+ backgroundStartTime . current = Date . now ( ) ;
90
+ }
91
+
92
+ setAppState ( nextAppState ) ;
28
93
} ) ;
29
- } ;
30
94
31
- applyGlobalPolyfills ( ) ;
95
+ return ( ) => subscription . remove ( ) ;
96
+ } , [ appState , handleBackgroundState ] ) ;
32
97
33
98
useEffect ( ( ) => {
34
99
LogBox . ignoreLogs ( [
@@ -37,13 +102,29 @@ export default function App () {
37
102
"TNodeChildrenRenderer: Support for defaultProps"
38
103
] ) ;
39
104
40
- // Register background tasks only if not running in the Expo Go app
41
105
expoGoWrapper ( async ( ) => {
42
106
const { registerBackgroundTasks } = await import ( "@/background/BackgroundTasks" ) ;
43
107
registerBackgroundTasks ( ) ;
44
108
} ) ;
45
109
} , [ ] ) ;
46
110
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
+
48
129
return < Router /> ;
49
- }
130
+ }
0 commit comments