Add more shortcuts

This commit is contained in:
Thomas Ricouard 2024-05-02 11:37:38 +02:00
parent 4e4d903c44
commit 49a5c6a56a
12 changed files with 295 additions and 7 deletions

View file

@ -44,6 +44,9 @@
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4B2952005C00B3281A /* MessagesTab.swift */; };
9F37BDDB2BE36E22007F28AD /* PostIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F37BDDA2BE36E22007F28AD /* PostIntent.swift */; };
9F37BDDD2BE37193007F28AD /* AppIntentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F37BDDC2BE37193007F28AD /* AppIntentService.swift */; };
9F37BDDF2BE37C35007F28AD /* TabIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F37BDDE2BE37C35007F28AD /* TabIntent.swift */; };
9F37BDE12BE38646007F28AD /* PostPhotoIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F37BDE02BE38646007F28AD /* PostPhotoIntent.swift */; };
9F37BDE32BE393A7007F28AD /* AppShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F37BDE22BE393A7007F28AD /* AppShortcuts.swift */; };
9F38A7332ACEA26100DBCD66 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9F38A7322ACEA26100DBCD66 /* Localizable.xcstrings */; };
9F38A7342ACEA26100DBCD66 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9F38A7322ACEA26100DBCD66 /* Localizable.xcstrings */; };
9F38A7352ACEA26100DBCD66 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9F38A7322ACEA26100DBCD66 /* Localizable.xcstrings */; };
@ -200,6 +203,9 @@
9F35DB4B2952005C00B3281A /* MessagesTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesTab.swift; sourceTree = "<group>"; };
9F37BDDA2BE36E22007F28AD /* PostIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostIntent.swift; sourceTree = "<group>"; };
9F37BDDC2BE37193007F28AD /* AppIntentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentService.swift; sourceTree = "<group>"; };
9F37BDDE2BE37C35007F28AD /* TabIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabIntent.swift; sourceTree = "<group>"; };
9F37BDE02BE38646007F28AD /* PostPhotoIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostPhotoIntent.swift; sourceTree = "<group>"; };
9F37BDE22BE393A7007F28AD /* AppShortcuts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppShortcuts.swift; sourceTree = "<group>"; };
9F38A7322ACEA26100DBCD66 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
9F398AA32935F90100A889F2 /* Models */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Models; path = Packages/Models; sourceTree = "<group>"; };
9F398AA52935FE8A00A889F2 /* AppRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRegistry.swift; sourceTree = "<group>"; };
@ -358,6 +364,9 @@
children = (
9F37BDDA2BE36E22007F28AD /* PostIntent.swift */,
9F37BDDC2BE37193007F28AD /* AppIntentService.swift */,
9F37BDDE2BE37C35007F28AD /* TabIntent.swift */,
9F37BDE02BE38646007F28AD /* PostPhotoIntent.swift */,
9F37BDE22BE393A7007F28AD /* AppShortcuts.swift */,
);
path = IceCubesAppIntents;
sourceTree = "<group>";
@ -844,6 +853,8 @@
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
9F37BDDB2BE36E22007F28AD /* PostIntent.swift in Sources */,
9F37BDDD2BE37193007F28AD /* AppIntentService.swift in Sources */,
9F37BDE12BE38646007F28AD /* PostPhotoIntent.swift in Sources */,
9F37BDDF2BE37C35007F28AD /* TabIntent.swift in Sources */,
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
9FC14EF42B494D940006CEE1 /* RemoteTimelinesSettingView.swift in Sources */,
@ -862,6 +873,7 @@
9F2B92FA295DA7D700DE16D0 /* AddAccountsView.swift in Sources */,
639CDF9C296AC82F00C35E58 /* SafariRouter.swift in Sources */,
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */,
9F37BDE32BE393A7007F28AD /* AppShortcuts.swift in Sources */,
9F654BEF299AC45B00D27FA5 /* ReportView.swift in Sources */,
D08A9C3529956CFA00204A4A /* SwipeActionsSettingsView.swift in Sources */,
9F7335F22967608F00AFF0BA /* AddRemoteTimelineView.swift in Sources */,

View file

@ -90,6 +90,9 @@ extension View {
case let .prefilledStatusEditor(text, visibility):
StatusEditor.MainView(mode: .new(text: text, visibility: visibility))
.withEnvironments()
case let .imageURL(urls, visibility):
StatusEditor.MainView(mode: .imageURL(urls: urls, visibility: visibility))
.withEnvironments()
case let .editStatusEditor(status):
StatusEditor.MainView(mode: .edit(status: status))
.withEnvironments()

View file

@ -135,6 +135,12 @@ extension IceCubesApp {
appRouterPath.presentedSheet = .prefilledStatusEditor(text: postIntent.content ?? "",
visibility: userPreferences.postVisibility)
#endif
} else if let tabIntent = appIntentService.handledIntent?.intent as? TabIntent {
selectedTab = tabIntent.tab.toAppTab
} else if let imageIntent = appIntentService.handledIntent?.intent as? PostPhotoIntent,
let urls = imageIntent.images?.compactMap({ $0.fileURL }) {
appRouterPath.presentedSheet = .imageURL(urls: urls,
visibility: userPreferences.postVisibility)
}
}
}

View file

@ -4,6 +4,7 @@ import Explore
import Foundation
import StatusKit
import SwiftUI
import AppIntents
@MainActor
enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {

View file

@ -20513,6 +20513,9 @@
}
}
}
},
"Bookmarks" : {
},
"conversations.action.delete" : {
"comment" : "MARK: Package: Conversations",
@ -26422,6 +26425,9 @@
}
}
}
},
"Explore & Trending" : {
},
"explore.navigation-title" : {
"comment" : "MARK: Package: Explore",
@ -28446,6 +28452,12 @@
}
}
}
},
"Favorites" : {
},
"Federated Timeline" : {
},
"filter.action.hide" : {
"extractionState" : "manual",
@ -30708,6 +30720,18 @@
}
}
}
},
"Followed Tags" : {
},
"Home Timeline" : {
},
"Image" : {
},
"Image to post on Mastodon" : {
},
"instance.info.domains" : {
"comment" : "MARK: Instances",
@ -32957,6 +32981,9 @@
}
}
}
},
"Lists" : {
},
"lists.add-remove-%@" : {
"comment" : "MARK: Package: Lists",
@ -33910,6 +33937,12 @@
}
}
}
},
"Local Timeline" : {
},
"Mentions" : {
},
"menu.font" : {
"localizations" : {
@ -34619,6 +34652,12 @@
}
}
}
},
"New post" : {
},
"Notifications" : {
},
"notifications-others-count %lld" : {
"extractionState" : "manual",
@ -39700,6 +39739,15 @@
}
}
}
},
"Open Ice Cubes" : {
},
"Open on a tab" : {
},
"Open the app on a specific tab" : {
},
"placeholder.loading.long" : {
"localizations" : {
@ -39936,11 +39984,26 @@
}
}
}
},
"Post a status" : {
},
"Post an image to Mastodon" : {
},
"Post content" : {
},
"Post to Mastodon" : {
"Post images" : {
},
"Post status to Mastodon" : {
},
"Private Messages" : {
},
"Profile" : {
},
"report.action.send" : {
@ -40416,6 +40479,12 @@
}
}
}
},
"Selected tab" : {
},
"Settings" : {
},
"settings.about.built-with" : {
"localizations" : {
@ -74591,6 +74660,9 @@
}
}
}
},
"Tab" : {
},
"tab.explore" : {
"comment" : "MARK: Tabs",
@ -80113,6 +80185,12 @@
}
}
}
},
"Trending Links" : {
},
"Trending Timeline" : {
},
"trending-tag-people-talking %lld" : {
"extractionState" : "manual",
@ -80473,7 +80551,10 @@
}
}
},
"Use Ice Cubes to post text to Mastodon" : {
"Use Ice Cubes to post a status to Mastodon" : {
},
"Use Ice Cubes to post a status with an image to Mastodon" : {
}
},

View file

@ -0,0 +1,34 @@
import AppIntents
struct AppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: PostIntent(),
phrases: [
"Post \(\.$content) in \(.applicationName)",
"Post a status on Mastodon with \(.applicationName)",
"Write a status in \(.applicationName)",
],
shortTitle: "Post a status",
systemImageName: "square.and.pencil"
)
AppShortcut(
intent: TabIntent(),
phrases: [
"Open \(\.$tab) in \(.applicationName)",
"Open \(.applicationName)",
],
shortTitle: "Open Ice Cubes",
systemImageName: "cube"
)
AppShortcut(
intent: PostPhotoIntent(),
phrases: [
"Post images \(\.$images) in \(.applicationName)",
"Send photos \(\.$images) with \(.applicationName)",
],
shortTitle: "Post images",
systemImageName: "photo"
)
}
}

View file

@ -2,10 +2,10 @@ import Foundation
import AppIntents
struct PostIntent: AppIntent {
static let title: LocalizedStringResource = "Post to Mastodon"
static let title: LocalizedStringResource = "Post status to Mastodon"
static var description: IntentDescription {
get {
"Use Ice Cubes to post text to Mastodon"
"Use Ice Cubes to post a status to Mastodon"
}
}
static let openAppWhenRun: Bool = true

View file

@ -0,0 +1,23 @@
import Foundation
import AppIntents
struct PostPhotoIntent: AppIntent {
static let title: LocalizedStringResource = "Post an image to Mastodon"
static var description: IntentDescription {
get {
"Use Ice Cubes to post a status with an image to Mastodon"
}
}
static let openAppWhenRun: Bool = true
@Parameter(title: "Image",
description: "Image to post on Mastodon",
supportedTypeIdentifiers: ["public.image"],
inputConnectionBehavior: .connectToPreviousIntentResult)
var images: [IntentFile]?
func perform() async throws -> some IntentResult {
AppIntentService.shared.handledIntent = .init(intent: self)
return .result()
}
}

View file

@ -0,0 +1,96 @@
import Foundation
import AppIntents
enum TabEnum: String, AppEnum, Sendable {
case timeline, notifications, mentions, explore, messages, settings
case trending, federated, local
case profile
case bookmarks
case favorites
case post
case followedTags
case lists
case links
static var typeDisplayName: LocalizedStringResource {
get { "Tab" }
}
static let typeDisplayRepresentation: TypeDisplayRepresentation = "Tab"
nonisolated static var caseDisplayRepresentations: [TabEnum : DisplayRepresentation] {
[.timeline: .init(title: "Home Timeline"),
.trending: .init(title: "Trending Timeline"),
.federated: .init(title: "Federated Timeline"),
.local: .init(title: "Local Timeline"),
.notifications: .init(title: "Notifications"),
.mentions: .init(title: "Mentions"),
.explore: .init(title: "Explore & Trending"),
.messages: .init(title: "Private Messages"),
.settings: .init(title: "Settings"),
.profile: .init(title: "Profile"),
.bookmarks: .init(title: "Bookmarks"),
.favorites: .init(title: "Favorites"),
.followedTags: .init(title: "Followed Tags"),
.lists: .init(title: "Lists"),
.links: .init(title: "Trending Links"),
.post: .init(title: "New post"),
]
}
var toAppTab: Tab {
switch self {
case .timeline:
.timeline
case .notifications:
.notifications
case .mentions:
.mentions
case .explore:
.explore
case .messages:
.messages
case .settings:
.settings
case .trending:
.trending
case .federated:
.federated
case .local:
.local
case .profile:
.profile
case .bookmarks:
.bookmarks
case .favorites:
.favorites
case .post:
.post
case .followedTags:
.followedTags
case .lists:
.lists
case .links:
.links
}
}
}
struct TabIntent: AppIntent {
static let title: LocalizedStringResource = "Open on a tab"
static var description: IntentDescription {
get {
"Open the app on a specific tab"
}
}
static let openAppWhenRun: Bool = true
@Parameter(title: "Selected tab")
var tab: TabEnum
@MainActor
func perform() async throws -> some IntentResult {
AppIntentService.shared.handledIntent = .init(intent: self)
return .result()
}
}

View file

@ -54,6 +54,7 @@ public enum SheetDestination: Identifiable, Hashable {
case newStatusEditor(visibility: Models.Visibility)
case prefilledStatusEditor(text: String, visibility: Models.Visibility)
case imageURL(urls: [URL], visibility: Models.Visibility)
case editStatusEditor(status: Status)
case replyToStatusEditor(status: Status)
case quoteStatusEditor(status: Status)
@ -80,7 +81,7 @@ public enum SheetDestination: Identifiable, Hashable {
public var id: String {
switch self {
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
.mentionStatusEditor, .quoteLinkStatusEditor, .prefilledStatusEditor:
.mentionStatusEditor, .quoteLinkStatusEditor, .prefilledStatusEditor, .imageURL:
"statusEditor"
case .listCreate:
"listCreate"

View file

@ -11,6 +11,7 @@ public extension StatusEditor.ViewModel {
case quoteLink(link: URL)
case mention(account: Account, visibility: Models.Visibility)
case shareExtension(items: [NSItemProvider])
case imageURL(urls: [URL], visibility: Models.Visibility)
var isInShareExtension: Bool {
switch self {
@ -41,7 +42,7 @@ public extension StatusEditor.ViewModel {
var title: LocalizedStringKey {
switch self {
case .new, .mention, .shareExtension, .quoteLink:
case .new, .mention, .shareExtension, .quoteLink, .imageURL:
"status.editor.mode.new"
case .edit:
"status.editor.mode.edit"

View file

@ -234,7 +234,7 @@ public extension StatusEditor {
language: selectedLanguage,
mediaAttributes: mediaAttributes)
switch mode {
case .new, .replyTo, .quote, .mention, .shareExtension, .quoteLink:
case .new, .replyTo, .quote, .mention, .shareExtension, .quoteLink, .imageURL:
postStatus = try await client.post(endpoint: Statuses.postStatus(json: data))
if let postStatus {
StreamWatcher.shared.emmitPostEvent(for: postStatus)
@ -311,6 +311,13 @@ public extension StatusEditor {
itemsProvider = items
visibility = .pub
processItemsProvider(items: items)
case let .imageURL(urls, visibility):
Task {
for container in await Self.makeImageContainer(from: urls) {
prepareToPost(for: container)
}
}
self.visibility = visibility
case let .replyTo(status):
var mentionString = ""
if (status.reblog?.account.acct ?? status.account.acct) != currentAccount?.acct {
@ -739,6 +746,29 @@ public extension StatusEditor {
error: nil
)
}
private static func makeImageContainer(from urls: [URL]) async -> [MediaContainer] {
var containers: [MediaContainer] = []
for url in urls {
let compressor = Compressor()
if let compressedData = await compressor.compressImageFrom(url: url),
let image = UIImage(data: compressedData) {
containers.append(MediaContainer(
id: UUID().uuidString,
image: image,
movieTransferable: nil,
gifTransferable: nil,
mediaAttachment: nil,
error: nil
))
}
}
return containers
}
func upload(container: MediaContainer) async {
if let index = indexOf(container: container) {