Add preliminary onboarding views

This commit is contained in:
2025-01-20 13:29:47 -05:00
parent 248fd03d4d
commit cbdadeb4b2
10 changed files with 294 additions and 61 deletions

View File

@@ -359,6 +359,7 @@
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
@@ -415,6 +416,7 @@
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = NO;
VALIDATE_PRODUCT = YES;
};
name = Release;

View File

@@ -0,0 +1,57 @@
{
"sourceLanguage" : "en",
"strings" : {
"Add a key" : {
"comment" : "Button to add a key."
},
"Add your key" : {
"comment" : "Title of view to add the users private key."
},
"Approve basic actions" : {
"comment" : "Name of event signing policy that approves basic actions."
},
"Create a key" : {
"comment" : "Button to create a key."
},
"Done" : {
"comment" : "Button to go to the next view that adds the user's entered private key."
},
"Got it!" : {
"comment" : "Button to go to the next view that adds the users entered private key."
},
"Hooo-raaaayyy!" : {
"comment" : "Title of view that confirms the users selection of the signing policy."
},
"Manually approve each app" : {
"comment" : "Name of event signing policy that requires manual approval to sign each event."
},
"Next" : {
"comment" : "Button to go to the next view that adds the users entered private key."
},
"nsec / private key" : {
"comment" : "Prompt asking user to enter in a Nostr private key."
},
"Recommended for most people. This policy will minimize the number of interruptions during your app usage." : {
"comment" : "Description of event signing policy that approves basic actions."
},
"Recommended for privacy-minded people who would like control over each app.\nChoosing this policy will prompt you to set a preference every time you try a new app." : {
"comment" : "Description of event signing policy that requires manual approval to sign each event."
},
"Select a signing policy" : {
"comment" : "Title of view to select a signing policy."
},
"Yeti: Nostr Helper" : {
"comment" : "Application title."
},
"Youre all set! Ill take care of most of the approval for you.\nIf I see an event I dont recognize, Ill ask you to review it." : {
"comment" : "Description of what the user should expect after selecting the basic signing policy."
},
"Youre set for now. Youll need to come back here with every new app and approve some nostr events." : {
"comment" : "Description of what the user should expect after selecting the manual signing policy."
},
"Your private key is stored locally. Only you can see it." : {
"comment" : "Footer text explaining that the private key is stored locally and only the user can see it."
}
},
"version" : "1.0"
}

View File

@@ -1,61 +0,0 @@
//
// ContentView.swift
// Yeti
//
// Created by Terry Yiu on 1/19/25.
//
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
var body: some View {
NavigationSplitView {
List {
ForEach(items) { item in
NavigationLink {
Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
} label: {
Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
}
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
} detail: {
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Item(timestamp: Date())
modelContext.insert(newItem)
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}

View File

@@ -0,0 +1,53 @@
//
// AddKeyView.swift
// Yeti
//
// Created by Terry Yiu on 1/19/25.
//
import SwiftUI
struct AddKeyView: View {
@State private var key: String = ""
var body: some View {
NavigationStack {
Form {
Text("Add your key", comment: "Title of view to add the users private key.")
.font(.title)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.listRowInsets(EdgeInsets())
.background(Color(UIColor.systemGroupedBackground))
Section(
content: {
SecureField(
String(
localized: "nsec / private key",
comment: "Prompt asking user to enter in a Nostr private key."
),
text: $key
)
},
footer: {
Text(
"Your private key is stored locally. Only you can see it.",
comment:
"""
Footer text explaining that the private key is stored locally and only the user can see it.
"""
)
}
)
NavigationLink(destination: SigningPolicySelectionView()) {
Text("Next", comment: "Button to go to the next view that adds the users entered private key.")
}
}
}
}
}
#Preview {
AddKeyView()
}

View File

@@ -0,0 +1,46 @@
//
// ContentView.swift
// Yeti
//
// Created by Terry Yiu on 1/19/25.
//
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
var body: some View {
NavigationStack {
Text("Yeti: Nostr Helper", comment: "Application title.")
HStack {
NavigationLink(
destination: {
AddKeyView()
},
label: {
Text("Add a key", comment: "Button to add a key.")
}
)
.buttonStyle(.bordered)
NavigationLink(
destination: {
AddKeyView()
},
label: {
Text("Create a key", comment: "Button to create a key.")
}
)
.buttonStyle(.borderedProminent)
}
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}

View File

@@ -0,0 +1,41 @@
//
// SigningPolicyConfirmationView.swift
// Yeti
//
// Created by Terry Yiu on 1/20/25.
//
import SwiftUI
struct SigningPolicyConfirmationView: View {
let signingPolicy: SigningPolicy
var body: some View {
VStack {
Text("Hooo-raaaayyy!", comment: "Title of view that confirms the users selection of the signing policy.")
.font(.title)
switch signingPolicy {
case .basic:
Text(
"""
Youre all set! Ill take care of most of the approval for you.
If I see an event I dont recognize, Ill ask you to review it.
""",
comment: "Description of what the user should expect after selecting the basic signing policy."
)
case .manual:
Text(
"""
Youre set for now. Youll need to come back here with every new app and approve some nostr events.
""",
comment: "Description of what the user should expect after selecting the manual signing policy."
)
}
}
}
}
#Preview {
SigningPolicyConfirmationView(signingPolicy: .basic)
}

View File

@@ -0,0 +1,95 @@
//
// SigningPolicySelectionView.swift
// Yeti
//
// Created by Terry Yiu on 1/20/25.
//
import SwiftUI
struct SigningPolicySelectionView: View {
@State var signingPolicy: SigningPolicy = .basic
var body: some View {
NavigationStack {
Form {
Text("Select a signing policy", comment: "Title of view to select a signing policy.")
.font(.title)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.listRowInsets(EdgeInsets())
.background(Color(UIColor.systemGroupedBackground))
Section(
content: {
Picker(
selection: $signingPolicy,
content: {
ForEach(SigningPolicy.allCases, id: \.self) { signingPolicy in
Text(signingPolicy.name)
.tag(signingPolicy)
}
},
label: {
EmptyView()
}
)
.pickerStyle(.inline)
},
footer: {
Text(signingPolicy.description)
}
)
NavigationLink(destination: SigningPolicyConfirmationView(signingPolicy: signingPolicy)) {
Text("Done", comment: "Button to go to the next view that adds the users entered private key.")
}
}
}
}
}
enum SigningPolicy: CaseIterable {
case basic
case manual
var name: String {
switch self {
case .basic:
return String(
localized: "Approve basic actions",
comment: "Name of event signing policy that approves basic actions."
)
case .manual:
return String(
localized: "Manually approve each app",
comment: "Name of event signing policy that requires manual approval to sign each event."
)
}
}
var description: String {
switch self {
case .basic:
return String(
localized:
"""
Recommended for most people. This policy will minimize the number of interruptions during your app usage.
""",
comment: "Description of event signing policy that approves basic actions."
)
case .manual:
return String(
localized:
"""
Recommended for privacy-minded people who would like control over each app.
Choosing this policy will prompt you to set a preference every time you try a new app.
""",
comment: "Description of event signing policy that requires manual approval to sign each event."
)
}
}
}
#Preview {
SigningPolicySelectionView()
}