iOS/Sources/Extensions/Widgets/Assist/Control/ControlAssistValueProvider....

136 lines
4.9 KiB
Swift

import AppIntents
import Foundation
import Shared
import WidgetKit
@available(iOS 18, *)
struct ControlAssistItem {
let pipeline: AssistPipelineEntity
}
@available(iOS 18, *)
struct ControlAssistValueProvider: AppIntentControlValueProvider {
func currentValue(configuration: ControlAssistConfiguration) async throws -> ControlAssistItem {
.init(pipeline: configuration.pipeline ?? placeholder())
}
func placeholder(for configuration: ControlAssistConfiguration) -> ControlAssistItem {
.init(pipeline: configuration.pipeline ?? placeholder())
}
func previewValue(configuration: ControlAssistConfiguration) -> ControlAssistItem {
.init(pipeline: configuration.pipeline ?? placeholder())
}
private func placeholder() -> AssistPipelineEntity {
AssistPipelineEntity(id: "", serverId: "", name: L10n.Widgets.Controls.Assist.Pipeline.placeholder)
}
}
@available(iOS 18.0, *)
struct ControlAssistConfiguration: ControlConfigurationIntent {
static var title: LocalizedStringResource = "Assist"
@Parameter(
title: .init("app_intents.assist.pipeline.title", defaultValue: "Pipeline")
)
var pipeline: AssistPipelineEntity?
}
@available(iOS 16.4, *)
struct AssistPipelineEntity: AppEntity {
static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Assist Pipeline")
static let defaultQuery = AssistPipelineEntityQuery()
let id: String
let serverId: String
let name: String
var displayRepresentation: DisplayRepresentation {
.init(title: .init(stringLiteral: name))
}
}
@available(iOS 16.4, *)
struct AssistPipelineEntityQuery: EntityQuery, EntityStringQuery {
func entities(for identifiers: [String]) async throws -> [AssistPipelineEntity] {
let pipelinesPerServer = try await pipelines()
let entities = pipelinesPerServer.flatMap { key, pipelines in
pipelines.filter({ identifiers.contains($0.id) }).compactMap { pipeline in
AssistPipelineEntity(id: pipeline.id, serverId: key.identifier.rawValue, name: pipeline.name)
}
}
return entities.filter { entity in
identifiers.contains(entity.id)
}
}
func entities(matching string: String) async throws -> IntentItemCollection<AssistPipelineEntity> {
let pipelines = try await pipelines()
var sections = pipelines.map({ server, pipelines in
IntentItemSection<AssistPipelineEntity>(
.init(stringLiteral: server.info.name),
items: pipelines.filter { $0.name.contains(string) }.map({ pipeline in
.init(AssistPipelineEntity(
id: pipeline.id,
serverId: server.identifier.rawValue,
name: pipeline.name
))
})
)
})
sections.append(.init(
.init(stringLiteral: L10n.helpLabel),
items: [.init(.init(
id: "",
serverId: "",
name: L10n.AppIntents.Assist.RefreshWarning.title
))]
))
return .init(sections: sections)
}
func suggestedEntities() async throws -> IntentItemCollection<AssistPipelineEntity> {
let pipelines = try await pipelines()
var sections = pipelines.map({ server, pipelines in
IntentItemSection<AssistPipelineEntity>(
.init(stringLiteral: server.info.name),
items: pipelines.map({ pipeline in
.init(AssistPipelineEntity(
id: pipeline.id,
serverId: server.identifier.rawValue,
name: pipeline.name
))
})
)
})
sections.append(.init(
.init(stringLiteral: L10n.helpLabel),
items: [.init(.init(id: "-1", serverId: "", name: L10n.AppIntents.Assist.RefreshWarning.title))]
))
return .init(sections: sections)
}
private func pipelines() async throws -> [Server: [Pipeline]] {
do {
var result: [Server: [Pipeline]] = [:]
let pipelines = try await Current.database.read { db in
try AssistPipelines.fetchAll(db)
}
pipelines.forEach { assistPipeline in
guard let server = Current.servers.all
.first(where: { $0.identifier.rawValue == assistPipeline.serverId }) else { return }
result[server] = assistPipeline.pipelines
// Empty id indicates use of preferred pipeline
result[server]?.insert(.init(id: "", name: L10n.AppIntents.Assist.PreferredPipeline.title), at: 0)
}
return result
} catch {
Current.Log.error("Failed to fetch assist pipelines for ControlAssist: \(error.localizedDescription)")
throw error
}
}
}