Loading mobile/src/common/components/audio_player.tsx 0 → 100644 +77 −0 Original line number Diff line number Diff line import { TouchableOpacity, View, StyleSheet, Text } from "react-native"; import { FontAwesome, Feather } from "@expo/vector-icons"; import Slider from "@react-native-community/slider"; import { millisecondsToHourFormat } from "../../utils/time"; import { LIGTHT_THEME } from "../../constants/theme"; import { useAudio } from "../../contexts/audio_context"; import { useEffect } from "react"; const audio = require("./../../../assets/audio_prueba.mp3"); interface AudioPlayerProps { audioUrl: string; title: string; description: string; } export const AudioPlayer = () => { const { loadAudio, position, togglePlay, isPlaying, duration, onValueChange } = useAudio(); useEffect(() => { loadAudio(audio); }, []); return ( <View style={styles.container}> <View style={styles.sliderContainer}> <Text style={styles.hour_text}> {millisecondsToHourFormat(position)} </Text> <Slider thumbTintColor="gray" minimumTrackTintColor={LIGTHT_THEME.color.white} minimumValue={0} maximumValue={duration} value={position} onValueChange={onValueChange} style={styles.slider} /> <Text style={styles.hour_text}> {millisecondsToHourFormat(duration)} </Text> </View> <TouchableOpacity onPress={togglePlay}> <Feather name={isPlaying ? "pause-circle" : "play-circle"} size={54} color={LIGTHT_THEME.color.white} /> </TouchableOpacity> </View> ); }; const styles = StyleSheet.create({ container: { height: 100, width: "100%", justifyContent: "center", alignItems: "center", backgroundColor: LIGTHT_THEME.color.primary, gap: 10, }, sliderContainer: { width: "100%", display: "flex", flexDirection: "row", paddingHorizontal: 20, }, slider: { flex: 1, }, hour_text: { color: LIGTHT_THEME.color.white, fontWeight: "bold", }, }); mobile/src/common/components/caroussel/caroussel.tsx 0 → 100644 +92 −0 Original line number Diff line number Diff line 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 ( <Animated.FlatList data={finalData} keyExtractor={(item) => 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 <View style={{ width: BLANK_ITEM_WIDTH }} />; } 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 ( <CarousselTile opacity={opacity} scale={scale} translateY={translateY} item={item} onPress={() => { if (item.id !== -1 && item.id !== -2) { onPress && onPress(item.id); } } } /> ); }} /> ); }; mobile/src/common/components/caroussel/caroussel_tile.tsx 0 → 100644 +77 −0 Original line number Diff line number Diff line 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<number>; scale: Animated.AnimatedInterpolation<number>; opacity: Animated.AnimatedInterpolation<number>; } 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 ( <Pressable onPress={onPress}> <Animated.View style={{ width: ITEM_WIDTH, height: ITEM_HEIGHT, padding: 10, alignItems: "center", justifyContent: "center", }} > <Animated.View style={{ transform: [{ translateY }, { scale }], opacity, height: "90%", width: "100%", overflow: "hidden", borderRadius: 20, alignItems: "center", justifyContent: "center", elevation: 10, borderWidth: 7, borderColor: "white", }} > <Animated.Image source={{ uri: item.imageUri }} style={{ width: "100%", height: "100%", }} /> <Text style={{ position: "absolute", top: 10, color: "white", fontSize: 20, fontWeight: "bold", textAlign: "center", }} > {item.name} </Text> </Animated.View> </Animated.View> </Pressable> ); } ); mobile/src/common/components/custom_tile_button.tsx 0 → 100644 +66 −0 Original line number Diff line number Diff line 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; subtitle?: string; onPress?: () => void; } export const CustomTileButton = ({ title, onPress, leadingIcon, subtitle }: CustomTileButtonProps) => { return ( <TouchableOpacity onPress={onPress} style={styles.account_option}> <View style={{ flexDirection: "row", gap: 20, alignItems: "center" }}> {leadingIcon} <View style={styles.titleContainer} > <Text style={styles.title} numberOfLines={1} >{title}</Text> { subtitle && <Text style={styles.subtitle} numberOfLines={1} >{subtitle}</Text> } </View> </View> <MaterialIcons name="keyboard-arrow-right" size={30} color="black" /> </TouchableOpacity> ); }; const styles = StyleSheet.create({ account_option: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", height: 60, paddingHorizontal: 10, borderTopWidth: 2, borderTopColor: "#ccc", backgroundColor: LIGTHT_THEME.color.white, borderRadius: 5, width: "100%", }, titleContainer: { height: "100%", alignItems: "flex-start", justifyContent: "space-between", paddingVertical: 10, maxWidth: "85%", }, title: { fontSize: 16, fontWeight: "700", }, subtitle: { fontSize: 14, color: "#777", fontWeight: "500", textAlign: "left", } }); mobile/src/common/components/form/date_text_input.tsx 0 → 100644 +42 −0 Original line number Diff line number Diff line import { useEffect, useState } from "react"; import { CustomTextInput } from "./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 ( <TouchableOpacity onPress={() => setIsVisible(true)}> <CustomTextInput label={label} value={value} onChangeText={() => {}} onBlur={onBlur} errors={errors} editable={false} /> <DateTimePickerModal mode="date" onConfirm={(data) => { onChangeText(data.toDateString()); setIsVisible(false); }} onCancel={() => { setIsVisible(false); }} isVisible={isVisible} /> </TouchableOpacity> ); }; Loading
mobile/src/common/components/audio_player.tsx 0 → 100644 +77 −0 Original line number Diff line number Diff line import { TouchableOpacity, View, StyleSheet, Text } from "react-native"; import { FontAwesome, Feather } from "@expo/vector-icons"; import Slider from "@react-native-community/slider"; import { millisecondsToHourFormat } from "../../utils/time"; import { LIGTHT_THEME } from "../../constants/theme"; import { useAudio } from "../../contexts/audio_context"; import { useEffect } from "react"; const audio = require("./../../../assets/audio_prueba.mp3"); interface AudioPlayerProps { audioUrl: string; title: string; description: string; } export const AudioPlayer = () => { const { loadAudio, position, togglePlay, isPlaying, duration, onValueChange } = useAudio(); useEffect(() => { loadAudio(audio); }, []); return ( <View style={styles.container}> <View style={styles.sliderContainer}> <Text style={styles.hour_text}> {millisecondsToHourFormat(position)} </Text> <Slider thumbTintColor="gray" minimumTrackTintColor={LIGTHT_THEME.color.white} minimumValue={0} maximumValue={duration} value={position} onValueChange={onValueChange} style={styles.slider} /> <Text style={styles.hour_text}> {millisecondsToHourFormat(duration)} </Text> </View> <TouchableOpacity onPress={togglePlay}> <Feather name={isPlaying ? "pause-circle" : "play-circle"} size={54} color={LIGTHT_THEME.color.white} /> </TouchableOpacity> </View> ); }; const styles = StyleSheet.create({ container: { height: 100, width: "100%", justifyContent: "center", alignItems: "center", backgroundColor: LIGTHT_THEME.color.primary, gap: 10, }, sliderContainer: { width: "100%", display: "flex", flexDirection: "row", paddingHorizontal: 20, }, slider: { flex: 1, }, hour_text: { color: LIGTHT_THEME.color.white, fontWeight: "bold", }, });
mobile/src/common/components/caroussel/caroussel.tsx 0 → 100644 +92 −0 Original line number Diff line number Diff line 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 ( <Animated.FlatList data={finalData} keyExtractor={(item) => 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 <View style={{ width: BLANK_ITEM_WIDTH }} />; } 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 ( <CarousselTile opacity={opacity} scale={scale} translateY={translateY} item={item} onPress={() => { if (item.id !== -1 && item.id !== -2) { onPress && onPress(item.id); } } } /> ); }} /> ); };
mobile/src/common/components/caroussel/caroussel_tile.tsx 0 → 100644 +77 −0 Original line number Diff line number Diff line 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<number>; scale: Animated.AnimatedInterpolation<number>; opacity: Animated.AnimatedInterpolation<number>; } 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 ( <Pressable onPress={onPress}> <Animated.View style={{ width: ITEM_WIDTH, height: ITEM_HEIGHT, padding: 10, alignItems: "center", justifyContent: "center", }} > <Animated.View style={{ transform: [{ translateY }, { scale }], opacity, height: "90%", width: "100%", overflow: "hidden", borderRadius: 20, alignItems: "center", justifyContent: "center", elevation: 10, borderWidth: 7, borderColor: "white", }} > <Animated.Image source={{ uri: item.imageUri }} style={{ width: "100%", height: "100%", }} /> <Text style={{ position: "absolute", top: 10, color: "white", fontSize: 20, fontWeight: "bold", textAlign: "center", }} > {item.name} </Text> </Animated.View> </Animated.View> </Pressable> ); } );
mobile/src/common/components/custom_tile_button.tsx 0 → 100644 +66 −0 Original line number Diff line number Diff line 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; subtitle?: string; onPress?: () => void; } export const CustomTileButton = ({ title, onPress, leadingIcon, subtitle }: CustomTileButtonProps) => { return ( <TouchableOpacity onPress={onPress} style={styles.account_option}> <View style={{ flexDirection: "row", gap: 20, alignItems: "center" }}> {leadingIcon} <View style={styles.titleContainer} > <Text style={styles.title} numberOfLines={1} >{title}</Text> { subtitle && <Text style={styles.subtitle} numberOfLines={1} >{subtitle}</Text> } </View> </View> <MaterialIcons name="keyboard-arrow-right" size={30} color="black" /> </TouchableOpacity> ); }; const styles = StyleSheet.create({ account_option: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", height: 60, paddingHorizontal: 10, borderTopWidth: 2, borderTopColor: "#ccc", backgroundColor: LIGTHT_THEME.color.white, borderRadius: 5, width: "100%", }, titleContainer: { height: "100%", alignItems: "flex-start", justifyContent: "space-between", paddingVertical: 10, maxWidth: "85%", }, title: { fontSize: 16, fontWeight: "700", }, subtitle: { fontSize: 14, color: "#777", fontWeight: "500", textAlign: "left", } });
mobile/src/common/components/form/date_text_input.tsx 0 → 100644 +42 −0 Original line number Diff line number Diff line import { useEffect, useState } from "react"; import { CustomTextInput } from "./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 ( <TouchableOpacity onPress={() => setIsVisible(true)}> <CustomTextInput label={label} value={value} onChangeText={() => {}} onBlur={onBlur} errors={errors} editable={false} /> <DateTimePickerModal mode="date" onConfirm={(data) => { onChangeText(data.toDateString()); setIsVisible(false); }} onCancel={() => { setIsVisible(false); }} isVisible={isVisible} /> </TouchableOpacity> ); };