mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-01-11 08:35:26 +00:00
Add AppIntent service + post to Mastodon intent
This commit is contained in:
parent
abcd4cc321
commit
4e4d903c44
11 changed files with 109 additions and 7 deletions
|
@ -42,6 +42,8 @@
|
||||||
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4629506F6600B3281A /* NotificationTab.swift */; };
|
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4629506F6600B3281A /* NotificationTab.swift */; };
|
||||||
9F35DB4A29506FA100B3281A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB4929506FA100B3281A /* Notifications */; };
|
9F35DB4A29506FA100B3281A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB4929506FA100B3281A /* Notifications */; };
|
||||||
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4B2952005C00B3281A /* MessagesTab.swift */; };
|
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 */; };
|
||||||
9F38A7332ACEA26100DBCD66 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9F38A7322ACEA26100DBCD66 /* Localizable.xcstrings */; };
|
9F38A7332ACEA26100DBCD66 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9F38A7322ACEA26100DBCD66 /* Localizable.xcstrings */; };
|
||||||
9F38A7342ACEA26100DBCD66 /* 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 */; };
|
9F38A7352ACEA26100DBCD66 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9F38A7322ACEA26100DBCD66 /* Localizable.xcstrings */; };
|
||||||
|
@ -196,6 +198,8 @@
|
||||||
9F35DB4629506F6600B3281A /* NotificationTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTab.swift; sourceTree = "<group>"; };
|
9F35DB4629506F6600B3281A /* NotificationTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTab.swift; sourceTree = "<group>"; };
|
||||||
9F35DB4829506F7F00B3281A /* Notifications */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Notifications; path = Packages/Notifications; sourceTree = "<group>"; };
|
9F35DB4829506F7F00B3281A /* Notifications */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Notifications; path = Packages/Notifications; sourceTree = "<group>"; };
|
||||||
9F35DB4B2952005C00B3281A /* MessagesTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesTab.swift; sourceTree = "<group>"; };
|
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>"; };
|
||||||
9F38A7322ACEA26100DBCD66 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; 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>"; };
|
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>"; };
|
9F398AA52935FE8A00A889F2 /* AppRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRegistry.swift; sourceTree = "<group>"; };
|
||||||
|
@ -349,6 +353,15 @@
|
||||||
path = IceCubesNotifications;
|
path = IceCubesNotifications;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
9F37BDD92BE36E08007F28AD /* IceCubesAppIntents */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9F37BDDA2BE36E22007F28AD /* PostIntent.swift */,
|
||||||
|
9F37BDDC2BE37193007F28AD /* AppIntentService.swift */,
|
||||||
|
);
|
||||||
|
path = IceCubesAppIntents;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9F398AB429360A5800A889F2 /* App */ = {
|
9F398AB429360A5800A889F2 /* App */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -454,6 +467,7 @@
|
||||||
DD31E2E5297FB68B00A4BE29 /* IceCubesApp.xcconfig */,
|
DD31E2E5297FB68B00A4BE29 /* IceCubesApp.xcconfig */,
|
||||||
9F7D939529800B0300EE6B7A /* IceCubesApp-release.xcconfig */,
|
9F7D939529800B0300EE6B7A /* IceCubesApp-release.xcconfig */,
|
||||||
9FBFE63B292A715500C250E9 /* IceCubesApp */,
|
9FBFE63B292A715500C250E9 /* IceCubesApp */,
|
||||||
|
9F37BDD92BE36E08007F28AD /* IceCubesAppIntents */,
|
||||||
E9DF41FD29830FEC0003AAD2 /* IceCubesActionExtension */,
|
E9DF41FD29830FEC0003AAD2 /* IceCubesActionExtension */,
|
||||||
9F2A5417296AB631009B2D7C /* IceCubesNotifications */,
|
9F2A5417296AB631009B2D7C /* IceCubesNotifications */,
|
||||||
9FAD858929743F7400496AB1 /* IceCubesShareExtension */,
|
9FAD858929743F7400496AB1 /* IceCubesShareExtension */,
|
||||||
|
@ -828,6 +842,8 @@
|
||||||
9F15D6042B3DC2180008C220 /* NavigationSheet.swift in Sources */,
|
9F15D6042B3DC2180008C220 /* NavigationSheet.swift in Sources */,
|
||||||
9FA6FD6229C04A8800E2312C /* TranslationSettingsView.swift in Sources */,
|
9FA6FD6229C04A8800E2312C /* TranslationSettingsView.swift in Sources */,
|
||||||
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
||||||
|
9F37BDDB2BE36E22007F28AD /* PostIntent.swift in Sources */,
|
||||||
|
9F37BDDD2BE37193007F28AD /* AppIntentService.swift in Sources */,
|
||||||
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
|
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
|
||||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
||||||
9FC14EF42B494D940006CEE1 /* RemoteTimelinesSettingView.swift in Sources */,
|
9FC14EF42B494D940006CEE1 /* RemoteTimelinesSettingView.swift in Sources */,
|
||||||
|
|
|
@ -85,7 +85,10 @@ extension View {
|
||||||
StatusEditor.MainView(mode: .replyTo(status: status))
|
StatusEditor.MainView(mode: .replyTo(status: status))
|
||||||
.withEnvironments()
|
.withEnvironments()
|
||||||
case let .newStatusEditor(visibility):
|
case let .newStatusEditor(visibility):
|
||||||
StatusEditor.MainView(mode: .new(visibility: visibility))
|
StatusEditor.MainView(mode: .new(text: nil, visibility: visibility))
|
||||||
|
.withEnvironments()
|
||||||
|
case let .prefilledStatusEditor(text, visibility):
|
||||||
|
StatusEditor.MainView(mode: .new(text: text, visibility: visibility))
|
||||||
.withEnvironments()
|
.withEnvironments()
|
||||||
case let .editStatusEditor(status):
|
case let .editStatusEditor(status):
|
||||||
StatusEditor.MainView(mode: .edit(status: status))
|
StatusEditor.MainView(mode: .edit(status: status))
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Env
|
||||||
import MediaUI
|
import MediaUI
|
||||||
import StatusKit
|
import StatusKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import AppIntents
|
||||||
|
|
||||||
extension IceCubesApp {
|
extension IceCubesApp {
|
||||||
var appScene: some Scene {
|
var appScene: some Scene {
|
||||||
|
@ -22,6 +23,7 @@ extension IceCubesApp {
|
||||||
.environment(theme)
|
.environment(theme)
|
||||||
.environment(watcher)
|
.environment(watcher)
|
||||||
.environment(pushNotificationsService)
|
.environment(pushNotificationsService)
|
||||||
|
.environment(appIntentService)
|
||||||
.environment(\.isSupporter, isSupporter)
|
.environment(\.isSupporter, isSupporter)
|
||||||
.sheet(item: $quickLook.selectedMediaAttachment) { selectedMediaAttachment in
|
.sheet(item: $quickLook.selectedMediaAttachment) { selectedMediaAttachment in
|
||||||
MediaUIView(selectedAttachment: selectedMediaAttachment,
|
MediaUIView(selectedAttachment: selectedMediaAttachment,
|
||||||
|
@ -47,6 +49,12 @@ extension IceCubesApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onChange(of: appIntentService.handledIntent) { _, _ in
|
||||||
|
if let intent = appIntentService.handledIntent?.intent {
|
||||||
|
handleIntent(intent)
|
||||||
|
appIntentService.handledIntent = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
.withModelContainer()
|
.withModelContainer()
|
||||||
}
|
}
|
||||||
#if targetEnvironment(macCatalyst)
|
#if targetEnvironment(macCatalyst)
|
||||||
|
@ -74,7 +82,9 @@ extension IceCubesApp {
|
||||||
Group {
|
Group {
|
||||||
switch destination.wrappedValue {
|
switch destination.wrappedValue {
|
||||||
case let .newStatusEditor(visibility):
|
case let .newStatusEditor(visibility):
|
||||||
StatusEditor.MainView(mode: .new(visibility: visibility))
|
StatusEditor.MainView(mode: .new(text: nil, visibility: visibility))
|
||||||
|
case let .prefilledStatusEditor(text, visibility):
|
||||||
|
StatusEditor.MainView(mode: .new(text: text, visibility: visibility))
|
||||||
case let .editStatusEditor(status):
|
case let .editStatusEditor(status):
|
||||||
StatusEditor.MainView(mode: .edit(status: status))
|
StatusEditor.MainView(mode: .edit(status: status))
|
||||||
case let .quoteStatusEditor(status):
|
case let .quoteStatusEditor(status):
|
||||||
|
@ -115,4 +125,16 @@ extension IceCubesApp {
|
||||||
.defaultSize(width: 1200, height: 1000)
|
.defaultSize(width: 1200, height: 1000)
|
||||||
.windowResizability(.contentMinSize)
|
.windowResizability(.contentMinSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func handleIntent(_ intent: any AppIntent) {
|
||||||
|
if let postIntent = appIntentService.handledIntent?.intent as? PostIntent {
|
||||||
|
#if os(visionOS) || os(macOS)
|
||||||
|
openWindow(value: WindowDestinationEditor.prefilledStatusEditor(text: postIntent.content ?? "",
|
||||||
|
visibility: userPreferences.postVisibility))
|
||||||
|
#else
|
||||||
|
appRouterPath.presentedSheet = .prefilledStatusEditor(text: postIntent.content ?? "",
|
||||||
|
visibility: userPreferences.postVisibility)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ struct IceCubesApp: App {
|
||||||
@State var currentAccount = CurrentAccount.shared
|
@State var currentAccount = CurrentAccount.shared
|
||||||
@State var userPreferences = UserPreferences.shared
|
@State var userPreferences = UserPreferences.shared
|
||||||
@State var pushNotificationsService = PushNotificationsService.shared
|
@State var pushNotificationsService = PushNotificationsService.shared
|
||||||
|
@State var appIntentService = AppIntentService.shared
|
||||||
@State var watcher = StreamWatcher.shared
|
@State var watcher = StreamWatcher.shared
|
||||||
@State var quickLook = QuickLook.shared
|
@State var quickLook = QuickLook.shared
|
||||||
@State var theme = Theme.shared
|
@State var theme = Theme.shared
|
||||||
|
|
|
@ -39936,6 +39936,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Post content" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Post to Mastodon" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"report.action.send" : {
|
"report.action.send" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
@ -80466,6 +80472,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Use Ice Cubes to post text to Mastodon" : {
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"version" : "1.0"
|
"version" : "1.0"
|
||||||
|
|
25
IceCubesAppIntents/AppIntentService.swift
Normal file
25
IceCubesAppIntents/AppIntentService.swift
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import SwiftUI
|
||||||
|
import AppIntents
|
||||||
|
|
||||||
|
@Observable
|
||||||
|
public class AppIntentService: @unchecked Sendable {
|
||||||
|
struct HandledIntent: Equatable {
|
||||||
|
static func == (lhs: AppIntentService.HandledIntent, rhs: AppIntentService.HandledIntent) -> Bool {
|
||||||
|
lhs.id == rhs.id
|
||||||
|
}
|
||||||
|
|
||||||
|
let id: String
|
||||||
|
let intent: any AppIntent
|
||||||
|
|
||||||
|
init(intent: any AppIntent) {
|
||||||
|
self.id = UUID().uuidString
|
||||||
|
self.intent = intent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static let shared = AppIntentService()
|
||||||
|
|
||||||
|
var handledIntent: HandledIntent?
|
||||||
|
|
||||||
|
private init() { }
|
||||||
|
}
|
20
IceCubesAppIntents/PostIntent.swift
Normal file
20
IceCubesAppIntents/PostIntent.swift
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import Foundation
|
||||||
|
import AppIntents
|
||||||
|
|
||||||
|
struct PostIntent: AppIntent {
|
||||||
|
static let title: LocalizedStringResource = "Post to Mastodon"
|
||||||
|
static var description: IntentDescription {
|
||||||
|
get {
|
||||||
|
"Use Ice Cubes to post text to Mastodon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static let openAppWhenRun: Bool = true
|
||||||
|
|
||||||
|
@Parameter(title: "Post content")
|
||||||
|
var content: String?
|
||||||
|
|
||||||
|
func perform() async throws -> some IntentResult {
|
||||||
|
AppIntentService.shared.handledIntent = .init(intent: self)
|
||||||
|
return .result()
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ public enum RouterDestination: Hashable {
|
||||||
|
|
||||||
public enum WindowDestinationEditor: Hashable, Codable {
|
public enum WindowDestinationEditor: Hashable, Codable {
|
||||||
case newStatusEditor(visibility: Models.Visibility)
|
case newStatusEditor(visibility: Models.Visibility)
|
||||||
|
case prefilledStatusEditor(text: String, visibility: Models.Visibility)
|
||||||
case editStatusEditor(status: Status)
|
case editStatusEditor(status: Status)
|
||||||
case replyToStatusEditor(status: Status)
|
case replyToStatusEditor(status: Status)
|
||||||
case quoteStatusEditor(status: Status)
|
case quoteStatusEditor(status: Status)
|
||||||
|
@ -52,6 +53,7 @@ public enum SheetDestination: Identifiable, Hashable {
|
||||||
}
|
}
|
||||||
|
|
||||||
case newStatusEditor(visibility: Models.Visibility)
|
case newStatusEditor(visibility: Models.Visibility)
|
||||||
|
case prefilledStatusEditor(text: String, visibility: Models.Visibility)
|
||||||
case editStatusEditor(status: Status)
|
case editStatusEditor(status: Status)
|
||||||
case replyToStatusEditor(status: Status)
|
case replyToStatusEditor(status: Status)
|
||||||
case quoteStatusEditor(status: Status)
|
case quoteStatusEditor(status: Status)
|
||||||
|
@ -78,7 +80,7 @@ public enum SheetDestination: Identifiable, Hashable {
|
||||||
public var id: String {
|
public var id: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
|
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
|
||||||
.mentionStatusEditor, .quoteLinkStatusEditor:
|
.mentionStatusEditor, .quoteLinkStatusEditor, .prefilledStatusEditor:
|
||||||
"statusEditor"
|
"statusEditor"
|
||||||
case .listCreate:
|
case .listCreate:
|
||||||
"listCreate"
|
"listCreate"
|
||||||
|
|
|
@ -161,7 +161,7 @@ extension StatusEditor {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
// all SEVM have the same visibility value
|
// all SEVM have the same visibility value
|
||||||
followUpSEVMs.append(ViewModel(mode: .new(visibility: focusedSEVM.visibility)))
|
followUpSEVMs.append(ViewModel(mode: .new(text: nil, visibility: focusedSEVM.visibility)))
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "arrowshape.turn.up.left.circle.fill")
|
Image(systemName: "arrowshape.turn.up.left.circle.fill")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import UIKit
|
||||||
public extension StatusEditor.ViewModel {
|
public extension StatusEditor.ViewModel {
|
||||||
enum Mode {
|
enum Mode {
|
||||||
case replyTo(status: Status)
|
case replyTo(status: Status)
|
||||||
case new(visibility: Models.Visibility)
|
case new(text: String?, visibility: Models.Visibility)
|
||||||
case edit(status: Status)
|
case edit(status: Status)
|
||||||
case quote(status: Status)
|
case quote(status: Status)
|
||||||
case quoteLink(link: URL)
|
case quoteLink(link: URL)
|
||||||
|
|
|
@ -301,7 +301,11 @@ public extension StatusEditor {
|
||||||
|
|
||||||
func prepareStatusText() {
|
func prepareStatusText() {
|
||||||
switch mode {
|
switch mode {
|
||||||
case let .new(visibility):
|
case let .new(text, visibility):
|
||||||
|
if let text {
|
||||||
|
statusText = .init(string: text)
|
||||||
|
selectedRange = .init(location: text.utf16.count, length: 0)
|
||||||
|
}
|
||||||
self.visibility = visibility
|
self.visibility = visibility
|
||||||
case let .shareExtension(items):
|
case let .shareExtension(items):
|
||||||
itemsProvider = items
|
itemsProvider = items
|
||||||
|
@ -557,7 +561,7 @@ public extension StatusEditor {
|
||||||
!statusText.string.contains(url.absoluteString)
|
!statusText.string.contains(url.absoluteString)
|
||||||
{
|
{
|
||||||
embeddedStatus = nil
|
embeddedStatus = nil
|
||||||
mode = .new(visibility: visibility)
|
mode = .new(text: nil, visibility: visibility)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue