Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add search functionality #18 #42

Merged
merged 52 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
610cbb1
Refactored ListItemView, added previews with different states
gemdev111 Jun 5, 2024
13a36fd
moved ListItemProgressView from ListItemView, since it has more then …
gemdev111 Jun 5, 2024
876e0fc
Project strucuture update
gemdev111 Jun 6, 2024
5a75224
Merge branch 'main'
gemdev111 Jun 6, 2024
167005d
fixed pbroj, unlinked files after vsc
gemdev111 Jun 6, 2024
21613a2
Search added for netwroks #18(NetworkSelectorNavigationStack) TODO: -…
gemdev111 Jun 6, 2024
45463e9
added preview for NetworkSelectorNavigationStack
gemdev111 Jun 6, 2024
9e7bdf9
empty view updated, empty view applied to networkselector empty view
gemdev111 Jun 6, 2024
17dc9cd
empty view added as overlay
gemdev111 Jun 6, 2024
e979bb7
updated state view to support exisiting empty view
gemdev111 Jun 6, 2024
810a60b
removed comments, removed hardcodede localizable
gemdev111 Jun 6, 2024
3a0d93a
localisation updates
gemdev111 Jun 6, 2024
a7e6f97
added no chains found
gemdev111 Jun 6, 2024
42593d2
refactor->rename StateEmptyView.message to title
gemdev111 Jun 6, 2024
d84c995
State empty view updates
gemdev111 Jun 6, 2024
2077f9b
Localisation updates
gemdev111 Jun 6, 2024
d8f2610
ImportWalletType scene updated EmptyState title
gemdev111 Jun 6, 2024
6a32cb6
Added search to ChainListSettingsScene
gemdev111 Jun 6, 2024
3e9f611
added constraint if no image and no description change title font to …
gemdev111 Jun 6, 2024
7fbfd61
Merge branch 'main'
gemdev111 Jun 7, 2024
932c2ab
Merge branch 'main'
gemdev111 Jun 7, 2024
76404c6
removed comments
gemdev111 Jun 7, 2024
353f560
Refactored ListItemView & Text Style added defaults value
gemdev111 Jun 7, 2024
a8e722b
added note to TextStyle
gemdev111 Jun 7, 2024
bd90f00
Updated Gem dependency with style init
gemdev111 Jun 7, 2024
7d9d847
Project structure update
gemdev111 Jun 7, 2024
2f75605
Moved System Image to Styles
gemdev111 Jun 7, 2024
88e96d6
Merge branch 'refactor-listItemView'
gemdev111 Jun 7, 2024
483bbf9
State emptyview updated with TextValueViw
gemdev111 Jun 7, 2024
8034560
added necessary import
gemdev111 Jun 7, 2024
f2c0e48
added necessary import, updated State empty view with new Style
gemdev111 Jun 7, 2024
8c0103d
Updated init StateEmptyView
gemdev111 Jun 7, 2024
b38e34d
updated default init of State Empty view with TextStyle static values
gemdev111 Jun 7, 2024
f947dd9
updated initi method with clearer init, added missed values to init
gemdev111 Jun 7, 2024
8131fc7
Merge branch 'refactor-listItemView'
gemdev111 Jun 7, 2024
f94462b
Update localize
gemcoder21 Jun 8, 2024
231279f
Updated preview mock chain
gemdev111 Jun 9, 2024
8cffbad
if let changed to a new flow
gemdev111 Jun 9, 2024
e3e8b1b
Merge branch 'main'
gemdev111 Jun 9, 2024
833103b
apr text style updated to correct callout
gemdev111 Jun 9, 2024
c5c00a4
Merge remote-tracking branch 'refs/remotes/origin/add-search-function…
gemdev111 Jun 9, 2024
c30cbf9
Merge branch 'main'
gemdev111 Jun 10, 2024
85ecfd2
Merge branch 'refactor-listItemView'
gemdev111 Jun 10, 2024
dfb00a3
Merge branch 'main' into add-search-functionality
gemdev111 Jun 10, 2024
9708d26
Asset Configuration re-indention
gemdev111 Jun 10, 2024
aca866e
Added SearchableListView to components
gemdev111 Jun 10, 2024
67e0b5b
Updates ChainListSettingsScene to support new search
gemdev111 Jun 10, 2024
7ff3dd8
Updates to search flow
gemdev111 Jun 10, 2024
4741772
Merge branch 'main'
gemdev111 Jun 10, 2024
9e674ef
refactoring ChainFilterable
gemdev111 Jun 10, 2024
8943eae
Updates AddTokenScene
gemdev111 Jun 10, 2024
677feef
ChainListSettingsScene fixed var -> let
gemdev111 Jun 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions Gem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
481152C82B566AC200377C75 /* Gemstone in Frameworks */ = {isa = PBXBuildFile; productRef = 481152C72B566AC200377C75 /* Gemstone */; };
832553982C0F27F000C9CA0C /* ChartScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832553972C0F27F000C9CA0C /* ChartScene.swift */; };
8325539A2C0FB11C00C9CA0C /* CurrencySceneViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832553992C0FB11C00C9CA0C /* CurrencySceneViewModelTests.swift */; };
83D824022C173FAF0023CA0C /* NetworkSelectorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D824012C173FAF0023CA0C /* NetworkSelectorViewModel.swift */; };
83D824042C176A0D0023CA0C /* ChainFilterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D824032C176A0D0023CA0C /* ChainFilterable.swift */; };
C30952B4299C39D70004C0F9 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C30952B3299C39D70004C0F9 /* App.swift */; };
C30952B8299C39D80004C0F9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C30952B7299C39D80004C0F9 /* Images.xcassets */; };
C30952C6299C39D80004C0F9 /* walletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C30952C5299C39D80004C0F9 /* walletTests.swift */; };
C313D2EC2A8356C2004520BB /* ChainNodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C313D2EB2A8356C2004520BB /* ChainNodeViewModel.swift */; };
C3340B722A4A292100884D48 /* ChainServiceFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3340B712A4A292100884D48 /* ChainServiceFactory.swift */; };
C3340B742A4A920100884D48 /* ChainListSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3340B732A4A920100884D48 /* ChainListSettings.swift */; };
C3340B762A4A92B000884D48 /* ChainSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3340B752A4A92B000884D48 /* ChainSettings.swift */; };
C3340B742A4A920100884D48 /* ChainListSettingsScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3340B732A4A920100884D48 /* ChainListSettingsScene.swift */; };
C3340B762A4A92B000884D48 /* ChainSettingsScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3340B752A4A92B000884D48 /* ChainSettingsScene.swift */; };
C3340B782A4A985000884D48 /* ChainSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3340B772A4A985000884D48 /* ChainSettingsViewModel.swift */; };
C3340B7A2A4A989E00884D48 /* ChainListSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3340B792A4A989E00884D48 /* ChainListSettingsViewModel.swift */; };
C34C7CF329FC920D009EEC21 /* DeveloperScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = C34C7CF229FC920D009EEC21 /* DeveloperScene.swift */; };
Expand Down Expand Up @@ -252,6 +254,8 @@
487A27E82B632CCE00BEEADB /* Gemstone */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Gemstone; sourceTree = "<group>"; };
832553972C0F27F000C9CA0C /* ChartScene.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartScene.swift; sourceTree = "<group>"; };
832553992C0FB11C00C9CA0C /* CurrencySceneViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencySceneViewModelTests.swift; sourceTree = "<group>"; };
83D824012C173FAF0023CA0C /* NetworkSelectorViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSelectorViewModel.swift; sourceTree = "<group>"; };
83D824032C176A0D0023CA0C /* ChainFilterable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainFilterable.swift; sourceTree = "<group>"; };
C30952B0299C39D70004C0F9 /* Gem.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Gem.app; sourceTree = BUILT_PRODUCTS_DIR; };
C30952B3299C39D70004C0F9 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
C30952B7299C39D80004C0F9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
Expand All @@ -260,8 +264,8 @@
C30952C5299C39D80004C0F9 /* walletTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = walletTests.swift; sourceTree = "<group>"; };
C313D2EB2A8356C2004520BB /* ChainNodeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainNodeViewModel.swift; sourceTree = "<group>"; };
C3340B712A4A292100884D48 /* ChainServiceFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainServiceFactory.swift; sourceTree = "<group>"; };
C3340B732A4A920100884D48 /* ChainListSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainListSettings.swift; sourceTree = "<group>"; };
C3340B752A4A92B000884D48 /* ChainSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainSettings.swift; sourceTree = "<group>"; };
C3340B732A4A920100884D48 /* ChainListSettingsScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainListSettingsScene.swift; sourceTree = "<group>"; };
C3340B752A4A92B000884D48 /* ChainSettingsScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainSettingsScene.swift; sourceTree = "<group>"; };
C3340B772A4A985000884D48 /* ChainSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainSettingsViewModel.swift; sourceTree = "<group>"; };
C3340B792A4A989E00884D48 /* ChainListSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChainListSettingsViewModel.swift; sourceTree = "<group>"; };
C34C7CF229FC920D009EEC21 /* DeveloperScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperScene.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -654,6 +658,7 @@
isa = PBXGroup;
children = (
C34C7D122A006E9F009EEC21 /* SecretPhraseViewableModel.swift */,
83D824032C176A0D0023CA0C /* ChainFilterable.swift */,
);
path = Protocols;
sourceTree = "<group>";
Expand Down Expand Up @@ -845,8 +850,8 @@
C3CF3B8029AAF87400E96586 /* SettingsScene.swift */,
C34C7CF229FC920D009EEC21 /* DeveloperScene.swift */,
C3D1C4DC2A295AEB006E8EEA /* AboutUsScene.swift */,
C3340B732A4A920100884D48 /* ChainListSettings.swift */,
C3340B752A4A92B000884D48 /* ChainSettings.swift */,
C3340B732A4A920100884D48 /* ChainListSettingsScene.swift */,
C3340B752A4A92B000884D48 /* ChainSettingsScene.swift */,
C3E99BCA2A76C8E8005DF35F /* CurrencyScene.swift */,
C3E99BD22A7A1D2E005DF35F /* SecurityScene.swift */,
D8B977FF2AAA4B97009933B5 /* NotificationsScene.swift */,
Expand Down Expand Up @@ -937,6 +942,7 @@
C313D2EB2A8356C2004520BB /* ChainNodeViewModel.swift */,
D8B978012AAA4C28009933B5 /* NotificationsViewModel.swift */,
D85620262AAAAD9A00D65D81 /* DeveloperViewModel.swift */,
83D824012C173FAF0023CA0C /* NetworkSelectorViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
Expand Down Expand Up @@ -1811,7 +1817,7 @@
C3D1C4E52A2D4EEF006E8EEA /* ShowSecretPhraseViewModel.swift in Sources */,
C3FB44892A7AD98B0056F124 /* NameService.swift in Sources */,
D8BAC9592BD0CDC8001608BC /* SelectAssetSceneNavigationStack.swift in Sources */,
C3340B742A4A920100884D48 /* ChainListSettings.swift in Sources */,
C3340B742A4A920100884D48 /* ChainListSettingsScene.swift in Sources */,
D8148C192BE2C65E0066586A /* VerifyPhraseWalletScene.swift in Sources */,
D848B59C2AE5CDF700814FB4 /* ConnectionView.swift in Sources */,
C3FA949429D7EEF000BFC23D /* WalletType.swift in Sources */,
Expand Down Expand Up @@ -1871,7 +1877,7 @@
D848B59A2AE5CDD700814FB4 /* WalletConnectionViewModel.swift in Sources */,
C3340B782A4A985000884D48 /* ChainSettingsViewModel.swift in Sources */,
C3CF3BBE29B7C00E00E96586 /* ReceiveScene.swift in Sources */,
C3340B762A4A92B000884D48 /* ChainSettings.swift in Sources */,
C3340B762A4A92B000884D48 /* ChainSettingsScene.swift in Sources */,
C3D1C5072A43AB11006E8EEA /* TransactionsViewModel.swift in Sources */,
C366790A2A0B7E5800F1D74D /* Environment.swift in Sources */,
D848B5932AE3024200814FB4 /* ConnectionsService.swift in Sources */,
Expand All @@ -1898,7 +1904,9 @@
C3549B3F29C51AA000B4BE01 /* PriceViewModel.swift in Sources */,
D89D69012A9D769400EA74EE /* TransactionsList.swift in Sources */,
C3D1C4FC2A3CD5EA006E8EEA /* DiscoverAssetsService.swift in Sources */,
83D824022C173FAF0023CA0C /* NetworkSelectorViewModel.swift in Sources */,
D8D203C02ACE058200261CA2 /* ChartValuesViewModel.swift in Sources */,
83D824042C176A0D0023CA0C /* ChainFilterable.swift in Sources */,
D8148C2D2BF54FB80066586A /* MemoListItem.swift in Sources */,
832553982C0F27F000C9CA0C /* ChartScene.swift in Sources */,
C3D1C4FE2A421842006E8EEA /* NodeService.swift in Sources */,
Expand Down
110 changes: 67 additions & 43 deletions Gem/Assets/Scenes/AddTokenScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import Blockchain
import Style

struct AddTokenScene: View {

@StateObject var model: AddTokenViewModel
@State var chain: Chain = .ethereum
@State private var address: String = ""

@State private var isPresentingScanner = false
@State private var isPresentingSelectNetwork = false
@State private var isPresentingErrorMessage: String?
@FocusState private var focusedField: Field?

var action: ((Asset) -> Void)?

enum Field: Int, Hashable {
Expand All @@ -25,27 +24,25 @@ struct AddTokenScene: View {
var body: some View {
VStack {
List {
Section(Localized.Transfer.network) {
NavigationCustomLink(with: ChainView(chain: chain)) {
isPresentingSelectNetwork = true
if let chain = model.chain {
Section(Localized.Transfer.network) {
NavigationCustomLink(with: ChainView(chain: chain), action: onSelectChain)
}
}
Section {
VStack {
HStack {
FloatTextField(Localized.Wallet.Import.contractAddressField, text: $address)
FloatTextField(Localized.Wallet.Import.contractAddressField, text: $model.address)
.textFieldStyle(.plain)
.focused($focusedField, equals: .address)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.submitLabel(.search)
.onSubmit {
Task { try await addToken(tokenId: address) }
}
.onSubmit(onSubmitAddress)
Spacer()
HStack(spacing: Spacing.medium) {
ListButton(image: Image(systemName: SystemImage.paste), action: paste)
ListButton(image: Image(systemName: SystemImage.qrCode), action: scan)
ListButton(image: Image(systemName: SystemImage.paste), action: onSelectPaste)
ListButton(image: Image(systemName: SystemImage.qrCode), action: onSelectScan)
}
}
}
Expand Down Expand Up @@ -78,7 +75,7 @@ struct AddTokenScene: View {
}
}
Spacer()
Button(Localized.Wallet.Import.action, action: importToken)
Button(Localized.Wallet.Import.action, action: onSelectImportToken)
.disabled(model.state.isLoading)
.frame(maxWidth: Spacing.scene.button.maxWidth)
.buttonStyle(BlueButton())
Expand All @@ -87,58 +84,85 @@ struct AddTokenScene: View {
.background(Colors.grayBackground)
.navigationTitle(model.title)
.sheet(isPresented: $isPresentingScanner) {
ScanQRCodeNavigationStack(isPresenting: $isPresentingScanner) {
scanResult($0)
}
ScanQRCodeNavigationStack(isPresenting: $isPresentingScanner, action: onScanFinished(_:))
}
.sheet(isPresented: $isPresentingSelectNetwork) {
NetworkSelectorNavigationStack(chains: model.chains, selectedChain: $chain, isPresenting: $isPresentingSelectNetwork) { _ in

if let chain = model.chain {
NetworkSelectorNavigationStack(
model: NetworkSelectorViewModel(chains: model.chains, selectedChain: chain),
isPresenting: $isPresentingSelectNetwork,
onSelectChain: onSelectNewChain(_:)
)
}
}
.taskOnce {
//This is a hack, in the future observer from model.chain
self.chain = model.defaultChain
}
.taskOnce(onTaskOnce)
.alert(item: $isPresentingErrorMessage) {
Alert(title: Text(""), message: Text($0))
}
}

func paste() {
guard let content = UIPasteboard.general.string else {
return
}
address = content
Task {
try await addToken(tokenId: content)
}
}

func scan() {
isPresentingScanner = true
}

func addToken(tokenId: String) async throws {
try await model.fetch(chain: chain, tokenId: tokenId)
}

// MARK: - Actions

extension AddTokenScene {
private func onTaskOnce() {
model.chain = model.defaultChain
}
func importToken() {

private func onSelectImportToken() {
switch model.state {
case .loaded(let asset):
action?(asset.asset)
default:
break
}
}

func scanResult(_ result: String) {

private func onSelectChain() {
isPresentingSelectNetwork = true
}

private func onSelectScan() {
isPresentingScanner = true
}

private func onSubmitAddress() {
Task {
try await addToken(tokenId: model.address)
}
}

private func onSelectPaste() {
guard let content = UIPasteboard.general.string else {
return
}
model.address = content
Task {
try await addToken(tokenId: content)
}
}

private func onScanFinished(_ result: String) {
Task {
try await addToken(tokenId: result)
}
}

private func onSelectNewChain(_ chain: Chain) {
model.chain = chain
}
}

// MARK: - Data Fetching

extension AddTokenScene {
private func addToken(tokenId: String) async throws {
try await model.fetch(tokenId: tokenId)
}
}

// MARK: - LocalizedError

extension TokenValidationError: LocalizedError {
public var errorDescription: String? {
switch self {
Expand Down
39 changes: 23 additions & 16 deletions Gem/Assets/ViewModels/AddTokenViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,33 @@ import Settings
import Components

class AddTokenViewModel: ObservableObject {

let wallet: Wallet
let service: AddTokenService

@Published var state: StateViewType<AddAssetViewModel> = .noData

@Published var address: String
@Published var chain: Chain?

init(
wallet: Wallet,
service: AddTokenService
service: AddTokenService,
address: String = "",
chain: Chain? = nil
) {
self.service = service
self.wallet = wallet
self.address = address
self.chain = chain
}

var title: String {
Localized.Wallet.AddToken.title
}
var defaultChain: Chain {
chains.first!

var defaultChain: Chain? {
chains.first
}

var chains: [Chain] {
AssetConfiguration.supportedChainsWithTokens
.asSet()
Expand All @@ -38,17 +43,19 @@ class AddTokenViewModel: ObservableObject {
AssetScore.defaultRank(chain: chain1) > AssetScore.defaultRank(chain: chain2)
}
}

func getTokenData(chain: Chain, tokenId: String) async throws -> Asset {
try await service.getTokenData(chain: chain, tokenId: tokenId)
}

func fetch(chain: Chain, tokenId: String) async throws {
}

// MARK: - State Logic

extension AddTokenViewModel {
func fetch(tokenId: String) async throws {
guard let chain = chain else { return }

DispatchQueue.main.async {
self.state = .loading
}
do {
let asset = try await getTokenData(chain: chain, tokenId: tokenId)
let asset = try await service.getTokenData(chain: chain, tokenId: tokenId)
DispatchQueue.main.async {
self.state = .loaded(AddAssetViewModel(asset: asset))
}
Expand Down
2 changes: 1 addition & 1 deletion Gem/Charts/Scenes/ChartScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct ChartScene: View {
VStack {
switch model.state {
case .noData:
StateEmptyView(message: Localized.Common.notAvailable)
StateEmptyView(title: Localized.Common.notAvailable)
case .loading:
StateLoadingView()
case .loaded(let model):
Expand Down
2 changes: 1 addition & 1 deletion Gem/Connections/Scenes/ConnectionsScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ struct ConnectionsScene: View {
)
}
if headers.isEmpty {
StateEmptyView(message: Localized.WalletConnect.noActiveConnections)
StateEmptyView(title: Localized.WalletConnect.noActiveConnections)
} else {
ForEach(headers, id: \.self) { header in
Section(
Expand Down
8 changes: 8 additions & 0 deletions Gem/Onboarding/Navigation/ImportWalletNavigationStack.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c). Gem Wallet. All rights reserved.

import SwiftUI
import Primitives

struct ImportWalletNavigationStack: View {

Expand All @@ -27,3 +28,10 @@ struct ImportWalletNavigationStack: View {
}
}
}

// MARK: - Previews

#Preview {
@State var isPresenting: Bool = false
return ImportWalletNavigationStack(isPresenting: $isPresenting)
}
Loading
Loading