Files
App/ios/Runner/Services/AppIntentHandlers.swift
2026-01-16 21:30:59 +08:00

297 lines
9.8 KiB
Swift

//
// AppIntentHandlers.swift
// Runner
//
// Created by LittleSheep on 2026/1/16.
//
import AppIntents
import UIKit
@available(iOS 16.0, *)
struct OpenChatIntent: AppIntent {
static var title: LocalizedStringResource = "intent_open_chat_title"
static var description = IntentDescription("intent_open_chat_desc")
static var isDiscoverable = true
static var openAppWhenRun = true
@Parameter(title: "Channel ID")
var channelId: String?
func perform() async throws -> some IntentResult & OpensIntent {
guard let channelId = channelId, !channelId.isEmpty else {
throw AppIntentError.requiredParameter("Channel ID")
}
DeepLinkHandler.shared.handle(url: URL(string: "solian://chat/\(channelId)")!)
return .result(value: "Opening chat \(channelId)")
}
}
@available(iOS 16.0, *)
struct OpenPostIntent: AppIntent {
static var title: LocalizedStringResource = "intent_open_post_title"
static var description = IntentDescription("intent_open_post_desc")
static var isDiscoverable = true
static var openAppWhenRun = true
@Parameter(title: "Post ID")
var postId: String?
func perform() async throws -> some IntentResult & OpensIntent {
guard let postId = postId, !postId.isEmpty else {
throw AppIntentError.requiredParameter("Post ID")
}
DeepLinkHandler.shared.handle(url: URL(string: "solian://posts/\(postId)")!)
return .result(value: "Opening post \(postId)")
}
}
@available(iOS 16.0, *)
struct OpenComposeIntent: AppIntent {
static var title: LocalizedStringResource = "intent_open_compose_title"
static var description = IntentDescription("intent_open_compose_desc")
static var isDiscoverable = true
static var openAppWhenRun = true
func perform() async throws -> some IntentResult & OpensIntent {
DeepLinkHandler.shared.handle(url: URL(string: "solian://compose")!)
return .result(value: "Opening compose screen")
}
}
@available(iOS 16.0, *)
struct ComposePostIntent: AppIntent {
static var title: LocalizedStringResource = "intent_compose_post_title"
static var description = IntentDescription("intent_compose_post_desc")
static var isDiscoverable = true
static var openAppWhenRun = true
func perform() async throws -> some IntentResult & OpensIntent {
DeepLinkHandler.shared.handle(url: URL(string: "solian://compose")!)
return .result(value: "Opening compose screen")
}
}
@available(iOS 16.0, *)
struct SearchContentIntent: AppIntent {
static var title: LocalizedStringResource = "intent_search_title"
static var description = IntentDescription("intent_search_desc")
static var isDiscoverable = true
static var openAppWhenRun = true
@Parameter(title: "Search Query")
var query: String?
func perform() async throws -> some IntentResult & OpensIntent {
guard let query = query, !query.isEmpty else {
throw AppIntentError.requiredParameter("Search Query")
}
let encodedQuery = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? query
DeepLinkHandler.shared.handle(url: URL(string: "solian://search?q=\(encodedQuery)")!)
return .result(value: "Searching for \"\(query)\"")
}
}
@available(iOS 16.0, *)
struct ViewNotificationsIntent: AppIntent {
static var title: LocalizedStringResource = "intent_notifications_title"
static var description = IntentDescription("intent_notifications_desc")
static var isDiscoverable = true
static var openAppWhenRun = true
func perform() async throws -> some IntentResult & OpensIntent {
DeepLinkHandler.shared.handle(url: URL(string: "solian://notifications")!)
return .result(value: "Opening notifications")
}
}
@available(iOS 16.0, *)
struct CheckNotificationsIntent: AppIntent {
static var title: LocalizedStringResource = "intent_check_notifications_title"
static var description = IntentDescription("intent_check_notifications_desc")
static var isDiscoverable = true
static var openAppWhenRun = false
func perform() async throws -> some IntentResult & ProvidesDialog {
do {
let count = try await NetworkService.shared.getNotificationCount()
let message: String
if count == 0 {
message = "You have no new notifications"
} else if count == 1 {
message = "You have 1 new notification"
} else {
message = "You have \(count) new notifications"
}
return .result(
value: message,
dialog: "\(message)"
)
} catch {
throw AppIntentError.networkError("Failed to check notifications: \(error.localizedDescription)")
}
}
}
@available(iOS 16.0, *)
struct SendMessageIntent: AppIntent {
static var title: LocalizedStringResource = "intent_send_message_title"
static var description = IntentDescription("intent_send_message_desc")
static var isDiscoverable = true
static var openAppWhenRun = false
@Parameter(title: "Channel ID")
var channelId: String?
@Parameter(title: "Message Content")
var content: String?
func perform() async throws -> some IntentResult & ProvidesDialog {
guard let channelId = channelId, !channelId.isEmpty else {
throw AppIntentError.requiredParameter("Channel ID")
}
guard let content = content, !content.isEmpty else {
throw AppIntentError.requiredParameter("Message Content")
}
do {
try await NetworkService.shared.sendMessage(channelId: channelId, content: content)
return .result(
value: "Message sent to channel \(channelId)",
dialog: "Message sent successfully"
)
} catch {
throw AppIntentError.networkError("Failed to send message: \(error.localizedDescription)")
}
}
}
@available(iOS 16.0, *)
struct ReadMessagesIntent: AppIntent {
static var title: LocalizedStringResource = "intent_read_messages_title"
static var description = IntentDescription("intent_read_messages_desc")
static var isDiscoverable = true
static var openAppWhenRun = false
@Parameter(title: "Channel ID")
var channelId: String?
@Parameter(title: "Number of Messages", default: "5")
var limit: String?
func perform() async throws -> some IntentResult & ProvidesDialog {
guard let channelId = channelId, !channelId.isEmpty else {
throw AppIntentError.requiredParameter("Channel ID")
}
let limitValue = Int(limit ?? "5") ?? 5
let safeLimit = max(1, min(20, limitValue))
do {
let messages = try await NetworkService.shared.getMessages(
channelId: channelId,
offset: 0,
take: safeLimit
)
if messages.isEmpty {
return .result(
value: "No messages found in channel \(channelId)",
dialog: "No messages found"
)
}
let formattedMessages = messages.compactMap { message -> String? in
let senderName = message.sender?.account?.name ?? "Unknown"
let content = message.content ?? ""
return "\(senderName): \(content)"
}.joined(separator: "\n")
return .result(
value: formattedMessages,
dialog: "Found \(messages.count) messages"
)
} catch {
throw AppIntentError.networkError("Failed to read messages: \(error.localizedDescription)")
}
}
}
@available(iOS 16.0, *)
struct CheckUnreadChatsIntent: AppIntent {
static var title: LocalizedStringResource = "intent_unread_chats_title"
static var description = IntentDescription("intent_unread_chats_desc")
static var isDiscoverable = true
static var openAppWhenRun = false
func perform() async throws -> some IntentResult & ProvidesDialog {
do {
let count = try await NetworkService.shared.getUnreadChatsCount()
let message: String
if count == 0 {
message = "You have no unread messages"
} else if count == 1 {
message = "You have 1 unread message"
} else {
message = "You have \(count) unread messages"
}
return .result(
value: message,
dialog: "\(message)"
)
} catch {
throw AppIntentError.networkError("Failed to check unread chats: \(error.localizedDescription)")
}
}
}
@available(iOS 16.0, *)
struct MarkNotificationsReadIntent: AppIntent {
static var title: LocalizedStringResource = "intent_mark_read_title"
static var description = IntentDescription("intent_mark_read_desc")
static var isDiscoverable = true
static var openAppWhenRun = false
func perform() async throws -> some IntentResult & ProvidesDialog {
do {
try await NetworkService.shared.markNotificationsRead()
return .result(
value: "All notifications marked as read",
dialog: "All notifications marked as read"
)
} catch {
throw AppIntentError.networkError("Failed to mark notifications: \(error.localizedDescription)")
}
}
}
enum AppIntentError: Error, CustomLocalizedStringResourceConvertible {
case requiredParameter(String)
case networkError(String)
var localizedStringResource: LocalizedStringResource {
switch self {
case .requiredParameter(let param):
return "\(param) is required"
case .networkError(let message):
return "Network error: \(message)"
}
}
}