mirror of
https://github.com/home-assistant/iOS.git
synced 2026-06-24 10:49:41 -05:00
<!-- Thank you for submitting a Pull Request and helping to improve Home Assistant. Please complete the following sections to help the processing and review of your changes. Please do not delete anything from this template. --> ## Summary <!-- Provide a brief summary of the changes you have made and most importantly what they aim to achieve --> As part of removing legacy iOS actions from the app completely, this PR adds a way for the user to choose what to display in app icon long press menu. ## Screenshots <!-- If this is a user-facing change not in the frontend, please include screenshots in light and dark mode. --> <img width="3216" height="1890" alt="CleanShot 2026-04-30 at 11 35 28@2x" src="https://github.com/user-attachments/assets/4680d7e4-4790-4f21-b4b4-b93f641ce769" /> ## Link to pull request in Documentation repository <!-- Pull requests that add, change or remove functionality must have a corresponding pull request in the Companion App Documentation repository (https://github.com/home-assistant/companion.home-assistant). Please add the number of this pull request after the "#" --> Documentation: home-assistant/companion.home-assistant# ## Any other notes <!-- If there is any other information of note, like if this Pull Request is part of a bigger change, please include it here. -->
127 lines
3.9 KiB
Swift
127 lines
3.9 KiB
Swift
import Combine
|
|
import Foundation
|
|
import Shared
|
|
|
|
final class AppIconShortcutsConfigurationViewModel: ObservableObject {
|
|
@Published private(set) var config = AppIconShortcutConfig()
|
|
@Published var showAddItem = false
|
|
@Published var showError = false
|
|
@Published private(set) var errorMessage: String?
|
|
|
|
private let magicItemProvider = Current.magicItemProvider()
|
|
private var cancellables = Set<AnyCancellable>()
|
|
private var isInitialLoad = true
|
|
|
|
init() {
|
|
setupAutoSave()
|
|
}
|
|
|
|
@MainActor
|
|
func loadConfig() {
|
|
magicItemProvider.loadInformation { [weak self] _ in
|
|
guard let self else { return }
|
|
loadDatabase()
|
|
}
|
|
}
|
|
|
|
func magicItemInfo(for item: MagicItem) -> MagicItem.Info? {
|
|
magicItemProvider.getInfo(for: item)
|
|
}
|
|
|
|
func addItem(_ item: MagicItem) {
|
|
let isDuplicate = config.items.contains(where: {
|
|
$0.id == item.id && $0.serverId == item.serverId && $0.type == item.type
|
|
})
|
|
guard !isDuplicate else {
|
|
showError(message: L10n.Settings.AppIconShortcuts.duplicateError)
|
|
return
|
|
}
|
|
config.items.append(item)
|
|
}
|
|
|
|
func updateItem(_ item: MagicItem) {
|
|
if let indexToUpdate = config.items
|
|
.firstIndex(where: { $0.id == item.id && $0.serverId == item.serverId && $0.type == item.type }) {
|
|
config.items[indexToUpdate] = item
|
|
}
|
|
}
|
|
|
|
func deleteItem(at offsets: IndexSet) {
|
|
config.items.remove(atOffsets: offsets)
|
|
}
|
|
|
|
func moveItem(from source: IndexSet, to destination: Int) {
|
|
config.items.move(fromOffsets: source, toOffset: destination)
|
|
}
|
|
|
|
func deleteConfiguration(completion: (Bool) -> Void) {
|
|
do {
|
|
_ = try Current.database().write { db in
|
|
try AppIconShortcutConfig.deleteAll(db)
|
|
}
|
|
AppIconShortcutItemsUpdater.update()
|
|
completion(true)
|
|
} catch {
|
|
showError(message: L10n.Grdb.Config.MigrationError.failedToSave(error.localizedDescription))
|
|
completion(false)
|
|
}
|
|
}
|
|
|
|
private func setupAutoSave() {
|
|
$config
|
|
.dropFirst()
|
|
.sink { [weak self] _ in
|
|
guard let self, !self.isInitialLoad else { return }
|
|
Task { @MainActor in
|
|
self.save()
|
|
}
|
|
}
|
|
.store(in: &cancellables)
|
|
}
|
|
|
|
@discardableResult
|
|
@MainActor
|
|
private func save() -> Bool {
|
|
do {
|
|
try Current.database().write { db in
|
|
try config.insert(db, onConflict: .replace)
|
|
}
|
|
AppIconShortcutItemsUpdater.update()
|
|
return true
|
|
} catch {
|
|
Current.Log.error("Failed to save App Icon Shortcuts config, error: \(error.localizedDescription)")
|
|
showError(message: L10n.Grdb.Config.MigrationError.failedToSave(error.localizedDescription))
|
|
return false
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private func loadDatabase() {
|
|
do {
|
|
if let config = try AppIconShortcutConfig.config() {
|
|
setConfig(config)
|
|
Current.Log.info("App Icon Shortcuts configuration exists")
|
|
} else {
|
|
setConfig(AppIconShortcutConfig())
|
|
Current.Log.info("No App Icon Shortcuts config found, initializing default configuration")
|
|
}
|
|
} catch {
|
|
Current.Log.error("Failed to access database (GRDB), error: \(error.localizedDescription)")
|
|
showError(message: L10n.Grdb.Config.MigrationError.failedAccessGrdb(error.localizedDescription))
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private func setConfig(_ config: AppIconShortcutConfig) {
|
|
self.config = config
|
|
isInitialLoad = false
|
|
}
|
|
|
|
private func showError(message: String) {
|
|
DispatchQueue.main.async { [weak self] in
|
|
self?.errorMessage = message
|
|
self?.showError = true
|
|
}
|
|
}
|
|
}
|