@capgo/capacitor-updater 8.47.8 → 8.47.10
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/README.md +8 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +53 -43
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +108 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeDetector.java +3 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +2 -0
- package/dist/docs.json +2 -2
- package/dist/esm/definitions.d.ts +8 -0
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +89 -46
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +24 -2
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +49 -31
- package/package.json +1 -1
|
@@ -79,7 +79,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
79
79
|
CAPPluginMethod(name: "completeFlexibleUpdate", returnType: CAPPluginReturnPromise)
|
|
80
80
|
]
|
|
81
81
|
public var implementation = CapgoUpdater()
|
|
82
|
-
private let pluginVersion: String = "8.47.
|
|
82
|
+
private let pluginVersion: String = "8.47.9"
|
|
83
83
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
84
84
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
85
85
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
@@ -989,6 +989,29 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
989
989
|
}
|
|
990
990
|
}
|
|
991
991
|
|
|
992
|
+
func reloadWithoutWaitingForAppReady() -> Bool {
|
|
993
|
+
guard let bridge = self.bridge else { return false }
|
|
994
|
+
|
|
995
|
+
let performReload: () -> Bool = {
|
|
996
|
+
guard self.applyCurrentBundleToBridge(bridge) else {
|
|
997
|
+
return false
|
|
998
|
+
}
|
|
999
|
+
self.checkAppReady()
|
|
1000
|
+
self.notifyListeners("appReloaded", data: [:])
|
|
1001
|
+
return true
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
if Thread.isMainThread {
|
|
1005
|
+
return performReload()
|
|
1006
|
+
} else {
|
|
1007
|
+
var result = false
|
|
1008
|
+
DispatchQueue.main.sync {
|
|
1009
|
+
result = performReload()
|
|
1010
|
+
}
|
|
1011
|
+
return result
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
992
1015
|
@objc func reload(_ call: CAPPluginCall) {
|
|
993
1016
|
let current: BundleInfo = self.implementation.getCurrentBundle()
|
|
994
1017
|
let next: BundleInfo? = self.implementation.getNextBundle()
|
|
@@ -1206,7 +1229,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1206
1229
|
}
|
|
1207
1230
|
|
|
1208
1231
|
let previewFallbackBundle = self.implementation.getPreviewFallbackBundle()
|
|
1209
|
-
self.endPreviewSession()
|
|
1232
|
+
self.endPreviewSession(keepPreviewGuard: true)
|
|
1210
1233
|
let restoredNextBundle = self.implementation.getNextBundle()
|
|
1211
1234
|
self.deletePreviewBundleIfUnused(previewBundle, previewFallbackBundle: previewFallbackBundle, restoredNextBundle: restoredNextBundle)
|
|
1212
1235
|
return true
|
|
@@ -1232,12 +1255,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1232
1255
|
|
|
1233
1256
|
private func leavePreviewSessionWithoutReload(keepPreviewGuard: Bool = false) -> Bool {
|
|
1234
1257
|
let previewBundle = self.implementation.getCurrentBundle()
|
|
1235
|
-
guard let previewFallbackBundle = self.
|
|
1236
|
-
logger.error("No preview fallback bundle available")
|
|
1237
|
-
return false
|
|
1238
|
-
}
|
|
1239
|
-
guard self.implementation.canSet(bundle: previewFallbackBundle) else {
|
|
1240
|
-
logger.error("Preview fallback bundle is not installable")
|
|
1258
|
+
guard let previewFallbackBundle = self.resolvePreviewFallbackBundle(reason: "preview deeplink launch") else {
|
|
1241
1259
|
return false
|
|
1242
1260
|
}
|
|
1243
1261
|
guard self.implementation.stagePreviewFallbackReload(bundle: previewFallbackBundle) else {
|
|
@@ -1254,14 +1272,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1254
1272
|
private func leavePreviewSessionForIncomingPreviewLink() -> Bool {
|
|
1255
1273
|
self.showPreviewTransitionLoader(reason: "incoming-preview-deeplink")
|
|
1256
1274
|
let previewBundle = self.implementation.getCurrentBundle()
|
|
1257
|
-
guard let previewFallbackBundle = self.
|
|
1258
|
-
logger.error("No preview fallback bundle available")
|
|
1259
|
-
self.clearIncomingPreviewTransition()
|
|
1260
|
-
self.hidePreviewTransitionLoader(reason: "incoming-preview-deeplink-failed")
|
|
1261
|
-
return false
|
|
1262
|
-
}
|
|
1263
|
-
guard self.implementation.canSet(bundle: previewFallbackBundle) else {
|
|
1264
|
-
logger.error("Preview fallback bundle is not installable")
|
|
1275
|
+
guard let previewFallbackBundle = self.resolvePreviewFallbackBundle(reason: "incoming preview deeplink") else {
|
|
1265
1276
|
self.clearIncomingPreviewTransition()
|
|
1266
1277
|
self.hidePreviewTransitionLoader(reason: "incoming-preview-deeplink-failed")
|
|
1267
1278
|
return false
|
|
@@ -1275,7 +1286,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1275
1286
|
return false
|
|
1276
1287
|
}
|
|
1277
1288
|
|
|
1278
|
-
let didReload = self.
|
|
1289
|
+
let didReload = self.reloadWithoutWaitingForAppReady()
|
|
1279
1290
|
if didReload {
|
|
1280
1291
|
self.endPreviewSession(keepPreviewGuard: true)
|
|
1281
1292
|
let restoredNextBundle = self.implementation.getNextBundle()
|
|
@@ -1312,7 +1323,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1312
1323
|
if let payloadUrl = self.storedPreviewPayloadUrl() {
|
|
1313
1324
|
didReload = self.refreshPreviewSessionFromPayloadUrl(payloadUrl)
|
|
1314
1325
|
} else {
|
|
1315
|
-
didReload = self.
|
|
1326
|
+
didReload = self.reloadWithoutWaitingForAppReady()
|
|
1316
1327
|
}
|
|
1317
1328
|
|
|
1318
1329
|
if !didReload {
|
|
@@ -1325,21 +1336,16 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1325
1336
|
self.previewSessionEnabled
|
|
1326
1337
|
}
|
|
1327
1338
|
|
|
1328
|
-
|
|
1339
|
+
func resetToPreviewFallbackBundle() -> Bool {
|
|
1329
1340
|
guard self.canPerformResetTransition() else { return false }
|
|
1330
|
-
guard let fallback = self.
|
|
1331
|
-
logger.error("No preview fallback bundle available")
|
|
1332
|
-
return false
|
|
1333
|
-
}
|
|
1334
|
-
guard self.implementation.canSet(bundle: fallback) else {
|
|
1335
|
-
logger.error("Preview fallback bundle is not installable")
|
|
1341
|
+
guard let fallback = self.resolvePreviewFallbackBundle(reason: "leave preview") else {
|
|
1336
1342
|
return false
|
|
1337
1343
|
}
|
|
1338
1344
|
|
|
1339
1345
|
let previousState = self.implementation.captureResetState()
|
|
1340
1346
|
let previousBundleName = self.implementation.getCurrentBundle().getVersionName()
|
|
1341
1347
|
logger.info("Resetting to preview fallback bundle: \(fallback.toString())")
|
|
1342
|
-
if self.implementation.stagePreviewFallbackReload(bundle: fallback) && self.
|
|
1348
|
+
if self.implementation.stagePreviewFallbackReload(bundle: fallback) && self.reloadWithoutWaitingForAppReady() {
|
|
1343
1349
|
self.implementation.finalizeResetTransition(previousBundleName: previousBundleName, isInternal: false)
|
|
1344
1350
|
self.notifyBundleSet(fallback)
|
|
1345
1351
|
return true
|
|
@@ -1349,11 +1355,36 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1349
1355
|
return false
|
|
1350
1356
|
}
|
|
1351
1357
|
|
|
1358
|
+
private func resolvePreviewFallbackBundle(reason: String) -> BundleInfo? {
|
|
1359
|
+
let fallback = self.implementation.getPreviewFallbackBundle()
|
|
1360
|
+
if let fallback, !fallback.isErrorStatus(), self.implementation.canSet(bundle: fallback) {
|
|
1361
|
+
return fallback
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
if let fallback {
|
|
1365
|
+
if fallback.isErrorStatus() {
|
|
1366
|
+
logger.warn("Preview fallback bundle is in error state for \(reason). Falling back to builtin bundle.")
|
|
1367
|
+
} else {
|
|
1368
|
+
logger.warn("Preview fallback bundle is not installable for \(reason). Falling back to builtin bundle.")
|
|
1369
|
+
}
|
|
1370
|
+
} else {
|
|
1371
|
+
logger.warn("No preview fallback bundle available for \(reason). Falling back to builtin bundle.")
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
let builtin = self.implementation.getBundleInfo(id: BundleInfo.ID_BUILTIN)
|
|
1375
|
+
if !builtin.isErrorStatus(), self.implementation.canSet(bundle: builtin) {
|
|
1376
|
+
return builtin
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
logger.error("Builtin bundle is not available to leave preview for \(reason)")
|
|
1380
|
+
return nil
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1352
1383
|
private func endPreviewSession(keepPreviewGuard: Bool = false) {
|
|
1353
1384
|
let previousShakeMenuEnabled = UserDefaults.standard.object(forKey: self.previewPreviousShakeMenuDefaultsKey) as? Bool
|
|
1354
|
-
??
|
|
1385
|
+
?? self.getBooleanConfig("shakeMenu", defaultValue: false)
|
|
1355
1386
|
let previousShakeChannelSelectorEnabled = UserDefaults.standard.object(forKey: self.previewPreviousShakeChannelSelectorDefaultsKey) as? Bool
|
|
1356
|
-
??
|
|
1387
|
+
?? self.getBooleanConfig("allowShakeChannelSelector", defaultValue: false)
|
|
1357
1388
|
self.restorePreviewPreviousNextBundle()
|
|
1358
1389
|
self.restorePreviewPreviousAppId()
|
|
1359
1390
|
self.restorePreviewPreviousDefaultChannel()
|
|
@@ -1374,15 +1405,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1374
1405
|
|
|
1375
1406
|
private func clearPreviewSessionBecauseDisabled() {
|
|
1376
1407
|
logger.info("Preview session disabled by config; restoring preview fallback")
|
|
1377
|
-
let
|
|
1378
|
-
let bundleToRestore: BundleInfo
|
|
1379
|
-
if let fallback, !fallback.isErrorStatus() {
|
|
1380
|
-
bundleToRestore = fallback
|
|
1381
|
-
} else {
|
|
1382
|
-
bundleToRestore = self.implementation.getBundleInfo(id: BundleInfo.ID_BUILTIN)
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
if self.implementation.canSet(bundle: bundleToRestore) {
|
|
1408
|
+
if let bundleToRestore = self.resolvePreviewFallbackBundle(reason: "preview disabled") {
|
|
1386
1409
|
_ = self.implementation.stagePreviewFallbackReload(bundle: bundleToRestore)
|
|
1387
1410
|
} else {
|
|
1388
1411
|
logger.warn("Could not restore preview fallback while disabling preview")
|
|
@@ -1396,11 +1419,25 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1396
1419
|
self.isLeavingPreviewForIncomingLink = false
|
|
1397
1420
|
self.implementation.previewSession = false
|
|
1398
1421
|
self.hidePreviewTransitionLoader(reason: "preview-session-disabled")
|
|
1399
|
-
self.shakeMenuEnabled =
|
|
1400
|
-
self.shakeChannelSelectorEnabled =
|
|
1422
|
+
self.shakeMenuEnabled = self.getBooleanConfig("shakeMenu", defaultValue: false)
|
|
1423
|
+
self.shakeChannelSelectorEnabled = self.getBooleanConfig("allowShakeChannelSelector", defaultValue: false)
|
|
1401
1424
|
self.clearPreviewSessionPreferences()
|
|
1402
1425
|
}
|
|
1403
1426
|
|
|
1427
|
+
private func getBooleanConfig(_ key: String, defaultValue: Bool) -> Bool {
|
|
1428
|
+
guard self.bridge != nil else {
|
|
1429
|
+
return defaultValue
|
|
1430
|
+
}
|
|
1431
|
+
return getConfig().getBoolean(key, defaultValue)
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
private func getStringConfig(_ key: String, defaultValue: String) -> String {
|
|
1435
|
+
guard self.bridge != nil else {
|
|
1436
|
+
return defaultValue
|
|
1437
|
+
}
|
|
1438
|
+
return getConfig().getString(key, defaultValue) ?? defaultValue
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1404
1441
|
private func clearPreviewSessionPreferences() {
|
|
1405
1442
|
_ = self.implementation.setPreviewFallbackBundle(fallback: nil)
|
|
1406
1443
|
UserDefaults.standard.removeObject(forKey: self.previewSessionDefaultsKey)
|
|
@@ -1426,7 +1463,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1426
1463
|
}
|
|
1427
1464
|
|
|
1428
1465
|
private func restorePreviewPreviousDefaultChannel() {
|
|
1429
|
-
let configDefaultChannel = self.
|
|
1466
|
+
let configDefaultChannel = self.getStringConfig("defaultChannel", defaultValue: "")
|
|
1430
1467
|
let hadPreviousDefaultChannel = UserDefaults.standard.object(forKey: self.previewPreviousDefaultChannelWasSetDefaultsKey) as? Bool ?? false
|
|
1431
1468
|
|
|
1432
1469
|
guard hadPreviousDefaultChannel,
|
|
@@ -1579,7 +1616,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1579
1616
|
let current = self.implementation.getCurrentBundle()
|
|
1580
1617
|
if current.getVersionName() == version {
|
|
1581
1618
|
self.logger.info("Preview payload unchanged, reloading current bundle")
|
|
1582
|
-
return self.
|
|
1619
|
+
return self.reloadWithoutWaitingForAppReady()
|
|
1583
1620
|
}
|
|
1584
1621
|
|
|
1585
1622
|
let next = try self.downloadBundle(
|
|
@@ -1597,7 +1634,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1597
1634
|
}
|
|
1598
1635
|
|
|
1599
1636
|
self.notifyBundleSet(next)
|
|
1600
|
-
return self.
|
|
1637
|
+
return self.reloadWithoutWaitingForAppReady()
|
|
1601
1638
|
} catch {
|
|
1602
1639
|
self.logger.error("Could not refresh preview session: \(error.localizedDescription)")
|
|
1603
1640
|
return false
|
|
@@ -1613,8 +1650,8 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1613
1650
|
self.previewSessionAlertPending = false
|
|
1614
1651
|
self.isLeavingPreviewForIncomingLink = false
|
|
1615
1652
|
self.implementation.previewSession = false
|
|
1616
|
-
self.shakeMenuEnabled =
|
|
1617
|
-
self.shakeChannelSelectorEnabled =
|
|
1653
|
+
self.shakeMenuEnabled = self.getBooleanConfig("shakeMenu", defaultValue: false)
|
|
1654
|
+
self.shakeChannelSelectorEnabled = self.getBooleanConfig("allowShakeChannelSelector", defaultValue: false)
|
|
1618
1655
|
self.restorePreviewPreviousAppId()
|
|
1619
1656
|
self.restorePreviewPreviousDefaultChannel()
|
|
1620
1657
|
_ = self.implementation.setPreviewFallbackBundle(fallback: nil)
|
|
@@ -1885,7 +1922,13 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1885
1922
|
let triggerAutoUpdate = call.getBool("triggerAutoUpdate") ?? false
|
|
1886
1923
|
self.saveCallForAsyncHandling(call)
|
|
1887
1924
|
DispatchQueue.global(qos: .utility).async {
|
|
1888
|
-
let
|
|
1925
|
+
let configDefaultChannel = self.getConfig().getString("defaultChannel", "")!
|
|
1926
|
+
let res = self.implementation.setChannel(
|
|
1927
|
+
channel: channel,
|
|
1928
|
+
defaultChannelKey: self.defaultChannelDefaultsKey,
|
|
1929
|
+
allowSetDefaultChannel: self.allowSetDefaultChannel,
|
|
1930
|
+
configDefaultChannel: configDefaultChannel
|
|
1931
|
+
)
|
|
1889
1932
|
if res.error != "" {
|
|
1890
1933
|
// Fire channelPrivate event if channel doesn't allow self-assignment
|
|
1891
1934
|
if res.error.contains("cannot_update_via_private_channel") || res.error.contains("channel_self_set_not_allowed") {
|
|
@@ -1916,7 +1959,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1916
1959
|
@objc func getChannel(_ call: CAPPluginCall) {
|
|
1917
1960
|
self.saveCallForAsyncHandling(call)
|
|
1918
1961
|
DispatchQueue.global(qos: .utility).async {
|
|
1919
|
-
let res = self.implementation.getChannel()
|
|
1962
|
+
let res = self.implementation.getChannel(defaultChannelKey: self.defaultChannelDefaultsKey)
|
|
1920
1963
|
if res.error != "" {
|
|
1921
1964
|
self.rejectCall(call, message: res.error, code: "GETCHANNEL_FAILED", data: [
|
|
1922
1965
|
"message": res.error,
|
|
@@ -2151,7 +2151,12 @@ import UIKit
|
|
|
2151
2151
|
return setChannel
|
|
2152
2152
|
}
|
|
2153
2153
|
|
|
2154
|
-
func setChannel(
|
|
2154
|
+
func setChannel(
|
|
2155
|
+
channel: String,
|
|
2156
|
+
defaultChannelKey: String,
|
|
2157
|
+
allowSetDefaultChannel: Bool,
|
|
2158
|
+
configDefaultChannel: String = ""
|
|
2159
|
+
) -> SetChannel {
|
|
2155
2160
|
let setChannel: SetChannel = SetChannel()
|
|
2156
2161
|
|
|
2157
2162
|
// Check if setting defaultChannel is allowed
|
|
@@ -2231,6 +2236,7 @@ import UIKit
|
|
|
2231
2236
|
} else if responseValue.unset == true {
|
|
2232
2237
|
UserDefaults.standard.removeObject(forKey: defaultChannelKey)
|
|
2233
2238
|
UserDefaults.standard.synchronize()
|
|
2239
|
+
self.defaultChannel = configDefaultChannel
|
|
2234
2240
|
self.logger.info("Public channel requested, channel override removed")
|
|
2235
2241
|
|
|
2236
2242
|
setChannel.status = responseValue.status ?? "ok"
|
|
@@ -2247,7 +2253,7 @@ import UIKit
|
|
|
2247
2253
|
return setChannel
|
|
2248
2254
|
}
|
|
2249
2255
|
|
|
2250
|
-
func getChannel() -> GetChannel {
|
|
2256
|
+
func getChannel(defaultChannelKey: String? = nil) -> GetChannel {
|
|
2251
2257
|
let getChannel: GetChannel = GetChannel()
|
|
2252
2258
|
|
|
2253
2259
|
// Check if rate limit was exceeded
|
|
@@ -2334,10 +2340,26 @@ import UIKit
|
|
|
2334
2340
|
getChannel.message = responseValue.message ?? ""
|
|
2335
2341
|
getChannel.channel = responseValue.channel ?? ""
|
|
2336
2342
|
getChannel.allowSet = responseValue.allowSet ?? true
|
|
2343
|
+
persistDefaultChannelFromResponse(channel: responseValue.channel, defaultChannelKey: defaultChannelKey)
|
|
2337
2344
|
}
|
|
2338
2345
|
return getChannel
|
|
2339
2346
|
}
|
|
2340
2347
|
|
|
2348
|
+
func persistDefaultChannelFromResponse(channel: String?, defaultChannelKey: String?) {
|
|
2349
|
+
guard let channelName = channel?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
2350
|
+
!channelName.isEmpty,
|
|
2351
|
+
channelName != BundleInfo.ID_BUILTIN else {
|
|
2352
|
+
return
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
self.defaultChannel = channelName
|
|
2356
|
+
if let defaultChannelKey, !defaultChannelKey.isEmpty {
|
|
2357
|
+
UserDefaults.standard.set(channelName, forKey: defaultChannelKey)
|
|
2358
|
+
UserDefaults.standard.synchronize()
|
|
2359
|
+
}
|
|
2360
|
+
logger.info("defaultChannel synchronized from getChannel(): \(channelName)")
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2341
2363
|
func listChannels() -> ListChannels {
|
|
2342
2364
|
let listChannels: ListChannels = ListChannels()
|
|
2343
2365
|
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
import UIKit
|
|
8
8
|
import Capacitor
|
|
9
9
|
|
|
10
|
+
private var lastShakeMenuShownAt: TimeInterval = 0
|
|
11
|
+
private let shakeMenuCooldownSeconds: TimeInterval = 1.2
|
|
12
|
+
|
|
10
13
|
extension UIApplication {
|
|
11
14
|
// swiftlint:disable:next line_length
|
|
12
15
|
public class func topViewController(_ base: UIViewController? = UIApplication.shared.windows.first?.rootViewController) -> UIViewController? {
|
|
@@ -43,25 +46,38 @@ extension UIWindow {
|
|
|
43
46
|
return
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
let now = Date().timeIntervalSince1970
|
|
50
|
+
guard now - lastShakeMenuShownAt >= shakeMenuCooldownSeconds else {
|
|
51
|
+
plugin.logger.info("Shake menu ignored because cooldown is active")
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
if canShowPreviewMenu {
|
|
47
|
-
showDefaultMenu(plugin: plugin, bridge: bridge)
|
|
56
|
+
if showDefaultMenu(plugin: plugin, bridge: bridge) {
|
|
57
|
+
lastShakeMenuShownAt = now
|
|
58
|
+
}
|
|
48
59
|
} else {
|
|
49
|
-
showChannelSelector(plugin: plugin, bridge: bridge)
|
|
60
|
+
if showChannelSelector(plugin: plugin, bridge: bridge) {
|
|
61
|
+
lastShakeMenuShownAt = now
|
|
62
|
+
}
|
|
50
63
|
}
|
|
51
64
|
}
|
|
52
65
|
}
|
|
53
66
|
|
|
54
|
-
|
|
67
|
+
@discardableResult
|
|
68
|
+
private func showDefaultMenu(plugin: CapacitorUpdaterPlugin, bridge: CAPBridgeProtocol) -> Bool {
|
|
55
69
|
// Prevent multiple alerts from showing
|
|
56
|
-
|
|
57
|
-
|
|
70
|
+
guard let topVC = UIApplication.topViewController() else {
|
|
71
|
+
return false
|
|
72
|
+
}
|
|
73
|
+
if topVC.isKind(of: UIAlertController.self) {
|
|
58
74
|
plugin.logger.info("UIAlertController is already presented")
|
|
59
|
-
return
|
|
75
|
+
return false
|
|
60
76
|
}
|
|
61
77
|
|
|
62
78
|
guard plugin.hasActivePreviewSession() else {
|
|
63
79
|
plugin.logger.info("Shake preview menu ignored because no preview session is active")
|
|
64
|
-
return
|
|
80
|
+
return false
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? "App"
|
|
@@ -96,7 +112,7 @@ extension UIWindow {
|
|
|
96
112
|
if plugin.shakeChannelSelectorEnabled {
|
|
97
113
|
alertShake.addAction(UIAlertAction(title: "Switch channel", style: .default) { _ in
|
|
98
114
|
let showSelector = {
|
|
99
|
-
self.showChannelSelector(plugin: plugin, bridge: bridge)
|
|
115
|
+
_ = self.showChannelSelector(plugin: plugin, bridge: bridge)
|
|
100
116
|
}
|
|
101
117
|
|
|
102
118
|
if let presenter = alertShake.presentingViewController {
|
|
@@ -110,10 +126,9 @@ extension UIWindow {
|
|
|
110
126
|
alertShake.addAction(UIAlertAction(title: cancelButtonTitle, style: .default))
|
|
111
127
|
|
|
112
128
|
DispatchQueue.main.async {
|
|
113
|
-
|
|
114
|
-
topVC.present(alertShake, animated: true)
|
|
115
|
-
}
|
|
129
|
+
topVC.present(alertShake, animated: true)
|
|
116
130
|
}
|
|
131
|
+
return true
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
private func showConfiguredDefaultMenu(plugin: CapacitorUpdaterPlugin, bridge: CAPBridgeProtocol) {
|
|
@@ -179,12 +194,15 @@ extension UIWindow {
|
|
|
179
194
|
}
|
|
180
195
|
}
|
|
181
196
|
|
|
182
|
-
|
|
197
|
+
@discardableResult
|
|
198
|
+
private func showChannelSelector(plugin: CapacitorUpdaterPlugin, bridge: CAPBridgeProtocol) -> Bool {
|
|
183
199
|
// Prevent multiple alerts from showing
|
|
184
|
-
|
|
185
|
-
|
|
200
|
+
guard let topVC = UIApplication.topViewController() else {
|
|
201
|
+
return false
|
|
202
|
+
}
|
|
203
|
+
if topVC.isKind(of: UIAlertController.self) {
|
|
186
204
|
plugin.logger.info("UIAlertController is already presented")
|
|
187
|
-
return
|
|
205
|
+
return false
|
|
188
206
|
}
|
|
189
207
|
|
|
190
208
|
let updater = plugin.implementation
|
|
@@ -208,28 +226,27 @@ extension UIWindow {
|
|
|
208
226
|
})
|
|
209
227
|
|
|
210
228
|
DispatchQueue.main.async {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
let result = updater.listChannels()
|
|
229
|
+
topVC.present(loadingAlert, animated: true) {
|
|
230
|
+
// Fetch channels in background
|
|
231
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
232
|
+
let result = updater.listChannels()
|
|
216
233
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
234
|
+
DispatchQueue.main.async {
|
|
235
|
+
loadingAlert.dismiss(animated: true) {
|
|
236
|
+
guard !didCancel else { return }
|
|
237
|
+
if !result.error.isEmpty {
|
|
238
|
+
self.showError(message: "Failed to load channels: \(result.error)", plugin: plugin)
|
|
239
|
+
} else if result.channels.isEmpty {
|
|
240
|
+
self.showError(message: "No channels available for self-assignment", plugin: plugin)
|
|
241
|
+
} else {
|
|
242
|
+
self.presentChannelPicker(channels: result.channels, plugin: plugin, bridge: bridge)
|
|
227
243
|
}
|
|
228
244
|
}
|
|
229
245
|
}
|
|
230
246
|
}
|
|
231
247
|
}
|
|
232
248
|
}
|
|
249
|
+
return true
|
|
233
250
|
}
|
|
234
251
|
|
|
235
252
|
private func presentChannelPicker(channels: [[String: Any]], plugin: CapacitorUpdaterPlugin, bridge: CAPBridgeProtocol) {
|
|
@@ -338,7 +355,8 @@ extension UIWindow {
|
|
|
338
355
|
let setResult = updater.setChannel(
|
|
339
356
|
channel: name,
|
|
340
357
|
defaultChannelKey: "CapacitorUpdater.defaultChannel",
|
|
341
|
-
allowSetDefaultChannel: plugin.allowSetDefaultChannel
|
|
358
|
+
allowSetDefaultChannel: plugin.allowSetDefaultChannel,
|
|
359
|
+
configDefaultChannel: plugin.getConfig().getString("defaultChannel", "") ?? ""
|
|
342
360
|
)
|
|
343
361
|
|
|
344
362
|
if !setResult.error.isEmpty {
|