From 3f99ed6acec5dfa9e3b65574a5258759d349ac24 Mon Sep 17 00:00:00 2001 From: Lorenzo Trujillo Date: Fri, 3 May 2024 10:01:13 -0600 Subject: [PATCH 01/27] =?UTF-8?q?Se=20cre=C3=B3=20una=20funci=C3=B3n=20que?= =?UTF-8?q?=20valida=20una=20ruta=20le=C3=ADda=20por=20el=20QR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mobile/src/utils/activity_route.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 mobile/src/utils/activity_route.ts diff --git a/mobile/src/utils/activity_route.ts b/mobile/src/utils/activity_route.ts new file mode 100644 index 00000000..3280cfe7 --- /dev/null +++ b/mobile/src/utils/activity_route.ts @@ -0,0 +1,4 @@ +export const activityPointRouteValidator = (route: string): boolean => { + const pattern = /^\/state\/(\d+)\/town\/(\d+)\/activity\/(\d+)\/travel\?id\=(\d+)$/g; + return pattern.test(route); +} \ No newline at end of file -- GitLab From e970aef344e878ec834aee2924de6b649f815bc2 Mon Sep 17 00:00:00 2001 From: Lorenzo Trujillo Date: Fri, 3 May 2024 10:19:05 -0600 Subject: [PATCH 02/27] =?UTF-8?q?Restructuraci=C3=B3n=20de=20la=20aplicaci?= =?UTF-8?q?=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mobile/app/login.tsx | 2 +- mobile/app/sign_up.tsx | 2 +- .../components}/login_form.tsx | 6 +- .../components}/sign_up_form.tsx | 10 +- mobile/src/{ => auth}/hooks/useLoggin.ts | 4 +- mobile/src/{ => auth}/hooks/useSignUp.ts | 4 +- .../login => auth/pages}/login_page.tsx | 4 +- .../components}/animated_background.tsx | 0 .../components/form}/or_division.tsx | 4 +- .../components/audio_player/audio_player.tsx | 32 ------ mobile/src/components/caroussel/caroussel.tsx | 92 --------------- .../components/caroussel/caroussel_tile.tsx | 77 ------------- .../custom_tile_button/custom_tile_button.tsx | 44 ------- .../date_text_input/date_text_input.tsx | 42 ------- .../full_page_loader/full_page_loader.tsx | 9 -- .../src/components/text_input/text_input.tsx | 98 ---------------- .../activity_description_page.tsx | 4 +- .../screens/activity_point/activity_point.tsx | 89 ++++++++++++--- .../edit_profile/edit_profile_page.tsx | 2 +- mobile/src/screens/scan/scan_page.tsx | 107 ++++++++++++++++++ .../state_selection/state_selection_page.tsx | 14 ++- .../town_activities/town_activities_page.tsx | 7 +- .../town_selection/town_selection_page.tsx | 12 +- 23 files changed, 226 insertions(+), 439 deletions(-) rename mobile/src/{components/login_form => auth/components}/login_form.tsx (93%) rename mobile/src/{components/sign_up_form => auth/components}/sign_up_form.tsx (93%) rename mobile/src/{ => auth}/hooks/useLoggin.ts (91%) rename mobile/src/{ => auth}/hooks/useSignUp.ts (93%) rename mobile/src/{screens/login => auth/pages}/login_page.tsx (93%) rename mobile/src/{components/animated_background => common/components}/animated_background.tsx (100%) rename mobile/src/{components/or_division => common/components/form}/or_division.tsx (84%) delete mode 100644 mobile/src/components/audio_player/audio_player.tsx delete mode 100644 mobile/src/components/caroussel/caroussel.tsx delete mode 100644 mobile/src/components/caroussel/caroussel_tile.tsx delete mode 100644 mobile/src/components/custom_tile_button/custom_tile_button.tsx delete mode 100644 mobile/src/components/date_text_input/date_text_input.tsx delete mode 100644 mobile/src/components/full_page_loader/full_page_loader.tsx delete mode 100644 mobile/src/components/text_input/text_input.tsx create mode 100644 mobile/src/screens/scan/scan_page.tsx diff --git a/mobile/app/login.tsx b/mobile/app/login.tsx index d574b543..f1e466a1 100644 --- a/mobile/app/login.tsx +++ b/mobile/app/login.tsx @@ -1,4 +1,4 @@ -import { LoginPage } from "../src/screens/login/login_page"; +import { LoginPage } from "../src/auth/pages/login_page"; const LoginScreen = () => { return ( diff --git a/mobile/app/sign_up.tsx b/mobile/app/sign_up.tsx index 236776db..26e0aa2b 100644 --- a/mobile/app/sign_up.tsx +++ b/mobile/app/sign_up.tsx @@ -1,6 +1,6 @@ import { View, StyleSheet, Image, Text, Button } from "react-native"; import { LIGTHT_THEME } from "../src/constants/theme"; -import { SignUpForm } from "../src/components/sign_up_form/sign_up_form"; +import { SignUpForm } from "../src/auth/components/sign_up_form"; import { router } from "expo-router"; const loginImage = require("../assets/login-image.jpg"); diff --git a/mobile/src/components/login_form/login_form.tsx b/mobile/src/auth/components/login_form.tsx similarity index 93% rename from mobile/src/components/login_form/login_form.tsx rename to mobile/src/auth/components/login_form.tsx index 1dd04ee7..0e0b55ba 100644 --- a/mobile/src/components/login_form/login_form.tsx +++ b/mobile/src/auth/components/login_form.tsx @@ -1,9 +1,9 @@ import { Control, Controller, FieldValues } from "react-hook-form"; import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native"; -import { CustomTextInput } from "../text_input/text_input"; +import { CustomTextInput } from "../../common/components/form/text_input"; import { LIGTHT_THEME } from "../../constants/theme"; -import { LoginFormValues } from "../../hooks/useLoggin"; -import { OrDivision } from "../or_division/or_division"; +import { LoginFormValues } from "../hooks/useLoggin"; +import { OrDivision } from "../../common/components/form/or_division"; import { LANG } from "../../lang/translations"; interface LoginFormProps { diff --git a/mobile/src/components/sign_up_form/sign_up_form.tsx b/mobile/src/auth/components/sign_up_form.tsx similarity index 93% rename from mobile/src/components/sign_up_form/sign_up_form.tsx rename to mobile/src/auth/components/sign_up_form.tsx index 33c0a12a..9b91b8e3 100644 --- a/mobile/src/components/sign_up_form/sign_up_form.tsx +++ b/mobile/src/auth/components/sign_up_form.tsx @@ -6,12 +6,12 @@ import { TouchableOpacity, View, } from "react-native"; -import { CustomTextInput } from "../text_input/text_input"; +import { CustomTextInput } from "../../common/components/form/text_input"; import { LIGTHT_THEME } from "../../constants/theme"; -import { useLoggin } from "../../hooks/useLoggin"; -import { OrDivision } from "../or_division/or_division"; -import { useSignUp } from "../../hooks/useSignUp"; -import { DateTextInput } from "../date_text_input/date_text_input"; +import { useLoggin } from "../hooks/useLoggin"; +import { OrDivision } from "../../common/components/form/or_division"; +import { useSignUp } from "../hooks/useSignUp"; +import { DateTextInput } from "../../common/components/form/date_text_input"; export const SignUpForm = () => { const { control, onSubmit } = useSignUp(); diff --git a/mobile/src/hooks/useLoggin.ts b/mobile/src/auth/hooks/useLoggin.ts similarity index 91% rename from mobile/src/hooks/useLoggin.ts rename to mobile/src/auth/hooks/useLoggin.ts index 379452da..802aefdd 100644 --- a/mobile/src/hooks/useLoggin.ts +++ b/mobile/src/auth/hooks/useLoggin.ts @@ -1,7 +1,7 @@ import { set, useForm } from "react-hook-form" -import { useAuth } from "../contexts/auth_context"; +import { useAuth } from "../../contexts/auth_context"; import { Navigator, Redirect, router } from "expo-router"; -import { useDataContext } from "../contexts/data_context"; +import { useDataContext } from "../../contexts/data_context"; export type LoginFormValues = { email: string; diff --git a/mobile/src/hooks/useSignUp.ts b/mobile/src/auth/hooks/useSignUp.ts similarity index 93% rename from mobile/src/hooks/useSignUp.ts rename to mobile/src/auth/hooks/useSignUp.ts index 07c89333..41dbc8a1 100644 --- a/mobile/src/hooks/useSignUp.ts +++ b/mobile/src/auth/hooks/useSignUp.ts @@ -1,6 +1,6 @@ import { useForm } from "react-hook-form" -import { useDataContext } from "../contexts/data_context"; -import { useAuth } from "../contexts/auth_context"; +import { useDataContext } from "../../contexts/data_context"; +import { useAuth } from "../../contexts/auth_context"; import { router } from "expo-router"; interface SignUpForm { diff --git a/mobile/src/screens/login/login_page.tsx b/mobile/src/auth/pages/login_page.tsx similarity index 93% rename from mobile/src/screens/login/login_page.tsx rename to mobile/src/auth/pages/login_page.tsx index 68dacfb0..4a3750d4 100644 --- a/mobile/src/screens/login/login_page.tsx +++ b/mobile/src/auth/pages/login_page.tsx @@ -1,6 +1,6 @@ import { View, Image, StyleSheet, Button } from "react-native"; -import { LoginForm } from "../../components/login_form/login_form"; -import { useLoggin } from "../../hooks/useLoggin"; +import { LoginForm } from "../components/login_form"; +import { useLoggin } from "../hooks/useLoggin"; import { LIGTHT_THEME } from "../../constants/theme"; import { router } from "expo-router"; import { LANG } from "../../lang/translations"; diff --git a/mobile/src/components/animated_background/animated_background.tsx b/mobile/src/common/components/animated_background.tsx similarity index 100% rename from mobile/src/components/animated_background/animated_background.tsx rename to mobile/src/common/components/animated_background.tsx diff --git a/mobile/src/components/or_division/or_division.tsx b/mobile/src/common/components/form/or_division.tsx similarity index 84% rename from mobile/src/components/or_division/or_division.tsx rename to mobile/src/common/components/form/or_division.tsx index af51421b..4fd16baa 100644 --- a/mobile/src/components/or_division/or_division.tsx +++ b/mobile/src/common/components/form/or_division.tsx @@ -1,6 +1,6 @@ import { View, Text, StyleSheet } from "react-native"; -import { LIGTHT_THEME } from "../../constants/theme"; -import { LANG } from "../../lang/translations"; +import { LIGTHT_THEME } from "../../../constants/theme"; +import { LANG } from "../../../lang/translations"; export const OrDivision = () => { return ( diff --git a/mobile/src/components/audio_player/audio_player.tsx b/mobile/src/components/audio_player/audio_player.tsx deleted file mode 100644 index ac2c83d1..00000000 --- a/mobile/src/components/audio_player/audio_player.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { TouchableOpacity, View, StyleSheet } from "react-native"; -import { useAudio } from "../../hooks/useAudio"; -import { FontAwesome } from '@expo/vector-icons'; - -const audio = require('./../../../assets/audio_prueba.mp3'); - -interface AudioPlayerProps { - audioUrl: string; - title: string; - description: string; -} - -export const AudioPlayer = ({ audioUrl, title, description }: AudioPlayerProps) => { - const { togglePlay, isPlaying } = useAudio({ source: audio }); - - - return - - - - ; -} - -const styles = StyleSheet.create({ - container: { - height: 100, - width: '100%', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'rgba(0,0,0,0.1)' - } -}); \ No newline at end of file diff --git a/mobile/src/components/caroussel/caroussel.tsx b/mobile/src/components/caroussel/caroussel.tsx deleted file mode 100644 index 6472d3f0..00000000 --- a/mobile/src/components/caroussel/caroussel.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { View, Animated, Text, Dimensions, Image, NativeSyntheticEvent, NativeScrollEvent } from "react-native"; -import { PlaceInfoEntity } from "../../domain/entities/place_info_entity"; -import { useRef } from "react"; -import { StateDataSource } from "../../domain/datasources/state_datasource"; -import { CarousselTile } from "./caroussel_tile"; -import { router } from "expo-router"; - -interface CarousselProps { - data: PlaceInfoEntity[]; - onPress?: (id: number) => void; - onIndexChange?: (index: number) => void; -} - -const ITEM_WIDTH = Dimensions.get("window").width * 0.7; -const BLANK_ITEM_WIDTH = Dimensions.get("window").width * 0.15; - -export const Caroussel = ({ data, onPress, onIndexChange }: CarousselProps) => { - const scrollX = useRef(new Animated.Value(0)).current; - const finalData = [ - { - id: -1, - name: "empty-left", - }, - ...data, - { - id: -2, - name: "empty-right", - }, - ]; - - scrollX.addListener(({ value }) => { - const index = Math.round(value / ITEM_WIDTH); - if (index < 0 || index >= data.length) { - return; - } - onIndexChange && onIndexChange(index); - }); - return ( - item.id.toString()} - horizontal - showsHorizontalScrollIndicator={false} - decelerationRate={0} - initialNumToRender={1} - - bounces={false} - snapToInterval={ITEM_WIDTH} - onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: scrollX } } }], { - useNativeDriver: true, - })} - renderItem={({ item, index }) => { - if (item.id === -1 || item.id === -2) { - return ; - } - const inputRange = [ - (index - 2) * ITEM_WIDTH, - (index - 1) * ITEM_WIDTH, - index * ITEM_WIDTH, - ]; - - const translateY = scrollX.interpolate({ - inputRange, - outputRange: [100, 0, 100], - }); - - const opacity = scrollX.interpolate({ - inputRange, - outputRange: [0.6, 1, 0.6], - }); - const scale = scrollX.interpolate({ - inputRange, - outputRange: [0.8, 1, 0.8], - }); - return ( - { - if (item.id !== -1 && item.id !== -2) { - onPress && onPress(item.id); - } - } - } - /> - ); - }} - /> - ); -}; diff --git a/mobile/src/components/caroussel/caroussel_tile.tsx b/mobile/src/components/caroussel/caroussel_tile.tsx deleted file mode 100644 index cdaf6e2a..00000000 --- a/mobile/src/components/caroussel/caroussel_tile.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { memo } from "react"; -import { - Animated, - Dimensions, - Image, - Pressable, - Text, - TouchableOpacity, -} from "react-native"; -import { PlaceInfoEntity } from "../../domain/entities/place_info_entity"; -import { Link } from "expo-router"; - -interface CarousselTileProps { - onPress?: () => void; - item: PlaceInfoEntity; - translateY: Animated.AnimatedInterpolation; - scale: Animated.AnimatedInterpolation; - opacity: Animated.AnimatedInterpolation; -} - -const ITEM_WIDTH = Dimensions.get("window").width * 0.7; -const BLANK_ITEM_WIDTH = Dimensions.get("window").width * 0.15; -const ITEM_HEIGHT = Dimensions.get("window").height * 0.7; - -export const CarousselTile = memo( - ({ translateY, scale, opacity, item, onPress }: CarousselTileProps) => { - return ( - - - - - - {item.name} - - - - - ); - } -); diff --git a/mobile/src/components/custom_tile_button/custom_tile_button.tsx b/mobile/src/components/custom_tile_button/custom_tile_button.tsx deleted file mode 100644 index 197cfc3c..00000000 --- a/mobile/src/components/custom_tile_button/custom_tile_button.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { ReactNode } from "react"; -import { TouchableOpacity, View, Text, StyleSheet } from "react-native"; -import { MaterialIcons } from "@expo/vector-icons"; -import { LIGTHT_THEME } from "../../constants/theme"; - -interface CustomTileButtonProps { - leadingIcon: ReactNode; - title: string; - onPress?: () => void; -} - -export const CustomTileButton = ({ - title, - onPress, - leadingIcon, -}: CustomTileButtonProps) => { - return ( - - - {leadingIcon} - {title} - - - - ); -}; - -const styles = StyleSheet.create({ - account_option: { - flexDirection: "row", - alignItems: "center", - justifyContent: "space-between", - height: 55, - paddingHorizontal: 10, - borderTopWidth: 2, - borderTopColor: "#ccc", - backgroundColor: LIGTHT_THEME.color.white, - borderRadius: 5, - width: "100%", - }, - title: { - fontSize: 16, - } -}); diff --git a/mobile/src/components/date_text_input/date_text_input.tsx b/mobile/src/components/date_text_input/date_text_input.tsx deleted file mode 100644 index 1e093494..00000000 --- a/mobile/src/components/date_text_input/date_text_input.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useEffect, useState } from "react"; -import { CustomTextInput } from "../text_input/text_input"; -import { TouchableOpacity, View } from "react-native"; -import DateTimePickerModal from "react-native-modal-datetime-picker"; - -interface DateInputProps { - label: string; - onChangeText: (text: string) => void; - value: string; - onBlur?: () => void; - errors?: string; -} - -export const DateTextInput = ({label, onChangeText, value, onBlur, errors}: DateInputProps) => { - const [isVisible, setIsVisible] = useState(false); - useEffect(() => { - console.log(isVisible); - }, [isVisible]); - return ( - setIsVisible(true)}> - {}} - onBlur={onBlur} - errors={errors} - editable={false} - /> - { - onChangeText(data.toDateString()); - setIsVisible(false); - }} - onCancel={() => { - setIsVisible(false); - }} - isVisible={isVisible} - /> - - ); -}; diff --git a/mobile/src/components/full_page_loader/full_page_loader.tsx b/mobile/src/components/full_page_loader/full_page_loader.tsx deleted file mode 100644 index 85879208..00000000 --- a/mobile/src/components/full_page_loader/full_page_loader.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { ActivityIndicator, View } from "react-native"; - -export const FullPageLoader = () => { - return ( - - - - ); -} \ No newline at end of file diff --git a/mobile/src/components/text_input/text_input.tsx b/mobile/src/components/text_input/text_input.tsx deleted file mode 100644 index 4a7ad5a9..00000000 --- a/mobile/src/components/text_input/text_input.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { useEffect, useRef } from "react"; -import { TextInput, Text, StyleSheet, View, Animated, TouchableOpacity } from "react-native"; -import { LIGTHT_THEME } from "../../constants/theme"; - -interface TextInputProps { - isPassword?: boolean; - label: string; - onChangeText: (text: string) => void; - value: string; - onBlur?: () => void; - errors?: string; - type?: any; - editable?: boolean; -} - -export const CustomTextInput = (props: TextInputProps) => { - const { isPassword, label, type, onBlur, errors, value, editable,...rest } = props; - const labelFocusAnimation = useRef(new Animated.Value(value.length > 0 ? 1 : 0)).current; - const inputRef = useRef(null); - - const handleFocus = () => { - Animated.timing(labelFocusAnimation, { - toValue: 1, - duration: 200, - useNativeDriver: false, - }).start(); - }; - - const handleBlur = () => { - if (!props.value || props.value === "") { - Animated.timing(labelFocusAnimation, { - toValue: 0, - duration: 200, - useNativeDriver: false, - }).start(); - } - onBlur && onBlur(); - }; - - return ( - - - - {label} - - - - { - errors ? {errors} : - } - - ); -}; - -const styles = StyleSheet.create({ - main_container: { - width: "100%", - }, - container: { - width: "100%", - borderWidth: 1, - borderRadius: 5, - borderColor: "black", - height: 40, - alignItems: "center", - justifyContent: "center", - }, - input: { - width: "100%", - height: "100%", - paddingHorizontal: 10, - }, - label: { - position: "absolute", - left: 10, - zIndex: 1, - backgroundColor: LIGTHT_THEME.color.white, - }, -}); diff --git a/mobile/src/screens/activity_description/activity_description_page.tsx b/mobile/src/screens/activity_description/activity_description_page.tsx index 2e02b8ac..27d92a83 100644 --- a/mobile/src/screens/activity_description/activity_description_page.tsx +++ b/mobile/src/screens/activity_description/activity_description_page.tsx @@ -8,7 +8,7 @@ import { ScrollView, Dimensions, } from "react-native"; -import { FullPageLoader } from "../../components/full_page_loader/full_page_loader"; +import { FullPageLoader } from "../../common/components/full_page_loader"; import { ApiRequestStatus } from "../../constants/api_request_states"; import { useGetActivityInfo } from "../../hooks/useGetActivityInfo"; import { router } from "expo-router"; @@ -52,7 +52,7 @@ export const ActivityDescriptionPage = ({ if (requestStatus === ApiRequestStatus.LOADING) { return ; } - if (activityInfo === undefined) { + if (activityInfo === undefined || activityInfo === null) { return ( Activity not found diff --git a/mobile/src/screens/activity_point/activity_point.tsx b/mobile/src/screens/activity_point/activity_point.tsx index 6769d640..feeeac1f 100644 --- a/mobile/src/screens/activity_point/activity_point.tsx +++ b/mobile/src/screens/activity_point/activity_point.tsx @@ -1,9 +1,15 @@ -import { Image, Text, View, StyleSheet } from "react-native"; -import { FullPageLoader } from "../../components/full_page_loader/full_page_loader"; +import { Image, Text, View, StyleSheet, Button, BackHandler } from "react-native"; +import { FullPageLoader } from "../../common/components/full_page_loader"; import { ApiRequestStatus } from "../../constants/api_request_states"; import { useGetActivityPoint } from "../../hooks/useGetActivityPoint"; import { ScrollView } from "react-native-gesture-handler"; -import { AudioPlayer } from "../../components/audio_player/audio_player"; +import { AudioPlayer } from "../../common/components/audio_player"; +import { FloatingBackButton } from "../../components/floating_back_button/floating_back_button"; +import { TouchableOpacity } from "@gorhom/bottom-sheet"; +import { LIGTHT_THEME } from "../../constants/theme"; +import { router, useFocusEffect } from "expo-router"; +import { useAudio } from "../../contexts/audio_context"; +import { useEffect } from "react"; interface ActivityPointScreenProps { stateId: number; @@ -25,6 +31,22 @@ export const ActivityPointScreen = ({ placeNumber: id, }); + const { onUnmount } = useAudio(); + + console.log('render') + + useEffect(() => { + const backAction = () => { + onUnmount(); + router.back(); + return true; + } + + const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction); + + return () => backHandler.remove(); + }, []); + if (requestStatus === ApiRequestStatus.LOADING) { return ; } @@ -32,24 +54,29 @@ export const ActivityPointScreen = ({ if (requestStatus === ApiRequestStatus.ERROR || !data) { return null; } + return ( {data.name} - - - - {data.content.content} + + + + + {data.content.content} + + {!data.directions && ( + + End Activity + + )} ); @@ -65,17 +92,43 @@ const styles = StyleSheet.create({ flex: 1, gap: 20, padding: 20, + alignItems: "center", }, title: { fontSize: 24, - fontWeight: "bold", + fontWeight: "400", textAlign: "center", }, contentText: { - fontSize: 16, + fontSize: 18, + lineHeight: 30, + textAlign: "justify", }, - image: { - height: 300, + imageContainer: { width: "100%", + height: 300, + backgroundColor: "lightgrey", + padding: 10, + borderRadius: 10, + overflow: "hidden", + borderWidth: 2, + }, + image: { + height: "70%", + width: "70%", }, + endActivityButton: { + backgroundColor: LIGTHT_THEME.color.primary, + padding: 10, + borderRadius: 25, + alignItems: "center", + justifyContent: "center", + width: 200, + height: 50 + }, + endActivityButtonText: { + color: LIGTHT_THEME.color.white, + fontWeight: "bold", + fontSize: 16, + } }); diff --git a/mobile/src/screens/edit_profile/edit_profile_page.tsx b/mobile/src/screens/edit_profile/edit_profile_page.tsx index 2f334772..abf6867e 100644 --- a/mobile/src/screens/edit_profile/edit_profile_page.tsx +++ b/mobile/src/screens/edit_profile/edit_profile_page.tsx @@ -2,7 +2,7 @@ import { View, Text, StyleSheet } from "react-native"; import { useEditProfile } from "../../hooks/useEditProfile"; import { Controller } from "react-hook-form"; import { useAuth } from "../../contexts/auth_context"; -import { CustomTextInput } from "../../components/text_input/text_input"; +import { CustomTextInput } from "../../common/components/form/text_input"; import Checkbox from "expo-checkbox"; import { useState } from "react"; import { TouchableOpacity } from "@gorhom/bottom-sheet"; diff --git a/mobile/src/screens/scan/scan_page.tsx b/mobile/src/screens/scan/scan_page.tsx new file mode 100644 index 00000000..0570d522 --- /dev/null +++ b/mobile/src/screens/scan/scan_page.tsx @@ -0,0 +1,107 @@ +import { View, Text, Button, StyleSheet, Animated } from "react-native"; +import { useQRScanner } from "../../hooks/useQRScanner"; +import { CameraView } from "expo-camera/next"; +import { BarCodeScanningResult } from "expo-camera"; +import { useEffect, useRef } from "react"; +import { activityPointRouteValidator } from "../../utils/activity_route"; + +export default function ScanPage() { + const { hasPermission, getPermission, scanning, onQRScanned, qrData } = + useQRScanner({validator: activityPointRouteValidator}); + + const qrAnimation = useRef(new Animated.Value(0)).current; + + const startAnimation = () => { + Animated.loop( + Animated.sequence([ + Animated.timing(qrAnimation, { + toValue: 1, + duration: 1000, + useNativeDriver: true, + }), + Animated.timing(qrAnimation, { + toValue: 0, + duration: 1000, + useNativeDriver: true, + }), + ]) + ).start(); + }; + + useEffect(() => { + return () => { + qrAnimation.stopAnimation(); + }; + }, []); + + if (!hasPermission) { + return ( + + No permission +