mirror of https://github.com/lukechilds/damus.git
William Casarin
2 years ago
17 changed files with 502 additions and 89 deletions
@ -1,4 +1,3 @@ |
|||
xcuserdata |
|||
Preview\ Content |
|||
damus/TestingPrivate.swift |
|||
.DS_Store |
|||
|
@ -0,0 +1,40 @@ |
|||
// |
|||
// CreateAccountModel.swift |
|||
// damus |
|||
// |
|||
// Created by William Casarin on 2022-05-20. |
|||
// |
|||
|
|||
import Foundation |
|||
|
|||
|
|||
class CreateAccountModel: ObservableObject { |
|||
@Published var real_name: String = "" |
|||
@Published var nick_name: String = "" |
|||
@Published var about: String = "" |
|||
@Published var pubkey: String = "" |
|||
@Published var privkey: String = "" |
|||
|
|||
var rendered_name: String { |
|||
if real_name.isEmpty { |
|||
return nick_name |
|||
} |
|||
return real_name |
|||
} |
|||
|
|||
init() { |
|||
let keypair = generate_new_keypair() |
|||
self.pubkey = keypair.pubkey |
|||
self.privkey = keypair.privkey |
|||
} |
|||
|
|||
init(real: String, nick: String, about: String) { |
|||
let keypair = generate_new_keypair() |
|||
self.pubkey = keypair.pubkey |
|||
self.privkey = keypair.privkey |
|||
|
|||
self.real_name = real |
|||
self.nick_name = nick |
|||
self.about = about |
|||
} |
|||
} |
@ -0,0 +1,43 @@ |
|||
// |
|||
// Keys.swift |
|||
// damus |
|||
// |
|||
// Created by William Casarin on 2022-05-21. |
|||
// |
|||
|
|||
import Foundation |
|||
import secp256k1 |
|||
|
|||
struct Keypair { |
|||
let pubkey: String |
|||
let privkey: String |
|||
} |
|||
|
|||
func generate_new_keypair() -> Keypair { |
|||
let key = try! secp256k1.Signing.PrivateKey() |
|||
let privkey = hex_encode(key.rawRepresentation) |
|||
let pubkey = hex_encode(Data(key.publicKey.xonlyKeyBytes)) |
|||
print("generating privkey:\(privkey) pubkey:\(pubkey)") |
|||
return Keypair(pubkey: pubkey, privkey: privkey) |
|||
} |
|||
|
|||
func save_keypair(pubkey: String, privkey: String) { |
|||
UserDefaults.standard.set(pubkey, forKey: "pubkey") |
|||
UserDefaults.standard.set(privkey, forKey: "privkey") |
|||
} |
|||
|
|||
func get_saved_keypair() -> Keypair? { |
|||
get_saved_pubkey().flatMap { pubkey in |
|||
get_saved_privkey().map { privkey in |
|||
return Keypair(pubkey: pubkey, privkey: privkey) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func get_saved_pubkey() -> String? { |
|||
return UserDefaults.standard.string(forKey: "pubkey") |
|||
} |
|||
|
|||
func get_saved_privkey() -> String? { |
|||
return UserDefaults.standard.string(forKey: "privkey") |
|||
} |
@ -0,0 +1,142 @@ |
|||
// |
|||
// CreateAccountView.swift |
|||
// damus |
|||
// |
|||
// Created by William Casarin on 2022-05-20. |
|||
// |
|||
|
|||
import SwiftUI |
|||
|
|||
struct CreateAccountView: View { |
|||
@StateObject var account: CreateAccountModel = CreateAccountModel() |
|||
@State var is_light: Bool = false |
|||
@State var is_done: Bool = false |
|||
|
|||
func FormTextInput(_ title: String, text: Binding<String>) -> some View { |
|||
return TextField("", text: text) |
|||
.placeholder(when: text.wrappedValue.isEmpty) { |
|||
Text(title).foregroundColor(.white.opacity(0.4)) |
|||
} |
|||
.padding() |
|||
.background { |
|||
RoundedRectangle(cornerRadius: 4.0).opacity(0.2) |
|||
} |
|||
.foregroundColor(.white) |
|||
.font(.body.bold()) |
|||
} |
|||
|
|||
func FormLabel(_ title: String, optional: Bool = false) -> some View { |
|||
return HStack { |
|||
Text(title) |
|||
.bold() |
|||
.foregroundColor(.white) |
|||
if optional { |
|||
Text("optional") |
|||
.font(.callout) |
|||
.foregroundColor(.white.opacity(0.5)) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func SignupForm<FormContent: View>(@ViewBuilder content: () -> FormContent) -> some View { |
|||
return VStack(alignment: .leading, spacing: 10.0, content: content) |
|||
} |
|||
|
|||
func regen_key() { |
|||
let keypair = generate_new_keypair() |
|||
self.account.pubkey = keypair.pubkey |
|||
self.account.privkey = keypair.privkey |
|||
} |
|||
|
|||
var body: some View { |
|||
ZStack(alignment: .top) { |
|||
DamusGradient() |
|||
|
|||
VStack { |
|||
Text("Create Account") |
|||
.font(.title.bold()) |
|||
.foregroundColor(.white) |
|||
|
|||
ProfilePictureSelector(pubkey: account.pubkey) |
|||
|
|||
HStack(alignment: .top) { |
|||
VStack { |
|||
Text(" ") |
|||
.foregroundColor(.white) |
|||
} |
|||
VStack { |
|||
SignupForm { |
|||
FormLabel("Username") |
|||
HStack(spacing: 0.0) { |
|||
Text("@") |
|||
.foregroundColor(.white) |
|||
.padding(.leading, -25.0) |
|||
|
|||
FormTextInput("satoshi", text: $account.nick_name) |
|||
.textInputAutocapitalization(.never) |
|||
|
|||
} |
|||
|
|||
FormLabel("Display Name", optional: true) |
|||
FormTextInput("Satoshi Nakamoto", text: $account.real_name) |
|||
.textInputAutocapitalization(.words) |
|||
|
|||
FormLabel("About", optional: true) |
|||
FormTextInput("Creator(s) of Bitcoin. Absolute legend.", text: $account.about) |
|||
|
|||
FormLabel("Account ID") |
|||
.onTapGesture { |
|||
regen_key() |
|||
} |
|||
|
|||
KeyInput($account.pubkey) |
|||
.onTapGesture { |
|||
regen_key() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
NavigationLink(destination: SaveKeysView(account: account), isActive: $is_done) { |
|||
EmptyView() |
|||
} |
|||
DamusWhiteButton("Create") { |
|||
self.is_done = true |
|||
} |
|||
.padding() |
|||
} |
|||
.padding(.leading, 14.0) |
|||
.padding(.trailing, 20.0) |
|||
|
|||
} |
|||
.navigationBarTitleDisplayMode(.inline) |
|||
} |
|||
} |
|||
|
|||
extension View { |
|||
func placeholder<Content: View>( |
|||
when shouldShow: Bool, |
|||
alignment: Alignment = .leading, |
|||
@ViewBuilder placeholder: () -> Content) -> some View { |
|||
|
|||
ZStack(alignment: alignment) { |
|||
placeholder().opacity(shouldShow ? 1 : 0) |
|||
self |
|||
} |
|||
} |
|||
} |
|||
|
|||
struct CreateAccountView_Previews: PreviewProvider { |
|||
static var previews: some View { |
|||
let model = CreateAccountModel(real: "", nick: "jb55", about: "") |
|||
return CreateAccountView(account: model) |
|||
} |
|||
} |
|||
|
|||
func KeyInput(_ text: Binding<String>) -> some View { |
|||
return Text("\(text.wrappedValue)") |
|||
.textSelection(.enabled) |
|||
.font(.callout.monospaced()) |
|||
.foregroundColor(.white) |
|||
} |
|||
|
@ -0,0 +1,30 @@ |
|||
// |
|||
// ProfilePictureSelector.swift |
|||
// damus |
|||
// |
|||
// Created by William Casarin on 2022-05-20. |
|||
// |
|||
|
|||
import SwiftUI |
|||
|
|||
struct ProfilePictureSelector: View { |
|||
let pubkey: String |
|||
|
|||
var body: some View { |
|||
let highlight: Highlight = .custom(Color.white, 2.0) |
|||
ZStack { |
|||
ProfilePicView(pubkey: pubkey, size: 80.0, highlight: highlight, image_cache: ImageCache(), profiles: Profiles()) |
|||
|
|||
Image(systemName: "camera") |
|||
.font(.title) |
|||
.foregroundColor(.white) |
|||
} |
|||
} |
|||
} |
|||
|
|||
struct ProfilePictureSelector_Previews: PreviewProvider { |
|||
static var previews: some View { |
|||
let test_pubkey = "ff48854ac6555fed8e439ebb4fa2d928410e0eef13fa41164ec45aaaa132d846" |
|||
ProfilePictureSelector(pubkey: test_pubkey) |
|||
} |
|||
} |
@ -0,0 +1,113 @@ |
|||
// |
|||
// SaveKeysView.swift |
|||
// damus |
|||
// |
|||
// Created by William Casarin on 2022-05-21. |
|||
// |
|||
|
|||
import SwiftUI |
|||
|
|||
struct SaveKeysView: View { |
|||
let account: CreateAccountModel |
|||
@State var is_done: Bool = false |
|||
@State var pub_copied: Bool = false |
|||
@State var priv_copied: Bool = false |
|||
|
|||
var body: some View { |
|||
ZStack(alignment: .top) { |
|||
DamusGradient() |
|||
|
|||
VStack(alignment: .center) { |
|||
Text("Welcome, \(account.rendered_name)!") |
|||
.font(.title.bold()) |
|||
.foregroundColor(.white) |
|||
.padding(.bottom, 10) |
|||
|
|||
Text("Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus.") |
|||
.foregroundColor(.white) |
|||
.padding(.bottom, 10) |
|||
|
|||
Text("Public Key") |
|||
.font(.title2.bold()) |
|||
.foregroundColor(.white) |
|||
.padding(.bottom, 10) |
|||
|
|||
Text("This is your account ID, you can give this to your friends so that they can follow you") |
|||
.foregroundColor(.white) |
|||
.padding(.bottom, 10) |
|||
|
|||
SaveKeyView(text: account.pubkey, is_copied: $pub_copied) |
|||
.padding(.bottom, 10) |
|||
|
|||
Text("Private Key") |
|||
.font(.title2.bold()) |
|||
.foregroundColor(.white) |
|||
.padding(.bottom, 10) |
|||
|
|||
Text("This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!") |
|||
.foregroundColor(.white) |
|||
.padding(.bottom, 10) |
|||
|
|||
SaveKeyView(text: account.privkey, is_copied: $priv_copied) |
|||
.padding(.bottom, 10) |
|||
|
|||
if pub_copied && priv_copied { |
|||
DamusWhiteButton("Let's go!") { |
|||
save_keypair(pubkey: account.pubkey, privkey: account.privkey) |
|||
notify(.login, ()) |
|||
} |
|||
} |
|||
} |
|||
.padding(20) |
|||
} |
|||
} |
|||
} |
|||
|
|||
struct SaveKeyView: View { |
|||
let text: String |
|||
@Binding var is_copied: Bool |
|||
|
|||
func copy_text() { |
|||
UIPasteboard.general.string = text |
|||
is_copied = true |
|||
} |
|||
|
|||
var body: some View { |
|||
HStack { |
|||
Button(action: copy_text) { |
|||
Label("", systemImage: is_copied ? "checkmark.circle.fill" : "doc.on.doc") |
|||
.foregroundColor(is_copied ? .green : .white) |
|||
.background { |
|||
if is_copied { |
|||
Circle() |
|||
.foregroundColor(.white) |
|||
.frame(width: 25, height: 25, alignment: .center) |
|||
.padding(.leading, -8) |
|||
.padding(.top, 1) |
|||
} else { |
|||
EmptyView() |
|||
} |
|||
} |
|||
} |
|||
|
|||
Text(text) |
|||
.padding(5) |
|||
.background { |
|||
RoundedRectangle(cornerRadius: 4.0).opacity(0.2) |
|||
} |
|||
.textSelection(.enabled) |
|||
.font(.callout.monospaced()) |
|||
.foregroundColor(.white) |
|||
.onTapGesture { |
|||
copy_text() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
struct SaveKeysView_Previews: PreviewProvider { |
|||
static var previews: some View { |
|||
let model = CreateAccountModel(real: "William", nick: "jb55", about: "I'm me") |
|||
SaveKeysView(account: model) |
|||
} |
|||
} |
Loading…
Reference in new issue