Upgrade spot screen and navbar 🎨
continuous-integration/drone/push Build is passing Details

pull/19/head
Emre KARTAL 1 year ago
parent e86a35118d
commit ae0ff4e455

@ -97,7 +97,7 @@ Tout d'abord, il faut fournir votre *adresse e-mail* et votre *nom Spotify* aux
<div align = center> <div align = center>
<img src="doc/Images/Real_RegisterPage.png" width="250" > <img src="doc/Maquettes/RegisterPage.png" width="200" >
</div> </div>
@ -108,10 +108,7 @@ Vous serez normalement redirigé sur la page Spotify où vous devrez vous connec
<div align = center> <div align = center>
<img src="doc/Images/Real_HomePage.png" width="250" > <img src="doc/Images/Overview.png">
<img src="doc/Images/Real_FavoritePage.png" width="250" >
<img src="doc/Images/Real_ConversationPage.png" width="250" >
<img src="doc/Images/Real_SettingPage.png" width="250" >
</div> </div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 KiB

@ -148,8 +148,6 @@ class UserController implements IController {
* description: Users information retrieved successfully * description: Users information retrieved successfully
* 401: * 401:
* description: Unauthorized - Invalid or missing authentication token * description: Unauthorized - Invalid or missing authentication token
* 400:
* description: Bad request - Invalid input data
*/ */
this.router.get(`${this.path}s`, authenticator, this.getUsers); this.router.get(`${this.path}s`, authenticator, this.getUsers);
@ -525,7 +523,7 @@ class UserController implements IController {
const userIds = req.query.ids as string; const userIds = req.query.ids as string;
if (!userIds) { if (!userIds) {
return res.status(400).json({ message: 'Please provide user ids' }); return res.status(200).json([]);
} }
const userIdArray = userIds.split('&'); const userIdArray = userIds.split('&');

@ -21,7 +21,7 @@ class LocationService {
snapshot.forEach(location => { snapshot.forEach(location => {
const distance = this.distanceBetween(latitude, longitude, location.latitude, location.longitude); const distance = this.distanceBetween(latitude, longitude, location.latitude, location.longitude);
if (distance <= 1000) { if (distance <= 1000) {
usersLocation.push(new UserLocation(location._id, location.userId, location.musicId, Math.ceil(distance / 200) * 200, location.updatedAt)); usersLocation.push(new UserLocation(location._id, location.userId, location.musicId, Math.ceil(distance + 0.1 / 200) * 200, location.updatedAt));
} }
}); });
return { data: usersLocation }; return { data: usersLocation };

@ -26,7 +26,7 @@ export default function Friend(props: FriendProps) {
paddingVertical: 9, paddingVertical: 9,
}, },
image: { image: {
marginLeft: 15, marginLeft: "7%",
marginRight: 12, marginRight: 12,
width: 55, width: 55,
height: 55, height: 55,
@ -51,7 +51,7 @@ export default function Friend(props: FriendProps) {
}, },
time: { time: {
fontSize: normalize(18), fontSize: normalize(18),
color: '#989898' color: style.Text
}, },
profilContainer: { profilContainer: {
marginTop: 5, marginTop: 5,

@ -0,0 +1,84 @@
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
import normalize from './Normalize';
type UserInfoProps = {
image: string,
date: Date,
distance: string,
}
export default function UserInfoBadge(props: UserInfoProps) {
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
height: 30,
paddingHorizontal: normalize(10),
backgroundColor: '#3F1DC3',
alignSelf: 'flex-start',
borderRadius: 12,
paddingRight: 20,
},
section: {
flexDirection: 'row',
alignItems: 'center',
},
image: {
width: normalize(26),
height: normalize(26),
borderRadius: 30,
marginRight: normalize(10),
},
text: {
fontSize: normalize(14),
color: '#FFFFFF',
},
boldText: {
fontWeight: 'bold',
},
});
const date = formatDate(props.date);
return (
<View style={styles.container}>
<Image source={{ uri: props.image }} style={styles.image} />
<Text style={styles.text}>Il y a </Text>
{date.days !== 0 ? (
<View style={styles.section}>
<Text style={[styles.text, styles.boldText]}>{date.days}</Text>
<Text style={styles.text}>j </Text>
</View>
) : date.hours !== 0 ? (
<View style={styles.section}>
<Text style={[styles.text, styles.boldText]}>{date.hours}</Text>
<Text style={styles.text}>h </Text>
</View>
) : (
<View style={styles.section}>
<Text style={[styles.text, styles.boldText]}>{date.minutes}</Text>
<Text style={styles.text}>min</Text>
</View>
)}
<Text style={styles.text}> {'<'} </Text>
<Text style={[styles.text, styles.boldText]}>{props.distance}</Text>
<Text style={styles.text}>m</Text>
</View>
);
}
const formatDate = (date: Date) => {
const now = new Date();
const diffInMilliseconds = now.getTime() - date.getTime();
const diffInSeconds = Math.floor(diffInMilliseconds / 1000);
const diffInMinutes = Math.floor(diffInSeconds / 60);
const diffInHours = Math.floor(diffInMinutes / 60);
const diffInDays = Math.floor(diffInHours / 24);
const days = Math.floor(diffInDays);
const hours = Math.floor(diffInHours % 24);
const minutes = Math.floor(diffInMinutes % 60);
return { days, hours, minutes };
};

@ -1,7 +1,7 @@
export default [ export default [
{ {
id: '1', id: '1',
title: 'Bienvenue sur Flad', title: 'Bienvenue sur Flad 🎶',
description: 'L\'application pour découvrir de nouvelles musiques et vous faire de nouveaux amis', description: 'L\'application pour découvrir de nouvelles musiques et vous faire de nouveaux amis',
image: require('../assets/images/board_1.png') image: require('../assets/images/board_1.png')
}, },

@ -1,15 +1,18 @@
import Music from "./Music"; import Music from "./Music";
import { Person } from "./Person";
export class Spot { export class Spot {
private _id: string; private _id: string;
private _user: string; private _user: Person;
private _music: Music; private _music: Music;
public distance: string;
private _date: Date; private _date: Date;
constructor(id: string, user: string, music: Music, date: Date) { constructor(id: string, user: Person, music: Music, distance: string, date: Date) {
this._id = id; this._id = id;
this._user = user; this._user = user;
this._music = music; this._music = music;
this.distance = distance;
this._date = date; this._date = date;
} }
@ -21,11 +24,11 @@ export class Spot {
this._id = value; this._id = value;
} }
get user(): string { get user(): Person {
return this._user; return this._user;
} }
set user(value: string) { set user(value: Person) {
this._user = value; this._user = value;
} }

@ -0,0 +1,7 @@
import { Person } from "../Person";
export class PersonMapper {
public static toModel(person: any): Person {
return new Person(person._id, person.name, person.image);
}
}

@ -2,6 +2,6 @@ import { Spot } from "../Spot";
export class SpotMapper { export class SpotMapper {
public static toModel(spot: any): Spot { public static toModel(spot: any): Spot {
return new Spot(spot._id, spot.userId, spot.music, new Date(spot.date)); return new Spot(spot._id, spot.user, spot.music, spot.distance, new Date(spot.date));
} }
} }

@ -1,23 +1,21 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { View, StyleSheet, Platform, Alert } from 'react-native'; import { View, Alert } from 'react-native';
import { faUser, faEnvelope, faHeart, faMusic } from "@fortawesome/free-solid-svg-icons"
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer, DefaultTheme, DarkTheme } from '@react-navigation/native';
import FavoriteNavigation from './FavoriteNavigation'; import FavoriteNavigation from './FavoriteNavigation';
import SettingNavigation from './SettingNavigation'; import SettingNavigation from './SettingNavigation';
import normalize from '../components/Normalize'; import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome";
// @ts-ignore
import FontAwesome from 'react-native-vector-icons/FontAwesome';
import SpotNavigation from './SpotNavigation'; import SpotNavigation from './SpotNavigation';
import MessagingNavigation from './MessagingNavigation'; import MessagingNavigation from './MessagingNavigation';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { colorsDark } from '../constants/colorsDark';
import { colorsLight } from '../constants/colorsLight';
import { getUserCurrentMusic } from '../redux/thunk/appThunk'; import { getUserCurrentMusic } from '../redux/thunk/appThunk';
import { logout } from '../redux/thunk/authThunk'; import { logout } from '../redux/thunk/authThunk';
import { setAccessError, setErrorEmptyMusic } from '../redux/actions/userActions'; import { setAccessError, setErrorEmptyMusic } from '../redux/actions/userActions';
import * as Location from 'expo-location'; import * as Location from 'expo-location';
import { getSpotList } from '../redux/thunk/spotThunk'; import { getSpotList } from '../redux/thunk/spotThunk';
import Music from '../models/Music'; import Music from '../models/Music';
import normalize from '../components/Normalize';
export default function HomeNavigation() { export default function HomeNavigation() {
//@ts-ignore //@ts-ignore
@ -32,17 +30,7 @@ export default function HomeNavigation() {
const currentMusic: Music = useSelector(state => state.appReducer.userCurrentMusic); const currentMusic: Music = useSelector(state => state.appReducer.userCurrentMusic);
const [locationPermission, setLocationPermission] = useState(false); const [locationPermission, setLocationPermission] = useState(false);
const style = isDark ? colorsDark : colorsLight;
const BottomTabNavigator = createBottomTabNavigator(); const BottomTabNavigator = createBottomTabNavigator();
const MyTheme = {
dark: false,
colors: {
primary: 'rgb(255, 45, 85)',
card: style.Card,
border: style.Card,
text: 'rgb(138, 138, 138)',
}
};
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -128,38 +116,44 @@ export default function HomeNavigation() {
return ( return (
// @ts-ignore // @ts-ignore
<NavigationContainer theme={MyTheme}> <NavigationContainer theme={isDark ? DarkTheme : DefaultTheme}>
<BottomTabNavigator.Navigator <BottomTabNavigator.Navigator
initialRouteName="Spots" initialRouteName="Spots"
screenOptions={{ screenOptions={{
tabBarStyle: styles.tabBar, tabBarActiveTintColor: isDark ? "white" : "rgb(255, 45, 85)",
...(Platform.OS === 'android' tabBarStyle: {
? { tabBarLabelStyle: { bottom: normalize(10) } } backgroundColor: isDark ? "rgb(46,46,46)" : "white",
: { tabBarLabelStyle: { bottom: normalize(-25) } } },
),
}}> }}>
<BottomTabNavigator.Screen name="Spots" component={SpotNavigation} <BottomTabNavigator.Screen name="Spots" component={SpotNavigation}
options={{ options={{
headerShown: false, headerShown: false,
tabBarIcon: ({ color }) => <View style={styles.IconContainer}><TabBarIcon name="music" color={color} /></View>, tabBarIcon: ({ color }) => <TabBarIcon name={faMusic} color={color} size={23} />,
}} /> }} />
<BottomTabNavigator.Screen name="Favorites" component={FavoriteNavigation} <BottomTabNavigator.Screen name="Favorites" component={FavoriteNavigation}
options={{ options={{
tabBarBadge: favoritesMusicLength, tabBarBadge: favoritesMusicLength,
tabBarBadgeStyle: { backgroundColor: 'yellow' }, tabBarBadgeStyle: {
backgroundColor: 'yellow',
maxWidth: 12.5,
marginTop: 3,
maxHeight: 13,
borderRadius: 7,
fontSize: normalize(10),
lineHeight: 12,
},
headerShown: false, headerShown: false,
tabBarIcon: ({ color }) => <View style={styles.IconContainer}><TabBarIcon name="heart" color={color} /></View>, tabBarIcon: ({ color }) => <View><TabBarIcon name={faHeart} color={color} size={23} /></View>,
}} /> }} />
<BottomTabNavigator.Screen name="Messages" component={MessagingNavigation} <BottomTabNavigator.Screen name="Messages" component={MessagingNavigation}
options={{ options={{
headerShown: false, headerShown: false,
tabBarIcon: ({ color }) => <View style={styles.IconContainer}><TabBarIcon name="comment" color={color} /></View>, tabBarIcon: ({ color }) => <View ><TabBarIcon name={faEnvelope} color={color} size={23} /></View>,
}} /> }} />
<BottomTabNavigator.Screen name="Settings" component={SettingNavigation} <BottomTabNavigator.Screen name="Profil" component={SettingNavigation}
options={{ options={{
headerShown: false, headerShown: false,
tabBarIcon: ({ color }) => <View style={styles.IconContainer}><TabBarIcon name="cog" color={color} /></View>, tabBarIcon: ({ color, size }) => <TabBarIcon name={faUser} color={color} size={23} />,
}} /> }} />
</BottomTabNavigator.Navigator> </BottomTabNavigator.Navigator>
</NavigationContainer> </NavigationContainer>
@ -167,22 +161,9 @@ export default function HomeNavigation() {
} }
function TabBarIcon(props: { function TabBarIcon(props: {
name: React.ComponentProps<typeof FontAwesome>['name']; name: any;
color: string; color: string;
size: number;
}) { }) {
return <FontAwesome size={30} {...props} />; return <FontAwesomeIcon icon={props.name} style={{ marginBottom: -5 }} size={props.size} color={props.color} />;
}
const styles = StyleSheet.create({
tabBar: {
height: 60,
position: 'absolute',
bottom: normalize(50),
borderRadius: 30,
marginHorizontal: 25
},
IconContainer: {
position: 'absolute',
top: 6,
} }
})

@ -13,7 +13,7 @@ export default function SettingNavigation() {
options={{ headerShown: false }} options={{ headerShown: false }}
/> />
<Stack.Screen <Stack.Screen
name="Profil" name="Account"
component={ProfilScreen} component={ProfilScreen}
options={{ headerShown: false }} options={{ headerShown: false }}
/> />

@ -8,6 +8,8 @@
"web": "expo start --web" "web": "expo start --web"
}, },
"dependencies": { "dependencies": {
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-native-fontawesome": "^0.3.0",
"@react-native-async-storage/async-storage": "~1.17.3", "@react-native-async-storage/async-storage": "~1.17.3",
"@react-navigation/bottom-tabs": "^6.5.4", "@react-navigation/bottom-tabs": "^6.5.4",
"@react-navigation/native": "^6.1.4", "@react-navigation/native": "^6.1.4",

@ -16,3 +16,16 @@ export const setFavoriteMusic = (spots: Spot[]) => {
payload: spots, payload: spots,
}; };
} }
export const addFavoriteMusic = (spot: Spot) => {
return {
type: favoritesTypes.ADD_FAVORITE_MUSICS,
payload: spot,
};
}
export const resetNbAddedFavoriteMusic = () => {
return {
type: favoritesTypes.RESET_NB_ADDED_FAVORITE_MUSIC
};
}

@ -8,25 +8,37 @@ const initialState = {
spot: [] as Spot[], spot: [] as Spot[],
favoriteMusic: [] as Spot[], favoriteMusic: [] as Spot[],
userCurrentMusic: null, userCurrentMusic: null,
nbAddedFavoriteMusic: 0 nbAddedFavoriteMusic: 0,
oldSpot: [] as String[],
} }
const appReducer = (state = initialState, action: any) => { const appReducer = (state = initialState, action: any) => {
switch (action.type) { switch (action.type) {
case favoritesTypes.GET_FAVORITE_MUSICS: case favoritesTypes.GET_FAVORITE_MUSICS:
return { ...state, favoriteMusic: action.payload }; return { ...state, favoriteMusic: action.payload };
case favoritesTypes.ADD_FAVORITE_MUSICS:
return {
...state, favoriteMusic: [...state.favoriteMusic, action.payload],
nbAddedFavoriteMusic: state.nbAddedFavoriteMusic + 1
};
case favoritesTypes.RESET_NB_ADDED_FAVORITE_MUSIC:
return { ...state, nbAddedFavoriteMusic: 0 };
case spotTypes.FETCH_SPOT: case spotTypes.FETCH_SPOT:
const uniqueSpots = action.payload.filter((spot: Spot) => { const uniqueSpots = action.payload.filter((spot: Spot) => {
return !state.spot.some((s) => s.user === spot.user && s.music.id === spot.music.id); const spotKey = `${spot.user}_${spot.music.id}`;
return !state.oldSpot.includes(spotKey);
}); });
const updatedSpotList = [...uniqueSpots, ...state.spot]; const updatedSpotList = [...uniqueSpots, ...state.spot];
return { ...state, spot: updatedSpotList }; const updatedOldSpotList = [...state.oldSpot, ...uniqueSpots.map((spot: Spot) => `${spot.user}_${spot.music.id}`)];
return { ...state, spot: updatedSpotList, oldSpot: updatedOldSpotList };
case spotTypes.REMOVE_SPOT: case spotTypes.REMOVE_SPOT:
return { ...state, spot: state.spot.filter((spot) => spot.user !== action.payload.user && 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: case spotifyTypes.GET_USER_CURRENT_MUSIC:
return { ...state, userCurrentMusic: action.payload }; return { ...state, userCurrentMusic: action.payload };
case userTypes.USER_LOGOUT: case userTypes.USER_LOGOUT:
return { ...state, spot: [], favoriteMusic: [], userCurrentMusic: null, nbAddedFavoriteMusic: 0 }; return { ...state, spot: [], favoriteMusic: [], userCurrentMusic: null, nbAddedFavoriteMusic: 0, oldSpot: [] };
default: default:
return state; return state;
} }

@ -13,6 +13,7 @@ const store = configureStore({
reducer: reducer, reducer: reducer,
middleware: (getDefaultMiddleware) => middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ getDefaultMiddleware({
immutableCheck: false,
serializableCheck: false, serializableCheck: false,
}) })
},); },);

@ -3,9 +3,11 @@ import * as SecureStore from 'expo-secure-store';
import { Spot } from "../../models/Spot"; import { Spot } from "../../models/Spot";
import configs from "../../constants/config"; import configs from "../../constants/config";
import { MusicServiceProvider } from "../../models/MusicServiceProvider"; import { MusicServiceProvider } from "../../models/MusicServiceProvider";
import { setFavoriteMusic, setUserCurrentMusic } from "../actions/appActions"; import { addFavoriteMusic, setFavoriteMusic, setUserCurrentMusic } from "../actions/appActions";
import { setAccessError, setErrorEmptyMusic } from "../actions/userActions"; import { setAccessError, setErrorEmptyMusic } from "../actions/userActions";
import { SpotMapper } from "../../models/mapper/SpotMapper"; import { SpotMapper } from "../../models/mapper/SpotMapper";
import { logout } from "./authThunk";
import { removeFromSpotList } from "../actions/spotActions";
export const getUserCurrentMusic = () => { export const getUserCurrentMusic = () => {
//@ts-ignore //@ts-ignore
@ -27,6 +29,9 @@ export const getUserCurrentMusic = () => {
dispatch(setUserCurrentMusic(music)) dispatch(setUserCurrentMusic(music))
} catch (error: any) { } catch (error: any) {
switch (error.response.status) { switch (error.response.status) {
case 401:
dispatch(logout);
break;
case 403: case 403:
dispatch(setAccessError(true)); dispatch(setAccessError(true));
break; break;
@ -39,13 +44,6 @@ export const getUserCurrentMusic = () => {
} }
} }
export const addFavoriteMusic = (spot: Spot) => {
//@ts-ignore
return async dispatch => {
};
}
export const getFavoriteMusic = () => { export const getFavoriteMusic = () => {
//@ts-ignore //@ts-ignore
return async dispatch => { return async dispatch => {
@ -79,3 +77,30 @@ export const getFavoriteMusic = () => {
} }
}; };
} }
export const addMusicToFavorite = (spot: Spot) => {
//@ts-ignore
return async dispatch => {
try {
dispatch(removeFromSpotList(spot));
let token: string | null = await SecureStore.getItemAsync(configs.key);
const headers = {
'Authorization': 'Bearer ' + token
};
const body = {
"musicId": spot.music.id,
"userId": spot.user
}
await axios.post(configs.API_URL + '/user/musics', body, { headers });
spot.date = new Date(Date.now());
dispatch(addFavoriteMusic(spot));
} catch (error: any) {
switch (error.response.status) {
default:
console.error("Error like spot : " + error);
break;
}
}
};
}

@ -2,9 +2,13 @@ import axios from "axios";
import configs from "../../constants/config"; import configs from "../../constants/config";
import * as SecureStore from 'expo-secure-store'; import * as SecureStore from 'expo-secure-store';
import qs from "qs"; import qs from "qs";
import { setSpotList } from "../actions/spotActions"; import { removeFromSpotList, setSpotList } from "../actions/spotActions";
import { MusicServiceProvider } from "../../models/MusicServiceProvider"; import { MusicServiceProvider } from "../../models/MusicServiceProvider";
import { SpotMapper } from "../../models/mapper/SpotMapper"; import { SpotMapper } from "../../models/mapper/SpotMapper";
import { Spot } from "../../models/Spot";
import { Person } from "../../models/Person";
import Music from "../../models/Music";
import { PersonMapper } from "../../models/mapper/PersonMapper";
export const getSpotList = (longitude: string, latitude: string, music: string) => { export const getSpotList = (longitude: string, latitude: string, music: string) => {
//@ts-ignore //@ts-ignore
@ -26,18 +30,27 @@ export const getSpotList = (longitude: string, latitude: string, music: string)
const musicIds = resp.data.data.map((music: any) => music.musicId); const musicIds = resp.data.data.map((music: any) => music.musicId);
const musics = await MusicServiceProvider.musicService.getMusicsWithIds(musicIds); const musics = await MusicServiceProvider.musicService.getMusicsWithIds(musicIds);
const userIds = resp.data.data.map((user: any) => user.userId);
const users = await getUsersInfo(userIds);
const result = resp.data.data const result = resp.data.data
.filter((spot: any) => musics.some((m: any) => m.id === spot.musicId)) .filter((spot: any) => musics.some((m: Music) => m.id === spot.musicId))
.filter((spot: any) => users.some((u: Person) => u.id === spot.userId))
.map((spot: any) => { .map((spot: any) => {
const matchingMusic = musics.find((m: any) => m.id === spot.musicId); const matchingMusic = musics.find((m: any) => m.id === spot.musicId);
const matchingUser = users.find((user: any) => user.id === spot.userId);
return { return {
...spot, ...spot,
music: matchingMusic, music: matchingMusic,
user: matchingUser,
}; };
}); });
dispatch(setSpotList(result.map((item: any) => SpotMapper.toModel(item)))); dispatch(setSpotList(result.map((item: any) => SpotMapper.toModel(item))));
} catch (error: any) { } catch (error: any) {
console.log(error);
switch (error.response.status) { switch (error.response.status) {
default: default:
console.error("Error retrieving spots : " + error); console.error("Error retrieving spots : " + error);
@ -47,3 +60,39 @@ export const getSpotList = (longitude: string, latitude: string, music: string)
} }
} }
const getUsersInfo = async (userIds: string[]) => {
try {
const token: string | null = await SecureStore.getItemAsync(configs.key);
const ids = userIds.join('&');
const resp = await axios({
url: configs.API_URL + `/users?ids=${ids}`,
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
},
});
return resp.data.map((item: any) => PersonMapper.toModel(item));
} catch (error) {
console.error("Error retrieving user information: " + error);
return [];
}
}
export const removeSpot = (spot: Spot) => {
//@ts-ignore
return async dispatch => {
dispatch(removeFromSpotList(spot));
}
}
export const addToPlaylist = (spot: Spot) => {
//@ts-ignore
return async dispatch => {
MusicServiceProvider.musicService.addToPlaylist(spot.music.id);
dispatch(removeFromSpotList(spot));
}
}

@ -1,4 +1,5 @@
export const favoritesTypes = { export const favoritesTypes = {
GET_FAVORITE_MUSICS: 'GET_FAVORITE_MUSICS', GET_FAVORITE_MUSICS: 'GET_FAVORITE_MUSICS',
ADD_FAVORITE_MUSICS: 'ADD_FAVORITE_MUSICS', ADD_FAVORITE_MUSICS: 'ADD_FAVORITE_MUSICS',
RESET_NB_ADDED_FAVORITE_MUSIC: 'RESET_NB_ADDED_FAVORITE_MUSIC',
} }

@ -32,8 +32,7 @@ export default function ConversationScreen() {
paddingTop: insets.top paddingTop: insets.top
}, },
titleContainer: { titleContainer: {
marginTop: 10, marginLeft: "7%",
marginLeft: 20,
}, },
title: { title: {
fontSize: normalize(28), fontSize: normalize(28),
@ -41,7 +40,7 @@ export default function ConversationScreen() {
color: style.Text, color: style.Text,
}, },
description: { description: {
marginTop: 10, marginTop: 3,
fontSize: normalize(20), fontSize: normalize(20),
color: '#787878', color: '#787878',
marginBottom: 5 marginBottom: 5
@ -52,7 +51,7 @@ export default function ConversationScreen() {
<SafeAreaView style={styles.mainSafeArea}> <SafeAreaView style={styles.mainSafeArea}>
<View style={styles.titleContainer}> <View style={styles.titleContainer}>
<Text style={styles.title}>Messages</Text> <Text style={styles.title}>Messages</Text>
<Text style={styles.description}>Retrouvez ici tous vos amis!</Text> <Text style={styles.description}>Retrouvez ici les discussions</Text>
</View> </View>
<FlatList <FlatList
style={{ marginTop: 10 }} style={{ marginTop: 10 }}

@ -1,5 +1,5 @@
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, View, FlatList, SafeAreaView, SectionList, TouchableOpacity } from 'react-native'; import { StyleSheet, Text, View, FlatList, SafeAreaView, SectionList, TouchableOpacity, RefreshControl } from 'react-native';
import CardMusic from '../components/CardMusicComponent'; import CardMusic from '../components/CardMusicComponent';
import normalize from '../components/Normalize'; import normalize from '../components/Normalize';
import { Svg, Path } from 'react-native-svg'; import { Svg, Path } from 'react-native-svg';
@ -11,12 +11,15 @@ import { colorsLight } from '../constants/colorsLight';
import { getFavoriteMusic } from '../redux/thunk/appThunk'; import { getFavoriteMusic } from '../redux/thunk/appThunk';
import { Spot } from '../models/Spot'; import { Spot } from '../models/Spot';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { resetNbAddedFavoriteMusic } from '../redux/actions/appActions';
import { useFocusEffect } from '@react-navigation/native';
export default function FavoriteScreen() { export default function FavoriteScreen() {
// @ts-ignore // @ts-ignore
const isDark = useSelector(state => state.userReducer.dark); const isDark = useSelector(state => state.userReducer.dark);
const style = isDark ? colorsDark : colorsLight; const style = isDark ? colorsDark : colorsLight;
const [refreshing, setRefreshing] = useState(false);
const images = [ const images = [
{ id: 1, source: require('../assets/images/flady_love.png') }, { id: 1, source: require('../assets/images/flady_love.png') },
@ -35,6 +38,21 @@ export default function FavoriteScreen() {
dispatch(getFavoriteMusic()); dispatch(getFavoriteMusic());
}, []); }, []);
const handleRefresh = () => {
setRefreshing(true);
//@ts-ignore
dispatch(getFavoriteMusic());
setTimeout(() => {
setRefreshing(false);
}, 900);
};
useFocusEffect(
React.useCallback(() => {
dispatch(resetNbAddedFavoriteMusic());
}, [])
);
const groupByDate = (data: Spot[]) => { const groupByDate = (data: Spot[]) => {
const groupedData: { [key: string]: Spot[] } = {}; const groupedData: { [key: string]: Spot[] } = {};
@ -74,8 +92,8 @@ export default function FavoriteScreen() {
paddingTop: insets.top paddingTop: insets.top
}, },
titleContainer: { titleContainer: {
marginVertical: 10, marginTop: 5,
marginLeft: 20, marginLeft: "7%",
}, },
header: { header: {
flexDirection: 'row', flexDirection: 'row',
@ -89,7 +107,6 @@ export default function FavoriteScreen() {
color: style.Text, color: style.Text,
}, },
description: { description: {
marginTop: 10,
fontSize: normalize(20), fontSize: normalize(20),
color: '#787878', color: '#787878',
marginBottom: 5 marginBottom: 5
@ -97,9 +114,13 @@ export default function FavoriteScreen() {
titleSection: { titleSection: {
fontSize: normalize(20), fontSize: normalize(20),
color: style.Text, color: style.Text,
fontWeight: 'medium', fontWeight: '500',
marginLeft: 20, marginLeft: "7%",
marginBottom: 10 marginBottom: 10
},
collection: {
marginTop: "3%",
marginLeft: "1%",
} }
}); });
@ -116,8 +137,16 @@ export default function FavoriteScreen() {
<Text style={styles.description}>Retrouvez ici vos musiques favorites</Text> <Text style={styles.description}>Retrouvez ici vos musiques favorites</Text>
</View> </View>
<SectionList <SectionList
style = {styles.collection}
sections={groupByDate(favoriteMusic)} sections={groupByDate(favoriteMusic)}
keyExtractor={(item: Spot) => item.music.id} refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
tintColor={style.Text}
/>
}
keyExtractor={(item: Spot) => item.music.id + item.user + item.date}
renderItem={({ item }) => ( renderItem={({ item }) => (
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {

@ -34,7 +34,7 @@ export default function LoginScreen() {
const submitForm = () => { const submitForm = () => {
const credentials: LoginCredentials = { const credentials: LoginCredentials = {
email: username.toLowerCase(), email: username.toLowerCase().trim(),
password: password password: password
}; };
//@ts-ignore //@ts-ignore

@ -291,7 +291,7 @@ export default function SettingScreen() {
<TouchableOpacity <TouchableOpacity
// @ts-ignore // @ts-ignore
onPress={() => navigation.navigate('Profil')} onPress={() => navigation.navigate('Account')}
> >
<View style={styles.profil}> <View style={styles.profil}>
<Image source={{ uri: currentUser.image }} style={styles.imageProfil} /> <Image source={{ uri: currentUser.image }} style={styles.imageProfil} />

@ -12,6 +12,9 @@ import { removeFromSpotList } from '../redux/actions/spotActions';
import { MusicServiceProvider } from '../models/MusicServiceProvider'; import { MusicServiceProvider } from '../models/MusicServiceProvider';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
import Artist from '../models/Artist'; import Artist from '../models/Artist';
import { addMusicToFavorite } from '../redux/thunk/appThunk';
import { addToPlaylist, removeSpot } from '../redux/thunk/spotThunk';
import UserInfoBadge from '../components/UserInfoBadgeComponent';
export default function SpotScreen() { export default function SpotScreen() {
//@ts-ignore //@ts-ignore
@ -29,13 +32,15 @@ export default function SpotScreen() {
const onSwipe = (direction: 'left' | 'right' | 'down') => { const onSwipe = (direction: 'left' | 'right' | 'down') => {
if (direction === 'right') { if (direction === 'right') {
dispatch(removeFromSpotList(currentCard)); //@ts-ignore
dispatch(addMusicToFavorite(currentCard));
} else if (direction === 'left') { } else if (direction === 'left') {
dispatch(removeFromSpotList(currentCard)); //@ts-ignore
dispatch(removeSpot(currentCard));
} }
else if (direction === 'down') { else if (direction === 'down') {
MusicServiceProvider.musicService.addToPlaylist(currentCard.music.id); //@ts-ignore
dispatch(removeFromSpotList(currentCard)); dispatch(addToPlaylist(currentCard));
} }
}; };
@ -80,18 +85,18 @@ export default function SpotScreen() {
right: 0, right: 0,
height: insets.top + 150, height: insets.top + 150,
}, },
header: {
left: width / 11,
top: '2.5%',
},
titleLabel: { titleLabel: {
fontStyle: 'normal', fontStyle: 'normal',
left: width / 9,
top: '5%',
color: "#FFFFFF", color: "#FFFFFF",
fontSize: normalize(40), fontSize: normalize(40),
fontWeight: "800", fontWeight: "800",
}, },
artistLabel: { artistLabel: {
fontStyle: 'normal', fontStyle: 'normal',
left: width / 9,
top: '5%',
color: "#FFFFFF", color: "#FFFFFF",
fontSize: normalize(20), fontSize: normalize(20),
}, },
@ -101,7 +106,7 @@ export default function SpotScreen() {
paddingHorizontal: 30, paddingHorizontal: 30,
width: '100%', width: '100%',
position: "absolute", position: "absolute",
top: "74%" top: "85%"
}, },
button: { button: {
alignItems: 'center', alignItems: 'center',
@ -114,18 +119,18 @@ export default function SpotScreen() {
}, },
dislikeIcon: { dislikeIcon: {
resizeMode: "stretch", resizeMode: "stretch",
height: '44%', height: '40%',
aspectRatio: 1.05, aspectRatio: 1.05,
}, },
discoveryIcon: { discoveryIcon: {
resizeMode: "stretch", resizeMode: "stretch",
height: '50%', height: '42%',
aspectRatio: 1.5, aspectRatio: 1.5,
marginLeft: '7%' marginLeft: '7%'
}, },
likeIcon: { likeIcon: {
resizeMode: "stretch", resizeMode: "stretch",
height: '50%', height: '42%',
aspectRatio: 1.1 aspectRatio: 1.1
}, },
loading: { loading: {
@ -152,8 +157,11 @@ export default function SpotScreen() {
}} }}
/> />
<LinearGradient colors={['rgba(2, 2, 2, 0.40) 0%', 'rgba(0, 0, 0, 0) 100%']} style={styles.gradient} /> <LinearGradient colors={['rgba(2, 2, 2, 0.40) 0%', 'rgba(0, 0, 0, 0) 100%']} style={styles.gradient} />
<View style={styles.header}>
<UserInfoBadge image={currentCard.user.image} date={currentCard.date} distance={currentCard.distance} />
<Text style={styles.titleLabel}>{currentCard.music.name}</Text> <Text style={styles.titleLabel}>{currentCard.music.name}</Text>
<Text style={styles.artistLabel}>{currentCard.music.artists.map((artist: Artist) => artist.name).join(', ')}</Text> <Text style={styles.artistLabel}>{currentCard.music.artists.map((artist: Artist) => artist.name).join(', ')}</Text>
</View>
<View style={{ flex: 0.7, justifyContent: 'center', alignItems: 'center' }}> <View style={{ flex: 0.7, justifyContent: 'center', alignItems: 'center' }}>
{cards.map((card, index) => ( {cards.map((card, index) => (
<View <View

Loading…
Cancel
Save