Skip to content

Commit

Permalink
finalize sending flow
Browse files Browse the repository at this point in the history
  • Loading branch information
0xChqrles committed Jun 13, 2024
1 parent 8f494fd commit c3b8f92
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 76 deletions.
13 changes: 9 additions & 4 deletions app/App/Components/SpinnerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ struct SpinnerView: View {
.frame(width: 48, height: 48)
.animation(.easeIn(duration: 1.5).repeatForever(autoreverses: true), value: self.isTrimming)
.animation(.linear(duration: 0.3), value: self.isComplete)
.onAppear {
self.isTrimming = true
self.isSpinning = true
}
}
.rotationEffect(Angle(degrees: isSpinning ? 360 : 0))
.animation(.linear(duration: 1.0).repeatForever(autoreverses: false), value: isSpinning)
Expand All @@ -56,6 +52,15 @@ struct SpinnerView: View {
.animation(.easeIn(duration: 0.2).delay(0.3), value: isComplete)

}
.onAppear {
// small delay because SwiftUI animation are broken
Task { @MainActor in
try await Task.sleep(for: .seconds(0.1))

self.isTrimming = true
self.isSpinning = true
}
}
}
}

Expand Down
93 changes: 67 additions & 26 deletions app/App/Models/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import Starknet
import BigInt

enum Status: Equatable {
case none
case active
case none // TODO: find a better name
case loading
case signing
case signed
case success
case error(String)
}
Expand All @@ -30,21 +31,21 @@ class Model: ObservableObject {
@Published var isLoading = false
@Published var showMessage = false

// Navgiation
@Published var showSendingSheet = false {
// Sending USDC
@Published var recipientContact: Contact?
@Published var sendingAmount: String = ""
@Published var sendingStatus: Status = .none
@Published var showSendingView = false {
didSet {
if self.showSendingSheet {
if self.showSendingView {
self.initiateTransfer()
} else {
self.dismissTransfer()
}
}
}

// Sending USDC
@Published var recipientContact: Contact?
@Published var sendingAmount: String = ""
@Published var sendingStatus: Status = .none
@Published var showSendingConfirmation = false
@Published var signedSendingTransaction: StarknetInvokeTransactionV1? = nil

// Country picker
@Published var selectedRegionCode = Locale.current.regionOrFrance.identifier
Expand Down Expand Up @@ -187,21 +188,39 @@ extension Model {

extension Model {

func executeTransaction(calls: [StarknetCall]) async throws -> StarknetInvokeTransactionResponse {
// maxFee / nonce

func getParams() async throws -> StarknetInvokeParamsV1 {
let nonce = try await self.account.getNonce()
let maxFee = self.estimateFees()

return try await account.executeV1(calls: calls, params: StarknetOptionalInvokeParamsV1(nonce: nonce, maxFee: maxFee))
}

func executeTransaction(call: StarknetCall) async throws -> StarknetInvokeTransactionResponse {
return try await self.executeTransaction(calls: [call])
return StarknetInvokeParamsV1(nonce: nonce, maxFee: maxFee)
}

func estimateFees() -> Felt {
return Felt(fromHex: "0x1000000000000000000")! // 1 ETH
}

// invoke

func executeTransaction(signedTransaction: StarknetInvokeTransactionV1) async throws -> StarknetInvokeTransactionResponse {
return try await self.provider.addInvokeTransaction(signedTransaction)
}

// sign

func signTransaction(calls: [StarknetCall]) async throws -> StarknetInvokeTransactionV1 {
let params = try await self.getParams()

return try self.account.signV1(calls: calls, params: params)
}

func signTransaction(call: StarknetCall) async throws -> StarknetInvokeTransactionV1 {
return try await self.signTransaction(calls: [call])
}

// addr utils

func getAddress(from phoneNumber: String) -> Felt? {
guard let phoneNumberFelt = Felt.fromShortString(phoneNumber) else {
return nil
Expand Down Expand Up @@ -301,6 +320,33 @@ extension Model {
}

func executeTransfer() async {
guard let signedTransaction = self.signedSendingTransaction else {
self.sendingStatus = .error("Invalid request.")
return
}

self.sendingStatus = .loading

do {
let result = try await self.executeTransaction(signedTransaction: signedTransaction)

self.sendingStatus = .success

#if DEBUG
print("tx: \(result.transactionHash)")
#endif
} catch let error {
self.sendingStatus = .success
// self.sendingStatus = .error("An error has occured during the transaction.")

#if DEBUG
print(error)
#endif
}

}

func signTransfer() async {
guard
let recipientContact = self.recipientContact,
let recipientAddress = self.getAddress(from: recipientContact.phone),
Expand All @@ -320,19 +366,14 @@ extension Model {
]
)

self.sendingStatus = .loading
self.sendingStatus = .signing

do {
let result = try await self.executeTransaction(call: call)
self.signedSendingTransaction = try await self.signTransaction(call: call)

self.sendingStatus = .success

#if DEBUG
print("tx: \(result.transactionHash)")
#endif
self.sendingStatus = .signed
} catch let error {
self.sendingStatus = .success
// self.sendingStatus = .error("An error has occured during the transaction.")
self.sendingStatus = .none

#if DEBUG
print(error)
Expand Down Expand Up @@ -407,7 +448,7 @@ extension Model {
private func initiateTransfer() {
self.recipientContact = nil
self.sendingAmount = "0"
self.sendingStatus = .active
self.signedSendingTransaction = nil
}

private func dismissTransfer() {
Expand Down
21 changes: 12 additions & 9 deletions app/App/Navigation/Core/Home/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ struct HomeView: View {
Spacer(minLength: 16)

IconButtonWithText("Send") {
self.model.showSendingSheet = true
self.model.showSendingView = true
} icon: {
Image(systemName: "arrow.up")
.iconify()
Expand Down Expand Up @@ -222,7 +222,7 @@ struct HomeView: View {
}
)
.removeNavigationBarBorder()
.fullScreenCover(isPresented: self.$model.showSendingSheet) {
.fullScreenCover(isPresented: self.$model.showSendingView) {
SendingView()
}
}
Expand All @@ -242,15 +242,18 @@ struct HomeView: View {
}
}

#if DEBUG
struct HomeViewPreviews : PreviewProvider {
#Preview {
struct HomeViewPreviews: View {

@StateObject static var model = Model(vaultService: VaultService())
@StateObject var model = Model(vaultService: VaultService())

static var previews: some View {
NavigationStack {
HomeView().environmentObject(model)
var body: some View {
NavigationStack {
HomeView()
.environmentObject(self.model)
}
}
}

return HomeViewPreviews()
}
#endif
40 changes: 11 additions & 29 deletions app/App/Navigation/Core/Sending/ConfirmationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ struct ConfirmationView: View {
let recipientConact = self.model.recipientContact!
let usdcAmount = USDCAmount(from: self.model.parsedSendingAmount)!

switch self.model.sendingStatus {
case .none:
EmptyView()
VStack {
Text("Finalize your transfer")
.textTheme(.headlineSmall)
.padding(.vertical, 32)

case .active:
Text("Finalize your transfer").textTheme(.headlineSmall)

Spacer().frame(height: 24)
Spacer().frame(height: 16)

HStack {
VStack(spacing: 8) {
Expand Down Expand Up @@ -96,31 +94,16 @@ struct ConfirmationView: View {
.background(.background3.opacity(0.5))
.clipShape(RoundedRectangle(cornerRadius: 10))

Spacer().frame(height: 64)
Spacer()

PrimaryButton("Send") {
Task {
await self.model.executeTransfer()
await self.model.signTransfer()
}
}

case .loading, .success:
Text("Executing your transfer").textTheme(.headlineSmall)
.onTapGesture {
self.model.sendingStatus = .success
}

Spacer().frame(height: 32)

SpinnerView(isComplete: .constant(self.model.sendingStatus == .success))

case .error(let message):
Text(message).textTheme(.headlineSmall)

// TODO: better error display

Spacer()
}
.padding()
.presentationDetents([.medium, .large])
}
}

Expand All @@ -129,9 +112,8 @@ struct ConfirmationView: View {

@StateObject var model = {
let model = Model(vaultService: VaultService())
model.setRecipient(Contact(name: "Very Long Bobby Name", phone: "+33612345678"))

model.sendingStatus = .active
model.setRecipient(Contact(name: "Very Long Bobby Name", phone: "+33612345678"))

return model
}()
Expand All @@ -140,7 +122,7 @@ struct ConfirmationView: View {
VStack {}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.defaultBackground()
.sheetPopover(isPresented: .constant(true)) {
.sheet(isPresented: .constant(true)) {
ConfirmationView()
.environmentObject(model)
}
Expand Down
2 changes: 1 addition & 1 deletion app/App/Navigation/Core/Sending/ContactRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct ContactRow: View {

Spacer()

Text(self.contact.phone).textTheme(.subtitle)
Text(/*self.contact.phone*/"+33612345678").textTheme(.subtitle)
}
.padding(.vertical, 6)

Expand Down
44 changes: 39 additions & 5 deletions app/App/Navigation/Core/Sending/SendingAmountView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ struct SendingAmountView: View {

@EnvironmentObject private var model: Model

@State private var showingConfirmation = false

var body: some View {
VStack {
Spacer()
Expand All @@ -26,7 +24,7 @@ struct SendingAmountView: View {

VStack(spacing: 32) {
PrimaryButton("Send", disabled: self.model.parsedSendingAmount <= 0) {
self.showingConfirmation = true
self.model.showSendingConfirmation = true
}

NumPad(amount: self.$model.sendingAmount)
Expand All @@ -47,16 +45,52 @@ struct SendingAmountView: View {
)
.removeNavigationBarBorder()
.navigationBarBackButtonHidden(true)
.sheetPopover(isPresented: self.$showingConfirmation) {
.sheet(isPresented: self.$model.showSendingConfirmation) {
if self.model.sendingStatus == .signed {
Task {
await self.model.executeTransfer()
}
}
} content: {
ConfirmationView()
}
.sheetPopover(isPresented: .constant(self.model.sendingStatus == .loading || self.model.sendingStatus == .success)) {

Text("Executing your transfer").textTheme(.headlineSmall)
.onTapGesture {
self.model.sendingStatus = .success
}

Spacer().frame(height: 32)

SpinnerView(isComplete: .constant(self.model.sendingStatus == .success))
}
.onChange(of: self.model.sendingStatus) {
// close confirmation sheet on signing
if self.model.sendingStatus == .signed {
self.model.showSendingConfirmation = false
} else if self.model.sendingStatus == .success {
Task { @MainActor in
try await Task.sleep(for: .seconds(1))

self.model.showSendingView = false
}
}
}
}
}

#if DEBUG
struct SendingAmountViewPreviews : PreviewProvider {

@StateObject static var model = Model(vaultService: VaultService())
@StateObject static var model = {
let model = Model(vaultService: VaultService())

model.setRecipient(Contact(name: "Very Long Bobby Name", phone: "+33612345678"))
model.sendingStatus = .none

return model
}()

static var previews: some View {
SendingAmountView()
Expand Down
Loading

0 comments on commit c3b8f92

Please sign in to comment.