gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-ios] branch master updated (4b4d112 -> 1a6daf7)


From: gnunet
Subject: [taler-taler-ios] branch master updated (4b4d112 -> 1a6daf7)
Date: Thu, 19 Dec 2024 23:12:30 +0100

This is an automated email from the git hooks/post-receive script.

marc-stibane pushed a change to branch master
in repository taler-ios.

    from 4b4d112  Spanish translations
     new 2e7d64a  fix #9400
     new 85bed85  isInternal #9332
     new 5654595  cleanup
     new 62c1a07  terms for tx
     new 8a66280  NavigationModel tosView
     new 661b668  ForEachWithIndex
     new f33818a  A11Y colors + fontsize
     new 111c5f5  A11Y VoiceOver dismiss button
     new 77cc979  hide tabBar in ToS
     new a142dd1  API wording
     new a761a68  exportDbToFile
     new 790f617  L10N
     new 6201f7e  withTerms
     new b205d06  prepare
     new 400a327  loc
     new 1d24c5f  cleanup
     new 642f7c9  L10N
     new 1a6daf7  Bump version to 0.14.1 (0.14.1)

The 18 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 TalerWallet.xcodeproj/project.pbxproj              | 20 ++++--
 .../Assets.xcassets/Error.colorset/Contents.json   |  6 +-
 TalerWallet1/Backend/WalletCore.swift              | 11 +++-
 TalerWallet1/Controllers/Controller.swift          |  2 +-
 TalerWallet1/Controllers/PublicConstants.swift     |  2 +
 TalerWallet1/Helper/Font+Taler.swift               |  2 +
 TalerWallet1/Helper/WalletColors.swift             | 33 +++++-----
 TalerWallet1/Localizable.xcstrings                 | 74 ++++++++++++++++++++--
 TalerWallet1/Model/Model+Deposit.swift             |  3 +
 TalerWallet1/Model/Model+Withdraw.swift            |  2 +-
 TalerWallet1/Model/WalletModel.swift               | 62 ++++++++++++++----
 TalerWallet1/Views/Actions/ActionsSheet.swift      | 29 +++++----
 .../Views/Actions/Banking/ManualWithdraw.swift     | 67 +++++++++++++-------
 TalerWallet1/Views/Actions/TwoRowButtons.swift     |  3 +-
 TalerWallet1/Views/HelperViews/Buttons.swift       | 19 +++---
 .../HelperViews/ForEachWithIndex.swift}            | 18 ++++--
 TalerWallet1/Views/HelperViews/SelectDays.swift    |  3 +-
 TalerWallet1/Views/HelperViews/SubjectInputV.swift |  2 +-
 TalerWallet1/Views/HelperViews/TabBarView.swift    |  2 +-
 .../Views/HelperViews/TransactionButton.swift      | 14 ++--
 TalerWallet1/Views/HelperViews/View+NavLink.swift  |  4 +-
 TalerWallet1/Views/Main/MainView.swift             | 27 +++-----
 TalerWallet1/Views/Main/NavigationModel.swift      | 35 ++++++++++
 .../Views/Settings/Bank/BankEditView.swift         |  8 +--
 .../Views/Settings/Exchange/ExchangeListView.swift |  6 +-
 .../Views/Settings/Exchange/ExchangeRowView.swift  | 28 +++++++-
 .../Settings/Exchange/ExchangeSectionView.swift    |  9 ++-
 TalerWallet1/Views/Settings/SettingsItem.swift     |  2 +-
 TalerWallet1/Views/Settings/SettingsView.swift     |  6 +-
 .../WithdrawBankIntegrated/WithdrawTOSView.swift   |  3 +-
 TalerWallet1/Views/Sheets/WithdrawExchangeV.swift  |  5 +-
 .../Views/Transactions/TransactionSummaryV.swift   |  3 +-
 TestFlight/WhatToTest.en-US.txt                    |  6 ++
 33 files changed, 369 insertions(+), 147 deletions(-)
 copy TalerWallet1/{Helper/AnyTransition+backslide.swift => 
Views/HelperViews/ForEachWithIndex.swift} (68%)
 create mode 100644 TalerWallet1/Views/Main/NavigationModel.swift

diff --git a/TalerWallet.xcodeproj/project.pbxproj 
b/TalerWallet.xcodeproj/project.pbxproj
index f7d9eec..f4b954b 100644
--- a/TalerWallet.xcodeproj/project.pbxproj
+++ b/TalerWallet.xcodeproj/project.pbxproj
@@ -312,6 +312,10 @@
                4EEC157A29F9427F00D46A03 /* QRSheet.swift in Sources */ = {isa 
= PBXBuildFile; fileRef = 4EEC157929F9427F00D46A03 /* QRSheet.swift */; };
                4EEC3A712B2285A200D05F9D /* WithdrawExchangeV.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EEC3A702B2285A200D05F9D /* 
WithdrawExchangeV.swift */; };
                4EEC3A722B2285A200D05F9D /* WithdrawExchangeV.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EEC3A702B2285A200D05F9D /* 
WithdrawExchangeV.swift */; };
+               4EED38552D140C1400F6C038 /* NavigationModel.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EED38542D140C1400F6C038 /* 
NavigationModel.swift */; };
+               4EED38562D140C1400F6C038 /* NavigationModel.swift in Sources */ 
= {isa = PBXBuildFile; fileRef = 4EED38542D140C1400F6C038 /* 
NavigationModel.swift */; };
+               4EED38582D1485AB00F6C038 /* ForEachWithIndex.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EED38572D1485AB00F6C038 /* 
ForEachWithIndex.swift */; };
+               4EED38592D1485AB00F6C038 /* ForEachWithIndex.swift in Sources 
*/ = {isa = PBXBuildFile; fileRef = 4EED38572D1485AB00F6C038 /* 
ForEachWithIndex.swift */; };
                4EF840A72A0B85F400EE0D47 /* CopyShare.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EF840A62A0B85F400EE0D47 /* CopyShare.swift */; 
};
                4EFA39602AA7946B00742548 /* ToSButtonView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EFA395F2AA7946B00742548 /* ToSButtonView.swift 
*/; };
                4EFA39612AA7946B00742548 /* ToSButtonView.swift in Sources */ = 
{isa = PBXBuildFile; fileRef = 4EFA395F2AA7946B00742548 /* ToSButtonView.swift 
*/; };
@@ -520,6 +524,8 @@
                4EEC157729F9032900D46A03 /* Sheet.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= Sheet.swift; sourceTree = "<group>"; };
                4EEC157929F9427F00D46A03 /* QRSheet.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= QRSheet.swift; sourceTree = "<group>"; };
                4EEC3A702B2285A200D05F9D /* WithdrawExchangeV.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= WithdrawExchangeV.swift; sourceTree = "<group>"; };
+               4EED38542D140C1400F6C038 /* NavigationModel.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
NavigationModel.swift; sourceTree = "<group>"; };
+               4EED38572D1485AB00F6C038 /* ForEachWithIndex.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
ForEachWithIndex.swift; sourceTree = "<group>"; };
                4EF840A62A0B85F400EE0D47 /* CopyShare.swift */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path 
= CopyShare.swift; sourceTree = "<group>"; };
                4EFA395F2AA7946B00742548 /* ToSButtonView.swift */ = {isa = 
PBXFileReference; lastKnownFileType = sourcecode.swift; path = 
ToSButtonView.swift; sourceTree = "<group>"; };
                4EFFDD6A2A501121000C1C6A /* Localizable.xcstrings */ = {isa = 
PBXFileReference; lastKnownFileType = text.json.xcstrings; path = 
Localizable.xcstrings; sourceTree = "<group>"; };
@@ -861,6 +867,7 @@
                        isa = PBXGroup;
                        children = (
                                4EB095442989CBFE0043A8A1 /* MainView.swift */,
+                               4EED38542D140C1400F6C038 /* 
NavigationModel.swift */,
                                4EB095452989CBFE0043A8A1 /* ErrorView.swift */,
                                4EB095392989CBFE0043A8A1 /* 
WalletEmptyView.swift */,
                        );
@@ -879,6 +886,7 @@
                                4EF840A62A0B85F400EE0D47 /* CopyShare.swift */,
                                4E53A33629F50B7B00830EC2 /* CurrencyField.swift 
*/,
                                4EA551242A2C923600FEC9A8 /* 
CurrencyInputView.swift */,
+                               4EED38572D1485AB00F6C038 /* 
ForEachWithIndex.swift */,
                                4E5D2C8A2C574CB0003F7A49 /* 
GradientBorder.swift */,
                                4E0A71172C3AB099002485BB /* IconBadge.swift */,
                                4EB095432989CBFE0043A8A1 /* 
LaunchAnimationView.swift */,
@@ -1236,6 +1244,7 @@
                                4E3EAE212A990778009F1BE8 /* Buttons.swift in 
Sources */,
                                4E3EAE222A990778009F1BE8 /* 
TransactionButton.swift in Sources */,
                                4EEC118D2B83DE4800146CFF /* AmountInputV.swift 
in Sources */,
+                               4EED38582D1485AB00F6C038 /* 
ForEachWithIndex.swift in Sources */,
                                4E77976F2C4BEA4E005D6ECB /* BalanceCellV.swift 
in Sources */,
                                4E3EAE232A990778009F1BE8 /* 
BalancesSectionView.swift in Sources */,
                                4E3EAE242A990778009F1BE8 /* 
QRGeneratorView.swift in Sources */,
@@ -1333,6 +1342,7 @@
                                4E3EAE5F2A990778009F1BE8 /* QRSheet.swift in 
Sources */,
                                4E3EAE602A990778009F1BE8 /* 
P2pReceiveURIView.swift in Sources */,
                                4E3EAE612A990778009F1BE8 /* ListStyle.swift in 
Sources */,
+                               4EED38552D140C1400F6C038 /* 
NavigationModel.swift in Sources */,
                                4E3EAE622A990778009F1BE8 /* 
TransactionSummaryV.swift in Sources */,
                                4E3EAE632A990778009F1BE8 /* WalletCore.swift in 
Sources */,
                                4E3EAE642A990778009F1BE8 /* 
LaunchAnimationView.swift in Sources */,
@@ -1375,6 +1385,7 @@
                                4EB0956A2989CBFE0043A8A1 /* Buttons.swift in 
Sources */,
                                4EBA82AB2A3EB2CA00E5F39A /* 
TransactionButton.swift in Sources */,
                                4EEC118E2B83DE4800146CFF /* AmountInputV.swift 
in Sources */,
+                               4EED38592D1485AB00F6C038 /* 
ForEachWithIndex.swift in Sources */,
                                4E7797702C4BEA4E005D6ECB /* BalanceCellV.swift 
in Sources */,
                                4EB095602989CBFE0043A8A1 /* 
BalancesSectionView.swift in Sources */,
                                4EEC157329F8242800D46A03 /* 
QRGeneratorView.swift in Sources */,
@@ -1472,6 +1483,7 @@
                                4EEC157A29F9427F00D46A03 /* QRSheet.swift in 
Sources */,
                                4E3B4BC12A41E6C200CC88B8 /* 
P2pReceiveURIView.swift in Sources */,
                                4E6EDD872A363D8D0031D520 /* ListStyle.swift in 
Sources */,
+                               4EED38562D140C1400F6C038 /* 
NavigationModel.swift in Sources */,
                                4EB095582989CBFE0043A8A1 /* 
TransactionSummaryV.swift in Sources */,
                                4EB095202989CBCB0043A8A1 /* WalletCore.swift in 
Sources */,
                                4EB095672989CBFE0043A8A1 /* 
LaunchAnimationView.swift in Sources */,
@@ -1753,7 +1765,7 @@
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 0.14.0;
+                               CURRENT_PROJECT_VERSION = 0.14.1;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
@@ -1771,7 +1783,7 @@
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               MARKETING_VERSION = 0.14.0;
+                               MARKETING_VERSION = 0.14.1;
                                PRODUCT_BUNDLE_IDENTIFIER = 
"com.taler-systems.talerwallet-2";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1795,7 +1807,7 @@
                                CODE_SIGN_ENTITLEMENTS = 
"$(TARGET_NAME).entitlements";
                                CODE_SIGN_IDENTITY = "Apple Development";
                                CODE_SIGN_STYLE = Automatic;
-                               CURRENT_PROJECT_VERSION = 0.14.0;
+                               CURRENT_PROJECT_VERSION = 0.14.1;
                                DEVELOPMENT_TEAM = GUDDQ9428Y;
                                ENABLE_PREVIEWS = YES;
                                GENERATE_INFOPLIST_FILE = YES;
@@ -1813,7 +1825,7 @@
                                        "$(inherited)",
                                        "@executable_path/Frameworks",
                                );
-                               MARKETING_VERSION = 0.14.0;
+                               MARKETING_VERSION = 0.14.1;
                                PRODUCT_BUNDLE_IDENTIFIER = 
"com.taler-systems.talerwallet-2";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/TalerWallet1/Assets.xcassets/Error.colorset/Contents.json 
b/TalerWallet1/Assets.xcassets/Error.colorset/Contents.json
index b09073b..b29ffd3 100644
--- a/TalerWallet1/Assets.xcassets/Error.colorset/Contents.json
+++ b/TalerWallet1/Assets.xcassets/Error.colorset/Contents.json
@@ -23,9 +23,9 @@
         "color-space" : "display-p3",
         "components" : {
           "alpha" : "1.000",
-          "blue" : "36",
-          "green" : "52",
-          "red" : "234"
+          "blue" : "99",
+          "green" : "99",
+          "red" : "255"
         }
       },
       "idiom" : "universal"
diff --git a/TalerWallet1/Backend/WalletCore.swift 
b/TalerWallet1/Backend/WalletCore.swift
index 5dbe9e4..8e8010a 100644
--- a/TalerWallet1/Backend/WalletCore.swift
+++ b/TalerWallet1/Backend/WalletCore.swift
@@ -76,6 +76,8 @@ class WalletCore: QuickjsMessageHandler {
         let type: String
         let id: String?
         let reservePub: String?
+        let isInternal: Bool?
+        let hintTransactionId: String?
         let event: [String: AnyCodable]?
     }
 
@@ -324,7 +326,14 @@ extension WalletCore {
                     try handlePendingProcessed(payload)
                 case Notification.Name.BalanceChange.rawValue:
                     symLog.log(message)
-                    postNotification(.BalanceChange)
+                    if !(payload.isInternal ?? false) {     // don't re-post 
internals
+                        if let txID = payload.hintTransactionId {
+                            if txID.contains("txn:refresh:") {
+                                break
+                            }
+                        }
+                        postNotification(.BalanceChange)
+                    }
                 case Notification.Name.BankAccountChange.rawValue:
                     symLog.log(message)
                     postNotification(.BankAccountChange)
diff --git a/TalerWallet1/Controllers/Controller.swift 
b/TalerWallet1/Controllers/Controller.swift
index 6a83ee5..499f5ff 100755
--- a/TalerWallet1/Controllers/Controller.swift
+++ b/TalerWallet1/Controllers/Controller.swift
@@ -259,7 +259,7 @@ class Controller: ObservableObject {
         if backendState == .instantiated {
             backendState = .initing
             do {
-                let versionInfo = try await model.initWalletCoreT(setTesting: 
setTesting)
+                let versionInfo = try await model.initWalletCore(setTesting: 
setTesting)
                 WalletCore.shared.versionInfo = versionInfo
                 backendState = .ready                       // dismiss the 
launch animation
             } catch {       // rethrows
diff --git a/TalerWallet1/Controllers/PublicConstants.swift 
b/TalerWallet1/Controllers/PublicConstants.swift
index e8589ec..1650361 100644
--- a/TalerWallet1/Controllers/PublicConstants.swift
+++ b/TalerWallet1/Controllers/PublicConstants.swift
@@ -7,6 +7,7 @@
  */
 import Foundation
 
+public let MAXEXCHANGES = 1000          // per currency
 public let CHF_4217 = "CHF"             // ISO-4217 Swiss Francs
 public let EUR_4217 = "EUR"             // ISO-4217 Euro
 public let HUF_4217 = "HUF"             // ISO-4217 Hungarian Forint
@@ -17,6 +18,7 @@ public let DRAGDELAY = 0.5
 public let DRAGDURATION = 0.45
 public let DRAGSPEED = 0.1
 public let MAXRECENT = 4                // # of rows in Recent Transactions
+public let SCANDETENT = 0.999           // instead .large sheet - in the .001 
is a VoiceOver dismiss button
 
 public let ICONLEADING = CGFloat(-8)
 public let HSPACING = CGFloat(10)                  // space between items in 
HStack
diff --git a/TalerWallet1/Helper/Font+Taler.swift 
b/TalerWallet1/Helper/Font+Taler.swift
index 7650a75..fe1075c 100644
--- a/TalerWallet1/Helper/Font+Taler.swift
+++ b/TalerWallet1/Helper/Font+Taler.swift
@@ -172,6 +172,7 @@ extension TalerFont {   // old running
 
     static var largeTitle:  TalerFont { TalerFont(fontName, size: 34, 
relativeTo: .largeTitle) }  // 34 -> 38
     static var title:       TalerFont { TalerFont(fontName, size: 28, 
relativeTo: .title) }       // 28 -> 31
+    static var title1:      TalerFont { TalerFont(fontName, size: 24, 
relativeTo: .title2) }      // Destructive Buttons
     static var title2:      TalerFont { TalerFont(fontName, size: 22, 
relativeTo: .title2) }      // 22 -> 25
     static var title3:      TalerFont { TalerFont(fontName, size: 20, 
relativeTo: .title3) }      // 20 -> 23
     static var picker:      TalerFont { TalerFont(fontName, size: 18, 
relativeTo: .body) }        // picker uses a different size!
@@ -179,6 +180,7 @@ extension TalerFont {   // old running
     static var body:        TalerFont { TalerFont(fontName, size: 17, 
relativeTo: .body) }        // 17 -> 19
     static var callout:     TalerFont { TalerFont(fontName, size: 16, 
relativeTo: .callout) }     // 16 -> 18
     static var subheadline: TalerFont { TalerFont(fontName, size: 15, 
relativeTo: .subheadline) } // 15 -> 17
+    static var table:       TalerFont { TalerFont(fontName, size: 14, 
relativeTo: .subheadline) } // tableview uses a different size!
     static var footnote:    TalerFont { TalerFont(fontName, size: 13, 
relativeTo: .footnote) }    // 13 -> 15
     static var caption:     TalerFont { TalerFont(fontName, size: 12, 
relativeTo: .caption) }     // 12 -> 13
     static var badge:       TalerFont { TalerFont(fontName, size: 10, 
relativeTo: .caption) }     // 12 -> 13
diff --git a/TalerWallet1/Helper/WalletColors.swift 
b/TalerWallet1/Helper/WalletColors.swift
index 830169f..6adf932 100644
--- a/TalerWallet1/Helper/WalletColors.swift
+++ b/TalerWallet1/Helper/WalletColors.swift
@@ -9,7 +9,7 @@ fileprivate let grouped = true
 public struct WalletColors {
     @AppStorage("minimalistic") var minimalistic: Bool = false
 
-    let tint = Color(.tintColor)
+//    let tint = Color(.tintColor)
     let gray1 = Color(.systemGray)          // uncompleted
     let gray2 = Color(.systemGray2)         // disabled Fore
     let gray3 = Color(.systemGray3)
@@ -21,26 +21,26 @@ public struct WalletColors {
     let gray8 = grouped ? Color(.secondarySystemGroupedBackground)
                         : Color(.secondarySystemBackground)       // disabled 
Back
 
-    let attention = Color.red
-    let confirm = Color.yellow
+    let attention = Color.red       // TODO: WalletColors().errorColor
+    let confirm = Color.yellow      // TODO: WalletColors().warningColor
 
     // TODO: In Japan, negative is blue, and positive is red
     let positive = Color.green
     let negative = Color.red
 
-    func buttonForeColor(pressed: Bool, disabled: Bool,
-                       prominent: Bool = false, balance: Bool = false) -> 
Color {
-             balance ? Color.primary
-          : disabled ? gray2
-        : !prominent ? tint
-           : pressed ? gray6 : gray5
+    func buttonForeColor(pressed: Bool,
+                        disabled: Bool,
+                       prominent: Bool = false) -> Color {
+            disabled ? gray2
+        : !prominent ? talerColor
+           : pressed ? gray6 : Color.white
     }
 
-    func buttonBackColor(pressed: Bool, disabled: Bool,
-                       prominent: Bool = false, balance: Bool = false) -> 
Color {
-             balance ? (pressed ? gray5 : gray6)
-          : disabled ? gray7
-         : prominent ? tint
+    func buttonBackColor(pressed: Bool,
+                        disabled: Bool,
+                       prominent: Bool = false) -> Color {
+            disabled ? gray7
+         : prominent ? talerColor
            : pressed ? gray4 : gray5
     }
 
@@ -90,6 +90,10 @@ public struct WalletColors {
         Color("Error")
     }
 
+    var talerColor: Color {
+        Color("Taler")
+    }
+
     func pendingColor(_ incoming: Bool) -> Color {
         incoming ? Color("PendingIncoming")
                  : Color("PendingOutgoing")
@@ -99,5 +103,4 @@ public struct WalletColors {
         incoming ? Color("Incoming")
                  : Color("Outgoing")
     }
-
 }
diff --git a/TalerWallet1/Localizable.xcstrings 
b/TalerWallet1/Localizable.xcstrings
index 55767fd..9faa69a 100755
--- a/TalerWallet1/Localizable.xcstrings
+++ b/TalerWallet1/Localizable.xcstrings
@@ -525,6 +525,12 @@
             "state" : "translated",
             "value" : "%@"
           }
+        },
+        "es" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "%@"
+          }
         }
       }
     },
@@ -558,6 +564,12 @@
             "state" : "translated",
             "value" : "%@:"
           }
+        },
+        "es" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "%@:"
+          }
         }
       }
     },
@@ -723,7 +735,7 @@
       }
     },
     "%llu Days" : {
-      "comment" : "30 Days, always plural (10..30), 4 letters max., abbreviate 
if longer\n7 Days, always plural (3..9), 4 letters max., abbreviate if longer",
+      "comment" : "7 Days, always plural (3..9), 4 letters max., abbreviate if 
longer",
       "localizations" : {
         "de" : {
           "stringUnit" : {
@@ -1353,7 +1365,7 @@
       }
     },
     "Add bank account" : {
-      "comment" : "VoiceOver for the + button\ntitle of the addExchange alert",
+      "comment" : "VoiceOver for the + button",
       "localizations" : {
         "de" : {
           "stringUnit" : {
@@ -1370,7 +1382,7 @@
       }
     },
     "Add payment service" : {
-      "comment" : "VoiceOver for the + button\ntitle of the addExchange alert",
+      "comment" : "VoiceOver for the + button",
       "localizations" : {
         "de" : {
           "stringUnit" : {
@@ -1939,7 +1951,7 @@
         },
         "es" : {
           "stringUnit" : {
-            "state" : "needs_review",
+            "state" : "translated",
             "value" : "Confirmar ahora"
           }
         },
@@ -2820,6 +2832,12 @@
             "state" : "translated",
             "value" : "Demo-Hinweise"
           }
+        },
+        "es" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "Pistas de demostración"
+          }
         }
       }
     },
@@ -2846,7 +2864,7 @@
       }
     },
     "Deposit" : {
-      "comment" : "RefreshReason\nTransactionType",
+      "comment" : "TransactionType",
       "localizations" : {
         "de" : {
           "stringUnit" : {
@@ -5795,6 +5813,12 @@
             "state" : "translated",
             "value" : "Einführung"
           }
+        },
+        "es" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "Incorporación"
+          }
         }
       }
     },
@@ -6889,7 +6913,7 @@
       }
     },
     "Recoup" : {
-      "comment" : "RefreshReason\nTransactionType",
+      "comment" : "TransactionType",
       "localizations" : {
         "de" : {
           "stringUnit" : {
@@ -6958,7 +6982,7 @@
       }
     },
     "Refund" : {
-      "comment" : "RefreshReason\nTransactionType",
+      "comment" : "TransactionType",
       "localizations" : {
         "de" : {
           "stringUnit" : {
@@ -7822,6 +7846,12 @@
             "state" : "translated",
             "value" : "Demo-Hinweise anzeigen"
           }
+        },
+        "es" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "Mostrar pistas para dinero de demostración"
+          }
         }
       }
     },
@@ -8469,6 +8499,30 @@
         }
       }
     },
+    "thirtyDays" : {
+      "comment" : "30 Days, always plural (10..30), 4 letters max., abbreviate 
if longer",
+      "extractionState" : "extracted_with_value",
+      "localizations" : {
+        "de" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "%llu Tage"
+          }
+        },
+        "en" : {
+          "stringUnit" : {
+            "state" : "new",
+            "value" : "%llu Days"
+          }
+        },
+        "es" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "%llu días"
+          }
+        }
+      }
+    },
     "This is mandatory, otherwise your money will not arrive in this wallet." 
: {
       "localizations" : {
         "de" : {
@@ -9398,6 +9452,12 @@
             "state" : "translated",
             "value" : "Unbekannter Zahlungsdienst"
           }
+        },
+        "es" : {
+          "stringUnit" : {
+            "state" : "translated",
+            "value" : "Servicio de pago desconocido"
+          }
         }
       }
     },
diff --git a/TalerWallet1/Model/Model+Deposit.swift 
b/TalerWallet1/Model/Model+Deposit.swift
index 444b98c..b5c54d5 100644
--- a/TalerWallet1/Model/Model+Deposit.swift
+++ b/TalerWallet1/Model/Model+Deposit.swift
@@ -121,6 +121,9 @@ struct CheckDepositResponse: Codable {
     let totalDepositCost: Amount
     let effectiveDepositAmount: Amount
     let fees: DepositFees
+    let kycSoftLimit: Amount?
+    let kycHardLimit: Amount?
+    let kycExchanges: [String]?                 // Base URL of exchanges that 
would likely require soft KYC
 }
 /// A request to get an exchange's deposit contract terms.
 fileprivate struct CheckDeposit: WalletBackendFormattedRequest {
diff --git a/TalerWallet1/Model/Model+Withdraw.swift 
b/TalerWallet1/Model/Model+Withdraw.swift
index 34c9a52..7e88add 100644
--- a/TalerWallet1/Model/Model+Withdraw.swift
+++ b/TalerWallet1/Model/Model+Withdraw.swift
@@ -235,7 +235,7 @@ fileprivate struct GetQrCodesForPayto: 
WalletBackendFormattedRequest {
 // MARK: -
 extension WalletModel {
     /// load withdraw-exchange details. Networking involved
-    nonisolated func loadWithdrawalExchangeForUri(_ talerUri: String,
+    nonisolated func prepareWithdrawExchange(_ talerUri: String,
                                                  viewHandles: Bool = false)
       async throws -> WithdrawExchangeResponse {
         let request = PrepareWithdrawExchange(talerUri: talerUri)
diff --git a/TalerWallet1/Model/WalletModel.swift 
b/TalerWallet1/Model/WalletModel.swift
index 091c6cb..0aac3e1 100644
--- a/TalerWallet1/Model/WalletModel.swift
+++ b/TalerWallet1/Model/WalletModel.swift
@@ -98,20 +98,22 @@ extension WalletModel {
 fileprivate struct GetTransactionById: WalletBackendFormattedRequest {
     typealias Response = Transaction
     func operation() -> String { "getTransactionById" }
-    func args() -> Args { Args(transactionId: transactionId) }
+    func args() -> Args { Args(transactionId: transactionId, 
includeContractTerms: withTerms) }
 
     var transactionId: String
+    var withTerms: Bool?
 
     struct Args: Encodable {
         var transactionId: String
+        var includeContractTerms: Bool?
     }
 }
 
 extension WalletModel {
     /// get the specified transaction from Wallet-Core. No networking involved
-    nonisolated func getTransactionById(_ transactionId: String, viewHandles: 
Bool = false)
+    nonisolated func getTransactionById(_ transactionId: String, withTerms: 
Bool? = nil, viewHandles: Bool = false)
       async throws -> Transaction {
-        let request = GetTransactionById(transactionId: transactionId)
+        let request = GetTransactionById(transactionId: transactionId, 
withTerms: withTerms)
         return try await sendRequest(request, ASYNCDELAY, viewHandles: 
viewHandles)
     }
 }
@@ -177,8 +179,7 @@ fileprivate struct ConfigRequest: 
WalletBackendFormattedRequest {
 
 extension WalletModel {
     /// initalize Wallet-Core. Will do networking
-    func setConfigT(setTesting: Bool) async throws -> VersionInfo {
-        // T for any Thread
+    nonisolated func setConfig(setTesting: Bool) async throws -> VersionInfo {
         let request = ConfigRequest(setTesting: setTesting)
         let response = try await sendRequest(request, 0)    // no Delay
         return response.versionInfo
@@ -216,8 +217,7 @@ fileprivate struct InitRequest: 
WalletBackendFormattedRequest {
 
 extension WalletModel {
     /// initalize Wallet-Core. Might do networking
-    func initWalletCoreT(setTesting: Bool, viewHandles: Bool = false) async 
throws -> VersionInfo {
-                    // T for any Thread
+    nonisolated func initWalletCore(setTesting: Bool, viewHandles: Bool = 
false) async throws -> VersionInfo {
         let dbPath = try dbPath()
 //        logger.debug("dbPath: \(dbPath)")
         let request = InitRequest(persistentStoragePath: dbPath, setTesting: 
setTesting)
@@ -285,12 +285,16 @@ extension WalletModel {
         }
     }
 
-    private func dbPath () throws -> String {
+    private func dbPath(_ export:Bool = false) throws -> String {
         if #available(iOS 16.0, *) {
             let documents = URL.documentsDirectory                             
 // accessible by FileSharing
+            let docsPath = documents.path(percentEncoded: false)
+            if export {
+                return docsPath
+            }
             let appSupport = URL.applicationSupportDirectory
 #if DEBUG || GNU_TALER
-            return documents.path(percentEncoded: false)
+            return docsPath
 #else     // TALER_WALLET
             migrate(from: documents, to: appSupport)
             return appSupport.path(percentEncoded: false)
@@ -300,6 +304,9 @@ extension WalletModel {
             let appDirNr: FileManager.SearchPathDirectory = 
.applicationSupportDirectory
             if let documentsDir = FileManager.default.urls(for: docDirNr, in: 
.userDomainMask).first,
                let appSupportDir = FileManager.default.urls(for: appDirNr, in: 
.userDomainMask).first {
+                if export {
+                    return path(documentsDir)
+                }
 #if DEBUG || GNU_TALER
                 return path(documentsDir)
 #else
@@ -313,7 +320,7 @@ extension WalletModel {
         }
     }
 
-    private func cachePath () throws -> String {
+    private func cachePath() throws -> String {
         let fileManager = FileManager.default
         if let cachesURL = fileManager.urls(for: .cachesDirectory, in: 
.userDomainMask).first {
             let cacheURL = cachesURL.appendingPathComponent("cache.json")
@@ -347,13 +354,42 @@ fileprivate struct ResetRequest: 
WalletBackendFormattedRequest {
 
 extension WalletModel {
     /// reset Wallet-Core
-    func resetWalletCoreT(viewHandles: Bool = false) async throws {
-        // T for any Thread
+    nonisolated func resetWalletCore(viewHandles: Bool = false) async throws {
         let request = ResetRequest()
         _ = try await sendRequest(request, 0, viewHandles: viewHandles)
     }
 }
 // MARK: -
+fileprivate struct ExportDbToFile: WalletBackendFormattedRequest {
+    func operation() -> String { "exportDbToFile" }
+    func args() -> Args { Args(directory: directory, stem: stem) }
+
+    var directory: String
+    var stem: String
+    struct Args: Encodable {
+        var directory: String
+        var stem: String
+    }                           // no arguments needed
+    struct Response: Decodable, Sendable {              // path of the copied 
DB
+        var path: String
+    }
+}
+
+extension WalletModel {
+    /// export DB
+    nonisolated func exportDbToFile(viewHandles: Bool = false)
+      async throws -> String? {
+        if let dbPath = try? dbPath(true) {
+            let stem = String("Taler " + TalerDater.dateString())
+            let request = ExportDbToFile(directory: dbPath, stem: stem)
+            let response = try await sendRequest(request, 0, viewHandles: 
viewHandles)
+            return response.path
+        } else {
+            return nil
+        }
+    }
+}
+// MARK: -
 fileprivate struct DevExperimentRequest: WalletBackendFormattedRequest {
     func operation() -> String { "applyDevExperiment" }
     func args() -> Args { Args(devExperimentUri: talerUri) }
@@ -368,7 +404,7 @@ fileprivate struct DevExperimentRequest: 
WalletBackendFormattedRequest {
 
 extension WalletModel {
     /// tell wallet-core to mock new transactions
-    func devExperimentT(talerUri: String, viewHandles: Bool = false) async 
throws {
+    nonisolated func devExperimentT(talerUri: String, viewHandles: Bool = 
false) async throws {
         // T for any Thread
         let request = DevExperimentRequest(talerUri: talerUri)
         _ = try await sendRequest(request, 0, viewHandles: viewHandles)
diff --git a/TalerWallet1/Views/Actions/ActionsSheet.swift 
b/TalerWallet1/Views/Actions/ActionsSheet.swift
index 094d0de..9786de6 100644
--- a/TalerWallet1/Views/Actions/ActionsSheet.swift
+++ b/TalerWallet1/Views/Actions/ActionsSheet.swift
@@ -65,7 +65,8 @@ struct ActionsSheet: View {
                 }
                 Button(action: shopAction) {
                     HStack {
-                        ButtonIconBadge(type: .payment, foreColor: 
.accentColor, done: false)
+                        let talerColor = WalletColors().talerColor
+                        ButtonIconBadge(type: .payment, foreColor: talerColor, 
done: false)
                         Spacer()
                         Text(title)
                         Spacer()
@@ -96,11 +97,13 @@ struct DualHeightSheet: View {
     @Binding var qrButtonTapped: Bool
 
     let logger = Logger(subsystem: "net.taler.gnu", category: "DualSheet")
-//    @State private var sheetHeight: CGFloat = .zero
-    @State private var selectedDetent: PresentationDetent = .fraction(0.1)
+    @Environment(\.colorScheme) private var colorScheme
+    @Environment(\.colorSchemeContrast) private var colorSchemeContrast
+    let scanDetent: PresentationDetent = .fraction(SCANDETENT)
+//    @State private var selectedDetent: PresentationDetent = scanDetent       
 // Cannot use instance member 'scanDetent' within property initializer
+    @State private var selectedDetent: PresentationDetent = .fraction(0.1)     
 // workaround - update in .task
+//    @State private var detents: Set<PresentationDetent> = [scanDetent]
     @State private var detents: Set<PresentationDetent> = [.fraction(0.1)]
-//    @State private var selectedDetent: PresentationDetent = .large
-//    @State private var detents: Set<PresentationDetent> = [.large]
     @State private var qrButtonTapped2: Bool = false
     @State private var innerHeight: CGFloat = .zero
     @State private var sheetHeight: CGFloat = .zero
@@ -109,18 +112,20 @@ struct DualHeightSheet: View {
         Task {
             //(1 second = 1_000_000_000 nanoseconds)
             try? await Task.sleep(nanoseconds: 80_000_000)
-            guard selectedDetent == .large else { return }
-            detents = [.large]
+            guard selectedDetent == scanDetent else { return }
+            detents = [scanDetent]
         }
     }
 
     var body: some View {
         ScrollView {
+            let background = colorScheme == .dark ? WalletColors().gray6
+                                                  : WalletColors().gray2
             ActionsSheet(stack: stack.push(),
                 qrButtonTapped: $qrButtonTapped2)
             .presentationDragIndicator(.hidden)
             .presentationBackground {
-                WalletColors().gray2
+                background
                 /// overflow the bottom of the screen by a sufficient amount 
to fill the gap that is seen when the size changes
                     .padding(.bottom, -1000)
             }
@@ -132,7 +137,7 @@ struct DualHeightSheet: View {
                     qrButtonTapped = true           // tell our caller
                     withAnimation(Animation.easeIn(duration: 0.6)) {
                         // animate this sheet to full height
-                        selectedDetent = .large
+                        selectedDetent = scanDetent
                     }
                 }
             }
@@ -152,13 +157,13 @@ struct DualHeightSheet: View {
             }
             .presentationDetents(detents, selection: $selectedDetent)
             .onChange(of: selectedDetent) { newValue in
-                if newValue == .large {
-                    logger.trace("onChange❗️selectedDetent = .large)")
+                if newValue == scanDetent {
+                    logger.trace("onChange❗️selectedDetent = \(SCANDETENT)")
                     updateDetentsWithDelay()
                     qrButtonTapped = true           // tell our caller
                 } else {
                     logger.trace("onChange❗️selectedDetent = 
.height(\(sheetHeight))")
-                    detents = [.large, .height(sheetHeight)]
+                    detents = [scanDetent, .height(sheetHeight)]
                 }
             }
             .task {
diff --git a/TalerWallet1/Views/Actions/Banking/ManualWithdraw.swift 
b/TalerWallet1/Views/Actions/Banking/ManualWithdraw.swift
index e430010..eb543cf 100644
--- a/TalerWallet1/Views/Actions/Banking/ManualWithdraw.swift
+++ b/TalerWallet1/Views/Actions/Banking/ManualWithdraw.swift
@@ -16,6 +16,8 @@ struct ManualWithdraw: View {
     let stack: CallStack
     @Binding var selectedBalance: Balance?
     @Binding var amountLastUsed: Amount
+    @Binding var amountToTransfer: Amount
+    @Binding var exchange: Exchange?           // only for withdraw-exchange
     let isSheet: Bool
 
     @EnvironmentObject private var controller: Controller
@@ -23,10 +25,13 @@ struct ManualWithdraw: View {
     @State private var balanceIndex = 0
     @State private var balance: Balance? = nil      // nil only when balances 
== []
     @State private var currencyInfo: CurrencyInfo = CurrencyInfo.zero(UNKNOWN)
-    @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING)   
 // Update currency when used
+//    @State private var amountToTransfer = Amount.zero(currency: EMPTYSTRING) 
   // Update currency when used
 
     private func viewDidLoad() async {
-        if let selectedBalance {
+        if let exchange {
+            currencyInfo = controller.info(for: exchange.scopeInfo, 
controller.currencyTicker)
+            return
+        } else if let selectedBalance {
             balance = selectedBalance
             balanceIndex = controller.balances.firstIndex(of: selectedBalance) 
?? 0
         } else {
@@ -56,20 +61,29 @@ struct ManualWithdraw: View {
         let count = controller.balances.count
         let _ = symLog.log("count = \(count)")
         let scrollView = ScrollView {
-            if count > 0 {
-                ScopePicker(value: $balanceIndex, onlyNonZero: false)
-                { index in
-                    balanceIndex = index
-                    balance = controller.balances[index]
-                }
-                .padding(.horizontal)
-                .padding(.bottom, 4)
-            }
-            if let balance {
+            if let exchange {
                 ManualWithdrawContent(stack: stack.push(),
-                                      scope: balance.scopeInfo,
+                                      scope: exchange.scopeInfo,
                              amountLastUsed: $amountLastUsed,
-                           amountToTransfer: $amountToTransfer)
+                           amountToTransfer: $amountToTransfer,
+                                   exchange: $exchange)
+            } else {
+                if count > 0 {
+                    ScopePicker(value: $balanceIndex, onlyNonZero: false)
+                    { index in
+                        balanceIndex = index
+                        balance = controller.balances[index]
+                    }
+                    .padding(.horizontal)
+                    .padding(.bottom, 4)
+                }
+                if let balance {
+                    ManualWithdrawContent(stack: stack.push(),
+                                          scope: balance.scopeInfo,
+                                 amountLastUsed: $amountLastUsed,
+                               amountToTransfer: $amountToTransfer,
+                                       exchange: $exchange)
+                }
             }
         } // ScrollView
             .navigationTitle(navTitle)
@@ -91,7 +105,11 @@ struct ManualWithdraw: View {
             .task(id: balanceIndex + (1000 * controller.currencyTicker)) {
                 // runs whenever the user changes the exchange via 
ScopePicker, or on new currencyInfo
                 symLog.log("❗️ task \(balanceIndex)")
-                if let balance {
+                if let exchange {
+                    let scopeInfo = exchange.scopeInfo
+                    amountToTransfer.setCurrency(scopeInfo.currency)
+                    currencyInfo = controller.info(for: scopeInfo, 
controller.currencyTicker)
+                } else if let balance {
                     let scopeInfo = balance.scopeInfo
                     amountToTransfer.setCurrency(scopeInfo.currency)
                     currencyInfo = controller.info(for: scopeInfo, 
controller.currencyTicker)
@@ -117,6 +135,7 @@ struct ManualWithdrawContent: View {
     let scope: ScopeInfo
     @Binding var amountLastUsed: Amount
     @Binding var amountToTransfer: Amount
+    @Binding var exchange: Exchange?
 
     @EnvironmentObject private var controller: Controller
     @EnvironmentObject private var model: WalletModel
@@ -126,10 +145,6 @@ struct ManualWithdrawContent: View {
 //    @State var ageMenuList: [Int] = []
 //    @State var selectedAge = 0
 
-    @State private var exchanges: [Exchange] = []
-    @State private var exchange: Exchange? = nil
-
-
     private func exchangeVia(_ baseURL: String?) -> String? {
         if let baseURL {
             return String(localized: "via \(baseURL.trimURL)", comment: 
"currency/exchange chooser")
@@ -161,6 +176,13 @@ struct ManualWithdrawContent: View {
         return nil
     } // computeFee
 
+    @MainActor
+    private func reloadExchange(_ baseURL: String) async {
+        if exchange == nil || exchange?.tosStatus != .accepted {
+            symLog.log("getExchangeByUrl(\(baseURL))")
+            exchange = try? await model.getExchangeByUrl(url: baseURL)
+        }
+    }
     @MainActor
     private func viewDidLoad2() async {
         // neues scope wenn balance geändert wird?
@@ -168,10 +190,6 @@ struct ManualWithdrawContent: View {
                                                          viewHandles: true)
         if let details {
             detailsForAmount = details
-            if exchange == nil || exchange?.tosStatus != .accepted {
-                symLog.log("getExchangeByUrl(\(details.exchangeBaseUrl))")
-                exchange = try? await model.getExchangeByUrl(url: 
details.exchangeBaseUrl)
-            }
         }
     }
 
@@ -243,7 +261,8 @@ struct ManualWithdrawContent: View {
                     .padding(.top)
                 }
             } // Group
-            .padding(.horizontal)
+                .padding(.horizontal)
+                .task { await reloadExchange(detailsForAmount.exchangeBaseUrl) 
}
         } else {
             LoadingView(scopeInfo: scope, message: nil)
                 .task { await viewDidLoad2() }
diff --git a/TalerWallet1/Views/Actions/TwoRowButtons.swift 
b/TalerWallet1/Views/Actions/TwoRowButtons.swift
index 3506d1d..7b7ad69 100644
--- a/TalerWallet1/Views/Actions/TwoRowButtons.swift
+++ b/TalerWallet1/Views/Actions/TwoRowButtons.swift
@@ -34,7 +34,8 @@ struct TypeButton: View {
         let blue = debug ? Color.blue : Color.clear
         let orange = debug ? Color.orange : Color.clear
 #endif
-        let badge = ButtonIconBadge(type: type, foreColor: .accentColor, done: 
false)
+        let talerColor = WalletColors().talerColor
+        let badge = ButtonIconBadge(type: type, foreColor: talerColor, done: 
false)
         let hLayout = HStack {
             badge
             Spacer()
diff --git a/TalerWallet1/Views/HelperViews/Buttons.swift 
b/TalerWallet1/Views/HelperViews/Buttons.swift
index f4c085d..34950e6 100644
--- a/TalerWallet1/Views/HelperViews/Buttons.swift
+++ b/TalerWallet1/Views/HelperViews/Buttons.swift
@@ -108,23 +108,23 @@ struct QRButton : View  {
             showCameraAlert = false
         }
         let scanText = String(localized: "Scan QR code", comment: "Button 
title, a11y")
-        let qr = "qrcode.viewfinder"
-        let qrImage = Text("\(Image(systemName: qr))", comment: "QR Image")
+        let qrImage = Image(systemName: QRBUTTON)
+        let qrText = Text("\(qrImage)", comment: "QR Image")
         Button(action: checkCameraAvailable) {
             if isNavBarItem {
                 if !minimalistic {
                     Text("Scan QR", comment: "Button title")
                         .talerFont(.title3)
                 }
-                qrImage.talerFont(.title2)
+                qrText.talerFont(.title2)
             } else if minimalistic {
                 let width = UIScreen.screenWidth / 7
-                qrImage.talerFont(.largeTitle)
+                qrText.talerFont(.largeTitle)
                     .padding(.horizontal, width)
 //                    .padding(.vertical)
             } else {
                 HStack(spacing: 16) {
-                    qrImage.talerFont(.title)
+                    qrText.talerFont(.title)
                     Text(scanText)
                 }.padding(.horizontal)
             }
@@ -192,7 +192,6 @@ struct ReloadButton : View  {
 struct TalerButtonStyle: ButtonStyle {
     enum TalerButtonStyleType {
         case plain
-        case balance
         case bordered
         case prominent
     }
@@ -221,8 +220,7 @@ struct TalerButtonStyle: ButtonStyle {
         }
         return WalletColors().buttonForeColor(pressed: pressed,
                                              disabled: disabled,
-                                            prominent: type == .prominent,
-                                              balance: type == .balance)
+                                            prominent: type == .prominent)
     }
     func backColor(type: TalerButtonStyleType, pressed: Bool, disabled: Bool) 
-> Color {
         if type == .plain && !pressed {
@@ -230,8 +228,7 @@ struct TalerButtonStyle: ButtonStyle {
         }
         return WalletColors().buttonBackColor(pressed: pressed,
                                              disabled: disabled,
-                                            prominent: type == .prominent,
-                                              balance: type == .balance)
+                                            prominent: type == .prominent)
     }
 
     struct BackgroundView: View {
@@ -297,7 +294,7 @@ fileprivate struct ContentView_Previews: PreviewProvider {
     static var previews: some View {
         let testButtonTitle = String("Placeholder")
         Button(testButtonTitle) {}
-            .buttonStyle(TalerButtonStyle(type: .balance, aligned: .trailing))
+            .buttonStyle(TalerButtonStyle(type: .bordered, aligned: .trailing))
     }
 }
 #endif
diff --git a/TalerWallet1/Helper/AnyTransition+backslide.swift 
b/TalerWallet1/Views/HelperViews/ForEachWithIndex.swift
similarity index 68%
copy from TalerWallet1/Helper/AnyTransition+backslide.swift
copy to TalerWallet1/Views/HelperViews/ForEachWithIndex.swift
index 3cd1d81..1eb5dfd 100644
--- a/TalerWallet1/Helper/AnyTransition+backslide.swift
+++ b/TalerWallet1/Views/HelperViews/ForEachWithIndex.swift
@@ -1,5 +1,6 @@
 /* MIT License
- * Copyright (c) 2021 noranraskin
+ * Copyright (c) 2021 Khoa aka onmyway133
+ * https://onmyway133.com/posts/how-to-use-foreach-with-indices-in-swiftui/
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to 
deal
@@ -19,12 +20,17 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
  * SOFTWARE.
  */
+
 import SwiftUI
 
-extension AnyTransition {
-    static var backslide: AnyTransition {
-        AnyTransition.asymmetric(
-            insertion: .move(edge: .trailing),
-            removal: .move(edge: .leading))
+struct ForEachWithIndex<Data: RandomAccessCollection, Content: View>: View
+                  where Data.Element: Identifiable, Data.Element: Hashable {
+    let data: Data
+    @ViewBuilder let content: (Data.Index, Data.Element) -> Content
+
+    var body: some View {
+        ForEach(Array(zip(data.indices, data)), id: \.1) { index, element in
+            content(index, element)
+        }
     }
 }
diff --git a/TalerWallet1/Views/HelperViews/SelectDays.swift 
b/TalerWallet1/Views/HelperViews/SelectDays.swift
index 633c750..6b5fa8f 100644
--- a/TalerWallet1/Views/HelperViews/SelectDays.swift
+++ b/TalerWallet1/Views/HelperViews/SelectDays.swift
@@ -69,7 +69,8 @@ struct SelectDays: View {
                     if developerMode {
                         Text(verbatim: "1 Day")
                     } else {
-                        Text("\(THIRTYDAYS) Days", comment: "30 Days, always 
plural (10..30), 4 letters max., abbreviate if longer")
+                        let thirtyDays = String(localized: "thirtyDays", 
defaultValue: "\(THIRTYDAYS) Days", comment: "30 Days, always plural (10..30), 
4 letters max., abbreviate if longer")
+                        Text(thirtyDays)
                     }
                 }.buttonStyle(TalerButtonStyle(type: (selected == THIRTYDAYS) 
? .prominent : .bordered, dimmed: true,
                                               disabled: !isEnabled || 
maxExpiration < THIRTYDAYS))
diff --git a/TalerWallet1/Views/HelperViews/SubjectInputV.swift 
b/TalerWallet1/Views/HelperViews/SubjectInputV.swift
index c5c306a..376f0ba 100644
--- a/TalerWallet1/Views/HelperViews/SubjectInputV.swift
+++ b/TalerWallet1/Views/HelperViews/SubjectInputV.swift
@@ -88,7 +88,7 @@ struct SubjectInputV<TargetView: View>: View {
 ////                Text(feeLabel)
 //            }
 //                .talerFont(.body)
-////                .foregroundColor(insufficient ? .red : 
WalletColors().secondary(colorScheme, colorSchemeContrast))
+// //                .foregroundColor(insufficient ? WalletColors().errorColor 
: WalletColors().secondary(colorScheme, colorSchemeContrast))
 //                .foregroundColor(WalletColors().secondary(colorScheme, 
colorSchemeContrast))
 ////                .accessibility(sortPriority: 1)
 //                .padding(4)
diff --git a/TalerWallet1/Views/HelperViews/TabBarView.swift 
b/TalerWallet1/Views/HelperViews/TabBarView.swift
index f1d4392..178c08f 100644
--- a/TalerWallet1/Views/HelperViews/TabBarView.swift
+++ b/TalerWallet1/Views/HelperViews/TabBarView.swift
@@ -93,7 +93,7 @@ struct TabBarView: View {
                 }
             }
         }.id(tab)
-        .foregroundColor(selection == tab ? .accentColor : .secondary)
+        .foregroundColor(selection == tab ? WalletColors().talerColor : 
.secondary)
         .padding(.vertical, 8)
         .accessibilityElement(children: .combine)
         .accessibility(label: Text(tab.title))
diff --git a/TalerWallet1/Views/HelperViews/TransactionButton.swift 
b/TalerWallet1/Views/HelperViews/TransactionButton.swift
index 21c8837..d54fa34 100755
--- a/TalerWallet1/Views/HelperViews/TransactionButton.swift
+++ b/TalerWallet1/Views/HelperViews/TransactionButton.swift
@@ -21,7 +21,7 @@ struct WarningButton: View {
     @State private var showAlert: Bool = false
 
     var body: some View {
-        Button(role: role,
+        Button(//role: role,
              action: {
                 if !disabled {
                     if shouldShowWarning && (role == .destructive || role == 
.cancel) {
@@ -31,15 +31,17 @@ struct WarningButton: View {
                     }
                 }
         }) {
-            HStack(spacing: 50) {
-                Text(buttonTitle)
+            HStack(spacing: 20) {
                 if let buttonIcon {
                     Image(systemName: buttonIcon)
                 }
+                Text(buttonTitle)
             }
             .frame(maxWidth: .infinity)
+            .foregroundColor(role == .destructive ? WalletColors().errorColor
+                                                  : WalletColors().talerColor)
         }
-        .talerFont(.title2)
+        .talerFont(.title1)
         .buttonStyle(.bordered)
         .controlSize(.large)
         .disabled(disabled)
@@ -88,9 +90,9 @@ struct TransactionButton: View {
         let buttonTitle = executed ? command.localizedActionExecuted
                                    : command.localizedActionTitle
         WarningButton(warningText: warning,
-                      buttonTitle: command.localizedActionTitle,
+                      buttonTitle: buttonTitle,
                        buttonIcon: command.localizedActionImage,
-                             role: role,
+                             role: role,                                       
 // TODO: WalletColors().errorColor
                          disabled: $disabled,
                            action: doAction)
     }
diff --git a/TalerWallet1/Views/HelperViews/View+NavLink.swift 
b/TalerWallet1/Views/HelperViews/View+NavLink.swift
index 54bc7a6..e453a42 100644
--- a/TalerWallet1/Views/HelperViews/View+NavLink.swift
+++ b/TalerWallet1/Views/HelperViews/View+NavLink.swift
@@ -48,10 +48,10 @@ struct NavLink <Content : View> : View {
     }
 
     var body: some View {
-        if tag != 0 {
+        if tag != 0 {       // actions: $navModel.actionSelected will hide the 
tabBar
             NavigationLink(destination: content, tag: tag, selection: 
$selection)
                 { EmptyView() }.frame(width: 0).opacity(0).hidden()
-        } else {
+        } else {            // shortcuts, AddButton
             NavigationLink(destination: content, isActive: $isActive)
             { EmptyView() }.frame(width: 0).opacity(0).hidden()
         }
diff --git a/TalerWallet1/Views/Main/MainView.swift 
b/TalerWallet1/Views/Main/MainView.swift
index 01364fa..68b342e 100644
--- a/TalerWallet1/Views/Main/MainView.swift
+++ b/TalerWallet1/Views/Main/MainView.swift
@@ -117,10 +117,11 @@ struct MainView: View {
         ) {
             let qrSheet = AnyView(QRSheet(stack: stack.push(".sheet"),
                                           selectedBalance: $selectedBalance))
-            let _ = logger.trace("❗️showScanner: .large❗️")
+            let _ = logger.trace("❗️showScanner: \(SCANDETENT)❗️")
             if #available(iOS 16.4, *) {
+                let scanDetent: PresentationDetent = .fraction(SCANDETENT)
                 Sheet(stack: stack.push(), sheetView: qrSheet)
-                    .presentationDetents([.large])
+                    .presentationDetents([scanDetent])
                     .transition(.opacity)
             } else {
                 Sheet(stack: stack.push(), sheetView: qrSheet)
@@ -201,19 +202,6 @@ extension Label where Title == Text, Icon == Image {
 
 // MARK: - Content
 extension MainView {
-    class NavigationModel: ObservableObject {
-        @Published var tabBarHidden = 0
-
-        @Published var actionSelected: Int? = nil {
-            didSet {
-                if actionSelected != nil {
-                    tabBarHidden += 1
-                } else {
-                    tabBarHidden = 0
-                }
-            }
-        }
-    }
 
     struct MainContent: View {
         let logger: Logger
@@ -247,7 +235,7 @@ extension MainView {
         @State private var amountToTransfer = Amount.zero(currency: 
EMPTYSTRING)  // Update currency when used
         @State private var amountLastUsed = Amount.zero(currency: EMPTYSTRING) 
   // Update currency when used
         @State private var summary: String = EMPTYSTRING
-        @State private var myExchange: Exchange? = nil
+        @State private var exchange: Exchange? = nil
 
         private var openKycButton: some View {
             Button("KYC") {
@@ -328,9 +316,11 @@ extension MainView {
             let manualWithdrawDest = ManualWithdraw(stack: 
stack.push(Self.name),
                                           selectedBalance: $selectedBalance,
                                            amountLastUsed: $amountLastUsed,    
 // currency needs to be updated!
+                                         amountToTransfer: $amountToTransfer,
+                                                 exchange: $exchange,          
 // only for withdraw-exchange
                                                   isSheet: false)
         /// each NavigationView needs its own NavLinks
-            let balanceActions = Group {
+            let balanceActions = Group {                                       
 // actionSelected will hide the tabBar
                 NavLink(1, $navModel.actionSelected) { sendDest }
                 NavLink(2, $navModel.actionSelected) { requestDest }
                 NavLink(3, $navModel.actionSelected) { depositDest }
@@ -363,7 +353,7 @@ extension MainView {
             }.navigationViewStyle(.stack)
             let settingsStack = NavigationView {
                 SettingsView(stack: stack.push(),
-                             navTitle: settingsTitle)
+                          navTitle: settingsTitle)
                 .background(settingsActions)
             }.navigationViewStyle(.stack)
         /// our custom tabBar with the Actions button in the middle
@@ -392,6 +382,7 @@ extension MainView {
                     .ignoresSafeArea(.keyboard, edges: .bottom)
                     .accessibilityHidden(true)                  // for a11y we 
use the original tabBar, not our custom one
             } // ZStack
+            .environmentObject(navModel)
             .frame(maxWidth: .infinity, maxHeight: .infinity)
             .onNotification(.SendAction)    { triggerAction(1) }
             .onNotification(.RequestAction) { triggerAction(2) }
diff --git a/TalerWallet1/Views/Main/NavigationModel.swift 
b/TalerWallet1/Views/Main/NavigationModel.swift
new file mode 100644
index 0000000..bdb48a0
--- /dev/null
+++ b/TalerWallet1/Views/Main/NavigationModel.swift
@@ -0,0 +1,35 @@
+/*
+ * This file is part of GNU Taler, ©2022-24 Taler Systems S.A.
+ * See LICENSE.md
+ */
+/**
+ * Controller
+ *
+ * @author Marc Stibane
+ */
+import Foundation
+import SwiftUI
+
+class NavigationModel: ObservableObject {
+    @Published var tabBarHidden = 0
+
+    @Published var tosView: Int? = nil {
+        didSet {
+            if tosView != nil {
+                tabBarHidden += 1
+            } else if actionSelected == nil {
+                tabBarHidden = 0
+            }
+        }
+    }
+
+    @Published var actionSelected: Int? = nil {
+        didSet {
+            if actionSelected != nil {
+                tabBarHidden += 1
+            } else if tosView == nil {
+                tabBarHidden = 0
+            }
+        }
+    }
+}
diff --git a/TalerWallet1/Views/Settings/Bank/BankEditView.swift 
b/TalerWallet1/Views/Settings/Bank/BankEditView.swift
index 38656a5..6258110 100755
--- a/TalerWallet1/Views/Settings/Bank/BankEditView.swift
+++ b/TalerWallet1/Views/Settings/Bank/BankEditView.swift
@@ -210,10 +210,10 @@ struct BankEditView: View {
             let warningText1 = String(localized: "Are you sure you want to 
forget this bank account?")
             WarningButton(warningText: warningText1,
                           buttonTitle: buttonTitle,
-                          buttonIcon: "trash",
-                          role: .destructive,
-                          disabled: $disabled,
-                          action: deleteAccount)
+                           buttonIcon: "trash",
+                                 role: .destructive,                           
 // TODO: WalletColors().errorColor
+                             disabled: $disabled,
+                               action: deleteAccount)
             .padding(.top)
           }.listRowSeparator(.hidden)
         } // List
diff --git a/TalerWallet1/Views/Settings/Exchange/ExchangeListView.swift 
b/TalerWallet1/Views/Settings/Exchange/ExchangeListView.swift
index 76b4b53..a87e640 100644
--- a/TalerWallet1/Views/Settings/Exchange/ExchangeListView.swift
+++ b/TalerWallet1/Views/Settings/Exchange/ExchangeListView.swift
@@ -75,7 +75,6 @@ struct ExchangeListCommonV: View {
     let symLog: SymLogV?
     let stack: CallStack
 
-    @EnvironmentObject private var model: WalletModel
     @EnvironmentObject private var controller: Controller
     @AppStorage("minimalistic") var minimalistic: Bool = false
     @AppStorage("myListStyle") var myListStyle: MyListStyle = .automatic
@@ -85,9 +84,10 @@ struct ExchangeListCommonV: View {
         let _ = Self._printChanges()
         let _ = symLog?.vlog()       // just to get the # to compare it with 
.onAppear & onDisappear
 #endif
-        let sortedList = List(controller.balances, id: \.self) { balance in
+        let sortedList = List(Array(controller.balances.enumerated()), id: 
\.element) { index, balance in
             ExchangeSectionView(stack: stack.push(),
-                             balance: balance)
+                              balance: balance,
+                             thousand: index * MAXEXCHANGES)
         }
 
         let emptyList = List {
diff --git a/TalerWallet1/Views/Settings/Exchange/ExchangeRowView.swift 
b/TalerWallet1/Views/Settings/Exchange/ExchangeRowView.swift
index 2050196..41fb08d 100644
--- a/TalerWallet1/Views/Settings/Exchange/ExchangeRowView.swift
+++ b/TalerWallet1/Views/Settings/Exchange/ExchangeRowView.swift
@@ -13,10 +13,12 @@ struct ExchangeRowView: View {
     private let symLog = SymLogV(0)
     let stack: CallStack
     let exchange: Exchange
+    let index: Int
 
     @Environment(\.sizeCategory) var sizeCategory
     @EnvironmentObject private var controller: Controller
     @EnvironmentObject private var model: WalletModel
+    @EnvironmentObject private var navModel: NavigationModel
     @AppStorage("minimalistic") var minimalistic: Bool = false
 
     var body: some View {
@@ -36,12 +38,33 @@ struct ExchangeRowView: View {
                     .talerFont(.body)
             }
         }
+        let cellView = HStack {
+            rowView
+            Spacer()
+            Text(Image(systemName: "chevron.right"))
+                .talerFont(.table)
+                .foregroundColor(.secondary)
+        }.background {           // only needed for .onTapGesture
+            Color.gray.opacity(0.001)
+        }
         let showToS = WithdrawTOSView(stack: stack.push(),
                             exchangeBaseUrl: baseURL,
                                      viewID: VIEW_WITHDRAW_TOS,
                                acceptAction: nil)         // pop back to here
+        let actions = Group {
+            NavLink(index, $navModel.tosView) { showToS }
+        }
+
         Group {
-            NavigationLink(destination: showToS) { rowView }
+//          NavigationLink(destination: showToS) { rowView }
+            cellView
+                .background(actions)
+                .onTapGesture {
+                    navModel.tosView = index
+                }
+//                .task {
+//                    navModel.tosView = false
+//                }
         }.listRowSeparator(.hidden)
     }
 }
@@ -75,7 +98,8 @@ fileprivate struct ExchangeRow_Previews: PreviewProvider {
                                 exchangeUpdateStatus: .ready,
                                ageRestrictionOptions: [])
             ExchangeRowView(stack: CallStack("Preview"),
-                         exchange: exchange1)
+                         exchange: exchange1,
+                            index: 1)
         }
     }
 
diff --git a/TalerWallet1/Views/Settings/Exchange/ExchangeSectionView.swift 
b/TalerWallet1/Views/Settings/Exchange/ExchangeSectionView.swift
index 6ac1195..0a58229 100755
--- a/TalerWallet1/Views/Settings/Exchange/ExchangeSectionView.swift
+++ b/TalerWallet1/Views/Settings/Exchange/ExchangeSectionView.swift
@@ -15,6 +15,7 @@ struct ExchangeSectionView: View {
     private let symLog = SymLogV(0)
     let stack: CallStack
     let balance: Balance
+    let thousand: Int
 
     @EnvironmentObject private var model: WalletModel
     @EnvironmentObject private var controller: Controller
@@ -102,9 +103,11 @@ struct ExchangeSectionView: View {
         let scopeInfo = balance.scopeInfo
         let currency = scopeInfo.currency
         Section {
-            ForEach(exchanges) { exchange in
+            ForEachWithIndex(data: exchanges) { index, exchange in
+                let _ = symLog.log("Index: \(thousand + index)")
                 ExchangeRowView(stack: stack.push(),
-                             exchange: exchange)
+                             exchange: exchange,
+                                index: thousand + index + 1)
                 .listRowSeparator(.hidden)
             }
             if developerMode && (DEMOCURRENCY == currency || TESTCURRENCY == 
currency) {
@@ -143,7 +146,7 @@ struct ExchangeSectionView: View {
             WarningButton(warningText: warningText1,
                           buttonTitle: buttonTitle,
                            buttonIcon: "trash",
-                                 role: .destructive,
+                                 role: .destructive,                           
 // TODO: WalletColors().errorColor
                              disabled: $disabled,
                                action: deleteExchange)
             .padding(.top)
diff --git a/TalerWallet1/Views/Settings/SettingsItem.swift 
b/TalerWallet1/Views/Settings/SettingsItem.swift
index fda6f70..6525148 100644
--- a/TalerWallet1/Views/Settings/SettingsItem.swift
+++ b/TalerWallet1/Views/Settings/SettingsItem.swift
@@ -31,7 +31,7 @@ struct SettingsItem<Content: View>: View {
         HStack {
             VStack {
                 let isWeb = id1?.hasPrefix("web") ?? false
-                let foreColor = isWeb ? WalletColors().tint
+                let foreColor = isWeb ? WalletColors().talerColor
                                       : .primary
                 HStack(spacing: 8.0) {
                     if let imageName {
diff --git a/TalerWallet1/Views/Settings/SettingsView.swift 
b/TalerWallet1/Views/Settings/SettingsView.swift
index da2d034..397e99d 100644
--- a/TalerWallet1/Views/Settings/SettingsView.swift
+++ b/TalerWallet1/Views/Settings/SettingsView.swift
@@ -59,12 +59,12 @@ struct SettingsView: View {
         }
     }
     private var resetButton: some View {
-        Button("Reset", role: .destructive) {
+        Button("Reset", role: .destructive) {                                  
 // TODO: WalletColors().errorColor
             didReset = true
             showResetAlert = false
             Task { // runs on MainActor
                 symLog.log("❗️Reset wallet-core❗️")
-                try? await model.resetWalletCoreT()
+                try? await model.resetWalletCore()
             }
         }
     }
@@ -223,7 +223,7 @@ struct SettingsView: View {
                             Button(title) {
                                 Task { // runs on MainActor
                                     symLog.log("running applyDevExperiment 
Refresh")
-                                    try? await model.setConfigT(setTesting: 
true)
+                                    try? await model.setConfig(setTesting: 
true)
                                     try? await model.devExperimentT(talerUri: 
"taler://dev-experiment/start-block-refresh")
                                     try? await model.devExperimentT(talerUri: 
"taler://dev-experiment/insert-pending-refresh")
                                 }
diff --git 
a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift 
b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
index ca4723c..3769237 100644
--- a/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawBankIntegrated/WithdrawTOSView.swift
@@ -163,7 +163,8 @@ extension WithdrawTOSView {
                             .padding(.horizontal)
             list.safeAreaInset(edge: .bottom) {
                 if showButton {
-                    button.padding(.bottom, 40)
+                    button
+//                        .padding(.bottom, 40)
                 }
             }
         }
diff --git a/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift 
b/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
index 464fd5b..42cd333 100644
--- a/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
+++ b/TalerWallet1/Views/Sheets/WithdrawExchangeV.swift
@@ -29,7 +29,7 @@ struct WithdrawExchangeV: View {
     private func viewDidLoad() async {
         if exchange == nil {
             symLog.log(".task")
-            if let withdrawExchange = try? await 
model.loadWithdrawalExchangeForUri(url.absoluteString) {
+            if let withdrawExchange = try? await 
model.prepareWithdrawExchange(url.absoluteString) {
                 let baseUrl = withdrawExchange.exchangeBaseUrl
                 symLog.log("getExchangeByUrl(\(baseUrl))")
                 if let exc = try? await model.getExchangeByUrl(url: baseUrl) {
@@ -42,6 +42,7 @@ struct WithdrawExchangeV: View {
                         amountToTransfer.setCurrency(currency)
                         // is already Amount.zero()
                     }
+                    amountLastUsed.setCurrency(amountToTransfer.currencyStr)
                     exchange = exc
                 } else {
                     exchange = nil
@@ -61,6 +62,8 @@ struct WithdrawExchangeV: View {
                 ManualWithdraw(stack: stack.push(),
                      selectedBalance: $selectedBalance,
                       amountLastUsed: $amountLastUsed,
+                    amountToTransfer: $amountToTransfer,
+                            exchange: $exchange,           // only for 
withdraw-exchange
                              isSheet: true)
             }
             .task(id: controller.currencyTicker) {
diff --git a/TalerWallet1/Views/Transactions/TransactionSummaryV.swift 
b/TalerWallet1/Views/Transactions/TransactionSummaryV.swift
index b1b71f5..9d4dab9 100755
--- a/TalerWallet1/Views/Transactions/TransactionSummaryV.swift
+++ b/TalerWallet1/Views/Transactions/TransactionSummaryV.swift
@@ -59,7 +59,8 @@ struct TransactionSummaryV: View {
     @Namespace var topID
 
     func loadTransaction() async {
-        if let reloadedTransaction = try? await 
model.getTransactionById(transactionId, viewHandles: false) {
+        if let reloadedTransaction = try? await 
model.getTransactionById(transactionId,
+                                                               withTerms: 
true, viewHandles: false) {
             symLog.log("reloaded transaction: 
\(reloadedTransaction.common.txState.major)")
             withAnimation { transaction = reloadedTransaction; viewId = UUID() 
}      // redraw
             if outTransaction != nil {
diff --git a/TestFlight/WhatToTest.en-US.txt b/TestFlight/WhatToTest.en-US.txt
index c091c21..bb5801a 100644
--- a/TestFlight/WhatToTest.en-US.txt
+++ b/TestFlight/WhatToTest.en-US.txt
@@ -1,3 +1,9 @@
+Version 0.14.1 (wallet-core 0.14.1)
+
+- bugfix: withdraw-template works again
+- Spanish localization
+
+
 Version 0.14.0 (wallet-core 0.14.0)
 
 • New UI with Action button

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]