Files
imexmobile/app/_layout.tsx
2025-11-04 13:53:52 -08:00

231 lines
6.7 KiB
TypeScript

import UploadProgress from "@/components/upload-progress/upload-progress";
import { checkUserSession } from "@/redux/user/user.actions";
import { selectBodyshop, selectCurrentUser } from "@/redux/user/user.selectors";
import { ApolloProvider } from "@apollo/client";
import { loadDevMessages, loadErrorMessages } from "@apollo/client/dev";
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import {
DarkTheme,
DefaultTheme,
ThemeProvider,
} from "@react-navigation/native";
import * as Sentry from "@sentry/react-native";
import * as Notifications from "expo-notifications";
import { Stack } from "expo-router";
import {
Icon,
Label,
NativeTabs,
VectorIcon,
} from "expo-router/unstable-native-tabs";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Platform, View } from "react-native";
import {
ActivityIndicator,
Provider as PaperProvider,
} from "react-native-paper";
import { connect, Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { createStructuredSelector } from "reselect";
import { client } from "../graphql/client";
import { useTheme as usePaperTheme } from "../hooks/useTheme";
import { persistor, store } from "../redux/store";
import "../translations/i18n";
import { registerForPushNotificationsAsync } from "../util/notificationHandler";
loadDevMessages();
loadErrorMessages();
Sentry.init({
dsn: "https://8d6c3de1940a4e4f8b81cf4d2150bdea@o492140.ingest.us.sentry.io/5558869",
// Adds more context data to events (IP address, cookies, user, etc.)
// For more information, visit: https://docs.sentry.io/platforms/react-native/data-management/data-collected/
sendDefaultPii: true,
// Enable Logs
enableLogs: true,
// Configure Session Replay
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1,
integrations: [
Sentry.mobileReplayIntegration(),
Sentry.feedbackIntegration(),
],
ignoreErrors: [/.*Network Error.*/i],
});
function AuthenticatedLayout() {
const { t } = useTranslation();
const paperTheme = usePaperTheme();
return (
<ThemeProvider
value={paperTheme.theme === "dark" ? DarkTheme : DefaultTheme}
>
<NativeTabs
minimizeBehavior="onScrollDown"
disableTransparentOnScrollEdge
{...(paperTheme.theme === "dark"
? {
backgroundColor: DarkTheme.colors.background,
tintColor: DarkTheme.colors.text,
indicatorColor: DarkTheme.colors.border,
}
: {})}
>
<NativeTabs.Trigger name="jobs">
<Label>{t("joblist.labels.activejobs")}</Label>
{Platform.select({
ios: <Icon sf="checklist" drawable="custom_android_drawable" />,
android: (
<Icon
src={<VectorIcon family={MaterialIcons} name="checklist" />}
/>
),
})}
</NativeTabs.Trigger>
{/* <NativeTabs.Trigger name="production">
{Platform.select({
ios: <Icon sf="gear" drawable="custom_android_drawable" />,
android: (
<Icon src={<VectorIcon family={MaterialIcons} name="start" />} />
),
})}
<Label>{t("production.titles.production")}</Label>
</NativeTabs.Trigger> */}
<NativeTabs.Trigger name="settings">
{Platform.select({
ios: <Icon sf="gear" drawable="custom_android_drawable" />,
android: (
<Icon
src={<VectorIcon family={MaterialIcons} name="settings" />}
/>
),
})}
<Label>{t("settings.titles.settings")}</Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="search" role="search">
{Platform.select({
//ios: <Icon sf="checklist" drawable="custom_android_drawable" />,
android: (
<Icon src={<VectorIcon family={MaterialIcons} name="search" />} />
),
})}
<Label>Search</Label>
</NativeTabs.Trigger>
</NativeTabs>
</ThemeProvider>
);
}
function UnauthenticatedLayout() {
const paperTheme = usePaperTheme();
return (
<ThemeProvider
value={paperTheme.theme === "dark" ? DarkTheme : DefaultTheme}
>
<Stack>
<Stack.Screen name="sign-in" options={{ headerShown: false }} />
</Stack>
</ThemeProvider>
);
}
function LoadingLayout() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator size="large" />
</View>
);
}
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch: any) => ({
checkUserSession: () => dispatch(checkUserSession()),
});
function AppContent({ currentUser, checkUserSession, bodyshop }: any) {
useEffect(() => {
checkUserSession();
}, [checkUserSession]);
useEffect(() => {
registerForPushNotificationsAsync()
.then((token) => console.log("Expo Push Token:", token))
.catch((error: any) =>
console.log("Error getting Expo Push Token:", error)
);
const notificationListener = Notifications.addNotificationReceivedListener(
async (notification) => {
console.log("Notification received:", notification);
Notifications.setBadgeCountAsync(
(await Notifications.getBadgeCountAsync()) + 1
);
}
);
const responseListener =
Notifications.addNotificationResponseReceivedListener((response) => {
console.log("Notification response received:", response);
});
//Clear the badges.
Notifications.setBadgeCountAsync(0);
return () => {
notificationListener.remove();
responseListener.remove();
};
}, []);
if (currentUser.authorized === null) {
return (
<ThemedLayout>
<LoadingLayout />
</ThemedLayout>
);
}
if (currentUser.authorized) {
return (
<ThemedLayout>
<UploadProgress />
<AuthenticatedLayout />
</ThemedLayout>
);
}
return (
<ThemedLayout>
<UnauthenticatedLayout />
</ThemedLayout>
);
}
function ThemedLayout({ children }: { children: React.ReactNode }) {
const themeToApply = usePaperTheme();
return <PaperProvider theme={themeToApply}>{children}</PaperProvider>;
}
const ConnectedAppContent = connect(
mapStateToProps,
mapDispatchToProps
)(AppContent);
function AppLayout() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
<ApolloProvider client={client}>
<ConnectedAppContent />
</ApolloProvider>
</PersistGate>
</Provider>
);
}
export default Sentry.wrap(AppLayout);