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

International event times #511

Merged
merged 10 commits into from
Oct 1, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Setup Xcode version
uses: maxim-lobanov/[email protected]
with:
xcode-version: 15.4
xcode-version: 16.0

- name: Run UnitTests
run: fastlane test
Expand Down
12 changes: 9 additions & 3 deletions CriticalMapsKit/Sources/AppFeature/AppFeatureCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ public struct AppFeature {
case requestTimer(RequestTimer.Action)
case settings(SettingsFeature.Action)
case social(SocialFeature.Action)
case didTapNextEventBanner

public enum Alert: Equatable, Sendable {
case observationMode(enabled: Bool)
Expand Down Expand Up @@ -322,10 +323,9 @@ public struct AppFeature {

case let .map(mapFeatureAction):
switch mapFeatureAction {
case .focusRideEvent,
.focusNextRide:
case .focusRideEvent, .focusNextRide:
if state.bottomSheetPosition != .hidden {
return .send(.set(\.$bottomSheetPosition, .relative(0.4)))
return .send(.set(\.$bottomSheetPosition, .relative(0.3)))
} else {
return .none
}
Expand Down Expand Up @@ -491,6 +491,12 @@ public struct AppFeature {
default:
return .none
}

case .didTapNextEventBanner:
return .merge(
.send(.map(.focusNextRide(state.nextRideState.nextRide?.coordinate))),
.send(.set(\.$bottomSheetPosition, .relative(0.3)))
)

case .binding:
return .none
Expand Down
94 changes: 48 additions & 46 deletions CriticalMapsKit/Sources/AppFeature/AppView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,6 @@ public struct AppView: View {
VStack(alignment: .leading) {
if shouldShowNextRideBanner {
nextRideBanner()
.contextMenu {
Button(
action: { viewStore.send(.set(\.$bottomSheetPosition, .relative(0.4))) },
label: { Label(contextMenuTitle, systemImage: "list.bullet") }
)
}
}

if viewStore.settingsState.infoViewEnabled {
Expand Down Expand Up @@ -98,7 +92,7 @@ public struct AppView: View {
.bottomSheet(
bottomSheetPosition: viewStore.$bottomSheetPosition,
switchablePositions: [
.relative(0.4),
.relative(0.3),
.relativeTop(0.975)
],
title: "Events",
Expand Down Expand Up @@ -181,45 +175,14 @@ public struct AppView: View {
func bottomSheetContentView() -> some View {
VStack {
List(viewStore.nextRideState.rideEvents, id: \.id) { ride in
HStack(alignment: .center, spacing: .grid(2)) {
Image(uiImage: Asset.cm.image)
.accessibilityHidden(true)

VStack(alignment: .leading, spacing: .grid(1)) {
Text(ride.title)
.multilineTextAlignment(.leading)
.font(Font.body.weight(.semibold))
.foregroundColor(Color(.textPrimary))
.padding(.bottom, .grid(1))

VStack(alignment: .leading, spacing: 2) {
Label(ride.dateTime.humanReadableDate, systemImage: "calendar")
.multilineTextAlignment(.leading)
.font(.bodyTwo)
.foregroundColor(Color(.textSecondary))

Label(ride.dateTime.humanReadableTime, systemImage: "clock")
.multilineTextAlignment(.leading)
.font(.bodyTwo)
.foregroundColor(Color(.textSecondary))

if let location = ride.location {
Label(location, systemImage: "location.fill")
.multilineTextAlignment(.leading)
.font(.bodyTwo)
.foregroundColor(Color(.textSecondary))
}
}
RideEventView(ride: ride)
.contentShape(Rectangle())
.padding(.vertical, .grid(1))
.accessibilityElement(children: .combine)
.onTapGesture {
viewStore.send(.onRideSelectedFromBottomSheet(ride))
}
Spacer()
}
.contentShape(Rectangle())
.padding(.vertical, .grid(1))
.accessibilityElement(children: .combine)
.onTapGesture {
viewStore.send(.onRideSelectedFromBottomSheet(ride))
}
.listRowBackground(Color.clear)
.listRowBackground(Color.clear)
}
.listStyle(.plain)
}
Expand Down Expand Up @@ -262,7 +225,7 @@ public struct AppView: View {
},
action: { $0 }
),
action: { viewStore.send(.map(.focusNextRide(viewStore.nextRideState.nextRide?.coordinate))) },
action: { viewStore.send(.didTapNextEventBanner) },
content: {
VStack(alignment: .leading, spacing: .grid(1)) {
Text(viewStore.state.nextRideState.nextRide?.title ?? "")
Expand Down Expand Up @@ -303,3 +266,42 @@ struct NumericContentTransition: ViewModifier {
}
}
}

struct RideEventView: View {
let ride: Ride

var body: some View {
HStack(alignment: .center, spacing: .grid(2)) {
Image(uiImage: Asset.cm.image)
.accessibilityHidden(true)

VStack(alignment: .leading, spacing: .grid(1)) {
Text(ride.title)
.multilineTextAlignment(.leading)
.font(Font.body.weight(.semibold))
.foregroundColor(Color(.textPrimary))
.padding(.bottom, .grid(1))

VStack(alignment: .leading, spacing: 2) {
Label(ride.dateTime.humanReadableDate, systemImage: "calendar")
.multilineTextAlignment(.leading)
.font(.bodyTwo)
.foregroundColor(Color(.textSecondary))

Label(ride.rideTime, systemImage: "clock")
.multilineTextAlignment(.leading)
.font(.bodyTwo)
.foregroundColor(Color(.textSecondary))

if let location = ride.location {
Label(location, systemImage: "location.fill")
.multilineTextAlignment(.leading)
.font(.bodyTwo)
.foregroundColor(Color(.textSecondary))
}
}
}
Spacer()
}
}
}
5 changes: 0 additions & 5 deletions CriticalMapsKit/Sources/Helpers/Date+Additions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ public extension Date {
return component
}

/// - Returns: Formatted time without date components.
var humanReadableTime: String {
self.formatted(Date.FormatStyle.localeAwareShortTime)
}

/// - Returns: Formatted date without time components.
var humanReadableDate: String {
self.formatted(Date.FormatStyle.localeAwareShortDate)
Expand Down
1 change: 1 addition & 0 deletions CriticalMapsKit/Sources/Helpers/Timezone+Extras.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public extension TimeZone {
static let spain = TimeZone(identifier: "Europe/Madrid")!
static let france = TimeZone(identifier: "Europe/Paris")!
static let greece = TimeZone(identifier: "Europe/Athens")!
static let london = TimeZone(identifier: "Europe/London")!

// America
static let ecuador = TimeZone(identifier: "America/Guayaquil")!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public extension Request {
URLQueryItem(name: NextRideQueryKeys.centerLatitude, value: String(coordinate.latitude)),
URLQueryItem(name: NextRideQueryKeys.radius, value: String(radius)),
URLQueryItem(name: NextRideQueryKeys.year, value: String(Date.getCurrent(\.year, date))),
URLQueryItem(name: NextRideQueryKeys.month, value: String(month))
URLQueryItem(name: NextRideQueryKeys.month, value: String(month)),
URLQueryItem(name: NextRideQueryKeys.extended, value: "true"),
]
)
}
Expand All @@ -31,4 +32,5 @@ enum NextRideQueryKeys {
static let radius = "radius"
static let year = "year"
static let month = "month"
static let extended = "extended"
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import ComposableArchitecture
import CoreLocation
import Foundation
import Helpers
import MapKit

public struct Ride: Hashable, Codable, Identifiable {
public let id: Int
public var city: City?
public let slug: String?
public let title: String
public let description: String?
Expand All @@ -22,6 +24,7 @@ public struct Ride: Hashable, Codable, Identifiable {

public init(
id: Int,
city: City? = nil,
slug: String? = nil,
title: String,
description: String? = nil,
Expand All @@ -38,6 +41,7 @@ public struct Ride: Hashable, Codable, Identifiable {
rideType: Ride.RideType? = nil
) {
self.id = id
self.city = city
self.slug = slug
self.title = title
self.description = description
Expand All @@ -55,6 +59,25 @@ public struct Ride: Hashable, Codable, Identifiable {
}
}

extension Ride {
public struct City: Codable, Hashable {
let id: Int
let name: String
let timezone: String

public init(
id: Int,
name: String,
timezone: String
) {
self.id = id
self.name = name
self.timezone = timezone
}
}

}

public extension Ride {
var coordinate: Coordinate? {
guard let lat = latitude, let lng = longitude else {
Expand All @@ -72,7 +95,18 @@ public extension Ride {
}

var rideDateAndTime: String {
"\(dateTime.humanReadableDate) - \(dateTime.humanReadableTime)"
"\(dateTime.humanReadableDate) - \(rideTime)"
}

var rideTime: String {
if
let cityTimeZone = city?.timezone,
let timeZone = TimeZone(identifier: cityTimeZone)
{
return dateTime.formatted(Date.FormatStyle.shortTimeWithEventTimeZone(timeZone))
} else {
return dateTime.formatted(Date.FormatStyle.localeAwareShortTime)
}
}

var shareMessage: String {
Expand All @@ -82,6 +116,8 @@ public extension Ride {
return """
\(titleAndTime)
\(location)

\(description ?? "")
"""
}
}
Expand Down Expand Up @@ -121,3 +157,16 @@ public extension Ride {
}
}
}

private extension Date.FormatStyle {
static func shortTimeWithEventTimeZone(_ timezone: TimeZone) -> Self {
@Dependency(\.locale) var locale

return Self(
date: .omitted,
time: .shortened,
locale: locale,
timeZone: timezone
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ final class AppFeatureTests: XCTestCase {
await store.send(.map(.focusRideEvent(coordinate))) {
$0.mapFeatureState.eventCenter = CoordinateRegion(center: coordinate.asCLLocationCoordinate)
}
await store.receive(.binding(.set(\.$bottomSheetPosition, .relative(0.4))))
await store.receive(.binding(.set(\.$bottomSheetPosition, .relative(0.3))))
await testClock.advance(by: .seconds(1))
await store.receive(.map(.resetRideEventCenter)) {
$0.mapFeatureState.eventCenter = nil
Expand Down Expand Up @@ -506,6 +506,24 @@ final class AppFeatureTests: XCTestCase {
let didStopLocationObservationValue = await didStopLocationUpdating.value
XCTAssertTrue(didStopLocationObservationValue)
}

func test_didTapNextEventBanner() async {
let store = await TestStore(
initialState: AppFeature.State(nextRideState: NextRideFeature.State(nextRide: Ride.mock1)),
reducer: { AppFeature() },
withDependencies: {
$0.continuousClock = TestClock()
}
)
store.exhaustivity = .off

// act
await store.send(.didTapNextEventBanner)

// assert
await store.receive(.map(.focusNextRide(Ride.mock1.coordinate)))
await store.receive(.set(\.$bottomSheetPosition, .relative(0.3)))
}
}

// MARK: Helper
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import MapFeature
import SnapshotTesting
import TestHelper
import XCTest
import Testing

final class UserTrackingButtonSnapshotTests: XCTestCase {
@MainActor
struct UserTrackingButtonSnapshotTests {
@Test(.disabled("Due to CI issue with selecting iPhone"))
func test_userTracking_none() {
let sut = UserTrackingButton(
store: .init(
Expand All @@ -16,7 +16,7 @@ final class UserTrackingButtonSnapshotTests: XCTestCase {
assertSnapshot(of: sut, as: .image)
}

@MainActor
@Test(.disabled("Due to CI issue with selecting iPhone"))
func test_userTracking_follow() {
let sut = UserTrackingButton(
store: .init(
Expand All @@ -28,7 +28,7 @@ final class UserTrackingButtonSnapshotTests: XCTestCase {
assertSnapshot(of: sut, as: .image)
}

@MainActor
@Test(.disabled("Due to CI issue with selecting iPhone"))
func test_userTracking_followWithHeading() {
let sut = UserTrackingButton(
store: .init(
Expand Down
Loading