feat(AppExtension): Improve open in link validation, add L10n

This commit is contained in:
Marcus Kida 2023-01-11 15:12:07 +01:00
parent 68fe3cb8f2
commit 5daaa5a32f
No known key found for this signature in database
GPG Key ID: 19FF64E08013CA40
7 changed files with 329 additions and 259 deletions

View File

@ -805,5 +805,10 @@
"unfollow": "Unfollow"
}
}
},
"extension": {
"open_in": {
"invalid_link_error": "This doesn't seem to be a valid Mastodon link."
}
}
}

View File

@ -35,6 +35,7 @@
2A71F543296DBDA80049F54A /* ActionRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A71F53F296DBDA80049F54A /* ActionRequestHandler.swift */; };
2A76F75C2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */; };
2A82294F29262EE000D2A1F7 /* AppContext+NextAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */; };
2A90A157296EEE500026C155 /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 2A90A156296EEE500026C155 /* MastodonSDKDynamic */; };
2AB12E4629362F27006BC925 /* DataSourceFacade+Translate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */; };
2AE244482927831100BDBF7C /* UIImage+SFSymbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */; };
2D198643261BF09500F0B013 /* SearchResultItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198642261BF09500F0B013 /* SearchResultItem.swift */; };
@ -504,6 +505,16 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
2A90A159296EEE500026C155 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
9E44C7212967AD17004B2A72 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@ -1131,6 +1142,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2A90A157296EEE500026C155 /* MastodonSDKDynamic in Frameworks */,
2A64515E29642A8A00CD8B8A /* UniformTypeIdentifiers.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -2832,12 +2844,16 @@
2A64515929642A8A00CD8B8A /* Sources */,
2A64515A29642A8A00CD8B8A /* Frameworks */,
2A64515B29642A8A00CD8B8A /* Resources */,
2A90A159296EEE500026C155 /* Embed Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = OpenInActionExtension;
packageProductDependencies = (
2A90A156296EEE500026C155 /* MastodonSDKDynamic */,
);
productName = FollowActionExtension;
productReference = 2A64515D29642A8A00CD8B8A /* OpenInActionExtension.appex */;
productType = "com.apple.product-type.app-extension";
@ -4948,6 +4964,10 @@
/* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */
2A90A156296EEE500026C155 /* MastodonSDKDynamic */ = {
isa = XCSwiftPackageProductDependency;
productName = MastodonSDKDynamic;
};
357FEEAE29523D470021C9DC /* MastodonSDKDynamic */ = {
isa = XCSwiftPackageProductDependency;
productName = MastodonSDKDynamic;

View File

@ -1,248 +1,250 @@
{
"pins" : [
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "8dd85aee02e39dd280c75eef88ffdb86eed4b07b",
"version" : "5.6.2"
"object": {
"pins": [
{
"package": "Alamofire",
"repositoryURL": "https://github.com/Alamofire/Alamofire.git",
"state": {
"branch": null,
"revision": "8dd85aee02e39dd280c75eef88ffdb86eed4b07b",
"version": "5.6.2"
}
},
{
"package": "AlamofireImage",
"repositoryURL": "https://github.com/Alamofire/AlamofireImage.git",
"state": {
"branch": null,
"revision": "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10",
"version": "4.2.0"
}
},
{
"package": "CommonOSLog",
"repositoryURL": "https://github.com/MainasuK/CommonOSLog",
"state": {
"branch": null,
"revision": "c121624a30698e9886efe38aebb36ff51c01b6c2",
"version": "0.1.1"
}
},
{
"package": "FaviconFinder",
"repositoryURL": "https://github.com/will-lumley/FaviconFinder.git",
"state": {
"branch": null,
"revision": "1f74844f77f79b95c0bb0130b3a87d4f340e6d3a",
"version": "3.3.0"
}
},
{
"package": "FLAnimatedImage",
"repositoryURL": "https://github.com/Flipboard/FLAnimatedImage.git",
"state": {
"branch": null,
"revision": "d4f07b6f164d53c1212c3e54d6460738b1981e9f",
"version": "1.0.17"
}
},
{
"package": "FPSIndicator",
"repositoryURL": "https://github.com/MainasuK/FPSIndicator.git",
"state": {
"branch": null,
"revision": "e4a5067ccd5293b024c767f09e51056afd4a4796",
"version": "1.1.0"
}
},
{
"package": "Fuzi",
"repositoryURL": "https://github.com/cezheng/Fuzi.git",
"state": {
"branch": null,
"revision": "f08c8323da21e985f3772610753bcfc652c2103f",
"version": "3.1.3"
}
},
{
"package": "KeychainAccess",
"repositoryURL": "https://github.com/kishikawakatsumi/KeychainAccess.git",
"state": {
"branch": null,
"revision": "84e546727d66f1adc5439debad16270d0fdd04e7",
"version": "4.2.2"
}
},
{
"package": "Kingfisher",
"repositoryURL": "https://github.com/onevcat/Kingfisher.git",
"state": {
"branch": null,
"revision": "44e891bdb61426a95e31492a67c7c0dfad1f87c5",
"version": "7.4.1"
}
},
{
"package": "MetaTextKit",
"repositoryURL": "https://github.com/TwidereProject/MetaTextKit.git",
"state": {
"branch": null,
"revision": "dcd5255d6930c2fab408dc8562c577547e477624",
"version": "2.2.5"
}
},
{
"package": "NextLevelSessionExporter",
"repositoryURL": "https://github.com/NextLevel/NextLevelSessionExporter.git",
"state": {
"branch": null,
"revision": "b6c0cce1aa37fe1547d694f958fac3c3524b74da",
"version": "0.4.6"
}
},
{
"package": "Nuke",
"repositoryURL": "https://github.com/kean/Nuke.git",
"state": {
"branch": null,
"revision": "a002b7fd786f2df2ed4333fe73a9727499fd9d97",
"version": "10.11.2"
}
},
{
"package": "NukeFLAnimatedImagePlugin",
"repositoryURL": "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git",
"state": {
"branch": null,
"revision": "b59c346a7d536336db3b0f12c72c6e53ee709e16",
"version": "8.0.0"
}
},
{
"package": "Pageboy",
"repositoryURL": "https://github.com/uias/Pageboy",
"state": {
"branch": null,
"revision": "af8fa81788b893205e1ff42ddd88c5b0b315d7c5",
"version": "3.7.0"
}
},
{
"package": "PanModal",
"repositoryURL": "https://github.com/slackhq/PanModal.git",
"state": {
"branch": null,
"revision": "b012aecb6b67a8e46369227f893c12544846613f",
"version": "1.2.7"
}
},
{
"package": "SDWebImage",
"repositoryURL": "https://github.com/SDWebImage/SDWebImage.git",
"state": {
"branch": null,
"revision": "3312bf5e67b52fbce7c3caf431b0cda721a9f7bb",
"version": "5.14.2"
}
},
{
"package": "Stripes",
"repositoryURL": "https://github.com/eneko/Stripes.git",
"state": {
"branch": null,
"revision": "d533fd44b8043a3abbf523e733599173d6f98c11",
"version": "0.2.0"
}
},
{
"package": "swift-collections",
"repositoryURL": "https://github.com/apple/swift-collections.git",
"state": {
"branch": null,
"revision": "f504716c27d2e5d4144fa4794b12129301d17729",
"version": "1.0.3"
}
},
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "546610d52b19be3e19935e0880bb06b9c03f5cef",
"version": "1.14.4"
}
},
{
"package": "swift-nio-zlib-support",
"repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git",
"state": {
"branch": null,
"revision": "37760e9a52030bb9011972c5213c3350fa9d41fd",
"version": "1.0.0"
}
},
{
"package": "SwiftSoup",
"repositoryURL": "https://github.com/scinfu/SwiftSoup.git",
"state": {
"branch": null,
"revision": "6778575285177365cbad3e5b8a72f2a20583cfec",
"version": "2.4.3"
}
},
{
"package": "Introspect",
"repositoryURL": "https://github.com/siteline/SwiftUI-Introspect.git",
"state": {
"branch": null,
"revision": "f2616860a41f9d9932da412a8978fec79c06fe24",
"version": "0.1.4"
}
},
{
"package": "TabBarPager",
"repositoryURL": "https://github.com/TwidereProject/TabBarPager.git",
"state": {
"branch": null,
"revision": "488aa66d157a648901b61721212c0dec23d27ee5",
"version": "0.1.0"
}
},
{
"package": "Tabman",
"repositoryURL": "https://github.com/uias/Tabman",
"state": {
"branch": null,
"revision": "4a4f7c755b875ffd4f9ef10d67a67883669d2465",
"version": "2.13.0"
}
},
{
"package": "TOCropViewController",
"repositoryURL": "https://github.com/TimOliver/TOCropViewController.git",
"state": {
"branch": null,
"revision": "d0470491f56e734731bbf77991944c0dfdee3e0e",
"version": "2.6.1"
}
},
{
"package": "UIHostingConfigurationBackport",
"repositoryURL": "https://github.com/woxtu/UIHostingConfigurationBackport.git",
"state": {
"branch": null,
"revision": "6091f2d38faa4b24fc2ca0389c651e2f666624a3",
"version": "0.1.0"
}
},
{
"package": "UITextView+Placeholder",
"repositoryURL": "https://github.com/MainasuK/UITextView-Placeholder.git",
"state": {
"branch": null,
"revision": "20f513ded04a040cdf5467f0891849b1763ede3b",
"version": "1.4.1"
}
}
},
{
"identity" : "alamofireimage",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/AlamofireImage.git",
"state" : {
"revision" : "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10",
"version" : "4.2.0"
}
},
{
"identity" : "commonoslog",
"kind" : "remoteSourceControl",
"location" : "https://github.com/MainasuK/CommonOSLog",
"state" : {
"revision" : "c121624a30698e9886efe38aebb36ff51c01b6c2",
"version" : "0.1.1"
}
},
{
"identity" : "faviconfinder",
"kind" : "remoteSourceControl",
"location" : "https://github.com/will-lumley/FaviconFinder.git",
"state" : {
"revision" : "1f74844f77f79b95c0bb0130b3a87d4f340e6d3a",
"version" : "3.3.0"
}
},
{
"identity" : "flanimatedimage",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Flipboard/FLAnimatedImage.git",
"state" : {
"revision" : "d4f07b6f164d53c1212c3e54d6460738b1981e9f",
"version" : "1.0.17"
}
},
{
"identity" : "fpsindicator",
"kind" : "remoteSourceControl",
"location" : "https://github.com/MainasuK/FPSIndicator.git",
"state" : {
"revision" : "e4a5067ccd5293b024c767f09e51056afd4a4796",
"version" : "1.1.0"
}
},
{
"identity" : "fuzi",
"kind" : "remoteSourceControl",
"location" : "https://github.com/cezheng/Fuzi.git",
"state" : {
"revision" : "f08c8323da21e985f3772610753bcfc652c2103f",
"version" : "3.1.3"
}
},
{
"identity" : "keychainaccess",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
"state" : {
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
"version" : "4.2.2"
}
},
{
"identity" : "kingfisher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/onevcat/Kingfisher.git",
"state" : {
"revision" : "44e891bdb61426a95e31492a67c7c0dfad1f87c5",
"version" : "7.4.1"
}
},
{
"identity" : "metatextkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/TwidereProject/MetaTextKit.git",
"state" : {
"revision" : "dcd5255d6930c2fab408dc8562c577547e477624",
"version" : "2.2.5"
}
},
{
"identity" : "nextlevelsessionexporter",
"kind" : "remoteSourceControl",
"location" : "https://github.com/NextLevel/NextLevelSessionExporter.git",
"state" : {
"revision" : "b6c0cce1aa37fe1547d694f958fac3c3524b74da",
"version" : "0.4.6"
}
},
{
"identity" : "nuke",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kean/Nuke.git",
"state" : {
"revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97",
"version" : "10.11.2"
}
},
{
"identity" : "nuke-flanimatedimage-plugin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git",
"state" : {
"revision" : "b59c346a7d536336db3b0f12c72c6e53ee709e16",
"version" : "8.0.0"
}
},
{
"identity" : "pageboy",
"kind" : "remoteSourceControl",
"location" : "https://github.com/uias/Pageboy",
"state" : {
"revision" : "af8fa81788b893205e1ff42ddd88c5b0b315d7c5",
"version" : "3.7.0"
}
},
{
"identity" : "panmodal",
"kind" : "remoteSourceControl",
"location" : "https://github.com/slackhq/PanModal.git",
"state" : {
"revision" : "b012aecb6b67a8e46369227f893c12544846613f",
"version" : "1.2.7"
}
},
{
"identity" : "sdwebimage",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SDWebImage/SDWebImage.git",
"state" : {
"revision" : "3312bf5e67b52fbce7c3caf431b0cda721a9f7bb",
"version" : "5.14.2"
}
},
{
"identity" : "stripes",
"kind" : "remoteSourceControl",
"location" : "https://github.com/eneko/Stripes.git",
"state" : {
"revision" : "d533fd44b8043a3abbf523e733599173d6f98c11",
"version" : "0.2.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "f504716c27d2e5d4144fa4794b12129301d17729",
"version" : "1.0.3"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "546610d52b19be3e19935e0880bb06b9c03f5cef",
"version" : "1.14.4"
}
},
{
"identity" : "swift-nio-zlib-support",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-zlib-support.git",
"state" : {
"revision" : "37760e9a52030bb9011972c5213c3350fa9d41fd",
"version" : "1.0.0"
}
},
{
"identity" : "swiftsoup",
"kind" : "remoteSourceControl",
"location" : "https://github.com/scinfu/SwiftSoup.git",
"state" : {
"revision" : "6778575285177365cbad3e5b8a72f2a20583cfec",
"version" : "2.4.3"
}
},
{
"identity" : "swiftui-introspect",
"kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"state" : {
"revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
"version" : "0.1.4"
}
},
{
"identity" : "tabbarpager",
"kind" : "remoteSourceControl",
"location" : "https://github.com/TwidereProject/TabBarPager.git",
"state" : {
"revision" : "488aa66d157a648901b61721212c0dec23d27ee5",
"version" : "0.1.0"
}
},
{
"identity" : "tabman",
"kind" : "remoteSourceControl",
"location" : "https://github.com/uias/Tabman",
"state" : {
"revision" : "4a4f7c755b875ffd4f9ef10d67a67883669d2465",
"version" : "2.13.0"
}
},
{
"identity" : "tocropviewcontroller",
"kind" : "remoteSourceControl",
"location" : "https://github.com/TimOliver/TOCropViewController.git",
"state" : {
"revision" : "d0470491f56e734731bbf77991944c0dfdee3e0e",
"version" : "2.6.1"
}
},
{
"identity" : "uihostingconfigurationbackport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/woxtu/UIHostingConfigurationBackport.git",
"state" : {
"revision" : "6091f2d38faa4b24fc2ca0389c651e2f666624a3",
"version" : "0.1.0"
}
},
{
"identity" : "uitextview-placeholder",
"kind" : "remoteSourceControl",
"location" : "https://github.com/MainasuK/UITextView-Placeholder.git",
"state" : {
"revision" : "20f513ded04a040cdf5467f0891849b1763ede3b",
"version" : "1.4.1"
}
}
],
"version" : 2
]
},
"version": 1
}

View File

@ -469,6 +469,12 @@ public enum L10n {
}
}
}
public enum Extension {
public enum OpenIn {
/// This doesn't seem to be a valid Mastodon link.
public static let invalidLinkError = L10n.tr("Localizable", "Extension.OpenIn.InvalidLinkError", fallback: "This doesn't seem to be a valid Mastodon link.")
}
}
public enum Scene {
public enum AccountList {
/// Add Account

View File

@ -513,3 +513,4 @@ You cant go wrong with any of our recommend servers, so regardless of which o
"Scene.Privacy.Button.confirm" = "I agree";
"Scene.Privacy.Policy.Ios" = "Privacy Policy - Mastodon for iOS";
"Scene.Privacy.Policy.Server" = "Privacy Policy - %@";
"Extension.OpenIn.InvalidLinkError" = "This doesn't seem to be a valid Mastodon link.";

View File

@ -19,7 +19,12 @@ Action.prototype = {
},
finalize: function(arguments) {
window.location = arguments["openURL"]
let alertMessage = arguments["alert"]
if (alertMessage) {
alert(alertMessage)
} else {
window.location = arguments["openURL"]
}
}
};
@ -27,12 +32,12 @@ Action.prototype = {
function detectUsername() {
var uriUsername = document.documentURI.match("@(.+)@([a-z0-9]+\.[a-z0-9]+)")
if (typeof uriUsername === "Array") {
if (Array.isArray(uriUsername)) {
return uriUsername[0]
}
var querySelector = document.head.querySelector('[property="profile:username"]')
if (typeof querySelector === "Object") {
if (querySelector !== null && typeof querySelector === "object") {
return querySelector.content
}

View File

@ -5,13 +5,16 @@
// Created by Marcus Kida on 03.01.23.
//
import Combine
import UIKit
import MobileCoreServices
import UniformTypeIdentifiers
import MastodonSDK
import MastodonLocalization
class ActionRequestHandler: NSObject, NSExtensionRequestHandling {
var extensionContext: NSExtensionContext?
var cancellables = [AnyCancellable]()
func beginRequest(with context: NSExtensionContext) {
// Do not call super in an Action extension with no user interface
@ -28,7 +31,7 @@ class ActionRequestHandler: NSObject, NSExtensionRequestHandling {
.first
guard let itemProvider = itemProvider else {
return doneWithResults(nil)
return doneWithInvalidLink()
}
itemProvider.loadItem(forTypeIdentifier: UTType.propertyList.identifier, options: nil, completionHandler: { [weak self] item, error in
@ -37,16 +40,16 @@ class ActionRequestHandler: NSObject, NSExtensionRequestHandling {
let dictionary = item as? NSDictionary,
let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as? NSDictionary
else {
self?.doneWithResults(nil)
self?.doneWithInvalidLink()
return
}
if let username = results["username"] as? String {
self?.completeWithOpenUserProfile(username)
} else if let url = results["url"] as? String {
self?.completeWithSearch(url)
self?.continueWithSearch(url)
} else {
self?.doneWithResults(nil)
self?.doneWithInvalidLink()
}
}
})
@ -60,13 +63,41 @@ private extension ActionRequestHandler {
])
}
func completeWithSearch(_ query: String) {
guard let query = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
return doneWithResults(nil)
func continueWithSearch(_ query: String) {
guard
let url = URL(string: query),
let host = url.host
else {
return doneWithInvalidLink()
}
doneWithResults(
["openURL": "mastodon://search?query=\(query)"]
)
Mastodon.API
.Instance
.instance(
session: .shared,
domain: host
)
.receive(on: DispatchQueue.main)
.sink { _ in
// no-op
} receiveValue: { [weak self] response in
guard response.value.version != nil else {
self?.doneWithInvalidLink()
return
}
guard let query = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
self?.doneWithInvalidLink()
return
}
self?.doneWithResults(
["openURL": "mastodon://search?query=\(query)"]
)
}
.store(in: &cancellables)
}
func doneWithInvalidLink() {
doneWithResults(["alert": L10n.Extension.OpenIn.invalidLinkError])
}
func doneWithResults(_ resultsForJavaScriptFinalizeArg: [String: Any]?) {