taler-ios

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

commit 318f15a049718853df3d0ddc917caab95137f9a8
parent 6ae5e98eb50ffd6f8ffd26d7fb07d06c350bb058
Author: Marc Stibane <marc@taler.net>
Date:   Wed, 13 Dec 2023 22:41:36 +0100

ToS language

Diffstat:
MTalerWallet1/Helper/CurrencySpecification.swift | 14++++++++++++++
MTalerWallet1/Model/Model+Withdraw.swift | 14+++++++++++---
MTalerWallet1/Views/Exchange/ExchangeRowView.swift | 25+++++++++++++++----------
MTalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
4 files changed, 93 insertions(+), 39 deletions(-)

diff --git a/TalerWallet1/Helper/CurrencySpecification.swift b/TalerWallet1/Helper/CurrencySpecification.swift @@ -6,6 +6,20 @@ import Foundation import taler_swift extension Locale { + static var preferredLanguageCode: String { + guard let preferredLanguage = preferredLanguages.first, + let code = Locale(identifier: preferredLanguage).languageCode else { + return "en" + } + return code + } + + static var preferredLanguageCodes: [String] { + return Locale.preferredLanguages.compactMap({Locale(identifier: $0).languageCode}) + } +} + +extension Locale { func leadingCurrencySymbol() -> Bool { let currencyFormatter = NumberFormatter() currencyFormatter.numberStyle = .currency diff --git a/TalerWallet1/Model/Model+Withdraw.swift b/TalerWallet1/Model/Model+Withdraw.swift @@ -18,6 +18,7 @@ struct AccountRestriction: Decodable { var human_hint_i18n: String? } struct WithdrawalExchangeAccountDetails: Decodable { + var status: String var bankName: String? var paytoUri: String var transferAmount: Amount @@ -92,20 +93,24 @@ struct ExchangeTermsOfService: Decodable { var currentEtag: String var acceptedEtag: String? var tosStatus: ExchangeTosStatus + var tosAvailableLanguages: [String] var contentType: String + var contentLanguage: String? var content: String } /// A request to query an exchange's terms of service. fileprivate struct GetExchangeTermsOfService: WalletBackendFormattedRequest { typealias Response = ExchangeTermsOfService func operation() -> String { "getExchangeTos" } - func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl, acceptedFormat: acceptedFormat) } + func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl, acceptedFormat: acceptedFormat, acceptLanguage: acceptLanguage) } var exchangeBaseUrl: String var acceptedFormat: [String]? + var acceptLanguage: String? struct Args: Encodable { var exchangeBaseUrl: String var acceptedFormat: [String]? + var acceptLanguage: String? } } /// A request to mark an exchange's terms of service as accepted. @@ -192,9 +197,12 @@ extension WalletModel { return response } @MainActor - func loadExchangeTermsOfServiceM(_ exchangeBaseUrl: String, acceptedFormat: [String]) // M for MainActor + func loadExchangeTermsOfServiceM(_ exchangeBaseUrl: String, acceptedFormat: [String], acceptLanguage: String) + // M for MainActor async throws -> ExchangeTermsOfService { - let request = GetExchangeTermsOfService(exchangeBaseUrl: exchangeBaseUrl, acceptedFormat: acceptedFormat) + let request = GetExchangeTermsOfService(exchangeBaseUrl: exchangeBaseUrl, + acceptedFormat: acceptedFormat, + acceptLanguage: acceptLanguage) let response = try await sendRequest(request, ASYNCDELAY) return response } diff --git a/TalerWallet1/Views/Exchange/ExchangeRowView.swift b/TalerWallet1/Views/Exchange/ExchangeRowView.swift @@ -73,18 +73,23 @@ struct ExchangeRowView: View { sendAction: { selectAndUpdate(1) }, recvAction: { selectAndUpdate(2) }) Group { - HStack(spacing: 0) { // can't use the built in Label because it adds the accessory arrow - Text(baseURL.trimURL()) - .accessibilityFont(.headline) + NavigationLink(destination: showToS) { + VStack(alignment: .leading) { + Text(baseURL.trimURL()) + .accessibilityFont(.headline) + if !iconOnly { + Text("Terms of Service") // VIEW_WITHDRAW_TOS + .accessibilityFont(.body) + } + } + } + .background( Group { NavigationLink(destination: deposit, tag: 1, selection: $buttonSelected) - { EmptyView() }.frame(width: 0).opacity(0) + { EmptyView() }.frame(width: 0).opacity(0).hidden() NavigationLink(destination: manualWithdraw, tag: 2, selection: $buttonSelected) - { EmptyView() }.frame(width: 0).opacity(0) - }.listRowSeparator(.hidden) - NavigationLink(destination: showToS) { - Text("Terms of Service") // VIEW_WITHDRAW_TOS - .accessibilityFont(.body) - }.listRowSeparator(.hidden) + { EmptyView() }.frame(width: 0).opacity(0).hidden() + }) + .listRowSeparator(.hidden) if #available(iOS 16.0, *) { ViewThatFits(in: .horizontal) { HStack(spacing: HSPACING) { diff --git a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift @@ -21,9 +21,30 @@ struct WithdrawTOSView: View { @State var exchangeTOS: ExchangeTermsOfService? + func loadToS(_ language: String) async { + do { + if let exchangeBaseUrl { + let acceptedFormat: [String] = [MARKDOWN, PLAINTEXT] + let someTOS = try await model.loadExchangeTermsOfServiceM(exchangeBaseUrl, + acceptedFormat: acceptedFormat, + acceptLanguage: language) + exchangeTOS = someTOS + } else { + // TODO: Yikes! No baseURL + + + + } + } catch { // TODO: error + symLog.log(error.localizedDescription) + } + } + var body: some View { + let language = Locale.preferredLanguageCode if let exchangeTOS { - Content(symLog: symLog, exchangeTOS: exchangeTOS, myListStyle: $myListStyle) { + Content(symLog: symLog, tos: exchangeTOS, myListStyle: $myListStyle, + language: language, languageAction: loadToS) { Task { // runs on MainActor do { if let exchangeBaseUrl { @@ -51,23 +72,9 @@ struct WithdrawTOSView: View { } } else { LoadingView(url: nil, message: exchangeBaseUrl?.trimURL() ?? "No exchangeBaseUrl!") -// ContactingExchangeV(url: nil, message: exchangeBaseUrl?.trimURL() -// ?? "No exchangeBaseUrl!") - .task { do { - if let exchangeBaseUrl { - let acceptedFormat: [String] = [MARKDOWN, PLAINTEXT] - let someTOS = try await model.loadExchangeTermsOfServiceM(exchangeBaseUrl, - acceptedFormat: acceptedFormat) - exchangeTOS = someTOS - } else { - // TODO: Yikes! No baseURL - - - - } - } catch { // TODO: error - symLog.log(error.localizedDescription) - } } + .task { + await loadToS(language) + } } } } @@ -75,15 +82,37 @@ struct WithdrawTOSView: View { extension WithdrawTOSView { struct Content: View { let symLog: SymLogV - var exchangeTOS: ExchangeTermsOfService? + var tos: ExchangeTermsOfService @Binding var myListStyle: MyListStyle + let language: String + var languageAction: (String) async -> () var acceptAction: () -> () + @State private var selectedLanguage = "en" + var body: some View { - if let tos = exchangeTOS { + let title = String(localized: "Language:", comment: "title of ToS language selection") + List { + if tos.tosAvailableLanguages.count > 1 { + Picker(title, selection: $selectedLanguage) { + ForEach(tos.tosAvailableLanguages, id: \.self) { + Text($0) + } + } + .accessibilityFont(.title3) + .pickerStyle(.menu) + .onAppear() { + withAnimation { selectedLanguage = language } + } + .onChange(of: selectedLanguage) { selected in + Task { + await languageAction(selected) + } + } + } let components = tos.content.components(separatedBy: "\n\n") - List (components, id: \.self) { term0 in + ForEach (components, id: \.self) { term0 in if tos.contentType == PLAINTEXT { let term1 = term0.replacingOccurrences(of: "\n ", with: " ") // newline + 5 blanks let term2 = term1.replacingOccurrences(of: "\n" , with: " ") // remove all other linebreaks @@ -108,7 +137,8 @@ extension WithdrawTOSView { .accessibilityFont(.body) } } - }.safeAreaInset(edge: .bottom) { + } + }.safeAreaInset(edge: .bottom) { let currentEtag = tos.currentEtag let showButton = tos.acceptedEtag == nil ? true : tos.acceptedEtag! == tos.currentEtag ? false @@ -118,11 +148,8 @@ extension WithdrawTOSView { .buttonStyle(TalerButtonStyle(type: .prominent)) .padding(.horizontal) } - } - .listStyle(myListStyle.style).anyView - } else { - ErrorView(errortext: String(localized: "unknown ToS")) // TODO: ??? } + .listStyle(myListStyle.style).anyView } } }