mirror of
https://github.com/home-assistant/iOS.git
synced 2026-06-26 03:02:18 -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 --> ## 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. --> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
219 lines
7.1 KiB
Swift
219 lines
7.1 KiB
Swift
import SFSafeSymbols
|
|
import Shared
|
|
import SwiftUI
|
|
|
|
struct MagicItemAddView: View {
|
|
enum Context {
|
|
case watch
|
|
case carPlay
|
|
case widget
|
|
case appIconShortcut
|
|
}
|
|
|
|
enum PickerOption {
|
|
case entities
|
|
case scripts
|
|
case scenes
|
|
case assistPipelines
|
|
}
|
|
|
|
@Environment(\.dismiss) private var dismiss
|
|
@StateObject private var viewModel = MagicItemAddViewModel()
|
|
@State private var selectedEntity: HAAppEntity?
|
|
private let visiblePickerOptions: [PickerOption]
|
|
private let initialItemType: MagicItemAddType
|
|
|
|
let context: Context
|
|
let itemToAdd: (MagicItem?) -> Void
|
|
|
|
init(
|
|
context: Context,
|
|
initialItemType: MagicItemAddType? = nil,
|
|
visiblePickerOptions: [PickerOption]? = nil,
|
|
itemToAdd: @escaping (MagicItem?) -> Void
|
|
) {
|
|
self.context = context
|
|
self.itemToAdd = itemToAdd
|
|
|
|
let resolvedPickerOptions = visiblePickerOptions ?? {
|
|
var options: [PickerOption] = []
|
|
if [.carPlay, .widget, .appIconShortcut].contains(context) {
|
|
options.append(.entities)
|
|
}
|
|
if context != .widget {
|
|
// In other context user can just select entities directly
|
|
// In Apple watch we don't have entity support yet
|
|
if context == .watch {
|
|
options.append(.scripts)
|
|
options.append(.scenes)
|
|
}
|
|
}
|
|
if [.carPlay, .appIconShortcut].contains(context), #available(iOS 26.0, *) {
|
|
options.append(.assistPipelines)
|
|
}
|
|
return options
|
|
}()
|
|
self.visiblePickerOptions = resolvedPickerOptions
|
|
self.initialItemType = initialItemType ?? Self.defaultItemType(
|
|
for: context,
|
|
visiblePickerOptions: resolvedPickerOptions
|
|
)
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
Group {
|
|
switch viewModel.selectedItemType {
|
|
case .entities:
|
|
VStack {
|
|
pickerView
|
|
.padding(.horizontal)
|
|
entitiesPerServerList()
|
|
}
|
|
case .scripts:
|
|
VStack {
|
|
pickerView
|
|
.padding(.horizontal)
|
|
entitiesPerServerList(domainFilter: .script)
|
|
}
|
|
case .scenes:
|
|
VStack {
|
|
pickerView
|
|
.padding(.horizontal)
|
|
entitiesPerServerList(domainFilter: .scene)
|
|
}
|
|
case .assistPipelines:
|
|
VStack {
|
|
pickerView
|
|
.padding(.horizontal)
|
|
AssistPipelineAddList { pipeline in
|
|
itemToAdd(pipeline)
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.onAppear {
|
|
autoSelectItemType()
|
|
|
|
if viewModel.selectedServerId == nil {
|
|
viewModel.selectedServerId = Current.servers.all.first?.identifier.rawValue
|
|
}
|
|
}
|
|
#if targetEnvironment(macCatalyst)
|
|
.toolbar(content: {
|
|
CloseButton {
|
|
dismiss()
|
|
}
|
|
})
|
|
#endif
|
|
}
|
|
.navigationViewStyle(.stack)
|
|
.modify { view in
|
|
if #available(iOS 16.0, *) {
|
|
view
|
|
.presentationDetents([.large])
|
|
.presentationDragIndicator(.visible)
|
|
} else {
|
|
view
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var pickerView: some View {
|
|
// If there is only one option, don't show the picker
|
|
if visiblePickerOptions.count > 1 {
|
|
Picker(L10n.MagicItem.ItemType.Selection.List.title, selection: $viewModel.selectedItemType) {
|
|
ForEach(visiblePickerOptions, id: \.self) { option in
|
|
switch option {
|
|
case .entities:
|
|
Text(verbatim: L10n.MagicItem.ItemType.Entity.List.title)
|
|
.tag(MagicItemAddType.entities)
|
|
case .scripts:
|
|
Text(verbatim: L10n.MagicItem.ItemType.Script.List.title)
|
|
.tag(MagicItemAddType.scripts)
|
|
case .scenes:
|
|
Text(verbatim: L10n.MagicItem.ItemType.Scene.List.title)
|
|
.tag(MagicItemAddType.scenes)
|
|
case .assistPipelines:
|
|
Text(verbatim: L10n.Widgets.Action.Name.assist)
|
|
.tag(MagicItemAddType.assistPipelines)
|
|
}
|
|
}
|
|
}
|
|
.pickerStyle(.segmented)
|
|
.listRowBackground(Color.clear)
|
|
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
|
|
.padding(.top)
|
|
}
|
|
}
|
|
|
|
private func autoSelectItemType() {
|
|
viewModel.selectedItemType = initialItemType
|
|
}
|
|
|
|
private static func defaultItemType(
|
|
for context: Context,
|
|
visiblePickerOptions: [PickerOption]
|
|
) -> MagicItemAddType {
|
|
if let firstOption = visiblePickerOptions.first {
|
|
switch firstOption {
|
|
case .entities:
|
|
return .entities
|
|
case .scripts:
|
|
return .scripts
|
|
case .scenes:
|
|
return .scenes
|
|
case .assistPipelines:
|
|
return .assistPipelines
|
|
}
|
|
}
|
|
|
|
switch context {
|
|
case .watch:
|
|
return .scripts
|
|
case .carPlay, .widget, .appIconShortcut:
|
|
return .entities
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func entitiesPerServerList(domainFilter: Domain? = nil) -> some View {
|
|
EntityPicker(
|
|
selectedServerId: Current.servers.all
|
|
.first(where: { $0.identifier.rawValue == viewModel.selectedServerId })?.identifier.rawValue,
|
|
selectedEntity: $selectedEntity,
|
|
domainFilter: domainFilter,
|
|
mode: .inline
|
|
)
|
|
.background(
|
|
NavigationLink("", isActive: .init(get: {
|
|
selectedEntity != nil
|
|
}, set: { _ in
|
|
selectedEntity = nil
|
|
})) {
|
|
if let selectedEntity {
|
|
MagicItemCustomizationView(
|
|
mode: .add,
|
|
context: context,
|
|
item: .init(
|
|
id: selectedEntity.entityId,
|
|
serverId: selectedEntity.serverId,
|
|
type: .entity
|
|
)
|
|
) { itemToAdd in
|
|
self.itemToAdd(itemToAdd)
|
|
dismiss()
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
MagicItemAddView(context: .carPlay) { _ in
|
|
}
|
|
}
|