merge resume
continuous-integration/drone/push Build is passing Details

pull/15/head
David D'ALMEIDA 2 years ago
commit 5228d86392

@ -22,7 +22,7 @@ class SpotifyController implements Controller {
this.router.get(`${this.path}/exchange`,this.login); this.router.get(`${this.path}/exchange`,this.login);
this.router.get(`${this.path}/callback`,this.getAccessToken); this.router.get(`${this.path}/callback`,this.getAccessToken);
// this.router.post(`${this.path}/refresh`,this.getRefreshToken); // this.router.post(`${this.path}/refresh`,this.getRefreshToken);
this.router.get(`${this.path}/play/:musicId`, this.getMusic); // this.router.get(`${this.path}/play/:musicId`, this.getMusic);
this.router.get(`${this.path}/spot`, this.getSpot); this.router.get(`${this.path}/spot`, this.getSpot);
} }
@ -137,10 +137,6 @@ class SpotifyController implements Controller {
} }
public getMusic(){
return null;
}
public getSpot = async ( public getSpot = async (
req: Request, req: Request,

@ -219,8 +219,9 @@ class UserController implements Controller {
console.log('Impossible de convertir la chaîne en nombre'); console.log('Impossible de convertir la chaîne en nombre');
} }
//} //}
const currentMusicId = req.body.currentMusicId;
const userId = req.user.idFlad; const userId = req.user.idFlad;
const data = await this.locationService.getNearUser(userId,latitude,longitude); const data = await this.locationService.getNearUser(userId,latitude,longitude, currentMusicId);
console.log(data); console.log(data);
res.status(201).send(data); res.status(201).send(data);

@ -18,6 +18,9 @@ const locationSchema = new Schema(
type: Number, type: Number,
required: true, required: true,
}, },
currentMusicId : {
type: String,
}
}, },

@ -32,10 +32,12 @@ export class UserLocation {
uuid: string; uuid: string;
latitude : number; latitude : number;
longitude: number; longitude: number;
constructor(uuid: string, latitude: number, longitude: number){ currentMusicId : string;
constructor(uuid: string, latitude: number, longitude: number, currentMusicId : string) {
this.uuid = uuid; this.uuid = uuid;
this.latitude = latitude; this.latitude = latitude;
this.longitude = longitude; this.longitude = longitude;
this.currentMusicId = currentMusicId;
} }
} }

@ -6,11 +6,11 @@ import LocationSchema from "../database/schema/LocationSchema";
class LocationService { class LocationService {
private locationCollection = LocationSchema; private locationCollection = LocationSchema;
// private API_KEY : string = "AIzaSyBFCEAtmhZ8jvw84UTQvX3Aqpr66GVqB_A"; // private API_KEY : string = "AIzaSyBFCEAtmhZ8jvw84UTQvX3Aqpr66GVqB_A";
public async getNearUser(idFlad : string, latitude : number, longitude : number) public async getNearUser(idFlad : string, latitude : number, longitude : number, currentMusicId: string)
{ {
await this.locationCollection.findOneAndUpdate( await this.locationCollection.findOneAndUpdate(
{ idFlad }, { idFlad },
{ idFlad, latitude, longitude }, { idFlad, latitude, longitude, currentMusicId },
{ upsert: true } { upsert: true }
); );
@ -22,18 +22,18 @@ class LocationService {
let dbUsersList:UserLocation[] = []; let dbUsersList:UserLocation[] = [];
snapshot.forEach(doc => { snapshot.forEach(doc => {
dbUsersList.push(new UserLocation(doc.idFlad,doc.latitude,doc.longitude)); dbUsersList.push(new UserLocation(doc.idFlad,doc.latitude,doc.longitude, doc.currentMusicId));
console.log(doc.idFlad, '=>', doc); console.log(doc.idFlad, '=>', doc);
}); });
// missing the curent music // missing the curent music
let listUser: string[] = []; let listUser: {userid: string, music : string}[] = [];
dbUsersList.forEach(user => { dbUsersList.forEach(user => {
console.log(user); console.log(user);
const dist = this.distanceBetween(latitude , longitude , user.latitude, user.longitude); const dist = this.distanceBetween(latitude , longitude , user.latitude, user.longitude);
console.log(user.uuid,dist); console.log(user.uuid,dist);
if (dist <= 100) { if (dist <= 100) {
listUser.push(user.uuid); listUser.push({userid : user.uuid, music : user.currentMusicId});
} }
}); });

@ -1,14 +1,14 @@
export default class Music { // export default class Music {
private id : string; // private id : string;
private name : string; // private name : string;
private artist : string; // private artist : string;
private linkCover : string; // Image.source // private linkCover : string; // Image.source
constructor(id : string, name : string, artist : string, linkCover : string){ // constructor(id : string, name : string, artist : string, linkCover : string){
this.id = id; // this.id = id;
this.name = name; // this.name = name;
this.artist = artist; // this.artist = artist;
this.linkCover = linkCover; // this.linkCover = linkCover;
} // }
} // }

@ -2,7 +2,7 @@ import Music from "./Music";
export class Spot { export class Spot {
private _userId : string; private _userId : string;
public _music : Music; private _music : Music;
constructor(userId : string, music : Music){ constructor(userId : string, music : Music){
this._userId = userId; this._userId = userId;
this._music = music; this._music = music;
@ -10,8 +10,13 @@ export class Spot {
get userSpotifyId(): string { get userSpotifyId(): string {
return this._userId; return this._userId;
} }
get idSpotify(): Music { set userSpotifyId(value: string) {
return this._music; this._userId = value;
} }
get music(): Music {
return this._music;
}
set music(value: Music) {
this._music = value;
}
} }

@ -5,7 +5,9 @@ export default class MusicFactory {
const music = new Music( const music = new Music(
jsonMusic.id, jsonMusic.id,
jsonMusic.name, jsonMusic.name,
jsonMusic.album.images[0].url "",
jsonMusic.album.images[0].url,
jsonMusic.preview_url
); );
return music; return music;
} }

@ -7,7 +7,7 @@
"icon": "./assets/icons/icon.png", "icon": "./assets/icons/icon.png",
"userInterfaceStyle": "light", "userInterfaceStyle": "light",
"splash": { "splash": {
"image": "./assets/images/RedFlady.png", "image": "./assets/icons/splash.png",
"resizeMode": "contain", "resizeMode": "contain",
"backgroundColor": "#1d2129" "backgroundColor": "#1d2129"
}, },

@ -11,7 +11,7 @@ const SCREEN_WIDTH = Dimensions.get('window').width
interface CardProps { interface CardProps {
title: string; title: string;
image: any; image: any;
onSwipe: (direction: "left" | "right") => void; onSwipe: (direction: "left" | "right" | "down") => void;
} }
type ContextType = { type ContextType = {
translateX: number; translateX: number;
@ -47,7 +47,12 @@ const Card = ({ title, image, onSwipe } : CardProps) => {
} else if (translateX.value < -160) { } else if (translateX.value < -160) {
runOnJS(onSwipe)("left"); runOnJS(onSwipe)("left");
// onSwipe("left"); // onSwipe("left");
} else { }else if (translateY.value > 250) {
runOnJS(onSwipe)("down");
// onSwipe("left");
}
else {
translateX.value = withSpring(0); translateX.value = withSpring(0);
translateY.value = withSpring(0); translateY.value = withSpring(0);
} }

@ -1,24 +1,33 @@
import React from 'react'; import React from 'react';
import { StyleSheet, Text, View , Image } from 'react-native'; import { StyleSheet, Text, View , Image } from 'react-native';
import { SharedElement } from 'react-navigation-shared-element';
import { useSelector } from 'react-redux';
import normalize from '../components/Normalize'; import normalize from '../components/Normalize';
type CustomCardMusic = { //Props type CustomCardMusic = { //Props
image: ImageSource; image: string;
title: string; title: string;
description: string; description: string;
id : string;
} }
export default function CardMusic(CBP: CustomCardMusic) { export default function CardMusic(CBP: CustomCardMusic) {
const currentMusic = useSelector(state => state.appReducer.currentMusic);
const source = typeof CBP.image === 'string' ? { uri: CBP.image } : CBP.image; const source = typeof CBP.image === 'string' ? { uri: CBP.image } : CBP.image;
return ( return (
<View style={styles.container}> <View style={styles.container}>
<View style={styles.imageContainer}> <View style={styles.imageContainer}>
<Image source={source} style={styles.image}/> <Image source={source} style={styles.image}/>
</View> </View>
<View style={styles.textContainer}> <View style={styles.textContainer}>
<Text style={styles.title}>{CBP.title}</Text> {/* currentMusic.id === CBP.id && styles.currentMusic */}
<Text style={styles.description}>{CBP.description}</Text> <Text style={[styles.title]}>{CBP.title}</Text>
<Text style={[styles.description]}>{CBP.description}</Text>
</View> </View>
</View> </View>
); );
@ -59,5 +68,8 @@ const styles = StyleSheet.create({
description: { description: {
color: 'white', color: 'white',
fontSize: normalize(18) fontSize: normalize(18)
},
currentMusic: {
color: 'red'
} }
}); });

@ -0,0 +1,145 @@
// import React, { useEffect, useRef } from 'react';
// import { StyleSheet, Text, View , Image } from 'react-native';
// import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
// import { SharedElement } from 'react-navigation-shared-element';
// import { useSelector } from 'react-redux';
// import normalize from '../components/Normalize';
// import SpotifyService from '../services/spotify/spotify.service';
// export default function CurrentMusic() {
// const animatedStyle = useAnimatedStyle(() => {
// return {
// transform: [
// { scale: withTiming(139.929, { duration: 1000 }) }
// ]
// }
// });
// const centerX = useSharedValue(0);
// const centerY = useSharedValue(0);
// const scale = useSharedValue(0);
// const width = useSharedValue(0);
// const height = useSharedValue(0);
// const rippleOpacity = useSharedValue(1);
// const rStyle = useAnimatedStyle(() => {
// const circleRadius = Math.sqrt(width.value ** 2 + height.value ** 2);
// const translateX = centerX.value - circleRadius;
// const translateY = centerY.value - circleRadius;
// return {
// width: circleRadius * 2,
// height: circleRadius * 2,
// borderRadius: circleRadius,
// opacity: rippleOpacity.value,
// backgroundColor: 'rgba(0,0,0,0.2)',
// position: 'absolute',
// top: 0,
// left: 0,
// transform: [
// { translateX },
// { translateY },
// {
// scale: scale.value,
// },
// ],
// };
// });
// useEffect(() => {
// // withTiming, withSpring
// rippleOpacity.value = 1;
// scale.value = withTiming(1, { duration: 1000 });
// }, [rippleOpacity,scale]);
// return (
// <View style={{
// width: 200,
// height: 200,
// backgroundColor: 'white',
// alignItems: 'center',
// justifyContent: 'center',
// borderRadius: 25,
// // iOS
// shadowOpacity: 0.2,
// shadowOffset: { width: 0, height: 0 },
// shadowRadius: 20,
// // Android
// elevation: 2,
// }}>
// <Animated.View style={[{
// width: 200,
// height: 200,
// backgroundColor: 'white',
// alignItems: 'center',
// justifyContent: 'center',
// borderRadius: 25,
// // iOS
// shadowOpacity: 0.2,
// shadowOffset: { width: 0, height: 0 },
// shadowRadius: 20,
// // Android
// elevation: 2,
// }, { overflow: 'hidden' }]}>
// <Animated.View style={rStyle} />
// </Animated.View>
// </View>
// );
// }
import React, { useEffect, useRef } from 'react';
import { StyleSheet, Text, View , Image } from 'react-native';
import Animated, { useAnimatedStyle, useDerivedValue, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
import { SharedElement } from 'react-navigation-shared-element';
import { useSelector } from 'react-redux';
import normalize from '../components/Normalize';
import SpotifyService from '../services/spotify/spotify.service';
export default function CurrentMusic() {
const transition = useDerivedValue(() => {
return withSpring(value.value);
});
return (
<View style={{
width: 200,
height: 200,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 25,
// iOS
shadowOpacity: 0.2,
shadowOffset: { width: 0, height: 0 },
shadowRadius: 20,
// Android
elevation: 2,
}}>
<Animated.View style={[{
width: 200,
height: 200,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 25,
// iOS
shadowOpacity: 0.2,
shadowOffset: { width: 0, height: 0 },
shadowRadius: 20,
// Android
elevation: 2,
}, { overflow: 'hidden' }]}>
<Animated.View style={rStyle} />
</Animated.View>
</View>
);
}

@ -0,0 +1,52 @@
import { View, StyleSheet, Dimensions,Text, Image, Pressable, TouchableWithoutFeedback, TouchableOpacity, TouchableHighlight, FlatList } from "react-native";
import Animated, {
Layout,
Transition,
ZoomIn,
ZoomOut,
} from "react-native-reanimated";
const { width } = Dimensions.get("window");
const SIZE = width / 3;
import { Feather as Icon } from "@expo/vector-icons";
import Music from "../Model/Music";
import { State, TapGestureHandler } from "react-native-gesture-handler";
import { useRef, useState } from "react";
interface HorizontalFlatListProps {
// React.ReactNode;
renderCell: (image: string, titre : string) => React.ReactElement
title : string;
data : any[];
}
export const HorizontalFlatList = ({ title, data, renderCell}: HorizontalFlatListProps) => {
return (
<View style={styles.similarSection}>
<Text style={styles.similarTitle} >{title}</Text>
<FlatList
showsHorizontalScrollIndicator={false}
data={data}
horizontal={true}
keyExtractor={item => item.id}
renderItem={({item}) =>{
return renderCell(item.image, image.titre);
}}
/></View>
);
};
const styles = StyleSheet.create({
similarSection: {
paddingTop: 30
},
similarTitle: {
color: "#2998FD",
paddingLeft: 35,
fontSize: 17,
fontWeight: "600",
paddingBottom: 20
}
});

@ -0,0 +1,74 @@
import { View, StyleSheet, Dimensions, Image, Pressable, TouchableWithoutFeedback, TouchableOpacity, TouchableHighlight } from "react-native";
import Animated, {
Layout,
Transition,
ZoomIn,
ZoomOut,
} from "react-native-reanimated";
const { width } = Dimensions.get("window");
const SIZE = width / 3;
import { Feather as Icon } from "@expo/vector-icons";
import Music from "../Model/Music";
import { State, TapGestureHandler } from "react-native-gesture-handler";
import { useRef, useState } from "react";
interface SelectedCardProps {
artist: Music;
onPress: () => void;
// cheepPosition: string ;(direction: "left" | "right" | "down")
}
export const SelectedCard = ({ artist, onPress }: SelectedCardProps) => {
const [selected,setSeleted] = useState(false);
const onS = () => {
setSeleted(!selected);
onPress();
};
return (
<TouchableOpacity onPress={onS}>
<Animated.View
style={styles.container}
entering={ZoomIn}
exiting={ZoomOut}
layout={Layout.delay(200)}
>
<View style={styles.card}>
{ selected && (
<View style={styles.cheked}>
<Icon name="check-circle" color="black" size={24} />
</View>
)}
</View>
</Animated.View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
width: SIZE,
height: SIZE,
padding: 8,
},
card: {
flex: 1,
padding: 8,
alignItems: "flex-end",
},
image: {
borderRadius: 8,
...StyleSheet.absoluteFillObject,
width: undefined,
height: undefined,
},
cheked : {
backgroundColor : "white",
borderRadius : 100,
alignItems : "center",
}
});

@ -0,0 +1,29 @@
import {TouchableOpacity, ScrollView, View, Text, StyleSheet, Image, SafeAreaView, FlatList, Animated} from 'react-native';
export default function littleCard ({image,titre}){
return (
<View style={styles.similarContainer}>
<Image source={{uri: image}} style={styles.similarPoster}></Image>
<Text numberOfLines={2} style={styles.similarTitleFilm}>{titre}
</Text>
</View>
)
}
const styles = StyleSheet.create({
similarContainer: {
width: 90,
marginHorizontal: 7
},
similarTitleFilm: {
color: "#DADADA",
paddingTop: 5,
fontWeight: "300"
},
similarPoster: {
height: 130,
width: 90,
borderRadius: 8
}
})

@ -1,6 +1,8 @@
export const cards = [{ import Music from "../Model/Music";
musicName : "Je bibi", import { Spot } from "../Model/Spot";
name : "Kaaris",
export const cards= [{
name : "blue",
sourceUrl : "https://th.bing.com/th/id/R.dbf87f0d8cbfd078ab6a589a5d921994?rik=1%2f6KliMpOAeh8A&pid=ImgRaw&r=0", sourceUrl : "https://th.bing.com/th/id/R.dbf87f0d8cbfd078ab6a589a5d921994?rik=1%2f6KliMpOAeh8A&pid=ImgRaw&r=0",
index : 4 index : 4
}, },
@ -42,3 +44,43 @@ export const cards = [{
}, },
] ]
const spotArray: Spot[] = [
new Spot("1", new Music("1", "Title 1", "Bio 1", "Image 1", "TrackPreviewUrl 1")),
new Spot("2", new Music("2", "Title 2", "Bio 2", "Image 2", "TrackPreviewUrl 2")),
new Spot("3", new Music("3", "Title 3", "Bio 3", "Image 3", "TrackPreviewUrl 3")),
new Spot("4", new Music("4", "Title 4", "Bio 4", "Image 4", "TrackPreviewUrl 4")),
new Spot("5", new Music("5", "Title 5", "Bio 5", "Image 5", "TrackPreviewUrl 5")),
];
export const spotArray2: Spot[] = [
new Spot("1", new Music("6KNw3UKRp3QRsO7Cf4ASVE",
"MOLLY - A COLORS SHOW",
"Tame Impala",
"https://i.scdn.co/image/ab67616d0000b2734299eb40408fc73ce8bf490a",
"https://p.scdn.co/mp3-preview/4faf99856f15e03a09d50b91006efd3205606866?cid=774b29d4f13844c495f206cafdad9c86")
),
new Spot("2", new Music("5yHoANSze7sGzhn9MUarH3",
"Passat",
"Silk Sonic, Bruno Mars, Anderson .Paak",
"https://i.scdn.co/image/ab67616d0000b273e9df9b5a7df491536c51c922",
"https://p.scdn.co/mp3-preview/0bb7472026a00790950fc231fe61963ef7cc867b?cid=774b29d4f13844c495f206cafdad9c86")
),
new Spot("3", new Music("7suNqxRED5CrwyZSzYC0nT",
"Extendo",
"Kali Uchis",
"https://i.scdn.co/image/ab67616d0000b273b856464c40a062d1723a21f2",
"https://p.scdn.co/mp3-preview/5398121f6295965e3c7cad8a6dca5667ba7f4713?cid=774b29d4f13844c495f206cafdad9c86")
),
new Spot("4", new Music("07JqNLmPUJSlcouGQoJlzq",
"Addiction",
"Harry Styles",
"https://i.scdn.co/image/ab67616d0000b2739297f238f237431d56c67460",
"https://p.scdn.co/mp3-preview/33d12e9e5a3dd3394b1649d515912260b01579dd?cid=774b29d4f13844c495f206cafdad9c86")
),
new Spot("5", new Music("5Ylp75kdffyJSwISRPqEiL",
"La Vidéo",
"Harry Styles",
"https://i.scdn.co/image/ab67616d0000b2738900d48677696015bf325b8b",
"https://p.scdn.co/mp3-preview/4fff3f8d76a422f42cea39f001836a3d54937fc4?cid=774b29d4f13844c495f206cafdad9c86")
)
];

@ -1,12 +1,16 @@
export default class Music { export default class Music {
private _id : string;
private _title: string; private _title: string;
private _bio: string; private _bio: string;
private _image: ImageSource; private _image: string;
private _trackPreviewUrl : string;
constructor(title: string, bio: string, image: ImageSource) { constructor(id : string,title: string, bio: string, image: string, trackPreviewUrl: string) {
this._title = title; this._title = title;
this._bio = bio; this._bio = bio;
this._image = image; this._image = image;
this._id = id;
this._trackPreviewUrl = trackPreviewUrl;
} }
get title(): string { get title(): string {
@ -25,11 +29,27 @@ export default class Music {
this._bio = value; this._bio = value;
} }
get image(): ImageSource { get image(): string {
return this._image; return this._image;
} }
set image(value: ImageSource) { set image(value: string) {
this._image = value; this._image = value;
} }
get id(): string {
return this._id;
}
set id(value: string) {
this._id = value;
}
get trackPreviewUrl(): string {
return this._trackPreviewUrl;
}
set trackPreviewUrl(value: string) {
this._trackPreviewUrl = value;
}
} }

@ -2,20 +2,28 @@ import React from 'react';
import Favorite from '../screens/Favorite'; import Favorite from '../screens/Favorite';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { ArtistLayout } from '../components/Genre'; import { ArtistLayout } from '../components/Genre';
import MusicDetail from '../screens/MusicDetail';
import InfoScreen from '../screens/testPage';
import { createSharedElementStackNavigator } from 'react-navigation-shared-element';
import CurrentMusic from '../components/CurrentMusic';
const Stack = createSharedElementStackNavigator();
export default function MusicNavigation() { export default function MusicNavigation() {
const Stack = createStackNavigator();
return ( return (
<Stack.Navigator initialRouteName="Favorite"> <Stack.Navigator initialRouteName="Favorite" screenOptions={{gestureEnabled: true, headerShown: false, cardOverlayEnabled: true, cardStyle: {backgroundColor: "transparent"}}} >
<Stack.Screen <Stack.Screen
name="Favorite" name="Favorite"
component={Favorite} component={Favorite}
options={{ headerShown: false }}
/>
<Stack.Screen
name="MusicDetail"
component={InfoScreen}
sharedElements ={(route)=> {return [route.params.music.id]}}
/> />
<Stack.Screen <Stack.Screen
name="Genre" name="Genre"
component={ArtistLayout} component={CurrentMusic}
options={{ headerShown: false }}
/> />
</Stack.Navigator> </Stack.Navigator>
) )

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect } from 'react';
import { View, StyleSheet, Platform } from 'react-native'; import { View, StyleSheet, Platform } from 'react-native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
@ -11,6 +11,9 @@ import FontAwesome from 'react-native-vector-icons/FontAwesome';
import SpotNavigation from './SpotNavigation'; import SpotNavigation from './SpotNavigation';
import Login from '../screens/login'; import Login from '../screens/login';
import FladLoading from '../components/FladLoadingScreen'; import FladLoading from '../components/FladLoadingScreen';
import { useDispatch, useSelector } from 'react-redux';
import { getFavoritesMusic } from '../redux/actions/appActions';
// import { fetchFavoritesMusic } from '../redux/thunk/spotThunk';
export default function Navigation() { export default function Navigation() {
const BottomTabNavigator = createBottomTabNavigator(); const BottomTabNavigator = createBottomTabNavigator();
@ -23,6 +26,15 @@ export default function Navigation() {
text: 'rgb(138, 138, 138)', text: 'rgb(138, 138, 138)',
} }
}; };
//@ts-ignore
const favoritesMusicLength : number = useSelector(state => state.appReducer.favoriteMusic.length);
// const dispatch = useDispatch();
// useEffect(() => {
// const loadFavoritesMusics = async () => {
// await dispatch(fetchFavoritesMusic());
// };
// loadFavoritesMusics();
// }, [dispatch]);
return ( return (
// @ts-ignore // @ts-ignore
<NavigationContainer theme={MyTheme}> <NavigationContainer theme={MyTheme}>
@ -46,7 +58,7 @@ export default function Navigation() {
options={{ options={{
// use Selector state redux badgeCount ? badgeCount : undefined // use Selector state redux badgeCount ? badgeCount : undefined
tabBarBadge : 2, tabBarBadge : favoritesMusicLength,
tabBarBadgeStyle : {backgroundColor : 'yellow'}, tabBarBadgeStyle : {backgroundColor : 'yellow'},
headerShown: false, headerShown: false,
tabBarIcon: ({color}) => <View style={styles.IconContainer}><TabBarIcon name="heart" color={color}/></View>, tabBarIcon: ({color}) => <View style={styles.IconContainer}><TabBarIcon name="heart" color={color}/></View>,

@ -1,5 +1,5 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import FavoritePage from '../screens/favoritePage'; import FavoritePage from '../screens/favorite';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import Spot from '../screens/spot' import Spot from '../screens/spot'
import { createSharedElementStackNavigator } from "react-navigation-shared-element"; import { createSharedElementStackNavigator } from "react-navigation-shared-element";
@ -22,10 +22,10 @@ export default function SpotNavigation() {
name="Spots" name="Spots"
component={Spot} component={Spot}
/> />
<Stack.Screen {/* <Stack.Screen
name="DetailsSpot" name="DetailsSpot"
component={SpotDetailsPage} component={SpotDetailsPage}
/> /> */}
{/* <Stack.Screen {/* <Stack.Screen
name="DetailsSpot" name="DetailsSpot"
component={SpotDetailsPage} component={SpotDetailsPage}

@ -0,0 +1,17 @@
import Music from "../../Model/Music";
import { Spot } from "../../Model/Spot";
import { favoritesTypes } from "../types/favoritesTypes";
import {spotTypes} from "../types/spotTypes";
export const getFavoritesMusic = (favoritesMusic: Music[]) => {
return {
type: favoritesTypes.GET_FAVORITE_MUSICS,
playload: favoritesMusic,
};
}
export const addFavoritesMusic = (favoritesMusic: Music) => {
return {
type: favoritesTypes.ADD_FAVORITE_MUSICS ,
playload: favoritesMusic,
};
}

@ -4,18 +4,30 @@ import { discoveriesTypes } from "../types/discoverieTypes";
import { favoritesTypes } from "../types/favoritesTypes"; import { favoritesTypes } from "../types/favoritesTypes";
import { spotifyTypes } from "../types/spotifyTypes"; import { spotifyTypes } from "../types/spotifyTypes";
import { spotTypes } from "../types/spotTypes"; import { spotTypes } from "../types/spotTypes";
let tmpMusic: Music[] = [
// new Music("La pharmacie", "Jul",require("../assets/images/jul.png")),
// new Music("Deux frères", "PNL", require("../assets/images/pnl.png")),
new Music("6npyDB4mn8MO1A1h666FTk","Bambina", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png","https://p.scdn.co/mp3-preview/d38052978a79adced2187cd8b6497bb10bedc452?cid=774b29d4f13844c495f206cafdad9c86"),
// new Music("0qwxx9ouUc5kGmMWHglDpq","Stratos", "Kekra", "https://images.genius.com/ddc9cadedd1d4cef0860aaa85af9cd46.705x705x1.png",""),
new Music("03o8WSqd2K5rkGvn9IsLy2","Autobahn", "Sch", "https://images.genius.com/83b6c98680d38bde1571f6b4093244b5.1000x1000x1.jpg","https://p.scdn.co/mp3-preview/c55f95de81b8c3d0df04148da1b03bd38db56e8f?cid=774b29d4f13844c495f206cafdad9c86"),
new Music("6DPrYPPGYK218iVIZDix3i","Freeze Raël", "Freeze Corleone", "https://intrld.com/wp-content/uploads/2020/08/freeze-corleone-la-menace-fanto%CC%82me.png","https://p.scdn.co/mp3-preview/a9f9cb19ac1fe6db0d06b67decf8edbb25895a33?cid=774b29d4f13844c495f206cafdad9c86"),
// new Music("Blanka", "PNL", require("../assets/images/pnl.png")),
new Music("5GFHFEASZeJF0gyWuDDjGE","Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png","https://p.scdn.co/mp3-preview/9e854f4905c1228482e390169eb76d8520076b8f?cid=774b29d4f13844c495f206cafdad9c86"),
] ;
const initialState = { const initialState = {
spot: [] as Spot[], spot: [] as Spot[],
favoriteMusic: [] as Music [], favoriteMusic: tmpMusic,
userCurrentMusic : null userCurrentMusic : null
} }
const appReducer = (state = initialState, action : any) => { const appReducer = (state = initialState, action : any) => {
switch (action.type) { switch (action.type) {
case favoritesTypes.GET_FAVORITE_MUSICS:
return {...state, favoriteMusic: action.playload};
case favoritesTypes.ADD_FAVORITE_MUSICS: case favoritesTypes.ADD_FAVORITE_MUSICS:
return {...state, favoriteMusic: state.favoriteMusic.push(action.payload)}; return {...state, favoriteMusic: [action.playload, ...state.favoriteMusic]};
case favoritesTypes.REMOVE_FAVORITE_MUSICS: case favoritesTypes.REMOVE_FAVORITE_MUSICS:
return {...state, favoriteMusic: state.favoriteMusic}; return {...state, favoriteMusic: state.favoriteMusic};
case spotTypes.FETCH_SPOT: case spotTypes.FETCH_SPOT:
return {...state, spot: action.payload}; return {...state, spot: action.payload};

@ -4,7 +4,7 @@ import userReducer from './reducers/userReducer';
// Reference here all your application reducers // Reference here all your application reducers
const reducer = { const reducer = {
// appReducer: appReducer, appReducer: appReducer,
userReducer: userReducer userReducer: userReducer
} }

@ -8,6 +8,8 @@ import { Spot } from "../../Model/Spot";
import SpotifyService from "../../services/spotify/spotify.service"; import SpotifyService from "../../services/spotify/spotify.service";
import * as Location from 'expo-location'; import * as Location from 'expo-location';
import { setSpotList, setUserCurrentMusic } from "../actions/spotActions"; import { setSpotList, setUserCurrentMusic } from "../actions/spotActions";
import Music from "../../Model/Music";
import { getFavoritesMusic } from "../actions/appActions";
const key = 'userToken'; const key = 'userToken';
export type CreateSpotReqBody = { export type CreateSpotReqBody = {
@ -91,6 +93,46 @@ export const getCurrentUserMusic = (resuestHandler : SpotifyService)=> {
} }
} }
} }
export const searchMusic = async (resuestHandler : SpotifyService,search: string) => {
//
return async (dispatch) => {
// const fetchAll = async () => {
// const data = await fetch(`https://kanjialive-api.p.rapidapi.com/api/public/search/${search}`, options)
// .then(response => response.json());
// const fetchPromises = data.map(it =>
// fetch(`https://kanjialive-api.p.rapidapi.com/api/public/kanji/${it.kanji.character}`, options)
// .then(detail => detail.json())
// );
// const kanjis = await Promise.all(fetchPromises)
// .then(details => details.map(detail_data => KanjiMapper.ApiJsonToKanji(detail_data)));
// return kanjis;
// };
return resuestHandler.searchMusic(search).then(musics => dispatch((musics))).catch(err => console.log("something goes wrong while searching : " + err));
};
}
// export const fetchFavoritesMusic = () => {
// //@ts-ignore
// return async dispatch => {
// const MovieList: Music[] = [
// // new Music("La pharmacie", "Jul",require("../assets/images/jul.png")),
// // new Music("Deux frères", "PNL", require("../assets/images/pnl.png")),
// new Music("Bambina", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png"),
// new Music("Stratos", "Kekra", "https://images.genius.com/ddc9cadedd1d4cef0860aaa85af9cd46.705x705x1.png"),
// new Music("Autobahn", "Sch", "https://images.genius.com/83b6c98680d38bde1571f6b4093244b5.1000x1000x1.jpg"),
// new Music("Freeze Raël", "Freeze Corleone", "https://intrld.com/wp-content/uploads/2020/08/freeze-corleone-la-menace-fanto%CC%82me.png"),
// // new Music("Blanka", "PNL", require("../assets/images/pnl.png")),
// new Music("Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png"),
// ] ;
// dispatch(getFavoritesMusic(MovieList));
// }
// }
// export const getSpotList = () => { // export const getSpotList = () => {
// return async dispatch => { // return async dispatch => {
// try { // try {

@ -1,6 +1,7 @@
export const favoritesTypes = { export const favoritesTypes = {
GET_FAVORITE_MUSICS : 'GET_FAVORITE_MUSICS',
ADD_FAVORITE_MUSICS : 'ADD_FAVORITE_MUSICS', ADD_FAVORITE_MUSICS : 'ADD_FAVORITE_MUSICS',
REMOVE_FAVORITE_MUSICS : 'REMOVE_FAVORITE_MUSICS', REMOVE_FAVORITE_MUSICS : 'REMOVE_FAVORITE_MUSICS',
} }

@ -1,29 +1,28 @@
import React, { useState} from 'react'; import React, { useEffect, useState} from 'react';
import { Image,StyleSheet, Text, View, FlatList, ScrollView, TouchableOpacity, SafeAreaView } from 'react-native'; import { Image,StyleSheet, Text, View, FlatList, ScrollView, TouchableOpacity, TouchableHighlight } from 'react-native';
import CardMusic from '../components/CardMusic'; import CardMusic from '../components/CardMusic';
import normalize from '../components/Normalize'; import normalize from '../components/Normalize';
import Music from '../Model/Music' import Music from '../Model/Music'
import {useNavigation} from "@react-navigation/native"; import {useNavigation} from "@react-navigation/native";
import { useDispatch, useSelector } from 'react-redux';
import { getFavoritesMusic } from '../redux/actions/appActions';
import { SharedElement } from 'react-navigation-shared-element';
export default function favoritePage() { export default function favoritePage() {
const navigation = useNavigation(); const navigation = useNavigation();
//@ts-ignore
const favoritesMusic = useSelector(state => state.appReducer.favoriteMusic);
const dispatch = useDispatch();
const MUSIC_LIST : Music[] = [ const navigueToDetail = (music : any) => {
new Music("La pharmacie", "Jul",require("../assets/images/jul.png")), navigation.navigate("MusicDetail", {"music": music})
new Music("Deux frères", "PNL", require("../assets/images/pnl.png")), };
new Music("Bambina", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png"),
new Music("Stratos", "Kekra", "https://images.genius.com/ddc9cadedd1d4cef0860aaa85af9cd46.705x705x1.png"),
new Music("Autobahn", "Sch", "https://images.genius.com/83b6c98680d38bde1571f6b4093244b5.1000x1000x1.jpg"),
new Music("Freeze Raël", "Freeze Corleone", "https://intrld.com/wp-content/uploads/2020/08/freeze-corleone-la-menace-fanto%CC%82me.png"),
new Music("Blanka", "PNL", require("../assets/images/pnl.png")),
new Music("Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png"),
]
// to do // to do
const [filteredDataSource, setFilteredDataSource] = useState<Music[]>([]); const [filteredDataSource, setFilteredDataSource] = useState<Music[]>([]);
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const searchMusic = (text: string) => { const searchMusic = (text: string) => {
if (text) { if (text) {
const newData = MUSIC_LIST.filter(function (item: Music) { const newData = favoritesMusic.filter(function (item: Music) {
const search = item.title const search = item.title
? item.title.toUpperCase() : ''.toUpperCase(); ? item.title.toUpperCase() : ''.toUpperCase();
const textsearch = text.toUpperCase(); const textsearch = text.toUpperCase();
@ -40,9 +39,24 @@ export default function favoritePage() {
return ( return (
<View style={styles.body}> <View style={styles.body}>
<SafeAreaView style={styles.mainSafeArea}> <SafeAreaView style={styles.mainSafeArea}>
<View style={styles.titleContainer}> <View style={styles.titleContainer}>
<Text style={styles.title}>Favoris</Text> <Text style={styles.title}>Favoris</Text>
<Text style={styles.description}>Retrouvez ici vos musiques favorites</Text> <Text style={styles.description}>Retrouvez ici vos musiques favorites</Text>
</View>
<ScrollView>
<View>
<FlatList style={{marginBottom: 80}}
data={favoritesMusic}
renderItem={({ item }) => (
<TouchableHighlight onPress={() => {navigueToDetail(item)}}>
<SharedElement id={item.id}>
<CardMusic image={item.image} title={item.title} description={item.bio} id={item.id}/>
</SharedElement>
</TouchableHighlight>
)}
keyExtractor={(item: Music) => item.title }
/>
</View> </View>
<ScrollView> <ScrollView>
<View> <View>

@ -0,0 +1,283 @@
import { NavigationProp, RouteProp } from "@react-navigation/native";
import { View,Text,Image,StyleSheet, Dimensions, useWindowDimensions, Button, TouchableOpacity } from "react-native";
import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, useDerivedValue, useSharedValue, Value, withSpring, withTiming } from "react-native-reanimated";
import { BlurView } from 'expo-blur';
import qs from "qs";
import axios from "axios";
import { Buffer } from 'buffer';
import { Audio } from 'expo-av';
import { useEffect, useState } from "react";
import { State, TapGestureHandler } from "react-native-gesture-handler";
import { RequestHandler } from "../services/spotify/spotifyRequestHandler/utils";
import { FetchRequest } from "expo-auth-session/build/Fetch";
import Music from "../Model/Music";
import SpotifyService from "../services/spotify/spotify.service";
import { SharedElement } from "react-navigation-shared-element";
const halfPi = Math.PI/2;
//@ts-ignore
const MusicDetail = ({ route }) => {
const music : Music = route.params.music;
const [currentspot, setCurrentspot] = useState(music);
const [sound, setSound] = useState(null);
const [isPlaying, setIsPlaying] = useState(false);
const loader = useSharedValue(0);
useEffect(() => {
loader.value = isPlaying ? 1 : 0
}, [isPlaying,loader ]);
const transition = useDerivedValue(()=>{
return withTiming(loader.value, {duration : 1000})
}
)
// const styleAniamatedButton = useAnimatedStyle(() => {
// const verticalAxis =interpolate(
// transition.value,
// [0,1],
// [circumference, 0]
// )
// return {
// top : withSpring( verticalAxis),
// left : withSpring(horizontalAxis),
// };
// })
const playTrackPreview = async () => {
console.log("===============================================================================================================");
console.log('get in Sound');
const { sound } = await Audio.Sound.createAsync({uri :music.trackPreviewUrl});
//@ts-ignore
setSound(sound);
console.log('Playing Sound');
await sound.playAsync();
setIsPlaying(true);
// const soundObject = new Audio.Sound();
// try {
// await soundObject.loadAsync({ uri: trackPreviewUrl });
// await soundObject.playAsync();
// setIsPlaying(true);
// } catch (error) {
// console.log('Error loading sound:', error);
// }
};
const handlePlaySound = async () => {
if (sound === null) {
const { sound: newSound } = await Audio.Sound.createAsync(
{ uri: music.trackPreviewUrl },
{ shouldPlay: true }
);
setSound(newSound);
setIsPlaying(true);
} else {
setIsPlaying(true);
//@ts-ignore
await sound.playAsync();
}
};
const handleStopSound = async () => {
if (sound !== null) {
setIsPlaying(false);
//@ts-ignore
await sound.stopAsync();
}
else{
}
};
useEffect(() => {
return sound ? () => {
console.log('Unloading Sound');
//@ts-ignore
sound.unloadAsync();
}
: undefined;
}, [sound]);
// useEffect(() => {
// if(isPlaying){
// }
// })
const sensor = useAnimatedSensor(SensorType.ROTATION);
const styleAniamatedImage = useAnimatedStyle(() => {
const {yaw, pitch, roll} = sensor.sensor.value;
const verticalAxis =interpolate(
pitch,
[-halfPi*2,halfPi*2],
[-45, 45]
)
const horizontalAxis =interpolate(
roll,
[-halfPi*2,halfPi*2],
[-45, 45]
)
return {
top : withSpring( verticalAxis),
left : withSpring(horizontalAxis),
};
})
// const CLIENT_ID = "1f1e34e4b6ba48b388469dba80202b10";
// const CLIENT_SECRET = "779371c6d4994a68b8dd6e84b0873c82";
// const spotify = "BQA2IAFZ-7ta4-_4_Uqdcdrqi_peE6Hlf1jwxFqjXTbwes0z8xgVGx0rE3zv4cQlusd1ILJhRwkxzPsL1YakzSvCxaTI1P7kOzBrrMqlkDgk4vlFvzLjScB0hBLULbpZyn3ylgx4RyZBEWfmc24wZPQOsrJU58AYCveA52UxYVSIc_Frr7LZyRmwjzGB68MPZeBD"
// var authOptions = {
// method: 'GET',
// url: 'https://api.spotify.com/v1/me/player/currently-playing',
// headers: {
// 'Authorization': 'Bearer ' + spotify,
// 'Content-Type' : 'application/json',
// 'market' : 'FR',
// },
// json: true
// };
// var id = '0cFS3AMF9Lhj3CNoFvwjvY'
// const requestor = new RequestHandler()
// const getCurrentTrack = async () => {
// try {
// const opt : FetchRequest ={headers : Record}
// requestor.spotifyFetch(`tracks${id}`,)
// // var GetTrackOptions = {
// // method: 'GET',
// // url: 'https://api.spotify.com/v1/tracks/'+id,
// // headers: {
// // 'Authorization': 'Bearer ' + spotify,
// // 'Content-Type' : 'application/json',
// // 'market' : 'FR',
// // },
// // json: true
// // };
// // const resp = await axios(GetTrackOptions)
// // console.log("============");
// // console.log(resp.data.href);
// // console.log("================================"+resp.data.album.images[0].url+ "================================");
// // var tmp = currentspot;
// // tmp.sourceUrl = resp.data.album.images[0].url;
// // setCurrentspot(tmp);
// // await axios(authOptions).then(async (response) =>{
// // console.log(response.data.item.preview_url);
// // const id = response.data.item.id;
// // var GetTrackOptions = {
// // method: 'GET',
// // url: 'https://api.spotify.com/v1/tracks/'+id,
// // headers: {
// // 'Authorization': 'Bearer ' + spotify,
// // 'Content-Type' : 'application/json',
// // 'market' : 'FR',
// // },
// // json: true
// // };
// // console.log("============");
// // const music = await axios(GetTrackOptions);
// // console.log("================================"+music.data+ "================================");
// // currentspot.sourceUrl = music.data.images[0];
// // setCurrentspot(currentspot);
// // })
// // const response = await fetch('https://api.spotify.com/v1/me', {
// // method: 'GET',
// // headers: {
// // Authorization: 'Bearer ' + spotify,
// // 'Content-Type': 'application/json',
// // },
// // });
// // response.json()
// // destructure the response and rename the properties to be in camelCase to satisfy my linter ;)
// } catch (err) {
// console.error(err);
// }
// }
const animationState = new Value(State.UNDETERMINED);
const playMusic = async (id: string) => {
try {
const service = new SpotifyService("BQDWJTPvSloZPYDqLc1YWri2LEcognvqoM5bdoCWMuHR9In2FhaKq5tW3-VC5JET9dD9K-W4Rmm0IiyhtX-fSL3Tb8RTHMJUc5GKFq2jxWlH7QXxsiYZV8Fhw2qU1eCpSof1qkPsBd1R36GOgcBaXq2N6kLTP5UcfP-gzjz65x_fVRSxoP6znK2dkvL6saQ6WwzoEFopqpqo") ;
console.log("=====================================================)))))))))))))))"+id+"================================")
await service.playMusic(id);
}catch(error){}
}
return (
<View style={{ flex: 1, justifyContent : 'flex-start', alignItems : 'center' }}>
{/* <SharedElement id={spot.name} style={{ flex: 1 }}> */}
<View style={{borderWidth : 1, borderColor : 'red'}}>
<Animated.Image
source={{
uri:currentspot.image ,
}}
style={[
{
width: 370,
height: 370,
borderRadius : 24,
resizeMode: 'stretch',
},styleAniamatedImage
]}
/>
<Button title="Play Track On Device"
onPress={() => {
playMusic(currentspot.id)
// promptAsync();
}}
/>
</View>
{/* Button */}
{/* <TapGestureHandler {...gestureHandler}> */}
<Animated.View>
<TouchableOpacity style={{
backgroundColor: '#1DB954',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 24,
}}
onPressOut={handleStopSound}
onLongPress={handlePlaySound}
delayLongPress={1000}>
<Text style={ {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',}}>
{isPlaying ? 'Playing...' : 'Play'}
</Text>
</TouchableOpacity>
</Animated.View>
{/* </TapGestureHandler> */}
{/* Button */}
{/* </SharedElement> */}
</View>
);
};
export default MusicDetail;

@ -6,7 +6,7 @@ import Animated from 'react-native-reanimated';
import Card from '../components/Card'; import Card from '../components/Card';
import { cards as cardArray } from '../data/data' import { cards as cardArray, spotArray2 } from '../data/data'
import FladButton from '../components/button/button'; import FladButton from '../components/button/button';
import axios from 'axios'; import axios from 'axios';
@ -21,6 +21,9 @@ import Lotties from '../assets/lottie/Lottie';
import FladLoading from '../components/FladLoadingScreen'; import FladLoading from '../components/FladLoadingScreen';
import { SharedElement } from 'react-navigation-shared-element'; import { SharedElement } from 'react-navigation-shared-element';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import Music from '../Model/Music';
import { addFavoritesMusic } from '../redux/actions/appActions';
import { useDispatch } from 'react-redux';
interface SpotProps { interface SpotProps {
} }
@ -90,17 +93,30 @@ async function getValueFor(key:string) :Promise<string | null> {
} }
export default function Spot() { export default function Spot() {
const [cards, setCards] = useState(cardArray); const [cards, setCards] = useState(spotArray2);
const [currentCard, setcurrentCard] = useState(cards[cards.length - 1]); const [currentCard, setcurrentCard] = useState(cards[cards.length - 1]);
const onSwipe = (index: number, direction: 'left' | 'right') => { const onSwipe = (index: number, direction: 'left' | 'right' | 'down') => {
if (direction === 'right') { if (direction === 'right') {
// Swiped right // Swiped right
console.log("====2==="+currentCard.music.title+"======2=========");
addLike(currentCard.music);
console.log('Swiped right');
} else if (direction === 'left') { } else if (direction === 'left') {
// Swiped left // Swiped left
console.log('Swiped left');
}
else if (direction === 'down') {
// Swiped down
console.log('Swiped down');
} }
// update the state of the cards state when it remove thisy // update the state of the cards state when it remove this
setCards(cards.filter((_, i) => i !== index)); setTimeout(() => {
setcurrentCard(cards[index-1]); setCards(cards.filter((_, i) => i !== index));
setcurrentCard(cards[index - 1]);
}, 3);
// setCards(cards.filter((_, i) => i !== index));
// setcurrentCard(cards[index-1]);
}; };
const likeButtonref = useRef<LottieView>(null); const likeButtonref = useRef<LottieView>(null);
@ -110,27 +126,23 @@ export default function Spot() {
const onLike = useCallback( () => { const onLike = useCallback( () => {
likeButtonref.current?.reset(); likeButtonref.current?.reset();
likeButtonref.current?.play(0,55); likeButtonref.current?.play(0,55);
likeButtonref.current?.play(55,0);
}, []) }, [])
const dispatch = useDispatch();
function addLike(music: Music) {
onLike();
console.log("====3==="+currentCard.music.title+"======3=========");
dispatch(addFavoritesMusic(music))
// dispatch(addFavoriteMusic(props));
// if (displayIndex == trendingMovies.length - 1) {
// setdisplayIndex(0);
// swiper.swipeLeft();
// }
}
// function addWatchLater(props: Movie) {
// dispatch(addMovieToWatchLater(props));
// dispatch(removeMovieTrending(props));
// if (displayIndex == trendingMovies.length - 1) {
// setdisplayIndex(0);
// swiper.swipeLeft();
// }
// }
// function addFavourite(props: Movie) {
// dispatch(addMovieToFavourite(props));
// dispatch(removeMovieTrending(props));
// if (displayIndex == trendingMovies.length - 1) {
// setdisplayIndex(0);
// swiper.swipeLeft();
// }
// }
// const hapti = (() => { // const hapti = (() => {
// Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy) // Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)
@ -206,7 +218,7 @@ export default function Spot() {
alignItems: "center", alignItems: "center",
}} }}
source={{ source={{
uri:currentCard.sourceUrl , uri:currentCard.music.image ,
}} }}
></ImageBackground> ></ImageBackground>
<SafeAreaView style={styles.mainSafeArea}> <SafeAreaView style={styles.mainSafeArea}>
@ -235,13 +247,13 @@ export default function Spot() {
<View style={{flex : 1.83, justifyContent: 'center', alignItems: 'center' }}> <View style={{flex : 1.83, justifyContent: 'center', alignItems: 'center' }}>
{cards.map((card, index) => ( {cards.map((card, index) => (
<View key={card.name} style = {{ position:'absolute'}} > <View key={card.userSpotifyId} style = {{ position:'absolute'}} >
<Pressable onLongPress={() => {hapti(card)}} > <Pressable onLongPress={() => {hapti(card)}} >
{/* <SharedElement id={card.name}> */} {/* <SharedElement id={card.name}> */}
<Card <Card
title={card.name} title={card.music.title}
image={card.sourceUrl} image={card.music.image}
onSwipe={(direction) => {onSwipe(index, direction)}} onSwipe={(direction) => {onSwipe(index, direction)}}
/> />
{/* </SharedElement> */} {/* </SharedElement> */}
@ -260,7 +272,7 @@ export default function Spot() {
<LottieView autoPlay={false} loop={false} ref={likeButtonref} source={Lotties.likeAnimation} style={styles.lottie}/> <LottieView autoPlay={false} loop={false} ref={likeButtonref} source={Lotties.likeAnimation} style={styles.lottie}/>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={onLike}> <TouchableOpacity style={styles.button} onPress={onLike}>
<LottieView autoPlay={false} loop={false} ref={likeButtonref} source={Lotties.likeAnimation} style={styles.lottie}/> <LottieView autoPlay={false} loop={false} ref={likeButtonref} speed={2} source={Lotties.likeAnimation} style={styles.lottie}/>
</TouchableOpacity> </TouchableOpacity>
</Animated.View> </Animated.View>

@ -0,0 +1,479 @@
import * as React from 'react';
import {TouchableOpacity, ScrollView, View, Text, StyleSheet, Image, SafeAreaView, FlatList, Animated} from 'react-native';
import {useSafeAreaInsets} from "react-native-safe-area-context";
import {LinearGradient} from 'expo-linear-gradient';
import {useEffect, useState} from "react";
import Ionicons from "@expo/vector-icons/Ionicons";
import { SharedElement } from "react-navigation-shared-element";
import { NavigationProp, RouteProp, useNavigation } from "@react-navigation/native";
import { Dimensions, useWindowDimensions, Button } from "react-native";
import { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, useDerivedValue, useSharedValue, Value, withSpring, withTiming } from "react-native-reanimated";
import { BlurView } from 'expo-blur';
import qs from "qs";
import axios from "axios";
import { Buffer } from 'buffer';
import { Audio } from 'expo-av';
import { State, TapGestureHandler } from "react-native-gesture-handler";
import { RequestHandler } from "../services/spotify/spotifyRequestHandler/utils";
import { FetchRequest } from "expo-auth-session/build/Fetch";
import SpotifyService from "../services/spotify/spotify.service";
import Music from '../Model/Music';
import { HorizontalFlatList } from '../components/HorizontalFlatList';
import littleCard from '../components/littleCard';
const halfPi = Math.PI/2;
// InfoScreen.sharedElement = (navigation : ReturnType<typeof useNavigation>)=>{
// const music = navigation.getParam('music');
// return [music.id];
// }
// @ts-ignore
export default function InfoScreen({route, navigation}) {
const item: Music = route.params.music;
const insets = useSafeAreaInsets();
const [similarMusics, setSimilarMusics] = useState<Music[]>([]);
// parralax
// parralax
const styles = StyleSheet.create({
background1: {
height: '100%',
width: '100%',
paddingTop: insets.top,
},
body: {
backgroundColor: "#0E0E0E"
},
backgroundSection: {
height: "100%",
width: "100%",
position: "absolute"
},
back_drop: {
height: "45%",
width: '100%',
position: "absolute",
},
gradientFade: {
height: "30%",
top: "25%"
},
backButton: {
position: "absolute",
top: 10,
left: 5
},
list: {
height: "100%"
},
section1: {
paddingHorizontal: 35
},
title: {
color: "white",
fontSize: 43,
fontWeight: "bold",
paddingBottom: 10,
paddingTop: "45%"
},
characteristics: {
flexDirection: "row",
width: "100%",
justifyContent: "flex-start"
},
stars: {
flexDirection: "row",
width: "100%",
justifyContent: "flex-start",
alignItems: "center",
paddingBottom: 30
},
starsLabel: {
color: "#FFC42D",
fontWeight: "bold",
paddingLeft: 10,
fontSize: 16
},
player: {
borderRadius: 10,
overflow: "hidden"
},
resume: {
color: "#B3B3B3",
paddingTop: 30,
fontSize: 17
},
creditSection: {
paddingTop: 30
},
creditTitle: {
color: "#2998FD",
paddingBottom: 20,
paddingLeft: 35,
fontSize: 17,
fontWeight: "600"
},
similarSection: {
paddingTop: 30
},
similarTitle: {
color: "#2998FD",
paddingLeft: 35,
fontSize: 17,
fontWeight: "600",
paddingBottom: 20
},
similarContainer: {
width: 90,
marginHorizontal: 7
},
similarPoster: {
height: 130,
width: 90,
borderRadius: 8
},
similarTitleFilm: {
color: "#DADADA",
paddingTop: 5,
fontWeight: "300"
},
reviewSection: {
paddingTop: 30
},
reviewTitle: {
color: "#2998FD",
paddingLeft: 35,
fontSize: 17,
fontWeight: "600",
paddingBottom: 10
},
reviewContainer: {
marginHorizontal: 7,
width: 300,
padding: 20,
backgroundColor: "#09090F",
marginVertical: 10,
borderRadius: 14,
borderWidth: 0.8,
borderColor: "rgba(223,223,223,0.14)"
},
reviewInfo: {
flexDirection: "row",
paddingBottom: 20
},
imageProfile: {
height: 50,
width: 50,
borderRadius: 100
},
infoContainer: {
paddingLeft: 10,
flexDirection: "row",
justifyContent: "space-between",
width: "80%",
alignItems: "center"
},
pseudo: {
color: "white",
paddingTop: 5,
fontWeight: "700",
fontSize: 16
},
date: {
color: "grey",
paddingTop: 5,
fontWeight: "500",
fontSize: 14
},
message: {
color: "#B3B3B3",
paddingTop: 5,
fontWeight: "400"
},
creditContainer: {
width: 90,
marginHorizontal: 7,
alignItems: "center"
},
bubble: {
justifyContent: "center"
},
photo: {
height: 90,
width: 90,
borderRadius: 200,
borderWidth: 3,
borderColor: "rgba(255,255,255,0.8)"
},
popularityDot: {
backgroundColor: "white",
borderRadius: 20,
padding: 2,
paddingHorizontal: 5,
justifyContent: "center",
alignItems: "center",
position: "absolute",
bottom: 0,
right: 0,
flexDirection: "row"
},
popularityLabel: {
color: "black",
fontWeight: "500",
paddingRight: 4
},
creditName: {
color: "#DADADA",
paddingTop: 5,
fontWeight: "300"
}
});
const getSimilarMusics = async () => {
const tmpMusic: Music[] = [
// new Music("La pharmacie", "Jul",require("../assets/images/jul.png")),
// new Music("Deux frères", "PNL", require("../assets/images/pnl.png")),
new Music("6npyDB4mn8MO1A1h666FTk","Bambina", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png","https://p.scdn.co/mp3-preview/d38052978a79adced2187cd8b6497bb10bedc452?cid=774b29d4f13844c495f206cafdad9c86"),
// new Music("0qwxx9ouUc5kGmMWHglDpq","Stratos", "Kekra", "https://images.genius.com/ddc9cadedd1d4cef0860aaa85af9cd46.705x705x1.png",""),
new Music("03o8WSqd2K5rkGvn9IsLy2","Autobahn", "Sch", "https://images.genius.com/83b6c98680d38bde1571f6b4093244b5.1000x1000x1.jpg","https://p.scdn.co/mp3-preview/c55f95de81b8c3d0df04148da1b03bd38db56e8f?cid=774b29d4f13844c495f206cafdad9c86"),
new Music("6DPrYPPGYK218iVIZDix3i","Freeze Raël", "Freeze Corleone", "https://intrld.com/wp-content/uploads/2020/08/freeze-corleone-la-menace-fanto%CC%82me.png","https://p.scdn.co/mp3-preview/a9f9cb19ac1fe6db0d06b67decf8edbb25895a33?cid=774b29d4f13844c495f206cafdad9c86"),
// new Music("Blanka", "PNL", require("../assets/images/pnl.png")),
new Music("5GFHFEASZeJF0gyWuDDjGE","Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png","https://p.scdn.co/mp3-preview/9e854f4905c1228482e390169eb76d8520076b8f?cid=774b29d4f13844c495f206cafdad9c86"),
] ;
setSimilarMusics(tmpMusic);
}
useEffect(() => {
getSimilarMusics();
}, []);
////////////////////////////////////////////////
const {width, height} = useWindowDimensions();
const [currentspot, setCurrentspot] = useState(item);
const [sound, setSound] = useState(null);
const [isPlaying, setIsPlaying] = useState(false);
const loader = useSharedValue(0);
useEffect(() => {
loader.value = isPlaying ? 1 : 0
}, [isPlaying,loader ]);
const transition = useDerivedValue(()=>{
return withTiming(loader.value, {duration : 1000})
}
)
// const styleAniamatedButton = useAnimatedStyle(() => {
// const verticalAxis =interpolate(
// transition.value,
// [0,1],
// [circumference, 0]
// )
// return {
// top : withSpring( verticalAxis),
// left : withSpring(horizontalAxis),
// };
// })
const trackPreviewUrl = 'https://p.scdn.co/mp3-preview/08ef3b9d6dbd6bab233f5e9ca564091902767f71?cid=774b29d4f13844c495f206cafdad9c86';
const playTrackPreview = async () => {
console.log("===============================================================================================================");
console.log('get in Sound');
const { sound } = await Audio.Sound.createAsync({uri :item.trackPreviewUrl});
//@ts-ignore
setSound(sound);
console.log('Playing Sound');
await sound.playAsync();
setIsPlaying(true);
// const soundObject = new Audio.Sound();
// try {
// await soundObject.loadAsync({ uri: trackPreviewUrl });
// await soundObject.playAsync();
// setIsPlaying(true);
// } catch (error) {
// console.log('Error loading sound:', error);
// }
};
const handlePlaySound = async () => {
if (sound === null) {
const { sound: newSound } = await Audio.Sound.createAsync(
{ uri: item.trackPreviewUrl },
{ shouldPlay: true }
);
setSound(newSound);
} else {
//@ts-ignore
await sound.playAsync();
}
};
const handleStopSound = async () => {
if (sound !== null) {
//@ts-ignore
await sound.stopAsync();
}
else{
setIsPlaying(true);
}
};
useEffect(() => {
return sound ? () => {
console.log('Unloading Sound');
//@ts-ignore
sound.unloadAsync();
}
: undefined;
}, [sound]);
const sensor = useAnimatedSensor(SensorType.ROTATION);
const styleAniamatedImage = useAnimatedStyle(() => {
const {yaw, pitch, roll} = sensor.sensor.value;
const verticalAxis =interpolate(
pitch,
[-halfPi*2,halfPi*2],
[-45, 45]
)
const horizontalAxis =interpolate(
roll,
[-halfPi*2,halfPi*2],
[-45, 45]
)
return {
top : withSpring( verticalAxis),
left : withSpring(horizontalAxis),
};
})
const animationState = new Value(State.UNDETERMINED);
const playMusic = async (id: string) => {
try {
const service = new SpotifyService("BQC4k_OPQXENwmm2S8qLm9whlJT9IjeKsuG6kJNyVCSd88b0L-zOY84VqwvQxFsc9G3GvtPyUMezwxi8BBBloitzbhWX5tmTKTaLsJosGTnb7xivwNhRv0-LnNYbZWB24ZGAg0xPmDLn0yYmYlo7M_SMK5cCZdYQcZNXAuMYaI18GVXKoICBaKfCn4GcqBiRRgXyCVQnNGU4") ;
console.log("=====================================================)))))))))))))))"+id+"================================")
await service.playMusic(id);
}catch(error){}
}
return (
<View style={styles.body}>
<View style={styles.backgroundSection}>
<Image
style={styles.back_drop}
source={{
uri: item.image,
}}
></Image>
<LinearGradient
// Background Linear Gradient
colors={['rgba(0,0,0,0.8)', 'transparent']}
/>
<LinearGradient style={styles.gradientFade}
// Button Linear Gradient
colors={['rgba(14,14,14,0)', 'rgba(14,14,14,0.7)', 'rgba(14,14,14,1)', 'rgba(14,14,14,1)']}>
</LinearGradient>
</View>
<View style={styles.background1}>
<TouchableOpacity onPress={() => navigation.goBack()} style={{zIndex: 100}}>
<Ionicons name="ios-arrow-back" size={30} color="white" style={styles.backButton}/>
</TouchableOpacity>
<ScrollView style={styles.list} showsVerticalScrollIndicator={false} scrollEventThrottle={4}
>
<View style={styles.section1}>
<View style={{ flex: 1, justifyContent : 'flex-start', alignItems : 'center' }}>
{/* <SharedElement id={spot.name} style={{ flex: 1 }}> */}
<View>
<SharedElement id ={item.id}>
<Animated.Image
source={{
uri:currentspot.image ,
}}
style={[
{
width: 370,
height: 370,
borderRadius : 24,
resizeMode: 'stretch',
},styleAniamatedImage
]}
/>
</SharedElement>
<Button title="Play Track On Device"
onPress={() => {
playMusic(currentspot.id)
// promptAsync();
}}
/>
</View>
{/* Button */}
{/* <TapGestureHandler {...gestureHandler}> */}
<Animated.View>
<TouchableOpacity style={{
backgroundColor: '#1DB954',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 24,
}} onPressIn={handlePlaySound}
onPressOut={handleStopSound}
onLongPress={handlePlaySound}
delayLongPress={1000}>
<Text style={ {
color: '#fff',
fontSize: 16,
fontWeight: 'bold',}}>
{isPlaying ? 'Playing...' : 'Play'}
</Text>
</TouchableOpacity>
</Animated.View>
</View>
</View>
{similarMusics.length !== 0 && (
<HorizontalFlatList renderCell={littleCard} title={'Simillar Music'} data={similarMusics}>
</HorizontalFlatList>
)}
</ScrollView>
</View>
</View>
)
}

@ -1,7 +1,7 @@
import axios from "axios"; import axios from "axios";
import MusicFactory from "../../Model/factory/MusicFactory"; import MusicFactory from "../../Model/factory/MusicFactory";
import Music from "../../Model/Music"; import Music from "../../Model/Music";
import { RequestHandler } from "./spotifyRequestHandler/utils"; import { FetchOptions, RequestHandler } from "./spotifyRequestHandler/utils";
export default class SpotifyService implements IspotifyService { export default class SpotifyService implements IspotifyService {
private readonly API_URL = "https://flad-api-production.up.railway.app/api/"; private readonly API_URL = "https://flad-api-production.up.railway.app/api/";
@ -46,17 +46,116 @@ export default class SpotifyService implements IspotifyService {
return respMusic.data.items[0].track.id; return respMusic.data.items[0].track.id;
} }
public async playMusic(): Promise<string | null>{ public async playMusic(idMusic : string): Promise<void>{
var requestData :string = '/me/player/recently-played'; var requestData :string = '/me/player/play';
const fetchOptions: FetchOptions = {
method: 'PUT',
body: {
uris: [`spotify:track:${idMusic}`],
position_ms: 0
}
};
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, fetchOptions,this.token);
console.log(respMusic.data);
// need to handle when
// if (respMusic.status != 200) {
// if (respMusic.status == 400 && respMusic.data.message =='need to use Spotify premium'){
// }
// }
// if(respMusic){
// console.log(respMusic);
// console.log(respMusic.data);
// }
return ;
}
public async searchMusic(text : string): Promise<Music[]>{
var requestData :string = '/search';
const fetchOptions: FetchOptions = {
params: {
q: text,
type: 'track'
}
};
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, fetchOptions,this.token);
if (respMusic.status != 200) {
}
const tracksData = respMusic?.data?.tracks?.items;
if (!tracksData || !Array.isArray(tracksData)) {
return [];
}
const tracks = tracksData.map((trackData: any) => {
// const { id, name, artists, album } = trackData;
return MusicFactory.mapFromSpotifyTrack(trackData)
// const artistNames = artists.map((artist: any) => artist.name).join(', ');
// const linkCover = album?.images[0]?.url || '';
// return new Music(id, name, artistNames, linkCover);
});
return tracks;
}
// tempo version
public async getMusicMoreDetails(idMusic : string): Promise<string>{
var requestData :string = '/audio-features/' + idMusic;
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, undefined,this.token); const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, undefined,this.token);
if (respMusic.status != 200) { if (respMusic.status != 200) {
} }
if (respMusic.data.items.length <= 0) {
return null; return respMusic.data.audio_features.tempo;
}
return respMusic.data.items[0].track.id;
} }
public async getRelatedArtist(idArtist : string): Promise<string>{
var requestData :string = '/artists/' + idArtist + '/related-artists';
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, undefined,this.token);
if (respMusic.status != 200) {
}
return respMusic.data.audio_features.tempo;
}
public async getArtistTopTracks(idArtist : string): Promise<string>{
var requestData :string = '/artists/' + idArtist + '/top-tracks';
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, undefined,this.token);
if (respMusic.status != 200) {
}
return respMusic.data.audio_features.tempo;
}
public async addItemToPlayList(playlistId : string, idMusic : string): Promise<void>{
var requestData :string = '/playlists/' + playlistId + '/tracks';
const fetchOptions: FetchOptions = {
method: 'POST',
body: {
uris: [`spotify:track:${idMusic}`]
}
};
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, fetchOptions,this.token);
console.log(respMusic.data);
return ;
}
public async createPlayList(userId : string,name : string,description : string): Promise<void>{
var requestData :string = '/users/' + encodeURIComponent(userId) + '/playlists';
const fetchOptions: FetchOptions = {
method: 'POST',
body: {
"public": false,
"name": name || "New Flad Playlist",
"description": description,
}
};
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, fetchOptions,this.token);
console.log(respMusic.data);
return ;
}
async getSpotifyCredentials() { async getSpotifyCredentials() {
const res = await axios.get(this.API_URL) const res = await axios.get(this.API_URL)
// then verify error // then verify error

@ -20,6 +20,7 @@ export class RequestHandler{
} }
public async spotifyFetch(url: string, options: FetchOptions = {}, token: string) { public async spotifyFetch(url: string, options: FetchOptions = {}, token: string) {
console.log(options+ "sds=============");
const resp = await axios({ const resp = await axios({
url: `https://api.spotify.com/${this.version}${url}`, url: `https://api.spotify.com/${this.version}${url}`,
method: options.method || 'GET', method: options.method || 'GET',
@ -31,7 +32,7 @@ export class RequestHandler{
}, },
data: options.body data: options.body
}); });
console.log() console.log(resp);
return resp; return resp;
// if ( // if (
// // @ts-ignore // // @ts-ignore

Loading…
Cancel
Save