Skip to content

Commit

Permalink
Merge pull request #39 from OneBusAway/modify-trip-planner-view
Browse files Browse the repository at this point in the history
Modify Itinerary View
  • Loading branch information
aaronbrethorst authored Aug 5, 2024
2 parents adb00b7 + 7c45a62 commit 570b3af
Show file tree
Hide file tree
Showing 14 changed files with 287 additions and 33 deletions.
1 change: 0 additions & 1 deletion OTPKit/Controls/PageHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import SwiftUI

/// Appears at the top of UI pages with a title and close button.
struct PageHeaderView: View {

private let text: String
private let action: VoidBlock?

Expand Down
1 change: 0 additions & 1 deletion OTPKit/Controls/SectionHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import SwiftUI
/// The view that appears above a section on the `OriginDestinationSheetView`.
/// For instance, the header for the Recents and Favorites sections.
struct SectionHeaderView: View {

private let text: String
private let action: VoidBlock?

Expand Down
3 changes: 2 additions & 1 deletion OTPKit/Features/OriginDestination/FavoriteView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct FavoriteView: View {
self.imageName = imageName
self.action = action
}

var body: some View {
Button(action: {
action?()
Expand All @@ -35,7 +36,7 @@ struct FavoriteView: View {
.truncationMode(.tail)
}
.padding(.all, 4)
.foregroundStyle(.black)
.foregroundStyle(.foreground)
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions OTPKit/Features/OriginDestination/OriginDestinationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ public struct OriginDestinationView: View {
}, label: {
HStack(spacing: 16) {
Image(systemName: "paperplane.fill")
.foregroundColor(.white)
.background(
Circle()
.fill(Color.green)
Expand All @@ -36,14 +35,14 @@ public struct OriginDestinationView: View {
Text(locationManagerService.originName)
}
})
.foregroundStyle(.foreground)

Button(action: {
sheetEnvironment.isSheetOpened.toggle()
locationManagerService.originDestinationState = .destination
}, label: {
HStack(spacing: 16) {
Image(systemName: "mappin")
.foregroundColor(.white)
.background(
Circle()
.fill(Color.green)
Expand All @@ -52,6 +51,7 @@ public struct OriginDestinationView: View {
Text(locationManagerService.destinationName)
}
})
.foregroundStyle(.foreground)
}
.frame(height: 135)
.scrollContentBackground(.hidden)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public struct AddFavoriteLocationsSheet: View {
Text(userLocation.title)
.font(.headline)
Text(userLocation.subTitle)
}.foregroundStyle(Color.black)
}.foregroundStyle(.foreground)

Spacer()

Expand Down Expand Up @@ -91,7 +91,7 @@ public struct AddFavoriteLocationsSheet: View {
Text(location.title)
.font(.headline)
Text(location.subTitle)
}.foregroundStyle(Color.black)
}.foregroundStyle(.foreground)

Spacer()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public struct FavoriteLocationsSheet: View {
.font(.headline)
Text(location.subTitle)
}
.foregroundStyle(Color.black)
.foregroundStyle(.foreground)
})
}
}
Expand Down
29 changes: 29 additions & 0 deletions OTPKit/Features/TripPlanner/ItineraryLegUnknownView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// ItineraryLegUnknownView.swift
// OTPKit
//
// Created by Aaron Brethorst on 8/5/24.
//

import SwiftUI

/// Represents an itinerary leg that uses an unknown method of conveyance.
struct ItineraryLegUnknownView: View {
let leg: Leg

var body: some View {
HStack(spacing: 4) {
Text("\(leg.mode): \(Formatters.formatTimeDuration(leg.duration))")
}
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color.gray.opacity(0.2))
.foregroundStyle(.gray)
.clipShape(RoundedRectangle(cornerRadius: 8))
.frame(height: 40)
}
}

#Preview {
ItineraryLegUnknownView(leg: PreviewHelpers.buildLeg())
}
53 changes: 53 additions & 0 deletions OTPKit/Features/TripPlanner/ItineraryLegVehicleView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// ItineraryLegVehicleView.swift
// OTPKit
//
// Created by Aaron Brethorst on 8/5/24.
//

import SwiftUI

/// Represents an itinerary leg that uses a vehicular method of conveyance.
struct ItineraryLegVehicleView: View {
let leg: Leg

var body: some View {
HStack(spacing: 4) {
Text(leg.route ?? "")
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(backgroundColor)
.foregroundStyle(.foreground)
.font(.caption)
.clipShape(RoundedRectangle(cornerRadius: 4))

Image(systemName: imageName)
.foregroundStyle(.foreground)
}.frame(height: 40)
}

private var imageName: String {
if leg.mode == "TRAM" {
return "tram"
} else if leg.mode == "BUS" {
return "bus"
} else {
return ""
}
}

private var backgroundColor: Color {
if leg.mode == "TRAM" {
return Color.blue
} else if leg.mode == "BUS" {
return Color.green
} else {
return Color.pink
}
}

}

#Preview {
ItineraryLegVehicleView(leg: PreviewHelpers.buildLeg())
}
30 changes: 30 additions & 0 deletions OTPKit/Features/TripPlanner/ItineraryLegWalkView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// ItineraryLegWalkView.swift
// OTPKit
//
// Created by Aaron Brethorst on 8/5/24.
//

import SwiftUI

/// Represents an itinerary leg that uses a walking method of conveyance.
struct ItineraryLegWalkView: View {
let leg: Leg

var body: some View {
HStack(spacing: 4) {
Image(systemName: "figure.walk")
Text(Formatters.formatTimeDuration(leg.duration))
}
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color.gray.opacity(0.2))
.foregroundStyle(.gray)
.clipShape(RoundedRectangle(cornerRadius: 8))
.frame(height: 40)
}
}

#Preview {
ItineraryLegWalkView(leg: PreviewHelpers.buildLeg())
}
72 changes: 49 additions & 23 deletions OTPKit/Features/TripPlanner/TripPlannerSheetView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,69 @@ public struct TripPlannerSheetView: View {

public init() {}

private func formatTimeDuration(_ duration: Int) -> String {
if duration < 60 {
return "Total duration: \(duration) second\(duration > 1 ? "s" : "")"
} else if duration < 3600 {
let minutes = Double(duration) / 60
return String(format: "Total duration: %.1f minutes", minutes)
} else {
let hours = Double(duration) / 3600
return String(format: "Total duration: %.1f hours", hours)
}
}

private func formatDistance(_ distance: Int) -> String {
if distance < 1000 {
return "Total distance: \(distance) meters"
} else {
let miles = Double(distance) / 1609.34
return String(format: "Total distance: %.1f miles", miles)
private func generateLegView(leg: Leg) -> some View {
Group {
switch leg.mode {
case "BUS", "TRAM":
ItineraryLegVehicleView(leg: leg)
case "WALK":
ItineraryLegWalkView(leg: leg)
default:
ItineraryLegUnknownView(leg: leg)
}
}
}

public var body: some View {
VStack {
if let itineraries = locationManagerService.planResponse?.plan?.itineraries {
List(itineraries, id: \.self) { itinerary in
let distance = itinerary.legs.map(\.distance).reduce(0, +)
Button(action: {
locationManagerService.selectedItinerary = itinerary
locationManagerService.planResponse = nil
dismiss()
}, label: {
VStack(alignment: .leading) {
Text(formatTimeDuration(itinerary.duration))
Text(formatDistance(Int(distance)))
HStack(spacing: 20) {
VStack(alignment: .leading) {
Text(Formatters.formatTimeDuration(itinerary.duration))
.font(.title)
.fontWeight(.bold)
.foregroundStyle(.foreground)
Text(Formatters.formatBusSchedule(itinerary.startTime))
.foregroundStyle(.gray)

FlowLayout {
ForEach(Array(zip(itinerary.legs.indices, itinerary.legs)), id: \.1) { index, leg in

generateLegView(leg: leg)

if index < itinerary.legs.count - 1 {
VStack {
Image(systemName: "chevron.right.circle.fill")
.frame(width: 8, height: 16)
}.frame(height: 40)
}
}
}
}

Button(action: {
locationManagerService.selectedItinerary = itinerary
locationManagerService.planResponse = nil
dismiss()
}, label: {
Text("Go")
.padding(30)
.background(Color.green)
.foregroundStyle(.foreground)
.font(.title)
.fontWeight(.bold)
.clipShape(RoundedRectangle(cornerRadius: 12))
})
}

})
.foregroundStyle(.foreground)
}
} else {
Text("Can't find trip planner. Please try another pin point")
Expand All @@ -63,7 +89,7 @@ public struct TripPlannerSheetView: View {
.frame(maxWidth: .infinity)
.padding()
.background(Color.gray)
.foregroundStyle(Color.white)
.foregroundStyle(.foreground)
.clipShape(RoundedRectangle(cornerRadius: 12))
.padding(.horizontal, 16)
})
Expand Down
4 changes: 2 additions & 2 deletions OTPKit/Features/TripPlanner/TripPlannerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public struct TripPlannerView: View {
.frame(maxWidth: .infinity)
.padding()
.background(Color.gray)
.foregroundStyle(Color.white)
.foregroundStyle(.foreground)
.clipShape(RoundedRectangle(cornerRadius: 12))
.padding(.horizontal, 16)

Expand All @@ -34,7 +34,7 @@ public struct TripPlannerView: View {
.frame(maxWidth: .infinity)
.padding()
.background(Color.gray)
.foregroundStyle(Color.white)
.foregroundStyle(.foreground)
.clipShape(RoundedRectangle(cornerRadius: 12))
.padding(.horizontal, 16)
}
Expand Down
45 changes: 45 additions & 0 deletions OTPKit/Miscellaneous/FlowLayout.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// FlowLayout.swift
// OTPKit
//
// Created by Hilmy Veradin on 04/08/24.
//

import SwiftUI

/// Extension to make adaptive layout
struct FlowLayout: Layout {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize {
let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
return layout(sizes: sizes, proposal: proposal).size
}

func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) {
let sizes = subviews.map { $0.sizeThatFits(.unspecified) }
let offsets = layout(sizes: sizes, proposal: proposal).offsets

for (offset, subview) in zip(offsets, subviews) {
subview.place(at: CGPoint(x: bounds.minX + offset.x, y: bounds.minY + offset.y), proposal: .unspecified)
}
}

private func layout(sizes: [CGSize], proposal: ProposedViewSize) -> (offsets: [CGPoint], size: CGSize) {
let verticalSpacing: CGFloat = 4
let horizontalSpacing: CGFloat = 8
var result: [CGPoint] = []
var currentPosition: CGPoint = .zero
var maxY: CGFloat = 0

for size in sizes {
if currentPosition.x + size.width > (proposal.width ?? .infinity) {
currentPosition.x = 0
currentPosition.y = maxY + verticalSpacing
}
result.append(currentPosition)
currentPosition.x += size.width + horizontalSpacing
maxY = max(maxY, currentPosition.y + size.height)
}

return (result, CGSize(width: proposal.width ?? .infinity, height: maxY))
}
}
Loading

0 comments on commit 570b3af

Please sign in to comment.