Redux #8

Merged
emre.kartal merged 3 commits from Redux into master 2 years ago

@ -50,6 +50,9 @@ class App {
private initialiseControllers(controllers: Controller[]): void { private initialiseControllers(controllers: Controller[]): void {
controllers.forEach((controller: Controller) => { controllers.forEach((controller: Controller) => {
this.express.use('/api', controller.router); this.express.use('/api', controller.router);
this.express.get('/toto', (req, res) => {
res.send('Hello World!');
})
}); });
} }

@ -161,7 +161,8 @@ class UserController implements Controller {
): Promise<Response | void> => { ): Promise<Response | void> => {
try { try {
// the FladId should be created by the Userservice // the FladId should be created by the Userservice
const { name, email, password,idFlad, idSpotify } = req.body; const { name, email, password , idFlad, idSpotify } = req.body;
console.log(name, email, password, idFlad, idSpotify);
const token = await this.userService.register( const token = await this.userService.register(
name, name,

@ -1,4 +1,4 @@
import { Schema, model } from 'mongoose'; import { Schema, model,Document } from 'mongoose';
const locationSchema = new Schema( const locationSchema = new Schema(

@ -7,7 +7,8 @@ const register = Joi.object({
password: Joi.string().min(6).required(), password: Joi.string().min(6).required(),
// can add an field like confimPassword and cheked that the password is equal to the confirmPassword // can add an field like confimPassword and cheked that the password is equal to the confirmPassword
idSpotify: Joi.string(),
idFlad : Joi.string(),
}); });
const login = Joi.object({ const login = Joi.object({

@ -39,13 +39,14 @@ class UserService {
email: string, email: string,
password: string password: string
): Promise<string | Error> { ): Promise<string | Error> {
try {
// should maybe creat a method base on id and other information for better security // should maybe creat a method base on id and other information for better security
// need to view with Emre // need to view with Emre
const user = await this.user.findOne({ email }); const user = await this.user.findOne({ email });
console.log(user?._id);
// const user = await this.user.findById(idFlad); // const user = await this.user.findById(idFlad);
if (!user) { if (user === undefined || user === null) {
console.log("Could")
throw new Error('Unable to find user with that email address'); throw new Error('Unable to find user with that email address');
} }
@ -54,9 +55,7 @@ class UserService {
} else { } else {
throw new Error('Wrong credentials given'); throw new Error('Wrong credentials given');
} }
} catch (error) {
throw new Error('Unable to create user');
}
} }
} }

@ -2,17 +2,18 @@ import Navigation from './navigation/Navigation';
import { StyleSheet,SafeAreaView } from 'react-native'; import { StyleSheet,SafeAreaView } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context'; import { SafeAreaProvider } from 'react-native-safe-area-context';
import StartNavigation from './navigation/StartNavigation'; import StartNavigation from './navigation/StartNavigation';
import { Provider, useDispatch, useSelector } from 'react-redux';
import store from './redux/store';
import { useCallback, useEffect } from 'react';
import * as SplashScreen from 'expo-splash-screen';
import AuthNavigation from './navigation/AuthNavigation';
export default function App() { export default function App() {
return ( return (
<SafeAreaProvider> <Provider store={store}>
<StartNavigation/> <AuthNavigation/>
</SafeAreaProvider> </Provider>
// <SafeAreaView style={styles.mainSafeArea}>
// {/* <Navigation/> */}
// </SafeAreaView>
); );
} }

@ -1,9 +1,17 @@
import Music from "./Music";
class Spot { export class Spot {
private userId : string; private _userId : string;
public music : Music; public _music : Music;
constructor(userId : string, music : Music){ constructor(userId : string, music : Music){
this.userId = userId; this._userId = userId;
this.music = music; this._music = music;
} }
get userSpotifyId(): string {
return this._userId;
}
get idSpotify(): Music {
return this._music;
}
} }

@ -0,0 +1,34 @@
class TokenSpotify {
String _accessToken;
final String _refreshToken;
late DateTime _tokenEnd;
// TokenSpotify(this._accessToken, this._refreshToken, int expiresIn) {
// _setTokenEnd(expiresIn);
// }
// _setTokenEnd(int expiresIn) {
// _tokenEnd = DateTime.now().add(Duration(seconds: expiresIn));
// }
// Future<String> getAccessToken() async {
// if (DateTime.now().isAfter(_tokenEnd)) {
// await _actualiseToken();
// }
// return _accessToken;
// }
// _actualiseToken() async {
// var urlToken = Uri.https('accounts.spotify.com', 'api/token', {
// 'grant_type': 'refresh_token',
// 'refresh_token': _refreshToken,
// 'client_id': ApiSpotifyIdentification.clientId
// });
// setResponse(await http.post(urlToken, headers: <String, String>{
// 'Content-Type': 'application/x-www-form-urlencoded'
// }));
// var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
// _accessToken = decodedResponse['access_token'];
// _setTokenEnd(decodedResponse['expires_in']);
// }
}

@ -1,9 +1,42 @@
class User { export class User {
//attributes from DAFL //attributes from DAFL
private idFlad : any;; private _idFlad : string;
private idSpotify : any; private _idSpotify : string;
private _email : string;
private _createdAt : Date;
private _name : string;
public image : string = require('../assets/images/jul.png');
//constructors //constructors
constructor(){ constructor(idFlad : string, idSpotify : string, email : string, createdAt : Date, name : string, image : string){
this._name = name;
this._idFlad = idFlad;
this._idSpotify = idSpotify;
this._createdAt = createdAt;
this._email = email;
this.image = image;
}
get idFlad(): string {
return this._idFlad;
}
get idSpotify(): string {
return this._idSpotify;
}
get email(): string {
return this._email;
}
get createAt(): Date {
return this._createdAt;
}
get name(): string {
return this._name;
}
static empty() {
return new User('','','',new Date(),'',require('../assets/images/jul.png'));
}
toString() {
return 'User : ' + this.idFlad + ', ' + this.name + ', ' + this.idSpotify;
} }
} }

@ -0,0 +1,12 @@
import Music from "../Music";
export default class MusicFactory {
static mapFromSpotifyTrack(jsonMusic :any ): Music {
const music = new Music(
jsonMusic.id,
jsonMusic.name,
jsonMusic.album.images[0].url
);
return music;
}
}

@ -0,0 +1,12 @@
import { User } from "../User";
export class UserFactory {
public static JsonToModel( jsonUser :any ) : User{
return new User(jsonUser.idFlad, jsonUser.idSpotify, jsonUser.email, jsonUser.createdAt, jsonUser.name, jsonUser.imageUrl);
}
public static uptade( jsonUser :any ) : User{
return new User(jsonUser.idFlad, jsonUser.idSpotify, jsonUser.email, jsonUser.createdAt, jsonUser.name, jsonUser.imageUrl);
}
}

@ -19,7 +19,12 @@
"**/*" "**/*"
], ],
"ios": { "ios": {
"supportsTablet": true "supportsTablet": true,
"infoPlist": {
"UIBackgroundModes": [
"audio"
]
}
}, },
"android": { "android": {
"adaptiveIcon": { "adaptiveIcon": {
@ -29,6 +34,14 @@
}, },
"web": { "web": {
"favicon": "./assets/icons/favicon.png" "favicon": "./assets/icons/favicon.png"
},
"plugins": [
[
"expo-location",
{
"locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location."
} }
]
]
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

@ -1,6 +1,9 @@
const Icons = { const Icons = {
discovery: require('./icon_discovery.png'), discovery: require('./icon_discovery.png'),
like: require('./icon_like.png'),
dislike: require('./icon_dislike.png'),
// riveLike : require('./light_like.riv'), // riveLike : require('./light_like.riv'),
} }

@ -0,0 +1,6 @@
const Lotties = {
likeAnimation: require('./spotify-like-interaction.json')
// riveLike : require('./light_like.riv'),
}
export default Lotties;

File diff suppressed because one or more lines are too long

@ -0,0 +1,53 @@
import { SharedElement } from "react-navigation-shared-element";
import { NavigationProp, RouteProp } from "@react-navigation/native";
import { View,Image,StyleSheet, Dimensions, useWindowDimensions } from "react-native";
import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, withTiming } from "react-native-reanimated";
interface SpotProps {
spot: { name: string, sourceUrl: string, index : number };
}
const halfPi = Math.PI/2;
const AnimatedParalax = ({}) => {
const {width, height} = useWindowDimensions();
const sensor = useAnimatedSensor(SensorType.ROTATION);
const styleAniamatedImage = useAnimatedStyle(() => {
const {yaw, pitch, roll} = sensor.sensor.value;
const verticalAxis =interpolate(
pitch,
[-halfPi,halfPi],
[-25, 25]
)
const horizontalAxis =interpolate(
roll,
[-halfPi*2,halfPi*2],
[-35, 35]
)
return {
top : verticalAxis,
left : horizontalAxis,
};
})
return (
<View style={{ flex: 1, justifyContent : 'flex-start', alignItems : 'center' }}>
<Animated.Image
source={{
uri:spot.sourceUrl ,
}}
style={[
{
width: 370,
height: 370,
borderRadius : 24,
resizeMode: 'stretch',
},styleAniamatedImage
]}
/>
</View>
);
};
export default AnimatedParalax;

@ -0,0 +1,82 @@
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 ArtistProps {
artist: Music;
onPress: () => void;
}
export const Artist = ({ artist, onPress }: ArtistProps) => {
const source = typeof artist.image === 'string' ? { uri: artist.image } : artist.image;
//@ts-ignore
const onSingleTapEvent = (event) => {
if (event.nativeEvent.state === State.ACTIVE) {
alert('Hey single tap!');
}
};
const doubleTapRef = useRef(null);
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}>
<Image source={source} style={styles.image} />
{ 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",
}
});

@ -1,6 +1,6 @@
import { View, Text, Image , Dimensions, StyleSheet } from 'react-native' import { View, Text, Image , Dimensions, StyleSheet } from 'react-native'
import React, { useRef, useState } from 'react' import React, { useRef, useState } from 'react'
import Animated,{ Extrapolate, interpolate, useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated'; import Animated,{ Extrapolate, interpolate, runOnJS, useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler'; import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
@ -14,17 +14,18 @@ const SCREEN_WIDTH = Dimensions.get('window').width
interface CardProps { interface CardProps {
title: string; title: string;
image: any; image: any;
onSwipe: (direction: "left" | "right") => void;
} }
type ContextType = { type ContextType = {
translateX: number; translateX: number;
translateY: number; translateY: number;
}; };
const Card : React.FC<CardProps> = ({ title, image} : CardProps) => { const Card = ({ title, image, onSwipe } : CardProps) => {
const translateX = useSharedValue(0); const translateX = useSharedValue(0);
const translateY = useSharedValue(0); const translateY = useSharedValue(0);
const scale = useSharedValue(1);
const onGestureEvent = useAnimatedGestureHandler< const onGestureEvent = useAnimatedGestureHandler<
PanGestureHandlerGestureEvent, PanGestureHandlerGestureEvent,
ContextType ContextType
@ -34,15 +35,20 @@ const Card : React.FC<CardProps> = ({ title, image} : CardProps) => {
context.translateY = translateY.value; context.translateY = translateY.value;
}, },
onActive : (event, context) => { onActive : (event, context) => {
translateX.value = event.translationX + context.translateX -5; translateX.value = event.translationX + context.translateX;
translateY.value = event.translationY + context.translateY; translateY.value = event.translationY + context.translateY;
}, },
onEnd : (event, context) => { onEnd : (event, context) => {
console.log(translateX.value - translateY.value);
// console.log(translateY.value);
// translateX.value = withSpring(0); // translateX.value = withSpring(0);
// translateY.value = withSpring(snapPoint(translateY.value,velocityY, snapPoints )) // translateY.value = withSpring(snapPoint(translateY.value,velocityY, snapPoints ))
if (translateX.value > 160) { if (translateX.value > 160) {
// onSwipe("right"); console.log("translateX2");
runOnJS(onSwipe)("right");
} else if (translateX.value < -160) { } else if (translateX.value < -160) {
runOnJS(onSwipe)("left");
// onSwipe("left"); // onSwipe("left");
} else { } else {
translateX.value = withSpring(0); translateX.value = withSpring(0);
@ -58,7 +64,7 @@ const Card : React.FC<CardProps> = ({ title, image} : CardProps) => {
const opacityl = interpolate const opacityl = interpolate
( translateX.value, ( translateX.value,
[-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4], [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4],
[0, 0, 1]); [ 0,0, 1]);
return { return {
opacity : opacityl, opacity : opacityl,
}; };
@ -76,23 +82,28 @@ const Card : React.FC<CardProps> = ({ title, image} : CardProps) => {
const opacCStyle = useAnimatedStyle(() => { const opacCStyle = useAnimatedStyle(() => {
const opacityl = interpolate const opacityl = interpolate
( translateX.value, ( translateX.value,
[-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4], [-SCREEN_WIDTH / 4, 0, SCREEN_WIDTH / 4],
[0.75, 1, 0.75]); [0.35, 0, 0.35]);
return { return {
opacity : opacityl, opacity : opacityl,
}; };
}); });
const opacDStyle = useAnimatedStyle(() => { const opacDStyle = useAnimatedStyle(() => {
const opacityl = interpolate const opacityl = interpolate
( translateY.value, ( translateY.value,
[-SCREEN_HEIGHT / 4, 0, SCREEN_HEIGHT / 2], [ 100, 300],
[0, 0, 1]); [ 0, 1]);
return { return {
backgroundColor : 'red',
opacity : opacityl, opacity : opacityl,
}; };
}); });
const horizontalThreshold = SCREEN_WIDTH * 0.65;
const rotateStyle = useAnimatedStyle(() => { const rotateStyle = useAnimatedStyle(() => {
const rot = interpolate const rot = interpolate
( translateX.value, ( translateX.value,
@ -106,6 +117,27 @@ const Card : React.FC<CardProps> = ({ title, image} : CardProps) => {
}; };
}); });
const styleCardsNew = useAnimatedStyle(() => {
const factor = 1;
const rot = interpolate
( translateX.value,
[0, factor * horizontalThreshold],
[0, 15],
);
return {
transform: [
{ scale: scale.value },
{ translateX: translateX.value },
{ translateY: translateY.value },
{ rotateZ: `${rot}deg` },
]
};
});
// Calculate the distance of the card from its starting position
const rStyle = useAnimatedStyle(() => { const rStyle = useAnimatedStyle(() => {
@ -120,14 +152,23 @@ const Card : React.FC<CardProps> = ({ title, image} : CardProps) => {
], ],
}; };
}); });
console.log('==========================================',SCREEN_WIDTH/4 , "===============================");
return ( return (
<View> <View>
<PanGestureHandler onGestureEvent={onGestureEvent}> <PanGestureHandler onGestureEvent={onGestureEvent}>
<Animated.View style={[ rStyle, styles.card,opacCStyle ]}>
<Animated.View style={[ styleCardsNew, styles.container]}>
<Animated.View style={[ styles.image,{backgroundColor: 'black',elevation: 100,
position: "absolute",borderWidth : 8, borderColor : '#DA1D1D',
zIndex: 1000,}, opacCStyle]}>
</Animated.View>
<Image source={{uri : image}} style={[styles.image]} />
<>
<Animated.View <Animated.View
style={[{ style={[{
// transform: [{ rotate: "30deg" }], // transform: [{ rotate: "30deg" }],
elevation: 100,
position: "absolute", position: "absolute",
zIndex: 1000, zIndex: 1000,
},opacRStyle]} },opacRStyle]}
@ -138,10 +179,13 @@ const Card : React.FC<CardProps> = ({ title, image} : CardProps) => {
</Animated.View> </Animated.View>
<Animated.View <Animated.View
style={[{ style={[{
width: '100%',
height: '100%',
position: "absolute", position: "absolute",
justifyContent : "center", justifyContent : "center",
alignContent : "center", alignContent : "center",
zIndex: 1000, zIndex: 1000,
elevation: 100,
},opacLStyle]} },opacLStyle]}
> >
<Image style={[{alignSelf : "center"}]} <Image style={[{alignSelf : "center"}]}
@ -151,10 +195,12 @@ const Card : React.FC<CardProps> = ({ title, image} : CardProps) => {
</Animated.View> </Animated.View>
<Animated.View <Animated.View
style={[{ style={[{
width: '100%',
height: '100%',
position: "absolute", position: "absolute",
justifyContent : "center", justifyContent : "center",
alignContent : "center", alignContent : "center",
elevation: 100,
zIndex: 1000, zIndex: 1000,
},opacDStyle]} },opacDStyle]}
> >
@ -164,7 +210,8 @@ const Card : React.FC<CardProps> = ({ title, image} : CardProps) => {
/> />
</Animated.View> </Animated.View>
<Image source={{uri : image}} style={[styles.image]} /> </>
</Animated.View> </Animated.View>
</PanGestureHandler> </PanGestureHandler>
</View> </View>
@ -176,17 +223,21 @@ const styles = StyleSheet.create({
card : { card : {
justifyContent : 'center', justifyContent : 'center',
alignItems : 'center', alignItems : 'center',
}, },
image : { image : {
borderRadius : 24, borderRadius : 24,
resizeMode: 'cover', resizeMode: 'stretch',
height: 529, height: 362,
width: 312, width: 362,
backgroundColor: 'black', },
container: {
flex: 1,
width: '100%',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
} }
}) })
export default Card;
export default Card;

@ -0,0 +1,76 @@
import { useState } from 'react';
import { View, Text, Image ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, TextInput } from 'react-native'
import Animated, { interpolate, lessThan, multiply, useAnimatedStyle } from 'react-native-reanimated';
import HalfCirlce from './HalfCircle';
interface CircularProps {
background : string,
foreground : string,
progress : Animated.Value<number>,
radius : number;
}
const PI= Math.PI;
const FladInput = ({background, foreground, progress} : CircularProps) => {
const [focused, setFocused] = useState<boolean>(false);
const theta = multiply(progress,2*PI);
const rotateTop = theta;
const opacity = lessThan(theta, PI);
const rotateAnimation = useAnimatedStyle(() => {
const rotate = interpolate
( theta,
[PI, 2*PI],
[0,PI]);
return {
...StyleSheet.absoluteFillObject,
transform: [
{rotate: rotate},
{translateX: RADUIS/2},
{translateY: RADUIS/2}
],
};
});
const rotateAnimation2 = useAnimatedStyle(() => {
const rotate = interpolate
( theta,
[PI, 2*PI],
[0,PI]);
return {
...StyleSheet.absoluteFillObject,
transform: [
{rotate: theta},
{translateX: RADUIS/2},
{translateY: RADUIS/2}
],
};
});
return (
<>
<View style={{zIndex : 1}}>
<HalfCirlce backgroundColor={background}/>
<Animated.View style={{...StyleSheet.absoluteFillObject, transform : [{rotate : '180%'}], opacity}}>
<HalfCirlce backgroundColor={background}/>
</Animated.View>
</View>
<View style={{ transform : [{rotate : '180%'}]}}>
<HalfCirlce backgroundColor={background}/>
<Animated.View style={{}}>
<HalfCirlce backgroundColor={background}/>
</Animated.View> </View>
</>
);
};
const styles = StyleSheet.create({
input : {
justifyContent : 'center',
alignItems : 'center',
placeholder : "placeholde"
},
})
export default FladInput;

@ -0,0 +1,114 @@
import { View, Text, Image , Dimensions, StyleSheet } from 'react-native'
import React, { useEffect, useRef, useState } from 'react'
import Animated,{ Extrapolate, interpolate, runOnJS, useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withRepeat, withSpring, withTiming } from 'react-native-reanimated';
import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics';
const {width : wWidht} = Dimensions.get("window");
const SCREEN_HEIGHT = Dimensions.get('window').height
const SCREEN_WIDTH = Dimensions.get('window').width
// const width = wWidht *0.75;
// const height = wWidht * (465/264);
// const borderRadius = 24;
const size= 100
const FladLoading = () => {
const progresse = useSharedValue(1);
useEffect(() => {
// withTiming, withSpring
progresse.value =withRepeat( withTiming(0.01,{duration : 750}), -1,true);
}, [progresse]);
const breatheStyle = useAnimatedStyle(() => {
const borderRange = interpolate
( progresse.value,
[0, 1],
[(0*size) / 2,(1*size)/2],
);
return {
justifyContent : 'center',
alignItems : 'center',
width : size,
height : size,
shadowColor : "#DA1D1D",
shadowOffset : {width : 0, height : 0},
shadowOpacity : 1,
shadowRadius :borderRange,
};
});
const breatheStyle2 = useAnimatedStyle(() => {
const borderRange = interpolate
( progresse.value,
[0, 1],
[(0*size) / 2,(1*size)/2],
);
return {
borderRadius : borderRange,
};
});
const breatheStyleSquare = useAnimatedStyle(() => {
const borderRange = interpolate
( progresse.value,
[0, 1],
[(size+20),(size)],
);
return {
width : borderRange,
height : borderRange,
borderRadius : borderRange/2,
borderWidth : size/10,
borderColor : "#F80404",
shadowColor : "#F40C1C",
shadowOffset : {width : 0, height : 0},
shadowOpacity : 1,
shadowRadius :10,
};
});
return (
<View style={{flex : 1, justifyContent : 'center', alignItems :'center'}}>
<Animated.View style={[{backgroundColor : '#B40404', justifyContent : 'center', alignItems : 'center'}, breatheStyleSquare]}>
<Animated.Image source={require('../assets/icons/icon.png')} style={[{height: size, width : size, borderColor : '#fff',borderRadius : size/2}]}/>
</Animated.View>
{/* <Animated.View style={[ {backgroundColor : 'green'},breatheStyleSquare]}>
</Animated.View> */}
{/* <Image source={require('../assets/icons/Spotify_-_Animation_1.gif')}/> */}
</View>
);
};
const styles = StyleSheet.create({
card : {
justifyContent : 'center',
alignItems : 'center',
},
image : {
borderRadius : 24,
resizeMode: 'stretch',
},
container: {
flex: 1,
width: '100%',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}
})
export default FladLoading;

@ -0,0 +1,83 @@
import { useState } from "react";
import { FlatList, ScrollView } from "react-native";
import Music from "../Model/Music";
import { Artist } from "./Artist";
import { StyleSheet } from "react-native";
export const ArtistLayout = () => {
const MUSIC_LIST : 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"),
]
const [artists, setArtists] = useState<Music[]>(MUSIC_LIST);
const [selectedArtists, setSelectedArtists] = useState<typeof MUSIC_LIST> ([]);
return (
<ScrollView contentContainerStyle={styles.container}>
{artists.map((artist, i) => (
<Artist
artist={artist}
key={artist.title}
onPress={() => {
// artists.splice(i, 1);
// // 2 implementation
const tmppArtist = new Music("Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png");
// const existingObjIndex = selectedArtists.findIndex(obj => obj.title === tmppArtist.title);
// if (existingObjIndex === -1) {
// selectedArtists.push(tmppArtist);
// } else {
// selectedArtists.splice(existingObjIndex, 1);
// }
// setSelectedArtists([...selectedArtists]);
// 1 implementation
// setSelectedArtists(selectedArtists.findIndex(obj => obj.title === tmppArtist.title) === -1
// ? [...selectedArtists, tmppArtist]
// : selectedArtists.filter(obj => obj.title !== tmppArtist.title))
// 3 implementations
// use the selectedProps of the Artist Component
// then when we need to finish
// onPress{ () => setSelectedArtists([...selectedArtists,artists.filter(artist => artist.selected)])}
artists.push(tmppArtist);
setArtists([...artists]);
}}
/>
))}
{/* <FlatList
data={artists}
// need to reverse colums oreder
numColumns = {3}
renderItem={({ item }) => (
<Artist
artist={item}
key={item.title}
onPress={() => {
artists.push(new Music("Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png"));
setArtists([...artists]);
}}/>
)}
keyExtractor={(item: Music) => item.title }
// ListEmptyComponent = {}
/> */}
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: "row",
flexWrap: "wrap",
},
});

@ -0,0 +1,33 @@
import { useState } from 'react';
import { View, Text, Image, Animated ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, TextInput } from 'react-native'
interface HalfCirlceProps {
backgroundColor : string;
}
const HalfCirlce = ({backgroundColor} : HalfCirlceProps) => {
const [focused, setFocused] = useState<boolean>(false);
return (
<View style={{
width : RADUIS* 2,
height : RADUIS* 2,
overflow : "hidden",
}}>
<View style={{backgroundColor : backgroundColor, width : RADUIS* 2, height : RADUIS * 2, borderRadius : RADUIS, }}>
</View>
</View>
);
};
const styles = StyleSheet.create({
input : {
justifyContent : 'center',
alignItems : 'center',
placeholder : "placeholde"
},
})
export default HalfCirlce;

@ -1,6 +1,6 @@
import React, { useState, useRef } from 'react'; import React, { useState, useRef } from 'react';
import { View, StyleSheet, Text, FlatList, Animated, TouchableOpacity, ImageBackground, Image } from 'react-native'; import { Modal, View, StyleSheet, Text, FlatList, Animated, TouchableOpacity, ImageBackground, Image } from 'react-native';
import Modal from "react-native-modal"; // import Modal from "react-native-modal";
import {useNavigation} from "@react-navigation/native"; import {useNavigation} from "@react-navigation/native";
import normalize from '../components/Normalize'; import normalize from '../components/Normalize';
@ -57,7 +57,7 @@ export default function Onboarding() {
<Paginator data={slides} scrollX={scrollX}/> <Paginator data={slides} scrollX={scrollX}/>
<NextButton scrollTo={scrollTo} percentage={(currentIndex + 1) * (100 / slides.length)} /> <NextButton scrollTo={scrollTo} percentage={(currentIndex + 1) * (100 / slides.length)} />
</View> </View>
<Modal isVisible={isModalVisible}> <Modal animationType="slide" visible={isModalVisible} transparent={true} presentationStyle={'pageSheet'}>
<View style={styles.modalContent}> <View style={styles.modalContent}>
<ImageBackground source={require("../assets/images/Background_Start_Page.png")} style={styles.backgroundImage}> <ImageBackground source={require("../assets/images/Background_Start_Page.png")} style={styles.backgroundImage}>
<View style={styles.modalView}> <View style={styles.modalView}>
@ -124,11 +124,10 @@ const styles = StyleSheet.create({
right: 10 right: 10
}, },
modalContent: { modalContent: {
position: 'absolute',
top: '7%', flex: 1,
left: '-5%', justifyContent: 'center',
right: '-5%', alignItems: 'center',
height: '100%',
}, },
modalView: { modalView: {
flex: 1, flex: 1,

@ -1,12 +1,37 @@
export const cards = [{ export const cards = [{
name : "blue", name : "blue",
sourceUrl : "https://i.ebayimg.com/images/g/rY0AAOSw97djEo2C/s-l500.jpg", sourceUrl : "https://th.bing.com/th/id/R.dbf87f0d8cbfd078ab6a589a5d921994?rik=1%2f6KliMpOAeh8A&pid=ImgRaw&r=0",
index : 3 index : 4
}, },
{ {
name : "her", name : "her",
sourceUrl : "https://i.ebayimg.com/images/g/rY0AAOSw97djEo2C/s-l500.jpg", sourceUrl : "https://i.ebayimg.com/images/g/rY0AAOSw97djEo2C/s-l500.jpg",
index : 9
},
{
name : "gambino",
sourceUrl : "https://th.bing.com/th/id/R.0b2d1a59bfda9b1a49ecb561e08535a8?rik=Xyc35OZU%2f6VOVw&pid=ImgRaw&r=0",
index : 3 index : 3
} },
{
name : "PNL",
sourceUrl : "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png",
index : 10
},
{
name : "Freeze Raël",
sourceUrl : "https://intrld.com/wp-content/uploads/2020/08/freeze-corleone-la-menace-fanto%CC%82me.png",
index : 23
},
{
name : "Sch",
sourceUrl : "https://images.genius.com/83b6c98680d38bde1571f6b4093244b5.1000x1000x1.jpg",
index : 44
},
{
name : "Stratos",
sourceUrl : "https://images.genius.com/ddc9cadedd1d4cef0860aaa85af9cd46.705x705x1.png",
index : 89
},
] ]

@ -0,0 +1 @@
export const API_URL = "https://flad-api-production.up.railway.app"

@ -0,0 +1,67 @@
import Navigation from './Navigation';
import { StyleSheet,SafeAreaView } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import StartNavigation from './StartNavigation';
import { Provider, useDispatch, useSelector } from 'react-redux';
import store from '../redux/store';
import { useCallback, useEffect } from 'react';
import * as SplashScreen from 'expo-splash-screen';
import { View } from 'react-native';
import { getRefreshToken } from '../redux/thunk/authThunk';
SplashScreen.preventAutoHideAsync();
export default function AuthNavigation() {
//@ts-ignore
const appIsReady : boolean = useSelector(state => state.userReducer.loading);
//@ts-ignore
const isLogin : boolean = useSelector(state => state.userReducer.isLogedIn);
// const userToken : string = useSelector(state => state.userReducer.userFladToken);
const dispatch = useDispatch();
useEffect(() => {
async function prepare() {
console.log(appIsReady, "1 AuthNav")
//@ts-ignore
await dispatch(getRefreshToken())
await SplashScreen.hideAsync();
}
prepare();
}, [dispatch]);
const onStackRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (appIsReady == false) {
console.log(appIsReady, "T9 AuthNav")
return null;
}
console.log(appIsReady, "k9 AuthNav")
// console.log(userToken, "k9 AuthNav")
return (
<>
{isLogin ? (
/* {userToken != null ? ( */
<SafeAreaView style={styles.mainSafeArea} >
<Navigation/>
</SafeAreaView>
) :
<SafeAreaProvider >
<StartNavigation/>
</SafeAreaProvider>
}
</>
)
}
const styles = StyleSheet.create({
mainSafeArea: {
flex: 1,
backgroundColor: "#141414",
}
});

@ -1,6 +1,7 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import FavoritePage from '../screens/favoritePage'; import FavoritePage from '../screens/favoritePage';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { ArtistLayout } from '../components/Genre';
export default function MusicNavigation() { export default function MusicNavigation() {
const Stack = createStackNavigator(); const Stack = createStackNavigator();
@ -11,6 +12,11 @@ export default function MusicNavigation() {
component={FavoritePage} component={FavoritePage}
options={{ headerShown: false }} options={{ headerShown: false }}
/> />
<Stack.Screen
name="Genre"
component={ArtistLayout}
options={{ headerShown: false }}
/>
</Stack.Navigator> </Stack.Navigator>
) )
} }

@ -2,8 +2,12 @@ import React, {Component} from 'react';
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';
import FavoriteNavigation from './FavoriteNavigation'; import FavoriteNavigation from './FavoriteNavigation';
// @ts-ignore // @ts-ignore
import FontAwesome from 'react-native-vector-icons/FontAwesome'; import FontAwesome from 'react-native-vector-icons/FontAwesome';
import SpotNavigation from './SpotNavigation';
import Login from '../screens/login';
import FladLoading from '../components/FladLoadingScreen';
export default function Navigation() { export default function Navigation() {
const BottomTabNavigator = createBottomTabNavigator(); const BottomTabNavigator = createBottomTabNavigator();
@ -27,22 +31,26 @@ export default function Navigation() {
tabBarLabelStyle: { bottom: 5 } tabBarLabelStyle: { bottom: 5 }
}}> }}>
<BottomTabNavigator.Screen name="Spots" component={FavoriteNavigation} <BottomTabNavigator.Screen name="Spots" component={SpotNavigation}
options={{ options={{
headerShown: false, headerShown: false,
tabBarIcon: ({color}) => <TabBarIcon name="music" color={color}/>, tabBarIcon: ({color}) => <TabBarIcon name="music" color={color}/>,
}}/> }}/>
<BottomTabNavigator.Screen name="Favories" component={FavoriteNavigation} <BottomTabNavigator.Screen name="Favories" component={FavoriteNavigation}
options={{ options={{
// use Selector state redux badgeCount ? badgeCount : undefined
tabBarBadge : 2,
tabBarBadgeStyle : {backgroundColor : 'yellow'},
headerShown: false, headerShown: false,
tabBarIcon: ({color}) => <TabBarIcon name="heart" color={color}/>, tabBarIcon: ({color}) => <TabBarIcon name="heart" color={color}/>,
}}/> }}/>
<BottomTabNavigator.Screen name="Messages" component={FavoriteNavigation} <BottomTabNavigator.Screen name="Messages" component={Login}
options={{ options={{
headerShown: false, headerShown: false,
tabBarIcon: ({color}) => <TabBarIcon name="comment" color={color}/>, tabBarIcon: ({color}) => <TabBarIcon name="comment" color={color}/>,
}}/> }}/>
<BottomTabNavigator.Screen name="Paramètres" component={FavoriteNavigation} <BottomTabNavigator.Screen name="Paramètres" component={FladLoading}
options={{ options={{
headerShown: false, headerShown: false,
tabBarIcon: ({color}) => <TabBarIcon name="cog" color={color}/>, tabBarIcon: ({color}) => <TabBarIcon name="cog" color={color}/>,

@ -0,0 +1,38 @@
import React, {Component} from 'react';
import FavoritePage from '../screens/favoritePage';
import { createStackNavigator } from '@react-navigation/stack';
import Spot from '../screens/spot'
import { createSharedElementStackNavigator } from "react-navigation-shared-element";
import SpotDetailsPage from '../screens/SpotDetailsPage';
export default function SpotNavigation() {
// const Stack = createSharedElementStackNavigator();
const Stack = createStackNavigator();
return (
<Stack.Navigator screenOptions={{
gestureEnabled: false,
headerShown: false,
cardOverlayEnabled: true,
}}
>
<Stack.Screen
name="Spots"
component={Spot}
/>
<Stack.Screen
name="DetailsSpot"
component={SpotDetailsPage}
/>
{/* <Stack.Screen
name="DetailsSpot"
component={SpotDetailsPage}
sharedElements={(route) => {
return [route.params.spot.name]
}}
/> */}
</Stack.Navigator>
)
}

@ -1,15 +1,40 @@
import React, {Component} from 'react'; import React, {Component, useCallback, useEffect} from 'react';
import LoginPage from '../screens/loginPage'; import LoginPage from '../screens/loginPage';
import InscriptionPage from '../screens/InscriptionPage'; import InscriptionPage from '../screens/InscriptionPage';
import Onboarding from '../components/Onboarding'; import Onboarding from '../components/Onboarding';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
import {useDispatch, useSelector} from 'react-redux';
import * as SplashScreen from 'expo-splash-screen';
import { getRefreshToken } from '../redux/thunk/authThunk';
import { ArtistLayout } from '../components/Genre';
export default function StartNavigation() { export default function StartNavigation() {
// //@ts-ignore
// const appIsReady : boolean = useSelector(state => state.userReducer.loading);
// const dispatch = useDispatch();
// useEffect(() => {
// async function prepare() {
// //@ts-ignore
// await dispatch(getRefreshToken());
// }
// prepare();
// }, [dispatch]);
// const onStackRootView = useCallback(async () => {
// if (appIsReady) {
// await SplashScreen.hideAsync();
// }
// }, [appIsReady]);
// if (!appIsReady) {
// return null;
// }
const Stack = createStackNavigator(); const Stack = createStackNavigator();
return ( return (
<NavigationContainer> <NavigationContainer>
<Stack.Navigator initialRouteName="Home"> <Stack.Navigator>
<Stack.Screen <Stack.Screen
name="Home" name="Home"
component={Onboarding} component={Onboarding}

@ -13,18 +13,23 @@
"@react-navigation/native": "^6.1.4", "@react-navigation/native": "^6.1.4",
"@react-navigation/native-stack": "^6.9.8", "@react-navigation/native-stack": "^6.9.8",
"@react-navigation/stack": "^6.3.12", "@react-navigation/stack": "^6.3.12",
"@reduxjs/toolkit": "^1.9.2",
"@types/react-redux": "^7.1.25",
"axios": "^1.2.6", "axios": "^1.2.6",
"buffer": "^6.0.3",
"expo": "~47.0.12", "expo": "~47.0.12",
"expo-auth-session": "~3.8.0", "expo-auth-session": "~3.8.0",
"expo-av": "~13.0.3",
"expo-blur": "~12.0.1",
"expo-cli": "^6.3.0", "expo-cli": "^6.3.0",
"@reduxjs/toolkit": "^1.9.2",
"@types/react-redux": "^7.1.25",
"expo-haptics": "~12.0.1", "expo-haptics": "~12.0.1",
"expo-linear-gradient": "~12.0.1", "expo-linear-gradient": "~12.0.1",
"expo-location": "~15.0.1", "expo-location": "~15.0.1",
"expo-random": "~13.0.0", "expo-random": "~13.0.0",
"expo-secure-store": "~12.0.0", "expo-secure-store": "~12.0.0",
"expo-splash-screen": "~0.17.5",
"expo-status-bar": "~1.4.2", "expo-status-bar": "~1.4.2",
"lottie-react-native": "5.1.4",
"react": "18.1.0", "react": "18.1.0",
"react-dom": "18.1.0", "react-dom": "18.1.0",
"react-native": "0.70.5", "react-native": "0.70.5",
@ -33,9 +38,11 @@
"react-native-reanimated": "~2.12.0", "react-native-reanimated": "~2.12.0",
"react-native-safe-area-context": "^4.4.1", "react-native-safe-area-context": "^4.4.1",
"react-native-screens": "~3.18.0", "react-native-screens": "~3.18.0",
"react-native-shared-element": "^0.8.8",
"react-native-svg": "13.4.0", "react-native-svg": "13.4.0",
"react-native-vector-icons": "^9.2.0", "react-native-vector-icons": "^9.2.0",
"react-native-web": "~0.18.9", "react-native-web": "~0.18.9",
"react-navigation-shared-element": "^3.1.3",
"react-redux": "^8.0.5", "react-redux": "^8.0.5",
"redux": "^4.2.1", "redux": "^4.2.1",
"rive-react-native": "^3.0.41" "rive-react-native": "^3.0.41"

@ -0,0 +1,6 @@
// export const UserLogin = (username: string, password: string) => {
// return {
// type: userTypes.LOGIN,
// playload : username, password
// };
// }

@ -0,0 +1,17 @@
import Music from "../../Model/Music";
import { Spot } from "../../Model/Spot";
import {spotTypes} from "../types/spotTypes";
export const setSpotList = (spotList: Spot[]) => {
return {
type: spotTypes.FETCH_SPOT,
playload: spotList,
};
}
export const setUserCurrentMusic = (currentMusic: Music) => {
return {
type: spotTypes.FETCH_SPOT,
playload: currentMusic,
};
}

@ -0,0 +1,46 @@
import { userTypes } from "../types/userTypes";
export interface Credentials {
email : string,
password : string
}
export interface CredentialsRegister {
email : string,
password : string,
name : string,
idFlad : string,
idSpotify : string
}
// export const setLoggedInState = loggedInState => (
// {
// type: types.SET_LOGGED_IN_STATE,
// loggedInState,
// }
// );
export const setLoginState = (cred : Credentials) => {
return {
type: userTypes.LOGIN,
playload : cred
};
}
export const restoreToken = (token : string) => {
return {
type: userTypes.RESTORE_TOKEN,
playload : token
};
}
// export const UserLogin = (username: string, password: string) => {
// return {
// type: userTypes.LOGIN,
// playload : username, password
// };
// }
export const UserLogout = () => {
return {
type: userTypes.USER_LOGOUT,
};
}

@ -0,0 +1,31 @@
import Music from "../../Model/Music";
import { Spot } from "../../Model/Spot";
import { discoveriesTypes } from "../types/discoverieTypes";
import { favoritesTypes } from "../types/favoritesTypes";
import { spotifyTypes } from "../types/spotifyTypes";
import { spotTypes } from "../types/spotTypes";
const initialState = {
spot: [] as Spot[],
favoriteMusic: [] as Music [],
userCurrentMusic : null
}
const appReducer = (state = initialState, action : any) => {
switch (action.type) {
case favoritesTypes.ADD_FAVORITE_MUSICS:
return {...state, favoriteMusic: state.favoriteMusic.push(action.payload)};
case favoritesTypes.REMOVE_FAVORITE_MUSICS:
return {...state, favoriteMusic: state.favoriteMusic};
case spotTypes.FETCH_SPOT:
return {...state, spot: action.payload};
case discoveriesTypes.FETCH_DISCOVERIES:
return;
case spotifyTypes.GET_USER_CURRENT_MUSIC:
return {...state, userCurrentMusic: action.payload};
default:
return state;
}
}
export default appReducer

@ -0,0 +1,18 @@
Uri getApiUrlAuthorize() => _api.identification.urlAuthorize;
String getApiRedirectUrl() => _api.identification.redirectUri;
String getIdSpotify() => _currentUser.idSpotify;
String getIdDafl() => _currentUser.idDafl;
case getCompleteMusic
playTrack(String id)
case addToPlaylist:
return {...state, spot: action.payload}
case removeFromPlaylist:

@ -0,0 +1,54 @@
import { User } from "../../Model/User";
import { userTypes } from "../types/userTypes";
const initialState = {
loading: false,
user: User, // for user object
userFladToken: null, // for storing the JWT
userSpotifyToken : null,
error: null,
isLogedIn: false,
}
const userReducer = (state = initialState, action : any) => {
switch (action.type) {
// just for the navigation and speciafly use
// and
case userTypes.RESTORE_TOKEN:
return {
...state,
userFladToken : action.playload,
loading: true,
// isLogedIn: true,
};
case userTypes.LOGIN:
console.log("++++++++++++++++++++++++++++++++++++++userRducer+++++++++++++++++++++++++++++3");
console.log(action.playload, "LOOGGIIINN");
console.log("++++++++++++++++++++++++++++++++++++++userRducer+++++++++++++++++++++++++++++3");
return {
...state,
user :action.playload,
isLogedIn: true
};
case userTypes.SIGNUP:
console.log("++++++++++++++++++++++++++++++++++++++userRducer+++++++++++++++++++++++++++++3");
console.log(action.playload, "LOOGGIIINN");
console.log("++++++++++++++++++++++++++++++++++++++userRducer+++++++++++++++++++++++++++++3");
return {
...state,
user :action.playload,
isLogedIn: true
};
// case USER_SIGNUP:
// return {...state, nounours: action.payload};
case userTypes.USER_LOGOUT:
return {...state,
user :null,
isLogedIn: false }
default:
return state;
}
}
export default userReducer

@ -0,0 +1,15 @@
import {configureStore} from '@reduxjs/toolkit'
import appReducer from './reducers/appReducer';
import userReducer from './reducers/userReducer';
// Reference here all your application reducers
const reducer = {
// appReducer: appReducer,
userReducer: userReducer
}
const store = configureStore({
reducer : reducer,
},);
export default store;

@ -0,0 +1,150 @@
//Define your action creators that will be responsible for asynchronous operations
import axios from "axios";
import { json } from "express";
import { useEffect } from "react";
import { API_URL } from "../../fladConfig";
import { Credentials, CredentialsRegister, restoreToken, setLoginState } from "../actions/userActions";
import * as SecureStore from 'expo-secure-store';
import { User } from "../../Model/User";
import { UserFactory } from "../../Model/factory/UserFactory";
const key = 'userToken';
export const registerUser = ( resgisterCredential : CredentialsRegister) => {
//@ts-ignore
return async dispatch => {
try {
console.log(resgisterCredential);
const config = {
headers: {
'Content-Type': 'application/json',
},
}
const resp = await axios.post(
`${API_URL}/api/users/register`,
resgisterCredential,
config
)
if (resp.data.token) {
console.log(resp.data.token);
const token = resp.data.token;
// await SecureStore.setItemAsync(key, token);
const headers = {
'Authorization': 'Bearer ' + token};
const user = await axios.get(
"https://flad-api-production.up.railway.app/api/users",
{headers}
)
dispatch(setLoginState( UserFactory.JsonToModel(user.data) )); // our action is called here
// console.log(user.data);
// dispatch(setLoginState(user.data) ); // our action is called here
} else {
console.log('Login Failed', 'Username or Password is incorrect');
}
// if (resp.data.msg === 'success') { // response success checking logic could differ
// await SecureStore.setItemAsync(key, resp.data.token);
// dispatch(setLoginState(resp.data.user) ); // our action is called here
// } else {
// console.log('Login Failed', 'Username or Password is incorrect');
// }
} catch (error) {
console.log('Error---------', error);
}
}
}
export const userLogin = ( loginCredential : Credentials) => {
//@ts-ignore
return async dispatch => {
try {
console.log(loginCredential);
const config = {
headers: {
'Content-Type': 'application/json',
},
}
// const resppp = await axios.get(`${API_URL}/toto`);
// console.log(resppp.data, "sddsd");
const resp = await axios.post(
"https://flad-api-production.up.railway.app/api/users/login",
loginCredential,
config
)
console.log("====================================================================================")
console.log(resp.data)
console.log("====================================================================================")
if (resp.data.token) {
console.log(resp.data.token);
const token = resp.data.token;
await SecureStore.setItemAsync(key, token);
const headers = {
'Authorization': 'Bearer ' + token};
const user = await axios.get(
"https://flad-api-production.up.railway.app/api/users",
{headers}
)
// dispatch(setLoginState(resp.data.user) ); // our action is called here
console.log(user.data);
dispatch(setLoginState(user.data) ); // our action is called here
} else {
console.log('Login Failed', 'Username or Password is incorrect');
}
} catch (error) {
console.log('Error---------', error);
}
}
}
export const getRefreshToken = () => {
//@ts-ignore
return async dispatch => {
try {
let userToken : string | null = await SecureStore.getItemAsync(key);
console.log("==========key ==================");
console.log(userToken);
console.log("==========key ==================");
if (userToken) {
console.log("==========key2 ==================");
console.log(userToken);
console.log("==========key ==================");
dispatch(restoreToken(userToken) );
} else {
console.log("==========OOOOOORRRRRRRRHHHHHHHHHH ==================");
const empty = "";
dispatch(restoreToken(empty) );
console.log("merddee");
}
} catch (e) {
console.log('Error---------', e);
}
}
}
// const logIn = (email, password) => {
// const action = (dispatch) => {
// if (email === user.email && password === user.password) {
// dispatch(setLoggedInState(true));
// return true;
// }
// dispatch(setLoggedInState(false));
// return false;
// };
// return action;
// };
// better
async function save(key : string, value : string) {
await SecureStore.setItemAsync(key, value);
}

@ -0,0 +1,109 @@
//Define your action creators that will be responsible for asynchronous operations
import axios from "axios";
import { API_URL } from "../../fladConfig";
import { RequestHandler } from "../../services/spotify/spotifyRequestHandler/utils";
import * as SecureStore from 'expo-secure-store';
import { Spot } from "../../Model/Spot";
import SpotifyService from "../../services/spotify/spotify.service";
import * as Location from 'expo-location';
import { setSpotList, setUserCurrentMusic } from "../actions/spotActions";
const key = 'userToken';
export type CreateSpotReqBody = {
id : string;
name : string;
artist : string;
linkCover : string;
user : string;
}
export const getSpotList = (resuestHandler : SpotifyService) => {
//@ts-ignore
return async dispatch => {
try {
// let { status } = await Location.requestForegroundPermissionsAsync();
// if (status !== 'granted') {
// setErrorMsg('Permission to access location was denied');
// return;
// }
// let location = await Location.getCurrentPositionAsync({});
// setLocation(location);
// const actualUser = MyApp.controller.getIdDafl();
// const actualSong = MyApp.controller.getCurrentMusic().id;
// const current = await new Promise<Position>((resolve, reject) => {
// Geolocation.getCurrentPosition(resolve, reject);
// });
//@ts-ignore
var userToken : string = await SecureStore.getItemAsync(key);
const headers = {
'Authorization': 'Bearer ' + userToken};
const data = await axios.get(
"https://flad-api-production.up.railway.app/api/users/nextTo",
{headers}
)
if (data.data.token) {
const spotsData: { [userId: string]: string } = {};
for (const item of data.data) {
spotsData[item.user] = item.music;
}
const spots = await Promise.all(
Object.entries(spotsData).map(async ([userId, value]) => {
const completeMusic = await resuestHandler.getMusicById(value);
return new Spot(userId, completeMusic);
})
);
dispatch(setSpotList(spots)); // our action is called here
} else {
console.log('Login Failed', 'Username or Password is incorrect');
}
} catch (error) {
console.log('Error---------', error);
}
}
}
export const getCurrentUserMusic = (resuestHandler : SpotifyService)=> {
//@ts-ignore
return async dispatch => {
try {
//@ts-ignore
var currentTrackResponse = await resuestHandler.getUserCurrentMusic();
if (!currentTrackResponse){
const recentlyTrackResponse = await resuestHandler.getUserRecentlyPlayedMusic();
if(!recentlyTrackResponse){
throw new Error;
}else{
currentTrackResponse = recentlyTrackResponse;
}
}
const completeMusic = await resuestHandler.getMusicById(currentTrackResponse);
dispatch(setUserCurrentMusic(completeMusic));
}
catch (error) {
console.log('Error---------', error);
}
}
}
// export const getSpotList = () => {
// return async dispatch => {
// try {
// const spotPromise = await fetch(`${API_URL}/spotify/spot`);
// const spotJson = await spotPromise.json();
// const spotList: Spot[] = spotJson.map(spot => {
// } );
// dispatch(setNounoursList(spotList));
// } catch (error) {
// console.log('Error---------', error);
// }
// }
// }

@ -0,0 +1,3 @@
export const discoveriesTypes = {
FETCH_DISCOVERIES : 'FETCH_DISCOVERIES',
}

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

@ -0,0 +1,5 @@
export const playlistTypes = {
FETCH_USER_PLAYLISTS : 'FETCH_SPOT',
SAVE_IN_FLAD_PLAYLIST : 'SAVE_IN_FLAD_PLAYLIST',
FETCH_FLAD_PLAYLIST : 'FETCH_SPOT',
}

@ -0,0 +1,3 @@
export const spotTypes = {
FETCH_SPOT : 'FETCH_SPOT',
}

@ -0,0 +1,3 @@
export const spotifyTypes = {
GET_USER_CURRENT_MUSIC : 'GET_USER_CURRENT_MUSIC',
}

@ -0,0 +1,9 @@
export const userTypes = {
LOGIN: 'LOGIN',
SIGNUP: 'SIGNUP',
UPDATE_USER: 'UPDATE_USER',
UPDATE_PROFILE_PICTURE: 'UPDATE_PROFILE_PICTURE',
USER_LOGOUT : 'USER_LOGOUT',
RESTORE_TOKEN : "RESTORE_TOKEN"
}

@ -1,7 +1,16 @@
import React, {Component, useState } from 'react'; import React, {Component, useEffect, useState } from 'react';
import { View, Image, StyleSheet, Text, ImageBackground, Button, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity } from 'react-native'; import { View, Image, StyleSheet, Text, ImageBackground, Button, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity, Platform } from 'react-native';
import {useNavigation} from "@react-navigation/native"; import {useNavigation} from "@react-navigation/native";
import normalize from '../components/Normalize'; import normalize from '../components/Normalize';
import * as SecureStore from 'expo-secure-store';
import * as AuthSession from 'expo-auth-session';
import axios from 'axios';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { registerUser } from '../redux/thunk/authThunk';
import { useDispatch } from 'react-redux';
import { CredentialsRegister } from '../redux/actions/userActions';
import { Buffer } from 'buffer';
// @ts-ignore // @ts-ignore
const DismissKeyboard = ({ children }) => ( const DismissKeyboard = ({ children }) => (
@ -10,14 +19,120 @@ const DismissKeyboard = ({ children }) => (
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
) )
export const MY_SECURE_AUTH_STATE_KEY = 'MySecureAuthStateKey';
WebBrowser.maybeCompleteAuthSession();
// Endpoint
const discovery = {
authorizationEndpoint: 'https://accounts.spotify.com/authorize',
tokenEndpoint: 'https://accounts.spotify.com/api/token',
};
// save the spotifyToken
async function save(key : string, value : string) {
await SecureStore.setItemAsync(key, value);
}
export default function InscriptionPage() { export default function InscriptionPage() {
const [rememberMe, setRememberMe] = useState(false); const [rememberMe, setRememberMe] = useState(false);
const navigation = useNavigation(); const navigation = useNavigation();
const [spotifyToken, setSpotifyToken] = useState('');
const [spotifyID, setSpotifyIds] = useState('')
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const toggleRememberMe = () => { const toggleRememberMe = () => {
setRememberMe(!rememberMe); setRememberMe(!rememberMe);
} }
//spotify auth
const [request, response, promptAsync] = useAuthRequest(
{
responseType: AuthSession.ResponseType.Token,
clientId: '1f1e34e4b6ba48b388469dba80202b10',
scopes: ['user-read-private','user-read-email','user-read-playback-state','user-read-currently-playing','user-read-recently-played','playlist-modify-public','ugc-image-upload','user-modify-playback-state'],
redirectUri: makeRedirectUri({
scheme: 'flad'
}),
},
discovery
);
useEffect(() => {
if (response && response.type === 'success') {
const auth = response.params.access_token;
const storageValue = JSON.stringify(auth);
if (Platform.OS !== 'web') {
// Securely store the auth on your device
save(MY_SECURE_AUTH_STATE_KEY, storageValue);
}
}
}, [response]);
const dispatch = useDispatch();
const submitForm = () => {
const credentials: CredentialsRegister = {
email: email,
password: password,
idSpotify : spotifyToken,
name : username,
idFlad : "3030"
};
//@ts-ignore
dispatch(registerUser(credentials))
}
const scopesArr = ['user-read-private','user-read-email','user-read-playback-state','user-read-currently-playing','user-read-recently-played','playlist-modify-public','ugc-image-upload','user-modify-playback-state'];
const scopes = scopesArr.join(' ');
//work so use this for my implementation
const getAuthorizationCode = async () => {
try {
const redirectUrl = "https://auth.expo.io/@anonymous/FLAD-7eafd441-fd6b-4fb6-924c-ec2b0ed5ce6d"; //this will be something like https://auth.expo.io/@your-username/your-app-slug
const result = await AuthSession.startAsync({
authUrl:
'https://accounts.spotify.com/authorize' +
'?response_type=code' +
'&client_id=' +
"1f1e34e4b6ba48b388469dba80202b10" +
(scopes ? '&scope=' + encodeURIComponent(scopes) : '') +
'&redirect_uri=' +
encodeURIComponent(redirectUrl),
})
console.log(result);
return result.params.code;
} catch (err) {
console.error(err)
}
}
const getTokens = async () => {
try {
const authorizationCode = await getAuthorizationCode() //we wrote this function above
console.log(authorizationCode, "shhhhhhhhhhhhhheeeeeeeeeeeeeeeetttttttttttt");
const response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
Authorization: 'Basic ' + (Buffer.from('1f1e34e4b6ba48b388469dba80202b10' + ':' + '779371c6d4994a68b8dd6e84b0873c82').toString('base64')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `grant_type=authorization_code&code=${authorizationCode}&redirect_uri=https://auth.expo.io/@anonymous/FLAD-7eafd441-fd6b-4fb6-924c-ec2b0ed5ce6d`,
});
const responseJson = await response.json();
console.log(responseJson, "okkkkkkkkkkkkkkk") ;
// destructure the response and rename the properties to be in camelCase to satisfy my linter ;)
const {
access_token: accessToken,
refresh_token: refreshToken,
expires_in: expiresIn,
} = responseJson;
await setSpotifyToken(accessToken);
console.log(spotifyToken);
} catch (err) {
console.error(err);
}
}
return ( return (
<DismissKeyboard> <DismissKeyboard>
<View style={styles.container}> <View style={styles.container}>
@ -28,22 +143,31 @@ export default function InscriptionPage() {
<Image source={require("../assets/icons/Logo_White_Flad.png")} style={styles.imageLogo}/> <Image source={require("../assets/icons/Logo_White_Flad.png")} style={styles.imageLogo}/>
<Text style={styles.text}>S'INSCRIRE</Text> <Text style={styles.text}>S'INSCRIRE</Text>
<View> <View>
<TextInput style={[styles.input, styles.shadow]}/> <TextInput style={[styles.input, styles.shadow]} placeholder="Username"
value={username}
onChangeText={setUsername}/>
<Image source={require('../assets/icons/icons/User.png')} style={styles.iconUser} /> <Image source={require('../assets/icons/icons/User.png')} style={styles.iconUser} />
</View> </View>
<View> <View>
<TextInput style={[styles.input, styles.shadow]}/> <TextInput style={[styles.input, styles.shadow]} placeholder="Email"
value={email}
onChangeText={setEmail}/>
<Image source={require('../assets/icons/icons/lock.png')} style={styles.iconLock} /> <Image source={require('../assets/icons/icons/lock.png')} style={styles.iconLock} />
</View> </View>
<View> <View>
<TextInput style={[styles.input, styles.shadow]}/> <TextInput style={[styles.input, styles.shadow]} placeholder="Password"
value={password} secureTextEntry={true}
onChangeText={setPassword}/>
<Image source={require('../assets/icons/icons/lock.png')} style={styles.iconLock} /> <Image source={require('../assets/icons/icons/lock.png')} style={styles.iconLock} />
</View> </View>
<TouchableOpacity style={[styles.buttonSpotify, styles.shadow]}> <TouchableOpacity onPress={async() => {
await getTokens();
}} style={[styles.buttonSpotify, styles.shadow]}>
<Text style={styles.textIntoButton}>Lier compte</Text> <Text style={styles.textIntoButton}>Lier compte</Text>
<Image source={require("../assets/icons/icons/Spotify.png")} style={{width: 30, height: 30}}/> <Image source={require("../assets/icons/icons/Spotify.png")} style={{width: 30, height: 30}}/>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.shadow]} onPress={() => console.log("Oui")}>
<TouchableOpacity style={[styles.button, styles.shadow]} onPress={() => submitForm()}>
<Image source={require("../assets/icons/icons/next.png")} style={styles.buttonImage}/> <Image source={require("../assets/icons/icons/next.png")} style={styles.buttonImage}/>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.connectionText}> <View style={styles.connectionText}>

@ -0,0 +1,294 @@
import { SharedElement } from "react-navigation-shared-element";
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";
interface SpotProps {
spot: { name: string, sourceUrl: string, index : number };
}
const halfPi = Math.PI/2;
// const {width : wWidht} = Dimensions.get("window");
//@ts-ignore
const SpotDetailsPage = ({ route }) => {
const {width, height} = useWindowDimensions();
console.log(route);
const spot : { name: string, sourceUrl: string, index : number } = route.params.spot;
const [currentspot, setCurrentspot] = useState(spot);
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 :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: 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]);
// useEffect(() => {
// if(isPlaying){
// }
// })
console.log(spot);
const sensor = useAnimatedSensor(SensorType.ROTATION);
const styleAniamatedImage = useAnimatedStyle(() => {
const {yaw, pitch, roll} = sensor.sensor.value;
const verticalAxis =interpolate(
pitch,
[-halfPi,halfPi],
[-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);
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.sourceUrl ,
}}
style={[
{
width: 370,
height: 370,
borderRadius : 24,
resizeMode: 'stretch',
},styleAniamatedImage
]}
/>
<Button title="Current Track"
onPress={() => {
getCurrentTrack()
// 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>
{/* </TapGestureHandler> */}
{/* Button */}
{/* <View style={detailRadicalStyle.container}>
<Text style={detailRadicalStyle.radicalText}>{props.character}</Text>
<SvgXml
xml={props.icon
.replace(/fill="#[0-9a-f]{6}"/g, `fill=${detailRadicalStyle.svg.color}`)}
width="30"
height="30"
opacity={0.5}
style={detailRadicalStyle.radicalIcon}
/>
</View> */}
{/* </SharedElement> */}
</View>
);
};
export default SpotDetailsPage;

@ -1,9 +1,13 @@
import React, {Component} from 'react'; import React, {Component, useState} from 'react';
import { Animated, StyleSheet, Text, View, FlatList, ScrollView } from 'react-native'; import { Animated, Image,StyleSheet, Text, View, FlatList, ScrollView, TouchableOpacity } from 'react-native';
import CardMusic from '../components/CardMusic'; import CardMusic from '../components/CardMusic';
import normalize from '../components/Normalize';
import Music from '../Model/Music' import Music from '../Model/Music'
import {useNavigation} from "@react-navigation/native";
export default function favoritePage() { export default function favoritePage() {
const navigation = useNavigation();
const MUSIC_LIST : Music[] = [ const MUSIC_LIST : Music[] = [
new Music("La pharmacie", "Jul",require("../assets/images/jul.png")), new Music("La pharmacie", "Jul",require("../assets/images/jul.png")),
new Music("Deux frères", "PNL", require("../assets/images/pnl.png")), new Music("Deux frères", "PNL", require("../assets/images/pnl.png")),
@ -14,6 +18,25 @@ export default function favoritePage() {
new Music("Blanka", "PNL", require("../assets/images/pnl.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"), new Music("Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png"),
] ]
// to do
const [filteredDataSource, setFilteredDataSource] = useState<Music[]>([]);
const [search, setSearch] = useState('');
const searchMusic = (text: string) => {
if (text) {
const newData = MUSIC_LIST.filter(function (item: Music) {
const search = item.title
? item.title.toUpperCase() : ''.toUpperCase();
const textsearch = text.toUpperCase();
return search.indexOf(textsearch) > -1;
});
setFilteredDataSource(newData);
setSearch(text);
} else {
setFilteredDataSource([]);
setSearch(text);
}
};
return ( return (
<View style={styles.body}> <View style={styles.body}>
<View style={styles.titleContainer}> <View style={styles.titleContainer}>
@ -32,6 +55,11 @@ export default function favoritePage() {
keyExtractor={(item: Music) => item.title } keyExtractor={(item: Music) => item.title }
/> />
</View> </View>
<TouchableOpacity style={[styles.button, styles.shadow]}
// @ts-ignore
onPress={() => navigation.navigate('Genre')}>
<Image source={require("../assets/icons/icons/next.png")} style={styles.buttonImage}/>
</TouchableOpacity>
</ScrollView> </ScrollView>
</View> </View>
); );
@ -57,5 +85,29 @@ const styles = StyleSheet.create({
fontSize: 18, fontSize: 18,
color: '#787878', color: '#787878',
marginBottom: 20 marginBottom: 20
},
button: {
marginTop: '10%',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
alignSelf: 'center',
backgroundColor: 'white',
width: normalize(100),
height: normalize(100),
borderRadius: 21
},
buttonImage: {
width: normalize(46),
height: normalize(46),
},
shadow: {
shadowColor: '#000',
shadowOffset: {
width: 2,
height: 3,
},
shadowOpacity: 0.50,
shadowRadius: 3.84,
} }
}); });

@ -10,6 +10,7 @@ import { cards as cardArray } from '../data/data'
import FladButton from '../components/button/button'; import FladButton from '../components/button/button';
import * as WebBrowser from 'expo-web-browser'; import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Buffer } from 'buffer';
const SCREEN_WIDTH = Dimensions.get('window').width const SCREEN_WIDTH = Dimensions.get('window').width
@ -26,6 +27,7 @@ interface Params {
email: string; email: string;
id: string; id: string;
} }
//generate random string //generate random string
export const MY_SECURE_AUTH_STATE_KEY = 'MySecureAuthStateKey'; export const MY_SECURE_AUTH_STATE_KEY = 'MySecureAuthStateKey';
@ -36,8 +38,7 @@ const discovery = {
authorizationEndpoint: 'https://accounts.spotify.com/authorize', authorizationEndpoint: 'https://accounts.spotify.com/authorize',
tokenEndpoint: 'https://accounts.spotify.com/api/token', tokenEndpoint: 'https://accounts.spotify.com/api/token',
}; };
// save the spotifyToken
async function save(key : string, value : string) { async function save(key : string, value : string) {
await SecureStore.setItemAsync(key, value); await SecureStore.setItemAsync(key, value);
} }
@ -45,14 +46,18 @@ async function save(key : string, value : string) {
export default function Login() { export default function Login() {
// const [advice, setAdvice] = useState("dd"); // const [advice, setAdvice] = useState("dd");
// there we use implicit grant flow
const [request, response, promptAsync] = useAuthRequest( const [request, response, promptAsync] = useAuthRequest(
{ {
responseType: AuthSession.ResponseType.Token, responseType: AuthSession.ResponseType.Token,
clientId: '1f1e34e4b6ba48b388469dba80202b10', clientId: '1f1e34e4b6ba48b388469dba80202b10',
scopes: ['user-read-private','user-read-email','user-read-playback-state','user-read-currently-playing','user-read-recently-played','playlist-modify-public','ugc-image-upload','user-modify-playback-state'], scopes: ['user-read-private','user-read-email','user-read-playback-state','user-read-currently-playing','user-read-recently-played','playlist-modify-public','ugc-image-upload','user-modify-playback-state'],
usePKCE: false,
redirectUri: makeRedirectUri({ redirectUri: makeRedirectUri({
scheme: 'flad' scheme: 'https://auth.expo.io/@anonymous/FLAD-7eafd441-fd6b-4fb6-924c-ec2b0ed5ce6d',
}), useProxy : true
})
}, },
discovery discovery
); );
@ -68,22 +73,80 @@ export default function Login() {
}; };
React.useEffect(() => { React.useEffect(() => {
if (response && response.type === 'success') { if (response && response.type === 'success') {
console.log(response);
console.log("========================code=====================");
console.log(response.params.code)
console.log("=============================================");
console.log("========================acess=====================");
console.log(response.params.access_token)
console.log("=============================================");
const auth = response.params.access_token; const auth = response.params.access_token;
const storageValue = JSON.stringify(auth); const storageValue = JSON.stringify(auth);
if (Platform.OS !== 'web') { if (Platform.OS !== 'web') {
// Securely store the auth on your device // Securely store the auth on your device
save(MY_SECURE_AUTH_STATE_KEY, storageValue); // save(MY_SECURE_AUTH_STATE_KEY, storageValue);
} }
} }
}, [response]); }, [response]);
const scopesArr = ['user-read-private','user-read-email','user-read-playback-state','user-read-currently-playing','user-read-recently-played','playlist-modify-public','ugc-image-upload','user-modify-playback-state'];
const scopes = scopesArr.join(' ');
//work so use this for my implementation
const getAuthorizationCode = async () => {
try {
const redirectUrl = "https://auth.expo.io/@anonymous/FLAD-7eafd441-fd6b-4fb6-924c-ec2b0ed5ce6d"; //this will be something like https://auth.expo.io/@your-username/your-app-slug
const result = await AuthSession.startAsync({
authUrl:
'https://accounts.spotify.com/authorize' +
'?response_type=code' +
'&client_id=' +
"1f1e34e4b6ba48b388469dba80202b10" +
(scopes ? '&scope=' + encodeURIComponent(scopes) : '') +
'&redirect_uri=' +
encodeURIComponent(redirectUrl),
})
console.log(result);
return result.params.code;
} catch (err) {
console.error(err)
}
}
const getTokens = async () => {
try {
const authorizationCode = await getAuthorizationCode() //we wrote this function above
console.log(authorizationCode, "shhhhhhhhhhhhhheeeeeeeeeeeeeeeetttttttttttt");
const response = await fetch('https://accounts.spotify.com/api/token', {
method: 'POST',
headers: {
Authorization: 'Basic ' + (Buffer.from('1f1e34e4b6ba48b388469dba80202b10' + ':' + '779371c6d4994a68b8dd6e84b0873c82').toString('base64')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `grant_type=authorization_code&code=${authorizationCode}&redirect_uri=https://auth.expo.io/@anonymous/FLAD-7eafd441-fd6b-4fb6-924c-ec2b0ed5ce6d`,
});
const responseJson = await response.json();
console.log(responseJson.access_token, "okkkkkkkkkkkkkkk") ;
// destructure the response and rename the properties to be in camelCase to satisfy my linter ;)
const {
access_token: accessToken,
refresh_token: refreshToken,
expires_in: expiresIn,
} = responseJson;
} catch (err) {
console.error(err);
}
}
return ( return (
<View style={styles.centeredView}> <View style={styles.centeredView}>
<Text style={styles.textStyle}>Hello flad test logIn</Text> <Text style={styles.textStyle}>Hello flad test logIn</Text>
<Button disabled={!request} title="Login" <Button disabled={!request} title="Login"
onPress={() => { onPress={() => {
promptAsync(); getTokens()
// promptAsync();
}} }}
/> />
</View> </View>

@ -2,6 +2,9 @@ import React, {Component, useState } from 'react';
import { View, Image, StyleSheet, Text, ImageBackground, Button, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity } from 'react-native'; import { View, Image, StyleSheet, Text, ImageBackground, Button, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity } from 'react-native';
import {useNavigation} from "@react-navigation/native"; import {useNavigation} from "@react-navigation/native";
import normalize from '../components/Normalize'; import normalize from '../components/Normalize';
import { userLogin } from '../redux/thunk/authThunk';
import { useDispatch } from 'react-redux';
import { Credentials } from '../redux/actions/userActions';
// @ts-ignore // @ts-ignore
const DismissKeyboard = ({ children }) => ( const DismissKeyboard = ({ children }) => (
@ -14,6 +17,20 @@ export default function loginPage() {
const [rememberMe, setRememberMe] = useState(false); const [rememberMe, setRememberMe] = useState(false);
const navigation = useNavigation(); const navigation = useNavigation();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const dispatch = useDispatch();
const submitForm = () => {
const credentials: Credentials = {
email: username,
password: password
};
//@ts-ignore
dispatch(userLogin(credentials))
}
const toggleRememberMe = () => { const toggleRememberMe = () => {
setRememberMe(!rememberMe); setRememberMe(!rememberMe);
} }
@ -28,18 +45,23 @@ export default function loginPage() {
<Image source={require("../assets/icons/Logo_White_Flad.png")} style={styles.imageLogo}/> <Image source={require("../assets/icons/Logo_White_Flad.png")} style={styles.imageLogo}/>
<Text style={styles.text}>SE CONNECTER</Text> <Text style={styles.text}>SE CONNECTER</Text>
<View> <View>
<TextInput style={[styles.input, styles.shadow]}/> <TextInput placeholder="Username"
value={username}
onChangeText={setUsername}style={[styles.input, styles.shadow]}/>
<Image source={require('../assets/icons/icons/User.png')} style={styles.iconUser} /> <Image source={require('../assets/icons/icons/User.png')} style={styles.iconUser} />
</View> </View>
<View> <View>
<TextInput style={[styles.input, styles.shadow]}/> <TextInput placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry style={[styles.input, styles.shadow]}/>
<Image source={require('../assets/icons/icons/lock.png')} style={styles.iconLock} /> <Image source={require('../assets/icons/icons/lock.png')} style={styles.iconLock} />
</View> </View>
<View style={styles.rememberMeContainer}> <View style={styles.rememberMeContainer}>
<TouchableOpacity style={[styles.checkbox, rememberMe ? styles.checkboxChecked : null]} onPress={toggleRememberMe}></TouchableOpacity> <TouchableOpacity style={[styles.checkbox, rememberMe ? styles.checkboxChecked : null]} onPress={toggleRememberMe}></TouchableOpacity>
<Text style={styles.rememberMeText}>SE SOUVENIR DE MOI</Text> <Text style={styles.rememberMeText}>SE SOUVENIR DE MOI</Text>
</View> </View>
<TouchableOpacity style={[styles.button, styles.shadow]} onPress={() => console.log("Oui")}> <TouchableOpacity style={[styles.button, styles.shadow]} onPress={submitForm}>
<Image source={require("../assets/icons/Check.png")} style={styles.buttonImage}/> <Image source={require("../assets/icons/Check.png")} style={styles.buttonImage}/>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.inscriptionText}> <View style={styles.inscriptionText}>

@ -1,7 +1,8 @@
import { View, Text, Image, Animated ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable } from 'react-native' import { View, Text, Image ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, TouchableOpacity, SafeAreaView } from 'react-native'
import React, { useCallback, useEffect, useRef, useState, useTransition } from 'react' import React, { useCallback, useEffect, useRef, useState, useTransition } from 'react'
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import * as Haptics from 'expo-haptics'; import * as Haptics from 'expo-haptics';
import Animated from 'react-native-reanimated';
import Card from '../components/Card'; import Card from '../components/Card';
@ -11,10 +12,15 @@ import axios from 'axios';
import * as SecureStore from 'expo-secure-store'; import * as SecureStore from 'expo-secure-store';
import { MY_SECURE_AUTH_STATE_KEY } from './login'; import { MY_SECURE_AUTH_STATE_KEY } from './login';
import * as AuthSession from 'expo-auth-session';
import * as Location from 'expo-location'; import * as Location from 'expo-location';
import Icons from '../assets/icons/icons/icon';
import LottieView from 'lottie-react-native'
import Lotties from '../assets/lottie/Lottie';
import FladLoading from '../components/FladLoadingScreen';
import { SharedElement } from 'react-navigation-shared-element';
import { useNavigation } from '@react-navigation/native';
interface SpotProps { interface SpotProps {
} }
@ -94,8 +100,38 @@ export default function Spot() {
} }
// update the state of the cards state when it remove thisy // update the state of the cards state when it remove thisy
setCards(cards.filter((_, i) => i !== index)); setCards(cards.filter((_, i) => i !== index));
setcurrentCard(cards[index-1]);
}; };
const likeButtonref = useRef<LottieView>(null);
const dislikeButtonref = useRef<LottieView>(null);
const discoveryButtonref = useRef<LottieView>(null);
const onLike = useCallback( () => {
likeButtonref.current?.reset();
likeButtonref.current?.play(0,55);
}, [])
// 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)
// getValueFor(MY_SECURE_AUTH_STATE_KEY) // getValueFor(MY_SECURE_AUTH_STATE_KEY)
@ -147,55 +183,107 @@ export default function Spot() {
// }; // };
// setInterval(sendLocationToServer, 30000) // setInterval(sendLocationToServer, 30000)
const navigator = useNavigation();
const {width : wWidht} = Dimensions.get("window");
const hapti = (card : any) => {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)
navigator.navigate("DetailsSpot", {"spot": card})
// Haptics.NotificationFeedbackType.Success
};
return ( return (
<View style={styles.spot}>
<ImageBackground blurRadius={20} <View style={{flex: 1,
}}>
{ cards.length > 0 ? (
<>
<ImageBackground blurRadius={7}
style={{ style={{
position: 'absolute', position: 'absolute',
width: "120%", width: "100%",
height: "120%", height: "100%",
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
opacity: 0.28 opacity: 0.48
}} }}
source={{ source={{
uri:currentCard.sourceUrl , uri:currentCard.sourceUrl ,
}} }}
></ImageBackground> ></ImageBackground>
{cards.map((card, index) => ( <View style={{flex : 1.65}}>
<LinearGradient colors={['rgba(2, 2, 2, 0.58) 0%','rgba(0, 0, 0, 0) 90.56%']}style={styles.gradient}>
<Text
style={{
fontStyle : 'normal',
left: wWidht/9 ,
top: 65,
color: "#FFFFFF",
fontSize: 20,
fontWeight: "800",
}}>LOST FOREST</Text>
<Text
style={{
fontStyle : 'normal',
left: wWidht/9 ,
top: 65,
color: "#FFFFFF",
fontSize: 18,
fontWeight: "800",
}}>Laylow</Text>
</LinearGradient>
</View>
<View style={{flex : 8.35}}>
<View key={card.name} style = {{ position:'absolute' <View style={{flex : 1.83, justifyContent: 'center', alignItems: 'center' }}>
}} >
<Pressable > {cards.map((card, index) => (
<View key={card.name} style = {{ position:'absolute'}} >
<Pressable onLongPress={() => {hapti(card)}} >
{/* <SharedElement id={card.name}> */}
<Card <Card
title={card.name} title={card.name}
image={card.sourceUrl} image={card.sourceUrl}
onSwipe={(direction) => {onSwipe(index, direction)}}
/> />
{/* </SharedElement> */}
</Pressable> </Pressable>
{/* <Button </View>
title="Success" ))
onPress={
() =>
Haptics.notificationAsync(
Haptics.NotificationFeedbackType.Success
)
} }
></Button> */}
<FladButton name="discovery"/>
</View> </View>
))}
<View style={{flex : 1,flexDirection : 'row', alignItems: "flex-start", justifyContent : 'center'}}>
<Animated.View style={{flexDirection : 'row', width : '92%', alignItems: "center", justifyContent : 'space-evenly'}}>
<TouchableOpacity style={styles.button} onPress={onLike}>
<LottieView autoPlay={false} loop={false} ref={likeButtonref} source={Lotties.likeAnimation} style={styles.lottie}/>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={onLike}>
<LottieView autoPlay={false} loop={false} ref={likeButtonref} source={Lotties.likeAnimation} style={styles.lottie}/>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={onLike}>
<LottieView autoPlay={false} loop={false} ref={likeButtonref} source={Lotties.likeAnimation} style={styles.lottie}/>
</TouchableOpacity>
</Animated.View>
</View>
{/* <LinearGradient </View>
// Background Linear Gradient </>
colors={['rgba(0,0,0,0.8)', 'transparent']} )
: (<View style={{justifyContent : 'center', alignItems : 'center', flex : 1}}>
<View style={{flex:7}}>
/> */} <FladLoading></FladLoading>
</View>
<View style={{flex : 3 , justifyContent: 'flex-start'}}>
<Text style={{color: "grey", fontWeight: "400", textAlign: "center"}}>Vous avez explorer toutes les spot autour de vous.
{"\n"}Continuer dans discoverie pour découvrir de nouvelles music basées dur vos gouts musicaux.</Text>
</View>
</View>)
}
</View> </View>
); );
@ -205,7 +293,36 @@ export default function Spot() {
flex: 1, flex: 1,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
alignContent : 'center',
flexDirection : 'column',
backgroundColor : '#000' backgroundColor : '#000'
},
lottie : {
width : '100%',
},
button : {
setOpacityTo: 0.8,
alignItems : 'center',
borderRadius : 100,
justifyContent : 'center',
width: 61,
height: 61,
backgroundColor: '#24243A',
opacity : 0.8,
shadowRadius : 2,
},
gradient : {
position : "absolute",
top : 0,
left : 0,
right : 0,
height : 209,
},
mainSafeArea: {
flex: 1,
} }
}) })

@ -1,11 +1,14 @@
import axios from "axios"; import axios from "axios";
import MusicFactory from "../../Model/factory/MusicFactory";
import Music from "../../Model/Music";
import { RequestHandler } from "./spotifyRequestHandler/utils";
export default class SpotifyService implements IspotifyService {
export default class SpotifyService { private readonly API_URL = "https://flad-api-production.up.railway.app/api/";
private readonly API_URL = "http://localhost:8080/api/spotify/exchange"; private spotifyRequestHandler = new RequestHandler();
private readonly token : string;
constructor() { constructor(token : string) {
this.token = token;
} }
// get id(){ // get id(){
// return this.identification; // return this.identification;
@ -15,6 +18,45 @@ export default class SpotifyService {
// await this.identification.setCode(url); // await this.identification.setCode(url);
// // this.request = ApiSpotifyRequests(await this.identification.createToken()); // // this.request = ApiSpotifyRequests(await this.identification.createToken());
// } // }
public async getMusicById(idMusic : string): Promise<Music>{
var requestData :string = '/tracks/' + idMusic;
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, undefined,this.token);
if (respMusic.status != 200) {
}
return MusicFactory.mapFromSpotifyTrack(respMusic.data);
}
public async getUserCurrentMusic(): Promise<string | null>{
var requestData :string = '/me/player/currently-playing';
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, undefined,this.token);
if (respMusic.status != 200) {
return null;
}
return respMusic.data.items.track.id;
}
public async getUserRecentlyPlayedMusic(): Promise<string | null>{
var requestData :string = '/me/player/recently-played';
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, undefined,this.token);
if (respMusic.status != 200) {
}
if (respMusic.data.items.length <= 0) {
return null;
}
return respMusic.data.items[0].track.id;
}
public async playMusic(): Promise<string | null>{
var requestData :string = '/me/player/recently-played';
const respMusic = await this.spotifyRequestHandler.spotifyFetch(requestData, undefined,this.token);
if (respMusic.status != 200) {
}
if (respMusic.data.items.length <= 0) {
return null;
}
return respMusic.data.items[0].track.id;
}
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

@ -0,0 +1,4 @@
interface IspotifyService {
getMusicById(idMusic : string): Promise<any>;
}

@ -0,0 +1,44 @@
import axios, { AxiosError } from "axios";
export type Methods = 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
export interface FetchOptions {
/** The headers to apply. */
headers?: Record<string, string>;
/** The method type. */
method?: Methods;
/** Search query parameters. */
params?: Record<string, any>;
/** The json body to send if available. */
body?: Record<string, string | boolean | number | (string | boolean | number)[]>;
}
export class RequestHandler{
private _version: `v${number}` = 'v1';
get version(): string {
return this._version;
}
public async spotifyFetch(url: string, options: FetchOptions = {}, token: string) {
const resp = await axios({
url: `https://api.spotify.com/${this.version}${url}`,
method: options.method || 'GET',
params: options.params,
headers: {
Authorization: "Bearer " + token,
Accept: 'application/json',
...options.headers
},
data: options.body
});
console.log()
return resp;
// if (
// // @ts-ignore
// error.response.data?.error?.message == "Invalid access token" ||
// // @ts-ignore
// error.response.data?.error?.message == "The access token expired" &&
// this.refreshMeta
// ) await this.refreshFromMeta();
}
}
Loading…
Cancel
Save