# 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.