taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

SettingsItem.swift (8595B)


      1 /*
      2  * This file is part of GNU Taler, ©2022-26 Taler Systems S.A.
      3  * See LICENSE.md
      4  */
      5 /**
      6  * @author Marc Stibane
      7  */
      8 import SwiftUI
      9 
     10 struct SettingsImage: View {
     11     let imageName: String?
     12 
     13     var hasImage: Bool {
     14         if let imageName {
     15             return UIImage(named: imageName) != nil
     16         }
     17         return false
     18     }
     19 
     20     var hasSysImage: Bool {
     21         if let imageName {
     22             return UIImage(systemName: imageName) != nil
     23         }
     24         return false
     25     }
     26 
     27     var body: some View {
     28         if hasImage {
     29             Image(imageName!)
     30                 .resizable()
     31                 .scaledToFit()
     32                 .frame(width: 44, height: 44)
     33         } else if hasSysImage {
     34             Image(systemName: imageName!)
     35                 .resizable()
     36                 .scaledToFit()
     37                 .frame(width: 36, height: 36)
     38                 .padding(.horizontal, 2)
     39         } else {
     40             EmptyView()
     41         }
     42     }
     43 }
     44 
     45 struct SettingsDescription: View {
     46     let id1: String?
     47     let description: String?
     48 
     49     @AppStorage("minimalistic") var minimalistic: Bool = false
     50 
     51     var body: some View {
     52         if !minimalistic {
     53             if let desc = description {
     54                 Text(desc)
     55                     .id(id1 == nil ? nil : id1! + "_T")
     56                     .frame(maxWidth: .infinity, alignment: .leading)
     57                     .talerFont(.caption)
     58             }
     59         }
     60     }
     61 }
     62 // MARK: -
     63 struct SettingsBase<Content: View>: View {
     64     let id1: String?
     65     let description: String?
     66     let content: () -> Content
     67 
     68     init(id1: String?, description: String? = nil, @ViewBuilder content: @escaping () -> Content) {
     69         self.id1 = id1
     70         self.description = description
     71         self.content = content
     72     }
     73 
     74     var body: some View {
     75         let isWeb = id1?.hasPrefix("web") ?? false
     76         let foreColor = isWeb ? Color.accentColor
     77                               : .primary
     78         VStack {
     79             content()
     80                 .id(id1)
     81                 .frame(maxWidth: .infinity, alignment: .leading)
     82                 .foregroundColor(foreColor)
     83                 .talerFont(.title2)
     84                 .padding([.bottom], 0.01)
     85             SettingsDescription(id1: id1, description: description)
     86         }.id(id1 == nil ? nil : id1! + "_V")
     87     }
     88 }
     89 // MARK: -
     90 struct SettingsItem<Content: View>: View {
     91     let name: String
     92     let id1: String?
     93     let imageName: String?
     94     let description: String?
     95     let content: () -> Content
     96 
     97     init(name: String, id1: String, imageName: String? = nil, description: String? = nil,
     98          @ViewBuilder content: @escaping () -> Content
     99     ) {
    100         self.name = name
    101         self.id1 = id1
    102         self.imageName = imageName
    103         self.description = description
    104         self.content = content
    105     }
    106     
    107     var body: some View {
    108         HStack {
    109             SettingsImage(imageName: imageName)
    110 
    111             SettingsBase(id1: id1, description: description) {
    112                 Text(name)
    113             }
    114             content()
    115                 .talerFont(.body)
    116         }.id(id1 == nil ? nil : id1! + "_H")
    117             .accessibilityElement(children: .combine)
    118             .padding([.bottom], 4)
    119     }
    120 }
    121 // MARK: -
    122 struct SettingsToggle: View {
    123     let name: String
    124     @Binding var value: Bool
    125     let id1: String?
    126     let imageName: String?
    127     let description: String?
    128     let action: (_ newValue: Bool) -> Void
    129 
    130     init(name: String, value: Binding<Bool>, id1: String? = nil,
    131          imageName: String? = nil,
    132          description: String? = nil,
    133          action: @escaping (_ newValue: Bool) -> Void = {_ in}
    134     ) {
    135         self.name = name
    136         self._value = value
    137         self.id1 = id1
    138         self.imageName = imageName
    139         self.description = description
    140         self.action = action
    141     }
    142 
    143     var body: some View {
    144         let accLabel: String = if let description {
    145             name + ", " + description
    146         } else {
    147             name
    148         }
    149         HStack {
    150             SettingsImage(imageName: imageName)
    151 
    152             SettingsBase(id1: id1, description: description) {
    153                 Toggle(name, isOn: $value.animation())
    154                     .accessibility(sortPriority: 1)
    155                     .onChange(of: value) { value in
    156                         action(value)
    157                     }
    158             }
    159         }.id(id1 == nil ? nil : id1! + "_H")
    160         .accessibilityElement(children: .combine)
    161         .accessibilityLabel(accLabel)
    162         .padding([.bottom], 4)
    163     }
    164 }
    165 // MARK: -
    166 struct SettingsFont: View {
    167     let title: String
    168     let value: Int
    169     let action: (Int) -> Void
    170 
    171     @State private var selectedFont = 0
    172     let fonts = [String(localized: "Standard iOS Font"), "Atkinson-Hyperlegible", "Nunito"]
    173 
    174     var body: some View {
    175         Picker(title, selection: $selectedFont, content: {
    176             ForEach(0..<fonts.count, id: \.self, content: { index in
    177                 Text(fonts[index]).tag(index)
    178             })
    179         })
    180             .talerFont(.title2)
    181             .pickerStyle(.menu)
    182             .onAppear() {
    183                 withAnimation { selectedFont = value }
    184             }
    185             .onChange(of: selectedFont) { selectedF in
    186                 action(selectedF)
    187             }
    188     }
    189 }
    190 // MARK: -
    191 struct SettingsStyle: View {
    192     let title: String
    193     @Binding var myListStyle: MyListStyle
    194 
    195     var body: some View {
    196         HStack {
    197             Text(title)
    198                 .talerFont(.title2)
    199             Spacer()
    200             Picker(selection: $myListStyle) {
    201                 ForEach(MyListStyle.allCases, id: \.self) {
    202                     Text($0.displayName.capitalized).tag($0)
    203                         .talerFont(.title2)
    204                 }
    205             } label: {}
    206                 .pickerStyle(.menu)
    207 //                .frame(alignment: .trailing)
    208 //                .background(WalletColors().buttonBackColor(pressed: false, disabled: false))  TODO: RoundRect
    209         }
    210         .accessibilityElement(children: .combine)
    211     }
    212 }
    213 // MARK: -
    214 struct SettingsTriState: View {
    215     var name: String
    216     @Binding var value: Int
    217     let id1: String?
    218     var description: String?
    219     var action: (_ value: Int) -> Void = {value in }
    220 
    221     func imageName(_ value: Int) -> (String, String) {
    222         return (value == 0) ? ("eye.slash", "Off")
    223              : (value == 1) ? ("eye",       "Type only")
    224                             : ("eye.fill",  "Type and JSON")
    225     }
    226     var body: some View {
    227         let image = imageName(value)
    228 
    229         VStack {
    230             HStack {
    231                 Text(name)
    232                     .talerFont(.title2)
    233                 Spacer()
    234                 Text(verbatim: " ")
    235                     .talerFont(.largeTitle)
    236                 Button {
    237                     if value > 0 {
    238                         value = -1
    239                         action(value)
    240                         Controller.shared.playSound(1)
    241                     } else {
    242                         value = value + 1
    243                         action(value)
    244                         Controller.shared.playSound(value)
    245                     }
    246                 } label: {
    247                     Image(systemName: image.0)
    248                         .talerFont(.largeTitle)
    249                 }
    250             }
    251 
    252             SettingsDescription(id1: id1, description: description)
    253         }
    254         .accessibilityElement(children: .combine)
    255         .accessibilityLabel(name)
    256         .accessibility(value: Text(image.1))
    257         .accessibilityHint(description ?? EMPTYSTRING)
    258         .padding([.bottom], 4)
    259     }
    260 }
    261 // MARK: -
    262 #if DEBUG
    263 struct SettingsItemPreview : View {
    264     @State var developerMode: Bool = false
    265     @State var observe: Int = 0
    266 
    267     var body: some View {
    268         VStack {
    269             SettingsToggle(name: "Developer Preview", value: $developerMode, id1: "dev1",
    270                     description: "More information intended for debugging")
    271             SettingsTriState(name: "Observe walletCore", value: $observe,
    272                               id1: "observe",
    273                       description: "on LocalConsole")
    274         }
    275     }
    276 }
    277 
    278 struct SettingsItem_Previews: PreviewProvider {
    279     static var previews: some View {
    280         List {
    281             SettingsItem(name: "Exchanges", id1: "list",
    282                   description: "Manage list of exchanges known to this wallet") {}
    283             SettingsItemPreview()
    284             SettingsItem(name: "Save Logfile", id1: "save",
    285                   description: "Help debugging wallet-core") {
    286                 Button("Save") { }
    287                     .buttonStyle(.bordered)
    288                     .disabled(true)
    289             }
    290         }
    291     }
    292 }
    293 #endif