added rider application

This commit is contained in:
Sharan 2022-12-21 15:01:12 +05:00
parent 054db872ef
commit fa225eccd0
95 changed files with 31268 additions and 0 deletions

BIN
.DS_Store vendored

Binary file not shown.

115
Rider App/App.js Normal file
View File

@ -0,0 +1,115 @@
import { ApolloProvider } from '@apollo/react-hooks'
import AsyncStorage from '@react-native-async-storage/async-storage'
import * as Font from 'expo-font'
import * as Notifications from 'expo-notifications'
import * as SplashScreen from 'expo-splash-screen'
import React, { useEffect, useState } from 'react'
import { Platform, StatusBar } from 'react-native'
import FlashMessage from 'react-native-flash-message'
import i18n from './i18n'
import setupApolloClient from './src/apollo/index'
import { AuthContext } from './src/context/auth'
import { ConfigurationProvider } from './src/context/configuration'
import AppContainer from './src/routes/index'
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false)
const [client, setClient] = useState(null)
const [token, setToken] = useState(false)
const [appIsReady, setAppIsReady] = useState(false)
useEffect(() => {
;(async () => {
const token = await AsyncStorage.getItem('rider-token')
if (token) setToken(token)
setAppIsReady(true)
})()
}, [])
useEffect(() => {
;(async () => {
try {
await SplashScreen.preventAutoHideAsync()
} catch (e) {
console.log(e)
}
})()
loadData()
}, [])
const setTokenAsync = async token => {
await AsyncStorage.setItem('rider-token', token)
setToken(token)
}
const logout = async () => {
try {
await AsyncStorage.removeItem('rider-token')
setToken(null)
} catch (e) {
console.log('Logout Error: ', e)
}
}
async function loadData() {
await i18n.initAsync()
await Font.loadAsync({
MuseoSans300: require('./assets/font/MuseoSans/MuseoSans300.ttf'),
MuseoSans500: require('./assets/font/MuseoSans/MuseoSans500.ttf'),
MuseoSans700: require('./assets/font/MuseoSans/MuseoSans700.ttf'),
icomoon: require('./assets/font/icomoon.ttf')
})
const client = await setupApolloClient()
await permissionForPushNotificationsAsync()
setClient(client)
setFontLoaded(true)
await SplashScreen.hideAsync()
}
async function permissionForPushNotificationsAsync() {
const { status: existingStatus } = await Notifications.getPermissionsAsync()
let finalStatus = existingStatus
// only ask if permissions have not already been determined, because
// iOS won't necessarily prompt the user a second time.
if (existingStatus !== 'granted') {
// Android remote notification permissions are granted during the app
// install, so this will only ask on iOS
const { status } = await Notifications.requestPermissionsAsync()
finalStatus = status
}
// Stop here if the user did not grant permissions
if (finalStatus !== 'granted') {
return
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
sound: true,
priority: 'max',
importance: Notifications.AndroidImportance.HIGH,
vibrate: [0, 250, 250, 250]
})
}
}
if (fontLoaded && client && appIsReady) {
return (
<ApolloProvider client={client}>
<StatusBar
translucent
backgroundColor={'transparent'}
barStyle="dark-content"
/>
<ConfigurationProvider>
<AuthContext.Provider value={{ token, setTokenAsync, logout }}>
<AppContainer />
</AuthContext.Provider>
</ConfigurationProvider>
<FlashMessage duration={2000} position="center" />
</ApolloProvider>
)
}
return null
}

66
Rider App/app.json Normal file
View File

@ -0,0 +1,66 @@
{
"expo": {
"name": "Enatega Rider",
"description": "Enatega is a starter kit food ordering app built in React Native using Expo for IOS and Android. It's made keeping good aesthetics in mind as well keeping the best coding practices in mind. Its fully customisable to easily help you in your next food delivery project. https://market.nativebase.io/view/react-native-food-delivery-backend-app",
"version": "4.2.2",
"slug": "food-delivery-rider",
"privacy": "public",
"androidStatusBar": {
"backgroundColor": "#000"
},
"platforms": [
"ios",
"android"
],
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "cover",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"notification": {
"iosDisplayInForeground": true,
"color": "#d83765",
"icon": "./assets/not-icon.png",
"androidMode": "default",
"androidCollapsedTitle": "Enatega Rider"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.enatega.driver",
"config": {
"googleMapsApiKey": "AIzaSyD0vSz1qjsn_RSPBB9HRD1Eqztm7DyuKw8"
},
"infoPlist": {
"NSLocationWhenInUseUsageDescription": "Allow $(PRODUCT_NAME) to use location to determine the delivery address for your orders.",
"NSLocationAlwaysUsageDescription": "For a better experience, while using our Service, we may require you to provide us Location Permissions",
"UIBackgroundModes": [
"location",
"fetch"
]
}
},
"android": {
"versionCode": 13,
"googleServicesFile": "./google-services.json",
"useNextNotificationsApi": true,
"permissions":[
"ACCESS_COARSE_LOCATION",
"ACCESS_FINE_LOCATION"
],
"config": {
"googleMaps": {
"apiKey": "AIzaSyBt9S6e7ig5EkNiSXw3sCyX7kgo1gzPxl4"
}
},
"package": "com.enatega.rider"
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Rider App/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,48 @@
import * as React from 'react'
import Svg, { G, Path, Text, TSpan } from 'react-native-svg'
/* SVGR has dropped some elements not supported by react-native-svg: style */
function Logo(props) {
return (
<Svg
xmlns="http://www.w3.org/2000/svg"
width={132}
height={78.635}
viewBox="0 0 132 78.635"
{...props}>
<G id="prefix__Group_371" transform="translate(-122 -367)">
<Path
id="prefix__Path_27625"
fill="#febb2c"
d="M10 0h122l-10 43H0z"
transform="translate(122 402.635)"
/>
<Text
id="prefix__enatega"
fill="#0b0b0b"
fontSize={32}
fontStyle="italic"
fontWeight={700}
className="prefix__cls-2"
transform="translate(188 394)">
<TSpan x={-62.4} y={0}>
{'enatega'}
</TSpan>
</Text>
<Text
id="prefix__rider"
fill="#0b0b0b"
fontSize={32}
fontStyle="italic"
fontWeight={700}
transform="translate(188 435)">
<TSpan x={-36.752} y={0}>
{'rider'}
</TSpan>
</Text>
</G>
</Svg>
)
}
export default React.memo(Logo)

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
Rider App/assets/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,7 @@
module.exports = function (api) {
api.cache(true)
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin']
}
}

37
Rider App/environment.js Normal file
View File

@ -0,0 +1,37 @@
/*****************************
* environment.js
* path: '/environment.js' (root of your project)
******************************/
import Constants from 'expo-constants'
const ENV = {
development: {
GRAPHQL_URL: 'http://192.168.100.90:8000/graphql',
WS_GRAPHQL_URL: 'ws://192.168.100.90:8000/graphql'
},
staging: {
GRAPHQL_URL: 'https://staging-enatega-single-api.herokuapp.com/graphql',
WS_GRAPHQL_URL: 'wss://staging-enatega-single-api.herokuapp.com/graphql'
},
production: {
GRAPHQL_URL: 'https://prod-enatega-single-api.herokuapp.com/graphql',
WS_GRAPHQL_URL: 'wss://prod-enatega-single-api.herokuapp.com/graphql'
}
}
const getEnvVars = (env = Constants.manifest.releaseChannel) => {
// What is __DEV__ ?
// This variable is set to true when react-native is running in Dev mode.
// __DEV__ is true when run locally, but false when published.
// eslint-disable-next-line no-undef
if (__DEV__) {
return ENV.development
} else if (env === 'production') {
return ENV.production
} else {
return ENV.production
}
}
export default getEnvVars

View File

@ -0,0 +1,83 @@
{
"project_info": {
"project_number": "94983896797",
"firebase_url": "https://enatega-production.firebaseio.com",
"project_id": "enatega-production",
"storage_bucket": "enatega-production.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:94983896797:android:ec37b623e2579690112534",
"android_client_info": {
"package_name": "com.enatega.rider"
}
},
"oauth_client": [
{
"client_id": "94983896797-9e36v3edasjt9t9r5q9uvkth700nn0nn.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBt9S6e7ig5EkNiSXw3sCyX7kgo1gzPxl4"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "94983896797-9e36v3edasjt9t9r5q9uvkth700nn0nn.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "94983896797-m8ri1dftjkik9etuid39top8g2c3ode3.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.enatega.driver"
}
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:94983896797:android:5b28fe2925a51512112534",
"android_client_info": {
"package_name": "com.enatega.vendor"
}
},
"oauth_client": [
{
"client_id": "94983896797-9e36v3edasjt9t9r5q9uvkth700nn0nn.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBt9S6e7ig5EkNiSXw3sCyX7kgo1gzPxl4"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "94983896797-9e36v3edasjt9t9r5q9uvkth700nn0nn.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "94983896797-m8ri1dftjkik9etuid39top8g2c3ode3.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.enatega.driver"
}
}
]
}
}
}
],
"configuration_version": "1"
}

22
Rider App/i18n.js Normal file
View File

@ -0,0 +1,22 @@
import * as Localization from 'expo-localization'
import { AsyncStorage, Platform } from 'react-native'
import i18n from 'i18n-js'
import { en } from './languages/en'
import { fr } from './languages/fr'
import { km } from './languages/km'
import { zh } from './languages/zh'
import { de } from './languages/de'
i18n.initAsync = async () => {
i18n.fallbacks = true
i18n.translations = { fr, en, km, zh, de }
// i18n.locale = 'km'
if (Platform.OS === 'android') {
const lang = await AsyncStorage.getItem('enatega-language')
i18n.locale = lang || 'en'
} else {
i18n.locale = Localization.locale
}
}
export default i18n

115
Rider App/languages/de.js Normal file
View File

@ -0,0 +1,115 @@
export const de = {
title0: 'Gewählte Sprache',
subtitle0: 'English',
description0:
'Wählen Sie eine Sprache Ihrer Wahl, um den Inhalt der App in die gewünschte Sprache zu ändern.',
title1: 'Lecker',
subtitle1: 'Frühstück',
description1:
'Frühstück ist alles. Der Anfang, das Erste. Es ist der Mundvoll, der die Verpflichtung zu einem neuen Tag, einem fortwährenden Leben ist.',
title2: 'Erfrischend',
subtitle2: 'Getränke',
description2:
'Wahre Stille ist der Rest des Geistes und ist für den Geist das, was Schlaf für den Körper ist, Nahrung und Erfrischung.',
title3: 'Köstlich',
subtitle3: 'Eis',
description3:
'Das Alter mindert nicht die extreme Enttäuschung darüber, dass eine Kugel Eis vom Kegel fällt',
getStarted: 'Loslegen!',
welcome: 'Willkommen',
loginBtn: 'Anmeldung',
registerBtn: 'Registrieren',
name: 'Name',
phone: 'Telefon',
email: 'Email',
emailphone: 'E-Mail oder Telefon',
username: 'Nutzername',
password: 'Passwort',
deliveryAddress: 'Lieferadresse',
registerText: 'Oder registriere dich bei',
forgotPassword: 'Passwort vergessen?',
loginText: 'Oder Einloggen mit',
deliveryLocation:
'Stellen Sie Ihren Lieferort so ein, dass wir Ihnen einen unendlichen Geschmack köstlichen Essens zusenden können.',
locationBtn: 'Standort einschalten',
locationPermissionDenied:
'Die Berechtigung zum Zugriff auf den Speicherort wurde verweigert',
locationOff: 'Ort einschalten und erneut versuchen',
titleLanguage: 'Sprache ändern',
titleMenu: 'Speisekarte',
titleOrders: 'meine Bestellungen',
titleNotifications: 'Benachrichtigungen',
titleReviews: 'Bewertungen',
titleSettings: 'die Einstellungen',
titleHelp: 'Hilfe',
titleLogout: 'Ausloggen',
titleCart: 'Mein Warenkorb',
titlePayment: 'Zahlung',
orderId: 'Auftragsnummer',
totalOrderAmount: 'Gesamtbestellmenge',
reOrder: 'Nachbestellen',
unReadNotifications: 'Keine ungelesenen Benachrichtigungen',
upload: 'Hochladen',
saveBtn: 'sparen',
emailUs: 'Mailen Sie uns an',
question1: 'Wo finden wir das Essen?',
question2: 'Wie treten wir in Kontakt?',
question3: 'Wie kann ich den Zusteller bezahlen?',
question4: 'Ist der Dienst in meiner Stadt verfügbar?',
answer1:
'Sie finden das Essen in einem Geschäft in Ihrer Nähe, ohne den Kundenservice zu belasten. Unsere Gebühren sind im Vergleich zu anderen extrem niedrig.',
answer2:
'Sie können uns über unsere E-Mail, Telefonnummer oder unsere Website kontaktieren.',
answer3:
'Sie können den Zusteller persönlich bezahlen oder auch online mit Kredit- oder Debitkarte bezahlen.',
answer4:
'Derzeit ist dieser Service in den Städten Islamabad und Karachi verfügbar. Sie können uns kontaktieren, um diesen Service in Ihrer Stadt in Anspruch zu nehmen.',
add: 'HINZUFÜGEN',
quantity: 'Menge',
size: 'Größe',
addToCart: 'in den Warenkorb legen',
orderNow: 'Jetzt bestellen',
addToCartMessage: 'Zum Warenkorb hinzugefügt',
emptyCart: 'Keine Artikel im Warenkorb',
itemTotal: 'Artikel Gesamt',
delvieryCharges: 'Versandkosten',
total: 'Gesamt',
contactInfo: 'Kontaktinformation',
deliveryAddressmessage: 'Lieferadresse einstellen',
proceedCheckout: 'Zur Kasse',
paymentText: 'Wie möchten Sie bezahlen?',
checkout: 'Auschecken',
header_title1: 'Kreditkarte Debitkarte',
header_subscript1: 'Bezahlen Sie mit Kredit- oder Debitkarte',
header_title2: 'Paypal',
header_subscipt2: 'Zahlen Sie online mit Paypal',
header_title3: 'Nachnahme',
header_subscript3: 'Zahlen Sie, wenn Sie den Artikel erhalten',
thankYou: 'Danke dir!',
orderConfirmed: 'Ihre Bestellung wird bestätigt',
orderAmount: 'Ihre Bestellmenge',
orderDetail: 'Bestelldetails',
paymentMethod: 'Zahlungsmethode',
trackOrder: 'Versandverfolgung',
backToMenu: 'Zurück zum Menü',
foodItem: 'Essensgegenstand',
deliveredTo: 'Geliefert an',
writeAReview: 'Eine Rezension schreiben',
orderReceived: 'Bestellung erhalten',
cancelled: 'Abgebrochen',
foodPreparing: 'Essen wird zubereitet',
delivered: 'Geliefert',
rateAndReview: 'Bewerten und bewerten',
reviewPlaceholder: 'Detaillierte Bewertungen erhalten mehr Sichtbarkeit ...',
submit: 'einreichen',
noWorriesText: 'Keine Sorge, lassen Sie sich von uns helfen!',
yourEmail: 'Deine E-Mail',
send: 'Senden',
apply: 'Sich bewerben',
checkEmail:
'Überprüfen Sie Ihre E-Mail-Adresse auf den Link zum Zurücksetzen des Kennworts',
languageText: 'Bitte wählen Sie Ihre gewünschte Sprache',
countryCodePickerTranslation: 'deu',
countryCodeSelect: 'Ländercode auswählen',
paymentNotSupported: 'Diese Zahlungsmethode unterstützt diese Währung nicht'
}

119
Rider App/languages/en.js Normal file
View File

@ -0,0 +1,119 @@
export const en = {
title0: 'Selected Language',
subtitle0: 'English',
description0:
'Select any language of your choice to change the content of the app to your required language.',
title1: 'Tasty',
subtitle1: 'BreakFast',
description1:
'Breakfast is everything. The beginning, the first thing. It is the mouthful that is the commitment to a new day, a continuing life.',
title2: 'Refreshing',
subtitle2: 'Drinks',
description2:
'True silence is the rest of the mind, and is to the spirit what sleep is to the body, nourishment and refreshment.',
title3: 'Delicous',
subtitle3: 'Icecream',
description3:
'Age does not diminish the extreme disappointment of having a scoop of ice cream fall from the cone',
getStarted: 'Get Started!',
welcome: 'Welcome',
loginBtn: 'Login',
registerBtn: 'Register',
name: 'Name',
phone: 'Phone',
email: 'Email',
emailphone: 'Email or Phone',
username: 'Username',
password: 'Password',
deliveryAddress: 'Delivery Address',
registerText: 'Or Register With',
forgotPassword: 'Forgot Password?',
loginText: 'Or Login With',
deliveryLocation:
'Turn on location so we could send you endless taste of delicious food.',
locationBtn: 'Turn on Location',
locationPermissionDenied: 'Permission to access location was denied',
cameraRollPermissionDenied: 'Permission to access Camera Roll was denied',
locationOff: 'Turn on location and try again',
titleLanguage: 'Change Language',
titleMenu: 'Menu',
titleOrders: 'My Orders',
NewOrders: 'New Orders',
titleNotifications: 'Notifications',
titleReviews: 'Reviews',
titleSettings: 'Settings',
titleHelp: 'Help',
titleLogout: 'Logout',
titleCart: 'My Cart',
titlePayment: 'Payment',
orderId: 'Order ID',
totalOrderAmount: 'Total Order Amount',
reOrder: 'Reorder',
unReadNotifications: 'No unread notifications',
upload: 'Upload',
saveBtn: 'Save',
emailUs: 'Email us at',
question1: 'Where do we find the food?',
question2: 'How do we contact?',
question3: 'How can I pay the delivery person?',
question4: 'Is the service available in my city?',
answer1:
'You can find the food at your nearest store without paying anything to customer service.Our charges are extremely low as compared to others.',
answer2: 'You can contact us through our email, phone number or our website.',
answer3:
'You can pay the delivery person in person or pay online as well through credit or debit card.',
answer4:
'Currently this service is available in cities Islamabad and Karachi you can contact us to avail this service in your city.',
add: 'ADD',
quantity: 'Quantity',
size: 'Size',
addToCart: 'Add to Cart',
orderNow: 'Order Now',
addToCartMessage: 'Added to cart',
emptyCart: 'No items in cart',
itemTotal: 'Item Total',
delvieryCharges: 'Delivery Charges',
total: 'Total',
contactInfo: 'Contact Info',
deliveryAddressmessage: 'Set delivery address',
proceedCheckout: 'Proceed to Checkout',
paymentText: 'How do you wish to pay?',
checkout: 'Checkout',
header_title1: 'Credit Card/Debit Card',
header_subscript1: 'Pay with Credit or Debit Card',
header_title2: 'PayPal',
header_subscript2: 'Pay online with PayPal',
header_title3: 'Cash on delivery',
header_subscript3: 'Pay when you recieve the item',
thankYou: 'Thank You!',
orderConfirmed: 'Your Order is confirmed',
orderAmount: 'Your Order Amount',
orderDetail: 'Order Detail',
paymentMethod: 'Payment Method',
trackOrder: 'Track Order',
backToMenu: 'Back To Menu',
foodItem: 'Food item',
deliveredTo: 'Delivered to',
writeAReview: 'Write a Review',
orderReceived: 'Order Received',
cancelled: 'Cancelled',
foodPreparing: 'Food is being prepared',
delivered: 'Delivered',
rateAndReview: 'Rate and Review',
reviewPlaceholder: 'More detailed reviews get more visibility...',
submit: 'Submit',
noWorriesText: 'No worries, let us help you out!',
yourEmail: 'Your Email',
send: 'Send',
apply: 'Apply',
checkEmail: 'Check your email for reset password link',
languageText: 'Please select your required language',
countryCodePickerTranslation: 'eng',
countryCodeSelect: 'Select Country Code',
paymentNotSupported: 'This payment method does not support this Currency',
Orders: 'Orders',
deliveryTime: 'Delivery Time',
myOrders: 'My Orders',
newOrders: 'New Orders',
titleChat: 'Chat'
}

115
Rider App/languages/fr.js Normal file
View File

@ -0,0 +1,115 @@
export const fr = {
title0: 'Langue sélectionnée',
subtitle0: 'English',
description0:
"Sélectionnez la langue de votre choix pour modifier le contenu de l'application dans la langue de votre choix.",
title1: 'Savoureux',
subtitle1: 'Petit déjeuner',
description1:
"Le petit déjeuner est tout. Le début, la première chose. C'est la bouchée qui est l'engagement pour un nouveau jour, une vie continue.",
title2: 'Rafraîchissant',
subtitle2: 'Boissons',
description2:
"Le vrai silence est le reste de l'esprit, et à l'esprit ce que le sommeil est pour le corps, nourriture et rafraîchissement.",
title3: 'Délicieux',
subtitle3: 'Crème glacée',
description3:
"L'âge ne diminue en rien l'extrême déception d'avoir une boule de glace tombée du cône.",
getStarted: 'Commencer!',
welcome: 'Bienvenue',
loginBtn: "S'identifier",
registerBtn: 'registre',
name: 'prénom',
phone: 'Téléphone',
email: 'Email',
emailphone: 'Email ou téléphone',
username: "Nom d'utilisateur",
password: 'Mot de passe',
deliveryAddress: 'Adresse de livraison',
registerText: "Ou s'inscrire avec",
forgotPassword: 'Mot de passe oublié?',
loginText: 'Ou connectez-vous avec',
deliveryLocation:
'Définissez votre lieu de livraison afin que nous puissions vous envoyer un goût infini de plats délicieux.',
locationBtn: "Activer l'emplacement",
locationPermissionDenied:
"La permission d'accéder à l'emplacement a été refusée",
locationOff: "Activer l'emplacement et réessayer",
titleLanguage: 'Changer de langue',
titleMenu: 'Menu',
titleOrders: 'Mes commandes',
titleNotifications: 'Les notifications',
titleReviews: 'Avis',
titleSettings: 'Réglages',
titleHelp: 'Aidez-moi',
titleLogout: 'Connectez - Out',
titleCart: 'Mon panier',
titlePayment: 'Paiement',
orderId: 'numéro de commande',
totalOrderAmount: 'Total de la commande',
reOrder: 'Réorganiser',
unReadNotifications: 'Pas de notifications non lues',
upload: 'Télécharger',
saveBtn: 'sauvegarder',
emailUs: 'écrivez-nous à',
question1: 'Où trouvons-nous la nourriture?',
question2: 'Comment pouvons-nous contacter?',
question3: 'Comment puis-je payer le livreur?',
question4: 'Le service est-il disponible dans ma ville?',
answer1:
'Vous pouvez trouver la nourriture dans le magasin le plus proche sans rien payer au service client. Nos frais sont extrêmement bas comparés aux autres.',
answer2:
'Vous pouvez nous contacter via notre email, numéro de téléphone ou notre site web.',
answer3:
'Vous pouvez payer le livreur en personne ou en ligne, par carte de crédit ou de débit.',
answer4:
"Actuellement, ce service est disponible dans les villes d'Islamabad et de Karachi. Vous pouvez nous contacter pour bénéficier de ce service dans votre ville.",
add: 'AJOUTER',
quantity: 'Quantité',
size: 'Taille',
addToCart: 'Ajouter au panier',
orderNow: 'Commandez maintenant',
addToCartMessage: 'Ajouté au panier',
emptyCart: 'Aucun article dans le panier',
itemTotal: 'Objet total',
delvieryCharges: 'Frais de livraison',
total: 'Total',
contactInfo: 'Informations de contact',
deliveryAddressmessage: "Définir l'adresse de livraison",
proceedCheckout: 'Passer à la caisse',
paymentText: 'Comment souhaitez-vous payer?',
checkout: 'Check-out',
header_title1: 'Carte de crédit carte de débit',
header_subscript1: 'Payer avec une carte de crédit ou de débit',
header_title2: 'PayPal',
header_subscript2: 'Payez en ligne avec PayPal',
header_title3: 'Paiement à la livraison',
header_subscript3: "Payer quand vous recevez l'article",
thankYou: 'Je vous remercie!',
orderConfirmed: 'Votre commande est confirmée',
orderAmount: 'Le montant de votre commande',
orderDetail: 'Détails de la commande',
paymentMethod: 'Mode de paiement',
trackOrder: 'Suivi de commande',
backToMenu: 'Suivi de commande',
foodItem: 'Produit alimentaire',
deliveredTo: 'livré à',
writeAReview: 'Écrire une critique',
orderReceived: 'Ordre reçu',
cancelled: 'Annulé',
foodPreparing: 'La nourriture est en cours de préparation',
delivered: 'Livré',
rateAndReview: 'Notez et évaluez',
reviewPlaceholder: 'Des revues plus détaillées ont plus de visibilité ...',
submit: 'Soumettre',
noWorriesText: 'Pas de soucis, laissez-nous vous aider!',
yourEmail: 'Votre email',
send: 'Envoyer',
apply: 'Appliquer',
checkEmail:
'Vérifiez votre email pour le lien de réinitialisation du mot de passe',
languageText: "S'il vous plaît sélectionnez votre langue requise",
countryCodePickerTranslation: 'fra',
countryCodeSelect: 'Sélectionnez le code pays',
paymentNotSupported: 'Ce mode de paiement ne prend pas en charge cette devise'
}

115
Rider App/languages/km.js Normal file
View File

@ -0,0 +1,115 @@
export const km = {
title0: 'ភាសាដែលបានជ្រើស',
subtitle0: 'English',
description0:
'ជ្រើសរើសភាសាណាមួយនៃជម្រើសរបស់អ្នកដើម្បីប្តូរមាតិកានៃកម្មវិធីទៅភាសាដែលអ្នកត្រូវការ។',
title1: 'ហ៊ាន',
subtitle1: 'អាហារពេលព្រឹក',
description1:
'អាហារពេលព្រឹកគឺជាអ្វីគ្រប់យ៉ាង។ ការចាប់ផ្តើមរឿងដំបូង។ វាគឺជាមាត់ដែលជាការប្តេជ្ញាចិត្តចំពោះថ្ងៃថ្មីនិងជីវិតដែលនៅតែបន្ត។',
title2: 'ធ្វើឱ្យស្រស់',
subtitle2: 'ភេសជ្ជៈ',
description2:
'ភាពស្ងៀមស្ងាត់ពិតគឺជាគំនិតដែលនៅសល់ហើយជាវិញ្ញាណដែលជាការដេកលក់ដល់រាងកាយការថែទាំនិងការសំរាក។',
title3: 'ឆ្ងាញ់',
subtitle3: 'ការ៉េម',
description3:
'អាយុមិនបន្ថយនូវការខកចិត្តយ៉ាងខ្លាំងនៃការធ្លាក់ចុះនៃការ៉េមពីកោណទេ',
getStarted: 'ចាប់ផ្ដើម!',
welcome: 'សូមស្វាគមន៏ទៅ',
loginBtn: 'ចូល',
registerBtn: 'ចុះឈ្មោះ',
name: 'ឈ្មោះ',
phone: 'ទូរស័ព្ទ',
email: 'អ៊ីមែល',
emailphone: 'អី​ុ​ម៉ែ​ល​ឬ​ទូរស័ព្ទ',
username: 'ឈ្មោះ​អ្នកប្រើប្រាស់',
password: 'ពាក្យសម្ងាត់',
deliveryAddress: 'អាស័យ​ដ្ឋាន​សំរាប់​ការ​ដឹកជញ្ជូន',
registerText: 'ឬចុះឈ្មោះជាមួយ',
forgotPassword: 'ភ្លេច​លេខសំងាត់​?',
loginText: 'ឬចូលជាមួយ',
deliveryLocation:
'កំណត់ទីតាំងដឹកជញ្ជូនរបស់អ្នកដូច្នេះយើងអាចផ្ញើឱ្យអ្នកនូវរសជាតិអាហារឆ្ងាញ់គ្មានទីបញ្ចប់។',
locationBtn: 'បើកទីតាំង',
locationPermissionDenied: 'ការអនុញ្ញាតចូលទៅកាន់ទីតាំងត្រូវបានបដិសេធ',
locationOff: 'បើកទីតាំងហើយព្យាយាមម្តងទៀត',
titleLanguage: 'ប្ដូរ​ភាសា',
titleMenu: 'ម៉ឺនុយ',
titleOrders: 'ការបញ្ជាទិញរបស់ខ្ញុំ',
titleNotifications: 'ការជូនដំណឹង',
titleReviews: 'ពិនិត្យ',
titleSettings: 'ការកំណត់',
titleHelp: 'ជំនួយ',
titleLogout: 'ចាកចេញ',
titleCart: 'កន្ត្រករបស់ខ្ញុំ',
titlePayment: 'ការទូទាត់',
orderId: 'លេខ​សម្គាល់​លំដាប់',
totalOrderAmount: 'បរិមាណសរុប',
reOrder: 'តម្រៀបឡើងវិញ',
unReadNotifications: 'គ្មានការជូនដំណឹងមិនទាន់អាន',
upload: 'ផ្ទុកឡើង',
saveBtn: 'រក្សាទុក',
emailUs: 'អ៊ីម៉ែលមកយើងនៅ',
question1: 'តើយើងរកម្ហូបបាននៅឯណា?',
question2: 'តើយើងទាក់ទងយ៉ាងដូចម្តេច?',
question3: 'តើខ្ញុំអាចបង់ប្រាក់ដល់មនុស្សដែលត្រូវដឹកជញ្ជូន?',
question4: 'តើសេវាកម្មនេះមាននៅក្នុងទីក្រុងរបស់ខ្ញុំដែរឬទេ?',
answer1:
'អ្នកអាចរកឃើញម្ហូបអាហារនៅហាងដែលនៅជិតបំផុតរបស់អ្នកដោយមិនបង់អ្វីឱ្យសេវាកម្មបំរើអតិថិជន។ ការចោទប្រកាន់របស់យើងមានកម្រិតទាបខ្លាំងបើប្រៀបធៀបទៅនឹងអ្នកដទៃ។',
answer2: 'អ្នកអាចទាក់ទងយើងតាមរយៈអ៊ីម៉ែលលេខទូរស័ព្ទឬវេបសាយរបស់យើង។',
answer3:
'អ្នកអាចបង់ប្រាក់ទៅកាន់បុគ្គលដឹកជញ្ជូនដោយផ្ទាល់ឬបង់ប្រាក់តាមរយៈអ៊ីនធឺណេតតាមរយៈប័ណ្ណឥណទានឬឥណពន្ធផងដែរ។',
answer4:
'បច្ចុប្បន្នសេវាកម្មនេះអាចរកបាននៅក្នុងទីក្រុងអ៊ីស្លាម៉ាបាតនិងការ៉ាជីដែលអ្នកអាចទាក់ទងមកយើងដើម្បីទទួលបានសេវាកម្មនេះនៅក្នុងទីក្រុងរបស់អ្នក។',
add: 'បន្ថែម',
quantity: 'បរិមាណ',
size: 'ទំហំ',
addToCart: 'បន្ថែមទៅកន្ត្រក',
orderNow: 'បញ្ជាទិញឥឡូវ',
addToCartMessage: 'បានបន្ថែមទៅរទេះ',
emptyCart: 'គ្មានធាតុក្នុងរទេះទេ',
itemTotal: 'ធាតុសរុប',
delvieryCharges: 'ការដឹកជញ្ជូន',
total: 'សរុប',
contactInfo: 'ព័ត៌មានទំនាក់ទំនង',
deliveryAddressmessage: 'កំណត់អាសយដ្ឋានបញ្ជូន',
proceedCheckout: 'បន្តដើម្បីពិនិត្យចេញ',
paymentText: 'តើអ្នកចង់បង់ប្រាក់ដោយរបៀបណា?',
checkout: 'ពិនិត្យ​មុន​ពេល​ចេញ',
header_title1: 'ប័ណ្ណឥណទាន / ប័ណ្ណឥណពន្ធ',
header_subscript1: 'ទូទាត់ជាមួយកាតឥណទានឬឥណពន្ធ',
header_title2: 'PayPal',
header_subscript2: 'ទូទាត់លើបណ្តាញ PayPal',
header_title3: 'សាច់ប្រាក់នៅពេលប្រគល់',
header_subscript3: 'បង់ពេលអ្នកទទួលបានធាតុ',
thankYou: 'សូមអរគុណ!',
orderConfirmed: 'បញ្ជាទិញរបស់អ្នកត្រូវបានបញ្ជាក់',
orderAmount: 'ចំនួនទឹកប្រាក់នៃការបញ្ជាទិញរបស់អ្នក',
orderDetail: 'លំអិតលំដាប់',
paymentMethod: 'វិធី​សា​ស្រ្ត​ទូទាត់',
trackOrder: 'លំដាប់តាមបទ',
backToMenu: 'លំដាប់តាមបទ',
foodItem: 'ម្ហូបអាហារ',
deliveredTo: 'បញ្ជូនទៅ',
writeAReview: 'សរសេរ​សង្ខេប​ឡើងវិញ',
orderReceived: 'បញ្ជាទិញដែលទទួលបាន',
cancelled: 'បានបោះបង់',
foodPreparing: 'ម្ហូបកំពុងត្រូវបានរៀបចំ',
delivered: 'បានបញ្ជូន',
rateAndReview: 'វាយតម្លៃនិងពិនិត្យ',
reviewPlaceholder: 'ការពិនិត្យលម្អិតបន្ថែមទៀតទទួលបានភាពមើលឃើញកាន់តែច្រើន ...',
submit: 'ដាក់ស្នើ',
noWorriesText: 'គ្មានការព្រួយបារម្ភសូមឱ្យយើងជួយអ្នកចេញ!',
yourEmail: 'អ៊ីមែល​របស់​អ្នក',
send: 'ផ្ញើ',
apply: 'អនុវត្ត',
checkEmail: 'ពិនិត្យអ៊ីមែលរបស់អ្នកសម្រាប់តំណពាក្យសម្ងាត់ឡើងវិញ',
languageText: 'សូមជ្រើសរើសភាសាដែលអ្នកត្រូវការ',
countryCodePickerTranslation: 'eng',
countryCodeSelect: 'ជ្រើសរើសលេខកូដប្រទេស',
paymentNotSupported: 'វិធីសាស្ត្រទូទាត់នេះមិនគាំទ្ររូបិយប័ណ្ណនេះទេ',
deliveryTime: 'ពេលវេលាដឹកជញ្ជូន',
myOrders: 'ការបញ្ជាទិញរបស់ខ្ញុំ',
newOrders: 'ការបញ្ជាទិញថ្មី'
}

108
Rider App/languages/zh.js Normal file
View File

@ -0,0 +1,108 @@
export const zh = {
title0: '选定的语言',
subtitle0: 'English',
description0: '选择您选择的任何语言,将应用内容更改为您所需的语言。',
title1: '可口',
subtitle1: '早餐',
description1:
'早餐就是一切。一开始,第一件事。这是对新的一天,持续生活的承诺。',
title2: '清爽',
subtitle2: '饮料',
description2:
'真正的沉默是心灵的其余部分,对于精神来说,睡眠对身体,营养和茶点来说都是如此。',
title3: '美味的',
subtitle3: '冰淇淋',
description3: '年龄不会减少从冰锥中舀出一勺冰淇淋的极度失望',
getStarted: '开始吧!',
welcome: '欢迎来到',
loginBtn: '登录',
registerBtn: '寄存器',
name: '名称',
phone: '电话',
email: '电子邮件',
emailphone: '邮件或者电话',
username: '用户名',
password: '密码',
deliveryAddress: '邮寄地址',
registerText: '或注册',
forgotPassword: '忘记密码?',
loginText: '或登录',
deliveryLocation: '设置您的送货地点,以便我们可以向您发送无尽的美味食物。',
locationBtn: '打开位置',
locationPermissionDenied: '访问位置的权限被拒绝',
locationOff: '访问位置的权限被拒绝',
titleLanguage: '改变语言',
titleMenu: '菜单',
titleOrders: '我的订单',
titleNotifications: '通知',
titleReviews: '评测',
titleSettings: '设置',
titleHelp: '救命',
titleLogout: '登出',
titleCart: '我的车',
titlePayment: '付款',
orderId: '订单ID',
totalOrderAmount: '总订单金额',
reOrder: '重新排序',
unReadNotifications: '没有未读通知',
upload: '上传',
saveBtn: '保存',
emailUs: '给我们发电子邮件',
question1: '我们在哪里找到食物?',
question2: '我们如何联系?',
question3: '我该如何付款给送货人?',
question4: '我的城市有这项服务吗?',
answer1:
'您可以在离您最近的商店找到食物,而无需向客户支付任何费用。与其他人相比,我们的收费极低。',
answer2: '您可以通过我们的电子邮件,电话号码或我们的网站联系我们。',
answer3: '您可以亲自向付款人付款或使用信用卡或借记卡在线付款。',
answer4:
'目前,这项服务在伊斯兰堡和卡拉奇市提供,您可以联系我们以便在您所在的城市使用此服务。',
add: '加',
quantity: '数量',
size: '尺寸',
addToCart: '添加到购物车',
orderNow: '现在下单',
addToCartMessage: '已添加到购物车',
emptyCart: '购物车中没有商品',
itemTotal: '项目总计',
delvieryCharges: '送货费',
total: '总',
contactInfo: '联系信息',
deliveryAddressmessage: '设置送货地址',
proceedCheckout: '进行结算',
paymentText: '你想怎么付钱?',
checkout: '查看',
header_title1: '信用卡/借记卡',
header_subscript1: '使用信用卡或借记卡付款',
header_title2: '贝宝',
header_subscript2: '使用PayPal在线支付',
header_title3: '货到付款',
header_subscript3: '收到物品时付款',
thankYou: 'ស谢谢!',
orderConfirmed: '您的订单已确认',
orderAmount: '您的订单金额',
orderDetail: '订单详情',
paymentMethod: '付款方法',
trackOrder: '跟踪订单',
backToMenu: '跟踪订单',
foodItem: '食品',
deliveredTo: '送到了(送去了',
writeAReview: '写评论',
orderReceived: '订单已经收到',
cancelled: '取消',
foodPreparing: '食物正在准备中',
delivered: '交付',
rateAndReview: '打分和评论',
reviewPlaceholder: '更详细的评论获得更多可见性......',
submit: '提交',
noWorriesText: '不用担心,让我们帮帮你吧!',
yourEmail: '你的邮件',
send: '发送',
apply: '应用',
checkEmail: '查看您的电子邮件以重置密码链接',
languageText: '请选择您需要的语言',
countryCodePickerTranslation: 'zho',
countryCodeSelect: '选择国家代码',
paymentNotSupported: '此付款方式不支持此货币'
}

25953
Rider App/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

88
Rider App/package.json Normal file
View File

@ -0,0 +1,88 @@
{
"name": "enatega-rider-app",
"version": "5.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"eject": "expo eject",
"format": "prettier --write '**/*.js'",
"lint:fix": "eslint . --ext .js --fix",
"postinstall": "patch-package"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": [
"npm run format",
"npm run lint:fix"
]
},
"dependencies": {
"@apollo/react-hooks": "^3.1.3",
"@expo/vector-icons": "^12.0.0",
"@react-native-async-storage/async-storage": "~1.15.0",
"@react-native-community/masked-view": "0.1.10",
"@react-navigation/bottom-tabs": "^5.11.11",
"@react-navigation/drawer": "^5.12.5",
"@react-navigation/native": "^5.5.1",
"@react-navigation/stack": "^5.14.5",
"apollo-boost": "^0.4.3",
"apollo-cache-inmemory": "^1.6.2",
"apollo-client": "^2.6.3",
"apollo-link": "^1.2.12",
"apollo-link-context": "^1.0.18",
"apollo-link-state": "^0.4.2",
"apollo-link-ws": "^1.0.20",
"apollo-upload-client": "^10.0.1",
"expo": "^44.0.0",
"expo-constants": "~13.0.1",
"expo-font": "~10.0.4",
"expo-localization": "~12.0.0",
"expo-location": "~14.0.1",
"expo-notifications": "~0.14.0",
"expo-splash-screen": "~0.14.1",
"expo-task-manager": "~10.1.0",
"expo-updates": "~0.11.6",
"graphql": "^14.3.1",
"graphql-tag": "^2.10.1",
"i18n-js": "^3.3.0",
"patch-package": "^6.2.2",
"react": "17.0.1",
"react-apollo": "^2.5.8",
"react-native": "0.64.3",
"react-native-animatable": "^1.3.2",
"react-native-flash-message": "^0.1.13",
"react-native-gesture-handler": "~2.1.0",
"react-native-gifted-chat": "^0.16.3",
"react-native-maps": "0.29.4",
"react-native-maps-directions": "^1.8.0",
"react-native-material-textfield": "^0.16.1",
"react-native-modal": "^11.5.6",
"react-native-reanimated": "~2.3.1",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "~3.10.1",
"react-native-svg": "12.1.1",
"react-native-webview": "11.15.0",
"subscriptions-transport-ws": "^0.9.16"
},
"devDependencies": {
"babel-preset-expo": "9.0.2",
"eslint": "^7.1.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.20.0",
"eslint-plugin-standard": "^4.0.1",
"husky": "^4.2.5",
"lint-staged": "^10.2.7",
"prettier": "2.0.5",
"prettier-config-standard": "^1.0.1"
},
"private": true
}

View File

@ -0,0 +1,51 @@
diff --git a/node_modules/react-native-material-textfield/src/components/affix/index.js b/node_modules/react-native-material-textfield/src/components/affix/index.js
index 0f85022..c12b3a6 100644
--- a/node_modules/react-native-material-textfield/src/components/affix/index.js
+++ b/node_modules/react-native-material-textfield/src/components/affix/index.js
@@ -11,7 +11,7 @@ export default class Affix extends PureComponent {
static propTypes = {
numberOfLines: PropTypes.number,
- style: Animated.Text.propTypes.style,
+ style: PropTypes.object,
color: PropTypes.string.isRequired,
fontSize: PropTypes.number.isRequired,
diff --git a/node_modules/react-native-material-textfield/src/components/field/index.js b/node_modules/react-native-material-textfield/src/components/field/index.js
index 494bbaa..9bbf2e2 100644
--- a/node_modules/react-native-material-textfield/src/components/field/index.js
+++ b/node_modules/react-native-material-textfield/src/components/field/index.js
@@ -221,6 +221,7 @@ export default class TextField extends PureComponent {
let options = {
toValue: this.focusState(),
+ useNativeDriver: false,
duration,
};
diff --git a/node_modules/react-native-material-textfield/src/components/helper/index.js b/node_modules/react-native-material-textfield/src/components/helper/index.js
index 6060f9f..fe9d9c4 100644
--- a/node_modules/react-native-material-textfield/src/components/helper/index.js
+++ b/node_modules/react-native-material-textfield/src/components/helper/index.js
@@ -11,7 +11,7 @@ export default class Helper extends PureComponent {
disabled: PropTypes.bool,
- style: Animated.Text.propTypes.style,
+ style: PropTypes.object,
baseColor: PropTypes.string,
errorColor: PropTypes.string,
diff --git a/node_modules/react-native-material-textfield/src/components/label/index.js b/node_modules/react-native-material-textfield/src/components/label/index.js
index 82eaf03..809fcdd 100644
--- a/node_modules/react-native-material-textfield/src/components/label/index.js
+++ b/node_modules/react-native-material-textfield/src/components/label/index.js
@@ -43,7 +43,7 @@ export default class Label extends PureComponent {
y1: PropTypes.number,
}),
- style: Animated.Text.propTypes.style,
+ style: PropTypes.object,
label: PropTypes.string,
};

View File

@ -0,0 +1,7 @@
{
"hostType": "lan",
"lanType": "ip",
"dev": true,
"minify": false,
"urlRandomness": null
}

View File

@ -0,0 +1,46 @@
export const order = `fragment orderItem on Order{
_id
order_id
delivery_address{
latitude
longitude
delivery_address
details
label
}
delivery_charges
items {
_id
food {
_id
title
}
quantity
variation{
_id
title
price
}
addons{
_id
title
options{
_id
title
price
}
}
}
user {
_id
name
phone
email
}
order_status
payment_status
payment_method
paid_amount
order_amount
createdAt
}`

View File

@ -0,0 +1,84 @@
import AsyncStorage from '@react-native-async-storage/async-storage'
import { ApolloClient } from 'apollo-client'
import { ApolloLink, split, concat, Observable } from 'apollo-link'
import { createHttpLink } from 'apollo-link-http'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
import { InMemoryCache } from 'apollo-cache-inmemory'
// import { withClientState } from 'apollo-link-state'
import getEnvVars from '../../environment'
const { GRAPHQL_URL, WS_GRAPHQL_URL } = getEnvVars()
export let clientRef = null
function setupApolloClient() {
const cache = new InMemoryCache()
const httpLink = createHttpLink({
uri: GRAPHQL_URL
})
const wsLink = new WebSocketLink({
uri: WS_GRAPHQL_URL,
options: {
reconnect: true
}
})
// const stateLink = withClientState({
// cache
// })
const request = async operation => {
const token = await AsyncStorage.getItem('rider-token')
operation.setContext({
// get the authentication token from local storage if it exists
// return the headers to the context so httpLink can read them
headers: {
authorization: token ? `Bearer ${token}` : ''
}
})
}
const requestLink = new ApolloLink(
(operation, forward) =>
new Observable(observer => {
// console.log(observer)
let handle
Promise.resolve(operation)
.then(oper => request(oper))
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer)
})
})
.catch(observer.error.bind(observer))
return () => {
if (handle) handle.unsubscribe()
}
})
)
const terminatingLink = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink
// httpLink,
)
const client = new ApolloClient({
// link: ApolloLink.from([stateLink, authLink.concat(httpLink)]),
link: concat(ApolloLink.from([terminatingLink, requestLink]), httpLink),
cache,
resolvers: {}
})
clientRef = client
return client
}
export default setupApolloClient

View File

@ -0,0 +1,29 @@
export const riderLogin = `mutation RiderLogin($username:String,$password:String,$notificationToken:String){
riderLogin(username:$username,password:$password,notificationToken:$notificationToken){
userId
token
}
}`
export const updateOrderStatusRider = `mutation UpdateOrderStatusRider($id:String!,$status:String!){
updateOrderStatusRider(id:$id,status:$status){
_id
order_status
}
}
`
export const assignOrder = `mutation AssignOrder($id:String!){
assignOrder(id:$id){
_id
rider{
_id
}
}
}`
export const updateLocation = `mutation UpdateRiderLocation($latitude:String!,$longitude:String!){
updateRiderLocation(latitude:$latitude,longitude:$longitude){
_id
}
}`

View File

@ -0,0 +1,231 @@
export const profile = `
query Rider($id:String){
rider(id:$id){
_id
name
username
available
}
}`
export const assignedOrders = `query AssignedOrders($id:String){
assignedOrders(id:$id){
_id
rider{
_id
}
order_id
delivery_address{
latitude
longitude
delivery_address
details
label
}
delivery_charges
payment_method
order_amount
paid_amount
order_status
payment_status
user{
_id
name
phone
email
}
items{
_id
food{
_id
title
}
quantity
variation{
_id
title
price
}
addons{
title
options{
title
price
}
}
}
createdAt
}
}`
export const subscriptionAssignRider = `subscription SubscriptionAssignRider($riderId:String!){
subscriptionAssignRider(riderId:$riderId){
userId
origin
order{
_id
rider{
_id
}
order_id
delivery_address{
latitude
longitude
delivery_address
details
label
}
delivery_charges
payment_method
order_amount
paid_amount
order_status
payment_status
user{
_id
name
phone
email
}
items{
_id
food{
_id
title
}
quantity
variation{
_id
title
price
}
addons{
_id
title
options{
_id
title
price
}
}
}
createdAt
}
}
}`
export const configuration = `query Configuration{
configuration{
_id
currency
currency_symbol
delivery_charges
}
}`
export const unassignedOrders = `query UnAssignedOrders{
unassignedOrders{
_id
rider{
_id
}
order_id
delivery_address{
latitude
longitude
delivery_address
details
label
}
delivery_charges
payment_method
order_amount
paid_amount
order_status
payment_status
user{
_id
name
phone
email
}
items{
_id
food{
_id
title
}
quantity
variation{
_id
title
price
}
addons{
_id
title
options{
_id
title
price
}
}
}
createdAt
}
}`
export const subscriptionUnAssignedOrder = `subscription SubscriptionUnAssignedOrder{
unassignedOrder{
userId
origin
order{
_id
rider{
_id
}
order_id
delivery_address{
latitude
longitude
delivery_address
details
label
}
delivery_charges
payment_method
order_amount
paid_amount
order_status
payment_status
user{
_id
name
phone
email
}
items{
_id
food{
_id
title
}
quantity
variation{
_id
title
price
}
addons{
_id
title
options{
_id
title
price
}
}
}
createdAt
}
}
}`

View File

@ -0,0 +1,56 @@
import React, { useContext } from 'react'
import { FlatList } from 'react-native'
import Spinner from '../Spinner/Spinner'
import Order from '../Order/Order'
import ConfigurationContext from '../../context/configuration'
import UserContext from '../../context/user'
import TextError from '../Text/TextError/TextError'
import { useNavigation } from '@react-navigation/native'
import { verticalScale } from '../../utilities/scaling'
export default function Orders() {
const navigation = useNavigation()
const configuration = useContext(ConfigurationContext)
const {
loadingAssigned,
errorAssigned,
assignedOrders,
refetchAssigned,
networkStatusAssigned
} = useContext(UserContext)
if (loadingAssigned) return <Spinner />
if (errorAssigned) return <TextError text="Something is worng" />
function emptyView() {
return <TextError text="No Orders Assigned yet!" />
}
return (
<FlatList
keyExtractor={item => item._id}
data={assignedOrders.length > 0 ? assignedOrders.slice().reverse() : []}
refreshing={networkStatusAssigned === 4}
onRefresh={() => refetchAssigned()}
initialNumToRender={3}
ListEmptyComponent={emptyView}
style={{ marginTop: verticalScale(-80) }}
renderItem={({ item }) => (
<Order
key={item._id}
orderId={item.order_id}
orderStatus={item.order_status}
orderAmount={`${configuration.currency_symbol}${item.order_amount}`}
orderDatetime={item.createdAt}
paymentMethod={item.payment_method}
onPress={() => {
navigation.navigate('OrderDetail', {
id: item._id,
orderId: item.order_id
})
}}
/>
)}
/>
)
}

View File

@ -0,0 +1,8 @@
import { createIconSetFromIcoMoon } from '@expo/vector-icons'
import icoMoonConfig from './selection.json'
export const CustomIcon = createIconSetFromIcoMoon(
icoMoonConfig,
'icomoon',
'icomoon.ttf'
)

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
import React from 'react'
import { Image } from 'react-native'
import styles from './styles'
const FdFullLogo = () => {
return (
<Image
style={styles.logo_image}
source={require('../../../assets/images/ui/transparent-icon.png')}
resizeMode="contain"
/>
)
}
export default FdFullLogo

View File

@ -0,0 +1,8 @@
export default {
logo_image: {
height: '17%',
width: '55%',
alignSelf: 'center',
marginBottom: '50%'
}
}

View File

@ -0,0 +1,48 @@
import React from 'react'
import { TouchableOpacity } from 'react-native'
import { View } from 'react-native-animatable'
import PropTypes from 'prop-types'
import colors from '../../utilities/colors'
import styles from './styles'
const DEFAULT_SIZE_MULTIPLIER = 0.7
export default function RadioButton(props) {
const { size, innerColor, outerColor, isSelected, onPress } = props
const outerStyle = {
borderColor: outerColor,
width: size + size * DEFAULT_SIZE_MULTIPLIER,
height: size + size * DEFAULT_SIZE_MULTIPLIER,
borderRadius: (size + size * DEFAULT_SIZE_MULTIPLIER) / 2,
borderWidth: isSelected ? size / 2 : 1
}
const innerStyle = {
width: size / 2,
height: size / 2,
borderRadius: size / 2,
backgroundColor: innerColor
}
return (
<TouchableOpacity style={[styles.radio, outerStyle]} onPress={onPress}>
{isSelected ? <View style={innerStyle} {...props} /> : null}
</TouchableOpacity>
)
}
RadioButton.propTypes = {
size: PropTypes.number,
innerColor: PropTypes.string,
outerColor: PropTypes.string,
isSelected: PropTypes.bool,
onPress: PropTypes.func
}
RadioButton.defaultProps = {
size: 16,
innerColor: colors.radioColor,
outerColor: colors.radioOuterColor,
isSelected: false,
onPress: () => null
}

View File

@ -0,0 +1,7 @@
export default {
radio: {
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center'
}
}

View File

@ -0,0 +1,20 @@
import { showMessage } from 'react-native-flash-message'
import PropTypes from 'prop-types'
import { scale } from '../../utilities/scaling'
import { textStyles } from '../../utilities/textStyles'
export const FlashMessage = props => {
showMessage({
backgroundColor: '#323232',
message: props.message,
type: 'info',
position: 'bottom',
titleStyle: {
fontSize: scale(12),
...textStyles.Bold
}
})
}
FlashMessage.propTypes = {
message: PropTypes.string.isRequired
}

View File

@ -0,0 +1,13 @@
import { fontStyles } from '../../utilities/fontStyles'
import { scale } from '../../utilities/scaling'
export default {
text: {
fontSize: scale(14),
fontFamily: fontStyles.MuseoSans500,
paddingTop: 6
},
container: {
borderRadius: 50
}
}

View File

@ -0,0 +1,120 @@
import {
CommonActions,
useNavigation,
useTheme
} from '@react-navigation/native'
import PropTypes from 'prop-types'
import React from 'react'
import { Pressable, View } from 'react-native'
import colors from '../../../utilities/colors'
import { ICONS_NAME } from '../../../utilities/constant'
import { scale } from '../../../utilities/scaling'
import { CustomIcon } from '../../CustomIcon'
import styles from './styles'
function HeaderIcon({ icon, iconColor, iconSize = scale(25) }) {
return (
<CustomIcon
name={icon}
size={iconSize}
color={iconColor || colors.fontSecondColor}
/>
)
}
function LeftButton(props) {
const { icon, outerView, iconColor } = props
const navigation = useNavigation()
const { colors } = useTheme()
switch (icon) {
case ICONS_NAME.Menu:
return (
<View style={[styles.btnContainer, outerView]}>
<Pressable
hitSlop={50}
pressRetentionOffset={50}
android_ripple={{
borderless: true,
color: colors.rippleColor,
radius: 23
}}
style={({ pressed }) => [
{
opacity: pressed ? 0.7 : 1
}
]}
onPress={() => navigation.toggleDrawer()}>
<HeaderIcon iconColor={iconColor} icon={icon} />
</Pressable>
</View>
)
case ICONS_NAME.Back:
return (
<View style={[styles.btnContainer, outerView]}>
<Pressable
hitSlop={50}
pressRetentionOffset={50}
android_ripple={{
borderless: true,
color: colors.rippleColor,
radius: 23
}}
style={({ pressed }) => [
{
opacity: pressed ? 0.7 : 1
}
]}
onPress={() => navigation.goBack()}>
<HeaderIcon iconColor={iconColor} icon={icon} />
</Pressable>
</View>
)
case ICONS_NAME.Cross:
return (
<View style={[styles.btnContainer, outerView]}>
<Pressable
hitSlop={50}
pressRetentionOffset={50}
android_ripple={{
borderless: true,
color: colors.rippleColor,
radius: 23
}}
style={({ pressed }) => [
{
opacity: pressed ? 0.7 : 1
}
]}
onPress={() =>
navigation.dispatch(state => {
const routes = state.routes.filter(r => r.name === 'Menu')
return CommonActions.reset({
...state,
routes,
index: 0
})
})
}>
<HeaderIcon iconColor={iconColor} icon={icon} />
</Pressable>
</View>
)
default:
return null
}
}
HeaderIcon.propTypes = {
outerView: PropTypes.object,
icon: PropTypes.string,
iconColor: PropTypes.string,
iconSize: PropTypes.number
}
LeftButton.propTypes = {
outerView: PropTypes.object,
icon: PropTypes.string,
iconColor: PropTypes.string
}
export { HeaderIcon, LeftButton }

View File

@ -0,0 +1,18 @@
import { StyleSheet } from 'react-native'
import { alignment } from '../../../utilities/alignment'
const styles = StyleSheet.create({
leftIconPadding: {
...alignment.PLsmall,
...alignment.PRlarge
},
btnContainer: {
backgroundColor: 'rgba(255, 255, 255,0.01)',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
width: 80
}
})
export default styles

View File

@ -0,0 +1,3 @@
import { HeaderIcon, LeftButton } from './HeaderIcons/HeaderIcons'
export { HeaderIcon, LeftButton }

View File

@ -0,0 +1,59 @@
import React, { useContext } from 'react'
import { FlatList } from 'react-native'
import Spinner from '../Spinner/Spinner'
import Order from '../Order/Order'
import ConfigurationContext from '../../context/configuration'
import UserContext from '../../context/user'
import TextError from '../Text/TextError/TextError'
import { useNavigation } from '@react-navigation/native'
import { verticalScale } from '../../utilities/scaling'
export default function Orders() {
const navigation = useNavigation()
const configuration = useContext(ConfigurationContext)
const {
loadingUnAssigned,
errorUnAssigned,
unAssignedOrders,
refetchUnAssigned,
networkStatusUnAssigned
} = useContext(UserContext)
function emptyView() {
return <TextError text="No New Order" />
}
if (loadingUnAssigned) return <Spinner />
if (errorUnAssigned) return <TextError text="Something is worng" />
return (
<FlatList
keyExtractor={item => item._id}
data={
unAssignedOrders.length > 0 ? unAssignedOrders.slice().reverse() : []
}
style={{ marginTop: verticalScale(-80) }}
refreshing={networkStatusUnAssigned === 4}
onRefresh={refetchUnAssigned}
initialNumToRender={3}
ListEmptyComponent={emptyView}
renderItem={({ item }) => (
<Order
key={item._id}
orderId={item.order_id}
orderStatus={item.order_status}
orderAmount={`${configuration.currency_symbol}${item.order_amount}`}
orderDatetime={item.createdAt}
paymentMethod={item.payment_method}
onPress={() => {
console.log('item', item)
navigation.navigate('OrderDetail', {
id: item._id,
orderId: item.order_id
})
}}
/>
)}
/>
)
}

View File

@ -0,0 +1,7 @@
const styles = {
flex: {
flex: 1
}
}
export default styles

View File

@ -0,0 +1,206 @@
import React from 'react'
import { View, TouchableOpacity } from 'react-native'
import { scale } from '../../utilities/scaling'
import colors from '../../utilities/colors'
import styles from './style'
import i18n from '../../../i18n'
import TextDefault from '../Text/TextDefault/TextDefault'
import { alignment } from '../../utilities/alignment'
import { AntDesign } from '@expo/vector-icons'
import PropTypes from 'prop-types'
export const orderStatuses = [
{
key: 'PENDING',
color: '#518ef8'
},
{
key: 'ACCEPTED',
color: '#518ef8'
},
{
key: 'PICKED',
color: '#febb2c'
},
{
key: 'DELIVERED',
color: '#28b446'
},
{
key: 'COMPLETED',
color: '#f14336'
}
]
function Order(props) {
// const cardHeight = props.height
// ? props.height
// : PixelRatio.getFontScale() * verticalScale(150)
const checkStatus = status => {
const obj = orderStatuses.filter(x => {
return x.key === status
})
return obj[0]
}
const statusColor = checkStatus(props.orderStatus).color
return (
<TouchableOpacity activeOpacity={1} onPress={props.onPress}>
<View style={[styles.card_container]}>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<View style={{ width: '50%' }}>
<TextDefault
numberOfLines={2}
bold
textColor={colors.placeHolderColor}>
Your {i18n.t('orderId')}
</TextDefault>
<TextDefault H4 bolder>
{props.orderId}
</TextDefault>
</View>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View
style={[
styles.cardStatusContainer,
{ borderColor: statusColor, borderWidth: 1 }
]}>
<TextDefault
textColor={statusColor}
bold
uppercase
style={{ ...alignment.PLxSmall, ...alignment.PRxSmall }}>
{props.orderStatus}
</TextDefault>
</View>
<View style={{ paddingLeft: '5%' }}>
<AntDesign
name="arrowright"
size={scale(20)}
color={colors.fontMainColor}
/>
</View>
</View>
</View>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
width: '90%'
}}>
<View>
<TextDefault
style={alignment.MTmedium}
textColor={colors.placeHolderColor}
bold>
{i18n.t('totalOrderAmount')}
</TextDefault>
<TextDefault
textColor={colors.placeHolderColor}
bold
style={{ ...alignment.MTxSmall }}>
Payment method
</TextDefault>
<TextDefault
textColor={colors.placeHolderColor}
bold
style={{ ...alignment.MTxSmall }}>
Delivery Time
</TextDefault>
</View>
<View>
<TextDefault
style={alignment.MTmedium}
textColor={colors.fontMainColor}
bolder>
{props.orderAmount}
</TextDefault>
<TextDefault
textColor={colors.fontMainColor}
bolder
style={{ ...alignment.MTxSmall }}>
{props.paymentMethod}
</TextDefault>
<TextDefault
textColor={colors.fontMainColor}
bolder
style={{ ...alignment.MTxSmall }}>
{new Date(props.orderDatetime).toLocaleDateString()}{' '}
{new Date(props.orderDatetime).toLocaleTimeString()}{' '}
</TextDefault>
</View>
</View>
{/* <View style={[styles.card_container__left]}>
<View style={[styles.left_toptextLine]}>
<TextDefault center H5 bold textColor={colors.placeHolderColor}>
Your {i18n.t('orderId')}
</TextDefault>
<TextDefault H3 bolder>
{props.orderId}
</TextDefault>
</View> */}
{/* <TextDefault style={alignment.MTmedium} textColor={colors.placeHolderColor} bold>
{i18n.t('totalOrderAmount')} - {props.orderAmount}
</TextDefault>
<TextDefault
textColor={colors.placeHolderColor}
bold
style={{ ...alignment.MTxSmall }}>
{new Date(props.orderDatetime).toDateString()}{' '}
{new Date(props.orderDatetime).toTimeString()}
</TextDefault>
<TextDefault
textColor={colors.placeHolderColor}
bold
style={{ ...alignment.MTxSmall }}>
Payment method: {props.paymentMethod}
</TextDefault> */}
{/* </View> */}
{/* <View style={styles.card_container__right}> */}
{/* <View style={styles.cardSubContainerRight}>
<View
style={[
styles.cardStatusContainer,
{ borderColor: statusColor, borderWidth:1 }
]}>
<TextDefault
textColor={statusColor}
bold
uppercase
style={{ ...alignment.PLxSmall, ...alignment.PRxSmall }}>
{props.orderStatus}
</TextDefault>
</View>
<View style={styles.cardRightArrow}>
<AntDesign
name="arrowright"
size={scale(25)}
color={colors.fontMainColor}
/>
</View>
</View> */}
{/* </View> */}
</View>
</TouchableOpacity>
)
}
Order.propTypes = {
height: PropTypes.oneOf([PropTypes.string, PropTypes.number]),
orderStatus: PropTypes.string.isRequired,
onPress: PropTypes.func,
orderId: PropTypes.string.isRequired,
orderAmount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
orderDatetime: PropTypes.oneOfType([
PropTypes.string,
PropTypes.instanceOf(Date)
]),
paymentMethod: PropTypes.string
}
export default Order

View File

@ -0,0 +1,59 @@
import { scale, verticalScale } from '../../utilities/scaling'
const Styles = {
card_container: {
elevation: 10,
width: '90%',
padding: 20,
justifyContent: 'center',
height: verticalScale(170),
alignSelf: 'center',
borderRadius: scale(20),
backgroundColor: '#fff',
marginBottom: scale(20),
shadowOffset: { width: 0, height: scale(2) },
shadowColor: 'black',
shadowOpacity: 0.3,
shadowRadius: scale(2),
borderWidth: 0.4,
borderColor: '#e1e1e1'
},
card_container__left: {
flexDirection: 'column',
justifyContent: 'center',
height: '70%',
width: '58%',
paddingLeft: '5%'
},
card_container__right: {
flexDirection: 'column',
justifyContent: 'space-around',
alignItems: 'flex-end',
height: '80%',
width: '35%'
},
left_toptextLine: {
alignSelf: 'flex-start',
justifyContent: 'space-between'
},
cardSubContainerRight: {
width: '100%',
height: '30%',
alignItems: 'center',
justifyContent: 'space-around',
flexDirection: 'row'
},
cardStatusContainer: {
padding: scale(8),
paddingHorizontal: scale(12),
justifyContent: 'center',
alignItems: 'center',
borderRadius: verticalScale(5)
},
cardRightArrow: {
justifyContent: 'center',
alignItems: 'center'
}
}
export default Styles

View File

@ -0,0 +1,44 @@
import React from 'react'
import { TouchableOpacity, View } from 'react-native'
import styles from './styles.js'
import TextDefault from '../../Text/TextDefault/TextDefault.js'
import { Entypo, FontAwesome } from '@expo/vector-icons'
import { scale } from '../../../utilities/scaling.js'
import colors from '../../../utilities/colors.js'
import PropTypes from 'prop-types'
const NavItem = props => (
<View style={styles.Flex}>
<TouchableOpacity
activeOpacity={0.8}
style={styles.container}
onPress={props.onPress}>
<View style={styles.leftContainer}>
{props.icon === 'shopping-bag' ? (
<Entypo
name={props.icon}
size={scale(20)}
color={colors.fontSecondColor}
/>
) : (
<FontAwesome
name={props.icon}
size={scale(20)}
color={colors.fontSecondColor}
/>
)}
</View>
<View style={styles.rightContainer}>
<TextDefault bolder H5 textColor={colors.fontSecondColor}>
{props.title}
</TextDefault>
</View>
</TouchableOpacity>
</View>
)
NavItem.propTypes = {
onPress: PropTypes.func,
icon: PropTypes.string,
title: PropTypes.string.isRequired
}
export default NavItem

View File

@ -0,0 +1,22 @@
export default {
Flex: {
flex: 1
},
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-evenly',
alignItems: 'center'
},
leftContainer: {
height: '100%',
width: '15%',
justifyContent: 'center',
alignItems: 'center'
},
rightContainer: {
height: '80%',
width: '75%',
justifyContent: 'center'
}
}

View File

@ -0,0 +1,44 @@
import React from 'react'
import { View } from 'react-native'
import gql from 'graphql-tag'
import Spinner from '../../Spinner/Spinner'
import styles from './styles'
import { profile } from '../../../apollo/queries'
import { useQuery } from '@apollo/react-hooks'
import TextDefault from '../../Text/TextDefault/TextDefault'
import colors from '../../../utilities/colors'
import TextError from '../../Text/TextError/TextError'
import i18n from '../../../../i18n'
const PROFILE = gql`
${profile}
`
function Profile() {
const { data, loading, error } = useQuery(PROFILE)
if (loading) return <Spinner />
if (error) return <TextError text="Something is worng" />
return (
<View style={styles.container}>
<View style={styles.leftContainer}>
{data.rider.name && (
<View style={styles.imgContainer}>
<TextDefault textColor={colors.themeBackground} bold H1>
{data.rider.name.substr(0, 1).toUpperCase()}
</TextDefault>
</View>
)}
</View>
<View style={styles.rightContainer}>
<TextDefault H5 bold textColor={colors.fontSecondColor}>
{i18n.t('welcome')}
</TextDefault>
<TextDefault H3 textColor={colors.fontSecondColor} bolder>
{data.rider.username}
</TextDefault>
</View>
</View>
)
}
export default Profile

View File

@ -0,0 +1,28 @@
import colors from '../../../utilities/colors'
import { scale } from '../../../utilities/scaling'
import { alignment } from '../../../utilities/alignment'
export default {
container: {
flex: 1,
justifyContent: 'center',
paddingLeft: '10%',
borderColor: colors.horizontalLine
},
leftContainer: {
justifyContent: 'center',
...alignment.MRsmall
},
imgContainer: {
width: scale(70),
height: scale(70),
borderRadius: scale(20),
justifyContent: 'center',
alignItems: 'center',
backgroundColor: colors.tagColor
},
rightContainer: {
justifyContent: 'center',
...alignment.MTmedium
}
}

View File

@ -0,0 +1,92 @@
import PropTypes from 'prop-types'
import React, { useContext } from 'react'
import { Platform, View } from 'react-native'
import i18n from '../../../i18n'
import { AuthContext } from '../../context/auth'
import { NAVIGATION_SCREEN } from '../../utilities/constant'
import NavItem from './NavItem/NavItem'
import Profile from './Profile/Profile'
import styles from './styles'
// constants
const datas = [
{
title: 'titleOrders',
// icon:'linode',
icon: 'shopping-bag',
navigateTo: NAVIGATION_SCREEN.Orders
},
{
title: 'titleLanguage',
icon: 'language',
navigateTo: NAVIGATION_SCREEN.Language
},
{
title: 'titleHelp',
icon: 'exclamation-circle',
navigateTo: NAVIGATION_SCREEN.Help
}
// {
// title: 'titleChat',
// icon: 'comments-o',
// navigateTo: NAVIGATION_SCREEN.Chat
// }
// {
// title: 'titleLogout',
// icon: 'sign-out',
// navigateTo: 'Login'
// }
]
function SidebBar({ navigation }) {
const { logout } = useContext(AuthContext)
// if (loading) return <Spinner />
return (
<>
<View style={styles.flex}>
<View style={styles.topContainer}>
<Profile />
</View>
<View style={styles.botContainer}>
{datas.map((data, ind) =>
(Platform.OS === 'ios' && ind === 0) ||
(Platform.OS === 'ios' && ind === 1) ? null : (
<View key={ind} style={styles.item}>
<NavItem
onPress={async () => {
if (data.navigateTo === 'Login') {
await logout()
navigation.closeDrawer()
} else {
navigation.navigate(data.navigateTo)
}
}}
icon={data.icon}
title={i18n.t(data.title)}
/>
</View>
)
)}
</View>
<View style={styles.item}>
<NavItem
onPress={async () => {
await logout()
navigation.closeDrawer()
}}
icon={'sign-out'}
title={i18n.t('titleLogout')}
/>
</View>
</View>
</>
)
}
SidebBar.propTypes = {
navigation: PropTypes.object
}
export default SidebBar

View File

@ -0,0 +1,27 @@
const styles = {
flex: {
flex: 1
},
topContainer: {
height: '40%'
},
botContainer: {
flex: 1,
marginTop: '4%',
marginBottom: 70,
height: '40%'
},
item: {
height: '20%',
marginBottom: '2%'
},
spinnerBackgorund: {
position: 'absolute',
width: '100%',
height: '100%',
backgroundColor: 'rgba(166,158,149,0.5)',
opacity: 1
}
}
export default styles

View File

@ -0,0 +1,18 @@
import React from 'react'
import { ActivityIndicator } from 'react-native'
import colors from '../../utilities/colors'
import PropTypes from 'prop-types'
function Spinner(props) {
return (
<ActivityIndicator
size="large"
color={props.spinnerColor ?? colors.spinnerColor}
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
/>
)
}
Spinner.propTypes = {
spinnerColor: PropTypes.string
}
export default Spinner

View File

@ -0,0 +1,61 @@
import React from 'react'
import { Text, StyleSheet } from 'react-native'
import color from './styles'
import { textStyles } from '../../../utilities/textStyles'
import colors from '../../../utilities/colors'
import PropTypes from 'prop-types'
function TextDefault(props) {
const textColor = props.textColor ? props.textColor : colors.fontMainColor
const defaultStyle = StyleSheet.flatten([
color(textColor).color,
textStyles.Regular,
textStyles.Normal
])
var customStyles = [defaultStyle]
if (props.bold) customStyles.push(textStyles.Bold)
if (props.bolder) customStyles.push(textStyles.Bolder)
if (props.center) customStyles.push(textStyles.Center)
if (props.right) customStyles.push(textStyles.Right)
if (props.small) customStyles.push(textStyles.Small)
if (props.xSmall) customStyles.push(textStyles.xSmall)
if (props.H5) customStyles.push(textStyles.H5)
if (props.H4) customStyles.push(textStyles.H4)
if (props.H3) customStyles.push(textStyles.H3)
if (props.H2) customStyles.push(textStyles.H2)
if (props.H1) customStyles.push(textStyles.H1)
if (props.uppercase) customStyles.push(textStyles.UpperCase)
if (props.lineOver) customStyles.push(textStyles.LineOver)
customStyles = StyleSheet.flatten([customStyles, props.style])
return (
<Text
numberOfLines={props.numberOfLines ? props.numberOfLines : 0}
style={customStyles}>
{props.children}
</Text>
)
}
TextDefault.propTypes = {
textColor: PropTypes.string,
bold: PropTypes.bool,
bolder: PropTypes.bool,
center: PropTypes.bool,
right: PropTypes.bool,
small: PropTypes.bool,
xSmall: PropTypes.bool,
H5: PropTypes.bool,
H4: PropTypes.bool,
H3: PropTypes.bool,
H2: PropTypes.bool,
H1: PropTypes.bool,
uppercase: PropTypes.bool,
lineOver: PropTypes.bool,
numberOfLines: PropTypes.number,
style: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
children: PropTypes.node.isRequired
}
export default TextDefault

View File

@ -0,0 +1,10 @@
import { StyleSheet } from 'react-native'
const color = textColor =>
StyleSheet.create({
color: {
color: textColor
}
})
export default color

View File

@ -0,0 +1,31 @@
import React from 'react'
import { View } from 'react-native'
import TextDefault from '../TextDefault/TextDefault'
import colors from '../../../utilities/colors'
import PropTypes from 'prop-types'
function TextError(props) {
return (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: props.backColor ? props.backColor : 'transparent'
}}>
<TextDefault
textColor={props.textColor ? props.textColor : colors.lightBackground}
bold
H4
center>
{props.text}{' '}
</TextDefault>
</View>
)
}
TextError.propTypes = {
text: PropTypes.string,
backColor: PropTypes.string,
textColor: PropTypes.string
}
export default TextError

View File

@ -0,0 +1,4 @@
import TextDefault from './TextDefault/TextDefault'
import TextError from './TextError/TextError'
export { TextDefault, TextError }

View File

@ -0,0 +1,24 @@
import PropTypes from 'prop-types'
import React from 'react'
import { View, Image } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import useStyle from './styles'
const BACKGROUND_IMAGE = require('../../../assets/images/ui/BG.png')
function MainWrapper({ children }) {
const styles = useStyle()
const inset = useSafeAreaInsets()
return (
<View style={[styles.flex, { paddingBottom: inset.bottom }]}>
<Image style={styles.imageContainer} source={BACKGROUND_IMAGE} />
{children}
</View>
)
}
MainWrapper.propTypes = {
children: PropTypes.node
}
export default React.memo(MainWrapper)

View File

@ -0,0 +1,16 @@
import PropTypes from 'prop-types'
import React from 'react'
import { View } from 'react-native'
import useStyle from './styles'
function WrapperView({ children }) {
const styles = useStyle()
return <View style={[styles.flex, styles.wrapperView]}>{children}</View>
}
WrapperView.propTypes = {
children: PropTypes.node
}
export default React.memo(WrapperView)

View File

@ -0,0 +1,4 @@
import MainWrapper from './MainWrapper'
import WrapperView from './WrapperView'
export { MainWrapper, WrapperView }

View File

@ -0,0 +1,30 @@
import { useHeaderHeight } from '@react-navigation/stack'
import { StyleSheet } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import colors from '../../utilities/colors'
import { verticalScale } from '../../utilities/scaling'
const useStyle = () => {
const inset = useSafeAreaInsets()
const headerHeight = useHeaderHeight()
return StyleSheet.create({
flex: {
flex: 1,
backgroundColor: colors.cartContainer
},
wrapperView: {
backgroundColor: colors.themeBackground,
paddingTop: headerHeight,
paddingBottom: inset.bottom
},
imageContainer: {
backgroundColor: colors.cartContainer,
width: '100%',
height: verticalScale(130),
alignSelf: 'center'
}
})
}
export default useStyle

View File

@ -0,0 +1,18 @@
import AssignedOrders from './AssignedOrders/AssignedOrders'
import { HeaderIcon, LeftButton } from './Header'
import NewOrders from './NewOrders/NewOrders'
import { MainWrapper, WrapperView } from './WrapperView'
import { TextDefault, TextError } from './Text'
import Spinner from './Spinner/Spinner'
export {
MainWrapper,
WrapperView,
HeaderIcon,
NewOrders,
LeftButton,
AssignedOrders,
TextDefault,
TextError,
Spinner
}

View File

@ -0,0 +1,3 @@
import React from 'react'
export const AuthContext = React.createContext()

View File

@ -0,0 +1,25 @@
/* eslint-disable react/prop-types */
import React from 'react'
import { useQuery } from '@apollo/react-hooks'
import gql from 'graphql-tag'
import { configuration } from '../apollo/queries'
const GETCONFIGURATION = gql`
${configuration}
`
const ConfigurationContext = React.createContext({})
export const ConfigurationProvider = props => {
const { loading, data, error } = useQuery(GETCONFIGURATION)
const configuration =
loading || error || !data.configuration ? {} : data.configuration
return (
<ConfigurationContext.Provider value={configuration}>
{props.children}
</ConfigurationContext.Provider>
)
}
export const ConfigurationConsumer = ConfigurationContext.Consumer
export default ConfigurationContext

View File

@ -0,0 +1,298 @@
/* eslint-disable no-unused-vars */
/* eslint-disable react/prop-types */
import { useQuery } from '@apollo/react-hooks'
import AsyncStorage from '@react-native-async-storage/async-storage'
import * as Location from 'expo-location'
import gql from 'graphql-tag'
import React, { useEffect, useRef, useState } from 'react'
import { updateLocation } from '../apollo/mutations'
import {
assignedOrders,
profile,
subscriptionAssignRider,
subscriptionUnAssignedOrder,
unassignedOrders
} from '../apollo/queries'
const PROFILE = gql`
${profile}
`
const ASSIGNED_ORDERS = gql`
${assignedOrders}
`
const SUBSCRIPTION_RIDER_ORDER = gql`
${subscriptionAssignRider}
`
const UNASSIGNED_ORDERS = gql`
${unassignedOrders}
`
const SUBSCRIPTION_UNASSIGNED_ORDER = gql`
${subscriptionUnAssignedOrder}
`
const UPDATE_LOCATION = gql`
${updateLocation}
`
const UserContext = React.createContext({})
export const UserProvider = props => {
const [token, setToken] = useState()
const [profile, setProfile] = useState({ isLoggedIn: false })
const [assignedOrders, setAssignedOrders] = useState([])
const [unAssignedOrders, setUnAssignedOrders] = useState([])
const locationListener = useRef()
const {
client,
loading: loadingProfile,
error: errorProfile,
data: dataProfile
} = useQuery(PROFILE, {
fetchPolicy: 'network-only',
onCompleted,
onError
})
const {
loading: loadingAssigned,
error: errorAssigned,
networkStatus: networkStatusAssigned,
subscribeToMore: subscribeToMoreAssigned,
refetch: refetchAssigned,
data: dataAssigned
} = useQuery(ASSIGNED_ORDERS, {
onCompleted,
onError,
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true
})
const {
loading: loadingUnAssigned,
error: errorUnAssigned,
networkStatus: networkStatusUnAssigned,
subscribeToMore: subscribeToMoreUnAssigned,
refetch: refetchUnAssigned,
data: dataUnassigned
} = useQuery(UNASSIGNED_ORDERS, {
onCompleted,
onError,
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true
})
useEffect(() => {
return () => {
locationListener.current && locationListener.current.remove()
}
}, [])
// useEffect(() => {
// let isSubscribed = true;
// (async () => {
// const token = await AsyncStorage.getItem('rider-token')
// isSubscribed && setToken(token)
// })()
// return () => {
// isSubscribed = false
// }
// }, [])
// useEffect(() => {
// if (!token) return
// let isSubscribed = true
// ; (async () => {
// isSubscribed && fetchProfile()
// isSubscribed && fetchAssigned()
// isSubscribed && fetchUnAssigned()
// isSubscribed && trackRiderLocation()
// })()
// return () => {
// isSubscribed = false
// }
// }, [token])
let unsubscribeAssignedOrders = null
let unsubscribeUnAssignedOrders = null
useEffect(() => {
if (!dataProfile) return
unsubscribeAssignedOrders = subscribeAssignedOrders()
unsubscribeUnAssignedOrders = subscribeUnAssignedOrders()
trackRiderLocation()
return () => {
unsubscribeAssignedOrders && unsubscribeAssignedOrders()
unsubscribeUnAssignedOrders && unsubscribeUnAssignedOrders()
}
}, [dataProfile])
function onCompleted({ rider, assignedOrders, unassignedOrders }) {
// if (rider) {
// setProfile({ ...rider, isLoggedIn: true })
// } else if (assignedOrders) {
// setAssignedOrders(assignedOrders)
// } else if (unassignedOrders) {
// setUnAssignedOrders(unassignedOrders)
// }
// if (dataProfile && dataUnassigned && dataAssigned) {
// getNotificationData()
// }
}
function onError(error) {
console.log('error on fetching context', error)
}
const trackRiderLocation = async () => {
const { status } = await Location.requestForegroundPermissionsAsync()
if (status === 'granted') {
if (locationListener.current) locationListener.current.remove()
locationListener.current = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.BestForNavigation,
timeInterval: 60000,
distanceInterval: 20
},
async ({ coords: { latitude, longitude }, error }) => {
try {
await client.mutate({
mutation: UPDATE_LOCATION,
variables: {
latitude: latitude.toString(),
longitude: longitude.toString()
}
})
} catch (error) {
console.log('error', JSON.stringify(error))
}
}
)
}
}
const subscribeAssignedOrders = () => {
try {
return subscribeToMoreAssigned({
document: SUBSCRIPTION_RIDER_ORDER,
variables: { riderId: dataProfile.rider._id },
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev
if (subscriptionData.data.subscriptionAssignRider.origin === 'new') {
setAssignedOrders([
subscriptionData.data.subscriptionAssignRider.order,
...prev.assignedOrders
])
return {
assignedOrders: [
subscriptionData.data.subscriptionAssignRider.order,
...prev.assignedOrders
]
}
} else {
const { assignedOrders } = prev
const {
orderStatus,
_id
} = subscriptionData.data.subscriptionAssignRider.order
const orderIndex = assignedOrders.findIndex(o => o._id === _id)
if (orderIndex > -1) {
if (orderStatus === 'DELIVERED' || orderStatus === 'CANCELLED') {
assignedOrders.splice(orderIndex, 1)
} else {
assignedOrders[orderIndex].orderStatus = orderStatus
}
}
setAssignedOrders([...assignedOrders])
return {
assignedOrders: [...assignedOrders]
}
}
}
})
} catch (error) {
console.log('error subscribe assigned orders', error)
}
}
const subscribeUnAssignedOrders = () => {
try {
return subscribeToMoreUnAssigned({
document: SUBSCRIPTION_UNASSIGNED_ORDER,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev
if (subscriptionData.data.unassignedOrder.origin === 'new') {
setUnAssignedOrders([
subscriptionData.data.unassignedOrder.order,
...prev.unassignedOrders
])
return {
unassignedOrders: [
subscriptionData.data.unassignedOrder.order,
...prev.unassignedOrders
]
}
} else {
const { _id } = subscriptionData.data.unassignedOrder.order
const { unassignedOrders } = prev
const orderIndex = unassignedOrders.findIndex(o => o._id === _id)
if (orderIndex > -1) {
unassignedOrders.splice(orderIndex, 1)
}
setUnAssignedOrders([...unassignedOrders])
return {
unassignedOrders: [...unassignedOrders]
}
}
}
})
} catch (error) {
console.log(error)
}
}
const setTokenAsync = async token => {
await AsyncStorage.setItem('rider-token', token)
setToken(token)
}
const logout = async () => {
try {
// await Location.stopLocationUpdatesAsync('RIDER_LOCATION')
await AsyncStorage.removeItem('rider-token')
setToken(null)
setProfile({ isLoggedIn: false })
setAssignedOrders([])
setUnAssignedOrders([])
} catch (error) {
console.log('error on logout', error)
}
}
return (
<UserContext.Provider
value={{
loadingProfile: loadingProfile,
errorProfile,
profile: loadingProfile || errorProfile ? null : dataProfile.rider,
logout,
setTokenAsync,
loadingAssigned,
errorAssigned,
assignedOrders:
loadingAssigned || errorAssigned ? [] : dataAssigned.assignedOrders,
refetchAssigned,
networkStatusAssigned,
loadingUnAssigned,
errorUnAssigned,
unAssignedOrders:
loadingUnAssigned || errorUnAssigned
? []
: dataUnassigned.unassignedOrders,
refetchUnAssigned,
networkStatusUnAssigned
}}>
{props.children}
</UserContext.Provider>
)
}
export const UserConsumer = UserContext.Consumer
export default UserContext

View File

@ -0,0 +1,196 @@
import { createDrawerNavigator } from '@react-navigation/drawer'
import { NavigationContainer } from '@react-navigation/native'
import { createStackNavigator } from '@react-navigation/stack'
import * as Notifications from 'expo-notifications'
import React, { useContext, useEffect } from 'react'
import Animated from 'react-native-reanimated'
import { LeftButton } from '../components/Header/HeaderIcons/HeaderIcons'
import Sidebar from '../components/Sidebar/Sidebar'
import { AuthContext } from '../context/auth'
import { UserProvider } from '../context/user'
import {
Chat,
Help,
HelpBrowser,
Language,
Login,
OrderDetail,
Orders
} from '../screens'
import colors from '../utilities/colors'
import { ICONS_NAME, NAVIGATION_SCREEN } from '../utilities/constant'
import navigationService from './navigationService'
import { screenOptions } from './screenOptions'
import styles from './styles'
const Stack = createStackNavigator()
const Drawer = createDrawerNavigator()
function Auth() {
return (
<Stack.Navigator
screenOptions={screenOptions({ textColor: colors.fontMainColor })}>
<Stack.Screen name={NAVIGATION_SCREEN.Login} component={Login} />
</Stack.Navigator>
)
}
function Main() {
let listener = null
let animatedStyle = {}
let opacity
let OuterWindowSlide, InnerWindowSlide
useEffect(() => {
// if (!loadingProfile && !loadingAssigned && !loadingUnAssigned)
listener = Notifications.addNotificationReceivedListener(
_handleNotification
)
return () => {
listener && listener.remove()
}
}, [])
function _handleNotification(notification) {
try {
if (notification.origin === 'selected') {
if (notification.data.order) {
navigationService.navigate('OrderDetail', {
id: notification.data._id
})
}
}
} catch (e) {
console.log(e)
}
}
useEffect(() => {
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false
})
})
const subscription = Notifications.addNotificationResponseReceivedListener(
_handleNotification
)
return () => subscription.remove()
}, [])
return (
<UserProvider>
<Drawer.Navigator
drawerStyle={{
flex: 1,
backgroundColor: colors.fontMainColor,
width: '60%',
justifyContent: 'space-between',
borderRightWidth: 0,
shadowOpacity: 0,
elevation: 0
}}
sceneContainerStyle={{ backgroundColor: colors.fontMainColor }}
overlayColor="transparent"
drawerType="slide"
drawerContent={props => {
const scale = Animated.interpolateNode(props.progress, {
inputRange: [0, 1],
outputRange: [1, 0.7]
})
const Animatedopacity = Animated.interpolateNode(props.progress, {
inputRange: [0, 0.6, 1],
outputRange: [0, 0, 1]
})
const AnimatedOuterSlide = Animated.interpolateNode(props.progress, {
inputRange: [0, 1],
outputRange: [0, -35]
})
const AnimatedInnerSlide = Animated.interpolateNode(props.progress, {
inputRange: [0, 1],
outputRange: [0, -15]
})
const borderRadius = Animated.interpolateNode(props.progress, {
inputRange: [0, 1],
outputRange: [0, 20]
})
animatedStyle = { borderRadius, transform: [{ scale }] }
opacity = Animatedopacity
OuterWindowSlide = AnimatedOuterSlide
InnerWindowSlide = AnimatedInnerSlide
return <Sidebar {...props} />
}}>
<Drawer.Screen name="noDrawer">
{props => (
<NoDrawer
{...props}
style={animatedStyle}
opacity={opacity}
OuterWindowSlide={OuterWindowSlide}
InnerWindowSlide={InnerWindowSlide}
/>
)}
</Drawer.Screen>
</Drawer.Navigator>
</UserProvider>
)
}
function NoDrawer({ style, opacity = 1, OuterWindowSlide, InnerWindowSlide }) {
return (
<React.Fragment>
<Animated.View
style={[styles.outerView, style, { marginLeft: OuterWindowSlide }]}
/>
<Animated.View
style={[styles.innerView, style, { marginLeft: InnerWindowSlide }]}
/>
<Animated.View style={[styles.animatedView, style]}>
<Stack.Navigator
headerMode="screen"
initialRouteName={NAVIGATION_SCREEN.Orders}
screenOptions={screenOptions({
textColor: colors.fontSecondColor
})}>
<Stack.Screen
name={NAVIGATION_SCREEN.Orders}
component={Orders}
options={{
headerLeft: () => (
<LeftButton
icon={ICONS_NAME.Menu}
iconColor={colors.fontSecondColor}
/>
)
}}
/>
<Stack.Screen
name={NAVIGATION_SCREEN.OrderDetail}
component={OrderDetail}
/>
<Stack.Screen name={NAVIGATION_SCREEN.Help} component={Help} />
<Stack.Screen
name={NAVIGATION_SCREEN.Language}
component={Language}
/>
<Stack.Screen name={NAVIGATION_SCREEN.Chat} component={Chat} />
<Stack.Screen
name={NAVIGATION_SCREEN.HelpBrowser}
component={HelpBrowser}
/>
</Stack.Navigator>
</Animated.View>
</React.Fragment>
)
}
function AppContainer() {
const { token } = useContext(AuthContext)
return (
// <SafeAreaProvider>
<NavigationContainer
ref={ref => {
navigationService.setGlobalRef(ref)
}}>
{token ? <Main /> : <Auth />}
</NavigationContainer>
// </SafeAreaProvider>
)
}
export default AppContainer

View File

@ -0,0 +1,19 @@
let navObj = null
function setGlobalRef(ref) {
navObj = ref
}
function navigate(path, props = {}) {
navObj.navigate(path, props)
}
function goBack() {
navObj.goBack()
}
export default {
setGlobalRef,
navigate,
goBack
}

View File

@ -0,0 +1,33 @@
/* eslint-disable react/prop-types */
import { TransitionPresets } from '@react-navigation/stack'
import React from 'react'
import { LeftButton } from '../components'
import { ICONS_NAME } from '../utilities/constant'
import { scale } from '../utilities/scaling'
import { textStyles } from '../utilities/textStyles'
const screenOptions = props => ({
headerTitleAlign: 'center',
headerBackTitleVisible: false,
headerTransparent: true,
headerStyle: {
elevation: 0,
shadowOpacity: 0,
borderBottomWidth: 0
},
headerTitleStyle: {
color: props && props.textColor,
...textStyles.H3,
...textStyles.Bold,
backgroundColor: 'transparent'
},
headerTitleContainerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginHorizontal: scale(80)
},
headerLeft: () => <LeftButton icon={ICONS_NAME.Back} />,
...TransitionPresets.SlideFromRightIOS
})
export { screenOptions }

View File

@ -0,0 +1,68 @@
import { StyleSheet } from 'react-native'
import colors from '../utilities/colors'
import { scale } from '../utilities/scaling'
const styles = StyleSheet.create({
badge: {
position: 'absolute',
right: -scale(6),
top: 0,
borderRadius: scale(9),
width: scale(18),
height: scale(18),
justifyContent: 'center',
alignItems: 'center'
},
outerView: {
position: 'absolute',
width: '100%',
height: '80%',
top: '10%',
left: 10,
bottom: 0,
backgroundColor: colors.fontMainColor,
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 5
},
shadowOpacity: 0.3,
shadowRadius: 3,
elevation: 10
},
innerView: {
position: 'absolute',
width: '100%',
height: '90%',
top: '5%',
bottom: 0,
backgroundColor: colors.fontMainColor,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 5
},
shadowOpacity: 0.3,
shadowRadius: 3,
elevation: 10
},
animatedView: {
flex: 1,
overflow: 'hidden',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 5
},
shadowOpacity: 1,
shadowRadius: 10,
elevation: 15
},
closeView: {
position: 'absolute',
top: 70,
left: 50
}
})
export default styles

View File

@ -0,0 +1,211 @@
import { Feather, FontAwesome } from '@expo/vector-icons'
import { useNavigation } from '@react-navigation/native'
import React, { useCallback, useEffect, useState } from 'react'
import { Keyboard, View } from 'react-native'
import {
Bubble,
GiftedChat,
InputToolbar,
Send
} from 'react-native-gifted-chat'
import { MainWrapper, TextDefault } from '../../components'
import { alignment } from '../../utilities/alignment'
import colors from '../../utilities/colors'
import { scale } from '../../utilities/scaling'
import useStyle from './styles'
const UserInfo = {
_id: 1,
name: 'Jason',
active: true
}
function Chat() {
const styles = useStyle()
const navigation = useNavigation()
const [messages, setMessages] = useState([])
const [isTyping, setIsTyping] = useState(false)
useEffect(() => {
Keyboard.addListener('keyboardDidShow', _keyboardDidShow)
Keyboard.addListener('keyboardDidHide', _keyboardDidHide)
// cleanup function
return () => {
Keyboard.removeListener('keyboardDidShow', _keyboardDidShow)
Keyboard.removeListener('keyboardDidHide', _keyboardDidHide)
}
}, [])
const _keyboardDidShow = () => setIsTyping(true)
const _keyboardDidHide = () => setIsTyping(false)
useEffect(() => {
navigation.setOptions({
title: 'Chat'
})
setMessages([
{
_id: 1,
text: 'How can I help you?',
sent: true,
received: true,
createdAt: new Date(),
user: {
_id: 2,
name: 'React Native',
avatar: 'https://placeimg.com/140/140/any'
}
}
])
}, [navigation])
const onSend = useCallback((messages = []) => {
setMessages(previousMessages =>
GiftedChat.append(previousMessages, messages)
)
}, [])
const renderBubble = props => {
return (
<Bubble
{...props}
textStyle={{
right: {
color: colors.fontMainColor
},
left: {
color: colors.fontMainColor
}
}}
bottomContainerStyle={{
right: {
...alignment.PTxSmall
},
left: {
...alignment.PTxSmall
}
}}
wrapperStyle={{
right: {
minWidth: 150,
backgroundColor: colors.yellowishOrange,
borderTopRightRadius: scale(15),
borderTopLeftRadius: scale(15),
borderBottomLeftRadius: scale(15),
borderBottomRightRadius: 0,
...alignment.MVxSmall,
...alignment.PVxSmall,
shadowColor: colors.black,
shadowOffset: {
width: 0,
height: 1
},
shadowOpacity: 0.2,
shadowRadius: 2,
elevation: 3
},
left: {
minWidth: 150,
backgroundColor: colors.lightBackground,
borderTopRightRadius: scale(15),
borderTopLeftRadius: scale(15),
borderBottomRightRadius: scale(15),
borderBottomLeftRadius: 0,
...alignment.MVxSmall,
...alignment.PVxSmall,
shadowColor: colors.black,
shadowOffset: {
width: 0,
height: 1
},
shadowOpacity: 0.2,
shadowRadius: 2,
elevation: 3
}
}}
/>
)
}
const renderSend = props => (
<Send {...props} containerStyle={styles.sendBtn}>
<View style={styles.rightBtn}>
<Feather
name={'send'}
color={colors.buttonText}
size={scale(17)}
style={{
transform: [
{ rotateZ: '45deg' },
{ translateY: 2 },
{ translateX: -1 }
]
}}
/>
</View>
</Send>
)
const customtInputToolbar = props => {
return (
<InputToolbar
{...props}
containerStyle={styles.inputContainer}
renderSend={renderSend}
render
/>
)
}
return (
<MainWrapper>
<View style={[styles.flex, styles.mainContainer]}>
<View style={styles.header}>
<FontAwesome
name="circle"
color={UserInfo.active ? colors.iconPink : colors.fontSecondColor}
/>
<TextDefault medium H5 style={alignment.PLsmall}>
{UserInfo.active ? UserInfo.name : 'Offline'}
</TextDefault>
</View>
<GiftedChat
inverted
alwaysShowSend
user={UserInfo}
isTyping={isTyping}
messages={messages}
onSend={messages => onSend(messages)}
renderAvatar={() => null}
renderBubble={renderBubble}
renderInputToolbar={customtInputToolbar}
textInputStyle={styles.inputStyle}
minInputToolbarHeight={60}
// renderFooter={() =>
// isTyping ? (
// <TextDefault
// textColor={colors.selected}
// style={[alignment.PLlarge, alignment.MBsmall]}>
// User is typing...
// </TextDefault>
// ) : null
// }
timeTextStyle={{
left: {
width: '100%',
color: colors.fontMainColor,
fontSize: 11,
textAlign: 'right'
},
right: {
color: colors.fontMainColor,
fontSize: 11
}
}}
/>
</View>
</MainWrapper>
)
}
export default Chat

View File

@ -0,0 +1,63 @@
import { Platform, StyleSheet } from 'react-native'
import { alignment } from '../../utilities/alignment'
import colors from '../../utilities/colors'
import { scale } from '../../utilities/scaling'
const useStyle = () => {
return StyleSheet.create({
flex: {
flex: 1
},
mainContainer: {
marginTop: 30,
backgroundColor: colors.cartContainer,
borderTopEndRadius: scale(20),
borderTopStartRadius: scale(20),
shadowColor: colors.shadowColor,
shadowOffset: {
width: 0,
height: -5
},
shadowOpacity: 0.4,
shadowRadius: 10,
elevation: 5,
...alignment.Plarge
},
header: {
// backgroundColor: 'blue',
flexDirection: 'row',
alignItems: 'center',
...alignment.PBmedium,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.horizontalLine
},
rightBtn: {
alignItems: 'center',
justifyContent: 'center',
backgroundColor: colors.buttonBackground,
width: scale(30),
aspectRatio: 1,
borderRadius: scale(10)
},
sendBtn: {
justifyContent: 'center'
},
inputContainer: {
backgroundColor: colors.lightBackground,
borderTopWidth: 0,
minHeight: scale(48),
alignItems: 'center',
justifyContent: 'center',
borderRadius: scale(25),
...alignment.PRsmall,
...alignment.PLxSmall
},
inputStyle: {
paddingTop: Platform.OS === 'ios' ? 8 : -8,
paddingBottom: 0,
textAlignVertical: 'center'
}
})
}
export default useStyle

View File

@ -0,0 +1,54 @@
import { AntDesign } from '@expo/vector-icons'
import { useNavigation } from '@react-navigation/native'
import React, { useLayoutEffect } from 'react'
import { TouchableOpacity, View } from 'react-native'
import i18n from '../../../i18n'
import { MainWrapper, TextDefault } from '../../components'
import colors from '../../utilities/colors'
import { scale, verticalScale } from '../../utilities/scaling'
import styles from './styles'
const links = [
{
title: 'Product Page',
url:
'https://market.nativebase.io/view/react-native-food-delivery-backend-app'
},
{ title: 'Docs', url: 'https://enatega.gitbook.io/enatega-full-app/' },
{
title: 'Blog',
url:
'https://blog.nativebase.io/enatega-full-food-delivery-app-is-finally-here-a6039de4a09d'
},
{ title: 'About Us', url: 'https://ninjascode.com/pages/ourteam.html' }
]
function Help() {
const navigation = useNavigation()
useLayoutEffect(() => {
navigation.setOptions({
headerRight: null,
headerTitle: i18n.t('titleHelp')
})
}, [navigation])
return (
<MainWrapper>
<View style={{ marginTop: verticalScale(20) }} />
{links.map(({ title, url }, index) => (
<TouchableOpacity
activeOpacity={0.8}
onPress={() => navigation.navigate('HelpBrowser', { title, url })}
style={styles.itemContainer}
key={index}>
<TextDefault textColor={colors.fontMainColor} H4>
{title}
</TextDefault>
<AntDesign name="arrowright" size={scale(20)} />
</TouchableOpacity>
))}
</MainWrapper>
)
}
export default Help

View File

@ -0,0 +1,33 @@
import { StyleSheet } from 'react-native'
import { alignment } from '../../utilities/alignment'
import colors from '../../utilities/colors'
import { moderateScale, verticalScale } from '../../utilities/scaling'
const styles = StyleSheet.create({
flex: {
flex: 1,
backgroundColor: colors.themeBackground
},
itemContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '90%',
alignSelf: 'center',
backgroundColor: colors.themeBackground,
borderRadius: moderateScale(10),
elevation: 2,
shadowColor: 'blue',
shadowOffset: {
width: 0,
height: verticalScale(2)
},
borderColor: 'black',
shadowOpacity: 0.1,
...alignment.PTlarge,
...alignment.PBlarge,
...alignment.PLlarge,
...alignment.PRlarge,
...alignment.MBlarge
}
})
export default styles

View File

@ -0,0 +1,29 @@
import { useNavigation, useRoute } from '@react-navigation/native'
import React, { useLayoutEffect } from 'react'
import { WebView } from 'react-native-webview'
import { MainWrapper, Spinner } from '../../components'
function HelpBrowser() {
const navigation = useNavigation()
const route = useRoute()
const { title, url } = route.params
useLayoutEffect(() => {
navigation.setOptions({
headerRight: null,
headerTitle: title
})
}, [navigation])
return (
<MainWrapper>
<WebView
source={{ uri: url }}
startInLoadingState={true}
renderLoading={() => <Spinner />}
/>
</MainWrapper>
)
}
export default HelpBrowser

View File

@ -0,0 +1,139 @@
import AsyncStorage from '@react-native-async-storage/async-storage'
import { useNavigation } from '@react-navigation/native'
import * as Localization from 'expo-localization'
import * as Updates from 'expo-updates'
import React, { useEffect, useLayoutEffect, useState } from 'react'
import { Platform, TouchableOpacity, View } from 'react-native'
import i18n from '../../../i18n'
import { MainWrapper } from '../../components'
import RadioButton from '../../components/FdRadioBtn/RadioBtn'
import TextDefault from '../../components/Text/TextDefault/TextDefault'
import { alignment } from '../../utilities/alignment'
import colors from '../../utilities/colors'
import { scale } from '../../utilities/scaling'
import styles from './styles'
const languageTypes = [
{ value: 'English', code: 'en', index: 0 },
{ value: 'français', code: 'fr', index: 1 },
{ value: 'ភាសាខ្មែរ', code: 'km', index: 2 },
{ value: '中文', code: 'zh', index: 3 },
{ value: 'Deutsche', code: 'de', index: 4 }
]
function Language() {
const navigation = useNavigation()
const [activeRadio, setActiveRadio] = useState(languageTypes[0].index)
// eslint-disable-next-line no-unused-vars
const [languageName, languageNameSetter] = useState('English')
useLayoutEffect(() => {
navigation.setOptions({
title: i18n.t('titleLanguage')
})
}, [navigation])
useEffect(() => {
selectedLanguageFunc()
}, [])
async function selectedLanguageFunc() {
const lang = await AsyncStorage.getItem('enatega-language')
if (lang) {
const defLang = languageTypes.findIndex(el => el.code === lang)
const langName = languageTypes[defLang].value
setActiveRadio(defLang)
languageNameSetter(langName)
}
}
async function onSelectedLanguage() {
const languageInd = activeRadio
if (Platform.OS === 'android') {
const localization = await Localization.getLocalizationAsync()
localization.locale = languageTypes[languageInd].code
await AsyncStorage.setItem(
'enatega-language',
languageTypes[languageInd].code
)
try {
Updates.reloadAsync()
} catch (error) {
console.log('err', error)
}
}
}
return (
<MainWrapper>
<View style={[styles.flex, styles.mainView]}>
<View style={[styles.languageContainer]}>
{languageTypes.map((item, index) => (
<TouchableOpacity
activeOpacity={0.7}
key={index}
onPress={() => setActiveRadio(item.index)}
style={[styles.radioContainer]}>
<TextDefault
numberOfLines={1}
textColor={
activeRadio === item.index
? colors.fontMainColor
: colors.placeHolderColor
}
bold
H5
style={alignment.MLsmall}>
{item.value}
</TextDefault>
<RadioButton
animation={'bounceIn'}
size={13}
outerColor={colors.tagColor}
innerColor={colors.radioColor}
isSelected={activeRadio === item.index}
onPress={() => setActiveRadio(item.index)}
/>
</TouchableOpacity>
))}
</View>
<View>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => onSelectedLanguage()}
style={{
backgroundColor: colors.tagColor,
width: '72%',
borderRadius: scale(10),
alignSelf: 'center',
alignItems: 'center',
height: scale(40),
justifyContent: 'center'
}}>
<TextDefault H5 bold textColor={colors.fontSecondColor}>
Done
</TextDefault>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => navigation.goBack()}
style={{
width: '72%',
alignSelf: 'center',
alignItems: 'center',
height: scale(40),
justifyContent: 'center'
}}>
<TextDefault H5 bold textColor={colors.fontMainColor}>
Cancel
</TextDefault>
</TouchableOpacity>
</View>
</View>
</MainWrapper>
)
}
export default Language

View File

@ -0,0 +1,73 @@
import colors from '../../utilities/colors'
import { verticalScale, scale } from '../../utilities/scaling'
import { alignment } from '../../utilities/alignment'
import { StyleSheet } from 'react-native'
export default {
flex: {
flex: 1
},
mainView: {
justifyContent: 'space-between',
backgroundColor: colors.cartContainer
},
headingLanguage: {
width: '85%',
justifyContent: 'center'
},
languageContainer: {
width: '80%',
alignSelf: 'center',
...alignment.PRmedium,
...alignment.PTsmall,
...alignment.PBlarge,
...alignment.PLmedium,
...alignment.MTmedium
},
shadow: {
shadowOffset: { width: 0, height: scale(2) },
shadowColor: 'black',
shadowOpacity: 0.3,
shadowRadius: scale(1),
elevation: 2,
borderWidth: 0.4,
borderColor: '#e1e1e1'
},
changeLanguage: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
height: verticalScale(40)
},
button: {
width: '15%',
height: '100%',
alignItems: 'center',
justifyContent: 'center'
},
modalContainer: {
width: '100%',
backgroundColor: colors.cartContainer,
borderRadius: verticalScale(4),
...alignment.Plarge
},
radioContainer: {
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
borderBottomWidth: StyleSheet.hairlineWidth,
borderColor: colors.horizontalLine,
alignItems: 'center',
...alignment.PTxSmall,
...alignment.PBmedium,
...alignment.MBmedium
},
modalButtonsContainer: {
flexDirection: 'row',
justifyContent: 'flex-end'
},
modalButtons: {
...alignment.Msmall,
marginBottom: 0
}
}

View File

@ -0,0 +1,53 @@
import React, { useEffect } from 'react'
import { View, ActivityIndicator } from 'react-native'
import AsyncStorage from '@react-native-async-storage/async-storage'
import * as Notifications from 'expo-notifications'
import colors from '../../utilities/colors'
import { useNavigation } from '@react-navigation/native'
function Loading() {
const navigation = useNavigation()
useEffect(() => {
loadData()
}, [])
async function loadData() {
// ask permissions for app here
await permissionForPushNotificationsAsync()
checkAuthenticated()
}
async function permissionForPushNotificationsAsync() {
const { status: existingStatus } = await Notifications.getPermissionsAsync()
let finalStatus = existingStatus
// only ask if permissions have not already been determined, because
// iOS won't necessarily prompt the user a second time.
if (existingStatus !== 'granted') {
// Android remote notification permissions are granted during the app
// install, so this will only ask on iOS
const { status } = await Notifications.requestPermissionsAsync()
finalStatus = status
}
// Stop here if the user did not grant permissions
if (finalStatus !== 'granted') {
}
}
async function checkAuthenticated() {
try {
const userToken = await AsyncStorage.getItem('rider-token')
if (userToken) {
navigation.navigate('Drawer')
return
}
} catch (error) {}
await AsyncStorage.removeItem('rider-token')
navigation.navigate('Auth', { screen: 'Login' })
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color={colors.spinnerColor} />
</View>
)
}
export default Loading

View File

@ -0,0 +1,186 @@
import { useMutation } from '@apollo/react-hooks'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { useNavigation } from '@react-navigation/native'
import Constants from 'expo-constants'
import * as Notifications from 'expo-notifications'
import gql from 'graphql-tag'
import React, { useContext, useEffect, useState } from 'react'
import {
KeyboardAvoidingView,
Platform,
ScrollView,
TouchableOpacity,
View
} from 'react-native'
import { FilledTextField } from 'react-native-material-textfield'
import Logo from '../../../assets/images/Svg/Logo'
import i18n from '../../../i18n'
import { riderLogin } from '../../apollo/mutations'
import { Spinner, TextDefault, WrapperView } from '../../components'
import { FlashMessage } from '../../components/FlashMessage/FlashMessage'
import { AuthContext } from '../../context/auth'
import { alignment } from '../../utilities/alignment'
import colors from '../../utilities/colors'
import { scale } from '../../utilities/scaling'
import useStyle from './styles'
const RIDER_LOGIN = gql`
${riderLogin}
`
export default function Login() {
const styles = useStyle()
const navigation = useNavigation()
const [username, setUsername] = useState('rider')
const [password, setPassword] = useState('123123')
const [usernameError, setUsernameError] = useState('')
const [passwordError, setPasswordError] = useState('')
const { setTokenAsync } = useContext(AuthContext)
useEffect(() => {
navigation.setOptions({
headerLeft: null
})
}, [])
const [mutate, { loading }] = useMutation(RIDER_LOGIN, {
onCompleted,
onError
})
function validateForm() {
let res = true
setUsernameError('')
setPasswordError('')
if (!username) {
setUsernameError('Username is required')
res = false
}
if (!password) {
setPasswordError('Password is required')
res = false
}
return res
}
async function onCompleted(data) {
FlashMessage({
message: 'Logged in'
})
await AsyncStorage.setItem('rider-id', data.riderLogin.userId)
setTokenAsync(data.riderLogin.token)
}
function onError({ networkError, graphQLErrors }) {
console.log('errors', networkError, graphQLErrors)
// let message = ''
// if (!!graphQLErrors && graphQLErrors.length) {
// message = graphQLErrors[0].message
// }
// if (!!networkError) {
// message = networkError.result.errors[0].message
// }
// FlashMessage({
// message: message
// })
}
return (
<WrapperView>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.flex}>
<ScrollView
style={styles.flex}
contentContainerStyle={styles.scrollContent}>
<View style={[styles.flex, styles.container]}>
<Logo width={scale(130)} height={scale(130)} />
<View style={styles.width100}>
<TextDefault
style={alignment.MBmedium}
textColor={colors.placeHolderColor}>
Enter your Email and Password
</TextDefault>
<FilledTextField
defaultValue={'rider'}
error={usernameError}
keyboardType={'email-address'}
label={'Email or Phone'}
labelFontSize={scale(12)}
fontSize={scale(12)}
activeLineWidth={0}
labelHeight={10}
lineWidth={0}
textColor={colors.fontMainColor}
baseColor={colors.fontMainColor}
errorColor={colors.textErrorColor}
tintColor={colors.tagColor}
labelTextStyle={styles.labelStyle}
inputContainerStyle={styles.textContainer}
onChangeText={text => {
setUsername(text.toLowerCase().trim())
}}
/>
<View style={styles.mt15} />
<FilledTextField
defaultValue={'123123'}
error={passwordError}
label={'Password'}
secureTextEntry
labelFontSize={scale(12)}
fontSize={scale(12)}
activeLineWidth={0}
labelHeight={10}
lineWidth={0}
textColor={colors.fontMainColor}
baseColor={colors.fontMainColor}
errorColor={colors.textErrorColor}
tintColor={colors.tagColor}
labelTextStyle={styles.labelStyle}
inputContainerStyle={styles.textContainer}
onChangeText={text => {
setPassword(text.trim())
}}
/>
<View style={[styles.lower_form]}>
{loading && <Spinner />}
{!loading && (
<TouchableOpacity
onPress={async () => {
if (validateForm()) {
let notificationToken = null
if (Constants.isDevice) {
const {
status: existingStatus
} = await Notifications.getPermissionsAsync()
if (existingStatus === 'granted') {
notificationToken = (
await Notifications.getExpoPushTokenAsync()
).data
}
}
mutate({
variables: {
username: username.toLowerCase(),
password: password,
notificationToken
}
})
}
}}
activeOpacity={0.5}
style={[styles.RContainer]}>
<TextDefault textColor={colors.fontMainColor} H5 bold>
{i18n.t('loginBtn')}
</TextDefault>
</TouchableOpacity>
)}
</View>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</WrapperView>
)
}

View File

@ -0,0 +1,60 @@
import { Dimensions, StyleSheet } from 'react-native'
import { alignment } from '../../utilities/alignment'
import colors from '../../utilities/colors'
import { moderateScale, scale, verticalScale } from '../../utilities/scaling'
import { textStyles } from '../../utilities/textStyles'
const { height } = Dimensions.get('window')
const useStyle = () => {
return StyleSheet.create({
flex: {
flex: 1
},
width100: { width: '100%' },
bgColor: {
backgroundColor: colors.themeBackground
},
scrollContent: {
flexGrow: 1,
backgroundColor: colors.themeBackground
},
mt15: {
marginTop: verticalScale(15)
},
container: {
marginTop: 100,
flexGrow: 1,
width: '80%',
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'space-between',
paddingBottom: verticalScale(30)
},
lower_form: {
alignItems: 'center',
...alignment.MTlarge
},
RContainer: {
width: '100%',
height: height * 0.06,
backgroundColor: colors.buttonBackgroundPink,
alignItems: 'center',
justifyContent: 'center',
borderRadius: moderateScale(10)
},
textContainer: {
borderRadius: 10,
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
backgroundColor: colors.lightBackground,
alignItems: 'center',
overflow: 'hidden'
},
labelStyle: {
...textStyles.Bold,
paddingLeft: 5,
paddingTop: scale(1)
}
})
}
export default useStyle

View File

@ -0,0 +1,59 @@
import React, { useContext } from 'react'
import { View, FlatList } from 'react-native'
import Order from '../../components/Order/Order'
import ConfigurationContext from '../../context/configuration'
import UserContext from '../../context/user'
import styles from './style'
import { TextError, Spinner } from '../../components'
import { useNavigation } from '@react-navigation/native'
export default function Orders() {
const navigation = useNavigation()
const configuration = useContext(ConfigurationContext)
const {
loadingUnAssigned,
errorUnAssigned,
unAssignedOrders,
refetchUnAssigned,
networkStatusUnAssigned
} = useContext(UserContext)
function emptyView() {
return <TextError text="No New Order" />
}
if (loadingUnAssigned) return <Spinner />
if (errorUnAssigned) return <TextError text="Something is worng" />
return (
<View style={styles.flex}>
<View style={styles.flex}>
<FlatList
contentContainerStyle={{ flexGrow: 1 }}
keyExtractor={item => item._id}
data={
unAssignedOrders.length > 0
? unAssignedOrders.slice().reverse()
: []
}
refreshing={networkStatusUnAssigned === 4}
onRefresh={refetchUnAssigned}
ListEmptyComponent={emptyView}
renderItem={({ item }) => (
<Order
key={item._id}
orderId={item.order_id}
orderStatus={item.order_status}
orderAmount={`${configuration.currency_symbol}${item.order_amount}`}
orderDatetime={item.createdAt}
paymentMethod={item.payment_method}
onPress={() => {
navigation.navigate('OrderDetail', { id: item._id })
}}
/>
)}
/>
</View>
</View>
)
}

View File

@ -0,0 +1,7 @@
const styles = {
flex: {
flex: 1
}
}
export default styles

View File

@ -0,0 +1,454 @@
import { useMutation, useQuery } from '@apollo/react-hooks'
import { useNavigation, useRoute } from '@react-navigation/native'
import gql from 'graphql-tag'
import React, { useContext, useLayoutEffect } from 'react'
import {
Image,
Linking,
ScrollView,
TouchableOpacity,
View
} from 'react-native'
import MapView, { Marker, PROVIDER_GOOGLE } from 'react-native-maps'
import { assignOrder, updateOrderStatusRider } from '../../apollo/mutations'
import { assignedOrders, configuration } from '../../apollo/queries'
import { MainWrapper, Spinner, TextDefault, TextError } from '../../components'
import { FlashMessage } from '../../components//FlashMessage/FlashMessage'
import UserContext from '../../context/user'
import { alignment } from '../../utilities/alignment'
import colors from '../../utilities/colors'
import { linkToMapsApp } from '../../utilities/links'
import { scale } from '../../utilities/scaling'
import styles from './styles'
const CONFIGURATION = gql`
${configuration}
`
const UPDATE_ORDER_STATUS = gql`
${updateOrderStatusRider}
`
const ASSIGN_ORDER = gql`
${assignOrder}
`
const ORDERS = gql`
${assignedOrders}
`
const LATITUDE_DELTA = 0.0022
const LONGITUDE_DELTA = 0.0021
function OrderDetail() {
const navigation = useNavigation()
const route = useRoute()
const {
unAssignedOrders,
assignedOrders,
loadingAssigned,
loadingUnAssigned
} = useContext(UserContext)
// const [selectedOrder, setOrder] = useState(selectedOrder)
const selectedOrder =
unAssignedOrders.find(o => o._id === route.params?.id) ||
assignedOrders.find(o => o._id === route.params?.id) ||
null
const { data, loading: loadingConfig, error: errorConfig } = useQuery(
CONFIGURATION
)
const [mutate, { loading: loadingMutation }] = useMutation(
UPDATE_ORDER_STATUS,
{
onCompleted,
onError,
refetchQueries: [{ query: ORDERS }]
}
)
const [mutateAssignOrder, { loading }] = useMutation(ASSIGN_ORDER, {
onCompleted,
onError
})
useLayoutEffect(() => {
if (!selectedOrder) return
navigation.setOptions({
title: `Order ${selectedOrder.order_id}`
})
}, [selectedOrder])
async function onCompleted({ updateOrderStatusRider, assignOrder }) {
if (updateOrderStatusRider) {
FlashMessage({
message: `Order marked as ${updateOrderStatusRider.order_status}`
})
if (updateOrderStatusRider.order_status === 'DELIVERED') {
navigation.goBack()
return
}
// setOrder({ ...order, order_status: updateOrderStatusRider.order_status })
}
if (assignOrder) {
// setOrder({ ...order, ...assignOrder })
}
}
function onError({ graphQLErrors, networkError }) {
let message = 'Unknown error occured'
if (networkError) message = 'Internal Server Error'
if (graphQLErrors) message = graphQLErrors.map(o => o.message).join(', ')
FlashMessage({
message: message
})
}
function getAddons(addons, currencySymbol) {
return addons.map((addon, index) => {
return addon.options.map((option, indexOption) => (
<View key={`${index}${indexOption}`} style={[styles.orderContent]}>
<View style={[styles.orderSubContent]}>
<View style={styles.orderTextLeftContainer}></View>
<View style={styles.orderTextCenterContainer}>
<TextDefault
bolder
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
{addon.title}:{option.title}
</TextDefault>
</View>
<View style={styles.orderTextRightContainer}>
<TextDefault
textColor={colors.placeHolderColor}
bolder
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
{currencySymbol}
{option.price.toFixed(2)}
</TextDefault>
</View>
</View>
</View>
))
})
}
function getOrderItems(items, currencySymbol) {
return items.map((item, index) => {
return (
<View key={index}>
<View style={styles.orderContent}>
<View style={styles.orderSubContent}>
<View style={styles.orderTextLeftContainer}>
<TextDefault
textColor={colors.fontMainColor}
bolder
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
{item.quantity}x
</TextDefault>
</View>
<View style={styles.orderTextCenterContainer}>
<TextDefault
bolder
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
{item.food.title}({item.variation.title})
</TextDefault>
</View>
<View style={styles.orderTextRightContainer}>
<TextDefault
numberOfLines={1}
textColor={colors.placeHolderColor}
bolder
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
{currencySymbol}
{(item.variation.price * item.quantity).toFixed(2)}
</TextDefault>
</View>
</View>
</View>
{!item.addons ? null : getAddons(item.addons, currencySymbol)}
</View>
)
})
}
if (loadingAssigned || loadingUnAssigned) {
return <TextError text="Loading orders" />
}
if (loadingConfig) {
return <Spinner />
}
if (errorConfig) {
return <TextError text="Something is worng" />
}
if (!selectedOrder) {
return (
<TextError text="Order assgined to other rider,(something like that)" />
)
}
return (
<MainWrapper>
<ScrollView
style={styles.flex}
contentContainerStyle={{ flexGrow: 1 }}
showsVerticalScrollIndicator={false}>
<View style={styles.customerCard}>
<View style={styles.customerSubCard}>
<View style={styles.customerHeader}>
<TextDefault
H3
bolder
textColor={colors.tagColor}
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
Customer Details
</TextDefault>
</View>
<View style={styles.customerContent}>
<View style={styles.customerSubContent}>
<View style={styles.customerContentRow}>
<View style={styles.customerTextContainer}>
<TextDefault
bolder
textColor={colors.placeHolderColor}
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
Name
</TextDefault>
<TextDefault bolder style={{ ...alignment.PBxSmall }}>
{selectedOrder.user.name}
</TextDefault>
</View>
</View>
<View style={styles.customerContentRow}>
<View style={styles.customerTextContainer}>
<TextDefault
bolder
textColor={colors.placeHolderColor}
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
Contact
</TextDefault>
<TextDefault bolder style={{ ...alignment.PBxSmall }}>
{selectedOrder.user.phone}
</TextDefault>
</View>
</View>
<View style={styles.customerContentRow}>
<View style={styles.customerAddContainer}>
<TextDefault
bolder
textColor={colors.placeHolderColor}
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
Delviery Location
</TextDefault>
<TextDefault
numberOfLines={2}
bolder
style={{ ...alignment.PBxSmall }}>
{`${selectedOrder.delivery_address.delivery_address}`}
</TextDefault>
</View>
</View>
</View>
</View>
</View>
</View>
<View style={styles.orderContainer}>
<View style={styles.orderSubContainer}>
<View style={styles.orderHeader}>
<TextDefault
H3
bolder
textColor={colors.tagColor}
style={{ ...alignment.PTmedium }}>
Order Details
</TextDefault>
</View>
<View style={styles.orderSpacer} />
{getOrderItems(
selectedOrder.items,
data.configuration.currency_symbol
)}
<View style={styles.orderSpacer} />
<View style={styles.orderRow}>
<View style={styles.orderSubRow}>
<View style={styles.orderTextLeft}>
<TextDefault
textColor={colors.placeHolderColor}
bolder
style={{ ...alignment.PTmedium, ...alignment.PLmedium }}>
Subtotal
</TextDefault>
</View>
<View style={styles.orderTextRight}>
<TextDefault
numberOfLines={1}
textColor={colors.placeHolderColor}
bolder
style={{ ...alignment.PTxSmall, ...alignment.PTmedium }}>
{data.configuration.currency_symbol}
{(
selectedOrder.order_amount -
selectedOrder.delivery_charges
).toFixed(2)}
</TextDefault>
</View>
</View>
</View>
<View style={styles.orderRow2}>
<View style={styles.orderSubRow}>
<View style={styles.orderTextLeft}>
<TextDefault
textColor={colors.placeHolderColor}
bolder
style={{ ...alignment.PLmedium }}>
Delivery Charges
</TextDefault>
</View>
<View style={styles.orderTextRight}>
<TextDefault
numberOfLines={1}
textColor={colors.placeHolderColor}
bolder>
{data.configuration.currency_symbol}
{selectedOrder.delivery_charges.toFixed(2)}
</TextDefault>
</View>
</View>
</View>
<View style={styles.line} />
<View style={styles.orderRow}>
<View style={styles.orderSubRow}>
<View style={styles.orderTextLeft}>
<TextDefault H4 bolder>
Total
</TextDefault>
</View>
<View style={styles.orderTextRight}>
<TextDefault
numberOfLines={1}
textColor={colors.fontMainColor}
H4
bolder>
{data.configuration.currency_symbol}
{selectedOrder.order_amount.toFixed(2)}
</TextDefault>
</View>
</View>
</View>
<View style={styles.orderSpacer}></View>
</View>
</View>
<View style={styles.baseSpacer} />
<View style={styles.mapContainer}>
<MapView
style={styles.flex}
scrollEnabled={false}
zoomEnabled={false}
loadingBackgroundColor={colors.tagColor}
zoomControlEnabled={false}
rotateEnabled={false}
cacheEnabled={true}
initialRegion={{
latitude: parseFloat(selectedOrder.delivery_address.latitude),
latitudeDelta: LATITUDE_DELTA,
longitude: parseFloat(selectedOrder.delivery_address.longitude),
longitudeDelta: LONGITUDE_DELTA
}}
provider={PROVIDER_GOOGLE}
onPress={() => {
linkToMapsApp(
{
latitude: selectedOrder.delivery_address.latitude,
longitude: selectedOrder.delivery_address.longitude
},
'Destination'
)
}}>
<Marker
title="Delivery Address"
coordinate={{
latitude: parseFloat(selectedOrder.delivery_address.latitude),
longitude: parseFloat(selectedOrder.delivery_address.longitude)
}}
onPress={() =>
Linking.openURL(
`google.navigation:q=${parseFloat(
selectedOrder.delivery_address.longitude
)}+${parseFloat(selectedOrder.delivery_address.longitude)}`
)
}>
<Image
source={require('../../../assets/images/ui/markerEnatega.png')}
style={{ width: scale(40), height: scale(40) }}
/>
</Marker>
</MapView>
</View>
<View style={styles.actionContainer}>
{selectedOrder.order_status === 'ACCEPTED' && !selectedOrder.rider && (
<TouchableOpacity
disabled={loading}
style={[
styles.cancelBtnStyle,
{ backgroundColor: colors.buttonBackground }
]}
onPress={() => {
mutateAssignOrder({ variables: { id: selectedOrder._id } })
}}>
{loading ? (
<Spinner spinnerColor={colors.buttonText} />
) : (
<TextDefault
textColor={colors.fontMainColor}
H4
bold
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
Assign to me
</TextDefault>
)}
</TouchableOpacity>
)}
{loadingMutation && <Spinner />}
{!loadingMutation && selectedOrder.rider && (
<View style={styles.actionSubContainer}>
{selectedOrder.order_status === 'ACCEPTED' && (
<TouchableOpacity
style={[
styles.cancelBtnStyle,
{ backgroundColor: colors.tagColor }
]}
onPress={() => {
mutate({
variables: { id: selectedOrder._id, status: 'PICKED' }
})
}}>
<TextDefault
textColor={colors.fontMainColor}
H4
bold
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
Picked
</TextDefault>
</TouchableOpacity>
)}
{selectedOrder.order_status === 'PICKED' && (
<TouchableOpacity
style={styles.acceptBtnStyle}
onPress={() => {
mutate({
variables: { id: selectedOrder._id, status: 'DELIVERED' }
})
}}>
<TextDefault
textColor={colors.fontMainColor}
H4
bold
style={{ ...alignment.PTxSmall, ...alignment.PBxSmall }}>
Delivered
</TextDefault>
</TouchableOpacity>
)}
</View>
)}
</View>
</ScrollView>
</MainWrapper>
)
}
export default OrderDetail

View File

@ -0,0 +1,197 @@
import { Dimensions, StyleSheet } from 'react-native'
import { alignment } from '../../utilities/alignment'
import colors from '../../utilities/colors'
import { scale, verticalScale } from '../../utilities/scaling'
const { height } = Dimensions.get('window')
export default {
flex: {
flex: 1,
backgroundColor: colors.themeBackground
},
line: {
width: '80%',
height: StyleSheet.hairlineWidth,
backgroundColor: colors.horizontalLine,
alignSelf: 'center',
...alignment.MTmedium,
...alignment.MBmedium
},
customerCard: {
width: '100%',
height: height * 0.4,
justifyContent: 'center',
alignItems: 'center'
},
customerSubCard: {
width: '85%',
height: '90%',
borderWidth: 2,
borderStyle: 'dashed',
backgroundColor: '#F3FAFE',
borderRadius: scale(10),
borderColor: colors.horizontalLine
},
customerHeader: {
width: '100%',
height: '30%',
justifyContent: 'center',
alignItems: 'center'
},
customerContent: {
flex: 1,
justifyContent: 'flex-start'
},
customerSubContent: {
width: '80%',
height: '95%',
justifyContent: 'space-around'
},
customerContentRow: {
width: '100%',
height: '50%',
flexDirection: 'row',
paddingLeft: '8%'
},
customerImgContainer: {
width: '20%',
height: '100%',
justifyContent: 'center',
alignItems: 'center'
},
customerTextContainer: {
flex: 1,
justifyContent: 'center'
},
customerAddContainer: {
justifyContent: 'center',
width: '90%'
},
orderContainer: {
width: '100%',
flexGrow: 1,
justifyContent: 'center',
alignItems: 'center',
...alignment.PTxSmall,
...alignment.PBxSmall
},
orderSubContainer: {
width: '85%',
minHeight: height * 0.3,
borderWidth: 2,
borderStyle: 'dashed',
borderRadius: scale(20),
backgroundColor: '#F3FAFE',
borderColor: colors.horizontalLine
},
orderHeader: {
width: '100%',
height: height * 0.08,
justifyContent: 'center',
alignItems: 'center'
},
orderContent: {
width: '100%',
alignItems: 'center'
},
orderSubContent: {
width: '90%',
flexDirection: 'row'
},
orderTextLeftContainer: {
width: '10%',
justifyContent: 'center',
alignItems: 'center'
},
orderTextCenterContainer: {
width: '65%',
justifyContent: 'center',
alignItems: 'flex-start'
},
orderTextRightContainer: {
width: '25%',
justifyContent: 'center',
alignItems: 'flex-end'
},
orderSpacer: {
width: '100%',
height: height * 0.02
},
orderRow: {
width: '100%',
height: height * 0.05,
alignItems: 'center'
},
orderRow2: {
width: '100%',
height: height * 0.07,
alignItems: 'center',
...alignment.PBlarge
},
orderSubRow: {
width: '90%',
height: '100%',
flexDirection: 'row'
},
orderTextLeft: {
width: '50%',
height: '100%',
paddingLeft: '5%',
justifyContent: 'center',
alignItems: 'flex-start'
},
orderTextRight: {
width: '50%',
height: '100%',
alignItems: 'flex-end',
justifyContent: 'center'
},
actionContainer: {
width: '100%',
height: height * 0.1,
justifyContent: 'center',
alignItems: 'center',
...alignment.MTmedium,
...alignment.MBlarge
},
actionSubContainer: {
width: '90%',
height: '80%',
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
borderRadius: scale(10),
...alignment.MBlarge
},
cancelBtnStyle: {
width: '80%',
height: '70%',
justifyContent: 'center',
alignItems: 'center',
borderRadius: scale(10)
},
acceptBtnStyle: {
backgroundColor: colors.tagColor,
width: '80%',
height: '70%',
justifyContent: 'center',
alignItems: 'center',
borderRadius: scale(10)
},
removeBtnStyle: {
backgroundColor: colors.tagColor,
width: '45%',
height: '70%',
justifyContent: 'center',
alignItems: 'center'
},
baseSpacer: {
marginTop: verticalScale(15)
},
mapContainer: {
width: '85%',
alignSelf: 'center',
height: verticalScale(200)
}
}

View File

@ -0,0 +1,62 @@
import React, { useEffect, useState } from 'react'
import {
ImageBackground,
StatusBar,
TouchableOpacity,
View
} from 'react-native'
import i18n from '../../../i18n'
import { AssignedOrders, NewOrders, TextDefault } from '../../components'
import colors from '../../utilities/colors'
import useStyle from './style'
const BACKGROUND_IMAGE = require('../../../assets/images/ui/BG.png')
export default function Orders() {
const styles = useStyle()
const [isNewOrderSelected, setIsNewOrderSelected] = useState(false)
useEffect(() => {
StatusBar.setBarStyle('light-content')
}, [isNewOrderSelected])
return (
<View style={[styles.flex, styles.bottom]}>
<ImageBackground style={styles.imageContainer} source={BACKGROUND_IMAGE}>
<View style={styles.toggleContainer}>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => setIsNewOrderSelected(false)}
style={[
styles.toggleBtn,
{
backgroundColor: !isNewOrderSelected
? colors.buttonBackgroundPink
: 'transparent'
}
]}>
<TextDefault bold H5 numberOfLines={1}>
{i18n.t('myOrders')}
</TextDefault>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => setIsNewOrderSelected(true)}
style={[
styles.toggleBtn,
{
backgroundColor: isNewOrderSelected
? colors.buttonBackgroundPink
: 'transparent'
}
]}>
<TextDefault bold H5 numberOfLines={1}>
{i18n.t('newOrders')}
</TextDefault>
</TouchableOpacity>
</View>
</ImageBackground>
{!isNewOrderSelected ? <AssignedOrders /> : <NewOrders />}
</View>
)
}

View File

@ -0,0 +1,50 @@
import { useHeaderHeight } from '@react-navigation/stack'
import { StyleSheet } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { scale, verticalScale } from '../../utilities/scaling'
const useStyle = () => {
const inset = useSafeAreaInsets()
const headerHeight = useHeaderHeight()
return StyleSheet.create({
flex: {
flex: 1
},
bottom: {
paddingBottom: inset.bottom
},
imageContainer: {
width: '100%',
paddingTop: headerHeight,
height: verticalScale(270)
},
headerContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
width: '65%',
padding: '5%'
},
toggleContainer: {
width: '65%',
borderRadius: scale(10),
justifyContent: 'space-between',
height: verticalScale(50),
alignItems: 'center',
flexDirection: 'row',
paddingHorizontal: scale(10),
backgroundColor: 'white',
marginTop: verticalScale(20),
alignSelf: 'center'
},
toggleBtn: {
justifyContent: 'center',
height: '70%',
alignItems: 'center',
width: '47%',
borderRadius: scale(10)
}
})
}
export default useStyle

View File

@ -0,0 +1,19 @@
import Chat from './Chat/Chat'
import Help from './Help/Help'
import HelpBrowser from './HelpBrowser/HelpBrowser'
import Language from './Language/Language'
import Login from './Login/Login'
import NewOrders from './NewOrders/NewOrders'
import OrderDetail from './OrderDetail/OrderDetail'
import Orders from './Orders/Orders'
export {
Chat,
Help,
HelpBrowser,
Language,
Login,
NewOrders,
OrderDetail,
Orders
}

View File

@ -0,0 +1,135 @@
import { scale } from './scaling'
const XSMALL = 5
const SMALL = 10
const MEDIUM = 15
const LARGE = 20
export const alignment = {
MxSmall: {
margin: scale(XSMALL)
},
MBxSmall: {
marginBottom: scale(XSMALL)
},
MTxSmall: {
marginTop: scale(XSMALL)
},
MRxSmall: {
marginRight: scale(XSMALL)
},
MLxSmall: {
marginLeft: scale(XSMALL)
},
Msmall: {
margin: scale(SMALL)
},
MBsmall: {
marginBottom: scale(SMALL)
},
MTsmall: {
marginTop: scale(SMALL)
},
MRsmall: {
marginRight: scale(SMALL)
},
MLsmall: {
marginLeft: scale(SMALL)
},
Mmedium: {
margin: scale(MEDIUM)
},
MBmedium: {
marginBottom: scale(MEDIUM)
},
MTmedium: {
marginTop: scale(MEDIUM)
},
MRmedium: {
marginRight: scale(MEDIUM)
},
MLmedium: {
marginLeft: scale(MEDIUM)
},
Mlarge: {
margin: scale(LARGE)
},
MBlarge: {
marginBottom: scale(LARGE)
},
MTlarge: {
marginTop: scale(LARGE)
},
MRlarge: {
marginRight: scale(LARGE)
},
MLlarge: {
marginLeft: scale(LARGE)
},
// Padding
PxSmall: {
padding: scale(XSMALL)
},
PBxSmall: {
paddingBottom: scale(XSMALL)
},
PTxSmall: {
paddingTop: scale(XSMALL)
},
PRxSmall: {
paddingRight: scale(XSMALL)
},
PLxSmall: {
paddingLeft: scale(XSMALL)
},
Psmall: {
padding: scale(SMALL)
},
PBsmall: {
paddingBottom: scale(SMALL)
},
PTsmall: {
paddingTop: scale(SMALL)
},
PRsmall: {
paddingRight: scale(SMALL)
},
PLsmall: {
paddingLeft: scale(SMALL)
},
Pmedium: {
padding: scale(MEDIUM)
},
PBmedium: {
paddingBottom: scale(MEDIUM)
},
PTmedium: {
paddingTop: scale(MEDIUM)
},
PRmedium: {
paddingRight: scale(MEDIUM)
},
PLmedium: {
paddingLeft: scale(MEDIUM)
},
Plarge: {
padding: scale(LARGE)
},
PBlarge: {
paddingBottom: scale(LARGE)
},
PTlarge: {
paddingTop: scale(LARGE)
},
PRlarge: {
paddingRight: scale(LARGE)
},
PLlarge: {
paddingLeft: scale(LARGE)
}
}

View File

@ -0,0 +1,28 @@
const colors = {
black: '#000',
themeBackground: '#FFFFFF',
iconColor: '#333333',
tagColor: '#febb2c',
iconPink: '#d83765',
radioColor: '#FFF',
radioOuterColor: '#d83765',
spinnerColor: '#febb2c',
orderComplete: '#518ef8',
orderUncomplete: '#f14336',
horizontalLine: '#B8B8B8',
buttonBackground: '#febb2c',
buttonText: '#FFF',
buttonBackgroundPink: '#febb2c',
buttonTextPink: '#FFF',
textErrorColor: '#FA7751',
headerBackground: '#FFF',
headerText: '#2a2a2a',
fontMainColor: '#000',
fontSecondColor: '#FFFFFF',
cartContainer: '#FFF',
placeHolderColor: '#a5a5a5',
lightBackground: '#f8f9fa',
yellowishOrange: '#fff1d4'
}
export default colors

View File

@ -0,0 +1,40 @@
const NAVIGATION_SCREEN = {
Chat: 'Chat',
Login: 'Login',
Orders: 'Orders',
OrderDetail: 'OrderDetail',
Help: 'Help',
Language: 'Language',
HelpBrowser: 'HelpBrowser'
}
const ICONS_NAME = {
Logo: 'logo',
Menu: 'menu',
Back: 'back',
Exit: 'exit',
Cash: 'cash',
Visa: 'visa',
Home: 'home',
Cart: 'cart',
Info: 'info',
Plus: 'plus',
Radio: 'radio',
Cross: 'cross',
Minus: 'minus',
Trash: 'trash',
Clock: 'clock',
Reload: 'reload',
Pencil: 'pencil',
Target: 'target',
Filter: 'filter',
Paypal: 'paypal',
Message: 'message',
Setting: 'setting',
Checked: 'checked',
Refresh: 'refresh',
Location: 'location',
RadioSelect: 'radioSelect'
}
export { NAVIGATION_SCREEN, ICONS_NAME }

View File

@ -0,0 +1,5 @@
export const fontStyles = {
MuseoSans300: 'MuseoSans300',
MuseoSans500: 'MuseoSans500',
MuseoSans700: 'MuseoSans700'
}

View File

@ -0,0 +1,13 @@
import { Platform, Linking } from 'react-native'
export function linkToMapsApp({ latitude, longitude }, label) {
const scheme = Platform.select({ ios: 'maps:0,0?q=', android: 'geo:0,0?q=' })
const latLng = `${latitude},${longitude}`
// const label = label;
const url = Platform.select({
ios: `${scheme}${label}@${latLng}`,
android: `${scheme}${latLng}(${label})`
})
Linking.openURL(url)
}

View File

@ -0,0 +1,33 @@
import * as TaskManager from 'expo-task-manager'
import { clientRef } from '../apollo/index'
import { updateLocation } from '../apollo/mutations'
import gql from 'graphql-tag'
const UPDATE_LOCATION = gql`
${updateLocation}
`
TaskManager.defineTask(
'RIDER_LOCATION',
async ({ data: { locations }, error }) => {
try {
if (error) {
console.log('rider location error')
return
}
if (locations.length > 0) {
const {
coords: { latitude, longitude }
} = locations[locations.length - 1]
await clientRef.mutate({
mutation: UPDATE_LOCATION,
variables: {
latitude: latitude.toString(),
longitude: longitude.toString()
}
})
}
} catch (error) {
console.log('error', error)
}
}
)

View File

@ -0,0 +1,12 @@
import { Dimensions } from 'react-native'
const { width, height } = Dimensions.get('window')
// Guideline sizes are based on standard ~5" screen mobile device
const guidelineBaseWidth = 350
const guidelineBaseHeight = 680
const scale = size => (width / guidelineBaseWidth) * size
const verticalScale = size => (height / guidelineBaseHeight) * size
const moderateScale = (size, factor = 0.5) =>
size + (scale(size) - size) * factor
export { scale, verticalScale, moderateScale }

View File

@ -0,0 +1,50 @@
import { scale } from './scaling'
import { fontStyles } from './fontStyles'
export const textStyles = {
H1: {
fontSize: scale(35)
},
H2: {
fontSize: scale(24)
},
H3: {
fontSize: scale(20)
},
H4: {
fontSize: scale(16)
},
H5: {
fontSize: scale(14)
},
Normal: {
fontSize: scale(12)
},
Small: {
fontSize: scale(10)
},
xSmall: {
fontSize: scale(8)
},
Regular: {
fontFamily: fontStyles.MuseoSans300
},
Bold: {
fontFamily: fontStyles.MuseoSans500
},
Bolder: {
fontFamily: fontStyles.MuseoSans700
},
Center: {
textAlign: 'center'
},
Right: {
textAlign: 'right'
},
UpperCase: {
textTransform: 'uppercase'
},
LineOver: {
textDecorationLine: 'line-through'
}
}