diff --git a/.gitignore b/.gitignore
index 0a9a1bd6..ba1dbe0b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,15 +58,9 @@ Baseline.json
remote-config-defaults.plist
service-data.plist
GoogleService-Info.plist
-
-/SourcePackages
-/brainwallet/launch-resources/GoogleService-Info.plist
-/brainwallet/launch-resources/remote-config-defaults.plist
-/brainwallet/launch-resources/service-data.plist
-# Storekit
-/Storekit
-*.storekit
-Brainwallet-StoreKit-v1.storekit
+
+# Storekit
+*.storekit
/fastlane/report.xml
**/fastlane/test_output/
## Games protection
diff --git a/BrainwalletHybrid.xcworkspace/xcshareddata/xcschemes/brainwallet-local-storekit.xcscheme b/BrainwalletHybrid.xcworkspace/xcshareddata/xcschemes/brainwallet-local-storekit.xcscheme
new file mode 100644
index 00000000..6b06dbc9
--- /dev/null
+++ b/BrainwalletHybrid.xcworkspace/xcshareddata/xcschemes/brainwallet-local-storekit.xcscheme
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BrainwalletHybrid.xcworkspace/xcshareddata/xcschemes/brainwallet.xcscheme b/BrainwalletHybrid.xcworkspace/xcshareddata/xcschemes/brainwallet.xcscheme
index 10ef2fdb..97e7d509 100644
--- a/BrainwalletHybrid.xcworkspace/xcshareddata/xcschemes/brainwallet.xcscheme
+++ b/BrainwalletHybrid.xcworkspace/xcshareddata/xcschemes/brainwallet.xcscheme
@@ -104,9 +104,6 @@
identifier = "San Francisco, CA, USA"
referenceType = "1">
-
-
UIWindowScene {
+ try XCTUnwrap(UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first,
+ "Test host has no connected UIWindowScene")
+ }
+
+ // MARK: - Lifecycle forwarding
+
+ func testSceneDidBecomeActiveClearsBadgeCount() throws {
+ UserDefaults.pendingNotificationBadgeCount = 7
+ sceneDelegate.sceneDidBecomeActive(try hostScene())
+ XCTAssertEqual(UserDefaults.pendingNotificationBadgeCount, 0)
+ }
+
+ func testSceneWillEnterForegroundDoesNotCrashWithoutWallet() throws {
+ // No wallet manager yet in the test host -> willEnterForeground early-returns
+ sceneDelegate.sceneWillEnterForeground(try hostScene())
+ }
+
+ func testSceneDidEnterBackgroundDoesNotCrash() throws {
+ sceneDelegate.sceneDidEnterBackground(try hostScene())
+ }
+
+ // Note: scene(_:willConnectTo:) and AppDelegate.configurationForConnecting cannot be unit tested,
+ // as UIScene.ConnectionOptions has no public initializer. Their wiring is covered by
+ // SceneLifecycleTests.testInfoPlistDeclaresSceneManifest.
+
+ // MARK: - URL handling
+
+ func testOpenURLContextsWithEmptySetIsIgnored() throws {
+ sceneDelegate.scene(try hostScene(), openURLContexts: [])
+ }
+}
diff --git a/BrainwalletUnitTests/Launch Class Tests/SceneLifecycleTests.swift b/BrainwalletUnitTests/Launch Class Tests/SceneLifecycleTests.swift
new file mode 100644
index 00000000..45bb98b4
--- /dev/null
+++ b/BrainwalletUnitTests/Launch Class Tests/SceneLifecycleTests.swift
@@ -0,0 +1,52 @@
+//
+// SceneLifecycleTests.swift
+// brainwallet
+//
+// Guards the UIScene lifecycle adoption required by iOS 27
+// (v3.10.1 crashed at launch with "NoSceneLifecycleAdoption").
+//
+
+@testable import brainwallet
+import XCTest
+
+final class SceneLifecycleTests: XCTestCase {
+
+ /// The Info.plist must declare a scene manifest pointing at SceneDelegate and
+ /// must not use the storyboard-created window.
+ func testInfoPlistDeclaresSceneManifest() throws {
+ let info = try XCTUnwrap(Bundle(for: AppDelegate.self).infoDictionary)
+ XCTAssertNil(info["UIMainStoryboardFile"], "Main storyboard window conflicts with scene lifecycle")
+
+ let manifest = try XCTUnwrap(info["UIApplicationSceneManifest"] as? [String: Any])
+ let configs = try XCTUnwrap(manifest["UISceneConfigurations"] as? [String: Any])
+ let appRole = try XCTUnwrap(configs["UIWindowSceneSessionRoleApplication"] as? [[String: Any]])
+ let first = try XCTUnwrap(appRole.first)
+
+ XCTAssertEqual(first["UISceneConfigurationName"] as? String, "Default Configuration")
+ let delegateName = try XCTUnwrap(first["UISceneDelegateClassName"] as? String)
+ XCTAssertTrue(delegateName.hasSuffix("SceneDelegate"))
+ XCTAssertNotNil(NSClassFromString(delegateName), "SceneDelegate class name must resolve at runtime")
+ }
+
+ func testAppDelegateNoLongerOwnsWindow() {
+ let appDelegate = AppDelegate()
+ XCTAssertFalse(appDelegate.responds(to: NSSelectorFromString("window")))
+ }
+
+ func testClearNotificationBadgeResetsPendingCount() {
+ UserDefaults.pendingNotificationBadgeCount = 5
+ AppDelegate().clearNotificationBadge()
+ XCTAssertEqual(UserDefaults.pendingNotificationBadgeCount, 0)
+ }
+
+ /// A URL that arrives before the wallet UI has launched is queued, not handled.
+ func testOpenURLBeforeLaunchIsQueued() throws {
+ let controller = ApplicationController()
+ let url = try XCTUnwrap(URL(string: "litecoin:LTC_ADDRESS"))
+ XCTAssertFalse(controller.open(url: url))
+ }
+
+ func testSceneDelegateStartsWithoutWindow() {
+ XCTAssertNil(SceneDelegate().window)
+ }
+}
diff --git a/BrainwalletUnitTests/Push Notification Tests/PushNotificationsTests.swift b/BrainwalletUnitTests/Push Notification Tests/PushNotificationsTests.swift
index 4f3b6db6..84c8fa96 100644
--- a/BrainwalletUnitTests/Push Notification Tests/PushNotificationsTests.swift
+++ b/BrainwalletUnitTests/Push Notification Tests/PushNotificationsTests.swift
@@ -17,7 +17,6 @@ class PushNotificationsTests: XCTestCase {
override func setUp() {
super.setUp()
appDelegate = AppDelegate()
- appDelegate.window = UIWindow()
}
override func tearDown() {
diff --git a/BrainwalletUnitTests/Settings Tests/NetworkFeeTierTests.swift b/BrainwalletUnitTests/Settings Tests/NetworkFeeTierTests.swift
new file mode 100644
index 00000000..44ae8a83
--- /dev/null
+++ b/BrainwalletUnitTests/Settings Tests/NetworkFeeTierTests.swift
@@ -0,0 +1,98 @@
+//
+// NetworkFeeTierTests.swift
+// brainwallet
+//
+// Covers the fee-tier math backing the Network Fee picker in
+// SettingsLitecoinDetailView (litoshis-per-tier, nearest-tier lookup for a
+// persisted preference, and litoshi -> fiat formatting).
+//
+// Copyright © 2025 Grunt Software, LTD. All rights reserved.
+
+@testable import brainwallet
+import XCTest
+
+class NetworkFeeTierTests: XCTestCase {
+
+ private let fees = Fees(luxury: 66746, regular: 25000, economy: 8000, timestamp: 0)
+ private let usdRate = Rate(code: "USD", name: "US Dollar", rate: 60.0, lastTimestamp: Date())
+
+ // MARK: - litoshis(from:)
+
+ func testLitoshisFromMapsEachTierToItsFeesField() {
+ XCTAssertEqual(NetworkFeeTier.economy.litoshis(from: fees), fees.economy)
+ XCTAssertEqual(NetworkFeeTier.regular.litoshis(from: fees), fees.regular)
+ XCTAssertEqual(NetworkFeeTier.luxury.litoshis(from: fees), fees.luxury)
+ }
+
+ // MARK: - closest(to:in:)
+
+ func testClosestReturnsExactTierOnExactMatch() {
+ XCTAssertEqual(NetworkFeeTier.closest(to: Int(fees.economy), in: fees), .economy)
+ XCTAssertEqual(NetworkFeeTier.closest(to: Int(fees.regular), in: fees), .regular)
+ XCTAssertEqual(NetworkFeeTier.closest(to: Int(fees.luxury), in: fees), .luxury)
+ }
+
+ func testClosestPicksNearerNeighborBetweenTwoTiers() {
+ // Between economy (8000) and regular (25000); nearer to economy.
+ XCTAssertEqual(NetworkFeeTier.closest(to: 10_000, in: fees), .economy)
+ // Between regular (25000) and luxury (66746); nearer to luxury.
+ XCTAssertEqual(NetworkFeeTier.closest(to: 60_000, in: fees), .luxury)
+ }
+
+ func testClosestClampsToEconomyBelowLowestTier() {
+ XCTAssertEqual(NetworkFeeTier.closest(to: 0, in: fees), .economy)
+ }
+
+ func testClosestClampsToLuxuryAboveHighestTier() {
+ XCTAssertEqual(NetworkFeeTier.closest(to: 1_000_000, in: fees), .luxury)
+ }
+
+ func testClosestBreaksTiesTowardTheLowerTier() {
+ // Exactly halfway between economy (8000) and regular (25000) is 16500;
+ // a tie should favor the lower (economy) tier.
+ let tieFees = Fees(luxury: 30000, regular: 20000, economy: 10000, timestamp: 0)
+ XCTAssertEqual(NetworkFeeTier.closest(to: 15000, in: tieFees), .economy)
+ // Halfway between regular (20000) and luxury (30000) is 25000.
+ XCTAssertEqual(NetworkFeeTier.closest(to: 25000, in: tieFees), .regular)
+ }
+
+ func testClosestFallsBackToDefaultValuesWhenNothingIsStored() {
+ // UserDefaults.userSetPreferredNetworkFee falls back to Fees.usingDefaultValues.luxury
+ // when the user has never picked a fee — confirm that round-trips to .luxury.
+ let defaults = Fees.usingDefaultValues
+ let storedFee = Int(defaults.luxury)
+ XCTAssertEqual(NetworkFeeTier.closest(to: storedFee, in: defaults), .luxury)
+ }
+
+ // MARK: - formattedFiatAmount(litoshis:rate:)
+
+ func testFormattedFiatAmountConvertsLitoshisThroughLTCToFiat() {
+ // 100_000_000 litoshis == 1 LTC == $60 at a $60/LTC rate.
+ let result = NetworkFeeTier.formattedFiatAmount(litoshis: 100_000_000, rate: usdRate)
+ XCTAssertEqual(result, "60.000 USD")
+ }
+
+ func testFormattedFiatAmountForRealisticRegularFee() {
+ // 25000 litoshis at $60/LTC = 0.00025 LTC * 60 = 0.015.
+ let result = NetworkFeeTier.formattedFiatAmount(litoshis: fees.regular, rate: usdRate)
+ XCTAssertEqual(result, "0.015 USD")
+ }
+
+ func testFormattedFiatAmountIsEmptyWhenFeeIsZero() {
+ XCTAssertEqual(NetworkFeeTier.formattedFiatAmount(litoshis: 0, rate: usdRate), "")
+ }
+
+ func testFormattedFiatAmountIsEmptyWhenRateIsZero() {
+ let noRate = Rate(code: "", name: "", rate: 0.0, lastTimestamp: Date())
+ XCTAssertEqual(NetworkFeeTier.formattedFiatAmount(litoshis: fees.luxury, rate: noRate), "")
+ }
+
+ // MARK: - CaseIterable ordering (Picker tags rely on rawValue == 0/1/2)
+
+ func testAllCasesOrderMatchesPickerTagOrder() {
+ XCTAssertEqual(NetworkFeeTier.allCases.map(\.rawValue), [0, 1, 2])
+ XCTAssertEqual(NetworkFeeTier.economy.rawValue, 0)
+ XCTAssertEqual(NetworkFeeTier.regular.rawValue, 1)
+ XCTAssertEqual(NetworkFeeTier.luxury.rawValue, 2)
+ }
+}
diff --git a/BrainwalletUnitTests/Settings Tests/SettingsTests.swift b/BrainwalletUnitTests/Settings Tests/SettingsTests.swift
index f3f5f6e5..6db674c9 100644
--- a/BrainwalletUnitTests/Settings Tests/SettingsTests.swift
+++ b/BrainwalletUnitTests/Settings Tests/SettingsTests.swift
@@ -14,7 +14,7 @@ class SettingsHelpersTests: XCTestCase {
// MARK: - Constants Tests
func testClosedRowHeight() {
- XCTAssertEqual(closedRowHeight, 65.0, "closedRowHeight should be 65.0")
+ XCTAssertEqual(closedRowHeight, 50.0, "closedRowHeight should be 50.0")
}
func testToggleRowHeight() {
@@ -42,7 +42,7 @@ class SettingsHelpersTests: XCTestCase {
}
func testExpandArrowSize() {
- XCTAssertEqual(expandArrowSize, 20.0, "expandArrowSize should be 20.0")
+ XCTAssertEqual(expandArrowSize, 16.0, "expandArrowSize should be 16.0")
}
func testPickerViewHeight() {
diff --git a/BrainwalletUnitTests/Settings Tests/TrustedNodeProductIdTests.swift b/BrainwalletUnitTests/Settings Tests/TrustedNodeProductIdTests.swift
new file mode 100644
index 00000000..4dc51748
--- /dev/null
+++ b/BrainwalletUnitTests/Settings Tests/TrustedNodeProductIdTests.swift
@@ -0,0 +1,36 @@
+//
+// TrustedNodeProductIdTests.swift
+// brainwallet
+//
+// Guards against the 3.10.0 App Review rejection (Guideline 2.1(b)): the app constant and the
+// product list shipped in BrainwalletiOSPrivateGeneralPurpose drifted apart, so the Trusted
+// Node IAP was filtered out of the purchase sheet.
+//
+
+import XCTest
+@testable import brainwallet
+import BrainwalletiOSPrivateGeneralPurpose
+
+class TrustedNodeProductIdTests: XCTestCase {
+
+ /// Product id configured in App Store Connect for the Trusted Litecoin Node IAP.
+ private let appStoreConnectTrustedNodeId = "com.gruntsoftware.brainwallet.trusted_ltc_node_2"
+
+ private func bundledProductIds() throws -> [String: String] {
+ let bundle = Bundle(for: StoreKitManager.self)
+ let url = try XCTUnwrap(bundle.url(forResource: "BWProductsList", withExtension: "plist"),
+ "BWProductsList.plist missing from the general-purpose bundle")
+ let data = try Data(contentsOf: url)
+ return try XCTUnwrap(try PropertyListSerialization.propertyList(from: data, format: nil) as? [String: String])
+ }
+
+ func testTrustedNodeConstantMatchesAppStoreConnect() {
+ XCTAssertEqual(trustedLTCNodeProductId, appStoreConnectTrustedNodeId)
+ }
+
+ func testTrustedNodeConstantIsInBundledProductList() throws {
+ let ids = try bundledProductIds()
+ XCTAssertEqual(ids["Trusted LTC Node Feature"], trustedLTCNodeProductId,
+ "BWProductsList.plist and trustedLTCNodeProductId must use the same product id")
+ }
+}
diff --git a/Modules/core b/Modules/core
index 36f38224..1afc45e0 160000
--- a/Modules/core
+++ b/Modules/core
@@ -1 +1 @@
-Subproject commit 36f38224b1ecca091a4c118b618254ab1575bd32
+Subproject commit 1afc45e0822eeb8d4c3a4b3c756bd2add75edf74
diff --git a/Private/general-purpose b/Private/general-purpose
index 378c2e0e..124e7b54 160000
--- a/Private/general-purpose
+++ b/Private/general-purpose
@@ -1 +1 @@
-Subproject commit 378c2e0ee89c4f91fc2a9526fd49b56905251651
+Subproject commit 124e7b54238893d6e2f682e8e487f2e791d8c995
diff --git a/README.md b/README.md
index 76fd7637..c748f124 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,17 @@
- Buy/sell LTC and gift cards via in-app widgets
- SwiftUI-based Bento UI hosted in a lightweight UIKit shell, with legacy UIKit screens still being migrated
+## Release Notes
+
+### v3.10.0
+- **Trusted Node**: connect to your own Litecoin node (IP + port) via in-app purchase, with a toggle between trusted node and random mainnet peers. Sync status shows the current peer mode.
+- **Live network fees**: Settings fee picker shows real Economy/Regular/Luxury tiers with the fiat cost, and remembers your choice.
+- **Redesigned Settings**: consistent expandable rows, clearer blockchain section, blockchain sync confirmation alert restored.
+- **Safer wallet wipe**: waits for wallet, database and keychain (incl. trusted-node data) cleanup and shows an error on failure.
+- Refreshed translations and updated submodules.
+
+Full notes: [RELEASE_NOTES.md](RELEASE_NOTES.md)
+
## Auditing code
### Prerequisites
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
new file mode 100644
index 00000000..a227a6c9
--- /dev/null
+++ b/RELEASE_NOTES.md
@@ -0,0 +1,49 @@
+# Brainwallet iOS v3.10.0
+
+## Highlights
+
+### Trusted Node (new)
+- Connect Brainwallet to your own Litecoin node by entering its IP address and port in Settings.
+- Unlocked with an in-app purchase (`trusted_ltc_node_2`); the purchase state, IP and port are stored in the iOS Keychain (service `brainwallet.trustednode`).
+- A toggle lets you switch between your trusted node and random mainnet peers. The preference is passed to `BRPeerManager` (`PeerSyncMode`: `random_mainnet` / `trusted`).
+- The sync status view shows an icon for the current peer sync mode.
+- Replaces the old, unused `customNodeIP` / `customNodePort` defaults.
+- Fixed before release: the purchase sheet could appear empty because the app listed a retired product id (`trusted_ltc_node_1`). It now uses `trusted_ltc_node_2`, matching App Store Connect, with tests that keep the ids in sync.
+
+### Live network fees
+- The Settings fee picker now shows real Economy / Regular / Luxury tiers fetched from the fee-per-kb endpoint, instead of placeholder values.
+- Shows the fiat cost of the selected tier and remembers your choice.
+- Fee tier math is in a new `NetworkFeeTier` type, covered by `NetworkFeeTierTests`.
+
+### Redesigned Settings
+- Blockchain, Currency, Security, Theme and Lock rows share one layout and row heights.
+- The blockchain detail view groups peer sync, blockchain sync and network fee sections more clearly.
+- The blockchain sync confirmation alert is back.
+- The old Settings footer view is removed.
+
+### Safer wallet wipe
+- Wiping the wallet now waits for the wallet wipe, database deletion and keychain cleanup to finish, and reports failure instead of ignoring it.
+- The wipe also clears the trusted-node keychain items and the remote-config environment keychain.
+- Lock screen wipe UI shows an error state if the wipe fails.
+
+## Other changes
+- Refreshed translations (`Localizable.xcstrings` auto-translate).
+- Transaction detail and color palette tweaks.
+- Updated submodules: `Modules/core` and `Private/general-purpose` (now at `124e7b5`, with the product id fix).
+- Build 2602316 replaces 2602315, which App Review could not use to find the Trusted Node purchase (Guideline 2.1(b)).
+- Added the `brainwallet-local-storekit` scheme for local StoreKit testing.
+- `.gitignore` cleanup.
+
+## Upgrade notes
+- If you used the old custom node preference, re-enter it as a Trusted Node. The old keys are removed.
+- Wiping a wallet also clears trusted-node settings and the purchase flag from the Keychain. Restore the purchase from the App Store.
+
+## App Store "What's New"
+> - New: Trusted Node. Connect Brainwallet to your own Litecoin node.
+> - Network fees now show live Economy, Regular and Luxury rates, with the fiat cost.
+> - Redesigned Settings.
+> - More reliable wallet wipe, with a clear error if it fails.
+> - Updated translations and other improvements.
+
+## Comparison basis
+Changes are listed against `main` (the last release, `v3.9.18`). `develop` and `release/v3.10.0` share the Trusted Node merge (#197). The release branch adds the version bump, an i18n catalog update, and the Trusted Node product id fix with its tests. `develop` has one more translation PR (#198) and a stale catalog removal that are not in the release.
diff --git a/brainwallet.xcodeproj/project.pbxproj b/brainwallet.xcodeproj/project.pbxproj
index 0f81e682..9ec7cdf0 100644
--- a/brainwallet.xcodeproj/project.pbxproj
+++ b/brainwallet.xcodeproj/project.pbxproj
@@ -216,7 +216,6 @@
24F378022DE89C7800A388AB /* MoonPayHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F378012DE89C6600A388AB /* MoonPayHelper.swift */; };
24F5BD442E042F1D0010D340 /* clicksound.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 24F5BD432E042F1D0010D340 /* clicksound.mp3 */; };
24F5BD482E043C2A0010D340 /* SettingsLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5BD472E043C1B0010D340 /* SettingsLabelView.swift */; };
- 24F5BD4A2E0442D00010D340 /* SettingsFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5BD492E0442D00010D340 /* SettingsFooterView.swift */; };
24F5BD4C2E0450B80010D340 /* SettingsExpandingSecurityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5BD4B2E0450B80010D340 /* SettingsExpandingSecurityView.swift */; };
24F5BD4E2E0452670010D340 /* SettingsActionLockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5BD4D2E0452670010D340 /* SettingsActionLockView.swift */; };
24F5BD502E0460C90010D340 /* SecurityListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5BD4F2E0460C90010D340 /* SecurityListView.swift */; };
@@ -225,6 +224,7 @@
24FBB86A2E035E10002B5F84 /* SettingsHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FBB8692E035E07002B5F84 /* SettingsHostingController.swift */; };
24FF3E6A2E898D4000FF43C5 /* BrainwalletiOSPrivateGeneralPurpose.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 246F97E72E846218001923A6 /* BrainwalletiOSPrivateGeneralPurpose.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
759DA0BE1DAC36A3008CC49B /* libBRCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 755CD9D11DAA197C0075898E /* libBRCore.a */; settings = {ATTRIBUTES = (Required, ); }; };
+ 89B92973AA76D3E95DC98045 /* SetTrustedNodeIPModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907FE3B2FCD7C7D94FA7D2A7 /* SetTrustedNodeIPModalView.swift */; };
9ED4226AC1AA476BB099F35A /* BalanceBentoReviewPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3F1465DCBE495A8993C1FD /* BalanceBentoReviewPolicy.swift */; };
C30D099E2FC5AB720083EDE2 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C30D099D2FC5AB720083EDE2 /* GoogleService-Info.plist */; };
C312D2D02D7DC27700BB97A4 /* ReachabilityMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C312D2912D7DC27700BB97A4 /* ReachabilityMonitor.swift */; };
@@ -358,6 +358,7 @@
C312D48F2D7DC27700BB97A4 /* SimpleRedux.swift in Sources */ = {isa = PBXBuildFile; fileRef = C312D2A92D7DC27700BB97A4 /* SimpleRedux.swift */; };
C312D4942D7DC27700BB97A4 /* RetryTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C312D2982D7DC27700BB97A4 /* RetryTimer.swift */; };
C312D4BB2D7DC27700BB97A4 /* coinflip.aiff in Resources */ = {isa = PBXBuildFile; fileRef = C312D0AF2D7DC27700BB97A4 /* coinflip.aiff */; };
+ C312D5E02D7DC27700BB97A4 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C312D5E12D7DC27700BB97A4 /* SceneDelegate.swift */; };
C31F416F2FF691B900D9DA3D /* SocialPostHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31F416E2FF691B200D9DA3D /* SocialPostHelper.swift */; };
C31F41712FF6968A00D9DA3D /* SocialPostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31F41702FF6968700D9DA3D /* SocialPostViewModel.swift */; };
C32233132FC214A70043CBA7 /* ShopImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = C322330D2FC214990043CBA7 /* ShopImages.swift */; };
@@ -376,6 +377,7 @@
C35C122A293D48340009022D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = C35C1229293D48340009022D /* KeychainAccess */; };
C38F57252FD490430005538A /* boingspringmouthharp042013.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C38F57242FD490430005538A /* boingspringmouthharp042013.mp3 */; };
C395E2B83013770A00CF5F5B /* EnterPinForSeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C395E2B2301376FE00CF5F5B /* EnterPinForSeedView.swift */; };
+ C39859383069234400E99439 /* bw-iap-config-v2.storekit in Resources */ = {isa = PBXBuildFile; fileRef = C39859373069234400E99439 /* bw-iap-config-v2.storekit */; };
C3B0BBC32FF6DB11000D5DB8 /* AlertToast in Frameworks */ = {isa = PBXBuildFile; productRef = C3B0BBC22FF6DB11000D5DB8 /* AlertToast */; };
C3BC6CBD303331FB00A04919 /* UIApplication+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BC6CBC303331D800A04919 /* UIApplication+Additions.swift */; };
C3C95F082FA13DEF002E2BEE /* SignupAskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C95F072FA13DEF002E2BEE /* SignupAskView.swift */; };
@@ -421,20 +423,6 @@
remoteGlobalIDString = 2425ACB32E4BD610007185C7;
remoteInfo = "private-general-purpose";
};
- 246F97E22E846218001923A6 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 246F97832E846218001923A6 /* private-general-purpose.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2425ACC02E4BD612007185C7;
- remoteInfo = "private-general-purposeTests";
- };
- 246F97E42E846218001923A6 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 246F97832E846218001923A6 /* private-general-purpose.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 2425ACCA2E4BD612007185C7;
- remoteInfo = "private-general-purposeUITests";
- };
246F97E62E846218001923A6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 246F97832E846218001923A6 /* private-general-purpose.xcodeproj */;
@@ -793,7 +781,6 @@
24F378012DE89C6600A388AB /* MoonPayHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonPayHelper.swift; sourceTree = ""; };
24F5BD432E042F1D0010D340 /* clicksound.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = clicksound.mp3; sourceTree = ""; };
24F5BD472E043C1B0010D340 /* SettingsLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLabelView.swift; sourceTree = ""; };
- 24F5BD492E0442D00010D340 /* SettingsFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFooterView.swift; sourceTree = ""; };
24F5BD4B2E0450B80010D340 /* SettingsExpandingSecurityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsExpandingSecurityView.swift; sourceTree = ""; };
24F5BD4D2E0452670010D340 /* SettingsActionLockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsActionLockView.swift; sourceTree = ""; };
24F5BD4F2E0460C90010D340 /* SecurityListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityListView.swift; sourceTree = ""; };
@@ -806,6 +793,7 @@
75A2A7F21DA5935F00A983D8 /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; };
75A2A8091DA5936F00A983D8 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; };
75FEFD1B1DAED56E00203D3A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
+ 907FE3B2FCD7C7D94FA7D2A7 /* SetTrustedNodeIPModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetTrustedNodeIPModalView.swift; sourceTree = ""; };
C30D099D2FC5AB720083EDE2 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
C312D06B2D7DC27700BB97A4 /* ApplicationController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ApplicationController+Extension.swift"; sourceTree = ""; };
C312D06C2D7DC27700BB97A4 /* Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Async.swift; sourceTree = ""; };
@@ -940,6 +928,7 @@
C312D2C82D7DC27700BB97A4 /* WalletCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletCoordinator.swift; sourceTree = ""; };
C312D2C92D7DC27700BB97A4 /* WalletManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletManager.swift; sourceTree = ""; };
C312D2CA2D7DC27700BB97A4 /* WalletManager+Auth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WalletManager+Auth.swift"; sourceTree = ""; };
+ C312D5E12D7DC27700BB97A4 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
C31F416E2FF691B200D9DA3D /* SocialPostHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialPostHelper.swift; sourceTree = ""; };
C31F41702FF6968700D9DA3D /* SocialPostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialPostViewModel.swift; sourceTree = ""; };
C322330D2FC214990043CBA7 /* ShopImages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopImages.swift; sourceTree = ""; };
@@ -958,6 +947,7 @@
C353E3B42FAE9F9000BFFB30 /* ShopBentoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopBentoViewModel.swift; sourceTree = ""; };
C38F57242FD490430005538A /* boingspringmouthharp042013.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = boingspringmouthharp042013.mp3; sourceTree = ""; };
C395E2B2301376FE00CF5F5B /* EnterPinForSeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterPinForSeedView.swift; sourceTree = ""; };
+ C39859373069234400E99439 /* bw-iap-config-v2.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = "bw-iap-config-v2.storekit"; sourceTree = ""; };
C3BC6CBC303331D800A04919 /* UIApplication+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Additions.swift"; sourceTree = ""; };
C3C95F072FA13DEF002E2BEE /* SignupAskView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignupAskView.swift; sourceTree = ""; };
C3CE34602FFD8D1F004A2ECA /* LilitaOne.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = LilitaOne.ttf; sourceTree = ""; };
@@ -1279,6 +1269,7 @@
children = (
C312D06B2D7DC27700BB97A4 /* ApplicationController+Extension.swift */,
C312D2002D7DC27700BB97A4 /* AppDelegate.swift */,
+ C312D5E12D7DC27700BB97A4 /* SceneDelegate.swift */,
C312D2012D7DC27700BB97A4 /* ApplicationController.swift */,
);
path = "App Launch Classes";
@@ -1323,8 +1314,6 @@
isa = PBXGroup;
children = (
246F97E12E846218001923A6 /* private-general-purpose.app */,
- 246F97E32E846218001923A6 /* private-general-purposeTests.xctest */,
- 246F97E52E846218001923A6 /* private-general-purposeUITests.xctest */,
246F97E72E846218001923A6 /* BrainwalletiOSPrivateGeneralPurpose.framework */,
246F97E92E846218001923A6 /* BrainwalletiOSPrivateGeneralPurposeTests.xctest */,
);
@@ -1725,7 +1714,6 @@
24DB05EB2E06B5BB00C57FE9 /* SettingsExpandingCurrencyView.swift */,
24DB05E92E06B49B00C57FE9 /* SettingsExpandingGamesView.swift */,
24DB05E72E06B44700C57FE9 /* SettingsExpandingBlockchainView.swift */,
- 24F5BD492E0442D00010D340 /* SettingsFooterView.swift */,
);
path = "Settings Classes";
sourceTree = "";
@@ -1768,6 +1756,7 @@
24D7E9FC2E336CE100A9AB68 /* PreLaunchResources */ = {
isa = PBXGroup;
children = (
+ C39859373069234400E99439 /* bw-iap-config-v2.storekit */,
24D7EA0D2E336F9100A9AB68 /* service-data.plist */,
24D7E9FD2E336D0800A9AB68 /* general.css */,
24D7E9FE2E336D0800A9AB68 /* moon_pay_buy_status.txt */,
@@ -1966,6 +1955,7 @@
C312D2CD2D7DC27700BB97A4 /* brainwallet */ = {
isa = PBXGroup;
children = (
+ C35BD2C63065C1C300B4EE55 /* TrustedNode Classes */,
C31F416D2FF6918E00D9DA3D /* Social Network Classes */,
C3C95F702FA1498E002E2BEE /* Signup Notification Classes */,
246C3F4F2F2430DF009BC881 /* Tutorial Classes */,
@@ -2113,6 +2103,14 @@
path = "Social Network Classes";
sourceTree = "";
};
+ C35BD2C63065C1C300B4EE55 /* TrustedNode Classes */ = {
+ isa = PBXGroup;
+ children = (
+ 907FE3B2FCD7C7D94FA7D2A7 /* SetTrustedNodeIPModalView.swift */,
+ );
+ path = "TrustedNode Classes";
+ sourceTree = "";
+ };
C3C95F702FA1498E002E2BEE /* Signup Notification Classes */ = {
isa = PBXGroup;
children = (
@@ -2374,20 +2372,6 @@
remoteRef = 246F97E02E846218001923A6 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
- 246F97E32E846218001923A6 /* private-general-purposeTests.xctest */ = {
- isa = PBXReferenceProxy;
- fileType = wrapper.cfbundle;
- path = "private-general-purposeTests.xctest";
- remoteRef = 246F97E22E846218001923A6 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 246F97E52E846218001923A6 /* private-general-purposeUITests.xctest */ = {
- isa = PBXReferenceProxy;
- fileType = wrapper.cfbundle;
- path = "private-general-purposeUITests.xctest";
- remoteRef = 246F97E42E846218001923A6 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
246F97E72E846218001923A6 /* BrainwalletiOSPrivateGeneralPurpose.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
@@ -2443,6 +2427,7 @@
24AFEEF22F3152CE00427D02 /* IBMPlexSans-Regular.ttf in Resources */,
24D7EA082E336D0800A9AB68 /* moon_pay_buy_status.txt in Resources */,
24D7EA092E336D0800A9AB68 /* RELEASE_NOTES.md in Resources */,
+ C39859383069234400E99439 /* bw-iap-config-v2.storekit in Resources */,
24D7EA0A2E336D0800A9AB68 /* general.css in Resources */,
24D7EA0B2E336D0800A9AB68 /* CHANGELOG.md in Resources */,
24D7EA0E2E336F9100A9AB68 /* service-data.plist in Resources */,
@@ -2800,13 +2785,13 @@
C312D3882D7DC27700BB97A4 /* Brainwallet+Color.swift in Sources */,
24B0652A2DC6135900B5E000 /* WalletBalanceView.swift in Sources */,
C312D38D2D7DC27700BB97A4 /* AppDelegate.swift in Sources */,
+ C312D5E02D7DC27700BB97A4 /* SceneDelegate.swift in Sources */,
24F64C202DAFBDA4001DC9B6 /* WelcomMojiDemoView.swift in Sources */,
241D223F2DB526A1000302C8 /* ReadyView.swift in Sources */,
C312D38E2D7DC27700BB97A4 /* UIFont+BRWAdditions.swift in Sources */,
24E7591E2E0B407F0065BA02 /* SettingsResetPINView.swift in Sources */,
24C8875E2F3DCDC100E14910 /* AudioServicesWrapper.swift in Sources */,
246C42A62F250E96009BC881 /* CalloutTextView.swift in Sources */,
- 24F5BD4A2E0442D00010D340 /* SettingsFooterView.swift in Sources */,
24572C762F1E30C0009C72F0 /* BentoNoSendModalView.swift in Sources */,
C312D3962D7DC27700BB97A4 /* LAContext+Extensions.swift in Sources */,
24006C2A2F0FCCA500F29F0A /* BentoSendCompletedView.swift in Sources */,
@@ -2861,6 +2846,7 @@
C312D3FD2D7DC27700BB97A4 /* LocaleChangeViewModel.swift in Sources */,
C312D3FE2D7DC27700BB97A4 /* ReduxState.swift in Sources */,
24A0ACAB2E0C812C0077E695 /* SettingsLitecoinDetailView.swift in Sources */,
+ 89B92973AA76D3E95DC98045 /* SetTrustedNodeIPModalView.swift in Sources */,
24D7AE562DCFF6870078252E /* NewConfirmView.swift in Sources */,
C312D4022D7DC27700BB97A4 /* DrawableCircle.swift in Sources */,
24B0653B2DC6433800B5E000 /* SimpleHeaderView.swift in Sources */,
@@ -3191,7 +3177,7 @@
CODE_SIGN_ENTITLEMENTS = brainwallet/brainwallet.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 2602312;
+ CURRENT_PROJECT_VERSION = 2602318;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEPLOYMENT_POSTPROCESSING = NO;
DEVELOPMENT_TEAM = U8WDVUDC2P;
@@ -3213,7 +3199,7 @@
"$(FRAMEWORK_SEARCH_PATHS)",
);
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/bw-gdlib/";
- MARKETING_VERSION = 3.9.17;
+ MARKETING_VERSION = 3.10.2;
ONLY_ACTIVE_ARCH = YES;
"OTHER_LDFLAGS[sdk=iphoneos*]" = (
"$(inherited)",
@@ -3393,7 +3379,7 @@
CODE_SIGN_ENTITLEMENTS = brainwallet/brainwallet.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 2602312;
+ CURRENT_PROJECT_VERSION = 2602318;
DEPLOYMENT_POSTPROCESSING = YES;
DEVELOPMENT_TEAM = U8WDVUDC2P;
DOCC_EXTRACT_SWIFT_INFO_FOR_OBJC_SYMBOLS = YES;
@@ -3414,7 +3400,7 @@
"$(FRAMEWORK_SEARCH_PATHS)",
);
LIBRARY_SEARCH_PATHS = "$(SRCROOT)/bw-gdlib/";
- MARKETING_VERSION = 3.9.17;
+ MARKETING_VERSION = 3.10.2;
ONLY_ACTIVE_ARCH = YES;
"OTHER_LDFLAGS[arch=*]" = "-ObjC";
"OTHER_LDFLAGS[sdk=iphoneos*]" = (
diff --git a/brainwallet.xcodeproj/xcshareddata/xcodecloud/manifest.json b/brainwallet.xcodeproj/xcshareddata/xcodecloud/manifest.json
new file mode 100644
index 00000000..de842085
--- /dev/null
+++ b/brainwallet.xcodeproj/xcshareddata/xcodecloud/manifest.json
@@ -0,0 +1,9 @@
+{
+ "id" : "1c298c87-65ad-41d7-ba5a-a86216fea7ff",
+ "targets" : [
+ {
+ "id" : "BC978526-32CC-4098-97E9-7A50FD84D5E8",
+ "name" : "brainwallet"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/brainwallet/Additions/UserDefaults+Additions.swift b/brainwallet/Additions/UserDefaults+Additions.swift
index 810f9b66..51fea0db 100644
--- a/brainwallet/Additions/UserDefaults+Additions.swift
+++ b/brainwallet/Additions/UserDefaults+Additions.swift
@@ -1,4 +1,5 @@
import Foundation
+import KeychainAccess
private let defaults = UserDefaults.standard
private let isBiometricsEnabledKey = "isbiometricsenabled"
@@ -12,20 +13,29 @@ private let isLTCValueShownKey = "isLTCValueShownKey"
private let maxDigitsKey = "SETTINGS_MAX_DIGITS"
private let pushTokenKey = "pushTokenKey"
private let currentRateKey = "currentRateKey"
-private let customNodeIPKey = "customNodeIPKey"
-private let customNodePortKey = "customNodePortKey"
private let hasPromptedShareDataKey = "hasPromptedShareDataKey"
private let didSeeTransactionCorruption = "DidSeeTransactionCorruption"
private let hasLoggedInitialSyncDurationKey = "hasLoggedInitialSyncDurationKey"
private let foregroundSyncDurationSecondsKey = "foregroundSyncDurationSecondsKey"
private let pendingNotificationBadgeCountKey = "pendingNotificationBadgeCountKey"
+private let trustedNodeKeychain = Keychain(service: "brainwallet.trustednode")
+private let userTrustedNodeIpAddressKey = "userTrustedNodeIpAddress"
+private let userTrustedNodePortKey = "userTrustedNodePort"
+private let userPurchasedTrustedNodeKey = "userPurchasedTrustedNode"
+
+/// StoreKit product id for the Trusted LTC Node in-app purchase. Must match
+/// "Trusted LTC Node Feature" in BWProductsList.plist (ios-private-general-purpose).
+let trustedLTCNodeProductId = "com.gruntsoftware.brainwallet.trusted_ltc_node_2"
+
let timeSinceLastExitKey = "TimeSinceLastExit"
let shouldRequireLoginTimeoutKey = "ShouldRequireLoginTimeoutKey"
let numberOfBrainwalletLaunches = "NumberOfBrainwalletLaunches"
let appHasRequestedReviewKey = "appHasRequestedReviewKey"
let userDidPreferDarkModeKey = "UserDidPreferDarkMode"
let userCurrentLocaleMPApprovedKey = "UserCurrentLocaleMPApproved"
+let userSetPreferredNetworkFeeKey = "UserSetPreferredNetworkFeeKey"
+let userPrefersUsingTrustedNodeKey = "UserPrefersUsingTrustedNodeKey"
public extension NSNotification.Name {
static let walletBalanceChangedNotification = NSNotification.Name("WalletBalanceChanged")
@@ -48,7 +58,58 @@ public extension NSNotification.Name {
}
extension UserDefaults {
+
+ // MARK: - Trusted Node
+ static var userTrustedNodeIpAddress: String? {
+ get { trustedNodeKeychain[userTrustedNodeIpAddressKey] }
+ set { trustedNodeKeychain[userTrustedNodeIpAddressKey] = newValue }
+ }
+
+ static var userTrustedNodePort: String? {
+ get { trustedNodeKeychain[userTrustedNodePortKey] }
+ set { trustedNodeKeychain[userTrustedNodePortKey] = newValue }
+ }
+
+ static var userTrustedNodePurchased: Bool? {
+ get {
+ guard let value = trustedNodeKeychain[userPurchasedTrustedNodeKey] else { return nil }
+ return value == "true"
+ }
+ set {
+ guard let newValue else {
+ trustedNodeKeychain[userPurchasedTrustedNodeKey] = nil
+ return
+ }
+ trustedNodeKeychain[userPurchasedTrustedNodeKey] = newValue ? "true" : "false"
+ }
+ }
+
+ static func wipeTrustedNodeKeychain() throws {
+ try trustedNodeKeychain.removeAll()
+ }
+ static var userPrefersUsingTrustedNode: Bool {
+ get {
+ guard defaults.object(forKey: userPrefersUsingTrustedNodeKey) != nil
+ else {
+ return true
+ }
+ return defaults.bool(forKey: userPrefersUsingTrustedNodeKey)
+ }
+ set { defaults.set(newValue, forKey: userPrefersUsingTrustedNodeKey) }
+ }
+
+ static var userSetPreferredNetworkFee: Int {
+ get {
+ guard defaults.object(forKey: userSetPreferredNetworkFeeKey) != nil
+ else {
+ return Int(Fees.usingDefaultValues.luxury)
+ }
+ return defaults.integer(forKey: userSetPreferredNetworkFeeKey)
+ }
+ set { defaults.set(newValue, forKey: userSetPreferredNetworkFeeKey) }
+ }
+
static var userCanBuyInCurrentLocale: Bool {
get {
guard defaults.object(forKey: userCurrentLocaleMPApprovedKey) != nil
@@ -186,22 +247,6 @@ extension UserDefaults {
set { defaults.set(newValue, forKey: currentRateKey) }
}
- static var customNodeIP: Int? {
- get {
- guard defaults.object(forKey: customNodeIPKey) != nil else { return nil }
- return defaults.integer(forKey: customNodeIPKey)
- }
- set { defaults.set(newValue, forKey: customNodeIPKey) }
- }
-
- static var customNodePort: Int? {
- get {
- guard defaults.object(forKey: customNodePortKey) != nil else { return nil }
- return defaults.integer(forKey: customNodePortKey)
- }
- set { defaults.set(newValue, forKey: customNodePortKey) }
- }
-
static var hasPromptedShareData: Bool {
get { return defaults.bool(forKey: hasPromptedBiometricsKey) }
set { defaults.set(newValue, forKey: hasPromptedBiometricsKey) }
diff --git a/brainwallet/App Launch Classes/AppDelegate.swift b/brainwallet/App Launch Classes/AppDelegate.swift
index d04489da..52e57add 100644
--- a/brainwallet/App Launch Classes/AppDelegate.swift
+++ b/brainwallet/App Launch Classes/AppDelegate.swift
@@ -9,7 +9,6 @@ import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
- var window: UIWindow?
var applicationController = ApplicationController()
var remoteConfigurationHelper: RemoteConfigHelper?
var resourceRequest: NSBundleResourceRequest?
@@ -79,19 +78,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
object: nil
)
- guard let thisWindow = window else { return false }
-
- // Set global themes
- thisWindow.overrideUserInterfaceStyle = UserDefaults.userPreferredDarkTheme ? .dark: .light
-
- UIView.appearance(whenContainedInInstancesOf:
- [UIAlertController.self])
- .tintColor = BrainwalletUIColor.content
-
- self.applicationController.launch(application: UIApplication.shared, window: thisWindow)
-
+ // Window creation and ApplicationController.launch happen in SceneDelegate.willConnectTo
return true
}
+
+ func application(_ application: UIApplication,
+ configurationForConnecting connectingSceneSession: UISceneSession,
+ options: UIScene.ConnectionOptions) -> UISceneConfiguration {
+ let config = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
+ config.delegateClass = SceneDelegate.self
+ return config
+ }
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
// Receved FCM Token
@@ -124,7 +121,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
}
}
- func applicationDidBecomeActive(_: UIApplication) {
+ /// Called from SceneDelegate.sceneDidBecomeActive
+ func clearNotificationBadge() {
UserDefaults.pendingNotificationBadgeCount = 0
UNUserNotificationCenter.current().setBadgeCount(0) { error in
if let error = error {
@@ -135,23 +133,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
/// AppsFlyerLib.shared().start()
}
- func applicationWillEnterForeground(_: UIApplication) {
- applicationController.willEnterForeground()
- }
-
- func applicationDidEnterBackground(_: UIApplication) {
- applicationController.didEnterBackground()
- }
-
func application(_: UIApplication,
shouldAllowExtensionPointIdentifier _: UIApplication.ExtensionPointIdentifier) -> Bool {
return false // disable extensions such as custom keyboards for security purposes
}
- func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
- return applicationController.open(url: url)
- }
-
func application(_: UIApplication, shouldSaveSecureApplicationState _: NSCoder) -> Bool {
return true
}
@@ -175,7 +161,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
debugPrint(":::: Restarting after wiping wallet")
DispatchQueue.main.async {
- guard let thisWindow = self.window else { return }
+ guard let thisWindow = self.applicationController.window else { return }
thisWindow.rootViewController?.dismiss(animated: false, completion: nil)
@@ -188,7 +174,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
@objc
func updateUserThemePreference() {
DispatchQueue.main.async {
- guard let thisWindow = self.window else { return }
+ guard let thisWindow = self.applicationController.window else { return }
thisWindow.overrideUserInterfaceStyle = UserDefaults.userPreferredDarkTheme ? .dark : .light
}
}
diff --git a/brainwallet/App Launch Classes/SceneDelegate.swift b/brainwallet/App Launch Classes/SceneDelegate.swift
new file mode 100644
index 00000000..53c4ccc3
--- /dev/null
+++ b/brainwallet/App Launch Classes/SceneDelegate.swift
@@ -0,0 +1,51 @@
+import UIKit
+
+/// Owns the app window and forwards scene lifecycle events to ApplicationController.
+/// Required since iOS 27: UIKit traps at launch if the app has not adopted the UIScene lifecycle.
+class SceneDelegate: UIResponder, UIWindowSceneDelegate {
+ var window: UIWindow?
+
+ private var appDelegate: AppDelegate? {
+ UIApplication.shared.delegate as? AppDelegate
+ }
+
+ func scene(_ scene: UIScene,
+ willConnectTo session: UISceneSession,
+ options connectionOptions: UIScene.ConnectionOptions) {
+ guard let windowScene = scene as? UIWindowScene else { return }
+ // Unit tests host the app without launching the wallet UI
+ if ProcessInfo.processInfo.environment["IS_RUNNING_UNIT_TESTS"] == "1" { return }
+
+ let thisWindow = UIWindow(windowScene: windowScene)
+ window = thisWindow
+ thisWindow.overrideUserInterfaceStyle = UserDefaults.userPreferredDarkTheme ? .dark : .light
+
+ UIView.appearance(whenContainedInInstancesOf:
+ [UIAlertController.self])
+ .tintColor = BrainwalletUIColor.content
+
+ appDelegate?.applicationController.launch(application: UIApplication.shared, window: thisWindow)
+
+ // URL used to cold-launch the app
+ if let url = connectionOptions.urlContexts.first?.url {
+ _ = appDelegate?.applicationController.open(url: url)
+ }
+ }
+
+ func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
+ guard let url = URLContexts.first?.url else { return }
+ _ = appDelegate?.applicationController.open(url: url)
+ }
+
+ func sceneDidBecomeActive(_ scene: UIScene) {
+ appDelegate?.clearNotificationBadge()
+ }
+
+ func sceneWillEnterForeground(_ scene: UIScene) {
+ appDelegate?.applicationController.willEnterForeground()
+ }
+
+ func sceneDidEnterBackground(_ scene: UIScene) {
+ appDelegate?.applicationController.didEnterBackground()
+ }
+}
diff --git a/brainwallet/Bento Views/SyncSubBentoView.swift b/brainwallet/Bento Views/SyncSubBentoView.swift
index 941de1ab..86c1eaf8 100644
--- a/brainwallet/Bento Views/SyncSubBentoView.swift
+++ b/brainwallet/Bento Views/SyncSubBentoView.swift
@@ -29,6 +29,7 @@ struct SyncSubBentoView: View {
private let progressBarHeight: CGFloat = 14.0
private let progressIconSize: CGFloat = 13.0
+ private let syncModeIconSize: CGFloat = 10.0
init(viewModel: SyncSubBentoViewModel) {
self.viewModel = viewModel
@@ -40,12 +41,21 @@ struct SyncSubBentoView: View {
Spacer()
HStack {
Spacer()
+ Image(viewModel.peerSyncMode == .random_mainnet ?
+ "main-network-icon" :"trusted-icon")
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: syncModeIconSize,
+ height: syncModeIconSize,
+ alignment: .bottomLeading)
+ .padding(.leading, 12)
Text(viewModel.syncStateMessage)
.modifier(BWIPSSemiBold(size: 11.0))
.frame(alignment: .trailing)
.foregroundColor(.white)
}
+ .frame(width: 160)
.padding(.bottom, 2)
HStack {
Spacer()
diff --git a/brainwallet/Bento Views/Transaction Bentos/TransactionDetailBentoView.swift b/brainwallet/Bento Views/Transaction Bentos/TransactionDetailBentoView.swift
index 50b5ced5..5263b68b 100644
--- a/brainwallet/Bento Views/Transaction Bentos/TransactionDetailBentoView.swift
+++ b/brainwallet/Bento Views/Transaction Bentos/TransactionDetailBentoView.swift
@@ -73,8 +73,6 @@ struct TransactionDetailBentoView: View {
@State
private var qrImage = UIImage()
- private var productsTitle = "Products"
-
init(viewModel: NewMainViewModel,
userPrefersDarkTheme: Binding) {
_userPrefersDarkTheme = userPrefersDarkTheme
@@ -270,13 +268,14 @@ struct TransactionDetailBentoView: View {
}
}
.sheet(isPresented: $shouldShowExportSheet) {
- WalletProductsModalView(title: .constant(productsTitle), data: exportViewModel.transactionData)
- .background(BrainwalletColor.surface)
+ ExportTransactionsModalView(userPrefersDarkTheme: userPrefersDarkTheme,
+ data: exportViewModel.transactionData)
.cornerRadius(bentoCornerRadius)
- .presentationDragIndicator(.hidden)
.presentationDetents([.height(height * 0.7)])
- .presentationBackground(.ultraThickMaterial)
- .ignoresSafeArea(edges: .bottom)
+ .presentationDetents([.medium])
+ .presentationBackground(BrainwalletColor
+ .modalBackground(userPrefersDarkTheme: userPrefersDarkTheme))
+ .presentationDragIndicator(.visible)
}
.onAppear {
mainGradientStyle = userPrefersDarkTheme ? .darkStyle : .lightStyle
diff --git a/brainwallet/Colors/Brainwallet+Color.swift b/brainwallet/Colors/Brainwallet+Color.swift
index a38b1cfe..c969c56b 100644
--- a/brainwallet/Colors/Brainwallet+Color.swift
+++ b/brainwallet/Colors/Brainwallet+Color.swift
@@ -45,6 +45,22 @@ struct BrainwalletColor {
}
+extension BrainwalletColor {
+ /// Modal sheet background: midnight for dark theme, lavender for light.
+ static func modalBackground(userPrefersDarkTheme: Bool) -> Color {
+ userPrefersDarkTheme ? midnight : lavender
+ }
+
+ /// Modal primary (title/product name) text color.
+ static func modalPrimaryText(userPrefersDarkTheme: Bool) -> Color {
+ userPrefersDarkTheme ? .white : nearBlack
+ }
+
+ /// Modal secondary (description/divider) text color.
+ static func modalSecondaryText(userPrefersDarkTheme: Bool) -> Color {
+ userPrefersDarkTheme ? Color.white.opacity(0.7) : nearBlack.opacity(0.7)
+ }
+}
struct BrainwalletUIColor {
/// surface - midnight or white
diff --git a/brainwallet/Emoji Classes/EnterPinForSeedView.swift b/brainwallet/Emoji Classes/EnterPinForSeedView.swift
index b3ac148f..328fe4c8 100644
--- a/brainwallet/Emoji Classes/EnterPinForSeedView.swift
+++ b/brainwallet/Emoji Classes/EnterPinForSeedView.swift
@@ -50,59 +50,76 @@ struct EnterPinForSeedView: View {
let width = geometry.size.width
VStack(alignment: .center) {
- Text("Enter your PIN to fetch the seed words")
- .modifier(BWIPSSemiBold(size: 22.0,
- lineLimit: 2))
- .foregroundColor(.white)
- .multilineTextAlignment(.center)
- .padding([.leading, .trailing], 40.0)
- .padding([.top, .bottom], 44.0)
- .accessibilityIdentifier("enterPINFetchSeedTitle")
-
-
- TextField(String(localized:"Enter PIN"),
- text: $enteredPin)
- .font(.ibmPlexSansRegular(size: 20))
- .multilineTextAlignment(.center)
- .keyboardType(.numberPad)
- .textFieldStyle(TranslucentWhiteTextFieldStyle())
- .frame(width: width * 0.4, alignment: .center)
- .onChange(of: enteredPin) { _,newValue in
- if newValue.count == 4 {
- if let walletManager = walletManager,
- let phrase = walletManager.seedPhrase(pin: newValue) {
- seedPhrase = phrase
- pinWasEnteredCorrectly = true
- keyboardFocused = false
+ ZStack {
+ HStack {
+ Button(action: {
+ withAnimation {
+ selectedPage = 0
+ }
+ }) {
+ Image(systemName: "xmark")
+ .resizable()
+ .frame(width: 20, height: 20)
+ .foregroundColor(.white)
}
+ Spacer()
}
+ .padding(.top, 30.0)
+ .padding(.leading, 24.0)
+ .frame(height: 50.0)
}
- .focused($keyboardFocused)
- .accessibilityIdentifier("enterPINFetchSeedTextField")
-
-
- Spacer()
-
- Button(action: {
- if pinWasEnteredCorrectly {
- selectedPage += 1
- }
- }) {
- ZStack {
- RoundedRectangle(cornerRadius: 8)
- .fill(.white)
- Text("Continue")
- .modifier(BWIPSSemiBold(size: 19.0))
- .foregroundColor(enteredPin.isEmpty ?
- BrainwalletColor.midnight.opacity(0.5) : BrainwalletColor.midnight)
+ Text("Enter your PIN to fetch the seed words")
+ .modifier(BWIPSSemiBold(size: 22.0,
+ lineLimit: 2))
+ .foregroundColor(.white)
+ .multilineTextAlignment(.center)
+ .padding([.leading, .trailing], 40.0)
+ .padding([.top, .bottom], 44.0)
+ .accessibilityIdentifier("enterPINFetchSeedTitle")
+
+ SecureField(String(localized:"Enter PIN"),
+ text: $enteredPin)
+ .font(.ibmPlexSansRegular(size: 20))
+ .multilineTextAlignment(.center)
+ .foregroundColor(.white)
+ .keyboardType(.numberPad)
+ .textFieldStyle(TranslucentWhiteTextFieldStyle())
+ .frame(width: width * 0.4, alignment: .center)
+ .onChange(of: enteredPin) { _,newValue in
+ if newValue.count == 4 {
+ if let walletManager = walletManager,
+ let phrase = walletManager.seedPhrase(pin: newValue) {
+ seedPhrase = phrase
+ pinWasEnteredCorrectly = true
+ keyboardFocused = false
+ }
+ }
}
- }
- .frame(width: width * 0.9, height: 50.0, alignment: .center)
- .cornerRadius(11.0)
- .padding( .bottom, 16.0)
- .disabled(!pinWasEnteredCorrectly)
+ .focused($keyboardFocused)
+ .accessibilityIdentifier("enterPINFetchSeedTextField")
+
+ Spacer()
+
+ Button(action: {
+ if pinWasEnteredCorrectly {
+ selectedPage += 1
+ }
+ }) {
+ ZStack {
+ RoundedRectangle(cornerRadius: 8)
+ .fill(.white)
+ Text("Continue")
+ .modifier(BWIPSSemiBold(size: 19.0))
+ .foregroundColor(enteredPin.isEmpty ?
+ BrainwalletColor.midnight.opacity(0.5) : BrainwalletColor.midnight)
+ }
+ }
+ .frame(width: width * 0.9, height: 50.0, alignment: .center)
+ .cornerRadius(11.0)
+ .padding( .bottom, 16.0)
+ .disabled(!pinWasEnteredCorrectly)
}
.frame(width: width, height: height, alignment: .center)
.onDisappear {
diff --git a/brainwallet/Emoji Classes/PickEmojisView.swift b/brainwallet/Emoji Classes/PickEmojisView.swift
index ae8e820c..e9c84af2 100644
--- a/brainwallet/Emoji Classes/PickEmojisView.swift
+++ b/brainwallet/Emoji Classes/PickEmojisView.swift
@@ -31,13 +31,7 @@ struct PickEmojisView: View {
private var thirdEmojiString: String = ""
@State
- private var firstEmojiSelected = false
-
- @State
- private var secondEmojiSelected = false
-
- @State
- private var thirdEmojiSelected = false
+ private var selectedField: SetField? = .firstField
@State
private var shouldShowSeedWords: Bool = false
@@ -45,8 +39,13 @@ struct PickEmojisView: View {
@Binding
var selectedPage: Int
- @FocusState
- private var focusedField: SetField?
+ private func selection(for field: SetField) -> Binding {
+ Binding(get: { selectedField == field },
+ set: { isOn in
+ if isOn { selectedField = field }
+ else if selectedField == field { selectedField = nil }
+ })
+ }
let backgroundGradient: LinearGradient
@@ -141,48 +140,48 @@ struct PickEmojisView: View {
.padding(.top, 30.0)
}
Text("Tap a box. Show or hide words to choose your emojis. Tap an emoji. Each emoji must be different.")
- .modifier(BWIPSRegular(size: 18, lineLimit: 3))
+ .modifier(BWIPSRegular(size: 20, lineLimit: 3))
.foregroundColor(.white)
.multilineTextAlignment(.center)
- .frame(height: 44.0)
- .padding([.leading, .trailing], 50.0)
+ .frame(height: 60.0)
+ .padding([.leading, .trailing], 30.0)
.padding(.top, 10.0)
.padding(.bottom, 10.0)
.accessibilityIdentifier("howItWorksViewDescription")
HStack {
- SetEmojiView(isSelected: $firstEmojiSelected,
+ SetEmojiView(isSelected: selection(for: .firstField),
seedWord: firstWord,
emojiString: $firstEmojiString,
shouldShowWord: $shouldShowSeedWords,
- viewIndex: 1)
+ viewIndex: 1,
+ isActive: selectedPage == 2)
.frame(width: width * setFactor)
.frame(height: emojiRowFactor)
.background(emojisFieldGradient)
.cornerRadius(16.0)
- .focused($focusedField, equals: .firstField)
- SetEmojiView(isSelected: $secondEmojiSelected,
+ SetEmojiView(isSelected: selection(for: .secondField),
seedWord: secondWord,
emojiString: $secondEmojiString,
shouldShowWord: $shouldShowSeedWords,
- viewIndex: 2)
+ viewIndex: 2,
+ isActive: selectedPage == 2)
.frame(width: width * setFactor)
.frame(height: emojiRowFactor)
.background(emojisFieldGradient)
.cornerRadius(16.0)
- .focused($focusedField, equals: .secondField)
- SetEmojiView(isSelected: $thirdEmojiSelected,
+ SetEmojiView(isSelected: selection(for: .thirdField),
seedWord: thirdWord,
emojiString: $thirdEmojiString,
shouldShowWord: $shouldShowSeedWords,
- viewIndex: 3)
+ viewIndex: 3,
+ isActive: selectedPage == 2)
.frame(width: width * setFactor)
.frame(height: emojiRowFactor)
.background(emojisFieldGradient)
.cornerRadius(16.0)
- .focused($focusedField, equals: .thirdField)
}
.frame(width: width * 0.95)
@@ -230,14 +229,9 @@ struct PickEmojisView: View {
.padding( .bottom, 16.0)
.disabled(!uniqueEmojisSelected)
}
- .background(backgroundGradient)
+ .background(backgroundGradient)
}
.ignoresSafeArea(.keyboard)
- .onChange(of: focusedField) { _, newValue in
- firstEmojiSelected = newValue == .firstField
- secondEmojiSelected = newValue == .secondField
- thirdEmojiSelected = newValue == .thirdField
- }
.onChange(of: firstEmojiString) { _, _ in uniqueEmojisSelected = areUniqueEmojisSelected() }
.onChange(of: secondEmojiString) { _, _ in uniqueEmojisSelected = areUniqueEmojisSelected() }
.onChange(of: thirdEmojiString) { _, _ in uniqueEmojisSelected = areUniqueEmojisSelected() }
diff --git a/brainwallet/Emoji Classes/SetEmojiView.swift b/brainwallet/Emoji Classes/SetEmojiView.swift
index 27e64973..e99c5d8d 100644
--- a/brainwallet/Emoji Classes/SetEmojiView.swift
+++ b/brainwallet/Emoji Classes/SetEmojiView.swift
@@ -25,6 +25,9 @@ struct SetEmojiView: View {
@Binding
var shouldShowWord: Bool
+ /// False while this page is off-screen so the keyboard doesn't pop early.
+ var isActive: Bool = true
+
private let emojisViewGradient = LinearGradient(colors: [BentoColor.color14134C.opacity(0.5),
BentoColor.color5827E2.opacity(0.1),
@@ -35,7 +38,9 @@ struct SetEmojiView: View {
seedWord: String,
emojiString: Binding,
shouldShowWord: Binding,
- viewIndex: Int) {
+ viewIndex: Int,
+ isActive: Bool = true) {
+ self.isActive = isActive
_isSelected = isSelected
_shouldShowWord = shouldShowWord
_emoji = emojiString
@@ -88,7 +93,9 @@ struct SetEmojiView: View {
.stroke(isSelected ? .white :.clear, lineWidth: 2.0)
EmojiTextField(text: $emoji,
- userPrefersDarkTheme: .constant(true))
+ userPrefersDarkTheme: .constant(true),
+ isFirstResponder: isSelected && isActive,
+ onBeginEditing: { isSelected = true })
.onChange(of: emoji) { _,_ in
if !emoji.isEmpty {
diff --git a/brainwallet/Emoji Classes/UIEmojiTextField.swift b/brainwallet/Emoji Classes/UIEmojiTextField.swift
index 94595039..e6ca60d2 100644
--- a/brainwallet/Emoji Classes/UIEmojiTextField.swift
+++ b/brainwallet/Emoji Classes/UIEmojiTextField.swift
@@ -41,6 +41,35 @@ class UIEmojiTextField: UITextField {
return nil
}
+ /// Set by SwiftUI; honoured as soon as the field is in a window,
+ /// because becomeFirstResponder() silently fails before that.
+ var wantsFocus = false
+
+ private var focusWorkItem: DispatchWorkItem?
+
+ /// Shows the keyboard after a delay; the box border is not affected.
+ func requestFocus(after delay: TimeInterval = 1.0) {
+ focusWorkItem?.cancel()
+ let item = DispatchWorkItem { [weak self] in
+ guard let self, self.wantsFocus, self.window != nil, !self.isFirstResponder else { return }
+ self.becomeFirstResponder()
+ }
+ focusWorkItem = item
+ DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: item)
+ }
+
+ func dropFocus() {
+ focusWorkItem?.cancel()
+ if isFirstResponder { resignFirstResponder() }
+ }
+
+ override func didMoveToWindow() {
+ super.didMoveToWindow()
+ if window != nil && wantsFocus && !isFirstResponder {
+ requestFocus()
+ }
+ }
+
override func layoutSubviews() {
super.layoutSubviews()
// Update frame when layout changes
@@ -55,6 +84,9 @@ struct EmojiTextField: UIViewRepresentable {
@Binding var text: String
@Binding var userPrefersDarkTheme: Bool
var placeholder: String = ""
+ /// nil = unmanaged (user taps decide). true/false = drive the keyboard.
+ var isFirstResponder: Bool?
+ var onBeginEditing: (() -> Void)?
func makeUIView(context: Context) -> UIEmojiTextField {
@@ -66,12 +98,23 @@ struct EmojiTextField: UIViewRepresentable {
textField.backgroundColor = .clear
textField.tintColor = UIColor(.white)
textField.delegate = context.coordinator
+ textField.wantsFocus = isFirstResponder ?? false
return textField
}
func updateUIView(_ uiView: UIEmojiTextField, context: Context) {
uiView.text = text
uiView.isEmoji = true
+ context.coordinator.parent = self
+
+ guard let wantsFocus = isFirstResponder else { return }
+ let changed = uiView.wantsFocus != wantsFocus
+ uiView.wantsFocus = wantsFocus
+ if wantsFocus && changed && uiView.window != nil {
+ uiView.requestFocus()
+ } else if !wantsFocus {
+ uiView.dropFocus()
+ }
}
func makeCoordinator() -> Coordinator {
@@ -85,6 +128,10 @@ struct EmojiTextField: UIViewRepresentable {
self.parent = parent
}
+ func textFieldDidBeginEditing(_ textField: UITextField) {
+ parent.onBeginEditing?()
+ }
+
func textFieldDidChangeSelection(_ textField: UITextField) {
Task {
parent.text = textField.text ?? ""
diff --git a/brainwallet/FeeManager.swift b/brainwallet/FeeManager.swift
index 5d4b0a47..6cc29996 100644
--- a/brainwallet/FeeManager.swift
+++ b/brainwallet/FeeManager.swift
@@ -19,3 +19,38 @@ struct Fees: Equatable {
timestamp: defaultTimestamp)
}
}
+
+/// The 3 selectable fee-per-kb tiers shown by the Settings network fee picker
+/// (SettingsLitecoinDetailView), split out from the view so the math is unit-testable
+/// without instantiating SwiftUI state.
+enum NetworkFeeTier: Int, CaseIterable {
+ case economy = 0
+ case regular = 1
+ case luxury = 2
+
+ /// The tier's fee-per-kb, in litoshis.
+ func litoshis(from fees: Fees) -> UInt64 {
+ switch self {
+ case .economy: return fees.economy
+ case .regular: return fees.regular
+ case .luxury: return fees.luxury
+ }
+ }
+
+ /// The tier whose fee (in litoshis) is numerically closest to `storedLitoshis`
+ /// (e.g. a previously persisted `UserDefaults.userSetPreferredNetworkFee`).
+ /// Ties favor the lower tier (economy over regular, regular over luxury).
+ static func closest(to storedLitoshis: Int, in fees: Fees) -> NetworkFeeTier {
+ allCases.min {
+ abs(Int($0.litoshis(from: fees)) - storedLitoshis) < abs(Int($1.litoshis(from: fees)) - storedLitoshis)
+ } ?? .regular
+ }
+
+ /// Converts a litoshi fee-per-kb into a formatted fiat string (e.g. "12.34 USD"),
+ /// or "" when the calculated amount is zero (no fee, or no rate loaded yet).
+ static func formattedFiatAmount(litoshis: UInt64, rate: Rate) -> String {
+ let feeInLTC = Double(litoshis) / Double(C.litoshis)
+ let calculation = feeInLTC * rate.rate
+ return calculation == 0 ? "" : String(format: "%.3f %@", calculation, rate.code)
+ }
+}
diff --git a/brainwallet/Info.plist b/brainwallet/Info.plist
index 8b890d1b..09fc365e 100644
--- a/brainwallet/Info.plist
+++ b/brainwallet/Info.plist
@@ -2,8 +2,6 @@
- FirebaseAppDelegateProxyEnabled
-
BGTaskSchedulerPermittedIdentifiers
ltd.grunt.brainwallet
@@ -35,10 +33,17 @@
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
+ FirebaseAppDelegateProxyEnabled
+
ITSAppUsesNonExemptEncryption
LSApplicationCategoryType
+ LSApplicationQueriesSchemes
+
+ instagram-stories
+ twitter
+
LSRequiresIPhoneOS
LSSupportsOpeningDocumentsInPlace
@@ -121,6 +126,8 @@
processing
remote-notification
+ UIDesignRequiresCompatibility
+
UILaunchScreen
UIImageName
@@ -128,8 +135,23 @@
UILaunchStoryboardName
LaunchScreen.storyboard
- UIMainStoryboardFile
- LaunchScreen
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneConfigurationName
+ Default Configuration
+ UISceneDelegateClassName
+ $(PRODUCT_MODULE_NAME).SceneDelegate
+
+
+
+
UIRequiredDeviceCapabilities
armv7
@@ -215,12 +237,5 @@
- UIDesignRequiresCompatibility
-
- LSApplicationQueriesSchemes
-
- instagram-stories
- twitter
-
diff --git a/brainwallet/Legacy_BW_BRClasses/BRPeerManager.swift b/brainwallet/Legacy_BW_BRClasses/BRPeerManager.swift
index 8e0053b6..3fc39853 100644
--- a/brainwallet/Legacy_BW_BRClasses/BRPeerManager.swift
+++ b/brainwallet/Legacy_BW_BRClasses/BRPeerManager.swift
@@ -1,11 +1,18 @@
import BRCore
import Foundation
+
+enum PeerSyncMode: String {
+ case random_mainnet
+ case trusted
+}
+
class BRPeerManager {
let cPointer: OpaquePointer
let listener: BRPeerManagerListener
let mainNetParams = [BRMainNetParams]
var falsePositiveRate: Double
+ var syncMode: PeerSyncMode = .random_mainnet
init?(wallet: BRWallet,
earliestKeyTime: TimeInterval,
@@ -65,12 +72,31 @@ class BRPeerManager {
// connect to litecoin peer-to-peer network (also call this whenever networkIsReachable() status changes)
func connect() {
- if let fixedAddress = UserDefaults.customNodeIP {
- setFixedPeer(address: fixedAddress, port: UserDefaults.customNodePort ?? C.standardPort)
- }
+ applyTrustedNodePreference()
BRPeerManagerConnect(cPointer)
}
+ private func applyTrustedNodePreference() {
+ guard UserDefaults.userPrefersUsingTrustedNode,
+ let ipAddress = UserDefaults.userTrustedNodeIpAddress,
+ let packedAddress = BRPeerManager.packedIPv4Address(ipAddress)
+ else {
+ setFixedPeer(address: 0, port: 0)
+ syncMode = .random_mainnet
+ return
+ }
+ syncMode = .trusted
+ let port = UserDefaults.userTrustedNodePort.flatMap(Int.init) ?? C.standardPort
+ setFixedPeer(address: packedAddress, port: port)
+ }
+
+ // packs a dotted-quad IPv4 string into the network-byte-order integer setFixedPeer expects
+ private static func packedIPv4Address(_ ipAddress: String) -> Int? {
+ var addr = in_addr()
+ guard inet_pton(AF_INET, ipAddress, &addr) == 1 else { return nil }
+ return Int(addr.s_addr)
+ }
+
// disconnect from litecoin peer-to-peer network
func disconnect() {
BRPeerManagerDisconnect(cPointer)
diff --git a/brainwallet/Localizations/Localizable.xcstrings b/brainwallet/Localizations/Localizable.xcstrings
index 7c93f511..bdac7045 100644
--- a/brainwallet/Localizations/Localizable.xcstrings
+++ b/brainwallet/Localizations/Localizable.xcstrings
@@ -123,137 +123,6 @@
},
"shouldTranslate" : false
},
- " Each emoji must be different." : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " يجب أن تكون كل رمز تعبيري مختلفاً."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Jedes Emoji muss unterschiedlich sein."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Cada emoji debe ser diferente."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " هر ایموجی باید متفاوت باشد."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Chaque emoji doit être différent."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " प्रत्येक emoji अलग होना चाहिए।"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Setiap emoji harus berbeda."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Ogni emoji deve essere diverso."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " 各絵文字は異なる必要があります。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " 각 이모지는 달라야 합니다."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Elke emoji moet verschillend zijn."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " ਹਰੇਕ emoji ਵੱਖਰਾ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Każdy emoji musi być inny."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Cada emoji deve ser diferente."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Каждый эмодзи должен быть разным."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Varje emoji måste vara olika."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " แต่ละ emoji ต้องแตกต่างกัน"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Her emoji farklı olmalıdır."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " Кожен емодзі має бути різним."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "每个表情符号必须不同。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : " 每個表情符號必須不同。"
- }
- }
- }
- },
" Home " : {
"localizations" : {
"ar" : {
@@ -1803,6 +1672,136 @@
}
}
},
+ "%llu ł" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "%llu ł"
+ }
+ }
+ }
+ },
"1. Check your balance" : {
"comment" : "Title for the first step in the send flow.",
"isCommentAutoGenerated" : true,
@@ -2595,6 +2594,136 @@
}
}
},
+ "A higher value means the transaction completes sooner" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "القيمة الأعلى تعني أن المعاملة تكتمل بشكل أسرع"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Ein höherer Wert bedeutet, dass die Transaktion schneller abgeschlossen wird"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Un valor más alto significa que la transacción se completa más rápido"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "مقدار بالاتر به معنای تکمیل سریعتر تراکنش است"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Une valeur plus élevée signifie que la transaction se termine plus rapidement"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "उच्च मान का मतलब है कि लेनदेन जल्दी पूरा हो जाएगा"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Nilai yang lebih tinggi berarti transaksi selesai lebih cepat"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Un valore più alto significa che la transazione si completa più velocemente"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "値が高いほど、トランザクションがより早く完了します"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "높은 값일수록 거래가 더 빨리 완료됩니다"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Een hogere waarde betekent dat de transactie sneller wordt voltooid"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ਉੱਚਾ ਮੁੱਲ ਮਤਲਬ ਲੈਣ-ਦੇਣ ਜਲਦੀ ਪੂਰਾ ਹੋਵੇਗਾ"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Wyższa wartość oznacza szybsze zakończenie transakcji"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Um valor mais alto significa que a transação é concluída mais rapidamente"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Более высокое значение означает более быстрое завершение транзакции"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Ett högre värde betyder att transaktionen slutförs snabbare"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ค่าที่สูงขึ้นหมายความว่าธุรกรรมเสร็จสิ้นเร็วขึ้น"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Daha yüksek bir değer işlemin daha hızlı tamamlanmasını sağlar"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Більша вартість означає швидше завершення транзакції"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "数值越高,交易完成速度越快"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "較高的數值表示交易完成速度更快"
+ }
+ }
+ }
+ },
"Add 3 more emojis, and you have a dozen." : {
"comment" : "Guide detail text for the final emoji triplet selection screen.",
"isCommentAutoGenerated" : true,
@@ -7032,6 +7161,136 @@
}
}
},
+ "Choose preferred fee:" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "اختر الرسوم المفضلة:"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Bevorzugte Gebühr wählen:"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Elige la comisión preferida:"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "کمیسیون مورد نظر را انتخاب کنید:"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Choisir les frais préférés :"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "पसंदीदा शुल्क चुनें:"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Pilih biaya yang diinginkan:"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Scegli la commissione preferita:"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "希望する手数料を選択:"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "선호하는 수수료 선택:"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Kies de gewenste vergoeding:"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ਪਸੰਦਦਾ ਫੀਸ ਚੁਣੋ:"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Wybierz preferowaną opłatę:"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Escolha a taxa preferida:"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Выберите предпочитаемую комиссию:"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Välj föredragen avgift:"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "เลือกค่าธรรมเนียมที่ต้องการ:"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Tercih edilen ücreti seçin:"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Виберіть бажану комісію:"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "选择首选费用:"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "選擇偏好的手續費:"
+ }
+ }
+ }
+ },
"Choose your 3 emojis for the game" : {
"localizations" : {
"ar" : {
@@ -9115,137 +9374,6 @@
}
}
},
- "Currency" : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "عملة"
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Währung"
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Moneda"
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ارز"
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Monnaie"
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "मुद्रा"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Mata uang"
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Valuta"
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "通貨"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "통화"
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Valuta"
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਮੁਦਰਾ"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Waluta"
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Moeda"
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Валюта"
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Valuta"
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "สกุลเงิน"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Para Birimi"
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Валюта"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "货币"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "貨幣"
- }
- }
- }
- },
"Dark Mode" : {
"localizations" : {
"ar" : {
@@ -10808,264 +10936,132 @@
}
}
},
- "Enter your current PIN." : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "أدخل رقم التعريف الشخصي الحالي الخاص بك."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Geben Sie Ihre aktuelle PIN ein."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ingrese su PIN actual."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "پین فعلی خود را وارد کنید."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Entrez votre code PIN actuel."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "अपना वर्तमान पिन दर्ज करें."
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Masukkan PIN Anda saat ini."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Inserisci il tuo PIN attuale."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "現在のPINを入力してください。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "현재 PIN을 입력하세요."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Voer uw huidige PIN in."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਆਪਣਾ ਮੌਜੂਦਾ ਪਿੰਨ ਦਰਜ ਕਰੋ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Wprowadź aktualny kod PIN."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Digite seu PIN atual."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Введите свой текущий PIN-код."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ange din nuvarande PIN-kod."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ป้อน PIN ปัจจุบันของคุณ"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Geçerli PIN kodunuzu girin."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Введіть свій поточний PIN-код."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "请输入您当前的PIN码。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "輸入您目前的 PIN。"
- }
- }
- }
- },
- "Enter your new PIN." : {
- "extractionState" : "stale",
+ "Enter the IPv4 address and port of the Litecoin node your Brainwallet should sync with." : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "أدخل رقم التعريف الشخصي الجديد الخاص بك."
+ "value" : "أدخل عنوان IPv4 والمنفذ لعقدة Litecoin التي يجب أن يتزامن معها Brainwallet."
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Geben Sie Ihre neue PIN ein."
+ "value" : "Geben Sie die IPv4-Adresse und den Port des Litecoin-Knotens ein, mit dem sich Ihr Brainwallet synchronisieren soll."
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ingrese su nuevo PIN."
+ "value" : "Ingresa la dirección IPv4 y el puerto del nodo Litecoin con el que tu Brainwallet debe sincronizarse."
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "رمز جدید خود را وارد کنید."
+ "value" : "آدرس IPv4 و درگاه گره Litecoin را که Brainwallet باید با آن همگامسازی شود وارد کنید."
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Entrez votre nouveau code PIN."
+ "value" : "Entrez l'adresse IPv4 et le port du nœud Litecoin avec lequel votre Brainwallet doit se synchroniser."
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "अपना नया पिन दर्ज करें."
+ "value" : "Litecoin नोड का IPv4 पता और पोर्ट दर्ज करें जिसके साथ आपका Brainwallet सिंक होना चाहिए।"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Masukkan PIN baru Anda."
+ "value" : "Masukkan alamat IPv4 dan port node Litecoin yang harus disinkronkan dengan Brainwallet Anda."
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Inserisci il tuo nuovo PIN."
+ "value" : "Inserisci l'indirizzo IPv4 e la porta del nodo Litecoin con cui Brainwallet deve sincronizzarsi."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "新しいPINを入力してください。"
+ "value" : "Brainwalletが同期するLitecoinノードのIPv4アドレスとポートを入力してください。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "새 PIN을 입력합니다."
+ "value" : "Brainwallet이 동기화할 Litecoin 노드의 IPv4 주소와 포트를 입력하세요."
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Voer uw nieuwe PIN in."
+ "value" : "Voer het IPv4-adres en de poort in van het Litecoin-knooppunt waarmee uw Brainwallet moet synchroniseren."
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਆਪਣਾ ਨਵਾਂ ਪਿੰਨ ਦਰਜ ਕਰੋ।"
+ "value" : "ਉਹ IPv4 ਪਤਾ ਅਤੇ ਪੋਰਟ ਦਰਜ ਕਰੋ ਜਿਸ ਨਾਲ ਤੁਹਾਡਾ Brainwallet ਸਿੰਕ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ।"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Wprowadź nowy kod PIN."
+ "value" : "Wprowadź adres IPv4 i port węzła Litecoin, z którym powinien synchronizować się Brainwallet."
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Digite seu novo PIN."
+ "value" : "Digite o endereço IPv4 e a porta do nó Litecoin com o qual sua Brainwallet deve sincronizar."
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Введите новый PIN-код."
+ "value" : "Введите IPv4-адрес и порт узла Litecoin, с которым должен синхронизироваться ваш Brainwallet."
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ange din nya PIN-kod."
+ "value" : "Ange IPv4-adressen och porten för den Litecoin-nod som din Brainwallet ska synkroniseras med."
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ป้อน PIN ใหม่ของคุณ"
+ "value" : "ป้อน IPv4 address และ port ของโหนด Litecoin ที่ Brainwallet ของคุณควรซิงค์กับ"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Yeni PIN kodunuzu girin."
+ "value" : "Brainwallet'ınızın senkronize olacağı Litecoin düğümünün IPv4 adresini ve portunu girin."
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Введіть свій новий PIN-код."
+ "value" : "Введіть IPv4 адресу та порт вузла Litecoin, з яким повинен синхронізуватися ваш Brainwallet."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "请输入您的新密码。"
+ "value" : "输入您的 Brainwallet 应同步的 Litecoin 节点的 IPv4 地址和端口。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "輸入您的新 PIN。"
+ "value" : "輸入您的 Brainwallet 應同步的 Litecoin 節點的 IPv4 位址和連接埠。"
}
}
}
@@ -11330,655 +11326,654 @@
}
}
},
- "Exchange Rate" : {
- "extractionState" : "stale",
+ "Exit" : {
+ "comment" : "A button label that says \"Exit\".",
+ "isCommentAutoGenerated" : true,
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "سعر الصرف"
+ "value" : "مخرج"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Wechselkurse"
+ "value" : "Ausfahrt"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Tipo de Cambio"
+ "value" : "Salida"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "نرخ ارز"
+ "value" : "خارج شوید"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Taux de change"
+ "value" : "Sortie"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "विनिमय दर"
+ "value" : "बाहर निकलना"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Nilai Tukar"
+ "value" : "KELUAR"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Tasso di cambio"
+ "value" : "Uscita"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "為替レート"
+ "value" : "出口"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "환율"
+ "value" : "출구"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Wisselkoers"
+ "value" : "Afsluiten"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਐਕਸਚੇਂਜ ਦਰ"
+ "value" : "ਨਿਕਾਸ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Kurs wymiany"
+ "value" : "Wyjście"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Taxa de câmbio"
+ "value" : "Saída"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Обменный курс"
+ "value" : "Выход"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Växelkurs"
+ "value" : "Utgång"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "อัตราแลกเปลี่ยน"
+ "value" : "ออก"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Döviz Kuru"
+ "value" : "Utgång"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Обмінний курс"
+ "value" : "Вихід"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "汇率"
+ "value" : "出口"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "匯率"
+ "value" : "出口"
}
}
}
},
- "Exit" : {
- "comment" : "A button label that says \"Exit\".",
- "isCommentAutoGenerated" : true,
+ "Export Transaction Data" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "مخرج"
+ "value" : "تصدير بيانات المعاملة"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ausfahrt"
+ "value" : "Transaktionsdaten exportieren"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Salida"
+ "value" : "Exportar datos de transacciones"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "خارج شوید"
+ "value" : "صادرات دادههای تراکنش"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Sortie"
+ "value" : "Exporter les données de la transaction"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "बाहर निकलना"
+ "value" : "निर्यात लेनदेन डेटा"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "KELUAR"
+ "value" : "Ekspor Data Transaksi"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Uscita"
+ "value" : "Esportazione dei dati delle transazioni"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "出口"
+ "value" : "取引データのエクスポート"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "출구"
+ "value" : "수출 거래 데이터"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Afsluiten"
+ "value" : "Transactiegegevens exporteren"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਨਿਕਾਸ"
+ "value" : "ਲੈਣ-ਦੇਣ ਡੇਟਾ ਨਿਰਯਾਤ ਕਰੋ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Wyjście"
+ "value" : "Eksport danych transakcji"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Saída"
+ "value" : "Exportar dados de transação"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Выход"
+ "value" : "Экспорт данных транзакции"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Utgång"
+ "value" : "Exportera transaktionsdata"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ออก"
+ "value" : "ส่งออกข้อมูลธุรกรรม"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Utgång"
+ "value" : "İşlem Verilerini Dışa Aktarma"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Вихід"
+ "value" : "Експорт даних транзакцій"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "出口"
+ "value" : "导出交易数据"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "出口"
+ "value" : "匯出交易數據"
}
}
}
},
- "Export Transaction Data" : {
+ "Face ID Spending Limit" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "تصدير بيانات المعاملة"
+ "value" : "حد الإنفاق باستخدام التعرف على الوجه"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Transaktionsdaten exportieren"
+ "value" : "Ausgabenlimit für Face ID"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Exportar datos de transacciones"
+ "value" : "Límite de Gasto de Face ID"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "صادرات دادههای تراکنش"
+ "value" : "حد مخارج فیس آیدی"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Exporter les données de la transaction"
+ "value" : "Limite de dépenses Face ID"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "निर्यात लेनदेन डेटा"
+ "value" : "फेस आईडी खर्च सीमा"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ekspor Data Transaksi"
+ "value" : "Batas Pengeluaran ID Wajah"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Esportazione dei dati delle transazioni"
+ "value" : "Limite di spesa per Face ID"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "取引データのエクスポート"
+ "value" : "フェイスID 支払い限度額"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "수출 거래 데이터"
+ "value" : "Face ID 지출 한도"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Transactiegegevens exporteren"
+ "value" : "Face ID-uitgavenlimiet"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਲੈਣ-ਦੇਣ ਡੇਟਾ ਨਿਰਯਾਤ ਕਰੋ"
+ "value" : "ਫੇਸ ਆਈਡੀ ਖਰਚ ਸੀਮਾ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Eksport danych transakcji"
+ "value" : "Limit wydatków na Face ID"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Exportar dados de transação"
+ "value" : "Limite de gastos do Face ID"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Экспорт данных транзакции"
+ "value" : "Лимит расходов Face ID"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Exportera transaktionsdata"
+ "value" : "Ansiktsgräns för utgifter"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ส่งออกข้อมูลธุรกรรม"
+ "value" : "ขีดจำกัดการใช้จ่าย Face ID"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "İşlem Verilerini Dışa Aktarma"
+ "value" : "Face ID Harcama Limiti"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Експорт даних транзакцій"
+ "value" : "Ліміт витрат Face ID"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "导出交易数据"
+ "value" : "面容ID消费限额"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "匯出交易數據"
+ "value" : "面容 ID 消費限額"
}
}
}
},
- "Face ID Spending Limit" : {
+ "Failed" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "حد الإنفاق باستخدام التعرف على الوجه"
+ "value" : "فشل"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ausgabenlimit für Face ID"
+ "value" : "Fehlgeschlagen"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Límite de Gasto de Face ID"
+ "value" : "Fallido"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "حد مخارج فیس آیدی"
+ "value" : "شکست خورده"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Limite de dépenses Face ID"
+ "value" : "Raté"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "फेस आईडी खर्च सीमा"
+ "value" : "असफल"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Batas Pengeluaran ID Wajah"
+ "value" : "Gagal"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Limite di spesa per Face ID"
+ "value" : "Fallito"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "フェイスID 支払い限度額"
+ "value" : "失敗"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Face ID 지출 한도"
+ "value" : "실패"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Face ID-uitgavenlimiet"
+ "value" : "Mislukt"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਫੇਸ ਆਈਡੀ ਖਰਚ ਸੀਮਾ"
+ "value" : "ਅਸਫਲ ਰਿਹਾ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Limit wydatków na Face ID"
+ "value" : "Nie powiodło się"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Limite de gastos do Face ID"
+ "value" : "Falha"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Лимит расходов Face ID"
+ "value" : "Не удалось"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ansiktsgräns för utgifter"
+ "value" : "Misslyckades"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ขีดจำกัดการใช้จ่าย Face ID"
+ "value" : "ล้มเหลว"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Face ID Harcama Limiti"
+ "value" : "Başarısız"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ліміт витрат Face ID"
+ "value" : "Не вдалося"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "面容ID消费限额"
+ "value" : "失败"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "面容 ID 消費限額"
+ "value" : "失敗的"
}
}
}
},
- "Failed" : {
+ "Failed to wipe wallet" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "فشل"
+ "value" : "فشل مسح المحفظة"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Fehlgeschlagen"
+ "value" : "Wallet konnte nicht gelöscht werden"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Fallido"
+ "value" : "No se pudo borrar la billetera"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "شکست خورده"
+ "value" : "پاکسازی کیفپول ناموفق بود"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Raté"
+ "value" : "Impossible d'effacer le portefeuille"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "असफल"
+ "value" : "वॉलेट को मिटाने में विफल"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Gagal"
+ "value" : "Gagal menghapus dompet"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Fallito"
+ "value" : "Cancellazione del portafoglio non riuscita"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "失敗"
+ "value" : "ウォレットのワイプに失敗しました"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "실패"
+ "value" : "지갑 초기화 실패"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Mislukt"
+ "value" : "Portemonnee wissen mislukt"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਅਸਫਲ ਰਿਹਾ"
+ "value" : "ਵਾਲਿਟ ਮਿਟਾਉਣ ਵਿੱਚ ਅਸਫਲ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Nie powiodło się"
+ "value" : "Nie udało się wyczyścić portfela"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Falha"
+ "value" : "Falha ao limpar a carteira"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Не удалось"
+ "value" : "Ошибка при очистке кошелька"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Misslyckades"
+ "value" : "Misslyckades att rensa plånbok"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ล้มเหลว"
+ "value" : "ล้างกระเป๋าสตางค์ไม่สำเร็จ"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Başarısız"
+ "value" : "Cüzdan silme başarısız oldu"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Не вдалося"
+ "value" : "Не вдалося очистити гаманець"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "失败"
+ "value" : "清除钱包失败"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "失敗的"
+ "value" : "清除錢包失敗"
}
}
}
@@ -12642,132 +12637,132 @@
}
}
},
- "Found transactions are added to the device's database and added or subtracted creating your Brainwallet's balance. There is no Litecoin in any wallet. Litecoin is only found on the network. Brainwallet is a simple way to check your transaction history and it stores your private keys" : {
+ "Full Blockchain sync:" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "تُضاف المعاملات التي تم العثور عليها إلى قاعدة بيانات الجهاز وتُضاف أو تُطرح لإنشاء رصيد محفظة الدماغ الخاصة بك. لا يوجد لايتكوين في أي محفظة. لايتكوين موجود فقط على الشبكة. براين والت هو طريقة بسيطة للتحقق من سجل معاملاتك ويخزن مفاتيحك الخاصة."
+ "value" : "مزامنة البلوكتشين الكاملة:"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Gefundene Transaktionen werden der Datenbank des Geräts hinzugefügt und addiert oder subtrahiert, wodurch der Kontostand Ihrer Brainwallet entsteht. Es gibt kein Litecoin in irgendeiner Wallet. Litecoin ist nur im Netzwerk zu finden. Brainwallet ist eine einfache Möglichkeit, Ihre Transaktionshistorie zu überprüfen, und es speichert Ihre privaten Schlüssel."
+ "value" : "Vollständige Blockchain-Synchronisierung:"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Las transacciones encontradas se agregan a la base de datos del dispositivo y se suman o restan creando el saldo de tu Brainwallet. No hay Litecoin en ninguna billetera. Litecoin solo se encuentra en la red. Brainwallet es una forma sencilla de verificar tu historial de transacciones y almacena tus claves privadas"
+ "value" : "Sincronización completa de Blockchain:"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "تراکنشهای یافت شده به پایگاه داده دستگاه اضافه میشوند و با اضافه یا کم کردن، موجودی کیف پول مغزی شما را ایجاد میکنند. هیچ لایت کوینی در هیچ کیف پولی وجود ندارد. لایت کوین فقط در شبکه یافت میشود. کیف پول مغزی (Brainwallet) روشی ساده برای بررسی تاریخچه تراکنشهای شماست و کلیدهای خصوصی شما را ذخیره میکند."
+ "value" : "همگامسازی کامل Blockchain:"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Les transactions trouvées sont ajoutées à la base de données de l'appareil et ajoutées ou soustraites, créant ainsi le solde de votre Brainwallet. Il n’y a pas de Litecoin dans un portefeuille. Le Litecoin ne se trouve que sur le réseau. Brainwallet est un moyen simple de vérifier l’historique de vos transactions et il stocke vos clés privées"
+ "value" : "Synchronisation complète de la Blockchain :"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "पाए गए लेनदेन को डिवाइस के डेटाबेस में जोड़ दिया जाता है और आपके ब्रेनवॉलेट के बैलेंस को बनाने के लिए जोड़ा या घटाया जाता है। किसी भी वॉलेट में लाइटकॉइन नहीं है। लाइटकॉइन केवल नेटवर्क पर ही पाया जाता है। ब्रेनवॉलेट आपके लेन-देन इतिहास की जांच करने का एक सरल तरीका है और यह आपकी निजी कुंजियों को संग्रहीत करता है"
+ "value" : "पूर्ण ब्लॉकचेन सिंक:"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Transaksi yang ditemukan akan ditambahkan ke database perangkat dan ditambahkan atau dikurangi sehingga menciptakan saldo Brainwallet Anda. Tidak ada Litecoin di dompet mana pun. Litecoin hanya ditemukan di jaringan. Brainwallet adalah cara sederhana untuk memeriksa riwayat transaksi Anda dan menyimpan kunci pribadi Anda"
+ "value" : "Sinkronisasi Blockchain penuh:"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Le transazioni trovate vengono aggiunte al database del dispositivo e sommate o sottratte, creando il saldo del tuo Brainwallet. Non c'è Litecoin in nessun portafoglio. Litecoin si trova solo sulla rete. Brainwallet è un modo semplice per controllare la cronologia delle transazioni e memorizza le tue chiavi private"
+ "value" : "Sincronizzazione completa della Blockchain:"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "見つかったトランザクションはデバイスのデータベースに追加され、加算または減算されてあなたのBrainwalletの残高が算出されます。どのウォレットにもライトコインはありません。ライトコインはネットワーク上にのみ存在する。ブレインウォレットは、取引履歴を確認する簡単な方法であり、秘密鍵を保存します。"
+ "value" : "完全ブロックチェーン同期:"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "찾은 트랜잭션이 장치의 데이터베이스에 추가되고 추가 또는 차감되어 Brainwallet의 잔액이 생성됩니다. 어떤 지갑에도 Litecoin은 없습니다. Litecoin은 네트워크에서만 발견됩니다. Brainwallet은 대금 수령 내역을 확인하고 개인 키를 저장하는 간단한 방법입니다."
+ "value" : "전체 블록체인 동기화:"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Gevonden transacties worden aan de database van het apparaat toegevoegd en bij elkaar opgeteld of afgetrokken, wat uw Brainwallet-saldo vormt. Er is geen Litecoin in enige portefeuille. Litecoin is alleen op het netwerk te vinden. Brainwallet is een eenvoudige manier om uw transactiegeschiedenis te controleren en slaat uw privésleutels op"
+ "value" : "Volledige blockchain-synchronisatie:"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਮਿਲੇ ਟ੍ਰਾਂਜੈਕਸ਼ਨਾਂ ਨੂੰ ਡਿਵਾਈਸ ਦੇ ਡੇਟਾਬੇਸ ਵਿੱਚ ਜੋੜਿਆ ਜਾਂਦਾ ਹੈ ਅਤੇ ਤੁਹਾਡੇ ਬ੍ਰੇਨਵਾਲਿਟ ਦਾ ਬਕਾਇਆ ਬਣਾਉਣ ਲਈ ਜੋੜਿਆ ਜਾਂ ਘਟਾ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ। ਕਿਸੇ ਵੀ ਵਾਲਿਟ ਵਿੱਚ Litecoin ਨਹੀਂ ਹੈ। ਲਾਈਟਕੋਇਨ ਸਿਰਫ਼ ਨੈੱਟਵਰਕ 'ਤੇ ਹੀ ਮਿਲਦਾ ਹੈ। ਬ੍ਰੇਨਵਾਲਟ ਤੁਹਾਡੇ ਲੈਣ-ਦੇਣ ਦੇ ਇਤਿਹਾਸ ਦੀ ਜਾਂਚ ਕਰਨ ਦਾ ਇੱਕ ਸਧਾਰਨ ਤਰੀਕਾ ਹੈ ਅਤੇ ਇਹ ਤੁਹਾਡੀਆਂ ਨਿੱਜੀ ਕੁੰਜੀਆਂ ਨੂੰ ਸਟੋਰ ਕਰਦਾ ਹੈ"
+ "value" : "ਪੂਰਾ ਬਲਾਕਚੇਨ ਸਿੰਕ:"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Znalezione transakcje są dodawane do bazy danych urządzenia i dodawane lub odejmowane, tworząc saldo Brainwallet. Litecoin nie znajduje się w żadnym portfelu. Litecoin znajduje się tylko w sieci. Brainwallet to prosty sposób na sprawdzenie historii transakcji i przechowywanie kluczy prywatnych"
+ "value" : "Pełna synchronizacja Blockchain:"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "As transações encontradas são adicionadas ao banco de dados do dispositivo e adicionadas ou subtraídas, criando o saldo de seu Brainwallet. Não há Litecoin em nenhuma carteira. O Litecoin só pode ser encontrado na rede. O Brainwallet é uma maneira simples de verificar seu histórico de transações e armazena suas chaves privadas"
+ "value" : "Sincronização completa da Blockchain:"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Найденные транзакции добавляются в базу данных устройства и прибавляются или вычитаются, создавая баланс вашего Brainwallet. В кошельке нет Litecoin. Litecoin можно найти только в сети. Brainwallet — это простой способ проверить историю ваших транзакций, и он хранит ваши приватные ключи."
+ "value" : "Полная синхронизация блокчейна:"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Hittade transaktioner läggs till i enhetens databas och adderas eller subtraheras för att skapa ditt Brainwallets saldo. Det finns ingen Litecoin i någon plånbok. Litecoin finns endast på nätverket. Brainwallet är ett enkelt sätt att kontrollera din transaktionshistorik och det lagrar dina privata nycklar"
+ "value" : "Fullständig blockkedje-synkronisering:"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ธุรกรรมที่พบจะถูกเพิ่มลงในฐานข้อมูลของอุปกรณ์และบวกหรือลบเพื่อสร้างยอดคงเหลือของ Brainwallet ของคุณ ไม่มี Litecoin อยู่ในกระเป๋าใดๆ Litecoin พบได้เฉพาะในเครือข่ายเท่านั้น Brainwallet เป็นวิธีง่ายๆ ในการตรวจสอบประวัติธุรกรรมของคุณ และจัดเก็บคีย์ส่วนตัวของคุณ"
+ "value" : "ซิงค์ Blockchain เต็มรูปแบบ:"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Bulunan işlemler cihazın veritabanına eklenir ve Brainwallet'ınızın bakiyesini oluşturacak şekilde eklenir veya çıkarılır. Hiçbir cüzdanda Litecoin yoktur. Litecoin yalnızca ağ üzerinde bulunur. Brainwallet işlem geçmişinizi kontrol etmenin basit bir yoludur ve özel anahtarlarınızı saklar"
+ "value" : "Tam Blockchain senkronizasyonu:"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Знайдені транзакції додаються до бази даних пристрою та додаються або віднімаються, створюючи баланс вашого Brainwallet. У жодному гаманці немає Litecoin. Лайткойн можна знайти лише в мережі. Brainwallet — це простий спосіб перевірити історію транзакцій, який зберігає ваші приватні ключі."
+ "value" : "Повна синхронізація блокчейну:"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "找到的交易会被添加到设备的数据库中,并进行加减以创建您的脑钱包余额。任何钱包中都没有莱特币。莱特币仅在网络上存在。Brainwallet 是一种简单的方法来检查您的交易历史,并存储您的私钥。"
+ "value" : "完全区块链同步:"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "發現的交易會被添加到設備的資料庫中,並添加或減去創建您的 Brainwallet 餘額。任何錢包中都沒有萊特幣。萊特幣僅在網路上存在。Brainwallet 是一種檢查您的交易歷史記錄的簡單方法,它儲存您的私鑰"
+ "value" : "完整區塊鏈同步:"
}
}
}
@@ -14996,6 +14991,136 @@
}
}
},
+ "If transactions are missing you can run a blockchain sync. This can take >3 hours." : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "إذا كانت المعاملات مفقودة، يمكنك تشغيل مزامنة البلوكتشين. قد يستغرق هذا أكثر من 3 ساعات."
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Falls Transaktionen fehlen, können Sie eine Blockchain-Synchronisierung durchführen. Dies kann über 3 Stunden dauern."
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Si faltan transacciones, puedes ejecutar una sincronización de blockchain. Esto puede tomar más de 3 horas."
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "اگر تراکنشها گم شدهاند میتوانید همگامسازی blockchain را اجرا کنید. این میتواند بیش از 3 ساعت طول بکشد."
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Si des transactions manquent, vous pouvez exécuter une synchronisation de la blockchain. Cela peut prendre plus de 3 heures."
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "यदि लेनदेन गायब हैं तो आप ब्लॉकचेन सिंक चला सकते हैं। इसमें 3 घंटे से अधिक समय लग सकता है।"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Jika transaksi hilang, Anda dapat menjalankan sinkronisasi blockchain. Ini dapat memakan waktu >3 jam."
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Se mancano transazioni puoi eseguire una sincronizzazione della blockchain. Questa operazione può richiedere più di 3 ore."
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "トランザクションが欠落している場合は、ブロックチェーン同期を実行できます。3時間以上かかる場合があります。"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "거래가 누락된 경우 블록체인 동기화를 실행할 수 있습니다. 3시간 이상 걸릴 수 있습니다."
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Als transacties ontbreken, kunt u een blockchain-synchronisatie uitvoeren. Dit kan meer dan 3 uur duren."
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ਜੇ ਲੈਣ-ਦੇਣ ਗਾਇਬ ਹਨ ਤਾਂ ਤੁਸੀਂ ਬਲਾਕਚੇਨ ਸਿੰਕ ਚਲਾ ਸਕਦੇ ਹੋ। ਇਹ 3 ਘੰਟੇ ਤੋਂ ਵੱਧ ਸਮਾਂ ਲੈ ਸਕਦਾ ਹੈ।"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Jeśli brakuje transakcji, możesz uruchomić synchronizację blockchain. Może to zająć ponad 3 godziny."
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Se há transações faltando, você pode executar uma sincronização da blockchain. Isso pode levar mais de 3 horas."
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Если транзакции отсутствуют, вы можете запустить синхронизацию блокчейна. Это может занять более 3 часов."
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Om transaktioner saknas kan du köra en blockkedje-synkronisering. Detta kan ta >3 timmar."
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "หากธุรกรรมหายไป คุณสามารถเรียกใช้การซิงค์ blockchain ได้ ซึ่งอาจใช้เวลา >3 ชั่วโมง"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "İşlemler eksikse blockchain senkronizasyonunu çalıştırabilirsiniz. Bu 3 saatten fazla sürebilir."
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Якщо транзакції відсутні, ви можете запустити синхронізацію блокчейну. Це може зайняти >3 годин."
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "如果交易缺失,您可以运行区块链同步。这可能需要超过 3 小时。"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "如果交易遺失,您可以執行區塊鏈同步。此操作可能需要超過 3 小時。"
+ }
+ }
+ }
+ },
"If you saved 12 word seed phrase on your phone or had it on Litewallet, you can restore your Brainwallet here." : {
"localizations" : {
"ar" : {
@@ -15256,6 +15381,136 @@
}
}
},
+ "IP address" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "عنوان IP"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP-Adresse"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Dirección IP"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "آدرس IP"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Adresse IP"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP पता"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Alamat IP"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Indirizzo IP"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IPアドレス"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP 주소"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP-adres"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP ਪਤਾ"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Adres IP"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Endereço IP"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP-адрес"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP-adress"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ที่อยู่ IP"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP adresi"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP адреса"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP 地址"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP 位址"
+ }
+ }
+ }
+ },
"It's your emojis you use to remember your 12 seed words." : {
"localizations" : {
"ar" : {
@@ -16168,264 +16423,132 @@
}
}
},
- "Litecoin Display Unit" : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "وحدة عرض لايتكوين"
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Litecoin-Anzeigeeinheit"
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Unidad de Visualización de Litecoin"
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "واحد نمایش لایت کوین"
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Unité d’affichage Litecoin"
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "लाइटकॉइन डिस्प्ले यूनिट"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Unit Tampilan Litecoin"
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Unità di visualizzazione Litecoin"
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ライトコイン表示ユニット"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Litecoin 디스플레이 장치"
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Litecoin weergave-eenheid"
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਲਾਈਟਕੋਇਨ ਡਿਸਪਲੇ ਯੂਨਿਟ"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Wyświetlacz Litecoin"
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Unidade de exibição de Litecoin"
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Блок отображения Litecoin"
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Litecoin Displayenhet"
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "หน่วยแสดงผล Litecoin"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Litecoin Görüntüleme Birimi"
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Блок відображення Litecoin"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "莱特币显示单位"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "萊特幣顯示單元"
- }
- }
- }
- },
- "Loading Wallet" : {
- "extractionState" : "stale",
+ "Litecoin Mainnet" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "جارٍ تحميل المحفظة"
+ "value" : "شبكة Litecoin الرئيسية"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Brieftasche laden"
+ "value" : "Litecoin Mainnet"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Cargando Billetera"
+ "value" : "Litecoin Mainnet"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "بارگذاری کیف پول"
+ "value" : "Litecoin Mainnet"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Chargement du portefeuille"
+ "value" : "Litecoin Mainnet"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "वॉलेट लोड हो रहा है"
+ "value" : "Litecoin Mainnet"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Memuat Dompet"
+ "value" : "Litecoin Mainnet"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Caricamento del portafoglio"
+ "value" : "Litecoin Mainnet"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ウォレットを読み込み中"
+ "value" : "Litecoin Mainnet"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "지갑 로딩 중"
+ "value" : "Litecoin Mainnet"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Portemonnee laden"
+ "value" : "Litecoin Mainnet"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਵਾਲਿਟ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"
+ "value" : "Litecoin Mainnet"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ładowanie portfela"
+ "value" : "Litecoin Mainnet"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Carregando a carteira"
+ "value" : "Litecoin Mainnet"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Загрузка кошелька"
+ "value" : "Litecoin Mainnet"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Laddar Plånbok"
+ "value" : "Litecoin Mainnet"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "กำลังโหลด Wallet"
+ "value" : "Litecoin Mainnet"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Cüzdan yükleniyor"
+ "value" : "Litecoin Mainnet"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Завантаження гаманця"
+ "value" : "Litecoin Mainnet"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "加载钱包"
+ "value" : "Litecoin 主网"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "加載錢包"
+ "value" : "Litecoin 主網"
}
}
}
@@ -17992,6 +18115,136 @@
}
}
},
+ "Network Fee (per kb):" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "رسوم الشبكة (لكل كيلوبايت):"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Netzwerkgebühr (pro kb):"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Comisión de red (por kb):"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "کمیسیون شبکه (به ازای هر kb):"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Frais réseau (par ko) :"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "नेटवर्क शुल्क (प्रति kb):"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Biaya Jaringan (per kb):"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Commissione di rete (per kb):"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ネットワーク手数料(1kbあたり):"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "네트워크 수수료 (kb당):"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Netwerkvergoeding (per kb):"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ਨੈਟਵਰਕ ਫੀਸ (ਪ್ਰਤਿ kb):"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Opłata sieciowa (na kb):"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Taxa de Rede (por kb):"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Сетевая комиссия (за кБ):"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Nätverksavgift (per kb):"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ค่าธรรมเนียมเครือข่าย (ต่อ kb):"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Ağ Ücreti (kb başına):"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Комісія мережі (за кб):"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "网络费用(每 kb):"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "網路手續費(每 kb):"
+ }
+ }
+ }
+ },
"Network fees" : {
"localizations" : {
"ar" : {
@@ -19428,263 +19681,262 @@
}
}
},
- "Paper Key" : {
- "extractionState" : "stale",
+ "Paper Key Set" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "مفتاح الورق"
+ "value" : "مجموعة مفاتيح الورق"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Papierschlüssel"
+ "value" : "Papierschlüsselsatz"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Llave de Papel"
+ "value" : "Conjunto de Llaves de Papel"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "کلید کاغذی"
+ "value" : "مجموعه کلید کاغذی"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Clé en papier"
+ "value" : "Jeu de clés en papier"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "कागज़ की कुंजी"
+ "value" : "कागज़ कुंजी सेट"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Kunci Kertas"
+ "value" : "Set Kunci Kertas"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Chiave di carta"
+ "value" : "Set di chiavi di carta"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "紙のキー"
+ "value" : "紙鍵セット"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "용지 키"
+ "value" : "종이 키 세트"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Papieren sleutel"
+ "value" : "Papieren sleutel ingesteld"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਕਾਗਜ਼ ਦੀ ਚਾਬੀ"
+ "value" : "ਕਾਗਜ਼ੀ ਚਾਬੀ ਸੈੱਟ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Klucz do papieru"
+ "value" : "Zestaw kluczy papierowych"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Chave de papel"
+ "value" : "Conjunto de chaves de papel"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Бумажный ключ"
+ "value" : "Набор бумажных ключей"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Pappersnyckel"
+ "value" : "Pappersnyckelsats"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "กุญแจกระดาษ"
+ "value" : "ชุดกุญแจกระดาษ"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Kağıt Anahtar"
+ "value" : "Kağıt Anahtar Seti"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Паперовий ключ"
+ "value" : "Набір паперових ключів"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "纸钥匙"
+ "value" : "纸质密钥集"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "紙鑰匙"
+ "value" : "紙質鑰匙套裝"
}
}
}
},
- "Paper Key Set" : {
+ "Peer sync mode" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "مجموعة مفاتيح الورق"
+ "value" : "وضع مزامنة النظير"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Papierschlüsselsatz"
+ "value" : "Peer-Synchronisierungsmodus"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Conjunto de Llaves de Papel"
+ "value" : "Modo de sincronización de pares"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "مجموعه کلید کاغذی"
+ "value" : "حالت همگامسازی Peer"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Jeu de clés en papier"
+ "value" : "Mode synchronisation pair"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "कागज़ कुंजी सेट"
+ "value" : "पीयर सिंक मोड"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Set Kunci Kertas"
+ "value" : "Mode sinkronisasi peer"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Set di chiavi di carta"
+ "value" : "Modalità sincronizzazione peer"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "紙鍵セット"
+ "value" : "ピア同期モード"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "종이 키 세트"
+ "value" : "피어 동기화 모드"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Papieren sleutel ingesteld"
+ "value" : "Peer-synchronisatiemodus"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਕਾਗਜ਼ੀ ਚਾਬੀ ਸੈੱਟ"
+ "value" : "ਪੀਅਰ ਸਿੰਕ ਮੋਡ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Zestaw kluczy papierowych"
+ "value" : "Tryb synchronizacji Peer"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Conjunto de chaves de papel"
+ "value" : "Modo de sincronização de pares"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Набор бумажных ключей"
+ "value" : "Режим синхронизации пиров"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Pappersnyckelsats"
+ "value" : "Peer-synkroniseringsläge"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ชุดกุญแจกระดาษ"
+ "value" : "โหมดซิงค์ Peer"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Kağıt Anahtar Seti"
+ "value" : "Peer senkronizasyon modu"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Набір паперових ключів"
+ "value" : "Режим синхронізації від пірів"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "纸质密钥集"
+ "value" : "对等节点同步模式"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "紙質鑰匙套裝"
+ "value" : "對等節點同步模式"
}
}
}
@@ -21255,6 +21507,136 @@
}
}
},
+ "Please try again." : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "يرجى المحاولة مرة أخرى."
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Bitte versuchen Sie es erneut."
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Por favor, intenta de nuevo."
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "لطفاً دوباره تلاش کنید."
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Veuillez réessayer."
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "कृपया पुनः प्रयास करें।"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Silakan coba lagi."
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Per favore, riprova."
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "もう一度お試しください。"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "다시 시도해주세요."
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Probeer het opnieuw."
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ਕਿਰਪਾ ਕਰਕੇ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Spróbuj ponownie."
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Tente novamente."
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Пожалуйста, попробуйте ещё раз."
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Försök igen."
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "โปรดลองอีกครั้ง"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Lütfen tekrar deneyin."
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Будь ласка, спробуйте ще раз."
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "请重试。"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "請重試。"
+ }
+ }
+ }
+ },
"Please write this down" : {
"localizations" : {
"ar" : {
@@ -21385,6 +21767,136 @@
}
}
},
+ "Port" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "المنفذ"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Port"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Puerto"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "درگاه"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Port"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "पोर्ट"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Port"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Porta"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ポート"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "포트"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Poort"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ਪੋਰਟ"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Port"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Porta"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Порт"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Port"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "พอร์ต"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Port"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Порт"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "端口"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "連接埠"
+ }
+ }
+ }
+ },
"POWERED BY MOONPAY" : {
"localizations" : {
"ar" : {
@@ -21515,268 +22027,6 @@
}
}
},
- "Re-Enter PIN" : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "أعد إدخال الرقم السري"
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Erneute PIN-Eingabe"
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Vuelva a ingresar el PIN"
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "وارد کردن مجدد پین"
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Entrez à nouveau le code PIN"
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "पिन पुनः दर्ज करें"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Masukkan Kembali PIN"
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Reinserisci il PIN"
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PINを再入力してください"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PIN 재입력"
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PIN opnieuw invoeren"
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਪਿੰਨ ਦੁਬਾਰਾ ਦਰਜ ਕਰੋ"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ponowne wprowadzenie kodu PIN"
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Digitar novamente o PIN"
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Повторный ввод PIN-кода"
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ange PIN-koden igen"
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ใส่ PIN ใหม่"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PIN'i Yeniden Girin"
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Повторно введіть PIN-код"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "重新输入密码"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "重新輸入 PIN"
- }
- }
- }
- },
- "Re-Enter your new PIN." : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "أعد إدخال رقم التعريف الشخصي الجديد الخاص بك."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Geben Sie Ihre neue PIN erneut ein."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Vuelva a ingresar su nuevo PIN."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "رمز جدید خود را دوباره وارد کنید."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Entrez à nouveau votre nouveau code PIN."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "अपना नया पिन पुनः दर्ज करें."
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Masukkan kembali PIN baru Anda."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Reinserisci il nuovo PIN."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "新しいPINを再度入力してください。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "새 PIN을 다시 입력하세요."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Voer je nieuwe PIN opnieuw in."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਆਪਣਾ ਨਵਾਂ ਪਿੰਨ ਦੁਬਾਰਾ ਦਰਜ ਕਰੋ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Wprowadź ponownie nowy kod PIN."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Digite novamente seu novo PIN."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Повторно введите новый PIN-код."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ange din nya PIN-kod igen."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ใส่ PIN ใหม่ของคุณอีกครั้ง"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Yeni PIN kodunuzu tekrar girin."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Введіть свій новий PIN-код ще раз."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "请重新输入您的新密码。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "重新輸入您的新 PIN。"
- }
- }
- }
- },
"Ready to start?" : {
"localizations" : {
"ar" : {
@@ -21907,137 +22157,6 @@
}
}
},
- "Ready, Set, Go!" : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "استعد، جهز، يا!"
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Auf geht's!"
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "¡Listos, en sus marcas, fuera!"
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "آماده، آماده شو، برو!"
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Prêt, Partez, Allez !"
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "तैयार, सेट, चलिए!"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Siap, Set, Mulai!"
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Pronti, via!"
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "レディ、セット、ゴー!"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "준비, 시작, 고!"
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Klaar, af, go!"
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਤਿਆਰ, ਸੈੱਟ, ਚੱਲੋ!"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Gotów, start, w drogę!"
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Preparado, Pronto, Já!"
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Готово, начали!"
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Redo, spara, kör!"
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "พร้อม ตั้งไว้ ไป!"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Hazır, Başla, Git!"
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Готові? Поїхали!"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "准备好了,开始,走!"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "準備好了,開始,出發!"
- }
- }
- }
- },
"Receipient" : {
"localizations" : {
"ar" : {
@@ -23078,137 +23197,6 @@
}
}
},
- "Remember this PIN. If you forget it, you won't be able to access your Litecoin." : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "تذكر هذا الرقم السري. إذا نسيت ذلك، فلن تتمكن من الوصول إلى اللايتكوين الخاص بك."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Merken Sie sich diese PIN. Wenn Sie es vergessen, können Sie nicht mehr auf Ihre Litecoin zugreifen."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Recuerda este PIN. Si lo olvidas, no podrás acceder a tu Litecoin."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "این پین را به خاطر بسپارید. اگر آن را فراموش کنید، قادر به دسترسی به لایتکوین خود نخواهید بود."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "N’oubliez pas ce code PIN. Si vous l’oubliez, vous ne pourrez pas accéder à votre Litecoin."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "इस पिन को याद रखें. यदि आप इसे भूल जाते हैं, तो आप अपने लाइटकॉइन तक पहुंच नहीं पाएंगे।"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ingat PIN ini. Jika Anda lupa, Anda tidak akan dapat mengakses Litecoin Anda."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ricordati questo PIN. Se lo dimentichi, non potrai più accedere ai tuoi Litecoin."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "このPINを覚えておいてください。忘れると、Litecoin にアクセスできなくなります。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "이 PIN을 기억하세요. 잊어버리면 Litecoin에 액세스할 수 없습니다."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Onthoud deze PIN. Als je deze vergeet, kun je je Litecoin niet meer openen."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਇਸ ਪਿੰਨ ਨੂੰ ਯਾਦ ਰੱਖੋ। ਜੇਕਰ ਤੁਸੀਂ ਇਸਨੂੰ ਭੁੱਲ ਜਾਂਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਆਪਣੇ Litecoin ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕੋਗੇ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Zapamiętaj ten kod PIN. Jeśli go zapomnisz, nie będziesz mieć dostępu do swoich Litecoinów."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Lembre-se deste PIN. Se você o esquecer, não poderá acessar seu Litecoin."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Запомните этот PIN-код. Если вы его забудете, то не сможете получить доступ к своим Litecoin."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Kom ihåg denna PIN-kod Om du glömmer det kommer du inte att kunna komma åt dina Litecoin."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "จำไว้ว่า PIN นี้ หากลืมไป คุณจะไม่สามารถเข้าถึง Litecoin ของคุณได้"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Bu şifreyi unutmayın. Eğer unutursanız, Litecoin'inize erişemezsiniz."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Запам'ятайте цей PIN-код. Якщо ви його забудете, то не зможете отримати доступ до свого Litecoin."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "请记住这个密码。如果你忘记了,你将无法访问你的莱特币。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "記住此 PIN。如果您忘記了,您將無法存取您的萊特幣。"
- }
- }
- }
- },
"RESET / START OVER" : {
"localizations" : {
"ar" : {
@@ -24121,6 +24109,136 @@
}
}
},
+ "Save trusted node" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "حفظ العقدة الموثوقة"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Vertrauenswürdigen Knoten speichern"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Guardar nodo de confianza"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ذخیره گره قابل اعتماد"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Enregistrer le nœud de confiance"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "विश्वसनीय नोड सहेजें"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Simpan node terpercaya"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Salva nodo affidabile"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "信頼できるノードを保存"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "신뢰할 수 있는 노드 저장"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Vertrouwd knooppunt opslaan"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ਵਿਸ਼ਵਾਸ ਕੀਤੀ ਨੋਡ ਸੰਭਾਲੋ"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Zapisz zaufany węzeł"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Salvar nó confiável"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Сохранить доверенный узел"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Spara betrodd nod"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "บันทึกโหนดที่เชื่อถือได้"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Güvenilen düğümü kaydet"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Зберегти довірений вузол"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "保存受信任节点"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "儲存信任的節點"
+ }
+ }
+ }
+ },
"Save your 12 words" : {
"localizations" : {
"ar" : {
@@ -26207,6 +26325,266 @@
}
}
},
+ "Set from spv peers in the Litecoin mainnet or a trusted peer to sync with the blockchain" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "اختر من نظراء SPV في شبكة Litecoin الرئيسية أو من نظير موثوق للمزامنة مع البلوكتشين"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Wählen Sie aus SPV-Peers im Litecoin-Mainnet oder einem vertrauenswürdigen Peer zum Synchronisieren mit der Blockchain"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Configura desde pares SPV en la red principal de Litecoin o un par de confianza para sincronizar con la blockchain"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "از spv peers در Litecoin mainnet یا یک peer قابل اعتماد برای همگامسازی با blockchain انتخاب کنید"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Définir à partir des pairs SPV du réseau principal Litecoin ou d'un pair de confiance pour se synchroniser avec la blockchain"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Litecoin mainnet में SPV पीयर्स से या ब्लॉकचेन के साथ सिंक करने के लिए एक विश्वसनीय पीयर से सेट करें"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Atur dari peer SPV di Litecoin mainnet atau peer terpercaya untuk disinkronkan dengan blockchain"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Scegli tra i peer SPV della rete principale Litecoin o un peer affidabile per sincronizzarti con la blockchain"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Litecoin mainnetのSPVピアまたは信頼できるピアから設定して、ブロックチェーンと同期します"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Litecoin 메인넷의 SPV 피어 또는 신뢰할 수 있는 피어에서 선택하여 블록체인과 동기화"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Stel in via SPV-peers in het Litecoin-netwerk of een vertrouwd peer om met de blockchain te synchroniseren"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Litecoin mainnet ਵਿੱਚ SPV ਪੀਅਰਸ ਜਾਂ ਵਿਸ਼ਵਾਸ ਕੀਤੇ ਪੀਅਰ ਤੋਂ ਬਲਾਕਚੇਨ ਨਾਲ ਸਿੰਕ ਕਰਨ ਲਈ ਸੈੱਟ ਕਰੋ"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Ustaw z równorzędnych węzłów SPV w sieci głównej Litecoin lub zaufanego węzła w celu synchronizacji z blockchain"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Defina a partir de pares SPV na rede principal Litecoin ou de um par confiável para sincronizar com a blockchain"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Установите пиры SPV из Litecoin mainnet или доверенного пира для синхронизации с блокчейном"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Ange från SPV-peers i Litecoin-huvudnätverket eller en betrodd peer för att synkronisera med blockkedjan"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ตั้งค่าจาก SPV peers ใน Litecoin mainnet หรือ trusted peer เพื่อซิงค์กับ blockchain"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Litecoin mainnet'indeki SPV eşlerinden veya blockchain ile senkronize olmak için güvenilen bir eşten ayarlayın"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Виберіть із SPV вузлів у мережі Litecoin або довіреного піра для синхронізації з блокчейном"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "从 Litecoin 主网中的 SPV 对等节点或受信任的对等节点设置以与区块链同步"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "從 Litecoin 主網中的 SPV 對等節點或信任的對等節點設定,以與區塊鏈同步"
+ }
+ }
+ }
+ },
+ "Set IP Address" : {
+ "localizations" : {
+ "ar" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "تعيين عنوان IP"
+ }
+ },
+ "de" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP-Adresse festlegen"
+ }
+ },
+ "es-419" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Establecer dirección IP"
+ }
+ },
+ "fa-IR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "تعیین آدرس IP"
+ }
+ },
+ "fr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Définir l'adresse IP"
+ }
+ },
+ "hi" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP पता सेट करें"
+ }
+ },
+ "id" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Atur Alamat IP"
+ }
+ },
+ "it" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Imposta indirizzo IP"
+ }
+ },
+ "ja" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IPアドレスを設定"
+ }
+ },
+ "ko" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP 주소 설정"
+ }
+ },
+ "nl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP-adres instellen"
+ }
+ },
+ "pa" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP ਪਤਾ ਸੈੱਟ ਕਰੋ"
+ }
+ },
+ "pl" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Ustaw adres IP"
+ }
+ },
+ "pt-BR" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Definir Endereço IP"
+ }
+ },
+ "ru" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Установить IP-адрес"
+ }
+ },
+ "sv" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Ange IP-adress"
+ }
+ },
+ "th" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "ตั้งค่าที่อยู่ IP"
+ }
+ },
+ "tr" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "IP Adresini Ayarla"
+ }
+ },
+ "uk" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Встановити IP адресу"
+ }
+ },
+ "zh-Hans" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "设置 IP 地址"
+ }
+ },
+ "zh-Hant" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "設定 IP 位址"
+ }
+ }
+ }
+ },
"Set PIN" : {
"localizations" : {
"ar" : {
@@ -26337,265 +26715,264 @@
}
}
},
- "Set your first emojis!" : {
- "comment" : "A title displayed at the top of the emoji picker view.",
- "isCommentAutoGenerated" : true,
+ "Set trusted Litecoin node / SPV Peer" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "اختر رموزك التعبيرية الأولى!"
+ "value" : "تعيين عقدة Litecoin الموثوقة / نظير SPV"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Lege deine ersten Emojis fest!"
+ "value" : "Vertrauenswürdigen Litecoin-Knoten / SPV-Peer festlegen"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "¡Establece tus primeros emojis!"
+ "value" : "Configurar nodo Litecoin de confianza / Par SPV"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "اولین ایموجیهای خود را تعیین کنید!"
+ "value" : "تعیین گره قابل اعتماد Litecoin / SPV Peer"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Définissez vos premiers emojis !"
+ "value" : "Définir le nœud Litecoin de confiance / Pair SPV"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "अपने पहले इमोजी सेट करें!"
+ "value" : "विश्वसनीय Litecoin नोड / SPV पीयर सेट करें"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Atur emoji pertama Anda!"
+ "value" : "Atur node Litecoin terpercaya / SPV Peer"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Imposta i tuoi primi emoji!"
+ "value" : "Imposta nodo Litecoin affidabile / Peer SPV"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "最初の絵文字を設定しましょう!"
+ "value" : "信頼できるLitecoinノード/SPVピアを設定"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "첫 번째 이모지를 설정하세요!"
+ "value" : "신뢰할 수 있는 Litecoin 노드 / SPV 피어 설정"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Stel je eerste emojis in!"
+ "value" : "Vertrouwd Litecoin-knooppunt / SPV-peer instellen"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਆਪਣੀਆਂ ਪਹਿਲੀਆਂ ਈਮੋਜੀਆਂ ਸੈੱਟ ਕਰੋ!"
+ "value" : "ਵਿਸ਼ਵਾਸ ਕੀਤੀ Litecoin ਨੋਡ / SPV ਪੀਅਰ ਸੈੱਟ ਕਰੋ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ustaw swoje pierwsze emoji!"
+ "value" : "Ustaw zaufany węzeł Litecoin / SPV Peer"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Configure seus primeiros emojis!"
+ "value" : "Definir nó Litecoin confiável / Par SPV"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Установите свои первые эмодзи!"
+ "value" : "Установить доверенный узел Litecoin / SPV пира"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ange dina första emojis!"
+ "value" : "Ange betrodd Litecoin-nod / SPV-peer"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ตั้งค่า Emoji แรกของคุณ!"
+ "value" : "ตั้งค่าโหนด Litecoin ที่เชื่อถือได้ / SPV Peer"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "İlk emojilerinizi ayarlayın!"
+ "value" : "Güvenilen Litecoin düğümü / SPV Eşi Ayarla"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Встановіть свої перші емодзі!"
+ "value" : "Встановити довірений вузол Litecoin / SPV Peer"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "设置你的第一批表情符号!"
+ "value" : "设置受信任的 Litecoin 节点 / SPV 对等节点"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "設定你的第一批表情符號!"
+ "value" : "設定信任的 Litecoin 節點 / SPV 對等節點"
}
}
}
},
- "Settings" : {
- "extractionState" : "stale",
+ "Set your first emojis!" : {
+ "comment" : "A title displayed at the top of the emoji picker view.",
+ "isCommentAutoGenerated" : true,
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "الإعدادات"
+ "value" : "اختر رموزك التعبيرية الأولى!"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Einstellungen"
+ "value" : "Lege deine ersten Emojis fest!"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Configuración"
+ "value" : "¡Establece tus primeros emojis!"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "تنظیمات"
+ "value" : "اولین ایموجیهای خود را تعیین کنید!"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Paramètres"
+ "value" : "Définissez vos premiers emojis !"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "सेटिंग्स"
+ "value" : "अपने पहले इमोजी सेट करें!"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Pengaturan"
+ "value" : "Atur emoji pertama Anda!"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Impostazioni"
+ "value" : "Imposta i tuoi primi emoji!"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "設定"
+ "value" : "最初の絵文字を設定しましょう!"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "설정"
+ "value" : "첫 번째 이모지를 설정하세요!"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Instellingen"
+ "value" : "Stel je eerste emojis in!"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਸੈਟਿੰਗਾਂ"
+ "value" : "ਆਪਣੀਆਂ ਪਹਿਲੀਆਂ ਈਮੋਜੀਆਂ ਸੈੱਟ ਕਰੋ!"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ustawienia"
+ "value" : "Ustaw swoje pierwsze emoji!"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Configurações"
+ "value" : "Configure seus primeiros emojis!"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Настройки"
+ "value" : "Установите свои первые эмодзи!"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Inställningar"
+ "value" : "Ange dina första emojis!"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "การตั้งค่า"
+ "value" : "ตั้งค่า Emoji แรกของคุณ!"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ayarlar"
+ "value" : "İlk emojilerinizi ayarlayın!"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Налаштування"
+ "value" : "Встановіть свої перші емодзі!"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "设置"
+ "value" : "设置你的第一批表情符号!"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "設定"
+ "value" : "設定你的第一批表情符號!"
}
}
}
@@ -27642,137 +28019,6 @@
}
}
},
- "Submit" : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "إرسال"
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Absenden"
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Enviar"
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ارسال کنید"
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Envoyer"
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "जमा करना"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Kirim"
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Invia"
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "送信"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "제출"
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Indienen"
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਜਮ੍ਹਾਂ ਕਰੋ"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Wyślij"
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Enviar"
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Отправить"
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Skicka in"
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ส่ง"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Gönder"
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Надіслати"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "提交"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "提交"
- }
- }
- }
- },
"Success" : {
"localizations" : {
"ar" : {
@@ -28293,136 +28539,6 @@
}
}
},
- "Sync to the Litecoin Blockchain" : {
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "المزامنة مع سلسلة كتل لايتكوين"
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Synchronisieren mit der Litecoin-Blockchain"
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Sincronizar con la Blockchain de Litecoin"
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "همگامسازی با بلاکچین لایتکوین"
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Synchronisation avec la blockchain Litecoin"
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "लाइटकॉइन ब्लॉकचेन से सिंक करें"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Sinkronisasi ke Blockchain Litecoin"
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Sincronizzazione con la blockchain di Litecoin"
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ライトコインブロックチェーンに同期"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Litecoin 블록체인에 동기화"
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Synchroniseren met de Litecoin Blockchain"
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਲਾਈਟਕੋਇਨ ਬਲਾਕਚੈਨ ਨਾਲ ਸਿੰਕ ਕਰੋ"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Synchronizacja z łańcuchem blokowym Litecoin"
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Sincronização com o blockchain do Litecoin"
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Синхронизация с блокчейном Litecoin"
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Synkronisera med Litecoin-blockkedjan"
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ซิงค์กับ Litecoin Blockchain"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Litecoin Blok Zinciri ile Senkronizasyon"
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Синхронізація з блокчейном Litecoin"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "同步到莱特币区块链"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "同步至萊特幣區塊鏈"
- }
- }
- }
- },
"Sync with Blockchain?" : {
"localizations" : {
"ar" : {
@@ -28553,136 +28669,6 @@
}
}
},
- "Syncing is a process where Brainwallet scans the Litecoin blockchain to see if any transactions that match your seed words (private keys) are in any transactions." : {
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "المزامنة هي عملية يقوم فيها Brainwallet بفحص سلسلة الكتل الخاصة بـ Litecoin لمعرفة ما إذا كانت هناك أي معاملات تتطابق مع كلمات البذور الخاصة بك (المفاتيح الخاصة) في أي معاملات."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Das Synchronisieren ist ein Prozess, bei dem Brainwallet die Litecoin-Blockchain durchsucht, um zu sehen, ob Transaktionen, die mit Ihren Seed-Wörtern (privaten Schlüsseln) übereinstimmen, in irgendwelchen Transaktionen enthalten sind."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "La sincronización es un proceso donde Brainwallet escanea la cadena de bloques de Litecoin para ver si alguna transacción que coincida con tus palabras semilla (claves privadas) está en alguna transacción."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "همگامسازی فرآیندی است که در آن برینوالت (Brainwallet) بلاکچین لایتکوین را اسکن میکند تا ببیند آیا تراکنشهایی که با کلمات بذر شما (کلیدهای خصوصی) مطابقت دارند در هیچ تراکنشی وجود دارند."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "La synchronisation est un processus par lequel Brainwallet analyse la blockchain Litecoin pour voir si des transactions qui correspondent à vos mots de récupération (clés privées) sont dans des transactions."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "सिंकिंग एक ऐसी प्रक्रिया है जिसमें ब्रेनवॉलेट लाइटकॉइन ब्लॉकचेन को स्कैन करता है, यह देखने के लिए कि क्या किसी लेनदेन में आपके बीज शब्दों (निजी कुंजी) से मेल खाने वाला कोई लेनदेन है या नहीं।"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Sinkronisasi adalah sebuah proses di mana Brainwallet memindai blockchain Litecoin untuk melihat apakah ada transaksi yang cocok dengan seed words (kunci pribadi) Anda dalam setiap transaksi."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "La sincronizzazione è un processo tramite il quale Brainwallet analizza la blockchain di Litecoin per verificare se sono presenti transazioni che corrispondono alle tue seed word (chiavi private)."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "同期とは、BrainwalletがLitecoinブロックチェーンをスキャンし、あなたのシードワード(秘密鍵)に一致するトランザクションが存在するかどうかを確認するプロセスです。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "동기화는 Brainwallet이 Litecoin 블록 체인을 스캔하여 시드 단어 (개인 키) 와 일치하는 거래가 거래에 있는지 확인하는 프로세스입니다."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Synchroniseren is een proces waarbij Brainwallet de Litecoin blockchain scant om te zien of transacties die overeenkomen met je seed woorden (private keys) in transacties voorkomen."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਸਿੰਕਿੰਗ ਇੱਕ ਪ੍ਰਕਿਰਿਆ ਹੈ ਜਿੱਥੇ ਬ੍ਰੇਨਵਾਲਟ ਲਾਈਟਕੋਇਨ ਬਲਾਕਚੈਨ ਨੂੰ ਸਕੈਨ ਕਰਦਾ ਹੈ ਇਹ ਦੇਖਣ ਲਈ ਕਿ ਕੀ ਤੁਹਾਡੇ ਸੀਡ ਵਰਡਜ਼ (ਪ੍ਰਾਈਵੇਟ ਕੁੰਜੀਆਂ) ਨਾਲ ਮੇਲ ਖਾਂਦਾ ਕੋਈ ਲੈਣ-ਦੇਣ ਕਿਸੇ ਲੈਣ-ਦੇਣ ਵਿੱਚ ਹੈ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Synchronizacja to proces, w którym Brainwallet skanuje łańcuch bloków Litecoin, aby sprawdzić, czy w transakcjach znajdują się jakiekolwiek transakcje pasujące do słów seed (kluczy prywatnych)."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "A sincronização é um processo em que o Brainwallet examina o blockchain do Litecoin para ver se alguma transação que corresponda às suas palavras-semente (chaves privadas) está em alguma transação."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Синхронизация — это процесс, при котором Brainwallet сканирует блокчейн Litecoin, чтобы проверить, есть ли какие-либо транзакции, соответствующие вашим seed-фразам (приватным ключам), в каких-либо транзакциях."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Synkronisering är en process där Brainwallet skannar Litecoin-blockkedjan för att se om några transaktioner som matchar dina frö-ord (privata nycklar) finns i några transaktioner."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "การซิงค์เป็นกระบวนการที่ Brainwallet สแกน Litecoin blockchain เพื่อตรวจสอบว่ามีธุรกรรมใดที่ตรงกับ seed words (private keys) ของคุณหรือไม่"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Senkronizasyon, Brainwallet'ın Litecoin blok zincirini tarayarak tohum kelimelerinizle (özel anahtarlar) eşleşen herhangi bir işlem olup olmadığını görmek için yaptığı bir işlemdir."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Синхронізація – це процес, під час якого Brainwallet сканує блокчейн Litecoin, щоб перевірити, чи є в ньому транзакції, що відповідають вашим основоположним словам (приватним ключам)."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "同步是一个过程,Brainwallet会扫描莱特币区块链,以查看是否有任何交易与您的种子词(私钥)匹配。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "同步是一個過程,其中 Brainwallet 掃描萊特幣區塊鏈以查看任何交易中是否有與您的種子詞(私鑰)匹配的交易。"
- }
- }
- }
- },
"Syncing..." : {
"localizations" : {
"ar" : {
@@ -30637,137 +30623,6 @@
}
}
},
- "The words entered do not match your paper key. Please try again." : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "الكلمات المدخلة لا تتطابق مع مفتاح الورقة الخاص بك. يرجى المحاولة مرة أخرى."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Die eingegebenen Wörter stimmen nicht mit Ihrem Papierschlüssel überein. Bitte versuchen Sie es erneut."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Las palabras ingresadas no coinciden con tu clave de papel. Por favor, inténtalo de nuevo."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "کلمات وارد شده با کلید کاغذی شما مطابقت ندارند. لطفاً دوباره تلاش کنید."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Les mots saisis ne correspondent pas à votre clé en papier. Veuillez réessayer."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "दर्ज किए गए शब्द आपके पेपर कुंजी से मेल नहीं खाते हैं। कृपया पुन: प्रयास करें।"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Kata-kata yang dimasukkan tidak cocok dengan kunci kertas Anda. Silakan coba lagi."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Le parole immesse non corrispondono alla chiave cartacea. Riprova per favore."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "入力された単語がお手元のペーパーキーと一致しません。もう一度お試しください。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "입력한 단어가 용지 키와 일치하지 않습니다. 다시 시도해주세요."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "De ingevoerde woorden komen niet overeen met je papieren sleutel. Probeer het opnieuw."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਦਰਜ ਕੀਤੇ ਸ਼ਬਦ ਤੁਹਾਡੀ ਪੇਪਰ ਕੀ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦੇ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Wprowadzone słowa nie pasują do klucza papierowego. Spróbuj ponownie."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "As palavras digitadas não correspondem à chave do papel. Tente novamente."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Введенные слова не соответствуют вашему бумажному ключу. Пожалуйста, попробуйте снова."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Orden som angavs matchar inte din pappernyckel. Försök igen."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "คำที่คุณป้อนไม่ตรงกับ paper key ของคุณ โปรดลองอีกครั้ง"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Girilen kelimeler kağıt anahtarınızla eşleşmiyor. Lütfen tekrar deneyin."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Введені слова не відповідають вашому паперовому ключу. Будь ласка, спробуйте ще раз."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "输入的单词与您的纸质密钥不匹配。请再试一次。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "輸入的單字與您的紙質鑰匙不符。請重試。"
- }
- }
- }
- },
"There was a serious error in creating your Brainwallet. Visit us at brainwallet.co to file a customer support ticket." : {
"localizations" : {
"ar" : {
@@ -31291,139 +31146,6 @@
}
}
},
- "This is your Brainwallet phrase. Its your emojis you use to remember your 12 seed words." : {
- "comment" : "A description of the purpose of the user's brainwallet phrase.",
- "extractionState" : "stale",
- "isCommentAutoGenerated" : true,
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "هذه عبارة Brainwallet الخاصة بك. إنها الرموز التعبيرية التي تستخدمها لتذكر كلمات البذرة الـ 12 الخاصة بك."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Dies ist deine Brainwallet-Phrase. Es sind deine Emojis, die du nutzt, um deine 12 Seed-Wörter zu merken."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Esta es tu frase Brainwallet. Son los emojis que usas para recordar tus 12 palabras semilla."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "این عبارت Brainwallet شما است. این ایموجیهای شما هستند که برای یادآوری 12 کلمه بذر استفاده میکنید."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ceci est votre phrase Brainwallet. Ce sont vos emojis que vous utilisez pour mémoriser vos 12 mots clés."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "यह आपका Brainwallet phrase है। ये आपके इमोजी हैं जिनका उपयोग आप अपने 12 seed words को याद रखने के लिए करते हैं।"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ini adalah frasa Brainwallet Anda. Ini emoji Anda yang digunakan untuk mengingat 12 seed words Anda."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Questa è la tua frase Brainwallet. Sono i tuoi emoji che usi per ricordare le tue 12 parole seed."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "これはあなたのBrainwalletフレーズです。12個のシードワードを覚えるために使う絵文字です。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "이것은 당신의 Brainwallet 구문입니다. 12개의 시드 단어를 기억하기 위해 사용하는 이모지입니다."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Dit is je Brainwallet-zin. Dit zijn je emoji's om je 12 zaadwoorden te onthouden."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਇਹ ਤੁਹਾਡਾ Brainwallet ਫਰੇਜ਼ ਹੈ। ਇਹ ਤੁਹਾਡੀਆਂ ਈਮੋਜੀਆਂ ਹਨ ਜਿਹਨਾਂ ਨੂੰ ਤੁਸੀਂ ਆਪਣੇ 12 seed words ਨੂੰ ਯਾਦ ਰੱਖਣ ਲਈ ਵਰਤਦੇ ਹੋ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "To jest Twoja fraza Brainwallet. To emoji, których używasz, aby zapamiętać swoje 12 seed words."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Esta é sua frase Brainwallet. São seus emojis que você usa para lembrar suas 12 palavras-chave."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Это ваша Brainwallet фраза. Это эмодзи, которые вы используете, чтобы запомнить ваши 12 seed words."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Detta är din Brainwallet-fras. Det är dina emojis som du använder för att komma ihåg dina 12 seed-ord."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "นี่คือวลี Brainwallet ของคุณ เป็น emoji ของคุณที่ใช้เพื่อจำคำ seed 12 คำของคุณ"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Bu sizin Brainwallet cümlenizdir. 12 tohum sözcüğünüzü hatırlamak için kullandığınız emojilerinizdir."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Це ваша фраза Brainwallet. Це ваші емодзі, які ви використовуєте, щоб запам'ятати свої 12 seed words."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "这是你的Brainwallet密语。用你选择的表情符号来记住你的12个助记词。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "這是你的 Brainwallet 助記詞。這些是你用來記憶 12 個助記詞的表情符號。"
- }
- }
- }
- },
"This is your final set!" : {
"comment" : "Title for the final emoji selection guide screen.",
"isCommentAutoGenerated" : true,
@@ -31556,136 +31278,6 @@
}
}
},
- "This process can take 5 - 45 mins depending on your connectivty to the Litecoin network." : {
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "يمكن أن تستغرق هذه العملية من 5 إلى 45 دقيقة اعتمادًا على اتصالك بشبكة لايتكوين."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Dieser Vorgang kann je nach Ihrer Verbindung zum Litecoin-Netzwerk 5 - 45 Minuten dauern."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Este proceso puede tomar de 5 a 45 minutos dependiendo de tu conectividad a la red de Litecoin."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "این فرآیند میتواند بسته به کیفیت اتصال شما به شبکه لایتکوین، بین ۵ تا ۴۵ دقیقه طول بکشد."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ce processus peut prendre de 5 à 45 minutes en fonction de votre connexion au réseau Litecoin."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "इस प्रक्रिया में लाइटकॉइन नेटवर्क से आपकी कनेक्टिविटी के आधार पर 5 - 45 मिनट लग सकते हैं।"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Proses ini dapat memakan waktu 5 - 45 menit tergantung pada konektivitas Anda ke jaringan Litecoin."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Questo processo può richiedere dai 5 ai 45 minuti, a seconda della connettività alla rete Litecoin."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "このプロセスは、ライトコインネットワークへの接続状況によって、5分から45分かかることがあります。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "이 프로세스는 Litecoin 네트워크에 연결하는 데 따라 5 ~ 45분이 소요될 수 있습니다."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Dit proces kan 5 tot 45 minuten duren, afhankelijk van je verbinding met het Litecoin-netwerk."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਇਸ ਪ੍ਰਕਿਰਿਆ ਵਿੱਚ 5 - 45 ਮਿੰਟ ਲੱਗ ਸਕਦੇ ਹਨ ਜੋ ਕਿ Litecoin ਨੈੱਟਵਰਕ ਨਾਲ ਤੁਹਾਡੀ ਕਨੈਕਟੀਵਿਟੀ ਦੇ ਆਧਾਰ 'ਤੇ ਹੁੰਦਾ ਹੈ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Proces ten może zająć od 5 do 45 minut w zależności od połączenia z siecią Litecoin."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Esse processo pode levar de 5 a 45 minutos, dependendo de sua conexão com a rede Litecoin."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Этот процесс может занять от 5 до 45 минут в зависимости от вашего подключения к сети Litecoin."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Denna process kan ta 5 - 45 minuter beroende på din anslutning till Litecoin-nätverket."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "กระบวนการนี้อาจใช้เวลา 5 - 45 นาที ขึ้นอยู่กับการเชื่อมต่อของคุณกับเครือข่าย Litecoin"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Bu işlem Litecoin ağına olan bağlantınıza bağlı olarak 5 - 45 dakika sürebilir."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Цей процес може тривати від 5 до 45 хвилин залежно від вашого підключення до мережі Litecoin."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "此过程可能需要5到45分钟,具体取决于您与莱特币网络的连接情况。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "此過程可能需要 5 - 45 分鐘,具體取決於您與萊特幣網路的連接情況。"
- }
- }
- }
- },
"This will allow you to send and receive from your previous balance. You will need to set a new PIN" : {
"localizations" : {
"ar" : {
@@ -32336,264 +31928,132 @@
}
}
},
- "To make sure everything was written down correctly, please enter the following words from your paper key." : {
- "extractionState" : "stale",
+ "Toggle mode" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "للتأكد من أن كل شيء قد كُتب بشكل صحيح، يرجى إدخال الكلمات التالية من مفتاح الورق الخاص بك."
+ "value" : "تبديل الوضع"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Um sicherzugehen, dass Sie alles richtig aufgeschrieben haben, tragen Sie bitte die folgenden Wörter auf Ihrem Papierschlüssel ein."
+ "value" : "Modus wechseln"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Para asegurarse de que todo se haya escrito correctamente, por favor ingrese las siguientes palabras de su clave en papel."
+ "value" : "Alternar modo"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "برای اطمینان از اینکه همه چیز به درستی نوشته شده است، لطفاً کلمات زیر را از کلید کاغذی خود وارد کنید."
+ "value" : "تغییر حالت"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Pour vous assurer que tout a été noté correctement, veuillez entrer les mots suivants sur votre clé en papier."
+ "value" : "Basculer le mode"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "यह सुनिश्चित करने के लिए कि सब कुछ सही ढंग से लिखा गया है, कृपया अपने पेपर कुंजी से निम्नलिखित शब्द दर्ज करें।"
+ "value" : "मोड टॉगल करें"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Untuk memastikan semuanya ditulis dengan benar, silakan masukkan kata-kata berikut ini dari kunci kertas Anda."
+ "value" : "Alihkan mode"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Per assicurarti che tutto sia stato scritto correttamente, inserisci le seguenti parole dalla tua legenda cartacea."
+ "value" : "Cambia modalità"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "すべてが正しく書き留められているか確認するには、ペーパーキーから次の単語を入力してください。"
+ "value" : "モードを切り替え"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "모든 내용이 올바르게 작성되었는지 확인하려면 종이 키에서 다음 단어를 입력하십시오."
+ "value" : "모드 전환"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Voer de volgende woorden van je papieren sleutel in om zeker te stellen dat alles correct is opgeschreven."
+ "value" : "Modus wisselen"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਇਹ ਯਕੀਨੀ ਬਣਾਉਣ ਲਈ ਕਿ ਸਭ ਕੁਝ ਸਹੀ ਢੰਗ ਨਾਲ ਲਿਖਿਆ ਗਿਆ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੀ ਪੇਪਰ ਕੀ ਤੋਂ ਹੇਠਾਂ ਦਿੱਤੇ ਸ਼ਬਦ ਦਰਜ ਕਰੋ।"
+ "value" : "ਮੋਡ ਬਦਲੋ"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Aby upewnić się, że wszystko zostało poprawnie zapisane, wpisz następujące słowa z klucza do papieru."
+ "value" : "Zmień tryb"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Para ter certeza de que tudo foi escrito corretamente, digite as seguintes palavras da chave de papel."
+ "value" : "Alternar modo"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Чтобы убедиться, что все записано правильно, введите следующие слова с бумажного ключа."
+ "value" : "Переключить режим"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "För att säkerställa att allt skrevs ner korrekt, vänligen ange följande ord från din pappersnyckel."
+ "value" : "Växla läge"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "เพื่อให้แน่ใจว่าทุกอย่างถูกเขียนลงอย่างถูกต้อง โปรดป้อนคำต่อไปนี้จาก paper key ของคุณ"
+ "value" : "สลับโหมด"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Her şeyin doğru yazıldığından emin olmak için, lütfen kağıt anahtarınızdan aşağıdaki kelimeleri girin."
+ "value" : "Modu değiştir"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Щоб переконатися, що все записано правильно, введіть наступні слова з вашого паперового ключа."
+ "value" : "Перемикнути режим"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "为了确保所有内容都正确记录下来,请输入纸质密钥中的以下单词。"
+ "value" : "切换模式"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "為了確保所有內容都正確寫下來,請從紙質密鑰中輸入以下單字。"
- }
- }
- }
- },
- "TOP SECRET" : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "سري للغاية"
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "STRENG GEHEIM"
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ALTO SECRETO"
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "کاملاً محرمانه"
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "TOP SECRET"
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "गोपनीय\nशीर्ष गोपनीय"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "SANGAT RAHASIA"
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "TOP SECRET"
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "トップシークレット"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "극비"
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "TOPGEHEIM"
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਸ਼ਾਇਦ ਬਾਅਦ ਵਿੱਚ (ਛੱਡੋ"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "TOP SECRET"
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "TOP SECRET"
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "СОВЕРШЕННО СЕКРЕТНО"
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "TOPP HEMLIG"
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ลับสูงสุด"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ÇOK GİZLİ"
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ЦІЛКОМ СЕКРЕТНО"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "绝密"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "絕密"
+ "value" : "切換模式"
}
}
}
@@ -34552,137 +34012,6 @@
}
}
},
- "via" : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "عبر"
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "über"
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "vía"
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "از طریق"
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "via"
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "के जरिए"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "melalui"
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "tramite"
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "経由"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "경유"
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "via"
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਰਾਹੀਂ"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "za pośrednictwem"
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "via"
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "через"
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "via"
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ผ่าน"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "üzerinden"
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "через"
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "通过"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "透過"
- }
- }
- }
- },
"Wallet Creation Error" : {
"localizations" : {
"ar" : {
@@ -35465,133 +34794,132 @@
}
}
},
- "Word" : {
- "extractionState" : "stale",
+ "Wiping your wallet…" : {
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
- "value" : "كلمة"
+ "value" : "جاري مسح محفظتك…"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Wort"
+ "value" : "Ihr Wallet wird gelöscht…"
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Palabra"
+ "value" : "Borrando tu billetera…"
}
},
"fa-IR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "واژه"
+ "value" : "درحال پاکسازی کیفپول…"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Mot"
+ "value" : "Effacement de votre portefeuille…"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
- "value" : "शब्द"
+ "value" : "आपके वॉलेट को मिटाया जा रहा है…"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Kata"
+ "value" : "Menghapus dompet Anda…"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Parola"
+ "value" : "Cancellazione del portafoglio in corso…"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
- "value" : "単語"
+ "value" : "ウォレットをワイプしています…"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
- "value" : "단어"
+ "value" : "지갑을 초기화하는 중…"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Woord"
+ "value" : "Uw portemonnee wordt gewist…"
}
},
"pa" : {
"stringUnit" : {
"state" : "translated",
- "value" : "ਸ਼ਬਦ"
+ "value" : "ਤੁਹਾਡਾ ਵਾਲਿਟ ਮਿਟਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Słowo"
+ "value" : "Czyszczenie portfela…"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Palavra"
+ "value" : "Limpando sua carteira…"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Слово"
+ "value" : "Очистка вашего кошелька…"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Ord"
+ "value" : "Rensar din plånbok…"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
- "value" : "คำ"
+ "value" : "กำลังล้างกระเป๋าสตางค์ของคุณ…"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Kelime"
+ "value" : "Cüzdanınız siliniyor…"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Слово"
+ "value" : "Очищення вашого гаманця…"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
- "value" : "单词"
+ "value" : "正在清除您的钱包…"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
- "value" : "單字"
+ "value" : "正在清除您的錢包…"
}
}
}
@@ -36772,137 +36100,6 @@
}
}
},
- "Your PIN will be used to unlock your Brainwallet and send money." : {
- "extractionState" : "stale",
- "localizations" : {
- "ar" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "سيتم استخدام رقم التعريف الشخصي لفتح محفظة الدماغ الخاصة بك وإرسال الأموال."
- }
- },
- "de" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ihre PIN wird verwendet, um Ihr Brainwallet zu entsperren und Geld zu senden."
- }
- },
- "es-419" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Su PIN se utilizará para desbloquear su Brainwallet y enviar dinero."
- }
- },
- "fa-IR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "رمز شما برای باز کردن قفل کیف پول مغزی شما و ارسال پول استفاده خواهد شد."
- }
- },
- "fr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Votre code PIN sera utilisé pour déverrouiller votre Brainwallet et envoyer de l’argent."
- }
- },
- "hi" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "आपके पिन का उपयोग आपके ब्रेनवॉलेट को अनलॉक करने और पैसे भेजने के लिए किया जाएगा।"
- }
- },
- "id" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PIN Anda akan digunakan untuk membuka kunci Brainwallet Anda dan mengirim uang."
- }
- },
- "it" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Il tuo PIN verrà utilizzato per sbloccare il tuo Brainwallet e inviare denaro."
- }
- },
- "ja" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PIN は Brainwallet のロックを解除して送金するために使用されます。"
- }
- },
- "ko" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PIN은 Brainwallet의 잠금을 해제하고 금액을 보내는 데 사용됩니다."
- }
- },
- "nl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Je PIN wordt gebruikt om je Brainwallet te ontgrendelen en geld te versturen."
- }
- },
- "pa" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "ਤੁਹਾਡੇ ਪਿੰਨ ਦੀ ਵਰਤੋਂ ਤੁਹਾਡੇ ਬ੍ਰੇਨਵਾਲਿਟ ਨੂੰ ਅਨਲੌਕ ਕਰਨ ਅਤੇ ਪੈਸੇ ਭੇਜਣ ਲਈ ਕੀਤੀ ਜਾਵੇਗੀ।"
- }
- },
- "pl" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Twój kod PIN zostanie użyty do odblokowania Brainwallet i wysłania pieniędzy."
- }
- },
- "pt-BR" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Seu PIN será usado para desbloquear o Brainwallet e enviar dinheiro."
- }
- },
- "ru" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ваш PIN-код будет использоваться для разблокировки кошелька Brainwallet и отправки денег."
- }
- },
- "sv" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Din PIN-kod kommer att användas för att låsa upp din Brainwallet och skicka pengar."
- }
- },
- "th" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PIN ของคุณจะใช้เพื่อปลดล็อก Brainwallet และส่งเงิน"
- }
- },
- "tr" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "PIN kodunuz Brainwallet'ınızın kilidini açmak ve para göndermek için kullanılacaktır."
- }
- },
- "uk" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "Ваш PIN-код буде використано для розблокування вашого Brainwallet та надсилання грошей."
- }
- },
- "zh-Hans" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "您的PIN码将用于解锁您的脑钱包并发送资金。"
- }
- },
- "zh-Hant" : {
- "stringUnit" : {
- "state" : "translated",
- "value" : "您的 PIN 將用於解鎖您的 Brainwallet 並匯款。"
- }
- }
- }
- },
"Your seed phrase is verified!" : {
"localizations" : {
"ar" : {
diff --git a/brainwallet/Networking/NetworkHelper.swift b/brainwallet/Networking/NetworkHelper.swift
index 3feef54b..ecc631b7 100644
--- a/brainwallet/Networking/NetworkHelper.swift
+++ b/brainwallet/Networking/NetworkHelper.swift
@@ -229,6 +229,8 @@ class NetworkHelper: ObservableObject, PublicIPAddressFetching {
completion(fallbackURLString)
}
}
+ } else {
+ completion("https://brainwallet.co/mobile-top-up.html")
}
}
task.resume()
diff --git a/brainwallet/New Buy Webview/WebBuyView.swift b/brainwallet/New Buy Webview/WebBuyView.swift
index f7b6ab11..b811c58d 100644
--- a/brainwallet/New Buy Webview/WebBuyView.swift
+++ b/brainwallet/New Buy Webview/WebBuyView.swift
@@ -28,10 +28,16 @@ struct WebBuyView: View {
private var signingData: MoonpaySigningData
- init(signingData: MoonpaySigningData, viewModel: NewReceiveViewModel) {
+ /// Called when the user taps back; the owner dismisses the web view sheet.
+ private var onBack: (() -> Void)?
+
+ init(signingData: MoonpaySigningData,
+ viewModel: NewReceiveViewModel,
+ onBack: (() -> Void)? = nil) {
self.signingData = signingData
self.viewModel = viewModel
+ self.onBack = onBack
}
var body: some View {
@@ -48,6 +54,23 @@ struct WebBuyView: View {
}
.frame(maxWidth: .infinity)
.padding([.leading, .trailing], 8.0)
+
+ if let onBack {
+ VStack {
+ HStack {
+ Button(action: onBack) {
+ Image(systemName: "arrow.backward")
+ .font(.system(size: 18, weight: .semibold))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(width: 44, height: 44)
+ }
+ .accessibilityIdentifier("moonPayWebBackButton")
+ Spacer()
+ }
+ .padding(.leading, 8.0)
+ Spacer()
+ }
+ }
}
.onChange(of: viewModel.didFetchURLString) { _,didFetchURL in
diff --git a/brainwallet/New Lock Screen Classes/LockScreenFooterView.swift b/brainwallet/New Lock Screen Classes/LockScreenFooterView.swift
index 3d7e0ff1..cabdd154 100644
--- a/brainwallet/New Lock Screen Classes/LockScreenFooterView.swift
+++ b/brainwallet/New Lock Screen Classes/LockScreenFooterView.swift
@@ -119,9 +119,6 @@ struct LockScreenFooterView: View {
.onChange(of: userPrefersDarkMode) { _,_ in
viewModel.userDidSetThemePreference(userPrefersDarkMode: userPrefersDarkMode)
}
- .onChange(of: viewModel.didCompleteWipingWallet) { _,_ in
- shouldShowWipeAlert.toggle()
- }
}
}
}
diff --git a/brainwallet/New Lock Screen Classes/LockScreenHostingController.swift b/brainwallet/New Lock Screen Classes/LockScreenHostingController.swift
index f6bed888..d1cbf236 100644
--- a/brainwallet/New Lock Screen Classes/LockScreenHostingController.swift
+++ b/brainwallet/New Lock Screen Classes/LockScreenHostingController.swift
@@ -63,32 +63,35 @@ class LockScreenHostingController: UIHostingController, Subscrib
private func wipeWallet() {
guard let walletManager = walletManager else {
+ viewModel.wipeWalletDidFail = true
return
}
- let group = DispatchGroup()
-
- group.enter()
- DispatchQueue.walletQueue.async {
- _ = walletManager.peerManager?.disconnect()
- group.leave()
- }
-
- group.enter()
DispatchQueue.walletQueue.async {
- _ = walletManager.wipeWallet(pin: "forceWipe")
- group.leave()
- }
+ walletManager.peerManager?.disconnect()
+ let didWipeWallet = walletManager.wipeWallet(pin: "forceWipe")
+ let didDeleteDatabase = walletManager.deleteWalletDatabase(pin: "forceWipe")
+
+ var didWipeKeychainAccess = true
+ do {
+ try UserDefaults.wipeTrustedNodeKeychain()
+ try RemoteConfigHelper.sharedInstance.wipeEnvironmentKeychain()
+ } catch let error as NSError {
+ debugPrint("Error | wipeWallet: Failed to wipe keychain: \(error.localizedDescription)")
+ didWipeKeychainAccess = false
+ }
- group.enter()
- DispatchQueue.walletQueue.asyncAfter(deadline: .now() + 1.0) {
- _ = walletManager.deleteWalletDatabase(pin: "forceWipe")
- group.leave()
- }
+ DispatchQueue.main.async {
+ guard didWipeWallet, didDeleteDatabase, didWipeKeychainAccess else {
+ self.viewModel.wipeWalletDidFail = true
+ return
+ }
- group.notify(queue: .main) {
- self.walletWiped()
- NotificationCenter.default.post(name: .walletDidWipeNotification, object: nil)
+ self.walletWiped()
+ self.store.trigger(name: .reinitWalletManager {
+ NotificationCenter.default.post(name: .walletDidWipeNotification, object: nil)
+ })
+ }
}
}
diff --git a/brainwallet/New Lock Screen Classes/LockScreenViewModel.swift b/brainwallet/New Lock Screen Classes/LockScreenViewModel.swift
index 8d6f5c5c..94b695e8 100644
--- a/brainwallet/New Lock Screen Classes/LockScreenViewModel.swift
+++ b/brainwallet/New Lock Screen Classes/LockScreenViewModel.swift
@@ -25,6 +25,9 @@ class LockScreenViewModel: ObservableObject, Subscriber {
@Published
var didCompleteWipingWallet = false
+ @Published
+ var wipeWalletDidFail = false
+
@Published
var pinDigits = [Int(),Int(),Int(),Int()]
diff --git a/brainwallet/New Lock Screen Classes/WipeWalletView.swift b/brainwallet/New Lock Screen Classes/WipeWalletView.swift
index 4f9017bb..8516e0f5 100644
--- a/brainwallet/New Lock Screen Classes/WipeWalletView.swift
+++ b/brainwallet/New Lock Screen Classes/WipeWalletView.swift
@@ -16,17 +16,25 @@ struct WipeWalletView: View {
@Binding
var shouldDismiss: Bool
+ @Binding
+ var didCompleteWipe: Bool
+
@State
var isWipingWallet: Bool = false
let squareImageSize: CGFloat = 25.0
let themeBorderSize: CGFloat = 44.0
+ private var isBusy: Bool {
+ isWipingWallet && !didCompleteWipe && !viewModel.wipeWalletDidFail
+ }
+
init(viewModel: LockScreenViewModel,
shouldDismiss: Binding,
- didCompleteWipe: Binding,) {
+ didCompleteWipe: Binding) {
self.viewModel = viewModel
_shouldDismiss = shouldDismiss
+ _didCompleteWipe = didCompleteWipe
}
var body: some View {
@@ -51,7 +59,37 @@ struct WipeWalletView: View {
Spacer()
- if !isWipingWallet {
+ if viewModel.wipeWalletDidFail {
+ Text("Failed to wipe wallet")
+ .modifier(BWIPSRegular(size: 24.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(alignment: .center)
+ .padding(.all, 20.0)
+
+ Text("Please try again.")
+ .modifier(BWIPSRegular(size: 24.0, lineLimit: 2))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(alignment: .center)
+ .padding(.all, 20.0)
+ } else if didCompleteWipe {
+ Text("Wallet is deleted")
+ .modifier(BWIPSRegular(size: 24.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(alignment: .center)
+ .padding(.all, 20.0)
+
+ Text("Start over by swiping up and restarting the app.")
+ .modifier(BWIPSRegular(size: 24.0, lineLimit: 2))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(alignment: .center)
+ .padding(.all, 20.0)
+ } else if isWipingWallet {
+ Text("Wiping your wallet…")
+ .modifier(BWIPSRegular(size: 24.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(alignment: .center)
+ .padding(.all, 20.0)
+ } else {
Text("This will erase your PIN, data & memos. This cannot be undone.")
.modifier(BWIPSRegular(size: 24.0, lineLimit: 2))
.foregroundColor(BrainwalletColor.content)
@@ -72,26 +110,13 @@ struct WipeWalletView: View {
.foregroundColor(BrainwalletColor.content)
.tint(BrainwalletColor.surface)
.padding(.all, 20.0)
- } else {
- Text("Wallet is deleted")
- .modifier(BWIPSRegular(size: 24.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(alignment: .center)
- .padding(.all, 20.0)
-
- Text("Start over by swiping up and restarting the app.")
- .modifier(BWIPSRegular(size: 24.0, lineLimit: 2))
- .foregroundColor(BrainwalletColor.content)
- .frame(alignment: .center)
- .padding(.all, 20.0)
}
Spacer()
Button(action: {
- isWipingWallet.toggle()
- delay(4.0) {
- viewModel.startWipeProcess()
- }
+ viewModel.wipeWalletDidFail = false
+ isWipingWallet = true
+ viewModel.startWipeProcess()
}) {
ZStack {
RoundedRectangle(cornerRadius: largeButtonHeight/2)
@@ -113,6 +138,7 @@ struct WipeWalletView: View {
}
.padding(.all, 8.0)
}
+ .disabled(isBusy || didCompleteWipe)
.accessibilityIdentifier("Wipe Button")
Button(action: {
@@ -126,14 +152,14 @@ struct WipeWalletView: View {
Text("Cancel")
.frame(width: width * 0.9, height: largeButtonHeight, alignment: .center)
.modifier(BWIPSBold(size: 24.0))
- .foregroundColor( isWipingWallet ? BrainwalletColor.content.opacity(0.2) : BrainwalletColor.content)
+ .foregroundColor( isBusy ? BrainwalletColor.content.opacity(0.2) : BrainwalletColor.content)
.overlay(
RoundedRectangle(cornerRadius: largeButtonHeight/2)
- .stroke(isWipingWallet ? BrainwalletColor.content.opacity(0.2) : BrainwalletColor.content, lineWidth: 2.0)
+ .stroke(isBusy ? BrainwalletColor.content.opacity(0.2) : BrainwalletColor.content, lineWidth: 2.0)
)
}
.padding(.all, 8.0)
- .disabled(isWipingWallet)
+ .disabled(isBusy)
.accessibilityIdentifier("Cancel")
}
}
diff --git a/brainwallet/New Main Classes/NewMainViewModel.swift b/brainwallet/New Main Classes/NewMainViewModel.swift
index 64314729..fd5ebe59 100644
--- a/brainwallet/New Main Classes/NewMainViewModel.swift
+++ b/brainwallet/New Main Classes/NewMainViewModel.swift
@@ -125,6 +125,11 @@ class NewMainViewModel: ObservableObject, Subscriber {
@Published
var currentNetworkFee = Litecoin(rawValue: 0.0)
+ /// Tiered (economy/regular/luxury) fee-per-kb, sourced from BWAPIClient.feePerKb(_:)
+ /// (GET v1/fee-per-kb) — backs the Network Fee picker in SettingsLitecoinDetailView.
+ @Published
+ var currentFees: Fees = .usingDefaultValues
+
@Published
var currentPreFeeAmount = Litecoin(rawValue: 0.0)
@@ -214,6 +219,13 @@ class NewMainViewModel: ObservableObject, Subscriber {
@Published
var canSelect: Bool = false
+
+
+ @Published
+ var shouldShowTrustedNodeSheet: Bool = false
+
+ @Published
+ var shouldShowEditTrustedIPAddress: Bool = false
init(store: Store, walletManager: WalletManager) {
self.store = store
@@ -253,6 +265,20 @@ class NewMainViewModel: ObservableObject, Subscriber {
dateFormatter!.setLocalizedDateFormatFromTemplate("dd MMM hh:mm:ss a")
setBalances()
updateTransactions()
+ fetchNetworkFees()
+ }
+
+ /// Fetches the tiered fee-per-kb from the mobile-api (v1/fee-per-kb) and
+ /// publishes it as `currentFees` for the Settings network fee picker.
+ private func fetchNetworkFees() {
+ walletManager?.apiClient?.feePerKb { [weak self] fees, error in
+ guard let self else { return }
+ if let error {
+ debugPrint("::: ERROR: fee-per-kb fetch failed: \(error)")
+ return
+ }
+ self.currentFees = fees
+ }
}
deinit {
diff --git a/brainwallet/New Main Classes/SyncSubBentoViewModel.swift b/brainwallet/New Main Classes/SyncSubBentoViewModel.swift
index cf6cad5c..37492cdc 100644
--- a/brainwallet/New Main Classes/SyncSubBentoViewModel.swift
+++ b/brainwallet/New Main Classes/SyncSubBentoViewModel.swift
@@ -45,6 +45,9 @@ class SyncSubBentoViewModel: ObservableObject, Subscriber {
@Published
var syncStateMessage = ""
+
+ @Published
+ var peerSyncMode: PeerSyncMode = .random_mainnet
@Published
var syncStateMessageColor: Color = BrainwalletColor.content
@@ -204,7 +207,7 @@ class SyncSubBentoViewModel: ObservableObject, Subscriber {
if reduxState.walletState.syncState == .syncing {
self.isSyncing = true
-
+ self.peerSyncMode = peerManager.syncMode
self.currentBlockHeightString = "\(peerManager.lastBlockHeight)"
self.currentBlockHeight = peerManager.lastBlockHeight
diff --git a/brainwallet/New Receive Classes/BuyReceiveView.swift b/brainwallet/New Receive Classes/BuyReceiveView.swift
index 0933673a..4fd4a3a5 100644
--- a/brainwallet/New Receive Classes/BuyReceiveView.swift
+++ b/brainwallet/New Receive Classes/BuyReceiveView.swift
@@ -84,6 +84,9 @@ struct BuyReceiveView: View {
@State
private var isModalMode: Bool = false
+ @Environment(\.dismiss)
+ private var dismiss
+
@FocusState
var keyboardFocused: Bool
@@ -204,7 +207,9 @@ struct BuyReceiveView: View {
if userIsBuying {
VStack {
ZStack {
- WebBuyView(signingData: viewModel.buildUnsignedMoonPayUrl(), viewModel: viewModel)
+ WebBuyView(signingData: viewModel.buildUnsignedMoonPayUrl(),
+ viewModel: viewModel,
+ onBack: { dismiss() })
Image("moonpay-symbol-prp")
.resizable()
diff --git a/brainwallet/New Receive Classes/NewReceiveViewModel.swift b/brainwallet/New Receive Classes/NewReceiveViewModel.swift
index e6997d72..8d19f575 100644
--- a/brainwallet/New Receive Classes/NewReceiveViewModel.swift
+++ b/brainwallet/New Receive Classes/NewReceiveViewModel.swift
@@ -201,6 +201,10 @@ class NewReceiveViewModel: ObservableObject, Subscriber {
/// fetchMoonpaySignedUrl()) wherever the app actually calls the MoonPay
/// signing endpoint.
func signAndFetchMoonPayUrl() {
+ // Reset so WebBuyView's onChange fires again on every launch
+ // and never loads a previous session's signed URL.
+ didFetchURLString = false
+ signedURLString = ""
guard cachedIPAddress.isEmpty else {
fetchMoonpaySignedUrl(signingData: buildUnsignedMoonPayUrl())
return
diff --git a/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/Contents.json b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/main-network-icon.imageset/Contents.json b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/main-network-icon.imageset/Contents.json
new file mode 100644
index 00000000..eb88a52e
--- /dev/null
+++ b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/main-network-icon.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "hub_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/main-network-icon.imageset/hub_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.png b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/main-network-icon.imageset/hub_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.png
new file mode 100644
index 00000000..0275158d
Binary files /dev/null and b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/main-network-icon.imageset/hub_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.png differ
diff --git a/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/trusted-icon.imageset/Contents.json b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/trusted-icon.imageset/Contents.json
new file mode 100644
index 00000000..1039e4cf
--- /dev/null
+++ b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/trusted-icon.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "handshake_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/trusted-icon.imageset/handshake_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.png b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/trusted-icon.imageset/handshake_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.png
new file mode 100644
index 00000000..5de0fb3f
Binary files /dev/null and b/brainwallet/PreLaunchResources/Assets.xcassets/Trusted/trusted-icon.imageset/handshake_24dp_FFFFFF_FILL0_wght400_GRAD0_opsz24.png differ
diff --git a/brainwallet/RemoteConfigHelper.swift b/brainwallet/RemoteConfigHelper.swift
index a37c6df5..59013210 100644
--- a/brainwallet/RemoteConfigHelper.swift
+++ b/brainwallet/RemoteConfigHelper.swift
@@ -30,6 +30,10 @@ class RemoteConfigHelper: NSObject {
deinit {}
+ func wipeEnvironmentKeychain() throws {
+ try keychainEnvironment.removeAll()
+ }
+
private func setupRemoteConfig() {
remoteConfig.setDefaults(fromPlist: "remote-config-defaults")
#if DEBUG
diff --git a/brainwallet/Set Passcode/SetPasscodeView.swift b/brainwallet/Set Passcode/SetPasscodeView.swift
index 1fa0956c..ab2b1598 100644
--- a/brainwallet/Set Passcode/SetPasscodeView.swift
+++ b/brainwallet/Set Passcode/SetPasscodeView.swift
@@ -28,8 +28,8 @@ struct SetPasscodeView: View {
private var userPrefersDarkTheme = UserDefaults.userPreferredDarkTheme
init(isRestoringAnOldWallet: Bool, path: Binding<[Onboarding]>) {
- self.isRestoringAnOldWallet = isRestoringAnOldWallet
_path = path
+ _isRestoringAnOldWallet = State(initialValue: isRestoringAnOldWallet)
}
var body: some View {
diff --git a/brainwallet/Settings Classes/CurrencyPickerView.swift b/brainwallet/Settings Classes/CurrencyPickerView.swift
index 743eda9b..9028e2ea 100644
--- a/brainwallet/Settings Classes/CurrencyPickerView.swift
+++ b/brainwallet/Settings Classes/CurrencyPickerView.swift
@@ -85,9 +85,9 @@ struct CurrencyPickerView: View {
Spacer()
}
.frame(width: width * 0.1, alignment: .leading)
- .background(BrainwalletColor.background)
+ .background(BrainwalletColor.surface)
}
- .background(BrainwalletColor.background)
+ .background(BrainwalletColor.surface)
}
}
diff --git a/brainwallet/Settings Classes/SettingsActionLockView.swift b/brainwallet/Settings Classes/SettingsActionLockView.swift
index b0d3f832..e01086fe 100644
--- a/brainwallet/Settings Classes/SettingsActionLockView.swift
+++ b/brainwallet/Settings Classes/SettingsActionLockView.swift
@@ -25,45 +25,32 @@ struct SettingsActionLockView: View {
}
var body: some View {
- NavigationStack {
- GeometryReader { _ in
- ZStack {
- HStack {
- VStack {
- Text(title)
- .modifier(BWIPSSemiBold(size: 19.0))
+ VStack(alignment: .leading, spacing: 0.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+ Text(title)
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .frame(height: closedRowHeight)
+ .overlay(alignment: .trailing) {
+ Button(action: {
+ didTriggerLock.toggle()
+ }) {
+ Image(systemName: didTriggerLock ? action.isOnSystemImage : action.isOffSystemImage)
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: 30.0,
+ height: 30.0)
.foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .frame(height: 25.0)
- .padding(.top, 8.0)
- Text(detailText)
- .modifier(BWIPSLight(size: 18.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .padding([.top, .bottom], 1.0)
- Spacer()
- }
- Spacer()
- VStack {
- Button(action: {
- didTriggerLock.toggle()
- }) {
- VStack {
- Image(systemName: didTriggerLock ? action.isOnSystemImage : action.isOffSystemImage)
- .resizable()
- .aspectRatio(contentMode: .fit)
- .frame(width: 30.0,
- height: 30.0)
- .foregroundColor(BrainwalletColor.content)
- Spacer()
- }
- }
- .frame(width: 30.0, height: 30.0)
- }
}
+ .frame(width: 30.0, height: 30.0)
+ .contentShape(Rectangle())
}
- }
}
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .fixedSize(horizontal: false, vertical: true)
}
}
diff --git a/brainwallet/Settings Classes/SettingsActionThemeView.swift b/brainwallet/Settings Classes/SettingsActionThemeView.swift
index 0cbc713b..516ee7db 100644
--- a/brainwallet/Settings Classes/SettingsActionThemeView.swift
+++ b/brainwallet/Settings Classes/SettingsActionThemeView.swift
@@ -10,59 +10,42 @@ import SwiftUI
struct SettingsActionThemeView: View {
private let title: String
- private let detailText: String
let action: SettingsAction
@Binding
var userPrefersDark: Bool
- init(title: String, detailText: String, action: SettingsAction, userPrefersDark: Binding) {
+ init(title: String, action: SettingsAction, userPrefersDark: Binding) {
self.title = title
- self.detailText = detailText
self.action = action
_userPrefersDark = userPrefersDark
}
var body: some View {
- NavigationStack {
- GeometryReader { _ in
- ZStack {
- HStack {
- VStack {
- Text(title)
- .modifier(BWIPSSemiBold(size: 19.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .frame(height: 25.0)
- .padding(.top, 8.0)
- Text(detailText)
- .modifier(BWIPSLight(size: 18.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .padding([.top, .bottom], 1.0)
- Spacer()
- }
-
- Spacer()
- VStack {
- Button(action: { userPrefersDark.toggle() }) {
- VStack {
- Image(systemName: userPrefersDark ? action.isOffSystemImage : action.isOnSystemImage)
- .resizable()
- .aspectRatio(contentMode: .fit)
- .frame(width: 30.0,
- height: 30.0)
- .foregroundColor(BrainwalletColor.content)
- .padding(20.0)
- Spacer()
- }
- }
+
+ VStack(alignment: .leading, spacing: 0.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+ Text(title)
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .frame(height: closedRowHeight)
+ .overlay(alignment: .trailing) {
+ Button(action: { userPrefersDark.toggle() }) {
+ Image(systemName: userPrefersDark ? action.isOffSystemImage : action.isOnSystemImage)
+ .resizable()
+ .aspectRatio(contentMode: .fit)
.frame(width: 30.0, height: 30.0)
- }
+ .foregroundColor(BrainwalletColor.content)
}
+ .frame(width: 30.0, height: 30.0)
+ .contentShape(Rectangle())
}
- }
}
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .fixedSize(horizontal: false, vertical: true)
}
}
diff --git a/brainwallet/Settings Classes/SettingsExpandingBlockchainView.swift b/brainwallet/Settings Classes/SettingsExpandingBlockchainView.swift
index 17572421..7e8c347b 100644
--- a/brainwallet/Settings Classes/SettingsExpandingBlockchainView.swift
+++ b/brainwallet/Settings Classes/SettingsExpandingBlockchainView.swift
@@ -19,73 +19,79 @@ struct SettingsExpandingBlockchainView: View {
@State
private var willSync: Bool = false
+
private var title: String
- init(title: String, viewModel: NewMainViewModel, shouldExpandBlockchain: Binding ) {
+ /// Height of the detail panel when expanded; total expanded row = closedRowHeight + this.
+ private let detailExpandedHeight: CGFloat = 340.0
+
+ init(title: String, viewModel: NewMainViewModel,
+ shouldExpandBlockchain: Binding ) {
self.title = title
_shouldExpandBlockchain = shouldExpandBlockchain
self.viewModel = viewModel
}
- var body: some View {
- NavigationStack {
- GeometryReader { _ in
- ZStack {
- VStack {
- HStack {
- VStack {
- Text(title)
- .modifier(BWIPSSemiBold(size: 19.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .frame(height: 25.0)
- .padding(.top, 8.0)
- }
+ @ViewBuilder
+ private var blockchainDetail: some View {
+ SettingsLitecoinDetailView(willSync: $willSync,
+ fees: viewModel.currentFees,
+ currentRate: viewModel.exchangeRate)
+ .frame(maxWidth: .infinity)
+ .environmentObject(viewModel)
+ }
- Spacer()
- VStack {
- Button(action: {
- shouldExpandBlockchain.toggle()
- }) {
- VStack {
- HStack {
- Image(systemName: "chevron.right")
- .resizable()
- .aspectRatio(contentMode: .fit)
- .frame(width: expandArrowSize,
- height: expandArrowSize)
- .foregroundColor(BrainwalletColor.content)
- .rotationEffect(Angle(degrees: shouldExpandBlockchain ? 90 : 0))
- }
- }
- .frame(width: 30.0, height: 30.0, alignment: .top)
- .padding(.top, 9.0)
- }
- .frame(width: 30.0, height: 30.0)
- }
+ var body: some View {
+ VStack(alignment: .leading, spacing: 0.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+ Text(title)
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leadingFirstTextBaseline)
+ .frame(height: closedRowHeight)
+ .overlay(alignment: .trailing) {
+ Button(action: {
+ withAnimation(.easeInOut(duration: 0.3)) {
+ shouldExpandBlockchain.toggle()
}
- .padding(.top, 1.0)
- SettingsLitecoinDetailView(willSync: $willSync)
- .transition(.opacity)
- .transition(.slide)
- .frame(height: shouldExpandBlockchain ? 200 : 0.1)
- .animation(.easeInOut(duration: 0.3), value: shouldExpandBlockchain)
- Spacer()
+ let impactMed = UIImpactFeedbackGenerator(style: .medium)
+ impactMed.impactOccurred()
+ }) {
+ Image(systemName: "chevron.down")
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: expandArrowSize, height: expandArrowSize)
+ .foregroundColor(BrainwalletColor.content)
+ .rotationEffect(Angle(degrees: shouldExpandBlockchain ? 180 : 0))
}
- .alert(String(localized: "Sync with Blockchain?"),
- isPresented: $willSync,
- actions: {
- Button(String(localized: "Cancel"), role: .cancel) { }
- Button( String(localized: "Ok"), role: .destructive) {
- viewModel.userWillSyncBlockchain()
- }
- },
- message: {
- Text("You will not be able to send Litecoin while syncing. It may take a while.")
- })
+ .frame(width: 30.0, height: 30.0)
+ .contentShape(Rectangle())
}
+
+ ScrollView(.vertical) {
+ blockchainDetail
}
+ .frame(height: shouldExpandBlockchain ? detailExpandedHeight : 0.0, alignment: .top)
+ .clipped()
+ .opacity(shouldExpandBlockchain ? 1.0 : 0.0)
+ .allowsHitTesting(shouldExpandBlockchain)
+ .scrollIndicators(.visible)
}
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .fixedSize(horizontal: false, vertical: true)
+ .alert(String(localized: "Sync with Blockchain?"),
+ isPresented: $willSync,
+ actions: {
+ Button(String(localized: "Cancel"), role: .cancel) { }
+ Button( String(localized: "Ok"), role: .destructive) {
+ viewModel.userWillSyncBlockchain()
+ }
+ },
+ message: {
+ Text("You will not be able to send Litecoin while syncing. It may take a while.")
+ })
}
}
diff --git a/brainwallet/Settings Classes/SettingsExpandingCurrencyView.swift b/brainwallet/Settings Classes/SettingsExpandingCurrencyView.swift
index 17954af8..7e92ca8e 100644
--- a/brainwallet/Settings Classes/SettingsExpandingCurrencyView.swift
+++ b/brainwallet/Settings Classes/SettingsExpandingCurrencyView.swift
@@ -24,6 +24,12 @@ struct SettingsExpandingCurrencyView: View {
private var pickedCurrency: GlobalCurrency = .USD
private var title: String
+
+ /// Height of the detail panel when expanded; total expanded row = closedRowHeight + this.
+ private let detailExpandedHeight: CGFloat = 120.0
+
+ //let pickerViewHeight: CGFloat = 160.0
+
init(title: String, viewModel: NewMainViewModel, shouldExpandCurrency: Binding ) {
self.title = title
@@ -32,64 +38,41 @@ struct SettingsExpandingCurrencyView: View {
}
var body: some View {
- NavigationStack {
- GeometryReader { _ in
- ZStack {
- VStack {
- HStack {
- VStack {
- Text("\(title) (\(pickedCurrency.symbol))")
- .modifier(BWIPSSemiBold(size: 19.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .frame(height: 25.0)
- .padding(.top, 8.0)
- }
- Spacer()
-
- VStack {
- Button(action: {
- shouldExpandCurrency.toggle()
- let impactRigid = UIImpactFeedbackGenerator(style: .rigid)
- impactRigid.impactOccurred()
- }) {
- VStack {
- HStack {
- Image(systemName: "chevron.right")
- .resizable()
- .aspectRatio(contentMode: .fit)
- .frame(width: expandArrowSize, height: expandArrowSize)
- .foregroundColor(BrainwalletColor.content)
- .rotationEffect(Angle(degrees: shouldExpandCurrency ? 90 : 0))
-
- }
-
- }
- .frame(width: 30.0, height: 30.0, alignment: .top)
- .padding(.top, 9.0)
-
- }
- .frame(width: 30.0, height: 30.0)
- }
- .frame(width: 30.0, height: 50.0)
-
+ VStack(alignment: .leading, spacing: 0.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+ Text("\(title) (\(pickedCurrency.symbol))")
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leadingFirstTextBaseline)
+ .frame(height: closedRowHeight)
+ .overlay(alignment: .trailing) {
+ Button(action: {
+ withAnimation(.easeInOut(duration: 0.3)) {
+ shouldExpandCurrency.toggle()
}
- .padding(.top, 1.0)
- .padding(.bottom, 8.0)
- CurrencyPickerView(viewModel: viewModel, pickedCurrency: $pickedCurrency)
- .transition(.opacity)
- .animation(.easeInOut(duration: 0.3), value: shouldExpandCurrency)
- .frame(height: shouldExpandCurrency ? pickerViewHeight : 0.1)
- Spacer()
- }
- .onChange(of: viewModel.currentGlobalFiat, { _, _ in
- pickedCurrency = viewModel.currentGlobalFiat
- })
- .onAppear {
- pickedCurrency = viewModel.currentGlobalFiat
+ let impactMed = UIImpactFeedbackGenerator(style: .medium)
+ impactMed.impactOccurred()
+ }) {
+ Image(systemName: "chevron.down")
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: expandArrowSize, height: expandArrowSize)
+ .foregroundColor(BrainwalletColor.content)
+ .rotationEffect(Angle(degrees: shouldExpandCurrency ? 180 : 0))
}
+ .frame(width: 30.0, height: 30.0)
+ .contentShape(Rectangle())
}
- }
+
+ CurrencyPickerView(viewModel: viewModel, pickedCurrency: $pickedCurrency)
+ .transition(.opacity)
+ .animation(.easeInOut(duration: 0.3), value: shouldExpandCurrency)
+ .frame(height: shouldExpandCurrency ? detailExpandedHeight : 0.0)
+ .opacity(shouldExpandCurrency ? 1.0 : 0.0)
}
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .fixedSize(horizontal: false, vertical: true)
}
}
diff --git a/brainwallet/Settings Classes/SettingsExpandingSecurityView.swift b/brainwallet/Settings Classes/SettingsExpandingSecurityView.swift
index 9c2ea4eb..612cf169 100644
--- a/brainwallet/Settings Classes/SettingsExpandingSecurityView.swift
+++ b/brainwallet/Settings Classes/SettingsExpandingSecurityView.swift
@@ -21,6 +21,9 @@ struct SettingsExpandingSecurityView: View {
var securityListView: SecurityListView
+ /// Height of the detail panel when expanded; total expanded row = closedRowHeight + this.
+ private let detailExpandedHeight: CGFloat = 250.0
+
init(title: String, viewModel: NewMainViewModel, shouldExpandSecurity: Binding ) {
self.title = title
_shouldExpandSecurity = shouldExpandSecurity
@@ -29,56 +32,42 @@ struct SettingsExpandingSecurityView: View {
}
var body: some View {
- NavigationStack {
- GeometryReader { _ in
- ZStack {
- VStack {
- HStack {
- VStack {
- Text(title)
- .modifier(BWIPSSemiBold(size: 19.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .frame(height: 25.0)
- .padding(.top, 8.0)
- }
-
- Spacer()
- VStack {
- Button(action: {
- withAnimation(.easeInOut(duration: 0.3)) {
- shouldExpandSecurity.toggle()
- }
- let impactMed = UIImpactFeedbackGenerator(style: .medium)
- impactMed.impactOccurred()
- }) {
- VStack {
- HStack {
- Image(systemName: "chevron.right")
- .resizable()
- .aspectRatio(contentMode: .fit)
- .frame(width: expandArrowSize, height: expandArrowSize)
- .foregroundColor(BrainwalletColor.content)
- .rotationEffect(Angle(degrees: shouldExpandSecurity ? 90 : 0))
- }
- }
- .frame(width: 30.0, height: 30.0, alignment: .top)
- .padding(.top, 11.0)
- }
- .frame(width: 30.0, height: 30.0)
- }
+ VStack(alignment: .leading, spacing: 0.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+ Text(title)
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leadingFirstTextBaseline)
+ .frame(height: closedRowHeight)
+ .overlay(alignment: .trailing) {
+ Button(action: {
+ withAnimation(.easeInOut(duration: 0.3)) {
+ shouldExpandSecurity.toggle()
}
- .padding(.top, 1.0)
- SecurityListView(viewModel: viewModel)
- .transition(.opacity)
- .transition(.move(edge: .top))
- .animation(.easeInOut(duration: 0.3), value: shouldExpandSecurity)
- .padding(.top, 16.0)
- Spacer()
+ let impactMed = UIImpactFeedbackGenerator(style: .medium)
+ impactMed.impactOccurred()
+ }) {
+ Image(systemName: "chevron.down")
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: expandArrowSize, height: expandArrowSize)
+ .foregroundColor(BrainwalletColor.content)
+ .rotationEffect(Angle(degrees: shouldExpandSecurity ? 180 : 0))
}
-
+ .frame(width: 30.0, height: 30.0)
+ .contentShape(Rectangle())
}
- }
+
+ SecurityListView(viewModel: viewModel)
+ .frame(maxWidth: .infinity)
+ .frame(height: shouldExpandSecurity ? detailExpandedHeight : 0.0, alignment: .top)
+ .clipped()
+ .opacity(shouldExpandSecurity ? 1.0 : 0.0)
+ .allowsHitTesting(shouldExpandSecurity)
}
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .fixedSize(horizontal: false, vertical: true)
}
}
diff --git a/brainwallet/Settings Classes/SettingsFooterView.swift b/brainwallet/Settings Classes/SettingsFooterView.swift
deleted file mode 100644
index 3c1acbac..00000000
--- a/brainwallet/Settings Classes/SettingsFooterView.swift
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-// SettingsFooterView.swift
-// brainwallet
-//
-// Created by Kerry Washington on 19/06/2025.
-// Copyright © 2025 Grunt Software, LTD. All rights reserved.
-//
-import SwiftUI
-
-struct SettingsFooterView: View {
-
- private let titleFont: Font = .ibmPlexSansSemiBold(size: 19.0)
- private let darkBackgroundColor: Color = BrainwalletColor.nearBlack
- private let lightBackgroundColor: Color = BrainwalletColor.grape
- private var setBackgroundColor: Color = BrainwalletColor.grape
-
- let userPreferredDarkMode = UserDefaults.userPreferredDarkTheme
- init() {
- setBackgroundColor = userPreferredDarkMode ? darkBackgroundColor : lightBackgroundColor
- }
-
- var body: some View {
- GeometryReader { _ in
-
- ZStack {
- setBackgroundColor.edgesIgnoringSafeArea(.all)
- HStack {
- Text("App Version:")
- .foregroundColor(.white)
- .font(titleFont)
- .frame(maxWidth: .infinity, alignment: .leading)
- .padding(.leading, 44.0)
- Spacer()
- Text("\(AppVersion.string)")
- .foregroundColor(.white)
- .modifier(BWIPSLight(size: 18.0))
- .padding(.trailing, 16.0)
-
- }
- .padding(.top, 8.0)
-
- }
- }
- }
-}
diff --git a/brainwallet/Settings Classes/SettingsHelpers.swift b/brainwallet/Settings Classes/SettingsHelpers.swift
index 30335469..f8578966 100644
--- a/brainwallet/Settings Classes/SettingsHelpers.swift
+++ b/brainwallet/Settings Classes/SettingsHelpers.swift
@@ -7,7 +7,7 @@
//
import SwiftUI
-let closedRowHeight: CGFloat = 65.0
+let closedRowHeight: CGFloat = 50.0
let toggleRowHeight: CGFloat = 50.0
let updatePINRowHeight: CGFloat = 100.0
let phraseRowHeight: CGFloat = 60.0
@@ -15,7 +15,7 @@ let expandedRowHeight: CGFloat = 240.0
let rowLeadingPad: CGFloat = 32.0
let leadRowPad: CGFloat = 40.0
let trailRowPad: CGFloat = 16.0
-let expandArrowSize: CGFloat = 20.0
+let expandArrowSize: CGFloat = 16.0
let pickerViewHeight: CGFloat = 160.0
enum SettingsAction: CaseIterable {
diff --git a/brainwallet/Settings Classes/SettingsLabelView.swift b/brainwallet/Settings Classes/SettingsLabelView.swift
index 23d5dd30..6be1e481 100644
--- a/brainwallet/Settings Classes/SettingsLabelView.swift
+++ b/brainwallet/Settings Classes/SettingsLabelView.swift
@@ -20,27 +20,26 @@ struct SettingsLabelView: View {
}
var body: some View {
- NavigationStack {
- GeometryReader { _ in
- ZStack {
- rowBackgroundColor.edgesIgnoringSafeArea(.all)
- VStack {
- Text(title)
- .modifier(BWIPSSemiBold(size: 19.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .frame(height: 25.0)
- .padding(.top, 8.0)
- Text(detailText)
- .modifier(BWIPSRegular(size: 19.0))
- .kerning(0.2)
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- .frame(height: 21.0, alignment: .center)
- .padding(.bottom, 11.0)
- }
- }
+ VStack(alignment: .leading, spacing: 0.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+ VStack(alignment: .leading, spacing: 2.0) {
+ Text(title)
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ Text(detailText)
+ .modifier(BWIPSRegular(size: 14.0))
+ .kerning(0.2)
+ .foregroundColor(BrainwalletColor.content.opacity(0.8))
+ .frame(maxWidth: .infinity, alignment: .leading)
}
+ .frame(maxWidth: .infinity, alignment: .leading)
+ .frame(height: closedRowHeight)
}
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .fixedSize(horizontal: false, vertical: true)
+ .background(rowBackgroundColor)
}
}
diff --git a/brainwallet/Settings Classes/SettingsLitecoinDetailView.swift b/brainwallet/Settings Classes/SettingsLitecoinDetailView.swift
index aa7bc912..23fc5a9f 100644
--- a/brainwallet/Settings Classes/SettingsLitecoinDetailView.swift
+++ b/brainwallet/Settings Classes/SettingsLitecoinDetailView.swift
@@ -7,124 +7,260 @@
//
import SwiftUI
import FirebaseAnalytics
+import BrainwalletiOSPrivateGeneralPurpose
struct SettingsLitecoinDetailView: View {
-
- let detailFont: Font = .ibmPlexSansLight(size: 15.0)
- let dotSize: CGFloat = 12.0
-
- let litecoinSyncDetailA: String = String(localized: "Syncing is a process where Brainwallet scans the Litecoin blockchain to see if any transactions that match your seed words (private keys) are in any transactions.")
- let litecoinSyncDetailB: String = String(localized: "Found transactions are added to the device's database and added or subtracted creating your Brainwallet's balance. There is no Litecoin in any wallet. Litecoin is only found on the network. Brainwallet is a simple way to check your transaction history and it stores your private keys")
- let litecoinSyncDetailC: String = String(localized: "This process can take 5 - 45 mins depending on your connectivty to the Litecoin network.")
+
+ @EnvironmentObject
+ var viewModel: NewMainViewModel
@Binding
var willSync: Bool
- init(willSync: Binding) {
+ @StateObject
+ private var storeKitManager = StoreKitManager()
+
+ @State
+ private var userPrefersMainnetVsTrusted: Bool = UserDefaults.userPrefersUsingTrustedNode
+
+ let fees: Fees
+
+ let rate: Rate
+
+ @State
+ private var feesPerFiat = ""
+
+ @State
+ private var nodeLabel: String = String(localized: "Set IP Address")
+
+ @State
+ private var selectedNetworkFeeIndex = 1
+
+ private let syncButtonHeight: CGFloat = 40.0
+ private let syncButtonWidth: CGFloat = 180.0
+
+ init(willSync: Binding,
+ fees: Fees,
+ currentRate: Rate?) {
_willSync = willSync
- }
+ self.fees = fees
+ self.rate = currentRate ?? Rate(code: "", name: "", rate: 0.0, lastTimestamp: Date())
- var body: some View {
- NavigationStack {
- GeometryReader { geometry in
- let width = geometry.size.width
- let height = geometry.size.height
+ let prefersTrustedNode = UserDefaults.userPrefersUsingTrustedNode
+ _nodeLabel = State(initialValue: Self.nodeLabel(prefersTrustedNode: prefersTrustedNode))
+ }
- ZStack {
- List {
- HStack {
- Text(String(localized: "Sync to the Litecoin Blockchain"))
- .modifier(BWIPSSemiBold(size: 19.0))
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- Spacer()
- }
- .frame(height: height * 0.1)
- .padding(.top, 1.0)
- .background(BrainwalletColor.background)
- .listRowBackground(BrainwalletColor.background)
- .listRowSeparator(.hidden)
+ private static func nodeLabel(prefersTrustedNode: Bool) -> String {
+ if prefersTrustedNode {
+ if let trustedIpAddress = UserDefaults.userTrustedNodeIpAddress,
+ let trustedPort = UserDefaults.userTrustedNodePort {
+ return "\(trustedIpAddress):\(trustedPort)"
+ } else {
+ return String(localized: "Set IP Address")
+ }
+ } else {
+ return String(localized: "Litecoin Mainnet")
+ }
+ }
+
+ private func purchaseTrustedNodeOrEditIPAddress() {
+ if storeKitManager.isPurchasedNonConsumable(trustedLTCNodeProductId) {
+ viewModel.shouldShowEditTrustedIPAddress = true
+ } else {
+ viewModel.shouldShowTrustedNodeSheet = true
+ }
+ }
- HStack {
- Text(litecoinSyncDetailA)
- .modifier(BWIPSLight(size: 18.0))
- .multilineTextAlignment(.leading)
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- Spacer()
- }
- .padding(.top, 1.0)
- .background(BrainwalletColor.background)
- .listRowBackground(BrainwalletColor.background)
- .listRowSeparator(.hidden)
+ /// Re-applies the trusted-node/mainnet preference to the running SPV sync so a toggle
+ /// flip takes effect immediately rather than on next launch. `setFixedPeer` (called from
+ /// `connect()`) disconnects any currently-connected peers before pinning/clearing the
+ /// fixed peer, so this alone is enough to force a clean switch.
+ private func reconnectPeerManager() {
+ guard let walletManager = viewModel.walletManager else { return }
+ DispatchQueue.walletQueue.async {
+ walletManager.peerManager?.connect()
+ }
+ }
- HStack {
+ /// Litoshis (fee-per-kb) for the currently selected Picker tag (0 = economy, 1 = regular, 2 = luxury).
+ /// Fee tier math lives in `NetworkFeeTier` (FeeManager.swift) so it's unit-testable on its own.
+ private func feeInLitoshis(forTag tag: Int) -> UInt64 {
+ (NetworkFeeTier(rawValue: tag) ?? .regular).litoshis(from: fees)
+ }
- Text(litecoinSyncDetailB)
- .modifier(BWIPSLight(size: 18.0))
- .multilineTextAlignment(.leading)
- .foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- Spacer()
- }
- .padding(.top, 1.0)
- .background(BrainwalletColor.background)
- .listRowBackground(BrainwalletColor.background)
- .listRowSeparator(.hidden)
+ /// Converts the selected fee from litoshis to fiat and persists it as the user's
+ /// preferred network fee.
+ private func updateFeesPerFiat(litoshis: UInt64) {
+ feesPerFiat = NetworkFeeTier.formattedFiatAmount(litoshis: litoshis, rate: rate)
+ UserDefaults.userSetPreferredNetworkFee = Int(litoshis)
+ }
- HStack {
+ /// Finds which of the 3 fee tiers (economy/regular/luxury -> 0/1/2) is numerically
+ /// closest to a previously stored litoshi fee value.
+ private func closestFeeIndex(toLitoshis storedFee: Int) -> Int {
+ NetworkFeeTier.closest(to: storedFee, in: fees).rawValue
+ }
- Text(litecoinSyncDetailC)
- .modifier(BWIPSLight(size: 18.0))
- .multilineTextAlignment(.leading)
+ var body: some View {
+ VStack(alignment: .leading, spacing: 20.0) {
+
+ // MARK: - Peer sync mode
+ VStack(alignment: .leading, spacing: 6.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+
+ Text(String(localized: "Peer sync mode"))
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ Text(String(localized: "Set from spv peers in the Litecoin mainnet or a trusted peer to sync with the blockchain"))
+ .modifier(BWIPSRegular(size: 14.0, lineLimit: 2))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ HStack(alignment: .center) {
+ VStack(alignment: .leading, spacing: 10.0) {
+ Text(String(localized: "Toggle mode"))
+ .modifier(BWIPSSemiBold(size: 14.0))
.foregroundColor(BrainwalletColor.content)
- .frame(maxWidth: .infinity, alignment: .leading)
- Spacer()
- }
- .padding(.top, 1.0)
- .background(BrainwalletColor.background)
- .listRowBackground(BrainwalletColor.background)
- .listRowSeparator(.hidden)
- .background(BrainwalletColor.background)
- HStack {
- Button(action: {
- willSync.toggle()
- Analytics.logEvent("did_start_resync",
- parameters: nil)
- }) {
- ZStack {
- RoundedRectangle(cornerRadius: largeButtonHeight/2)
- .frame(width: width * 0.9, height: largeButtonHeight,
- alignment: .center)
- .frame(height: largeButtonHeight, alignment: .center)
- .foregroundColor(BrainwalletColor.surface)
-
- Text("Sync")
- .frame(width: width * 0.9, height: largeButtonHeight,
- alignment: .center)
- .frame(maxWidth: .infinity, alignment: .center)
- .modifier(BWIPSSemiBold(size: 19.0))
- .foregroundColor(BrainwalletColor.content)
- .overlay(
- RoundedRectangle(cornerRadius: largeButtonHeight/2)
- .stroke(BrainwalletColor.content, lineWidth: 1.0)
- )
- }
+
+ Toggle(isOn: $userPrefersMainnetVsTrusted) {
+
+ }
+ .labelsHidden()
+ .toggleStyle(.switch)
+ .onChange(of: userPrefersMainnetVsTrusted) { _, newValue in
+ UserDefaults.userPrefersUsingTrustedNode = newValue
+ nodeLabel = Self.nodeLabel(prefersTrustedNode: newValue)
+ reconnectPeerManager()
}
+
}
- .frame(height: height * 0.2)
- .background(BrainwalletColor.background)
- .listRowBackground(BrainwalletColor.background)
- .listRowSeparator(.hidden)
- .padding(.bottom, 32.0)
+
Spacer()
-
+
+ Button(action: {
+ purchaseTrustedNodeOrEditIPAddress()
+ }) {
+ Text(nodeLabel)
+ .frame(width: syncButtonWidth, height: syncButtonHeight,
+ alignment: .center)
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundStyle(.white)
+ .background(
+ RoundedRectangle(cornerRadius: syncButtonHeight/2)
+ .fill(BrainwalletColor.background)
+
+ )
+ }
+ .buttonStyle(.plain)
+ }
+ .padding(.top, 6.0)
+ }
+
+ // MARK: - Sync duration
+ VStack(alignment: .leading, spacing: 0.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+
+ HStack(alignment: .center) {
+ GeometryReader { geo in
+ VStack(alignment: .center, spacing: 12.0) {
+ Text(String(localized: "Full Blockchain sync:"))
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ Text(String(localized: "If transactions are missing you can run a blockchain sync. This can take >3 hours."))
+ .modifier(BWIPSRegular(size: 14.0, lineLimit: 3))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ }
+ }
+ .frame(height: 90.0)
+ .padding(.top, 2.0)
+
+ Button(action: {
+ willSync.toggle()
+ Analytics.logEvent("did_start_resync",
+ parameters: nil)
+ }) {
+ Text("Sync")
+ .frame(width: syncButtonWidth, height: syncButtonHeight,
+ alignment: .center)
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundStyle(.white)
+ .background(
+ RoundedRectangle(cornerRadius: syncButtonHeight/2)
+ .fill(BrainwalletColor.background)
+ )
+ }
+ .frame(height: 44.0)
+ .buttonStyle(.plain)
}
- .background(BrainwalletColor.background)
- .listRowBackground(BrainwalletColor.background)
- .listRowSeparator(.hidden)
+ .padding(.top, 4.0)
}
+
+ // MARK: - Network fee
+ VStack(alignment: .leading, spacing: 6.0) {
+ Divider()
+ .frame(height: 1)
+ .overlay(Color.white)
+
+ Text(String(localized: "Network Fee (per kb):"))
+ .modifier(BWIPSSemiBold(size: 15.0))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ Text(String(localized: "A higher value means the transaction completes sooner"))
+ .modifier(BWIPSRegular(size: 14.0, lineLimit: 2))
+ .foregroundColor(BrainwalletColor.content)
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ GeometryReader { geo in
+ let height = geo.size.height
+ HStack(alignment: .center, spacing: 8.0) {
+ Text(String(localized: "Choose preferred fee:"))
+ .modifier(BWIPSSemiBold(size: 15.0, lineLimit: 1))
+ .foregroundColor(BrainwalletColor.content)
+
+ Picker("", selection: $selectedNetworkFeeIndex) {
+ Text("\(fees.economy) ł").modifier(BWIPSSemiBold(size: 14.0)).tag(0)
+ Text("\(fees.regular) ł").modifier(BWIPSSemiBold(size: 14.0)).tag(1)
+ Text("\(fees.luxury) ł").modifier(BWIPSSemiBold(size: 14.0)).tag(2)
+ }
+ .pickerStyle(.wheel)
+ .frame(width: geo.size.width * 0.4, height: 80.0)
+ .clipped()
+ .onAppear {
+ let fetchedFee = UserDefaults.userSetPreferredNetworkFee
+ selectedNetworkFeeIndex = closestFeeIndex(toLitoshis: fetchedFee)
+ updateFeesPerFiat(litoshis: feeInLitoshis(forTag: selectedNetworkFeeIndex))
+ }
+ .onChange(of: selectedNetworkFeeIndex) { _, _ in
+ switch selectedNetworkFeeIndex {
+ case 0: updateFeesPerFiat(litoshis: fees.economy)
+ case 1: updateFeesPerFiat(litoshis: fees.regular)
+ case 2: updateFeesPerFiat(litoshis: fees.luxury)
+ default: updateFeesPerFiat(litoshis: fees.luxury)
+ }
+ }
+
+ Text(feesPerFiat)
+ .modifier(BWIPSRegular(size: 14.0, lineLimit: 1))
+ .foregroundColor(BrainwalletColor.content)
+ }
+ }
+ .frame(height: 80.0)
+ .padding(.top, 2.0)
+ }
+ .padding(.bottom, 12.0)
+ }.onChange(of: viewModel.shouldShowEditTrustedIPAddress) { _,newValue in
+ debugPrint("||||shouldShowEditTrustedIPAddress \(newValue)")
+ nodeLabel = Self.nodeLabel(prefersTrustedNode: true)
}
- }
}
}
diff --git a/brainwallet/Settings Classes/SettingsView.swift b/brainwallet/Settings Classes/SettingsView.swift
index 0aa655b3..6b0857bd 100644
--- a/brainwallet/Settings Classes/SettingsView.swift
+++ b/brainwallet/Settings Classes/SettingsView.swift
@@ -7,157 +7,139 @@
//
//
import SwiftUI
+import BrainwalletiOSPrivateGeneralPurpose
struct SettingsView: View {
-
+
@ObservedObject
var newMainViewModel: NewMainViewModel
-
+
@Binding var path: [Onboarding]
-
+
@State
private var shouldLock: Bool = false
-
+
@State
private var didTriggerLock: Bool = false
-
+
@State
private var userPrefersDarkMode: Bool = true
-
+
@State
private var shouldExpandSecurity: Bool = false
-
+
@State
private var shouldExpandCurrency: Bool = false
-
+
@State
private var shouldExpandGames: Bool = false
-
+
@State
private var shouldExpandBlockchain: Bool = false
-
+
@State
private var shouldShowSocialSheet: Bool = false
-
+
@State
private var shouldShowSupportSheet: Bool = false
-
- @State
- private var tempRowHeight: CGFloat = closedRowHeight
-
+
let footerRowHeight: CGFloat = 55.0
-
let squareButtonSize: CGFloat = 55.0
let squareImageSize: CGFloat = 25.0
let themeBorderSize: CGFloat = 44.0
-
+
private let supportURL = URL(string: "https://brainwallet.co/support")!
-
+
private let socialsURL = URL(string: BrainwalletSocials.linktree)!
-
+
init(viewModel: NewMainViewModel, path: Binding<[Onboarding]>) {
self.newMainViewModel = viewModel
_path = path
-
+
userPrefersDarkMode = UserDefaults.userPreferredDarkTheme
}
-
+
var body: some View {
-
+
NavigationStack {
GeometryReader { geometry in
let width = geometry.size.width
+
ZStack {
-
+
if userPrefersDarkMode {
BrainwalletColor.surface.edgesIgnoringSafeArea(.all)
} else {
BrainwalletColor.surface.edgesIgnoringSafeArea(.all)
.padding(.trailing, 1.0)
}
-
+
HStack {
VStack {
- List {
- SettingsExpandingSecurityView(title: String(localized: "Security"),
- viewModel: newMainViewModel, shouldExpandSecurity: $shouldExpandSecurity)
- .frame(height: shouldExpandSecurity ? 300 : tempRowHeight)
- .listRowBackground(shouldExpandSecurity ? BrainwalletColor.background : BrainwalletColor.surface)
- .listRowInsets(EdgeInsets())
- .listRowSeparatorTint(BrainwalletColor.content)
- .padding(.top, leadRowPad)
- .padding(.leading, leadRowPad)
- .padding(.trailing, trailRowPad)
- SettingsExpandingCurrencyView(title: String(localized: "Fiat Currency"),
- viewModel: newMainViewModel, shouldExpandCurrency: $shouldExpandCurrency)
- .frame(height: shouldExpandCurrency ? pickerViewHeight : tempRowHeight)
- .listRowBackground(shouldExpandCurrency ? BrainwalletColor.background : BrainwalletColor.surface)
- .listRowInsets(EdgeInsets())
- .listRowSeparatorTint(BrainwalletColor.content)
- .padding(.leading, leadRowPad)
- .padding(.trailing, trailRowPad)
- SettingsExpandingBlockchainView(title: String(localized: "Blockchain: Litecoin"),
- viewModel: newMainViewModel, shouldExpandBlockchain: $shouldExpandBlockchain)
- .frame(maxWidth: .infinity)
- .frame(height: shouldExpandBlockchain ? 200 : tempRowHeight)
- .listRowBackground(shouldExpandBlockchain ? BrainwalletColor.background : BrainwalletColor.surface)
- .listRowInsets(EdgeInsets())
- .listRowSeparatorTint(BrainwalletColor.content)
- .padding(.leading, leadRowPad)
- .padding(.trailing, trailRowPad)
- SettingsLabelView(title: String(localized: "Social"),
- detailText: "linktr.ee/brainwallet")
- .frame(height: tempRowHeight)
- .listRowBackground(BrainwalletColor.surface)
- .listRowInsets(EdgeInsets())
- .listRowSeparatorTint(BrainwalletColor.content)
- .padding(.leading, leadRowPad)
- .padding(.trailing, trailRowPad)
- .onTapGesture {
- shouldShowSocialSheet.toggle()
- }
- SettingsLabelView(title: String(localized: "Support"),
- detailText: "support.brainwallet.co")
- .frame(height: tempRowHeight)
- .listRowBackground(BrainwalletColor.surface)
- .listRowInsets(EdgeInsets())
- .listRowSeparatorTint(BrainwalletColor.content)
- .padding(.leading, leadRowPad)
- .padding(.trailing, trailRowPad)
- .onTapGesture {
- shouldShowSupportSheet.toggle()
- }
- SettingsActionThemeView(title:
- userPrefersDarkMode ?
- String(localized: "Dark Mode")
- : String(localized: "Light Mode"),
- detailText: "",
- action: .preferDarkMode,
- userPrefersDark: $userPrefersDarkMode)
- .frame(height: tempRowHeight)
- .listRowBackground(BrainwalletColor.surface)
- .listRowInsets(EdgeInsets())
- .listRowSeparatorTint(BrainwalletColor.content)
- .padding(.leading, leadRowPad)
- .padding(.trailing, trailRowPad)
-
- SettingsActionLockView(title: String(localized: "Lock"),
- detailText: "", action: .lock, didTriggerLock: $didTriggerLock)
- .frame(height: tempRowHeight)
- .listRowBackground(BrainwalletColor.surface)
- .listRowInsets(EdgeInsets())
- .listRowSeparatorTint(BrainwalletColor.content)
- .padding(.leading, leadRowPad)
- .padding(.trailing, trailRowPad)
+ SettingsExpandingSecurityView(title: String(localized: "Security"),
+ viewModel: newMainViewModel, shouldExpandSecurity: $shouldExpandSecurity)
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .animation(.easeInOut(duration: 0.3), value: shouldExpandSecurity)
+ .padding(.top, leadRowPad)
+ .padding(.leading, leadRowPad)
+ .padding(.trailing, trailRowPad)
+ SettingsExpandingCurrencyView(title: String(localized: "Fiat Currency"),
+ viewModel: newMainViewModel,
+ shouldExpandCurrency: $shouldExpandCurrency)
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .animation(.easeInOut(duration: 0.3), value: shouldExpandCurrency)
+ .padding(.leading, leadRowPad)
+ .padding(.trailing, trailRowPad)
+ SettingsExpandingBlockchainView(title: String(localized: "Blockchain: Litecoin"),
+ viewModel: newMainViewModel,
+ shouldExpandBlockchain: $shouldExpandBlockchain)
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .animation(.easeInOut(duration: 0.3), value: shouldExpandBlockchain)
+ .padding(.leading, leadRowPad)
+ .padding(.trailing, trailRowPad)
+ SettingsLabelView(title: String(localized: "Social"),
+ detailText: "linktr.ee/brainwallet")
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .padding(.leading, leadRowPad)
+ .padding(.trailing, trailRowPad)
+ .onTapGesture {
+ shouldShowSocialSheet.toggle()
}
- .listStyle(.plain)
- .scrollIndicators(.hidden)
- .buttonStyle(PlainButtonStyle())
- SettingsFooterView()
- .frame(height: footerRowHeight * 0.4, alignment: .bottom)
- .padding(.bottom, 1.0)
- .padding(.top, 24.0)
-
+ SettingsLabelView(title: String(localized: "Support"),
+ detailText: "support.brainwallet.co")
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .padding(.leading, leadRowPad)
+ .padding(.trailing, trailRowPad)
+ .onTapGesture {
+ shouldShowSupportSheet.toggle()
+ }
+ SettingsActionThemeView(title:
+ userPrefersDarkMode ?
+ String(localized: "Dark Mode")
+ : String(localized: "Light Mode"),
+ action: .preferDarkMode,
+ userPrefersDark: $userPrefersDarkMode)
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .padding(.leading, leadRowPad)
+ .padding(.trailing, trailRowPad)
+
+ SettingsActionLockView(title: String(localized: "Lock"),
+ detailText: "", action: .lock, didTriggerLock: $didTriggerLock)
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .padding(.leading, leadRowPad)
+ .padding(.trailing, trailRowPad)
+
+ SettingsLabelView(title: String(localized: "App Version:"),
+ detailText: "\(AppVersion.string)")
+ .frame(maxWidth: .infinity, alignment: .topLeading)
+ .padding(.leading, leadRowPad)
+ .padding(.trailing, trailRowPad)
+ Divider()
+ .frame(height: 1)
+ .frame(width: width * 0.9)
+ .overlay(Color.white)
+ Spacer()
+
}
.frame(width: width * 0.9)
.onChange(of: userPrefersDarkMode) { _,hasDarkPreference in
@@ -180,9 +162,10 @@ struct SettingsView: View {
}
}
}
- .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
+ .simultaneousGesture(DragGesture(minimumDistance: 20, coordinateSpace: .global)
.onEnded({ value in
- if value.translation.width < 0 {
+ let didStartAtLeadingEdge = value.startLocation.x < 24.0
+ if didStartAtLeadingEdge && value.translation.width < 0 {
newMainViewModel.userDidTapTheSettingsButton()
}
}))
@@ -206,5 +189,34 @@ struct SettingsView: View {
.padding(8.0)
}
}
+ .sheet(isPresented: $newMainViewModel.shouldShowTrustedNodeSheet) {
+ TrustedNodeProductsModalView(userPrefersDarkTheme: userPrefersDarkMode) { didPurchase in
+ Task(priority: .userInitiated) {
+ if didPurchase {
+ UserDefaults.userTrustedNodePurchased = didPurchase
+ newMainViewModel.shouldShowTrustedNodeSheet = false
+ delay(0.1) {
+ newMainViewModel.shouldShowEditTrustedIPAddress = true
+ }
+ }
+ }
+ }
+ .padding(.bottom, 4)
+ .presentationDetents([.medium])
+ .presentationBackground(BrainwalletColor
+ .modalBackground(userPrefersDarkTheme: userPrefersDarkMode))
+ .presentationDragIndicator(.visible)
+ }
+ .sheet(isPresented: $newMainViewModel.shouldShowEditTrustedIPAddress) {
+ SetTrustedNodeIPModalView(shouldPresent: $newMainViewModel.shouldShowEditTrustedIPAddress,
+ userPrefersDarkTheme: userPrefersDarkMode)
+ .padding(.bottom, 4)
+ .presentationDetents([.medium])
+ .presentationBackground(BrainwalletColor
+ .modalBackground(userPrefersDarkTheme: userPrefersDarkMode))
+ .presentationDragIndicator(.visible)
+ }
}
}
+
+
diff --git a/brainwallet/Top Up/TopUpSetAmountView.swift b/brainwallet/Top Up/TopUpSetAmountView.swift
index d20478b8..45097ac1 100644
--- a/brainwallet/Top Up/TopUpSetAmountView.swift
+++ b/brainwallet/Top Up/TopUpSetAmountView.swift
@@ -196,7 +196,8 @@ struct TopUpSetAmountView: View {
VStack {
ZStack {
WebBuyView(signingData: receiveViewModel.buildUnsignedMoonPayUrl(),
- viewModel: receiveViewModel)
+ viewModel: receiveViewModel,
+ onBack: { userIsBuying = false })
Image("moonpay-symbol-prp")
.resizable()
.frame(width: 50.0, height: 50.0)
diff --git a/brainwallet/TrustedNode Classes/SetTrustedNodeIPModalView.swift b/brainwallet/TrustedNode Classes/SetTrustedNodeIPModalView.swift
new file mode 100644
index 00000000..447c65e0
--- /dev/null
+++ b/brainwallet/TrustedNode Classes/SetTrustedNodeIPModalView.swift
@@ -0,0 +1,153 @@
+//
+// SetTrustedNodeIPModalView.swift
+// brainwallet
+//
+// Created by Kerry Washington on 24/09/2026.
+// Copyright © 2026 Grunt Software, LTD. All rights reserved.
+//
+import SwiftUI
+
+struct SetTrustedNodeIPModalView: View {
+
+ @Binding
+ var shouldPresent: Bool
+
+ var userPrefersDarkTheme: Bool
+
+ @State
+ private var ipAddress: String = ""
+
+ @State
+ private var port: String = ""
+
+ private let fieldHeight: CGFloat = 66.0
+ private let buttonHeight: CGFloat = 56.0
+
+ init(shouldPresent: Binding, userPrefersDarkTheme: Bool) {
+ _shouldPresent = shouldPresent
+ self.userPrefersDarkTheme = userPrefersDarkTheme
+ _ipAddress = State(initialValue: UserDefaults.userTrustedNodeIpAddress ?? "")
+ _port = State(initialValue: UserDefaults.userTrustedNodePort ?? "")
+ }
+
+ private var canSave: Bool {
+ Self.isValidIPv4(ipAddress) && Self.isValidPort(port)
+ }
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 22.0) {
+
+ Text(String(localized: "Set trusted Litecoin node / SPV Peer"))
+ .modifier(BWIPSBold(size: 22.0, lineLimit: 2))
+ .foregroundColor(BrainwalletColor.modalPrimaryText(userPrefersDarkTheme: userPrefersDarkTheme))
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ Text(String(localized: "Enter the IPv4 address and port of the Litecoin node your Brainwallet should sync with."))
+ .modifier(BWIPSRegular(size: 15.0, lineLimit: 3))
+ .foregroundColor(BrainwalletColor.modalSecondaryText(userPrefersDarkTheme: userPrefersDarkTheme))
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ HStack(alignment: .top, spacing: 12.0) {
+ OutlinedNodeField(label: String(localized: "IP address"),
+ placeholder: "1.1.1.1",
+ text: $ipAddress,
+ keyboardType: .decimalPad,
+ height: fieldHeight,
+ userPrefersDarkTheme: userPrefersDarkTheme)
+ .frame(maxWidth: .infinity)
+
+ OutlinedNodeField(label: String(localized: "Port"),
+ placeholder: "9333",
+ text: $port,
+ keyboardType: .numberPad,
+ height: fieldHeight,
+ userPrefersDarkTheme: userPrefersDarkTheme)
+ .frame(width: 110.0)
+ }
+
+ Button(action: save) {
+ Text(String(localized: "Save trusted node"))
+ .modifier(BWIPSSemiBold(size: 17.0))
+ .foregroundColor(.white)
+ .frame(maxWidth: .infinity, minHeight: buttonHeight)
+ .background(
+ Capsule()
+ .fill(BentoColor.purple2.opacity(canSave ? 1.0 : 0.4))
+ )
+ }
+ .buttonStyle(.plain)
+ .disabled(!canSave)
+ }
+ .padding(.horizontal, 24.0)
+ .padding(.top, 28.0)
+ .padding(.bottom, 20.0)
+ .frame(maxWidth: .infinity, alignment: .top)
+ }
+
+ private func save() {
+ guard canSave else { return }
+ UserDefaults.userTrustedNodeIpAddress = ipAddress
+ UserDefaults.userTrustedNodePort = port
+ shouldPresent = false
+ }
+
+ private static func isValidIPv4(_ value: String) -> Bool {
+ let parts = value.split(separator: ".", omittingEmptySubsequences: false)
+ guard parts.count == 4 else { return false }
+ return parts.allSatisfy { part in
+ guard let octet = Int(part), (0 ... 255).contains(octet) else { return false }
+ return String(octet) == part // rejects leading zeros / stray characters
+ }
+ }
+
+ private static func isValidPort(_ value: String) -> Bool {
+ guard let number = Int(value) else { return false }
+ return (1 ... 65535).contains(number)
+ }
+}
+
+/// A bordered text field whose label sits notched into the top border,
+/// matching the outlined-field style from the Trusted Node design.
+private struct OutlinedNodeField: View {
+ let label: String
+ let placeholder: String
+
+ @Binding
+ var text: String
+
+ let keyboardType: UIKeyboardType
+ let height: CGFloat
+ let userPrefersDarkTheme: Bool
+
+ private var borderColor: Color {
+ BrainwalletColor.modalSecondaryText(userPrefersDarkTheme: userPrefersDarkTheme)
+ }
+
+ var body: some View {
+ ZStack(alignment: .topLeading) {
+ RoundedRectangle(cornerRadius: 12.0)
+ .stroke(borderColor, lineWidth: 1.0)
+
+ TextField("",
+ text: $text,
+ prompt: Text(placeholder)
+ .foregroundColor(borderColor.opacity(0.6)))
+ .keyboardType(keyboardType)
+ .autocorrectionDisabled(true)
+ .textInputAutocapitalization(.never)
+ .modifier(BWIPSSemiBold(size: 19.0))
+ .foregroundColor(BrainwalletColor.modalPrimaryText(userPrefersDarkTheme: userPrefersDarkTheme))
+ .padding(.horizontal, 14.0)
+ .padding(.top, 20.0)
+ .padding(.bottom, 10.0)
+
+ Text(label)
+ .modifier(BWIPSRegular(size: 13.0))
+ .foregroundColor(borderColor)
+ .padding(.horizontal, 4.0)
+ .background(BrainwalletColor.modalBackground(userPrefersDarkTheme: userPrefersDarkTheme))
+ .offset(x: 12.0, y: -9.0)
+ }
+ .frame(height: height)
+ }
+}