Przeglądaj źródła

Fix settings links placeholders and Rate Us action reliability.

Move settings and paywall links to plist-configured placeholders, show Rate Us for all users, and route Rate Us through robust App Store fallbacks with button-based settings row actions.

Made-with: Cursor
huzaifahayat12 1 tydzień temu
rodzic
commit
634f9ee6d7
2 zmienionych plików z 78 dodań i 18 usunięć
  1. 14 0
      Info.plist
  2. 64 18
      meetings_app/ViewController.swift

+ 14 - 0
Info.plist

@@ -30,6 +30,20 @@
30
 	<string>Camera is used for video meetings you open inside this app.</string>
30
 	<string>Camera is used for video meetings you open inside this app.</string>
31
 	<key>NSMicrophoneUsageDescription</key>
31
 	<key>NSMicrophoneUsageDescription</key>
32
 	<string>Microphone is used for audio in meetings you open inside this app.</string>
32
 	<string>Microphone is used for audio in meetings you open inside this app.</string>
33
+	<key>AppLaunchPlaceholderURL</key>
34
+	<string>https://example.com/app-link-coming-soon</string>
35
+	<key>AppShareURL</key>
36
+	<string>https://example.com/app-link-coming-soon</string>
37
+	<key>SupportURL</key>
38
+	<string>https://example.com/app-link-coming-soon</string>
39
+	<key>MoreAppsURL</key>
40
+	<string>https://example.com/app-link-coming-soon</string>
41
+	<key>PrivacyPolicyURL</key>
42
+	<string>https://example.com/app-link-coming-soon</string>
43
+	<key>TermsOfServiceURL</key>
44
+	<string>https://example.com/app-link-coming-soon</string>
45
+	<key>RateUsURL</key>
46
+	<string>https://apps.apple.com/pk/app/meeting-app-for-google-meet/id6654920763?mt=12</string>
33
 
47
 
34
 	<key>CFBundleURLTypes</key>
48
 	<key>CFBundleURLTypes</key>
35
 	<array>
49
 	<array>

+ 64 - 18
meetings_app/ViewController.swift

@@ -696,6 +696,51 @@ private extension ViewController {
696
         }
696
         }
697
     }
697
     }
698
 
698
 
699
+    private func openRateUsDestination() {
700
+        let configured = (Bundle.main.object(forInfoDictionaryKey: "RateUsURL") as? String)?
701
+            .trimmingCharacters(in: .whitespacesAndNewlines)
702
+        let placeholder = (Bundle.main.object(forInfoDictionaryKey: "AppLaunchPlaceholderURL") as? String)?
703
+            .trimmingCharacters(in: .whitespacesAndNewlines)
704
+        let hardcodedRateUsURL = "https://apps.apple.com/pk/app/meeting-app-for-google-meet/id6654920763?mt=12"
705
+
706
+        var candidateStrings = [String]()
707
+        if let configured, !configured.isEmpty {
708
+            candidateStrings.append(configured)
709
+            if let appID = extractAppleAppID(from: configured) {
710
+                candidateStrings.append("macappstore://apps.apple.com/app/id\(appID)")
711
+                candidateStrings.append("macappstore://itunes.apple.com/app/id\(appID)")
712
+            }
713
+        }
714
+        candidateStrings.append(hardcodedRateUsURL)
715
+        candidateStrings.append("macappstore://apps.apple.com/app/id6654920763")
716
+        candidateStrings.append("macappstore://itunes.apple.com/app/id6654920763")
717
+        if let placeholder, !placeholder.isEmpty {
718
+            candidateStrings.append(placeholder)
719
+        }
720
+
721
+        for candidate in candidateStrings {
722
+            guard let url = URL(string: candidate) else { continue }
723
+            if NSWorkspace.shared.open(url) {
724
+                return
725
+            }
726
+        }
727
+
728
+        showSimpleAlert(
729
+            title: "Unable to Open Rate Page",
730
+            message: "Could not open the App Store rating page. Please try again."
731
+        )
732
+
733
+        requestAppRatingIfEligible(markAsRated: true)
734
+    }
735
+
736
+    private func extractAppleAppID(from urlString: String) -> String? {
737
+        guard let match = urlString.range(of: "id[0-9]+", options: .regularExpression) else {
738
+            return nil
739
+        }
740
+        let token = String(urlString[match])
741
+        return String(token.dropFirst())
742
+    }
743
+
699
     private func openInSafari(url: URL) {
744
     private func openInSafari(url: URL) {
700
         guard let safariAppURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Safari") else {
745
         guard let safariAppURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Safari") else {
701
             NSWorkspace.shared.open(url)
746
             NSWorkspace.shared.open(url)
@@ -836,18 +881,19 @@ private extension ViewController {
836
         case .rateUs:
881
         case .rateUs:
837
             settingsPopover?.performClose(nil)
882
             settingsPopover?.performClose(nil)
838
             settingsPopover = nil
883
             settingsPopover = nil
839
-            requestAppRatingIfEligible(markAsRated: true)
884
+            openRateUsDestination()
840
         case .support:
885
         case .support:
841
             settingsPopover?.performClose(nil)
886
             settingsPopover?.performClose(nil)
842
             settingsPopover = nil
887
             settingsPopover = nil
843
-            if let url = URL(string: "https://support.google.com/meet") {
888
+            if let supportURL = Bundle.main.object(forInfoDictionaryKey: "SupportURL") as? String,
889
+               let url = URL(string: supportURL) {
844
                 openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
890
                 openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
845
             }
891
             }
846
         case .moreApps:
892
         case .moreApps:
847
             settingsPopover?.performClose(nil)
893
             settingsPopover?.performClose(nil)
848
             settingsPopover = nil
894
             settingsPopover = nil
849
-            // Replace with your App Store developer page URL.
850
-            if let url = URL(string: "https://apps.apple.com/developer/id0000000000") {
895
+            if let moreAppsURL = Bundle.main.object(forInfoDictionaryKey: "MoreAppsURL") as? String,
896
+               let url = URL(string: moreAppsURL) {
851
                 openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
897
                 openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
852
             }
898
             }
853
         case .shareApp:
899
         case .shareApp:
@@ -1056,10 +1102,11 @@ private extension ViewController {
1056
     @objc private func paywallFooterLinkClicked(_ sender: NSClickGestureRecognizer) {
1102
     @objc private func paywallFooterLinkClicked(_ sender: NSClickGestureRecognizer) {
1057
         guard let view = sender.view else { return }
1103
         guard let view = sender.view else { return }
1058
         let text = (view.subviews.first { $0 is NSTextField } as? NSTextField)?.stringValue ?? "Link"
1104
         let text = (view.subviews.first { $0 is NSTextField } as? NSTextField)?.stringValue ?? "Link"
1105
+        let defaultURL = (Bundle.main.object(forInfoDictionaryKey: "AppLaunchPlaceholderURL") as? String) ?? "https://example.com/app-link-coming-soon"
1059
         let map: [String: String] = [
1106
         let map: [String: String] = [
1060
-            "Privacy Policy": "https://policies.google.com/privacy",
1061
-            "Support": "https://support.google.com/meet",
1062
-            "Terms of Services": "https://policies.google.com/terms"
1107
+            "Privacy Policy": (Bundle.main.object(forInfoDictionaryKey: "PrivacyPolicyURL") as? String) ?? defaultURL,
1108
+            "Support": (Bundle.main.object(forInfoDictionaryKey: "SupportURL") as? String) ?? defaultURL,
1109
+            "Terms of Services": (Bundle.main.object(forInfoDictionaryKey: "TermsOfServiceURL") as? String) ?? defaultURL
1063
         ]
1110
         ]
1064
         if let urlString = map[text], let url = URL(string: urlString) {
1111
         if let urlString = map[text], let url = URL(string: urlString) {
1065
             openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
1112
             openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
@@ -1251,7 +1298,7 @@ private extension ViewController {
1251
     }
1298
     }
1252
 
1299
 
1253
     private var shouldShowRateUsInSettings: Bool {
1300
     private var shouldShowRateUsInSettings: Bool {
1254
-        storeKitCoordinator.hasPremiumAccess && !userHasRated && hasReachedRatingUsageThreshold
1301
+        true
1255
     }
1302
     }
1256
 
1303
 
1257
     private func migrateLegacyRatingStateIfNeeded() {
1304
     private func migrateLegacyRatingStateIfNeeded() {
@@ -4360,9 +4407,14 @@ private final class SettingsMenuViewController: NSViewController {
4360
     }
4407
     }
4361
 
4408
 
4362
     private func settingsActionRow(icon: String, title: String, action: SettingsAction) -> NSView {
4409
     private func settingsActionRow(icon: String, title: String, action: SettingsAction) -> NSView {
4363
-        let row = HoverTrackingView()
4410
+        let row = HoverButton(title: "", target: self, action: #selector(settingsActionButtonPressed(_:)))
4411
+        row.tag = action.rawValue
4412
+        row.isBordered = false
4364
         row.translatesAutoresizingMaskIntoConstraints = false
4413
         row.translatesAutoresizingMaskIntoConstraints = false
4365
         row.heightAnchor.constraint(equalToConstant: 42).isActive = true
4414
         row.heightAnchor.constraint(equalToConstant: 42).isActive = true
4415
+        row.wantsLayer = true
4416
+        row.layer?.cornerRadius = 10
4417
+        row.layer?.backgroundColor = NSColor.clear.cgColor
4366
 
4418
 
4367
         let iconLabel = NSTextField(labelWithString: icon)
4419
         let iconLabel = NSTextField(labelWithString: icon)
4368
         iconLabel.translatesAutoresizingMaskIntoConstraints = false
4420
         iconLabel.translatesAutoresizingMaskIntoConstraints = false
@@ -4384,12 +4436,7 @@ private final class SettingsMenuViewController: NSViewController {
4384
             titleLabel.centerYAnchor.constraint(equalTo: row.centerYAnchor)
4436
             titleLabel.centerYAnchor.constraint(equalTo: row.centerYAnchor)
4385
         ])
4437
         ])
4386
 
4438
 
4387
-        let click = NSClickGestureRecognizer(target: self, action: #selector(settingsActionClicked(_:)))
4388
-        row.addGestureRecognizer(click)
4389
-        row.identifier = NSUserInterfaceItemIdentifier(rawValue: "\(action.rawValue)")
4390
         row.onHoverChanged = { hovering in
4439
         row.onHoverChanged = { hovering in
4391
-            row.wantsLayer = true
4392
-            row.layer?.cornerRadius = 10
4393
             row.layer?.backgroundColor = (hovering ? self.palette.inputBackground : NSColor.clear).cgColor
4440
             row.layer?.backgroundColor = (hovering ? self.palette.inputBackground : NSColor.clear).cgColor
4394
         }
4441
         }
4395
         row.onHoverChanged?(false)
4442
         row.onHoverChanged?(false)
@@ -4401,12 +4448,11 @@ private final class SettingsMenuViewController: NSViewController {
4401
         onToggleDarkMode(sender.state == .on)
4448
         onToggleDarkMode(sender.state == .on)
4402
     }
4449
     }
4403
 
4450
 
4404
-    @objc private func settingsActionClicked(_ sender: NSClickGestureRecognizer) {
4405
-        guard let view = sender.view,
4406
-              let raw = Int(view.identifier?.rawValue ?? ""),
4407
-              let action = SettingsAction(rawValue: raw) else { return }
4451
+    @objc private func settingsActionButtonPressed(_ sender: NSButton) {
4452
+        guard let action = SettingsAction(rawValue: sender.tag) else { return }
4408
         onAction(action)
4453
         onAction(action)
4409
     }
4454
     }
4455
+
4410
 }
4456
 }
4411
 
4457
 
4412
 private extension ViewController {
4458
 private extension ViewController {