commit 318f15a049718853df3d0ddc917caab95137f9a8
parent 6ae5e98eb50ffd6f8ffd26d7fb07d06c350bb058
Author: Marc Stibane <marc@taler.net>
Date: Wed, 13 Dec 2023 22:41:36 +0100
ToS language
Diffstat:
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
}
}
}