USB-Meter / USB Meter / Views / Meter / MeterSettingsView.swift
1 contributor
264 lines | 8.889kb
//
//  SettingsView.swift
//  USB Meter
//
//  Created by Bogdan Timofte on 14/03/2020.
//  Copyright © 2020 Bogdan Timofte. All rights reserved.
//

import SwiftUI

struct MeterSettingsView: View {
    
    @EnvironmentObject private var meter: Meter
    @Environment(\.dismiss) private var dismiss

    private static let isMacIPadApp: Bool = ProcessInfo.processInfo.isiOSAppOnMac
    private static let isCatalyst: Bool = {
        #if targetEnvironment(macCatalyst)
        return true
        #else
        return false
        #endif
    }()

    @State private var editingName = false
    @State private var editingScreenTimeout = false
    @State private var editingScreenBrightness = false

    var body: some View {
        VStack(spacing: 0) {
            if Self.isMacIPadApp {
                macSettingsHeader
            }
            ScrollView {
            VStack (spacing: 14) {
                settingsCard(title: "Name", tint: meter.color) {
                    HStack {
                        Spacer()
                        if !editingName {
                            Text(meter.name)
                                .foregroundColor(.secondary)
                        }
                        ChevronView(rotate: $editingName)
                    }
                    if editingName {
                        EditNameView(editingName: $editingName, newName: meter.name)
                    }
                }

                if meter.operationalState == .dataIsAvailable && meter.supportsManualTemperatureUnitSelection {
                    settingsCard(title: "Meter Temperature Unit", tint: .orange) {
                        Text("TC66 temperature is shown as degrees without assuming Celsius or Fahrenheit. Keep this matched to the unit configured on the device so you can interpret the reading correctly.")
                            .font(.footnote)
                            .foregroundColor(.secondary)
                        Picker("", selection: $meter.tc66TemperatureUnitPreference) {
                            ForEach(TemperatureUnitPreference.allCases) { unit in
                                Text(unit.title).tag(unit)
                            }
                        }
                        .pickerStyle(SegmentedPickerStyle())
                    }
                }

                if meter.operationalState == .dataIsAvailable {
                    settingsCard(
                        title: meter.reportsCurrentScreenIndex ? "Screen Controls" : "Page Controls",
                        tint: .indigo
                    ) {
                        if meter.reportsCurrentScreenIndex {
                            Text("Use these controls when you want to change the screen shown on the device without crowding the main meter view.")
                                .font(.footnote)
                                .foregroundColor(.secondary)
                        } else {
                            Text("Use these controls when you want to switch device pages without crowding the main meter view.")
                                .font(.footnote)
                                .foregroundColor(.secondary)
                        }

                        ControlView(showsHeader: false)
                    }
                }

                if meter.operationalState == .dataIsAvailable && meter.supportsUMSettings {
                    settingsCard(title: "Screen Timeout", tint: .purple) {
                        HStack {
                            Spacer()
                            if !editingScreenTimeout {
                                Text(meter.screenTimeout != 0 ? "\(meter.screenTimeout) Minutes" : "Off")
                                    .foregroundColor(.secondary)
                            }
                            ChevronView(rotate: $editingScreenTimeout)
                        }
                        if editingScreenTimeout {
                            EditScreenTimeoutView()
                        }
                    }

                    settingsCard(title: "Screen Brightness", tint: .yellow) {
                        HStack {
                            Spacer()
                            if !editingScreenBrightness {
                                Text("\(meter.screenBrightness)")
                                    .foregroundColor(.secondary)
                            }
                            ChevronView(rotate: $editingScreenBrightness)
                        }
                        if editingScreenBrightness {
                            EditScreenBrightnessView()
                        }
                    }
                }
            }
            .padding()
        }
        .background(
            LinearGradient(
                colors: [meter.color.opacity(0.14), Color.clear],
                startPoint: .topLeading,
                endPoint: .bottomTrailing
            )
            .ignoresSafeArea()
        )
        }
        .modifier(IOSOnlySettingsNavBar(
            apply: !Self.isMacIPadApp && !Self.isCatalyst,
            rssi: meter.btSerial.averageRSSI
        ))
    }

    // MARK: - Custom navigation header for Designed-for-iPad on Mac

    private var macSettingsHeader: some View {
        HStack(spacing: 12) {
            Button {
                dismiss()
            } label: {
                HStack(spacing: 4) {
                    Image(systemName: "chevron.left")
                        .font(.body.weight(.semibold))
                    Text("Back")
                }
                .foregroundColor(.accentColor)
            }
            .buttonStyle(.plain)

            Text("Meter Settings")
                .font(.headline)
                .lineLimit(1)

            Spacer()

            if meter.operationalState >= .peripheralNotConnected {
                RSSIView(RSSI: meter.btSerial.averageRSSI)
                    .frame(width: 18, height: 18)
            }
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 10)
        .background(
            Rectangle()
                .fill(.ultraThinMaterial)
                .ignoresSafeArea(edges: .top)
        )
        .overlay(alignment: .bottom) {
            Rectangle()
                .fill(Color.secondary.opacity(0.12))
                .frame(height: 1)
        }
    }

    private func settingsCard<Content: View>(
        title: String,
        tint: Color,
        @ViewBuilder content: () -> Content
    ) -> some View {
        VStack(alignment: .leading, spacing: 12) {
            Text(title)
                .font(.headline)
            content()
        }
        .padding(18)
        .meterCard(tint: tint, fillOpacity: 0.18, strokeOpacity: 0.24)
    }
}

struct EditNameView: View {
    
    @EnvironmentObject private var meter: Meter
    
    @Binding var editingName: Bool
    @State var newName: String
    
    var body: some View {
        TextField("Name", text: self.$newName, onCommit: {
            self.meter.name = self.newName
            self.editingName = false
        })
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .lineLimit(1)
            .disableAutocorrection(true)
            .multilineTextAlignment(.center)
    }
}

struct EditScreenTimeoutView: View {
    
    @EnvironmentObject private var meter: Meter
    
    var body: some View {
        Picker("", selection: self.$meter.screenTimeout ) {
            Text("1").tag(1)
            Text("2").tag(2)
            Text("3").tag(3)
            Text("4").tag(4)
            Text("5").tag(5)
            Text("6").tag(6)
            Text("7").tag(7)
            Text("8").tag(8)
            Text("9").tag(9)
            Text("Off").tag(0)
        }
        .pickerStyle( SegmentedPickerStyle() )
    }
}

struct EditScreenBrightnessView: View {
    
    @EnvironmentObject private var meter: Meter
    
    var body: some View {
        Picker("", selection: self.$meter.screenBrightness ) {
            Text("0").tag(0)
            Text("1").tag(1)
            Text("2").tag(2)
            Text("3").tag(3)
            Text("4").tag(4)
            Text("5").tag(5)
        }
        .pickerStyle( SegmentedPickerStyle() )
    }
}

// MARK: - Conditional navigation bar modifier (skipped on Designed-for-iPad / Mac)

private struct IOSOnlySettingsNavBar: ViewModifier {
    let apply: Bool
    let rssi: Int

    @ViewBuilder
    func body(content: Content) -> some View {
        if apply {
            content
                .navigationBarTitle("Meter Settings")
                .toolbar {
                    ToolbarItem(placement: .navigationBarTrailing) {
                        RSSIView(RSSI: rssi).frame(width: 18, height: 18)
                    }
                }
        } else {
            content
                .navigationBarHidden(true)
        }
    }
}