@swiftpatch/react-native 2.0.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.
Files changed (182) hide show
  1. package/README.md +430 -0
  2. package/android/build.gradle +105 -0
  3. package/android/src/main/AndroidManifest.xml +6 -0
  4. package/android/src/main/java/com/swiftpatch/BundleManager.kt +107 -0
  5. package/android/src/main/java/com/swiftpatch/CrashDetector.kt +79 -0
  6. package/android/src/main/java/com/swiftpatch/CryptoVerifier.kt +69 -0
  7. package/android/src/main/java/com/swiftpatch/DownloadManager.kt +120 -0
  8. package/android/src/main/java/com/swiftpatch/EventQueue.kt +86 -0
  9. package/android/src/main/java/com/swiftpatch/FileUtils.kt +60 -0
  10. package/android/src/main/java/com/swiftpatch/PatchApplier.kt +60 -0
  11. package/android/src/main/java/com/swiftpatch/SignalCrashHandler.kt +84 -0
  12. package/android/src/main/java/com/swiftpatch/SlotManager.kt +299 -0
  13. package/android/src/main/java/com/swiftpatch/SwiftPatchModule.kt +630 -0
  14. package/android/src/main/java/com/swiftpatch/SwiftPatchPackage.kt +21 -0
  15. package/android/src/main/jni/CMakeLists.txt +12 -0
  16. package/android/src/main/jni/bspatch.c +188 -0
  17. package/android/src/main/jni/bspatch.h +57 -0
  18. package/android/src/main/jni/bspatch_jni.c +28 -0
  19. package/ios/Libraries/bspatch/bspatch.c +188 -0
  20. package/ios/Libraries/bspatch/bspatch.h +50 -0
  21. package/ios/Libraries/bspatch/module.modulemap +4 -0
  22. package/ios/SwiftPatch/BundleManager.swift +113 -0
  23. package/ios/SwiftPatch/CrashDetector.swift +71 -0
  24. package/ios/SwiftPatch/CryptoVerifier.swift +70 -0
  25. package/ios/SwiftPatch/DownloadManager.swift +125 -0
  26. package/ios/SwiftPatch/EventQueue.swift +116 -0
  27. package/ios/SwiftPatch/FileUtils.swift +38 -0
  28. package/ios/SwiftPatch/PatchApplier.swift +41 -0
  29. package/ios/SwiftPatch/SignalCrashHandler.swift +129 -0
  30. package/ios/SwiftPatch/SlotManager.swift +360 -0
  31. package/ios/SwiftPatch/SwiftPatchModule.m +56 -0
  32. package/ios/SwiftPatch/SwiftPatchModule.swift +621 -0
  33. package/lib/commonjs/SwiftPatchCore.js +140 -0
  34. package/lib/commonjs/SwiftPatchCore.js.map +1 -0
  35. package/lib/commonjs/SwiftPatchProvider.js +617 -0
  36. package/lib/commonjs/SwiftPatchProvider.js.map +1 -0
  37. package/lib/commonjs/constants.js +50 -0
  38. package/lib/commonjs/constants.js.map +1 -0
  39. package/lib/commonjs/core/Downloader.js +63 -0
  40. package/lib/commonjs/core/Downloader.js.map +1 -0
  41. package/lib/commonjs/core/Installer.js +46 -0
  42. package/lib/commonjs/core/Installer.js.map +1 -0
  43. package/lib/commonjs/core/Rollback.js +36 -0
  44. package/lib/commonjs/core/Rollback.js.map +1 -0
  45. package/lib/commonjs/core/UpdateChecker.js +57 -0
  46. package/lib/commonjs/core/UpdateChecker.js.map +1 -0
  47. package/lib/commonjs/core/Verifier.js +82 -0
  48. package/lib/commonjs/core/Verifier.js.map +1 -0
  49. package/lib/commonjs/core/index.js +41 -0
  50. package/lib/commonjs/core/index.js.map +1 -0
  51. package/lib/commonjs/index.js +154 -0
  52. package/lib/commonjs/index.js.map +1 -0
  53. package/lib/commonjs/modal/SwiftPatchModal.js +667 -0
  54. package/lib/commonjs/modal/SwiftPatchModal.js.map +1 -0
  55. package/lib/commonjs/modal/useSwiftPatchModal.js +26 -0
  56. package/lib/commonjs/modal/useSwiftPatchModal.js.map +1 -0
  57. package/lib/commonjs/native/NativeSwiftPatch.js +85 -0
  58. package/lib/commonjs/native/NativeSwiftPatch.js.map +1 -0
  59. package/lib/commonjs/native/NativeSwiftPatchSpec.js +15 -0
  60. package/lib/commonjs/native/NativeSwiftPatchSpec.js.map +1 -0
  61. package/lib/commonjs/package.json +1 -0
  62. package/lib/commonjs/types.js +126 -0
  63. package/lib/commonjs/types.js.map +1 -0
  64. package/lib/commonjs/useSwiftPatch.js +31 -0
  65. package/lib/commonjs/useSwiftPatch.js.map +1 -0
  66. package/lib/commonjs/utils/api.js +206 -0
  67. package/lib/commonjs/utils/api.js.map +1 -0
  68. package/lib/commonjs/utils/device.js +23 -0
  69. package/lib/commonjs/utils/device.js.map +1 -0
  70. package/lib/commonjs/utils/logger.js +30 -0
  71. package/lib/commonjs/utils/logger.js.map +1 -0
  72. package/lib/commonjs/utils/storage.js +31 -0
  73. package/lib/commonjs/utils/storage.js.map +1 -0
  74. package/lib/commonjs/withSwiftPatch.js +42 -0
  75. package/lib/commonjs/withSwiftPatch.js.map +1 -0
  76. package/lib/module/SwiftPatchCore.js +135 -0
  77. package/lib/module/SwiftPatchCore.js.map +1 -0
  78. package/lib/module/SwiftPatchProvider.js +611 -0
  79. package/lib/module/SwiftPatchProvider.js.map +1 -0
  80. package/lib/module/constants.js +46 -0
  81. package/lib/module/constants.js.map +1 -0
  82. package/lib/module/core/Downloader.js +57 -0
  83. package/lib/module/core/Downloader.js.map +1 -0
  84. package/lib/module/core/Installer.js +41 -0
  85. package/lib/module/core/Installer.js.map +1 -0
  86. package/lib/module/core/Rollback.js +31 -0
  87. package/lib/module/core/Rollback.js.map +1 -0
  88. package/lib/module/core/UpdateChecker.js +51 -0
  89. package/lib/module/core/UpdateChecker.js.map +1 -0
  90. package/lib/module/core/Verifier.js +76 -0
  91. package/lib/module/core/Verifier.js.map +1 -0
  92. package/lib/module/core/index.js +8 -0
  93. package/lib/module/core/index.js.map +1 -0
  94. package/lib/module/index.js +34 -0
  95. package/lib/module/index.js.map +1 -0
  96. package/lib/module/modal/SwiftPatchModal.js +661 -0
  97. package/lib/module/modal/SwiftPatchModal.js.map +1 -0
  98. package/lib/module/modal/useSwiftPatchModal.js +22 -0
  99. package/lib/module/modal/useSwiftPatchModal.js.map +1 -0
  100. package/lib/module/native/NativeSwiftPatch.js +78 -0
  101. package/lib/module/native/NativeSwiftPatch.js.map +1 -0
  102. package/lib/module/native/NativeSwiftPatchSpec.js +12 -0
  103. package/lib/module/native/NativeSwiftPatchSpec.js.map +1 -0
  104. package/lib/module/types.js +139 -0
  105. package/lib/module/types.js.map +1 -0
  106. package/lib/module/useSwiftPatch.js +26 -0
  107. package/lib/module/useSwiftPatch.js.map +1 -0
  108. package/lib/module/utils/api.js +197 -0
  109. package/lib/module/utils/api.js.map +1 -0
  110. package/lib/module/utils/device.js +18 -0
  111. package/lib/module/utils/device.js.map +1 -0
  112. package/lib/module/utils/logger.js +26 -0
  113. package/lib/module/utils/logger.js.map +1 -0
  114. package/lib/module/utils/storage.js +24 -0
  115. package/lib/module/utils/storage.js.map +1 -0
  116. package/lib/module/withSwiftPatch.js +37 -0
  117. package/lib/module/withSwiftPatch.js.map +1 -0
  118. package/lib/typescript/SwiftPatchCore.d.ts +64 -0
  119. package/lib/typescript/SwiftPatchCore.d.ts.map +1 -0
  120. package/lib/typescript/SwiftPatchProvider.d.ts +22 -0
  121. package/lib/typescript/SwiftPatchProvider.d.ts.map +1 -0
  122. package/lib/typescript/constants.d.ts +33 -0
  123. package/lib/typescript/constants.d.ts.map +1 -0
  124. package/lib/typescript/core/Downloader.d.ts +34 -0
  125. package/lib/typescript/core/Downloader.d.ts.map +1 -0
  126. package/lib/typescript/core/Installer.d.ts +25 -0
  127. package/lib/typescript/core/Installer.d.ts.map +1 -0
  128. package/lib/typescript/core/Rollback.d.ts +18 -0
  129. package/lib/typescript/core/Rollback.d.ts.map +1 -0
  130. package/lib/typescript/core/UpdateChecker.d.ts +27 -0
  131. package/lib/typescript/core/UpdateChecker.d.ts.map +1 -0
  132. package/lib/typescript/core/Verifier.d.ts +31 -0
  133. package/lib/typescript/core/Verifier.d.ts.map +1 -0
  134. package/lib/typescript/core/index.d.ts +8 -0
  135. package/lib/typescript/core/index.d.ts.map +1 -0
  136. package/lib/typescript/index.d.ts +13 -0
  137. package/lib/typescript/index.d.ts.map +1 -0
  138. package/lib/typescript/modal/SwiftPatchModal.d.ts +11 -0
  139. package/lib/typescript/modal/SwiftPatchModal.d.ts.map +1 -0
  140. package/lib/typescript/modal/useSwiftPatchModal.d.ts +7 -0
  141. package/lib/typescript/modal/useSwiftPatchModal.d.ts.map +1 -0
  142. package/lib/typescript/native/NativeSwiftPatch.d.ts +61 -0
  143. package/lib/typescript/native/NativeSwiftPatch.d.ts.map +1 -0
  144. package/lib/typescript/native/NativeSwiftPatchSpec.d.ts +67 -0
  145. package/lib/typescript/native/NativeSwiftPatchSpec.d.ts.map +1 -0
  146. package/lib/typescript/types.d.ts +266 -0
  147. package/lib/typescript/types.d.ts.map +1 -0
  148. package/lib/typescript/useSwiftPatch.d.ts +12 -0
  149. package/lib/typescript/useSwiftPatch.d.ts.map +1 -0
  150. package/lib/typescript/utils/api.d.ts +87 -0
  151. package/lib/typescript/utils/api.d.ts.map +1 -0
  152. package/lib/typescript/utils/device.d.ts +9 -0
  153. package/lib/typescript/utils/device.d.ts.map +1 -0
  154. package/lib/typescript/utils/logger.d.ts +8 -0
  155. package/lib/typescript/utils/logger.d.ts.map +1 -0
  156. package/lib/typescript/utils/storage.d.ts +14 -0
  157. package/lib/typescript/utils/storage.d.ts.map +1 -0
  158. package/lib/typescript/withSwiftPatch.d.ts +12 -0
  159. package/lib/typescript/withSwiftPatch.d.ts.map +1 -0
  160. package/package.json +99 -0
  161. package/react-native-swiftpatch.podspec +50 -0
  162. package/src/SwiftPatchCore.ts +148 -0
  163. package/src/SwiftPatchProvider.tsx +514 -0
  164. package/src/constants.ts +49 -0
  165. package/src/core/Downloader.ts +74 -0
  166. package/src/core/Installer.ts +38 -0
  167. package/src/core/Rollback.ts +28 -0
  168. package/src/core/UpdateChecker.ts +70 -0
  169. package/src/core/Verifier.ts +92 -0
  170. package/src/core/index.ts +11 -0
  171. package/src/index.ts +64 -0
  172. package/src/modal/SwiftPatchModal.tsx +657 -0
  173. package/src/modal/useSwiftPatchModal.ts +24 -0
  174. package/src/native/NativeSwiftPatch.ts +205 -0
  175. package/src/native/NativeSwiftPatchSpec.ts +139 -0
  176. package/src/types.ts +336 -0
  177. package/src/useSwiftPatch.ts +29 -0
  178. package/src/utils/api.ts +244 -0
  179. package/src/utils/device.ts +15 -0
  180. package/src/utils/logger.ts +29 -0
  181. package/src/utils/storage.ts +23 -0
  182. package/src/withSwiftPatch.tsx +41 -0
@@ -0,0 +1,661 @@
1
+ "use strict";
2
+
3
+ import React, { useState, useEffect, useCallback } from 'react';
4
+ import { Modal, View, Text, TextInput, TouchableOpacity, FlatList, ActivityIndicator, StyleSheet, SafeAreaView, ScrollView } from 'react-native';
5
+ import { useSwiftPatch } from '../useSwiftPatch';
6
+ import { verifySDKPin, listBuckets, listBundles } from '../utils/api';
7
+ import NativeSwiftPatch from '../native/NativeSwiftPatch';
8
+ import { EnvironmentMode } from '../types';
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ export function SwiftPatchModal({
11
+ visible,
12
+ onClose,
13
+ serverUrl,
14
+ deploymentKey,
15
+ customHeaders = {}
16
+ }) {
17
+ const ctx = useSwiftPatch();
18
+ const [screen, setScreen] = useState('login');
19
+ const [sdkToken, setSdkToken] = useState(null);
20
+ const [pin, setPin] = useState('');
21
+ const [loginError, setLoginError] = useState('');
22
+ const [isLoading, setIsLoading] = useState(false);
23
+ const [buckets, setBuckets] = useState([]);
24
+ const [bundles, setBundles] = useState([]);
25
+ const [selectedBucket, setSelectedBucket] = useState(null);
26
+ const [hasMoreBundles, setHasMoreBundles] = useState(false);
27
+ const [pageOffset, setPageOffset] = useState(0);
28
+ const [downloadingHash, setDownloadingHash] = useState(null);
29
+ const [downloadProgress, setDownloadProgress] = useState(0);
30
+ const [metadata, setMetadata] = useState(null);
31
+
32
+ // Fetch metadata on open
33
+ useEffect(() => {
34
+ if (visible) {
35
+ NativeSwiftPatch.getSlotMetadata().then(setMetadata).catch(() => {});
36
+ }
37
+ }, [visible]);
38
+
39
+ // Login handler
40
+ const handleLogin = useCallback(async () => {
41
+ if (pin.length < 4) {
42
+ setLoginError('PIN must be at least 4 characters');
43
+ return;
44
+ }
45
+ setIsLoading(true);
46
+ setLoginError('');
47
+ const result = await verifySDKPin({
48
+ serverUrl,
49
+ deploymentKey,
50
+ pin,
51
+ customHeaders
52
+ });
53
+ setIsLoading(false);
54
+ if (result) {
55
+ setSdkToken(result.sdkToken);
56
+ setScreen('dashboard');
57
+ // Fetch buckets
58
+ const bucketsData = await listBuckets({
59
+ serverUrl,
60
+ sdkToken: result.sdkToken,
61
+ deploymentKey,
62
+ customHeaders
63
+ });
64
+ setBuckets(bucketsData);
65
+ } else {
66
+ setLoginError('Invalid PIN. Please try again.');
67
+ }
68
+ }, [pin, serverUrl, deploymentKey, customHeaders]);
69
+
70
+ // Load bundles for a bucket
71
+ const loadBundles = useCallback(async (bucket, offset = 0) => {
72
+ if (!sdkToken) return;
73
+ setIsLoading(true);
74
+ setSelectedBucket(bucket);
75
+ setPageOffset(offset);
76
+ const result = await listBundles({
77
+ serverUrl,
78
+ sdkToken,
79
+ deploymentKey,
80
+ channelId: bucket.id,
81
+ pageOffset: offset,
82
+ customHeaders
83
+ });
84
+ if (offset === 0) {
85
+ setBundles(result.bundles);
86
+ } else {
87
+ setBundles(prev => [...prev, ...result.bundles]);
88
+ }
89
+ setHasMoreBundles(result.hasMore);
90
+ setIsLoading(false);
91
+ setScreen('bundles');
92
+ }, [sdkToken, serverUrl, deploymentKey, customHeaders]);
93
+
94
+ // Download a specific bundle (stage)
95
+ const handleDownloadBundle = useCallback(async bundle => {
96
+ try {
97
+ setDownloadingHash(bundle.bundleHash);
98
+ setDownloadProgress(0);
99
+
100
+ // Download as stage bundle for testing
101
+ await ctx.downloadStageBundle(`${serverUrl}/releases/${bundle.id}/download`, bundle.bundleHash);
102
+ setDownloadingHash(null);
103
+ setDownloadProgress(100);
104
+
105
+ // Refresh metadata
106
+ const meta = await NativeSwiftPatch.getSlotMetadata();
107
+ setMetadata(meta);
108
+ } catch (e) {
109
+ setDownloadingHash(null);
110
+ setDownloadProgress(0);
111
+ }
112
+ }, [ctx, serverUrl]);
113
+
114
+ // Switch environment
115
+ const handleSwitchEnvironment = useCallback(async () => {
116
+ const currentEnv = metadata?.environment || 'PROD';
117
+ const newEnv = currentEnv === 'PROD' ? EnvironmentMode.STAGING : EnvironmentMode.PRODUCTION;
118
+ const meta = await ctx.switchEnvironment(newEnv);
119
+ setMetadata(meta);
120
+ }, [ctx, metadata]);
121
+
122
+ // Stabilize
123
+ const handleStabilize = useCallback(async () => {
124
+ try {
125
+ const meta = await ctx.stabilize();
126
+ setMetadata(meta);
127
+ } catch (_e) {}
128
+ }, [ctx]);
129
+
130
+ // Rollback
131
+ const handleRollback = useCallback(async () => {
132
+ try {
133
+ await ctx.rollback();
134
+ const meta = await NativeSwiftPatch.getSlotMetadata();
135
+ setMetadata(meta);
136
+ } catch (_e) {}
137
+ }, [ctx]);
138
+
139
+ // Restart
140
+ const handleRestart = useCallback(() => {
141
+ ctx.restart();
142
+ }, [ctx]);
143
+ const handleClose = useCallback(() => {
144
+ setPin('');
145
+ setLoginError('');
146
+ setScreen(sdkToken ? 'dashboard' : 'login');
147
+ onClose();
148
+ }, [onClose, sdkToken]);
149
+
150
+ // ─── Render Login Screen ───────────────────────────────────
151
+
152
+ const renderLogin = () => /*#__PURE__*/_jsxs(View, {
153
+ style: styles.screenContainer,
154
+ children: [/*#__PURE__*/_jsx(Text, {
155
+ style: styles.title,
156
+ children: "SwiftPatch Dashboard"
157
+ }), /*#__PURE__*/_jsx(Text, {
158
+ style: styles.subtitle,
159
+ children: "Enter your SDK PIN to access"
160
+ }), /*#__PURE__*/_jsx(TextInput, {
161
+ style: styles.pinInput,
162
+ value: pin,
163
+ onChangeText: text => {
164
+ setPin(text.replace(/[^0-9a-zA-Z]/g, ''));
165
+ setLoginError('');
166
+ },
167
+ placeholder: "Enter PIN",
168
+ placeholderTextColor: "#666",
169
+ secureTextEntry: true,
170
+ maxLength: 8,
171
+ autoFocus: true
172
+ }), loginError ? /*#__PURE__*/_jsx(Text, {
173
+ style: styles.errorText,
174
+ children: loginError
175
+ }) : null, /*#__PURE__*/_jsx(TouchableOpacity, {
176
+ style: [styles.button, pin.length < 4 && styles.buttonDisabled],
177
+ onPress: handleLogin,
178
+ disabled: pin.length < 4 || isLoading,
179
+ children: isLoading ? /*#__PURE__*/_jsx(ActivityIndicator, {
180
+ color: "#fff",
181
+ size: "small"
182
+ }) : /*#__PURE__*/_jsx(Text, {
183
+ style: styles.buttonText,
184
+ children: "Unlock"
185
+ })
186
+ })]
187
+ });
188
+
189
+ // ─── Render Dashboard Screen ───────────────────────────────
190
+
191
+ const renderDashboard = () => /*#__PURE__*/_jsxs(ScrollView, {
192
+ style: styles.screenContainer,
193
+ children: [/*#__PURE__*/_jsx(Text, {
194
+ style: styles.title,
195
+ children: "Dashboard"
196
+ }), /*#__PURE__*/_jsxs(View, {
197
+ style: styles.card,
198
+ children: [/*#__PURE__*/_jsx(Text, {
199
+ style: styles.cardTitle,
200
+ children: "Current Status"
201
+ }), /*#__PURE__*/_jsxs(View, {
202
+ style: styles.row,
203
+ children: [/*#__PURE__*/_jsx(Text, {
204
+ style: styles.label,
205
+ children: "Environment:"
206
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
207
+ style: styles.envBadge,
208
+ onPress: handleSwitchEnvironment,
209
+ children: /*#__PURE__*/_jsx(Text, {
210
+ style: styles.envBadgeText,
211
+ children: metadata?.environment || 'PROD'
212
+ })
213
+ })]
214
+ }), /*#__PURE__*/_jsxs(View, {
215
+ style: styles.row,
216
+ children: [/*#__PURE__*/_jsx(Text, {
217
+ style: styles.label,
218
+ children: "Slot:"
219
+ }), /*#__PURE__*/_jsx(Text, {
220
+ style: styles.value,
221
+ children: metadata?.prod?.currentSlot || 'DEFAULT'
222
+ })]
223
+ }), /*#__PURE__*/_jsxs(View, {
224
+ style: styles.row,
225
+ children: [/*#__PURE__*/_jsx(Text, {
226
+ style: styles.label,
227
+ children: "New Hash:"
228
+ }), /*#__PURE__*/_jsxs(Text, {
229
+ style: styles.value,
230
+ children: [metadata?.prod?.newHash?.substring(0, 12) || 'none', "..."]
231
+ })]
232
+ }), /*#__PURE__*/_jsxs(View, {
233
+ style: styles.row,
234
+ children: [/*#__PURE__*/_jsx(Text, {
235
+ style: styles.label,
236
+ children: "Stable Hash:"
237
+ }), /*#__PURE__*/_jsxs(Text, {
238
+ style: styles.value,
239
+ children: [metadata?.prod?.stableHash?.substring(0, 12) || 'none', "..."]
240
+ })]
241
+ }), metadata?.prod?.tempHash && /*#__PURE__*/_jsxs(View, {
242
+ style: styles.row,
243
+ children: [/*#__PURE__*/_jsx(Text, {
244
+ style: styles.label,
245
+ children: "Pending:"
246
+ }), /*#__PURE__*/_jsxs(Text, {
247
+ style: [styles.value, {
248
+ color: '#f59e0b'
249
+ }],
250
+ children: [metadata.prod.tempHash.substring(0, 12), "... (restart required)"]
251
+ })]
252
+ })]
253
+ }), /*#__PURE__*/_jsxs(View, {
254
+ style: styles.card,
255
+ children: [/*#__PURE__*/_jsx(Text, {
256
+ style: styles.cardTitle,
257
+ children: "Actions"
258
+ }), /*#__PURE__*/_jsxs(View, {
259
+ style: styles.actionsRow,
260
+ children: [metadata?.prod?.currentSlot === 'NEW_SLOT' && /*#__PURE__*/_jsx(TouchableOpacity, {
261
+ style: styles.actionButton,
262
+ onPress: handleStabilize,
263
+ children: /*#__PURE__*/_jsx(Text, {
264
+ style: styles.actionButtonText,
265
+ children: "Stabilize"
266
+ })
267
+ }), metadata?.prod?.currentSlot !== 'DEFAULT_SLOT' && /*#__PURE__*/_jsx(TouchableOpacity, {
268
+ style: [styles.actionButton, styles.dangerButton],
269
+ onPress: handleRollback,
270
+ children: /*#__PURE__*/_jsx(Text, {
271
+ style: styles.actionButtonText,
272
+ children: "Rollback"
273
+ })
274
+ }), ctx.isRestartRequired && /*#__PURE__*/_jsx(TouchableOpacity, {
275
+ style: [styles.actionButton, styles.successButton],
276
+ onPress: handleRestart,
277
+ children: /*#__PURE__*/_jsx(Text, {
278
+ style: styles.actionButtonText,
279
+ children: "Restart"
280
+ })
281
+ })]
282
+ })]
283
+ }), /*#__PURE__*/_jsxs(View, {
284
+ style: styles.card,
285
+ children: [/*#__PURE__*/_jsx(Text, {
286
+ style: styles.cardTitle,
287
+ children: "Channels"
288
+ }), buckets.length === 0 ? /*#__PURE__*/_jsx(Text, {
289
+ style: styles.emptyText,
290
+ children: "No channels found"
291
+ }) : buckets.map(bucket => /*#__PURE__*/_jsxs(TouchableOpacity, {
292
+ style: styles.bucketItem,
293
+ onPress: () => loadBundles(bucket),
294
+ children: [/*#__PURE__*/_jsxs(View, {
295
+ children: [/*#__PURE__*/_jsx(Text, {
296
+ style: styles.bucketName,
297
+ children: bucket.name
298
+ }), /*#__PURE__*/_jsxs(Text, {
299
+ style: styles.bucketMeta,
300
+ children: [bucket.totalReleases, " releases ", bucket.isDefault ? '(default)' : '']
301
+ })]
302
+ }), /*#__PURE__*/_jsx(Text, {
303
+ style: styles.chevron,
304
+ children: "\u203A"
305
+ })]
306
+ }, bucket.id))]
307
+ })]
308
+ });
309
+
310
+ // ─── Render Bundles Screen ─────────────────────────────────
311
+
312
+ const renderBundles = () => /*#__PURE__*/_jsxs(View, {
313
+ style: styles.screenContainer,
314
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
315
+ onPress: () => setScreen('dashboard'),
316
+ style: styles.backButton,
317
+ children: /*#__PURE__*/_jsx(Text, {
318
+ style: styles.backText,
319
+ children: "\u2039 Back"
320
+ })
321
+ }), /*#__PURE__*/_jsx(Text, {
322
+ style: styles.title,
323
+ children: selectedBucket?.name || 'Bundles'
324
+ }), /*#__PURE__*/_jsx(FlatList, {
325
+ data: bundles,
326
+ keyExtractor: item => item.id,
327
+ renderItem: ({
328
+ item
329
+ }) => /*#__PURE__*/_jsxs(View, {
330
+ style: styles.bundleCard,
331
+ children: [/*#__PURE__*/_jsxs(View, {
332
+ style: styles.bundleHeader,
333
+ children: [/*#__PURE__*/_jsxs(Text, {
334
+ style: styles.bundleVersion,
335
+ children: ["v", item.version, " (", item.buildNumber, ")"]
336
+ }), /*#__PURE__*/_jsx(View, {
337
+ style: [styles.statusBadge, item.status === 'RELEASED' ? styles.releasedBadge : styles.readyBadge],
338
+ children: /*#__PURE__*/_jsx(Text, {
339
+ style: styles.statusBadgeText,
340
+ children: item.status
341
+ })
342
+ })]
343
+ }), /*#__PURE__*/_jsx(Text, {
344
+ style: styles.bundlePlatform,
345
+ children: item.platform
346
+ }), item.releaseNote && /*#__PURE__*/_jsx(Text, {
347
+ style: styles.bundleNote,
348
+ children: item.releaseNote
349
+ }), /*#__PURE__*/_jsxs(View, {
350
+ style: styles.bundleFooter,
351
+ children: [/*#__PURE__*/_jsx(Text, {
352
+ style: styles.bundleSize,
353
+ children: formatSize(item.bundleSize)
354
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
355
+ style: [styles.downloadButton, downloadingHash === item.bundleHash && styles.buttonDisabled],
356
+ onPress: () => handleDownloadBundle(item),
357
+ disabled: downloadingHash !== null,
358
+ children: downloadingHash === item.bundleHash ? /*#__PURE__*/_jsx(ActivityIndicator, {
359
+ color: "#fff",
360
+ size: "small"
361
+ }) : /*#__PURE__*/_jsx(Text, {
362
+ style: styles.downloadButtonText,
363
+ children: "Download"
364
+ })
365
+ })]
366
+ })]
367
+ }),
368
+ ListEmptyComponent: /*#__PURE__*/_jsx(Text, {
369
+ style: styles.emptyText,
370
+ children: "No bundles found"
371
+ }),
372
+ onEndReached: () => {
373
+ if (hasMoreBundles && !isLoading && selectedBucket) {
374
+ loadBundles(selectedBucket, pageOffset + 1);
375
+ }
376
+ },
377
+ onEndReachedThreshold: 0.5,
378
+ ListFooterComponent: isLoading ? /*#__PURE__*/_jsx(ActivityIndicator, {
379
+ style: {
380
+ margin: 20
381
+ },
382
+ color: "#007AFF"
383
+ }) : null
384
+ })]
385
+ });
386
+
387
+ // ─── Main Render ───────────────────────────────────────────
388
+
389
+ return /*#__PURE__*/_jsx(Modal, {
390
+ visible: visible,
391
+ animationType: "slide",
392
+ presentationStyle: "pageSheet",
393
+ onRequestClose: handleClose,
394
+ children: /*#__PURE__*/_jsxs(SafeAreaView, {
395
+ style: styles.container,
396
+ children: [/*#__PURE__*/_jsxs(View, {
397
+ style: styles.header,
398
+ children: [/*#__PURE__*/_jsx(Text, {
399
+ style: styles.headerTitle,
400
+ children: "SwiftPatch"
401
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
402
+ onPress: handleClose,
403
+ children: /*#__PURE__*/_jsx(Text, {
404
+ style: styles.closeButton,
405
+ children: "\u2715"
406
+ })
407
+ })]
408
+ }), downloadingHash && /*#__PURE__*/_jsx(View, {
409
+ style: styles.progressBar,
410
+ children: /*#__PURE__*/_jsx(View, {
411
+ style: [styles.progressFill, {
412
+ width: `${downloadProgress}%`
413
+ }]
414
+ })
415
+ }), screen === 'login' && renderLogin(), screen === 'dashboard' && renderDashboard(), screen === 'bundles' && renderBundles()]
416
+ })
417
+ });
418
+ }
419
+ function formatSize(bytes) {
420
+ if (bytes < 1024) return `${bytes} B`;
421
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
422
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
423
+ }
424
+ const styles = StyleSheet.create({
425
+ container: {
426
+ flex: 1,
427
+ backgroundColor: '#0f0f23'
428
+ },
429
+ header: {
430
+ flexDirection: 'row',
431
+ justifyContent: 'space-between',
432
+ alignItems: 'center',
433
+ paddingHorizontal: 20,
434
+ paddingVertical: 16,
435
+ borderBottomWidth: 1,
436
+ borderBottomColor: '#1a1a3e'
437
+ },
438
+ headerTitle: {
439
+ fontSize: 18,
440
+ fontWeight: '700',
441
+ color: '#fff'
442
+ },
443
+ closeButton: {
444
+ fontSize: 20,
445
+ color: '#888',
446
+ padding: 4
447
+ },
448
+ progressBar: {
449
+ height: 3,
450
+ backgroundColor: '#1a1a3e'
451
+ },
452
+ progressFill: {
453
+ height: '100%',
454
+ backgroundColor: '#007AFF'
455
+ },
456
+ screenContainer: {
457
+ flex: 1,
458
+ padding: 20
459
+ },
460
+ title: {
461
+ fontSize: 24,
462
+ fontWeight: '700',
463
+ color: '#fff',
464
+ marginBottom: 8
465
+ },
466
+ subtitle: {
467
+ fontSize: 14,
468
+ color: '#888',
469
+ marginBottom: 24
470
+ },
471
+ pinInput: {
472
+ backgroundColor: '#1a1a3e',
473
+ color: '#fff',
474
+ fontSize: 24,
475
+ textAlign: 'center',
476
+ letterSpacing: 8,
477
+ padding: 16,
478
+ borderRadius: 12,
479
+ marginBottom: 16
480
+ },
481
+ errorText: {
482
+ color: '#ef4444',
483
+ fontSize: 14,
484
+ marginBottom: 16,
485
+ textAlign: 'center'
486
+ },
487
+ button: {
488
+ backgroundColor: '#007AFF',
489
+ paddingVertical: 14,
490
+ borderRadius: 10,
491
+ alignItems: 'center'
492
+ },
493
+ buttonDisabled: {
494
+ opacity: 0.5
495
+ },
496
+ buttonText: {
497
+ color: '#fff',
498
+ fontSize: 16,
499
+ fontWeight: '600'
500
+ },
501
+ card: {
502
+ backgroundColor: '#1a1a3e',
503
+ borderRadius: 12,
504
+ padding: 16,
505
+ marginBottom: 16
506
+ },
507
+ cardTitle: {
508
+ fontSize: 16,
509
+ fontWeight: '600',
510
+ color: '#fff',
511
+ marginBottom: 12
512
+ },
513
+ row: {
514
+ flexDirection: 'row',
515
+ justifyContent: 'space-between',
516
+ alignItems: 'center',
517
+ paddingVertical: 6
518
+ },
519
+ label: {
520
+ color: '#888',
521
+ fontSize: 14
522
+ },
523
+ value: {
524
+ color: '#fff',
525
+ fontSize: 14,
526
+ fontFamily: 'Courier'
527
+ },
528
+ envBadge: {
529
+ backgroundColor: '#007AFF',
530
+ paddingHorizontal: 12,
531
+ paddingVertical: 4,
532
+ borderRadius: 6
533
+ },
534
+ envBadgeText: {
535
+ color: '#fff',
536
+ fontSize: 12,
537
+ fontWeight: '600'
538
+ },
539
+ actionsRow: {
540
+ flexDirection: 'row',
541
+ gap: 8
542
+ },
543
+ actionButton: {
544
+ backgroundColor: '#007AFF',
545
+ paddingHorizontal: 16,
546
+ paddingVertical: 10,
547
+ borderRadius: 8,
548
+ flex: 1,
549
+ alignItems: 'center'
550
+ },
551
+ dangerButton: {
552
+ backgroundColor: '#ef4444'
553
+ },
554
+ successButton: {
555
+ backgroundColor: '#22c55e'
556
+ },
557
+ actionButtonText: {
558
+ color: '#fff',
559
+ fontSize: 14,
560
+ fontWeight: '600'
561
+ },
562
+ bucketItem: {
563
+ flexDirection: 'row',
564
+ justifyContent: 'space-between',
565
+ alignItems: 'center',
566
+ paddingVertical: 12,
567
+ borderBottomWidth: 1,
568
+ borderBottomColor: '#252550'
569
+ },
570
+ bucketName: {
571
+ color: '#fff',
572
+ fontSize: 15,
573
+ fontWeight: '500'
574
+ },
575
+ bucketMeta: {
576
+ color: '#666',
577
+ fontSize: 12,
578
+ marginTop: 2
579
+ },
580
+ chevron: {
581
+ color: '#666',
582
+ fontSize: 20
583
+ },
584
+ emptyText: {
585
+ color: '#666',
586
+ textAlign: 'center',
587
+ paddingVertical: 20
588
+ },
589
+ backButton: {
590
+ marginBottom: 12
591
+ },
592
+ backText: {
593
+ color: '#007AFF',
594
+ fontSize: 16
595
+ },
596
+ bundleCard: {
597
+ backgroundColor: '#1a1a3e',
598
+ borderRadius: 10,
599
+ padding: 14,
600
+ marginBottom: 10
601
+ },
602
+ bundleHeader: {
603
+ flexDirection: 'row',
604
+ justifyContent: 'space-between',
605
+ alignItems: 'center',
606
+ marginBottom: 4
607
+ },
608
+ bundleVersion: {
609
+ color: '#fff',
610
+ fontSize: 16,
611
+ fontWeight: '600'
612
+ },
613
+ statusBadge: {
614
+ paddingHorizontal: 8,
615
+ paddingVertical: 2,
616
+ borderRadius: 4
617
+ },
618
+ releasedBadge: {
619
+ backgroundColor: '#22c55e33'
620
+ },
621
+ readyBadge: {
622
+ backgroundColor: '#f59e0b33'
623
+ },
624
+ statusBadgeText: {
625
+ fontSize: 11,
626
+ fontWeight: '600',
627
+ color: '#fff'
628
+ },
629
+ bundlePlatform: {
630
+ color: '#888',
631
+ fontSize: 12,
632
+ marginBottom: 4
633
+ },
634
+ bundleNote: {
635
+ color: '#aaa',
636
+ fontSize: 13,
637
+ marginBottom: 8
638
+ },
639
+ bundleFooter: {
640
+ flexDirection: 'row',
641
+ justifyContent: 'space-between',
642
+ alignItems: 'center',
643
+ marginTop: 8
644
+ },
645
+ bundleSize: {
646
+ color: '#666',
647
+ fontSize: 12
648
+ },
649
+ downloadButton: {
650
+ backgroundColor: '#007AFF',
651
+ paddingHorizontal: 16,
652
+ paddingVertical: 8,
653
+ borderRadius: 6
654
+ },
655
+ downloadButtonText: {
656
+ color: '#fff',
657
+ fontSize: 13,
658
+ fontWeight: '600'
659
+ }
660
+ });
661
+ //# sourceMappingURL=SwiftPatchModal.js.map