Overview
The TripStore is an Angular injectable service that manages the driver’s trip state using Angular signals. It tracks trip phases, offer countdowns, passenger information, and dynamic fare calculations including waiting time penalties.
State Interface
DriverTripState
status
'idle' | 'loading' | 'success' | 'error'
Current loading status of trip operations
Error message if any operation failed
ID of the currently active trip
Complete trip data object from the API
Current trip phase: 'idle', 'assigned', 'arriving', 'in_progress', 'completed', 'cancelled', or 'no_drivers_found'
ID of the current trip offer assignment
ISO timestamp when the current offer expires
Remaining seconds on the offer countdown timer
Whether the trip offer modal is currently open
Array of assignment IDs that have already been accepted or declined
Passenger information subset:
id (string): Passenger ID
name (string | null): Passenger name
phoneMasked (string | null): Masked phone number
photoUrl (string | null): Profile photo URL
ISO timestamp when driver marked arrival at pickup location
Pricing and Waiting Time Fields
Initial base fare for the trip
Currency code (e.g., “CUP”)
Current fare including any waiting penalties
ISO timestamp when waiting period started
Total seconds driver has been waiting at pickup
Whether a waiting time penalty has been applied
Description text for the waiting penalty
Additional fare amount from waiting penalty
Reason for the waiting charge
Computed Signals
state
readonly state = computed(() => this._s())
Returns the complete state object.
trip
readonly trip = computed(() => this._s().trip)
Returns the current trip data.
phase
readonly phase = computed(() => this._s().phase)
Returns the current trip phase.
remainingSec
readonly remainingSec = computed(() => this._s().remainingSec)
Returns remaining seconds on offer countdown.
modalOpen
readonly modalOpen = computed(() => this._s().modalOpen)
Returns modal open state.
baseFare
readonly baseFare = computed(() => this._s().baseFare)
Returns the base fare amount.
liveFare
readonly liveFare = computed(() => this._s().liveFare ?? this._s().baseFare)
Returns the current live fare, falling back to base fare if not set.
waitingSeconds
readonly waitingSeconds = computed(() => this._s().waitingSeconds)
Returns total waiting seconds.
waitingPenaltyApplied
readonly waitingPenaltyApplied = computed(() => this._s().waitingPenaltyApplied)
Returns whether waiting penalty has been applied.
waitingPenaltyText
readonly waitingPenaltyText = computed(() => this._s().waitingPenaltyText)
Returns the waiting penalty description text.
modalVm
readonly modalVm = computed(() => ({ ... }))
Computed view model for the trip offer modal, includes:
phase: Current trip phase
remainingSec: Countdown timer
originLabel: Formatted pickup address or coordinates
destinationLabel: Formatted destination address or coordinates
distanceText: Formatted distance (e.g., “5.2 km”)
durationText: Formatted duration (e.g., ”≈ 15 min”)
priceText: Formatted price (e.g., “250 CUP”)
- Raw values:
distanceKm, durationMin, total, currency
State Mutation Methods
setLoading()
Sets loading state and clears any errors.
setError(msg)
setError(msg: string): void
Sets error state with the provided message.
setTrip(trip)
Trip data object or null to clear
setTrip(trip: TripDto | null): void
Updates the trip data and sets status to success.
setActiveTripId(id)
setActiveTripId(id: string | null): void
Sets the active trip ID.
setPhase(phase)
setPhase(phase: TripPhase): void
Updates the current trip phase.
setOfferAssignmentId(id)
Assignment ID or null to clear
setOfferAssignmentId(id: string | null): void
Sets the current offer assignment ID.
setOfferCountdown(expiresAtIso, remaining)
ISO timestamp when offer expires
setOfferCountdown(expiresAtIso?: string | null, remaining?: number | null): void
Sets the offer expiration time and countdown.
tickCountdown()
Decrements the countdown timer by 1 second (minimum 0).
setModalOpen(open)
Whether modal should be open
setModalOpen(open: boolean): void
Sets the modal open state.
setPassengerSlim(passenger)
Passenger information object or null
setPassengerSlim(p: DriverTripState['passengerSlim']): void
Sets the passenger information subset.
markAssignmentHandled(id)
Assignment ID to mark as handled
markAssignmentHandled(id: string | null): void
Adds the assignment ID to the handled list (prevents duplicate processing).
isAssignmentHandled(id)
isAssignmentHandled(id: string | null): boolean
Checks if an assignment has already been handled.
setArrivedPickup(at)
setArrivedPickup(at: string): void
Marks the timestamp when driver arrived at pickup location.
setInitialFare(total, currency)
Currency code (e.g., “CUP”)
setInitialFare(total: number | null, currency: string | null): void
Sets the base fare and currency, initializes live fare if not already set.
startWaiting(atIso)
ISO timestamp when waiting started
startWaiting(atIso: string): void
Initializes waiting period tracking. Sets waitingSinceAt and resets all waiting-related fields.
tickWaiting(deltaSec)
Seconds to increment (default: 1)
tickWaiting(deltaSec = 1): void
Increments the waiting seconds counter.
Additional fare amount to charge
Description of the penalty
applyWaitingPenalty(extra: number, text: string): void
Applies a waiting penalty to the fare. Updates liveFare = baseFare + extra.
setWaitingReason(reason)
Reason for the waiting charge
setWaitingReason(reason: string | null): void
Sets the waiting charge reason.
reset()
Resets the entire state to initial values.
Usage Example
import { Component, inject } from '@angular/core';
import { TripStore } from '@/app/store/trip/trip.store';
@Component({
selector: 'app-trip-status',
template: `
<div>
<p>Phase: {{ tripStore.phase() }}</p>
<p>Base Fare: {{ tripStore.baseFare() }} {{ trip()?.fareFinalCurrency }}</p>
<p>Live Fare: {{ tripStore.liveFare() }}</p>
@if (tripStore.waitingPenaltyApplied()) {
<p class="warning">
{{ tripStore.waitingPenaltyText() }}
</p>
}
<p>Waiting: {{ tripStore.waitingSeconds() }}s</p>
</div>
`
})
export class TripStatusComponent {
tripStore = inject(TripStore);
trip = this.tripStore.trip;
}
State Flow Example
// 1. Receive offer
tripStore.setActiveTripId('trip-123');
tripStore.setOfferAssignmentId('assignment-456');
tripStore.setPhase('assigned');
tripStore.setOfferCountdown(expiresAt, 30);
// 2. Accept offer
tripStore.markAssignmentHandled('assignment-456');
tripStore.setPhase('assigned');
// 3. Arrive at pickup
tripStore.setArrivedPickup(new Date().toISOString());
tripStore.startWaiting(new Date().toISOString());
tripStore.setPhase('arriving');
// 4. Tick waiting timer every second
setInterval(() => {
tripStore.tickWaiting(1);
// Apply penalty after threshold
if (tripStore.waitingSeconds() >= 300 && !tripStore.waitingPenaltyApplied()) {
const extra = 50;
tripStore.applyWaitingPenalty(extra, 'Recargo por espera prolongada');
tripStore.setWaitingReason('Pasajero no se presentó a tiempo');
}
}, 1000);
// 5. Start trip
tripStore.setPhase('in_progress');
// 6. Complete trip
tripStore.setPhase('completed');
// 7. Reset for next trip
tripStore.reset();