From d9766b842cde87c760a1edb3ea3b59df4dd4b1df Mon Sep 17 00:00:00 2001 From: Tamojit Biswas Date: Tue, 21 Jul 2026 13:04:40 +0530 Subject: [PATCH] feat: implement KYC document viewing screen and foundational utility services --- .../completeProfileScreen.tsx | 2 +- .../documentsScreen/documentsScreen.styles.ts | 223 +++++++++++- .../documentsScreen/documentsScreen.tsx | 301 ++++++++++++---- .../screens/profileScreen/profileScreen.tsx | 2 +- app/services/apiClient.ts | 2 +- app/services/socketService.ts | 2 +- app/utils/constants.ts | 2 +- app/utils/helper.ts | 11 + app/utils/index.ts | 4 + implementation_plan_for_socket | 233 ++++++++++++ mobile_delivery_app_integration.md | 110 ++++++ package.json | 2 + yarn.lock | 334 +++++++++--------- 13 files changed, 985 insertions(+), 243 deletions(-) create mode 100644 app/utils/helper.ts create mode 100644 app/utils/index.ts create mode 100644 implementation_plan_for_socket create mode 100644 mobile_delivery_app_integration.md diff --git a/app/features/screens/completeProfileScreen/completeProfileScreen.tsx b/app/features/screens/completeProfileScreen/completeProfileScreen.tsx index 2610ce2..82d06d3 100644 --- a/app/features/screens/completeProfileScreen/completeProfileScreen.tsx +++ b/app/features/screens/completeProfileScreen/completeProfileScreen.tsx @@ -77,7 +77,7 @@ export const CompleteProfileScreen: React.FC = () => { ) .unwrap() .then(() => { - navigation.navigate(RouteNames.Kyc); + navigation.replace(RouteNames.Kyc); }) .catch(error => { Alert.alert('Error', error.message); diff --git a/app/features/screens/documentsScreen/documentsScreen.styles.ts b/app/features/screens/documentsScreen/documentsScreen.styles.ts index a252ee2..0b9fa89 100644 --- a/app/features/screens/documentsScreen/documentsScreen.styles.ts +++ b/app/features/screens/documentsScreen/documentsScreen.styles.ts @@ -1,6 +1,8 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Dimensions } from 'react-native'; import { typography, spacing, borderRadius, shadow } from '@theme'; +const { width, height } = Dimensions.get('window'); + export const getStyles = (colors: any) => StyleSheet.create({ container: { @@ -9,35 +11,81 @@ export const getStyles = (colors: any) => }, scrollContainer: { paddingHorizontal: spacing.lg, + paddingTop: spacing.md, paddingBottom: spacing.xxl, }, - docCard: { + summaryCard: { + backgroundColor: colors.primaryLight, + borderRadius: borderRadius.lg, + padding: spacing.lg, + marginBottom: spacing.lg, + borderWidth: 1, + borderColor: colors.border, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', + }, + summaryLeft: { + flex: 1, + }, + summaryTitle: { + fontSize: typography.fontSize.lg, + fontWeight: typography.fontWeight.bold, + color: colors.primaryDark || colors.primary, + marginBottom: spacing.xs, + }, + summarySub: { + fontSize: typography.fontSize.sm, + color: colors.textSecondary, + }, + summaryBadge: { + backgroundColor: colors.primary, + paddingHorizontal: spacing.md, + paddingVertical: spacing.xs, + borderRadius: borderRadius.full, + }, + summaryBadgeText: { + fontSize: typography.fontSize.xs, + fontWeight: typography.fontWeight.bold, + color: colors.white, + }, + sectionTitle: { + fontSize: typography.fontSize.md, + fontWeight: typography.fontWeight.bold, + color: colors.text, + marginBottom: spacing.md, + }, + docCard: { backgroundColor: colors.cardBg, - borderRadius: borderRadius.md, - padding: spacing.md, - marginBottom: spacing.sm, + borderRadius: borderRadius.lg, + padding: spacing.lg, + marginBottom: spacing.md, borderWidth: 1, borderColor: colors.border, ...shadow.sm, }, - docLeft: { + cardHeader: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: spacing.md, + }, + cardHeaderLeft: { flexDirection: 'row', alignItems: 'center', flex: 1, + marginRight: spacing.sm, }, iconContainer: { - width: 40, - height: 40, - borderRadius: borderRadius.full, + width: 44, + height: 44, + borderRadius: borderRadius.md, backgroundColor: colors.primaryLight, justifyContent: 'center', alignItems: 'center', marginRight: spacing.md, }, - docInfo: { + docTypeContainer: { flex: 1, }, docTitle: { @@ -46,22 +94,161 @@ export const getStyles = (colors: any) => color: colors.text, marginBottom: 2, }, - docSub: { + docNumber: { fontSize: typography.fontSize.xs, color: colors.textSecondary, - }, - statusContainer: { - flexDirection: 'row', - alignItems: 'center', + fontWeight: typography.fontWeight.medium, }, statusBadge: { paddingHorizontal: spacing.sm, paddingVertical: 4, borderRadius: borderRadius.sm, - marginRight: spacing.sm, + alignSelf: 'flex-start', }, statusText: { - fontSize: 10, - fontWeight: 'bold', + fontSize: typography.fontSize.xs - 2, + fontWeight: typography.fontWeight.bold, + letterSpacing: 0.5, + }, + divider: { + height: 1, + backgroundColor: colors.divider, + marginVertical: spacing.md, + }, + detailsRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: spacing.md, + }, + detailItem: { + flex: 1, + }, + detailLabel: { + fontSize: typography.fontSize.xs - 1, + color: colors.textSecondary, + marginBottom: 2, + }, + detailValue: { + fontSize: typography.fontSize.xs, + color: colors.text, + fontWeight: typography.fontWeight.semibold, + }, + viewButton: { + backgroundColor: colors.inputBg, + borderWidth: 1, + borderColor: colors.border, + borderRadius: borderRadius.md, + paddingVertical: spacing.sm, + paddingHorizontal: spacing.md, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + viewButtonText: { + fontSize: typography.fontSize.sm, + fontWeight: typography.fontWeight.semibold, + color: colors.primary, + marginRight: spacing.xs, + }, + filePill: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: colors.inputBg, + paddingHorizontal: spacing.md, + paddingVertical: spacing.xs + 2, + borderRadius: borderRadius.sm, + maxWidth: '70%', + }, + fileNameText: { + fontSize: typography.fontSize.xs, + color: colors.textSecondary, + marginLeft: spacing.xs, + }, + // Full Screen Modal Styles + modalContainer: { + flex: 1, + backgroundColor: colors.background, + }, + modalHeader: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: spacing.lg, + paddingTop: spacing.xl, + paddingBottom: spacing.md, + backgroundColor: colors.cardBg, + borderBottomWidth: 1, + borderBottomColor: colors.border, + ...shadow.sm, + }, + modalHeaderInfo: { + flex: 1, + marginRight: spacing.md, + }, + modalTitle: { + fontSize: typography.fontSize.lg, + fontWeight: typography.fontWeight.bold, + color: colors.text, + }, + modalSubtitle: { + fontSize: typography.fontSize.xs, + color: colors.textSecondary, + marginTop: 2, + }, + closeButton: { + width: 38, + height: 38, + borderRadius: borderRadius.full, + backgroundColor: colors.inputBg, + justifyContent: 'center', + alignItems: 'center', + }, + viewerContainer: { + flex: 1, + backgroundColor: '#121212', + justifyContent: 'center', + alignItems: 'center', + }, + pdfViewer: { + flex: 1, + width: width, + height: height, + backgroundColor: '#121212', + }, + imageViewer: { + width: '100%', + height: '100%', + }, + loadingContainer: { + position: 'absolute', + zIndex: 10, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'rgba(0,0,0,0.4)', + padding: spacing.xl, + borderRadius: borderRadius.lg, + }, + loadingText: { + color: colors.white, + marginTop: spacing.md, + fontSize: typography.fontSize.sm, + fontWeight: typography.fontWeight.medium, + }, + emptyContainer: { + alignItems: 'center', + justifyContent: 'center', + paddingVertical: spacing.huge, + }, + emptyTitle: { + fontSize: typography.fontSize.lg, + fontWeight: typography.fontWeight.bold, + color: colors.text, + marginTop: spacing.md, + }, + emptySub: { + fontSize: typography.fontSize.sm, + color: colors.textSecondary, + marginTop: spacing.xs, }, }); diff --git a/app/features/screens/documentsScreen/documentsScreen.tsx b/app/features/screens/documentsScreen/documentsScreen.tsx index d37338f..ea8fcdc 100644 --- a/app/features/screens/documentsScreen/documentsScreen.tsx +++ b/app/features/screens/documentsScreen/documentsScreen.tsx @@ -1,87 +1,266 @@ -import React from 'react'; -import { View, Text, ScrollView, TouchableOpacity, Alert } from 'react-native'; +import React, { useEffect, useState } from 'react'; +import { + View, + Text, + ScrollView, + TouchableOpacity, + Modal, + Image, + ActivityIndicator, + SafeAreaView, +} from 'react-native'; +import Pdf from 'react-native-pdf'; import { useAppTheme } from '@theme'; import { ScreenHeader } from '@components'; -import { ClipboardIcon, ChevronRightIcon } from '@icons'; +import { ClipboardIcon, CloseIcon, ChevronRightIcon } from '@icons'; import { useNavigation } from '@react-navigation/native'; +import { getFullUrl, isPdf } from '@utils/helper'; import { getStyles } from './documentsScreen.styles'; +import { KycDocUploadResponseDocument } from '@interfaces'; +import { formatDate } from '@utils/formatters'; +import { useAppDispatch, useAppSelector } from '@store'; +import { fetchMyKyc } from '../kycScreen'; export const DocumentsScreen: React.FC = () => { const { colors } = useAppTheme(); const styles = getStyles(colors); const navigation = useNavigation(); + const dispatch = useAppDispatch(); - const documentsList = [ - { id: 'dl', title: 'Driving License', sub: 'Verified', status: 'verified' }, - { id: 'aadhaar', title: 'Aadhaar Card', sub: 'Verified', status: 'verified' }, - { id: 'rc', title: 'Vehicle RC', sub: 'Verified', status: 'verified' }, - { id: 'insurance', title: 'Insurance', sub: 'Expires on 12 May 2026', status: 'expiring' }, - { id: 'vehicle_photo', title: 'Vehicle Photo', sub: 'Verified', status: 'verified' }, - { id: 'profile_photo', title: 'Profile Photo', sub: 'Verified', status: 'verified' }, - ]; + const [selectedDoc, setSelectedDoc] = + useState(null); + const [isModalLoading, setIsModalLoading] = useState(true); + const { myKycResponse } = useAppSelector(state => state.kyc); - const handleDocClick = (title: string) => { - Alert.alert('Document Preview', `Opening document view for ${title}...`); + useEffect(() => { + dispatch(fetchMyKyc()); + }, []); + + const getStatusBadgeConfig = (status: string) => { + switch (status?.toUpperCase()) { + case 'APPROVED': + case 'VERIFIED': + return { + bg: colors.successLight, + text: colors.success, + label: 'APPROVED', + }; + case 'PENDING': + return { + bg: colors.warningLight, + text: colors.warning, + label: 'PENDING', + }; + case 'REJECTED': + return { + bg: colors.errorLight, + text: colors.error, + label: 'REJECTED', + }; + default: + return { + bg: colors.primaryLight, + text: colors.primary, + label: status?.toUpperCase() || 'ACTIVE', + }; + } }; + const handleOpenDocModal = (doc: KycDocUploadResponseDocument) => { + setIsModalLoading(true); + setSelectedDoc(doc); + }; + + const approvedCount = myKycResponse?.documents?.filter( + d => d.status === 'APPROVED', + ).length; + return ( - {/* Navigation Screen Header */} + {/* Screen Header */} navigation.goBack()} /> - - {documentsList.map((doc) => ( - handleDocClick(doc.title)} - > - - - - - - {doc.title} - {doc.sub} - + + {/* Document Status Overview Banner */} + + + Document Verification + + {approvedCount} of {myKycResponse?.documents?.length} Documents + Verified + + + + + {approvedCount === myKycResponse?.documents?.length + ? 'VERIFIED' + : 'PENDING'} + + + + + Uploaded Documents + + {myKycResponse?.documents?.length === 0 ? ( + + + No Documents Found + + Your uploaded documents will appear here. + + + ) : ( + myKycResponse?.documents?.map(doc => { + const statusConfig = getStatusBadgeConfig(doc.status); + const formattedDocName = + doc.documentType === 'Pan' ? 'PAN Card' : doc.documentType; + + return ( + handleOpenDocModal(doc)} + > + {/* Header Row */} + + + + + + + + {formattedDocName} + + + Doc #: {doc.documentNumber} + + + + + + + {statusConfig.label} + + + + + {/* Details Section */} + + + Uploaded On + + {formatDate(doc.createdAt)} + + + + {doc.verifiedAt && ( + + Verified On + + {formatDate(doc.verifiedAt)} + + + )} + + + + + {/* View Button Footer */} + + View Document + + + + ); + }) + )} + + + {/* Full Screen View Modal */} + setSelectedDoc(null)} + > + + {/* Modal Header */} + + + + {selectedDoc?.documentType === 'Pan' + ? 'PAN Card' + : selectedDoc?.documentType}{' '} + Preview + + + {selectedDoc?.fileName} + - - - - {doc.status === 'verified' ? 'VERIFIED' : 'ACTIVE'} - + setSelectedDoc(null)} + > + + + + + {/* Modal Body / Viewer */} + + {isModalLoading && ( + + + Loading file preview... - - - - ))} - + )} + + {selectedDoc && + isPdf(selectedDoc.fileName || selectedDoc.fileUrl) ? ( + setIsModalLoading(false)} + onError={error => { + setIsModalLoading(false); + console.log('PDF Load Error:', error); + }} + style={styles.pdfViewer} + /> + ) : ( + selectedDoc && ( + setIsModalLoading(true)} + onLoadEnd={() => setIsModalLoading(false)} + onError={() => setIsModalLoading(false)} + /> + ) + )} + + + ); }; diff --git a/app/features/screens/profileScreen/profileScreen.tsx b/app/features/screens/profileScreen/profileScreen.tsx index dc8b46b..7b742bf 100644 --- a/app/features/screens/profileScreen/profileScreen.tsx +++ b/app/features/screens/profileScreen/profileScreen.tsx @@ -54,7 +54,7 @@ export const ProfileScreen: React.FC = () => { const handleMenuPress = (item: any) => { if (item.route) { - navigation.navigate(item.route); + navigation.navigate(RouteNames.Documents); } else { Alert.alert('Menu Option', `Viewing ${item.title} section...`); } diff --git a/app/services/apiClient.ts b/app/services/apiClient.ts index da29139..ff70287 100644 --- a/app/services/apiClient.ts +++ b/app/services/apiClient.ts @@ -15,7 +15,7 @@ const STORAGE_KEYS = { } as const; // ─── Config ────────────────────────────────────────────────────────────────── -const BASE_URL = 'https://53a9-202-8-116-13.ngrok-free.app'; // TODO: replace with your actual base URL +const BASE_URL = 'https://sg-delivery-api.convexsol.co'; // TODO: replace with your actual base URL // ─── Token Helpers ─────────────────────────────────────────────────────────── export const tokenManager = { diff --git a/app/services/socketService.ts b/app/services/socketService.ts index 1da1616..58deacd 100644 --- a/app/services/socketService.ts +++ b/app/services/socketService.ts @@ -6,7 +6,7 @@ import { setDeliveryOffer } from '@store/commonReducers/delivery'; import { RouteNames } from '@utils/constants'; // Must match the REST API base URL (without trailing slash) -const BASE_URL = 'https://53a9-202-8-116-13.ngrok-free.app'; +const BASE_URL = 'https://sg-delivery-api.convexsol.co'; type Callback = (data: any) => void; diff --git a/app/utils/constants.ts b/app/utils/constants.ts index 0bedfc1..fd6e50d 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -47,7 +47,7 @@ export enum SocketEvents { NewJob = 'new_job', DeliveryOffer = 'delivery_offer', JobCancelled = 'job_cancelled', - LocationUpdate = 'location_update', + LocationUpdate = 'driver_location_update', GoOnline = 'go_online', GoOffline = 'go_offline', } diff --git a/app/utils/helper.ts b/app/utils/helper.ts new file mode 100644 index 0000000..48e7216 --- /dev/null +++ b/app/utils/helper.ts @@ -0,0 +1,11 @@ +export const getFullUrl = (url?: string) => { + const BASE_URL = 'https://sg-delivery-api.convexsol.co'; + if (!url) return ''; + return url.startsWith('/') ? `${BASE_URL}${url}` : url; +}; + +export const isPdf = (fileNameOrUrl?: string) => { + if (!fileNameOrUrl) return false; + const clean = fileNameOrUrl.toLowerCase().split('?')[0]; + return clean.endsWith('.pdf'); +}; diff --git a/app/utils/index.ts b/app/utils/index.ts new file mode 100644 index 0000000..b131143 --- /dev/null +++ b/app/utils/index.ts @@ -0,0 +1,4 @@ +export * from './validators'; +export * from './helper'; +export * from './constants'; +export * from './formatters'; diff --git a/implementation_plan_for_socket b/implementation_plan_for_socket new file mode 100644 index 0000000..3468e82 --- /dev/null +++ b/implementation_plan_for_socket @@ -0,0 +1,233 @@ +# 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. diff --git a/mobile_delivery_app_integration.md b/mobile_delivery_app_integration.md new file mode 100644 index 0000000..1c1c470 --- /dev/null +++ b/mobile_delivery_app_integration.md @@ -0,0 +1,110 @@ +# Mobile App Integration Guide (Delivery Partner) + +This developer guide details how to implement the real-time delivery dispatch flow in the Delivery Partner mobile application (e.g. React Native, Flutter, or Native iOS/Android). + +--- + +### Step 1: Establish Socket Connection (on Login / Going Online) +Initialize the socket connection and pass the JWT token in the handshake authentication options. + +```javascript +import { io } from "socket.io-client"; + +// Replace with your backend server URL +const socket = io("http://YOUR_BACKEND_IP:8000", { + auth: { + token: DRIVER_JWT_TOKEN // Pass the verified driver token here + } +}); + +socket.on("connect", () => { + console.log("Connected to dispatch server!"); +}); +``` + +--- + +### Step 2: Listen for Incoming Offers (`delivery_offer`) +When the socket receives the `delivery_offer` event, display a modal or popup banner in your UI with the order details and Accept/Reject buttons. + +```javascript +socket.on("delivery_offer", (offer) => { + console.log("New offer received:", offer); + + // Example UI action: Open the screen showing details + showNewOfferModal({ + deliveryId: offer.deliveryId, + orderId: offer.orderId, + pickupAddress: offer.pickupAddress.label, + dropAddress: offer.dropAddress.label, + amount: offer.totalAmount, + distance: offer.distanceKm + }); +}); +``` + +--- + +### Step 3: Respond to the Offer (Accept / Reject) +When the driver clicks Accept or Reject, hit the existing REST endpoint `POST /delivery-partners/deliveries/{id}/respond`. + +```javascript +async function respondToOffer(deliveryId, isAccepted) { + const action = isAccepted ? "ACCEPT" : "REJECT"; + + try { + const response = await fetch(`http://YOUR_BACKEND_IP:8000/delivery-partners/deliveries/${deliveryId}/respond`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${DRIVER_JWT_TOKEN}` + }, + body: JSON.stringify({ action }) + }); + + const result = await response.json(); + if (response.ok) { + console.log(`Successfully responded with action: ${action}`); + // Close the modal or update app state + } else { + console.error(result.message); + } + } catch (error) { + console.error("Network error: ", error); + } +} +``` + +--- + +### Step 4: Handle App Restarts / Notification Clicks +If the app was closed when the offer was dispatched or opened via a push notification: + +1. On app launch, call `GET /delivery-partners/deliveries/pending-offer`. +2. If the offer is not `null`, load the accept/reject screen immediately. + +```javascript +async function checkPendingOffers() { + const response = await fetch("http://YOUR_BACKEND_IP:8000/delivery-partners/deliveries/pending-offer", { + headers: { 'Authorization': `Bearer ${DRIVER_JWT_TOKEN}` } + }); + + const data = await response.json(); + if (data.offer) { + showNewOfferModal(data.offer); // Load the screen if there's a pending offer + } +} +``` + +--- + +### App State Lifecycle & Flow Handling + +Depending on the operational state of the delivery partner's mobile application, the offer is received and presented to the rider in different ways: + +| App State | Delivery Offer Trigger Mechanism | What Happens | +| :--- | :--- | :--- | +| **Foreground (Open & Active)** | **WebSocket Listener** | The `delivery_offer` WebSocket event fires instantly (handled in **Step 2**). The modal or bottom sheet overlay pops up immediately in front of the rider. | +| **Background (Open but Minimized)** | **Push Notification (FCM / APNs)** | Mobile OSs sleep WebSocket connections when minimized. The backend dispatches a push notification. Tapping the notification brings the app to the foreground and calls `checkPendingOffers()` (handled in **Step 4**) to display the offer. | +| **Closed / Killed (Not Running)** | **Push Notification (FCM / APNs)** | The backend dispatches a push notification. Tapping it starts up the app, which then runs `checkPendingOffers()` on load to retrieve the pending offer. | + diff --git a/package.json b/package.json index 2a3b909..e68fde1 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,11 @@ "axios": "^1.18.1", "react": "19.2.3", "react-native": "0.86.0", + "react-native-blob-util": "^0.24.10", "react-native-geolocation-service": "^5.3.1", "react-native-gesture-handler": "^2.32.0", "react-native-maps": "^1.29.0", + "react-native-pdf": "^7.0.4", "react-native-reanimated": "^4.5.0", "react-native-safe-area-context": "^5.8.0", "react-native-screens": "^4.25.2", diff --git a/yarn.lock b/yarn.lock index ed5a01b..76b692c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,7 +16,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz" integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg== -"@babel/core@*", "@babel/core@^7.0.0", "@babel/core@^7.0.0 || ^8.0.0-0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.11.0", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.25.2", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.8.0": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.25.2": version "7.29.7" resolved "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz" integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA== @@ -37,7 +37,7 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/eslint-parser@^7.12.0", "@babel/eslint-parser@^7.25.1": +"@babel/eslint-parser@^7.25.1": version "7.29.7" resolved "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.7.tgz" integrity sha512-zxt+UJTOMKvUt3yOg+D58MLuz334pHp93qifMFcjIIO+9hN6t+ufw2gi7vDPMpxvfnHRR+3VVXvIjineCcgyXw== @@ -1366,7 +1366,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -1522,7 +1522,7 @@ dependencies: joi "^17.2.1" -"@react-native-community/cli@*", "@react-native-community/cli@20.1.0": +"@react-native-community/cli@20.1.0": version "20.1.0" resolved "https://registry.npmjs.org/@react-native-community/cli/-/cli-20.1.0.tgz" integrity sha512-441WsVtRe4nGJ9OzA+QMU1+22lA6Q2hRWqqIMKD0wjEMLqcSfOZyu2UL9a/yRpL/dRpyUsU4n7AxqKfTKO/Csg== @@ -1701,7 +1701,7 @@ hermes-parser "0.36.0" nullthrows "^1.1.1" -"@react-native/metro-config@*", "@react-native/metro-config@0.86.0": +"@react-native/metro-config@0.86.0": version "0.86.0" resolved "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.86.0.tgz" integrity sha512-7v+xbTeEci9ZcQ/Z1OqI4RXcqN69wSMDYL5BAMvOReZ7U04+aDQ0/SQhClYPn6x2/RxM4WzMKSAuNyLKqvYVtw== @@ -1716,6 +1716,11 @@ resolved "https://registry.npmjs.org/@react-native/new-app-screen/-/new-app-screen-0.86.0.tgz" integrity sha512-hgZYg2I6gk+U0PWpV/JWg02KaS+FYYQ7OpFPVyG5RbFRAKNJw38mp8up3JqWFCMHeUH/kjWLEbJsCA0O5Qx2MA== +"@react-native/normalize-color@*": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91" + integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA== + "@react-native/normalize-colors@0.86.0": version "0.86.0" resolved "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.86.0.tgz" @@ -1776,7 +1781,7 @@ sf-symbols-typescript "^2.1.0" warn-once "^0.1.1" -"@react-navigation/native@^7.3.4", "@react-navigation/native@^7.3.5": +"@react-navigation/native@^7.3.4": version "7.3.5" resolved "https://registry.npmjs.org/@react-navigation/native/-/native-7.3.5.tgz" integrity sha512-lzcRpMoLCIypXXKm8KhpyZ81XpQBO/i8Oy1due4kUR97kG8kBlJxKUeP8yNnT5Fcl85rNdLXWbpwEr+9HPu4Ig== @@ -1958,7 +1963,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.2.25 || ^19", "@types/react@^19.1.0", "@types/react@^19.1.1", "@types/react@^19.2.0": +"@types/react@*", "@types/react@^19.2.0": version "19.2.17" resolved "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz" integrity sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw== @@ -1987,7 +1992,7 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^8.0.0", "@typescript-eslint/eslint-plugin@^8.36.0": +"@typescript-eslint/eslint-plugin@^8.36.0": version "8.62.1" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz" integrity sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA== @@ -2001,7 +2006,7 @@ natural-compare "^1.4.0" ts-api-utils "^2.5.0" -"@typescript-eslint/parser@^8.36.0", "@typescript-eslint/parser@^8.62.1": +"@typescript-eslint/parser@^8.36.0": version "8.62.1" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz" integrity sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA== @@ -2029,7 +2034,7 @@ "@typescript-eslint/types" "8.62.1" "@typescript-eslint/visitor-keys" "8.62.1" -"@typescript-eslint/tsconfig-utils@^8.62.1", "@typescript-eslint/tsconfig-utils@8.62.1": +"@typescript-eslint/tsconfig-utils@8.62.1", "@typescript-eslint/tsconfig-utils@^8.62.1": version "8.62.1" resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz" integrity sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g== @@ -2045,7 +2050,7 @@ debug "^4.4.3" ts-api-utils "^2.5.0" -"@typescript-eslint/types@^8.62.1", "@typescript-eslint/types@8.62.1": +"@typescript-eslint/types@8.62.1", "@typescript-eslint/types@^8.62.1": version "8.62.1" resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz" integrity sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q== @@ -2065,7 +2070,7 @@ tinyglobby "^0.2.15" ts-api-utils "^2.5.0" -"@typescript-eslint/utils@^8.0.0", "@typescript-eslint/utils@8.62.1": +"@typescript-eslint/utils@8.62.1", "@typescript-eslint/utils@^8.0.0": version "8.62.1" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz" integrity sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g== @@ -2121,16 +2126,11 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.15.0, acorn@^8.9.0: +acorn@^8.15.0, acorn@^8.9.0: version "8.17.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz" integrity sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg== -agent-base@^7.1.2: - version "7.1.4" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" - integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== - agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" @@ -2138,6 +2138,11 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + ajv@^6.12.4: version "6.15.0" resolved "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz" @@ -2473,6 +2478,11 @@ balanced-match@^4.0.2: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz" integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== +base-64@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" + integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== + base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" @@ -2544,7 +2554,7 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -browserslist@^4.24.0, browserslist@^4.28.1, "browserslist@>= 4.21.0": +browserslist@^4.24.0, browserslist@^4.28.1: version "4.28.4" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz" integrity sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw== @@ -2575,7 +2585,7 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bytes@~3.1.2, bytes@3.1.2: +bytes@3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -2734,16 +2744,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + color-string@^1.9.0: version "1.9.1" resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" @@ -2876,6 +2886,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" +crypto-js@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + css-select@^5.1.0: version "5.2.2" resolved "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz" @@ -2937,27 +2952,20 @@ dayjs@^1.8.15: resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz" integrity sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA== -debug@^2.6.9: +debug@2.6.9, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3, debug@~4.4.1, debug@4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3, debug@~4.4.1: version "4.4.3" resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - decamelize@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" @@ -3013,12 +3021,21 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -depd@~2.0.0, depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -destroy@~1.2.0, destroy@1.2.0: +deprecated-react-native-prop-types@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz#c10c6ee75ff2b6de94bb127f142b814e6e08d9ab" + integrity sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA== + dependencies: + "@react-native/normalize-color" "*" + invariant "*" + prop-types "*" + +destroy@1.2.0, destroy@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -3407,14 +3424,6 @@ eslint-plugin-react@^7.37.5: string.prototype.matchall "^4.0.12" string.prototype.repeat "^1.0.0" -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" @@ -3423,6 +3432,14 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" @@ -3438,7 +3455,7 @@ eslint-visitor-keys@^5.0.0: resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz" integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== -"eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0", "eslint@^3.17.0 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.5.0 || ^8.0.0 || ^9.0.0", "eslint@^8.0.0 || ^9.0.0", eslint@^8.1.0, eslint@^8.19.0, "eslint@^8.57.0 || ^9.0.0 || ^10.0.0", eslint@>=4.19.1, eslint@>=7.0.0: +eslint@^8.19.0: version "8.57.1" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz" integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== @@ -3751,6 +3768,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -3848,6 +3870,15 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob@13.0.6: + version "13.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.6.tgz#078666566a425147ccacfbd2e332deb66a2be71d" + integrity sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw== + dependencies: + minimatch "^10.2.2" + minipass "^7.1.3" + path-scurry "^2.0.2" + glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" @@ -3963,13 +3994,6 @@ hermes-estree@0.36.0: resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.36.0.tgz" integrity sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w== -hermes-parser@^0.25.1: - version "0.25.1" - resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz" - integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== - dependencies: - hermes-estree "0.25.1" - hermes-parser@0.35.0: version "0.35.0" resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz" @@ -3984,6 +4008,13 @@ hermes-parser@0.36.0: dependencies: hermes-estree "0.36.0" +hermes-parser@^0.25.1: + version "0.25.1" + resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz" + integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== + dependencies: + hermes-estree "0.25.1" + hoist-non-react-statics@^3.3.0: version "3.3.2" resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" @@ -4096,7 +4127,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4, inherits@2: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4110,7 +4141,7 @@ internal-slot@^1.1.0: hasown "^2.0.2" side-channel "^1.1.0" -invariant@^2.2.4: +invariant@*, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -4648,7 +4679,7 @@ jest-resolve-dependencies@^29.7.0: jest-regex-util "^29.6.3" jest-snapshot "^29.7.0" -jest-resolve@*, jest-resolve@^29.7.0: +jest-resolve@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== @@ -4792,7 +4823,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@*, jest@^29.6.3: +jest@^29.6.3: version "29.7.0" resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== @@ -5002,6 +5033,11 @@ lru-cache@^10.2.0: resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.0: + version "11.5.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.5.2.tgz#00e16665c90c620fba14a3c368732a976493f760" + integrity sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" @@ -5086,7 +5122,7 @@ metro-cache@0.84.4: https-proxy-agent "^7.0.5" metro-core "0.84.4" -metro-config@^0.84.3, metro-config@0.84.4: +metro-config@0.84.4, metro-config@^0.84.3: version "0.84.4" resolved "https://registry.npmjs.org/metro-config/-/metro-config-0.84.4.tgz" integrity sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q== @@ -5100,7 +5136,7 @@ metro-config@^0.84.3, metro-config@0.84.4: metro-runtime "0.84.4" yaml "^2.6.1" -metro-core@^0.84.3, metro-core@0.84.4: +metro-core@0.84.4, metro-core@^0.84.3: version "0.84.4" resolved "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz" integrity sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ== @@ -5139,7 +5175,7 @@ metro-resolver@0.84.4: dependencies: flow-enums-runtime "^0.0.6" -metro-runtime@^0.84.3, metro-runtime@0.84.4: +metro-runtime@0.84.4, metro-runtime@^0.84.3: version "0.84.4" resolved "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.4.tgz" integrity sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q== @@ -5147,7 +5183,7 @@ metro-runtime@^0.84.3, metro-runtime@0.84.4: "@babel/runtime" "^7.25.0" flow-enums-runtime "^0.0.6" -metro-source-map@^0.84.3, metro-source-map@0.84.4: +metro-source-map@0.84.4, metro-source-map@^0.84.3: version "0.84.4" resolved "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz" integrity sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g== @@ -5205,7 +5241,7 @@ metro-transform-worker@0.84.4: metro-transform-plugins "0.84.4" nullthrows "^1.1.1" -metro@^0.84.3, metro@0.84.4: +metro@0.84.4, metro@^0.84.3: version "0.84.4" resolved "https://registry.npmjs.org/metro/-/metro-0.84.4.tgz" integrity sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA== @@ -5258,16 +5294,16 @@ micromatch@^4.0.4, micromatch@^4.0.8: braces "^3.0.3" picomatch "^2.3.1" -mime-db@^1.54.0, "mime-db@>= 1.43.0 < 2": - version "1.54.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" - integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" @@ -5275,30 +5311,23 @@ mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34: dependencies: mime-db "1.52.0" -mime-types@^3.0.0: +mime-types@^3.0.0, mime-types@^3.0.1: version "3.0.2" resolved "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz" integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== dependencies: mime-db "^1.54.0" -mime-types@^3.0.1: - version "3.0.2" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz" - integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== - dependencies: - mime-db "^1.54.0" - -mime@^2.4.1: - version "2.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - mime@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mime@^2.4.1: + version "2.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" @@ -5330,7 +5359,7 @@ minipass@^4.2.4: resolved "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz" integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2, minipass@^7.1.3: version "7.1.3" resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz" integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== @@ -5345,16 +5374,16 @@ mkdirp@^1.0.4: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -ms@^2.1.3, ms@2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@2.1.3, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + nanoid@^3.3.11: version "3.3.15" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz" @@ -5365,6 +5394,11 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + negotiator@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz" @@ -5375,11 +5409,6 @@ negotiator@~0.6.4: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz" integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - nocache@^3.0.1: version "3.0.4" resolved "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz" @@ -5582,14 +5611,7 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" -p-limit@^2.0.0: - version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -5684,6 +5706,14 @@ path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.2.tgz#6be0d0ee02a10d9e0de7a98bae65e182c9061f85" + integrity sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + picocolors@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" @@ -5694,7 +5724,7 @@ picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz" integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== -"picomatch@^3 || ^4", picomatch@^4.0.4: +picomatch@^4.0.4: version "4.0.4" resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz" integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== @@ -5728,7 +5758,7 @@ prelude-ls@^1.2.1: resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@>=2, prettier@2.8.8: +prettier@2.8.8: version "2.8.8" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== @@ -5757,7 +5787,7 @@ prompts@^2.0.1, prompts@^2.4.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.8.1: +prop-types@*, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -5839,12 +5869,7 @@ react-freeze@^1.0.0: resolved "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz" integrity sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA== -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-is@^16.7.0: +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -5859,12 +5884,20 @@ react-is@^19.1.0, react-is@^19.2.3: resolved "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz" integrity sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A== +react-native-blob-util@^0.24.10: + version "0.24.10" + resolved "https://registry.yarnpkg.com/react-native-blob-util/-/react-native-blob-util-0.24.10.tgz#f213e3efd5ee42037d9d39efb1fe23926ef5304e" + integrity sha512-4yazgoCstXgt2/CRURtowGP3furAzbWdfMQ4jyUPDo5xtLx7KE2F/xMz3oU3shLwfL4id0iEKsBywoxvJSHPzQ== + dependencies: + base-64 "1.0.0" + glob "13.0.6" + react-native-geolocation-service@^5.3.1: version "5.3.1" resolved "https://registry.npmjs.org/react-native-geolocation-service/-/react-native-geolocation-service-5.3.1.tgz" integrity sha512-LTXPtPNmrdhx+yeWG47sAaCgQc3nG1z+HLLHlhK/5YfOgfLcAb9HAkhREPjQKPZOUx8pKZMIpdGFUGfJYtimXQ== -react-native-gesture-handler@^2.32.0, "react-native-gesture-handler@>= 2.0.0": +react-native-gesture-handler@^2.32.0: version "2.32.0" resolved "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.32.0.tgz" integrity sha512-uYIMOKlKENORq2SABE+jIjbPU+h5I/sQKcq2v16zRq848nwEp1fWRVwML4QWqijc8UcXJC25o54S8GQd4Mf2OA== @@ -5886,6 +5919,14 @@ react-native-maps@^1.29.0: dependencies: "@types/geojson" "^7946.0.13" +react-native-pdf@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/react-native-pdf/-/react-native-pdf-7.0.4.tgz#f48d485d32cef24f41511ba2072bc99ba94b13c9" + integrity sha512-Uhxn5SSguMKvbwD50iIZfYPmYeKcX+9I3tc4J3HCbRExAN0uqXTAKx2pvbR2Y62umAj0JS274FcbSldQfoQfyg== + dependencies: + crypto-js "4.2.0" + deprecated-react-native-prop-types "^2.3.0" + react-native-reanimated@^4.5.0: version "4.5.1" resolved "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.5.1.tgz" @@ -5894,12 +5935,12 @@ react-native-reanimated@^4.5.0: react-native-is-edge-to-edge "^1.3.1" semver "^7.7.3" -react-native-safe-area-context@^5.8.0, "react-native-safe-area-context@>= 4.0.0": +react-native-safe-area-context@^5.8.0: version "5.8.0" resolved "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.8.0.tgz" integrity sha512-t+ZsAVzY/wWzzx34vqGbo3/as9EEESJdbyZNL7Yg5EYX+toYMtMqFoDDCvqZUi35eeGVsXc6pAaEk4edMwbuCQ== -react-native-screens@^4.25.2, "react-native-screens@>= 4.0.0": +react-native-screens@^4.25.2: version "4.25.2" resolved "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.25.2.tgz" integrity sha512-1Nj1fusFd+rIMKU/qC9yGKVG+3ofh11d3OdBQKL1iVvQfKvcB8vhvTGQf2TkfxW3bamxN+hCZIXmNuU0mRkyDg== @@ -5920,7 +5961,7 @@ react-native-uuid@^2.0.4: resolved "https://registry.npmjs.org/react-native-uuid/-/react-native-uuid-2.0.4.tgz" integrity sha512-LSJNeh559qC17fgVPBsWuTSW/OygFp2dwTcf94IQBLYft5FzIQS9pCsuT36OPvyvDOMb6yiGr6TafaJDnz9PPQ== -react-native-worklets@^0.10.0, react-native-worklets@0.10.x: +react-native-worklets@^0.10.0: version "0.10.2" resolved "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.10.2.tgz" integrity sha512-LX27ejYI8veeDp59Z3rjo2pYyPa9euzSH8GUlem7cnNqfsDtGum8PQpkbzrqhLsWH0CjdeHR7p3sncCyYbwaVw== @@ -5938,7 +5979,7 @@ react-native-worklets@^0.10.0, react-native-worklets@0.10.x: convert-source-map "^2.0.0" semver "^7.7.4" -react-native@*, "react-native@>= 0.62", "react-native@>= 0.76.0", react-native@>=0.40.0, react-native@>=0.79.0, react-native@>=0.82.0, "react-native@0.83 - 0.86", react-native@0.86.0: +react-native@0.86.0: version "0.86.0" resolved "https://registry.npmjs.org/react-native/-/react-native-0.86.0.tgz" integrity sha512-17ALh/dd6AO4pgOVmOO5Axll5PbErEo3XFyLokyzW6usyi+OShIEPwUW26wLPlhVifgSOIfECCH0WN+0IqtJ1w== @@ -5976,7 +6017,7 @@ react-native@*, "react-native@>= 0.62", "react-native@>= 0.76.0", react-native@> ws "^7.5.10" yargs "^17.6.2" -"react-redux@^7.2.1 || ^8.1.3 || ^9.0.0", react-redux@^9.3.0: +react-redux@^9.3.0: version "9.3.0" resolved "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz" integrity sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g== @@ -5997,7 +6038,7 @@ react-test-renderer@19.2.3: react-is "^19.2.3" scheduler "^0.27.0" -react@*, "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.9.0 || ^17.0.0 || ^18 || ^19", "react@^18.0 || ^19", react@^19.2.3, "react@>= 18.2.0", "react@>= 18.3.1", react@>=16.8, react@>=17.0.0, react@19.2.3: +react@19.2.3: version "19.2.3" resolved "https://registry.npmjs.org/react/-/react-19.2.3.tgz" integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA== @@ -6042,7 +6083,7 @@ redux-thunk@^3.1.0: resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz" integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== -redux@^5.0.0, redux@^5.0.1, redux@>4.0.0: +redux@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz" integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== @@ -6216,7 +6257,7 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@~5.2.0, safe-buffer@5.2.1: +safe-buffer@5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6243,7 +6284,7 @@ safe-regex-test@^1.1.0: resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.27.0, scheduler@0.27.0: +scheduler@0.27.0, scheduler@^0.27.0: version "0.27.0" resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== @@ -6253,32 +6294,7 @@ semver@^6.3.0, semver@^6.3.1: resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.1.3: - version "7.8.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" - integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== - -semver@^7.5.2: - version "7.8.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" - integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== - -semver@^7.5.3: - version "7.8.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" - integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== - -semver@^7.5.4: - version "7.8.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" - integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== - -semver@^7.7.3: - version "7.8.5" - resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" - integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== - -semver@^7.7.4: +semver@^7.1.3, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.7.3, semver@^7.7.4: version "7.8.5" resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== @@ -6469,14 +6485,6 @@ socket.io-parser@~4.2.4: "@socket.io/component-emitter" "~3.1.0" debug "~4.4.1" -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" @@ -6485,6 +6493,14 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map@^0.5.6: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" @@ -6552,13 +6568,6 @@ strict-uri-encode@^2.0.0: resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz" integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -6641,6 +6650,13 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + strip-ansi@^5.0.0: version "5.2.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" @@ -6833,7 +6849,7 @@ typed-array-length@^1.0.7: possible-typed-array-names "^1.1.0" reflect.getprototypeof "^1.0.10" -typescript@^5.8.3, typescript@>=4.8.4, "typescript@>=4.8.4 <6.1.0", "typescript@>=4.8.4 <7.0.0", typescript@>=4.9.5: +typescript@^5.8.3: version "5.9.3" resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==