@appsonair.ir/react-native 1.0.11 → 1.0.12
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.
- package/lib/sync.js +18 -4
- package/lib/ui/OTAUpdateModal.js +129 -27
- package/package.json +1 -1
package/lib/sync.js
CHANGED
|
@@ -812,15 +812,29 @@ async function downloadBundle(downloadUrl, deploymentKey, label, expectedSize, o
|
|
|
812
812
|
'X-Deployment-Key': deploymentKey,
|
|
813
813
|
...(deviceAbi ? { 'X-Device-Abi': deviceAbi } : {}),
|
|
814
814
|
});
|
|
815
|
+
let lastReportedProgress = 0;
|
|
816
|
+
let lastReportedReceived = 0;
|
|
817
|
+
let lastReportedTotal = totalBytes > 0 ? totalBytes : 0;
|
|
815
818
|
const reportDownloadProgress = (received, total) => {
|
|
816
819
|
if (!onProgress)
|
|
817
820
|
return;
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
|
|
821
|
+
const safeReceived = Math.max(Number(received) || 0, lastReportedReceived);
|
|
822
|
+
let resolvedTotal = total > 0 ? total : totalBytes;
|
|
823
|
+
if (!(resolvedTotal > 0)) {
|
|
824
|
+
resolvedTotal = lastReportedTotal;
|
|
825
|
+
}
|
|
826
|
+
if (resolvedTotal > lastReportedTotal) {
|
|
827
|
+
lastReportedTotal = resolvedTotal;
|
|
828
|
+
}
|
|
829
|
+
// Keep bytes monotonic even when total is still unknown.
|
|
830
|
+
lastReportedReceived = safeReceived;
|
|
831
|
+
if (!(resolvedTotal > 0)) {
|
|
832
|
+
onProgress(lastReportedProgress, safeReceived, 0);
|
|
821
833
|
return;
|
|
822
834
|
}
|
|
823
|
-
|
|
835
|
+
const nextProgress = Math.min(Math.max(safeReceived / resolvedTotal, lastReportedProgress), 0.99);
|
|
836
|
+
lastReportedProgress = nextProgress;
|
|
837
|
+
onProgress(nextProgress, safeReceived, resolvedTotal);
|
|
824
838
|
};
|
|
825
839
|
let pollTimer = null;
|
|
826
840
|
if (onProgress && totalBytes > 0) {
|
package/lib/ui/OTAUpdateModal.js
CHANGED
|
@@ -38,6 +38,25 @@ const react_1 = __importStar(require("react"));
|
|
|
38
38
|
const react_native_1 = require("react-native");
|
|
39
39
|
const sync_1 = require("../sync");
|
|
40
40
|
const types_1 = require("../types");
|
|
41
|
+
/** Never returns "—" so download size labels don't flicker between ticks. */
|
|
42
|
+
function stableFormatBytes(bytes) {
|
|
43
|
+
if (bytes == null || !Number.isFinite(bytes) || bytes < 0) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (bytes < 1024) {
|
|
47
|
+
return `${Math.round(bytes)} B`;
|
|
48
|
+
}
|
|
49
|
+
if (bytes < 1024 * 1024) {
|
|
50
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
51
|
+
}
|
|
52
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
53
|
+
}
|
|
54
|
+
function joinVersionParts(parts) {
|
|
55
|
+
const filtered = parts
|
|
56
|
+
.map((part) => (part == null ? '' : String(part).trim()))
|
|
57
|
+
.filter(Boolean);
|
|
58
|
+
return filtered.length > 0 ? filtered.join(' ') : null;
|
|
59
|
+
}
|
|
41
60
|
function OTAUpdateModal({ visible, status, progress, message, description, isMandatory, error, showActions, canSkip, updateType, appVersion, buildNumber, currentAppVersion, currentBuildNumber, currentLabel, label, totalBytes, downloadedBytes, title, fontFamily, messages, onUpdate, onSkip, onDismiss, }) {
|
|
42
61
|
const copy = (0, react_1.useMemo)(() => ({ ...sync_1.defaultMessages, ...messages }), [messages]);
|
|
43
62
|
const resolvedTitle = (0, react_1.useMemo)(() => {
|
|
@@ -59,13 +78,52 @@ function OTAUpdateModal({ visible, status, progress, message, description, isMan
|
|
|
59
78
|
...(customFontStyle ?? null),
|
|
60
79
|
writingDirection: 'rtl',
|
|
61
80
|
}), [customFontStyle]);
|
|
81
|
+
const isDownloading = status === types_1.SyncStatus.DOWNLOADING;
|
|
82
|
+
const stableProgressRef = (0, react_1.useRef)({ progress: 0, downloaded: 0, total: 0 });
|
|
83
|
+
(0, react_1.useEffect)(() => {
|
|
84
|
+
if (!isDownloading) {
|
|
85
|
+
stableProgressRef.current = { progress: 0, downloaded: 0, total: 0 };
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const nextProgress = Math.min(Math.max(Number(progress) || 0, 0), 1);
|
|
89
|
+
const nextDownloaded = Number(downloadedBytes);
|
|
90
|
+
const nextTotal = Number(totalBytes);
|
|
91
|
+
if (nextProgress >= stableProgressRef.current.progress) {
|
|
92
|
+
stableProgressRef.current.progress = nextProgress;
|
|
93
|
+
}
|
|
94
|
+
if (Number.isFinite(nextTotal) && nextTotal > stableProgressRef.current.total) {
|
|
95
|
+
stableProgressRef.current.total = nextTotal;
|
|
96
|
+
}
|
|
97
|
+
if (Number.isFinite(nextDownloaded) &&
|
|
98
|
+
nextDownloaded >= stableProgressRef.current.downloaded) {
|
|
99
|
+
stableProgressRef.current.downloaded = nextDownloaded;
|
|
100
|
+
}
|
|
101
|
+
}, [isDownloading, progress, downloadedBytes, totalBytes]);
|
|
62
102
|
if (!visible)
|
|
63
103
|
return null;
|
|
64
|
-
|
|
104
|
+
if (isDownloading) {
|
|
105
|
+
const nextProgress = Math.min(Math.max(Number(progress) || 0, 0), 1);
|
|
106
|
+
const nextDownloaded = Number(downloadedBytes);
|
|
107
|
+
const nextTotal = Number(totalBytes);
|
|
108
|
+
if (nextProgress >= stableProgressRef.current.progress) {
|
|
109
|
+
stableProgressRef.current.progress = nextProgress;
|
|
110
|
+
}
|
|
111
|
+
if (Number.isFinite(nextTotal) && nextTotal > stableProgressRef.current.total) {
|
|
112
|
+
stableProgressRef.current.total = nextTotal;
|
|
113
|
+
}
|
|
114
|
+
if (Number.isFinite(nextDownloaded) &&
|
|
115
|
+
nextDownloaded >= stableProgressRef.current.downloaded) {
|
|
116
|
+
stableProgressRef.current.downloaded = nextDownloaded;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const stableProgress = isDownloading
|
|
120
|
+
? stableProgressRef.current.progress
|
|
121
|
+
: Math.min(Math.max(Number(progress) || 0, 0), 1);
|
|
122
|
+
const progressPercent = Math.round(stableProgress * 100);
|
|
65
123
|
const isPrompt = status === types_1.SyncStatus.UPDATE_AVAILABLE;
|
|
66
124
|
const isDeferredDone = status === types_1.SyncStatus.UPDATE_INSTALLED;
|
|
67
125
|
const isError = status === types_1.SyncStatus.ERROR;
|
|
68
|
-
const showProgressBar =
|
|
126
|
+
const showProgressBar = isDownloading;
|
|
69
127
|
const showSpinner = status === types_1.SyncStatus.RESTARTING ||
|
|
70
128
|
status === types_1.SyncStatus.INSTALLING ||
|
|
71
129
|
(!showProgressBar && !isPrompt && !isDeferredDone && !isError);
|
|
@@ -74,22 +132,42 @@ function OTAUpdateModal({ visible, status, progress, message, description, isMan
|
|
|
74
132
|
: updateType === 'bundle'
|
|
75
133
|
? copy.versionBundle
|
|
76
134
|
: null;
|
|
135
|
+
// Show both version number and release label/name when available.
|
|
77
136
|
const currentVersionText = updateType === 'apk'
|
|
78
|
-
? [
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
137
|
+
? joinVersionParts([
|
|
138
|
+
currentAppVersion,
|
|
139
|
+
currentBuildNumber ? `(${currentBuildNumber})` : null,
|
|
140
|
+
])
|
|
141
|
+
: joinVersionParts([
|
|
142
|
+
currentAppVersion,
|
|
143
|
+
currentLabel && currentLabel !== currentAppVersion ? currentLabel : null,
|
|
144
|
+
]);
|
|
82
145
|
const nextVersionText = updateType === 'apk'
|
|
83
|
-
? [
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
146
|
+
? joinVersionParts([
|
|
147
|
+
appVersion,
|
|
148
|
+
buildNumber != null ? `(${buildNumber})` : null,
|
|
149
|
+
label && label !== appVersion ? label : null,
|
|
150
|
+
])
|
|
151
|
+
: joinVersionParts([
|
|
152
|
+
appVersion,
|
|
153
|
+
label && label !== appVersion ? label : null,
|
|
154
|
+
]);
|
|
87
155
|
const handleUpdate = () => (onUpdate ? onUpdate() : (0, sync_1.respondToUpdatePrompt)('update'));
|
|
88
156
|
const handleSkip = () => (onSkip ? onSkip() : (0, sync_1.respondToUpdatePrompt)('skip'));
|
|
89
157
|
const handleDismiss = () => onDismiss ? onDismiss() : (0, sync_1.respondToUpdatePrompt)('dismiss');
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
158
|
+
const stableDownloaded = showProgressBar
|
|
159
|
+
? stableProgressRef.current.downloaded
|
|
160
|
+
: downloadedBytes ?? 0;
|
|
161
|
+
const stableTotal = showProgressBar
|
|
162
|
+
? stableProgressRef.current.total
|
|
163
|
+
: totalBytes ?? 0;
|
|
164
|
+
const downloadedLabel = stableFormatBytes(stableDownloaded) || '0.0 MB';
|
|
165
|
+
const totalLabel = stableFormatBytes(stableTotal);
|
|
166
|
+
const sizeLabel = totalLabel
|
|
167
|
+
? `${downloadedLabel} / ${totalLabel}`
|
|
168
|
+
: downloadedLabel;
|
|
169
|
+
const showSizes = showProgressBar;
|
|
170
|
+
const showPromptSize = !showProgressBar && stableTotal > 0 && (isPrompt || isError);
|
|
93
171
|
return (<react_native_1.Modal visible transparent animationType="fade" statusBarTranslucent onRequestClose={() => {
|
|
94
172
|
if (canSkip)
|
|
95
173
|
handleSkip();
|
|
@@ -134,20 +212,25 @@ function OTAUpdateModal({ visible, status, progress, message, description, isMan
|
|
|
134
212
|
</react_native_1.ScrollView>
|
|
135
213
|
</react_native_1.View>) : null}
|
|
136
214
|
|
|
137
|
-
{showProgressBar ? (
|
|
215
|
+
{showProgressBar ? (<react_native_1.View style={styles.progressBlock}>
|
|
138
216
|
<react_native_1.View style={styles.progressTrack}>
|
|
139
|
-
<react_native_1.View style={[
|
|
217
|
+
<react_native_1.View style={[
|
|
218
|
+
styles.progressFill,
|
|
219
|
+
{ width: `${Math.max(progressPercent, 2)}%` },
|
|
220
|
+
]}/>
|
|
140
221
|
</react_native_1.View>
|
|
141
|
-
<react_native_1.
|
|
142
|
-
{
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
222
|
+
<react_native_1.View style={styles.progressMetaRow}>
|
|
223
|
+
<react_native_1.Text style={[styles.percent, !fontFamily && styles.percentBold]}>
|
|
224
|
+
{progressPercent}٪
|
|
225
|
+
</react_native_1.Text>
|
|
226
|
+
{showSizes ? (<react_native_1.Text style={styles.sizeText} numberOfLines={1}>
|
|
227
|
+
{sizeLabel}
|
|
228
|
+
</react_native_1.Text>) : null}
|
|
229
|
+
</react_native_1.View>
|
|
230
|
+
</react_native_1.View>) : null}
|
|
148
231
|
|
|
149
|
-
{
|
|
150
|
-
حجم دانلود: {(
|
|
232
|
+
{showPromptSize ? (<react_native_1.Text style={[styles.sizeTextCentered, textStyle]}>
|
|
233
|
+
حجم دانلود: {stableFormatBytes(stableTotal)}
|
|
151
234
|
</react_native_1.Text>) : null}
|
|
152
235
|
|
|
153
236
|
{showSpinner ? (<react_native_1.ActivityIndicator size="large" color="#2563eb" style={styles.spinner}/>) : null}
|
|
@@ -273,29 +356,48 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
273
356
|
textAlign: 'center',
|
|
274
357
|
lineHeight: 20,
|
|
275
358
|
},
|
|
359
|
+
progressBlock: {
|
|
360
|
+
width: '100%',
|
|
361
|
+
marginTop: 8,
|
|
362
|
+
},
|
|
276
363
|
progressTrack: {
|
|
277
364
|
width: '100%',
|
|
278
365
|
height: 8,
|
|
279
366
|
backgroundColor: '#e5e7eb',
|
|
280
367
|
borderRadius: 999,
|
|
281
368
|
overflow: 'hidden',
|
|
282
|
-
marginTop: 8,
|
|
283
369
|
},
|
|
284
370
|
progressFill: {
|
|
285
371
|
height: '100%',
|
|
286
372
|
backgroundColor: '#2563eb',
|
|
287
373
|
borderRadius: 999,
|
|
288
374
|
},
|
|
289
|
-
|
|
375
|
+
progressMetaRow: {
|
|
290
376
|
marginTop: 8,
|
|
377
|
+
width: '100%',
|
|
378
|
+
flexDirection: 'row',
|
|
379
|
+
alignItems: 'center',
|
|
380
|
+
justifyContent: 'space-between',
|
|
381
|
+
direction: 'ltr',
|
|
382
|
+
},
|
|
383
|
+
percent: {
|
|
291
384
|
fontSize: 13,
|
|
292
385
|
color: '#6b7280',
|
|
293
|
-
textAlign: '
|
|
386
|
+
textAlign: 'left',
|
|
387
|
+
writingDirection: 'ltr',
|
|
294
388
|
},
|
|
295
389
|
percentBold: {
|
|
296
390
|
fontWeight: '600',
|
|
297
391
|
},
|
|
298
392
|
sizeText: {
|
|
393
|
+
fontSize: 12,
|
|
394
|
+
color: '#9ca3af',
|
|
395
|
+
textAlign: 'right',
|
|
396
|
+
writingDirection: 'ltr',
|
|
397
|
+
flexShrink: 1,
|
|
398
|
+
marginLeft: 8,
|
|
399
|
+
},
|
|
400
|
+
sizeTextCentered: {
|
|
299
401
|
marginTop: 4,
|
|
300
402
|
fontSize: 12,
|
|
301
403
|
color: '#9ca3af',
|