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 --> This PR adds device and area context to Watch, CarPlay, Widgets and App Icon shortcuts configuration screens. ## Screenshots <!-- If this is a user-facing change not in the frontend, please include screenshots in light and dark mode. --> ## 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. -->
140 lines
4.4 KiB
Swift
140 lines
4.4 KiB
Swift
import Foundation
|
|
import SFSafeSymbols
|
|
import Shared
|
|
import SwiftUI
|
|
|
|
struct AppIconShortcutsConfigurationView: View {
|
|
@Environment(\.dismiss) private var dismiss
|
|
@StateObject private var viewModel = AppIconShortcutsConfigurationViewModel()
|
|
|
|
@State private var isLoaded = false
|
|
@State private var showResetConfirmation = false
|
|
|
|
var body: some View {
|
|
List {
|
|
header
|
|
itemsSection
|
|
resetView
|
|
}
|
|
.navigationTitle("")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.onAppear {
|
|
guard !isLoaded else { return }
|
|
viewModel.loadConfig()
|
|
isLoaded = true
|
|
}
|
|
.sheet(isPresented: $viewModel.showAddItem) {
|
|
MagicItemAddView(context: .appIconShortcut) { itemToAdd in
|
|
guard let itemToAdd else { return }
|
|
viewModel.addItem(itemToAdd)
|
|
}
|
|
}
|
|
.alert(viewModel.errorMessage ?? L10n.errorLabel, isPresented: $viewModel.showError) {
|
|
Button(action: {}, label: {
|
|
Text(verbatim: L10n.okLabel)
|
|
})
|
|
}
|
|
}
|
|
|
|
private var header: some View {
|
|
AppleLikeListTopRowHeader(
|
|
image: .applicationCogOutlineIcon,
|
|
title: L10n.Settings.AppIconShortcuts.title,
|
|
subtitle: L10n.Settings.AppIconShortcuts.subtitle
|
|
)
|
|
}
|
|
|
|
private var itemsSection: some View {
|
|
Section {
|
|
ForEach(viewModel.config.items, id: \.serverUniqueId) { item in
|
|
makeListItem(item: item)
|
|
}
|
|
.onMove { indices, newOffset in
|
|
viewModel.moveItem(from: indices, to: newOffset)
|
|
}
|
|
.onDelete { indexSet in
|
|
viewModel.deleteItem(at: indexSet)
|
|
}
|
|
Button {
|
|
viewModel.showAddItem = true
|
|
} label: {
|
|
Label(L10n.Settings.AppIconShortcuts.AddItem.title, systemSymbol: .plus)
|
|
}
|
|
} header: {
|
|
Text(L10n.Settings.AppIconShortcuts.Items.title)
|
|
} footer: {
|
|
Text(L10n.Settings.AppIconShortcuts.Footer.title)
|
|
}
|
|
}
|
|
|
|
private func makeListItem(item: MagicItem) -> some View {
|
|
let itemInfo = viewModel.magicItemInfo(for: item) ?? .init(
|
|
id: item.id,
|
|
name: item.id,
|
|
iconName: "",
|
|
customization: nil
|
|
)
|
|
return makeListItemRow(item: item, info: itemInfo)
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func makeListItemRow(item: MagicItem, info: MagicItem.Info) -> some View {
|
|
NavigationLink {
|
|
MagicItemCustomizationView(mode: .edit, context: .appIconShortcut, item: item) { updatedMagicItem in
|
|
viewModel.updateItem(updatedMagicItem)
|
|
}
|
|
} label: {
|
|
itemRow(item: item, info: info)
|
|
}
|
|
}
|
|
|
|
private func itemRow(item: MagicItem, info: MagicItem.Info) -> some View {
|
|
HStack {
|
|
Image(uiImage: image(for: item, itemInfo: info))
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(item.name(info: info))
|
|
if let contextSubtitle = info.contextSubtitle {
|
|
Text(contextSubtitle)
|
|
.font(.footnote)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
Image(systemSymbol: .line3Horizontal)
|
|
.foregroundStyle(.gray)
|
|
}
|
|
}
|
|
|
|
private func image(for item: MagicItem, itemInfo: MagicItem.Info) -> UIImage {
|
|
item.icon(info: itemInfo).image(
|
|
ofSize: .init(width: 18, height: 18),
|
|
color: .init(hex: itemInfo.customization?.iconColor)
|
|
)
|
|
}
|
|
|
|
private var resetView: some View {
|
|
Button(L10n.Settings.AppIconShortcuts.Reset.title, role: .destructive) {
|
|
showResetConfirmation = true
|
|
}
|
|
.confirmationDialog(
|
|
L10n.Settings.AppIconShortcuts.Reset.confirmationTitle,
|
|
isPresented: $showResetConfirmation,
|
|
titleVisibility: .visible
|
|
) {
|
|
Button(L10n.yesLabel, role: .destructive) {
|
|
viewModel.deleteConfiguration { success in
|
|
if success {
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
Button(L10n.noLabel, role: .cancel) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
AppIconShortcutsConfigurationView()
|
|
}
|