diff --git a/mobile/app.json b/mobile/app.json
index 384972acebb6dcf4953420af816652e47ae7ad93..2329aa44d979dd3482badc525c712402a46098bd 100644
--- a/mobile/app.json
+++ b/mobile/app.json
@@ -50,7 +50,8 @@
}
],
"expo-router",
- "expo-secure-store"
+ "expo-secure-store",
+ "expo-localization"
],
"extra": {
"router": {
diff --git a/mobile/app/state/[stateId]/town/[townId]/activity/[activityId]/travel.tsx b/mobile/app/state/[stateId]/town/[townId]/activity/[activityId]/travel.tsx
index b5c9f27212b4bb504ea98296d52e328237efcd79..e75e46755d133a5e9777374443566c2cbf536b5f 100644
--- a/mobile/app/state/[stateId]/town/[townId]/activity/[activityId]/travel.tsx
+++ b/mobile/app/state/[stateId]/town/[townId]/activity/[activityId]/travel.tsx
@@ -1,9 +1,16 @@
+import { useLocalSearchParams } from "expo-router";
import { View, Text } from "react-native";
+import { ActivityPointScreen } from "../../../../../../../src/screens/activity_point/activity_point";
+
export default function Travel() {
+ const { ...rest } = useLocalSearchParams<{ stateId: string, townId: string, activityId: string, id: string }>();
+
+ if (!rest.stateId || !rest.townId || !rest.activityId || !rest.id) {
+ return null;
+ }
+
return (
-
- Travel
-
+
);
}
\ No newline at end of file
diff --git a/mobile/app/state/[stateId]/town/[townId]/activity/_layout.tsx b/mobile/app/state/[stateId]/town/[townId]/activity/_layout.tsx
index 02fde054082c18137b8722d15cf47b1958c531c7..496d77698aef63ff9bdd88047237d6ae17fb54c2 100644
--- a/mobile/app/state/[stateId]/town/[townId]/activity/_layout.tsx
+++ b/mobile/app/state/[stateId]/town/[townId]/activity/_layout.tsx
@@ -8,6 +8,11 @@ export default function ActivitySelectionScreen() {
headerShown: false
}
} />
+
);
}
\ No newline at end of file
diff --git a/mobile/package-lock.json b/mobile/package-lock.json
index 764d18dfadc74a474f2a281d7f7f3682833b8c89..b8242da5e3e4c2e2bbec9aa7a076062039a1d6b2 100644
--- a/mobile/package-lock.json
+++ b/mobile/package-lock.json
@@ -12,16 +12,19 @@
"@react-native-community/datetimepicker": "7.6.1",
"axios": "^1.6.8",
"expo": "~50.0.14",
+ "expo-av": "~13.10.6",
"expo-barcode-scanner": "~12.9.3",
"expo-camera": "~14.1.1",
"expo-checkbox": "~2.7.0",
"expo-constants": "~15.4.5",
"expo-image-picker": "~14.7.1",
"expo-linking": "~6.2.2",
+ "expo-localization": "~14.8.4",
"expo-router": "~3.4.8",
"expo-screen-orientation": "~6.4.1",
"expo-secure-store": "~12.8.1",
"expo-status-bar": "~1.11.1",
+ "i18n-js": "^4.4.3",
"nativewind": "^2.0.11",
"react": "18.2.0",
"react-hook-form": "^7.51.2",
@@ -6854,6 +6857,14 @@
"node": ">=0.6"
}
},
+ "node_modules/bignumber.js": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
+ "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -8182,6 +8193,14 @@
"md5-file": "^3.2.3"
}
},
+ "node_modules/expo-av": {
+ "version": "13.10.6",
+ "resolved": "https://registry.npmjs.org/expo-av/-/expo-av-13.10.6.tgz",
+ "integrity": "sha512-h3c1fg5yhWnP0RIGO+fhgPx6cmh4B4lnKdXR2i69aC3vs5D5Cu+JlzBon1gLIu6eUo2IVfC0RjSLpfQbcJ4doQ==",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-barcode-scanner": {
"version": "12.9.3",
"resolved": "https://registry.npmjs.org/expo-barcode-scanner/-/expo-barcode-scanner-12.9.3.tgz",
@@ -8275,6 +8294,17 @@
"invariant": "^2.2.4"
}
},
+ "node_modules/expo-localization": {
+ "version": "14.8.4",
+ "resolved": "https://registry.npmjs.org/expo-localization/-/expo-localization-14.8.4.tgz",
+ "integrity": "sha512-WWcG6Bg9s8p9wk5BvdXfxBLCEcvzS75O+nN5O+kWA3+cGDKcbfvExWmXcCSz8hn8y8rRVIEx8wwSgysMTtptUA==",
+ "dependencies": {
+ "rtl-detect": "^1.0.2"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-modules-autolinking": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-1.10.3.tgz",
@@ -9097,6 +9127,16 @@
"node": ">=10.17.0"
}
},
+ "node_modules/i18n-js": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-4.4.3.tgz",
+ "integrity": "sha512-QIIyvJ+wOKdigL4BlgwiFFrpoXeGdlC8EYgori64YSWm1mnhNYYjIfRu5wETFrmiNP2fyD6xIjVG8dlzaiQr/A==",
+ "dependencies": {
+ "bignumber.js": "*",
+ "lodash": "*",
+ "make-plural": "*"
+ }
+ },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -10593,6 +10633,11 @@
"semver": "bin/semver"
}
},
+ "node_modules/make-plural": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.3.0.tgz",
+ "integrity": "sha512-/K3BC0KIsO+WK2i94LkMPv3wslMrazrQhfi5We9fMbLlLjzoOSJWr7TAdupLlDWaJcWxwoNosBkhFDejiu5VDw=="
+ },
"node_modules/makeerror": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
@@ -13044,6 +13089,11 @@
"rimraf": "bin.js"
}
},
+ "node_modules/rtl-detect": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.1.2.tgz",
+ "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ=="
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
diff --git a/mobile/package.json b/mobile/package.json
index 567cd1d5d9c08096fcf5be276fb99a84335ef4c3..51a98de3c9092e2a63367007f9ff1b0a2280d59e 100644
--- a/mobile/package.json
+++ b/mobile/package.json
@@ -17,11 +17,14 @@
"expo-camera": "~14.1.1",
"expo-checkbox": "~2.7.0",
"expo-constants": "~15.4.5",
+ "expo-image-picker": "~14.7.1",
"expo-linking": "~6.2.2",
+ "expo-localization": "~14.8.4",
"expo-router": "~3.4.8",
"expo-screen-orientation": "~6.4.1",
"expo-secure-store": "~12.8.1",
"expo-status-bar": "~1.11.1",
+ "i18n-js": "^4.4.3",
"nativewind": "^2.0.11",
"react": "18.2.0",
"react-hook-form": "^7.51.2",
@@ -31,7 +34,7 @@
"react-native-reanimated": "~3.6.2",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
- "expo-image-picker": "~14.7.1"
+ "expo-av": "~13.10.6"
},
"devDependencies": {
"@babel/core": "^7.20.0",
diff --git a/mobile/src/components/activity_bottom_sheet/activity_bottom_sheet.tsx b/mobile/src/components/activity_bottom_sheet/activity_bottom_sheet.tsx
index d3c9cba62c47c5356a22c8702640c762a640efd9..440756af050ddf4a05d9dee2a1763098316f1eda 100644
--- a/mobile/src/components/activity_bottom_sheet/activity_bottom_sheet.tsx
+++ b/mobile/src/components/activity_bottom_sheet/activity_bottom_sheet.tsx
@@ -1,6 +1,6 @@
import { useRef, useState } from "react";
import BottomSheet, {
- BottomSheetFlatList,
+ BottomSheetFlatList,
BottomSheetScrollView,
BottomSheetView,
} from "@gorhom/bottom-sheet";
@@ -38,34 +38,45 @@ export const ActivityBottomSheet = ({
onChange={onSnapPointChange}
snapPoints={snapPoints}
>
-
+
- {activity.name}
- {
- router.push('/scan')
- }}
- style={styles.do_activity_button}>
- Do Activity
-
+ {activity.name}
+ {
+ router.push("/scan");
+ }}
+ style={styles.do_activity_button}
+ >
+ Do Activity
+
- {
- activity.tags && (
- item}
- ItemSeparatorComponent={() => }
- renderItem={({ item }) => (
-
- {item}
-
- )}
- />
- )
- }
+ {activity.tags && (
+ item}
+ ItemSeparatorComponent={() => }
+ renderItem={({ item }) => (
+
+ {item}
+
+ )}
+ />
+ )}
Description
@@ -92,12 +103,13 @@ const styles = StyleSheet.create({
padding: 20,
},
activity_name_container: {
- flexDirection: 'row',
- justifyContent: 'space-between'
+ flexDirection: "row",
+ justifyContent: "space-between",
},
name_text: {
fontSize: 24,
fontWeight: "bold",
+ width: "70%",
},
do_activity_button: {
height: 40,
@@ -105,9 +117,9 @@ const styles = StyleSheet.create({
backgroundColor: LIGTHT_THEME.color.primary,
padding: 10,
borderRadius: 20,
- justifyContent : 'center',
- alignItems: 'center',
- paddingHorizontal: 20
+ justifyContent: "center",
+ alignItems: "center",
+ paddingHorizontal: 20,
},
do_activity_text: {
color: LIGTHT_THEME.color.white,
diff --git a/mobile/src/components/audio_player/audio_player.tsx b/mobile/src/components/audio_player/audio_player.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ac2c83d11c76a29733d11bc4919f5f311e083fd3
--- /dev/null
+++ b/mobile/src/components/audio_player/audio_player.tsx
@@ -0,0 +1,32 @@
+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/language_icon/language_icon.tsx b/mobile/src/components/language_icon/language_icon.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..657cb3202b0c54312e72a4a117267278c8a8223d
--- /dev/null
+++ b/mobile/src/components/language_icon/language_icon.tsx
@@ -0,0 +1,23 @@
+import { Entypo } from '@expo/vector-icons';
+import { TouchableOpacity } from 'react-native-gesture-handler';
+import { LANG } from '../../lang/translations';
+
+export const LanguageIcon = () => {
+ const changeLanguage = () => {
+ if (LANG.locale === 'es') {
+ // Change to english RTL
+ LANG.locale = 'en';
+
+
+ }
+ else {
+ LANG.locale = 'es';
+ }
+ }
+
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/mobile/src/components/login_form/login_form.tsx b/mobile/src/components/login_form/login_form.tsx
index 39eb15fa5614eb8feebe36671b74c5f1721b2b94..1dd04ee7a77ca2ac8f218af683f353086e25587a 100644
--- a/mobile/src/components/login_form/login_form.tsx
+++ b/mobile/src/components/login_form/login_form.tsx
@@ -4,6 +4,7 @@ import { CustomTextInput } from "../text_input/text_input";
import { LIGTHT_THEME } from "../../constants/theme";
import { LoginFormValues } from "../../hooks/useLoggin";
import { OrDivision } from "../or_division/or_division";
+import { LANG } from "../../lang/translations";
interface LoginFormProps {
control: Control;
@@ -13,7 +14,7 @@ interface LoginFormProps {
export const LoginForm = ({ control, onSubmit }: LoginFormProps) => {
return (
- Login
+ {LANG.t('loginScreen.title')}
{
formState: { errors },
}) => (
{
formState: { errors },
}) => (
{
/>
- Login
+ {LANG.t('loginScreen.loginButton')}
diff --git a/mobile/src/components/or_division/or_division.tsx b/mobile/src/components/or_division/or_division.tsx
index 95430d938c48499a032bcaf4da5dccb1fac2baef..af51421b3e6c4dd50e92715efc2fb0d5f018917e 100644
--- a/mobile/src/components/or_division/or_division.tsx
+++ b/mobile/src/components/or_division/or_division.tsx
@@ -1,10 +1,11 @@
import { View, Text, StyleSheet } from "react-native";
import { LIGTHT_THEME } from "../../constants/theme";
+import { LANG } from "../../lang/translations";
export const OrDivision = () => {
return (
- OR
+ {LANG.t('common.or')}
);
};
diff --git a/mobile/src/contexts/data_context.tsx b/mobile/src/contexts/data_context.tsx
index e216b1c88d40cb59714756d2163de3f4880d290f..b5e0584cbd70f9d025ee62f7b90505fdd9b105f0 100644
--- a/mobile/src/contexts/data_context.tsx
+++ b/mobile/src/contexts/data_context.tsx
@@ -6,29 +6,36 @@ import { AuthRepository } from "../domain/repositories/auth_repository";
import { AuthDataSourceDev } from "../infrastructure/datasource/dev/auth_datasource";
import { AuthRepositoryImpl } from "../infrastructure/repositories/auth_repository";
import { AuthDatasourceProd } from "../infrastructure/datasource/prod/auth_datasource";
+import { ActivityRepository } from "../domain/repositories/activity_repository";
+import { ActivityDatasourceDev } from "../infrastructure/datasource/dev/activity_datasource";
+import { ActivityRepositoryDev } from '../infrastructure/repositories/activity_repository';
type DataContextType = {
statesRepository: StateRepository | null;
authRepository: AuthRepository | null;
+ activityRepository: ActivityRepository | null;
};
type DataContextProviderProps = PropsWithChildren<{}>;
const DataContext = createContext({
statesRepository: null,
- authRepository: null
+ authRepository: null,
+ activityRepository: null
});
export const DataContextProvider = ({ children }: DataContextProviderProps) => {
const statesDataSource = new StateDataSourceDev();
const statesRepository = new StateRepositoryImpl(statesDataSource);
- const authDataSource = new AuthDatasourceProd();
+ const authDataSource = new AuthDataSourceDev();
const authRepository = new AuthRepositoryImpl(authDataSource);
-
+ const activityDataSource = new ActivityDatasourceDev();
+ const activityRepository = new ActivityRepositoryDev(activityDataSource);
const value = {
statesRepository,
- authRepository
+ authRepository,
+ activityRepository
};
return (
diff --git a/mobile/src/domain/datasources/activity_datasource.ts b/mobile/src/domain/datasources/activity_datasource.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9ae21e7a369defd0760c1a1d3187af5bfe8177fa
--- /dev/null
+++ b/mobile/src/domain/datasources/activity_datasource.ts
@@ -0,0 +1,5 @@
+import { ActivityPlaceEntity } from "../entities/activity_place_entity";
+
+export interface ActivityDataSource {
+ getPlaceActivity(activityId: number, townId: number, stateId: number, placeNumber: number): Promise;
+}
\ No newline at end of file
diff --git a/mobile/src/domain/entities/activity_place_entity.ts b/mobile/src/domain/entities/activity_place_entity.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e23bfddc0c1c5ef69394600056386b18d1f06ce8
--- /dev/null
+++ b/mobile/src/domain/entities/activity_place_entity.ts
@@ -0,0 +1,19 @@
+export interface ActivityPlaceEntity {
+ idPlaceActivity: number;
+ name: string;
+ number: number;
+ idPlace: number;
+ imageUrl: string;
+ directions?: DirectionEntity;
+ content: ContentEntity
+}
+
+interface DirectionEntity {
+ content: string;
+ speakUrl: string;
+}
+
+interface ContentEntity {
+ content: string;
+ speakUrl: string;
+}
\ No newline at end of file
diff --git a/mobile/src/domain/repositories/activity_repository.ts b/mobile/src/domain/repositories/activity_repository.ts
new file mode 100644
index 0000000000000000000000000000000000000000..45706508e0122cf58755941178cdc9c7c05b0f1c
--- /dev/null
+++ b/mobile/src/domain/repositories/activity_repository.ts
@@ -0,0 +1,5 @@
+import { ActivityPlaceEntity } from "../entities/activity_place_entity";
+
+export interface ActivityRepository {
+ getPlaceActivity(activityId: number, townId: number, stateId: number, placeNumber: number): Promise;
+}
\ No newline at end of file
diff --git a/mobile/src/hooks/useAudio.ts b/mobile/src/hooks/useAudio.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b166f2ff28ae65e263dda1068033d4100ae70e1e
--- /dev/null
+++ b/mobile/src/hooks/useAudio.ts
@@ -0,0 +1,44 @@
+import { AVPlaybackSource, AVPlaybackStatus, Audio } from "expo-av";
+import { useEffect, useState } from "react";
+
+interface AudioPlayerProps {
+ source: AVPlaybackSource;
+}
+
+export const useAudio = ({ source }: AudioPlayerProps) => {
+ const [sound, setSound] = useState(null);
+ const [isPlaying, setIsPlaying] = useState(false);
+
+ const loadAudio = async () => {
+ const { sound, status } = await Audio.Sound.createAsync(source, {
+ shouldPlay: false
+ });
+ if (status.isLoaded) {
+ setSound(sound);
+ setIsPlaying(status.isPlaying);
+ }
+ }
+
+ const togglePlay = async () => {
+ if (sound) {
+ if (isPlaying) {
+ await sound.pauseAsync();
+ } else {
+ await sound.playAsync();
+ }
+ setIsPlaying(!isPlaying);
+ }
+ }
+
+ useEffect(() => {
+ loadAudio();
+ return () => {
+ if (sound) {
+ sound.pauseAsync();
+ sound.unloadAsync();
+ }
+ }
+ }, [source]);
+
+ return { togglePlay, isPlaying };
+}
\ No newline at end of file
diff --git a/mobile/src/hooks/useGetActivityPoint.ts b/mobile/src/hooks/useGetActivityPoint.ts
new file mode 100644
index 0000000000000000000000000000000000000000..151b5f452c45fa5bec63a2f5cd460bbead7e6e9d
--- /dev/null
+++ b/mobile/src/hooks/useGetActivityPoint.ts
@@ -0,0 +1,19 @@
+import { useDataContext } from "../contexts/data_context"
+import { ActivityPlaceEntity } from "../domain/entities/activity_place_entity";
+import { useGet } from "./useGet";
+
+interface UseGetActivityPointProps {
+ stateId: number;
+ townId: number;
+ activityId: number;
+ placeNumber: number;
+}
+
+export const useGetActivityPoint = ({ activityId, townId, stateId, placeNumber }: UseGetActivityPointProps) => {
+ const { activityRepository } = useDataContext();
+ const callback = () => {
+ return activityRepository!.getPlaceActivity(activityId, townId, stateId, placeNumber);
+ }
+ const { data, requestStatus } = useGet(callback);
+ return { data, requestStatus };
+}
\ No newline at end of file
diff --git a/mobile/src/hooks/useQRScanner.ts b/mobile/src/hooks/useQRScanner.ts
index bddddd319393dd4911e91c67d93bc76a2d13b49c..a6af09ce986ffaff6e386926298167c92986d290 100644
--- a/mobile/src/hooks/useQRScanner.ts
+++ b/mobile/src/hooks/useQRScanner.ts
@@ -20,7 +20,8 @@ export const useQRScanner = () => {
console.log(data.data);
setQRData(data.data);
setScanning(false);
- router.replace(data.data);
+ router.back();
+ router.push(data.data);
};
useEffect(() => {
if (status?.granted) {
diff --git a/mobile/src/infrastructure/datasource/dev/activity_datasource.ts b/mobile/src/infrastructure/datasource/dev/activity_datasource.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21e709766ee0a85354f85d572203dcf603970fa6
--- /dev/null
+++ b/mobile/src/infrastructure/datasource/dev/activity_datasource.ts
@@ -0,0 +1,25 @@
+import { ActivityDataSource } from "../../../domain/datasources/activity_datasource";
+import { ActivityPlaceEntity } from "../../../domain/entities/activity_place_entity";
+
+export class ActivityDatasourceDev implements ActivityDataSource {
+ async getPlaceActivity(activityId: number, townId: number, stateId: number, placeNumber: number): Promise {
+ return new Promise((resolve) => {
+ resolve(placeActivities.find(place => place.idPlace === activityId && place.number === placeNumber) as ActivityPlaceEntity);
+ });
+ }
+
+}
+
+const placeActivities: ActivityPlaceEntity[] = [
+ {
+ idPlaceActivity: 1,
+ name: "Puerta del santuario de Nuestra Señora de la Soledad",
+ number: 1,
+ idPlace: 1,
+ imageUrl: "https://fastly.4sqi.net/img/general/200x200/50314270_u7Rp3Fk5Z9eaNC24PzKmzZ2iBOublx1bVaqDHE8TNFM.jpg",
+ content: {
+ content: "La puerta del santuario de Nuestra Señora de la Soledad es un lugar de culto católico en Jerez de García Salinas, Zacatecas, México. Es un lugar de peregrinación y oración para los fieles católicos.",
+ speakUrl: "https://www.google.com"
+ }
+ },
+];
\ No newline at end of file
diff --git a/mobile/src/infrastructure/datasource/dev/state_datasource.ts b/mobile/src/infrastructure/datasource/dev/state_datasource.ts
index 555dda5c4589f81f4fef7513bae498a95e405332..0fb31de13e4dc689d1b2d32e1279efb2dafd74d5 100644
--- a/mobile/src/infrastructure/datasource/dev/state_datasource.ts
+++ b/mobile/src/infrastructure/datasource/dev/state_datasource.ts
@@ -78,12 +78,12 @@ const towns: TownEntity[] = [
const activities: ActivityInfoEntity[] = [
{
id: 1,
- name: 'Feria de la primavera',
- description: 'Feria de la primavera en Jerez',
+ name: 'Santuario de Nuestra Señora de la Soledad',
+ description: 'Santuario de Nuestra Señora de la Soledad en Jerez',
townId: 1,
- available: 'Primavera',
+ available: 'Todo el año',
location: 'Jerez, Zacatecas',
- imageUri: 'https://www.liderempresarial.com/wp-content/uploads/2022/03/Feria-de-Primavera-Jerez-2022-1024x1024.jpg'
+ imageUri: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAOt4JS0AzrMsxDp0caz26vuanMq692g17nbI6-_nycw&s'
},
{
id: 2,
diff --git a/mobile/src/infrastructure/repositories/activity_repository.ts b/mobile/src/infrastructure/repositories/activity_repository.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c4e2083ce2c342e2c9fa9de54eb0e27e26875a9
--- /dev/null
+++ b/mobile/src/infrastructure/repositories/activity_repository.ts
@@ -0,0 +1,12 @@
+import { ActivityDataSource } from "../../domain/datasources/activity_datasource";
+import { ActivityPlaceEntity } from "../../domain/entities/activity_place_entity";
+import { ActivityRepository } from "../../domain/repositories/activity_repository";
+
+export class ActivityRepositoryDev implements ActivityRepository {
+ constructor(
+ private activityDataSource: ActivityDataSource
+ ) {}
+ async getPlaceActivity(activityId: number, townId: number, stateId: number, placeNumber: number): Promise {
+ return this.activityDataSource.getPlaceActivity(activityId, townId, stateId, placeNumber);
+ }
+}
\ No newline at end of file
diff --git a/mobile/src/lang/english_lang.ts b/mobile/src/lang/english_lang.ts
new file mode 100644
index 0000000000000000000000000000000000000000..991949b71c630317ef2b4f463277e8d6e89297b9
--- /dev/null
+++ b/mobile/src/lang/english_lang.ts
@@ -0,0 +1,31 @@
+import { Lang } from "./lang";
+
+export const ENGLISH_LANG: Lang = {
+ loginScreen:{
+ title: "Login",
+ loginButton: "Login",
+ registerButton: "Register",
+ },
+ registerScreen:{
+ title: "Register",
+ loginButton: "Login",
+ registerButton: "Register",
+ },
+ forms : {
+ email: "Email",
+ password: "Password",
+ confirmPassword: "Confirm password",
+ name: "Name",
+ lastName: "Last name",
+ dateOfBirth: "Date of birth",
+ },
+ formsErrors : {
+ invalidEmailFormat: "Invalid email format",
+ requiredField: "Required field",
+ passwordNotMatch: "Passwords do not match",
+ emailAlreadyInUse: "Email already in use",
+ },
+ common : {
+ or: "or",
+ }
+}
\ No newline at end of file
diff --git a/mobile/src/lang/lang.ts b/mobile/src/lang/lang.ts
new file mode 100644
index 0000000000000000000000000000000000000000..22dd3943b3eb30f6574e6c7f1701eb2e7d9ec0f3
--- /dev/null
+++ b/mobile/src/lang/lang.ts
@@ -0,0 +1,30 @@
+export interface Lang {
+ loginScreen:{
+ title: string;
+ loginButton: string;
+ registerButton: string;
+ },
+ registerScreen:{
+ title: string;
+ loginButton: string;
+ registerButton: string;
+ },
+ forms : {
+ email: string;
+ password: string;
+ confirmPassword: string;
+ name: string;
+ lastName: string;
+ dateOfBirth: string;
+ },
+ formsErrors : {
+ invalidEmailFormat: string;
+ requiredField: string;
+ passwordNotMatch: string;
+ emailAlreadyInUse: string;
+ },
+ common : {
+ or: string;
+ }
+}
+
\ No newline at end of file
diff --git a/mobile/src/lang/spanish_lang.ts b/mobile/src/lang/spanish_lang.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c7eb5fcdb383cff9db2a44ba535f4ae01483660
--- /dev/null
+++ b/mobile/src/lang/spanish_lang.ts
@@ -0,0 +1,31 @@
+import { Lang } from "./lang";
+
+export const SPANISH_LANG: Lang = {
+ loginScreen:{
+ title: "Iniciar sesión",
+ loginButton: "Iniciar sesión",
+ registerButton: "Registrarse",
+ },
+ registerScreen:{
+ title: "Registrarse",
+ loginButton: "Iniciar sesión",
+ registerButton: "Registrarse",
+ },
+ forms : {
+ email: "Correo electrónico",
+ password: "Contraseña",
+ confirmPassword: "Confirmar contraseña",
+ name: "Nombre",
+ lastName: "Apellido",
+ dateOfBirth: "Fecha de nacimiento",
+ },
+ formsErrors : {
+ invalidEmailFormat: "Formato de correo electrónico inválido",
+ requiredField: "Campo requerido",
+ passwordNotMatch: "Las contraseñas no coinciden",
+ emailAlreadyInUse: "Correo electrónico ya en uso",
+ },
+ common : {
+ or: "o",
+ }
+};
\ No newline at end of file
diff --git a/mobile/src/lang/translations.ts b/mobile/src/lang/translations.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a21b25e350aa762239a30abc7ee3f4029683bf29
--- /dev/null
+++ b/mobile/src/lang/translations.ts
@@ -0,0 +1,18 @@
+import { ENGLISH_LANG } from "./english_lang";
+import { SPANISH_LANG } from "./spanish_lang";
+import { I18n } from 'i18n-js';
+
+const translations = {
+ en : ENGLISH_LANG,
+ es : SPANISH_LANG
+}
+
+const LANG = new I18n(translations);
+
+LANG.locale = 'es';
+
+LANG.enableFallback = true;
+
+export { LANG };
+
+
diff --git a/mobile/src/screens/activity_point/activity_point.tsx b/mobile/src/screens/activity_point/activity_point.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6769d6403aa2389ebcfe3464da3888e05ed0db83
--- /dev/null
+++ b/mobile/src/screens/activity_point/activity_point.tsx
@@ -0,0 +1,81 @@
+import { Image, Text, View, StyleSheet } from "react-native";
+import { FullPageLoader } from "../../components/full_page_loader/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";
+
+interface ActivityPointScreenProps {
+ stateId: number;
+ townId: number;
+ activityId: number;
+ id: number;
+}
+
+export const ActivityPointScreen = ({
+ stateId,
+ townId,
+ activityId,
+ id,
+}: ActivityPointScreenProps) => {
+ const { data, requestStatus } = useGetActivityPoint({
+ activityId,
+ townId,
+ stateId,
+ placeNumber: id,
+ });
+
+ if (requestStatus === ApiRequestStatus.LOADING) {
+ return ;
+ }
+
+ if (requestStatus === ApiRequestStatus.ERROR || !data) {
+ return null;
+ }
+ return (
+
+
+ {data.name}
+
+
+
+ {data.content.content}
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ gap: 15,
+ justifyContent: "space-between",
+ },
+ placeContainer: {
+ flex: 1,
+ gap: 20,
+ padding: 20,
+ },
+ title: {
+ fontSize: 24,
+ fontWeight: "bold",
+ textAlign: "center",
+ },
+ contentText: {
+ fontSize: 16,
+ },
+ image: {
+ height: 300,
+ width: "100%",
+ },
+});
diff --git a/mobile/src/screens/login/login_page.tsx b/mobile/src/screens/login/login_page.tsx
index 385cb637c7d5b1cf99d4bf728176f13509dbece0..68dacfb00cd4ba2f5c6901489092457da1395995 100644
--- a/mobile/src/screens/login/login_page.tsx
+++ b/mobile/src/screens/login/login_page.tsx
@@ -3,6 +3,8 @@ import { LoginForm } from "../../components/login_form/login_form";
import { useLoggin } from "../../hooks/useLoggin";
import { LIGTHT_THEME } from "../../constants/theme";
import { router } from "expo-router";
+import { LANG } from "../../lang/translations";
+import { LanguageIcon } from "../../components/language_icon/language_icon";
const loginImage = require("../../../assets/login-image.jpg");
export const LoginPage = () => {
@@ -18,7 +20,7 @@ export const LoginPage = () => {
-
);