diff --git a/app/App/Models/History.swift b/app/App/Models/History.swift new file mode 100644 index 00000000..1e72e286 --- /dev/null +++ b/app/App/Models/History.swift @@ -0,0 +1,61 @@ +// +// History.swift +// Vault +// +// Created by Charles Lanier on 19/06/2024. +// + +import Foundation + +class User { + let nickname: String + let avatarUrl: String? = nil + let address: String? + + init(transactionUser: RawTransactionUser) { + self.nickname = transactionUser.nickname ?? transactionUser.phone_number ?? "UNKNOWN" + self.address = transactionUser.contract_address + } +} + +class Transaction: Identifiable { + let from: User + let to: User + let amount: USDCAmount + let date: Date + let isSending: Bool + + static let dateFormatter = { + let dateFormatter = DateFormatter() + + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + dateFormatter.locale = Locale(identifier: "en_US_POSIX") + dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) + + return dateFormatter + }() + + init(address: String, transaction: RawTransaction) { + self.from = User(transactionUser: transaction.from) + self.to = User(transactionUser: transaction.to) + self.amount = USDCAmount(from: transaction.amount)! + self.date = Self.dateFormatter.date(from: transaction.transaction_timestamp)! + self.isSending = transaction.from.contract_address == address + } +} + +class History { + let transactions: [Transaction] + + var groupedTransactions: [Date: [Transaction]] { + get { + Dictionary(grouping: self.transactions) { (transaction) -> Date in + return Calendar.current.startOfDay(for: transaction.date) + } + } + } + + init(address: String, transactions: [RawTransaction]) { + self.transactions = transactions.map { Transaction(address: address, transaction: $0) } + } +} diff --git a/app/App/Models/Model.swift b/app/App/Models/Model.swift index a8f855c2..d012cace 100644 --- a/app/App/Models/Model.swift +++ b/app/App/Models/Model.swift @@ -26,9 +26,11 @@ enum Status: Equatable { class Model: ObservableObject { @AppStorage("starknetMainAddress") private var address: String = "" + @AppStorage("isOnboarded") var isOnboarded: Bool = false // API Data @Published var balance: USDCAmount? + @Published var txHistory: History? // App @Published var isLoading = false @@ -98,9 +100,8 @@ class Model: ObservableObject { // Contacts checkContactsAuthorizationStatus() - self.address = "0x039fd69d03e3735490a86925612072c5612cbf7a0223678619a1b7f30f4bdc8f" - self.getBalance() + self.getTxHistory() } deinit { @@ -185,6 +186,21 @@ extension Model { } } } + + func getTxHistory() { + vaultService.send(GetTransactionsHistory(address: self.address, first: 10)) { result in + DispatchQueue.main.async { + switch result { + case .success(let response): + self.txHistory = History(address: self.address, transactions: response.transactions) + + case .failure(let error): + // TODO: Handle error + print(error) + } + } + } + } } // MARK: - Country picker @@ -248,7 +264,12 @@ extension Model { guard let phoneNumberFelt = Felt.fromShortString(phoneNumber) else { return nil } - let phoneNumberHex = phoneNumberFelt.toHex().dropFirst(2) + + // TODO: remove this extra step after nonce support + guard let phoneNumberHex = Felt.fromShortString(phoneNumberFelt.toHex())?.toHex().dropFirst(2) else { + return nil + } + guard let phoneNumberBytes = BigUInt(phoneNumberHex, radix: 16)?.serialize().bytes else { return nil } diff --git a/app/App/Navigation/ContentView.swift b/app/App/Navigation/ContentView.swift index 654aaa40..e651a93e 100644 --- a/app/App/Navigation/ContentView.swift +++ b/app/App/Navigation/ContentView.swift @@ -11,8 +11,6 @@ struct ContentView: View { @EnvironmentObject var model: Model - @AppStorage("isOnboarded") var isOnboarded: Bool = false - @State private var selectedTab: Tab = Tab.payments init() { @@ -36,7 +34,7 @@ struct ContentView: View { var body: some View { if !self.model.isProperlyConfigured { ErrorView() - } else if self.isOnboarded { + } else if self.model.isOnboarded { ZStack(alignment: .bottom) { TabView(selection: $selectedTab) { NavigationStack { diff --git a/app/App/Navigation/Core/Home/HomeView.swift b/app/App/Navigation/Core/Home/HomeView.swift index 86ff40cc..4040823b 100644 --- a/app/App/Navigation/Core/Home/HomeView.swift +++ b/app/App/Navigation/Core/Home/HomeView.swift @@ -7,108 +7,12 @@ import SwiftUI -class User { - let address: String - let username: String - let avatarUrl: String? - - init(address: String, username: String, avatarUrl: String? = nil) { - self.address = address - self.username = username - self.avatarUrl = avatarUrl - } -} - -class Transfer: Identifiable { - let from: User - let to: User - let amount: USDCAmount - let date: Date - - init(from: User, to: User, amount: USDCAmount, timestamp: Double) { - self.from = from - self.to = to - self.amount = amount - self.date = Date(timeIntervalSince1970: timestamp) - } -} - -class History { - let transfers: [Transfer] - - var groupedTransfers: [Date: [Transfer]] { - get { - return Dictionary(grouping: self.transfers) { (transfer) -> Date in - Calendar.current.startOfDay(for: transfer.date) - } - } - } - - init(transfers: [Transfer]) { - self.transfers = transfers - } -} - -let users: [String: User] = [ - "me": User( - address: "0xdead", - username: "Bobby" - ), - "sbf": User( - address: "0x1", - username: "SBF", - avatarUrl: "https://fortune.com/img-assets/wp-content/uploads/2022/11/SBF-1.jpg" - ), - "apple": User( - address: "0x2", - username: "Apple", - avatarUrl: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRIHoznvT47BiebsgSlaiey1FKjGR8xZru6gROHvntwI3QSA2I7T08Ys7g1by9_iBw-ekI&usqp=CAU" - ), - "vitalik": User( - address: "0x3", - username: "Vitalik", - avatarUrl: "https://images.moneycontrol.com/static-mcnews/2021/05/vitalik-Buterin-ethereum.jpg?impolicy=website&width=1600&height=900" - ), - "satoshi": User(address: "0x4", username: "Satoshi N"), - "alex": User( - address: "0x5", - username: "Alex", - avatarUrl: "https://www.cryptotimes.io/wp-content/uploads/2024/02/Matter_Labs_co-founder_and_CEO_Alex_Gluchowski_proposed_an_Ethereum_court_system.jpg.webp" - ), - "abdel": User( - address: "0x6", - username: "Abdel.stark", - avatarUrl: "https://miro.medium.com/v2/resize:fit:1400/1*BTiOG6PF5d9ToTAZqlIjuw.jpeg" - ), -] - struct HomeView: View { @EnvironmentObject private var model: Model @State private var showingAddFundsWebView = false - private var me: User { - get { - return users["me"]! - } - } - - let history: History - - init() { - self.history = History(transfers: [ - Transfer(from: users["me"]!, to: users["sbf"]!, amount: USDCAmount(from: 1_604_568.230)!, timestamp: 1712199068), - Transfer(from: users["me"]!, to: users["apple"]!, amount: USDCAmount(from: 4_249.99)!, timestamp: 1711924459), - Transfer(from: users["vitalik"]!, to: users["me"]!, amount: USDCAmount(from: 70_000)!, timestamp: 1711878225), - - Transfer(from: users["alex"]!, to: users["me"]!, amount: USDCAmount(from: 1_000_000)!, timestamp: 1711847328), - Transfer(from: users["me"]!, to: users["satoshi"]!, amount: USDCAmount(from: 32.57)!, timestamp: 1712000648), - - Transfer(from: users["abdel"]!, to: users["me"]!, amount: USDCAmount(from: 0.01)!, timestamp: 1711828026), - ]) - } - var body: some View { List { @@ -174,33 +78,38 @@ struct HomeView: View { // MARK: History - ForEach(self.history.groupedTransfers.keys.sorted(by: >), id: \.self) { day in - Section { - ForEach(0..), + id: \.self + ) { day in + Section { + ForEach(0..