From aedb0ebbb68780e2b22de4f77e171d5c24480beb Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Tue, 18 Jul 2023 12:48:24 +0200 Subject: [PATCH 01/13] Store Recently used wallets in UserDefaults --- .../Modal/ModalViewModel.swift | 68 ++++++++++++++++++- .../Modal/RecentWalletStorage.swift | 31 +++++++++ .../Modal/Screens/WalletList.swift | 2 +- .../Explorer/ListingsResponse.swift | 2 + 4 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 Sources/WalletConnectModal/Modal/RecentWalletStorage.swift diff --git a/Sources/WalletConnectModal/Modal/ModalViewModel.swift b/Sources/WalletConnectModal/Modal/ModalViewModel.swift index 6c1e4a5fb..86d63acca 100644 --- a/Sources/WalletConnectModal/Modal/ModalViewModel.swift +++ b/Sources/WalletConnectModal/Modal/ModalViewModel.swift @@ -55,9 +55,11 @@ final class ModalViewModel: ObservableObject { } var filteredWallets: [Listing] { - if searchTerm.isEmpty { return wallets } + if searchTerm.isEmpty { return sortByRecent(wallets) } - return wallets.filter { $0.name.lowercased().contains(searchTerm.lowercased()) } + return sortByRecent( + wallets.filter { $0.name.lowercased().contains(searchTerm.lowercased()) } + ) } private var disposeBag = Set() @@ -120,6 +122,8 @@ final class ModalViewModel: ObservableObject { } func onListingTap(_ listing: Listing) { + setLastTimeUsed(listing.id) + navigateToDeepLink( universalLink: listing.mobile.universal ?? "", nativeLink: listing.mobile.native ?? "" @@ -146,7 +150,6 @@ final class ModalViewModel: ObservableObject { func onCopyButton() { - guard let uri else { toast = Toast(style: .error, message: "No uri found") return @@ -190,6 +193,8 @@ final class ModalViewModel: ObservableObject { return lhs < rhs } + + loadRecentWallets() } } catch { toast = Toast(style: .error, message: error.localizedDescription) @@ -197,6 +202,63 @@ final class ModalViewModel: ObservableObject { } } +// MARK: - Recent Wallets + +private extension ModalViewModel { + + func sortByRecent(_ input: [Listing]) -> [Listing] { + input.sorted { lhs, rhs in + guard let lhsLastTimeUsed = lhs.lastTimeUsed else { + return false + } + + guard let rhsLastTimeUsed = rhs.lastTimeUsed else { + return true + } + + return lhsLastTimeUsed > rhsLastTimeUsed + } + } + + func loadRecentWallets() { + RecentWalletsStorage().recentWallets.forEach { wallet in + + guard let lastTimeUsed = wallet.lastTimeUsed else { + return + } + + // Consider Recent only for 3 days + if abs(lastTimeUsed.timeIntervalSinceNow) > (24 * 60 * 60 * 3) { + return + } + + setLastTimeUsed(wallet.id, date: lastTimeUsed) + } + } + + func saveRecentWallets() { + RecentWalletsStorage().recentWallets = Array(wallets.filter { + $0.lastTimeUsed != nil + }.prefix(5)) + } + + func setLastTimeUsed(_ walletId: String, date: Date = Date()) { + guard let index = wallets.firstIndex(where: { + $0.id == walletId + }) else { + return + } + + var copy = wallets[index] + copy.lastTimeUsed = date + wallets[index] = copy + + saveRecentWallets() + } +} + +// MARK: - Deeplinking + private extension ModalViewModel { enum DeeplinkErrors: LocalizedError { case noWalletLinkFound diff --git a/Sources/WalletConnectModal/Modal/RecentWalletStorage.swift b/Sources/WalletConnectModal/Modal/RecentWalletStorage.swift new file mode 100644 index 000000000..04487edce --- /dev/null +++ b/Sources/WalletConnectModal/Modal/RecentWalletStorage.swift @@ -0,0 +1,31 @@ +import Foundation + +final class RecentWalletsStorage { + private let defaults: UserDefaults + + init(defaults: UserDefaults = .standard) { + self.defaults = defaults + } + + var recentWallets: [Listing] { + get { + guard + let data = defaults.data(forKey: "recentWallets"), + let wallets = try? JSONDecoder().decode([Listing].self, from: data) + else { + return [] + } + + return wallets + } + set { + guard + let walletsData = try? JSONEncoder().encode(newValue) + else { + return + } + + defaults.set(walletsData, forKey: "recentWallets") + } + } +} diff --git a/Sources/WalletConnectModal/Modal/Screens/WalletList.swift b/Sources/WalletConnectModal/Modal/Screens/WalletList.swift index e601a5de7..1e17ae6a9 100644 --- a/Sources/WalletConnectModal/Modal/Screens/WalletList.swift +++ b/Sources/WalletConnectModal/Modal/Screens/WalletList.swift @@ -176,7 +176,7 @@ struct WalletList: View { .minimumScaleFactor(0.4) Text("RECENT") - .opacity(0) + .opacity(wallet.lastTimeUsed != nil ? 1 : 0) .font(.system(size: 10)) .foregroundColor(.foreground3) .padding(.horizontal, 12) diff --git a/Sources/WalletConnectModal/Networking/Explorer/ListingsResponse.swift b/Sources/WalletConnectModal/Networking/Explorer/ListingsResponse.swift index 8c08458a5..bc7c91619 100644 --- a/Sources/WalletConnectModal/Networking/Explorer/ListingsResponse.swift +++ b/Sources/WalletConnectModal/Networking/Explorer/ListingsResponse.swift @@ -12,6 +12,7 @@ struct Listing: Codable, Hashable, Identifiable { let imageId: String let app: App let mobile: Mobile + var lastTimeUsed: Date? private enum CodingKeys: String, CodingKey { case id @@ -21,6 +22,7 @@ struct Listing: Codable, Hashable, Identifiable { case imageId = "image_id" case app case mobile + case lastTimeUsed } struct App: Codable, Hashable { From 8fb631d7290dac226d2baa1646db60e1dee32cf5 Mon Sep 17 00:00:00 2001 From: Alexander Lisovik Date: Thu, 20 Jul 2023 11:17:47 +0200 Subject: [PATCH 02/13] Update verify logic for the unknown state --- .../Auth/Services/Wallet/WalletRequestSubscriber.swift | 8 +++----- .../WalletConnectSign/Engine/Common/ApproveEngine.swift | 5 +++-- .../WalletConnectSign/Engine/Common/SessionEngine.swift | 8 +++----- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift index 3467dc57c..d1b523f4b 100644 --- a/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift +++ b/Sources/Auth/Services/Wallet/WalletRequestSubscriber.swift @@ -44,13 +44,11 @@ class WalletRequestSubscriber { let assertionId = payload.decryptedPayload.sha256().toHexString() do { let origin = try await verifyClient.verifyOrigin(assertionId: assertionId) - let verifyContext = await verifyClient.createVerifyContext( - origin: origin, - domain: payload.request.payloadParams.domain - ) + let verifyContext = verifyClient.createVerifyContext(origin: origin, domain: payload.request.payloadParams.domain) onRequest?((request, verifyContext)) } catch { - onRequest?((request, nil)) + let verifyContext = verifyClient.createVerifyContext(origin: nil, domain: payload.request.payloadParams.domain) + onRequest?((request, verifyContext)) return } } diff --git a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift index fe160fd50..f8a87ddce 100644 --- a/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/ApproveEngine.swift @@ -297,13 +297,14 @@ private extension ApproveEngine { let assertionId = payload.decryptedPayload.sha256().toHexString() do { let origin = try await verifyClient.verifyOrigin(assertionId: assertionId) - let verifyContext = await verifyClient.createVerifyContext( + let verifyContext = verifyClient.createVerifyContext( origin: origin, domain: payload.request.proposer.metadata.url ) onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), verifyContext) } catch { - onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), nil) + let verifyContext = verifyClient.createVerifyContext(origin: nil, domain: payload.request.proposer.metadata.url) + onSessionProposal?(proposal.publicRepresentation(pairingTopic: payload.topic), verifyContext) return } } diff --git a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift index 50f460642..d8f265dab 100644 --- a/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift +++ b/Sources/WalletConnectSign/Engine/Common/SessionEngine.swift @@ -242,13 +242,11 @@ private extension SessionEngine { let assertionId = payload.decryptedPayload.sha256().toHexString() do { let origin = try await verifyClient.verifyOrigin(assertionId: assertionId) - let verifyContext = await verifyClient.createVerifyContext( - origin: origin, - domain: session.peerParticipant.metadata.url - ) + let verifyContext = verifyClient.createVerifyContext(origin: origin, domain: session.peerParticipant.metadata.url) onSessionRequest?(request, verifyContext) } catch { - onSessionRequest?(request, nil) + let verifyContext = verifyClient.createVerifyContext(origin: nil, domain: session.peerParticipant.metadata.url) + onSessionRequest?(request, verifyContext) return } } From 0b3db5598f5a4e0f862d5e70494235a83a505c87 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Thu, 20 Jul 2023 16:41:12 +0300 Subject: [PATCH 03/13] Push server bearer auth --- Example/IntegrationTests/Push/Publisher.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Example/IntegrationTests/Push/Publisher.swift b/Example/IntegrationTests/Push/Publisher.swift index 711c37837..50667f1ce 100644 --- a/Example/IntegrationTests/Push/Publisher.swift +++ b/Example/IntegrationTests/Push/Publisher.swift @@ -11,7 +11,7 @@ class Publisher { let payload = try encoder.encode(notifyRequestPayload) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") - request.setValue("Authorization", forHTTPHeaderField: InputConfig.gmDappProjectSecret) + request.setValue("Bearer \(InputConfig.gmDappProjectSecret)", forHTTPHeaderField: "Authorization") request.httpBody = payload let (_, response) = try await URLSession.shared.data(for: request) guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Notify error") } From 07efbeb4c908478bdcde43192049451ac8a146a0 Mon Sep 17 00:00:00 2001 From: Artur Guseinov Date: Mon, 24 Jul 2023 13:48:20 +0400 Subject: [PATCH 04/13] Upload testflight script updated --- fastlane/Fastfile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index b7e71fc34..2c35fdf42 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -108,10 +108,15 @@ platform :ios do upload_to_testflight( apple_id: ENV["APPLE_ID"], app_identifier: ENV["APP_IDENTIFIER"], - skip_waiting_for_build_processing: true, - distribute_external: true, changelog: "#{ENV["SCHEME"]} app weekly build 🚀", - notify_external_testers: false, + distribute_external: true, + skip_submission: true, + notify_external_testers: true, + skip_waiting_for_build_processing: false, + groups: [ + "WalletConnect", + "WalletConnect Users" + ] ) clean_build_artifacts() end From 85ab00067f26214733452e401b1aa5132e1eec44 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Wed, 12 Jul 2023 16:04:10 +0200 Subject: [PATCH 05/13] Limit screens in height for landscape --- .../Modal/ModalContainerView.swift | 4 +- .../WalletConnectModal/Modal/ModalSheet.swift | 39 +++--- .../Modal/Screens/GetAWalletView.swift | 90 +++++++------- .../Modal/Screens/WalletList.swift | 112 +++++++++--------- .../Modal/Screens/WhatIsWalletView.swift | 80 +++++++++---- .../WalletConnectModal/UI/Common/Toast.swift | 13 +- 6 files changed, 183 insertions(+), 155 deletions(-) diff --git a/Sources/WalletConnectModal/Modal/ModalContainerView.swift b/Sources/WalletConnectModal/Modal/ModalContainerView.swift index d256bf328..af30dd194 100644 --- a/Sources/WalletConnectModal/Modal/ModalContainerView.swift +++ b/Sources/WalletConnectModal/Modal/ModalContainerView.swift @@ -31,9 +31,11 @@ struct ModalContainerView: View { .environment(\.projectId, WalletConnectModal.config.projectId) .transition(.move(edge: .bottom)) .animation(.spring(), value: showModal) + .background(Color.thickOverlay + .colorScheme(.light).opacity(showModal ? 1 : 0)) } } - + .background(Color.clear) .edgesIgnoringSafeArea(.all) .onChangeBackported(of: showModal, perform: { newValue in if newValue == false { diff --git a/Sources/WalletConnectModal/Modal/ModalSheet.swift b/Sources/WalletConnectModal/Modal/ModalSheet.swift index 328ed5b84..21009b030 100644 --- a/Sources/WalletConnectModal/Modal/ModalSheet.swift +++ b/Sources/WalletConnectModal/Modal/ModalSheet.swift @@ -3,6 +3,12 @@ import SwiftUI public struct ModalSheet: View { @ObservedObject var viewModel: ModalViewModel + @Environment(\.verticalSizeClass) var verticalSizeClass + + var isLandscape: Bool { + verticalSizeClass == .compact + } + public var body: some View { VStack(spacing: 0) { modalHeader() @@ -15,27 +21,25 @@ public struct ModalSheet: View { .background(Color.background1) .cornerRadius(30, corners: [.topLeft, .topRight]) } - .padding(.bottom, 40) .edgesIgnoringSafeArea(.bottom) + .background( + VStack(spacing: 0) { + Color.accent + .frame(height: 90) + .cornerRadius(8, corners: [[.topLeft, .topRight]]) + Color.background1 + } + ) + .toastView(toast: $viewModel.toast) + .if(isLandscape) { + $0.padding(.horizontal, 80) + } .onAppear { Task { await viewModel.fetchWallets() await viewModel.createURI() } } - .background( - ZStack { - Color.thickOverlay.colorScheme(.light) - - VStack(spacing: 0) { - Color.accent - .frame(height: 90) - .cornerRadius(8, corners: [[.topLeft, .topRight]]) - Color.background1 - } - } - ) - .toastView(toast: $viewModel.toast) } private func modalHeader() -> some View { @@ -55,7 +59,7 @@ public struct ModalSheet: View { .padding(.trailing, 10) } .foregroundColor(Color.foreground1) - .frame(height: 48) + .frame(height: 44) } private func contentHeader() -> some View { @@ -77,7 +81,7 @@ public struct ModalSheet: View { } .animation(.default) .foregroundColor(.accent) - .frame(height: 60) + .frame(height: 50) .overlay( VStack { if viewModel.destination.hasSearch { @@ -135,14 +139,17 @@ public struct ModalSheet: View { navigateTo: viewModel.navigateTo(_:), navigateToExternalLink: viewModel.navigateToExternalLink(_:) ) + .padding(.bottom, 20) case .qr: qrCode() + .padding(.bottom, 20) case .getWallet: GetAWalletView( wallets: Array(viewModel.wallets.prefix(6)), onWalletTap: viewModel.onGetWalletTap(_:), navigateToExternalLink: viewModel.navigateToExternalLink(_:) ) + .padding(.bottom, 20) } } } diff --git a/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift b/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift index 9f71c7b55..61d23b00b 100644 --- a/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift +++ b/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift @@ -1,79 +1,71 @@ import SwiftUI struct GetAWalletView: View { - let wallets: [Listing] let onWalletTap: (Listing) -> Void let navigateToExternalLink: (URL) -> Void var body: some View { - VStack { - - List { - ForEach(wallets) { wallet in - Button { - onWalletTap(wallet) - } label: { - - HStack { - WalletImage(wallet: wallet) - .frame(width: 40, height: 40) + List { + ForEach(wallets, id: \.id) { wallet in + Button { + onWalletTap(wallet) + } label: { + HStack { + WalletImage(wallet: wallet) + .frame(width: 40, height: 40) - Text(wallet.name) - .font(.system(size: 16, weight: .medium)) - .padding(.horizontal) + Text(wallet.name) + .font(.system(size: 16, weight: .medium)) + .padding(.horizontal) - Spacer() + Spacer() - Image(systemName: "chevron.right") - .font(.system(.footnote).weight(.semibold)) - } + Image(systemName: "chevron.right") + .font(.system(.footnote).weight(.semibold)) } } } - .frame(minHeight: 400) - .listStyle(.plain) + } + .frame(minHeight: 400) + .listStyle(.plain) - VStack(alignment: .center, spacing: 8) { - - Text("Not what you’re looking for?") - .font( - .system(size: 16) + VStack(alignment: .center, spacing: 8) { + Text("Not what you’re looking for?") + .font( + .system(size: 16) .weight(.semibold) - ) - .multilineTextAlignment(.center) - .foregroundColor(.foreground1) + ) + .multilineTextAlignment(.center) + .foregroundColor(.foreground1) - Text("With hundreds of wallets out there, 
there’s something for everyone ") - .font( - .system(size: 14) + Text("With hundreds of wallets out there, 
there’s something for everyone ") + .font( + .system(size: 14) .weight(.medium) - ) - .foregroundColor(.foreground2) - .fixedSize(horizontal: false, vertical: true) + ) + .foregroundColor(.foreground2) + .fixedSize(horizontal: false, vertical: true) - Button(action: { - navigateToExternalLink(URL(string: "https://walletconnect.com/explorer?type=wallet")!) - }) { - HStack { - Text("Explore Wallets") - Image(.external_link) - } + Button(action: { + navigateToExternalLink(URL(string: "https://walletconnect.com/explorer?type=wallet")!) + }) { + HStack { + Text("Explore Wallets") + Image(.external_link) } - .buttonStyle(W3MButtonStyle()) } - .multilineTextAlignment(.center) - .padding(.horizontal, 0) - .padding(.top, 0) - .frame(maxHeight: .infinity, alignment: .bottom) + .buttonStyle(W3MButtonStyle()) } + .multilineTextAlignment(.center) + .padding(.horizontal, 0) + .padding(.top, 0) + .frame(maxHeight: .infinity, alignment: .bottom) } } struct GetAWalletView_Previews: PreviewProvider { - static var previews: some View { - GetAWalletView( wallets: Listing.stubList, onWalletTap: { _ in }, diff --git a/Sources/WalletConnectModal/Modal/Screens/WalletList.swift b/Sources/WalletConnectModal/Modal/Screens/WalletList.swift index 1e17ae6a9..d2bd5c6de 100644 --- a/Sources/WalletConnectModal/Modal/Screens/WalletList.swift +++ b/Sources/WalletConnectModal/Modal/Screens/WalletList.swift @@ -13,18 +13,18 @@ struct WalletList: View { @State var availableSize: CGSize = .zero var body: some View { - content() - .readSize { size in - if availableSize == size { - return + ZStack { + content() + .animation(.default) + .readSize { size in + if availableSize == size { + return + } + + numberOfColumns = Int(round(size.width / 100)) + availableSize = size } - - numberOfColumns = Int(round(size.width / 100)) - availableSize = size - - return - } - .id(numberOfColumns) + } } @ViewBuilder @@ -32,8 +32,10 @@ struct WalletList: View { switch destination { case .welcome: initialList() + .padding(.bottom, 20) case .viewAll: viewAll() + .frame(minHeight: 250) case let .walletDetail(wallet): walletDetail(wallet) default: @@ -47,15 +49,15 @@ struct WalletList: View { VStack { HStack { - ForEach(0.. numberOfColumns * 2 { viewAllItem() .transform { @@ -80,21 +82,21 @@ struct WalletList: View { @ViewBuilder private func viewAll() -> some View { ZStack { - Spacer().frame(height: 450) + Spacer().frame(maxWidth: .infinity, maxHeight: 150) ScrollView(.vertical) { VStack(alignment: .leading) { ForEach(Array(stride(from: 0, to: wallets.count, by: numberOfColumns)), id: \.self) { row in HStack { ForEach(row..<(row + numberOfColumns), id: \.self) { index in - if wallets.indices.contains(index) { - gridItem(for: index) + if let wallet = wallets[safe: index] { + gridItem(for: wallet) } } } } } - .padding(.top) + .padding(.vertical) } LinearGradient( @@ -157,49 +159,44 @@ struct WalletList: View { } @ViewBuilder - func gridItem(for index: Int) -> some View { - if let wallet = wallets[safe: index] { - VStack { - WalletImage(wallet: wallet) - .frame(width: 60, height: 60) - .cornerRadius(16) - .overlay( - RoundedRectangle(cornerRadius: 16) - .stroke(.gray.opacity(0.4), lineWidth: 1) - ) - - Text(wallet.name) - .font(.system(size: 12)) - .foregroundColor(.foreground1) - .padding(.horizontal, 12) - .multilineTextAlignment(.center) - .minimumScaleFactor(0.4) - - Text("RECENT") - .opacity(wallet.lastTimeUsed != nil ? 1 : 0) - .font(.system(size: 10)) - .foregroundColor(.foreground3) - .padding(.horizontal, 12) - } - .frame(maxWidth: 80, maxHeight: 96) - .transform { - #if os(iOS) - $0.onTapGesture { - withAnimation { - navigateTo(.walletDetail(wallet)) - } + func gridItem(for wallet: Listing) -> some View { + VStack { + WalletImage(wallet: wallet) + .frame(width: 60, height: 60) + .cornerRadius(16) + .overlay( + RoundedRectangle(cornerRadius: 16) + .stroke(.gray.opacity(0.4), lineWidth: 1) + ) + + Text(String(wallet.name.split(separator: " ").first!)) + .font(.system(size: 12)) + .foregroundColor(.foreground1) + .multilineTextAlignment(.center) + + Text("RECENT") + .opacity(wallet.lastTimeUsed != nil ? 1 : 0) + .font(.system(size: 10)) + .foregroundColor(.foreground3) + .padding(.horizontal, 12) + } + .frame(maxWidth: 80, maxHeight: 96) + .transform { + #if os(iOS) + $0.onTapGesture { + withAnimation { + navigateTo(.walletDetail(wallet)) } - #endif - } - } else { - EmptyView() + } + #endif } } private func walletDetail(_ wallet: Listing) -> some View { VStack(spacing: 8) { WalletImage(wallet: wallet, size: .large) - .frame(maxWidth: 96, maxHeight: 96) + .frame(width: 96, height: 96) + .minimumScaleFactor(0.4) Text("Continue in \(wallet.name)...") .font(.system(size: 16, weight: .medium)) @@ -218,11 +215,10 @@ struct WalletList: View { } } .buttonStyle(W3MButtonStyle()) - .padding() + .padding(.vertical) .opacity(retryButtonShown ? 1 : 0) .animation(.easeIn) } - .padding() .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { onListingTap(wallet) diff --git a/Sources/WalletConnectModal/Modal/Screens/WhatIsWalletView.swift b/Sources/WalletConnectModal/Modal/Screens/WhatIsWalletView.swift index 508530924..4ff8828bf 100644 --- a/Sources/WalletConnectModal/Modal/Screens/WhatIsWalletView.swift +++ b/Sources/WalletConnectModal/Modal/Screens/WhatIsWalletView.swift @@ -1,29 +1,40 @@ import SwiftUI struct WhatIsWalletView: View { - var navigateTo: (Destination) -> Void var navigateToExternalLink: (URL) -> Void - + + @Environment(\.verticalSizeClass) var verticalSizeClass + var body: some View { - - VStack(spacing: 10) { - HelpSection( - title: "A home for your digital assets", - description: "A wallet lets you store, send and receive digital assets like cryptocurrencies and NFTs.", - assets: [.DeFi, .NFT, .ETH] - ) - HelpSection( - title: "One login for all of web3", - description: "Log in to any app by connecting your wallet. Say goodbye to countless passwords!", - assets: [.Login, .Profile, .Lock] - ) - HelpSection( - title: "Your gateway to a new web", - description: "With your wallet, you can explore and interact with DeFi, NFTs, DAOs, and much more.", - assets: [.Browser, .Noun, .DAO] - ) - + content() + .onAppear { + UIPageControl.appearance().currentPageIndicatorTintColor = AssetColor.foreground1.uiColor + UIPageControl.appearance().pageIndicatorTintColor = AssetColor.foreground1.uiColor.withAlphaComponent(0.2) + } + } + + func content() -> some View { + VStack(spacing: 0) { + if verticalSizeClass == .compact { + TabView { + ForEach(sections(), id: \.title) { section in + section + .padding(.bottom, 40) + } + } + .tabViewStyle(.page(indexDisplayMode: .always)) + .frame(height: 150) + + } else { + VStack(spacing: 10) { + ForEach(sections(), id: \.title) { section in + section + } + } + .padding(.bottom) + } + HStack { Button(action: { navigateTo(.getWallet) @@ -46,14 +57,33 @@ struct WhatIsWalletView: View { } .padding(.horizontal, 24) } + + func sections() -> [HelpSection] { + [ + HelpSection( + title: "A home for your digital assets", + description: "A wallet lets you store, send and receive digital assets like cryptocurrencies and NFTs.", + assets: [.DeFi, .NFT, .ETH] + ), + HelpSection( + title: "One login for all of web3", + description: "Log in to any app by connecting your wallet. Say goodbye to countless passwords!", + assets: [.Login, .Profile, .Lock] + ), + HelpSection( + title: "Your gateway to a new web", + description: "With your wallet, you can explore and interact with DeFi, NFTs, DAOs, and much more.", + assets: [.Browser, .Noun, .DAO] + ) + ] + } } struct HelpSection: View { - let title: String let description: String let assets: [Asset] - + var body: some View { VStack { HStack { @@ -61,7 +91,7 @@ struct HelpSection: View { Image(asset) } } - + Text(title) .font(.system(size: 16)) .foregroundColor(.foreground1) @@ -79,9 +109,7 @@ struct HelpSection: View { } struct WhatIsWalletView_Previews: PreviewProvider { - static var previews: some View { - - WhatIsWalletView(navigateTo: { _ in}, navigateToExternalLink: { _ in }) + WhatIsWalletView(navigateTo: { _ in }, navigateToExternalLink: { _ in }) } } diff --git a/Sources/WalletConnectModal/UI/Common/Toast.swift b/Sources/WalletConnectModal/UI/Common/Toast.swift index e36475df7..44b9315d4 100644 --- a/Sources/WalletConnectModal/UI/Common/Toast.swift +++ b/Sources/WalletConnectModal/UI/Common/Toast.swift @@ -1,10 +1,10 @@ import SwiftUI struct Toast: Equatable { - var style: ToastStyle - var message: String - var duration: Double = 3 - var width: Double = .infinity + var style: ToastStyle + var message: String + var duration: Double = 3 + var width: Double = .infinity } enum ToastStyle { @@ -73,7 +73,8 @@ struct ToastModifier: ViewModifier { ZStack { mainToastView() .offset(y: -64) - }.animation(.spring(), value: toast) + } + .animation(.spring(), value: toast) ) .onChangeBackported(of: toast) { _ in showToast() @@ -90,7 +91,9 @@ struct ToastModifier: ViewModifier { ) { dismissToast() } + Spacer() + .allowsHitTesting(false) } } } From a9d48d1aa40bbb17c6097037469f4f633d13f087 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Wed, 12 Jul 2023 16:26:06 +0200 Subject: [PATCH 06/13] Fix modal background for landscape --- .../Modal/ModalContainerView.swift | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/Sources/WalletConnectModal/Modal/ModalContainerView.swift b/Sources/WalletConnectModal/Modal/ModalContainerView.swift index af30dd194..4e89e5d17 100644 --- a/Sources/WalletConnectModal/Modal/ModalContainerView.swift +++ b/Sources/WalletConnectModal/Modal/ModalContainerView.swift @@ -7,19 +7,7 @@ struct ModalContainerView: View { var body: some View { VStack(spacing: 0) { - - Color.thickOverlay - .colorScheme(.light) - .transform { - #if os(iOS) - $0.onTapGesture { - withAnimation { - showModal = false - } - } - #endif - } - .opacity(showModal ? 1 : 0) + Color.clear if showModal { ModalSheet( @@ -31,11 +19,22 @@ struct ModalContainerView: View { .environment(\.projectId, WalletConnectModal.config.projectId) .transition(.move(edge: .bottom)) .animation(.spring(), value: showModal) - .background(Color.thickOverlay - .colorScheme(.light).opacity(showModal ? 1 : 0)) } } - .background(Color.clear) + .background( + Color.thickOverlay + .colorScheme(.light) + .opacity(showModal ? 1 : 0) + .transform { + #if os(iOS) + $0.onTapGesture { + withAnimation { + showModal = false + } + } + #endif + } + ) .edgesIgnoringSafeArea(.all) .onChangeBackported(of: showModal, perform: { newValue in if newValue == false { From 3d2c179d36ff8c024588a00b1c633be46e99b38b Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Wed, 12 Jul 2023 16:32:25 +0200 Subject: [PATCH 07/13] Fix Get wallet view for landscape --- .../WalletConnectModal/Modal/ModalSheet.swift | 1 + .../Modal/Screens/GetAWalletView.swift | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Sources/WalletConnectModal/Modal/ModalSheet.swift b/Sources/WalletConnectModal/Modal/ModalSheet.swift index 21009b030..9e91ecb4a 100644 --- a/Sources/WalletConnectModal/Modal/ModalSheet.swift +++ b/Sources/WalletConnectModal/Modal/ModalSheet.swift @@ -149,6 +149,7 @@ public struct ModalSheet: View { onWalletTap: viewModel.onGetWalletTap(_:), navigateToExternalLink: viewModel.navigateToExternalLink(_:) ) + .frame(minHeight: isLandscape ? 200 : 550) .padding(.bottom, 20) } } diff --git a/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift b/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift index 61d23b00b..b6f01d9f0 100644 --- a/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift +++ b/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift @@ -6,14 +6,15 @@ struct GetAWalletView: View { let navigateToExternalLink: (URL) -> Void var body: some View { - List { - ForEach(wallets, id: \.id) { wallet in - Button { - onWalletTap(wallet) - } label: { - HStack { - WalletImage(wallet: wallet) - .frame(width: 40, height: 40) + ScrollView { + List { + ForEach(wallets, id: \.id) { wallet in + Button { + onWalletTap(wallet) + } label: { + HStack { + WalletImage(wallet: wallet) + .frame(width: 40, height: 40) Text(wallet.name) .font(.system(size: 16, weight: .medium)) @@ -30,10 +31,11 @@ struct GetAWalletView: View { .frame(minHeight: 400) .listStyle(.plain) - VStack(alignment: .center, spacing: 8) { - Text("Not what you’re looking for?") - .font( - .system(size: 16) + + VStack(alignment: .center, spacing: 8) { + Text("Not what you’re looking for?") + .font( + .system(size: 16) .weight(.semibold) ) .multilineTextAlignment(.center) From 4d2e08c163fd009c0b80a1d1a9c88f5f9592d704 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Thu, 13 Jul 2023 17:13:17 +0200 Subject: [PATCH 08/13] Fix viewAll item --- .../Modal/Screens/WalletList.swift | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Sources/WalletConnectModal/Modal/Screens/WalletList.swift b/Sources/WalletConnectModal/Modal/Screens/WalletList.swift index d2bd5c6de..94b00c7fa 100644 --- a/Sources/WalletConnectModal/Modal/Screens/WalletList.swift +++ b/Sources/WalletConnectModal/Modal/Screens/WalletList.swift @@ -118,20 +118,22 @@ struct WalletList: View { func viewAllItem() -> some View { VStack { VStack(spacing: 3) { - let startingIndex = (2 * numberOfColumns - 1) + let viewAllWalletsFirstRow = wallets.dropFirst(2 * numberOfColumns - 1).prefix(2) HStack(spacing: 3) { - ForEach(startingIndex..<(startingIndex + 2)) { index in - WalletImage(wallet: wallets[safe: index]) + ForEach(viewAllWalletsFirstRow) { wallet in + WalletImage(wallet: wallet) .cornerRadius(8) .aspectRatio(1, contentMode: .fit) } } .padding(.horizontal, 5) + let viewAllWalletsSecondRow = wallets.dropFirst(2 * numberOfColumns + 1).prefix(2) + HStack(spacing: 3) { - ForEach((startingIndex + 2)..<(startingIndex + 4)) { index in - WalletImage(wallet: wallets[safe: index]) + ForEach(viewAllWalletsSecondRow) { wallet in + WalletImage(wallet: wallet) .cornerRadius(8) .aspectRatio(1, contentMode: .fit) } From 9335b04ef75348d4a052c77163e34b778bef0cff Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Fri, 14 Jul 2023 17:52:45 +0200 Subject: [PATCH 09/13] Use correct design for textfield + keyboard avoidance --- .../Modal/ModalContainerView.swift | 1 + .../WalletConnectModal/Modal/ModalSheet.swift | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Sources/WalletConnectModal/Modal/ModalContainerView.swift b/Sources/WalletConnectModal/Modal/ModalContainerView.swift index 4e89e5d17..4d96e386e 100644 --- a/Sources/WalletConnectModal/Modal/ModalContainerView.swift +++ b/Sources/WalletConnectModal/Modal/ModalContainerView.swift @@ -36,6 +36,7 @@ struct ModalContainerView: View { } ) .edgesIgnoringSafeArea(.all) + .ignoresSafeArea(.keyboard, edges: .bottom) .onChangeBackported(of: showModal, perform: { newValue in if newValue == false { withAnimation { diff --git a/Sources/WalletConnectModal/Modal/ModalSheet.swift b/Sources/WalletConnectModal/Modal/ModalSheet.swift index 9e91ecb4a..0e8baf9cf 100644 --- a/Sources/WalletConnectModal/Modal/ModalSheet.swift +++ b/Sources/WalletConnectModal/Modal/ModalSheet.swift @@ -5,6 +5,8 @@ public struct ModalSheet: View { @Environment(\.verticalSizeClass) var verticalSizeClass + @State var searchEditing = false + var isLandscape: Bool { verticalSizeClass == .compact } @@ -16,6 +18,7 @@ public struct ModalSheet: View { VStack(spacing: 0) { contentHeader() content() + } .frame(maxWidth: .infinity) .background(Color.background1) @@ -59,7 +62,7 @@ public struct ModalSheet: View { .padding(.trailing, 10) } .foregroundColor(Color.foreground1) - .frame(height: 44) + .frame(height: 48) } private func contentHeader() -> some View { @@ -81,18 +84,35 @@ public struct ModalSheet: View { } .animation(.default) .foregroundColor(.accent) - .frame(height: 50) + .frame(height: 60) .overlay( VStack { if viewModel.destination.hasSearch { - TextField("Search", text: $viewModel.searchTerm) - .transform { - #if os(iOS) - $0.textFieldStyle(.roundedBorder) - .autocapitalization(.none) - #endif - } - .padding(.horizontal, 50) + + HStack { + Image(systemName: "magnifyingglass") + TextField("Search", text: $viewModel.searchTerm, onEditingChanged: { editing in + self.searchEditing = editing + }) + } + .padding(.vertical, 4) + .padding(.horizontal, 10) + .background(Color.background3) + .foregroundColor(searchEditing ? .foreground1 : .foreground3) + .cornerRadius(28) + .overlay( + RoundedRectangle(cornerRadius: 28) + .stroke(searchEditing ? Color.accent : Color.thinOverlay, lineWidth: 1) + ) + .onDisappear { + searchEditing = false + } + .transform { + #if os(iOS) + $0.autocapitalization(.none) + #endif + } + .padding(.horizontal, 50) } else { Text(viewModel.destination.contentTitle) .font(.system(size: 20).weight(.semibold)) From 43225983c0d360cb06888a4b5c16b4dfbbff4191 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Thu, 20 Jul 2023 14:21:04 +0200 Subject: [PATCH 10/13] Fix subsequent run of the test --- Tests/WalletConnectModalTests/ModalViewModelTests.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Tests/WalletConnectModalTests/ModalViewModelTests.swift b/Tests/WalletConnectModalTests/ModalViewModelTests.swift index 73922b94e..afe61b693 100644 --- a/Tests/WalletConnectModalTests/ModalViewModelTests.swift +++ b/Tests/WalletConnectModalTests/ModalViewModelTests.swift @@ -49,10 +49,8 @@ final class ModalViewModelTests: XCTestCase { XCTAssertEqual(sut.uri, "wc:foo@2?symKey=bar&relay-protocol=irn") XCTAssertEqual(sut.wallets.count, 2) - XCTAssertEqual(sut.wallets, [ - Listing(id: "1", name: "Sample App", homepage: "https://example.com", order: 1, imageId: "1", app: Listing.App(ios: "https://example.com/download-ios", mac: "https://example.com/download-mac", safari: "https://example.com/download-safari"), mobile: Listing.Mobile(native: nil, universal: "https://example.com/universal")), - Listing(id: "2", name: "Awesome App", homepage: "https://example.com/awesome", order: 2, imageId: "2", app: Listing.App(ios: "https://example.com/download-ios", mac: "https://example.com/download-mac", safari: "https://example.com/download-safari"), mobile: Listing.Mobile(native: "awesomeapp://deeplink", universal: "https://awesome.com/awesome/universal")), - ]) + XCTAssertEqual(sut.wallets.map(\.id), ["1", "2"]) + XCTAssertEqual(sut.wallets.map(\.name), ["Sample App", "Awesome App"]) expectation = XCTestExpectation(description: "Wait for openUrl to be called") From e1a69648335f5d3852735793b8bdf10859f68201 Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Thu, 20 Jul 2023 15:03:59 +0200 Subject: [PATCH 11/13] PR feedback --- .../WalletConnectModal/Modal/ModalSheet.swift | 12 ++-- .../Modal/ModalViewModel.swift | 2 +- .../Modal/Screens/GetAWalletView.swift | 59 ++++++++++--------- .../Modal/Screens/WhatIsWalletView.swift | 6 +- .../WalletConnectModal/UI/Common/Toast.swift | 4 +- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/Sources/WalletConnectModal/Modal/ModalSheet.swift b/Sources/WalletConnectModal/Modal/ModalSheet.swift index 0e8baf9cf..0d0f0b239 100644 --- a/Sources/WalletConnectModal/Modal/ModalSheet.swift +++ b/Sources/WalletConnectModal/Modal/ModalSheet.swift @@ -94,6 +94,13 @@ public struct ModalSheet: View { TextField("Search", text: $viewModel.searchTerm, onEditingChanged: { editing in self.searchEditing = editing }) + .transform { view in + #if os(macOS) + view + #else + view.autocapitalization(.none) + #endif + } } .padding(.vertical, 4) .padding(.horizontal, 10) @@ -107,11 +114,6 @@ public struct ModalSheet: View { .onDisappear { searchEditing = false } - .transform { - #if os(iOS) - $0.autocapitalization(.none) - #endif - } .padding(.horizontal, 50) } else { Text(viewModel.destination.contentTitle) diff --git a/Sources/WalletConnectModal/Modal/ModalViewModel.swift b/Sources/WalletConnectModal/Modal/ModalViewModel.swift index 86d63acca..fd59956f4 100644 --- a/Sources/WalletConnectModal/Modal/ModalViewModel.swift +++ b/Sources/WalletConnectModal/Modal/ModalViewModel.swift @@ -79,7 +79,7 @@ final class ModalViewModel: ObservableObject { .sink { sessions in print(sessions) isShown.wrappedValue = false - self.toast = Toast(style: .success, message: "Session estabilished", duration: 15) + self.toast = Toast(style: .success, message: "Session estabilished", duration: 5) } .store(in: &disposeBag) diff --git a/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift b/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift index b6f01d9f0..323a4ddb5 100644 --- a/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift +++ b/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift @@ -16,20 +16,20 @@ struct GetAWalletView: View { WalletImage(wallet: wallet) .frame(width: 40, height: 40) - Text(wallet.name) - .font(.system(size: 16, weight: .medium)) - .padding(.horizontal) + Text(wallet.name) + .font(.system(size: 16, weight: .medium)) + .padding(.horizontal) - Spacer() + Spacer() - Image(systemName: "chevron.right") - .font(.system(.footnote).weight(.semibold)) + Image(systemName: "chevron.right") + .font(.system(.footnote).weight(.semibold)) + } } } } - } - .frame(minHeight: 400) - .listStyle(.plain) + .frame(minHeight: 400) + .listStyle(.plain) VStack(alignment: .center, spacing: 8) { @@ -37,32 +37,33 @@ struct GetAWalletView: View { .font( .system(size: 16) .weight(.semibold) - ) - .multilineTextAlignment(.center) - .foregroundColor(.foreground1) + ) + .multilineTextAlignment(.center) + .foregroundColor(.foreground1) - Text("With hundreds of wallets out there, 
there’s something for everyone ") - .font( - .system(size: 14) + Text("With hundreds of wallets out there, 
there’s something for everyone ") + .font( + .system(size: 14) .weight(.medium) - ) - .foregroundColor(.foreground2) - .fixedSize(horizontal: false, vertical: true) + ) + .foregroundColor(.foreground2) + .fixedSize(horizontal: false, vertical: true) - Button(action: { - navigateToExternalLink(URL(string: "https://walletconnect.com/explorer?type=wallet")!) - }) { - HStack { - Text("Explore Wallets") - Image(.external_link) + Button(action: { + navigateToExternalLink(URL(string: "https://walletconnect.com/explorer?type=wallet")!) + }) { + HStack { + Text("Explore Wallets") + Image(.external_link) + } } + .buttonStyle(W3MButtonStyle()) } - .buttonStyle(W3MButtonStyle()) + .multilineTextAlignment(.center) + .padding(.horizontal, 0) + .padding(.top, 0) + .frame(maxHeight: .infinity, alignment: .bottom) } - .multilineTextAlignment(.center) - .padding(.horizontal, 0) - .padding(.top, 0) - .frame(maxHeight: .infinity, alignment: .bottom) } } diff --git a/Sources/WalletConnectModal/Modal/Screens/WhatIsWalletView.swift b/Sources/WalletConnectModal/Modal/Screens/WhatIsWalletView.swift index 4ff8828bf..1a2e1fec5 100644 --- a/Sources/WalletConnectModal/Modal/Screens/WhatIsWalletView.swift +++ b/Sources/WalletConnectModal/Modal/Screens/WhatIsWalletView.swift @@ -23,7 +23,11 @@ struct WhatIsWalletView: View { .padding(.bottom, 40) } } - .tabViewStyle(.page(indexDisplayMode: .always)) + .transform { + #if os(iOS) + $0.tabViewStyle(.page(indexDisplayMode: .always)) + #endif + } .frame(height: 150) } else { diff --git a/Sources/WalletConnectModal/UI/Common/Toast.swift b/Sources/WalletConnectModal/UI/Common/Toast.swift index 44b9315d4..f8e276a25 100644 --- a/Sources/WalletConnectModal/UI/Common/Toast.swift +++ b/Sources/WalletConnectModal/UI/Common/Toast.swift @@ -1,8 +1,8 @@ import SwiftUI struct Toast: Equatable { - var style: ToastStyle - var message: String + let style: ToastStyle + let message: String var duration: Double = 3 var width: Double = .infinity } From 63b87889d056251e017f3f54b603c9253d222473 Mon Sep 17 00:00:00 2001 From: flypaper0 Date: Mon, 24 Jul 2023 15:00:07 +0000 Subject: [PATCH 12/13] Set User Agent --- Sources/WalletConnectRelay/PackageConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index 94c279119..e0c9c2edf 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.6.13"} +{"version": "1.6.14"} From 064fba653b8a3b708baf2355d0776cd986d1675c Mon Sep 17 00:00:00 2001 From: Radek Novak Date: Mon, 24 Jul 2023 17:11:29 +0200 Subject: [PATCH 13/13] Fix build error for release --- .../WalletConnectModal/Modal/Screens/GetAWalletView.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift b/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift index 323a4ddb5..084e18c55 100644 --- a/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift +++ b/Sources/WalletConnectModal/Modal/Screens/GetAWalletView.swift @@ -41,7 +41,7 @@ struct GetAWalletView: View { .multilineTextAlignment(.center) .foregroundColor(.foreground1) - Text("With hundreds of wallets out there, 
there’s something for everyone ") + Text("With hundreds of wallets out there, there’s something for everyone ") .font( .system(size: 14) .weight(.medium) @@ -67,6 +67,8 @@ struct GetAWalletView: View { } } +#if DEBUG + struct GetAWalletView_Previews: PreviewProvider { static var previews: some View { GetAWalletView( @@ -77,3 +79,5 @@ struct GetAWalletView_Previews: PreviewProvider { .environment(\.projectId, Secrets.load().projectID) } } + +#endif