diff --git a/mobile/src/common/components/audio_player.tsx b/mobile/src/common/components/audio_player.tsx index 6de5d55ca5d5e036c62016644ef0e80962a39413..fd3e59dd9f2eb93eb17f52abae7089a307d42ece 100644 --- a/mobile/src/common/components/audio_player.tsx +++ b/mobile/src/common/components/audio_player.tsx @@ -5,21 +5,20 @@ import { millisecondsToHourFormat } from "../../utils/time"; import { LIGHT_THEME } from "../constants/theme"; import { useAudio } from "../contexts/audio_context"; import { useEffect } from "react"; +import { API_URL } from "../constants/api"; const audio = require("./../../../assets/audio_prueba.mp3"); interface AudioPlayerProps { - audioUrl: string; - title: string; - description: string; + pointId: number; } -export const AudioPlayer = () => { +export const AudioPlayer = ({ pointId }: AudioPlayerProps) => { const { loadAudio, position, togglePlay, isPlaying, duration, onValueChange } = useAudio(); useEffect(() => { - loadAudio(audio); + loadAudio({ uri: `${API_URL}/point/${pointId}/audio?lang=es`}); }, []); return ( diff --git a/mobile/src/common/contexts/data_context.tsx b/mobile/src/common/contexts/data_context.tsx index a9f0f4b40bc16d6a2c79a74194ab0b64669a7e07..83d3a4f8bb5936e797fce2bdd48c73ffac90a4d1 100644 --- a/mobile/src/common/contexts/data_context.tsx +++ b/mobile/src/common/contexts/data_context.tsx @@ -19,6 +19,7 @@ import { StateDataSourceProd } from "../../infrastructure/datasource/prod/state_ import { ProfileRepository } from "../../profile/domain/repositories/profile_repository"; import { ProfileDataSourceDev } from "../../profile/infrastructure/datasources/dev/profile_datasource"; import { ProfileRepositoryImpl } from "../../profile/infrastructure/repositories/profile_repository"; +import { ActivityDatasourceProd } from "../../infrastructure/datasource/prod/activity_datasource"; type DataContextType = { statesRepository: StateRepository | null; @@ -41,11 +42,14 @@ const DataContext = createContext({ }); export const DataContextProvider = ({ children }: DataContextProviderProps) => { - const statesDataSource = new StateDataSourceDev(); + //const statesDataSource = new StateDataSourceDev(); + const statesDataSource = new StateDataSourceProd(); const statesRepository = new StateRepositoryImpl(statesDataSource); - const authDataSource = new AuthDataSourceDev(); + //const authDataSource = new AuthDataSourceDev(); + const authDataSource = new AuthDatasourceProd(); const authRepository = new AuthRepositoryImpl(authDataSource); - const activityDataSource = new ActivityDatasourceDev(); + //const activityDataSource = new ActivityDatasourceDev(); + const activityDataSource = new ActivityDatasourceProd(); const activityRepository = new ActivityRepositoryDev(activityDataSource); const travelDatasource = new TravelDatasourceDev(); const travelRepository = new TravelRepositoryImpl(travelDatasource); diff --git a/mobile/src/hooks/useGet.ts b/mobile/src/hooks/useGet.ts index 77a31116fd14e838776606c33db53103d510bdc4..dead195efaddc5b4127ac83d41598a316c44265f 100644 --- a/mobile/src/hooks/useGet.ts +++ b/mobile/src/hooks/useGet.ts @@ -1,5 +1,6 @@ import { useEffect, useState } from "react"; import { ApiRequestStatus } from "../common/constants/api_request_states"; +import { set } from "react-hook-form"; export const useGet = (callback: () => Promise) => { const [data, setData] = useState(null); @@ -9,6 +10,7 @@ export const useGet = (callback: () => Promise) => { const fetchData = async () => { try { + await setLoading(); const response = await callback(); setData(response); setRequestStatus(ApiRequestStatus.SUCCESS); diff --git a/mobile/src/hooks/useGetStates.ts b/mobile/src/hooks/useGetStates.ts index e098e988002cfd06e2305e9f048d7e93a202af6c..6d8e9d9cdeea4830309ffebcdabd81c79568832a 100644 --- a/mobile/src/hooks/useGetStates.ts +++ b/mobile/src/hooks/useGetStates.ts @@ -8,7 +8,7 @@ export const useGetStates = () => { const callback = async () =>{ return await statesRepository?.getStates() || []; } - const { requestStatus, data } = useGet(callback); + const { requestStatus, data, refresh } = useGet(callback); - return { requestStatus, data }; + return { requestStatus, data, refresh }; } \ No newline at end of file diff --git a/mobile/src/hooks/useGetTowns.ts b/mobile/src/hooks/useGetTowns.ts index de59d0397b75067e9f87f35fd94e14565907ae88..ae7c22edabd30d667beb5a68ddde046beb36cf6a 100644 --- a/mobile/src/hooks/useGetTowns.ts +++ b/mobile/src/hooks/useGetTowns.ts @@ -9,7 +9,7 @@ export const useGetTowns = (stateId: number) => { const callback = async () =>{ return await statesRepository?.getTowns(stateId) || []; } - const { requestStatus, data } = useGet(callback); + const { requestStatus, data, refresh } = useGet(callback); - return { requestStatus, data }; + return { requestStatus, data, refresh }; } \ No newline at end of file diff --git a/mobile/src/infrastructure/datasource/prod/activity_datasource.ts b/mobile/src/infrastructure/datasource/prod/activity_datasource.ts new file mode 100644 index 0000000000000000000000000000000000000000..41e8b4acc45ec16568c2238cce136585b964b46a --- /dev/null +++ b/mobile/src/infrastructure/datasource/prod/activity_datasource.ts @@ -0,0 +1,16 @@ +import axios from "axios"; +import { ActivityDataSource } from "../../../domain/datasources/activity_datasource"; +import { ActivityPlaceEntity } from "../../../domain/entities/activity_place_entity"; +import { API_URL } from "../../../common/constants/api"; +import { ActivityPlaceModel } from "../../models/prod/activity_place_model"; +import { activityPlaceModelToEntity } from "../../utils/activity_utils"; + +export class ActivityDatasourceProd implements ActivityDataSource { + async getPlaceActivity(activityId: number, townId: number, stateId: number, placeNumber: number): Promise { + const { data, status } = await axios.get(`${API_URL}/point/${placeNumber}lang?lang=es`); + if (status !== 200) { + throw new Error("Error al obtener la información del lugar"); + } + return activityPlaceModelToEntity(data); + } +} \ No newline at end of file diff --git a/mobile/src/infrastructure/datasource/prod/state_datasource.ts b/mobile/src/infrastructure/datasource/prod/state_datasource.ts index 6f2c329da1cf35a149854785bbf0d9031d3b5364..7007dbc9102bbe29f52b818df7e5e42c9f9bab8d 100644 --- a/mobile/src/infrastructure/datasource/prod/state_datasource.ts +++ b/mobile/src/infrastructure/datasource/prod/state_datasource.ts @@ -6,6 +6,10 @@ import { TownEntity } from "../../../domain/entities/town_entity"; import { API_URL } from "../../../common/constants/api"; import { StateModel } from "../../models/prod/states_model"; import { stateModelToEntity } from "../../utils/states_utils"; +import { TownModel } from "../../models/prod/town_model"; +import { townModelToEntity } from "../../utils/town_utils"; +import { ActivityModel } from "../../models/prod/activity_model"; +import { placeModelToEntity } from "../../utils/place_utils"; export class StateDataSourceProd implements StateDataSource { async getStates(): Promise { @@ -15,14 +19,33 @@ export class StateDataSourceProd implements StateDataSource { } return data.map(stateModelToEntity); } - getTowns(stateId: number): Promise { - throw new Error("Method not implemented."); + async getTowns(stateId: number): Promise { + // TODO: get lang from user preferences + const {status, data} = await axios.get(API_URL + '/state/' + stateId + '/town?lang=es'); + if (status !== 200) { + throw new Error('Error fetching towns'); + } + return data.map(townModelToEntity); } - getTownActivities(townId: number): Promise { - throw new Error("Method not implemented."); + async getTownActivities(townId: number): Promise { + // TODO: get lang from user preferences + // TODO: get townId from user preferences + console.log('townId', townId); + const {status, data} = await axios.get(API_URL + `/place/town/${townId}/place?lang=es`); + if (status !== 200) { + throw new Error('Error fetching activities'); + } + console.log('data', data); + return data.map(placeModelToEntity); } - getActivityInfo(activityId: number): Promise { - throw new Error("Method not implemented."); + async getActivityInfo(activityId: number): Promise { + // TODO: get lang from user preferences + // TODO: get townId from user preferences + const {status, data} = await axios.get(API_URL + `/place/${activityId}`); + if (status !== 200) { + throw new Error('Error fetching activity'); + } + return placeModelToEntity(data); } } \ No newline at end of file diff --git a/mobile/src/infrastructure/models/prod/activity_model.ts b/mobile/src/infrastructure/models/prod/activity_model.ts new file mode 100644 index 0000000000000000000000000000000000000000..228e187d89a96d037f2fea9c2c01415ec13bf59a --- /dev/null +++ b/mobile/src/infrastructure/models/prod/activity_model.ts @@ -0,0 +1,13 @@ +export interface ActivityModel { + idTown: number; + idPlace: number; + available: string; + description: string; + coords: string; + imageName: string; + name: string; + openAt: number; + closeAt: number; + startDate: Date | null; + endDate: Date | null; +} diff --git a/mobile/src/infrastructure/models/prod/activity_place_model.ts b/mobile/src/infrastructure/models/prod/activity_place_model.ts new file mode 100644 index 0000000000000000000000000000000000000000..d69d3f4e1f59cd1577d4095817e84877cc0c37c5 --- /dev/null +++ b/mobile/src/infrastructure/models/prod/activity_place_model.ts @@ -0,0 +1,8 @@ +export interface ActivityPlaceModel { + idPoint: number; + idPlace: number; + name: string; + imageName: string; + content: string; + directions: string; +} diff --git a/mobile/src/infrastructure/models/prod/town_model.ts b/mobile/src/infrastructure/models/prod/town_model.ts new file mode 100644 index 0000000000000000000000000000000000000000..230dc58831a177aa00b5f4d9f80dd9ee59f40403 --- /dev/null +++ b/mobile/src/infrastructure/models/prod/town_model.ts @@ -0,0 +1,7 @@ +export interface TownModel { + townId: number, + name: string, + imageName: string, + description: string, + stateId: number +} \ No newline at end of file diff --git a/mobile/src/infrastructure/utils/activity_utils.ts b/mobile/src/infrastructure/utils/activity_utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ce6415a6b8175e0e9fd7aa8a06177901a7a59d5 --- /dev/null +++ b/mobile/src/infrastructure/utils/activity_utils.ts @@ -0,0 +1,20 @@ +import { ActivityPlaceEntity } from "../../domain/entities/activity_place_entity"; +import { ActivityPlaceModel } from "../models/prod/activity_place_model"; + +export const activityPlaceModelToEntity = (activity: ActivityPlaceModel): ActivityPlaceEntity => { + return { + idPlaceActivity: activity.idPoint, + name: activity.name, + number: activity.idPlace, + idPlace: activity.idPlace, + imageUrl: activity.imageName, + directions: { + content: activity.directions, + speakUrl: "" + }, + content: { + content: activity.content, + speakUrl: "" + } + } +}; \ No newline at end of file diff --git a/mobile/src/infrastructure/utils/place_utils.ts b/mobile/src/infrastructure/utils/place_utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..3e16b116e32ad5376abe06599128da895d73aaca --- /dev/null +++ b/mobile/src/infrastructure/utils/place_utils.ts @@ -0,0 +1,15 @@ +import { ActivityInfoEntity } from "../../domain/entities/activity_info_entity"; +import { ActivityModel } from "../models/prod/activity_model"; + +export const placeModelToEntity = (placeModel: ActivityModel): ActivityInfoEntity => { + return { + id: placeModel.idPlace, + name: placeModel.name, + imageUri: placeModel.imageName, + description: placeModel.description, + location: placeModel.coords, + townId: placeModel.idTown, + available: 'open', + tags: undefined, + } +} \ No newline at end of file diff --git a/mobile/src/infrastructure/utils/states_utils.ts b/mobile/src/infrastructure/utils/states_utils.ts index 4083616425aea28b9bc58a8569162652bb547b45..2f9022a955ff1b990b23bbfd10e1e7a25d599471 100644 --- a/mobile/src/infrastructure/utils/states_utils.ts +++ b/mobile/src/infrastructure/utils/states_utils.ts @@ -3,12 +3,6 @@ import { StateEntity } from "../../domain/entities/state_entity"; import { StateModel } from "../models/prod/states_model"; export const stateModelToEntity = (stateModel: StateModel): StateEntity => { - //TODO: Fix this - let imageURL = stateModel.imageURL.split('/'); - imageURL = imageURL.filter((item) => item !== '' && item !== 'http:' && item !== 'https:'); - imageURL[0] = API_URL; - stateModel.imageURL = imageURL.join('/'); - console.log(stateModel.imageURL); return { id: stateModel.stateId, name: stateModel.name, diff --git a/mobile/src/infrastructure/utils/town_utils.ts b/mobile/src/infrastructure/utils/town_utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..a531744fc0936e1bd4551d4d09144463c8a72eff --- /dev/null +++ b/mobile/src/infrastructure/utils/town_utils.ts @@ -0,0 +1,13 @@ +import { API_URL } from "../../common/constants/api"; +import { TownEntity } from "../../domain/entities/town_entity"; +import { TownModel } from "../models/prod/town_model"; + +export const townModelToEntity = (townModel: TownModel): TownEntity => { + return { + id: townModel.townId, + name: townModel.name, + imageUri: townModel.imageName, + description: townModel.description, + stateId: townModel.stateId + } +} \ No newline at end of file diff --git a/mobile/src/profile/hooks/useSetUpProfile.ts b/mobile/src/profile/hooks/useSetUpProfile.ts index e0ef97beebfefa38ce8f267a679be8e6afd2bd94..9acb88383871614d8500d1efbb94b3e4f5ccf8b1 100644 --- a/mobile/src/profile/hooks/useSetUpProfile.ts +++ b/mobile/src/profile/hooks/useSetUpProfile.ts @@ -3,6 +3,9 @@ import { useGetInterests } from "./useGetInterests"; import { IOption } from "../../common/domain/entities/option"; import { useEffect, useState } from "react"; import { ApiRequestStatus } from "../../common/constants/api_request_states"; +import { useDataContext } from "../../common/contexts/data_context"; +import { useSetUp } from "../../common/contexts/set_up_context"; +import { router } from "expo-router"; export type SetUpProfileFormValues = { interests: number[]; @@ -11,6 +14,7 @@ export type SetUpProfileFormValues = { export const useSetUpProfile = () => { const { control, handleSubmit, setValue } = useForm(); + const { setFirstTime } = useSetUp(); const { interests: allCategories, requestStatus } = useGetInterests(); // TODO: Si se agregan mas campos que requieran datos de la API, debemos procurar que todos los request status esten en success const [interests, setInterests] = useState([]); @@ -39,6 +43,8 @@ export const useSetUpProfile = () => { //TODO: Verificar si se enviará la fecha de nacimiento y los intereses en el mismo request o por separado console.log(data); })(); + await setFirstTime(); + router.replace("(tabs)"); }; return { control, onSubmit, toogleInterest, interests, requestStatus }; }; diff --git a/mobile/src/screens/activity_point/activity_point.tsx b/mobile/src/screens/activity_point/activity_point.tsx index 7035470f028a8776cdc46daceca0bc1886602673..1a6ebe75330aeb242ff18b9dc852c5a343b1604a 100644 --- a/mobile/src/screens/activity_point/activity_point.tsx +++ b/mobile/src/screens/activity_point/activity_point.tsx @@ -66,6 +66,14 @@ export const ActivityPointScreen = memo(({ {data.content.content} + { + data.directions && ( + <> + Directions + {data.directions.content} + + ) + } {!data.directions && ( End Activity @@ -73,8 +81,7 @@ export const ActivityPointScreen = memo(({ )} - + ); }); diff --git a/mobile/src/screens/error_page/error_page.tsx b/mobile/src/screens/error_page/error_page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f6d618e8e35031e9d9472e87567f1cd97cfddb4e --- /dev/null +++ b/mobile/src/screens/error_page/error_page.tsx @@ -0,0 +1,44 @@ +import { View, Text, StyleSheet } from "react-native"; +import { MaterialIcons } from "@expo/vector-icons"; +import { TouchableOpacity } from "react-native-gesture-handler"; + +interface ErrorPageProps { + refresh?: () => void; +} + +export const ErrorPage = ({ refresh }: ErrorPageProps) => { + return ( + + + + Ups, algo salio mal, revisa tu conexión a internet o intentalo más + tarde... + + {refresh && ( + + + Reintentar + + )} + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + alignItems: "center", + gap: 20, + }, + text: { + width: "60%", + textAlign: "center", + }, + refreshButton: { + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + gap: 5, + }, +}); diff --git a/mobile/src/screens/state_selection/state_selection_page.tsx b/mobile/src/screens/state_selection/state_selection_page.tsx index bdc42c14221c3773a1f637ce15e262adad9c6ca6..358e2ab73c37f7e6b98548455ad796ee1ffdb58d 100644 --- a/mobile/src/screens/state_selection/state_selection_page.tsx +++ b/mobile/src/screens/state_selection/state_selection_page.tsx @@ -6,9 +6,10 @@ import { Caroussel } from "../../common/components/caroussel/caroussel"; import { router } from "expo-router"; import { useAnimatedSelectedIndex } from "../../hooks/useAnimatedSelectedIndex"; import { AnimatedBackground } from "../../common/components/animated_background"; +import { ErrorPage } from "../error_page/error_page"; export const StateSelectionPage = () => { - const { data, requestStatus } = useGetStates(); + const { data, requestStatus, refresh } = useGetStates(); const { selectedStateIndex, onIndexChange, backgroundImageAnimation } = useAnimatedSelectedIndex(200, 200); @@ -22,9 +23,7 @@ export const StateSelectionPage = () => { if (requestStatus === ApiRequestStatus.ERROR || !data) { return ( - - Something went wrong - + ); } diff --git a/mobile/src/screens/town_selection/town_selection_page.tsx b/mobile/src/screens/town_selection/town_selection_page.tsx index 84e4d48bd2a7adfec183a17a1047cfdd57ac2947..09cc498de7a3ee484655cbdd0ccf0acd3f72ea07 100644 --- a/mobile/src/screens/town_selection/town_selection_page.tsx +++ b/mobile/src/screens/town_selection/town_selection_page.tsx @@ -9,6 +9,7 @@ import { useCallback, useState } from "react"; import { useAnimatedSelectedIndex } from "../../hooks/useAnimatedSelectedIndex"; import { AnimatedBackground } from "../../common/components/animated_background"; import BottomSheet, { BottomSheetView } from "@gorhom/bottom-sheet"; +import { ErrorPage } from "../error_page/error_page"; interface TownSelectionPageProps { stateId: number; @@ -17,7 +18,7 @@ interface TownSelectionPageProps { const snapPoints = ["15%", "50%"]; export const TownSelectionPage = ({ stateId }: TownSelectionPageProps) => { - const { data, requestStatus } = useGetTowns(stateId); + const { data, requestStatus, refresh } = useGetTowns(stateId); const { selectedStateIndex, onIndexChange, backgroundImageAnimation } = useAnimatedSelectedIndex(200, 200); @@ -31,9 +32,7 @@ export const TownSelectionPage = ({ stateId }: TownSelectionPageProps) => { if (requestStatus === ApiRequestStatus.ERROR || !data) { return ( - - Something went wrong - + ); }