Clean up, reimplement native paper, and sign in screen.
This commit is contained in:
2
App.js
2
App.js
@@ -1,6 +1,6 @@
|
|||||||
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 { SafeAreaProvider } from "react-native-safe-area-context";
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
|
import { checkUserSession } from "@/redux/user/user.actions";
|
||||||
|
import { selectBodyshop, selectCurrentUser } from "@/redux/user/user.selectors";
|
||||||
import { ApolloProvider } from "@apollo/client";
|
import { ApolloProvider } from "@apollo/client";
|
||||||
|
import { Stack } from "expo-router";
|
||||||
import { Icon, Label, NativeTabs } from "expo-router/unstable-native-tabs";
|
import { Icon, Label, NativeTabs } from "expo-router/unstable-native-tabs";
|
||||||
|
import { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Provider } from "react-redux";
|
import { ActivityIndicator, View } from "react-native";
|
||||||
|
import { MD3LightTheme, Provider as PaperProvider } from "react-native-paper";
|
||||||
|
import { connect, Provider } from "react-redux";
|
||||||
import { PersistGate } from "redux-persist/integration/react";
|
import { PersistGate } from "redux-persist/integration/react";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
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";
|
||||||
|
|
||||||
export default function TabLayout() {
|
function AuthenticatedLayout() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
|
||||||
<PersistGate persistor={persistor}>
|
|
||||||
<ApolloProvider client={client}>
|
|
||||||
<NativeTabs>
|
<NativeTabs>
|
||||||
<NativeTabs.Trigger name="jobs">
|
<NativeTabs.Trigger name="jobs">
|
||||||
<Label>{t("joblist.labels.activejobs")}</Label>
|
<Label>{t("joblist.labels.activejobs")}</Label>
|
||||||
@@ -24,6 +27,67 @@ export default function TabLayout() {
|
|||||||
<Label>{t("settings.titles.settings")}</Label>
|
<Label>{t("settings.titles.settings")}</Label>
|
||||||
</NativeTabs.Trigger>
|
</NativeTabs.Trigger>
|
||||||
</NativeTabs>
|
</NativeTabs>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UnauthenticatedLayout() {
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<Stack.Screen name="sign-in" options={{ headerShown: false }} />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => ({
|
||||||
|
checkUserSession: () => dispatch(checkUserSession()),
|
||||||
|
});
|
||||||
|
function AppContent({ currentUser, checkUserSession, bodyshop }) {
|
||||||
|
useEffect(() => {
|
||||||
|
checkUserSession();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (currentUser.authorized === null) {
|
||||||
|
return <LoadingLayout />;
|
||||||
|
}
|
||||||
|
if (currentUser.authorized) {
|
||||||
|
return <AuthenticatedLayout />;
|
||||||
|
}
|
||||||
|
return <UnauthenticatedLayout />;
|
||||||
|
}
|
||||||
|
const ConnectedAppContent = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(AppContent);
|
||||||
|
|
||||||
|
const theme = {
|
||||||
|
...MD3LightTheme,
|
||||||
|
colors: {
|
||||||
|
...MD3LightTheme.colors,
|
||||||
|
primary: "#1890ff",
|
||||||
|
accent: "tomato",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AppLayout() {
|
||||||
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
<PersistGate persistor={persistor}>
|
||||||
|
<ApolloProvider client={client}>
|
||||||
|
<PaperProvider theme={theme}>
|
||||||
|
<ConnectedAppContent />
|
||||||
|
</PaperProvider>
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
|
import { selectCurrentUser } from "@/redux/user/user.selectors";
|
||||||
import { Redirect } from "expo-router";
|
import { Redirect } from "expo-router";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
|
||||||
export default function Index() {
|
const mapStateToProps = createStructuredSelector({
|
||||||
return <Redirect href={`/jobs`} />;
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
function Index({ currentUser }) {
|
||||||
|
if (currentUser.authorized) {
|
||||||
|
return <Redirect href="/" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Redirect href="/sign-in" />;
|
||||||
}
|
}
|
||||||
|
export default connect(mapStateToProps, null)(Index);
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { StyleSheet, Text, View } from 'react-native';
|
import SignOutButton from "@/components-old/sign-out-button/sign-out-button.component";
|
||||||
|
import { StyleSheet, Text, View } from "react-native";
|
||||||
export default function Tab() {
|
export default function Tab() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text>Tab [Home|Settings]</Text>
|
<Text>Tab [Home|Settings]</Text>
|
||||||
|
<SignOutButton />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -11,7 +12,7 @@ export default function Tab() {
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: "center",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
4
app/sign-in.tsx
Normal file
4
app/sign-in.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import SignIn from "@/components/sign-in/sign-in";
|
||||||
|
export default function SignInScreen() {
|
||||||
|
return <SignIn />;
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
@@ -1,25 +0,0 @@
|
|||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
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',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
53
components/sign-in/sign-in-error.jsx
Normal file
53
components/sign-in/sign-in-error.jsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { StyleSheet, View } from "react-native";
|
||||||
|
import { Text } from "react-native-paper";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectSignInError } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
signInError: selectSignInError,
|
||||||
|
});
|
||||||
|
|
||||||
|
function SignInError({ signInError }) {
|
||||||
|
const [errorText, setErrorText] = useState("");
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let text;
|
||||||
|
if (signInError && signInError.code) {
|
||||||
|
switch (signInError.code) {
|
||||||
|
case "auth/user-not-found":
|
||||||
|
text = t("signin.errors.wronginfo");
|
||||||
|
break;
|
||||||
|
case "auth/invalid-email":
|
||||||
|
text = t("signin.errors.wronginfo");
|
||||||
|
break;
|
||||||
|
case "auth/wrong-password":
|
||||||
|
text = t("signin.errors.wronginfo");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
text = signInError.code + " " + signInError.message;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setErrorText(text);
|
||||||
|
}
|
||||||
|
}, [t, signInError, setErrorText]);
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{errorText ? <Text style={localStyles.alert}>{errorText}</Text> : null}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(SignInError);
|
||||||
|
|
||||||
|
const localStyles = StyleSheet.create({
|
||||||
|
alert: {
|
||||||
|
color: "red",
|
||||||
|
textAlign: "center",
|
||||||
|
margin: 15,
|
||||||
|
padding: 15,
|
||||||
|
},
|
||||||
|
});
|
||||||
132
components/sign-in/sign-in.jsx
Normal file
132
components/sign-in/sign-in.jsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { Formik } from "formik";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Image, StyleSheet, View } from "react-native";
|
||||||
|
import { Button, Text, TextInput } from "react-native-paper";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { emailSignInStart } from "../../redux/user/user.actions";
|
||||||
|
import SignInError from "./sign-in-error";
|
||||||
|
|
||||||
|
import {
|
||||||
|
selectCurrentUser,
|
||||||
|
selectSigningIn,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
//import SignInErrorAlertComponent from "../sign-in-error-alert/sign-in-error-alert.component";
|
||||||
|
import Constants from "expo-constants";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
signingIn: selectSigningIn,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
emailSignInStart: (email, password) =>
|
||||||
|
dispatch(emailSignInStart({ email, password })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function SignIn({ emailSignInStart, signingIn }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const formSubmit = (values) => {
|
||||||
|
const { email, password } = values;
|
||||||
|
emailSignInStart(email, password);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View styles={styles.container}>
|
||||||
|
<View style={styles.imageContainer}>
|
||||||
|
<Image
|
||||||
|
style={styles.logo}
|
||||||
|
source={require("@/assets/images/logo192.png")}
|
||||||
|
/>
|
||||||
|
<Text variant="headlineLarge">{t("app.title")}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Formik initialValues={{ email: "", password: "" }} onSubmit={formSubmit}>
|
||||||
|
{({ handleChange, handleBlur, handleSubmit, values }) => (
|
||||||
|
<View style={styles.formContainer}>
|
||||||
|
<TextInput
|
||||||
|
label={t("signin.fields.email")}
|
||||||
|
placeholder={t("signin.fields.email")}
|
||||||
|
autoCapitalize="none"
|
||||||
|
mode="outlined"
|
||||||
|
autoComplete="email"
|
||||||
|
keyboardType="email-address"
|
||||||
|
onChangeText={handleChange("email")}
|
||||||
|
onBlur={handleBlur("email")}
|
||||||
|
value={values.email}
|
||||||
|
style={[styles.input]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
label={t("signin.fields.password")}
|
||||||
|
placeholder={t("signin.fields.password")}
|
||||||
|
secureTextEntry={true}
|
||||||
|
autoCorrect={false}
|
||||||
|
mode="outlined"
|
||||||
|
autoCapitalize="none"
|
||||||
|
onChangeText={handleChange("password")}
|
||||||
|
onBlur={handleBlur("password")}
|
||||||
|
value={values.password}
|
||||||
|
style={[styles.input]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SignInError />
|
||||||
|
<Button loading={signingIn} onPress={handleSubmit}>
|
||||||
|
{t("signin.actions.signin")}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
|
||||||
|
<Text style={styles.footer}>
|
||||||
|
{t("settings.labels.version", {
|
||||||
|
number: Constants.expoConfig.version,
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
|
imageContainer: {
|
||||||
|
display: "flex",
|
||||||
|
marginTop: 80,
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
maxWidth: "20%",
|
||||||
|
resizeMode: "contain",
|
||||||
|
},
|
||||||
|
formContainer: {
|
||||||
|
display: "flex",
|
||||||
|
width: "100%",
|
||||||
|
padding: 20,
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
// content: {
|
||||||
|
// display: "flex",
|
||||||
|
// flex: 1,
|
||||||
|
// },
|
||||||
|
// logo: { width: 100, height: 100 },
|
||||||
|
inputContainer: {
|
||||||
|
marginBottom: 20,
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
|
input: {},
|
||||||
|
footer: {
|
||||||
|
padding: 10,
|
||||||
|
alignSelf: "center",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(SignIn);
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
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',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
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} />;
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
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,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// 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} />;
|
|
||||||
}
|
|
||||||
222
package-lock.json
generated
222
package-lock.json
generated
@@ -11,6 +11,7 @@
|
|||||||
"@apollo/client": "^3.12.11",
|
"@apollo/client": "^3.12.11",
|
||||||
"@expo/vector-icons": "^15.0.2",
|
"@expo/vector-icons": "^15.0.2",
|
||||||
"@react-native-async-storage/async-storage": "2.2.0",
|
"@react-native-async-storage/async-storage": "2.2.0",
|
||||||
|
"@react-native-vector-icons/material-design-icons": "^12.3.0",
|
||||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||||
"@react-navigation/elements": "^2.6.3",
|
"@react-navigation/elements": "^2.6.3",
|
||||||
"@react-navigation/native": "^7.1.8",
|
"@react-navigation/native": "^7.1.8",
|
||||||
@@ -53,6 +54,7 @@
|
|||||||
"react-native": "0.81.4",
|
"react-native": "0.81.4",
|
||||||
"react-native-gesture-handler": "~2.28.0",
|
"react-native-gesture-handler": "~2.28.0",
|
||||||
"react-native-image-viewing": "^0.2.2",
|
"react-native-image-viewing": "^0.2.2",
|
||||||
|
"react-native-paper": "^5.14.5",
|
||||||
"react-native-reanimated": "~4.1.1",
|
"react-native-reanimated": "~4.1.1",
|
||||||
"react-native-safe-area-context": "~5.6.0",
|
"react-native-safe-area-context": "~5.6.0",
|
||||||
"react-native-screens": "~4.16.0",
|
"react-native-screens": "~4.16.0",
|
||||||
@@ -1606,6 +1608,28 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@callstack/react-theme-provider": {
|
||||||
|
"version": "3.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@callstack/react-theme-provider/-/react-theme-provider-3.0.9.tgz",
|
||||||
|
"integrity": "sha512-tTQ0uDSCL0ypeMa8T/E9wAZRGKWj8kXP7+6RYgPTfOPs9N07C9xM8P02GJ3feETap4Ux5S69D9nteq9mEj86NA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"deepmerge": "^3.2.0",
|
||||||
|
"hoist-non-react-statics": "^3.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@callstack/react-theme-provider/node_modules/deepmerge": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@egjs/hammerjs": {
|
"node_modules/@egjs/hammerjs": {
|
||||||
"version": "2.0.17",
|
"version": "2.0.17",
|
||||||
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
|
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
|
||||||
@@ -4317,6 +4341,132 @@
|
|||||||
"react-native": "^0.0.0-0 || >=0.65 <1.0"
|
"react-native": "^0.0.0-0 || >=0.65 <1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-native-vector-icons/common": {
|
||||||
|
"version": "12.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native-vector-icons/common/-/common-12.3.0.tgz",
|
||||||
|
"integrity": "sha512-5GMBcLBkA0MuciweYcrSyvi9fYGanfVnE2J+pwHx1QiaVgTaoCm4rylJgSS77MVI5qUiGh7aJpqq5afSz2U4bw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"find-up": "^7.0.0",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"plist": "^3.1.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rnvi-update-plist": "lib/commonjs/scripts/updatePlist.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@react-native-vector-icons/get-image": "^12.2.0",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@react-native-vector-icons/get-image": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native-vector-icons/common/node_modules/find-up": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^7.2.0",
|
||||||
|
"path-exists": "^5.0.0",
|
||||||
|
"unicorn-magic": "^0.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native-vector-icons/common/node_modules/locate-path": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native-vector-icons/common/node_modules/p-limit": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"yocto-queue": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native-vector-icons/common/node_modules/p-locate": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native-vector-icons/common/node_modules/path-exists": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native-vector-icons/common/node_modules/yocto-queue": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native-vector-icons/material-design-icons": {
|
||||||
|
"version": "12.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native-vector-icons/material-design-icons/-/material-design-icons-12.3.0.tgz",
|
||||||
|
"integrity": "sha512-0fut9zjUJtGWwjGQ0lbirmPnjMkou9vkBY3d3ZsaHqXCBgV3fGeOWuRZ17eDpsGy/9BTRtBRI85RYdFGhdcB4Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-native-vector-icons/common": "^12.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@react-native/assets-registry": {
|
"node_modules/@react-native/assets-registry": {
|
||||||
"version": "0.81.4",
|
"version": "0.81.4",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.4.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.4.tgz",
|
||||||
@@ -4911,7 +5061,6 @@
|
|||||||
"version": "19.1.17",
|
"version": "19.1.17",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz",
|
||||||
"integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==",
|
"integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
@@ -6843,7 +6992,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/data-view-buffer": {
|
"node_modules/data-view-buffer": {
|
||||||
@@ -12678,6 +12826,62 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-pager-view": {
|
||||||
|
"version": "6.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-6.9.1.tgz",
|
||||||
|
"integrity": "sha512-uUT0MMMbNtoSbxe9pRvdJJKEi9snjuJ3fXlZhG8F2vVMOBJVt/AFtqMPUHu9yMflmqOr08PewKzj9EPl/Yj+Gw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-paper": {
|
||||||
|
"version": "5.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.14.5.tgz",
|
||||||
|
"integrity": "sha512-eaIH5bUQjJ/mYm4AkI6caaiyc7BcHDwX6CqNDi6RIxfxfWxROsHpll1oBuwn/cFvknvA8uEAkqLk/vzVihI3AQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"workspaces": [
|
||||||
|
"example",
|
||||||
|
"docs"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@callstack/react-theme-provider": "^3.0.9",
|
||||||
|
"color": "^3.1.2",
|
||||||
|
"use-latest-callback": "^0.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*",
|
||||||
|
"react-native-safe-area-context": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-paper/node_modules/color": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.3",
|
||||||
|
"color-string": "^1.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-paper/node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-paper/node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/react-native-reanimated": {
|
"node_modules/react-native-reanimated": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.2.tgz",
|
||||||
@@ -14671,7 +14875,7 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
@@ -14779,6 +14983,18 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unicorn-magic": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unique-string": {
|
"node_modules/unique-string": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz",
|
||||||
|
|||||||
50
package.json
50
package.json
@@ -20,44 +20,36 @@
|
|||||||
"lint": "expo lint"
|
"lint": "expo lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@apollo/client": "^3.12.11",
|
||||||
"@expo/vector-icons": "^15.0.2",
|
"@expo/vector-icons": "^15.0.2",
|
||||||
|
"@react-native-async-storage/async-storage": "2.2.0",
|
||||||
|
"@react-native-vector-icons/material-design-icons": "^12.3.0",
|
||||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||||
"@react-navigation/elements": "^2.6.3",
|
"@react-navigation/elements": "^2.6.3",
|
||||||
"@react-navigation/native": "^7.1.8",
|
"@react-navigation/native": "^7.1.8",
|
||||||
|
"@reduxjs/toolkit": "^2.9.0",
|
||||||
|
"axios": "^1.12.2",
|
||||||
|
"dinero.js": "^1.9.1",
|
||||||
"expo": "~54.0.12",
|
"expo": "~54.0.12",
|
||||||
|
"expo-application": "~7.0.7",
|
||||||
"expo-constants": "~18.0.9",
|
"expo-constants": "~18.0.9",
|
||||||
|
"expo-dev-client": "~6.0.13",
|
||||||
|
"expo-file-system": "~19.0.16",
|
||||||
"expo-font": "~14.0.8",
|
"expo-font": "~14.0.8",
|
||||||
"expo-haptics": "~15.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
"expo-image": "~3.0.8",
|
"expo-image": "~3.0.8",
|
||||||
|
"expo-image-picker": "~17.0.8",
|
||||||
"expo-linking": "~8.0.8",
|
"expo-linking": "~8.0.8",
|
||||||
|
"expo-localization": "~17.0.7",
|
||||||
|
"expo-media-library": "~18.2.0",
|
||||||
|
"expo-notifications": "~0.32.12",
|
||||||
"expo-router": "~6.0.10",
|
"expo-router": "~6.0.10",
|
||||||
"expo-splash-screen": "~31.0.10",
|
"expo-splash-screen": "~31.0.10",
|
||||||
"expo-status-bar": "~3.0.8",
|
"expo-status-bar": "~3.0.8",
|
||||||
"expo-symbols": "~1.0.7",
|
"expo-symbols": "~1.0.7",
|
||||||
"expo-system-ui": "~6.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-localization": "~17.0.7",
|
|
||||||
"expo-media-library": "~18.2.0",
|
|
||||||
"expo-notifications": "~0.32.12",
|
|
||||||
"expo-updates": "~29.0.12",
|
"expo-updates": "~29.0.12",
|
||||||
|
"expo-web-browser": "~15.0.8",
|
||||||
"firebase": "^12.3.0",
|
"firebase": "^12.3.0",
|
||||||
"formik": "^2.4.6",
|
"formik": "^2.4.6",
|
||||||
"graphql": "^16.11.0",
|
"graphql": "^16.11.0",
|
||||||
@@ -68,9 +60,19 @@
|
|||||||
"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-native": "0.81.4",
|
||||||
|
"react-native-gesture-handler": "~2.28.0",
|
||||||
"react-native-image-viewing": "^0.2.2",
|
"react-native-image-viewing": "^0.2.2",
|
||||||
|
"react-native-paper": "^5.14.5",
|
||||||
|
"react-native-reanimated": "~4.1.1",
|
||||||
|
"react-native-safe-area-context": "~5.6.0",
|
||||||
|
"react-native-screens": "~4.16.0",
|
||||||
"react-native-tab-view": "4.1.3",
|
"react-native-tab-view": "4.1.3",
|
||||||
|
"react-native-web": "~0.21.0",
|
||||||
|
"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",
|
||||||
@@ -80,8 +82,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "~19.1.0",
|
"@types/react": "~19.1.0",
|
||||||
"typescript": "~5.9.2",
|
|
||||||
"eslint": "^9.25.0",
|
"eslint": "^9.25.0",
|
||||||
"eslint-config-expo": "~10.0.0"
|
"eslint-config-expo": "~10.0.0",
|
||||||
|
"typescript": "~5.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,6 +299,7 @@
|
|||||||
"errors": {
|
"errors": {
|
||||||
"emailformat": "The email you have entered is not formatted correctly. ",
|
"emailformat": "The email you have entered is not formatted correctly. ",
|
||||||
"usernotfound": "No user found.",
|
"usernotfound": "No user found.",
|
||||||
|
"wronginfo": "The email or password you entered is not correct.",
|
||||||
"wrongpassword": "The password you entered is not correct."
|
"wrongpassword": "The password you entered is not correct."
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
|
|||||||
Reference in New Issue
Block a user