iOS/Sources/Extensions/Watch/Home/MagicItemRow/WatchMagicViewRow.swift

139 lines
4.6 KiB
Swift

import Shared
import SwiftUI
struct WatchMagicViewRow: View {
@StateObject private var viewModel: WatchMagicViewRowViewModel
init(item: MagicItem, itemInfo: MagicItem.Info) {
self._viewModel = .init(wrappedValue: .init(item: item, itemInfo: itemInfo))
}
var body: some View {
Button {
viewModel.executeItem()
} label: {
HStack(spacing: Spaces.one) {
iconToDisplay
.animation(.bouncy, value: viewModel.state)
Text(viewModel.itemInfo.name)
.font(.body.bold())
.foregroundStyle(textColor)
.lineLimit(3)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.trailing)
}
}
.listRowBackground(backgroundForWatchItem.cornerRadius(14))
.confirmationDialog(
L10n.Watch.Home.Run.Confirmation.title(viewModel.itemInfo.name),
isPresented: $viewModel.showConfirmationDialog,
actions: {
Button(action: {
viewModel.confirmationAction()
}, label: {
Text(L10n.yesLabel)
})
Button(action: {}, label: {
Text(L10n.cancelLabel)
})
.tint(.red)
}
)
.modify { view in
if let backgroundColor = viewModel.item.customization?.backgroundColor {
view.listRowBackground(
Color(uiColor: .init(hex: backgroundColor))
.clipShape(RoundedRectangle(cornerRadius: 14))
)
} else {
view
}
}
.onChange(of: viewModel.state) { newValue in
// TODO: On watchOS 10 this can be replaced by '.sensoryFeedback' modifier
let currentDevice = WKInterfaceDevice.current()
switch newValue {
case .success:
currentDevice.play(.success)
case .failure:
currentDevice.play(.failure)
case .loading:
currentDevice.play(.click)
default:
break
}
}
}
private var iconToDisplay: some View {
VStack {
switch viewModel.state {
case .idle:
Image(uiImage: viewModel.item.icon(info: viewModel.itemInfo).image(
ofSize: .init(width: 24, height: 24),
color: .init(hex: viewModel.itemInfo.customization?.iconColor)
))
.foregroundStyle(Color(uiColor: .init(hex: viewModel.itemInfo.customization?.iconColor)))
.padding()
case .loading:
ProgressView()
.progressViewStyle(.circular)
.frame(width: 24, height: 24)
.shadow(color: .white, radius: 10)
.padding()
case .success:
makeStateImage(systemName: "checkmark.circle.fill")
case .failure:
makeStateImage(systemName: "xmark.circle")
}
}
.frame(width: 38, height: 38)
.background(Color(uiColor: .init(hex: viewModel.itemInfo.customization?.iconColor)).opacity(0.3))
.clipShape(Circle())
.padding([.vertical, .trailing], Spaces.half)
}
private func makeStateImage(systemName: String) -> some View {
Image(systemName: systemName)
.font(.system(size: 24))
.foregroundStyle(.white)
.padding()
}
private var textColor: Color {
if let textColor = viewModel.itemInfo.customization?.textColor {
.init(uiColor: .init(hex: textColor))
} else {
.white
}
}
private var backgroundForWatchItem: Color {
if let backgroundColor = viewModel.itemInfo.customization?.backgroundColor {
Color(uiColor: .init(hex: backgroundColor))
} else {
.gray.opacity(0.3)
}
}
}
#Preview {
MaterialDesignIcons.register()
return List {
WatchMagicViewRow(
item: .init(id: "1", serverId: "1", type: .script),
itemInfo: .init(
id: "1",
name: "New script",
iconName: "mdi:door-closed-lock",
customization: .init(backgroundColor: "#ff00ff")
)
)
WatchMagicViewRow(
item: .init(id: "1", serverId: "1", type: .action),
itemInfo: .init(id: "1", name: "New Action", iconName: "earth")
)
}
.background(Color.red)
}