@terreno/rtk 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,26 +1,68 @@
1
1
  import {useToast} from "@terreno/ui";
2
2
  import Constants from "expo-constants";
3
- import {useCallback, useEffect, useState} from "react";
4
- import {Linking} from "react-native";
3
+ import {useCallback, useEffect, useRef, useState} from "react";
4
+ import {AppState, Linking} from "react-native";
5
5
 
6
6
  import {useLazyGetVersionCheckQuery} from "./emptyApi";
7
7
  import {IsWeb} from "./platform";
8
8
 
9
+ interface UseUpgradeCheckOptions {
10
+ /**
11
+ * How often to re-check for updates (in milliseconds).
12
+ * Defaults to undefined (no polling — single check on mount only).
13
+ */
14
+ pollingIntervalMs?: number;
15
+ /**
16
+ * Re-check when the app or browser tab returns to the foreground.
17
+ * Defaults to false.
18
+ */
19
+ recheckOnForeground?: boolean;
20
+ }
21
+
9
22
  interface UseUpgradeCheckResult {
10
23
  canUpdate: boolean;
11
24
  isRequired: boolean;
25
+ isWarning: boolean;
12
26
  requiredMessage?: string;
27
+ warningMessage?: string;
13
28
  onUpdate: () => void;
14
29
  }
15
30
 
16
- export const useUpgradeCheck = (): UseUpgradeCheckResult => {
31
+ /**
32
+ * Checks the running app build number against the backend's VersionConfig
33
+ * thresholds and returns the current upgrade status.
34
+ *
35
+ * - `isRequired` — the build is below the required threshold; the caller
36
+ * should block the UI (e.g. with `UpgradeRequiredScreen`).
37
+ * - `isWarning` — the build is below the warning threshold; the caller
38
+ * can render a dismissible `Banner` or similar prompt.
39
+ *
40
+ * By default a single check runs on mount. Pass `pollingIntervalMs` and/or
41
+ * `recheckOnForeground` to keep long-lived sessions up to date.
42
+ *
43
+ * @param options - Optional polling and foreground re-check configuration.
44
+ * @returns Current upgrade status, messages, and an `onUpdate` callback.
45
+ */
46
+ export const useUpgradeCheck = (options?: UseUpgradeCheckOptions): UseUpgradeCheckResult => {
47
+ const {pollingIntervalMs, recheckOnForeground = false} = options ?? {};
48
+
17
49
  const [isRequired, setIsRequired] = useState(false);
50
+ const [isWarning, setIsWarning] = useState(false);
18
51
  const [requiredMessage, setRequiredMessage] = useState<string>();
19
- const [updateUrl, setUpdateUrl] = useState<string>();
20
52
  const [warningMessage, setWarningMessage] = useState<string>();
53
+ const [updateUrl, setUpdateUrl] = useState<string>();
21
54
  const toast = useToast();
22
55
  const [triggerVersionCheck, result] = useLazyGetVersionCheckQuery();
23
56
  const buildNumber = Constants.expoConfig?.extra?.buildNumber as number | undefined;
57
+ const appState = useRef(AppState.currentState);
58
+
59
+ const runCheck = useCallback(() => {
60
+ if (buildNumber === undefined || buildNumber === null) {
61
+ return;
62
+ }
63
+ const platform = IsWeb ? "web" : "mobile";
64
+ void triggerVersionCheck({platform, version: buildNumber});
65
+ }, [buildNumber, triggerVersionCheck]);
24
66
 
25
67
  const onUpdate = useCallback(() => {
26
68
  if (IsWeb) {
@@ -36,10 +78,45 @@ export const useUpgradeCheck = (): UseUpgradeCheckResult => {
36
78
  }
37
79
  }, [updateUrl]);
38
80
 
81
+ // Initial check on mount
82
+ useEffect(() => {
83
+ runCheck();
84
+ }, [runCheck]);
85
+
86
+ // Periodic re-check at the configured interval
87
+ useEffect(() => {
88
+ if (!pollingIntervalMs) {
89
+ return;
90
+ }
91
+ const interval = setInterval(runCheck, pollingIntervalMs);
92
+ return () => clearInterval(interval);
93
+ }, [runCheck, pollingIntervalMs]);
94
+
95
+ // Re-check when app/tab returns to foreground
96
+ useEffect(() => {
97
+ if (!recheckOnForeground) {
98
+ return;
99
+ }
100
+ const subscription = AppState.addEventListener("change", (nextAppState) => {
101
+ const wasBackground = /inactive|background/.test(appState.current);
102
+ const isNowActive = nextAppState === "active";
103
+
104
+ if (wasBackground && isNowActive) {
105
+ runCheck();
106
+ }
107
+
108
+ appState.current = nextAppState;
109
+ });
110
+
111
+ return () => subscription.remove();
112
+ }, [runCheck, recheckOnForeground]);
113
+
39
114
  // Show warning toast in a separate effect. ToastProvider initializes its ref
40
115
  // in useEffect, so we may need to retry when toast becomes available.
41
116
  useEffect(() => {
42
- if (!warningMessage) return;
117
+ if (!warningMessage) {
118
+ return;
119
+ }
43
120
  const toastId = toast.warn(warningMessage, {persistent: true});
44
121
  if (toastId) {
45
122
  setWarningMessage(undefined);
@@ -48,16 +125,7 @@ export const useUpgradeCheck = (): UseUpgradeCheckResult => {
48
125
  }
49
126
  }, [warningMessage, toast]);
50
127
 
51
- useEffect(() => {
52
- if (buildNumber === undefined || buildNumber === null) {
53
- return;
54
- }
55
-
56
- const platform = IsWeb ? "web" : "mobile";
57
- void triggerVersionCheck({platform, version: buildNumber});
58
- }, [buildNumber, triggerVersionCheck]);
59
-
60
- // Process the version-check response: block on required, warn on warning
128
+ // Process version-check response — update warning/required state
61
129
  useEffect(() => {
62
130
  if (result.isError) {
63
131
  console.debug("Version check failed, continuing normally", result.error);
@@ -66,13 +134,19 @@ export const useUpgradeCheck = (): UseUpgradeCheckResult => {
66
134
  if (!result.isSuccess || !result.data) {
67
135
  return;
68
136
  }
137
+
69
138
  const {message, status, updateUrl: responseUpdateUrl} = result.data;
70
139
 
71
140
  if (status === "required") {
72
141
  setIsRequired(true);
73
142
  setRequiredMessage(message);
74
- } else if (status === "warning" && message) {
143
+ setIsWarning(false);
144
+ } else if (status === "warning") {
145
+ setIsWarning(true);
75
146
  setWarningMessage(message);
147
+ } else {
148
+ setIsWarning(false);
149
+ setIsRequired(false);
76
150
  }
77
151
 
78
152
  if (responseUpdateUrl) {
@@ -82,5 +156,5 @@ export const useUpgradeCheck = (): UseUpgradeCheckResult => {
82
156
 
83
157
  const canUpdate = IsWeb || !!updateUrl;
84
158
 
85
- return {canUpdate, isRequired, onUpdate, requiredMessage};
159
+ return {canUpdate, isRequired, isWarning, onUpdate, requiredMessage, warningMessage};
86
160
  };