- all features of the button done - cleanup of everything that is not part of the design anymore, needed for the colors for the buttons - zcashStyle used for the buttons
This commit is contained in:
parent
e1a5fa9778
commit
e33ee405ee
|
@ -35,7 +35,6 @@ let package = Package(
|
|||
.library(name: "Pasteboard", targets: ["Pasteboard"]),
|
||||
.library(name: "Profile", targets: ["Profile"]),
|
||||
.library(name: "RecoveryPhraseDisplay", targets: ["RecoveryPhraseDisplay"]),
|
||||
.library(name: "RecoveryPhraseValidationFlow", targets: ["RecoveryPhraseValidationFlow"]),
|
||||
.library(name: "ReviewRequest", targets: ["ReviewRequest"]),
|
||||
.library(name: "Root", targets: ["Root"]),
|
||||
.library(name: "Sandbox", targets: ["Sandbox"]),
|
||||
|
@ -317,20 +316,6 @@ let package = Package(
|
|||
],
|
||||
path: "Sources/Features/RecoveryPhraseDisplay"
|
||||
),
|
||||
.target(
|
||||
name: "RecoveryPhraseValidationFlow",
|
||||
dependencies: [
|
||||
"FeedbackGenerator",
|
||||
"Generated",
|
||||
"Models",
|
||||
"Pasteboard",
|
||||
"UIComponents",
|
||||
"Utils",
|
||||
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
|
||||
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
|
||||
],
|
||||
path: "Sources/Features/RecoveryPhraseValidationFlow"
|
||||
),
|
||||
.target(
|
||||
name: "ReviewRequest",
|
||||
dependencies: [
|
||||
|
@ -355,7 +340,6 @@ let package = Package(
|
|||
"Models",
|
||||
"OnboardingFlow",
|
||||
"RecoveryPhraseDisplay",
|
||||
"RecoveryPhraseValidationFlow",
|
||||
"Sandbox",
|
||||
"SDKSynchronizer",
|
||||
"UIComponents",
|
||||
|
|
|
@ -26,7 +26,7 @@ public struct BalanceBreakdownView: View {
|
|||
HStack {
|
||||
Spacer()
|
||||
Text(L10n.BalanceBreakdown.blockId(viewStore.latestBlock))
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
.padding(.horizontal, 50)
|
||||
.padding(.vertical, 20)
|
||||
|
@ -34,7 +34,7 @@ public struct BalanceBreakdownView: View {
|
|||
balanceView(
|
||||
title: L10n.BalanceBreakdown.shieldedZec(tokenName),
|
||||
viewStore.shieldedBalance.data.verified,
|
||||
titleColor: Asset.Colors.Mfp.fontDark.color
|
||||
titleColor: Asset.Colors.primary.color
|
||||
)
|
||||
balanceView(title: L10n.BalanceBreakdown.transparentBalance, viewStore.transparentBalance.data.verified)
|
||||
balanceView(title: L10n.BalanceBreakdown.totalSpendableBalance, viewStore.totalSpendableBalance)
|
||||
|
@ -49,7 +49,7 @@ public struct BalanceBreakdownView: View {
|
|||
tokenName
|
||||
)
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
.padding(.horizontal, 50)
|
||||
|
||||
|
@ -67,7 +67,7 @@ public struct BalanceBreakdownView: View {
|
|||
}
|
||||
|
||||
extension BalanceBreakdownView {
|
||||
func balanceView(title: String, _ balance: Zatoshi, titleColor: Color = Asset.Colors.Mfp.fontDark.color) -> some View {
|
||||
func balanceView(title: String, _ balance: Zatoshi, titleColor: Color = Asset.Colors.primary.color) -> some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("\(title)")
|
||||
.foregroundColor(titleColor)
|
||||
|
@ -81,7 +81,7 @@ extension BalanceBreakdownView {
|
|||
.custom(FontFamily.Inter.regular.name, size: 32)
|
||||
.weight(.bold)
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
.padding(.horizontal, 50)
|
||||
}
|
||||
|
@ -93,18 +93,18 @@ extension BalanceBreakdownView {
|
|||
if viewStore.shieldingFunds {
|
||||
HStack(spacing: 10) {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: Asset.Colors.Text.activeButtonText.color))
|
||||
Text(L10n.BalanceBreakdown.shieldingFunds)
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: Asset.Colors.primary.color))
|
||||
Text(L10n.BalanceBreakdown.shieldingFunds.uppercased())
|
||||
}
|
||||
} else {
|
||||
Text(L10n.BalanceBreakdown.shieldFunds)
|
||||
}
|
||||
}
|
||||
)
|
||||
.activeButtonStyle
|
||||
.padding(.horizontal, 50)
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
.padding(.vertical, 20)
|
||||
.disable(when: !viewStore.isShieldableBalanceAvailable || viewStore.shieldingFunds, dimmingOpacity: 0.5)
|
||||
.disabled(!viewStore.isShieldableBalanceAvailable || viewStore.shieldingFunds)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ public struct HomeView: View {
|
|||
viewStore.send(.updateDestination(.transactionHistory))
|
||||
} label: {
|
||||
Text(L10n.Home.transactionHistory)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
@ -99,30 +99,29 @@ extension HomeView {
|
|||
SettingsView(store: store.settingsStore())
|
||||
}
|
||||
)
|
||||
.tint(Asset.Colors.Mfp.primary.color)
|
||||
.tint(Asset.Colors.primary.color)
|
||||
}
|
||||
|
||||
func sendButton(_ viewStore: HomeViewStore) -> some View {
|
||||
Button(action: {
|
||||
viewStore.send(.updateDestination(.send))
|
||||
}, label: {
|
||||
Text(L10n.Home.sendZec(tokenName))
|
||||
Text(L10n.Home.sendZec(tokenName).uppercased())
|
||||
})
|
||||
.activeButtonStyle
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
.padding(.bottom, 30)
|
||||
.disable(
|
||||
when: viewStore.isSendButtonDisabled,
|
||||
dimmingOpacity: 0.5
|
||||
)
|
||||
.disabled(viewStore.isSendButtonDisabled)
|
||||
}
|
||||
|
||||
func receiveButton(_ viewStore: HomeViewStore) -> some View {
|
||||
Button(action: {
|
||||
viewStore.send(.updateDestination(.profile))
|
||||
}, label: {
|
||||
Text(L10n.Home.receiveZec(tokenName))
|
||||
Text(L10n.Home.receiveZec(tokenName).uppercased())
|
||||
})
|
||||
.activeButtonStyle
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
.padding(.bottom, 30)
|
||||
}
|
||||
|
||||
|
@ -154,7 +153,7 @@ extension HomeView {
|
|||
}
|
||||
}
|
||||
}
|
||||
.foregroundColor(Asset.Colors.Mfp.primary.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ public struct ImportBirthdayView: View {
|
|||
.custom(FontFamily.Inter.regular.name, size: 16)
|
||||
.weight(.bold)
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
TextField(
|
||||
L10n.ImportWallet.Birthday.placeholder,
|
||||
|
@ -35,15 +35,13 @@ public struct ImportBirthdayView: View {
|
|||
.autocapitalization(.none)
|
||||
.importSeedEditorModifier()
|
||||
|
||||
Button(L10n.ImportWallet.Button.restoreWallet) {
|
||||
Button(L10n.ImportWallet.Button.restoreWallet.uppercased()) {
|
||||
viewStore.send(.restoreWallet)
|
||||
}
|
||||
.activeButtonStyle
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
.importWalletButtonLayout()
|
||||
.disable(
|
||||
when: !viewStore.isValidForm,
|
||||
dimmingOpacity: 0.5
|
||||
)
|
||||
.disabled(!viewStore.isValidForm)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
|
|
@ -25,21 +25,19 @@ public struct ImportWalletView: View {
|
|||
.custom(FontFamily.Inter.regular.name, size: 27)
|
||||
.weight(.bold)
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
.minimumScaleFactor(0.3)
|
||||
|
||||
ImportSeedEditor(store: store)
|
||||
.frame(width: nil, height: 200, alignment: .center)
|
||||
|
||||
Button(L10n.General.next) {
|
||||
Button(L10n.General.next.uppercased()) {
|
||||
viewStore.send(.updateDestination(.birthday))
|
||||
}
|
||||
.activeButtonStyle
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
.importWalletButtonLayout()
|
||||
.disable(
|
||||
when: !viewStore.isValidForm,
|
||||
dimmingOpacity: 0.5
|
||||
)
|
||||
.disabled(!viewStore.isValidForm)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
@ -71,8 +69,8 @@ extension ImportWalletView {
|
|||
)
|
||||
.foregroundColor(
|
||||
viewStore.isValidNumberOfWords ?
|
||||
Asset.Colors.Text.validMnemonic.color :
|
||||
Asset.Colors.Text.heading.color
|
||||
Asset.Colors.primary.color :
|
||||
Asset.Colors.primary.color
|
||||
)
|
||||
.padding(.trailing, 35)
|
||||
.padding(.bottom, 15)
|
||||
|
|
|
@ -16,7 +16,7 @@ public struct ImportSeedEditor: View {
|
|||
WithViewStore(store) { viewStore in
|
||||
TextEditor(text: viewStore.bindingForRedactableSeedPhrase(viewStore.importedSeedPhrase))
|
||||
.autocapitalization(.none)
|
||||
.importSeedEditorModifier(Asset.Colors.Mfp.fontDark.color)
|
||||
.importSeedEditorModifier(Asset.Colors.primary.color)
|
||||
.padding(.horizontal, 28)
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ struct ImportSeedEditorModifier: ViewModifier {
|
|||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
.padding(1)
|
||||
.background(backgroundColor)
|
||||
}
|
||||
|
|
|
@ -24,58 +24,27 @@ public struct OnboardingFlowReducer: ReducerProtocol {
|
|||
case createNewWallet
|
||||
case importExistingWallet
|
||||
}
|
||||
|
||||
public struct Step: Equatable, Identifiable {
|
||||
public let id: UUID
|
||||
public let title: String
|
||||
public let description: String
|
||||
public let background: Image
|
||||
}
|
||||
|
||||
public var destination: Destination?
|
||||
public var walletConfig: WalletConfig
|
||||
public var importWalletState: ImportWalletReducer.State
|
||||
public var index = 0
|
||||
public var skippedAtindex: Int?
|
||||
public var steps: IdentifiedArrayOf<Step> = Self.onboardingSteps
|
||||
|
||||
public var currentStep: Step { steps[index] }
|
||||
public var isFinalStep: Bool { steps.count == index + 1 }
|
||||
public var isInitialStep: Bool { index == 0 }
|
||||
public var progress: Int { ((index + 1) * 100) / (steps.count) }
|
||||
|
||||
public var offset: CGFloat {
|
||||
let maxOffset = CGFloat(-60)
|
||||
let stepOffset = CGFloat(maxOffset / CGFloat(steps.count - 1))
|
||||
guard index != 0 else { return .zero }
|
||||
return stepOffset * CGFloat(index)
|
||||
}
|
||||
|
||||
public init(
|
||||
destination: Destination? = nil,
|
||||
walletConfig: WalletConfig,
|
||||
importWalletState: ImportWalletReducer.State,
|
||||
index: Int = 0,
|
||||
skippedAtindex: Int? = nil,
|
||||
steps: IdentifiedArrayOf<Step> = Self.onboardingSteps
|
||||
importWalletState: ImportWalletReducer.State
|
||||
) {
|
||||
self.destination = destination
|
||||
self.walletConfig = walletConfig
|
||||
self.importWalletState = importWalletState
|
||||
self.index = index
|
||||
self.skippedAtindex = skippedAtindex
|
||||
self.steps = steps
|
||||
}
|
||||
}
|
||||
|
||||
public enum Action: Equatable {
|
||||
case back
|
||||
case createNewWallet
|
||||
case importExistingWallet
|
||||
case importWallet(ImportWalletReducer.Action)
|
||||
case next
|
||||
case onAppear
|
||||
case skip
|
||||
case updateDestination(OnboardingFlowReducer.State.Destination?)
|
||||
}
|
||||
|
||||
|
@ -91,30 +60,6 @@ public struct OnboardingFlowReducer: ReducerProtocol {
|
|||
Reduce { state, action in
|
||||
switch action {
|
||||
case .onAppear:
|
||||
if !state.walletConfig.isEnabled(.onboardingFlow) {
|
||||
return EffectTask(value: .skip)
|
||||
}
|
||||
return .none
|
||||
|
||||
case .back:
|
||||
guard state.index > 0 else { return .none }
|
||||
if let skippedFrom = state.skippedAtindex {
|
||||
state.index = skippedFrom
|
||||
state.skippedAtindex = nil
|
||||
} else {
|
||||
state.index -= 1
|
||||
}
|
||||
return .none
|
||||
|
||||
case .next:
|
||||
guard state.index < state.steps.count - 1 else { return .none }
|
||||
state.index += 1
|
||||
return .none
|
||||
|
||||
case .skip:
|
||||
guard state.skippedAtindex == nil else { return .none }
|
||||
state.skippedAtindex = state.index
|
||||
state.index = state.steps.count - 1
|
||||
return .none
|
||||
|
||||
case .updateDestination(let destination):
|
||||
|
@ -136,37 +81,6 @@ public struct OnboardingFlowReducer: ReducerProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
extension OnboardingFlowReducer.State {
|
||||
public static let onboardingSteps = IdentifiedArray(
|
||||
uniqueElements: [
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: L10n.Onboarding.Step1.title,
|
||||
description: L10n.Onboarding.Step1.description,
|
||||
background: Asset.Assets.Backgrounds.callout1.image
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: L10n.Onboarding.Step2.title,
|
||||
description: L10n.Onboarding.Step2.description,
|
||||
background: Asset.Assets.Backgrounds.callout2.image
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: L10n.Onboarding.Step3.title,
|
||||
description: L10n.Onboarding.Step3.description,
|
||||
background: Asset.Assets.Backgrounds.callout3.image
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: L10n.Onboarding.Step4.title,
|
||||
description: L10n.Onboarding.Step4.description,
|
||||
background: Asset.Assets.Backgrounds.callout4.image
|
||||
)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - ViewStore
|
||||
|
||||
extension OnboardingFlowViewStore {
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
//
|
||||
// OnboardingScreen.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Adam Stener on 11/7/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import UIComponents
|
||||
|
||||
public struct OnboardingScreen: View {
|
||||
let store: OnboardingFlowStore
|
||||
|
||||
public init(store: OnboardingFlowStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
VStack {
|
||||
ZStack {
|
||||
OnboardingHeaderView(
|
||||
store: store.scope(
|
||||
state: { state in
|
||||
OnboardingHeaderView.ViewState(
|
||||
walletConfig: state.walletConfig,
|
||||
isInitialStep: state.isInitialStep,
|
||||
isFinalStep: state.isFinalStep
|
||||
)
|
||||
},
|
||||
action: { action in
|
||||
switch action {
|
||||
case .back: return .back
|
||||
case .skip: return .skip
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
.zIndex(1)
|
||||
|
||||
OnboardingContentView(store: store)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
OnboardingFooterView(store: store)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.applyScreenBackground()
|
||||
.onAppear { viewStore.send(.onAppear) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingScreen_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingFlowReducer.State(
|
||||
walletConfig: .default,
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingFlowReducer(saplingActivationHeight: 0)
|
||||
)
|
||||
)
|
||||
.preferredColorScheme(.light)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingFlowReducer.State(
|
||||
walletConfig: .default,
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingFlowReducer(saplingActivationHeight: 0)
|
||||
)
|
||||
)
|
||||
.preferredColorScheme(.light)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone 8"))
|
||||
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingFlowReducer.State(
|
||||
walletConfig: .default,
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingFlowReducer(saplingActivationHeight: 0)
|
||||
)
|
||||
)
|
||||
.preferredColorScheme(.light)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone 14 Pro"))
|
||||
}
|
||||
}
|
|
@ -38,16 +38,19 @@ public struct PlainOnboardingView: View {
|
|||
|
||||
Spacer()
|
||||
|
||||
Button(L10n.PlainOnboarding.Button.createNewWallet) {
|
||||
Button(L10n.PlainOnboarding.Button.createNewWallet.uppercased()) {
|
||||
viewStore.send(.createNewWallet, animation: .easeInOut(duration: animationDuration))
|
||||
}
|
||||
.activeButtonStyle
|
||||
|
||||
Button(L10n.PlainOnboarding.Button.restoreWallet) {
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
.padding(.bottom, 30)
|
||||
|
||||
Button(L10n.PlainOnboarding.Button.restoreWallet.uppercased()) {
|
||||
viewStore.send(.importExistingWallet, animation: .easeInOut(duration: animationDuration))
|
||||
}
|
||||
.frame(height: 80)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.zcashStyle(.secondary)
|
||||
.padding(.horizontal, 70)
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
.padding(.all)
|
||||
.navigationLinkEmpty(
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
//
|
||||
// OnboardingContentView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Adam Stener on 11/18/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
public struct OnboardingContentView: View {
|
||||
let store: OnboardingFlowStore
|
||||
|
||||
public init(store: OnboardingFlowStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(self.store) { viewStore in
|
||||
let image = viewStore.steps[viewStore.index].background
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
|
||||
let title = Text(viewStore.steps[viewStore.index].title)
|
||||
.titleText()
|
||||
.lineLimit(0)
|
||||
.minimumScaleFactor(0.1)
|
||||
.padding(.vertical, 10)
|
||||
|
||||
let text = Text(viewStore.steps[viewStore.index].description)
|
||||
.paragraphText()
|
||||
.lineSpacing(2)
|
||||
.minimumScaleFactor(0.1)
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
if viewStore.isFinalStep {
|
||||
VStack {
|
||||
HStack {
|
||||
title
|
||||
.padding(.top, 60)
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
text
|
||||
image
|
||||
}
|
||||
} else {
|
||||
VStack {
|
||||
image
|
||||
HStack {
|
||||
title
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct OnboardingContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let store = Store(
|
||||
initialState: OnboardingFlowReducer.State(
|
||||
walletConfig: .default,
|
||||
importWalletState: .placeholder,
|
||||
index: 0
|
||||
),
|
||||
reducer: OnboardingFlowReducer(saplingActivationHeight: 0)
|
||||
)
|
||||
|
||||
OnboardingContentView_Previews.example(store)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
|
||||
OnboardingContentView_Previews.example(store)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone 8"))
|
||||
|
||||
OnboardingContentView_Previews.example(store)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone 12 Pro"))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
extension OnboardingContentView_Previews {
|
||||
static func example(_ store: Store<OnboardingFlowReducer.State, OnboardingFlowReducer.Action>) -> some View {
|
||||
ZStack {
|
||||
OnboardingHeaderView(
|
||||
store: store.scope(
|
||||
state: { state in
|
||||
OnboardingHeaderView.ViewState(
|
||||
walletConfig: state.walletConfig,
|
||||
isInitialStep: state.isInitialStep,
|
||||
isFinalStep: state.isFinalStep
|
||||
)
|
||||
},
|
||||
action: { action in
|
||||
switch action {
|
||||
case .back: return .back
|
||||
case .skip: return .skip
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
.zIndex(1)
|
||||
|
||||
OnboardingContentView(
|
||||
store: store
|
||||
)
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.preferredColorScheme(.light)
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
//
|
||||
// OnboardingFooterView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Adam Stener on 11/18/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
import ImportWallet
|
||||
|
||||
public struct OnboardingFooterView: View {
|
||||
let store: OnboardingFlowStore
|
||||
let animationDuration: CGFloat = 0.8
|
||||
|
||||
public init(store: OnboardingFlowStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(self.store) { viewStore in
|
||||
VStack(spacing: 5) {
|
||||
if viewStore.isFinalStep {
|
||||
Button(L10n.Onboarding.Button.newWallet) {
|
||||
viewStore.send(.createNewWallet, animation: .easeInOut(duration: animationDuration))
|
||||
}
|
||||
.activeButtonStyle
|
||||
.minimumScaleFactor(0.1)
|
||||
|
||||
Button(L10n.Onboarding.Button.importWallet) {
|
||||
viewStore.send(.importExistingWallet, animation: .easeInOut(duration: animationDuration))
|
||||
}
|
||||
.activeButtonStyle
|
||||
.minimumScaleFactor(0.1)
|
||||
} else {
|
||||
Button(L10n.General.next) {
|
||||
viewStore.send(.next, animation: .easeInOut(duration: animationDuration))
|
||||
}
|
||||
.activeButtonStyle
|
||||
.minimumScaleFactor(0.1)
|
||||
|
||||
ProgressView(
|
||||
String(format: "%02d", viewStore.index + 1),
|
||||
value: Double(viewStore.index + 1),
|
||||
total: Double(viewStore.steps.count)
|
||||
)
|
||||
.onboardingProgressStyle
|
||||
.padding(.horizontal, 30)
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
}
|
||||
.padding(.top, 10)
|
||||
.padding(.horizontal, 30)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForDestination(.importExistingWallet),
|
||||
destination: {
|
||||
ImportWalletView(
|
||||
store: store.scope(
|
||||
state: \.importWalletState,
|
||||
action: OnboardingFlowReducer.Action.importWallet
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable:next private_over_fileprivate strict_fileprivate
|
||||
fileprivate struct OnboardingFooterButtonLayout: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.frame(height: 60)
|
||||
.padding(.horizontal, 28)
|
||||
.transition(.opacity)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func onboardingFooterButtonLayout() -> some View {
|
||||
modifier(OnboardingFooterButtonLayout())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingFooterView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let store = Store<OnboardingFlowReducer.State, OnboardingFlowReducer.Action>(
|
||||
initialState: OnboardingFlowReducer.State(
|
||||
walletConfig: .default,
|
||||
importWalletState: .placeholder,
|
||||
index: 3
|
||||
),
|
||||
reducer: OnboardingFlowReducer(saplingActivationHeight: 0)
|
||||
)
|
||||
|
||||
Group {
|
||||
OnboardingFooterView(store: store)
|
||||
.applyScreenBackground()
|
||||
.preferredColorScheme(.light)
|
||||
.previewDevice("iPhone 14 Pro")
|
||||
|
||||
OnboardingFooterView(store: store)
|
||||
.applyScreenBackground()
|
||||
.preferredColorScheme(.light)
|
||||
.previewDevice("iPhone 13 Pro Max")
|
||||
|
||||
OnboardingFooterView(store: store)
|
||||
.applyScreenBackground()
|
||||
.preferredColorScheme(.light)
|
||||
.previewDevice("iPhone 13 mini")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
//
|
||||
// OnboardingNavigationButtons.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Adam Stener on 11/18/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
import Models
|
||||
|
||||
public struct OnboardingHeaderView: View {
|
||||
public struct ViewState: Equatable {
|
||||
public let walletConfig: WalletConfig
|
||||
public let isInitialStep: Bool
|
||||
public let isFinalStep: Bool
|
||||
}
|
||||
|
||||
public enum ViewAction {
|
||||
case back
|
||||
case skip
|
||||
}
|
||||
|
||||
let store: Store<ViewState, ViewAction>
|
||||
let animationDuration: CGFloat = 0.8
|
||||
|
||||
public init(store: Store<ViewState, ViewAction>) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(self.store) { viewStore in
|
||||
VStack {
|
||||
HStack {
|
||||
if !viewStore.isInitialStep && viewStore.walletConfig.isEnabled(.onboardingFlow) {
|
||||
Button(L10n.General.back) {
|
||||
viewStore.send(.back, animation: .easeInOut(duration: animationDuration))
|
||||
}
|
||||
.activeButtonStyle
|
||||
.frame(width: 75)
|
||||
.disabled(viewStore.isInitialStep)
|
||||
.minimumScaleFactor(0.1)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
if !viewStore.isInitialStep && !viewStore.isFinalStep {
|
||||
Button(L10n.General.skip) {
|
||||
viewStore.send(.skip, animation: .easeInOut(duration: animationDuration))
|
||||
}
|
||||
.activeButtonStyle
|
||||
.disabled(viewStore.isFinalStep)
|
||||
.frame(width: 150)
|
||||
.minimumScaleFactor(0.1)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
.frame(height: 40)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingHeaderView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let store = Store<OnboardingFlowReducer.State, OnboardingFlowReducer.Action>(
|
||||
initialState: OnboardingFlowReducer.State(
|
||||
walletConfig: .default,
|
||||
importWalletState: .placeholder,
|
||||
index: 0
|
||||
),
|
||||
reducer: OnboardingFlowReducer(saplingActivationHeight: 0)
|
||||
)
|
||||
|
||||
OnboardingHeaderView(
|
||||
store: store.scope(
|
||||
state: { state in
|
||||
OnboardingHeaderView.ViewState(
|
||||
walletConfig: state.walletConfig,
|
||||
isInitialStep: state.isInitialStep,
|
||||
isFinalStep: state.isFinalStep
|
||||
)
|
||||
},
|
||||
action: { action in
|
||||
switch action {
|
||||
case .back: return .back
|
||||
case .skip: return .skip
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
.preferredColorScheme(.light)
|
||||
.applyScreenBackground()
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
//
|
||||
// OnboardingProgressIndicator.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Adam Stener on 10/15/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Generated
|
||||
|
||||
struct OnboardingProgressStyle: ProgressViewStyle {
|
||||
let height: CGFloat = 3
|
||||
let gradient = LinearGradient(
|
||||
colors: [
|
||||
Asset.Colors.ProgressIndicator.gradientLeft.color,
|
||||
Asset.Colors.ProgressIndicator.gradientRight.color
|
||||
],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
let fractionCompleted = configuration.fractionCompleted ?? 0
|
||||
|
||||
return VStack {
|
||||
HStack {
|
||||
configuration.label
|
||||
.foregroundColor(Asset.Colors.Text.heading.color)
|
||||
.font(
|
||||
.custom(FontFamily.Inter.regular.name, size: 16)
|
||||
)
|
||||
.opacity(0.3)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
ZStack {
|
||||
GeometryReader { proxy in
|
||||
let currentWidth = proxy.size.width
|
||||
let progressMaxWidth = currentWidth * CGFloat(fractionCompleted)
|
||||
let trailingMaxWidth = currentWidth - (currentWidth * CGFloat(fractionCompleted))
|
||||
|
||||
HStack(spacing: 15) {
|
||||
if fractionCompleted > 0 {
|
||||
Capsule()
|
||||
.fill(gradient)
|
||||
.frame(maxWidth: progressMaxWidth)
|
||||
}
|
||||
|
||||
if fractionCompleted < 1 {
|
||||
Capsule()
|
||||
.fill(Asset.Colors.ProgressIndicator.negativeSpace.color)
|
||||
.frame(maxWidth: trailingMaxWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: height)
|
||||
.animation(.easeInOut, value: fractionCompleted)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ProgressView : onboardingProgressStyle
|
||||
|
||||
extension ProgressView {
|
||||
public var onboardingProgressStyle: some View {
|
||||
progressViewStyle(OnboardingProgressStyle())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Interactive ProgressStyle View
|
||||
|
||||
struct OnboardingProgressViewPreviewHelper: View {
|
||||
@State private var value: CGFloat = 35.0
|
||||
|
||||
var progressString: String {
|
||||
String(format: "%02d", value)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 50) {
|
||||
ProgressView(
|
||||
"\(Int(value))",
|
||||
value: value,
|
||||
total: 100
|
||||
)
|
||||
.onboardingProgressStyle
|
||||
|
||||
Slider(value: $value, in: 0...100, step: 1)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingProgressIndicator_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
OnboardingProgressViewPreviewHelper()
|
||||
}
|
||||
}
|
|
@ -68,10 +68,10 @@ public struct RecoveryPhraseDisplayView: View {
|
|||
VStack {
|
||||
Button(
|
||||
action: { viewStore.send(.finishedPressed) },
|
||||
label: { Text(L10n.RecoveryPhraseDisplay.Button.wroteItDown) }
|
||||
label: { Text(L10n.RecoveryPhraseDisplay.Button.wroteItDown.uppercased()) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(height: 60)
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
}
|
||||
.padding()
|
||||
} else {
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
//
|
||||
// RecoveryPhraseRandomizer.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 01.06.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Models
|
||||
|
||||
public struct RecoveryPhraseRandomizer {
|
||||
public func random(phrase: RecoveryPhrase) -> RecoveryPhraseValidationFlowReducer.State {
|
||||
let missingIndices = randomIndices()
|
||||
let missingWordChipKind = phrase.words(fromMissingIndices: missingIndices).shuffled()
|
||||
|
||||
return RecoveryPhraseValidationFlowReducer.State(
|
||||
phrase: phrase,
|
||||
missingIndices: missingIndices,
|
||||
missingWordChips: missingWordChipKind,
|
||||
validationWords: []
|
||||
)
|
||||
}
|
||||
|
||||
public func randomIndices() -> [Int] {
|
||||
(0..<RecoveryPhraseValidationFlowReducer.State.phraseChunks).map { _ in
|
||||
Int.random(in: 0 ..< RecoveryPhraseValidationFlowReducer.State.wordGroupSize)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
//
|
||||
// RecoveryPhraseRandomizerInterface.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 15.11.2022.
|
||||
//
|
||||
|
||||
import ComposableArchitecture
|
||||
import Models
|
||||
|
||||
extension DependencyValues {
|
||||
public var randomRecoveryPhrase: RecoveryPhraseRandomizerClient {
|
||||
get { self[RecoveryPhraseRandomizerClient.self] }
|
||||
set { self[RecoveryPhraseRandomizerClient.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
public struct RecoveryPhraseRandomizerClient {
|
||||
public var random: (RecoveryPhrase) -> RecoveryPhraseValidationFlowReducer.State
|
||||
|
||||
public init(random: @escaping (RecoveryPhrase) -> RecoveryPhraseValidationFlowReducer.State) {
|
||||
self.random = random
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
//
|
||||
// RecoveryPhraseRandomizerLiveKey.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 15.11.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ComposableArchitecture
|
||||
|
||||
extension RecoveryPhraseRandomizerClient: DependencyKey {
|
||||
public static let liveValue = Self(
|
||||
random: { RecoveryPhraseRandomizer().random(phrase: $0) }
|
||||
)
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
//
|
||||
// RecoveryPhraseRandomizerTestKey.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 15.11.2022.
|
||||
//
|
||||
|
||||
import ComposableArchitecture
|
||||
import XCTestDynamicOverlay
|
||||
|
||||
extension RecoveryPhraseRandomizerClient: TestDependencyKey {
|
||||
public static let testValue = Self(
|
||||
random: XCTUnimplemented("\(Self.self).random", placeholder: .placeholder)
|
||||
)
|
||||
}
|
|
@ -1,326 +0,0 @@
|
|||
//
|
||||
// RecoveryPhraseValidation.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Francisco Gindre on 10/29/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ComposableArchitecture
|
||||
import SwiftUI
|
||||
import Utils
|
||||
import FeedbackGenerator
|
||||
import UIComponents
|
||||
import Models
|
||||
import Pasteboard
|
||||
|
||||
public typealias RecoveryPhraseValidationFlowStore = Store<RecoveryPhraseValidationFlowReducer.State, RecoveryPhraseValidationFlowReducer.Action>
|
||||
public typealias RecoveryPhraseValidationFlowViewStore = ViewStore<RecoveryPhraseValidationFlowReducer.State, RecoveryPhraseValidationFlowReducer.Action>
|
||||
|
||||
public struct RecoveryPhraseValidationFlowReducer: ReducerProtocol {
|
||||
public struct State: Equatable {
|
||||
public enum Destination: Equatable, CaseIterable {
|
||||
case validation
|
||||
case success
|
||||
case failure
|
||||
}
|
||||
|
||||
static let wordGroupSize = 6
|
||||
static let phraseChunks = 4
|
||||
|
||||
public var phrase: RecoveryPhrase
|
||||
public var missingIndices: [Int]
|
||||
public var missingWordChips: [PhraseChip.Kind]
|
||||
public var validationWords: [ValidationWord]
|
||||
public var destination: Destination?
|
||||
|
||||
public var isComplete: Bool {
|
||||
!validationWords.isEmpty && validationWords.count == missingIndices.count
|
||||
}
|
||||
|
||||
public var isValid: Bool {
|
||||
guard let resultingPhrase = self.resultingPhrase else { return false }
|
||||
return resultingPhrase == phrase.words
|
||||
}
|
||||
|
||||
public init(phrase: RecoveryPhrase, missingIndices: [Int], missingWordChips: [PhraseChip.Kind], validationWords: [ValidationWord], destination: Destination? = nil) {
|
||||
self.phrase = phrase
|
||||
self.missingIndices = missingIndices
|
||||
self.missingWordChips = missingWordChips
|
||||
self.validationWords = validationWords
|
||||
self.destination = destination
|
||||
}
|
||||
}
|
||||
|
||||
public enum Action: Equatable {
|
||||
case updateDestination(RecoveryPhraseValidationFlowReducer.State.Destination?)
|
||||
case reset
|
||||
case move(wordChip: PhraseChip.Kind, intoGroup: Int)
|
||||
case succeed
|
||||
case fail
|
||||
case failureFeedback
|
||||
case proceedToHome
|
||||
case displayBackedUpPhrase
|
||||
}
|
||||
|
||||
@Dependency(\.feedbackGenerator) var feedbackGenerator
|
||||
@Dependency(\.mainQueue) var mainQueue
|
||||
@Dependency(\.pasteboard) var pasteboard
|
||||
@Dependency(\.randomRecoveryPhrase) var randomRecoveryPhrase
|
||||
|
||||
public init() {}
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
public func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask<Action> {
|
||||
switch action {
|
||||
case .reset:
|
||||
state = randomRecoveryPhrase.random(state.phrase)
|
||||
state.destination = .validation
|
||||
// FIXME [#186]: Resetting causes destination to be nil = preamble screen, hence setting the .validation. The transition back is not animated
|
||||
// though
|
||||
|
||||
case let .move(wordChip, group):
|
||||
guard
|
||||
case let PhraseChip.Kind.unassigned(word, _) = wordChip,
|
||||
let missingChipIndex = state.missingWordChips.firstIndex(of: wordChip)
|
||||
else { return .none }
|
||||
|
||||
state.missingWordChips[missingChipIndex] = .empty
|
||||
state.validationWords.append(ValidationWord(groupIndex: group, word: word))
|
||||
|
||||
if state.isComplete {
|
||||
let value: RecoveryPhraseValidationFlowReducer.Action = state.isValid ? .succeed : .fail
|
||||
let effect = EffectTask<RecoveryPhraseValidationFlowReducer.Action>(value: value)
|
||||
.delay(for: 1, scheduler: mainQueue)
|
||||
.eraseToEffect()
|
||||
|
||||
if value == .succeed {
|
||||
return effect
|
||||
} else {
|
||||
return .concatenate(
|
||||
EffectTask(value: .failureFeedback),
|
||||
effect
|
||||
)
|
||||
}
|
||||
}
|
||||
return .none
|
||||
|
||||
case .succeed:
|
||||
state.destination = .success
|
||||
|
||||
case .fail:
|
||||
state.destination = .failure
|
||||
|
||||
case .failureFeedback:
|
||||
feedbackGenerator.generateErrorFeedback()
|
||||
|
||||
case .updateDestination(let destination):
|
||||
guard let destination else {
|
||||
state = randomRecoveryPhrase.random(state.phrase)
|
||||
return .none
|
||||
}
|
||||
state.destination = destination
|
||||
|
||||
case .proceedToHome:
|
||||
break
|
||||
|
||||
case .displayBackedUpPhrase:
|
||||
break
|
||||
}
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationFlowReducer.State {
|
||||
func groupCompleted(index: Int) -> Bool {
|
||||
validationWords.first(where: { $0.groupIndex == index }) != nil
|
||||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationFlowReducer.State {
|
||||
/// Given an array of RecoveryPhraseStepCompletion, missing indices, original phrase and the number of groups it was split into,
|
||||
/// assembly the resulting phrase. This comes up with the "proposed solution" for the recovery phrase validation challenge.
|
||||
/// - returns:an array of String containing the recovery phrase words ordered by the original phrase order, or `nil`
|
||||
/// if a resulting phrase can't be formed because the validation state is not complete.
|
||||
public var resultingPhrase: [RedactableString]? {
|
||||
guard missingIndices.count == validationWords.count else { return nil }
|
||||
|
||||
guard validationWords.count == Self.phraseChunks else { return nil }
|
||||
|
||||
var words = phrase.words
|
||||
let groupLength = words.count / Self.phraseChunks
|
||||
// iterate based on the completions the user did on the UI
|
||||
for validationWord in validationWords {
|
||||
// figure out which phrase group (chunk) this completion belongs to
|
||||
let groupIndex = validationWord.groupIndex
|
||||
|
||||
// validate that's the right number
|
||||
assert(groupIndex < Self.phraseChunks)
|
||||
|
||||
// get the missing index that the user did this completion for on the given group
|
||||
let missingIndex = missingIndices[groupIndex]
|
||||
|
||||
// figure out what this means in terms of the whole recovery phrase
|
||||
let concreteIndex = groupIndex * groupLength + missingIndex
|
||||
|
||||
assert(concreteIndex < words.count)
|
||||
|
||||
// replace the word on the copy of the original phrase with the completion the user did
|
||||
words[concreteIndex] = validationWord.word
|
||||
}
|
||||
|
||||
return words
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ViewStore
|
||||
|
||||
extension RecoveryPhraseValidationFlowViewStore {
|
||||
func bindingForDestination(_ destination: RecoveryPhraseValidationFlowReducer.State.Destination) -> Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.destination == destination },
|
||||
send: { isActive in
|
||||
return .updateDestination(isActive ? destination : nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationFlowViewStore {
|
||||
var bindingForValidation: Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.destination != nil },
|
||||
send: { isActive in
|
||||
return .updateDestination(isActive ? .validation : nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForSuccess: Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.destination == .success },
|
||||
send: { isActive in
|
||||
return .updateDestination(isActive ? .success : .validation)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForFailure: Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.destination == .failure },
|
||||
send: { isActive in
|
||||
return .updateDestination(isActive ? .failure : .validation)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension RecoveryPhraseValidationFlowReducer.State {
|
||||
public static let placeholder = RecoveryPhraseValidationFlowReducer.State(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.unassigned(word: "thank".redacted),
|
||||
.unassigned(word: "morning".redacted),
|
||||
.unassigned(word: "boil".redacted),
|
||||
.unassigned(word: "garlic".redacted)
|
||||
],
|
||||
validationWords: [],
|
||||
destination: nil
|
||||
)
|
||||
|
||||
public static let placeholderStep1 = RecoveryPhraseValidationFlowReducer.State(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.unassigned(word: "thank".redacted),
|
||||
.empty,
|
||||
.unassigned(word: "boil".redacted),
|
||||
.unassigned(word: "garlic".redacted)
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning".redacted)
|
||||
],
|
||||
destination: nil
|
||||
)
|
||||
|
||||
public static let placeholderStep2 = RecoveryPhraseValidationFlowReducer.State(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.unassigned(word: "boil".redacted),
|
||||
.unassigned(word: "garlic".redacted)
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning".redacted),
|
||||
.init(groupIndex: 0, word: "thank".redacted)
|
||||
],
|
||||
destination: nil
|
||||
)
|
||||
|
||||
public static let placeholderStep3 = RecoveryPhraseValidationFlowReducer.State(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.unassigned(word: "boil".redacted),
|
||||
.empty
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning".redacted),
|
||||
.init(groupIndex: 0, word: "thank".redacted),
|
||||
.init(groupIndex: 3, word: "garlic".redacted)
|
||||
],
|
||||
destination: nil
|
||||
)
|
||||
|
||||
public static let placeholderStep4 = RecoveryPhraseValidationFlowReducer.State(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.empty,
|
||||
.empty
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning".redacted),
|
||||
.init(groupIndex: 0, word: "thank".redacted),
|
||||
.init(groupIndex: 3, word: "garlic".redacted),
|
||||
.init(groupIndex: 1, word: "boil".redacted)
|
||||
],
|
||||
destination: nil
|
||||
)
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationFlowStore {
|
||||
static let demo = Store(
|
||||
initialState: .placeholder,
|
||||
reducer: RecoveryPhraseValidationFlowReducer()
|
||||
)
|
||||
|
||||
static let demoStep1 = Store(
|
||||
initialState: .placeholderStep1,
|
||||
reducer: RecoveryPhraseValidationFlowReducer()
|
||||
)
|
||||
|
||||
static let demoStep2 = Store(
|
||||
initialState: .placeholderStep1,
|
||||
reducer: RecoveryPhraseValidationFlowReducer()
|
||||
)
|
||||
|
||||
static let demoStep3 = Store(
|
||||
initialState: .placeholderStep3,
|
||||
reducer: RecoveryPhraseValidationFlowReducer()
|
||||
)
|
||||
|
||||
static let demoStep4 = Store(
|
||||
initialState: .placeholderStep4,
|
||||
reducer: RecoveryPhraseValidationFlowReducer()
|
||||
)
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
//
|
||||
// RecoveryPhraseValidationFlowView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 03/01/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
|
||||
public struct RecoveryPhraseValidationFlowView: View {
|
||||
var store: RecoveryPhraseValidationFlowStore
|
||||
|
||||
public init(store: RecoveryPhraseValidationFlowStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
GeometryReader { proxy in
|
||||
VStack {
|
||||
VStack(alignment: .center, spacing: 20) {
|
||||
Text(L10n.RecoveryPhraseTestPreamble.title)
|
||||
.titleText()
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Text(L10n.RecoveryPhraseTestPreamble.paragraph1)
|
||||
.paragraphText()
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 44)
|
||||
.opacity(0.53)
|
||||
}
|
||||
.padding(.bottom, 40)
|
||||
|
||||
Asset.Assets.Backgrounds.calloutBackupFlow1.image
|
||||
.frame(
|
||||
width: circularFrameUniformSize(width: proxy.size.width, height: proxy.size.height),
|
||||
height: circularFrameUniformSize(width: proxy.size.width, height: proxy.size.height)
|
||||
)
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .center, spacing: 40) {
|
||||
VStack(alignment: .center, spacing: 20) {
|
||||
Text(L10n.RecoveryPhraseTestPreamble.paragraph2)
|
||||
.paragraphText()
|
||||
.multilineTextAlignment(.center)
|
||||
.opacity(0.53)
|
||||
|
||||
Text(L10n.RecoveryPhraseTestPreamble.paragraph3)
|
||||
.paragraphText()
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 10)
|
||||
.opacity(0.53)
|
||||
}
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.updateDestination(.validation)) },
|
||||
label: { Text(L10n.RecoveryPhraseTestPreamble.Button.goNext) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(
|
||||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 64,
|
||||
maxHeight: .infinity,
|
||||
alignment: .center
|
||||
)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.padding()
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.frame(width: proxy.size.width)
|
||||
.scrollableWhenScaledUp()
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForValidation,
|
||||
destination: {
|
||||
RecoveryPhraseBackupView(store: store)
|
||||
}
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
.navigationBarHidden(true)
|
||||
.applyScreenBackground()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Following computations are necessary to handle properly sizing and positioning of elements
|
||||
/// on different devices (aspects). iPhone SE and iPhone 8 are similar aspect family devices
|
||||
/// while iPhone X, 11, etc are different family devices, capable to use more of the space.
|
||||
extension RecoveryPhraseValidationFlowView {
|
||||
func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat {
|
||||
var deviceMultiplier = 1.0
|
||||
|
||||
if width > 0.0 {
|
||||
let aspect = height / width
|
||||
deviceMultiplier = 1.0 + (((aspect / 1.51) - 1.0) * 2.8)
|
||||
}
|
||||
|
||||
return width * 0.4 * deviceMultiplier
|
||||
}
|
||||
}
|
||||
|
||||
struct RecoveryPhraseTestPreambleView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NavigationView {
|
||||
RecoveryPhraseValidationFlowView(store: .demo)
|
||||
}
|
||||
|
||||
RecoveryPhraseValidationFlowView(store: .demo)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
//
|
||||
// ValidationFailed.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Francisco Gindre on 12/22/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
import Utils
|
||||
import UIComponents
|
||||
|
||||
public struct RecoveryPhraseBackupFailedView: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
var store: RecoveryPhraseValidationFlowStore
|
||||
|
||||
public init(store: RecoveryPhraseValidationFlowStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
GeometryReader { proxy in
|
||||
VStack {
|
||||
VStack(alignment: .center, spacing: 20) {
|
||||
Text(L10n.ValidationFailed.title)
|
||||
.titleText()
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding(.bottom, 40)
|
||||
|
||||
Asset.Assets.Backgrounds.calloutBackupFailed.image
|
||||
.frame(
|
||||
width: circularFrameUniformSize(width: proxy.size.width, height: proxy.size.height),
|
||||
height: circularFrameUniformSize(width: proxy.size.width, height: proxy.size.height)
|
||||
)
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .center, spacing: 40) {
|
||||
VStack(alignment: .center, spacing: 20) {
|
||||
Text(L10n.ValidationFailed.description)
|
||||
.paragraphText()
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 30)
|
||||
|
||||
Text(L10n.ValidationFailed.incorrectBackupDescription)
|
||||
.paragraphText()
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 20)
|
||||
}
|
||||
|
||||
Button(
|
||||
action: {
|
||||
viewStore.send(.reset)
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
},
|
||||
label: { Text(L10n.ValidationFailed.Button.tryAgain) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(
|
||||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 64,
|
||||
maxHeight: .infinity,
|
||||
alignment: .center
|
||||
)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.horizontal, 28)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.scrollableWhenScaledUp()
|
||||
}
|
||||
.padding()
|
||||
.navigationBarHidden(true)
|
||||
.applyScreenBackground()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Following computations are necessary to handle properly sizing and positioning of elements
|
||||
/// on different devices (aspects). iPhone SE and iPhone 8 are similar aspect family devices
|
||||
/// while iPhone X, 11, etc are different family devices, capable to use more of the space.
|
||||
extension RecoveryPhraseBackupFailedView {
|
||||
func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat {
|
||||
var deviceMultiplier = 1.0
|
||||
|
||||
if width > 0.0 {
|
||||
let aspect = height / width
|
||||
deviceMultiplier = 1.0 + (((aspect / 1.51) - 1.0) * 2.0)
|
||||
}
|
||||
|
||||
return width * 0.48 * deviceMultiplier
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct RecoveryPhraseBackupValidationFailedView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NavigationView {
|
||||
RecoveryPhraseBackupFailedView(store: .demo)
|
||||
}
|
||||
|
||||
RecoveryPhraseBackupFailedView(store: .demo)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
//
|
||||
// SuccessView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Adam Stener on 12/8/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
|
||||
public struct RecoveryPhraseBackupSucceededView: View {
|
||||
var store: RecoveryPhraseValidationFlowStore
|
||||
|
||||
public init(store: RecoveryPhraseValidationFlowStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
GeometryReader { proxy in
|
||||
VStack {
|
||||
VStack(spacing: 20) {
|
||||
Text(L10n.ValidationSuccess.title)
|
||||
.titleText()
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Text(L10n.ValidationSuccess.description)
|
||||
.paragraphText()
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 45)
|
||||
}
|
||||
.padding(.vertical, 40)
|
||||
|
||||
VStack {
|
||||
Asset.Assets.Backgrounds.calloutBackupSucceeded.image
|
||||
.frame(
|
||||
width: circularFrameUniformSize(width: proxy.size.width, height: proxy.size.height),
|
||||
height: circularFrameUniformSize(width: proxy.size.width, height: proxy.size.height)
|
||||
)
|
||||
}
|
||||
.padding(.bottom, 40)
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(spacing: 15) {
|
||||
Button(
|
||||
action: {
|
||||
viewStore.send(.proceedToHome, animation: .easeIn(duration: 1))
|
||||
},
|
||||
label: {
|
||||
Text(L10n.ValidationSuccess.Button.goToWallet)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
)
|
||||
.activeButtonStyle
|
||||
.recoveryPhraseBackupValidationSucceededViewLayout()
|
||||
|
||||
Button(
|
||||
action: {
|
||||
viewStore.send(
|
||||
.displayBackedUpPhrase,
|
||||
animation: .easeIn(duration: 1)
|
||||
)
|
||||
},
|
||||
label: {
|
||||
Text(L10n.ValidationSuccess.Button.phraseAgain)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
)
|
||||
.activeButtonStyle
|
||||
.recoveryPhraseBackupValidationSucceededViewLayout()
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.scrollableWhenScaledUp()
|
||||
}
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.applyScreenBackground()
|
||||
}
|
||||
}
|
||||
|
||||
/// Following computations are necessary to handle properly sizing and positioning of elements
|
||||
/// on different devices (aspects). iPhone SE and iPhone 8 are similar aspect family devices
|
||||
/// while iPhone X, 11, etc are different family devices, capable to use more of the space.
|
||||
extension RecoveryPhraseBackupSucceededView {
|
||||
func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat {
|
||||
var deviceMultiplier = 1.0
|
||||
|
||||
if width > 0.0 {
|
||||
let aspect = height / width
|
||||
deviceMultiplier = 1.0 + (((aspect / 1.51) - 1.0) * 2.0)
|
||||
}
|
||||
|
||||
return width * 0.48 * deviceMultiplier
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable:next private_over_fileprivate strict_fileprivate type_name
|
||||
fileprivate struct RecoveryPhraseBackupValidationSucceededViewLayout: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.frame(
|
||||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 64,
|
||||
maxHeight: .infinity,
|
||||
alignment: .center
|
||||
)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.horizontal, 28)
|
||||
.transition(.opacity)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func recoveryPhraseBackupValidationSucceededViewLayout() -> some View {
|
||||
modifier(RecoveryPhraseBackupValidationSucceededViewLayout())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
// swiftlint:disable:next type_name
|
||||
struct RecoveryPhraseBackupValidationSucceededView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NavigationView {
|
||||
RecoveryPhraseBackupSucceededView(store: .demo)
|
||||
}
|
||||
|
||||
RecoveryPhraseBackupSucceededView(store: .demo)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
//
|
||||
// RecoveryPhraseBackupView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Francisco Gindre on 10/29/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
import UIComponents
|
||||
import Models
|
||||
|
||||
public struct RecoveryPhraseBackupView: View {
|
||||
let store: RecoveryPhraseValidationFlowStore
|
||||
|
||||
var viewStore: RecoveryPhraseValidationFlowViewStore {
|
||||
ViewStore(store)
|
||||
}
|
||||
|
||||
public init(store: RecoveryPhraseValidationFlowStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
VStack(alignment: .center) {
|
||||
header(for: viewStore)
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
ZStack {
|
||||
Asset.Colors.BackgroundColors.phraseGridDarkGray.color
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
|
||||
VStack(alignment: .center, spacing: 35) {
|
||||
let state = viewStore.state
|
||||
let groups = state.phrase.toGroups()
|
||||
|
||||
ForEach(Array(zip(groups.indices, groups)), id: \.0) { index, group in
|
||||
WordChipGrid(
|
||||
state: state,
|
||||
groupIndex: index,
|
||||
wordGroup: group,
|
||||
misingIndex: index
|
||||
)
|
||||
.frame(alignment: .center)
|
||||
.background(Asset.Colors.BackgroundColors.phraseGridDarkGray.color)
|
||||
.whenIsDroppable(
|
||||
!state.groupCompleted(index: index),
|
||||
dropDelegate: WordChipDropDelegate { chipKind in
|
||||
viewStore.send(.move(wordChip: chipKind, intoGroup: index))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding()
|
||||
.padding(.top, 0)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForSuccess,
|
||||
destination: { RecoveryPhraseBackupSucceededView(store: store) }
|
||||
)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForFailure,
|
||||
destination: { RecoveryPhraseBackupFailedView(store: store) }
|
||||
)
|
||||
}
|
||||
.frame(alignment: .top)
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.scrollableWhenScaledUp()
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle(Text(L10n.RecoveryPhraseBackupValidation.title))
|
||||
}
|
||||
}
|
||||
|
||||
private extension RecoveryPhraseBackupView {
|
||||
@ViewBuilder func header(for viewStore: RecoveryPhraseValidationFlowViewStore) -> some View {
|
||||
VStack {
|
||||
if viewStore.isComplete {
|
||||
completeHeader(for: viewStore.state)
|
||||
} else {
|
||||
Text(L10n.RecoveryPhraseBackupValidation.description)
|
||||
.bodyText()
|
||||
}
|
||||
|
||||
viewStore.state.missingWordGrid()
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
}
|
||||
|
||||
@ViewBuilder func completeHeader(for state: RecoveryPhraseValidationFlowReducer.State) -> some View {
|
||||
if state.isValid {
|
||||
Text(L10n.RecoveryPhraseBackupValidation.successResult)
|
||||
.bodyText()
|
||||
} else {
|
||||
Text(L10n.RecoveryPhraseBackupValidation.failedResult)
|
||||
.bodyText()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension RecoveryPhraseValidationFlowReducer.State {
|
||||
@ViewBuilder func missingWordGrid() -> some View {
|
||||
let columns = Array(
|
||||
repeating: GridItem(.flexible(minimum: 100, maximum: 120), spacing: 20),
|
||||
count: 2
|
||||
)
|
||||
|
||||
LazyVGrid(columns: columns, alignment: .center, spacing: 20) {
|
||||
ForEach(0..<missingWordChips.count, id: \.self) { chipIndex in
|
||||
PhraseChip(kind: missingWordChips[chipIndex])
|
||||
.makeDraggable()
|
||||
.frame(
|
||||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 30
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(0)
|
||||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationFlowReducer.State {
|
||||
public func wordsChips(
|
||||
for groupIndex: Int,
|
||||
groupSize: Int,
|
||||
from wordGroup: RecoveryPhrase.Group
|
||||
) -> [PhraseChip.Kind] {
|
||||
let validationWord = validationWords.first(where: { $0.groupIndex == groupIndex })
|
||||
|
||||
return wordGroup.words.enumerated().map { index, word in
|
||||
guard index == missingIndices[groupIndex] else {
|
||||
return .ordered(position: (groupSize * groupIndex) + index + 1, word: word)
|
||||
}
|
||||
|
||||
if let completedWord = validationWord?.word {
|
||||
return .unassigned(word: completedWord, color: self.coloredChipColor)
|
||||
}
|
||||
|
||||
return .empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension WordChipGrid {
|
||||
init(
|
||||
state: RecoveryPhraseValidationFlowReducer.State,
|
||||
groupIndex: Int,
|
||||
wordGroup: RecoveryPhrase.Group,
|
||||
misingIndex: Int
|
||||
) {
|
||||
let chips = state.wordsChips(
|
||||
for: groupIndex,
|
||||
groupSize: RecoveryPhraseValidationFlowReducer.State.wordGroupSize,
|
||||
from: wordGroup
|
||||
)
|
||||
|
||||
self.init(chips: chips, coloredChipColor: state.coloredChipColor)
|
||||
}
|
||||
}
|
||||
|
||||
private extension RecoveryPhraseValidationFlowReducer.State {
|
||||
var coloredChipColor: Color {
|
||||
if self.isComplete {
|
||||
return isValid ? Asset.Colors.Buttons.activeButton.color : Asset.Colors.BackgroundColors.red.color
|
||||
} else {
|
||||
return Asset.Colors.Buttons.activeButton.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct RecoveryPhraseBackupView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
RecoveryPhraseValidationFlowView(store: .demoStep4)
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
RecoveryPhraseValidationFlowView(store: .demoStep1)
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
RecoveryPhraseValidationFlowView(store: .demoStep1)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ extension RootReducer {
|
|||
case home
|
||||
case onboarding
|
||||
case phraseDisplay
|
||||
case phraseValidation
|
||||
case sandbox
|
||||
case startup
|
||||
case welcome
|
||||
|
@ -55,25 +54,8 @@ extension RootReducer {
|
|||
case .sandbox(.reset):
|
||||
state.destinationState.destination = .startup
|
||||
|
||||
case .phraseValidation(.proceedToHome):
|
||||
state.destinationState.destination = .home
|
||||
|
||||
case .phraseValidation(.displayBackedUpPhrase):
|
||||
state.destinationState.destination = .phraseDisplay
|
||||
|
||||
case .phraseDisplay(.finishedPressed):
|
||||
// user is still supposed to do the backup phrase validation test
|
||||
if (state.destinationState.previousDestination == .welcome
|
||||
|| state.destinationState.previousDestination == .onboarding
|
||||
|| state.destinationState.previousDestination == .startup)
|
||||
&& state.walletConfig.isEnabled(.testBackupPhraseFlow) {
|
||||
state.destinationState.destination = .phraseValidation
|
||||
}
|
||||
// user wanted to see the backup phrase once again (at validation finished screen)
|
||||
if state.destinationState.previousDestination == .phraseValidation
|
||||
|| !state.walletConfig.isEnabled(.testBackupPhraseFlow) {
|
||||
state.destinationState.destination = .home
|
||||
}
|
||||
state.destinationState.destination = .home
|
||||
|
||||
case .destination(.deeplink(let url)):
|
||||
// get the latest synchronizer state
|
||||
|
@ -122,7 +104,7 @@ extension RootReducer {
|
|||
}
|
||||
return EffectTask(value: .destination(.deeplink(url)))
|
||||
|
||||
case .home, .initialization, .onboarding, .phraseDisplay, .phraseValidation, .sandbox, .updateStateAfterConfigUpdate, .alert,
|
||||
case .home, .initialization, .onboarding, .phraseDisplay, .sandbox, .updateStateAfterConfigUpdate, .alert,
|
||||
.welcome, .binding, .nukeWalletFailed, .nukeWalletSucceeded, .debug, .walletConfigLoaded, .exportLogs:
|
||||
return .none
|
||||
}
|
||||
|
|
|
@ -175,7 +175,6 @@ extension RootReducer {
|
|||
|
||||
let recoveryPhrase = RecoveryPhrase(words: phraseWords.map { $0.redacted })
|
||||
state.phraseDisplayState.phrase = recoveryPhrase
|
||||
state.phraseValidationState = randomRecoveryPhrase.random(recoveryPhrase)
|
||||
landingDestination = .phraseDisplay
|
||||
}
|
||||
|
||||
|
@ -199,25 +198,16 @@ extension RootReducer {
|
|||
let randomRecoveryPhraseWords = mnemonic.asWords(newRandomPhrase)
|
||||
let recoveryPhrase = RecoveryPhrase(words: randomRecoveryPhraseWords.map { $0.redacted })
|
||||
state.phraseDisplayState.phrase = recoveryPhrase
|
||||
state.phraseValidationState = randomRecoveryPhrase.random(recoveryPhrase)
|
||||
|
||||
return .concatenate(
|
||||
EffectTask(value: .initialization(.initializeSDK(.newWallet))),
|
||||
EffectTask(value: .phraseValidation(.displayBackedUpPhrase))
|
||||
EffectTask(value: .destination(.updateDestination(.phraseDisplay)))
|
||||
)
|
||||
} catch {
|
||||
state.alert = AlertState.cantCreateNewWallet(error.toZcashError())
|
||||
}
|
||||
return .none
|
||||
|
||||
case .phraseValidation(.succeed):
|
||||
do {
|
||||
try walletStorage.markUserPassedPhraseBackupTest(true)
|
||||
} catch {
|
||||
state.alert = AlertState.cantStoreThatUserPassedPhraseBackupTest(error.toZcashError())
|
||||
}
|
||||
return .none
|
||||
|
||||
case .initialization(.nukeWalletRequest):
|
||||
state.alert = AlertState.wipeRequest()
|
||||
return .none
|
||||
|
@ -237,7 +227,6 @@ extension RootReducer {
|
|||
case .nukeWalletSucceeded:
|
||||
walletStorage.nukeWallet()
|
||||
state.onboardingState.destination = nil
|
||||
state.onboardingState.index = 0
|
||||
return .concatenate(
|
||||
.cancel(id: SynchronizerCancelId.timer),
|
||||
EffectTask(value: .initialization(.checkWalletInitialization))
|
||||
|
@ -288,7 +277,7 @@ extension RootReducer {
|
|||
state.alert = AlertState.initializationFailed(error)
|
||||
return .none
|
||||
|
||||
case .home, .destination, .onboarding, .phraseDisplay, .phraseValidation, .sandbox,
|
||||
case .home, .destination, .onboarding, .phraseDisplay, .sandbox,
|
||||
.welcome, .binding, .debug, .exportLogs, .alert:
|
||||
return .none
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import ComposableArchitecture
|
|||
import ZcashLightClientKit
|
||||
import DatabaseFiles
|
||||
import Deeplink
|
||||
import RecoveryPhraseValidationFlow
|
||||
import ZcashSDKEnvironment
|
||||
import WalletStorage
|
||||
import WalletConfigProvider
|
||||
|
@ -36,7 +35,6 @@ public struct RootReducer: ReducerProtocol {
|
|||
public var exportLogsState: ExportLogsReducer.State
|
||||
public var homeState: HomeReducer.State
|
||||
public var onboardingState: OnboardingFlowReducer.State
|
||||
public var phraseValidationState: RecoveryPhraseValidationFlowReducer.State
|
||||
public var phraseDisplayState: RecoveryPhraseDisplayReducer.State
|
||||
public var sandboxState: SandboxReducer.State
|
||||
public var storedWallet: StoredWallet?
|
||||
|
@ -50,7 +48,6 @@ public struct RootReducer: ReducerProtocol {
|
|||
exportLogsState: ExportLogsReducer.State,
|
||||
homeState: HomeReducer.State,
|
||||
onboardingState: OnboardingFlowReducer.State,
|
||||
phraseValidationState: RecoveryPhraseValidationFlowReducer.State,
|
||||
phraseDisplayState: RecoveryPhraseDisplayReducer.State,
|
||||
sandboxState: SandboxReducer.State,
|
||||
storedWallet: StoredWallet? = nil,
|
||||
|
@ -63,7 +60,6 @@ public struct RootReducer: ReducerProtocol {
|
|||
self.exportLogsState = exportLogsState
|
||||
self.homeState = homeState
|
||||
self.onboardingState = onboardingState
|
||||
self.phraseValidationState = phraseValidationState
|
||||
self.phraseDisplayState = phraseDisplayState
|
||||
self.sandboxState = sandboxState
|
||||
self.storedWallet = storedWallet
|
||||
|
@ -84,7 +80,6 @@ public struct RootReducer: ReducerProtocol {
|
|||
case nukeWalletSucceeded
|
||||
case onboarding(OnboardingFlowReducer.Action)
|
||||
case phraseDisplay(RecoveryPhraseDisplayReducer.Action)
|
||||
case phraseValidation(RecoveryPhraseValidationFlowReducer.Action)
|
||||
case sandbox(SandboxReducer.Action)
|
||||
case updateStateAfterConfigUpdate(WalletConfig)
|
||||
case walletConfigLoaded(WalletConfig)
|
||||
|
@ -97,7 +92,6 @@ public struct RootReducer: ReducerProtocol {
|
|||
@Dependency(\.derivationTool) var derivationTool
|
||||
@Dependency(\.mainQueue) var mainQueue
|
||||
@Dependency(\.mnemonic) var mnemonic
|
||||
@Dependency(\.randomRecoveryPhrase) var randomRecoveryPhrase
|
||||
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
|
||||
@Dependency(\.userStoredPreferences) var userStoredPreferences
|
||||
@Dependency(\.walletConfigProvider) var walletConfigProvider
|
||||
|
@ -123,10 +117,6 @@ public struct RootReducer: ReducerProtocol {
|
|||
OnboardingFlowReducer(saplingActivationHeight: zcashNetwork.constants.saplingActivationHeight)
|
||||
}
|
||||
|
||||
Scope(state: \.phraseValidationState, action: /Action.phraseValidation) {
|
||||
RecoveryPhraseValidationFlowReducer()
|
||||
}
|
||||
|
||||
Scope(state: \.phraseDisplayState, action: /Action.phraseDisplay) {
|
||||
RecoveryPhraseDisplayReducer()
|
||||
}
|
||||
|
@ -336,7 +326,6 @@ extension RootReducer.State {
|
|||
walletConfig: .default,
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
phraseValidationState: .placeholder,
|
||||
phraseDisplayState: RecoveryPhraseDisplayReducer.State(
|
||||
phrase: .placeholder
|
||||
),
|
||||
|
|
|
@ -2,7 +2,6 @@ import SwiftUI
|
|||
import StoreKit
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
import RecoveryPhraseValidationFlow
|
||||
import Models
|
||||
import RecoveryPhraseDisplay
|
||||
import Welcome
|
||||
|
@ -74,22 +73,12 @@ private extension RootView {
|
|||
|
||||
case .onboarding:
|
||||
NavigationView {
|
||||
if viewStore.walletConfig
|
||||
.isEnabled(.onboardingFlow) {
|
||||
OnboardingScreen(
|
||||
store: store.scope(
|
||||
state: \.onboardingState,
|
||||
action: RootReducer.Action.onboarding
|
||||
)
|
||||
PlainOnboardingView(
|
||||
store: store.scope(
|
||||
state: \.onboardingState,
|
||||
action: RootReducer.Action.onboarding
|
||||
)
|
||||
} else {
|
||||
PlainOnboardingView(
|
||||
store: store.scope(
|
||||
state: \.onboardingState,
|
||||
action: RootReducer.Action.onboarding
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
|
||||
|
@ -98,18 +87,7 @@ private extension RootView {
|
|||
debugView(viewStore)
|
||||
.transition(.opacity)
|
||||
}
|
||||
|
||||
case .phraseValidation:
|
||||
NavigationView {
|
||||
RecoveryPhraseValidationFlowView(
|
||||
store: store.scope(
|
||||
state: \.phraseValidationState,
|
||||
action: RootReducer.Action.phraseValidation
|
||||
)
|
||||
)
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
|
||||
|
||||
case .phraseDisplay:
|
||||
NavigationView {
|
||||
RecoveryPhraseDisplayView(
|
||||
|
@ -163,10 +141,10 @@ private extension RootView {
|
|||
@ViewBuilder func debugView(_ viewStore: RootViewStore) -> some View {
|
||||
VStack(alignment: .leading) {
|
||||
if viewStore.destinationState.previousDestination == .home {
|
||||
Button(L10n.General.back) {
|
||||
Button(L10n.General.back.uppercased()) {
|
||||
viewStore.goToDestination(.home)
|
||||
}
|
||||
.activeButtonStyle
|
||||
.zcashStyle()
|
||||
.frame(width: 150)
|
||||
.padding()
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ extension ScanView {
|
|||
presentationMode.wrappedValue.dismiss()
|
||||
}, label: {
|
||||
Image(systemName: "arrow.backward")
|
||||
.foregroundColor(Asset.Colors.Mfp.background.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
.font(
|
||||
.custom(FontFamily.Inter.regular.name, size: 30)
|
||||
)
|
||||
|
@ -101,7 +101,7 @@ extension ScanView {
|
|||
Image(
|
||||
systemName: viewStore.isTorchOn ? "lightbulb.fill" : "lightbulb.slash"
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.background.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
.font(
|
||||
.custom(FontFamily.Inter.regular.name, size: 30)
|
||||
)
|
||||
|
@ -118,7 +118,7 @@ extension ScanView {
|
|||
|
||||
func frameOfInterest(_ size: CGSize) -> some View {
|
||||
Rectangle()
|
||||
.stroke(Asset.Colors.Mfp.background.color, lineWidth: 5.0)
|
||||
.stroke(Asset.Colors.primary.color, lineWidth: 5.0)
|
||||
.frame(
|
||||
width: frameSize(size),
|
||||
height: frameSize(size),
|
||||
|
|
|
@ -31,7 +31,7 @@ public struct CreateTransaction: View {
|
|||
.custom(FontFamily.Inter.regular.name, size: 14)
|
||||
)
|
||||
}
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
.padding(.horizontal)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
|
@ -44,7 +44,7 @@ public struct CreateTransaction: View {
|
|||
|
||||
if viewStore.isInvalidAddressFormat {
|
||||
Text(L10n.Send.Error.invalidAddress)
|
||||
.foregroundColor(Asset.Colors.Mfp.error.color)
|
||||
.foregroundColor(Asset.Colors.error.color)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
|
@ -61,10 +61,10 @@ public struct CreateTransaction: View {
|
|||
|
||||
if viewStore.isInvalidAmountFormat {
|
||||
Text(L10n.Send.Error.invalidAmount)
|
||||
.foregroundColor(Asset.Colors.Mfp.error.color)
|
||||
.foregroundColor(Asset.Colors.error.color)
|
||||
} else if viewStore.isInsufficientFunds {
|
||||
Text(L10n.Send.Error.insufficientFunds)
|
||||
.foregroundColor(Asset.Colors.Mfp.error.color)
|
||||
.foregroundColor(Asset.Colors.error.color)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
|
@ -77,19 +77,19 @@ public struct CreateTransaction: View {
|
|||
L10n.Send.editMemo
|
||||
: L10n.Send.includeMemo
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
.padding(.top, 10)
|
||||
.disable(when: !viewStore.isMemoInputEnabled, dimmingOpacity: 0.5)
|
||||
.disabled(!viewStore.isMemoInputEnabled)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.sendPressed) },
|
||||
label: { Text(L10n.General.send) }
|
||||
label: { Text(L10n.General.send.uppercased()) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.disable(when: !viewStore.isValidForm, dimmingOpacity: 0.5)
|
||||
.zcashStyle()
|
||||
.disabled(!viewStore.isValidForm)
|
||||
.padding(.top, 10)
|
||||
.padding(.horizontal)
|
||||
.padding(.horizontal, 70)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
|
|
@ -18,10 +18,11 @@ public struct TransactionFailed: View {
|
|||
action: {
|
||||
viewStore.send(.updateDestination(.done))
|
||||
},
|
||||
label: { Text(L10n.General.close) }
|
||||
label: { Text(L10n.General.close.uppercased()) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.zcashStyle()
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal, 70)
|
||||
.padding()
|
||||
|
||||
Spacer()
|
||||
|
|
|
@ -25,11 +25,11 @@ public struct TransactionSendingView: View {
|
|||
VStack(alignment: .center, spacing: 40) {
|
||||
Spacer()
|
||||
Text(L10n.Send.sendingTo(viewStore.amount.decimalString(), tokenName))
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Text(viewStore.address)
|
||||
.truncationMode(.middle)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
.lineLimit(1)
|
||||
|
||||
Spacer()
|
||||
|
|
|
@ -18,10 +18,11 @@ public struct TransactionSent: View {
|
|||
action: {
|
||||
viewStore.send(.updateDestination(.done))
|
||||
},
|
||||
label: { Text(L10n.General.close) }
|
||||
label: { Text(L10n.General.close.uppercased()) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.zcashStyle()
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal, 70)
|
||||
.padding()
|
||||
|
||||
Text(L10n.Send.amount(viewStore.amount.decimalString()))
|
||||
|
|
|
@ -19,48 +19,46 @@ public struct SettingsView: View {
|
|||
L10n.Settings.crashReporting,
|
||||
isOn: viewStore.binding(\.$isCrashReportingOn)
|
||||
)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.backupWalletAccessRequest) },
|
||||
label: { Text(L10n.Settings.backupWallet) }
|
||||
label: { Text(L10n.Settings.backupWallet.uppercased()) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.exportLogs(.start)) },
|
||||
label: {
|
||||
if viewStore.exportLogsState.exportLogsDisabled {
|
||||
HStack {
|
||||
ProgressView()
|
||||
Text(L10n.Settings.exporting)
|
||||
Text(L10n.Settings.exporting.uppercased())
|
||||
}
|
||||
} else {
|
||||
Text(L10n.Settings.exportLogs)
|
||||
Text(L10n.Settings.exportLogs.uppercased())
|
||||
}
|
||||
}
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
.disable(
|
||||
when: viewStore.exportLogsState.exportLogsDisabled,
|
||||
dimmingOpacity: 0.5
|
||||
)
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
.disabled(viewStore.exportLogsState.exportLogsDisabled)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.sendSupportMail) },
|
||||
label: { Text(L10n.Settings.feedback) }
|
||||
label: { Text(L10n.Settings.feedback.uppercased()) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.updateDestination(.about)) },
|
||||
label: { Text(L10n.Settings.about) }
|
||||
label: { Text(L10n.Settings.about.uppercased()) }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(maxHeight: 50)
|
||||
.zcashStyle()
|
||||
.padding(.horizontal, 70)
|
||||
.padding(.bottom, 50)
|
||||
}
|
||||
.padding(.horizontal, 30)
|
||||
|
|
|
@ -20,7 +20,7 @@ public struct About: View {
|
|||
WithViewStore(store) { viewStore in
|
||||
VStack {
|
||||
Text(L10n.Settings.version(viewStore.appVersion, viewStore.appBuild))
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
|
|
@ -175,11 +175,11 @@ struct TransactionDetailRow: ViewModifier {
|
|||
let markColor: Color
|
||||
|
||||
switch mark {
|
||||
case .neutral: markColor = Asset.Colors.TransactionDetail.neutralMark.color
|
||||
case .success: markColor = Asset.Colors.TransactionDetail.succeededMark.color
|
||||
case .fail: markColor = Asset.Colors.TransactionDetail.failedMark.color
|
||||
case .inactive: markColor = Asset.Colors.TransactionDetail.inactiveMark.color
|
||||
case .highlight: markColor = Asset.Colors.TransactionDetail.highlightMark.color
|
||||
case .neutral: markColor = Asset.Colors.primary.color
|
||||
case .success: markColor = Asset.Colors.primary.color
|
||||
case .fail: markColor = Asset.Colors.primary.color
|
||||
case .inactive: markColor = Asset.Colors.primary.color
|
||||
case .highlight: markColor = Asset.Colors.primary.color
|
||||
}
|
||||
|
||||
return markColor
|
||||
|
@ -194,9 +194,9 @@ extension View {
|
|||
TransactionDetailRow(
|
||||
mark: mark,
|
||||
textColor: mark == .inactive ?
|
||||
Asset.Colors.TransactionDetail.inactiveMark.color :
|
||||
Asset.Colors.Text.transactionDetailText.color,
|
||||
backgroundColor: Asset.Colors.BackgroundColors.numberedChip.color
|
||||
Asset.Colors.primary.color :
|
||||
Asset.Colors.primary.color,
|
||||
backgroundColor: Asset.Colors.primary.color
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@ public struct TransactionRowView: View {
|
|||
.font(
|
||||
.custom(FontFamily.Inter.regular.name, size: 16)
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Text("\(transaction.date?.asHumanReadable() ?? L10n.General.dateNotAvailable)")
|
||||
.font(
|
||||
.custom(FontFamily.Inter.regular.name, size: 16)
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
.opacity(0.5)
|
||||
}
|
||||
|
||||
|
@ -46,12 +46,12 @@ public struct TransactionRowView: View {
|
|||
.font(
|
||||
.custom(FontFamily.Inter.regular.name, size: 16)
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
+ Text(L10n.balance(transaction.zecAmount.decimalString(), tokenName))
|
||||
.font(
|
||||
.custom(FontFamily.Inter.regular.name, size: 16)
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Mfp.fontDark.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
.padding(.trailing, 30)
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public struct TransactionRowView: View {
|
|||
Rectangle()
|
||||
.padding(.horizontal, 30)
|
||||
.frame(height: 1, alignment: .center)
|
||||
.foregroundColor(Asset.Colors.Text.transactionRowSubtitle.color)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
}
|
||||
.frame(height: 60)
|
||||
|
@ -96,7 +96,7 @@ extension TransactionRowView {
|
|||
.frame(width: 12, height: 12)
|
||||
.foregroundColor(inTransaction ? .yellow : .white)
|
||||
.padding(10)
|
||||
.background(Asset.Colors.Mfp.walletEvents.color)
|
||||
.background(Asset.Colors.suppressed72.color)
|
||||
.cornerRadius(40)
|
||||
.rotationEffect(Angle(degrees: inTransaction ? 135 : -45))
|
||||
.padding(.leading, 14)
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "callout0.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "callout0-1.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "callout0-2.jpg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ua_welcome_lighttheme.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "ua_welcome_darktheme.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "illus_ua_lighttheme.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "illus_ua_darktheme.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "illus_future_lighttheme.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "illus_future_darktheme.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "illus_done_lighttheme.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "illus_done_darktheme.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "illus_alert_lighttheme.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "illus_alert_darktheme.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "illus_bank_lighttheme.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "illus_bank_darktheme.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "illus_bigsuccess_lighttheme.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "illus_bigsuccess_darktheme.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFB",
|
||||
"green" : "0xF5",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x62",
|
||||
"green" : "0x3D",
|
||||
"red" : "0x30"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xE9",
|
||||
"green" : "0xE1",
|
||||
"red" : "0xD8"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x3A",
|
||||
"green" : "0x36",
|
||||
"red" : "0x31"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x2C",
|
||||
"green" : "0x0B",
|
||||
"red" : "0xC6"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "251",
|
||||
"green" : "245",
|
||||
"red" : "238"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF9",
|
||||
"green" : "0xEF",
|
||||
"red" : "0xE3"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5A",
|
||||
"green" : "0x36",
|
||||
"red" : "0x29"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.341",
|
||||
"green" : "0.200",
|
||||
"red" : "0.149"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.725",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.341",
|
||||
"green" : "0.200",
|
||||
"red" : "0.149"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.200",
|
||||
"blue" : "0x28",
|
||||
"green" : "0xB7",
|
||||
"red" : "0xF4"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x57",
|
||||
"green" : "0x33",
|
||||
"red" : "0x26"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.847",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.330",
|
||||
"blue" : "0x36",
|
||||
"green" : "0x2C",
|
||||
"red" : "0x27"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.000",
|
||||
"blue" : "0x36",
|
||||
"green" : "0x2C",
|
||||
"red" : "0x27"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.820",
|
||||
"green" : "0.722",
|
||||
"red" : "0.631"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.150",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.992",
|
||||
"green" : "0.980",
|
||||
"red" : "0.969"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.150",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFD",
|
||||
"green" : "0xF7",
|
||||
"red" : "0xF1"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "217",
|
||||
"green" : "192",
|
||||
"red" : "167"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFC",
|
||||
"green" : "0xF8",
|
||||
"red" : "0xF5"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xEF",
|
||||
"green" : "0xDC",
|
||||
"red" : "0xC8"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFD",
|
||||
"green" : "0xF7",
|
||||
"red" : "0xF1"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "217",
|
||||
"green" : "192",
|
||||
"red" : "167"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.976",
|
||||
"green" : "0.941",
|
||||
"red" : "0.902"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.200",
|
||||
"blue" : "0xEF",
|
||||
"green" : "0xDC",
|
||||
"red" : "0xC8"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "253",
|
||||
"green" : "250",
|
||||
"red" : "244"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.937",
|
||||
"green" : "0.863",
|
||||
"red" : "0.784"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "0.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "246",
|
||||
"green" : "234",
|
||||
"red" : "222"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "106",
|
||||
"green" : "72",
|
||||
"red" : "61"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x65",
|
||||
"green" : "0x65",
|
||||
"red" : "0x65"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x65",
|
||||
"green" : "0x65",
|
||||
"red" : "0x65"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x94",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x94",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.171",
|
||||
"green" : "0.138",
|
||||
"red" : "0.126"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0xCE",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.000",
|
||||
"red" : "0.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.008",
|
||||
"green" : "0.000",
|
||||
"red" : "0.806"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.008",
|
||||
"green" : "0.000",
|
||||
"red" : "0.806"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.235",
|
||||
"green" : "0.235",
|
||||
"red" : "0.235"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.235",
|
||||
"green" : "0.235",
|
||||
"red" : "0.235"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x3C",
|
||||
"green" : "0x3C",
|
||||
"red" : "0x3C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.235",
|
||||
"green" : "0.235",
|
||||
"red" : "0.235"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.235",
|
||||
"green" : "0.235",
|
||||
"red" : "0.235"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF7",
|
||||
"green" : "0xED",
|
||||
"red" : "0xE3"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "69",
|
||||
"green" : "32",
|
||||
"red" : "19"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xEF",
|
||||
"green" : "0xE6",
|
||||
"red" : "0xDB"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x46",
|
||||
"green" : "0x25",
|
||||
"red" : "0x19"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF9",
|
||||
"green" : "0xF2",
|
||||
"red" : "0xEF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x73",
|
||||
"green" : "0x4F",
|
||||
"red" : "0x42"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFA",
|
||||
"green" : "0xF0",
|
||||
"red" : "0xE4"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x82",
|
||||
"green" : "0x51",
|
||||
"red" : "0x3D"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFA",
|
||||
"green" : "0xF0",
|
||||
"red" : "0xE4"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x82",
|
||||
"green" : "0x51",
|
||||
"red" : "0x3D"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF7",
|
||||
"green" : "0xED",
|
||||
"red" : "0xE3"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xD9",
|
||||
"green" : "0xC0",
|
||||
"red" : "0xA7"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue