From 2c2dfba45357bbd205a59145ae55a2779c911ae0 Mon Sep 17 00:00:00 2001 From: Lucas Faria Mendes Date: Fri, 14 Aug 2026 15:06:35 -0300 Subject: remake Signed-off-by: Lucas Faria Mendes --- src/app/(app)/(tabs)/_layout.tsx | 169 +++++++++++++++++++++++++++++++++++++ src/app/(app)/(tabs)/expenses.tsx | 2 + src/app/(app)/(tabs)/index.tsx | 2 + src/app/(app)/(tabs)/recurring.tsx | 2 + src/app/(app)/(tabs)/salary.tsx | 2 + src/app/(app)/_layout.tsx | 21 +++++ src/app/_layout.tsx | 54 ++++++++++++ src/app/biometrics.tsx | 120 ++++++++++++++++++++++++++ src/app/login.tsx | 144 +++++++++++++++++++++++++++++++ src/app/register.tsx | 142 +++++++++++++++++++++++++++++++ 10 files changed, 658 insertions(+) create mode 100644 src/app/(app)/(tabs)/_layout.tsx create mode 100644 src/app/(app)/(tabs)/expenses.tsx create mode 100644 src/app/(app)/(tabs)/index.tsx create mode 100644 src/app/(app)/(tabs)/recurring.tsx create mode 100644 src/app/(app)/(tabs)/salary.tsx create mode 100644 src/app/(app)/_layout.tsx create mode 100644 src/app/_layout.tsx create mode 100644 src/app/biometrics.tsx create mode 100644 src/app/login.tsx create mode 100644 src/app/register.tsx (limited to 'src/app') diff --git a/src/app/(app)/(tabs)/_layout.tsx b/src/app/(app)/(tabs)/_layout.tsx new file mode 100644 index 0000000..5033232 --- /dev/null +++ b/src/app/(app)/(tabs)/_layout.tsx @@ -0,0 +1,169 @@ +import React from 'react'; +import { Platform, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { Tabs } from 'expo-router'; +import { BlurView } from 'expo-blur'; +import { Feather } from '@expo/vector-icons'; +import Animated, { + useAnimatedStyle, + withSpring, + withTiming, +} from 'react-native-reanimated'; +import * as Haptics from 'expo-haptics'; +import { Colors, Spacing } from '@/constants/theme'; + +function TabBarIcon({ + name, + isFocused, +}: { + name: keyof typeof Feather.glyphMap; + isFocused: boolean; +}) { + const animatedIconStyle = useAnimatedStyle(() => { + return { + transform: [ + { scale: withSpring(isFocused ? 1.15 : 1, { damping: 12, stiffness: 300 }) }, + ], + }; + }); + + const animatedIndicatorStyle = useAnimatedStyle(() => { + return { + opacity: withTiming(isFocused ? 1 : 0, { duration: 200 }), + transform: [ + { scale: withSpring(isFocused ? 1 : 0.5, { damping: 15, stiffness: 300 }) }, + ], + }; + }); + + return ( + + + + + + + ); +} + +function CustomTabBar({ state, descriptors, navigation }: any) { + return ( + + + {state.routes.map((route: any, index: number) => { + const { options } = descriptors[route.key]; + const isFocused = state.index === index; + + const onPress = () => { + const event = navigation.emit({ + type: 'tabPress', + target: route.key, + canPreventDefault: true, + }); + + if (!isFocused && !event.defaultPrevented) { + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); + navigation.navigate(route.name); + } + }; + + let iconName: keyof typeof Feather.glyphMap = 'home'; + if (route.name === 'index') iconName = 'pie-chart'; + else if (route.name === 'salary') iconName = 'dollar-sign'; + else if (route.name === 'recurring') iconName = 'repeat'; + else if (route.name === 'expenses') iconName = 'plus-square'; + + return ( + + + + ); + })} + + + ); +} + +export default function TabsLayout() { + return ( + } + screenOptions={{ + headerShown: false, + }} + > + + + + + + ); +} + +const styles = StyleSheet.create({ + tabBarContainer: { + position: 'absolute', + bottom: Platform.OS === 'ios' ? 30 : 20, + left: 24, + right: 24, + borderRadius: 32, + ...Platform.select({ + ios: { + shadowColor: '#000', + shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.5, + shadowRadius: 16, + }, + android: { + elevation: 10, + }, + }), + }, + blurView: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-around', + borderRadius: 32, + backgroundColor: 'rgba(22, 27, 34, 0.75)', + borderWidth: 1, + borderColor: 'rgba(255, 255, 255, 0.08)', + overflow: 'hidden', + height: 64, + paddingHorizontal: Spacing.sm, + }, + tabItem: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + height: '100%', + }, + iconContainer: { + alignItems: 'center', + justifyContent: 'center', + width: 44, + height: 44, + }, + indicator: { + position: 'absolute', + bottom: -2, + width: 4, + height: 4, + borderRadius: 2, + backgroundColor: Colors.primaryLight, + }, +}); diff --git a/src/app/(app)/(tabs)/expenses.tsx b/src/app/(app)/(tabs)/expenses.tsx new file mode 100644 index 0000000..c313dc6 --- /dev/null +++ b/src/app/(app)/(tabs)/expenses.tsx @@ -0,0 +1,2 @@ +import OneTimeExpensesScreen from '@/screens/one-time-expenses-screen'; +export default OneTimeExpensesScreen; diff --git a/src/app/(app)/(tabs)/index.tsx b/src/app/(app)/(tabs)/index.tsx new file mode 100644 index 0000000..73bede0 --- /dev/null +++ b/src/app/(app)/(tabs)/index.tsx @@ -0,0 +1,2 @@ +import DashboardScreen from '@/screens/dashboard-screen'; +export default DashboardScreen; diff --git a/src/app/(app)/(tabs)/recurring.tsx b/src/app/(app)/(tabs)/recurring.tsx new file mode 100644 index 0000000..c5340ed --- /dev/null +++ b/src/app/(app)/(tabs)/recurring.tsx @@ -0,0 +1,2 @@ +import RecurringExpensesScreen from '@/screens/recurring-expenses-screen'; +export default RecurringExpensesScreen; diff --git a/src/app/(app)/(tabs)/salary.tsx b/src/app/(app)/(tabs)/salary.tsx new file mode 100644 index 0000000..1d30bb1 --- /dev/null +++ b/src/app/(app)/(tabs)/salary.tsx @@ -0,0 +1,2 @@ +import SalaryScreen from '@/screens/salary-screen'; +export default SalaryScreen; diff --git a/src/app/(app)/_layout.tsx b/src/app/(app)/_layout.tsx new file mode 100644 index 0000000..6099274 --- /dev/null +++ b/src/app/(app)/_layout.tsx @@ -0,0 +1,21 @@ +import { Colors } from "@/constants/theme"; +import { useAuth } from "@/contexts/AuthContext"; +import { Redirect, Slot } from "expo-router"; +import { ActivityIndicator, View } from "react-native"; + +export default function AppLayout() { + const { user, isLoading } = useAuth(); + + if (isLoading) { + return ( + + + + ); + } + + if (!user) + return ; + + return ; +} \ No newline at end of file diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx new file mode 100644 index 0000000..fe9f265 --- /dev/null +++ b/src/app/_layout.tsx @@ -0,0 +1,54 @@ +import { SplashScreen, Stack } from 'expo-router'; +import { useState, useEffect } from 'react'; +import { StatusBar } from 'expo-status-bar'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; +import { useAppFonts } from '@/hooks/use-app-hooks'; +import { AuthProvider, useAuth } from '@/contexts/AuthContext'; +import { Colors } from '@/constants/theme'; + +import { AnimatedSplashScreen } from '@/components/animated-splash-screen'; +import { View } from 'react-native'; + +SplashScreen.preventAutoHideAsync(); + +function RootContent() { + const { isLoading } = useAuth(); + const { fontsLoaded, fontsError } = useAppFonts(); + const [splashFinished, setSplashFinished] = useState(false); + + const isAppReady = !isLoading; + + useEffect(() => { + if (fontsLoaded || fontsError) { + SplashScreen.hideAsync(); + } + }, [fontsLoaded, fontsError]); + + if (!fontsLoaded && !fontsError) + return null; + + return ( + + + + + + + + {!splashFinished && ( + setSplashFinished(true)} /> + )} + + ) +} + +export default function RootLayout() { + return ( + + + + + + + ) +} diff --git a/src/app/biometrics.tsx b/src/app/biometrics.tsx new file mode 100644 index 0000000..500adc9 --- /dev/null +++ b/src/app/biometrics.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import { View, Text, StyleSheet } from 'react-native'; +import { useRouter } from 'expo-router'; +import * as SecureStore from 'expo-secure-store'; +import Animated, { FadeInDown, FadeInUp } from 'react-native-reanimated'; +import { Feather } from '@expo/vector-icons'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { Button } from '@/components/ui/button'; +import { Colors, Spacing, Typography, Radius } from '@/constants/theme'; +import { BlurView } from 'expo-blur'; + +export default function BiometricsScreen() { + const router = useRouter(); + const insets = useSafeAreaInsets(); + + async function handleAccept() { + await SecureStore.setItemAsync('yaba_use_biometrics', 'true'); + router.replace('/'); + } + + function handleDecline() { + router.replace('/'); + } + + return ( + + + + + + + + + + + Login por Biometria + + Deseja utilizar sua biometria (Face ID / Digital) para acessar o app mais rapidamente nas próximas vezes? + + + + + + +