From ac406bf7a644ffd616f2a586ae407cf67665fe1a Mon Sep 17 00:00:00 2001 From: Philipp Walter Date: Mon, 16 Mar 2026 16:46:51 +0100 Subject: [PATCH] feat(settings): update design v60 --- Bitkit/AppScene.swift | 1 + .../Contents.json | 15 + .../arrow-counter-clockwise.pdf | Bin 0 -> 5971 bytes .../icons/barcode.imageset /Contents.json | 15 + .../icons/barcode.imageset /barcode.pdf | Bin 0 -> 5680 bytes .../icons/broadcast.imageset/Contents.json | 15 + .../icons/broadcast.imageset/broadcast.pdf | Bin 0 -> 6673 bytes .../caret-double-right.imageset/Contents.json | 15 + .../caret-double-right.pdf | Bin 0 -> 5716 bytes .../icons/cube.imageset/Contents.json | 15 + .../icons/cube.imageset/cube.pdf | Bin 0 -> 6277 bytes .../icons/database.imageset/Contents.json | 15 + .../icons/database.imageset/database.pdf | Bin 0 -> 5871 bytes .../Contents.json | 15 + .../device-mobile-speaker.pdf | Bin 0 -> 5688 bytes .../icons/eye-slash.imageset/Contents.json | 15 + .../icons/eye-slash.imageset/eye-slash.pdf | Bin 0 -> 8251 bytes .../icons/file-text.imageset/Contents.json | 15 + .../icons/file-text.imageset/file-text.pdf | Bin 0 -> 5903 bytes .../icons/git-branch.imageset/Contents.json | 15 + .../icons/git-branch.imageset/git-branch.pdf | Bin 0 -> 6002 bytes .../icons/git-diff.imageset/Contents.json | 15 + .../icons/git-diff.imageset/git-diff.pdf | Bin 0 -> 6278 bytes .../hand-pointing.imageset/Contents.json | 15 + .../hand-pointing.imageset/hand-pointing.pdf | Bin 0 -> 7703 bytes .../icons/hard-drives.imageset/Contents.json | 15 + .../hard-drives.imageset/hard-drives.pdf | Bin 0 -> 5719 bytes .../icons/list-dashes.imageset/Contents.json | 15 + .../list-dashes.imageset/list-dashes.pdf | Bin 0 -> 5501 bytes .../icons/lock-key.imageset/Contents.json | 15 + .../icons/lock-key.imageset/lock-key.pdf | Bin 0 -> 6344 bytes .../icons/question.imageset/Contents.json | 15 + .../icons/question.imageset/question.pdf | Bin 0 -> 6178 bytes .../icons/smiley.imageset/Contents.json | 15 + .../icons/smiley.imageset/smiley.pdf | Bin 0 -> 5965 bytes .../icons/stop-circle.imageset/Contents.json | 15 + .../stop-circle.imageset/stop-circle.pdf | Bin 0 -> 5984 bytes .../icons/translate.imageset/Contents.json | 15 + .../icons/translate.imageset/translate.pdf | Bin 0 -> 6120 bytes .../synonym-logo.imageset/Contents.json | 21 ++ .../synonym-logo.imageset/synonym-logo.png | Bin 0 -> 3620 bytes .../tether-logo.imageset/Contents.json | 21 ++ .../tether-logo.imageset/tether-logo.png | Bin 0 -> 3342 bytes Bitkit/Components/DrawerView.swift | 11 +- Bitkit/Components/SegmentedControl.swift | 9 +- ...tingsListLabel.swift => SettingsRow.swift} | 46 ++- Bitkit/Components/Social.swift | 6 +- Bitkit/Extensions/String+Utilities.swift | 30 +- Bitkit/MainNavView.swift | 21 +- .../Localization/en.lproj/Localizable.strings | 41 ++- Bitkit/ViewModels/NavigationViewModel.swift | 38 +- Bitkit/Views/Settings/AboutView.swift | 104 ------ .../Advanced/AdvancedSettingsView.swift | 159 ++++---- .../Advanced/CoinSelectionSettingsView.swift | 22 +- .../LightningConnectionDetailView.swift | 79 ++-- .../Advanced/LightningConnectionsView.swift | 24 +- Bitkit/Views/Settings/AppStatusView.swift | 2 +- .../Views/Settings/BlocktankRegtestView.swift | 347 +++++++++--------- Bitkit/Views/Settings/DevSettingsView.swift | 26 +- .../General/DefaultUnitSettingsView.swift | 31 +- .../General/LanguageSettingsScreen.swift | 11 +- .../General/LocalCurrencySettingsView.swift | 16 +- .../Settings/General/TagSettingsView.swift | 10 +- .../General/WidgetsSettingsScreen.swift | 82 +++++ .../General/WidgetsSettingsView.swift | 37 -- .../Views/Settings/GeneralSettingsView.swift | 137 +++---- Bitkit/Views/Settings/LdkDebugScreen.swift | 8 +- Bitkit/Views/Settings/MainSettings.swift | 107 ------ .../Views/Settings/MainSettingsScreen.swift | 43 +++ .../Notifications/NotificationsSettings.swift | 4 +- .../Settings/Quickpay/QuickpaySettings.swift | 6 +- .../Settings/Security/ChangePinScreen.swift | 76 ++++ .../DataBackupsScreen.swift} | 35 +- .../Settings/Security/DisablePinView.swift | 57 --- .../ResetScreen.swift} | 4 +- ...sView.swift => SecuritySettingsView.swift} | 160 ++++---- Bitkit/Views/Settings/SupportScreen.swift | 178 +++++++++ Bitkit/Views/Settings/SupportView.swift | 55 --- .../TransactionSpeedSettingsView.swift | 9 +- Bitkit/Views/Wallets/Receive/ReceiveQr.swift | 6 +- 80 files changed, 1348 insertions(+), 1017 deletions(-) create mode 100644 Bitkit/Assets.xcassets/icons/arrow-counter-clockwise.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/arrow-counter-clockwise.imageset/arrow-counter-clockwise.pdf create mode 100644 Bitkit/Assets.xcassets/icons/barcode.imageset /Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/barcode.imageset /barcode.pdf create mode 100644 Bitkit/Assets.xcassets/icons/broadcast.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/broadcast.imageset/broadcast.pdf create mode 100644 Bitkit/Assets.xcassets/icons/caret-double-right.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/caret-double-right.imageset/caret-double-right.pdf create mode 100644 Bitkit/Assets.xcassets/icons/cube.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/cube.imageset/cube.pdf create mode 100644 Bitkit/Assets.xcassets/icons/database.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/database.imageset/database.pdf create mode 100644 Bitkit/Assets.xcassets/icons/device-mobile-speaker.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/device-mobile-speaker.imageset/device-mobile-speaker.pdf create mode 100644 Bitkit/Assets.xcassets/icons/eye-slash.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/eye-slash.imageset/eye-slash.pdf create mode 100644 Bitkit/Assets.xcassets/icons/file-text.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/file-text.imageset/file-text.pdf create mode 100644 Bitkit/Assets.xcassets/icons/git-branch.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/git-branch.imageset/git-branch.pdf create mode 100644 Bitkit/Assets.xcassets/icons/git-diff.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/git-diff.imageset/git-diff.pdf create mode 100644 Bitkit/Assets.xcassets/icons/hand-pointing.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/hand-pointing.imageset/hand-pointing.pdf create mode 100644 Bitkit/Assets.xcassets/icons/hard-drives.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/hard-drives.imageset/hard-drives.pdf create mode 100644 Bitkit/Assets.xcassets/icons/list-dashes.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/list-dashes.imageset/list-dashes.pdf create mode 100644 Bitkit/Assets.xcassets/icons/lock-key.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/lock-key.imageset/lock-key.pdf create mode 100644 Bitkit/Assets.xcassets/icons/question.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/question.imageset/question.pdf create mode 100644 Bitkit/Assets.xcassets/icons/smiley.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/smiley.imageset/smiley.pdf create mode 100644 Bitkit/Assets.xcassets/icons/stop-circle.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/stop-circle.imageset/stop-circle.pdf create mode 100644 Bitkit/Assets.xcassets/icons/translate.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/icons/translate.imageset/translate.pdf create mode 100644 Bitkit/Assets.xcassets/synonym-logo.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/synonym-logo.imageset/synonym-logo.png create mode 100644 Bitkit/Assets.xcassets/tether-logo.imageset/Contents.json create mode 100644 Bitkit/Assets.xcassets/tether-logo.imageset/tether-logo.png rename Bitkit/Components/{SettingsListLabel.swift => SettingsRow.swift} (69%) delete mode 100644 Bitkit/Views/Settings/AboutView.swift create mode 100644 Bitkit/Views/Settings/General/WidgetsSettingsScreen.swift delete mode 100644 Bitkit/Views/Settings/General/WidgetsSettingsView.swift delete mode 100644 Bitkit/Views/Settings/MainSettings.swift create mode 100644 Bitkit/Views/Settings/MainSettingsScreen.swift create mode 100644 Bitkit/Views/Settings/Security/ChangePinScreen.swift rename Bitkit/Views/Settings/{Backup/BackupSettings.swift => Security/DataBackupsScreen.swift} (76%) delete mode 100644 Bitkit/Views/Settings/Security/DisablePinView.swift rename Bitkit/Views/Settings/{Backup/ResetAndRestore.swift => Security/ResetScreen.swift} (96%) rename Bitkit/Views/Settings/{SecurityPrivacySettingsView.swift => SecuritySettingsView.swift} (70%) create mode 100644 Bitkit/Views/Settings/SupportScreen.swift delete mode 100644 Bitkit/Views/Settings/SupportView.swift diff --git a/Bitkit/AppScene.swift b/Bitkit/AppScene.swift index 4b0268ce6..596e47020 100644 --- a/Bitkit/AppScene.swift +++ b/Bitkit/AppScene.swift @@ -97,6 +97,7 @@ struct AppScene: View { if UserDefaults.standard.bool(forKey: "pinOnLaunch") && settings.pinEnabled { isPinVerified = false } + if migrations.needsPostMigrationSync { app.toast( type: .warning, diff --git a/Bitkit/Assets.xcassets/icons/arrow-counter-clockwise.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/arrow-counter-clockwise.imageset/Contents.json new file mode 100644 index 000000000..e84178605 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/arrow-counter-clockwise.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "arrow-counter-clockwise.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/arrow-counter-clockwise.imageset/arrow-counter-clockwise.pdf b/Bitkit/Assets.xcassets/icons/arrow-counter-clockwise.imageset/arrow-counter-clockwise.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6c28c8d72576d6a7c8f395cecd0bc371e8cfc611 GIT binary patch literal 5971 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 literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/broadcast.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/broadcast.imageset/Contents.json new file mode 100644 index 000000000..8e5a73516 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/broadcast.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "broadcast.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/broadcast.imageset/broadcast.pdf b/Bitkit/Assets.xcassets/icons/broadcast.imageset/broadcast.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2817a11379c98a79b5d633510a3784ec7512142d GIT binary patch literal 6673 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 D8ETZ} literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/caret-double-right.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/caret-double-right.imageset/Contents.json new file mode 100644 index 000000000..528c5fb61 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/caret-double-right.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "caret-double-right.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/caret-double-right.imageset/caret-double-right.pdf b/Bitkit/Assets.xcassets/icons/caret-double-right.imageset/caret-double-right.pdf new file mode 100644 index 0000000000000000000000000000000000000000..154d89b67edbd97db44607987352a3ebc503a863 GIT binary patch literal 5716 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 IG}~qVKdwZsX8-^I literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/cube.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/cube.imageset/Contents.json new file mode 100644 index 000000000..9c36c08b7 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/cube.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "cube.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/cube.imageset/cube.pdf b/Bitkit/Assets.xcassets/icons/cube.imageset/cube.pdf new file mode 100644 index 0000000000000000000000000000000000000000..75afbf287f2979c87bcc090a480d7f5d040eeb45 GIT binary patch literal 6277 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 literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/device-mobile-speaker.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/device-mobile-speaker.imageset/Contents.json new file mode 100644 index 000000000..1486dd2e4 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/device-mobile-speaker.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "device-mobile-speaker.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/device-mobile-speaker.imageset/device-mobile-speaker.pdf b/Bitkit/Assets.xcassets/icons/device-mobile-speaker.imageset/device-mobile-speaker.pdf new file mode 100644 index 0000000000000000000000000000000000000000..94677a2abdc92a1386471c10c9be317d3c8d3553 GIT binary patch literal 5688 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 REeH`-3yVQ)*kEpN@jqAJq)Pw* literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/eye-slash.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/eye-slash.imageset/Contents.json new file mode 100644 index 000000000..969759f92 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/eye-slash.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "eye-slash.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/eye-slash.imageset/eye-slash.pdf b/Bitkit/Assets.xcassets/icons/eye-slash.imageset/eye-slash.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3690f6ae067591d8374e5aefe0587b73ec38afa7 GIT binary patch literal 8251 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 D0#_a3 literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/git-branch.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/git-branch.imageset/Contents.json new file mode 100644 index 000000000..2941a65ec --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/git-branch.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "git-branch.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/git-branch.imageset/git-branch.pdf b/Bitkit/Assets.xcassets/icons/git-branch.imageset/git-branch.pdf new file mode 100644 index 0000000000000000000000000000000000000000..05bf87c9ffce3e32c1a1206d4951b20e470c65fe GIT binary patch literal 6002 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

`l9z7i+L6* zonb5V^`NOu!AEnE=Ca?5aEF>W$Yd5A_|%9k;=3ycT2hByGETuJEC++d^DRk}~A&5^U>x9x&Zi z3*!2fD%6mxr^mf-+{u=y)v)h|g}~nRMQKIbQbIFJ?8kBhtu;ObV+6M!%*fGODlP7k ztaA$}_xhAtmvOwa^eunP(djQ=m--2eB>8>q z_KEK46Va2|7$qGr7P0{!6!;@(^iJ{J(Uq3t2hJt_f^B=>KlVY9g?*GT@X{rlW;{CO zHufR6x7Rkdr6rIjsN#kU3f{0X^l$h{8VD;=;=h%@wcVmTa{_7Au_ zSchx7#i&^c*1Rfz=F-_9dsY4J(P#D-0gtHQ^_&dz0Lr>l3h}NbQNcNjp&}+TWRt?@ z`6<=JqUgQMkcNq(7=e9w#my7(J4J6+A|HjzjA)pYp?Jh!mv8k8mo4#Mf)u!0{2v1xy<@)PByp8#*?WJcAY@-g8hyQ^xIiRkk`3wyLqpJ!nphJobq6&fJR_L z&8z>PfW-|5p4CuW!W7`Ji~#TX_5A$>zR)@h5{YQ{X(6m zsVal<@h1%fuJRu=Ec!1#C5#f7{)_pPFqpsjRKU~wPd*jwUvrf)f6GX2b8}e?Jc$f2U{G)v>VXNgOBtz(!P+BL6csT@ uB^BAf3EYhW0mUsG@-Q!x5f&sL09p`mm6$9rp}9+hMq^ZAJ9g+F+W$XMOHKFy literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/git-diff.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/git-diff.imageset/Contents.json new file mode 100644 index 000000000..416ad0367 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/git-diff.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "git-diff.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/git-diff.imageset/git-diff.pdf b/Bitkit/Assets.xcassets/icons/git-diff.imageset/git-diff.pdf new file mode 100644 index 0000000000000000000000000000000000000000..79e7a522a226757f0a23814de6a4e4e62d19bf2f GIT binary patch literal 6278 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 YAwZUBbfBHtDMF~Js6eEo42~H759ip*&;S4c literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/hand-pointing.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/hand-pointing.imageset/Contents.json new file mode 100644 index 000000000..798be8db4 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/hand-pointing.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "hand-pointing.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/hand-pointing.imageset/hand-pointing.pdf b/Bitkit/Assets.xcassets/icons/hand-pointing.imageset/hand-pointing.pdf new file mode 100644 index 0000000000000000000000000000000000000000..edbe95255ceec519e6c09327ed4bb39461312e86 GIT binary patch literal 7703 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 KE?-vEQu-g9DXJ*| literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/hard-drives.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/hard-drives.imageset/Contents.json new file mode 100644 index 000000000..b320970d6 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/hard-drives.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "hard-drives.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/hard-drives.imageset/hard-drives.pdf b/Bitkit/Assets.xcassets/icons/hard-drives.imageset/hard-drives.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0939363b42120fc89b37bb86892e43ab4ede83f0 GIT binary patch literal 5719 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 Q5n4+R4coZUa);G_0JLG7MgRZ+ literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/list-dashes.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/list-dashes.imageset/Contents.json new file mode 100644 index 000000000..7da1884ca --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/list-dashes.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "list-dashes.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/list-dashes.imageset/list-dashes.pdf b/Bitkit/Assets.xcassets/icons/list-dashes.imageset/list-dashes.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4e7f8e65a579d25ad9aa5fed9a4279043e21c26d GIT binary patch literal 5501 zcma)Ac|25W_&20QNvlHYpt6LS!3<-|9$6YoQdt@^2g8^dvt+V_XuC=xOZFutk*K7s zQQ>m4<=RW7TXK`7Y$?BUW)O9MpWpBNF`x53@AH12{mlD3yYNOPN@!(up;b!T}1DiF~IB0&$*mhH+qK?eb(gQdkB$&S0W1X%vE43e9sjgiS56SUHi_F%E-nn*#E#3S%jyJ{8c8b1dPfixc{n$0m1zz)Pf$mav=486}9LHSJYBuo;r*C z&V%O~y@)#JdOFFCO@Py8QG5wxvNtyd+)X5vi6oO2IdtxbCY``w>7uofOV(T`|ECH( zB9v878X_0+5LzsxF-V2<@P+dfdYemf=nXCzA-GI*p)debCQC4hkCU6u$ zEd-hy?xRkN*K-RbudeU}29#iS3=o8Ppvs!ZRa=4!g%)H`2`s2Ejlkn%B51SEfQN}R zA;WtB(u4wS8-YX`2}XjRh4RK=)#oTyfGSVQoN1h-`>|yF#!k^1n(z2UB?RJdRrwjq zSiZtDN@=HpDH$80_z0`kZK!lwZymDU*l3xf-g4t@be02Y-FoYu<${8Cj;GcL{i&hx zhlE1Dzz)_&%eGsG@?TculmrESOzM80@-2OqFOn)?gkof^e4260<$#|NX_Mr{vQ>4V zIGzz?!m_*Bobu#)w=-SPyk=n8I!VLf%Lo{`msx85qdsYgr0tCR^kZ1py6fwnoT0Bq*mQS zbS>Yk_Y{w_6KvDpYxq7bya|&n@<`;O-o_@ECfTim(FF0_RT5RPsEy;n0)2SS=4~Q3 zst5ZVIqHtf4^`jjbDNY75ZE4^HEhli7sT(pBD8Sd|{%^ zzK2F9ET({cF?S=0^&|b*`h&HY`mgn6QL?oXwf?pFu`aSM3PJWiVrQ=L>p#YaWK~*s zT*If+tq7)sBbReL^0sEjXEf8)w6mc>9+i`-`8w#>q8$B$S~ z;jb#}3um&#nmg_p9YK7OJ%aS!f7qTkmVMxYJ)pmCU^JkA!#0u?qd5m6}R?}COewk4GdTq66wU5j}nWARROux*t z&o&X&-?H9%zAf49u<^QCRGn0G_w_qBlgX#Zrw5c&I~rbE96Mf{+qjK5x*X5Pko3 zmt9AXlhNV+yB=d1e;em}|3#T9I_v$p)B00v*QYy!h2LC1A43+>?xfP2e9!r0v$ndn z#OpU0{Bv;Mh0Ggo-D-~4T(TOsa=oEy6{M+}6rFTH^AZw!q`}4UF6V`D`{}nf@1)sT zCC`etvtu3&6#Mk#^rk!-d^pzQ)tl-c&)MmhG|G8LekVUl84YM;xO$V=e(9wFWus+& zWih3PIC1`a$D6;NpSU@3b@cw2)93Divm>jAy9d>u{7qtZvj3G2S}}X^M=zp3I5K!N z_=dSf+_~6$N~hzdk-ZPTyjBSXt3ag?AX2gzIcyzijrVKHuP>+%jXk6oBubGil`57E z6EED|PpU1!H#TK7z25w6&(1xMs`{%lsy|k}JnMgY%UN?7MH#F~&pEqGn~aJ~UT0Kg zcxGNdfdbdEAM|ewx6>xV9QDL?1?0EN1q-}srgL#H-1Nk5sT_H# z*1wNo>#EqPELO+*E4Owga&s&jdlY>%K9HF=(BxMAKn`MF7-0~NA=KIi3pGF@*1N9*?k|%6kg^0<5~T&h1fy7T9eWi*7jwl zcBbIAeOXc3c)ibSpMmpz8++Mn*=$*im1tY_*Qn|9w#_%Fv;ALfy#zATbF8xp`qVU( zaNpm&k566ExGZTvvtGMzF5akD1# zebvFZTc6sKZYP&KFMY|V{$Vm^S(0d3(fR%F7hbQr1L|Mf4|>;hdk2Pp)12;|M!p<* z#C)gxZRlbMC=eLBXZdGgV-d5@BSRNo9Vnlj*8eB;>T4wtw8+>Fd2NNj+>zqx^L5cC zH_N`voNKTB>xR^Uv-=)qB7+(yzmGW#ss4Gl<$YJ<;4|-M-Le6@sTB@WuK$8v^;hdk z_imca4ghC9#_jE$EyZm{W5$ns@f**tQum}qRNN2j#z?AtpLF`OleOT_i@xLfn5JjqX=#IS3;9&OnhA!8=m3OV>%#tkdqUjDxN1b+c=&~(enfgp+2 z+48Z9!K10KN(r#R1cTc;Po7P@tKQT+ulDwQ-bHVPi17L=}A!F6D zHuyrZF_vkrPkJIlOlf#tj$X!hh`%x}qqFmBPG={wRuNy#$J+lx@Z!p5ng6)#VivBS zD)9Aj{#J@zARh&gW`>3a1SUuV;Jc~~;IzP{zQ6_Fi4AEK8p9Uq6G3j%X@~}36dUNW zyZFAVvEz9=wklg(B%&bwmd88va} zIsJ#vlPkN@||5)^Y@X8%Z3D1W-~AgyNX)wpa*+c)8kkF z2%6%B=HrnHL-koj?}im8FB>Mm2^q$}Z~GQ?;#I=4!aK(|I>tv4G)=EX?&^rRDydXy z;Jj+$)HzdU>C56)aw1cD((MuKfOmc3#}M}CrXsf*O=o>tE%{;a`j)1!w15rUe1|I9 zo!akTPwwaldheuOkd~;nr%h@|aDD5GAe5P~?3J?tz@C9w6MuV)lBz2@fARqv#B)b5|IXhMXY4AwE7-8+f=z<(&aOzS@~0roVn?NL?1EQA=1S|GzQ zHYbzdz6TA@7vN0bH6VsGHkAdS=T`ImjyuTY1#Uj6xTkm`U@UzqmBxb5=23VZ62O%> zKc=t&nKU+o2r>aU*XF5~Ac;&cpmE?1Oc}*}e!ifw^Q50|^CEt#Hm@tel?e?)=2o^W z2Ajy@>A{?D?x4J;vf$YO0H^L;E-sk#vsr{q!UJfyPn_4&7NS6%>l`}Xea>(5vbpmX zo=jtC{s55|^@UuZpUyIez0MurEO;Ff#x2Ns<)N05Mr6ZdjQ^j2rJX0wYPdhO0y!*m z@Wetqf9?Q_jpJ&>B`j#fb1%;#7|Di4gLPSK)z9w@>@Q0K!^@27M&o`k;05%Nfnor{ zM|i09;?jhi-%@0%%zFVbCn@9Xrs{F zA+|+6;y`u@p#U^ih?m;%FM!2hR55^?@B$4Dd6K(BU7%^GLE-q7hW(unhxv_96{`jn z`qHziSnTh7>L@4>zwoL5j;DskLdm+6PYtaBrQ9zx73^|98u~jDp+;2s{HQ+?w0}yCoTYcm{-b3Te2Sr Z2Ly@|lLb{YH$)f|^oO}?m+=9U{{hi;TqXbj literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/lock-key.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/lock-key.imageset/Contents.json new file mode 100644 index 000000000..0100d4a72 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/lock-key.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "lock-key.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/lock-key.imageset/lock-key.pdf b/Bitkit/Assets.xcassets/icons/lock-key.imageset/lock-key.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ce72eab326704bc72cee4b1dd158073de49cff44 GIT binary patch literal 6344 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 literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/question.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/question.imageset/Contents.json new file mode 100644 index 000000000..fd3293f15 --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/question.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "question.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/question.imageset/question.pdf b/Bitkit/Assets.xcassets/icons/question.imageset/question.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9aac3752295fe8eb142ed0a361a97dc783bdd32b GIT binary patch literal 6178 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 LL|NHvulfG~yE_^- literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/icons/stop-circle.imageset/Contents.json b/Bitkit/Assets.xcassets/icons/stop-circle.imageset/Contents.json new file mode 100644 index 000000000..0d8d7715c --- /dev/null +++ b/Bitkit/Assets.xcassets/icons/stop-circle.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "stop-circle.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Bitkit/Assets.xcassets/icons/stop-circle.imageset/stop-circle.pdf b/Bitkit/Assets.xcassets/icons/stop-circle.imageset/stop-circle.pdf new file mode 100644 index 0000000000000000000000000000000000000000..59047f7b65a8124dc698955b57680a7861b8352b GIT binary patch literal 5984 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 literal 0 HcmV?d00001 diff --git a/Bitkit/Assets.xcassets/synonym-logo.imageset/Contents.json b/Bitkit/Assets.xcassets/synonym-logo.imageset/Contents.json new file mode 100644 index 000000000..3336f23a9 --- /dev/null +++ b/Bitkit/Assets.xcassets/synonym-logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "synonym-logo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Bitkit/Assets.xcassets/synonym-logo.imageset/synonym-logo.png b/Bitkit/Assets.xcassets/synonym-logo.imageset/synonym-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1d871d71cd7166394f82fb3f39496f503c158fae GIT binary patch literal 3620 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: View { private let defaultActiveColor: Color @Namespace private var underlineNamespace - init(selectedTab: Binding, tabs: [T], activeColor: Color = .brandAccent) { + init(selectedTab: Binding, tabs: [T], activeColor: Color = .textPrimary) { _selectedTab = selectedTab tabItems = tabs.map { TabItem($0) } defaultActiveColor = activeColor } - init(selectedTab: Binding, tabItems: [TabItem], defaultActiveColor: Color = .brandAccent) { + init(selectedTab: Binding, tabItems: [TabItem], defaultActiveColor: Color = .textPrimary) { _selectedTab = selectedTab self.tabItems = tabItems self.defaultActiveColor = defaultActiveColor @@ -32,7 +32,7 @@ struct SegmentedControl: View { HStack(spacing: 8) { ForEach(tabItems, id: \.tab) { tabItem in Button(action: { - withAnimation(.spring(response: 0.35, dampingFraction: 0.8)) { + withAnimation(.easeInOut(duration: 0.2)) { selectedTab = tabItem.tab } }) { @@ -43,6 +43,7 @@ struct SegmentedControl: View { Rectangle() .frame(height: 2) .foregroundColor(Color.white64) + if selectedTab == tabItem.tab { Rectangle() .frame(height: 2) @@ -51,7 +52,7 @@ struct SegmentedControl: View { } } } - .frame(maxWidth: .infinity) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) .contentShape(Rectangle()) } .buttonStyle(PlainButtonStyle()) diff --git a/Bitkit/Components/SettingsListLabel.swift b/Bitkit/Components/SettingsRow.swift similarity index 69% rename from Bitkit/Components/SettingsListLabel.swift rename to Bitkit/Components/SettingsRow.swift index 38328a2bf..f3a11e3f6 100644 --- a/Bitkit/Components/SettingsListLabel.swift +++ b/Bitkit/Components/SettingsRow.swift @@ -1,15 +1,32 @@ import SwiftUI -enum SettingsListRightIcon { +/// Section header for settings screens +struct SettingsSectionHeader: View { + let title: String + + init(_ title: String) { + self.title = title + } + + var body: some View { + CaptionMText(title) + .frame(height: 50) + .frame(maxWidth: .infinity, alignment: .leading) + .accessibilityAddTraits(.isHeader) + } +} + +enum SettingsRowRightIcon { case chevron case checkmark } -struct SettingsListLabel: View { +struct SettingsRow: View { let title: String let iconName: String? + let iconColor: Color? let rightText: String? - let rightIcon: SettingsListRightIcon? + let rightIcon: SettingsRowRightIcon? let toggle: Binding? let disabled: Bool? let testIdentifier: String? @@ -17,14 +34,16 @@ struct SettingsListLabel: View { init( title: String, iconName: String? = nil, + iconColor: Color? = .brandAccent, rightText: String? = nil, - rightIcon: SettingsListRightIcon? = .chevron, + rightIcon: SettingsRowRightIcon? = .chevron, toggle: Binding? = nil, disabled: Bool? = nil, testIdentifier: String? = nil ) { self.title = title self.iconName = iconName + self.iconColor = iconColor self.rightText = rightText self.rightIcon = rightIcon self.toggle = toggle @@ -36,16 +55,12 @@ struct SettingsListLabel: View { VStack(spacing: 0) { HStack(alignment: .center, spacing: 0) { if let iconName { - Label { - BodyMText(title, textColor: .textPrimary) - } icon: { - CircularIcon(icon: iconName, iconColor: .textPrimary) - .padding(.trailing, 8) - } - } else { - BodyMText(title, textColor: .textPrimary) + CircularIcon(icon: iconName, iconColor: iconColor ?? .brandAccent, backgroundColor: .black) + .padding(.trailing, 8) } + BodyMText(title, textColor: .textPrimary) + Spacer() if let toggle { @@ -57,7 +72,7 @@ struct SettingsListLabel: View { } else { if let rightText { - BodyMText(rightText, textColor: .textPrimary) + BodyMText(rightText, textColor: .textSecondary) .padding(.trailing, 5) .accessibilityIdentifier("Value") } @@ -80,10 +95,7 @@ struct SettingsListLabel: View { } .frame(height: 50) - // Bottom border - Rectangle() - .fill(Color.white10) - .frame(height: 1) + CustomDivider() } } } diff --git a/Bitkit/Components/Social.swift b/Bitkit/Components/Social.swift index 63b873cf8..09f9082ab 100644 --- a/Bitkit/Components/Social.swift +++ b/Bitkit/Components/Social.swift @@ -3,11 +3,7 @@ import SwiftUI struct Social: View { @Environment(\.openURL) private var openURL - let backgroundColor: Color - - init(backgroundColor: Color = .white16) { - self.backgroundColor = backgroundColor - } + let backgroundColor: Color = .clear var body: some View { HStack { diff --git a/Bitkit/Extensions/String+Utilities.swift b/Bitkit/Extensions/String+Utilities.swift index 454f6efdf..2c14c562f 100644 --- a/Bitkit/Extensions/String+Utilities.swift +++ b/Bitkit/Extensions/String+Utilities.swift @@ -1,15 +1,31 @@ import Foundation extension String { - /// Truncates a string to a maximum length and adds ellipsis in the middle - /// - Parameter maxLength: The maximum length of the string - /// - Returns: The truncated string with ellipsis in the middle - func ellipsis(maxLength: Int) -> String { + enum EllipsisStyle { + /// Ellipsis in the middle: "ab...de" + case middle + /// Ellipsis at the end: "abcde..." + case end + } + + /// Truncates a string to a maximum length and adds ellipsis. + /// - Parameters: + /// - maxLength: The maximum length of the string + /// - style: `.middle` (default) keeps start and end with "..." in between; `.end` keeps prefix and "..." at the end + /// - Returns: The truncated string with ellipsis + func ellipsis(maxLength: Int, style: EllipsisStyle = .middle) -> String { if count <= maxLength { return self } - let start = prefix(maxLength / 2) - let end = suffix(maxLength / 2) - return "\(start)...\(end)" + + switch style { + case .middle: + let half = maxLength / 2 + let start = prefix(half) + let end = suffix(half) + return "\(start)...\(end)" + case .end: + return String(prefix(maxLength)) + "..." + } } } diff --git a/Bitkit/MainNavView.swift b/Bitkit/MainNavView.swift index f3f5725e6..d029eebf1 100644 --- a/Bitkit/MainNavView.swift +++ b/Bitkit/MainNavView.swift @@ -320,14 +320,8 @@ struct MainNavView: View { case let .widgetEdit(widgetType): WidgetEditView(id: widgetType) // Settings - case .settings: MainSettings() - case .generalSettings: GeneralSettingsView() - case .securitySettings: SecurityPrivacySettingsView() - case .backupSettings: BackupSettings() - case .advancedSettings: AdvancedSettingsView() - case .support: SupportView() - case .about: AboutView() - case .devSettings: DevSettingsView() + case .settings: MainSettingsScreen() + case .support: SupportScreen() // General settings case .languageSettings: LanguageSettingsScreen() @@ -338,16 +332,16 @@ struct MainNavView: View { case .quickpayIntro: QuickpayIntroView() case .customSpeedSettings: CustomSpeedView() case .tagSettings: TagSettingsView() - case .widgetsSettings: WidgetsSettingsView() + case .widgetsSettings: WidgetsSettingsScreen() case .notifications: NotificationsSettings() case .notificationsIntro: NotificationsIntro() // Security settings - case .disablePin: DisablePinView() - case .changePin: PinChangeView() + case .changePin: ChangePinScreen() // Backup settings - case .resetAndRestore: ResetAndRestore() + case .dataBackups: DataBackupsScreen() + case .reset: ResetScreen() // Support settings case .reportIssue: ReportIssue() @@ -363,9 +357,10 @@ struct MainNavView: View { case .electrumSettings: ElectrumSettingsScreen() case .rgsSettings: RgsSettingsScreen() case .addressViewer: AddressViewer() + case .devSettings: DevSettingsView() // Dev settings - case .blocktankRegtest: BlocktankRegtestView() + case .blocktankRegtest: BlocktankRegtestScreen() case .ldkDebug: LdkDebugScreen() case .vssDebug: VssDebugScreen() case .probingTool: ProbingToolScreen() diff --git a/Bitkit/Resources/Localization/en.lproj/Localizable.strings b/Bitkit/Resources/Localization/en.lproj/Localizable.strings index a508858dc..729fda157 100644 --- a/Bitkit/Resources/Localization/en.lproj/Localizable.strings +++ b/Bitkit/Resources/Localization/en.lproj/Localizable.strings @@ -532,6 +532,7 @@ "security__pin_not_match" = "Try again, this is not the same PIN."; "security__pin_disable_title" = "Disable PIN"; "security__pin_disable_text" = "PIN code is currently enabled. If you want to disable your PIN, you need to enter your current PIN code first."; +"security__pin_enable_button" = "Enable PIN"; "security__pin_disable_button" = "Disable PIN"; "security__pin_enter" = "Please enter your PIN code"; "security__pin_last_attempt" = "Last attempt. Entering the wrong PIN again will reset your wallet."; @@ -612,13 +613,10 @@ "settings__dev_disabled_title" = "Dev Options Disabled"; "settings__dev_disabled_message" = "Developer options are now disabled throughout the app."; "settings__general_title" = "General"; -"settings__security_title" = "Security and Privacy"; -"settings__backup_title" = "Back up or Restore"; +"settings__security_title" = "Security"; "settings__advanced_title" = "Advanced"; -"settings__about_title" = "About"; +"settings__data_backups_nav_title" = "Data Backups"; "settings__support_title" = "Support"; -"settings__about__title" = "About Bitkit"; -"settings__about__text" = "Thank you for being a responsible Bitcoiner.\nChange your wallet, change the world.\n\nBitkit hands you the keys to your money, profile, contacts, and web accounts.\n\nBitkit was crafted by Synonym Software Ltd."; "settings__about__legal" = "Legal"; "settings__about__share" = "Share"; "settings__about__version" = "Version"; @@ -637,7 +635,7 @@ "settings__general__denomination_label" = "Bitcoin denomination"; "settings__general__denomination_modern" = "Modern (₿ 10 000)"; "settings__general__denomination_classic" = "Classic (₿ 0.00010000)"; -"settings__general__speed" = "Transaction speed"; +"settings__general__speed" = "Transaction Speed"; "settings__general__speed_title" = "Transaction Speed"; "settings__general__speed_default" = "Default Transaction Speed"; "settings__general__speed_fee_custom" = "Set Custom Fee"; @@ -648,9 +646,17 @@ "settings__general__language" = "Language"; "settings__general__language_title" = "Language"; "settings__general__language_other" = "Interface language"; -"settings__widgets__nav_title" = "Widgets and Suggestions"; -"settings__widgets__showWidgets" = "Widgets and Suggestions"; +"settings__general__section_interface" = "Interface"; +"settings__general__section_payments" = "Payments"; +"settings__widgets__nav_title" = "Widgets"; +"settings__widgets__section_display" = "Display"; +"settings__widgets__section_reset" = "Reset To Defaults"; +"settings__widgets__showWidgets" = "Show Widgets"; "settings__widgets__showWidgetTitles" = "Show Widget Titles"; +"settings__widgets__reset_widgets" = "Reset Widgets"; +"settings__widgets__reset_widgets_dialog_title" = "Reset Widgets?"; +"settings__widgets__reset_widgets_dialog_description" = "Are you sure you want to reset the widgets? The default widget set with default configurations will be displayed."; +"settings__widgets__reset_suggestions" = "Reset Suggestions Cards"; "settings__notifications__nav_title" = "Background Payments"; "settings__notifications__intro__title" = "Get Paid\nPassively"; "settings__notifications__intro__text" = "Turn on notifications to get paid, even when your Bitkit app is closed."; @@ -682,17 +688,22 @@ "settings__security__warn_100" = "Warn when sending over $100"; "settings__security__pin" = "PIN Code"; "settings__security__pin_change" = "Change PIN Code"; +"settings__security__section_backup" = "Back up or reset"; +"settings__security__section_privacy" = "Privacy"; +"settings__security__section_safety" = "Safety"; +"settings__security__section_pin" = "PIN Code"; "settings__security__pin_launch" = "Require PIN on launch"; "settings__security__pin_idle" = "Require PIN when idle"; "settings__security__pin_payments" = "Require PIN for payments"; "settings__security__pin_enabled" = "Enabled"; "settings__security__pin_disabled" = "Disabled"; -"settings__security__use_bio" = "Use {biometryTypeName} instead"; +"settings__security__use_bio" = "{biometryTypeName} instead of PIN"; "settings__security__footer" = "When enabled, you can use {biometryTypeName} instead of your PIN code to unlock your wallet or send payments."; -"settings__backup__title" = "Back Up Or Restore"; +"settings__reset_nav_title" = "Reset"; "settings__backup__wallet" = "Back up your wallet"; -"settings__backup__export" = "Export wallet data to phone"; -"settings__backup__reset" = "Reset and restore wallet"; +"settings__backup__data" = "Data backups"; +"settings__backup__export" = "Export wallet data"; +"settings__backup__reset" = "Reset wallet"; "settings__backup__failed_title" = "Data Backup Failure"; "settings__backup__failed_message" = "Bitkit failed to back up wallet data. Retrying in {interval, plural, one {# minute} other {# minutes}}."; "settings__backup__latest" = "latest data backups"; @@ -708,7 +719,7 @@ "settings__backup__category_profile" = "Profile"; "settings__backup__category_contacts" = "Contacts"; "settings__support__title" = "Support"; -"settings__support__text" = "Need help? Report your issue from within Bitkit, visit the help center, check the status, or reach out to us on our social channels."; +"settings__support__text" = "Need help? Report your issue from within Bitkit or visit our help center."; "settings__support__report" = "Report Issue"; "settings__support__help" = "Help Center"; "settings__support__status" = "App Status"; @@ -745,9 +756,10 @@ "settings__status__backup__ready" = "Backed up"; "settings__status__backup__pending" = "Backing up..."; "settings__status__backup__error" = "Failed to complete a full backup"; +"settings__adv__section_debug" = "Debug"; "settings__adv__section_payments" = "Payments"; "settings__adv__section_networks" = "Networks"; -"settings__adv__section_other" = "Other"; +"settings__adv__address_type_title" = "Address Type"; "settings__adv__address_type" = "Bitcoin Address Type"; "settings__adv__monitored_address_types" = "Monitored Address Types"; "settings__adv__addr_type_failed_title" = "Failed"; @@ -967,6 +979,7 @@ "wallet__drawer__widgets" = "Widgets"; "wallet__drawer__shop" = "Shop"; "wallet__drawer__settings" = "Settings"; +"wallet__drawer__support" = "Support"; "wallet__drawer__status" = "App Status"; "wallet__send" = "Send"; "wallet__receive" = "Receive"; diff --git a/Bitkit/ViewModels/NavigationViewModel.swift b/Bitkit/ViewModels/NavigationViewModel.swift index b43a79bc0..942182348 100644 --- a/Bitkit/ViewModels/NavigationViewModel.swift +++ b/Bitkit/ViewModels/NavigationViewModel.swift @@ -33,6 +33,7 @@ enum Route: Hashable { case savingsAdvanced case savingsProgress case scanner + case support // Shop case shopIntro @@ -46,37 +47,33 @@ enum Route: Hashable { case widgetDetail(WidgetType) case widgetEdit(WidgetType) - // Main Settings - case settings - case generalSettings - case securitySettings - case backupSettings - case advancedSettings - case support - case about - case devSettings + // Support + case reportIssue + case appStatus - // General settings + // Settings + // General/Interface + case settings case languageSettings case currencySettings case unitSettings - case transactionSpeedSettings - case customSpeedSettings case tagSettings case widgetsSettings + + // General/Payments + case transactionSpeedSettings + case customSpeedSettings case quickpay case quickpayIntro case notifications case notificationsIntro - // Security settings - case disablePin + // Security + case dataBackups + case reset case changePin - /// Backup settings - case resetAndRestore - - // Advanced settings + // Advanced/Payments case coinSelection case addressTypePreference case connections @@ -86,10 +83,7 @@ enum Route: Hashable { case electrumSettings case rgsSettings case addressViewer - - // Support settings - case reportIssue - case appStatus + case devSettings // Dev settings case blocktankRegtest diff --git a/Bitkit/Views/Settings/AboutView.swift b/Bitkit/Views/Settings/AboutView.swift deleted file mode 100644 index a8d5c8766..000000000 --- a/Bitkit/Views/Settings/AboutView.swift +++ /dev/null @@ -1,104 +0,0 @@ -import SwiftUI - -struct DiagonalCut: Shape { - func path(in rect: CGRect) -> Path { - var path = Path() - - let leftCutX = rect.maxX * 0.15 - path.move(to: CGPoint(x: leftCutX, y: rect.maxY)) - - let topCutY = rect.maxY * 0.63 - path.addLine(to: CGPoint(x: rect.maxX, y: topCutY)) - - // Line to the top-right corner - path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY)) - // Line to the bottom-right corner - path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) - // Line to the bottom-left corner - path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) - // Close the path back to the starting point - path.closeSubpath() - - return path - } -} - -struct AboutView: View { - @Environment(\.openURL) private var openURL - - private var appVersion: String { - let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown" - let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown" - return "\(version) (\(build))" - } - - private var shareText: String { - return t( - "settings__about__shareText", - variables: ["appStoreUrl": Env.appStoreUrl, "playStoreUrl": Env.playStoreUrl] - ) - } - - var body: some View { - ZStack { - // Orange diagonal background - Color.brandAccent - .clipShape(DiagonalCut()) - .ignoresSafeArea() - - VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("settings__about__title")) - .padding(.bottom, 16) - - BodyMText(t("settings__about__text")) - .padding(.vertical, 16) - - VStack(spacing: 0) { - Button(action: { - openURL(URL(string: Env.termsOfServiceUrl)!) - }) { - SettingsListLabel(title: t("settings__about__legal")) - } - - ShareLink(item: shareText, message: Text(shareText)) { - SettingsListLabel(title: t("settings__about__share")) - } - - Button(action: { - openURL(URL(string: Env.githubReleasesUrl)!) - }) { - SettingsListLabel( - title: t("settings__about__version"), - rightText: appVersion, - rightIcon: nil - ) - } - } - - Spacer(minLength: 32) - - VStack(alignment: .center, spacing: 0) { - Image("logo") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxHeight: 82) - .accessibilityIdentifier("AboutLogo") - } - .frame(maxWidth: .infinity) - .padding(.bottom, 32) - - Social(backgroundColor: .clear) - } - .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() - } - } -} - -#Preview { - NavigationView { - AboutView() - } - .preferredColorScheme(.dark) -} diff --git a/Bitkit/Views/Settings/Advanced/AdvancedSettingsView.swift b/Bitkit/Views/Settings/Advanced/AdvancedSettingsView.swift index c693806d4..5d24873fd 100644 --- a/Bitkit/Views/Settings/Advanced/AdvancedSettingsView.swift +++ b/Bitkit/Views/Settings/Advanced/AdvancedSettingsView.swift @@ -1,113 +1,100 @@ import SwiftUI struct AdvancedSettingsView: View { - @EnvironmentObject var navigation: NavigationViewModel - @EnvironmentObject var suggestionsManager: SuggestionsManager - @EnvironmentObject var settings: SettingsViewModel - @State private var showingResetAlert = false + @EnvironmentObject private var settings: SettingsViewModel + @EnvironmentObject private var wallet: WalletViewModel + + @AppStorage("showDevSettings") private var showDevSettings = Env.isDebug var body: some View { VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("settings__advanced_title")) - .padding(.bottom, 16) - ScrollView(showsIndicators: false) { - VStack(alignment: .leading, spacing: 16) { - // PAYMENTS Section - VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__adv__section_payments")) - .padding(.bottom, 8) - - NavigationLink(value: Route.addressTypePreference) { - SettingsListLabel( - title: t("settings__adv__address_type"), - rightText: settings.selectedAddressType.localizedTitle + VStack(alignment: .leading, spacing: 0) { + // Debug Section + if showDevSettings { + SettingsSectionHeader(t("settings__adv__section_debug")) + + NavigationLink(value: Route.devSettings) { + SettingsRow( + title: t("settings__dev_title"), + iconName: "game-controller" ) } - .accessibilityIdentifier("AddressTypePreference") - - NavigationLink(value: Route.coinSelection) { - SettingsListLabel(title: t("settings__adv__coin_selection")) - } - .accessibilityIdentifier("CoinSelectPreference") + .padding(.bottom, 16) + .accessibilityIdentifier("DevSettings") + } - // NavigationLink(destination: Text("Coming soon")) { - // SettingsListLabel(title: t("settings__adv__payment_preference")) - // } + // Payments section + SettingsSectionHeader(t("settings__adv__section_payments")) - // NavigationLink(destination: Text("Coming soon")) { - // SettingsListLabel(title: t("settings__adv__gap_limit")) - // } + NavigationLink(value: Route.addressTypePreference) { + SettingsRow( + title: t("settings__adv__address_type_title"), + iconName: "list-dashes", + rightText: settings.selectedAddressType.localizedTitle + ) } + .accessibilityIdentifier("AddressTypePreference") - // NETWORKS Section - VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__adv__section_networks")) - .padding(.top, 24) - .padding(.bottom, 8) - - NavigationLink(value: Route.connections) { - SettingsListLabel(title: t("settings__adv__lightning_connections")) - } - .accessibilityIdentifier("Channels") + NavigationLink(value: Route.coinSelection) { + SettingsRow( + title: t("settings__adv__coin_selection"), + iconName: "coins", + rightText: settings.coinSelectionMethod.localizedTitle + ) + } + .accessibilityIdentifier("CoinSelectPreference") - NavigationLink(value: Route.node) { - SettingsListLabel(title: t("settings__adv__lightning_node")) - } - .accessibilityIdentifier("LightningNodeInfo") + NavigationLink(value: Route.addressViewer) { + SettingsRow( + title: t("settings__adv__address_viewer"), + iconName: "eye" + ) + } + .accessibilityIdentifier("AddressViewer") - NavigationLink(value: Route.electrumSettings) { - SettingsListLabel(title: t("settings__adv__electrum_server")) - } - .accessibilityIdentifier("ElectrumConfig") + // Networks section + SettingsSectionHeader(t("settings__adv__section_networks")) + .padding(.top, 16) - NavigationLink(value: Route.rgsSettings) { - SettingsListLabel(title: t("settings__adv__rgs_server")) - } - .accessibilityIdentifier("RGSServer") + NavigationLink(value: Route.connections) { + SettingsRow( + title: t("settings__adv__lightning_connections"), + iconName: "bolt-hollow", + rightText: String(wallet.channels?.count ?? 0) + ) } + .accessibilityIdentifier("Channels") - // OTHER Section - VStack(alignment: .leading, spacing: 0) { - CaptionMText( - t("settings__adv__section_other") + NavigationLink(value: Route.node) { + SettingsRow( + title: t("settings__adv__lightning_node"), + iconName: "git-branch", + rightText: wallet.nodeId?.ellipsis(maxLength: 5, style: .end) ) - .padding(.top, 24) - .padding(.bottom, 8) - - NavigationLink(value: Route.addressViewer) { - SettingsListLabel(title: t("settings__adv__address_viewer")) - } - .accessibilityIdentifier("AddressViewer") - - // SettingsListLabel(title: t("settings__adv__rescan"), rightIcon: nil) + } + .accessibilityIdentifier("LightningNodeInfo") - Button(action: { - showingResetAlert = true - }) { - SettingsListLabel(title: t("settings__adv__suggestions_reset")) - } - .accessibilityIdentifier("ResetSuggestions") + NavigationLink(value: Route.electrumSettings) { + SettingsRow( + title: t("settings__adv__electrum_server"), + iconName: "hard-drives" + ) + } + .accessibilityIdentifier("ElectrumConfig") - Spacer() + NavigationLink(value: Route.rgsSettings) { + SettingsRow( + title: t("settings__adv__rgs_server"), + iconName: "broadcast" + ) } + .accessibilityIdentifier("RGSServer") } + .padding(.top, 16) + .padding(.horizontal, 16) + .bottomSafeAreaPadding() } } - .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() - .alert(t("settings__adv__reset_title"), isPresented: $showingResetAlert) { - Button(t("settings__adv__reset_confirm"), role: .destructive) { - suggestionsManager.resetDismissed() - navigation.reset() - } - .accessibilityIdentifier("DialogConfirm") - - Button(t("common__dialog_cancel"), role: .cancel) {} - .accessibilityIdentifier("DialogCancel") - } message: { - Text(t("settings__adv__reset_desc")) - } } } diff --git a/Bitkit/Views/Settings/Advanced/CoinSelectionSettingsView.swift b/Bitkit/Views/Settings/Advanced/CoinSelectionSettingsView.swift index b2587c31a..7a32a9b59 100644 --- a/Bitkit/Views/Settings/Advanced/CoinSelectionSettingsView.swift +++ b/Bitkit/Views/Settings/Advanced/CoinSelectionSettingsView.swift @@ -117,14 +117,12 @@ struct CoinSelectionSettingsView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { NavigationBar(title: t("settings__adv__coin_selection")) - .padding(.bottom, 16) ScrollView(showsIndicators: false) { - VStack(spacing: 0) { + VStack(spacing: 32) { // COIN SELECTION METHOD Section VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__adv__cs_method")) - .padding(.bottom, 8) + SettingsSectionHeader(t("settings__adv__cs_method")) VStack(spacing: 0) { ForEach(CoinSelectionMethod.allCases, id: \.self) { method in @@ -136,9 +134,7 @@ struct CoinSelectionSettingsView: View { settingsViewModel.coinSelectionMethod = method } - if method != CoinSelectionMethod.allCases.last { - Divider() - } + CustomDivider() } } } @@ -147,9 +143,7 @@ struct CoinSelectionSettingsView: View { // AUTOPILOT MODE Section (only show if Autopilot is selected) if settingsViewModel.coinSelectionMethod == .autopilot { VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__adv__cs_auto_mode")) - .padding(.top, 24) - .padding(.bottom, 8) + SettingsSectionHeader(t("settings__adv__cs_auto_mode")) VStack(spacing: 0) { ForEach(CoinSelectionAlgorithm.supportedAlgorithms, id: \.self) { algorithm in @@ -165,16 +159,12 @@ struct CoinSelectionSettingsView: View { } } } - - // Add spacing at the bottom - Spacer() - .frame(height: 32) } + .padding(.horizontal, 16) + .bottomSafeAreaPadding() } } .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() } } diff --git a/Bitkit/Views/Settings/Advanced/LightningConnectionDetailView.swift b/Bitkit/Views/Settings/Advanced/LightningConnectionDetailView.swift index 661cce2f3..f2b7aff4c 100644 --- a/Bitkit/Views/Settings/Advanced/LightningConnectionDetailView.swift +++ b/Bitkit/Views/Settings/Advanced/LightningConnectionDetailView.swift @@ -66,12 +66,12 @@ struct LightningConnectionDetailView: View { ) .padding(.bottom, 28) - VStack(alignment: .leading, spacing: 32) { + VStack(alignment: .leading, spacing: 16) { // STATUS Section - VStack(alignment: .leading, spacing: 16) { - Divider() + VStack(alignment: .leading, spacing: 0) { + CustomDivider() - CaptionMText(t("lightning__status")) + SettingsSectionHeader(t("lightning__status")) HStack(alignment: .center, spacing: 8) { let status = detailedStatus(for: channel) @@ -84,15 +84,15 @@ struct LightningConnectionDetailView: View { BodyMSBText(status.text, textColor: status.color) } + .padding(.bottom, 16) - Divider() + CustomDivider() } // ORDER DETAILS Section if let order = channelDetails.linkedOrder { VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("lightning__order_details")) - .padding(.bottom, 16) + SettingsSectionHeader(t("lightning__order_details")) DetailRow(label: t("lightning__order"), value: order.id) @@ -116,8 +116,7 @@ struct LightningConnectionDetailView: View { // BALANCE Section VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("lightning__balance")) - .padding(.bottom, 16) + SettingsSectionHeader(t("lightning__balance")) DetailRowWithAmount( label: t("lightning__receiving_label"), @@ -140,49 +139,45 @@ struct LightningConnectionDetailView: View { // FEES Section VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("lightning__fees")) - .padding(.bottom, 16) - + SettingsSectionHeader(t("lightning__fees")) DetailRowWithAmount(label: t("lightning__base_fee"), amount: UInt64(channel.forwardingFeeBaseMsat / 1000)) DetailRow(label: t("lightning__fee_rate"), value: "\(channel.forwardingFeeProportionalMillionths) ppm") } // OTHER Section - VStack(alignment: .leading, spacing: 16) { - CaptionMText(t("lightning__other")) - - VStack(spacing: 0) { - DetailRow( - label: t("lightning__is_usable"), - value: channel.isUsable ? t("common__yes") : t("common__no"), - valueTestId: channel.isUsable ? "IsUsableYes" : "IsUsableNo" - ) + VStack(alignment: .leading, spacing: 0) { + SettingsSectionHeader(t("lightning__other")) - // TODO: Add channel opening date - // if let formattedDate = formatDate(channel.fundingTxo) { - // DetailRow(label: t("lightning__opened_on"), value: formattedDate) - // } + DetailRow( + label: t("lightning__is_usable"), + value: channel.isUsable ? t("common__yes") : t("common__no"), + valueTestId: channel.isUsable ? "IsUsableYes" : "IsUsableNo" + ) - if let closedAt = channel.displayedClosedAt { - if let formattedCloseDate = formatDate(closedAt) { - DetailRow(label: t("lightning__closed_on"), value: formattedCloseDate) - } + // TODO: Add channel opening date + // if let formattedDate = formatDate(channel.fundingTxo) { + // DetailRow(label: t("lightning__opened_on"), value: formattedDate) + // } + + if let closedAt = channel.displayedClosedAt { + if let formattedCloseDate = formatDate(closedAt) { + DetailRow(label: t("lightning__closed_on"), value: formattedCloseDate) } + } - DetailRow(label: t("lightning__channel_id"), value: channel.channelIdString) + DetailRow(label: t("lightning__channel_id"), value: channel.channelIdString) - if let txid = channel.displayedFundingTxoTxid, let vout = channel.fundingTxoVout { - DetailRow(label: t("lightning__channel_point"), value: "\(txid):\(vout)") - } + if let txid = channel.displayedFundingTxoTxid, let vout = channel.fundingTxoVout { + DetailRow(label: t("lightning__channel_point"), value: "\(txid):\(vout)") + } - DetailRow( - label: t("lightning__channel_node_id"), - value: channel.counterpartyNodeIdString - ) + DetailRow( + label: t("lightning__channel_node_id"), + value: channel.counterpartyNodeIdString + ) - if let reason = channel.closureReason { - DetailRow(label: t("lightning__closure_reason"), value: reason) - } + if let reason = channel.closureReason { + DetailRow(label: t("lightning__closure_reason"), value: reason) } } } @@ -352,7 +347,7 @@ struct LightningConnectionDetailView: View { .frame(height: 50) } - Divider() + CustomDivider() } .frame(height: 51) } @@ -371,7 +366,7 @@ struct LightningConnectionDetailView: View { .frame(height: 50) } - Divider() + CustomDivider() } .frame(height: 51) } diff --git a/Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift b/Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift index 318783829..1cd390ad2 100644 --- a/Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift +++ b/Bitkit/Views/Settings/Advanced/LightningConnectionsView.swift @@ -61,14 +61,13 @@ struct LightningConnectionsView: View { } .padding(.bottom, 16) - Divider() + CustomDivider() // Pending Connections section if !pendingConnections.isEmpty { - VStack(alignment: .leading, spacing: 16) { - CaptionMText(t("lightning__conn_pending")) - .padding(.top, 16) + SettingsSectionHeader(t("lightning__conn_pending")) + VStack(alignment: .leading, spacing: 16) { ForEach(Array(pendingConnections.enumerated()), id: \.element.channelId) { index, channel in let labelIndex = pendingConnections.count - index Button { @@ -93,7 +92,7 @@ struct LightningConnectionsView: View { ) .padding(.bottom, 16) - Divider() + CustomDivider() } } .buttonStyle(PlainButtonStyle()) @@ -105,10 +104,9 @@ struct LightningConnectionsView: View { // Open Connections section if !openChannels.isEmpty { - VStack(alignment: .leading, spacing: 16) { - CaptionMText(t("lightning__conn_open")) - .padding(.top, 16) + SettingsSectionHeader(t("lightning__conn_open")) + VStack(alignment: .leading, spacing: 16) { ForEach(Array(openChannels.enumerated()), id: \.element.channelId) { index, channel in let labelIndex = openChannels.count - index Button { @@ -133,7 +131,7 @@ struct LightningConnectionsView: View { ) .padding(.bottom, 16) - Divider() + CustomDivider() } .opacity((!channel.isChannelReady || !channel.isUsable) ? 0.64 : 1.0) } @@ -144,10 +142,9 @@ struct LightningConnectionsView: View { // Closed Connections section if showClosedConnections && !closedChannels.isEmpty { - VStack(alignment: .leading, spacing: 16) { - CaptionMText(t("lightning__conn_closed")) - .padding(.top, 16) + SettingsSectionHeader(t("lightning__conn_closed")) + VStack(alignment: .leading, spacing: 16) { ForEach(Array(closedChannels.enumerated()), id: \.element.channelId) { index, channel in let labelIndex = closedChannels.count - index Button { @@ -172,7 +169,7 @@ struct LightningConnectionsView: View { ) .padding(.bottom, 16) - Divider() + CustomDivider() } .opacity(0.64) } @@ -196,7 +193,6 @@ struct LightningConnectionsView: View { } Spacer() - // .frame(height: 32) HStack(spacing: 16) { CustomButton(title: t("lightning__conn_button_export_logs"), variant: .secondary) { diff --git a/Bitkit/Views/Settings/AppStatusView.swift b/Bitkit/Views/Settings/AppStatusView.swift index 7cc129760..daa2e0699 100644 --- a/Bitkit/Views/Settings/AppStatusView.swift +++ b/Bitkit/Views/Settings/AppStatusView.swift @@ -159,7 +159,7 @@ struct AppStatusView: View { description: description, status: status, onTap: { - navigation.navigate(.backupSettings) + navigation.navigate(.dataBackups) } ) .accessibilityIdentifier("Status-backup") diff --git a/Bitkit/Views/Settings/BlocktankRegtestView.swift b/Bitkit/Views/Settings/BlocktankRegtestView.swift index 53176c339..130ed77b3 100644 --- a/Bitkit/Views/Settings/BlocktankRegtestView.swift +++ b/Bitkit/Views/Settings/BlocktankRegtestView.swift @@ -1,57 +1,41 @@ import SwiftUI -struct RegtestButton: View { - let title: String - let action: () async throws -> Void - - @EnvironmentObject var app: AppViewModel - @State private var isLoading = false - - var body: some View { - Button(isLoading ? "Loading..." : title) { - isLoading = true - Task { - do { - try await action() - } catch { - Logger.error("Regtest action failed: \(error.localizedDescription)", context: "BlocktankRegtestView") - app.toast(type: .error, title: "Regtest action failed: \(error.localizedDescription)") - } - isLoading = false - } - } - .disabled(isLoading) - .opacity(isLoading ? 0.5 : 1) - } -} - -struct BlocktankRegtestView: View { +struct BlocktankRegtestScreen: View { @EnvironmentObject var app: AppViewModel @EnvironmentObject var wallet: WalletViewModel + @State private var result: String = "" - @State private var mineBlockCount: String = "1" + @State private var selectedMineBlockCount: Int = 1 @State private var depositAmount: String = "100000" @State private var depositAddress: String = "" @State private var paymentInvoice: String = "" @State private var paymentAmount: String = "" @State private var forceCloseAfterSeconds: String = "" @State private var showingResult = false + @State private var isDepositLoading = false + @State private var isMiningLoading = false + @State private var isPayInvoiceLoading = false + @State private var isClosingChannelLoading = false + + private let mineBlockOptions = [1, 3, 20, 144] var body: some View { VStack(alignment: .leading, spacing: 0) { NavigationBar(title: "Blocktank Regtest") .padding(.horizontal, 16) - List { - serverInfoSection - depositSection - miningSection - lightningPaymentSection - channelCloseSection + ScrollView(showsIndicators: false) { + VStack(alignment: .leading, spacing: 16) { + depositSection + miningSection + lightningPaymentSection + channelCloseSection + } + .padding(.horizontal, 16) + .bottomSafeAreaPadding() } } .navigationBarHidden(true) - .bottomSafeAreaPadding() .onAppear { // Generate a fresh address when the view appears Task { @@ -66,190 +50,207 @@ struct BlocktankRegtestView: View { } } - var serverInfoSection: some View { - Section { - Text(Env.blocktankClientServer) - } footer: { - Text("These actions are executed on the staging Blocktank server node.") - } - } - var depositSection: some View { - Section { - HStack { - TextField("Address", text: $depositAddress) - .lineLimit(1) - .truncationMode(.middle) + VStack(alignment: .leading, spacing: 0) { + SettingsSectionHeader("Deposit") - Button { - if let string = UIPasteboard.general.string { - depositAddress = string - } - } label: { - Image(systemName: "doc.on.clipboard") - } + VStack(alignment: .leading, spacing: 8) { + HStack { + TextField("Address", text: $depositAddress) + .lineLimit(1) + .truncationMode(.middle) - Button { - Task { - do { - let newAddress = try await LightningService.shared.newAddress() - depositAddress = newAddress - } catch { - app.toast(type: .error, title: "Failed to generate address", description: error.localizedDescription) + Button { + if let string = UIPasteboard.general.string { + depositAddress = string } + } label: { + Image(systemName: "doc.on.clipboard") } - } label: { - Image(systemName: "arrow.clockwise") - } - } - - TextField("Amount (sats)", text: $depositAmount) - .keyboardType(.numberPad) - RegtestButton(title: "Make Deposit") { - Logger.debug("Initiating regtest deposit with amount: \(depositAmount)", context: "BlocktankRegtestView") - guard let amount = UInt64(depositAmount) else { - Logger.error("Invalid deposit amount: \(depositAmount)", context: "BlocktankRegtestView") - throw ValidationError("Invalid amount") + Button { + Task { + do { + let newAddress = try await LightningService.shared.newAddress() + depositAddress = newAddress + } catch { + app.toast(type: .error, title: "Failed to generate address", description: error.localizedDescription) + } + } + } label: { + Image(systemName: "arrow.clockwise") + } } - // Generate a new address for each deposit - let newAddress = try await LightningService.shared.newAddress() - Logger.debug("Generated new address for deposit: \(newAddress)", context: "BlocktankRegtestView") - - let txId = try await CoreService.shared.blocktank.regtestDepositFunds( - address: newAddress, - amountSat: amount - ) - Logger.debug("Deposit successful with txId: \(txId)", context: "BlocktankRegtestView") - app.toast(type: .success, title: "Success", description: "Deposit successful. TxID: \(txId)") + TextField("Amount (sats)", text: $depositAmount) + .keyboardType(.numberPad) - // Update the displayed address to the new one - depositAddress = newAddress + CustomButton(title: "Deposit", size: .small, isDisabled: depositAmount.isEmpty, isLoading: isDepositLoading) { + isDepositLoading = true + defer { isDepositLoading = false } + do { + Logger.debug("Initiating regtest deposit with amount: \(depositAmount)", context: "BlocktankRegtestScreen") + guard let amount = UInt64(depositAmount) else { + Logger.error("Invalid deposit amount: \(depositAmount)", context: "BlocktankRegtestScreen") + throw ValidationError("Invalid amount") + } - // Sync wallet after deposit without waiting - Task { - try? await wallet.sync() + let newAddress = try await LightningService.shared.newAddress() + Logger.debug("Generated new address for deposit: \(newAddress)", context: "BlocktankRegtestScreen") + + let txId = try await CoreService.shared.blocktank.regtestDepositFunds( + address: newAddress, + amountSat: amount + ) + Logger.debug("Deposit successful with txId: \(txId)", context: "BlocktankRegtestScreen") + app.toast(type: .success, title: "Success", description: "Deposit successful. TxID: \(txId)") + depositAddress = newAddress + Task { try? await wallet.sync() } + } catch { + Logger.error("Regtest action failed: \(error.localizedDescription)", context: "BlocktankRegtestScreen") + app.toast(type: .error, title: "Regtest action failed: \(error.localizedDescription)") + } } } - .disabled(depositAmount.isEmpty) - .tint(.orange) - } header: { - Text("Deposit") } } var miningSection: some View { - Section { - HStack { - TextField("Block count", text: $mineBlockCount) - .keyboardType(.numberPad) - - RegtestButton(title: "Mine Blocks") { - Logger.debug("Starting regtest mining with block count: \(mineBlockCount)", context: "BlocktankRegtestView") - guard let count = UInt32(mineBlockCount) else { - Logger.error("Invalid block count: \(mineBlockCount)", context: "BlocktankRegtestView") - throw ValidationError("Invalid block count") + VStack(alignment: .leading, spacing: 0) { + SettingsSectionHeader("Mining") + + VStack(alignment: .leading, spacing: 8) { + HStack(spacing: 8) { + ForEach(mineBlockOptions, id: \.self) { count in + Button { + selectedMineBlockCount = count + } label: { + BodyMSBText("\(count)", textColor: selectedMineBlockCount == count ? .white : .textSecondary) + .frame(maxWidth: .infinity) + .padding(.vertical, 10) + .background(selectedMineBlockCount == count ? Color.brandAccent : Color.white10) + .cornerRadius(8) + } + .buttonStyle(PlainButtonStyle()) } - try await CoreService.shared.blocktank.regtestMineBlocks(count) - Logger.debug("Successfully mined \(count) blocks", context: "BlocktankRegtestView") - app.toast(type: .success, title: "Success", description: "Successfully mined \(count) blocks") + } - // Sync wallet after mining blocks without waiting - Task { - try? await wallet.sync() + CustomButton(title: "Mine Blocks", size: .small, isDisabled: selectedMineBlockCount == 0, isLoading: isMiningLoading) { + isMiningLoading = true + defer { isMiningLoading = false } + do { + let count = UInt32(selectedMineBlockCount) + Logger.debug("Starting regtest mining with block count: \(count)", context: "BlocktankRegtestScreen") + try await CoreService.shared.blocktank.regtestMineBlocks(count) + Logger.debug("Successfully mined \(count) blocks", context: "BlocktankRegtestScreen") + app.toast(type: .success, title: "Success", description: "Successfully mined \(count) blocks") + Task { try? await wallet.sync() } + } catch { + Logger.error("Regtest action failed: \(error.localizedDescription)", context: "BlocktankRegtestScreen") + app.toast(type: .error, title: "Regtest action failed: \(error.localizedDescription)") } } - .tint(.orange) } - } header: { - Text("Mining") } } var lightningPaymentSection: some View { - Section { - HStack { - TextField("Invoice", text: $paymentInvoice) + VStack(alignment: .leading, spacing: 0) { + SettingsSectionHeader("Lightning Payment") + + VStack(alignment: .leading, spacing: 8) { + HStack { + TextField("Invoice", text: $paymentInvoice) - Button { - if let string = UIPasteboard.general.string { - paymentInvoice = string + Button { + if let string = UIPasteboard.general.string { + paymentInvoice = string + } + } label: { + Image(systemName: "doc.on.clipboard") } - } label: { - Image(systemName: "doc.on.clipboard") } - } - TextField("Amount (optional, sats)", text: $paymentAmount) - .keyboardType(.numberPad) + TextField("Amount (optional, sats)", text: $paymentAmount) + .keyboardType(.numberPad) - RegtestButton(title: "Pay Invoice") { - Logger.debug("Initiating regtest payment with invoice: \(paymentInvoice), amount: \(paymentAmount)", context: "BlocktankRegtestView") - let amount = paymentAmount.isEmpty ? nil : UInt64(paymentAmount) ?? 0 - let paymentId = try await CoreService.shared.blocktank.regtestPayInvoice(paymentInvoice, amountSat: amount) - Logger.debug("Payment successful with ID: \(paymentId)", context: "BlocktankRegtestView") - app.toast(type: .success, title: "Success", description: "Payment successful. ID: \(paymentId)") + CustomButton(title: "Pay Invoice", size: .small, isDisabled: paymentInvoice.isEmpty, isLoading: isPayInvoiceLoading) { + isPayInvoiceLoading = true + defer { isPayInvoiceLoading = false } + do { + Logger.debug( + "Initiating regtest payment with invoice: \(paymentInvoice), amount: \(paymentAmount)", + context: "BlocktankRegtestScreen" + ) + let amount = paymentAmount.isEmpty ? nil : UInt64(paymentAmount) ?? 0 + let paymentId = try await CoreService.shared.blocktank.regtestPayInvoice(paymentInvoice, amountSat: amount) + Logger.debug("Payment successful with ID: \(paymentId)", context: "BlocktankRegtestScreen") + app.toast(type: .success, title: "Success", description: "Payment successful. ID: \(paymentId)") + } catch { + Logger.error("Regtest action failed: \(error.localizedDescription)", context: "BlocktankRegtestScreen") + app.toast(type: .error, title: "Regtest action failed: \(error.localizedDescription)") + } + } } - .disabled(paymentInvoice.isEmpty) - .tint(.orange) - } header: { - Text("Lightning Payment") } } var channelCloseSection: some View { - Section { - TextField("Force close after (seconds)", text: $forceCloseAfterSeconds) - .keyboardType(.numberPad) - - if let channels = wallet.channels, !channels.isEmpty { - ForEach(channels, id: \.channelId) { channel in - VStack(alignment: .leading) { - Text(channel.channelId) - .font(.caption) - - Text("Ready: \(channel.isChannelReady ? "✅" : "❌")") - Text("Usable: \(channel.isUsable ? "✅" : "❌")") - - RegtestButton(title: "Close This Channel") { - Logger.debug("Closing channel: \(channel.channelId)", context: "BlocktankRegtestView") - - let closeAfter = forceCloseAfterSeconds.isEmpty ? nil : UInt64(forceCloseAfterSeconds) + VStack(alignment: .leading, spacing: 0) { + SettingsSectionHeader("Channel close from Blocktank side") - let closingTxId = try await CoreService.shared.blocktank.regtestRemoteCloseChannel( - channel: channel, - forceCloseAfterSeconds: closeAfter - ) + VStack(alignment: .leading, spacing: 8) { + TextField("Force close after (seconds)", text: $forceCloseAfterSeconds) + .keyboardType(.numberPad) - Logger.debug("Channel closed successfully with txId: \(closingTxId)", context: "BlocktankRegtestView") - app.toast(type: .success, title: "Success", description: "Channel closed. Closing TxID: \(closingTxId)") + if let channels = wallet.channels, !channels.isEmpty { + ForEach(channels, id: \.channelId) { channel in + VStack(alignment: .leading) { + CaptionMText(channel.channelId, textColor: .textSecondary) + + HStack(spacing: 6) { + BodyMText("Ready:", textColor: .textPrimary) + Image(systemName: channel.isChannelReady ? "checkmark.circle.fill" : "xmark.circle.fill") + .font(.system(size: 17)) + .foregroundColor(channel.isChannelReady ? .greenAccent : .redAccent) + } + HStack(spacing: 6) { + BodyMText("Usable:", textColor: .textPrimary) + Image(systemName: channel.isUsable ? "checkmark.circle.fill" : "xmark.circle.fill") + .font(.system(size: 17)) + .foregroundColor(channel.isUsable ? .greenAccent : .redAccent) + } + + CustomButton(title: "Close This Channel", size: .small, isDisabled: false, isLoading: isClosingChannelLoading) { + isClosingChannelLoading = true + defer { isClosingChannelLoading = false } + do { + Logger.debug("Closing channel: \(channel.channelId)", context: "BlocktankRegtestScreen") + let closeAfter = forceCloseAfterSeconds.isEmpty ? nil : UInt64(forceCloseAfterSeconds) + let closingTxId = try await CoreService.shared.blocktank.regtestRemoteCloseChannel( + channel: channel, + forceCloseAfterSeconds: closeAfter + ) + Logger.debug("Channel closed successfully with txId: \(closingTxId)", context: "BlocktankRegtestScreen") + app.toast(type: .success, title: "Success", description: "Channel closed. Closing TxID: \(closingTxId)") + } catch { + Logger.error("Regtest action failed: \(error.localizedDescription)", context: "BlocktankRegtestScreen") + app.toast(type: .error, title: "Regtest action failed: \(error.localizedDescription)") + } + } + .padding(.top, 8) } - .tint(.red) - .padding(.top, 8) + .padding(.vertical, 4) } - .padding(.vertical, 4) + } else { + BodyMText("No channels available") + .padding(.vertical, 8) } - } else { - Text("No channels available") - .foregroundColor(.secondary) - .padding(.vertical, 8) } - } header: { - Text("Channel close from Blocktank side") } } } -#Preview { - NavigationStack { - BlocktankRegtestView() - .environmentObject(AppViewModel()) - .environmentObject(WalletViewModel()) - } - .preferredColorScheme(.dark) -} - private struct ValidationError: LocalizedError { let message: String diff --git a/Bitkit/Views/Settings/DevSettingsView.swift b/Bitkit/Views/Settings/DevSettingsView.swift index 73e4d2756..0e3544b1d 100644 --- a/Bitkit/Views/Settings/DevSettingsView.swift +++ b/Bitkit/Views/Settings/DevSettingsView.swift @@ -18,12 +18,12 @@ struct DevSettingsView: View { VStack(alignment: .leading, spacing: 0) { if Env.network == .regtest { NavigationLink(value: Route.blocktankRegtest) { - SettingsListLabel(title: "Blocktank Regtest") + SettingsRow(title: "Blocktank Regtest") } } if Env.network == .regtest { - SettingsListLabel( + SettingsRow( title: "Override Fees", rightIcon: nil, toggle: $feeEstimatesManager.devOverrideFeeEstimates @@ -31,19 +31,19 @@ struct DevSettingsView: View { } NavigationLink(value: Route.ldkDebug) { - SettingsListLabel(title: "LDK") + SettingsRow(title: "LDK") } NavigationLink(value: Route.vssDebug) { - SettingsListLabel(title: "VSS") + SettingsRow(title: "VSS") } NavigationLink(value: Route.probingTool) { - SettingsListLabel(title: "Probing Tool") + SettingsRow(title: "Probing Tool") } NavigationLink(value: Route.orders) { - SettingsListLabel(title: "Orders") + SettingsRow(title: "Orders") } Button { @@ -57,7 +57,7 @@ struct DevSettingsView: View { } } } label: { - SettingsListLabel(title: "Generate Test Activities", rightIcon: nil) + SettingsRow(title: "Generate Test Activities", rightIcon: nil) } Button { @@ -71,11 +71,11 @@ struct DevSettingsView: View { } } } label: { - SettingsListLabel(title: "Reset All Activities", rightIcon: nil) + SettingsRow(title: "Reset All Activities", rightIcon: nil) } NavigationLink(value: Route.logs) { - SettingsListLabel(title: "Show Logs") + SettingsRow(title: "Show Logs") } Button { @@ -100,7 +100,7 @@ struct DevSettingsView: View { } } } label: { - SettingsListLabel(title: "Export Logs", rightIcon: nil) + SettingsRow(title: "Export Logs", rightIcon: nil) } Button { @@ -117,13 +117,13 @@ struct DevSettingsView: View { } } } label: { - SettingsListLabel(title: "Test Push Notification", rightIcon: nil) + SettingsRow(title: "Test Push Notification", rightIcon: nil) } Button { fatalError("Simulate Crash") } label: { - SettingsListLabel(title: "Simulate Crash", rightIcon: nil) + SettingsRow(title: "Simulate Crash", rightIcon: nil) } Button { @@ -139,7 +139,7 @@ struct DevSettingsView: View { } } } label: { - SettingsListLabel(title: "Wipe Wallet", rightIcon: nil) + SettingsRow(title: "Wipe Wallet", rightIcon: nil) } } .padding(.horizontal, 16) diff --git a/Bitkit/Views/Settings/General/DefaultUnitSettingsView.swift b/Bitkit/Views/Settings/General/DefaultUnitSettingsView.swift index e5d2b53e4..6a8efe7f0 100644 --- a/Bitkit/Views/Settings/General/DefaultUnitSettingsView.swift +++ b/Bitkit/Views/Settings/General/DefaultUnitSettingsView.swift @@ -6,64 +6,61 @@ struct DefaultUnitSettingsView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { NavigationBar(title: t("settings__general__unit_title")) + .padding(.horizontal, 16) ScrollView(showsIndicators: false) { - VStack(alignment: .leading, spacing: 8) { - CaptionMText(t("settings__general__unit_display")) - .padding(.vertical, 16) - .frame(maxWidth: .infinity, alignment: .leading) + VStack(alignment: .leading, spacing: 0) { + SettingsSectionHeader(t("settings__general__unit_display")) Button(action: { currency.primaryDisplay = .bitcoin }) { - SettingsListLabel( + SettingsRow( title: t("settings__general__unit_bitcoin"), iconName: "b-unit", + iconColor: .textPrimary, rightIcon: currency.primaryDisplay == .bitcoin ? .checkmark : nil ) } - .buttonStyle(PlainButtonStyle()) if let rate = currency.convert(sats: 1)?.currency { Button(action: { currency.primaryDisplay = .fiat }) { - SettingsListLabel( + SettingsRow( title: rate, iconName: "globe", + iconColor: .textPrimary, rightIcon: currency.primaryDisplay == .fiat ? .checkmark : nil ) } - .buttonStyle(PlainButtonStyle()) } BodyMText(t("settings__general__unit_note", variables: ["currency": currency.selectedCurrency])) .padding(.vertical, 16) - } - VStack(alignment: .leading, spacing: 8) { - CaptionMText(t("settings__general__denomination_label")) - .padding(.vertical, 16) - .frame(maxWidth: .infinity, alignment: .leading) + CustomDivider() + + SettingsSectionHeader(t("settings__general__denomination_label")) + .padding(.top, 16) ForEach(BitcoinDisplayUnit.allCases, id: \.self) { unit in Button(action: { currency.displayUnit = unit }) { - SettingsListLabel( + SettingsRow( title: t("settings__general__denomination_\(unit.rawValue)"), rightIcon: currency.displayUnit == unit ? .checkmark : nil ) } - .buttonStyle(PlainButtonStyle()) .accessibilityIdentifier(unit.testIdentifier) } } + .padding(.horizontal, 16) + .bottomSafeAreaPadding() } } .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() } } diff --git a/Bitkit/Views/Settings/General/LanguageSettingsScreen.swift b/Bitkit/Views/Settings/General/LanguageSettingsScreen.swift index d6c0a4957..97d58429d 100644 --- a/Bitkit/Views/Settings/General/LanguageSettingsScreen.swift +++ b/Bitkit/Views/Settings/General/LanguageSettingsScreen.swift @@ -8,7 +8,7 @@ struct LanguageSettingsScreen: View { Button(action: { selectLanguage(language) }) { - SettingsListLabel( + SettingsRow( title: language.name, rightIcon: languageManager.currentLanguage.code == language.code ? .checkmark : nil ) @@ -26,22 +26,21 @@ struct LanguageSettingsScreen: View { var body: some View { VStack(alignment: .leading, spacing: 0) { NavigationBar(title: t("settings__general__language_title")) + .padding(.horizontal, 16) ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__general__language_other")) - .padding(.vertical, 16) - .frame(maxWidth: .infinity, alignment: .leading) + SettingsSectionHeader(t("settings__general__language_other")) ForEach(SupportedLanguage.allLanguages, id: \.id) { language in languageRow(language) } } + .padding(.horizontal, 16) + .bottomSafeAreaPadding() } } .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() .alert("Language Changed", isPresented: $showAlert) { Button("OK", role: .cancel) {} } message: { diff --git a/Bitkit/Views/Settings/General/LocalCurrencySettingsView.swift b/Bitkit/Views/Settings/General/LocalCurrencySettingsView.swift index d92f79e3f..a6144fa5a 100644 --- a/Bitkit/Views/Settings/General/LocalCurrencySettingsView.swift +++ b/Bitkit/Views/Settings/General/LocalCurrencySettingsView.swift @@ -2,6 +2,8 @@ import SwiftUI struct LocalCurrencySettingsView: View { @EnvironmentObject var currency: CurrencyViewModel + @EnvironmentObject var navigation: NavigationViewModel + @State private var searchText = "" private let mostUsedCurrencies = ["USD", "GBP", "CAD", "CNY", "EUR"] @@ -25,7 +27,7 @@ struct LocalCurrencySettingsView: View { } private func currencyRow(_ rate: FxRate) -> some View { - SettingsListLabel( + SettingsRow( title: "\(rate.quote) (\(rate.currencySymbol))", rightIcon: currency.selectedCurrency == rate.quote ? .checkmark : nil, testIdentifier: "Currency-\(rate.quote)" @@ -35,6 +37,7 @@ struct LocalCurrencySettingsView: View { currency.selectedCurrency = rate.quote Task { await currency.refresh() + navigation.navigateBack() } } .accessibilityAddTraits(.isButton) @@ -64,10 +67,7 @@ struct LocalCurrencySettingsView: View { ScrollView(showsIndicators: false) { if !availableMostUsed.isEmpty { VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__general__currency_most_used")) - .padding(.top, 16) - .padding(.bottom, 8) - .frame(maxWidth: .infinity, alignment: .leading) + SettingsSectionHeader(t("settings__general__currency_most_used")) ForEach(availableMostUsed, id: \.quote) { rate in currencyRow(rate) @@ -76,15 +76,13 @@ struct LocalCurrencySettingsView: View { } VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__general__currency_other")) - .padding(.top, 24) - .padding(.bottom, 8) - .frame(maxWidth: .infinity, alignment: .leading) + SettingsSectionHeader(t("settings__general__currency_other")) ForEach(otherCurrencies, id: \.quote) { rate in currencyRow(rate) } } + .padding(.top, 16) CaptionText(t("settings__general__currency_footer")) .padding(.top, 16) diff --git a/Bitkit/Views/Settings/General/TagSettingsView.swift b/Bitkit/Views/Settings/General/TagSettingsView.swift index e9601854e..61ec7bc61 100644 --- a/Bitkit/Views/Settings/General/TagSettingsView.swift +++ b/Bitkit/Views/Settings/General/TagSettingsView.swift @@ -1,18 +1,16 @@ import SwiftUI struct TagSettingsView: View { - @EnvironmentObject var app: AppViewModel @EnvironmentObject var tagManager: TagManager var body: some View { VStack(alignment: .leading, spacing: 0) { NavigationBar(title: t("settings__general__tags")) + .padding(.horizontal, 16) ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__general__tags_previously")) - .padding(.top, 24) - .padding(.bottom, 16) + SettingsSectionHeader(t("settings__general__tags_previously")) TagsListView( tags: tagManager.lastUsedTags, @@ -22,10 +20,10 @@ struct TagSettingsView: View { } ) } + .padding(.horizontal, 16) + .bottomSafeAreaPadding() } } .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() } } diff --git a/Bitkit/Views/Settings/General/WidgetsSettingsScreen.swift b/Bitkit/Views/Settings/General/WidgetsSettingsScreen.swift new file mode 100644 index 000000000..c60e42ca1 --- /dev/null +++ b/Bitkit/Views/Settings/General/WidgetsSettingsScreen.swift @@ -0,0 +1,82 @@ +import SwiftUI + +struct WidgetsSettingsScreen: View { + @EnvironmentObject var navigation: NavigationViewModel + @EnvironmentObject var settings: SettingsViewModel + @EnvironmentObject var suggestionsManager: SuggestionsManager + @EnvironmentObject var widgets: WidgetsViewModel + + @State private var showWidgetsResetAlert = false + @State private var showSuggestionsResetAlert = false + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + NavigationBar(title: t("settings__widgets__nav_title")) + .padding(.horizontal, 16) + + ScrollView(showsIndicators: false) { + VStack(alignment: .leading, spacing: 0) { + SettingsSectionHeader(t("settings__widgets__section_display")) + + SettingsRow( + title: t("settings__widgets__showWidgets"), + toggle: $settings.showWidgets + ) + + SettingsRow( + title: t("settings__widgets__showWidgetTitles"), + toggle: $settings.showWidgetTitles + ) + + SettingsSectionHeader(t("settings__widgets__section_reset")) + .padding(.top, 16) + + Button(action: { + showWidgetsResetAlert = true + }) { + SettingsRow( + title: t("settings__widgets__reset_widgets"), + iconName: "arrow-counter-clockwise" + ) + } + + Button(action: { + showSuggestionsResetAlert = true + }) { + SettingsRow( + title: t("settings__widgets__reset_suggestions"), + iconName: "arrow-counter-clockwise" + ) + } + } + .padding(.horizontal, 16) + .bottomSafeAreaPadding() + } + } + .navigationBarHidden(true) + .alert(t("settings__widgets__reset_widgets_dialog_title"), isPresented: $showWidgetsResetAlert) { + Button(t("settings__adv__reset_confirm"), role: .destructive) { + widgets.clearWidgets() + navigation.reset() + } + .accessibilityIdentifier("DialogConfirm") + + Button(t("common__dialog_cancel"), role: .cancel) {} + .accessibilityIdentifier("DialogCancel") + } message: { + Text(t("settings__widgets__reset_widgets_dialog_description")) + } + .alert(t("settings__adv__reset_title"), isPresented: $showSuggestionsResetAlert) { + Button(t("settings__adv__reset_confirm"), role: .destructive) { + suggestionsManager.resetDismissed() + navigation.reset() + } + .accessibilityIdentifier("DialogConfirm") + + Button(t("common__dialog_cancel"), role: .cancel) {} + .accessibilityIdentifier("DialogCancel") + } message: { + Text(t("settings__adv__reset_desc")) + } + } +} diff --git a/Bitkit/Views/Settings/General/WidgetsSettingsView.swift b/Bitkit/Views/Settings/General/WidgetsSettingsView.swift deleted file mode 100644 index 30e5a99b2..000000000 --- a/Bitkit/Views/Settings/General/WidgetsSettingsView.swift +++ /dev/null @@ -1,37 +0,0 @@ -import SwiftUI - -struct WidgetsSettingsView: View { - @EnvironmentObject var settings: SettingsViewModel - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("settings__widgets__nav_title")) - .padding(.horizontal, 16) - - ScrollView(showsIndicators: false) { - VStack(alignment: .leading, spacing: 0) { - SettingsListLabel( - title: t("settings__widgets__showWidgets"), - toggle: $settings.showWidgets - ) - - SettingsListLabel( - title: t("settings__widgets__showWidgetTitles"), - toggle: $settings.showWidgetTitles - ) - } - .padding(.horizontal, 16) - .bottomSafeAreaPadding() - } - } - .navigationBarHidden(true) - } -} - -#Preview { - NavigationView { - WidgetsSettingsView() - .environmentObject(SettingsViewModel.shared) - } - .preferredColorScheme(.dark) -} diff --git a/Bitkit/Views/Settings/GeneralSettingsView.swift b/Bitkit/Views/Settings/GeneralSettingsView.swift index 454d14e84..ccbc84f53 100644 --- a/Bitkit/Views/Settings/GeneralSettingsView.swift +++ b/Bitkit/Views/Settings/GeneralSettingsView.swift @@ -8,82 +8,93 @@ struct GeneralSettingsView: View { @StateObject private var languageManager = LanguageManager.shared var body: some View { - VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("settings__general_title")) + ScrollView(showsIndicators: false) { + VStack(alignment: .leading, spacing: 0) { + // Interface section + SettingsSectionHeader(t("settings__general__section_interface")) - ScrollView(showsIndicators: false) { - VStack(alignment: .leading, spacing: 0) { - NavigationLink(value: Route.languageSettings) { - SettingsListLabel( - title: t("settings__general__language"), - rightText: languageManager.currentLanguageDisplayName - ) - } + NavigationLink(value: Route.languageSettings) { + SettingsRow( + title: t("settings__general__language"), + iconName: "translate", + rightText: languageManager.currentLanguageDisplayName + ) + } - NavigationLink(value: Route.currencySettings) { - SettingsListLabel( - title: t("settings__general__currency_local"), - rightText: currency.selectedCurrency - ) - } - .accessibilityIdentifier("CurrenciesSettings") + NavigationLink(value: Route.currencySettings) { + SettingsRow( + title: t("settings__general__currency_local"), + iconName: "coins", + rightText: "\(currency.selectedCurrency) (\(currency.symbol))" + ) + } + .accessibilityIdentifier("CurrenciesSettings") - NavigationLink(value: Route.unitSettings) { - SettingsListLabel( - title: t("settings__general__unit"), - rightText: currency.primaryDisplay == .bitcoin ? currency.primaryDisplay.rawValue : currency.selectedCurrency - ) - } - .accessibilityIdentifier("UnitSettings") + NavigationLink(value: Route.unitSettings) { + SettingsRow( + title: t("settings__general__unit"), + iconName: currency.primaryDisplay == .bitcoin ? "b-unit" : "globe", + rightText: currency.primaryDisplay == .bitcoin ? currency.primaryDisplay.rawValue : currency.selectedCurrency + ) + } + .accessibilityIdentifier("UnitSettings") + + NavigationLink(value: Route.widgetsSettings) { + SettingsRow( + title: t("settings__widgets__nav_title"), + iconName: "cube", + rightText: settings.showWidgets ? t("common__on") : t("common__off") + ) + } + .accessibilityIdentifier("WidgetsSettings") - NavigationLink(value: Route.transactionSpeedSettings) { - SettingsListLabel( - title: t("settings__general__speed"), - rightText: settings.defaultTransactionSpeed.title + if !tagManager.lastUsedTags.isEmpty { + NavigationLink(value: Route.tagSettings) { + SettingsRow( + title: t("settings__general__tags"), + iconName: "tag", + rightText: String(tagManager.lastUsedTags.count) ) } - .accessibilityElement(children: .contain) - .accessibilityIdentifier("TransactionSpeedSettings") + .accessibilityIdentifier("TagsSettings") + } - if !tagManager.lastUsedTags.isEmpty { - NavigationLink(value: Route.tagSettings) { - SettingsListLabel( - title: t("settings__general__tags"), - rightText: String(tagManager.lastUsedTags.count) - ) - } - .accessibilityIdentifier("TagsSettings") - } + // Payments section + SettingsSectionHeader(t("settings__general__section_payments")) + .padding(.top, 16) - NavigationLink(value: Route.widgetsSettings) { - SettingsListLabel( - title: t("settings__widgets__nav_title"), - rightText: settings.showWidgets ? t("common__on") : t("common__off") - ) - } - .accessibilityIdentifier("WidgetsSettings") + NavigationLink(value: Route.transactionSpeedSettings) { + SettingsRow( + title: t("settings__general__speed"), + iconName: settings.defaultTransactionSpeed.iconName, + rightText: settings.defaultTransactionSpeed.title + ) + } + .accessibilityElement(children: .contain) + .accessibilityIdentifier("TransactionSpeedSettings") - NavigationLink(value: app.hasSeenQuickpayIntro ? Route.quickpay : Route.quickpayIntro) { - SettingsListLabel( - title: t("settings__quickpay__nav_title"), - rightText: settings.enableQuickpay ? t("common__on") : t("common__off") - ) - } - .accessibilityIdentifier("QuickpaySettings") + NavigationLink(value: app.hasSeenQuickpayIntro ? Route.quickpay : Route.quickpayIntro) { + SettingsRow( + title: t("settings__quickpay__nav_title"), + iconName: "caret-double-right", + rightText: settings.enableQuickpay ? t("common__on") : t("common__off") + ) + } + .accessibilityIdentifier("QuickpaySettings") - NavigationLink(value: app.hasSeenNotificationsIntro ? Route.notifications : Route.notificationsIntro) { - SettingsListLabel( - title: t("settings__notifications__nav_title"), - rightText: settings.enableNotifications ? t("common__on") : t("common__off") - ) - } - .accessibilityIdentifier("NotificationsSettings") + NavigationLink(value: app.hasSeenNotificationsIntro ? Route.notifications : Route.notificationsIntro) { + SettingsRow( + title: t("settings__notifications__nav_title"), + iconName: "bell", + rightText: settings.enableNotifications ? t("common__on") : t("common__off") + ) } + .accessibilityIdentifier("NotificationsSettings") } + .padding(.top, 16) + .padding(.horizontal, 16) + .bottomSafeAreaPadding() } - .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() } } diff --git a/Bitkit/Views/Settings/LdkDebugScreen.swift b/Bitkit/Views/Settings/LdkDebugScreen.swift index 69d1556cb..49439a570 100644 --- a/Bitkit/Views/Settings/LdkDebugScreen.swift +++ b/Bitkit/Views/Settings/LdkDebugScreen.swift @@ -17,7 +17,7 @@ struct LdkDebugScreen: View { VStack(alignment: .leading, spacing: 32) { // Add Peer VStack(alignment: .leading, spacing: 8) { - CaptionMText("Add Peer") + SettingsSectionHeader("Add Peer") TextField("039b8d4d...a8f3eae3@127.0.0.1:9735", text: $nodeUri) @@ -37,7 +37,7 @@ struct LdkDebugScreen: View { // Network Graph Storage VStack(alignment: .leading, spacing: 8) { - CaptionMText("Network Graph Storage") + SettingsSectionHeader("Network Graph Storage") HStack(spacing: 8) { CustomButton(title: "Log Graph Info", size: .small) { @@ -56,7 +56,7 @@ struct LdkDebugScreen: View { // Node VStack(alignment: .leading, spacing: 8) { - CaptionMText("Node") + SettingsSectionHeader("Node") HStack(spacing: 8) { CustomButton(title: "Restart", size: .small, isLoading: isRestartingNode) { @@ -69,7 +69,7 @@ struct LdkDebugScreen: View { // Peer Simulation VStack(alignment: .leading, spacing: 8) { - CaptionMText("Peer Simulation") + SettingsSectionHeader("Peer Simulation") Picker("Peer Simulation", selection: Binding( get: { WalletViewModel.peerSimulation }, diff --git a/Bitkit/Views/Settings/MainSettings.swift b/Bitkit/Views/Settings/MainSettings.swift deleted file mode 100644 index d558b1119..000000000 --- a/Bitkit/Views/Settings/MainSettings.swift +++ /dev/null @@ -1,107 +0,0 @@ -import SwiftUI - -struct MainSettings: View { - @EnvironmentObject private var app: AppViewModel - - @AppStorage("showDevSettings") private var showDevSettings = Env.isDebug - @State private var cogTapCount = 0 - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("settings__settings")) - - GeometryReader { geometry in - ScrollView(showsIndicators: false) { - VStack(alignment: .leading, spacing: 0) { - NavigationLink(value: Route.generalSettings) { - SettingsListLabel( - title: t("settings__general_title"), - iconName: "gear-six" - ) - } - .accessibilityIdentifier("GeneralSettings") - - NavigationLink(value: Route.securitySettings) { - SettingsListLabel( - title: t("settings__security_title"), - iconName: "shield" - ) - } - .accessibilityIdentifier("SecuritySettings") - - NavigationLink(value: Route.backupSettings) { - SettingsListLabel( - title: t("settings__backup_title"), - iconName: "rewind" - ) - } - .accessibilityIdentifier("BackupSettings") - - NavigationLink(value: Route.advancedSettings) { - SettingsListLabel( - title: t("settings__advanced_title"), - iconName: "sliders" - ) - } - .accessibilityIdentifier("AdvancedSettings") - - NavigationLink(value: Route.support) { - SettingsListLabel( - title: t("settings__support_title"), - iconName: "chat" - ) - } - .accessibilityIdentifier("Support") - - NavigationLink(value: Route.about) { - SettingsListLabel( - title: t("settings__about_title"), - iconName: "info" - ) - } - .accessibilityIdentifier("About") - - if showDevSettings { - NavigationLink(value: Route.devSettings) { - SettingsListLabel( - title: t("settings__dev_title"), - iconName: "game-controller" - ) - } - .accessibilityIdentifier("DevSettings") - } - - Spacer() - - Image("cog") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 256, height: 256) - .frame(maxWidth: .infinity, alignment: .center) - .onTapGesture { - cogTapCount += 1 - - // Toggle dev settings every 5 taps - if cogTapCount >= 5 { - showDevSettings.toggle() - cogTapCount = 0 - - app.toast( - type: .success, - title: t(showDevSettings ? "settings__dev_enabled_title" : "settings__dev_disabled_title"), - description: t(showDevSettings ? "settings__dev_enabled_message" : "settings__dev_disabled_message") - ) - } - } - .accessibilityIdentifier("DevOptions") - - Spacer() - } - .frame(minHeight: geometry.size.height) - } - } - } - .navigationBarHidden(true) - .padding(.horizontal, 16) - } -} diff --git a/Bitkit/Views/Settings/MainSettingsScreen.swift b/Bitkit/Views/Settings/MainSettingsScreen.swift new file mode 100644 index 000000000..93088c337 --- /dev/null +++ b/Bitkit/Views/Settings/MainSettingsScreen.swift @@ -0,0 +1,43 @@ +import SwiftUI + +enum SettingsTab: String, CaseIterable, CustomStringConvertible { + case general + case security + case advanced + + var description: String { + switch self { + case .general: return t("settings__general_title") + case .security: return t("settings__security_title") + case .advanced: return t("settings__advanced_title") + } + } +} + +struct MainSettingsScreen: View { + @State private var selectedTab: SettingsTab = .general + + private var settingsTabItems: [TabItem] { + SettingsTab.allCases.map { TabItem($0) } + } + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + NavigationBar(title: t("settings__settings")) + .padding(.horizontal, 16) + + SegmentedControl(selectedTab: $selectedTab, tabItems: settingsTabItems) + .padding(.horizontal, 16) + + Group { + switch selectedTab { + case .general: GeneralSettingsView() + case .security: SecuritySettingsView() + case .advanced: AdvancedSettingsView() + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + .navigationBarHidden(true) + } +} diff --git a/Bitkit/Views/Settings/Notifications/NotificationsSettings.swift b/Bitkit/Views/Settings/Notifications/NotificationsSettings.swift index 0ef7fbe1e..eeae11de8 100644 --- a/Bitkit/Views/Settings/Notifications/NotificationsSettings.swift +++ b/Bitkit/Views/Settings/Notifications/NotificationsSettings.swift @@ -29,7 +29,7 @@ struct NotificationsSettings: View { VStack(alignment: .leading, spacing: 0) { NavigationBar(title: t("settings__notifications__nav_title")) - SettingsListLabel( + SettingsRow( title: t("settings__notifications__settings__toggle"), toggle: $settings.enableNotifications, disabled: isDenied @@ -51,7 +51,7 @@ struct NotificationsSettings: View { CaptionMText(t("settings__notifications__settings__privacy__label")) .padding(.top, 32) - SettingsListLabel( + SettingsRow( title: t("settings__notifications__settings__privacy__text"), toggle: $settings.enableNotificationsAmount ) diff --git a/Bitkit/Views/Settings/Quickpay/QuickpaySettings.swift b/Bitkit/Views/Settings/Quickpay/QuickpaySettings.swift index d19182eb1..ca920ec57 100644 --- a/Bitkit/Views/Settings/Quickpay/QuickpaySettings.swift +++ b/Bitkit/Views/Settings/Quickpay/QuickpaySettings.swift @@ -13,7 +13,7 @@ struct QuickpaySettings: View { GeometryReader { geometry in ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { - SettingsListLabel( + SettingsRow( title: t("settings__quickpay__settings__toggle"), toggle: $settings.enableQuickpay, testIdentifier: "QuickpayToggle" @@ -24,8 +24,8 @@ struct QuickpaySettings: View { ) .padding(.top, 16) - VStack(alignment: .leading, spacing: 16) { - CaptionMText(t("settings__quickpay__settings__label")) + VStack(alignment: .leading, spacing: 0) { + SettingsSectionHeader(t("settings__quickpay__settings__label")) CustomSlider(value: $settings.quickpayAmount, steps: sliderSteps) } .padding(.top, 32) diff --git a/Bitkit/Views/Settings/Security/ChangePinScreen.swift b/Bitkit/Views/Settings/Security/ChangePinScreen.swift new file mode 100644 index 000000000..7ab33adfb --- /dev/null +++ b/Bitkit/Views/Settings/Security/ChangePinScreen.swift @@ -0,0 +1,76 @@ +import SwiftUI + +struct ChangePinScreen: View { + @EnvironmentObject private var settings: SettingsViewModel + @EnvironmentObject private var sheets: SheetViewModel + + var navTitle: String { + settings.pinEnabled ? t("security__pin_disable_title") : t("settings__security__pin") + } + + var description: String { + settings.pinEnabled ? t("security__pin_disable_text") : t("security__pin_security_text") + } + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + NavigationBar(title: navTitle) + .padding(.bottom, 16) + + BodyMText(description) + + Spacer() + + Image("shield-figure") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 256, height: 256) + .frame(maxWidth: .infinity) + .padding(.top, 32) + + Spacer() + + HStack(alignment: .center, spacing: 16) { + if settings.pinEnabled { + CustomButton(title: t("security__cp_title"), variant: .secondary) { + sheets.showSheet(.security, data: SecurityConfig(showLaterButton: false)) + } + .accessibilityIdentifier("PINCode") + + CustomButton(title: t("security__pin_disable_button")) { + sheets.showSheet(.security, data: SecurityConfig(showLaterButton: false)) + } + .accessibilityIdentifier("DisablePin") + } else { + CustomButton(title: t("security__pin_enable_button")) { + sheets.showSheet(.security, data: SecurityConfig(showLaterButton: false)) + } + .accessibilityIdentifier("EnablePin") + } + } + + // CustomButton( + // title: t("security__pin_disable_button"), + // destination: PinCheckView( + // title: t("security__pin_enter"), + // explanation: "", + // onCancel: {}, + // onPinVerified: { pin in + // do { + // try settings.removePin(pin: pin) + // dismiss() + // } catch { + // Logger.error("Failed to remove PIN: \(error)", context: "DisablePinView") + // // Still dismiss even if there's an error, as the PIN was verified + // dismiss() + // } + // } + // ) + // ) + // .accessibilityIdentifier("DisablePin") + } + .navigationBarHidden(true) + .padding(.horizontal, 16) + .bottomSafeAreaPadding() + } +} diff --git a/Bitkit/Views/Settings/Backup/BackupSettings.swift b/Bitkit/Views/Settings/Security/DataBackupsScreen.swift similarity index 76% rename from Bitkit/Views/Settings/Backup/BackupSettings.swift rename to Bitkit/Views/Settings/Security/DataBackupsScreen.swift index de0909a96..31b84c1a2 100644 --- a/Bitkit/Views/Settings/Backup/BackupSettings.swift +++ b/Bitkit/Views/Settings/Security/DataBackupsScreen.swift @@ -1,19 +1,5 @@ import SwiftUI -private struct SettingsLabel: View { - let text: String - - init(_ text: String) { - self.text = text - } - - var body: some View { - CaptionMText(text) - .frame(height: 40) - .frame(maxWidth: .infinity, alignment: .leading) - } -} - private struct StatusItemView: View { let imageName: String let title: String @@ -51,8 +37,7 @@ private struct StatusItemView: View { } } -struct BackupSettings: View { - @EnvironmentObject var sheets: SheetViewModel +struct DataBackupsScreen: View { @StateObject private var viewModel = BackupViewModel() private var allSynced: Bool { @@ -64,26 +49,12 @@ struct BackupSettings: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("settings__backup_title")) + NavigationBar(title: t("settings__data_backups_nav_title")) GeometryReader { geometry in ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { - Button(action: { - sheets.showSheet(.backup, data: BackupConfig(view: .mnemonic)) - }) { - SettingsListLabel(title: t("settings__backup__wallet")) - } - .accessibilityIdentifier("BackupWallet") - - NavigationLink(value: Route.resetAndRestore) { - SettingsListLabel(title: t("settings__backup__reset")) - } - .accessibilityIdentifier("ResetAndRestore") - HStack(alignment: .center, spacing: 8) { - SettingsLabel(t("settings__backup__latest")) - if Env.isE2E, allSynced { Image("check") .resizable() @@ -112,6 +83,8 @@ struct BackupSettings: View { viewModel.triggerBackup(for: category) } ) + + Divider() } Spacer() diff --git a/Bitkit/Views/Settings/Security/DisablePinView.swift b/Bitkit/Views/Settings/Security/DisablePinView.swift deleted file mode 100644 index f285e0425..000000000 --- a/Bitkit/Views/Settings/Security/DisablePinView.swift +++ /dev/null @@ -1,57 +0,0 @@ -import SwiftUI - -struct DisablePinView: View { - @Environment(\.dismiss) private var dismiss - @EnvironmentObject var settings: SettingsViewModel - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("security__pin_disable_title")) - .padding(.bottom, 16) - - BodyMText(t("security__pin_disable_text")) - - Spacer() - - Image("shield-figure") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 256, height: 256) - .frame(maxWidth: .infinity) - .padding(.top, 32) - - Spacer() - - CustomButton( - title: t("security__pin_disable_button"), - destination: PinCheckView( - title: t("security__pin_enter"), - explanation: "", - onCancel: {}, - onPinVerified: { pin in - do { - try settings.removePin(pin: pin) - dismiss() - } catch { - Logger.error("Failed to remove PIN: \(error)", context: "DisablePinView") - // Still dismiss even if there's an error, as the PIN was verified - dismiss() - } - } - ) - ) - .accessibilityIdentifier("DisablePin") - } - .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() - } -} - -#Preview { - NavigationStack { - DisablePinView() - } - .preferredColorScheme(.dark) - .environmentObject(SettingsViewModel.shared) -} diff --git a/Bitkit/Views/Settings/Backup/ResetAndRestore.swift b/Bitkit/Views/Settings/Security/ResetScreen.swift similarity index 96% rename from Bitkit/Views/Settings/Backup/ResetAndRestore.swift rename to Bitkit/Views/Settings/Security/ResetScreen.swift index 97bcd23ab..89e55da30 100644 --- a/Bitkit/Views/Settings/Backup/ResetAndRestore.swift +++ b/Bitkit/Views/Settings/Security/ResetScreen.swift @@ -1,6 +1,6 @@ import SwiftUI -struct ResetAndRestore: View { +struct ResetScreen: View { @EnvironmentObject var app: AppViewModel @EnvironmentObject var sheets: SheetViewModel @EnvironmentObject var wallet: WalletViewModel @@ -10,7 +10,7 @@ struct ResetAndRestore: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("settings__backup__title")) + NavigationBar(title: t("settings__reset_nav_title")) VStack(spacing: 0) { BodyMText(t("security__reset_text")) diff --git a/Bitkit/Views/Settings/SecurityPrivacySettingsView.swift b/Bitkit/Views/Settings/SecuritySettingsView.swift similarity index 70% rename from Bitkit/Views/Settings/SecurityPrivacySettingsView.swift rename to Bitkit/Views/Settings/SecuritySettingsView.swift index 8cff031b8..70a2c4653 100644 --- a/Bitkit/Views/Settings/SecurityPrivacySettingsView.swift +++ b/Bitkit/Views/Settings/SecuritySettingsView.swift @@ -1,7 +1,7 @@ import LocalAuthentication import SwiftUI -struct SecurityPrivacySettingsView: View { +struct SecuritySettingsView: View { @EnvironmentObject var app: AppViewModel @EnvironmentObject var sheets: SheetViewModel @EnvironmentObject var settings: SettingsViewModel @@ -12,12 +12,9 @@ struct SecurityPrivacySettingsView: View { private var biometryTypeName: String { switch Env.biometryType { - case .touchID: - return t("security__bio_touch_id") - case .faceID: - return t("security__bio_face_id") - default: - return t("security__bio_face_id") // Default to Face ID + case .touchID: return t("security__bio_touch_id") + case .faceID: return t("security__bio_face_id") + default: return t("security__bio_face_id") // Default to Face ID } } @@ -29,71 +26,57 @@ struct SecurityPrivacySettingsView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - NavigationBar(title: t("settings__security__title")) - .padding(.bottom, 16) - .padding(.horizontal, 16) - ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { - // Privacy Settings Section - SettingsListLabel( - title: t("settings__security__swipe_balance_to_hide"), - toggle: $settings.swipeBalanceToHide, - testIdentifier: "SwipeBalanceToHide" - ) - - SettingsListLabel( - title: t("settings__security__hide_balance_on_open"), - toggle: $settings.hideBalanceOnOpen, - testIdentifier: "HideBalanceOnOpen" - ) - - SettingsListLabel( - title: t("settings__security__clipboard"), - toggle: $settings.readClipboard, - testIdentifier: "AutoReadClipboard" - ) + // Backup section + SettingsSectionHeader(t("settings__security__section_backup")) + + Button(action: { + sheets.showSheet(.backup, data: BackupConfig(view: .mnemonic)) + }) { + SettingsRow( + title: t("settings__backup__wallet"), + iconName: "lock-key" + ) + } + .accessibilityIdentifier("BackupWallet") - SettingsListLabel( - title: t("settings__security__warn_100"), - toggle: $settings.warnWhenSendingOver100, - testIdentifier: "SendAmountWarning" - ) + NavigationLink(value: Route.dataBackups) { + SettingsRow( + title: t("settings__backup__data"), + iconName: "database" + ) + } + .accessibilityIdentifier("BackupSettings") - // PIN Code Section - if !settings.pinEnabled { - Button { - sheets.showSheet(.security, data: SecurityConfig(showLaterButton: false)) - } label: { - SettingsListLabel( - title: t("settings__security__pin"), - rightText: t("settings__security__pin_disabled") - ) - } - .accessibilityIdentifier("PINCode") - } else { - NavigationLink(value: Route.disablePin) { - SettingsListLabel( - title: t("settings__security__pin"), - rightText: t("settings__security__pin_enabled") - ) - } - .accessibilityIdentifier("PINCode") + NavigationLink(value: Route.reset) { + SettingsRow( + title: t("settings__backup__reset"), + iconName: "arrow-counter-clockwise" + ) } + .accessibilityIdentifier("ResetAndRestore") + + // Safety section + SettingsSectionHeader(t("settings__security__section_safety")) + .padding(.top, 16) + + NavigationLink(value: Route.changePin) { + SettingsRow( + title: t("settings__security__pin"), + iconName: "shield", + rightText: settings.pinEnabled ? t("settings__security__pin_enabled") : t("settings__security__pin_disabled") + ) + } + .accessibilityIdentifier("PINCode") if settings.pinEnabled { - NavigationLink(value: Route.changePin) { - SettingsListLabel( - title: t("settings__security__pin_change") - ) - } - .accessibilityIdentifier("PINChange") - Button { showPinCheckForPayments = true } label: { - SettingsListLabel( + SettingsRow( title: t("settings__security__pin_payments"), + iconName: "coins", rightIcon: nil, toggle: Binding( get: { settings.requirePinForPayments }, @@ -104,9 +87,9 @@ struct SecurityPrivacySettingsView: View { .accessibilityIdentifier("EnablePinForPayments") if isBiometricAvailable { - // Biometrics toggle with custom handling - SettingsListLabel( + SettingsRow( title: t("settings__security__use_bio", variables: ["biometryTypeName": biometryTypeName]), + iconName: "smiley", toggle: Binding( get: { settings.useBiometrics }, set: { newValue in @@ -115,18 +98,46 @@ struct SecurityPrivacySettingsView: View { ), testIdentifier: "UseBiometryInstead" ) - - // Footer text for Biometrics - BodySText(t("settings__security__footer", variables: ["biometryTypeName": biometryTypeName])) - .padding(.top, 16) } } + + SettingsRow( + title: t("settings__security__warn_100"), + iconName: "warning", + toggle: $settings.warnWhenSendingOver100, + testIdentifier: "SendAmountWarning" + ) + + // Privacy section + SettingsSectionHeader(t("settings__security__section_privacy")) + .padding(.top, 16) + + SettingsRow( + title: t("settings__security__swipe_balance_to_hide"), + iconName: "hand-pointing", + toggle: $settings.swipeBalanceToHide, + testIdentifier: "SwipeBalanceToHide" + ) + + SettingsRow( + title: t("settings__security__hide_balance_on_open"), + iconName: "eye-slash", + toggle: $settings.hideBalanceOnOpen, + testIdentifier: "HideBalanceOnOpen" + ) + + SettingsRow( + title: t("settings__security__clipboard"), + iconName: "clipboard", + toggle: $settings.readClipboard, + testIdentifier: "AutoReadClipboard" + ) } + .padding(.top, 16) .padding(.horizontal, 16) .bottomSafeAreaPadding() } } - .navigationBarHidden(true) .navigationDestination(isPresented: $showPinCheckForPayments) { PinCheckView( title: t("security__pin_enter"), @@ -137,10 +148,7 @@ struct SecurityPrivacySettingsView: View { } ) } - .alert( - t("security__bio_error_title"), - isPresented: $showingBiometricError - ) { + .alert(t("security__bio_error_title"), isPresented: $showingBiometricError) { Button(t("common__ok")) { // Error handled, user acknowledged } @@ -155,7 +163,7 @@ struct SecurityPrivacySettingsView: View { requestBiometricPermission { success in if success { settings.useBiometrics = true - Logger.debug("Biometric authentication enabled", context: "SecurityPrivacySettingsView") + Logger.debug("Biometric authentication enabled", context: "SecuritySettingsView") } else { // Authentication failed - keep toggle off // The toggle will automatically revert since we're not setting the value @@ -167,7 +175,7 @@ struct SecurityPrivacySettingsView: View { requestBiometricPermission { success in if success { settings.useBiometrics = false - Logger.debug("Biometric authentication disabled", context: "SecurityPrivacySettingsView") + Logger.debug("Biometric authentication disabled", context: "SecuritySettingsView") } else { // Authentication failed - keep toggle on // The toggle will automatically revert since we're not setting the value @@ -234,12 +242,12 @@ struct SecurityPrivacySettingsView: View { showingBiometricError = true } - Logger.error("Biometric authentication error: \(error)", context: "SecurityPrivacySettingsView") + Logger.error("Biometric authentication error: \(error)", context: "SecuritySettingsView") } } #Preview { - SecurityPrivacySettingsView() + SecuritySettingsView() .environmentObject(SheetViewModel()) .environmentObject(SettingsViewModel.shared) .preferredColorScheme(.dark) diff --git a/Bitkit/Views/Settings/SupportScreen.swift b/Bitkit/Views/Settings/SupportScreen.swift new file mode 100644 index 000000000..8b5c97061 --- /dev/null +++ b/Bitkit/Views/Settings/SupportScreen.swift @@ -0,0 +1,178 @@ +import SwiftUI + +private struct DiagonalCut: Shape { + var cornerRadius: CGFloat = 36 + + func path(in rect: CGRect) -> Path { + var path = Path() + let r = min(cornerRadius, rect.width / 4, rect.height / 4) + + let leftCutY = rect.maxY - 210 + path.move(to: CGPoint(x: rect.minX, y: leftCutY)) + + let rightCutY = rect.maxY - 300 + path.addLine(to: CGPoint(x: rect.maxX, y: rightCutY)) + + // Right edge to just above bottom-right corner + path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - r)) + // Rounded bottom-right corner + path.addArc( + center: CGPoint(x: rect.maxX - r, y: rect.maxY - r), + radius: r, + startAngle: Angle(radians: 0), + endAngle: Angle(radians: .pi / 2), + clockwise: false + ) + // Bottom edge to just before bottom-left corner + path.addLine(to: CGPoint(x: rect.minX + r, y: rect.maxY)) + // Rounded bottom-left corner + path.addArc( + center: CGPoint(x: rect.minX + r, y: rect.maxY - r), + radius: r, + startAngle: Angle(radians: .pi / 2), + endAngle: Angle(radians: .pi), + clockwise: false + ) + path.closeSubpath() + + return path + } +} + +struct SupportScreen: View { + @EnvironmentObject private var app: AppViewModel + @Environment(\.openURL) private var openURL + + @State private var versionTapCount = 0 + + @AppStorage("showDevSettings") private var showDevSettings = Env.isDebug + + private var appVersion: String { + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown" + let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown" + return "\(version) (\(build))" + } + + private var shareText: String { + return t( + "settings__about__shareText", + variables: ["appStoreUrl": Env.appStoreUrl, "playStoreUrl": Env.playStoreUrl] + ) + } + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + NavigationBar(title: t("settings__support__title")) + .padding(.horizontal, 16) + .padding(.bottom, 16) + + GeometryReader { geometry in + ScrollView(showsIndicators: false) { + ZStack { + // Orange diagonal background (scrolls with content) + Color.brandAccent + .clipShape(DiagonalCut()) + .ignoresSafeArea() + + VStack(alignment: .leading, spacing: 0) { + BodyMText(t("settings__support__text")) + .padding(.bottom, 16) + + VStack(spacing: 0) { + NavigationLink(value: Route.reportIssue) { + SettingsRow(title: t("settings__support__report"), iconName: "warning") + } + + Button(action: { + openURL(URL(string: Env.helpUrl)!) + }) { + SettingsRow(title: t("settings__support__help"), iconName: "question") + } + + NavigationLink(value: Route.appStatus) { + SettingsRow(title: t("settings__support__status"), iconName: "power") + } + .accessibilityIdentifier("AppStatus") + + Button(action: { + openURL(URL(string: Env.termsOfServiceUrl)!) + }) { + SettingsRow(title: t("settings__about__legal"), iconName: "file-text") + } + + ShareLink(item: shareText, message: Text(shareText)) { + SettingsRow(title: t("settings__about__share"), iconName: "share") + } + + Button(action: { + onVersionTap() + }) { + SettingsRow( + title: t("settings__about__version"), + iconName: "stack", + rightText: appVersion, + rightIcon: nil + ) + } + } + + Spacer(minLength: 32) + + VStack(alignment: .center, spacing: 0) { + Image("logo") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxHeight: 100) + .accessibilityIdentifier("AboutLogo") + } + .frame(maxWidth: .infinity) + .padding(.bottom, 16) + + Social() + .padding(.bottom, 16) + + BodyMText("Bitkit was crafted by Synonym Software, S.A. DE C.V. ©2025. All rights reserved.") + .padding(.bottom, 16) + + HStack(alignment: .center, spacing: 10) { + Image("synonym-logo") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 24) + + Image("tether-logo") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 16) + } + .frame(maxWidth: .infinity, alignment: .center) + .frame(height: 24) + .padding(.bottom, 32) + } + .frame(minHeight: geometry.size.height) + .padding(.horizontal, 16) + .bottomSafeAreaPadding() + } + } + } + .ignoresSafeArea() + } + .navigationBarHidden(true) + } + + private func onVersionTap() { + versionTapCount += 1 + + // When tapped 5 times, toggle developer mode + if versionTapCount >= 5 { + versionTapCount = 0 + showDevSettings.toggle() + + app.toast( + type: .info, + title: t(showDevSettings ? "settings__dev_enabled_title" : "settings__dev_disabled_title"), + description: t(showDevSettings ? "settings__dev_enabled_message" : "settings__dev_disabled_message") + ) + } + } +} diff --git a/Bitkit/Views/Settings/SupportView.swift b/Bitkit/Views/Settings/SupportView.swift deleted file mode 100644 index af614d099..000000000 --- a/Bitkit/Views/Settings/SupportView.swift +++ /dev/null @@ -1,55 +0,0 @@ -import SwiftUI - -struct SupportView: View { - @Environment(\.openURL) private var openURL - - var body: some View { - VStack(spacing: 0) { - NavigationBar(title: t("settings__support__title")) - .padding(.bottom, 16) - - BodyMText(t("settings__support__text")) - .padding(.bottom, 16) - - VStack(spacing: 0) { - NavigationLink(value: Route.reportIssue) { - SettingsListLabel(title: t("settings__support__report")) - } - - Button(action: { - openURL(URL(string: Env.helpUrl)!) - }) { - SettingsListLabel(title: t("settings__support__help")) - } - - NavigationLink(value: Route.appStatus) { - SettingsListLabel(title: t("settings__support__status")) - } - .accessibilityIdentifier("AppStatus") - } - - Spacer(minLength: 32) - - VStack { - Image("question-mark") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(maxHeight: 256) - } - - Spacer(minLength: 32) - - Social() - } - .navigationBarHidden(true) - .padding(.horizontal, 16) - .bottomSafeAreaPadding() - } -} - -#Preview { - NavigationView { - SupportView() - } - .preferredColorScheme(.dark) -} diff --git a/Bitkit/Views/Settings/TransactionSpeed/TransactionSpeedSettingsView.swift b/Bitkit/Views/Settings/TransactionSpeed/TransactionSpeedSettingsView.swift index e95b42aff..ed9c0183a 100644 --- a/Bitkit/Views/Settings/TransactionSpeed/TransactionSpeedSettingsView.swift +++ b/Bitkit/Views/Settings/TransactionSpeed/TransactionSpeedSettingsView.swift @@ -66,8 +66,7 @@ struct TransactionSpeedSettingsView: View { ScrollView(showsIndicators: false) { VStack(alignment: .leading, spacing: 0) { - CaptionMText(t("settings__general__speed_default")) - .frame(height: 50) + SettingsSectionHeader(t("settings__general__speed_default")) VStack(spacing: 0) { TransactionSpeedSettingsRow( @@ -79,7 +78,7 @@ struct TransactionSpeedSettingsView: View { } ) - Divider() + CustomDivider() TransactionSpeedSettingsRow( speed: .normal, @@ -90,7 +89,7 @@ struct TransactionSpeedSettingsView: View { } ) - Divider() + CustomDivider() TransactionSpeedSettingsRow( speed: .slow, @@ -101,7 +100,7 @@ struct TransactionSpeedSettingsView: View { } ) - Divider() + CustomDivider() TransactionSpeedSettingsRow( speed: .custom(satsPerVByte: 1), // Placeholder diff --git a/Bitkit/Views/Wallets/Receive/ReceiveQr.swift b/Bitkit/Views/Wallets/Receive/ReceiveQr.swift index 03d5f3a5f..541f23223 100644 --- a/Bitkit/Views/Wallets/Receive/ReceiveQr.swift +++ b/Bitkit/Views/Wallets/Receive/ReceiveQr.swift @@ -46,13 +46,13 @@ struct ReceiveQr: View { // Show unified tab when we have a Lightning invoice (even if channels not yet usable) if !wallet.bolt11.isEmpty { return [ - TabItem(.savings), - TabItem(.unified, activeColor: .white), + TabItem(.savings, activeColor: .brandAccent), + TabItem(.unified, activeColor: .textPrimary), TabItem(.spending, activeColor: .purpleAccent), ] } else { return [ - TabItem(.savings), + TabItem(.savings, activeColor: .textPrimary), TabItem(.spending, activeColor: .purpleAccent), ] }