montana/Монтана-iOS/Sources/MontanaApp/UI/Onboarding/OnboardingView.swift

167 lines
5.6 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
struct OnboardingView: View {
@State private var screen: Screen = .welcome
@EnvironmentObject private var identityManager: IdentityManager
enum Screen {
case welcome
case createNew
case restore
}
var body: some View {
NavigationStack {
switch screen {
case .welcome:
WelcomeView(onCreate: { screen = .createNew }, onRestore: { screen = .restore })
case .createNew:
CreateMnemonicView(onBack: { screen = .welcome })
case .restore:
RestoreMnemonicView(onBack: { screen = .welcome })
}
}
}
}
private struct WelcomeView: View {
let onCreate: () -> Void
let onRestore: () -> Void
var body: some View {
VStack(spacing: 32) {
Spacer()
Image(systemName: "infinity.circle.fill")
.font(.system(size: 80))
.foregroundStyle(.tint)
Text("Montana")
.font(.system(size: 48, weight: .bold))
Text("Цифровая собственность.\nПостквантовая криптография.\nТвой ключ — твоя идентичность.")
.font(.body)
.multilineTextAlignment(.center)
.foregroundStyle(.secondary)
.padding(.horizontal)
Spacer()
VStack(spacing: 12) {
Button(action: onCreate) {
Text("Создать новую идентичность")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
Button(action: onRestore) {
Text("Восстановить из мнемоники")
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.controlSize(.large)
}
.padding(.horizontal)
Spacer().frame(height: 24)
}
}
}
private struct CreateMnemonicView: View {
let onBack: () -> Void
@EnvironmentObject private var identityManager: IdentityManager
@State private var mnemonic: String = ""
@State private var confirmedSaved = false
@State private var error: String?
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Text("Запишите 24 слова в надёжное место")
.font(.title2.bold())
Text("Это единственный способ восстановить идентичность. Никому не показывайте.")
.font(.footnote)
.foregroundStyle(.secondary)
if !mnemonic.isEmpty {
MnemonicGridView(mnemonic: mnemonic)
}
if let error {
Text(error)
.foregroundStyle(.red)
.font(.footnote)
}
Toggle("Я записал(-а) все 24 слова", isOn: $confirmedSaved)
.padding(.top)
Button {
do {
try identityManager.restore(from: mnemonic)
} catch {
self.error = String(describing: error)
}
} label: {
Text("Завершить настройку")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.disabled(!confirmedSaved || mnemonic.isEmpty)
}
.padding()
}
.navigationTitle("Новая идентичность")
.toolbar {
ToolbarItem(placement: .topBarLeading) { Button("Назад", action: onBack) }
}
.task {
if mnemonic.isEmpty {
mnemonic = Mnemonic.generate()
}
}
}
}
private struct RestoreMnemonicView: View {
let onBack: () -> Void
@EnvironmentObject private var identityManager: IdentityManager
@State private var mnemonicInput: String = ""
@State private var error: String?
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("Введите 24 слова мнемоники через пробел")
.font(.title3)
TextEditor(text: $mnemonicInput)
.font(.body.monospaced())
.frame(minHeight: 160)
.border(.gray.opacity(0.4))
.autocorrectionDisabled()
.textInputAutocapitalization(.never)
if let error {
Text(error).foregroundStyle(.red).font(.footnote)
}
Button {
do {
try identityManager.restore(from: mnemonicInput)
} catch {
self.error = String(describing: error)
}
} label: {
Text("Восстановить")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.disabled(mnemonicInput.split(whereSeparator: { $0.isWhitespace }).count != 24)
Spacer()
}
.padding()
.navigationTitle("Восстановление")
.toolbar {
ToolbarItem(placement: .topBarLeading) { Button("Назад", action: onBack) }
}
}
}