Browse Source

ios universal link

Tienson Qin 1 month ago
parent
commit
2f23188acc

+ 6 - 9
ios/App/App.xcodeproj/project.pbxproj

@@ -28,8 +28,8 @@
 		D3490CC62E7CE9EA00E796A6 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D3490CC52E7CE9EA00E796A6 /* WidgetKit.framework */; };
 		D3490CC82E7CE9EA00E796A6 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D3490CC72E7CE9EA00E796A6 /* SwiftUI.framework */; };
 		D3490CD52E7CE9EB00E796A6 /* shortcutsExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D3490CC42E7CE9EA00E796A6 /* shortcutsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
-		D39D1FDD2E7D6D8A00C903D1 /* LogseqIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39D1FDB2E7D6D8A00C903D1 /* LogseqIntents.swift */; };
-		D39D1FDE2E7D6D8A00C903D1 /* LogseqShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39D1FDC2E7D6D8A00C903D1 /* LogseqShortcuts.swift */; };
+		D39D1FE02E7DAFB000C903D1 /* LogseqIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39D1FDF2E7DAFB000C903D1 /* LogseqIntents.swift */; };
+		D39D1FE12E7DAFB000C903D1 /* LogseqIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39D1FDF2E7DAFB000C903D1 /* LogseqIntents.swift */; };
 		D3D62A0A275C92880003FBDC /* FileContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D62A09275C92880003FBDC /* FileContainer.swift */; };
 		D3D62A0C275C928F0003FBDC /* FileContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D62A0B275C928F0003FBDC /* FileContainer.m */; };
 		FE647FF427BDFEDE00F3206B /* FsWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE647FF327BDFEDE00F3206B /* FsWatcher.swift */; };
@@ -100,8 +100,7 @@
 		D3490CC52E7CE9EA00E796A6 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
 		D3490CC72E7CE9EA00E796A6 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
 		D3490CDA2E7CEA3900E796A6 /* shortcutsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = shortcutsExtension.entitlements; sourceTree = "<group>"; };
-		D39D1FDB2E7D6D8A00C903D1 /* LogseqIntents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogseqIntents.swift; sourceTree = "<group>"; };
-		D39D1FDC2E7D6D8A00C903D1 /* LogseqShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogseqShortcuts.swift; sourceTree = "<group>"; };
+		D39D1FDF2E7DAFB000C903D1 /* LogseqIntents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogseqIntents.swift; sourceTree = "<group>"; };
 		D3D62A09275C92880003FBDC /* FileContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileContainer.swift; sourceTree = "<group>"; };
 		D3D62A0B275C928F0003FBDC /* FileContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileContainer.m; sourceTree = "<group>"; };
 		DE5650F4AD4E2242AB9C012D /* Pods-Logseq.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Logseq.debug.xcconfig"; path = "Target Support Files/Pods-Logseq/Pods-Logseq.debug.xcconfig"; sourceTree = "<group>"; };
@@ -179,6 +178,7 @@
 		504EC3061FED79650016851F /* App */ = {
 			isa = PBXGroup;
 			children = (
+				D39D1FDF2E7DAFB000C903D1 /* LogseqIntents.swift */,
 				CBF2D2E12DE95970006338BE /* AppViewController.swift */,
 				CBF2D2D92DE83CB0006338BE /* UILocalPlugin.swift */,
 				5FF86329283B5ADB0047731B /* Utils.swift */,
@@ -187,8 +187,6 @@
 				D32752BC275496A60039291C /* App.entitlements */,
 				50379B222058CBB4000EE86E /* capacitor.config.json */,
 				504EC3071FED79650016851F /* AppDelegate.swift */,
-				D39D1FDB2E7D6D8A00C903D1 /* LogseqIntents.swift */,
-				D39D1FDC2E7D6D8A00C903D1 /* LogseqShortcuts.swift */,
 				504EC30B1FED79650016851F /* Main.storyboard */,
 				504EC30E1FED79650016851F /* Assets.xcassets */,
 				504EC3101FED79650016851F /* LaunchScreen.storyboard */,
@@ -314,7 +312,6 @@
 					504EC3031FED79650016851F = {
 						CreatedOnToolsVersion = 9.2;
 						LastSwiftMigration = 1250;
-						ProvisioningStyle = Automatic;
 					};
 					5FFF7D6927E343FA00B00DA8 = {
 						CreatedOnToolsVersion = 13.3;
@@ -421,8 +418,6 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				D39D1FDD2E7D6D8A00C903D1 /* LogseqIntents.swift in Sources */,
-				D39D1FDE2E7D6D8A00C903D1 /* LogseqShortcuts.swift in Sources */,
 				504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
 				5FF8632C283B5BFD0047731B /* Utils.m in Sources */,
 				FE647FF427BDFEDE00F3206B /* FsWatcher.swift in Sources */,
@@ -432,6 +427,7 @@
 				CBF2D2DA2DE83CB8006338BE /* UILocalPlugin.swift in Sources */,
 				D3D62A0C275C928F0003FBDC /* FileContainer.m in Sources */,
 				7435D10F2704660B00AB88E0 /* FolderPicker.m in Sources */,
+				D39D1FE12E7DAFB000C903D1 /* LogseqIntents.swift in Sources */,
 				7435D10C2704659F00AB88E0 /* FolderPicker.swift in Sources */,
 				FE647FF627BDFEF500F3206B /* FsWatcher.m in Sources */,
 			);
@@ -450,6 +446,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				D39D1FE02E7DAFB000C903D1 /* LogseqIntents.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 4 - 4
ios/App/App/App.entitlements

@@ -4,10 +4,6 @@
 <dict>
 	<key>aps-environment</key>
 	<string>development</string>
-	<key>com.apple.developer.associated-domains</key>
-	<array>
-		<string>applinks:https://logseq.com</string>
-	</array>
 	<key>com.apple.developer.icloud-container-identifiers</key>
 	<array>
 		<string>iCloud.com.logseq.logseq</string>
@@ -24,5 +20,9 @@
 	<array>
 		<string>group.com.logseq.logseq</string>
 	</array>
+        <key>com.apple.developer.associated-domains</key>
+	<array>
+		<string>applinks:logseq.com</string>
+	</array>
 </dict>
 </plist>

+ 26 - 6
ios/App/App/AppDelegate.swift

@@ -28,11 +28,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         switch shortcutItem.type {
         case "logseq.quickadd":
             donateQuickAddShortcut()
-            openURL("logseq://go/quick-add")
+            openURL("logseq://mobile/go/quick-add")
             return true
         case "logseq.voice":
             donateAudioShortcut()
-            openURL("logseq://go/audio")
+            openURL("logseq://mobile/go/audio")
             return true
         default:
             return false
@@ -40,17 +40,37 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     }
 
     func application(_ application: UIApplication,
-                     continue userActivity: NSUserActivity,
-                     restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
+                 continue userActivity: NSUserActivity,
+                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
+
+        // Case 1: custom NSUserActivities
         if userActivity.activityType == "com.logseq.quickadd" {
-            openURL("logseq://go/quick-add")
+            openURL("logseq://mobile/go/quick-add")
             return true
         }
         if userActivity.activityType == "com.logseq.audio" {
-            openURL("logseq://go/audio")
+            openURL("logseq://mobile/go/audio")
+            return true
+        }
+
+        // Case 2: Universal Links
+        if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
+           let url = userActivity.webpageURL {
+            print("🌐 Universal link opened:", url)
+
+            // Forward to Capacitor (so JS gets `appUrlOpen`)
+            NotificationCenter.default.post(
+              name: .capacitorOpenURL,
+              object: nil,
+              userInfo: [
+                "url": url,
+                "options": [:]
+              ]
+            )
             return true
         }
 
+        // Default: let Capacitor handle other cases
         return ApplicationDelegateProxy.shared.application(application,
                                                            continue: userActivity,
                                                            restorationHandler: restorationHandler)

+ 35 - 10
ios/App/App/LogseqIntents.swift

@@ -1,26 +1,51 @@
 import AppIntents
 import UIKit
 
+@available(iOS 18.0, *)
 struct QuickAddIntent: AppIntent {
     static var title: LocalizedStringResource = "Quick Add"
     static var description = IntentDescription("Open Logseq Quick Add")
 
-    func perform() async throws -> some IntentResult {
-        if let url = URL(string: "logseq://go/quick-add") {
-            await UIApplication.shared.open(url)
-        }
-        return .result()
+    func perform() async throws -> some IntentResult & OpensIntent {
+        let url = URL(string: "https://logseq.com/mobile/go/quick-add")!
+        return .result(opensIntent: OpenURLIntent(url))
     }
 }
 
+@available(iOS 18.0, *)
 struct RecordAudioIntent: AppIntent {
     static var title: LocalizedStringResource = "Record Audio"
     static var description = IntentDescription("Open Logseq Record Audio")
 
-    func perform() async throws -> some IntentResult {
-        if let url = URL(string: "logseq://go/audio") {
-            await UIApplication.shared.open(url)
-        }
-        return .result()
+    func perform() async throws -> some IntentResult & OpensIntent {
+        let url = URL(string: "https://logseq.com/mobile/go/audio")!
+        return .result(opensIntent: OpenURLIntent(url))
+    }
+}
+
+@available(iOS 18.0, *)
+struct LogseqShortcuts: AppShortcutsProvider {
+    static var appShortcuts: [AppShortcut] {
+        return [
+          AppShortcut(
+            intent: QuickAddIntent(),
+            phrases: [
+              "Quick add in \(.applicationName)",
+              "Add note in \(.applicationName)"
+            ],
+            shortTitle: "Quick Add",
+            systemImageName: "plus.circle"
+          ),
+
+          AppShortcut(
+            intent: RecordAudioIntent(),
+            phrases: [
+              "Record audio in \(.applicationName)",
+              "Start recording in \(.applicationName)"
+            ],
+            shortTitle: "Record Audio",
+            systemImageName: "waveform"
+          )
+        ]
     }
 }

+ 0 - 27
ios/App/App/LogseqShortcuts.swift

@@ -1,27 +0,0 @@
-import AppIntents
-
-struct LogseqShortcuts: AppShortcutsProvider {
-    static var appShortcuts: [AppShortcut] {
-        return [
-            AppShortcut(
-                intent: QuickAddIntent(),
-                phrases: [
-                    "Quick add in \(.applicationName)",
-                    "Add note in \(.applicationName)"
-                ],
-                shortTitle: "Quick Add",
-                systemImageName: "plus.circle"
-            ),
-            
-            AppShortcut(
-                intent: RecordAudioIntent(),
-                phrases: [
-                    "Record audio in \(.applicationName)",
-                    "Start recording in \(.applicationName)"
-                ],
-                shortTitle: "Record Audio",
-                systemImageName: "waveform"
-            )
-        ]
-    }
-}

+ 3 - 3
ios/App/shortcuts/shortcuts.swift

@@ -27,7 +27,7 @@ struct ShortcutsEntryView: View {
     var entry: Provider.Entry
 
     var body: some View {
-        Link(destination: URL(string: "logseq://go/quick-add")!) {
+        Link(destination: URL(string: "logseq://mobile/go/quick-add")!) {
             VStack(alignment: .leading, spacing: 0) {
                 // Top heading
                 Text("Logseq")
@@ -46,7 +46,7 @@ struct ShortcutsEntryView: View {
                 // Bottom buttons row
                 HStack(spacing: 8) {
                     // Left button (audio waves)
-                    Link(destination: URL(string: "logseq://go/audio")!) {
+                    Link(destination: URL(string: "logseq://mobile/go/audio")!) {
                         Image(systemName: "waveform")
                             .font(.body)
                             .foregroundColor(Color(hex: "#002b36"))
@@ -57,7 +57,7 @@ struct ShortcutsEntryView: View {
                     }
 
                     // Right button (quick add)
-                    Link(destination: URL(string: "logseq://go/quick-add")!) {
+                    Link(destination: URL(string: "logseq://mobile/go/quick-add")!) {
                         Image(systemName: "plus")
                             .font(.body)
                             .foregroundColor(.white)

+ 1 - 23
ios/App/shortcuts/shortcutsControl.swift

@@ -30,7 +30,7 @@ struct RecordAudioButton: ControlWidget {
         StaticControlConfiguration(
           kind: "com.logseq.logseq.recordAudioButton"
         ) {
-            ControlWidgetButton(action: RecordAudioIntent()) {   // ✅ fixed
+            ControlWidgetButton(action: RecordAudioIntent()) {
                 Label("Record Audio", systemImage: "waveform")
             }
         }
@@ -38,25 +38,3 @@ struct RecordAudioButton: ControlWidget {
           .description("Record Audio.")
     }
 }
-
-@available(iOS 18.0, *)
-struct QuickAddIntent: AppIntent {
-    static var title: LocalizedStringResource = "Quick Add"
-    static var description = IntentDescription("Open Logseq Quick Add")
-
-    // TODO: use https://logseq.com/mobile/go/quick-add
-    func perform() async throws -> some IntentResult {
-        .result()
-    }
-}
-
-@available(iOS 18.0, *)
-struct RecordAudioIntent: AppIntent {
-    static var title: LocalizedStringResource = "Record Audio"
-    static var description = IntentDescription("Open Logseq Record Audio")
-
-    // TODO: https://logseq.com/mobile/go/record-audio
-    func perform() async throws -> some IntentResult {
-        .result()
-    }
-}

+ 4 - 2
src/main/mobile/deeplink.cljs

@@ -29,10 +29,12 @@
                    (remove #(= (:url %) config/demo-repo))
                    (map :url))
         repo-names (map #(get-graph-name-fn %) repos)]
+    (prn :debug :hostname hostname
+         :pathname pathname)
     (cond
-      (and (= hostname "go") (= pathname "/audio"))
+      (and (= hostname "mobile") (= pathname "/go/audio"))
       (state/pub-event! [:mobile/start-audio-record])
-      (and (= hostname "go") (= pathname "/quick-add"))
+      (and (= hostname "mobile") (= pathname "/go/quick-add"))
       (state/pub-event! [:dialog/mobile-quick-add])
       (= hostname "graph")
       (let [graph-name (some-> pathname