From 633fd91a639374cea0568eb1f63517553a1c56c9 Mon Sep 17 00:00:00 2001 From: 0xChqrles Date: Mon, 24 Jun 2024 16:13:01 +0200 Subject: [PATCH] some UI updates --- .../Colors/Background2.colorset/Contents.json | 6 +- .../Contents.json | 2 +- .../Contents.json | 2 +- .../Contents.json | 2 +- .../Contents.json | 2 +- .../Contents.json | 2 +- .../Contents.json | 2 +- app/App/Components/{ => Avatar}/Avatar.swift | 14 +- .../Components/{ => Avatar}/NoAvatar.swift | 33 +-- app/App/Components/Buttons.swift | 9 +- app/App/Components/{ => Input}/Input.swift | 0 app/App/Components/{ => Input}/OTPInput.swift | 0 .../Components/{ => Input}/PhoneInput.swift | 0 .../Components/{ => Modifiers}/Popover.swift | 0 .../{ => Modifiers}/ThemedText.swift | 0 app/App/Models/History.swift | 4 +- app/App/Navigation/ContentView.swift | 12 +- app/App/Navigation/Core/Home/HomeView.swift | 212 +++++++++--------- .../Navigation/Core/Home/TransferRow.swift | 13 +- .../Navigation/Core/Request/RequestView.swift | 8 + .../Core/Sending/SendingRecipientView.swift | 6 +- app/App/Utils/NavigationBar.swift | 21 +- app/Vault.xcodeproj/project.pbxproj | 66 ++++-- 23 files changed, 240 insertions(+), 176 deletions(-) rename app/App/Assets.xcassets/Colors/Rainbow/Blue/{VBlueA50.colorset => VBlueDarker.colorset}/Contents.json (91%) rename app/App/Assets.xcassets/Colors/Rainbow/Green/{VGreenA50.colorset => VGreenDarker.colorset}/Contents.json (91%) rename app/App/Assets.xcassets/Colors/Rainbow/Lime/{VLimeA50.colorset => VLimeDarker.colorset}/Contents.json (91%) rename app/App/Assets.xcassets/Colors/Rainbow/Orange/{VOrangeA50.colorset => VOrangeDarker.colorset}/Contents.json (91%) rename app/App/Assets.xcassets/Colors/Rainbow/Pink/{VPinkA50.colorset => VPinkDarker.colorset}/Contents.json (91%) rename app/App/Assets.xcassets/Colors/Rainbow/Purple/{VPurpleA50.colorset => VPurpleDarker.colorset}/Contents.json (91%) rename app/App/Components/{ => Avatar}/Avatar.swift (84%) rename app/App/Components/{ => Avatar}/NoAvatar.swift (60%) rename app/App/Components/{ => Input}/Input.swift (100%) rename app/App/Components/{ => Input}/OTPInput.swift (100%) rename app/App/Components/{ => Input}/PhoneInput.swift (100%) rename app/App/Components/{ => Modifiers}/Popover.swift (100%) rename app/App/Components/{ => Modifiers}/ThemedText.swift (100%) create mode 100644 app/App/Navigation/Core/Request/RequestView.swift diff --git a/app/App/Assets.xcassets/Colors/Background2.colorset/Contents.json b/app/App/Assets.xcassets/Colors/Background2.colorset/Contents.json index 54dd1a1a..7a2b0a6f 100644 --- a/app/App/Assets.xcassets/Colors/Background2.colorset/Contents.json +++ b/app/App/Assets.xcassets/Colors/Background2.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0x1D", - "green" : "0x18", - "red" : "0x17" + "blue" : "0x22", + "green" : "0x1D", + "red" : "0x1C" } }, "idiom" : "universal" diff --git a/app/App/Assets.xcassets/Colors/Rainbow/Blue/VBlueA50.colorset/Contents.json b/app/App/Assets.xcassets/Colors/Rainbow/Blue/VBlueDarker.colorset/Contents.json similarity index 91% rename from app/App/Assets.xcassets/Colors/Rainbow/Blue/VBlueA50.colorset/Contents.json rename to app/App/Assets.xcassets/Colors/Rainbow/Blue/VBlueDarker.colorset/Contents.json index 0dc41fd5..cfcddbdd 100644 --- a/app/App/Assets.xcassets/Colors/Rainbow/Blue/VBlueA50.colorset/Contents.json +++ b/app/App/Assets.xcassets/Colors/Rainbow/Blue/VBlueDarker.colorset/Contents.json @@ -4,7 +4,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.500", + "alpha" : "1.000", "blue" : "0xD5", "green" : "0xA0", "red" : "0x26" diff --git a/app/App/Assets.xcassets/Colors/Rainbow/Green/VGreenA50.colorset/Contents.json b/app/App/Assets.xcassets/Colors/Rainbow/Green/VGreenDarker.colorset/Contents.json similarity index 91% rename from app/App/Assets.xcassets/Colors/Rainbow/Green/VGreenA50.colorset/Contents.json rename to app/App/Assets.xcassets/Colors/Rainbow/Green/VGreenDarker.colorset/Contents.json index 2e960daf..46664cc4 100644 --- a/app/App/Assets.xcassets/Colors/Rainbow/Green/VGreenA50.colorset/Contents.json +++ b/app/App/Assets.xcassets/Colors/Rainbow/Green/VGreenDarker.colorset/Contents.json @@ -4,7 +4,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.500", + "alpha" : "1.000", "blue" : "0x26", "green" : "0xD5", "red" : "0x7E" diff --git a/app/App/Assets.xcassets/Colors/Rainbow/Lime/VLimeA50.colorset/Contents.json b/app/App/Assets.xcassets/Colors/Rainbow/Lime/VLimeDarker.colorset/Contents.json similarity index 91% rename from app/App/Assets.xcassets/Colors/Rainbow/Lime/VLimeA50.colorset/Contents.json rename to app/App/Assets.xcassets/Colors/Rainbow/Lime/VLimeDarker.colorset/Contents.json index 98a03b85..082d4cf6 100644 --- a/app/App/Assets.xcassets/Colors/Rainbow/Lime/VLimeA50.colorset/Contents.json +++ b/app/App/Assets.xcassets/Colors/Rainbow/Lime/VLimeDarker.colorset/Contents.json @@ -4,7 +4,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.500", + "alpha" : "1.000", "blue" : "0x80", "green" : "0xD5", "red" : "0x26" diff --git a/app/App/Assets.xcassets/Colors/Rainbow/Orange/VOrangeA50.colorset/Contents.json b/app/App/Assets.xcassets/Colors/Rainbow/Orange/VOrangeDarker.colorset/Contents.json similarity index 91% rename from app/App/Assets.xcassets/Colors/Rainbow/Orange/VOrangeA50.colorset/Contents.json rename to app/App/Assets.xcassets/Colors/Rainbow/Orange/VOrangeDarker.colorset/Contents.json index c9a43c50..8c3bd2b2 100644 --- a/app/App/Assets.xcassets/Colors/Rainbow/Orange/VOrangeA50.colorset/Contents.json +++ b/app/App/Assets.xcassets/Colors/Rainbow/Orange/VOrangeDarker.colorset/Contents.json @@ -4,7 +4,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.500", + "alpha" : "1.000", "blue" : "0x26", "green" : "0xA3", "red" : "0xD5" diff --git a/app/App/Assets.xcassets/Colors/Rainbow/Pink/VPinkA50.colorset/Contents.json b/app/App/Assets.xcassets/Colors/Rainbow/Pink/VPinkDarker.colorset/Contents.json similarity index 91% rename from app/App/Assets.xcassets/Colors/Rainbow/Pink/VPinkA50.colorset/Contents.json rename to app/App/Assets.xcassets/Colors/Rainbow/Pink/VPinkDarker.colorset/Contents.json index d464d450..6536f7e3 100644 --- a/app/App/Assets.xcassets/Colors/Rainbow/Pink/VPinkA50.colorset/Contents.json +++ b/app/App/Assets.xcassets/Colors/Rainbow/Pink/VPinkDarker.colorset/Contents.json @@ -4,7 +4,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.500", + "alpha" : "1.000", "blue" : "0xB8", "green" : "0x26", "red" : "0xD5" diff --git a/app/App/Assets.xcassets/Colors/Rainbow/Purple/VPurpleA50.colorset/Contents.json b/app/App/Assets.xcassets/Colors/Rainbow/Purple/VPurpleDarker.colorset/Contents.json similarity index 91% rename from app/App/Assets.xcassets/Colors/Rainbow/Purple/VPurpleA50.colorset/Contents.json rename to app/App/Assets.xcassets/Colors/Rainbow/Purple/VPurpleDarker.colorset/Contents.json index afdfa410..76f982ce 100644 --- a/app/App/Assets.xcassets/Colors/Rainbow/Purple/VPurpleA50.colorset/Contents.json +++ b/app/App/Assets.xcassets/Colors/Rainbow/Purple/VPurpleDarker.colorset/Contents.json @@ -4,7 +4,7 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "0.500", + "alpha" : "1.000", "blue" : "0xD5", "green" : "0x26", "red" : "0x53" diff --git a/app/App/Components/Avatar.swift b/app/App/Components/Avatar/Avatar.swift similarity index 84% rename from app/App/Components/Avatar.swift rename to app/App/Components/Avatar/Avatar.swift index 18b02396..b5a96797 100644 --- a/app/App/Components/Avatar.swift +++ b/app/App/Components/Avatar/Avatar.swift @@ -21,17 +21,17 @@ struct Avatar: View { init( salt: String? = nil, - name: String = Self.defaultName, + name: String? = nil, size: CGFloat = Self.defaultSize ) { - self.name = name + self.name = name ?? Self.defaultName self.size = size self.salt = salt } init( salt: String? = nil, - name: String = Self.defaultName, + name: String? = nil, size: CGFloat = Self.defaultSize, url: String? = nil ) { @@ -41,7 +41,7 @@ struct Avatar: View { init( salt: String? = nil, - name: String = Self.defaultName, + name: String? = nil, size: CGFloat = Self.defaultSize, data: Data? = nil ) { @@ -57,7 +57,7 @@ struct Avatar: View { image .resizable() .aspectRatio(contentMode: .fill) - .frame(width: 42, height: 42) + .frame(width: self.size, height: self.size) .scaledToFit() }, placeholder: { @@ -72,11 +72,11 @@ struct Avatar: View { Image(uiImage: uiImage) .resizable() .aspectRatio(contentMode: .fill) - .frame(width: size, height: size) + .frame(width: self.size, height: self.size) .scaledToFit() .clipShape(Circle()) } else { - NoAvatar(salt: self.salt, name: self.name) + NoAvatar(salt: self.salt, name: self.name, size: self.size) } } } diff --git a/app/App/Components/NoAvatar.swift b/app/App/Components/Avatar/NoAvatar.swift similarity index 60% rename from app/App/Components/NoAvatar.swift rename to app/App/Components/Avatar/NoAvatar.swift index fec1cd20..1674e560 100644 --- a/app/App/Components/NoAvatar.swift +++ b/app/App/Components/Avatar/NoAvatar.swift @@ -16,25 +16,25 @@ enum NoAvatarSalt: Int, CaseIterable { case s5 = 4 case s6 = 5 - var colors: (Color, Color) { + var gradient: Gradient { switch self { case .s1: - return (.vPurple, .vPurpleA50) + return Gradient(colors: [.vPurple, .vPurpleDarker]) case .s2: - return (.vGreen, .vGreenA50) + return Gradient(colors: [.vGreen, .vGreenDarker]) case .s3: - return (.vLime, .vLimeA50) + return Gradient(colors: [.vLime, .vLimeDarker]) case .s4: - return (.vPink, .vPinkA50) + return Gradient(colors: [.vPink, .vPinkDarker]) case .s5: - return (.vOrange, .vOrangeA50) + return Gradient(colors: [.vOrange, .vOrangeDarker]) case .s6: - return (.vBlue, .vBlueA50) + return Gradient(colors: [.vBlue, .vBlueDarker]) } } @@ -44,25 +44,30 @@ enum NoAvatarSalt: Int, CaseIterable { struct NoAvatar: View { let salt: NoAvatarSalt let name: String + let size: CGFloat - init(salt: String?, name: String) { + init(salt: String?, name: String, size: CGFloat = Avatar.defaultSize) { let saltInt = Int(salt?.bytes.last ?? 0) % NoAvatarSalt.allCases.count self.salt = NoAvatarSalt(rawValue: saltInt) ?? .s1 self.name = name + self.size = size } var body: some View { - let (strokeColor, fillColor) = self.salt.colors + let linearGradient = LinearGradient( + gradient: self.salt.gradient, + startPoint: .top, + endPoint: .bottom + ) Capsule() - .fill(fillColor) - .strokeBorder(strokeColor, lineWidth: 1) - .frame(width: 42, height: 42) + .fill(linearGradient) + .frame(width: self.size, height: self.size) .overlay() { Text(name.initials.uppercased()) .font(.system(size: 18)) - .fontWeight(.semibold) - .foregroundStyle(strokeColor) + .fontWeight(.medium) + .foregroundStyle(.neutral1) } } } diff --git a/app/App/Components/Buttons.swift b/app/App/Components/Buttons.swift index 97fdf167..9e3f239e 100644 --- a/app/App/Components/Buttons.swift +++ b/app/App/Components/Buttons.swift @@ -14,6 +14,7 @@ struct PrimaryButtonStyle: ButtonStyle { configuration.label .background(.accent) .clipShape(RoundedRectangle(cornerRadius: 10)) + .shadow(color: .accent.opacity(0.2), radius: 15, y: 5) .opacity(configuration.isPressed ? 0.7 : 1) .animation(.easeOut(duration: 0.1), value: configuration.isPressed) } @@ -96,7 +97,7 @@ enum IconButtonSize { return 36 case .large: - return 64 + return 54 case .custom(let buttonSize, _): return buttonSize @@ -109,7 +110,7 @@ enum IconButtonSize { return 12 case .large: - return 20 + return 16 case .custom(_, let iconSize): return iconSize @@ -139,6 +140,7 @@ struct IconButtonStyle: ButtonStyle { configuration.label .background(self.priority.background) .clipShape(Capsule()) + .shadow(color: self.priority.background.opacity(0.2), radius: 8, y: 4) .opacity(configuration.isPressed ? 0.7 : 1) .animation(.easeOut(duration: 0.1), value: configuration.isPressed) } @@ -188,7 +190,6 @@ struct IconButtonWithTextModifier: ViewModifier { VStack(spacing: 10) { content .preferredColorScheme(.dark) - .background(.background1) Text(self.text).textTheme(.buttonIcon) } @@ -238,7 +239,7 @@ struct NoopButtonStyle: ButtonStyle { } .withText("Settings") - IconButton(size: .large) {} icon: { + IconButton(size: .large, priority: .primary) {} icon: { Image("FaceID").iconify() } .withText("Face ID") diff --git a/app/App/Components/Input.swift b/app/App/Components/Input/Input.swift similarity index 100% rename from app/App/Components/Input.swift rename to app/App/Components/Input/Input.swift diff --git a/app/App/Components/OTPInput.swift b/app/App/Components/Input/OTPInput.swift similarity index 100% rename from app/App/Components/OTPInput.swift rename to app/App/Components/Input/OTPInput.swift diff --git a/app/App/Components/PhoneInput.swift b/app/App/Components/Input/PhoneInput.swift similarity index 100% rename from app/App/Components/PhoneInput.swift rename to app/App/Components/Input/PhoneInput.swift diff --git a/app/App/Components/Popover.swift b/app/App/Components/Modifiers/Popover.swift similarity index 100% rename from app/App/Components/Popover.swift rename to app/App/Components/Modifiers/Popover.swift diff --git a/app/App/Components/ThemedText.swift b/app/App/Components/Modifiers/ThemedText.swift similarity index 100% rename from app/App/Components/ThemedText.swift rename to app/App/Components/Modifiers/ThemedText.swift diff --git a/app/App/Models/History.swift b/app/App/Models/History.swift index 82a5995a..f28de6a6 100644 --- a/app/App/Models/History.swift +++ b/app/App/Models/History.swift @@ -8,12 +8,12 @@ import Foundation class User { - let nickname: String + let nickname: String? let avatarUrl: String? = nil let address: String? init(transactionUser: RawTransactionUser) { - self.nickname = transactionUser.nickname ?? transactionUser.phone_number ?? "UNKNOWN" + self.nickname = transactionUser.nickname ?? transactionUser.phone_number self.address = transactionUser.contract_address } } diff --git a/app/App/Navigation/ContentView.swift b/app/App/Navigation/ContentView.swift index e651a93e..da5610ed 100644 --- a/app/App/Navigation/ContentView.swift +++ b/app/App/Navigation/ContentView.swift @@ -65,5 +65,15 @@ struct ContentView: View { } #Preview { - ContentView() + struct ContentViewPreviews: View { + + @StateObject var model = Model() + + var body: some View { + ContentView() + .environmentObject(self.model) + } + } + + return ContentViewPreviews() } diff --git a/app/App/Navigation/Core/Home/HomeView.swift b/app/App/Navigation/Core/Home/HomeView.swift index ae00ef35..f852d30a 100644 --- a/app/App/Navigation/Core/Home/HomeView.swift +++ b/app/App/Navigation/Core/Home/HomeView.swift @@ -7,131 +7,55 @@ import SwiftUI +// PreferenceKey to store the scroll offset +struct ScrollOffsetKey: PreferenceKey { + typealias Value = CGFloat + static var defaultValue: CGFloat = 0 + + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value += nextValue() + } +} + struct HomeView: View { @EnvironmentObject private var model: Model - @StateObject private var txHistoryModel: PaginationModel = PaginationModel(threshold: 1, pageSize: 1) + @StateObject private var txHistoryModel: PaginationModel = PaginationModel(threshold: 7, pageSize: 15) @State private var showingAddFundsWebView = false + @State private var scrollOffset: CGFloat = 0 var body: some View { - List { - - VStack { - - // MARK: Balance - - VStack(spacing: 12) { - Text("Account Balance") - .foregroundStyle(.neutral2) - .textTheme(.bodyPrimary) - - BalanceView(balance: self.$model.balance) - } - .padding(EdgeInsets(top: 32, leading: 0, bottom: 42, trailing: 0)) + ScrollView { - // MARK: Transfers + // MARK: Balance + VStack(spacing: 48) { HStack { - Spacer(minLength: 8) - - IconButton(size: .large, priority: .primary) { - self.model.showSendingView = true - } icon: { - Image(systemName: "arrow.up") - .iconify() - .fontWeight(.semibold) - } - .withText("Send") - .frame(maxWidth: .infinity) - Spacer() - IconButton(size: .large) { - // TODO: Handle sending - } icon: { - Image(systemName: "arrow.down") - .iconify() - .fontWeight(.semibold) - } - .withText("Request") - .frame(maxWidth: .infinity) - - Spacer() + VStack(spacing: 12) { + Text("Account Balance") + .foregroundStyle(.neutral2) + .textTheme(.bodyPrimary) - IconButton(size: .large) { - self.showingAddFundsWebView = true - } icon: { - Image(systemName: "plus") - .iconify() - .fontWeight(.medium) - } - .withText("Add funds") - .frame(maxWidth: .infinity) - .sheet(isPresented: $showingAddFundsWebView) { - WebView(url: URL(string: "https://app.fun.xyz")!) + BalanceView(balance: self.$model.balance) } - Spacer(minLength: 8) + Spacer() } - } - .padding(.bottom, 16) - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) - .listRowBackground(EmptyView()) - .listRowSeparator(.hidden) + .padding(.top, 180) + .padding(.bottom, 58) - // MARK: History + ActionsView() - if let txHistory = self.txHistoryModel.source { - ForEach( - txHistory.groupedTransactions.keys.sorted(by: >), - id: \.self - ) { day in - Section { - ForEach(0.. String { @@ -158,6 +83,91 @@ struct HomeView: View { return formatter.string(from: date) } } + + // MARK: - Actions View + + @ViewBuilder + func ActionsView() -> some View { + HStack { + Spacer(minLength: 24) + + IconButton(size: .large, priority: .primary) { + self.model.showSendingView = true + } icon: { + Image(systemName: "arrow.up") + .iconify() + .fontWeight(.semibold) + } + .withText("Send") + .frame(maxWidth: .infinity) + + Spacer() + + IconButton(size: .large) { + // TODO: Handle sending + } icon: { + Image(systemName: "arrow.down") + .iconify() + .fontWeight(.semibold) + } + .withText("Request") + .frame(maxWidth: .infinity) + + Spacer() + + IconButton(size: .large) { + self.showingAddFundsWebView = true + } icon: { + Image(systemName: "plus") + .iconify() + .fontWeight(.medium) + } + .withText("Add funds") + .frame(maxWidth: .infinity) + .sheet(isPresented: $showingAddFundsWebView) { + WebView(url: URL(string: "https://app.fun.xyz")!) + } + + Spacer(minLength: 24) + } + } + + // MARK: - History View + + @ViewBuilder + func HistoryView() -> some View { + LazyVStack(spacing: 48) { + if let txHistory = self.txHistoryModel.source { + ForEach( + txHistory.groupedTransactions.keys.sorted(by: >), + id: \.self + ) { day in + VStack(alignment: .leading, spacing: 12) { + Text(self.formatSectionHeader(for: day).uppercased()) + .textTheme(.headlineSmall) + + VStack(spacing: 32) { + ForEach(0..