From 5a3636cb2266e7ad688109c7381c82fff3b3d34f Mon Sep 17 00:00:00 2001 From: Marcus Kida Date: Tue, 24 Jan 2023 11:14:50 +0100 Subject: [PATCH] feat(Widget): Implement FollowersCountIntent --- Mastodon.xcodeproj/project.pbxproj | 265 ++++++++++++++++++ Mastodon/Info.plist | 1 + .../Handler/FollowersCountIntentHandler.swift | 42 +++ MastodonIntent/Info.plist | 1 + MastodonIntent/IntentHandler.swift | 2 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + WidgetExtension/Assets.xcassets/Contents.json | 6 + .../WidgetBackground.colorset/Contents.json | 11 + .../FollowersWidgetExtension.swift | 61 ++++ WidgetExtension/Info.plist | 11 + WidgetExtension/WidgetExtension.entitlements | 10 + .../WidgetExtension.intentdefinition | 153 ++++++++++ WidgetExtension/WidgetExtension.swift | 9 + WidgetExtension/WidgetExtensionBundle.swift | 11 + 15 files changed, 607 insertions(+) create mode 100644 MastodonIntent/Handler/FollowersCountIntentHandler.swift create mode 100644 WidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 WidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 WidgetExtension/Assets.xcassets/Contents.json create mode 100644 WidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 WidgetExtension/FollowersWidgetExtension.swift create mode 100644 WidgetExtension/Info.plist create mode 100644 WidgetExtension/WidgetExtension.entitlements create mode 100644 WidgetExtension/WidgetExtension.intentdefinition create mode 100644 WidgetExtension/WidgetExtension.swift create mode 100644 WidgetExtension/WidgetExtensionBundle.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 4caf3ebe5..31a895c05 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -33,10 +33,23 @@ 2A71F541296DBDA80049F54A /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2A71F53D296DBDA80049F54A /* Media.xcassets */; }; 2A71F542296DBDA80049F54A /* Action.js in Resources */ = {isa = PBXBuildFile; fileRef = 2A71F53E296DBDA80049F54A /* Action.js */; }; 2A71F543296DBDA80049F54A /* ActionRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A71F53F296DBDA80049F54A /* ActionRequestHandler.swift */; }; + 2A728122297EA9D7004138C5 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A728121297EA9D7004138C5 /* WidgetKit.framework */; }; + 2A728124297EA9D7004138C5 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A728123297EA9D7004138C5 /* SwiftUI.framework */; }; + 2A728127297EA9D7004138C5 /* WidgetExtensionBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A728126297EA9D7004138C5 /* WidgetExtensionBundle.swift */; }; + 2A72812B297EA9D7004138C5 /* FollowersWidgetExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A72812A297EA9D7004138C5 /* FollowersWidgetExtension.swift */; }; + 2A72812E297EA9D8004138C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2A72812D297EA9D8004138C5 /* Assets.xcassets */; }; + 2A728130297EA9D8004138C5 /* WidgetExtension.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 2A72812C297EA9D7004138C5 /* WidgetExtension.intentdefinition */; }; + 2A728131297EA9D8004138C5 /* WidgetExtension.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 2A72812C297EA9D7004138C5 /* WidgetExtension.intentdefinition */; }; + 2A728134297EA9D8004138C5 /* WidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 2A728120297EA9D7004138C5 /* WidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 2A72813B297EC6F7004138C5 /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 2A72813A297EC6F7004138C5 /* MastodonSDKDynamic */; }; + 2A72813F297EC762004138C5 /* WidgetExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A72813E297EC762004138C5 /* WidgetExtension.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 */; }; + 2AE202AA297FE10B00F66E55 /* WidgetExtension.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 2A72812C297EA9D7004138C5 /* WidgetExtension.intentdefinition */; }; + 2AE202AC297FE19100F66E55 /* FollowersCountIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE202AB297FE19100F66E55 /* FollowersCountIntentHandler.swift */; }; + 2AE202AD297FE1CD00F66E55 /* WidgetExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A72813E297EC762004138C5 /* WidgetExtension.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 */; }; 2D198649261C0B8500F0B013 /* SearchResultSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D198648261C0B8500F0B013 /* SearchResultSection.swift */; }; @@ -467,6 +480,13 @@ remoteGlobalIDString = 2A64515C29642A8A00CD8B8A; remoteInfo = FollowActionExtension; }; + 2A728132297EA9D8004138C5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DB427DCA25BAA00100D1B89D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2A72811F297EA9D7004138C5; + remoteInfo = WidgetExtensionExtension; + }; DB427DE925BAA00100D1B89D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = DB427DCA25BAA00100D1B89D /* Project object */; @@ -505,6 +525,16 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 2A72813D297EC6F7004138C5 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 2A90A159296EEE500026C155 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -544,6 +574,7 @@ dstSubfolderSpec = 13; files = ( DB8FABCE26AEC7B2008E5AF4 /* MastodonIntent.appex in Embed Foundation Extensions */, + 2A728134297EA9D8004138C5 /* WidgetExtension.appex in Embed Foundation Extensions */, 2A64516929642A8B00CD8B8A /* OpenInActionExtension.appex in Embed Foundation Extensions */, DBC6461C26A170AB00B0E31B /* ShareActionExtension.appex in Embed Foundation Extensions */, DBF8AE1A263293E400C9C23C /* NotificationService.appex in Embed Foundation Extensions */, @@ -584,9 +615,20 @@ 2A71F53E296DBDA80049F54A /* Action.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = Action.js; sourceTree = ""; }; 2A71F53F296DBDA80049F54A /* ActionRequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionRequestHandler.swift; sourceTree = ""; }; 2A71F540296DBDA80049F54A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2A728120297EA9D7004138C5 /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 2A728121297EA9D7004138C5 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 2A728123297EA9D7004138C5 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 2A728126297EA9D7004138C5 /* WidgetExtensionBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetExtensionBundle.swift; sourceTree = ""; }; + 2A72812A297EA9D7004138C5 /* FollowersWidgetExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersWidgetExtension.swift; sourceTree = ""; }; + 2A72812C297EA9D7004138C5 /* WidgetExtension.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = WidgetExtension.intentdefinition; sourceTree = ""; }; + 2A72812D297EA9D8004138C5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 2A72812F297EA9D8004138C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2A72813E297EC762004138C5 /* WidgetExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetExtension.swift; sourceTree = ""; }; 2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineHeaderViewActionButton.swift; sourceTree = ""; }; 2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+NextAccount.swift"; sourceTree = ""; }; 2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Translate.swift"; sourceTree = ""; }; + 2AE202A9297FDDF500F66E55 /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = ""; }; + 2AE202AB297FE19100F66E55 /* FollowersCountIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersCountIntentHandler.swift; sourceTree = ""; }; 2AE244472927831100BDBF7C /* UIImage+SFSymbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+SFSymbols.swift"; sourceTree = ""; }; 2D198642261BF09500F0B013 /* SearchResultItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultItem.swift; sourceTree = ""; }; 2D198648261C0B8500F0B013 /* SearchResultSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultSection.swift; sourceTree = ""; }; @@ -1148,6 +1190,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2A72811D297EA9D7004138C5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2A72813B297EC6F7004138C5 /* MastodonSDKDynamic in Frameworks */, + 2A728124297EA9D7004138C5 /* SwiftUI.framework in Frameworks */, + 2A728122297EA9D7004138C5 /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DB427DCF25BAA00100D1B89D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1343,6 +1395,20 @@ path = OpenInActionExtension; sourceTree = ""; }; + 2A728125297EA9D7004138C5 /* WidgetExtension */ = { + isa = PBXGroup; + children = ( + 2AE202A9297FDDF500F66E55 /* WidgetExtension.entitlements */, + 2A72813E297EC762004138C5 /* WidgetExtension.swift */, + 2A728126297EA9D7004138C5 /* WidgetExtensionBundle.swift */, + 2A72812A297EA9D7004138C5 /* FollowersWidgetExtension.swift */, + 2A72812C297EA9D7004138C5 /* WidgetExtension.intentdefinition */, + 2A72812D297EA9D8004138C5 /* Assets.xcassets */, + 2A72812F297EA9D8004138C5 /* Info.plist */, + ); + path = WidgetExtension; + sourceTree = ""; + }; 2D152A8A25C295B8009AA50C /* Content */ = { isa = PBXGroup; children = ( @@ -1562,6 +1628,8 @@ DB8FAB9E26AEC3A2008E5AF4 /* Intents.framework */, DB8FABA926AEC3A2008E5AF4 /* IntentsUI.framework */, 2A6451022964223800CD8B8A /* UniformTypeIdentifiers.framework */, + 2A728121297EA9D7004138C5 /* WidgetKit.framework */, + 2A728123297EA9D7004138C5 /* SwiftUI.framework */, ); name = Frameworks; sourceTree = ""; @@ -1880,6 +1948,7 @@ DBC6461326A170AB00B0E31B /* ShareActionExtension */, DB8FABC826AEC7B2008E5AF4 /* MastodonIntent */, 2A71F53C296DBDA80049F54A /* OpenInActionExtension */, + 2A728125297EA9D7004138C5 /* WidgetExtension */, DB427DD325BAA00100D1B89D /* Products */, 1EBA4F56E920856A3FC84ACB /* Pods */, 3FE14AD363ED19AE7FF210A6 /* Frameworks */, @@ -1898,6 +1967,7 @@ DBC6461226A170AB00B0E31B /* ShareActionExtension.appex */, DB8FABC626AEC7B2008E5AF4 /* MastodonIntent.appex */, 2A64515D29642A8A00CD8B8A /* OpenInActionExtension.appex */, + 2A728120297EA9D7004138C5 /* WidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -2164,6 +2234,7 @@ isa = PBXGroup; children = ( DBB8AB4526AECDE200F6D281 /* SendPostIntentHandler.swift */, + 2AE202AB297FE19100F66E55 /* FollowersCountIntentHandler.swift */, ); path = Handler; sourceTree = ""; @@ -2860,6 +2931,27 @@ productReference = 2A64515D29642A8A00CD8B8A /* OpenInActionExtension.appex */; productType = "com.apple.product-type.app-extension"; }; + 2A72811F297EA9D7004138C5 /* WidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2A728139297EA9D8004138C5 /* Build configuration list for PBXNativeTarget "WidgetExtension" */; + buildPhases = ( + 2A72811C297EA9D7004138C5 /* Sources */, + 2A72811D297EA9D7004138C5 /* Frameworks */, + 2A72811E297EA9D7004138C5 /* Resources */, + 2A72813D297EC6F7004138C5 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WidgetExtension; + packageProductDependencies = ( + 2A72813A297EC6F7004138C5 /* MastodonSDKDynamic */, + ); + productName = WidgetExtensionExtension; + productReference = 2A728120297EA9D7004138C5 /* WidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; DB427DD125BAA00100D1B89D /* Mastodon */ = { isa = PBXNativeTarget; buildConfigurationList = DB427DFC25BAA00100D1B89D /* Build configuration list for PBXNativeTarget "Mastodon" */; @@ -2882,6 +2974,7 @@ DBC6461B26A170AB00B0E31B /* PBXTargetDependency */, DB8FABCD26AEC7B2008E5AF4 /* PBXTargetDependency */, 2A64516829642A8B00CD8B8A /* PBXTargetDependency */, + 2A728133297EA9D8004138C5 /* PBXTargetDependency */, ); name = Mastodon; packageProductDependencies = ( @@ -3006,6 +3099,9 @@ 2A64515C29642A8A00CD8B8A = { CreatedOnToolsVersion = 14.2; }; + 2A72811F297EA9D7004138C5 = { + CreatedOnToolsVersion = 14.2; + }; DB427DD125BAA00100D1B89D = { CreatedOnToolsVersion = 12.4; LastSwiftMigration = 1300; @@ -3076,6 +3172,7 @@ DBC6461126A170AB00B0E31B /* ShareActionExtension */, DB8FABC526AEC7B2008E5AF4 /* MastodonIntent */, 2A64515C29642A8A00CD8B8A /* OpenInActionExtension */, + 2A72811F297EA9D7004138C5 /* WidgetExtension */, ); }; /* End PBXProject section */ @@ -3090,6 +3187,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2A72811E297EA9D7004138C5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2A72812E297EA9D8004138C5 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DB427DD025BAA00100D1B89D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -3316,6 +3421,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2A72811C297EA9D7004138C5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2A728130297EA9D8004138C5 /* WidgetExtension.intentdefinition in Sources */, + 2A72813F297EC762004138C5 /* WidgetExtension.swift in Sources */, + 2A728127297EA9D7004138C5 /* WidgetExtensionBundle.swift in Sources */, + 2A72812B297EA9D7004138C5 /* FollowersWidgetExtension.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; DB427DCE25BAA00100D1B89D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3610,6 +3726,7 @@ DB98EB5627B0FF1B0082E365 /* ReportViewControllerAppearance.swift in Sources */, DB3EA8E6281B79E200598866 /* DiscoveryCommunityViewController.swift in Sources */, 2D206B8625F5FB0900143C56 /* Double.swift in Sources */, + 2A728131297EA9D8004138C5 /* WidgetExtension.intentdefinition in Sources */, DB9F58F126EF512300E7BBE9 /* AccountListTableViewCell.swift in Sources */, 2D76319F25C1521200929FB9 /* StatusSection.swift in Sources */, DB938F0326240EA300E5B6C1 /* CachedThreadViewModel.swift in Sources */, @@ -3745,7 +3862,10 @@ buildActionMask = 2147483647; files = ( DB0009A726AEE5DC009B9D2D /* Intents.intentdefinition in Sources */, + 2AE202AD297FE1CD00F66E55 /* WidgetExtension.swift in Sources */, DBB8AB4626AECDE200F6D281 /* SendPostIntentHandler.swift in Sources */, + 2AE202AA297FE10B00F66E55 /* WidgetExtension.intentdefinition in Sources */, + 2AE202AC297FE19100F66E55 /* FollowersCountIntentHandler.swift in Sources */, DB64BA452851F23000ADF1B7 /* MastodonAuthentication+Fetch.swift in Sources */, DB64BA482851F29300ADF1B7 /* Account+Fetch.swift in Sources */, DB8FABCA26AEC7B2008E5AF4 /* IntentHandler.swift in Sources */, @@ -3781,6 +3901,11 @@ target = 2A64515C29642A8A00CD8B8A /* OpenInActionExtension */; targetProxy = 2A64516729642A8B00CD8B8A /* PBXContainerItemProxy */; }; + 2A728133297EA9D8004138C5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2A72811F297EA9D7004138C5 /* WidgetExtension */; + targetProxy = 2A728132297EA9D8004138C5 /* PBXContainerItemProxy */; + }; DB427DEA25BAA00100D1B89D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DB427DD125BAA00100D1B89D /* Mastodon */; @@ -4057,6 +4182,131 @@ }; name = "Release Snapshot"; }; + 2A728135297EA9D8004138C5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 5Z4GVSS33P; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = WidgetExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.WidgetExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2A728136297EA9D8004138C5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5Z4GVSS33P; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = WidgetExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.WidgetExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; + 2A728137297EA9D8004138C5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5Z4GVSS33P; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = WidgetExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.WidgetExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 2A728138297EA9D8004138C5 /* Release Snapshot */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5Z4GVSS33P; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = WidgetExtension/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = WidgetExtension; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = org.joinmastodon.app.WidgetExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Release Snapshot"; + }; DB427DFA25BAA00100D1B89D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4890,6 +5140,17 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 2A728139297EA9D8004138C5 /* Build configuration list for PBXNativeTarget "WidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2A728135297EA9D8004138C5 /* Debug */, + 2A728136297EA9D8004138C5 /* Profile */, + 2A728137297EA9D8004138C5 /* Release */, + 2A728138297EA9D8004138C5 /* Release Snapshot */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; DB427DCD25BAA00100D1B89D /* Build configuration list for PBXProject "Mastodon" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -4970,6 +5231,10 @@ /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ + 2A72813A297EC6F7004138C5 /* MastodonSDKDynamic */ = { + isa = XCSwiftPackageProductDependency; + productName = MastodonSDKDynamic; + }; 2A90A156296EEE500026C155 /* MastodonSDKDynamic */ = { isa = XCSwiftPackageProductDependency; productName = MastodonSDKDynamic; diff --git a/Mastodon/Info.plist b/Mastodon/Info.plist index df221e66d..fbe3fd94a 100644 --- a/Mastodon/Info.plist +++ b/Mastodon/Info.plist @@ -61,6 +61,7 @@ NSUserActivityTypes + FollowersCountIntent SendPostIntent UIApplicationSceneManifest diff --git a/MastodonIntent/Handler/FollowersCountIntentHandler.swift b/MastodonIntent/Handler/FollowersCountIntentHandler.swift new file mode 100644 index 000000000..7cf852de1 --- /dev/null +++ b/MastodonIntent/Handler/FollowersCountIntentHandler.swift @@ -0,0 +1,42 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import Foundation +import Intents +import MastodonCore +import MastodonSDK +import MastodonLocalization + +class FollowersCountIntentHandler: INExtension, FollowersCountIntentHandling { + func resolveAccount(for intent: FollowersCountIntent) async -> INStringResolutionResult { + .confirmationRequired(with: intent.account) + } + + func provideAccountOptionsCollection(for intent: FollowersCountIntent, searchTerm: String?) async throws -> INObjectCollection { + guard + let searchTerm = searchTerm, + let authenticationBox = WidgetExtension.appContext + .authenticationService + .mastodonAuthenticationBoxes + .first + else { + return INObjectCollection(items: []) + } + + let results = try await WidgetExtension.appContext + .apiService + .search(query: .init(q: searchTerm), authenticationBox: authenticationBox) + + debugPrint(results.value.statuses) + + return INObjectCollection(items: results.value.accounts.map { $0.acctWithDomain(localDomain: authenticationBox.domain) as NSString }) + } +} + +private extension Mastodon.Entity.Account { + func acctWithDomain(localDomain: String) -> String { + guard acct.contains("@") else { + return "\(acct)@\(localDomain)" + } + return acct + } +} diff --git a/MastodonIntent/Info.plist b/MastodonIntent/Info.plist index 3c4a6e453..7dbb09f9e 100644 --- a/MastodonIntent/Info.plist +++ b/MastodonIntent/Info.plist @@ -30,6 +30,7 @@ IntentsSupported + FollowersCountIntent SendPostIntent diff --git a/MastodonIntent/IntentHandler.swift b/MastodonIntent/IntentHandler.swift index cb8588d67..b8f26d8ca 100644 --- a/MastodonIntent/IntentHandler.swift +++ b/MastodonIntent/IntentHandler.swift @@ -15,6 +15,8 @@ class IntentHandler: INExtension { switch intent { case is SendPostIntent: return SendPostIntentHandler() + case is FollowersCountIntent: + return FollowersCountIntentHandler() default: return self } diff --git a/WidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json b/WidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/WidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json b/WidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..13613e3ee --- /dev/null +++ b/WidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WidgetExtension/Assets.xcassets/Contents.json b/WidgetExtension/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/WidgetExtension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json b/WidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/WidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/WidgetExtension/FollowersWidgetExtension.swift b/WidgetExtension/FollowersWidgetExtension.swift new file mode 100644 index 000000000..e279831d0 --- /dev/null +++ b/WidgetExtension/FollowersWidgetExtension.swift @@ -0,0 +1,61 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import WidgetKit +import SwiftUI +import Intents + +struct FollowersProvider: IntentTimelineProvider { + func placeholder(in context: Context) -> FollowersEntry { + FollowersEntry(date: Date(), configuration: FollowersCountIntent()) + } + + func getSnapshot(for configuration: FollowersCountIntent, in context: Context, completion: @escaping (FollowersEntry) -> ()) { + let entry = FollowersEntry(date: Date(), configuration: configuration) + completion(entry) + } + + func getTimeline(for configuration: FollowersCountIntent, in context: Context, completion: @escaping (Timeline) -> ()) { + var entries: [FollowersEntry] = [] + + // Generate a timeline consisting of five entries an hour apart, starting from the current date. + let currentDate = Date() + for hourOffset in 0 ..< 5 { + let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! + let entry = FollowersEntry(date: entryDate, configuration: configuration) + entries.append(entry) + } + + let timeline = Timeline(entries: entries, policy: .atEnd) + completion(timeline) + } +} + +struct FollowersEntry: TimelineEntry { + let date: Date + let configuration: FollowersCountIntent +} + +struct FollowersWidgetExtensionEntryView : View { + var entry: FollowersProvider.Entry + + var body: some View { + Text(entry.date, style: .time) + } +} + +struct FollowersWidgetExtension: Widget { + var body: some WidgetConfiguration { + IntentConfiguration(kind: "Followers", intent: FollowersCountIntent.self, provider: FollowersProvider()) { entry in + FollowersWidgetExtensionEntryView(entry: entry) + } + .configurationDisplayName("Followers") + .description("Show number of followers.") + } +} + +struct WidgetExtension_Previews: PreviewProvider { + static var previews: some View { + FollowersWidgetExtensionEntryView(entry: FollowersEntry(date: Date(), configuration: FollowersCountIntent())) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + } +} diff --git a/WidgetExtension/Info.plist b/WidgetExtension/Info.plist new file mode 100644 index 000000000..0f118fb75 --- /dev/null +++ b/WidgetExtension/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/WidgetExtension/WidgetExtension.entitlements b/WidgetExtension/WidgetExtension.entitlements new file mode 100644 index 000000000..c3bc3f816 --- /dev/null +++ b/WidgetExtension/WidgetExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.org.joinmastodon.app + + + diff --git a/WidgetExtension/WidgetExtension.intentdefinition b/WidgetExtension/WidgetExtension.intentdefinition new file mode 100644 index 000000000..4c32b0533 --- /dev/null +++ b/WidgetExtension/WidgetExtension.intentdefinition @@ -0,0 +1,153 @@ + + + + + INEnums + + INIntentDefinitionModelVersion + 1.2 + INIntentDefinitionNamespace + 88xZPY + INIntentDefinitionSystemVersion + 22C65 + INIntentDefinitionToolsBuildVersion + 14C18 + INIntentDefinitionToolsVersion + 14.2 + INIntents + + + INIntentCategory + information + INIntentDescription + Followers + INIntentDescriptionID + tVvJ9c + INIntentEligibleForWidgets + + INIntentIneligibleForSuggestions + + INIntentLastParameterTag + 5 + INIntentName + FollowersCount + INIntentParameters + + + INIntentParameterConfigurable + + INIntentParameterDisplayName + Account + INIntentParameterDisplayNameID + OL6lkx + INIntentParameterDisplayPriority + 1 + INIntentParameterMetadata + + INIntentParameterMetadataCapitalization + Sentences + INIntentParameterMetadataDefaultValueID + 2V4PKr + + INIntentParameterName + account + INIntentParameterPromptDialogs + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + Enter follower Username + INIntentParameterPromptDialogFormatStringID + sOLUtG + INIntentParameterPromptDialogType + Configuration + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Primary + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + There are ${count} options matching ‘${account}’. + INIntentParameterPromptDialogFormatStringID + ceymMR + INIntentParameterPromptDialogType + DisambiguationIntroduction + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + Just to confirm, you wanted ‘${account}’? + INIntentParameterPromptDialogFormatStringID + z8tfVG + INIntentParameterPromptDialogType + Confirmation + + + INIntentParameterSupportsDynamicEnumeration + + INIntentParameterSupportsSearch + + INIntentParameterTag + 5 + INIntentParameterType + String + + + INIntentResponse + + INIntentResponseCodes + + + INIntentResponseCodeName + success + INIntentResponseCodeSuccess + + + + INIntentResponseCodeName + failure + + + INIntentResponseLastParameterTag + 4 + INIntentResponseOutput + username + INIntentResponseParameters + + + INIntentResponseParameterDisplayName + Username + INIntentResponseParameterDisplayNameID + BFppgH + INIntentResponseParameterDisplayPriority + 1 + INIntentResponseParameterName + username + INIntentResponseParameterTag + 4 + INIntentResponseParameterType + String + + + + INIntentTitle + Followers Count + INIntentTitleID + gpCwrM + INIntentType + Custom + INIntentVerb + View + + + INTypes + + + diff --git a/WidgetExtension/WidgetExtension.swift b/WidgetExtension/WidgetExtension.swift new file mode 100644 index 000000000..43dd95b6d --- /dev/null +++ b/WidgetExtension/WidgetExtension.swift @@ -0,0 +1,9 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import MastodonCore +import MastodonSDK +import MastodonLocalization + +enum WidgetExtension { + static let appContext = AppContext() +} diff --git a/WidgetExtension/WidgetExtensionBundle.swift b/WidgetExtension/WidgetExtensionBundle.swift new file mode 100644 index 000000000..d94557d88 --- /dev/null +++ b/WidgetExtension/WidgetExtensionBundle.swift @@ -0,0 +1,11 @@ +// Copyright © 2023 Mastodon gGmbH. All rights reserved. + +import WidgetKit +import SwiftUI + +@main +struct WidgetExtensionBundle: WidgetBundle { + var body: some Widget { + FollowersWidgetExtension() + } +}