Skip to content

Commit

Permalink
Merge pull request #32 from OneBusAway/Issue-22-origin-destinaton_bas…
Browse files Browse the repository at this point in the history
…ed_on_map

Make Origin Destination Working Marker View
  • Loading branch information
aaronbrethorst authored Jul 20, 2024
2 parents 4f79722 + 5ac946b commit 76c3721
Show file tree
Hide file tree
Showing 11 changed files with 397 additions and 278 deletions.
53 changes: 53 additions & 0 deletions OTPKit/Features/MapExtension/MapMarkingView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// MapMarkingView.swift
// OTPKit
//
// Created by Hilmy Veradin on 18/07/24.
//

import SwiftUI

public struct MapMarkingView: View {
@ObservedObject private var locationManagerService = LocationManagerService.shared

public init() {}
public var body: some View {
VStack {
Spacer()

Text("Tap on the map to add a pin.")
.padding(16)
.background(.regularMaterial)
.cornerRadius(16)

HStack(spacing: 16) {
Button {
locationManagerService.toggleMapMarkingMode(false)
locationManagerService.selectCoordinate()
} label: {
Text("Cancel")
.padding(8)
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)

Button {
locationManagerService.toggleMapMarkingMode(false)
locationManagerService.selectCoordinate()
} label: {
Text("Add Pin")
.padding(8)
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
}
.frame(maxWidth: .infinity)
.padding(16)
}
.padding(.bottom, 24)
}
}

#Preview {
MapMarkingView()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,11 @@
import Foundation
import SwiftUI

/// OriginDestinationSheetState responsible for managing states of the shown `OriginDestinationSheetView`
/// - Enums:
/// - origin: This manage origin state of the trip planner
/// - destination: This manage destination state of the trip planner
public enum OriginDestinationSheetState {
case origin
case destination
}

/// OriginDestinationSheetEnvironment responsible for manage the environment of `OriginDestination` features
/// - sheetState: responsible for managing shown sheet in `OriginDestinationView`
/// - selectedValue: responsible for managing selected value when user taped the list in `OriginDestinationSheetView`
public final class OriginDestinationSheetEnvironment: ObservableObject {
@Published public var isSheetOpened = false
@Published public var sheetState: OriginDestinationSheetState = .origin
@Published public var selectedValue: String = ""

// This responsible for showing favorite locations and recent locations in sheets
Expand Down
6 changes: 4 additions & 2 deletions OTPKit/Features/OriginDestination/OriginDestinationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import SwiftUI
/// It consists a list of Origin and Destination along with the `MapKit`
public struct OriginDestinationView: View {
@EnvironmentObject private var sheetEnvironment: OriginDestinationSheetEnvironment
@ObservedObject private var locationManagerService = LocationManagerService.shared
@State private var isSheetOpened = false

// Public Initializer
Expand All @@ -22,7 +23,7 @@ public struct OriginDestinationView: View {
List {
Button(action: {
sheetEnvironment.isSheetOpened.toggle()
sheetEnvironment.sheetState = .origin
locationManagerService.originDestinationState = .origin
}, label: {
HStack(spacing: 16) {
Image(systemName: "paperplane.fill")
Expand All @@ -38,7 +39,7 @@ public struct OriginDestinationView: View {

Button(action: {
sheetEnvironment.isSheetOpened.toggle()
sheetEnvironment.sheetState = .destination
locationManagerService.originDestinationState = .destination
}, label: {
HStack(spacing: 16) {
Image(systemName: "mappin")
Expand All @@ -55,6 +56,7 @@ public struct OriginDestinationView: View {
.frame(height: 135)
.scrollContentBackground(.hidden)
.scrollDisabled(true)
.padding(.bottom, 24)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,107 +11,124 @@ import SwiftUI
/// Users can search and add their favorite locations
public struct AddFavoriteLocationsSheet: View {
@Environment(\.dismiss) var dismiss

@EnvironmentObject private var locationService: LocationService
@EnvironmentObject private var sheetEnvironment: OriginDestinationSheetEnvironment

@ObservedObject private var locationManagerService = LocationManagerService.shared

@State private var search = ""
private let userLocation = UserLocationServices.shared.currentLocation

@FocusState private var isSearchActive: Bool
@FocusState private var isSearchFocused: Bool

private var filteredCompletions: [Location] {
let favorites = sheetEnvironment.favoriteLocations
return locationService.completions.filter { completion in
return locationManagerService.completions.filter { completion in
!favorites.contains { favorite in
favorite.title == completion.title &&
favorite.subTitle == completion.subTitle
}
}
}

public var body: some View {
VStack {
HStack {
Text("Add favorite location")
private func headerView() -> some View {
HStack {
Text("Add favorite location")
.font(.title2)
.fontWeight(.bold)
Spacer()
Button(action: {
dismiss()
}, label: {
Image(systemName: "xmark.circle.fill")
.font(.title2)
.fontWeight(.bold)
Spacer()
.foregroundColor(.gray)
})
}
.padding()
}

private func searchView() -> some View {
HStack {
Image(systemName: "magnifyingglass")
TextField("Search for a place", text: $search)
.autocorrectionDisabled()
.focused($isSearchFocused)
}
.padding(.vertical, 8)
.padding(.horizontal, 12)
.background(Color.gray.opacity(0.2))
.clipShape(RoundedRectangle(cornerRadius: 12))
.padding(.horizontal, 16)
}

private func currentUserSection() -> some View {
if search.isEmpty, let userLocation = locationManagerService.currentLocation {
AnyView(
Button(action: {
dismiss()
switch UserDefaultsServices.shared.saveFavoriteLocationData(data: userLocation) {
case .success:
sheetEnvironment.refreshFavoriteLocations()
dismiss()
case let .failure(error):
print(error)
}
}, label: {
Image(systemName: "xmark.circle.fill")
.font(.title2)
.foregroundColor(.gray)
HStack {
VStack(alignment: .leading) {
Text(userLocation.title)
.font(.headline)
Text(userLocation.subTitle)
}.foregroundStyle(Color.black)

Spacer()

Image(systemName: "plus")
}

})
}
.padding()
)

HStack {
Image(systemName: "magnifyingglass")
TextField("Search for a place", text: $search)
.autocorrectionDisabled()
.focused($isSearchActive)
}
.padding(.vertical, 8)
.padding(.horizontal, 12)
.background(Color.gray.opacity(0.2))
.clipShape(.rect(cornerRadius: 12))
.padding(.horizontal, 16)
} else {
AnyView(EmptyView())
}
}

List {
if search.isEmpty, let userLocation {
Button(action: {
switch UserDefaultsServices.shared.saveFavoriteLocationData(data: userLocation) {
case .success:
sheetEnvironment.refreshFavoriteLocations()
dismiss()
case let .failure(error):
print(error)
}
}, label: {
HStack {
VStack(alignment: .leading) {
Text(userLocation.title)
.font(.headline)
Text(userLocation.subTitle)
}.foregroundStyle(Color.black)

Spacer()

Image(systemName: "plus")
}

})
private func searchedResultsSection() -> some View {
ForEach(filteredCompletions) { location in
Button(action: {
switch UserDefaultsServices.shared.saveFavoriteLocationData(data: location) {
case .success:
sheetEnvironment.refreshFavoriteLocations()
dismiss()
case let .failure(error):
print(error)
}
}, label: {
HStack {
VStack(alignment: .leading) {
Text(location.title)
.font(.headline)
Text(location.subTitle)
}.foregroundStyle(Color.black)

Spacer()

ForEach(filteredCompletions) { location in
Button(action: {
switch UserDefaultsServices.shared.saveFavoriteLocationData(data: location) {
case .success:
sheetEnvironment.refreshFavoriteLocations()
dismiss()
case let .failure(error):
print(error)
}
}, label: {
HStack {
VStack(alignment: .leading) {
Text(location.title)
.font(.headline)
Text(location.subTitle)
}.foregroundStyle(Color.black)

Spacer()

Image(systemName: "plus")
}

})
Image(systemName: "plus")
}

})
}
}

public var body: some View {
VStack {
headerView()
searchView()
List {
currentUserSection()
searchedResultsSection()
}
.onChange(of: search) { _, searchValue in
locationService.update(queryFragment: searchValue)
locationManagerService.updateQuery(queryFragment: searchValue)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ public struct OriginDestinationSheetView: View {
@Environment(\.dismiss) var dismiss
@EnvironmentObject var sheetEnvironment: OriginDestinationSheetEnvironment

@StateObject private var locationService = LocationService()
@ObservedObject private var locationManagerService = LocationManagerService.shared

@State private var search: String = ""

private let userLocation = UserLocationServices.shared.currentLocation

// Sheet States
@State private var isAddSavedLocationsSheetOpen = false
@State private var isFavoriteLocationSheetOpen = false
Expand Down Expand Up @@ -116,7 +114,6 @@ public struct OriginDestinationSheetView: View {
})
.sheet(isPresented: $isAddSavedLocationsSheetOpen, content: {
AddFavoriteLocationsSheet()
.environmentObject(locationService)
.environmentObject(sheetEnvironment)
})
.sheet(isPresented: $isFavoriteLocationSheetOpen, content: {
Expand Down Expand Up @@ -167,7 +164,7 @@ public struct OriginDestinationSheetView: View {

private func searchResultsSection() -> some View {
Group {
ForEach(locationService.completions) { location in
ForEach(locationManagerService.completions) { location in
Button(action: {
switch UserDefaultsServices.shared.saveRecentLocations(data: location) {
case .success:
Expand All @@ -190,7 +187,7 @@ public struct OriginDestinationSheetView: View {

private func currentUserSection() -> some View {
Group {
if let userLocation {
if let userLocation = locationManagerService.currentLocation {
Button(action: {
switch UserDefaultsServices.shared.saveRecentLocations(data: userLocation) {
case .success:
Expand All @@ -213,6 +210,19 @@ public struct OriginDestinationSheetView: View {
}
}

private func selectLocationBasedOnMap() -> some View {
Button(action: {
locationManagerService.toggleMapMarkingMode(true)
dismiss()
}, label: {
HStack {
Image(systemName: "mappin")
Text("Choose on Map")
}
})
.buttonStyle(PlainButtonStyle())
}

public var body: some View {
VStack {
headerView()
Expand All @@ -225,14 +235,15 @@ public struct OriginDestinationSheetView: View {
if search.isEmpty, isSearchFocused {
currentUserSection()
} else if search.isEmpty {
selectLocationBasedOnMap()
favoritesSection()
recentsSection()
} else {
searchResultsSection()
}
}
.onChange(of: search) { _, searchValue in
locationService.update(queryFragment: searchValue)
locationManagerService.updateQuery(queryFragment: searchValue)
}

Spacer()
Expand Down
Loading

0 comments on commit 76c3721

Please sign in to comment.