148 lines
5.7 KiB
Swift
148 lines
5.7 KiB
Swift
import AppIntents
|
|
import GRDB
|
|
import PromiseKit
|
|
import RealmSwift
|
|
import Shared
|
|
import WidgetKit
|
|
|
|
struct WidgetScriptsEntry: TimelineEntry {
|
|
let date: Date
|
|
let scripts: [ScriptServer]
|
|
let showServerName: Bool
|
|
let showConfirmationDialog: Bool
|
|
|
|
struct ScriptServer {
|
|
let id: String
|
|
let entityId: String
|
|
let serverId: String
|
|
let serverName: String
|
|
let name: String
|
|
let icon: String
|
|
}
|
|
}
|
|
|
|
@available(iOS 17, *)
|
|
struct WidgetScriptsAppIntentTimelineProvider: AppIntentTimelineProvider {
|
|
typealias Entry = WidgetScriptsEntry
|
|
typealias Intent = WidgetScriptsAppIntent
|
|
|
|
static var expiration: Measurement<UnitDuration> {
|
|
.init(value: 24, unit: .hours)
|
|
}
|
|
|
|
func snapshot(for configuration: WidgetScriptsAppIntent, in context: Context) async -> Entry {
|
|
let suggestions = await suggestions()
|
|
let placeholder: [WidgetScriptsEntry.ScriptServer] = await Array(suggestions.flatMap { serverCollection in
|
|
serverCollection.value.map { script in
|
|
WidgetScriptsEntry.ScriptServer(
|
|
id: script.id,
|
|
entityId: script.entityId,
|
|
serverId: serverCollection.key.identifier.rawValue,
|
|
serverName: serverCollection.key.info.name,
|
|
name: script.name,
|
|
icon: script.icon ?? ""
|
|
)
|
|
}
|
|
}.prefix(WidgetBasicContainerView.maximumCount(family: context.family)))
|
|
|
|
return .init(
|
|
date: Date(),
|
|
scripts: configuration.scripts?.compactMap({ intentScriptEntity in
|
|
.init(
|
|
id: intentScriptEntity.id,
|
|
entityId: intentScriptEntity.entityId,
|
|
serverId: intentScriptEntity.serverId,
|
|
serverName: intentScriptEntity.serverName,
|
|
name: intentScriptEntity.displayString,
|
|
icon: intentScriptEntity.iconName
|
|
)
|
|
}) ?? placeholder,
|
|
showServerName: showServerName(),
|
|
showConfirmationDialog: configuration.showConfirmationDialog
|
|
)
|
|
}
|
|
|
|
func timeline(for configuration: Intent, in context: Context) async -> Timeline<Entry> {
|
|
let entry: Entry = await {
|
|
if let configurationScripts = await configuration.scripts?
|
|
.prefix(WidgetBasicContainerView.maximumCount(family: context.family)) {
|
|
return Entry(date: Date(), scripts: configurationScripts.compactMap({ intentScriptEntity in
|
|
.init(
|
|
id: intentScriptEntity.id,
|
|
entityId: intentScriptEntity.entityId,
|
|
serverId: intentScriptEntity.serverId,
|
|
serverName: intentScriptEntity.serverName,
|
|
name: intentScriptEntity.displayString,
|
|
icon: intentScriptEntity.iconName
|
|
)
|
|
}), showServerName: showServerName(), showConfirmationDialog: configuration.showConfirmationDialog)
|
|
} else {
|
|
let entries = await suggestions().flatMap { server, scripts in
|
|
scripts.map { script in
|
|
WidgetScriptsEntry.ScriptServer(
|
|
id: script.entityId,
|
|
entityId: script.entityId,
|
|
serverId: server.identifier.rawValue,
|
|
serverName: server.info.name,
|
|
name: script.name,
|
|
icon: script.icon ?? ""
|
|
)
|
|
}
|
|
}.prefix(WidgetBasicContainerView.maximumCount(family: context.family))
|
|
return Entry(
|
|
date: Date(),
|
|
scripts: Array(entries),
|
|
showServerName: showServerName(),
|
|
showConfirmationDialog: configuration.showConfirmationDialog
|
|
)
|
|
}
|
|
}()
|
|
return .init(
|
|
entries: [entry],
|
|
policy: .after(
|
|
Current.date()
|
|
.addingTimeInterval(Self.expiration.converted(to: .seconds).value)
|
|
)
|
|
)
|
|
}
|
|
|
|
func placeholder(in context: Context) -> Entry {
|
|
.init(
|
|
date: Date(),
|
|
scripts: [.init(
|
|
id: "1",
|
|
entityId: "1",
|
|
serverId: "1",
|
|
serverName: "Home",
|
|
name: L10n.Widgets.Scripts.title,
|
|
icon: ""
|
|
)],
|
|
showServerName: true, showConfirmationDialog: true
|
|
)
|
|
}
|
|
|
|
private func showServerName() -> Bool {
|
|
Current.servers.all.count > 1
|
|
}
|
|
|
|
private func suggestions() async -> [Server: [HAAppEntity]] {
|
|
await withCheckedContinuation { continuation in
|
|
var entities: [Server: [HAAppEntity]] = [:]
|
|
for server in Current.servers.all.sorted(by: { $0.info.name < $1.info.name }) {
|
|
do {
|
|
let scripts: [HAAppEntity] = try Current.database.read { db in
|
|
try HAAppEntity
|
|
.filter(Column(DatabaseTables.AppEntity.serverId.rawValue) == server.identifier.rawValue)
|
|
.filter(Column(DatabaseTables.AppEntity.domain.rawValue) == Domain.script.rawValue)
|
|
.fetchAll(db)
|
|
}
|
|
entities[server] = scripts
|
|
} catch {
|
|
Current.Log.error("Failed to load scripts from database: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
continuation.resume(returning: entities)
|
|
}
|
|
}
|
|
}
|