From bc612f98dd6460962b7dd11f5c77833738f20ee3 Mon Sep 17 00:00:00 2001 From: emkartal1 Date: Sun, 26 Nov 2023 14:33:56 +0100 Subject: [PATCH] add a new API route and getSpots :white_check_mark: --- src/Api/src/controllers/spotifyController.ts | 1 - src/Api/src/controllers/userController.ts | 32 +++++++++-- src/Api/src/database/UserSchema.ts | 4 +- src/Api/src/models/Location.ts | 15 +++--- src/Api/src/models/Music.ts | 4 +- src/Api/src/models/Person.ts | 5 ++ src/Api/src/services/LocationService.ts | 17 +++--- src/Api/src/services/TokenService.ts | 4 +- src/Api/src/services/UserService.ts | 19 +++++++ src/FLAD/components/LoadingComponent.tsx | 9 ++-- src/FLAD/{model => models}/Artist.ts | 0 src/FLAD/{model => models}/Music.ts | 0 .../{model => models}/MusicServiceProvider.ts | 0 src/FLAD/{model => models}/Person.ts | 10 +++- src/FLAD/{model => models}/Spot.ts | 19 +++++-- src/FLAD/{model => models}/User.ts | 0 .../{model => models}/mapper/ArtistMapper.ts | 0 .../{model => models}/mapper/MusicMapper.ts | 0 .../{model => models}/mapper/SpotMapper.ts | 2 +- .../{model => models}/mapper/UserMapper.ts | 0 src/FLAD/navigation/HomeNavigation.tsx | 54 +++++++++++++++++-- src/FLAD/redux/actions/appActions.ts | 4 +- src/FLAD/redux/actions/spotActions.tsx | 10 +--- src/FLAD/redux/actions/userActions.tsx | 2 +- src/FLAD/redux/reducers/appReducer.tsx | 11 ++-- src/FLAD/redux/reducers/userReducer.tsx | 2 +- src/FLAD/redux/thunk/appThunk.tsx | 12 ++--- src/FLAD/redux/thunk/authThunk.tsx | 4 +- src/FLAD/redux/thunk/spotThunk.tsx | 44 ++++++++++++++- src/FLAD/redux/thunk/userThunk.tsx | 2 +- src/FLAD/redux/types/spotTypes.tsx | 1 - src/FLAD/screens/DetailScreen.tsx | 6 +-- src/FLAD/screens/FavoriteScreen.tsx | 2 +- src/FLAD/screens/SettingScreen.tsx | 35 ++++++++---- src/FLAD/screens/SpotScreen.tsx | 20 +++---- src/FLAD/services/EmptyMusicService.ts | 3 ++ .../musics/interfaces/IMusicService.ts | 2 +- .../services/musics/spotify/SpotifyService.ts | 4 +- 38 files changed, 259 insertions(+), 100 deletions(-) create mode 100644 src/Api/src/models/Person.ts rename src/FLAD/{model => models}/Artist.ts (100%) rename src/FLAD/{model => models}/Music.ts (100%) rename src/FLAD/{model => models}/MusicServiceProvider.ts (100%) rename src/FLAD/{model => models}/Person.ts (61%) rename src/FLAD/{model => models}/Spot.ts (61%) rename src/FLAD/{model => models}/User.ts (100%) rename src/FLAD/{model => models}/mapper/ArtistMapper.ts (100%) rename src/FLAD/{model => models}/mapper/MusicMapper.ts (100%) rename src/FLAD/{model => models}/mapper/SpotMapper.ts (57%) rename src/FLAD/{model => models}/mapper/UserMapper.ts (100%) diff --git a/src/Api/src/controllers/spotifyController.ts b/src/Api/src/controllers/spotifyController.ts index e1e02f2..9c91125 100644 --- a/src/Api/src/controllers/spotifyController.ts +++ b/src/Api/src/controllers/spotifyController.ts @@ -1,4 +1,3 @@ - import IController from './interfaces/IController'; import { Router, Request, Response, NextFunction } from 'express'; import HttpException from '../exception/HttpException'; diff --git a/src/Api/src/controllers/userController.ts b/src/Api/src/controllers/userController.ts index 4eea2d7..1b337a4 100644 --- a/src/Api/src/controllers/userController.ts +++ b/src/Api/src/controllers/userController.ts @@ -35,6 +35,7 @@ class UserController implements IController { this.login ); this.router.get(`${this.path}`, authenticator, this.getUser); + this.router.get(`${this.path}s`, authenticator, this.getUsers); this.router.delete(`${this.path}`, authenticator, this.deleteUser); this.router.get(`${this.path}/nextTo`, authenticator, this.getUserNext); this.router.delete(`${this.path}/musics/:id`, authenticator, this.deleteMusic); @@ -134,6 +135,27 @@ class UserController implements IController { res.status(200).send({ data: req.user }); }; + private getUsers = async ( + req: Request, + res: Response, + next: NextFunction + ): Promise => { + const userIds = req.query.ids as string; + + if (!userIds) { + return res.status(400).json({ message: 'Please provide user ids' }); + } + + const userIdArray = userIds.split('&'); + + try { + const users = await this.userService.getUsers(userIdArray); + res.json(users); + } catch (error) { + res.status(500).json({ message: error.message }); + } + }; + private deleteUser = async ( req: Request, res: Response, @@ -202,13 +224,13 @@ class UserController implements IController { ): Promise => { try { const { _id } = req.user; - const { idMusic, idUser } = req.body; - if (!idMusic || !idUser) { - return res.status(400).json({ error: 'idMusic and idUser are required fields.' }); + const { musicId, userId } = req.body; + if (!musicId || !userId) { + return res.status(400).json({ error: 'musicId and userId are required fields.' }); } const music: IMusic = { - idMusic, - idUser, + musicId, + userId, date: new Date(), }; await this.userService.addMusic(_id, music); diff --git a/src/Api/src/database/UserSchema.ts b/src/Api/src/database/UserSchema.ts index 8ea93a4..51ae320 100644 --- a/src/Api/src/database/UserSchema.ts +++ b/src/Api/src/database/UserSchema.ts @@ -34,8 +34,8 @@ const userSchema = new Schema({ }, musics_likes: { type: [{ - idMusic: String, - idUser: String, + musicId: String, + userId: String, date: Date }], default: [] diff --git a/src/Api/src/models/Location.ts b/src/Api/src/models/Location.ts index 1384b30..39a9251 100644 --- a/src/Api/src/models/Location.ts +++ b/src/Api/src/models/Location.ts @@ -1,15 +1,17 @@ import { Document } from 'mongoose'; export class UserLocation { + _id: string; userId: string; - musicId : string; - latitude : number; - longitude: number; - constructor(userId: string, musicId : string,latitude: number, longitude: number){ + musicId: string; + distance: number; + date: Date; + constructor(id: string, userId: string, musicId: string, distance: number, date: Date) { + this._id = id; this.userId = userId; this.musicId = musicId; - this.latitude = latitude; - this.longitude = longitude; + this.distance = distance; + this.date = date; } } @@ -18,4 +20,5 @@ export class Location extends Document { musicId: string; latitude: number; longitude: number; + updatedAt: Date; } \ No newline at end of file diff --git a/src/Api/src/models/Music.ts b/src/Api/src/models/Music.ts index 6146620..9da69eb 100644 --- a/src/Api/src/models/Music.ts +++ b/src/Api/src/models/Music.ts @@ -1,5 +1,5 @@ export interface IMusic { - idMusic: string; - idUser: string; + musicId: string; + userId: string; date: Date; } \ No newline at end of file diff --git a/src/Api/src/models/Person.ts b/src/Api/src/models/Person.ts new file mode 100644 index 0000000..22abca9 --- /dev/null +++ b/src/Api/src/models/Person.ts @@ -0,0 +1,5 @@ +export interface IPerson { + _id: string; + name: string; + image: string; +} \ No newline at end of file diff --git a/src/Api/src/services/LocationService.ts b/src/Api/src/services/LocationService.ts index 28c05c2..936b3e9 100644 --- a/src/Api/src/services/LocationService.ts +++ b/src/Api/src/services/LocationService.ts @@ -17,17 +17,14 @@ class LocationService { } let usersLocation: UserLocation[] = []; - snapshot.forEach(doc => { - usersLocation.push(new UserLocation(doc.userId, doc.musicId, doc.latitude, doc.longitude)); - }); - const listUser: Record = {}; - usersLocation.forEach(user => { - const distance = this.distanceBetween(latitude, longitude, user.latitude, user.longitude); - if (distance <= 100) { - listUser[user.userId] = user.musicId; + + snapshot.forEach(location => { + const distance = this.distanceBetween(latitude, longitude, location.latitude, location.longitude); + if (distance <= 1000) { + usersLocation.push(new UserLocation(location._id, location.userId, location.musicId, Math.ceil(distance / 200) * 200, location.updatedAt)); } }); - return { listUser }; + return { data: usersLocation }; } private distanceBetween(lat1: number, lon1: number, lat2: number, lon2: number): number { @@ -48,7 +45,7 @@ class LocationService { dist = Math.acos(dist); dist = dist * 180 / Math.PI; dist = dist * 60 * 1.1515; - dist = dist * 1.609344; + dist = dist * 1.609344 * 1000; return dist; } diff --git a/src/Api/src/services/TokenService.ts b/src/Api/src/services/TokenService.ts index d842957..cee35b6 100644 --- a/src/Api/src/services/TokenService.ts +++ b/src/Api/src/services/TokenService.ts @@ -3,7 +3,7 @@ import User from '../models/User'; import Token from '../models/Token'; export const createToken = (user: User): string => { - return jwt.sign({ id: user._id }, "dave" as jwt.Secret, { + return jwt.sign({ id: user._id }, process.env.SECRET_JWT as jwt.Secret, { expiresIn: '1d', }); }; @@ -14,7 +14,7 @@ export const verifyToken = async ( return new Promise((resolve, reject) => { jwt.verify( token, - "dave" as jwt.Secret, + process.env.SECRET_JWT as jwt.Secret, (err, payload) => { if (err) return reject(err); resolve(payload as Token); diff --git a/src/Api/src/services/UserService.ts b/src/Api/src/services/UserService.ts index b03cfee..1afe066 100644 --- a/src/Api/src/services/UserService.ts +++ b/src/Api/src/services/UserService.ts @@ -2,6 +2,8 @@ import { IMusic } from "../models/Music"; import LocationSchema from "../database/LocationSchema"; import UserSchema from "../database/UserSchema"; import token from "./TokenService"; +import { IPerson } from "../models/Person"; +import mongoose from "mongoose"; class UserService { private user = UserSchema; @@ -56,6 +58,23 @@ class UserService { } } + public async getUsers( + ids: string[] + ): Promise { + try { + const validIds = ids.filter(id => mongoose.Types.ObjectId.isValid(id)); + + if (validIds.length === 0) { + return []; + } + + return await this.user.find({ _id: { $in: validIds } }) + .select('_id name image') + } catch (error: any) { + throw new Error(error.message); + } + } + public async addMusic(userId: string, music: IMusic): Promise { try { return await this.user.findByIdAndUpdate(userId, { diff --git a/src/FLAD/components/LoadingComponent.tsx b/src/FLAD/components/LoadingComponent.tsx index a628b90..6341159 100644 --- a/src/FLAD/components/LoadingComponent.tsx +++ b/src/FLAD/components/LoadingComponent.tsx @@ -5,16 +5,16 @@ import Animated, { interpolate, useAnimatedStyle, useSharedValue, withRepeat, wi export default function Loading() { const size = 100 - const progresse = useSharedValue(1); + const progress = useSharedValue(1); useEffect(() => { - progresse.value = withRepeat(withTiming(0.01, { duration: 750 }), -1, true); - }, [progresse]); + progress.value = withRepeat(withTiming(0.01, { duration: 750 }), -1, true); + }, [progress]); const breatheStyleSquare = useAnimatedStyle(() => { const borderRange = interpolate - (progresse.value, + (progress.value, [0, 1], [(size + 20), (size)], ); @@ -26,7 +26,6 @@ export default function Loading() { borderWidth: size / 10, borderColor: "#F80404", shadowColor: "#F40C1C", - //shadowOffset: { width: 0, height: 0 }, shadowOpacity: 1, shadowRadius: 10, }; diff --git a/src/FLAD/model/Artist.ts b/src/FLAD/models/Artist.ts similarity index 100% rename from src/FLAD/model/Artist.ts rename to src/FLAD/models/Artist.ts diff --git a/src/FLAD/model/Music.ts b/src/FLAD/models/Music.ts similarity index 100% rename from src/FLAD/model/Music.ts rename to src/FLAD/models/Music.ts diff --git a/src/FLAD/model/MusicServiceProvider.ts b/src/FLAD/models/MusicServiceProvider.ts similarity index 100% rename from src/FLAD/model/MusicServiceProvider.ts rename to src/FLAD/models/MusicServiceProvider.ts diff --git a/src/FLAD/model/Person.ts b/src/FLAD/models/Person.ts similarity index 61% rename from src/FLAD/model/Person.ts rename to src/FLAD/models/Person.ts index 3473eff..aa22acb 100644 --- a/src/FLAD/model/Person.ts +++ b/src/FLAD/models/Person.ts @@ -3,7 +3,7 @@ export class Person { private _name: string; public image: string; - constructor(id: string, idSpotify: string, name: string, email: string, creationDate: Date, image: string) { + constructor(id: string, name: string, image: string) { this._id = id; this._name = name; this.image = image; @@ -13,7 +13,15 @@ export class Person { return this._id; } + set id(value: string) { + this._id = value; + } + get name(): string { return this._name; } + + set name(value: string) { + this._name = value; + } } \ No newline at end of file diff --git a/src/FLAD/model/Spot.ts b/src/FLAD/models/Spot.ts similarity index 61% rename from src/FLAD/model/Spot.ts rename to src/FLAD/models/Spot.ts index 24b0b5e..37f5315 100644 --- a/src/FLAD/model/Spot.ts +++ b/src/FLAD/models/Spot.ts @@ -1,22 +1,31 @@ import Music from "./Music"; -import { Person } from "./Person"; export class Spot { + private _id: string; private _user: string; private _music: Music; private _date: Date; - constructor(userId: string, music: Music, date: Date) { - this._user = userId; + constructor(id: string, user: string, music: Music, date: Date) { + this._id = id; + this._user = user; this._music = music; this._date = date; } - get userSpotifyId(): string { + get id(): string { + return this._id; + } + + set id(value: string) { + this._id = value; + } + + get user(): string { return this._user; } - set userSpotifyId(value: string) { + set user(value: string) { this._user = value; } diff --git a/src/FLAD/model/User.ts b/src/FLAD/models/User.ts similarity index 100% rename from src/FLAD/model/User.ts rename to src/FLAD/models/User.ts diff --git a/src/FLAD/model/mapper/ArtistMapper.ts b/src/FLAD/models/mapper/ArtistMapper.ts similarity index 100% rename from src/FLAD/model/mapper/ArtistMapper.ts rename to src/FLAD/models/mapper/ArtistMapper.ts diff --git a/src/FLAD/model/mapper/MusicMapper.ts b/src/FLAD/models/mapper/MusicMapper.ts similarity index 100% rename from src/FLAD/model/mapper/MusicMapper.ts rename to src/FLAD/models/mapper/MusicMapper.ts diff --git a/src/FLAD/model/mapper/SpotMapper.ts b/src/FLAD/models/mapper/SpotMapper.ts similarity index 57% rename from src/FLAD/model/mapper/SpotMapper.ts rename to src/FLAD/models/mapper/SpotMapper.ts index 8a2c257..d12f14d 100644 --- a/src/FLAD/model/mapper/SpotMapper.ts +++ b/src/FLAD/models/mapper/SpotMapper.ts @@ -2,6 +2,6 @@ import { Spot } from "../Spot"; export class SpotMapper { public static toModel(spot: any): Spot { - return new Spot(spot.idUser, spot.music, new Date(spot.date)); + return new Spot(spot._id, spot.userId, spot.music, new Date(spot.date)); } } \ No newline at end of file diff --git a/src/FLAD/model/mapper/UserMapper.ts b/src/FLAD/models/mapper/UserMapper.ts similarity index 100% rename from src/FLAD/model/mapper/UserMapper.ts rename to src/FLAD/models/mapper/UserMapper.ts diff --git a/src/FLAD/navigation/HomeNavigation.tsx b/src/FLAD/navigation/HomeNavigation.tsx index 6c66b20..c899bad 100644 --- a/src/FLAD/navigation/HomeNavigation.tsx +++ b/src/FLAD/navigation/HomeNavigation.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { View, StyleSheet, Platform, Alert } from 'react-native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { NavigationContainer } from '@react-navigation/native'; @@ -15,17 +15,23 @@ import { colorsLight } from '../constants/colorsLight'; import { getUserCurrentMusic } from '../redux/thunk/appThunk'; import { logout } from '../redux/thunk/authThunk'; import { setAccessError, setErrorEmptyMusic } from '../redux/actions/userActions'; - +import * as Location from 'expo-location'; +import { getSpotList } from '../redux/thunk/spotThunk'; +import Music from '../models/Music'; export default function HomeNavigation() { //@ts-ignore - const favoritesMusicLength = useSelector(state => state.appReducer.nbAddedFavoritesMusic); + const favoritesMusicLength = useSelector(state => state.appReducer.nbAddedFavoriteMusic); //@ts-ignore const accessError = useSelector(state => state.userReducer.accessError); //@ts-ignore const errorEmptyMusic = useSelector(state => state.userReducer.errorEmptyMusic); // @ts-ignore const isDark = useSelector(state => state.userReducer.dark); + // @ts-ignore + const currentMusic: Music = useSelector(state => state.appReducer.userCurrentMusic); + const [locationPermission, setLocationPermission] = useState(false); + const style = isDark ? colorsDark : colorsLight; const BottomTabNavigator = createBottomTabNavigator(); const MyTheme = { @@ -40,11 +46,49 @@ export default function HomeNavigation() { const dispatch = useDispatch(); + const requestLocationPermission = async () => { + const { status } = await Location.requestForegroundPermissionsAsync(); + if (status !== 'granted') { + alert( + "Oups ! Il semble que l'accès à votre localisation soit désactivé. Pour découvrir la musique des personnes autour de vous, veuillez autoriser l'accès à la localisation dans les paramètres de votre appareil." + ); + } else { + setLocationPermission(true); + } + } + + useEffect(() => { + requestLocationPermission(); + }, []); + useEffect(() => { - //@ts-ignore - dispatch(getUserCurrentMusic()); + const getSpots = async () => { + //@ts-ignore + dispatch(getUserCurrentMusic()); + } + + getSpots(); + + const interval = setInterval(getSpots, 30000); + return () => { + clearInterval(interval); + }; }, []); + useEffect(() => { + getSpots(); + }, [currentMusic]); + + const getSpots = async () => { + if (currentMusic && locationPermission) { + const location = await Location.getCurrentPositionAsync({ + accuracy: Location.Accuracy.Low, + }); + //@ts-ignore + dispatch(getSpotList(location.coords.longitude, location.coords.latitude, currentMusic._id)); + } + } + useEffect(() => { if (accessError) { Alert.alert( diff --git a/src/FLAD/redux/actions/appActions.ts b/src/FLAD/redux/actions/appActions.ts index a4e662a..b2ed4e4 100644 --- a/src/FLAD/redux/actions/appActions.ts +++ b/src/FLAD/redux/actions/appActions.ts @@ -1,5 +1,5 @@ -import Music from "../../model/Music"; -import { Spot } from "../../model/Spot"; +import Music from "../../models/Music"; +import { Spot } from "../../models/Spot"; import { favoritesTypes } from "../types/favoritesTypes"; import { spotifyTypes } from "../types/spotifyTypes"; diff --git a/src/FLAD/redux/actions/spotActions.tsx b/src/FLAD/redux/actions/spotActions.tsx index 677691a..0938c7c 100644 --- a/src/FLAD/redux/actions/spotActions.tsx +++ b/src/FLAD/redux/actions/spotActions.tsx @@ -1,5 +1,6 @@ -import { Spot } from "../../model/Spot"; +import { Spot } from "../../models/Spot"; import { spotTypes } from "../types/spotTypes"; +import { userTypes } from "../types/userTypes"; export const setSpotList = (spotList: Spot[]) => { return { @@ -13,11 +14,4 @@ export const removeFromSpotList = (spot: Spot) => { type: spotTypes.REMOVE_SPOT, payload: spot } -} - -export const addSpotListMock = (spotList: Spot[]) => { - return { - type: spotTypes.ADD_SPOT_MOCK, - payload: spotList, - }; } \ No newline at end of file diff --git a/src/FLAD/redux/actions/userActions.tsx b/src/FLAD/redux/actions/userActions.tsx index f31cbed..e8ec712 100644 --- a/src/FLAD/redux/actions/userActions.tsx +++ b/src/FLAD/redux/actions/userActions.tsx @@ -1,4 +1,4 @@ -import { User } from "../../model/User"; +import { User } from "../../models/User"; import { userTypes } from "../types/userTypes"; export interface LoginCredentials { diff --git a/src/FLAD/redux/reducers/appReducer.tsx b/src/FLAD/redux/reducers/appReducer.tsx index 8ea05f7..35d5421 100644 --- a/src/FLAD/redux/reducers/appReducer.tsx +++ b/src/FLAD/redux/reducers/appReducer.tsx @@ -1,13 +1,14 @@ -import { Spot } from "../../model/Spot"; +import { Spot } from "../../models/Spot"; import { favoritesTypes } from "../types/favoritesTypes"; import { spotifyTypes } from "../types/spotifyTypes"; import { spotTypes } from "../types/spotTypes"; +import { userTypes } from "../types/userTypes"; const initialState = { spot: [] as Spot[], favoriteMusic: [] as Spot[], userCurrentMusic: null, - nbAddedFavoritesMusic: 0 + nbAddedFavoriteMusic: 0 } const appReducer = (state = initialState, action: any) => { @@ -16,14 +17,16 @@ const appReducer = (state = initialState, action: any) => { return { ...state, favoriteMusic: action.payload }; case spotTypes.FETCH_SPOT: const uniqueSpots = action.payload.filter((spot: Spot) => { - return !state.spot.some((s) => s.userSpotifyId === spot.userSpotifyId && s.music.id === spot.music.id); + return !state.spot.some((s) => s.user === spot.user && s.music.id === spot.music.id); }); const updatedSpotList = [...uniqueSpots, ...state.spot]; return { ...state, spot: updatedSpotList }; case spotTypes.REMOVE_SPOT: - return { ...state, spot: state.spot.filter((spot) => spot.userSpotifyId !== action.payload.userSpotifyId && spot.music.id !== action.payload.music.id) }; + return { ...state, spot: state.spot.filter((spot) => spot.user !== action.payload.user && spot.music.id !== action.payload.music.id) }; case spotifyTypes.GET_USER_CURRENT_MUSIC: return { ...state, userCurrentMusic: action.payload }; + case userTypes.USER_LOGOUT: + return { ...state, spot: [], favoriteMusic: [], userCurrentMusic: null, nbAddedFavoriteMusic: 0 }; default: return state; } diff --git a/src/FLAD/redux/reducers/userReducer.tsx b/src/FLAD/redux/reducers/userReducer.tsx index 7be604d..874aa54 100644 --- a/src/FLAD/redux/reducers/userReducer.tsx +++ b/src/FLAD/redux/reducers/userReducer.tsx @@ -1,5 +1,5 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; -import { User } from "../../model/User"; +import { User } from "../../models/User"; import { userTypes } from "../types/userTypes"; const initialState = { diff --git a/src/FLAD/redux/thunk/appThunk.tsx b/src/FLAD/redux/thunk/appThunk.tsx index 1630200..ec4a928 100644 --- a/src/FLAD/redux/thunk/appThunk.tsx +++ b/src/FLAD/redux/thunk/appThunk.tsx @@ -1,11 +1,11 @@ import axios from "axios"; import * as SecureStore from 'expo-secure-store'; -import { Spot } from "../../model/Spot"; +import { Spot } from "../../models/Spot"; import configs from "../../constants/config"; -import { MusicServiceProvider } from "../../model/MusicServiceProvider"; +import { MusicServiceProvider } from "../../models/MusicServiceProvider"; import { setFavoriteMusic, setUserCurrentMusic } from "../actions/appActions"; import { setAccessError, setErrorEmptyMusic } from "../actions/userActions"; -import { SpotMapper } from "../../model/mapper/SpotMapper"; +import { SpotMapper } from "../../models/mapper/SpotMapper"; export const getUserCurrentMusic = () => { //@ts-ignore @@ -59,12 +59,12 @@ export const getFavoriteMusic = () => { { headers } ) - const musicIds = resp.data.musics.map((music: any) => music.idMusic); + const musicIds = resp.data.musics.map((music: any) => music.musicId); const musics = await MusicServiceProvider.musicService.getMusicsWithIds(musicIds); const result = resp.data.musics - .filter((music: any) => musics.some((m: any) => m.id === music.idMusic)) + .filter((music: any) => musics.some((m: any) => m.id === music.musicId)) .map((music: any) => { - const matchingMusic = musics.find((m: any) => m.id === music.idMusic); + const matchingMusic = musics.find((m: any) => m.id === music.musicId); return { ...music, music: matchingMusic, diff --git a/src/FLAD/redux/thunk/authThunk.tsx b/src/FLAD/redux/thunk/authThunk.tsx index 46a9ac3..e2e43f4 100644 --- a/src/FLAD/redux/thunk/authThunk.tsx +++ b/src/FLAD/redux/thunk/authThunk.tsx @@ -2,8 +2,8 @@ import axios from "axios"; import configs from "../../constants/config"; import { LoginCredentials, RegisterCredentials, restoreToken, userLogin, userLogout, setErrorLogin, setErrorSignup, setErrorNetwork } from "../actions/userActions"; import * as SecureStore from 'expo-secure-store'; -import { UserMapper } from "../../model/mapper/UserMapper"; -import { MusicServiceProvider } from "../../model/MusicServiceProvider"; +import { UserMapper } from "../../models/mapper/UserMapper"; +import { MusicServiceProvider } from "../../models/MusicServiceProvider"; const keyRemember = 'rememberUser'; diff --git a/src/FLAD/redux/thunk/spotThunk.tsx b/src/FLAD/redux/thunk/spotThunk.tsx index 72c1659..4b2ecd9 100644 --- a/src/FLAD/redux/thunk/spotThunk.tsx +++ b/src/FLAD/redux/thunk/spotThunk.tsx @@ -1,9 +1,49 @@ import axios from "axios"; -const key = 'userToken'; +import configs from "../../constants/config"; +import * as SecureStore from 'expo-secure-store'; +import qs from "qs"; +import { setSpotList } from "../actions/spotActions"; +import { MusicServiceProvider } from "../../models/MusicServiceProvider"; +import { SpotMapper } from "../../models/mapper/SpotMapper"; -export const getSpotList = () => { +export const getSpotList = (longitude: string, latitude: string, music: string) => { //@ts-ignore return async dispatch => { + try { + let token: string | null = await SecureStore.getItemAsync(configs.key); + const body: Record = { + longitude: longitude, + latitude: latitude, + currentMusic: music + } + const resp = await axios({ + url: configs.API_URL + '/user/nextTo?' + qs.stringify(body), + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + const musicIds = resp.data.data.map((music: any) => music.musicId); + const musics = await MusicServiceProvider.musicService.getMusicsWithIds(musicIds); + const result = resp.data.data + .filter((spot: any) => musics.some((m: any) => m.id === spot.musicId)) + .map((spot: any) => { + const matchingMusic = musics.find((m: any) => m.id === spot.musicId); + return { + ...spot, + music: matchingMusic, + }; + }); + dispatch(setSpotList(result.map((item: any) => SpotMapper.toModel(item)))); + + } catch (error: any) { + switch (error.response.status) { + default: + console.error("Error retrieving spots : " + error); + break; + } + } } } \ No newline at end of file diff --git a/src/FLAD/redux/thunk/userThunk.tsx b/src/FLAD/redux/thunk/userThunk.tsx index 0ea79f6..bd48e74 100644 --- a/src/FLAD/redux/thunk/userThunk.tsx +++ b/src/FLAD/redux/thunk/userThunk.tsx @@ -2,7 +2,7 @@ import axios from "axios"; import configs from "../../constants/config"; import { setDarkMode, setErrorNetwork, setErrorUpdateMessage, userLogin } from "../actions/userActions"; import * as SecureStore from 'expo-secure-store'; -import { UserMapper } from "../../model/mapper/UserMapper"; +import { UserMapper } from "../../models/mapper/UserMapper"; export const darkMode = (value: boolean) => { //@ts-ignore diff --git a/src/FLAD/redux/types/spotTypes.tsx b/src/FLAD/redux/types/spotTypes.tsx index f865c98..8c8024e 100644 --- a/src/FLAD/redux/types/spotTypes.tsx +++ b/src/FLAD/redux/types/spotTypes.tsx @@ -1,5 +1,4 @@ export const spotTypes = { FETCH_SPOT: 'FETCH_SPOT', - ADD_SPOT_MOCK: 'ADD_SPOT_MOCK', REMOVE_SPOT: 'REMOVE_SPOT' } \ No newline at end of file diff --git a/src/FLAD/screens/DetailScreen.tsx b/src/FLAD/screens/DetailScreen.tsx index 7e438b9..fe456b1 100644 --- a/src/FLAD/screens/DetailScreen.tsx +++ b/src/FLAD/screens/DetailScreen.tsx @@ -4,12 +4,12 @@ import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, import { Audio } from 'expo-av'; import { useEffect, useState } from "react"; import normalize from '../components/Normalize'; -import Music from "../model/Music"; +import Music from "../models/Music"; import { LinearGradient } from "expo-linear-gradient"; -import { MusicServiceProvider } from "../model/MusicServiceProvider"; +import { MusicServiceProvider } from "../models/MusicServiceProvider"; import { SimilarMusic } from "../components/SimilarMusicComponent"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import Artist from "../model/Artist"; +import Artist from "../models/Artist"; import { BlurView } from 'expo-blur'; const halfPi = Math.PI / 2; diff --git a/src/FLAD/screens/FavoriteScreen.tsx b/src/FLAD/screens/FavoriteScreen.tsx index cc177ca..152816d 100644 --- a/src/FLAD/screens/FavoriteScreen.tsx +++ b/src/FLAD/screens/FavoriteScreen.tsx @@ -9,7 +9,7 @@ import { useSelector, useDispatch } from 'react-redux'; import { colorsDark } from '../constants/colorsDark'; import { colorsLight } from '../constants/colorsLight'; import { getFavoriteMusic } from '../redux/thunk/appThunk'; -import { Spot } from '../model/Spot'; +import { Spot } from '../models/Spot'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; export default function FavoriteScreen() { diff --git a/src/FLAD/screens/SettingScreen.tsx b/src/FLAD/screens/SettingScreen.tsx index ad23152..c0dbf87 100644 --- a/src/FLAD/screens/SettingScreen.tsx +++ b/src/FLAD/screens/SettingScreen.tsx @@ -1,6 +1,6 @@ -import React, { useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import { View, StyleSheet, Text, Image, TouchableWithoutFeedback, Keyboard, TouchableOpacity, SafeAreaView } from 'react-native'; +import { View, StyleSheet, Text, Image, TouchableWithoutFeedback, Keyboard, TouchableOpacity, SafeAreaView, Alert } from 'react-native'; import { Svg, Path } from 'react-native-svg'; import { useNavigation } from "@react-navigation/native"; import { useDispatch, useSelector } from 'react-redux'; @@ -11,8 +11,9 @@ import { logout } from '../redux/thunk/authThunk'; import { darkMode } from '../redux/thunk/userThunk'; import { colorsDark } from '../constants/colorsDark'; import { colorsLight } from '../constants/colorsLight'; -import { User } from '../model/User'; +import { User } from '../models/User'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import * as Location from 'expo-location'; // @ts-ignore const DismissKeyboard = ({ children }) => ( @@ -38,7 +39,7 @@ export default function SettingScreen() { // @ts-ignore const isDark = useSelector(state => state.userReducer.dark); - + const [locationPermission, setLocationPermission] = useState(false); const style = isDark ? colorsDark : colorsLight; async function ChangeDarkMode() { @@ -48,6 +49,19 @@ export default function SettingScreen() { dispatch(darkMode(JSON.parse(newValue))) } + const checkLocationPermission = async () => { + const { status } = await Location.getForegroundPermissionsAsync(); + if (status !== 'granted') { + setLocationPermission(false); + } else { + setLocationPermission(true); + } + } + + useEffect(() => { + checkLocationPermission(); + }, []); + const [isCheckedNotif, setIsCheckedNotif] = useState(false); const toggleNotif = @@ -58,20 +72,21 @@ export default function SettingScreen() { dispatch(logout()); } - const [isCheckedLocalisation, setIsCheckedLocalisation] = useState(false); - const toggleLocalisation = - () => setIsCheckedLocalisation(value => !value); + () => { + Alert.alert( + 'Localisation', + "Pour changer la permission de localisation, veuillez aller dans les paramètres de votre téléphone et ajuster les autorisations pour l'application." + ); + }; const insets = useSafeAreaInsets(); - const styles = StyleSheet.create({ mainSafeArea: { flex: 1, backgroundColor: style.body, paddingTop: insets.top - }, container: { marginTop: 30, @@ -323,7 +338,7 @@ export default function SettingScreen() { Localisation - + diff --git a/src/FLAD/screens/SpotScreen.tsx b/src/FLAD/screens/SpotScreen.tsx index ef45228..cc9cf66 100644 --- a/src/FLAD/screens/SpotScreen.tsx +++ b/src/FLAD/screens/SpotScreen.tsx @@ -10,12 +10,13 @@ import Lotties from '../assets/lottie/Lottie'; import Loading from '../components/LoadingComponent'; import { useNavigation } from '@react-navigation/native'; import { useDispatch, useSelector } from 'react-redux'; -import { Spot } from '../model/Spot'; +import { Spot } from '../models/Spot'; import { removeFromSpotList, setSpotList } from '../redux/actions/spotActions'; +import { MusicServiceProvider } from '../models/MusicServiceProvider'; export default function SpotScreen() { //@ts-ignore - const spotReducer = useSelector(state => state.appReducer.spot) + const spotReducer: Spot[] = useSelector(state => state.appReducer.spot) const [cards, setCards] = useState(spotReducer); const [currentCard, setcurrentCard] = useState(cards[cards.length - 1]); @@ -33,8 +34,8 @@ export default function SpotScreen() { removeSpots(currentCard); } else if (direction === 'down') { - addMockSpots(); - console.log('Swiped down'); + MusicServiceProvider.musicService.addToPlaylist(currentCard.music.id); + removeSpots(currentCard); } }; @@ -69,11 +70,10 @@ export default function SpotScreen() { // @ts-ignore navigator.navigate("Detail", { "music": card.music }) }; + return ( - + <> {cards.length > 0 ? ( <> {currentCard.music.name} - Vous avez explorer toutes les spot autour de vous. + Vous avez explorer toutes les spot autour de vous. {"\n"}Continuer dans discoverie pour découvrir de nouvelles music basées sur vos gouts musicaux. ) } - + ); }; diff --git a/src/FLAD/services/EmptyMusicService.ts b/src/FLAD/services/EmptyMusicService.ts index e277f98..1cee193 100644 --- a/src/FLAD/services/EmptyMusicService.ts +++ b/src/FLAD/services/EmptyMusicService.ts @@ -2,6 +2,9 @@ import Music from "../models/Music"; import IMusicService from "./musics/interfaces/IMusicService"; export default class EmptyMusicService implements IMusicService { + getImageArtistWithId(idArtist: string): Promise { + throw new Error("Method not implemented."); + } getMusicById(id: string): Promise { throw new Error("Method not implemented."); } diff --git a/src/FLAD/services/musics/interfaces/IMusicService.ts b/src/FLAD/services/musics/interfaces/IMusicService.ts index d4c5134..0eacd53 100644 --- a/src/FLAD/services/musics/interfaces/IMusicService.ts +++ b/src/FLAD/services/musics/interfaces/IMusicService.ts @@ -1,4 +1,4 @@ -import Music from "../../../model/Music"; +import Music from "../../../models/Music"; export default interface IMusicService { getMusicById(id: string): Promise; diff --git a/src/FLAD/services/musics/spotify/SpotifyService.ts b/src/FLAD/services/musics/spotify/SpotifyService.ts index f4e43e0..e871dad 100644 --- a/src/FLAD/services/musics/spotify/SpotifyService.ts +++ b/src/FLAD/services/musics/spotify/SpotifyService.ts @@ -1,8 +1,8 @@ import axios from "axios"; -import Music from "../../../model/Music"; +import Music from "../../../models/Music"; import IMusicService from "../interfaces/IMusicService"; import TokenSpotify from "./TokenSpotify"; -import MusicMapper from "../../../model/mapper/MusicMapper"; +import MusicMapper from "../../../models/mapper/MusicMapper"; export default class SpotifyService implements IMusicService { private readonly API_URL = "https://api.spotify.com/v1";