SettingsView.swift (6167B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2026 Bohdan, Volodymyr Potuzhnyi 4 * 5 * GNU Taler is free software; you can redistribute it and/or modify it under the 6 * terms of the GNU General Public License as published by the Free Software 7 * Foundation; either version 3, or (at your option) any later version. 8 * 9 * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 import SwiftUI 18 19 20 private struct LanguageOption: Identifiable { 21 let tag: String 22 let label: String 23 var id: String { tag } 24 } 25 26 private let supportedLanguages: [LanguageOption] = [ 27 LanguageOption(tag: "", label: S.settingsLanguageSystemDefault), 28 LanguageOption(tag: "en", label: "English"), 29 LanguageOption(tag: "de", label: "Deutsch"), 30 LanguageOption(tag: "es", label: "Español"), 31 LanguageOption(tag: "fi", label: "Suomi"), 32 LanguageOption(tag: "fr", label: "Français"), 33 LanguageOption(tag: "it", label: "Italiano"), 34 LanguageOption(tag: "ru", label: "Русский"), 35 LanguageOption(tag: "sv", label: "Svenska"), 36 LanguageOption(tag: "tr", label: "Türkçe"), 37 LanguageOption(tag: "uk", label: "Українська"), 38 LanguageOption(tag: "he", label: "עברית"), 39 ] 40 41 struct SettingsView: View { 42 @EnvironmentObject var configManager: ConfigManager 43 @ObservedObject private var languageManager = LanguageManager.shared 44 let onNavigate: (PosDestination) -> Void 45 var openDrawer: (() -> Void)? 46 47 private var username: String? { 48 guard !configManager.merchantUrl.isEmpty, 49 let url = URL(string: configManager.merchantUrl) else { return nil } 50 return url.pathComponents.last(where: { !$0.isEmpty && $0 != "/" }) 51 } 52 53 var body: some View { 54 VStack(spacing: 0) { 55 PosTopBar(title: S.menuSettings, onMenuTap: { openDrawer?() }) 56 List { 57 Section { 58 Picker(S.settingsInitialOrderLabel, selection: Binding( 59 get: { configManager.initialOrderScreen }, 60 set: { configManager.setInitialOrderScreen($0) } 61 )) { 62 Text(S.menuAmountEntry).tag(InitialOrderScreen.amountEntry) 63 Text(S.menuOrder).tag(InitialOrderScreen.inventory) 64 } 65 66 Picker(S.settingsLanguageLabel, selection: Binding( 67 get: { languageManager.selectedLanguage }, 68 set: { languageManager.setLanguage($0) } 69 )) { 70 ForEach(supportedLanguages) { lang in 71 if lang.tag.isEmpty, let resolved = languageManager.resolvedLanguageLabel { 72 Text("\(lang.label) (\(resolved))").tag(lang.tag) 73 } else { 74 Text(lang.label).tag(lang.tag) 75 } 76 } 77 } 78 } header: { 79 Text(S.settingsAppTitle) 80 .foregroundColor(.posOnSurface) 81 } 82 83 Section { 84 VStack(alignment: .leading, spacing: 12) { 85 Text(S.settingsInstanceDescription) 86 .font(.subheadline) 87 .foregroundColor(.posOnSurface) 88 89 Button { 90 onNavigate(.config) 91 } label: { 92 HStack { 93 Image(systemName: "gearshape.2") 94 Text(S.settingsInstanceButton) 95 } 96 .frame(maxWidth: .infinity) 97 .padding(.vertical, 8) 98 .background(Color.posPrimaryContainer) 99 .foregroundColor(.posOnPrimaryContainer) 100 .cornerRadius(8) 101 } 102 .buttonStyle(.plain) 103 104 Button { 105 configManager.logout() 106 } label: { 107 Text(S.settingsLogoutButton) 108 .frame(maxWidth: .infinity) 109 .padding(.vertical, 8) 110 .background(Color.posErrorContainer) 111 .foregroundColor(.posOnErrorContainer) 112 .cornerRadius(8) 113 } 114 .buttonStyle(.plain) 115 } 116 } header: { 117 Text(S.settingsInstanceTitle) 118 .foregroundColor(.posOnSurface) 119 } 120 121 Section { 122 aboutRow(label: S.appName, value: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "") 123 if let username { 124 aboutRow(label: S.configUsername, value: username) 125 } 126 if !configManager.merchantUrl.isEmpty { 127 let suffix = configManager.backendVersion.map { " (\($0))" } ?? "" 128 aboutRow(label: S.configMerchantUrl, value: "\(configManager.merchantUrl)\(suffix)") 129 } 130 } header: { 131 Text(S.settingsAboutTitle) 132 .foregroundColor(.posOnSurface) 133 } 134 135 } 136 .scrollContentBackground(.hidden) 137 .background(Color.posSurface) 138 } 139 .background(Color.posSurface) 140 .navigationBarHidden(true) 141 .toast(message: Binding( 142 get: { languageManager.languageChangedToast }, 143 set: { languageManager.languageChangedToast = $0 } 144 ), duration: 4) 145 } 146 147 private func aboutRow(label: String, value: String) -> some View { 148 HStack(spacing: 4) { 149 Text(label + ":") 150 .fontWeight(.bold) 151 Text(value) 152 } 153 .font(.subheadline) 154 .foregroundColor(.posOnSurfaceVariant) 155 } 156 }