react-native-update 10.30.2 → 10.31.0-beta.0

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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "mcpServers": {
3
+ "RadonAi": {
4
+ "url": "http://127.0.0.1:62020/mcp",
5
+ "type": "http",
6
+ "headers": {
7
+ "nonce": "a3d4f0e6-86bb-4afa-a501-58b346e4afb0"
8
+ }
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>react-native-update</name>
4
+ <comment>Project react-native-update created by Buildship.</comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ </buildSpec>
14
+ <natures>
15
+ <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
16
+ </natures>
17
+ <filteredResources>
18
+ <filter>
19
+ <id>1727963310481</id>
20
+ <name></name>
21
+ <type>30</type>
22
+ <matcher>
23
+ <id>org.eclipse.core.resources.regexFilterMatcher</id>
24
+ <arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
25
+ </matcher>
26
+ </filter>
27
+ </filteredResources>
28
+ </projectDescription>
@@ -0,0 +1,34 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>react-native-update</name>
4
+ <comment>Project react-native-update created by Buildship.</comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>org.eclipse.jdt.core.javabuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ <buildCommand>
14
+ <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
15
+ <arguments>
16
+ </arguments>
17
+ </buildCommand>
18
+ </buildSpec>
19
+ <natures>
20
+ <nature>org.eclipse.jdt.core.javanature</nature>
21
+ <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
22
+ </natures>
23
+ <filteredResources>
24
+ <filter>
25
+ <id>1727963310481</id>
26
+ <name></name>
27
+ <type>30</type>
28
+ <matcher>
29
+ <id>org.eclipse.core.resources.regexFilterMatcher</id>
30
+ <arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
31
+ </matcher>
32
+ </filter>
33
+ </filteredResources>
34
+ </projectDescription>
@@ -0,0 +1,13 @@
1
+ arguments=--init-script /var/folders/l6/0fn3x28s5s585ld3p04gsy1h0000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/l6/0fn3x28s5s585ld3p04gsy1h0000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
2
+ auto.sync=false
3
+ build.scans.enabled=false
4
+ connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(8.9))
5
+ connection.project.dir=
6
+ eclipse.preferences.version=1
7
+ gradle.user.home=
8
+ java.home=/Users/sunny/.sdkman/candidates/java/17.0.9-zulu/zulu-17.jdk/Contents/Home
9
+ jvm.arguments=
10
+ offline.mode=false
11
+ override.workspace.settings=true
12
+ show.console.view=true
13
+ show.executions.view=true
@@ -40,6 +40,16 @@ public class UpdateContext {
40
40
  String storedPackageVersion = this.sp.getString("packageVersion", null);
41
41
  String storedBuildTime = this.sp.getString("buildTime", null);
42
42
 
43
+ // If stored versions don't exist, write current versions first
44
+ if (storedPackageVersion == null || storedBuildTime == null) {
45
+ SharedPreferences.Editor editor = sp.edit();
46
+ editor.putString("packageVersion", packageVersion);
47
+ editor.putString("buildTime", buildTime);
48
+ editor.apply();
49
+ storedPackageVersion = packageVersion;
50
+ storedBuildTime = buildTime;
51
+ }
52
+
43
53
  boolean packageVersionChanged = !packageVersion.equals(storedPackageVersion);
44
54
  boolean buildTimeChanged = !buildTime.equals(storedBuildTime);
45
55
 
@@ -77,6 +77,14 @@ RCT_EXPORT_MODULE(RCTPushy);
77
77
  NSString *storedPackageVersion = [defaults stringForKey:paramPackageVersion];
78
78
  NSString *storedBuildTime = [defaults stringForKey:paramBuildTime];
79
79
 
80
+ // If stored versions don't exist, write current versions first
81
+ if (!storedPackageVersion || !storedBuildTime) {
82
+ [defaults setObject:curPackageVersion forKey:paramPackageVersion];
83
+ [defaults setObject:curBuildTime forKey:paramBuildTime];
84
+ storedPackageVersion = curPackageVersion;
85
+ storedBuildTime = curBuildTime;
86
+ }
87
+
80
88
  BOOL packageVersionChanged = ![curPackageVersion isEqualToString:storedPackageVersion];
81
89
  BOOL buildTimeChanged = ![curBuildTime isEqualToString:storedBuildTime];
82
90
 
@@ -124,7 +132,6 @@ RCT_EXPORT_MODULE(RCTPushy);
124
132
  loadVersion = [self rollback];
125
133
  }
126
134
  }
127
- }
128
135
  }
129
136
 
130
137
  return [RCTPushy binaryBundleURL];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-update",
3
- "version": "10.30.2",
3
+ "version": "10.31.0-beta.0",
4
4
  "description": "react-native hot update",
5
5
  "main": "src/index",
6
6
  "scripts": {
@@ -72,5 +72,6 @@
72
72
  "react-native": "0.73",
73
73
  "ts-jest": "^29.3.2",
74
74
  "typescript": "^5.6.3"
75
- }
75
+ },
76
+ "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
76
77
  }
package/src/client.ts CHANGED
@@ -28,6 +28,7 @@ import {
28
28
  promiseAny,
29
29
  testUrls,
30
30
  } from './utils';
31
+ import i18n from './i18n';
31
32
 
32
33
  const SERVER_PRESETS = {
33
34
  // cn
@@ -91,7 +92,8 @@ export class Pushy {
91
92
  options = defaultClientOptions;
92
93
  clientType: 'Pushy' | 'Cresc' = 'Pushy';
93
94
  lastChecking?: number;
94
- lastRespJson?: Promise<any>;
95
+ lastRespJson?: Promise<CheckResult>;
96
+ lastRespText?: Promise<string>;
95
97
 
96
98
  version = cInfo.rnu;
97
99
  loggerPromise = (() => {
@@ -106,13 +108,18 @@ export class Pushy {
106
108
  })();
107
109
 
108
110
  constructor(options: ClientOptions, clientType?: 'Pushy' | 'Cresc') {
111
+ this.clientType = clientType || 'Pushy';
112
+ this.options.server = SERVER_PRESETS[this.clientType];
113
+
114
+ // Initialize i18n based on clientType
115
+ i18n.setLocale(this.clientType === 'Pushy' ? 'zh' : 'en');
116
+
109
117
  if (Platform.OS === 'ios' || Platform.OS === 'android') {
110
118
  if (!options.appKey) {
111
- throw new Error('appKey is required');
119
+ throw new Error(i18n.t('error_appkey_required'));
112
120
  }
113
121
  }
114
- this.clientType = clientType || 'Pushy';
115
- this.options.server = SERVER_PRESETS[this.clientType];
122
+
116
123
  this.setOptions(options);
117
124
  if (isRolledBack) {
118
125
  this.report({
@@ -135,6 +142,16 @@ export class Pushy {
135
142
  }
136
143
  };
137
144
 
145
+ /**
146
+ * Get translated text based on current clientType
147
+ * @param key - Translation key
148
+ * @param values - Values for interpolation (optional)
149
+ * @returns Translated string
150
+ */
151
+ t = (key: string, values?: Record<string, string | number>) => {
152
+ return i18n.t(key as any, values);
153
+ };
154
+
138
155
  report = async ({
139
156
  type,
140
157
  message = '',
@@ -174,11 +191,7 @@ export class Pushy {
174
191
  };
175
192
  assertDebug = (matter: string) => {
176
193
  if (__DEV__ && !this.options.debug) {
177
- console.info(
178
- `You are currently in the development environment and have not enabled debug mode.
179
- ${matter} will not be performed.
180
- If you need to debug ${matter} in the development environment, please set debug to true in the client.`,
181
- );
194
+ console.info(this.t('dev_debug_disabled', { matter }));
182
195
  return false;
183
196
  }
184
197
  return true;
@@ -269,7 +282,7 @@ export class Pushy {
269
282
  } catch (e: any) {
270
283
  this.report({
271
284
  type: 'errorChecking',
272
- message: `Can not connect to update server: ${e.message}. Trying backup endpoints.`,
285
+ message: this.t('error_cannot_connect_backup', { message: e.message }),
273
286
  });
274
287
  const backupEndpoints = await this.getBackupEndpoints();
275
288
  if (backupEndpoints) {
@@ -289,24 +302,30 @@ export class Pushy {
289
302
  if (!resp) {
290
303
  this.report({
291
304
  type: 'errorChecking',
292
- message: 'Can not connect to update server. Please check your network.',
305
+ message: this.t('error_cannot_connect_server'),
293
306
  });
294
307
  this.throwIfEnabled(new Error('errorChecking'));
295
308
  return this.lastRespJson ? await this.lastRespJson : emptyObj;
296
309
  }
297
- this.lastRespJson = resp.json();
298
-
299
- const result: CheckResult = await this.lastRespJson;
300
-
301
- log('checking result:', result);
302
310
 
303
311
  if (resp.status !== 200) {
312
+ const errorMessage = this.t('error_http_status', {
313
+ status: resp.status,
314
+ statusText: resp.statusText,
315
+ });
304
316
  this.report({
305
317
  type: 'errorChecking',
306
- message: result.message,
318
+ message: errorMessage,
307
319
  });
308
- this.throwIfEnabled(new Error(result.message));
320
+ this.throwIfEnabled(new Error(errorMessage));
321
+ log('error checking response:', resp.status, await resp.text());
322
+ return this.lastRespJson ? await this.lastRespJson : emptyObj;
309
323
  }
324
+ this.lastRespJson = resp.json();
325
+
326
+ const result: CheckResult = await this.lastRespJson;
327
+
328
+ log('checking result:', result);
310
329
 
311
330
  return result;
312
331
  };
@@ -412,7 +431,9 @@ export class Pushy {
412
431
  });
413
432
  succeeded = 'diff';
414
433
  } catch (e: any) {
415
- const errorMessage = `diff error: ${e.message}`;
434
+ const errorMessage = this.t('error_diff_failed', {
435
+ message: e.message,
436
+ });
416
437
  errorMessages.push(errorMessage);
417
438
  lastError = new Error(errorMessage);
418
439
  log(errorMessage);
@@ -429,7 +450,9 @@ export class Pushy {
429
450
  });
430
451
  succeeded = 'pdiff';
431
452
  } catch (e: any) {
432
- const errorMessage = `pdiff error: ${e.message}`;
453
+ const errorMessage = this.t('error_pdiff_failed', {
454
+ message: e.message,
455
+ });
433
456
  errorMessages.push(errorMessage);
434
457
  lastError = new Error(errorMessage);
435
458
  log(errorMessage);
@@ -447,7 +470,9 @@ export class Pushy {
447
470
  });
448
471
  succeeded = 'full';
449
472
  } catch (e: any) {
450
- const errorMessage = `full patch error: ${e.message}`;
473
+ const errorMessage = this.t('error_full_patch_failed', {
474
+ message: e.message,
475
+ });
451
476
  errorMessages.push(errorMessage);
452
477
  lastError = new Error(errorMessage);
453
478
  log(errorMessage);
package/src/i18n.ts ADDED
@@ -0,0 +1,108 @@
1
+ import zhTranslations from './locales/zh';
2
+ import enTranslations from './locales/en';
3
+
4
+ type TranslationKey = keyof typeof zhTranslations | keyof typeof enTranslations;
5
+ type TranslationValues = Record<string, string | number>;
6
+
7
+ class I18n {
8
+ private currentLocale: 'zh' | 'en' = 'en';
9
+ private translations = {
10
+ zh: zhTranslations,
11
+ en: enTranslations,
12
+ };
13
+
14
+ /**
15
+ * Set locale directly
16
+ * @param locale - 'zh' or 'en'
17
+ */
18
+ setLocale(locale: 'zh' | 'en') {
19
+ this.currentLocale = locale;
20
+ }
21
+
22
+ /**
23
+ * Get current locale
24
+ */
25
+ getLocale(): 'zh' | 'en' {
26
+ return this.currentLocale;
27
+ }
28
+
29
+ /**
30
+ * Translate a key with optional interpolation
31
+ * @param key - Translation key
32
+ * @param values - Values for interpolation (optional)
33
+ * @returns Translated string with interpolated values
34
+ */
35
+ t(key: TranslationKey, values?: TranslationValues): string {
36
+ const translation =
37
+ this.translations[this.currentLocale][
38
+ key as keyof (typeof this.translations)[typeof this.currentLocale]
39
+ ];
40
+
41
+ if (!translation) {
42
+ // Fallback to the other locale if key not found
43
+ const fallbackLocale = this.currentLocale === 'zh' ? 'en' : 'zh';
44
+ const fallbackTranslation =
45
+ this.translations[fallbackLocale][
46
+ key as keyof (typeof this.translations)[typeof fallbackLocale]
47
+ ];
48
+
49
+ if (!fallbackTranslation) {
50
+ // If still not found, return the key itself
51
+ return String(key);
52
+ }
53
+
54
+ return this.interpolate(fallbackTranslation, values);
55
+ }
56
+
57
+ return this.interpolate(translation, values);
58
+ }
59
+
60
+ /**
61
+ * Interpolate values into a string template
62
+ * Supports {{key}} syntax
63
+ * @param template - String template with {{key}} placeholders
64
+ * @param values - Values to interpolate
65
+ * @returns Interpolated string
66
+ */
67
+ private interpolate(template: string, values?: TranslationValues): string {
68
+ if (!values) {
69
+ return template;
70
+ }
71
+
72
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
73
+ const value = values[key];
74
+ return value !== undefined ? String(value) : match;
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Add or update translations for a specific locale
80
+ * @param locale - Target locale
81
+ * @param translations - Translation object to merge
82
+ */
83
+ addTranslations(locale: 'zh' | 'en', translations: Record<string, string>) {
84
+ this.translations[locale] = {
85
+ ...this.translations[locale],
86
+ ...translations,
87
+ };
88
+ }
89
+ }
90
+
91
+ // Create singleton instance
92
+ const i18n = new I18n();
93
+
94
+ // Export both the instance and the class for flexibility
95
+ export { i18n, I18n };
96
+ export default i18n;
97
+
98
+ /**
99
+ * Usage examples:
100
+ *
101
+ * // Direct locale setting (new preferred method)
102
+ * i18n.setLocale('zh'); // Chinese
103
+ * i18n.setLocale('en'); // English
104
+ *
105
+ * // Get translations
106
+ * i18n.t('checking_update'); // Based on current locale
107
+ * i18n.t('download_progress', { progress: 50 }); // With interpolation
108
+ */
@@ -0,0 +1,70 @@
1
+ export default {
2
+ // Common messages
3
+ checking_update: 'Checking for updates...',
4
+ downloading_update: 'Downloading update package...',
5
+ installing_update: 'Installing update...',
6
+ update_available: 'Update available',
7
+ update_downloaded: 'Update downloaded successfully',
8
+ update_installed: 'Update installed successfully',
9
+ no_update_available: 'You are up to date',
10
+ update_failed: 'Update failed',
11
+ network_error: 'Network connection error',
12
+ download_failed: 'Download failed',
13
+ install_failed: 'Installation failed',
14
+
15
+ // Progress messages with interpolation
16
+ download_progress: 'Download progress: {{progress}}%',
17
+ download_speed: 'Download speed: {{speed}}/s',
18
+ file_size: 'File size: {{size}}',
19
+ time_remaining: 'Time remaining: {{time}}',
20
+
21
+ // Error messages
22
+ error_code: 'Error code: {{code}}',
23
+ error_message: 'Error message: {{message}}',
24
+ retry_count: 'Retry attempt: {{count}}/{{max}}',
25
+
26
+ // Update info
27
+ version_info: 'Version {{version}} ({{build}})',
28
+ release_notes: 'Release notes: {{notes}}',
29
+ update_size: 'Update size: {{size}}MB',
30
+
31
+ // Alert messages
32
+ alert_title: 'Notice',
33
+ alert_update_ready: 'Download completed. Update now?',
34
+ alert_next_time: 'Later',
35
+ alert_update_now: 'Update Now',
36
+ alert_app_updated:
37
+ 'Your app version has been updated. Click update to download and install the new version',
38
+ alert_update_button: 'Update',
39
+ alert_cancel: 'Cancel',
40
+ alert_confirm: 'OK',
41
+ alert_info: 'Info',
42
+ alert_no_update_wait:
43
+ 'No update found, please wait 10s for the server to generate the patch package',
44
+
45
+ // Error messages
46
+ error_appkey_required: 'appKey is required',
47
+ error_update_check_failed: 'Update check failed',
48
+ error_cannot_connect_server:
49
+ 'Can not connect to update server. Please check your network.',
50
+ error_cannot_connect_backup:
51
+ 'Can not connect to update server: {{message}}. Trying backup endpoints.',
52
+ error_diff_failed: 'diff error: {{message}}',
53
+ error_pdiff_failed: 'pdiff error: {{message}}',
54
+ error_full_patch_failed: 'full patch error: {{message}}',
55
+ error_all_promises_rejected: 'All promises were rejected',
56
+ error_ping_failed: 'Ping failed',
57
+ error_ping_timeout: 'Ping timeout',
58
+ error_http_status: '{{status}} {{statusText}}',
59
+
60
+ // Development messages
61
+ dev_debug_disabled:
62
+ 'You are currently in the development environment and have not enabled debug mode. {{matter}} will not be performed. If you need to debug {{matter}} in the development environment, please set debug to true in the client.',
63
+ dev_log_prefix: 'react-native-update: ',
64
+ dev_web_not_supported:
65
+ 'react-native-update does not support the Web platform and will not perform any operations',
66
+
67
+ // More alert messages
68
+ alert_new_version_found:
69
+ 'New version {{name}} found. Download now?\n{{description}}',
70
+ };
@@ -0,0 +1,67 @@
1
+ export default {
2
+ // Common messages
3
+ checking_update: '正在检查更新...',
4
+ downloading_update: '正在下载更新包...',
5
+ installing_update: '正在安装更新...',
6
+ update_available: '发现新版本',
7
+ update_downloaded: '更新包下载完成',
8
+ update_installed: '更新安装完成',
9
+ no_update_available: '已是最新版本',
10
+ update_failed: '更新失败',
11
+ network_error: '网络连接错误',
12
+ download_failed: '下载失败',
13
+ install_failed: '安装失败',
14
+
15
+ // Progress messages with interpolation
16
+ download_progress: '下载进度: {{progress}}%',
17
+ download_speed: '下载速度: {{speed}}/s',
18
+ file_size: '文件大小: {{size}}',
19
+ time_remaining: '剩余时间: {{time}}',
20
+
21
+ // Error messages
22
+ error_code: '错误代码: {{code}}',
23
+ error_message: '错误信息: {{message}}',
24
+ retry_count: '重试次数: {{count}}/{{max}}',
25
+
26
+ // Update info
27
+ version_info: '版本 {{version}} ({{build}})',
28
+ release_notes: '更新说明: {{notes}}',
29
+ update_size: '更新包大小: {{size}}MB',
30
+
31
+ // Alert messages
32
+ alert_title: '提示',
33
+ alert_update_ready: '下载完毕,是否立即更新?',
34
+ alert_next_time: '下次再说',
35
+ alert_update_now: '立即更新',
36
+ alert_app_updated: '您的应用版本已更新,点击更新下载安装新版本',
37
+ alert_update_button: '更新',
38
+ alert_cancel: '取消',
39
+ alert_confirm: '确定',
40
+ alert_info: '信息',
41
+ alert_no_update_wait: '未发现更新,请等待10秒让服务器生成补丁包',
42
+
43
+ // Error messages
44
+ error_appkey_required: '需要提供 appKey',
45
+ error_update_check_failed: '更新检查失败',
46
+ error_cannot_connect_server: '无法连接到更新服务器。请检查网络连接。',
47
+ error_cannot_connect_backup:
48
+ '无法连接到更新服务器: {{message}}。正在尝试备用端点。',
49
+ error_diff_failed: 'diff 错误: {{message}}',
50
+ error_pdiff_failed: 'pdiff 错误: {{message}}',
51
+ error_full_patch_failed: '完整补丁错误: {{message}}',
52
+ error_all_promises_rejected: '所有请求都被拒绝',
53
+ error_ping_failed: 'Ping 失败',
54
+ error_ping_timeout: 'Ping 超时',
55
+ error_http_status: '{{status}} {{statusText}}',
56
+
57
+ // Development messages
58
+ dev_debug_disabled:
59
+ '您当前处于开发环境且未启用调试模式。{{matter}} 将不会执行。如需在开发环境中调试 {{matter}},请在客户端中将 debug 设为 true。',
60
+ dev_log_prefix: 'react-native-update: ',
61
+ dev_web_not_supported:
62
+ 'react-native-update 不支持 Web 平台,不会执行任何操作',
63
+
64
+ // More alert messages
65
+ alert_new_version_found:
66
+ '检查到新的版本{{name}},是否下载?\n{{description}}',
67
+ };
package/src/provider.tsx CHANGED
@@ -14,7 +14,12 @@ import {
14
14
  } from 'react-native';
15
15
  import { Pushy, Cresc, sharedState } from './client';
16
16
  import { currentVersion, packageVersion, getCurrentVersionInfo } from './core';
17
- import { CheckResult, ProgressData, UpdateTestPayload } from './type';
17
+ import {
18
+ CheckResult,
19
+ MixedCheckResult,
20
+ ProgressData,
21
+ UpdateTestPayload,
22
+ } from './type';
18
23
  import { UpdateContext } from './context';
19
24
  import { URL } from 'react-native-url-polyfill';
20
25
  import { isInRollout } from './isInRollout';
@@ -115,16 +120,16 @@ export const UpdateProvider = ({
115
120
  client.switchVersionLater(hash);
116
121
  return true;
117
122
  }
118
- alertUpdate('提示', '下载完毕,是否立即更新?', [
123
+ alertUpdate(client.t('alert_title'), client.t('alert_update_ready'), [
119
124
  {
120
- text: '下次再说',
125
+ text: client.t('alert_next_time'),
121
126
  style: 'cancel',
122
127
  onPress: () => {
123
128
  client.switchVersionLater(hash);
124
129
  },
125
130
  },
126
131
  {
127
- text: '立即更新',
132
+ text: client.t('alert_update_now'),
128
133
  style: 'default',
129
134
  onPress: () => {
130
135
  client.switchVersion(hash);
@@ -134,7 +139,7 @@ export const UpdateProvider = ({
134
139
  return true;
135
140
  } catch (e: any) {
136
141
  setLastError(e);
137
- alertError('更新失败', e.message);
142
+ alertError(client.t('update_failed'), e.message);
138
143
  throwErrorIfEnabled(e);
139
144
  return false;
140
145
  }
@@ -158,84 +163,102 @@ export const UpdateProvider = ({
158
163
  return;
159
164
  }
160
165
  lastChecking.current = now;
161
- let info: CheckResult;
166
+ let rootInfo: MixedCheckResult | undefined;
162
167
  try {
163
- info = await client.checkUpdate(extra);
168
+ rootInfo = await client.checkUpdate(extra);
164
169
  } catch (e: any) {
165
170
  setLastError(e);
166
- alertError('更新检查失败', e.message);
171
+ alertError(client.t('error_update_check_failed'), e.message);
167
172
  throwErrorIfEnabled(e);
168
173
  return;
169
174
  }
170
- if (!info) {
175
+ if (!rootInfo) {
171
176
  return;
172
177
  }
173
- const rollout = info.config?.rollout?.[packageVersion];
174
- if (info.update && rollout) {
175
- if (!isInRollout(rollout)) {
176
- log(`not in ${rollout}% rollout, ignored`);
177
- return;
178
- }
179
- log(`in ${rollout}% rollout, continue`);
180
- }
181
- info.description = info.description ?? '';
182
- updateInfoRef.current = info;
183
- setUpdateInfo(info);
184
- if (info.expired) {
185
- if (
186
- options.onPackageExpired &&
187
- (await options.onPackageExpired(info)) === false
188
- ) {
189
- log('onPackageExpired returned false, skipping');
190
- return;
178
+ const versions = rootInfo.versions || [rootInfo as CheckResult];
179
+ delete rootInfo.versions;
180
+ for (const versionInfo of versions) {
181
+ const info: CheckResult = {
182
+ ...versionInfo,
183
+ ...rootInfo,
184
+ };
185
+ const rollout = info.config?.rollout?.[packageVersion];
186
+ if (info.update && rollout) {
187
+ if (!isInRollout(rollout)) {
188
+ log(`${info.name} not in ${rollout}% rollout, ignored`);
189
+ continue;
190
+ }
191
+ log(`${info.name} in ${rollout}% rollout, continue`);
191
192
  }
192
- const { downloadUrl } = info;
193
- if (downloadUrl && sharedState.apkStatus === null) {
194
- if (options.updateStrategy === 'silentAndNow') {
195
- if (Platform.OS === 'android' && downloadUrl.endsWith('.apk')) {
196
- downloadAndInstallApk(downloadUrl);
197
- } else {
198
- Linking.openURL(downloadUrl);
193
+ info.description = info.description ?? '';
194
+ updateInfoRef.current = info;
195
+ setUpdateInfo(info);
196
+ if (info.expired) {
197
+ if (
198
+ options.onPackageExpired &&
199
+ (await options.onPackageExpired(info)) === false
200
+ ) {
201
+ log('onPackageExpired returned false, skipping');
202
+ return;
203
+ }
204
+ const { downloadUrl } = info;
205
+ if (downloadUrl && sharedState.apkStatus === null) {
206
+ if (options.updateStrategy === 'silentAndNow') {
207
+ if (Platform.OS === 'android' && downloadUrl.endsWith('.apk')) {
208
+ downloadAndInstallApk(downloadUrl);
209
+ } else {
210
+ Linking.openURL(downloadUrl);
211
+ }
212
+ return info;
199
213
  }
214
+ alertUpdate(
215
+ client.t('alert_title'),
216
+ client.t('alert_app_updated'),
217
+ [
218
+ {
219
+ text: client.t('alert_update_button'),
220
+ onPress: () => {
221
+ if (
222
+ Platform.OS === 'android' &&
223
+ downloadUrl.endsWith('.apk')
224
+ ) {
225
+ downloadAndInstallApk(downloadUrl);
226
+ } else {
227
+ Linking.openURL(downloadUrl);
228
+ }
229
+ },
230
+ },
231
+ ],
232
+ );
233
+ }
234
+ } else if (info.update) {
235
+ if (
236
+ options.updateStrategy === 'silentAndNow' ||
237
+ options.updateStrategy === 'silentAndLater'
238
+ ) {
239
+ downloadUpdate(info);
200
240
  return info;
201
241
  }
202
- alertUpdate('提示', '您的应用版本已更新,点击更新下载安装新版本', [
203
- {
204
- text: '更新',
205
- onPress: () => {
206
- if (Platform.OS === 'android' && downloadUrl.endsWith('.apk')) {
207
- downloadAndInstallApk(downloadUrl);
208
- } else {
209
- Linking.openURL(downloadUrl);
210
- }
242
+ alertUpdate(
243
+ client.t('alert_title'),
244
+ client.t('alert_new_version_found', {
245
+ name: info.name,
246
+ description: info.description,
247
+ }),
248
+ [
249
+ { text: client.t('alert_cancel'), style: 'cancel' },
250
+ {
251
+ text: client.t('alert_confirm'),
252
+ style: 'default',
253
+ onPress: () => {
254
+ downloadUpdate();
255
+ },
211
256
  },
212
- },
213
- ]);
257
+ ],
258
+ );
214
259
  }
215
- } else if (info.update) {
216
- if (
217
- options.updateStrategy === 'silentAndNow' ||
218
- options.updateStrategy === 'silentAndLater'
219
- ) {
220
- downloadUpdate(info);
221
- return info;
222
- }
223
- alertUpdate(
224
- '提示',
225
- '检查到新的版本' + info.name + ',是否下载?\n' + info.description,
226
- [
227
- { text: '取消', style: 'cancel' },
228
- {
229
- text: '确定',
230
- style: 'default',
231
- onPress: () => {
232
- downloadUpdate();
233
- },
234
- },
235
- ],
236
- );
260
+ return info;
237
261
  }
238
- return info;
239
262
  },
240
263
  [
241
264
  client,
@@ -297,8 +320,8 @@ export const UpdateProvider = ({
297
320
  checkUpdate({ extra: { toHash: payload.data } }).then(() => {
298
321
  if (updateInfoRef.current && updateInfoRef.current.upToDate) {
299
322
  Alert.alert(
300
- 'Info',
301
- 'No update found, please wait 10s for the server to generate the patch package',
323
+ client.t('alert_info'),
324
+ client.t('alert_no_update_wait'),
302
325
  );
303
326
  }
304
327
  options.logger = logger;
@@ -308,7 +331,7 @@ export const UpdateProvider = ({
308
331
  }
309
332
  return false;
310
333
  },
311
- [checkUpdate, options],
334
+ [checkUpdate, options, client],
312
335
  );
313
336
 
314
337
  const parseTestQrCode = useCallback(
package/src/type.ts CHANGED
@@ -1,14 +1,10 @@
1
- export interface CheckResult {
2
- upToDate?: true;
3
- expired?: true;
4
- downloadUrl?: string;
5
- update?: true;
6
- name?: string; // version name
7
- hash?: string;
8
- description?: string;
9
- metaInfo?: string;
10
- config?: {
11
- rollout?: {
1
+ export interface VersionInfo {
2
+ name: string;
3
+ hash: string;
4
+ description: string;
5
+ metaInfo: string;
6
+ config: {
7
+ rollout: {
12
8
  [packageVersion: string]: number;
13
9
  };
14
10
  [key: string]: any;
@@ -16,11 +12,27 @@ export interface CheckResult {
16
12
  pdiff?: string;
17
13
  diff?: string;
18
14
  full?: string;
19
- paths?: string[];
15
+ }
16
+
17
+ interface RootResult {
18
+ upToDate?: true;
19
+ expired?: true;
20
+ downloadUrl?: string;
21
+ update?: true;
20
22
  paused?: 'app' | 'package';
21
23
  message?: string;
24
+ paths?: string[];
22
25
  }
23
26
 
27
+ export type CheckResult = RootResult & VersionInfo;
28
+
29
+ export type CheckResultV2 = RootResult & {
30
+ versions?: VersionInfo[];
31
+ };
32
+
33
+ export type MixedCheckResult = CheckResult | CheckResultV2;
34
+
35
+
24
36
  export interface ProgressData {
25
37
  hash: string;
26
38
  received: number;
package/src/utils.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { Platform } from 'react-native';
2
+ import i18n from './i18n';
2
3
 
3
4
  export function log(...args: any[]) {
4
- console.log('react-native-update: ', ...args);
5
+ console.log(i18n.t('dev_log_prefix'), ...args);
5
6
  }
6
7
 
7
8
  export function promiseAny<T>(promises: Promise<T>[]) {
@@ -14,7 +15,7 @@ export function promiseAny<T>(promises: Promise<T>[]) {
14
15
  .catch(() => {
15
16
  count++;
16
17
  if (count === promises.length) {
17
- reject(new Error('All promises were rejected'));
18
+ reject(new Error(i18n.t('error_all_promises_rejected')));
18
19
  }
19
20
  });
20
21
  });
@@ -49,7 +50,7 @@ const ping =
49
50
  return finalUrl;
50
51
  }
51
52
  log('ping failed', url, status, statusText);
52
- throw new Error('Ping failed');
53
+ throw new Error(i18n.t('error_ping_failed'));
53
54
  })
54
55
  .catch(e => {
55
56
  pingFinished = true;
@@ -58,7 +59,7 @@ const ping =
58
59
  }),
59
60
  new Promise((_, reject) =>
60
61
  setTimeout(() => {
61
- reject(new Error('Ping timeout'));
62
+ reject(new Error(i18n.t('error_ping_timeout')));
62
63
  if (!pingFinished) {
63
64
  log('ping timeout', url);
64
65
  }
@@ -91,9 +92,7 @@ export const testUrls = async (urls?: string[]) => {
91
92
 
92
93
  export const assertWeb = () => {
93
94
  if (Platform.OS === 'web') {
94
- console.warn(
95
- 'react-native-update does not support the Web platform and will not perform any operations',
96
- );
95
+ console.warn(i18n.t('dev_web_not_supported'));
97
96
  return false;
98
97
  }
99
98
  return true;
@@ -115,7 +114,12 @@ export const enhancedFetch = async (
115
114
  if (r.ok) {
116
115
  return r;
117
116
  }
118
- throw new Error(`${r.status} ${r.statusText}`);
117
+ throw new Error(
118
+ i18n.t('error_http_status', {
119
+ status: r.status,
120
+ statusText: r.statusText,
121
+ }),
122
+ );
119
123
  })
120
124
  .catch(e => {
121
125
  log('fetch error', url, e);