@teardown/react-native 2.0.1 → 2.0.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.
@@ -71,7 +71,11 @@ export type VersionStatusChangeEvents = {
71
71
  export type ForceUpdateClientOptions = {
72
72
  /** Min ms between foreground checks (default: 30000) */
73
73
  throttleMs?: number;
74
- /** Min ms since last successful check before re-checking (default: 300000 = 5min) */
74
+ /**
75
+ * Min ms since last successful check before re-checking (default: 300000 = 5min)
76
+ * Set this to 0 to disable cooldown and check immediately on every foreground transition.
77
+ * Set to -1 disable checking entirely.
78
+ */
75
79
  checkCooldownMs?: number;
76
80
  };
77
81
 
@@ -84,11 +88,12 @@ export const VERSION_STATUS_STORAGE_KEY = "VERSION_STATUS";
84
88
 
85
89
  export class ForceUpdateClient {
86
90
  private emitter = new EventEmitter<VersionStatusChangeEvents>();
87
- private versionStatus: VersionStatus;
91
+ private versionStatus: VersionStatus = { type: "initializing" };
88
92
  private unsubscribe: (() => void) | null = null;
89
93
  private appStateSubscription: NativeEventSubscription | null = null;
90
94
  private lastCheckTime: number | null = null;
91
95
  private lastForegroundTime: number | null = null;
96
+ private initialized = false;
92
97
 
93
98
  private readonly logger: Logger;
94
99
  private readonly storage: SupportedStorage;
@@ -103,18 +108,55 @@ export class ForceUpdateClient {
103
108
  this.logger = logging.createLogger({ name: "ForceUpdateClient" });
104
109
  this.storage = storage.createStorage("version");
105
110
  this.options = { ...DEFAULT_OPTIONS, ...options };
111
+ // Don't initialize here - defer to initialize()
112
+ }
113
+
114
+ initialize(): void {
115
+ if (this.initialized) {
116
+ this.logger.debug("ForceUpdateClient already initialized");
117
+ return;
118
+ }
119
+ this.initialized = true;
120
+
121
+ // Load from storage, subscribe to events, and sync with current identity state
106
122
  this.versionStatus = this.getVersionStatusFromStorage();
123
+ this.logger.debug(`Initialized with version status: ${this.versionStatus.type}`);
107
124
  this.subscribeToIdentity();
125
+ this.initializeFromCurrentIdentityState();
108
126
  this.subscribeToAppState();
109
127
  }
110
128
 
129
+ private initializeFromCurrentIdentityState() {
130
+ const currentState = this.identity.getIdentifyState();
131
+ this.logger.debug(`Current identity state during init: ${currentState.type}`);
132
+ if (currentState.type === "identified") {
133
+ this.logger.debug(`Identity already identified, syncing version status from: ${currentState.version_info.status}`);
134
+ this.updateFromVersionStatus(currentState.version_info.status);
135
+ } else {
136
+ this.logger.debug(`Identity not yet identified (${currentState.type}), waiting for identify event`);
137
+ }
138
+ }
139
+
111
140
  private getVersionStatusFromStorage(): VersionStatus {
112
141
  const stored = this.storage.getItem(VERSION_STATUS_STORAGE_KEY);
142
+
113
143
  if (stored == null) {
144
+ this.logger.debug("No stored version status, returning initializing");
145
+ return InitializingVersionStatusSchema.parse({ type: "initializing" });
146
+ }
147
+
148
+ const parsed = VersionStatusSchema.parse(JSON.parse(stored));
149
+ this.logger.debug(`Parsed version status from storage: ${parsed.type}`);
150
+
151
+ // "checking" and "initializing" are transient states - if we restore them, reset to initializing
152
+ // This can happen if the app was killed during a version check
153
+ if (parsed.type === "checking" || parsed.type === "initializing") {
154
+ this.logger.debug(`Found stale '${parsed.type}' state in storage, resetting to initializing`);
155
+ this.storage.removeItem(VERSION_STATUS_STORAGE_KEY);
114
156
  return InitializingVersionStatusSchema.parse({ type: "initializing" });
115
157
  }
116
158
 
117
- return VersionStatusSchema.parse(JSON.parse(stored));
159
+ return parsed;
118
160
  }
119
161
 
120
162
  private saveVersionStatusToStorage(status: VersionStatus): void {
@@ -123,10 +165,15 @@ export class ForceUpdateClient {
123
165
 
124
166
  private subscribeToIdentity() {
125
167
  this.unsubscribe = this.identity.onIdentifyStateChange((state) => {
126
- if (state.type === "identifying") {
127
- this.setVersionStatus({ type: "checking" });
128
- } else if (state.type === "identified") {
129
- this.updateFromVersionStatus(state.version_info.status ?? IdentifyVersionStatusEnum.UP_TO_DATE);
168
+ this.logger.debug(`Identity state changed: ${state.type}`);
169
+ switch (state.type) {
170
+ case "identifying":
171
+ this.setVersionStatus({ type: "checking" });
172
+ break;
173
+ case "identified":
174
+ this.logger.debug(`Identified with version_info.status: ${state.version_info.status}`);
175
+ this.updateFromVersionStatus(state.version_info.status ?? IdentifyVersionStatusEnum.UP_TO_DATE);
176
+ break;
130
177
  }
131
178
  });
132
179
  }
@@ -164,9 +211,22 @@ export class ForceUpdateClient {
164
211
 
165
212
  private handleAppStateChange = (nextState: AppStateStatus) => {
166
213
  if (nextState === "active") {
214
+ this.logger.debug("App state changed to active");
215
+
216
+ // If checkCooldownMs is -1, disable checking entirely
217
+ if (this.options.checkCooldownMs === -1) {
218
+ this.logger.debug("Version checking disabled (checkCooldownMs = -1)");
219
+ return;
220
+ }
221
+
167
222
  const now = Date.now();
168
223
  const throttleOk = !this.lastForegroundTime || now - this.lastForegroundTime >= this.options.throttleMs;
169
- const cooldownOk = !this.lastCheckTime || now - this.lastCheckTime >= this.options.checkCooldownMs;
224
+
225
+ // If checkCooldownMs is 0, always allow check (no cooldown)
226
+ // Otherwise, check if enough time has passed since last successful check
227
+ const cooldownOk = this.options.checkCooldownMs === 0
228
+ || !this.lastCheckTime
229
+ || now - this.lastCheckTime >= this.options.checkCooldownMs;
170
230
 
171
231
  this.lastForegroundTime = now;
172
232
 
@@ -177,11 +237,11 @@ export class ForceUpdateClient {
177
237
  };
178
238
 
179
239
  private async checkVersionOnForeground() {
180
- this.logger.info("Checking version status on foreground");
240
+ this.logger.debug("Checking version status on foreground");
181
241
  const result = await this.identity.identify();
182
242
 
183
243
  if (!result) {
184
- this.logger.info("Skipping version check - not identified");
244
+ this.logger.debug("Skipping version check - not identified");
185
245
  return;
186
246
  }
187
247
 
@@ -203,7 +263,7 @@ export class ForceUpdateClient {
203
263
  }
204
264
 
205
265
  private setVersionStatus(newStatus: VersionStatus) {
206
- this.logger.info(`Version status changing: ${this.versionStatus.type} -> ${newStatus.type}`);
266
+ this.logger.debug(`Version status changing: ${this.versionStatus.type} -> ${newStatus.type}`);
207
267
  this.versionStatus = newStatus;
208
268
  this.saveVersionStatusToStorage(newStatus);
209
269
  this.emitter.emit("VERSION_STATUS_CHANGED", newStatus);