Skip to main content

Overview

The PassengerWsService handles real-time WebSocket communication using Socket.io. It connects passengers to the backend to receive live trip updates, driver assignments, and status changes. Location: src/app/core/services/socket/passenger-ws.service.ts Namespace: /passengers

Configuration

Connection Setup

private socket?: Socket;
private connected$ = new BehaviorSubject<boolean>(false);
readonly onConnected$ = this.connected$.asObservable();
The service connects to:
`${environment.wsBase}/passengers`
With configuration:
{
  transports: ['websocket'],
  auth: { token: accessToken },
  reconnection: true,
  reconnectionAttempts: Infinity,
  reconnectionDelayMax: 5000
}

Methods

connect()

Establishes WebSocket connection to the server.
connect(): void
Features:
  • Prevents duplicate connections (checks if already connected)
  • Automatically includes access token from AuthStore
  • Refreshes token on reconnection attempts
  • Sets up all event listeners
  • Logs all incoming events for debugging

Usage Example

import { PassengerWsService } from '@/app/core/services/socket/passenger-ws.service';

const wsService = inject(PassengerWsService);

// Connect to WebSocket
wsService.connect();

// Monitor connection status
wsService.onConnected$.subscribe(connected => {
  if (connected) {
    console.log('Connected to WebSocket');
  } else {
    console.log('Disconnected from WebSocket');
  }
});

disconnect()

Disconnects from the WebSocket server and cleans up listeners.
disconnect(): void

Usage Example

// Clean disconnect when leaving app or logging out
wsService.disconnect();

Event Observables

The service exposes RxJS Subjects for each event type. Subscribe to these to receive real-time updates.

onAssigningStarted$

Emitted when the system begins searching for a driver.
readonly onAssigningStarted$: Subject<AssigningStartedPayload>
AssigningStartedPayload
object
wsService.onAssigningStarted$.subscribe(payload => {
  console.log(`Searching for driver for trip ${payload.tripId}`);
  // Show "Finding driver..." UI
});

onDriverAssigned$

Emitted when a driver is assigned to the trip.
readonly onDriverAssigned$: Subject<DriverAssignedPayloadForPassenger>
DriverAssignedPayloadForPassenger
object
wsService.onDriverAssigned$.subscribe(payload => {
  console.log(`Driver ${payload.driverId} assigned to trip ${payload.tripId}`);
  // Fetch driver details and show "Driver assigned" UI
});

onDriverAccepted$

Emitted when driver accepts the trip (enriched with driver and vehicle details).
readonly onDriverAccepted$: Subject<DriverAcceptedEnrichedPayload>
DriverAcceptedEnrichedPayload
object
wsService.onDriverAccepted$.subscribe(payload => {
  console.log('Driver info:', payload.driver);
  console.log('Vehicle:', `${payload.vehicle.make} ${payload.vehicle.model}`);
  console.log('Plate:', payload.vehicle.plateNumber);
  // Display driver card with photo, rating, and vehicle details
});

onNoDriversFound$

Emitted when no drivers are available for the trip.
readonly onNoDriversFound$: Subject<NoDriversFoundPayload>
NoDriversFoundPayload
object
wsService.onNoDriversFound$.subscribe(payload => {
  console.log('No drivers available:', payload.reason);
  // Show "No drivers available" message
});

onDriverEnRoute$

Emitted when the driver is en route to pick up the passenger.
readonly onDriverEnRoute$: Subject<DriverEnRoutePayload>
DriverEnRoutePayload
object
wsService.onDriverEnRoute$.subscribe(payload => {
  console.log(`Driver ETA: ${payload.etaMinutes} minutes`);
  if (payload.driverPosition) {
    // Update driver marker on map
    updateDriverMarker(payload.driverPosition);
  }
});

onDriverArrived$

Emitted when the driver arrives at the pickup location.
readonly onDriverArrived$: Subject<DriverArrivedPickupPayload>
DriverArrivedPickupPayload
object
wsService.onDriverArrived$.subscribe(payload => {
  console.log('Driver has arrived!');
  // Show notification: "Your driver has arrived"
  // Play sound alert
});

onTripStarted$

Emitted when the trip begins (passenger is in the vehicle).
readonly onTripStarted$: Subject<TripStartedPayload>
TripStartedPayload
object
wsService.onTripStarted$.subscribe(payload => {
  console.log('Trip started!');
  // Update UI to show "In Progress"
  // Start tracking route
});

onTripCompleted$

Emitted when the trip is completed.
readonly onTripCompleted$: Subject<TripCompletedPayload>
TripCompletedPayload
object
wsService.onTripCompleted$.subscribe(payload => {
  console.log(`Trip completed! Fare: ${payload.fareTotal} ${payload.currency}`);
  // Show rating screen
  // Display fare breakdown
});

onTripCancelled$

Emitted when the trip is cancelled.
readonly onTripCancelled$: Subject<TripCancelledPayload>
TripCancelledPayload
object
wsService.onTripCancelled$.subscribe(payload => {
  console.log('Trip cancelled:', payload.reason);
  // Show cancellation message
  // Return to home screen
});

Complete Usage Example

import { Component, inject, OnInit, OnDestroy } from '@angular/core';
import { PassengerWsService } from '@/app/core/services/socket/passenger-ws.service';
import { Subject, takeUntil } from 'rxjs';

@Component({
  selector: 'app-trip-tracker',
  template: `
    <div *ngIf="connected">
      <p>Status: {{ tripStatus }}</p>
      <p *ngIf="etaMinutes">ETA: {{ etaMinutes }} min</p>
    </div>
  `
})
export class TripTrackerComponent implements OnInit, OnDestroy {
  private wsService = inject(PassengerWsService);
  private destroy$ = new Subject<void>();
  
  connected = false;
  tripStatus = 'pending';
  etaMinutes?: number;

  ngOnInit() {
    // Connect to WebSocket
    this.wsService.connect();

    // Monitor connection
    this.wsService.onConnected$
      .pipe(takeUntil(this.destroy$))
      .subscribe(connected => {
        this.connected = connected;
      });

    // Listen for trip events
    this.wsService.onAssigningStarted$
      .pipe(takeUntil(this.destroy$))
      .subscribe(payload => {
        this.tripStatus = 'Finding driver...';
      });

    this.wsService.onDriverAccepted$
      .pipe(takeUntil(this.destroy$))
      .subscribe(payload => {
        this.tripStatus = `Driver: ${payload.driver.name}`;
      });

    this.wsService.onDriverEnRoute$
      .pipe(takeUntil(this.destroy$))
      .subscribe(payload => {
        this.tripStatus = 'Driver en route';
        this.etaMinutes = payload.etaMinutes ?? undefined;
      });

    this.wsService.onDriverArrived$
      .pipe(takeUntil(this.destroy$))
      .subscribe(() => {
        this.tripStatus = 'Driver arrived!';
      });

    this.wsService.onTripStarted$
      .pipe(takeUntil(this.destroy$))
      .subscribe(() => {
        this.tripStatus = 'In progress';
      });

    this.wsService.onTripCompleted$
      .pipe(takeUntil(this.destroy$))
      .subscribe(payload => {
        this.tripStatus = 'Completed';
        console.log('Fare:', payload.fareTotal, payload.currency);
      });

    this.wsService.onTripCancelled$
      .pipe(takeUntil(this.destroy$))
      .subscribe(payload => {
        this.tripStatus = 'Cancelled';
        console.log('Reason:', payload.reason);
      });

    this.wsService.onNoDriversFound$
      .pipe(takeUntil(this.destroy$))
      .subscribe(() => {
        this.tripStatus = 'No drivers available';
      });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
    this.wsService.disconnect();
  }
}

TypeScript Interfaces

Event Payload Types

export type AssigningStartedPayload = {
  tripId: string;
  at: string;
  previousStatus: 'pending';
  currentStatus: 'assigning';
};

export type DriverAssignedPayloadForPassenger = {
  tripId: string;
  driverId: string;
  vehicleId: string;
  at: string;
  currentStatus: 'accepted';
};

export type DriverAcceptedEnrichedPayload = {
  tripId: string;
  at: string;
  currentStatus: 'accepted';
  driver: DriverSlimForPassenger;
  vehicle: VehicleSlimForPassenger;
};

export type DriverSlimForPassenger = {
  id: string;
  name: string;
  profilePictureUrl?: string | null;
  ratingAvg?: number | null;
  ratingCount?: number | null;
  phone?: string | null;
};

export type VehicleSlimForPassenger = {
  id: string;
  plateNumber?: string | null;
  color?: string | null;
  make?: string | null;
  model?: string | null;
  year?: number | null;
};

export type DriverEnRoutePayload = {
  tripId: string;
  driverId: string;
  at: string;
  etaMinutes?: number | null;
  driverPosition?: { lat: number; lng: number } | null;
};

export type DriverArrivedPickupPayload = {
  tripId: string;
  driverId: string;
  at: string;
  currentStatus: 'arriving';
};

export type TripStartedPayload = {
  tripId: string;
  driverId: string;
  at: string;
  currentStatus: 'in_progress';
};

export type TripCompletedPayload = {
  tripId: string;
  driverId: string;
  at: string;
  currentStatus: 'completed';
  fareTotal: number;
  currency: string;
};

export type TripCancelledPayload = {
  tripId: string;
  at: string;
  reason?: string | null;
  currentStatus: 'cancelled';
};

export type NoDriversFoundPayload = {
  tripId: string;
  at: string;
  reason?: string | null;
};

Debugging

The service logs all incoming events:
this.socket.onAny((evt: string, ...args: unknown[]) => {
  console.log('[WS:pax] >>>', evt, ...args);
});
Check the browser console to see all WebSocket events in real-time.

Connection Lifecycle


Build docs developers (and LLMs) love