From 18ce21c2c65060cc6948add21b567a5bc6221887 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 24 May 2021 19:07:47 -0400 Subject: [PATCH] Add Open in Tusker action extension --- OpenInTusker/Action.js | 29 +++ OpenInTusker/ActionViewController.swift | 100 ++++++++++ .../Base.lproj/MainInterface.storyboard | 65 +++++++ OpenInTusker/Info.plist | 55 ++++++ .../AppIcon.appiconset/1024x1024@1x.png | Bin 0 -> 6002 bytes .../AppIcon.appiconset/60x60@2x.png | Bin 0 -> 900 bytes .../AppIcon.appiconset/60x60@3x.png | Bin 0 -> 1264 bytes .../AppIcon.appiconset/76x76@2x.png | Bin 0 -> 1094 bytes .../AppIcon.appiconset/83.5x83.5@2x.png | Bin 0 -> 1182 bytes .../AppIcon.appiconset/Contents.json | 103 ++++++++++ OpenInTusker/Media.xcassets/Contents.json | 6 + .../TouchBarBezel.colorset/Contents.json | 14 ++ OpenInTusker/OpenInTusker.entitlements | 10 + Tusker.xcodeproj/project.pbxproj | 177 +++++++++++++++++- Tusker/AppDelegate.swift | 3 - Tusker/MainSceneDelegate.swift | 19 +- ...ountSwitchingContainerViewController.swift | 9 +- .../Main/MainSidebarViewController.swift | 2 +- .../Main/MainSplitViewController.swift | 60 ++++-- .../Main/MainTabBarViewController.swift | 34 +++- .../Main/TuskerRootViewController.swift | 1 + .../Screens/Search/SearchViewController.swift | 2 +- 22 files changed, 655 insertions(+), 34 deletions(-) create mode 100644 OpenInTusker/Action.js create mode 100644 OpenInTusker/ActionViewController.swift create mode 100644 OpenInTusker/Base.lproj/MainInterface.storyboard create mode 100644 OpenInTusker/Info.plist create mode 100644 OpenInTusker/Media.xcassets/AppIcon.appiconset/1024x1024@1x.png create mode 100644 OpenInTusker/Media.xcassets/AppIcon.appiconset/60x60@2x.png create mode 100644 OpenInTusker/Media.xcassets/AppIcon.appiconset/60x60@3x.png create mode 100644 OpenInTusker/Media.xcassets/AppIcon.appiconset/76x76@2x.png create mode 100644 OpenInTusker/Media.xcassets/AppIcon.appiconset/83.5x83.5@2x.png create mode 100644 OpenInTusker/Media.xcassets/AppIcon.appiconset/Contents.json create mode 100644 OpenInTusker/Media.xcassets/Contents.json create mode 100644 OpenInTusker/Media.xcassets/TouchBarBezel.colorset/Contents.json create mode 100644 OpenInTusker/OpenInTusker.entitlements diff --git a/OpenInTusker/Action.js b/OpenInTusker/Action.js new file mode 100644 index 00000000..932ba21b --- /dev/null +++ b/OpenInTusker/Action.js @@ -0,0 +1,29 @@ +// +// Action.js +// OpenInTusker +// +// Created by Shadowfacts on 5/22/21. +// Copyright © 2021 Shadowfacts. All rights reserved. +// + +var Action = function() {}; + +Action.prototype = { + + run: function(arguments) { + const results = { + url: window.location.href, + }; + const el = document.querySelector('link[rel=alternate][type="application/activity+json"]'); + if (el) { + results.activityPubURL = el.href; + } + arguments.completionFunction(results); + }, + + finalize: function(arguments) { + } + +}; + +var ExtensionPreprocessingJS = new Action(); diff --git a/OpenInTusker/ActionViewController.swift b/OpenInTusker/ActionViewController.swift new file mode 100644 index 00000000..ff89ef27 --- /dev/null +++ b/OpenInTusker/ActionViewController.swift @@ -0,0 +1,100 @@ +// +// ActionViewController.swift +// OpenInTusker +// +// Created by Shadowfacts on 5/23/21. +// Copyright © 2021 Shadowfacts. All rights reserved. +// + +import UIKit +import MobileCoreServices + +class ActionViewController: UIViewController { + + @IBOutlet weak var imageView: UIImageView! + + override func viewDidLoad() { + super.viewDidLoad() + + findURLFromWebPage { (components) in + if let components = components { + self.searchForURLInApp(components) + } else { + self.findURLItem { (components) in + if let components = components { + self.searchForURLInApp(components) + } + } + } + } + } + + private func findURLFromWebPage(completion: @escaping (URLComponents?) -> Void) { + for item in extensionContext!.inputItems as! [NSExtensionItem] { + for provider in item.attachments! { + guard provider.hasItemConformingToTypeIdentifier(kUTTypePropertyList as String) else { + continue + } + provider.loadItem(forTypeIdentifier: kUTTypePropertyList as String, options: nil) { (result, error) in + guard let result = result as? [String: Any], + let jsResult = result[NSExtensionJavaScriptPreprocessingResultsKey] as? [String: Any], + let urlString = jsResult["activityPubURL"] as? String ?? jsResult["url"] as? String, + let components = URLComponents(string: urlString) else { + completion(nil) + return + } + + completion(components) + } + return + } + } + + completion(nil) + } + + private func findURLItem(completion: @escaping (URLComponents?) -> Void) { + for item in extensionContext!.inputItems as! [NSExtensionItem] { + for provider in item.attachments! { + guard provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) else { + continue + } + provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { (result, error) in + guard let result = result as? URL, + let components = URLComponents(url: result, resolvingAgainstBaseURL: false) else { + completion(nil) + return + } + completion(components) + } + return + } + } + + completion(nil) + } + + private func searchForURLInApp(_ components: URLComponents) { + var components = components + components.scheme = "tusker" + self.openURL(components.url!) + self.extensionContext!.completeRequest(returningItems: nil, completionHandler: nil) + } + + @objc private func openURL(_ url: URL) { + var responder: UIResponder = self + while let parent = responder.next { + if let application = parent as? UIApplication { + application.perform(#selector(openURL(_:)), with: url) + break + } else { + responder = parent + } + } + } + + @IBAction func done() { + extensionContext!.completeRequest(returningItems: nil, completionHandler: nil) + } + +} diff --git a/OpenInTusker/Base.lproj/MainInterface.storyboard b/OpenInTusker/Base.lproj/MainInterface.storyboard new file mode 100644 index 00000000..5ba07252 --- /dev/null +++ b/OpenInTusker/Base.lproj/MainInterface.storyboard @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenInTusker/Info.plist b/OpenInTusker/Info.plist new file mode 100644 index 00000000..f974a9c3 --- /dev/null +++ b/OpenInTusker/Info.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Open in Tusker + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSExtension + + NSExtensionAttributes + + NSExtensionJavaScriptPreprocessingFile + Action + NSExtensionActivationRule + + NSExtensionActivationSupportsAttachmentsWithMinCount + 1 + NSExtensionActivationSupportsWebPageWithMaxCount + 1 + NSExtensionActivationSupportsWebURLWithMaxCount + 1 + + NSExtensionServiceAllowsFinderPreviewItem + + NSExtensionServiceAllowsTouchBarItem + + NSExtensionServiceFinderPreviewIconName + NSActionTemplate + NSExtensionServiceTouchBarBezelColorName + TouchBarBezel + NSExtensionServiceTouchBarIconName + NSActionTemplate + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.ui-services + + + diff --git a/OpenInTusker/Media.xcassets/AppIcon.appiconset/1024x1024@1x.png b/OpenInTusker/Media.xcassets/AppIcon.appiconset/1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..a8995cff4732398684fe0a5f5e67cbba5a6252ce GIT binary patch literal 6002 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 literal 0 HcmV?d00001 diff --git a/OpenInTusker/Media.xcassets/AppIcon.appiconset/83.5x83.5@2x.png b/OpenInTusker/Media.xcassets/AppIcon.appiconset/83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..23356523b2981a886053d59f38acb5af634eb60c GIT binary patch literal 1182 zcmeAS@N?(olHy`uVBq!ia0vp^%R!if8Av*x*mDm^r3LtexVjhk0GYG~-KpZ+fOaUA z1o;Ish?VTT_UvqrI)_2#$N%3q{5ulN7$^6;k^kPJx0a4R(^*z_mlRkrFfbqWba4!+ zV0?415#+pv1%FqHuVjL`H0_+-1sRq`4Tk=`4-~ArVn6rk8CujXy$(P zdN|I%k*Rwkw7$c9#&4$eH&kVQR%jb0q+iRO$H2$axm`>CTk7N9Sq~Hra;mq)?yOkd z(9E-Zme{wD$GQg}LjizYx#q2u6=u_ZQYCTx2WM7$hCKIrPNk=Bf|PJ%JB+tnbbXU!YXI-ue(p9|FMzaPUhEtgr-eRd=`23b^Y9c zIPX3F1?rvb{TsJr-!?Gc_apaq`kB?j?cM)>9d`bApisB4$#~YktIJnje^9oLf4!S( zF>BtTZiau1j@CI6A(Fp8#B02G{i0tpy24QRRX}HZcB1@0dCmE~eFsXH*p_g8b2@c? z&yk7c!bR44zvd+5FX=JUZZj-=>mm2?MA_YnFI>~wb~b%?a+&}3AR{j@Zyr+ky?ono db{<$Z_2OG}dBu$lqK^YXBA%{(F6*2UngBS2=+*!L literal 0 HcmV?d00001 diff --git a/OpenInTusker/Media.xcassets/AppIcon.appiconset/Contents.json b/OpenInTusker/Media.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..852eaa2a --- /dev/null +++ b/OpenInTusker/Media.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,103 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "60x60@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "60x60@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "76x76@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "83.5x83.5@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "1024x1024@1x.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/OpenInTusker/Media.xcassets/Contents.json b/OpenInTusker/Media.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/OpenInTusker/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/OpenInTusker/Media.xcassets/TouchBarBezel.colorset/Contents.json b/OpenInTusker/Media.xcassets/TouchBarBezel.colorset/Contents.json new file mode 100644 index 00000000..94a9fc21 --- /dev/null +++ b/OpenInTusker/Media.xcassets/TouchBarBezel.colorset/Contents.json @@ -0,0 +1,14 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "mac", + "color" : { + "reference" : "systemPurpleColor" + } + } + ] +} \ No newline at end of file diff --git a/OpenInTusker/OpenInTusker.entitlements b/OpenInTusker/OpenInTusker.entitlements new file mode 100644 index 00000000..ee95ab7e --- /dev/null +++ b/OpenInTusker/OpenInTusker.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 1389c16d..036e95c8 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -307,6 +307,11 @@ D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */; }; D6DFC6A0242C4CCC00ACC392 /* WeakArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */; }; D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E0DC8D216EDF1E00369478 /* Previewing.swift */; }; + D6E343AB265AAD6B00C4AA01 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D6E343AA265AAD6B00C4AA01 /* Media.xcassets */; }; + D6E343AD265AAD6B00C4AA01 /* ActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E343AC265AAD6B00C4AA01 /* ActionViewController.swift */; }; + D6E343B0265AAD6B00C4AA01 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6E343AE265AAD6B00C4AA01 /* MainInterface.storyboard */; }; + D6E343B4265AAD6B00C4AA01 /* OpenInTusker.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D6E343A8265AAD6B00C4AA01 /* OpenInTusker.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + D6E343BA265AAD8C00C4AA01 /* Action.js in Resources */ = {isa = PBXBuildFile; fileRef = D6E343B9265AAD8C00C4AA01 /* Action.js */; }; D6E4267725327FB400C02E1C /* ComposeAutocompleteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E4267625327FB400C02E1C /* ComposeAutocompleteView.swift */; }; D6E4269D2532A3E100C02E1C /* FuzzyMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E4269C2532A3E100C02E1C /* FuzzyMatcher.swift */; }; D6E426AD25334DA500C02E1C /* FuzzyMatcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E426AC25334DA500C02E1C /* FuzzyMatcherTests.swift */; }; @@ -363,9 +368,27 @@ remoteGlobalIDString = D6D4DDCB212518A000E1C4BB; remoteInfo = Tusker; }; + D6E343B2265AAD6B00C4AA01 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D6D4DDC4212518A000E1C4BB /* Project object */; + proxyType = 1; + remoteGlobalIDString = D6E343A7265AAD6B00C4AA01; + remoteInfo = OpenInTusker; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + D6E3438F2659849800C4AA01 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + D6E343B4265AAD6B00C4AA01 /* OpenInTusker.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; D6F953E52125197500CF0F2B /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -687,6 +710,13 @@ D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackpadScrollGestureRecognizer.swift; sourceTree = ""; }; D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakArray.swift; sourceTree = ""; }; D6E0DC8D216EDF1E00369478 /* Previewing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Previewing.swift; sourceTree = ""; }; + D6E343A8265AAD6B00C4AA01 /* OpenInTusker.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = OpenInTusker.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + D6E343AA265AAD6B00C4AA01 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; + D6E343AC265AAD6B00C4AA01 /* ActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionViewController.swift; sourceTree = ""; }; + D6E343AF265AAD6B00C4AA01 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + D6E343B1265AAD6B00C4AA01 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D6E343B5265AAD6B00C4AA01 /* OpenInTusker.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OpenInTusker.entitlements; sourceTree = ""; }; + D6E343B9265AAD8C00C4AA01 /* Action.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = Action.js; sourceTree = ""; }; D6E4267625327FB400C02E1C /* ComposeAutocompleteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAutocompleteView.swift; sourceTree = ""; }; D6E4269C2532A3E100C02E1C /* FuzzyMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FuzzyMatcher.swift; sourceTree = ""; }; D6E426AC25334DA500C02E1C /* FuzzyMatcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FuzzyMatcherTests.swift; sourceTree = ""; }; @@ -752,6 +782,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D6E343A5265AAD6B00C4AA01 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -1472,6 +1509,7 @@ D6D4DDCE212518A000E1C4BB /* Tusker */, D6D4DDE3212518A200E1C4BB /* TuskerTests */, D6D4DDEE212518A200E1C4BB /* TuskerUITests */, + D6E343A9265AAD6B00C4AA01 /* OpenInTusker */, D6D4DDCD212518A000E1C4BB /* Products */, D65A37F221472F300087646E /* Frameworks */, ); @@ -1485,6 +1523,7 @@ D6D4DDEB212518A200E1C4BB /* TuskerUITests.xctest */, D61099AB2144B0CC00432DC2 /* Pachyderm.framework */, D61099B32144B0CC00432DC2 /* PachydermTests.xctest */, + D6E343A8265AAD6B00C4AA01 /* OpenInTusker.appex */, ); name = Products; sourceTree = ""; @@ -1552,6 +1591,19 @@ path = TuskerUITests; sourceTree = ""; }; + D6E343A9265AAD6B00C4AA01 /* OpenInTusker */ = { + isa = PBXGroup; + children = ( + D6E343B5265AAD6B00C4AA01 /* OpenInTusker.entitlements */, + D6E343AA265AAD6B00C4AA01 /* Media.xcassets */, + D6E343AC265AAD6B00C4AA01 /* ActionViewController.swift */, + D6E343AE265AAD6B00C4AA01 /* MainInterface.storyboard */, + D6E343B1265AAD6B00C4AA01 /* Info.plist */, + D6E343B9265AAD8C00C4AA01 /* Action.js */, + ); + path = OpenInTusker; + sourceTree = ""; + }; D6F1F84E2193B9BE00F5FE67 /* Caching */ = { isa = PBXGroup; children = ( @@ -1642,11 +1694,13 @@ D6D4DDCA212518A000E1C4BB /* Resources */, D6F953E52125197500CF0F2B /* Embed Frameworks */, D65F612C23AE957600F3CFD3 /* Embed debug-only frameworks */, + D6E3438F2659849800C4AA01 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( D61099BF2144B0CC00432DC2 /* PBXTargetDependency */, + D6E343B3265AAD6B00C4AA01 /* PBXTargetDependency */, ); name = Tusker; packageProductDependencies = ( @@ -1696,13 +1750,30 @@ productReference = D6D4DDEB212518A200E1C4BB /* TuskerUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + D6E343A7265AAD6B00C4AA01 /* OpenInTusker */ = { + isa = PBXNativeTarget; + buildConfigurationList = D6E343B6265AAD6B00C4AA01 /* Build configuration list for PBXNativeTarget "OpenInTusker" */; + buildPhases = ( + D6E343A4265AAD6B00C4AA01 /* Sources */, + D6E343A5265AAD6B00C4AA01 /* Frameworks */, + D6E343A6265AAD6B00C4AA01 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OpenInTusker; + productName = OpenInTusker; + productReference = D6E343A8265AAD6B00C4AA01 /* OpenInTusker.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ D6D4DDC4212518A000E1C4BB /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1200; + LastSwiftUpdateCheck = 1250; LastUpgradeCheck = 1250; ORGANIZATIONNAME = Shadowfacts; TargetAttributes = { @@ -1729,6 +1800,9 @@ LastSwiftMigration = 1020; TestTargetID = D6D4DDCB212518A000E1C4BB; }; + D6E343A7265AAD6B00C4AA01 = { + CreatedOnToolsVersion = 12.5; + }; }; }; buildConfigurationList = D6D4DDC7212518A000E1C4BB /* Build configuration list for PBXProject "Tusker" */; @@ -1760,6 +1834,7 @@ D6D4DDEA212518A200E1C4BB /* TuskerUITests */, D61099AA2144B0CC00432DC2 /* Pachyderm */, D61099B22144B0CC00432DC2 /* PachydermTests */, + D6E343A7265AAD6B00C4AA01 /* OpenInTusker */, ); }; /* End PBXProject section */ @@ -1827,6 +1902,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D6E343A6265AAD6B00C4AA01 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D6E343BA265AAD8C00C4AA01 /* Action.js in Resources */, + D6E343AB265AAD6B00C4AA01 /* Media.xcassets in Resources */, + D6E343B0265AAD6B00C4AA01 /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -2170,6 +2255,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D6E343A4265AAD6B00C4AA01 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D6E343AD265AAD6B00C4AA01 /* ActionViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -2198,6 +2291,11 @@ target = D6D4DDCB212518A000E1C4BB /* Tusker */; targetProxy = D6D4DDEC212518A200E1C4BB /* PBXContainerItemProxy */; }; + D6E343B3265AAD6B00C4AA01 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D6E343A7265AAD6B00C4AA01 /* OpenInTusker */; + targetProxy = D6E343B2265AAD6B00C4AA01 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -2209,6 +2307,14 @@ name = LaunchScreen.storyboard; sourceTree = ""; }; + D6E343AE265AAD6B00C4AA01 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + D6E343AF265AAD6B00C4AA01 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; D6E57FA525C26FAB00341037 /* Localizable.stringsdict */ = { isa = PBXVariantGroup; children = ( @@ -2234,6 +2340,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Pachyderm/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2264,6 +2371,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Pachyderm/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2451,7 +2559,7 @@ DEVELOPMENT_TEAM = V4WK9KR9U2; INFOPLIST_FILE = Tusker/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.1; - "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.0; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2480,7 +2588,7 @@ DEVELOPMENT_TEAM = V4WK9KR9U2; INFOPLIST_FILE = Tusker/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.1; - "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.0; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.3; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2578,6 +2686,60 @@ }; name = Release; }; + D6E343B7265AAD6B00C4AA01 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 19; + DEVELOPMENT_TEAM = V4WK9KR9U2; + INFOPLIST_FILE = OpenInTusker/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 2021.1; + PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,6"; + }; + name = Debug; + }; + D6E343B8265AAD6B00C4AA01 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = OpenInTusker/OpenInTusker.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 19; + DEVELOPMENT_TEAM = V4WK9KR9U2; + INFOPLIST_FILE = OpenInTusker/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 2021.1; + PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.OpenInTusker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,6"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -2635,6 +2797,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D6E343B6265AAD6B00C4AA01 /* Build configuration list for PBXNativeTarget "OpenInTusker" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D6E343B7265AAD6B00C4AA01 /* Debug */, + D6E343B8265AAD6B00C4AA01 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/Tusker/AppDelegate.swift b/Tusker/AppDelegate.swift index def7da56..a3ec3194 100644 --- a/Tusker/AppDelegate.swift +++ b/Tusker/AppDelegate.swift @@ -76,9 +76,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { case .newPost: return "compose" - - default: - fatalError("no scene for activity type \(type)") } } } diff --git a/Tusker/MainSceneDelegate.swift b/Tusker/MainSceneDelegate.swift index cf693152..4e515783 100644 --- a/Tusker/MainSceneDelegate.swift +++ b/Tusker/MainSceneDelegate.swift @@ -28,6 +28,9 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate { handlePendingCrashReport(report, session: session) } else { showAppOrOnboardingUI(session: session) + if connectionOptions.urlContexts.count > 0 { + self.scene(scene, openURLContexts: connectionOptions.urlContexts) + } } window!.makeKeyAndVisible() @@ -50,20 +53,10 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate { if url.host == "x-callback-url" { _ = XCBManager.handle(url: url) } else if var components = URLComponents(url: url, resolvingAgainstBaseURL: false), - let tabBarController = window!.rootViewController as? MainTabBarViewController, - let exploreNavController = tabBarController.getTabController(tab: .explore) as? UINavigationController, - let exploreController = exploreNavController.viewControllers.first as? ExploreViewController { - - tabBarController.select(tab: .explore) - exploreNavController.popToRootViewController(animated: false) - - exploreController.loadViewIfNeeded() - exploreController.searchController.isActive = true - + let rootViewController = window!.rootViewController as? TuskerRootViewController { components.scheme = "https" - let query = url.absoluteString - exploreController.searchController.searchBar.text = query - exploreController.resultsController.performSearch(query: query) + let query = components.string! + rootViewController.performSearch(query: query) } } diff --git a/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift b/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift index 9dbaf7b2..4c7a30cd 100644 --- a/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift +++ b/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift @@ -71,14 +71,21 @@ extension AccountSwitchingContainerViewController { extension AccountSwitchingContainerViewController: TuskerRootViewController { func presentCompose() { + loadViewIfNeeded() root.presentCompose() } func select(tab: MainTabBarViewController.Tab) { + loadViewIfNeeded() root.select(tab: tab) } func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController? { - root.getTabController(tab: tab) + loadViewIfNeeded() + return root.getTabController(tab: tab) + } + + func performSearch(query: String) { + root.performSearch(query: query) } } diff --git a/Tusker/Screens/Main/MainSidebarViewController.swift b/Tusker/Screens/Main/MainSidebarViewController.swift index dd021d18..53922328 100644 --- a/Tusker/Screens/Main/MainSidebarViewController.swift +++ b/Tusker/Screens/Main/MainSidebarViewController.swift @@ -48,7 +48,7 @@ class MainSidebarViewController: UIViewController { private(set) var previouslySelectedItem: Item? var selectedItem: Item? { - guard let indexPath = collectionView.indexPathsForSelectedItems?.first else { + guard let indexPath = collectionView?.indexPathsForSelectedItems?.first else { return nil } return dataSource.itemIdentifier(for: indexPath) diff --git a/Tusker/Screens/Main/MainSplitViewController.swift b/Tusker/Screens/Main/MainSplitViewController.swift index 49f7cd69..57598583 100644 --- a/Tusker/Screens/Main/MainSplitViewController.swift +++ b/Tusker/Screens/Main/MainSplitViewController.swift @@ -19,6 +19,10 @@ class MainSplitViewController: UISplitViewController { private var tabBarViewController: MainTabBarViewController! + private var secondaryNavController: UINavigationController! { + viewController(for: .secondary) as? UINavigationController + } + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { if UIDevice.current.userInterfaceIdiom == .phone { return .portrait @@ -67,8 +71,7 @@ class MainSplitViewController: UISplitViewController { } func select(item: MainSidebarViewController.Item) { - let nav = viewController(for: .secondary) as! UINavigationController - nav.viewControllers = getOrCreateNavigationStack(item: item) + secondaryNavController.viewControllers = getOrCreateNavigationStack(item: item) } func getOrCreateNavigationStack(item: MainSidebarViewController.Item) -> [UIViewController] { @@ -109,8 +112,7 @@ extension MainSplitViewController: UISplitViewControllerDelegate { private func transferNavigationStack(from item: MainSidebarViewController.Item, to destination: UINavigationController, dropFirst: Bool = false, append: Bool = false) { var itemNavStack: [UIViewController] if item == sidebar.selectedItem { - let detailNav = viewController(for: .secondary) as! UINavigationController - itemNavStack = detailNav.viewControllers + itemNavStack = secondaryNavController.viewControllers } else { itemNavStack = navigationStacks[item] ?? [] navigationStacks.removeValue(forKey: item) @@ -190,8 +192,7 @@ extension MainSplitViewController: UISplitViewControllerDelegate { // Make sure viewDidLoad is called so that the searchController/resultsController have been initialized explore.loadViewIfNeeded() - let nav = viewController(for: .secondary) as! UINavigationController - let search = nav.viewControllers.first as! SearchViewController + let search = secondaryNavController.viewControllers.first as! SearchViewController // Copy the search query from the search VC to the Explore VC's search controller. let query = search.searchController.searchBar.text ?? "" explore.searchController.searchBar.text = query @@ -318,9 +319,8 @@ extension MainSplitViewController: MainSidebarViewControllerDelegate { } func sidebar(_ sidebarViewController: MainSidebarViewController, didSelectItem item: MainSidebarViewController.Item) { - let nav = viewController(for: .secondary) as! UINavigationController if let previous = sidebar.previouslySelectedItem { - navigationStacks[previous] = nav.viewControllers + navigationStacks[previous] = secondaryNavController.viewControllers } select(item: item) } @@ -366,8 +366,15 @@ extension MainSplitViewController: TuskerRootViewController { if tab == .compose { presentCompose() } else { - select(item: .tab(tab)) - sidebar.select(item: .tab(tab), animated: false) + if presentedViewController != nil { + dismiss(animated: true) { + self.select(item: .tab(tab)) + self.sidebar.select(item: .tab(tab), animated: false) + } + } else { + select(item: .tab(tab)) + sidebar.select(item: .tab(tab), animated: false) + } } } } @@ -379,12 +386,43 @@ extension MainSplitViewController: TuskerRootViewController { if tab == .compose { return nil } else if case .tab(tab) = sidebar.selectedItem { - return viewController(for: .secondary) + return secondaryNavController } else { return nil } } } + + func performSearch(query: String) { + guard traitCollection.horizontalSizeClass != .compact else { + // ensure the tab bar VC is loaded + loadViewIfNeeded() + tabBarViewController.performSearch(query: query) + return + } + + if sidebar.selectedItem != .search { + select(item: .search) + } + + guard let searchViewController = secondaryNavController.viewControllers.first as? SearchViewController else { + return + } + + secondaryNavController.popToRootViewController(animated: false) + + if searchViewController.isViewLoaded { + DispatchQueue.main.async { + searchViewController.searchController.isActive = true + } + } else { + searchViewController.searchControllerStatusOnAppearance = true + searchViewController.loadViewIfNeeded() + } + + searchViewController.searchController.searchBar.text = query + searchViewController.resultsController.performSearch(query: query) + } } extension MainSplitViewController: BackgroundableViewController { diff --git a/Tusker/Screens/Main/MainTabBarViewController.swift b/Tusker/Screens/Main/MainTabBarViewController.swift index ceebf63d..dead5878 100644 --- a/Tusker/Screens/Main/MainTabBarViewController.swift +++ b/Tusker/Screens/Main/MainTabBarViewController.swift @@ -137,6 +137,8 @@ extension MainTabBarViewController { if tab == .compose { return nil } else { + // viewWControllers array is setup in viewDidLoad + loadViewIfNeeded() return viewControllers![tab.rawValue] } } @@ -168,9 +170,39 @@ extension MainTabBarViewController: TuskerRootViewController { if tab == .compose { presentCompose() } else { - selectedIndex = tab.rawValue + // when switching tabs, dismiss the currently presented VC + // otherwise the selected tab changes behind the presented VC + if presentedViewController != nil { + dismiss(animated: true) { + self.selectedIndex = tab.rawValue + } + } else { + selectedIndex = tab.rawValue + } } } + + func performSearch(query: String) { + guard let exploreNavController = getTabController(tab: .explore) as? UINavigationController, + let exploreController = exploreNavController.viewControllers.first as? ExploreViewController else { + return + } + + select(tab: .explore) + exploreNavController.popToRootViewController(animated: false) + + // setting searchController.isActive directly doesn't work until the view has loaded/appeared for the first time + if exploreController.isViewLoaded { + exploreController.searchController.isActive = true + } else { + exploreController.searchControllerStatusOnAppearance = true + // we still need to load the view so that we can setup the search query + exploreController.loadViewIfNeeded() + } + + exploreController.searchController.searchBar.text = query + exploreController.resultsController.performSearch(query: query) + } } extension MainTabBarViewController: BackgroundableViewController { diff --git a/Tusker/Screens/Main/TuskerRootViewController.swift b/Tusker/Screens/Main/TuskerRootViewController.swift index 94f7b3f6..80baafe9 100644 --- a/Tusker/Screens/Main/TuskerRootViewController.swift +++ b/Tusker/Screens/Main/TuskerRootViewController.swift @@ -12,4 +12,5 @@ protocol TuskerRootViewController: UIViewController { func presentCompose() func select(tab: MainTabBarViewController.Tab) func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController? + func performSearch(query: String) } diff --git a/Tusker/Screens/Search/SearchViewController.swift b/Tusker/Screens/Search/SearchViewController.swift index cedd099b..73b735a7 100644 --- a/Tusker/Screens/Search/SearchViewController.swift +++ b/Tusker/Screens/Search/SearchViewController.swift @@ -37,7 +37,7 @@ class SearchViewController: UIViewController { resultsController = SearchResultsViewController(mastodonController: mastodonController) resultsController.exploreNavigationController = self.navigationController searchController = UISearchController(searchResultsController: resultsController) - searchController.searchResultsUpdater = resultsController + searchController.obscuresBackgroundDuringPresentation = false searchController.searchBar.autocapitalizationType = .none searchController.searchBar.delegate = resultsController searchController.hidesNavigationBarDuringPresentation = false