Overview
The deliveries feature allows riders to browse available delivery requests, filter orders by status, and manage their delivery queue. The interface uses FlashList for optimal performance even with large lists of deliveries.
Delivery History Screen
Main Component
The history screen provides a comprehensive view of all delivery orders with filtering capabilities.
src/app/(parcel)/(tabs)/history.tsx
import { ShipmentCard } from "@/components/shipment-card" ;
import { api } from "@/services/api" ;
import { ShipmentStatus } from "@/types/enums/shipment.enum" ;
import { FlashList } from "@shopify/flash-list" ;
import { useInfiniteQuery } from "@tanstack/react-query" ;
import { Link } from "expo-router" ;
import { Chip } from "heroui-native" ;
import { Pressable , ScrollView , Text , View } from "react-native" ;
const FILTER_OPTIONS = [
{ value: "all" , label: "All Orders" , icon: "apps" },
{ value: ShipmentStatus . RIDER_ASSIGNED , label: "Assigned" , icon: "bicycle" },
{ value: ShipmentStatus . IN_TRANSIT , label: "In Transit" , icon: "bicycle" },
{ value: ShipmentStatus . OUT_FOR_DELIVERY , label: "Out for Delivery" , icon: "navigate" },
{ value: ShipmentStatus . DELIVERED , label: "Delivered" , icon: "checkmark-circle" },
];
export default function History () {
const [ selectedFilter , setSelectedFilter ] = React . useState < string >( "all" );
const { data , isLoading , hasNextPage , fetchNextPage , refetch } = useInfiniteQuery ({
queryKey: [ "shipments" , "infinite" , selectedFilter ],
queryFn : ({ pageParam = 1 }) => {
const params : { limit : number ; page : number ; status ?: string } = {
limit: 8 ,
page: pageParam ,
};
if ( selectedFilter !== "all" ) {
params . status = selectedFilter ;
}
return api . shipments . list ( params );
},
getNextPageParam : ( lastPage ) => {
const currentPage = lastPage . data . meta . currentPage ;
const totalPages = lastPage . data . meta . totalPages ;
return currentPage < totalPages ? currentPage + 1 : undefined ;
},
initialPageParam: 1 ,
});
const shipments = data ?. pages . flatMap (( page ) => page . data . items ) || [];
const totalItems = data ?. pages [ 0 ]?. data . meta ?. totalItems || 0 ;
return (
< View className = "flex-1 bg-white" >
{ /* Header */ }
< View className = "px-5 pt-safe pb-4 border-b border-gray-100" >
< View className = "flex-row items-center justify-between mb-3" >
< Text className = "text-2xl font-bold text-secondary" > Order History </ Text >
< View className = "bg-accent/10 rounded-full px-3 py-1.5" >
< Text className = "text-accent font-semibold text-sm" >
{ totalItems } { totalItems === 1 ? "order" : "orders" }
</ Text >
</ View >
</ View >
</ View >
{ /* Status Filters */ }
< View className = "border-b border-gray-100" >
< ScrollView horizontal contentContainerClassName = "px-5 py-4 gap-2" >
{ FILTER_OPTIONS . map (( filter ) => (
< Pressable key = { filter . value } >
< Chip
variant = { selectedFilter === filter . value ? "primary" : "secondary" }
onPress = { () => setSelectedFilter ( filter . value ) }
>
< Ionicons name = { filter . icon } size = { 14 } />
< Text > { filter . label } </ Text >
</ Chip >
</ Pressable >
)) }
</ ScrollView >
</ View >
{ /* Delivery List */ }
< View className = "flex-1 px-5" >
< FlashList
data = { shipments }
renderItem = { ({ item }) => (
< Link asChild href = { `/shipments/ ${ item . reference } ` } >
< ShipmentCard shipment = { item } />
</ Link >
) }
keyExtractor = { ( item ) => item . id }
onEndReached = { () => {
if ( hasNextPage ) fetchNextPage ();
} }
/>
</ View >
</ View >
);
}
Status Filters
The delivery list supports multiple status filters to help riders find specific orders quickly.
All Orders Shows all delivery orders regardless of status.
Assigned Orders assigned to the rider but not yet picked up.
In Transit Orders currently being transported by the rider.
Out for Delivery Orders in the final delivery stage.
Delivered Successfully completed deliveries.
On Hold Deliveries temporarily paused.
Available Filter Options
const FILTER_OPTIONS = [
{ value: "all" , label: "All Orders" , icon: "apps" },
{ value: ShipmentStatus . RIDER_ASSIGNED , label: "Assigned" , icon: "bicycle" },
{ value: ShipmentStatus . IN_TRANSIT , label: "In Transit" , icon: "bicycle" },
{ value: ShipmentStatus . OUT_FOR_DELIVERY , label: "Out for Delivery" , icon: "navigate" },
{ value: ShipmentStatus . PICKUP_CONFIRMED , label: "Pickup Confirmed" , icon: "checkmark-circle" },
{ value: ShipmentStatus . DELIVERED , label: "Delivered" , icon: "checkmark-circle" },
{ value: ShipmentStatus . FAILED_DELIVERY_ATTEMPT , label: "Failed Delivery" , icon: "close-circle" },
{ value: ShipmentStatus . REPACKAGED , label: "Repackaged" , icon: "refresh" },
{ value: ShipmentStatus . REFUNDED , label: "Refunded" , icon: "cash" },
{ value: ShipmentStatus . ON_HOLD , label: "On Hold" , icon: "pause-circle" },
{ value: ShipmentStatus . RETURNED , label: "Returned" , icon: "return-up-back" },
];
Shipment Card Component
Each delivery is displayed using an optimized card component.
src/components/shipment-card.tsx
import type { Shipment } from "@/types/shipment.types" ;
import { getStatusColor , getStatusTextColor } from "@/utils/style" ;
import { Ionicons } from "@expo/vector-icons" ;
import { Pressable , Text , View } from "react-native" ;
export function ShipmentCard ({ shipment , onPress } : ShipmentCardProps ) {
const formatStatus = ( status : string ) => {
return status
. split ( "_" )
. map (( word ) => word . charAt ( 0 ). toUpperCase () + word . slice ( 1 ))
. join ( " " );
};
return (
< Pressable
onPress = { onPress }
className = "bg-gray-50 rounded-2xl p-4 mb-3 border border-gray-200"
>
{ /* Status Badge */ }
< View className = "flex-row items-center justify-between mb-3" >
< View
className = { ` ${ getStatusColor ( shipment . status ) } rounded-full px-3 py-1` }
>
< Text className = { ` ${ getStatusTextColor ( shipment . status ) } text-xs font-semibold` } >
{ formatStatus ( shipment . status ) }
</ Text >
</ View >
</ View >
{ /* Route Information */ }
< View className = "flex-row items-start mb-4" >
{ /* Route Line */ }
< View className = "items-center mr-3" >
< View className = "w-3 h-3 rounded-full bg-orange-500" />
< View className = "w-0.5 h-8 bg-gray-300 my-1" />
< View className = "w-3 h-3 rounded-full bg-cyan-500" />
</ View >
{ /* Addresses */ }
< View className = "flex-1" >
{ /* Pickup */ }
< View className = "mb-4" >
< Text className = "text-xs text-gray-500 font-medium" > Pickup </ Text >
< Text className = "text-sm text-secondary font-semibold" >
{ shipment . pickupArea } , { shipment . pickupCity }
</ Text >
</ View >
{ /* Dropoff */ }
< View >
< Text className = "text-xs text-gray-500 font-medium" > Drop-off </ Text >
< Text className = "text-sm text-secondary font-semibold" >
{ shipment . dropOffArea } , { shipment . dropOffCity }
</ Text >
</ View >
</ View >
</ View >
{ /* Bottom Info */ }
< View className = "flex-row items-center justify-between" >
< View className = "flex-row items-center gap-1" >
< Ionicons name = "person" size = { 14 } color = "#6b7280" />
< Text className = "text-xs text-gray-600" > { shipment . recipientPhone } </ Text >
</ View >
< View className = "flex-row items-center gap-1" >
< Ionicons name = "time" size = { 14 } color = "#6b7280" />
< Text className = "text-xs text-gray-600" > { formattedDate } </ Text >
</ View >
</ View >
</ Pressable >
);
}
The app uses FlashList for optimal rendering performance with large lists.
Why FlashList?
Key Benefits
Implementation
FlashList provides up to 10x better performance than FlatList for large datasets by using a different recycling approach.
Faster initial render
Lower memory usage
Smoother scrolling
Better blank space management
< FlashList
data = { shipments }
renderItem = { ({ item }) => < ShipmentCard shipment = { item } /> }
estimatedItemSize = { 150 }
onEndReachedThreshold = { 0.5 }
onEndReached = { () => hasNextPage && fetchNextPage () }
/>
The delivery list implements infinite scroll for seamless browsing.
const { data , hasNextPage , fetchNextPage , isFetchingNextPage } = useInfiniteQuery ({
queryKey: [ "shipments" , "infinite" , selectedFilter ],
queryFn : ({ pageParam = 1 }) => {
return api . shipments . list ({
limit: 8 ,
page: pageParam ,
status: selectedFilter !== "all" ? selectedFilter : undefined ,
});
},
getNextPageParam : ( lastPage ) => {
const { currentPage , totalPages } = lastPage . data . meta ;
return currentPage < totalPages ? currentPage + 1 : undefined ;
},
initialPageParam: 1 ,
});
Automatically loads more items when the user scrolls near the bottom of the list.
Tracks current page and total pages to determine when to stop loading.
Shows skeleton loaders at the bottom while fetching next page.
Caches all loaded pages for instant back navigation.
Empty States
The app provides helpful empty states when no deliveries match the current filter.
function EmptyState ({ selectedFilter } : { selectedFilter : string }) {
const filterLabel =
FILTER_OPTIONS . find (( f ) => f . value === selectedFilter )?. label || "All Orders" ;
return (
< View className = "flex-1 items-center justify-center px-6 py-12" >
< View className = "bg-gray-50 rounded-full p-6 mb-4" >
< Ionicons name = "folder-open-outline" size = { 48 } color = "#9ca3af" />
</ View >
< Text className = "text-xl font-bold text-secondary text-center mb-2" >
No Orders Found
</ Text >
< Text className = "text-sm text-gray-500 text-center" >
{ selectedFilter === "all"
? "You don't have any order history yet."
: `No orders found with status " ${ filterLabel } ".` }
</ Text >
</ View >
);
}
Available Deliveries Section
The dashboard also includes a quick preview of available deliveries.
src/modules/dashboard/parcels/riders/home/available-deliveries.tsx
export function AvailableDeliveries () {
const user = Storage . getObject ( StorageKeys . USER ) as User ;
const { data : shipmentsResponse , refetch } = useQuery (
getShipmentsQueryOptions ( user . id )
);
useFocusEffect (
React . useCallback (() => {
refetch ();
}, [ refetch ]),
);
return (
< View className = "mt-4" >
< Text className = "text-base text-secondary font-semibold mb-2" >
Available deliveries
</ Text >
< FlashList
data = { shipments }
renderItem = { ({ item }) => (
< Link asChild href = { `/shipments/ ${ item . reference } ` } >
< ShipmentCard shipment = { item } />
</ Link >
) }
/>
</ View >
);
}
The available deliveries list automatically refreshes when the screen comes into focus using the useFocusEffect hook.
Shipment Details View detailed information about individual shipments.
Dashboard Return to the main dashboard overview.