** Major Change**. Removed unknown dependencies, and reset project to a new start state. See MD file for instructions.
3
.gitignore
vendored
@@ -15,4 +15,5 @@ yarn-error.log
|
|||||||
|
|
||||||
|
|
||||||
*.ipa
|
*.ipa
|
||||||
*.aab
|
*.aab
|
||||||
|
.expo
|
||||||
1
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{ "recommendations": ["expo.vscode-expo-tools"] }
|
||||||
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit",
|
||||||
|
"source.organizeImports": "explicit",
|
||||||
|
"source.sortMembers": "explicit"
|
||||||
|
}
|
||||||
|
}
|
||||||
9
54 Updates.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
New Router Changes
|
||||||
|
|
||||||
|
1. Added Packages
|
||||||
|
2. Had to add scheme to App.Json
|
||||||
|
3. Changed entry point for package.
|
||||||
|
|
||||||
|
|
||||||
|
**If having issues when running, run these steps**
|
||||||
|
https://docs.expo.dev/troubleshooting/clear-cache-macos-linux/
|
||||||
25
App.js
@@ -1,24 +1,16 @@
|
|||||||
|
import 'expo-dev-client';
|
||||||
import { ApolloProvider } from "@apollo/client";
|
import { ApolloProvider } from "@apollo/client";
|
||||||
import "expo-asset";
|
import "expo-asset";
|
||||||
import 'expo-dev-client';
|
|
||||||
import "intl";
|
import "intl";
|
||||||
import "intl/locale-data/jsonp/en";
|
import "intl/locale-data/jsonp/en";
|
||||||
import {
|
|
||||||
MD2LightTheme as DefaultTheme,
|
|
||||||
Provider as PaperProvider,
|
|
||||||
} from "react-native-paper";
|
|
||||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { PersistGate } from "redux-persist/integration/react";
|
import { PersistGate } from "redux-persist/integration/react";
|
||||||
import ScreenMainComponent from "./components/screen-main/screen-main.component";
|
import Router from "./components-new/router/router";
|
||||||
import { client } from "./graphql/client";
|
import { client } from "./graphql/client";
|
||||||
import { persistor, store } from "./redux/store";
|
import { persistor, store } from "./redux/store";
|
||||||
import "./translations/i18n";
|
import "./translations/i18n";
|
||||||
|
|
||||||
import RNEventSource from "react-native-event-source";
|
|
||||||
globalThis.EventSource = RNEventSource;
|
|
||||||
|
|
||||||
// Sentry.init({
|
// Sentry.init({
|
||||||
// dsn: "https://8d6c3de1940a4e4f8b81cf4d2150bdea@o492140.ingest.sentry.io/5558869",
|
// dsn: "https://8d6c3de1940a4e4f8b81cf4d2150bdea@o492140.ingest.sentry.io/5558869",
|
||||||
// enableInExpoDevelopment: true,
|
// enableInExpoDevelopment: true,
|
||||||
@@ -32,14 +24,6 @@ globalThis.EventSource = RNEventSource;
|
|||||||
// //debug: true, // Sentry will try to print out useful debugging information if something goes wrong with sending an event. Set this to `false` in production.
|
// //debug: true, // Sentry will try to print out useful debugging information if something goes wrong with sending an event. Set this to `false` in production.
|
||||||
// });
|
// });
|
||||||
|
|
||||||
const theme = {
|
|
||||||
...DefaultTheme,
|
|
||||||
colors: {
|
|
||||||
...DefaultTheme.colors,
|
|
||||||
primary: "#1890ff",
|
|
||||||
accent: "tomato",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
@@ -47,10 +31,7 @@ const App = () => {
|
|||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<PersistGate persistor={persistor}>
|
<PersistGate persistor={persistor}>
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<PaperProvider theme={theme}>
|
<Router />
|
||||||
<ScreenMainComponent />
|
|
||||||
<Toast />
|
|
||||||
</PaperProvider>
|
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|||||||
22
app.json
@@ -3,6 +3,7 @@
|
|||||||
"name": "ImEX Mobile",
|
"name": "ImEX Mobile",
|
||||||
"slug": "imexmobile",
|
"slug": "imexmobile",
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
|
"scheme": "imex-mobile-scheme",
|
||||||
"extra": {
|
"extra": {
|
||||||
"expover": "1",
|
"expover": "1",
|
||||||
"eas": {
|
"eas": {
|
||||||
@@ -64,14 +65,6 @@
|
|||||||
},
|
},
|
||||||
"description": "",
|
"description": "",
|
||||||
"plugins": [
|
"plugins": [
|
||||||
[
|
|
||||||
"@sentry/react-native/expo",
|
|
||||||
{
|
|
||||||
"url": "https://sentry.io/",
|
|
||||||
"organization": "imex",
|
|
||||||
"project": "imexmobile"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
"expo-media-library",
|
"expo-media-library",
|
||||||
{
|
{
|
||||||
@@ -88,18 +81,7 @@
|
|||||||
],
|
],
|
||||||
"expo-localization",
|
"expo-localization",
|
||||||
"expo-font",
|
"expo-font",
|
||||||
[
|
"expo-router"
|
||||||
"expo-build-properties",
|
|
||||||
{
|
|
||||||
"android": {
|
|
||||||
"minSdkVersion": 25,
|
|
||||||
"compileSdkVersion": 35,
|
|
||||||
"targetSdkVersion": 35,
|
|
||||||
"buildToolsVersion": "35.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@logrocket/react-native"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
35
app/(tabs)/_layout.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { Tabs } from 'expo-router';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { HapticTab } from '@/components/haptic-tab';
|
||||||
|
import { IconSymbol } from '@/components/ui/icon-symbol';
|
||||||
|
import { Colors } from '@/constants/theme';
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
|
||||||
|
export default function TabLayout() {
|
||||||
|
const colorScheme = useColorScheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
screenOptions={{
|
||||||
|
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
||||||
|
headerShown: false,
|
||||||
|
tabBarButton: HapticTab,
|
||||||
|
}}>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="index"
|
||||||
|
options={{
|
||||||
|
title: 'Home',
|
||||||
|
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="explore"
|
||||||
|
options={{
|
||||||
|
title: 'Explore',
|
||||||
|
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
112
app/(tabs)/explore.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { Image } from 'expo-image';
|
||||||
|
import { Platform, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { Collapsible } from '@/components/ui/collapsible';
|
||||||
|
import { ExternalLink } from '@/components/external-link';
|
||||||
|
import ParallaxScrollView from '@/components/parallax-scroll-view';
|
||||||
|
import { ThemedText } from '@/components/themed-text';
|
||||||
|
import { ThemedView } from '@/components/themed-view';
|
||||||
|
import { IconSymbol } from '@/components/ui/icon-symbol';
|
||||||
|
import { Fonts } from '@/constants/theme';
|
||||||
|
|
||||||
|
export default function TabTwoScreen() {
|
||||||
|
return (
|
||||||
|
<ParallaxScrollView
|
||||||
|
headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }}
|
||||||
|
headerImage={
|
||||||
|
<IconSymbol
|
||||||
|
size={310}
|
||||||
|
color="#808080"
|
||||||
|
name="chevron.left.forwardslash.chevron.right"
|
||||||
|
style={styles.headerImage}
|
||||||
|
/>
|
||||||
|
}>
|
||||||
|
<ThemedView style={styles.titleContainer}>
|
||||||
|
<ThemedText
|
||||||
|
type="title"
|
||||||
|
style={{
|
||||||
|
fontFamily: Fonts.rounded,
|
||||||
|
}}>
|
||||||
|
Explore
|
||||||
|
</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
<ThemedText>This app includes example code to help you get started.</ThemedText>
|
||||||
|
<Collapsible title="File-based routing">
|
||||||
|
<ThemedText>
|
||||||
|
This app has two screens:{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
|
||||||
|
</ThemedText>
|
||||||
|
<ThemedText>
|
||||||
|
The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{' '}
|
||||||
|
sets up the tab navigator.
|
||||||
|
</ThemedText>
|
||||||
|
<ExternalLink href="https://docs.expo.dev/router/introduction">
|
||||||
|
<ThemedText type="link">Learn more</ThemedText>
|
||||||
|
</ExternalLink>
|
||||||
|
</Collapsible>
|
||||||
|
<Collapsible title="Android, iOS, and web support">
|
||||||
|
<ThemedText>
|
||||||
|
You can open this project on Android, iOS, and the web. To open the web version, press{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project.
|
||||||
|
</ThemedText>
|
||||||
|
</Collapsible>
|
||||||
|
<Collapsible title="Images">
|
||||||
|
<ThemedText>
|
||||||
|
For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
|
||||||
|
different screen densities
|
||||||
|
</ThemedText>
|
||||||
|
<Image
|
||||||
|
source={require('@/assets/images/react-logo.png')}
|
||||||
|
style={{ width: 100, height: 100, alignSelf: 'center' }}
|
||||||
|
/>
|
||||||
|
<ExternalLink href="https://reactnative.dev/docs/images">
|
||||||
|
<ThemedText type="link">Learn more</ThemedText>
|
||||||
|
</ExternalLink>
|
||||||
|
</Collapsible>
|
||||||
|
<Collapsible title="Light and dark mode components">
|
||||||
|
<ThemedText>
|
||||||
|
This template has light and dark mode support. The{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect
|
||||||
|
what the user's current color scheme is, and so you can adjust UI colors accordingly.
|
||||||
|
</ThemedText>
|
||||||
|
<ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
|
||||||
|
<ThemedText type="link">Learn more</ThemedText>
|
||||||
|
</ExternalLink>
|
||||||
|
</Collapsible>
|
||||||
|
<Collapsible title="Animations">
|
||||||
|
<ThemedText>
|
||||||
|
This template includes an example of an animated component. The{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses
|
||||||
|
the powerful{' '}
|
||||||
|
<ThemedText type="defaultSemiBold" style={{ fontFamily: Fonts.mono }}>
|
||||||
|
react-native-reanimated
|
||||||
|
</ThemedText>{' '}
|
||||||
|
library to create a waving hand animation.
|
||||||
|
</ThemedText>
|
||||||
|
{Platform.select({
|
||||||
|
ios: (
|
||||||
|
<ThemedText>
|
||||||
|
The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{' '}
|
||||||
|
component provides a parallax effect for the header image.
|
||||||
|
</ThemedText>
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
</Collapsible>
|
||||||
|
</ParallaxScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
headerImage: {
|
||||||
|
color: '#808080',
|
||||||
|
bottom: -90,
|
||||||
|
left: -35,
|
||||||
|
position: 'absolute',
|
||||||
|
},
|
||||||
|
titleContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
});
|
||||||
98
app/(tabs)/index.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { Image } from 'expo-image';
|
||||||
|
import { Platform, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { HelloWave } from '@/components/hello-wave';
|
||||||
|
import ParallaxScrollView from '@/components/parallax-scroll-view';
|
||||||
|
import { ThemedText } from '@/components/themed-text';
|
||||||
|
import { ThemedView } from '@/components/themed-view';
|
||||||
|
import { Link } from 'expo-router';
|
||||||
|
|
||||||
|
export default function HomeScreen() {
|
||||||
|
return (
|
||||||
|
<ParallaxScrollView
|
||||||
|
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
|
||||||
|
headerImage={
|
||||||
|
<Image
|
||||||
|
source={require('@/assets/images/partial-react-logo.png')}
|
||||||
|
style={styles.reactLogo}
|
||||||
|
/>
|
||||||
|
}>
|
||||||
|
<ThemedView style={styles.titleContainer}>
|
||||||
|
<ThemedText type="title">Welcome!</ThemedText>
|
||||||
|
<HelloWave />
|
||||||
|
</ThemedView>
|
||||||
|
<ThemedView style={styles.stepContainer}>
|
||||||
|
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
|
||||||
|
<ThemedText>
|
||||||
|
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
|
||||||
|
Press{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">
|
||||||
|
{Platform.select({
|
||||||
|
ios: 'cmd + d',
|
||||||
|
android: 'cmd + m',
|
||||||
|
web: 'F12',
|
||||||
|
})}
|
||||||
|
</ThemedText>{' '}
|
||||||
|
to open developer tools.
|
||||||
|
</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
<ThemedView style={styles.stepContainer}>
|
||||||
|
<Link href="/modal">
|
||||||
|
<Link.Trigger>
|
||||||
|
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
|
||||||
|
</Link.Trigger>
|
||||||
|
<Link.Preview />
|
||||||
|
<Link.Menu>
|
||||||
|
<Link.MenuAction title="Action" icon="cube" onPress={() => alert('Action pressed')} />
|
||||||
|
<Link.MenuAction
|
||||||
|
title="Share"
|
||||||
|
icon="square.and.arrow.up"
|
||||||
|
onPress={() => alert('Share pressed')}
|
||||||
|
/>
|
||||||
|
<Link.Menu title="More" icon="ellipsis">
|
||||||
|
<Link.MenuAction
|
||||||
|
title="Delete"
|
||||||
|
icon="trash"
|
||||||
|
destructive
|
||||||
|
onPress={() => alert('Delete pressed')}
|
||||||
|
/>
|
||||||
|
</Link.Menu>
|
||||||
|
</Link.Menu>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<ThemedText>
|
||||||
|
{`Tap the Explore tab to learn more about what's included in this starter app.`}
|
||||||
|
</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
<ThemedView style={styles.stepContainer}>
|
||||||
|
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
|
||||||
|
<ThemedText>
|
||||||
|
{`When you're ready, run `}
|
||||||
|
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
|
||||||
|
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
|
||||||
|
</ThemedText>
|
||||||
|
</ThemedView>
|
||||||
|
</ParallaxScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
titleContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
stepContainer: {
|
||||||
|
gap: 8,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
reactLogo: {
|
||||||
|
height: 178,
|
||||||
|
width: 290,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
position: 'absolute',
|
||||||
|
},
|
||||||
|
});
|
||||||
24
app/_layout.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
|
||||||
|
import { Stack } from 'expo-router';
|
||||||
|
import { StatusBar } from 'expo-status-bar';
|
||||||
|
import 'react-native-reanimated';
|
||||||
|
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
|
||||||
|
export const unstable_settings = {
|
||||||
|
anchor: '(tabs)',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout() {
|
||||||
|
const colorScheme = useColorScheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||||
|
<Stack>
|
||||||
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||||
|
<Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} />
|
||||||
|
</Stack>
|
||||||
|
<StatusBar style="auto" />
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
29
app/modal.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Link } from 'expo-router';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
import { ThemedText } from '@/components/themed-text';
|
||||||
|
import { ThemedView } from '@/components/themed-view';
|
||||||
|
|
||||||
|
export default function ModalScreen() {
|
||||||
|
return (
|
||||||
|
<ThemedView style={styles.container}>
|
||||||
|
<ThemedText type="title">This is a modal</ThemedText>
|
||||||
|
<Link href="/" dismissTo style={styles.link}>
|
||||||
|
<ThemedText type="link">Go to home screen</ThemedText>
|
||||||
|
</Link>
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: 20,
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
marginTop: 15,
|
||||||
|
paddingVertical: 15,
|
||||||
|
},
|
||||||
|
});
|
||||||
BIN
assets/images/android-icon-background.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/images/android-icon-foreground.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
assets/images/android-icon-monochrome.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
assets/images/favicon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/icon.png
Normal file
|
After Width: | Height: | Size: 384 KiB |
BIN
assets/images/partial-react-logo.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
assets/images/react-logo.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/images/react-logo@2x.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/react-logo@3x.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
assets/images/splash-icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
@@ -1,7 +0,0 @@
|
|||||||
module.exports = function (api) {
|
|
||||||
api.cache(true);
|
|
||||||
return {
|
|
||||||
presets: ["babel-preset-expo"],
|
|
||||||
plugins: ["react-native-reanimated/plugin"],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -4,7 +4,7 @@ import { NavigationContainer } from "@react-navigation/native";
|
|||||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Button } from "react-native-paper";
|
import { Button } from "react-native-paper";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -19,16 +19,12 @@ import {
|
|||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser,
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import env from "../../env";
|
|
||||||
import ScreenJobDetail from "../screen-job-detail/screen-job-detail.component";
|
import ScreenJobDetail from "../screen-job-detail/screen-job-detail.component";
|
||||||
import ScreenJobList from "../screen-job-list/screen-job-list.component";
|
import ScreenJobList from "../screen-job-list/screen-job-list.component";
|
||||||
import ScreenMediaBrowser from "../screen-media-browser/screen-media-browser.component";
|
import ScreenMediaBrowser from "../screen-media-browser/screen-media-browser.component";
|
||||||
import ScreenSettingsComponent from "../screen-settings/screen-settings.component";
|
import ScreenSettingsComponent from "../screen-settings/screen-settings.component";
|
||||||
import ScreenSignIn from "../screen-sign-in/screen-sign-in.component";
|
import ScreenSignIn from "../screen-sign-in/screen-sign-in.component";
|
||||||
import ScreenSplash from "../screen-splash/screen-splash.component";
|
import ScreenSplash from "../screen-splash/screen-splash.component";
|
||||||
import { SplitFactory } from "@splitsoftware/splitio-react-native";
|
|
||||||
import * as Updates from "expo-updates";
|
|
||||||
import LogRocket from "@logrocket/react-native";
|
|
||||||
|
|
||||||
const ActiveJobStack = createNativeStackNavigator();
|
const ActiveJobStack = createNativeStackNavigator();
|
||||||
const MoreStack = createNativeStackNavigator();
|
const MoreStack = createNativeStackNavigator();
|
||||||
@@ -152,14 +148,11 @@ const BottomTabsNavigator = () => (
|
|||||||
</BottomTabs.Navigator>
|
</BottomTabs.Navigator>
|
||||||
);
|
);
|
||||||
|
|
||||||
export var splitClient;
|
|
||||||
|
|
||||||
export function ScreenMainComponent({
|
export function ScreenMainComponent({
|
||||||
checkUserSession,
|
checkUserSession,
|
||||||
currentUser,
|
currentUser,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkUserSession();
|
checkUserSession();
|
||||||
}, [checkUserSession]);
|
}, [checkUserSession]);
|
||||||
@@ -171,16 +164,6 @@ export function ScreenMainComponent({
|
|||||||
// // });
|
// // });
|
||||||
// }, []);
|
// }, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (bodyshop && bodyshop.imexshopid) {
|
|
||||||
splitClient = SplitFactory({
|
|
||||||
//debug: true,
|
|
||||||
core: { authorizationKey: env.SPLIT_API, key: bodyshop.imexshopid },
|
|
||||||
}).client();
|
|
||||||
splitClient.setAttribute("imexshopid", bodyshop.imexshopid);
|
|
||||||
}
|
|
||||||
}, [bodyshop]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationContainer>
|
<NavigationContainer>
|
||||||
{currentUser.authorized === null ? (
|
{currentUser.authorized === null ? (
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import Constants from "expo-constants";
|
||||||
//import { AssetsSelector } from "expo-images-picker";
|
import * as ImagePicker from "expo-image-picker";
|
||||||
import { MediaType } from "expo-media-library";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { StyleSheet, Text, View } from "react-native";
|
import { StyleSheet, Text, View } from "react-native";
|
||||||
|
import { Button, SegmentedButtons } from "react-native-paper";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.analytics";
|
|
||||||
import { toggleDeleteAfterUpload } from "../../redux/app/app.actions";
|
import { toggleDeleteAfterUpload } from "../../redux/app/app.actions";
|
||||||
import {
|
import {
|
||||||
selectCurrentCameraJobId,
|
selectCurrentCameraJobId,
|
||||||
@@ -15,13 +14,9 @@ import {
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CameraSelectJob from "../camera-select-job/camera-select-job.component";
|
import CameraSelectJob from "../camera-select-job/camera-select-job.component";
|
||||||
import JobSpaceAvailable from "../job-space-available/job-space-available.component";
|
import JobSpaceAvailable from "../job-space-available/job-space-available.component";
|
||||||
import UploadProgressLocal from "../upload-progress-local/upload-progress-local.component";
|
|
||||||
import UploadDeleteSwitch from "../upload-delete-switch/upload-delete-switch.component";
|
import UploadDeleteSwitch from "../upload-delete-switch/upload-delete-switch.component";
|
||||||
|
import UploadProgressLocal from "../upload-progress-local/upload-progress-local.component";
|
||||||
import UploadProgress from "../upload-progress/upload-progress.component";
|
import UploadProgress from "../upload-progress/upload-progress.component";
|
||||||
import { SegmentedButtons } from "react-native-paper";
|
|
||||||
import * as ImagePicker from "expo-image-picker";
|
|
||||||
import { Button } from "react-native-paper";
|
|
||||||
// import * as MediaLibrary from "expo-media-library";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
selectedCameraJobId: selectCurrentCameraJobId,
|
selectedCameraJobId: selectCurrentCameraJobId,
|
||||||
@@ -43,15 +38,11 @@ export function ImageBrowserScreen({
|
|||||||
const [uploads, setUploads] = useState(null);
|
const [uploads, setUploads] = useState(null);
|
||||||
const [density, setDensity] = useState(3);
|
const [density, setDensity] = useState(3);
|
||||||
const [tick, setTick] = useState(0);
|
const [tick, setTick] = useState(0);
|
||||||
// const [medialLibraryPermissionStatus, requestmediaLibraryPermission] =
|
|
||||||
// ImagePicker.useMediaLibraryPermissions();
|
|
||||||
|
|
||||||
const forceRerender = useCallback(() => {
|
const forceRerender = useCallback(() => {
|
||||||
setTick((tick) => tick + 1);
|
setTick((tick) => tick + 1);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [percentage, setPercentage] = useState(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
if (Constants.platform.ios) {
|
if (Constants.platform.ios) {
|
||||||
@@ -77,11 +68,6 @@ export function ImageBrowserScreen({
|
|||||||
});
|
});
|
||||||
setUploads(result.assets);
|
setUploads(result.assets);
|
||||||
};
|
};
|
||||||
const onDone = (data) => {
|
|
||||||
logImEXEvent("imexmobile_upload_documents", { count: data.length });
|
|
||||||
|
|
||||||
if (data.length !== 0) setUploads(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.flex, styles.container]}>
|
<View style={[styles.flex, styles.container]}>
|
||||||
@@ -97,25 +83,6 @@ export function ImageBrowserScreen({
|
|||||||
)}
|
)}
|
||||||
<UploadDeleteSwitch />
|
<UploadDeleteSwitch />
|
||||||
|
|
||||||
<SegmentedButtons
|
|
||||||
value={density}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setDensity(value);
|
|
||||||
forceRerender();
|
|
||||||
}}
|
|
||||||
buttons={[
|
|
||||||
{
|
|
||||||
value: 4,
|
|
||||||
label: "Small",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 3,
|
|
||||||
label: "Normal",
|
|
||||||
},
|
|
||||||
{ value: 2, label: "Large" },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{!selectedCameraJobId && (
|
{!selectedCameraJobId && (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -156,7 +123,6 @@ const styles = StyleSheet.create({
|
|||||||
buttonStyle: {
|
buttonStyle: {
|
||||||
//backgroundColor: "tomato",
|
//backgroundColor: "tomato",
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-native/no-color-literals
|
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: "dodgerblue",
|
color: "dodgerblue",
|
||||||
},
|
},
|
||||||
25
components/external-link.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Href, Link } from 'expo-router';
|
||||||
|
import { openBrowserAsync, WebBrowserPresentationStyle } from 'expo-web-browser';
|
||||||
|
import { type ComponentProps } from 'react';
|
||||||
|
|
||||||
|
type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: Href & string };
|
||||||
|
|
||||||
|
export function ExternalLink({ href, ...rest }: Props) {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
{...rest}
|
||||||
|
href={href}
|
||||||
|
onPress={async (event) => {
|
||||||
|
if (process.env.EXPO_OS !== 'web') {
|
||||||
|
// Prevent the default behavior of linking to the default browser on native.
|
||||||
|
event.preventDefault();
|
||||||
|
// Open the link in an in-app browser.
|
||||||
|
await openBrowserAsync(href, {
|
||||||
|
presentationStyle: WebBrowserPresentationStyle.AUTOMATIC,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
18
components/haptic-tab.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs';
|
||||||
|
import { PlatformPressable } from '@react-navigation/elements';
|
||||||
|
import * as Haptics from 'expo-haptics';
|
||||||
|
|
||||||
|
export function HapticTab(props: BottomTabBarButtonProps) {
|
||||||
|
return (
|
||||||
|
<PlatformPressable
|
||||||
|
{...props}
|
||||||
|
onPressIn={(ev) => {
|
||||||
|
if (process.env.EXPO_OS === 'ios') {
|
||||||
|
// Add a soft haptic feedback when pressing down on the tabs.
|
||||||
|
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
||||||
|
}
|
||||||
|
props.onPressIn?.(ev);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
19
components/hello-wave.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Animated from 'react-native-reanimated';
|
||||||
|
|
||||||
|
export function HelloWave() {
|
||||||
|
return (
|
||||||
|
<Animated.Text
|
||||||
|
style={{
|
||||||
|
fontSize: 28,
|
||||||
|
lineHeight: 32,
|
||||||
|
marginTop: -6,
|
||||||
|
animationName: {
|
||||||
|
'50%': { transform: [{ rotate: '25deg' }] },
|
||||||
|
},
|
||||||
|
animationIterationCount: 4,
|
||||||
|
animationDuration: '300ms',
|
||||||
|
}}>
|
||||||
|
👋
|
||||||
|
</Animated.Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
79
components/parallax-scroll-view.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import Animated, {
|
||||||
|
interpolate,
|
||||||
|
useAnimatedRef,
|
||||||
|
useAnimatedStyle,
|
||||||
|
useScrollOffset,
|
||||||
|
} from 'react-native-reanimated';
|
||||||
|
|
||||||
|
import { ThemedView } from '@/components/themed-view';
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
import { useThemeColor } from '@/hooks/use-theme-color';
|
||||||
|
|
||||||
|
const HEADER_HEIGHT = 250;
|
||||||
|
|
||||||
|
type Props = PropsWithChildren<{
|
||||||
|
headerImage: ReactElement;
|
||||||
|
headerBackgroundColor: { dark: string; light: string };
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export default function ParallaxScrollView({
|
||||||
|
children,
|
||||||
|
headerImage,
|
||||||
|
headerBackgroundColor,
|
||||||
|
}: Props) {
|
||||||
|
const backgroundColor = useThemeColor({}, 'background');
|
||||||
|
const colorScheme = useColorScheme() ?? 'light';
|
||||||
|
const scrollRef = useAnimatedRef<Animated.ScrollView>();
|
||||||
|
const scrollOffset = useScrollOffset(scrollRef);
|
||||||
|
const headerAnimatedStyle = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
translateY: interpolate(
|
||||||
|
scrollOffset.value,
|
||||||
|
[-HEADER_HEIGHT, 0, HEADER_HEIGHT],
|
||||||
|
[-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Animated.ScrollView
|
||||||
|
ref={scrollRef}
|
||||||
|
style={{ backgroundColor, flex: 1 }}
|
||||||
|
scrollEventThrottle={16}>
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.header,
|
||||||
|
{ backgroundColor: headerBackgroundColor[colorScheme] },
|
||||||
|
headerAnimatedStyle,
|
||||||
|
]}>
|
||||||
|
{headerImage}
|
||||||
|
</Animated.View>
|
||||||
|
<ThemedView style={styles.content}>{children}</ThemedView>
|
||||||
|
</Animated.ScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
height: HEADER_HEIGHT,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 32,
|
||||||
|
gap: 16,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
});
|
||||||
60
components/themed-text.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { StyleSheet, Text, type TextProps } from 'react-native';
|
||||||
|
|
||||||
|
import { useThemeColor } from '@/hooks/use-theme-color';
|
||||||
|
|
||||||
|
export type ThemedTextProps = TextProps & {
|
||||||
|
lightColor?: string;
|
||||||
|
darkColor?: string;
|
||||||
|
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ThemedText({
|
||||||
|
style,
|
||||||
|
lightColor,
|
||||||
|
darkColor,
|
||||||
|
type = 'default',
|
||||||
|
...rest
|
||||||
|
}: ThemedTextProps) {
|
||||||
|
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
{ color },
|
||||||
|
type === 'default' ? styles.default : undefined,
|
||||||
|
type === 'title' ? styles.title : undefined,
|
||||||
|
type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
|
||||||
|
type === 'subtitle' ? styles.subtitle : undefined,
|
||||||
|
type === 'link' ? styles.link : undefined,
|
||||||
|
style,
|
||||||
|
]}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
default: {
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: 24,
|
||||||
|
},
|
||||||
|
defaultSemiBold: {
|
||||||
|
fontSize: 16,
|
||||||
|
lineHeight: 24,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
lineHeight: 32,
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
lineHeight: 30,
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#0a7ea4',
|
||||||
|
},
|
||||||
|
});
|
||||||
14
components/themed-view.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { View, type ViewProps } from 'react-native';
|
||||||
|
|
||||||
|
import { useThemeColor } from '@/hooks/use-theme-color';
|
||||||
|
|
||||||
|
export type ThemedViewProps = ViewProps & {
|
||||||
|
lightColor?: string;
|
||||||
|
darkColor?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
|
||||||
|
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||||
|
|
||||||
|
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
|
||||||
|
}
|
||||||
45
components/ui/collapsible.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { PropsWithChildren, useState } from 'react';
|
||||||
|
import { StyleSheet, TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
|
import { ThemedText } from '@/components/themed-text';
|
||||||
|
import { ThemedView } from '@/components/themed-view';
|
||||||
|
import { IconSymbol } from '@/components/ui/icon-symbol';
|
||||||
|
import { Colors } from '@/constants/theme';
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
|
||||||
|
export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const theme = useColorScheme() ?? 'light';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemedView>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.heading}
|
||||||
|
onPress={() => setIsOpen((value) => !value)}
|
||||||
|
activeOpacity={0.8}>
|
||||||
|
<IconSymbol
|
||||||
|
name="chevron.right"
|
||||||
|
size={18}
|
||||||
|
weight="medium"
|
||||||
|
color={theme === 'light' ? Colors.light.icon : Colors.dark.icon}
|
||||||
|
style={{ transform: [{ rotate: isOpen ? '90deg' : '0deg' }] }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ThemedText type="defaultSemiBold">{title}</ThemedText>
|
||||||
|
</TouchableOpacity>
|
||||||
|
{isOpen && <ThemedView style={styles.content}>{children}</ThemedView>}
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
heading: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 6,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
marginTop: 6,
|
||||||
|
marginLeft: 24,
|
||||||
|
},
|
||||||
|
});
|
||||||
32
components/ui/icon-symbol.ios.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols';
|
||||||
|
import { StyleProp, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
export function IconSymbol({
|
||||||
|
name,
|
||||||
|
size = 24,
|
||||||
|
color,
|
||||||
|
style,
|
||||||
|
weight = 'regular',
|
||||||
|
}: {
|
||||||
|
name: SymbolViewProps['name'];
|
||||||
|
size?: number;
|
||||||
|
color: string;
|
||||||
|
style?: StyleProp<ViewStyle>;
|
||||||
|
weight?: SymbolWeight;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SymbolView
|
||||||
|
weight={weight}
|
||||||
|
tintColor={color}
|
||||||
|
resizeMode="scaleAspectFit"
|
||||||
|
name={name}
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
},
|
||||||
|
style,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
components/ui/icon-symbol.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Fallback for using MaterialIcons on Android and web.
|
||||||
|
|
||||||
|
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||||
|
import { SymbolWeight, SymbolViewProps } from 'expo-symbols';
|
||||||
|
import { ComponentProps } from 'react';
|
||||||
|
import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native';
|
||||||
|
|
||||||
|
type IconMapping = Record<SymbolViewProps['name'], ComponentProps<typeof MaterialIcons>['name']>;
|
||||||
|
type IconSymbolName = keyof typeof MAPPING;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add your SF Symbols to Material Icons mappings here.
|
||||||
|
* - see Material Icons in the [Icons Directory](https://icons.expo.fyi).
|
||||||
|
* - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app.
|
||||||
|
*/
|
||||||
|
const MAPPING = {
|
||||||
|
'house.fill': 'home',
|
||||||
|
'paperplane.fill': 'send',
|
||||||
|
'chevron.left.forwardslash.chevron.right': 'code',
|
||||||
|
'chevron.right': 'chevron-right',
|
||||||
|
} as IconMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web.
|
||||||
|
* This ensures a consistent look across platforms, and optimal resource usage.
|
||||||
|
* Icon `name`s are based on SF Symbols and require manual mapping to Material Icons.
|
||||||
|
*/
|
||||||
|
export function IconSymbol({
|
||||||
|
name,
|
||||||
|
size = 24,
|
||||||
|
color,
|
||||||
|
style,
|
||||||
|
}: {
|
||||||
|
name: IconSymbolName;
|
||||||
|
size?: number;
|
||||||
|
color: string | OpaqueColorValue;
|
||||||
|
style?: StyleProp<TextStyle>;
|
||||||
|
weight?: SymbolWeight;
|
||||||
|
}) {
|
||||||
|
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
|
||||||
|
}
|
||||||
53
constants/theme.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Below are the colors that are used in the app. The colors are defined in the light and dark mode.
|
||||||
|
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
|
const tintColorLight = '#0a7ea4';
|
||||||
|
const tintColorDark = '#fff';
|
||||||
|
|
||||||
|
export const Colors = {
|
||||||
|
light: {
|
||||||
|
text: '#11181C',
|
||||||
|
background: '#fff',
|
||||||
|
tint: tintColorLight,
|
||||||
|
icon: '#687076',
|
||||||
|
tabIconDefault: '#687076',
|
||||||
|
tabIconSelected: tintColorLight,
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
text: '#ECEDEE',
|
||||||
|
background: '#151718',
|
||||||
|
tint: tintColorDark,
|
||||||
|
icon: '#9BA1A6',
|
||||||
|
tabIconDefault: '#9BA1A6',
|
||||||
|
tabIconSelected: tintColorDark,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Fonts = Platform.select({
|
||||||
|
ios: {
|
||||||
|
/** iOS `UIFontDescriptorSystemDesignDefault` */
|
||||||
|
sans: 'system-ui',
|
||||||
|
/** iOS `UIFontDescriptorSystemDesignSerif` */
|
||||||
|
serif: 'ui-serif',
|
||||||
|
/** iOS `UIFontDescriptorSystemDesignRounded` */
|
||||||
|
rounded: 'ui-rounded',
|
||||||
|
/** iOS `UIFontDescriptorSystemDesignMonospaced` */
|
||||||
|
mono: 'ui-monospace',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
sans: 'normal',
|
||||||
|
serif: 'serif',
|
||||||
|
rounded: 'normal',
|
||||||
|
mono: 'monospace',
|
||||||
|
},
|
||||||
|
web: {
|
||||||
|
sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
|
||||||
|
serif: "Georgia, 'Times New Roman', serif",
|
||||||
|
rounded: "'SF Pro Rounded', 'Hiragino Maru Gothic ProN', Meiryo, 'MS PGothic', sans-serif",
|
||||||
|
mono: "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// https://docs.expo.dev/guides/using-eslint/
|
// https://docs.expo.dev/guides/using-eslint/
|
||||||
const { defineConfig } = require('eslint/config');
|
const { defineConfig } = require('eslint/config');
|
||||||
const expoConfig = require("eslint-config-expo/flat");
|
const expoConfig = require('eslint-config-expo/flat');
|
||||||
|
|
||||||
module.exports = defineConfig([
|
module.exports = defineConfig([
|
||||||
expoConfig,
|
expoConfig,
|
||||||
{
|
{
|
||||||
ignores: ["dist/*"],
|
ignores: ['dist/*'],
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
1
hooks/use-color-scheme.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { useColorScheme } from 'react-native';
|
||||||
21
hooks/use-color-scheme.web.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useColorScheme as useRNColorScheme } from 'react-native';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To support static rendering, this value needs to be re-calculated on the client side for web
|
||||||
|
*/
|
||||||
|
export function useColorScheme() {
|
||||||
|
const [hasHydrated, setHasHydrated] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHasHydrated(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const colorScheme = useRNColorScheme();
|
||||||
|
|
||||||
|
if (hasHydrated) {
|
||||||
|
return colorScheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'light';
|
||||||
|
}
|
||||||
21
hooks/use-theme-color.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Learn more about light and dark modes:
|
||||||
|
* https://docs.expo.dev/guides/color-schemes/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Colors } from '@/constants/theme';
|
||||||
|
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||||
|
|
||||||
|
export function useThemeColor(
|
||||||
|
props: { light?: string; dark?: string },
|
||||||
|
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
|
||||||
|
) {
|
||||||
|
const theme = useColorScheme() ?? 'light';
|
||||||
|
const colorFromProps = props[theme];
|
||||||
|
|
||||||
|
if (colorFromProps) {
|
||||||
|
return colorFromProps;
|
||||||
|
} else {
|
||||||
|
return Colors[theme][colorName];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This replaces `const { getDefaultConfig } = require('expo/metro-config');`
|
|
||||||
const { getSentryExpoConfig } = require('@sentry/react-native/metro');
|
|
||||||
|
|
||||||
// This replaces `const config = getDefaultConfig(__dirname);`
|
|
||||||
const config = getSentryExpoConfig(__dirname);
|
|
||||||
|
|
||||||
module.exports = config;
|
|
||||||
2612
package-lock.json
generated
101
package.json
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"main": "node_modules/expo/AppEntry.js",
|
"private": true,
|
||||||
|
"name": "imexmobile",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "expo-router/entry",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start",
|
"start": "expo start",
|
||||||
"android": "expo run:android",
|
"android": "expo run:android",
|
||||||
@@ -17,44 +20,44 @@
|
|||||||
"lint": "expo lint"
|
"lint": "expo lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.12.11",
|
|
||||||
"@babel/preset-env": "7.28.3",
|
|
||||||
"@expo/vector-icons": "^15.0.2",
|
"@expo/vector-icons": "^15.0.2",
|
||||||
"@logrocket/react-native": "^1.57.3",
|
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||||
"@react-native-async-storage/async-storage": "2.2.0",
|
"@react-navigation/elements": "^2.6.3",
|
||||||
"@react-native-community/cli-debugger-ui": "^15.1.3",
|
"@react-navigation/native": "^7.1.8",
|
||||||
"@react-native-community/datetimepicker": "8.4.4",
|
"expo": "~54.0.12",
|
||||||
"@react-native-community/masked-view": "^0.1.11",
|
|
||||||
"@react-navigation/bottom-tabs": "^7.4.7",
|
|
||||||
"@react-navigation/drawer": "^7.5.8",
|
|
||||||
"@react-navigation/native": "^7.1.17",
|
|
||||||
"@react-navigation/native-stack": "^7.3.26",
|
|
||||||
"@react-navigation/stack": "^7.4.8",
|
|
||||||
"@reduxjs/toolkit": "^2.9.0",
|
|
||||||
"@sentry/react-native": "~7.1.0",
|
|
||||||
"@splitsoftware/splitio-react-native": "^1.3.0",
|
|
||||||
"axios": "^1.12.2",
|
|
||||||
"cloudinary-core": "^2.14.0",
|
|
||||||
"dinero.js": "^1.9.1",
|
|
||||||
"expo": "^54.0.12",
|
|
||||||
"expo-application": "~7.0.7",
|
|
||||||
"expo-av": "~16.0.7",
|
|
||||||
"expo-build-properties": "~1.0.9",
|
|
||||||
"expo-constants": "~18.0.9",
|
"expo-constants": "~18.0.9",
|
||||||
"expo-dev-client": "~6.0.13",
|
|
||||||
"expo-device": "~8.0.9",
|
|
||||||
"expo-file-system": "~19.0.16",
|
|
||||||
"expo-font": "~14.0.8",
|
"expo-font": "~14.0.8",
|
||||||
"expo-image-manipulator": "~14.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
|
"expo-image": "~3.0.8",
|
||||||
|
"expo-linking": "~8.0.8",
|
||||||
|
"expo-router": "~6.0.10",
|
||||||
|
"expo-splash-screen": "~31.0.10",
|
||||||
|
"expo-status-bar": "~3.0.8",
|
||||||
|
"expo-symbols": "~1.0.7",
|
||||||
|
"expo-system-ui": "~6.0.7",
|
||||||
|
"expo-web-browser": "~15.0.8",
|
||||||
|
"react": "19.1.0",
|
||||||
|
"react-dom": "19.1.0",
|
||||||
|
"react-native": "0.81.4",
|
||||||
|
"react-native-gesture-handler": "~2.28.0",
|
||||||
|
"react-native-worklets": "0.5.1",
|
||||||
|
"react-native-reanimated": "~4.1.1",
|
||||||
|
"react-native-safe-area-context": "~5.6.0",
|
||||||
|
"react-native-screens": "~4.16.0",
|
||||||
|
"react-native-web": "~0.21.0",
|
||||||
|
"expo-application": "~7.0.7",
|
||||||
|
"@apollo/client": "^3.12.11",
|
||||||
|
"@react-native-async-storage/async-storage": "2.2.0",
|
||||||
|
"@reduxjs/toolkit": "^2.9.0",
|
||||||
|
"dinero.js": "^1.9.1",
|
||||||
|
"axios": "^1.12.2",
|
||||||
|
"expo-dev-client": "~6.0.13",
|
||||||
|
"expo-file-system": "~19.0.16",
|
||||||
"expo-image-picker": "~17.0.8",
|
"expo-image-picker": "~17.0.8",
|
||||||
"expo-images-picker": "^2.5.1",
|
|
||||||
"expo-localization": "~17.0.7",
|
"expo-localization": "~17.0.7",
|
||||||
"expo-media-library": "~18.2.0",
|
"expo-media-library": "~18.2.0",
|
||||||
"expo-notifications": "~0.32.12",
|
"expo-notifications": "~0.32.12",
|
||||||
"expo-status-bar": "~3.0.8",
|
|
||||||
"expo-system-ui": "~6.0.7",
|
|
||||||
"expo-updates": "~29.0.12",
|
"expo-updates": "~29.0.12",
|
||||||
"expo-video-thumbnails": "~10.0.7",
|
|
||||||
"firebase": "^12.3.0",
|
"firebase": "^12.3.0",
|
||||||
"formik": "^2.4.6",
|
"formik": "^2.4.6",
|
||||||
"graphql": "^16.11.0",
|
"graphql": "^16.11.0",
|
||||||
@@ -65,31 +68,9 @@
|
|||||||
"mime": "^4.1.0",
|
"mime": "^4.1.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"normalize-url": "^8.1.0",
|
"normalize-url": "^8.1.0",
|
||||||
"react": "19.1.0",
|
|
||||||
"react-dom": "19.1.0",
|
|
||||||
"react-i18next": "^16.0.0",
|
"react-i18next": "^16.0.0",
|
||||||
"react-is": ">=19.2.0",
|
|
||||||
"react-native": "0.81.4",
|
|
||||||
"react-native-draggable-flatlist": "^4.0.3",
|
|
||||||
"react-native-element-dropdown": "^2.12.4",
|
|
||||||
"react-native-event-source": "^1.1.0",
|
|
||||||
"react-native-gesture-handler": "~2.28.0",
|
|
||||||
"react-native-image-gallery": "^2.1.5",
|
|
||||||
"react-native-image-viewing": "^0.2.2",
|
"react-native-image-viewing": "^0.2.2",
|
||||||
"react-native-indicators": "^0.17.0",
|
|
||||||
"react-native-modal-datetime-picker": "^18.0.0",
|
|
||||||
"react-native-pager-view": "6.9.1",
|
|
||||||
"react-native-paper": "^5.14.5",
|
|
||||||
"react-native-progress": "^5.0.1",
|
|
||||||
"react-native-reanimated": "~4.1.2",
|
|
||||||
"react-native-safe-area-context": "~5.6.1",
|
|
||||||
"react-native-screens": "~4.16.0",
|
|
||||||
"react-native-svg": "15.12.1",
|
|
||||||
"react-native-tab-view": "4.1.3",
|
"react-native-tab-view": "4.1.3",
|
||||||
"react-native-toast-message": "^2.3.3",
|
|
||||||
"react-native-vector-icons": "*",
|
|
||||||
"react-native-web": "^0.21.1",
|
|
||||||
"react-native-worklets": "0.5.1",
|
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
@@ -98,13 +79,9 @@
|
|||||||
"reselect": "^5.1.1"
|
"reselect": "^5.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.28.4",
|
"@types/react": "~19.1.0",
|
||||||
"babel-preset-expo": "~54.0.3",
|
"typescript": "~5.9.2",
|
||||||
"eslint": "^9.0.0",
|
"eslint": "^9.25.0",
|
||||||
"eslint-config-expo": "~10.0.0",
|
"eslint-config-expo": "~10.0.0"
|
||||||
"typescript": "^5.9.3"
|
}
|
||||||
},
|
|
||||||
"private": true,
|
|
||||||
"name": "imexmobile",
|
|
||||||
"version": "1.0.0"
|
|
||||||
}
|
}
|
||||||
|
|||||||
15
tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"extends": "expo/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ export const handleLocalUpload = async ({
|
|||||||
const { jobid } = context;
|
const { jobid } = context;
|
||||||
const bodyshop = store.getState().user.bodyshop;
|
const bodyshop = store.getState().user.bodyshop;
|
||||||
try {
|
try {
|
||||||
var options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "multipart/form-data",
|
"Content-Type": "multipart/form-data",
|
||||||
ims_token: bodyshop.localmediatoken,
|
ims_token: bodyshop.localmediatoken,
|
||||||
|
|||||||