@craft-native/ios 0.0.189 → 0.0.192
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/package.json
CHANGED
package/templates/CraftApp.swift
CHANGED
|
@@ -614,7 +614,10 @@ final class CraftChrome: ObservableObject {
|
|
|
614
614
|
let id: String
|
|
615
615
|
let title: String
|
|
616
616
|
let symbol: String
|
|
617
|
-
|
|
617
|
+
var badge: String?
|
|
618
|
+
var iconText: String? = nil
|
|
619
|
+
var accessibilityLabel: String? = nil
|
|
620
|
+
var image: String? = nil
|
|
618
621
|
}
|
|
619
622
|
|
|
620
623
|
@Published var tabs: [Tab] = []
|
|
@@ -668,6 +671,23 @@ final class CraftChrome: ObservableObject {
|
|
|
668
671
|
/// the bar away while it shows (`tabBarHidden`, CraftNativeScreen.swift);
|
|
669
672
|
/// the next screen shown says again. The page's own hideTabBar is apart.
|
|
670
673
|
private(set) var hiddenByScreen = false
|
|
674
|
+
|
|
675
|
+
/// Change one tab's icon without replacing tabs, navigation stacks or selection.
|
|
676
|
+
func updateTab(_ item: [String: Any]) {
|
|
677
|
+
guard let id = item["id"] as? String, let index = tabs.firstIndex(where: { $0.id == id }) else { return }
|
|
678
|
+
if item.keys.contains("iconText") {
|
|
679
|
+
tabs[index].iconText = (item["iconText"] as? String).flatMap { $0.isEmpty ? nil : String($0.prefix(8)) }
|
|
680
|
+
}
|
|
681
|
+
if item.keys.contains("accessibilityLabel") {
|
|
682
|
+
tabs[index].accessibilityLabel = (item["accessibilityLabel"] as? String).map { String($0.prefix(160)) }
|
|
683
|
+
}
|
|
684
|
+
if item.keys.contains("image") {
|
|
685
|
+
tabs[index].image = (item["image"] as? String).flatMap { URL(string: $0)?.scheme == "https" ? $0 : nil }
|
|
686
|
+
}
|
|
687
|
+
if item.keys.contains("badge") {
|
|
688
|
+
tabs[index].badge = (item["badge"] as? String).flatMap { $0.isEmpty ? nil : String($0.prefix(8)) }
|
|
689
|
+
}
|
|
690
|
+
}
|
|
671
691
|
func setHiddenByScreen(_ hidden: Bool) {
|
|
672
692
|
guard hiddenByScreen != hidden else { return }
|
|
673
693
|
hiddenByScreen = hidden
|
|
@@ -941,9 +961,28 @@ struct CraftTabBarView: View {
|
|
|
941
961
|
Button { chrome.tap(tab) } label: {
|
|
942
962
|
VStack(spacing: 3) {
|
|
943
963
|
ZStack(alignment: .topTrailing) {
|
|
944
|
-
|
|
945
|
-
|
|
964
|
+
if let image = tab.image, let url = URL(string: image) {
|
|
965
|
+
AsyncImage(url: url) { image in
|
|
966
|
+
image.resizable().scaledToFill()
|
|
967
|
+
} placeholder: {
|
|
968
|
+
Image(systemName: symbol(tab.symbol, selected: isSelected))
|
|
969
|
+
}
|
|
970
|
+
.frame(width: iconSize + 4, height: iconSize + 4)
|
|
971
|
+
.clipShape(Circle())
|
|
946
972
|
.frame(height: iconSize + 6)
|
|
973
|
+
} else if let text = tab.iconText {
|
|
974
|
+
Text(text)
|
|
975
|
+
.font(.system(size: iconSize, weight: .semibold, design: .rounded))
|
|
976
|
+
.monospacedDigit()
|
|
977
|
+
.foregroundStyle(Color.red)
|
|
978
|
+
.lineLimit(1)
|
|
979
|
+
.minimumScaleFactor(0.7)
|
|
980
|
+
.frame(height: iconSize + 6)
|
|
981
|
+
} else {
|
|
982
|
+
Image(systemName: symbol(tab.symbol, selected: isSelected))
|
|
983
|
+
.font(.system(size: iconSize, weight: isSelected ? .semibold : .regular))
|
|
984
|
+
.frame(height: iconSize + 6)
|
|
985
|
+
}
|
|
947
986
|
if let badge = tab.badge {
|
|
948
987
|
Text(badge)
|
|
949
988
|
.font(.system(size: badgeSize, weight: .bold))
|
|
@@ -975,7 +1014,7 @@ struct CraftTabBarView: View {
|
|
|
975
1014
|
Image(systemName: symbol(tab.symbol, selected: isSelected))
|
|
976
1015
|
Text(tab.title)
|
|
977
1016
|
}
|
|
978
|
-
.accessibilityLabel(tab.badge.map { "\(tab.title), \($0)" } ?? tab.title)
|
|
1017
|
+
.accessibilityLabel(tab.accessibilityLabel ?? tab.badge.map { "\(tab.title), \($0)" } ?? tab.title)
|
|
979
1018
|
.accessibilityAddTraits(isSelected ? [.isSelected, .isButton] : .isButton)
|
|
980
1019
|
}
|
|
981
1020
|
}
|
|
@@ -5709,9 +5748,11 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
5709
5748
|
return
|
|
5710
5749
|
}
|
|
5711
5750
|
var items: [Any] = []
|
|
5712
|
-
|
|
5751
|
+
let title = options["title"] as? String ?? ""
|
|
5713
5752
|
if let text = options["text"] as? String, !text.isEmpty { items.append(text) }
|
|
5714
|
-
if let urlString = options["url"] as? String, let url = URL(string: urlString) {
|
|
5753
|
+
if let urlString = options["url"] as? String, let url = URL(string: urlString) {
|
|
5754
|
+
items.append(CraftShareLink(url: url, title: title))
|
|
5755
|
+
} else if !title.isEmpty { items.append(title) }
|
|
5715
5756
|
if let files = options["files"] as? [String] {
|
|
5716
5757
|
for file in files {
|
|
5717
5758
|
if let url = URL(string: file), url.isFileURL { items.append(url) }
|
|
@@ -4355,6 +4355,9 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
|
|
|
4355
4355
|
/// `craft.navigation.setOptions`: this screen's bar. Every key is
|
|
4356
4356
|
/// optional and only the keys given change.
|
|
4357
4357
|
private func applyNavigationOptions(_ options: [String: Any]) {
|
|
4358
|
+
if let tab = options["tabItem"] as? [String: Any] {
|
|
4359
|
+
CraftChrome.shared.updateTab(tab)
|
|
4360
|
+
}
|
|
4358
4361
|
// The app script has no screen, so no bar and no tab bar to change.
|
|
4359
4362
|
guard !isAppScript else { return }
|
|
4360
4363
|
if options.keys.contains("title") {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import AVFoundation
|
|
2
2
|
import Foundation
|
|
3
|
+
import LinkPresentation
|
|
3
4
|
import UIKit
|
|
4
5
|
import UserNotifications
|
|
5
6
|
|
|
@@ -687,6 +688,32 @@ enum CraftNativePush {
|
|
|
687
688
|
|
|
688
689
|
// MARK: - Share sheet
|
|
689
690
|
|
|
691
|
+
/// A URL stays a URL in Messages, AirDrop and Copy. Its title belongs in the
|
|
692
|
+
/// system preview and mail subject, rather than becoming a second text item.
|
|
693
|
+
final class CraftShareLink: NSObject, UIActivityItemSource {
|
|
694
|
+
let url: URL
|
|
695
|
+
let title: String
|
|
696
|
+
|
|
697
|
+
init(url: URL, title: String) {
|
|
698
|
+
self.url = url
|
|
699
|
+
self.title = title
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { url }
|
|
703
|
+
|
|
704
|
+
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { url }
|
|
705
|
+
|
|
706
|
+
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String { title }
|
|
707
|
+
|
|
708
|
+
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
|
|
709
|
+
let metadata = LPLinkMetadata()
|
|
710
|
+
metadata.originalURL = url
|
|
711
|
+
metadata.url = url
|
|
712
|
+
if !title.isEmpty { metadata.title = title }
|
|
713
|
+
return metadata
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
690
717
|
/// `craft.share` on a native screen: the system share sheet with the page's
|
|
691
718
|
/// options (`title`, `text`, `url`, `files`), plus `image`, a picture by URL
|
|
692
719
|
/// that is shared as the picture rather than as its address. Answers true
|
|
@@ -694,9 +721,11 @@ enum CraftNativePush {
|
|
|
694
721
|
enum CraftNativeShare {
|
|
695
722
|
static func share(_ options: [String: Any], done: @escaping (Result<Any, CraftNativeActionError>) -> Void) {
|
|
696
723
|
var items: [Any] = []
|
|
697
|
-
|
|
724
|
+
let title = options["title"] as? String ?? ""
|
|
698
725
|
if let text = options["text"] as? String, !text.isEmpty { items.append(text) }
|
|
699
|
-
if let address = options["url"] as? String, let url = URL(string: address) {
|
|
726
|
+
if let address = options["url"] as? String, let url = URL(string: address) {
|
|
727
|
+
items.append(CraftShareLink(url: url, title: title))
|
|
728
|
+
} else if !title.isEmpty { items.append(title) }
|
|
700
729
|
// A path or file URL, or a file craft.files made (`{ path, name }`).
|
|
701
730
|
let files = (options["files"] as? [Any] ?? []).compactMap { item -> String? in
|
|
702
731
|
item as? String ?? (item as? [String: Any])?["path"] as? String
|
|
@@ -715,7 +744,7 @@ enum CraftNativeShare {
|
|
|
715
744
|
if let error { done(.failure(CraftNativeActionError(code: "SHARE_ERROR", message: error.localizedDescription))) }
|
|
716
745
|
else { done(.success(completed)) }
|
|
717
746
|
}
|
|
718
|
-
if !CraftPresenter.present(sheet, from: nil, anchor:
|
|
747
|
+
if !CraftPresenter.present(sheet, from: nil, anchor: CraftPresenter.rect(options["anchor"])) {
|
|
719
748
|
done(.failure(CraftNativeActionError(code: "SHARE_ERROR", message: "Unable to present the share sheet")))
|
|
720
749
|
}
|
|
721
750
|
}
|