Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const DefaultConfig = {
defaultTab: toArray(config('DRIVER_NAVIGATOR_DEFAULT_TAB', 'DriverDashboardTab')),
},
defaultLocale: config('DEFAULT_LOCALE', 'en'),
colors: {
loginBackground: config('LOGIN_BG_COLOR', '#111827'),
},
};

export function createNavigatorConfig(userConfig = {}) {
Expand Down
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fleetbase/navigator-app",
"version": "2.0.3",
"version": "2.0.4",
"license": "AGPL-3.0-or-later",
"author": "Fleetbase Pte Ltd <hello@fleetbase.io>",
"description": "Order management, geolocation tracking and navigation for Fleetbase drivers.",
Expand Down Expand Up @@ -48,7 +48,7 @@
"@react-navigation/native": "^7.0.0",
"@react-navigation/native-stack": "^7.0.0",
"@tamagui/babel-plugin": "^1.121.5",
"@tamagui/config": "^1.116.14",
"@tamagui/config": "1.125.20",
"add": "^2.0.6",
"countries-list": "^3.0.6",
"country-locale-map": "^1.9.0",
Expand Down Expand Up @@ -101,7 +101,7 @@
"react-redux": "^9.0.4",
"recyclerlistview": "^4.2.1",
"socketcluster-client": "^19.2.3",
"tamagui": "^1.121.5",
"tamagui": "1.125.20",
"yarn": "^1.22.22"
},
"devDependencies": {
Expand All @@ -122,11 +122,19 @@
"babel-jest": "^29.6.3",
"babel-loader": "^9.2.1",
"babel-plugin-preval": "^5.1.0",
"babel-plugin-react-native-web": "^0.19.13",
"css-loader": "^7.1.2",
"dotenv": "^16.4.7",
"eslint": "^8.19.0",
"html-webpack-plugin": "^5.6.3",
"jest": "^29.6.3",
"metro-react-native-babel-preset": "^0.77.0",
"prettier": "^3.3.3",
"react-test-renderer": "18.3.1",
"style-loader": "^4.0.0",
"tamagui-loader": "1.125.20",
"typescript": "5.0.4",
"url-loader": "^4.1.1",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.0",
"webpack-dev-server": "^5.2.0"
Expand Down
2 changes: 1 addition & 1 deletion src/components/Buttons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const PhoneLoginButton = ({ onPress, ...props }) => {
const theme = useTheme();

return (
<Button onPress={onPress} bg='$secondary' width='100%' {...props} rounded>
<Button onPress={onPress} bg='$subsurface' borderWidth={1} borderColor='$borderColor' width='100%' {...props} rounded>
<Button.Icon>
<FontAwesomeIcon icon={faPhone} color={theme['$textPrimary'].val} />
</Button.Icon>
Expand Down
3 changes: 3 additions & 0 deletions src/components/ChatKeyboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { YStack, XStack, Button, TextArea, useTheme } from 'tamagui';
import { KeyboardAvoidingView, Platform } from 'react-native';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faCamera, faPlus, faPaperPlane } from '@fortawesome/free-solid-svg-icons';
import useAppTheme from '../hooks/use-app-theme';
import CameraCapture from './CameraCapture';

const INPUT_MIN_HEIGHT = 40;
const INPUT_MAX_HEIGHT = 120;
const ChatKeyboard = ({ onSend, onAttach, onCamera, onFocus, onBlur }) => {
const { isDarkMode } = useAppTheme();
const theme = useTheme();
const headerHeight = useHeaderHeight();
const [message, setMessage] = useState('');
Expand Down Expand Up @@ -78,6 +80,7 @@ const ChatKeyboard = ({ onSend, onAttach, onCamera, onFocus, onBlur }) => {
fontSize={14}
onFocus={onFocus}
onBlur={onBlur}
placeholderTextColor={isDarkMode ? '$gray-500' : '$gray-400'}
/>
</YStack>
<XStack px='$2' gap='$1'>
Expand Down
11 changes: 8 additions & 3 deletions src/components/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import { faCheck } from '@fortawesome/free-solid-svg-icons';
import { abbreviateName } from '../utils';
import { formatWhatsAppTimestamp } from '../utils/format';
import { useChat } from '../contexts/ChatContext';
import useAppTheme from '../hooks/use-app-theme';
import ChatAttachment from './ChatAttachment';

const ChatMessage = ({ record, participant }) => {
const theme = useTheme();
const { isDarkMode } = useAppTheme();
const { createReadReceipt } = useChat();
const isSender = participant.id === record.sender.id;
const background = isDarkMode ? (isSender ? '$green-900' : '$gray-900') : isSender ? '$green-800' : '$gray-300';
const messageTextColor = isDarkMode ? '$textPrimary' : isSender ? '$green-100' : '$gray-900';
const senderTextColor = isDarkMode ? (isSender ? '$green-200' : '$blue-600') : isSender ? '$green-100' : '$gray-900';

// Create read recipt if participant doesn't have
useEffect(() => {
Expand Down Expand Up @@ -39,12 +44,12 @@ const ChatMessage = ({ record, participant }) => {
</YStack>
</YStack>
<YStack flex={1}>
<XStack bg={isSender ? '$green-900' : '$gray-900'} borderRadius='$4' px='$2' py='$2' space='$3'>
<XStack bg={background} borderRadius='$4' px='$2' py='$2' space='$3'>
<YStack flex={1}>
<Text color={isSender ? '$green-200' : '$blue-600'} mb='$2'>
<Text color={senderTextColor} fontWeight='bold' mb='$2'>
{record.sender.name}
</Text>
<Text color='$textPrimary'>{record.content}</Text>
<Text color={messageTextColor}>{record.content}</Text>
<XStack gap='$1' flexWrap='wrap'>
{record.attachments.map((attachment, index) => (
<ChatAttachment key={index} record={attachment} />
Expand Down
9 changes: 6 additions & 3 deletions src/components/ChatParticipants.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React from 'react';
import { Pressable } from 'react-native';
import { Avatar, XStack, YStack, Text, useTheme } from 'tamagui';
import useAppTheme from '../hooks/use-app-theme';

export const ChatParticipants = ({ participants = [], size = 30, onPress }) => {
const { isDarkMode } = useAppTheme();

// Display a friendly message when no participants exist
if (participants.length === 0) {
return (
Expand Down Expand Up @@ -31,7 +34,7 @@ export const ChatParticipants = ({ participants = [], size = 30, onPress }) => {
<XStack alignItems='center'>
{displayParticipants.map((participant, index) => (
<YStack key={participant.id} ml={index === 0 ? 0 : -overlapMargin} width={size}>
<Avatar circular size={size} borderWidth={1} borderColor='$gray-900' style={shadowStyle}>
<Avatar circular size={size} borderWidth={1} borderColor={isDarkMode ? '$gray-900' : '$borderColorWithShadow'} style={shadowStyle}>
<Avatar.Image accessibilityLabel={participant.name} src={participant.avatar_url} />
<Avatar.Fallback backgroundColor='$blue-500' />
</Avatar>
Expand All @@ -40,9 +43,9 @@ export const ChatParticipants = ({ participants = [], size = 30, onPress }) => {
opacity={0.75}
textAlign='center'
mt='$1'
bg='$black'
bg={isDarkMode ? '$black' : '$gray-200'}
borderWidth={1}
borderColor='$borderColor'
borderColor={isDarkMode ? '$borderColor' : '$borderColorWithShadow'}
borderRadius='$6'
px='$2'
py='$1'
Expand Down
14 changes: 13 additions & 1 deletion src/components/CommentThread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { Alert } from 'react-native';
import { YStack, XStack, Text, Button, Spinner, TextArea, useTheme } from 'tamagui';
import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome';
import { faPaperPlane, faRotate } from '@fortawesome/free-solid-svg-icons';
import useAppTheme from '../hooks/use-app-theme';
import Comment from './Comment';

const CommentThread = ({ comments: initialComments = [], subject, onReloadComments, isReloading }) => {
const { isDarkMode } = useAppTheme();
const theme = useTheme();
const [comments, setComments] = useState(initialComments);
const [input, setInput] = useState('');
Expand Down Expand Up @@ -48,7 +50,17 @@ const CommentThread = ({ comments: initialComments = [], subject, onReloadCommen
return (
<YStack space='$4'>
<YStack>
<TextArea value={input} placeholder='Write a comment...' onChangeText={setInput} width='100%' borderWidth={1} borderColor='$borderColor' minHeight={100} />
<TextArea
value={input}
placeholder='Write a comment...'
onChangeText={setInput}
width='100%'
bg={isDarkMode ? '$secondary' : '$white'}
borderWidth={1}
borderColor={isDarkMode ? '$gray-600' : '$borderColorWithShadow'}
minHeight={100}
placeholderTextColor={isDarkMode ? '$gray-500' : '$gray-400'}
/>
<XStack justifyContent='flex-end' alignItems='center' marginTop='$2' space='$2'>
{isReloading ? (
<YStack alignItems='center' justifyContent='center' pr='$2'>
Expand Down
11 changes: 9 additions & 2 deletions src/components/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Text, YStack, XStack } from 'tamagui';
import useAppTheme from '../hooks/use-app-theme';

export const SectionHeader = ({ title, children }) => {
const { isDarkMode } = useAppTheme();

return (
<YStack>
<XStack
px='$3'
py='$3'
justifyContent='space-between'
bg='$info'
bg={isDarkMode ? '$info' : '$blue-700'}
borderBottomWidth={1}
borderTopWidth={1}
borderColor='$infoBorder'
Expand All @@ -28,6 +31,8 @@ export const SectionHeader = ({ title, children }) => {
};

export const SectionInfoLine = ({ title, value }) => {
const { isDarkMode } = useAppTheme();

return (
<YStack>
<XStack px='$3' py='$2' justifyContent='space-between'>
Expand All @@ -51,6 +56,8 @@ export const SectionInfoLine = ({ title, value }) => {
};

export const ActionContainer = ({ children, ...props }) => {
const { isDarkMode } = useAppTheme();

return (
<YStack>
<YStack
Expand All @@ -60,7 +67,7 @@ export const ActionContainer = ({ children, ...props }) => {
bg='$background'
borderBottomWidth={0}
borderTopWidth={1}
borderColor='$infoBorder'
borderColor={isDarkMode ? '$infoBorder' : '$borderColorWithShadow'}
shadowColor='$shadowColor'
shadowOffset={{ width: 0, height: 2 }}
shadowOpacity={0.2}
Expand Down
20 changes: 11 additions & 9 deletions src/components/CurrentDestinationSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import { formattedAddressFromPlace, formatAddressSecondaryIdentifier } from '../
import PlaceMapView from './PlaceMapView';
import Badge from './Badge';
import Spacer from './Spacer';
import useAppTheme from '../hooks/use-app-theme';

const CurrentDestinationSelect = ({ onChange, destination, waypoints = [], snapTo = '100%', isLoading = false, ...props }) => {
const { isDarkMode } = useAppTheme();
const theme = useTheme();
const navigation = useNavigation();
const bottomSheetRef = useRef<BottomSheet>(null);
Expand All @@ -38,7 +40,7 @@ const CurrentDestinationSelect = ({ onChange, destination, waypoints = [], snapT
return (
<YStack>
<Pressable onPress={openBottomSheet}>
<YStack bg='$default' borderWidth={1} borderColor='$defaultBorder' borderRadius='$4' px='$3' py='$3' {...props}>
<YStack bg={isDarkMode ? '$default' : '$gray-200'} borderWidth={1} borderColor={isDarkMode ? '$defaultBorder' : '$gray-300'} borderRadius='$4' px='$3' py='$3' {...props}>
<XStack>
<YStack width={100} height={90}>
<PlaceMapView place={destination} zoom={2} markerSize='xs' width={100} height={90} borderWidth={1} borderColor='$borderColor' />
Expand All @@ -50,10 +52,10 @@ const CurrentDestinationSelect = ({ onChange, destination, waypoints = [], snapT
</YStack>
) : (
<YStack>
<Text size={15} color='$infoText' fontWeight='bold' textTransform='uppercase' mb={2}>
<Text size={15} color={isDarkMode ? '$infoText' : '$gray-700'} fontWeight='bold' textTransform='uppercase' mb={2}>
{destination.getAttribute('name') ?? 'Current Destination'}
</Text>
<Text color='$textSecondary' textTransform='uppercase' mb='$1'>
<Text color={isDarkMode ? '$textSecondary' : '$gray-500'} textTransform='uppercase' mb='$1'>
{formattedAddressFromPlace(destination)}
</Text>
<YStack alignSelf='flex-start'>
Expand All @@ -71,7 +73,7 @@ const CurrentDestinationSelect = ({ onChange, destination, waypoints = [], snapT
)}
</YStack>
<YStack justifyContent='center'>
<FontAwesomeIcon icon={faChevronRight} color={theme.infoText.val} />
<FontAwesomeIcon icon={faChevronRight} color={isDarkMode ? theme.infoText.val : theme['gray-500'].val} />
</YStack>
</XStack>
</YStack>
Expand Down Expand Up @@ -109,7 +111,7 @@ const CurrentDestinationSelect = ({ onChange, destination, waypoints = [], snapT
<YStack px='$4'>
<Button
onPress={() => handleDestinationSelect(waypoint)}
size='$8'
size={isDestination ? '$9' : '$8'}
bg={isCompleted ? '$success' : isDestination ? '$info' : '$secondary'}
borderWidth={1}
borderColor={isCompleted ? '$successBorder' : isDestination ? '$infoBorder' : '$borderColorWithShadow'}
Expand Down Expand Up @@ -155,15 +157,15 @@ const CurrentDestinationSelect = ({ onChange, destination, waypoints = [], snapT
alignItems='center'
justifyContent='center'
>
<FontAwesomeIcon icon={faLocationDot} color={theme['$info'].val} />
<FontAwesomeIcon icon={faLocationDot} color={isDestination ? theme['white'].val : theme['$info'].val} />
</YStack>
</YStack>
)}
<XStack flex={1} alignItems='flex-start'>
<YStack flex={1}>
<XStack alignItems='flex-start' justifyContent='space-between' mb='$1'>
<YStack flex={1} space='$1'>
<Text color='$textPrimary' fontWeight='bold' numberOfLines={1}>
<Text color={isDestination ? '$white' : '$textPrimary'} fontWeight='bold' numberOfLines={1}>
{waypoint.getAttribute('name') ?? waypoint.getAttribute('street1')}
</Text>
{isDestination && <Text color='$infoText'>(Destination)</Text>}
Expand All @@ -172,10 +174,10 @@ const CurrentDestinationSelect = ({ onChange, destination, waypoints = [], snapT
<Badge status={waypoint.getAttribute('status')} fontSize='$1' px='$2' py='$1' />
)}
</XStack>
<Text color='$textSecondary' numberOfLines={1}>
<Text color={isDestination ? '$gray-200' : '$textSecondary'} numberOfLines={1}>
{formattedAddressFromPlace(waypoint)}
</Text>
<Text color='$textSecondary'>{formatAddressSecondaryIdentifier(waypoint)}</Text>
<Text color={isDestination ? '$gray-200' : '$textSecondary'}>{formatAddressSecondaryIdentifier(waypoint)}</Text>
</YStack>
</XStack>
</XStack>
Expand Down
2 changes: 1 addition & 1 deletion src/components/DriverMarker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const DriverMarker = ({ driver, onPositionChange, onHeadingChange, onMovement, .
<TrackingMarker
ref={markerRef}
coordinate={{ latitude: driver.latitude, longitude: driver.longitude }}
imageSource={{ uri: driver.getAttribute('avatar_url') }}
imageSource={{ uri: driver.getAttribute('vehicle_avatar') }}
size={{ width: 50, height: 50 }}
{...props}
/>
Expand Down
4 changes: 2 additions & 2 deletions src/components/DriverOnlineToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ const DriverOnlineToggle = ({ showLabel = false, ...props }) => {
opacity={isUpdating ? 0.75 : 1}
bg={checked ? '$green-600' : '$gray-500'}
borderWidth={1}
borderColor={isDarkMode ? '$gray-700' : '$gray-200'}
borderColor={isDarkMode ? '$gray-700' : '$white'}
>
<Switch.Thumb animation='quick' />
<Switch.Thumb animation='quick' bg={isDarkMode ? '$gray-200' : '$white'} borderColor={isDarkMode ? '$gray-700' : '$gray-500'} borderWidth={1} />
</Switch>
{showLabel === true && (
<Label htmlFor='driverOnline' color='$gray-500' size='$2' lineHeight='$4'>
Expand Down
2 changes: 1 addition & 1 deletion src/components/LoadingOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({
}
}, [bgColor, theme]);

if (!visible) return null;
if (!visible) return <YStack />;
return (
<YStack position='absolute' top={0} left={0} right={0} bottom={0} justifyContent='center' alignItems='center' zIndex={9999}>
<LinearGradient
Expand Down
2 changes: 1 addition & 1 deletion src/components/MultipleCustomerAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const MultipleCustomerAvatars: React.FC<MultipleCustomerAvatarsProps> = (
return (
<XStack alignItems='center'>
{displayCustomers.map((customer, index) => (
<Avatar key={customer.id} circular size={size} borderWidth={1} borderColor='$gray-900' ml={index === 0 ? 0 : -overlapMargin} style={shadowStyle}>
<Avatar key={index} circular size={size} borderWidth={1} borderColor='$gray-900' ml={index === 0 ? 0 : -overlapMargin} style={shadowStyle}>
<Avatar.Image accessibilityLabel={customer.name} src={customer.photo_url} />
<Avatar.Fallback backgroundColor='$blue-500' />
</Avatar>
Expand Down
Loading
Loading