craft-native 0.0.164 → 0.0.166
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/dist/android/templates/AndroidManifest.xml.template +1 -1
- package/dist/android/templates/MainActivityNative.kt.template +416 -36
- package/dist/api/mobile.d.ts +25 -1
- package/dist/cli.js +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/ios/src/index.d.ts +1 -0
- package/dist/ios/src/index.js +138 -88
- package/dist/ios/src/source-audit.d.ts +8 -0
- package/dist/ios/templates/CraftApp.swift +63 -2
- package/dist/ios/templates/CraftHealth.swift +214 -39
- package/dist/ios/templates/CraftHeartRate.swift +34 -4
- package/dist/ios/templates/CraftHybrid.swift +140 -17
- package/dist/ios/templates/CraftNativeScreen.swift +320 -56
- package/dist/mobile.js +29 -0
- package/package.json +1 -1
|
@@ -1004,7 +1004,8 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
|
|
|
1004
1004
|
private var openingWeb = false
|
|
1005
1005
|
private var firstFrameLogged = false
|
|
1006
1006
|
private var tabBarHeight: CGFloat = 0
|
|
1007
|
-
private var tabSwipeRecognizers: [
|
|
1007
|
+
private var tabSwipeRecognizers: [UIGestureRecognizer] = []
|
|
1008
|
+
private var activeTabSwipeTarget: String?
|
|
1008
1009
|
|
|
1009
1010
|
init(config: CraftConfig) {
|
|
1010
1011
|
self.config = config
|
|
@@ -1047,28 +1048,137 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
|
|
|
1047
1048
|
|
|
1048
1049
|
private func installTabSwipeGestures() {
|
|
1049
1050
|
guard tabSwipeRecognizers.isEmpty else { return }
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
view.addGestureRecognizer(recognizer)
|
|
1057
|
-
tabSwipeRecognizers.append(recognizer)
|
|
1058
|
-
}
|
|
1051
|
+
let recognizer = UIPanGestureRecognizer(target: self, action: #selector(tabSwipeRecognized(_:)))
|
|
1052
|
+
recognizer.delegate = self
|
|
1053
|
+
recognizer.cancelsTouchesInView = false
|
|
1054
|
+
recognizer.delaysTouchesBegan = false
|
|
1055
|
+
view.addGestureRecognizer(recognizer)
|
|
1056
|
+
tabSwipeRecognizers.append(recognizer)
|
|
1059
1057
|
}
|
|
1060
1058
|
|
|
1061
|
-
@objc private func tabSwipeRecognized(_ recognizer:
|
|
1059
|
+
@objc private func tabSwipeRecognized(_ recognizer: UIPanGestureRecognizer) {
|
|
1062
1060
|
let tabs = hybrid.tabIds
|
|
1063
1061
|
guard let index = tabs.firstIndex(of: currentTab), tabs.count > 1 else { return }
|
|
1064
|
-
let
|
|
1065
|
-
|
|
1066
|
-
|
|
1062
|
+
let translation = recognizer.translation(in: view).x
|
|
1063
|
+
let velocity = recognizer.velocity(in: view).x
|
|
1064
|
+
switch recognizer.state {
|
|
1065
|
+
case .changed:
|
|
1066
|
+
if activeTabSwipeTarget == nil {
|
|
1067
|
+
let direction = abs(translation) > 1 ? translation : velocity
|
|
1068
|
+
guard abs(direction) > 1 else { return }
|
|
1069
|
+
let next = direction < 0 ? index + 1 : index - 1
|
|
1070
|
+
guard tabs.indices.contains(next), beginInteractiveTabSwipe(to: tabs[next], direction: direction < 0 ? -1 : 1) else { return }
|
|
1071
|
+
activeTabSwipeTarget = tabs[next]
|
|
1072
|
+
}
|
|
1073
|
+
updateInteractiveTabSwipe(translation: translation)
|
|
1074
|
+
case .ended, .cancelled, .failed:
|
|
1075
|
+
guard let target = activeTabSwipeTarget else {
|
|
1076
|
+
guard recognizer.state == .ended else { return }
|
|
1077
|
+
let delta = abs(translation) >= 32 ? translation : velocity
|
|
1078
|
+
guard abs(delta) >= 32 else { return }
|
|
1079
|
+
let next = delta < 0 ? index + 1 : index - 1
|
|
1080
|
+
guard tabs.indices.contains(next) else { return }
|
|
1081
|
+
CraftChrome.shared.swipe(to: tabs[next])
|
|
1082
|
+
return
|
|
1083
|
+
}
|
|
1084
|
+
activeTabSwipeTarget = nil
|
|
1085
|
+
let complete = recognizer.state == .ended && (abs(translation) >= max(32, view.bounds.width * 0.28) || abs(velocity) > 700)
|
|
1086
|
+
finishInteractiveTabSwipe(completed: complete) { [weak self] in
|
|
1087
|
+
guard complete else {
|
|
1088
|
+
CraftChrome.shared.cancelTabSwipe()
|
|
1089
|
+
return
|
|
1090
|
+
}
|
|
1091
|
+
CraftChrome.shared.commitTabSwipe(to: target)
|
|
1092
|
+
self?.view.layoutIfNeeded()
|
|
1093
|
+
}
|
|
1094
|
+
default:
|
|
1095
|
+
break
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/// Prepare a mounted target beside the current stack. Native roots render
|
|
1100
|
+
/// immediately; a web root borrows the shared WebView and stays
|
|
1101
|
+
/// transparent until its page route catches up. Two web roots still use
|
|
1102
|
+
/// the route-aware fallback because one WebView cannot render both pages.
|
|
1103
|
+
private func beginInteractiveTabSwipe(to id: String, direction: CGFloat) -> Bool {
|
|
1104
|
+
guard let current = visibleStack, let target = stacks[id],
|
|
1105
|
+
current.view !== target.view,
|
|
1106
|
+
!(current.topViewController is CraftHybridWebSlot && target.topViewController is CraftHybridWebSlot),
|
|
1107
|
+
view.bounds.width > 0 else { return false }
|
|
1108
|
+
if let slot = target.topViewController as? CraftHybridWebSlot {
|
|
1109
|
+
// There is one shared WebView, so a web-to-web gesture cannot
|
|
1110
|
+
// render two pages at once. A native-to-web gesture can: move the
|
|
1111
|
+
// WebView into the hidden target slot and let its route catch up
|
|
1112
|
+
// while the slot follows the finger. It stays transparent until
|
|
1113
|
+
// the page reports that it reached the target path.
|
|
1114
|
+
slot.awaitingPage = true
|
|
1115
|
+
adoptWeb(into: slot, allowHiddenStack: true)
|
|
1116
|
+
webHost?.view.alpha = 0
|
|
1117
|
+
hybrid.navigatePage(slot.path) { [weak self, weak slot] depth in
|
|
1118
|
+
guard let self, let slot else { return }
|
|
1119
|
+
slot.depth = depth
|
|
1120
|
+
guard self.activeTabSwipeTarget == id,
|
|
1121
|
+
CraftHybridRoutes.normalize(self.hybrid.pagePath ?? "") == CraftHybridRoutes.normalize(slot.path) else { return }
|
|
1122
|
+
self.reveal(slot)
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
target.view.isHidden = false
|
|
1126
|
+
let width = view.bounds.width
|
|
1127
|
+
target.view.frame = view.bounds.offsetBy(dx: direction * width, dy: 0)
|
|
1128
|
+
view.bringSubviewToFront(target.view)
|
|
1129
|
+
CraftChrome.shared.beginTabSwipe(to: id)
|
|
1130
|
+
return true
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
private func updateInteractiveTabSwipe(translation: CGFloat) {
|
|
1134
|
+
guard let targetId = activeTabSwipeTarget, let current = visibleStack,
|
|
1135
|
+
let target = stacks[targetId] else { return }
|
|
1136
|
+
let width = max(1, view.bounds.width)
|
|
1137
|
+
let start = target.view.frame.minX >= width * 0.5 ? width : -width
|
|
1138
|
+
let offset = min(width, max(-width, translation))
|
|
1139
|
+
target.view.frame.origin.x = start + offset
|
|
1140
|
+
current.view.frame.origin.x = offset
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
private func finishInteractiveTabSwipe(completed: Bool, completion: @escaping () -> Void) {
|
|
1144
|
+
guard let targetId = activeTabSwipeTarget, let current = visibleStack,
|
|
1145
|
+
let target = stacks[targetId] else { completion(); return }
|
|
1146
|
+
let width = view.bounds.width
|
|
1147
|
+
let direction: CGFloat = target.view.frame.minX >= 0 ? 1 : -1
|
|
1148
|
+
let end = completed ? 0 : direction * width
|
|
1149
|
+
let duration = CraftChrome.motion(.easeOut(duration: completed ? 0.22 : 0.18)) == nil ? 0 : 0.22
|
|
1150
|
+
UIView.animate(withDuration: duration, animations: {
|
|
1151
|
+
target.view.frame.origin.x = end
|
|
1152
|
+
current.view.frame.origin.x = completed ? -direction * width : 0
|
|
1153
|
+
}, completion: { _ in
|
|
1154
|
+
if !completed {
|
|
1155
|
+
target.view.isHidden = true
|
|
1156
|
+
target.view.frame = self.view.bounds
|
|
1157
|
+
current.view.frame = self.view.bounds
|
|
1158
|
+
if target.topViewController is CraftHybridWebSlot,
|
|
1159
|
+
current.topViewController is CraftNativeScreenController {
|
|
1160
|
+
self.parkWeb()
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
completion()
|
|
1164
|
+
})
|
|
1067
1165
|
}
|
|
1068
1166
|
|
|
1069
1167
|
private func horizontalScrollAt(_ point: CGPoint, in root: UIView) -> Bool {
|
|
1070
1168
|
let local = root.convert(point, from: view)
|
|
1071
1169
|
guard root.bounds.contains(local), let hit = root.hitTest(local, with: nil) else { return false }
|
|
1170
|
+
// WKWebView keeps its scrolling UIScrollView below the hosting view
|
|
1171
|
+
// that owns the native slot, so it is not necessarily on the hit
|
|
1172
|
+
// view's superview chain. Treat an overflowing page as its own
|
|
1173
|
+
// horizontal gesture surface before the shell claims the swipe.
|
|
1174
|
+
if let webView = CraftChrome.shared.webView,
|
|
1175
|
+
hit === webView || hit.isDescendant(of: webView) || webView.isDescendant(of: hit) {
|
|
1176
|
+
let scroll = webView.scrollView
|
|
1177
|
+
if scroll.isScrollEnabled,
|
|
1178
|
+
scroll.alwaysBounceHorizontal || scroll.contentSize.width > scroll.bounds.width + 1 {
|
|
1179
|
+
return true
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1072
1182
|
var candidate: UIView? = hit
|
|
1073
1183
|
while let current = candidate {
|
|
1074
1184
|
if let scroll = current as? UIScrollView,
|
|
@@ -1193,6 +1303,7 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
|
|
|
1193
1303
|
}
|
|
1194
1304
|
currentTab = id
|
|
1195
1305
|
for (key, other) in stacks { other.view.isHidden = key != id }
|
|
1306
|
+
nav.view.frame = view.bounds
|
|
1196
1307
|
view.bringSubviewToFront(nav.view)
|
|
1197
1308
|
if let slot = nav.topViewController as? CraftHybridWebSlot, slot.isViewLoaded {
|
|
1198
1309
|
adoptWeb(into: slot)
|
|
@@ -1206,8 +1317,9 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
|
|
|
1206
1317
|
pendingTab = nil
|
|
1207
1318
|
}
|
|
1208
1319
|
|
|
1209
|
-
func adoptWeb(into slot: CraftHybridWebSlot) {
|
|
1210
|
-
guard let webHost,
|
|
1320
|
+
func adoptWeb(into slot: CraftHybridWebSlot, allowHiddenStack: Bool = false) {
|
|
1321
|
+
guard let webHost,
|
|
1322
|
+
allowHiddenStack || slot.navigationController === visibleStack || visibleStack == nil else { return }
|
|
1211
1323
|
attachWeb()
|
|
1212
1324
|
moveWeb(to: slot, in: slot.view)
|
|
1213
1325
|
webHost.view.alpha = slot.awaitingPage ? 0 : 1
|
|
@@ -1333,6 +1445,13 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
|
|
|
1333
1445
|
|
|
1334
1446
|
func pageNavigated(path: String, direction: String, depth: Int, byHost: Bool) {
|
|
1335
1447
|
let owner = CraftHybridRoutes.tab(owning: path, among: hybrid.tabIds)
|
|
1448
|
+
if let targetId = activeTabSwipeTarget,
|
|
1449
|
+
let target = stacks[targetId]?.topViewController as? CraftHybridWebSlot,
|
|
1450
|
+
owner == targetId || CraftHybridRoutes.normalize(path) == CraftHybridRoutes.normalize(target.path) {
|
|
1451
|
+
target.depth = depth
|
|
1452
|
+
target.awaitingPage = false
|
|
1453
|
+
reveal(target)
|
|
1454
|
+
}
|
|
1336
1455
|
if let pending = pendingTab, owner == pending.id || direction == "tab" {
|
|
1337
1456
|
show(tab: pending.id)
|
|
1338
1457
|
}
|
|
@@ -1521,6 +1640,11 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
|
|
|
1521
1640
|
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
1522
1641
|
if tabSwipeRecognizers.contains(where: { $0 === gestureRecognizer }) {
|
|
1523
1642
|
guard hybrid.tabIds.count > 1, let top = visibleStack?.topViewController else { return false }
|
|
1643
|
+
guard CraftChrome.shared.tabSwipeEnabled else { return false }
|
|
1644
|
+
if let pan = gestureRecognizer as? UIPanGestureRecognizer {
|
|
1645
|
+
let velocity = pan.velocity(in: view)
|
|
1646
|
+
guard abs(velocity.x) > abs(velocity.y) else { return false }
|
|
1647
|
+
}
|
|
1524
1648
|
if let native = top as? CraftNativeScreenController,
|
|
1525
1649
|
!native.tabSwipeEnabled || !native.contentSwipeBack { return false }
|
|
1526
1650
|
return !horizontalScrollAt(gestureRecognizer.location(in: view), in: top.view)
|
|
@@ -1533,7 +1657,6 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
|
|
|
1533
1657
|
return false
|
|
1534
1658
|
}
|
|
1535
1659
|
if let native = nav.topViewController as? CraftNativeScreenController,
|
|
1536
|
-
gestureRecognizer !== nav.interactivePopGestureRecognizer,
|
|
1537
1660
|
nativeScreenContainsHorizontalScroll(native) {
|
|
1538
1661
|
let moved = (gestureRecognizer as? UIPanGestureRecognizer)?.translation(in: nav.view).x ?? 0
|
|
1539
1662
|
if gestureRecognizer.location(in: nav.view).x - moved > 32 { return false }
|