Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,11 @@ import Resources
public extension View {
func blurBackground() -> some View {
self
.padding([.top, .bottom], 10)
.padding([.leading, .trailing], 20)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.ultraThinMaterial, in: Rectangle())
}

func blurRoundedBackground(cornerRadius: CGFloat) -> some View {
self
.padding([.top, .bottom], 10)
.padding([.leading, .trailing], 20)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: cornerRadius))
}
}
201 changes: 64 additions & 137 deletions Surcharges/PresentationLayer/UIs/Main/Sources/MainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ public struct MainView<VM: MainViewModelProtocol, Router: MainRouterProtocol, Ad
@StateObject private var _adsService: AdsService = AdsService()

private let _surchargeStatusTip = SurchargeStatusTip()
private let _useLocationTip = UseLocationTip()
@State private var _showLocationDeniedAlert = false
@FocusState private var _isSearchTextFeildFocused: Bool

public init(viewModel: VM, router: Router) {
__viewModel = StateObject(wrappedValue: viewModel)
Expand All @@ -48,49 +45,24 @@ public struct MainView<VM: MainViewModelProtocol, Router: MainRouterProtocol, Ad
.padding([.top], 10)
.padding([.leading, .trailing], 20)

/*
// Favourite Places is currently not available.
Section {

FavouritePlacesView()
.padding([.leading, .trailing], 20)

} header: {
Text("Favourite Places")
.font(.title3)
.blurBackground()
}
*/

if _viewModel.isLoading {

Spacer()

CircleProgress()

Spacer()

} else {

if _viewModel.noResults {
if _viewModel.isLoading {

if _adsService.isShowingAds {
ListAdsView(unitId: _adsService.listBannerUnitId)
}

NoResultView(searchedText: _viewModel.searchedText)
.padding(.top, 40)
.padding([.leading, .trailing], 20)
CircleProgress()

} else {

if !_viewModel.mainModel.places.isEmpty {
if _viewModel.noResults {

if _adsService.isShowingAds {
ListAdsView(unitId: _adsService.listBannerUnitId)
}
NoResultView(searchedText: _viewModel.searchedText)
.padding(.top, 40)
.padding([.leading, .trailing], 20)

} else {

Section {
if !_viewModel.mainModel.places.isEmpty {

PlacesView(
mainModel: $_viewModel.mainModel,
selectedPlace: { placeId in
Expand All @@ -103,117 +75,72 @@ public struct MainView<VM: MainViewModelProtocol, Router: MainRouterProtocol, Ad
}
)

} header: {
Text("🔎\(R.string.localizable.searchFor(_viewModel.searchedText))")
.blurBackground()
} else {

WelcomeView()
.padding(.top, 40)
.padding([.leading, .trailing], 20)

FixedAdsView(isAdShowing: $_adsService.isShowingAds, unitId: _adsService.fixedBannerUnitId)
.blurRoundedBackground(cornerRadius: 20)
.padding([.top, .bottom], 10)
.padding([.leading, .trailing], 20)

}
}

}

} header: {

if !_viewModel.showWelcome {

VStack(spacing: 0) {

} else {

WelcomeView()
.padding(.top, 40)
.padding([.leading, .trailing], 20)
if _adsService.isShowingAds {
ListAdsView(unitId: _adsService.listBannerUnitId)
}

FixedAdsView(isAdShowing: $_adsService.isShowingAds, unitId: _adsService.fixedBannerUnitId)
.padding(.top, 20)
Text("🔎\(R.string.localizable.searchFor(_viewModel.searchedText))")
.frame(maxWidth: .infinity, alignment: .leading)
.padding([.leading, .trailing], 20)
.padding(.top, 10)
.padding(.bottom, 10)

}
.blurBackground()

} else {

Rectangle()
.foregroundStyle(R.color.clear.color)
.frame(height: 20)

}
}
}
}
.scrollDismissesKeyboard(.interactively)

HStack(spacing: 10) {

Button {

if _viewModel.isDeniedToUseUserLocation {
_showLocationDeniedAlert.toggle()
} else {

withAnimation {
_viewModel.toggleUserLocation()
}

_useLocationTip.invalidate(reason: .actionPerformed)

}

} label: {

if _viewModel.isDeniedToUseUserLocation {
Image(systemName: "location.slash")
.foregroundStyle(R.color.blue600.color)
} else {
Image(systemName: _viewModel.isUserLocationOn ? "location.fill" : "location")
.foregroundStyle(R.color.blue600.color)
}

}
.buttonStyle(.plain)
.contentTransition(.symbolEffect(.replace))
.alert(
R.string.localizable.alertUseLocationDeniedTitle(),
isPresented: $_showLocationDeniedAlert
) {

Button {

UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)

} label: {
Text(R.string.localizable.goToSettings())
}

Button(role: .cancel) {
_showLocationDeniedAlert.toggle()
} label: {
Text(R.string.localizable.close())
}

} message: {
Text(R.string.localizable.alertUseLocationDeniedMessage())
}
.popoverTip(_useLocationTip, arrowEdge: .leading) { action in
withAnimation {
_viewModel.toggleUserLocation()
}
_useLocationTip.invalidate(reason: .actionPerformed)
}

TextField(R.string.localizable.searchBoxPlaceholder(), text: $_viewModel.searchText)
.textFieldStyle(.roundedBorder)
.font(.body)
.disabled(_viewModel.isLoading)
.onSubmit {
Task {
await _viewModel.search()
}
}
.submitLabel(.search)
.focused($_isSearchTextFeildFocused)
.onChange(of: _isSearchTextFeildFocused, { _, newValue in
if newValue {
UseLocationTip.tryToSearch.toggle()
}
})

Button {
Task {
await _viewModel.search()
}
} label: {
Text(R.string.localizable.searchButtonTitle())
.font(.body)
.disabled(!_viewModel.canSearch)
/*
// Favourite Places is currently not available.
Section {

FavouritePlacesView()
.padding([.leading, .trailing], 20)

} header: {
Text("Favourite Places")
.font(.title3)
.blurBackground()
}
*/
}

}
.padding([.top], 10)
.padding([.leading, .trailing], 30)
.padding([.bottom], 10)
.scrollDismissesKeyboard(.interactively)

SearchView(viewModel: __viewModel)
.padding([.top], 10)
.padding([.leading, .trailing], 30)
.padding([.bottom], 10)

}
.navigationTitle("Surcharges💸")
Expand Down
120 changes: 120 additions & 0 deletions Surcharges/PresentationLayer/UIs/Main/Sources/Search/SearchView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// SearchView.swift
// Main
//
// Created by Bonsung Koo on 01/02/2025.
// Copyright © 2025 Surcharges. All rights reserved.
//

import SwiftUI

import Resources
import ViewModelProtocols

struct SearchView<VM: MainViewModelProtocol>: View {

private let _useLocationTip = UseLocationTip()
@State private var _showLocationDeniedAlert = false
@StateObject private var _viewModel: VM

@FocusState private var _isSearchTextFeildFocused: Bool

init(viewModel: StateObject<VM>) {
__viewModel = viewModel
}

var body: some View {
HStack(spacing: 10) {

Button {

if _viewModel.isDeniedToUseUserLocation {
_showLocationDeniedAlert.toggle()
} else {

withAnimation {
_viewModel.toggleUserLocation()
}

_useLocationTip.invalidate(reason: .actionPerformed)

}

} label: {

if _viewModel.isDeniedToUseUserLocation {
Image(systemName: "location.slash")
.foregroundStyle(R.color.blue600.color)
} else {
Image(systemName: _viewModel.isUserLocationOn ? "location.fill" : "location")
.foregroundStyle(R.color.blue600.color)
}

}
.buttonStyle(.plain)
.contentTransition(.symbolEffect(.replace))
.alert(
R.string.localizable.alertUseLocationDeniedTitle(),
isPresented: $_showLocationDeniedAlert
) {

Button {

UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)

} label: {
Text(R.string.localizable.goToSettings())
}

Button(role: .cancel) {
_showLocationDeniedAlert.toggle()
} label: {
Text(R.string.localizable.close())
}

} message: {
Text(R.string.localizable.alertUseLocationDeniedMessage())
}
.popoverTip(_useLocationTip, arrowEdge: .leading) { action in
withAnimation {
_viewModel.toggleUserLocation()
}
_useLocationTip.invalidate(reason: .actionPerformed)
}

TextField(R.string.localizable.searchBoxPlaceholder(), text: $_viewModel.searchText)
.textFieldStyle(.roundedBorder)
.font(.body)
.disabled(_viewModel.isLoading)
.onSubmit {

if _viewModel.canSearch {
Task {
await _viewModel.search()
}
}

}
.submitLabel(.search)
.focused($_isSearchTextFeildFocused)
.onChange(of: _isSearchTextFeildFocused, { _, newValue in
if newValue {
UseLocationTip.tryToSearch.toggle()
}
})

Button {

Task {
await _viewModel.search()
}

} label: {
Text(R.string.localizable.searchButtonTitle())
.font(.body)
.disabled(!_viewModel.canSearch)
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ public struct FixedAdsView: View {

}
}
.background(R.color.gray100.color)
.frame(minHeight: _adSize.size.width, maxHeight: _adSize.size.width)
.cornerRadius(20)
.readSize { size in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public struct ListAdsView: View {
.frame(height: _adSize.size.height)
}
.background(R.color.gray100.color)
.cornerRadius(20)
.readSize { size in
_adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(size.width)
}
Expand Down
Loading
Loading