234 lines
9.6 KiB
Plaintext
234 lines
9.6 KiB
Plaintext
# Delivery Socket Integration — Implementation Plan
|
|
|
|
## Overview
|
|
|
|
This plan covers the end-to-end integration of the real-time delivery dispatch flow, as described in `mobile_delivery_app_integration.md`. The work involves:
|
|
|
|
1. **Interfaces** — Add typed contracts for the `delivery_offer` socket event and the REST respond endpoint.
|
|
2. **API Layer** — Implement `deliveryApi.ts` with `respondToOffer` and `getPendingOffer` calls.
|
|
3. **Redux (delivery slice)** — Wire thunks + reducer in `app/store/commonReducers/delivery/` for the respond flow.
|
|
4. **SocketService** — Implement real socket.io connection in `socketService.ts` (replacing the empty file), initialized globally.
|
|
5. **Global socket bootstrap** — Initialise/teardown the socket in `App.tsx` (or root navigator) so every screen benefits.
|
|
6. **NewJobRequestScreen** — Connect to Redux delivery state, replace current `job.newJobOffer` source with socket-driven data, and enhance UI.
|
|
7. **DashboardScreen** — Remove the mock 12-s timer and wire real socket online/offline control.
|
|
|
|
---
|
|
|
|
## User Review Required
|
|
|
|
> [!IMPORTANT]
|
|
> The integration guide specifies the socket event name as `delivery_offer` and the action values `"ACCEPT"` / `"REJECT"`. The existing `SocketEvents` enum uses `new_job`. The plan aligns with the integration guide naming while keeping the old mock service unchanged.
|
|
|
|
> [!WARNING]
|
|
> `socket.io-client` must already be installed. If not, run: `npm install socket.io-client`. Please confirm before executing.
|
|
|
|
> [!IMPORTANT]
|
|
> The base URL for the socket is the same as the REST API (`https://8415-202-8-116-13.ngrok-free.app`). Confirm this is correct for the socket server too.
|
|
|
|
---
|
|
|
|
## Open Questions
|
|
|
|
> [!NOTE]
|
|
> 1. **Timer duration** — The integration guide shows 15 s timer on the screen. The `newJobRequestScreen` already uses 15 s. Keep it or change to 30 s (constant `JOB_ACCEPT_TIMEOUT_SECONDS`)?
|
|
> 2. **Pending offer on app start** — Should `checkPendingOffers()` run every time the app launches (even before going online), or only after the auth/onboarding flow completes?
|
|
> 3. **NewJobRequestScreen UI** — User says "you can enhance it". Plan is to add an animated circular progress timer ring instead of plain text countdown, and a subtle pulsing accept button. Agree?
|
|
|
|
---
|
|
|
|
## Proposed Changes
|
|
|
|
### 1 — Interfaces
|
|
|
|
#### [MODIFY] [delivery.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/interfaces/delivery.ts)
|
|
|
|
Add typed contracts that mirror the integration guide's socket offer payload and REST respond endpoint.
|
|
|
|
```typescript
|
|
// Socket offer payload (delivery_offer event)
|
|
export interface DeliveryOffer {
|
|
deliveryId: string;
|
|
orderId: string;
|
|
pickupAddress: { label: string; lat: number; lng: number };
|
|
dropAddress: { label: string; lat: number; lng: number };
|
|
totalAmount: number;
|
|
distanceKm: number;
|
|
}
|
|
|
|
// REST: POST /delivery-partners/deliveries/{id}/respond
|
|
export type DeliveryAction = 'ACCEPT' | 'REJECT';
|
|
|
|
export interface RespondToOfferPayload {
|
|
action: DeliveryAction;
|
|
}
|
|
|
|
export interface RespondToOfferResponse {
|
|
success: boolean;
|
|
message: string;
|
|
deliveryId?: string;
|
|
}
|
|
|
|
// REST: GET /delivery-partners/deliveries/pending-offer
|
|
export interface PendingOfferResponse {
|
|
offer: DeliveryOffer | null;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2 — API Layer
|
|
|
|
#### [MODIFY] [deliveryApi.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/api/deliveryApi.ts)
|
|
|
|
Implement two API calls matching the integration guide:
|
|
|
|
- `respondToOffer(deliveryId, action)` → `POST /delivery-partners/deliveries/{id}/respond`
|
|
- `getPendingOffer()` → `GET /delivery-partners/deliveries/pending-offer`
|
|
|
|
---
|
|
|
|
### 3 — Redux Delivery Slice
|
|
|
|
#### [MODIFY] [thunk.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/store/commonReducers/delivery/thunk.ts)
|
|
|
|
Two async thunks:
|
|
- `respondToOfferThunk(deliveryId, action)` — calls `deliveryApi.respondToOffer`
|
|
- `checkPendingOfferThunk()` — calls `deliveryApi.getPendingOffer`
|
|
|
|
#### [MODIFY] [reducer.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/store/commonReducers/delivery/reducer.ts)
|
|
|
|
`DeliveryState`:
|
|
```typescript
|
|
interface DeliveryState {
|
|
currentOffer: DeliveryOffer | null; // live socket offer
|
|
respondLoading: boolean;
|
|
respondError: string | null;
|
|
pendingOfferLoading: boolean;
|
|
}
|
|
```
|
|
|
|
Actions:
|
|
- `setDeliveryOffer(offer)` — dispatched by socketService on `delivery_offer`
|
|
- `clearDeliveryOffer()` — dispatched after accept/reject
|
|
|
|
Thunk cases: pending/fulfilled/rejected for both thunks.
|
|
|
|
#### [MODIFY] [index.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/store/commonReducers/delivery/index.ts)
|
|
|
|
Export reducer, thunks, and actions.
|
|
|
|
#### [MODIFY] [commonReducers/index.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/store/commonReducers/index.ts)
|
|
|
|
Add `export * from './delivery'`.
|
|
|
|
#### [MODIFY] [rootReducer.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/store/rootReducer.ts)
|
|
|
|
Import and register `deliveryReducer` under key `delivery`.
|
|
|
|
---
|
|
|
|
### 4 — SocketService
|
|
|
|
#### [MODIFY] [socketService.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/services/socketService.ts)
|
|
|
|
Real `socket.io-client` implementation with the following contract:
|
|
|
|
```typescript
|
|
class SocketService {
|
|
connect(token: string): void // init socket with JWT auth
|
|
disconnect(): void
|
|
goOnline(): void // emit partner going online
|
|
goOffline(): void // emit partner going offline
|
|
updateLocation(coords): void // emit location update
|
|
on(event, callback): void
|
|
off(event, callback?): void
|
|
isConnected(): boolean
|
|
}
|
|
|
|
export const socketService = new SocketService();
|
|
```
|
|
|
|
Key behaviors:
|
|
- Connects to `BASE_URL` with `auth: { token }` — same URL as axiosInstance
|
|
- Listens for `delivery_offer` → dispatches `setDeliveryOffer` + navigates to `NewJobRequest` screen (store reference injected)
|
|
- Handles reconnection automatically (socket.io built-in)
|
|
- On `connect_error` logs the error
|
|
|
|
> [!NOTE]
|
|
> The socketService needs a reference to the Redux store and the navigation ref to dispatch and navigate globally. We will use a `bootstrapSocketService(store, navRef)` function called once in `App.tsx`.
|
|
|
|
---
|
|
|
|
### 5 — Global Bootstrap in App.tsx
|
|
|
|
#### [MODIFY] [App.tsx](file:///d:/SG-Delivery/sg-delivery-partner/app/App.tsx)
|
|
|
|
- Create a `navigationRef` using `createNavigationContainerRef`.
|
|
- After `PersistGate` hydrates, run `useEffect` that: reads `accessToken` from store → if present, calls `socketService.connect(token)`.
|
|
- Pass `navigationRef` to `NavigationContainer`.
|
|
- On token change (logout), call `socketService.disconnect()`.
|
|
- Also call `checkPendingOfferThunk()` on app start when token exists.
|
|
|
|
---
|
|
|
|
### 6 — NewJobRequestScreen Enhancement
|
|
|
|
#### [MODIFY] [newJobRequestScreen.tsx](file:///d:/SG-Delivery/sg-delivery-partner/app/features/screens/newJobRequestScreen/newJobRequestScreen.tsx)
|
|
|
|
**Data source change**: Read from `state.delivery.currentOffer` (the real socket offer) instead of `state.job.newJobOffer`. Map `DeliveryOffer` fields to UI fields.
|
|
|
|
**UI enhancements**:
|
|
- Animated circular countdown ring (using `Animated` API — no extra deps needed) instead of the plain text timer box.
|
|
- Pulsing green ring on the Accept button when < 5 s remain.
|
|
- Order amount shown prominently with a gradient badge.
|
|
- Accept/Reject dispatch `respondToOfferThunk` + `clearDeliveryOffer`.
|
|
|
|
#### [MODIFY] [newJobRequestScreen.styles.ts](file:///d:/SG-Delivery/sg-delivery-partner/app/features/screens/newJobRequestScreen/newJobRequestScreen.styles.ts)
|
|
|
|
Add styles for the circular timer, pulse ring, earnings badge.
|
|
|
|
---
|
|
|
|
### 7 — DashboardScreen Cleanup
|
|
|
|
#### [MODIFY] [dashboardScreen.tsx](file:///d:/SG-Delivery/sg-delivery-partner/app/features/screens/dashboardScreen/dashboardScreen.tsx)
|
|
|
|
- Remove the mock 12-s `setTimeout` that simulates a job offer.
|
|
- When going online → call `socketService.goOnline()`.
|
|
- When going offline → call `socketService.goOffline()`.
|
|
|
|
---
|
|
|
|
## File Change Summary
|
|
|
|
| File | Action | Purpose |
|
|
|---|---|---|
|
|
| `app/interfaces/delivery.ts` | MODIFY | DeliveryOffer, RespondToOfferPayload, PendingOfferResponse types |
|
|
| `app/api/deliveryApi.ts` | MODIFY | respondToOffer, getPendingOffer API calls |
|
|
| `app/store/commonReducers/delivery/thunk.ts` | MODIFY | respondToOfferThunk, checkPendingOfferThunk |
|
|
| `app/store/commonReducers/delivery/reducer.ts` | MODIFY | DeliveryState, setDeliveryOffer, clearDeliveryOffer |
|
|
| `app/store/commonReducers/delivery/index.ts` | MODIFY | barrel export |
|
|
| `app/store/commonReducers/index.ts` | MODIFY | add delivery export |
|
|
| `app/store/rootReducer.ts` | MODIFY | register deliveryReducer |
|
|
| `app/services/socketService.ts` | MODIFY | real socket.io-client implementation |
|
|
| `app/App.tsx` | MODIFY | global socket bootstrap + navigationRef |
|
|
| `app/features/screens/newJobRequestScreen/newJobRequestScreen.tsx` | MODIFY | socket-driven data + enhanced UI |
|
|
| `app/features/screens/newJobRequestScreen/newJobRequestScreen.styles.ts` | MODIFY | timer ring + pulse styles |
|
|
| `app/features/screens/dashboardScreen/dashboardScreen.tsx` | MODIFY | remove mock timer, add socket calls |
|
|
|
|
---
|
|
|
|
## Verification Plan
|
|
|
|
### Automated Checks
|
|
- TypeScript compilation: `npx tsc --noEmit` — must pass with 0 errors.
|
|
|
|
### Manual Verification
|
|
- App launches → socket connects (check logs: `[Socket] Connected`).
|
|
- Go online → `goOnline()` called.
|
|
- Backend dispatches `delivery_offer` → `NewJobRequestScreen` pops up from any screen.
|
|
- Accept → REST `POST /respond` with `ACCEPT` fires → screen navigates to `OrderAccepted`.
|
|
- Reject / timeout → REST `POST /respond` with `REJECT` fires → back to Dashboard.
|
|
- Kill app → relaunch → `checkPendingOfferThunk` fires → pending offer screen shown if offer exists.
|
|
- Go offline → socket `goOffline()` event sent.
|