| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- // LoopFollow
- // LiveActivitySettingsView.swift
- #if !targetEnvironment(macCatalyst)
- import SwiftUI
- struct LiveActivitySettingsView: View {
- @State private var laEnabled: Bool = Storage.shared.laEnabled.value
- @State private var restartConfirmed = false
- @State private var slots: [LiveActivitySlotOption] = LAAppGroupSettings.slots()
- @State private var smallWidgetSlot: LiveActivitySlotOption = LAAppGroupSettings.smallWidgetSlot()
- @State private var keyId: String = Storage.shared.lfKeyId.value
- @State private var apnsKey: String = Storage.shared.lfApnsKey.value
- private let slotLabels = [String(localized: "Top left"), String(localized: "Top right"), String(localized: "Bottom left"), String(localized: "Bottom right")]
- private var apnsConfigured: Bool {
- APNsCredentialValidator.isFullyConfigured(keyId: keyId, apnsKey: apnsKey)
- }
- var body: some View {
- Form {
- Section(
- header: Text("Live Activity"),
- footer: Text("Live Activity updates require APNs credentials. Configure them in Settings → APN.")
- ) {
- Toggle("Enable Live Activity", isOn: $laEnabled)
- }
- if laEnabled {
- if !apnsConfigured {
- Section {
- Label {
- Text("APNs credentials are missing or invalid — Live Activity updates will not work. Open Settings → APN to fix.")
- .font(.callout)
- } icon: {
- Image(systemName: "exclamationmark.triangle.fill")
- .foregroundColor(.orange)
- }
- }
- }
- Section {
- Button(restartConfirmed ? String(localized: "Live Activity Restarted") : String(localized: "Restart Live Activity")) {
- LiveActivityManager.shared.forceRestart()
- restartConfirmed = true
- DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
- restartConfirmed = false
- }
- }
- .disabled(restartConfirmed)
- }
- }
- Section(header: Text("Grid Slots - Live Activity")) {
- ForEach(0 ..< 4, id: \.self) { index in
- Picker(slotLabels[index], selection: Binding(
- get: { slots[index] },
- set: { selectSlot($0, at: index) }
- )) {
- ForEach(LiveActivitySlotOption.gridCases, id: \.self) { option in
- Text(option.displayName).tag(option)
- }
- }
- }
- }
- Section(header: Text("Grid Slot - CarPlay / Watch")) {
- Picker("Right slot", selection: Binding(
- get: { smallWidgetSlot },
- set: { newValue in
- smallWidgetSlot = newValue
- LAAppGroupSettings.setSmallWidgetSlot(newValue)
- LiveActivityManager.shared.refreshFromCurrentState(reason: "small widget slot changed")
- }
- )) {
- ForEach(LiveActivitySlotOption.allCases, id: \.self) { option in
- Text(option.displayName).tag(option)
- }
- }
- }
- }
- .onReceive(Storage.shared.laEnabled.$value) { newValue in
- if newValue != laEnabled { laEnabled = newValue }
- }
- .onReceive(Storage.shared.lfKeyId.$value) { newValue in
- if newValue != keyId { keyId = newValue }
- }
- .onReceive(Storage.shared.lfApnsKey.$value) { newValue in
- if newValue != apnsKey { apnsKey = newValue }
- }
- .onChange(of: laEnabled) { newValue in
- Storage.shared.laEnabled.value = newValue
- if newValue {
- LiveActivityManager.shared.forceRestart()
- } else {
- LiveActivityManager.shared.end(dismissalPolicy: .immediate)
- }
- }
- .preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
- .navigationTitle("Live Activity")
- .navigationBarTitleDisplayMode(.inline)
- }
- /// Selects an option for the given slot index, enforcing uniqueness:
- /// if the chosen option is already in another slot, that slot is cleared to `.none`.
- private func selectSlot(_ option: LiveActivitySlotOption, at index: Int) {
- if option != .none {
- for i in 0 ..< slots.count where i != index && slots[i] == option {
- slots[i] = .none
- }
- }
- slots[index] = option
- LAAppGroupSettings.setSlots(slots)
- LiveActivityManager.shared.refreshFromCurrentState(reason: "slot config changed")
- }
- }
- #endif
|