diff --git a/src/Api/src/app.ts b/src/Api/src/app.ts index d3f8d36..41db4a0 100644 --- a/src/Api/src/app.ts +++ b/src/Api/src/app.ts @@ -50,6 +50,9 @@ class App { private initialiseControllers(controllers: Controller[]): void { controllers.forEach((controller: Controller) => { this.express.use('/api', controller.router); + this.express.get('/toto', (req, res) => { + res.send('Hello World!'); + }) }); } diff --git a/src/Api/src/controller/user-controller/userCtrl.ts b/src/Api/src/controller/user-controller/userCtrl.ts index 004e20e..9f5c740 100644 --- a/src/Api/src/controller/user-controller/userCtrl.ts +++ b/src/Api/src/controller/user-controller/userCtrl.ts @@ -161,8 +161,9 @@ class UserController implements Controller { ): Promise => { try { // 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( name, email, diff --git a/src/Api/src/database/schema/LocationSchema.ts b/src/Api/src/database/schema/LocationSchema.ts index 795d45f..37d37b4 100644 --- a/src/Api/src/database/schema/LocationSchema.ts +++ b/src/Api/src/database/schema/LocationSchema.ts @@ -1,4 +1,4 @@ -import { Schema, model } from 'mongoose'; +import { Schema, model,Document } from 'mongoose'; const locationSchema = new Schema( diff --git a/src/Api/src/database/schema/User/UserValidation.ts b/src/Api/src/database/schema/User/UserValidation.ts index ec48660..e00e988 100644 --- a/src/Api/src/database/schema/User/UserValidation.ts +++ b/src/Api/src/database/schema/User/UserValidation.ts @@ -7,7 +7,8 @@ const register = Joi.object({ password: Joi.string().min(6).required(), // 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({ diff --git a/src/Api/src/service/UserService.ts b/src/Api/src/service/UserService.ts index 682a963..fa7128d 100644 --- a/src/Api/src/service/UserService.ts +++ b/src/Api/src/service/UserService.ts @@ -39,13 +39,14 @@ class UserService { email: string, password: string ): Promise { - try { // should maybe creat a method base on id and other information for better security // need to view with Emre const user = await this.user.findOne({ email }); + console.log(user?._id); // 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'); } @@ -54,9 +55,7 @@ class UserService { } else { throw new Error('Wrong credentials given'); } - } catch (error) { - throw new Error('Unable to create user'); - } + } } diff --git a/src/FLAD/App.tsx b/src/FLAD/App.tsx index 2873efb..5e8131a 100644 --- a/src/FLAD/App.tsx +++ b/src/FLAD/App.tsx @@ -2,17 +2,18 @@ import Navigation from './navigation/Navigation'; import { StyleSheet,SafeAreaView } from 'react-native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; 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() { - + return ( - - - - // - // {/* */} - // - + + + ); } diff --git a/src/FLAD/Model/Spot.tsx b/src/FLAD/Model/Spot.tsx index cc8fa6d..46a6f9d 100644 --- a/src/FLAD/Model/Spot.tsx +++ b/src/FLAD/Model/Spot.tsx @@ -1,9 +1,17 @@ +import Music from "./Music"; -class Spot { - private userId : string; - public music : Music; +export class Spot { + private _userId : string; + public _music : Music; constructor(userId : string, music : Music){ - this.userId = userId; - this.music = music; + this._userId = userId; + this._music = music; } + get userSpotifyId(): string { + return this._userId; + } + get idSpotify(): Music { + return this._music; + } + } \ No newline at end of file diff --git a/src/FLAD/Model/SpotifyToken.tsx b/src/FLAD/Model/SpotifyToken.tsx new file mode 100644 index 0000000..d208ef5 --- /dev/null +++ b/src/FLAD/Model/SpotifyToken.tsx @@ -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 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: { +// 'Content-Type': 'application/x-www-form-urlencoded' +// })); +// var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map; +// _accessToken = decodedResponse['access_token']; +// _setTokenEnd(decodedResponse['expires_in']); +// } +} \ No newline at end of file diff --git a/src/FLAD/Model/User.tsx b/src/FLAD/Model/User.tsx index 1e7f2bd..d8e6438 100644 --- a/src/FLAD/Model/User.tsx +++ b/src/FLAD/Model/User.tsx @@ -1,9 +1,42 @@ -class User { +export class User { //attributes from DAFL - private idFlad : any;; - private idSpotify : any; + private _idFlad : string; + private _idSpotify : string; + private _email : string; + private _createdAt : Date; + private _name : string; + public image : string = require('../assets/images/jul.png'); //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; + } } \ No newline at end of file diff --git a/src/FLAD/Model/factory/MusicFactory.ts b/src/FLAD/Model/factory/MusicFactory.ts new file mode 100644 index 0000000..1e813c2 --- /dev/null +++ b/src/FLAD/Model/factory/MusicFactory.ts @@ -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; + } + } \ No newline at end of file diff --git a/src/FLAD/Model/factory/UserFactory.tsx b/src/FLAD/Model/factory/UserFactory.tsx new file mode 100644 index 0000000..105b4b0 --- /dev/null +++ b/src/FLAD/Model/factory/UserFactory.tsx @@ -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); + } + +} \ No newline at end of file diff --git a/src/FLAD/app.json b/src/FLAD/app.json index 906754f..8db8416 100644 --- a/src/FLAD/app.json +++ b/src/FLAD/app.json @@ -19,7 +19,12 @@ "**/*" ], "ios": { - "supportsTablet": true + "supportsTablet": true, + "infoPlist": { + "UIBackgroundModes": [ + "audio" + ] + } }, "android": { "adaptiveIcon": { @@ -29,6 +34,14 @@ }, "web": { "favicon": "./assets/icons/favicon.png" - } + }, + "plugins": [ + [ + "expo-location", + { + "locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location." + } + ] + ] } } diff --git a/src/FLAD/assets/icons/Spotify_-_Animation_1.gif b/src/FLAD/assets/icons/Spotify_-_Animation_1.gif new file mode 100644 index 0000000..8df51f0 Binary files /dev/null and b/src/FLAD/assets/icons/Spotify_-_Animation_1.gif differ diff --git a/src/FLAD/assets/icons/icons/icon.ts b/src/FLAD/assets/icons/icons/icon.ts index 35b3a1f..670589c 100644 --- a/src/FLAD/assets/icons/icons/icon.ts +++ b/src/FLAD/assets/icons/icons/icon.ts @@ -1,6 +1,9 @@ const Icons = { discovery: require('./icon_discovery.png'), + like: require('./icon_like.png'), + dislike: require('./icon_dislike.png'), + // riveLike : require('./light_like.riv'), } diff --git a/src/FLAD/assets/lottie/Lottie.tsx b/src/FLAD/assets/lottie/Lottie.tsx new file mode 100644 index 0000000..30a1482 --- /dev/null +++ b/src/FLAD/assets/lottie/Lottie.tsx @@ -0,0 +1,6 @@ +const Lotties = { + likeAnimation: require('./spotify-like-interaction.json') + // riveLike : require('./light_like.riv'), +} + +export default Lotties; \ No newline at end of file diff --git a/src/FLAD/assets/lottie/spotify-like-interaction.json b/src/FLAD/assets/lottie/spotify-like-interaction.json new file mode 100644 index 0000000..9f4cb7e --- /dev/null +++ b/src/FLAD/assets/lottie/spotify-like-interaction.json @@ -0,0 +1 @@ +{"nm":"Like lottie","mn":"","layers":[{"ty":4,"nm":"Heart Outlines 3","mn":"","sr":1,"st":10,"op":149,"ip":20,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[204.026,184.705,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[100,100,100],"t":20},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0,100],"t":29}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[399.737,393.085,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":1,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[6.941,6.941],[36.832,-36.832],[-37.159,-37.16],[0,0],[-4.267,4.553],[0,0],[19.073,36.414],[44.334,-44.335]],"o":[[-6.941,6.941],[-37.494,-37.494],[-37.159,37.16],[0,0],[4.268,4.553],[0,0],[28.62,-29.507],[-31.636,-60.401],[0,0]],"v":[[12.834,-120.539],[-12.302,-120.539],[-146.867,-123.137],[-146.867,11.429],[-7.601,160.152],[8.162,160.152],[147.181,11.837],[164.953,-98.922],[12.849,-120.37]]},"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[204.026,184.705],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0,0,0],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}}],"ind":1},{"ty":4,"nm":"Initial ring Outlines 2","mn":"","sr":1,"st":10,"op":149,"ip":12,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[280.077,280.077,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0},"i":{"x":0.667,"y":1},"s":[0,0,100],"t":20},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[150,150,100],"t":42}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[400,372,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":35},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":42}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-141.427,0],[0,-141.428],[141.427,0],[0,141.427]],"o":[[141.427,0],[0,141.427],[-141.427,0],[0,-141.428]],"v":[[0,-256.077],[256.077,0.001],[0,256.077],[-256.077,0.001]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"o":{"x":0.157,"y":0},"i":{"x":0.651,"y":0.816},"s":[48],"t":20},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[4],"t":31}],"ix":5},"d":[],"c":{"a":0,"k":[0.9725,0.0157,0.0157],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[280.077,280.077],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2},{"ty":4,"nm":"Initial ring Outlines","mn":"","sr":1,"st":10,"op":149,"ip":12,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[280.077,280.077,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0},"i":{"x":0.667,"y":1},"s":[0,0,100],"t":20},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[120,120,100],"t":49}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[400,372,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":36},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":49}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-141.427,0],[0,-141.428],[141.427,0],[0,141.427]],"o":[[141.427,0],[0,141.427],[-141.427,0],[0,-141.428]],"v":[[0,-256.077],[256.077,0.001],[0,256.077],[-256.077,0.001]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[64],"t":29},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[32],"t":46}],"ix":5},"d":[],"c":{"a":0,"k":[0.9725,0.0157,0.0157],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[280.077,280.077],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3},{"ty":4,"nm":"Heart Outlines 2","mn":"","sr":1,"st":10,"op":149,"ip":20,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[204.026,184.705,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[100,100,100],"t":20},{"o":{"x":0.167,"y":0},"i":{"x":0.833,"y":1},"s":[130,130,100],"t":26},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100,100,100],"t":34}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[399.737,393.085,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":1,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[6.941,6.941],[36.832,-36.832],[-37.159,-37.16],[0,0],[-4.267,4.553],[0,0],[19.073,36.414],[44.334,-44.335]],"o":[[-6.941,6.941],[-37.494,-37.494],[-37.159,37.16],[0,0],[4.268,4.553],[0,0],[28.62,-29.507],[-31.636,-60.401],[0,0]],"v":[[12.834,-120.539],[-12.302,-120.539],[-146.867,-123.137],[-146.867,11.429],[-7.601,160.152],[8.162,160.152],[147.181,11.837],[164.953,-98.922],[12.849,-120.37]]},"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[204.026,184.705],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9725,0.0157,0.0157],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}}],"ind":4},{"ty":4,"nm":"Heart Outlines","mn":"","sr":1,"st":10,"op":68,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[204.026,184.705,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[399.737,393.085,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":30},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":40}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":false,"i":[[0,0],[6.941,6.941],[36.832,-36.832],[-37.159,-37.16],[0,0],[-4.267,4.553],[0,0],[19.073,36.414],[44.334,-44.335]],"o":[[-6.941,6.941],[-37.494,-37.494],[-37.159,37.16],[0,0],[4.268,4.553],[0,0],[28.62,-29.507],[-31.636,-60.401],[0,0]],"v":[[12.834,-120.539],[-12.302,-120.539],[-146.867,-123.137],[-146.867,11.429],[-7.601,160.152],[8.162,160.152],[147.181,11.837],[164.953,-98.922],[12.849,-120.37]]},"ix":2}},{"ty":"st","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":10,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"d":[],"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[204.026,184.705],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":5},{"ty":4,"nm":"Small white Outlines 2","mn":"","sr":1,"st":1,"op":149,"ip":36,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[56.457,47.72,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[80,80,100],"t":36},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[110,110,100],"t":54}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[127.655,124.53,0],"t":36,"ti":[-10.333,10.333,0],"to":[10.333,-10.333,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[189.655,62.53,0],"t":54}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":43},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":54}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0.835,0.835],[11.836,-11.836],[-11.837,-11.836],[0,0],[-7.862,8.378],[0,0],[11.836,11.834],[11.827,-11.836]],"o":[[-11.837,-11.836],[-11.837,11.834],[0,0],[7.851,8.378],[0,0],[11.836,-11.836],[-11.837,-11.836],[-0.845,0.835]],"v":[[-1.515,-34.888],[-44.369,-35.633],[-44.369,7.224],[-14.505,39.091],[14.506,39.091],[44.37,7.224],[44.37,-35.633],[1.515,-34.888]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.7059,0.0157,0.0157],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[56.456,47.72],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":6,"parent":7},{"ty":4,"nm":"Small green Outlines 2","mn":"","sr":1,"st":1,"op":149,"ip":36,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[56.457,47.72,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[80,80,100],"t":36},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[110,110,100],"t":54}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[487.64,495.007,0],"t":36,"ti":[-5.667,4.667,0],"to":[1.667,3.333,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[543.64,153.007,0],"t":54}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":43},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":54}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0.835,0.836],[11.836,-11.836],[-11.827,-11.835],[0,0],[-7.851,8.378],[0,0],[11.837,11.834],[11.837,-11.836]],"o":[[-11.837,-11.836],[-11.827,11.834],[0,0],[7.851,8.378],[0,0],[11.837,-11.835],[-11.837,-11.836],[-0.835,0.836]],"v":[[-1.516,-34.888],[-44.38,-35.633],[-44.38,7.224],[-14.505,39.091],[14.495,39.091],[44.37,7.224],[44.37,-35.633],[1.505,-34.888]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[56.457,47.72],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":7},{"ty":4,"nm":"Small white Outlines","mn":"","sr":1,"st":-5,"op":149,"ip":30,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[56.457,47.72,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[80,80,100],"t":30},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[110,110,100],"t":40}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[127.655,124.53,0],"t":30,"ti":[-10.333,10.333,0],"to":[10.333,-10.333,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[189.655,62.53,0],"t":50}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":40},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":50}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0.835,0.835],[11.836,-11.836],[-11.837,-11.836],[0,0],[-7.862,8.378],[0,0],[11.836,11.834],[11.827,-11.836]],"o":[[-11.837,-11.836],[-11.837,11.834],[0,0],[7.851,8.378],[0,0],[11.836,-11.836],[-11.837,-11.836],[-0.845,0.835]],"v":[[-1.515,-34.888],[-44.369,-35.633],[-44.369,7.224],[-14.505,39.091],[14.506,39.091],[44.37,7.224],[44.37,-35.633],[1.515,-34.888]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,1,1],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[56.456,47.72],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":8,"parent":9},{"ty":4,"nm":"Small green Outlines","mn":"","sr":1,"st":-5,"op":149,"ip":30,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[56.457,47.72,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[80,80,100],"t":30},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[120,120,100],"t":40}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[243.64,469.007,0],"t":30,"ti":[-5.667,4.667,0],"to":[1.667,3.333,0]},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[95.64,133.007,0],"t":50}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":40},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":50}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":4,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0.835,0.836],[11.836,-11.836],[-11.827,-11.835],[0,0],[-7.851,8.378],[0,0],[11.837,11.834],[11.837,-11.836]],"o":[[-11.837,-11.836],[-11.827,11.834],[0,0],[7.851,8.378],[0,0],[11.837,-11.835],[-11.837,-11.836],[-0.835,0.836]],"v":[[-1.516,-34.888],[-44.38,-35.633],[-44.38,7.224],[-14.505,39.091],[14.495,39.091],[44.37,7.224],[44.37,-35.633],[1.505,-34.888]]},"ix":2}},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.7059,0.0157,0.0157],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[56.457,47.72],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":9}],"ddd":0,"h":800,"w":800,"meta":{"a":"","k":"","d":"","g":"LottieFiles AE 1.0.0","tc":"#000000"},"v":"4.8.0","fr":60,"op":55,"ip":0,"assets":[]} \ No newline at end of file diff --git a/src/FLAD/components/AnimatedParalax.tsx b/src/FLAD/components/AnimatedParalax.tsx new file mode 100644 index 0000000..4bb9c95 --- /dev/null +++ b/src/FLAD/components/AnimatedParalax.tsx @@ -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 ( + + + + + ); +}; + + +export default AnimatedParalax; \ No newline at end of file diff --git a/src/FLAD/components/Artist.tsx b/src/FLAD/components/Artist.tsx new file mode 100644 index 0000000..89ba68c --- /dev/null +++ b/src/FLAD/components/Artist.tsx @@ -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 ( + + + + + { selected && ( + + + + ) + + } + + + + + ); + }; + 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", + + + } + }); \ No newline at end of file diff --git a/src/FLAD/components/Card.tsx b/src/FLAD/components/Card.tsx index 9ca470b..347da23 100644 --- a/src/FLAD/components/Card.tsx +++ b/src/FLAD/components/Card.tsx @@ -1,6 +1,6 @@ import { View, Text, Image , Dimensions, StyleSheet } from 'react-native' 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 * as Haptics from 'expo-haptics'; @@ -14,17 +14,18 @@ const SCREEN_WIDTH = Dimensions.get('window').width interface CardProps { title: string; image: any; + onSwipe: (direction: "left" | "right") => void; } type ContextType = { translateX: number; translateY: number; }; -const Card : React.FC = ({ title, image} : CardProps) => { +const Card = ({ title, image, onSwipe } : CardProps) => { const translateX = useSharedValue(0); const translateY = useSharedValue(0); - + const scale = useSharedValue(1); const onGestureEvent = useAnimatedGestureHandler< PanGestureHandlerGestureEvent, ContextType @@ -34,15 +35,20 @@ const Card : React.FC = ({ title, image} : CardProps) => { context.translateY = translateY.value; }, onActive : (event, context) => { - translateX.value = event.translationX + context.translateX -5; + translateX.value = event.translationX + context.translateX; translateY.value = event.translationY + context.translateY; + }, onEnd : (event, context) => { + console.log(translateX.value - translateY.value); + // console.log(translateY.value); // translateX.value = withSpring(0); // translateY.value = withSpring(snapPoint(translateY.value,velocityY, snapPoints )) if (translateX.value > 160) { - // onSwipe("right"); + console.log("translateX2"); + runOnJS(onSwipe)("right"); } else if (translateX.value < -160) { + runOnJS(onSwipe)("left"); // onSwipe("left"); } else { translateX.value = withSpring(0); @@ -58,7 +64,7 @@ const Card : React.FC = ({ title, image} : CardProps) => { const opacityl = interpolate ( translateX.value, [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4], - [0, 0, 1]); + [ 0,0, 1]); return { opacity : opacityl, }; @@ -76,22 +82,27 @@ const Card : React.FC = ({ title, image} : CardProps) => { const opacCStyle = useAnimatedStyle(() => { const opacityl = interpolate ( translateX.value, - [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4], - [0.75, 1, 0.75]); + [-SCREEN_WIDTH / 4, 0, SCREEN_WIDTH / 4], + [0.35, 0, 0.35]); return { opacity : opacityl, }; }); + const opacDStyle = useAnimatedStyle(() => { const opacityl = interpolate ( translateY.value, - [-SCREEN_HEIGHT / 4, 0, SCREEN_HEIGHT / 2], - [0, 0, 1]); + [ 100, 300], + [ 0, 1]); + return { + backgroundColor : 'red', opacity : opacityl, }; }); + +const horizontalThreshold = SCREEN_WIDTH * 0.65; const rotateStyle = useAnimatedStyle(() => { const rot = interpolate @@ -106,7 +117,28 @@ const Card : React.FC = ({ 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(() => { return { @@ -120,14 +152,23 @@ const Card : React.FC = ({ title, image} : CardProps) => { ], }; }); - + console.log('==========================================',SCREEN_WIDTH/4 , "==============================="); return ( - + + + + + + <> = ({ title, image} : CardProps) => { = ({ title, image} : CardProps) => { @@ -164,8 +210,9 @@ const Card : React.FC = ({ title, image} : CardProps) => { /> - - + + + ); @@ -176,17 +223,21 @@ const styles = StyleSheet.create({ card : { justifyContent : 'center', alignItems : 'center', - }, image : { borderRadius : 24, - resizeMode: 'cover', - height: 529, - width: 312, - backgroundColor: 'black', + resizeMode: 'stretch', + height: 362, + width: 362, +}, +container: { + flex: 1, + width: '100%', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', } }) - - export default Card; +export default Card; diff --git a/src/FLAD/components/CircularProgressBar.tsx b/src/FLAD/components/CircularProgressBar.tsx new file mode 100644 index 0000000..fbe7296 --- /dev/null +++ b/src/FLAD/components/CircularProgressBar.tsx @@ -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, + radius : number; +} + +const PI= Math.PI; +const FladInput = ({background, foreground, progress} : CircularProps) => { + const [focused, setFocused] = useState(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 ( + <> + + + + + + + + + + + + + ); + }; + +const styles = StyleSheet.create({ + input : { + justifyContent : 'center', + alignItems : 'center', + placeholder : "placeholde" + }, +}) + +export default FladInput; \ No newline at end of file diff --git a/src/FLAD/components/FladLoadingScreen.tsx b/src/FLAD/components/FladLoadingScreen.tsx new file mode 100644 index 0000000..6ff701e --- /dev/null +++ b/src/FLAD/components/FladLoadingScreen.tsx @@ -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 ( + + + + + + {/* + */} + + {/* */} + + + ); + }; + + +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; + diff --git a/src/FLAD/components/Genre.tsx b/src/FLAD/components/Genre.tsx new file mode 100644 index 0000000..d1fdcb2 --- /dev/null +++ b/src/FLAD/components/Genre.tsx @@ -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_LIST); + const [selectedArtists, setSelectedArtists] = useState ([]); + + return ( + + {artists.map((artist, i) => ( + { + // 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]); + }} + /> + ))} + {/* ( + { + 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 = {} + /> */} + + + + ); + }; + + + + const styles = StyleSheet.create({ + container: { + flexDirection: "row", + flexWrap: "wrap", + + }, + }); \ No newline at end of file diff --git a/src/FLAD/components/HalfCircle.tsx b/src/FLAD/components/HalfCircle.tsx new file mode 100644 index 0000000..bde8742 --- /dev/null +++ b/src/FLAD/components/HalfCircle.tsx @@ -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(false); + + return ( + + + + + + ); + }; + +const styles = StyleSheet.create({ + input : { + justifyContent : 'center', + alignItems : 'center', + placeholder : "placeholde" + }, +}) + +export default HalfCirlce; \ No newline at end of file diff --git a/src/FLAD/components/Onboarding.tsx b/src/FLAD/components/Onboarding.tsx index 7dd4388..8c48acd 100644 --- a/src/FLAD/components/Onboarding.tsx +++ b/src/FLAD/components/Onboarding.tsx @@ -1,6 +1,6 @@ import React, { useState, useRef } from 'react'; -import { View, StyleSheet, Text, FlatList, Animated, TouchableOpacity, ImageBackground, Image } from 'react-native'; -import Modal from "react-native-modal"; +import { Modal, View, StyleSheet, Text, FlatList, Animated, TouchableOpacity, ImageBackground, Image } from 'react-native'; +// import Modal from "react-native-modal"; import {useNavigation} from "@react-navigation/native"; import normalize from '../components/Normalize'; @@ -53,11 +53,11 @@ export default function Onboarding() { onViewableItemsChanged={viewableItemsChanged} viewabilityConfig={viewConfig} ref={slidesRef} - /> + /> - + @@ -124,11 +124,10 @@ const styles = StyleSheet.create({ right: 10 }, modalContent: { - position: 'absolute', - top: '7%', - left: '-5%', - right: '-5%', - height: '100%', + + flex: 1, + justifyContent: 'center', + alignItems: 'center', }, modalView: { flex: 1, diff --git a/src/FLAD/data/data.ts b/src/FLAD/data/data.ts index 6dcc56d..85abaf4 100644 --- a/src/FLAD/data/data.ts +++ b/src/FLAD/data/data.ts @@ -1,12 +1,37 @@ export const cards = [{ name : "blue", - sourceUrl : "https://i.ebayimg.com/images/g/rY0AAOSw97djEo2C/s-l500.jpg", - index : 3 + sourceUrl : "https://th.bing.com/th/id/R.dbf87f0d8cbfd078ab6a589a5d921994?rik=1%2f6KliMpOAeh8A&pid=ImgRaw&r=0", + index : 4 }, { name : "her", 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 -} +}, +{ + 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 +}, -] \ No newline at end of file +] diff --git a/src/FLAD/fladConfig.tsx b/src/FLAD/fladConfig.tsx new file mode 100644 index 0000000..0c6f028 --- /dev/null +++ b/src/FLAD/fladConfig.tsx @@ -0,0 +1 @@ +export const API_URL = "https://flad-api-production.up.railway.app" diff --git a/src/FLAD/navigation/AuthNavigation.tsx b/src/FLAD/navigation/AuthNavigation.tsx new file mode 100644 index 0000000..a0b4377 --- /dev/null +++ b/src/FLAD/navigation/AuthNavigation.tsx @@ -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 ? ( */ + + + + + ) : + + + + } + + ) + } + const styles = StyleSheet.create({ + mainSafeArea: { + flex: 1, + backgroundColor: "#141414", + } + }); \ No newline at end of file diff --git a/src/FLAD/navigation/FavoriteNavigation.tsx b/src/FLAD/navigation/FavoriteNavigation.tsx index 40fc83b..f60f3ae 100644 --- a/src/FLAD/navigation/FavoriteNavigation.tsx +++ b/src/FLAD/navigation/FavoriteNavigation.tsx @@ -1,6 +1,7 @@ import React, {Component} from 'react'; import FavoritePage from '../screens/favoritePage'; import { createStackNavigator } from '@react-navigation/stack'; +import { ArtistLayout } from '../components/Genre'; export default function MusicNavigation() { const Stack = createStackNavigator(); @@ -11,6 +12,11 @@ export default function MusicNavigation() { component={FavoritePage} options={{ headerShown: false }} /> + ) } \ No newline at end of file diff --git a/src/FLAD/navigation/Navigation.tsx b/src/FLAD/navigation/Navigation.tsx index 9243d9f..a712ef6 100644 --- a/src/FLAD/navigation/Navigation.tsx +++ b/src/FLAD/navigation/Navigation.tsx @@ -2,8 +2,12 @@ import React, {Component} from 'react'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { NavigationContainer } from '@react-navigation/native'; import FavoriteNavigation from './FavoriteNavigation'; + // @ts-ignore 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() { const BottomTabNavigator = createBottomTabNavigator(); @@ -27,22 +31,26 @@ export default function Navigation() { tabBarLabelStyle: { bottom: 5 } }}> - , }}/> , }}/> - , }}/> - , diff --git a/src/FLAD/navigation/SpotNavigation.tsx b/src/FLAD/navigation/SpotNavigation.tsx new file mode 100644 index 0000000..158e177 --- /dev/null +++ b/src/FLAD/navigation/SpotNavigation.tsx @@ -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 ( + + + + {/* { + return [route.params.spot.name] + }} + /> */} + + ) + } \ No newline at end of file diff --git a/src/FLAD/navigation/StartNavigation.tsx b/src/FLAD/navigation/StartNavigation.tsx index ee07e8c..6104df7 100644 --- a/src/FLAD/navigation/StartNavigation.tsx +++ b/src/FLAD/navigation/StartNavigation.tsx @@ -1,15 +1,40 @@ -import React, {Component} from 'react'; +import React, {Component, useCallback, useEffect} from 'react'; import LoginPage from '../screens/loginPage'; import InscriptionPage from '../screens/InscriptionPage'; import Onboarding from '../components/Onboarding'; import { createStackNavigator } from '@react-navigation/stack'; 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() { + // //@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(); return ( - + { +// return { +// type: userTypes.LOGIN, +// playload : username, password +// }; +// } \ No newline at end of file diff --git a/src/FLAD/redux/actions/spotActions.tsx b/src/FLAD/redux/actions/spotActions.tsx new file mode 100644 index 0000000..41fbfa3 --- /dev/null +++ b/src/FLAD/redux/actions/spotActions.tsx @@ -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, + }; + } \ No newline at end of file diff --git a/src/FLAD/redux/actions/userActions.tsx b/src/FLAD/redux/actions/userActions.tsx new file mode 100644 index 0000000..f470e84 --- /dev/null +++ b/src/FLAD/redux/actions/userActions.tsx @@ -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, + }; + } + \ No newline at end of file diff --git a/src/FLAD/redux/reducers/appReducer.tsx b/src/FLAD/redux/reducers/appReducer.tsx new file mode 100644 index 0000000..c08cc64 --- /dev/null +++ b/src/FLAD/redux/reducers/appReducer.tsx @@ -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 \ No newline at end of file diff --git a/src/FLAD/redux/reducers/spotifyAuthReducer.tsx b/src/FLAD/redux/reducers/spotifyAuthReducer.tsx new file mode 100644 index 0000000..42effc8 --- /dev/null +++ b/src/FLAD/redux/reducers/spotifyAuthReducer.tsx @@ -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: diff --git a/src/FLAD/redux/reducers/userReducer.tsx b/src/FLAD/redux/reducers/userReducer.tsx new file mode 100644 index 0000000..421f706 --- /dev/null +++ b/src/FLAD/redux/reducers/userReducer.tsx @@ -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 + + \ No newline at end of file diff --git a/src/FLAD/redux/store.tsx b/src/FLAD/redux/store.tsx new file mode 100644 index 0000000..9595adf --- /dev/null +++ b/src/FLAD/redux/store.tsx @@ -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; \ No newline at end of file diff --git a/src/FLAD/redux/thunk/authThunk.tsx b/src/FLAD/redux/thunk/authThunk.tsx new file mode 100644 index 0000000..df93371 --- /dev/null +++ b/src/FLAD/redux/thunk/authThunk.tsx @@ -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); + } \ No newline at end of file diff --git a/src/FLAD/redux/thunk/spotThunk.tsx b/src/FLAD/redux/thunk/spotThunk.tsx new file mode 100644 index 0000000..907a329 --- /dev/null +++ b/src/FLAD/redux/thunk/spotThunk.tsx @@ -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((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); +// } +// } +// } \ No newline at end of file diff --git a/src/FLAD/redux/types/discoverieTypes.tsx b/src/FLAD/redux/types/discoverieTypes.tsx new file mode 100644 index 0000000..b94726f --- /dev/null +++ b/src/FLAD/redux/types/discoverieTypes.tsx @@ -0,0 +1,3 @@ +export const discoveriesTypes = { + FETCH_DISCOVERIES : 'FETCH_DISCOVERIES', + } \ No newline at end of file diff --git a/src/FLAD/redux/types/favoritesTypes.tsx b/src/FLAD/redux/types/favoritesTypes.tsx new file mode 100644 index 0000000..3634534 --- /dev/null +++ b/src/FLAD/redux/types/favoritesTypes.tsx @@ -0,0 +1,6 @@ + + +export const favoritesTypes = { + ADD_FAVORITE_MUSICS : 'ADD_FAVORITE_MUSICS', + REMOVE_FAVORITE_MUSICS : 'REMOVE_FAVORITE_MUSICS', + } \ No newline at end of file diff --git a/src/FLAD/redux/types/playlistTypes.tsx b/src/FLAD/redux/types/playlistTypes.tsx new file mode 100644 index 0000000..d4a5f0f --- /dev/null +++ b/src/FLAD/redux/types/playlistTypes.tsx @@ -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', +} \ No newline at end of file diff --git a/src/FLAD/redux/types/spotTypes.tsx b/src/FLAD/redux/types/spotTypes.tsx new file mode 100644 index 0000000..c4893a5 --- /dev/null +++ b/src/FLAD/redux/types/spotTypes.tsx @@ -0,0 +1,3 @@ +export const spotTypes = { + FETCH_SPOT : 'FETCH_SPOT', + } \ No newline at end of file diff --git a/src/FLAD/redux/types/spotifyTypes.ts b/src/FLAD/redux/types/spotifyTypes.ts new file mode 100644 index 0000000..0f2039b --- /dev/null +++ b/src/FLAD/redux/types/spotifyTypes.ts @@ -0,0 +1,3 @@ +export const spotifyTypes = { + GET_USER_CURRENT_MUSIC : 'GET_USER_CURRENT_MUSIC', +} \ No newline at end of file diff --git a/src/FLAD/redux/types/userTypes.tsx b/src/FLAD/redux/types/userTypes.tsx new file mode 100644 index 0000000..cdea161 --- /dev/null +++ b/src/FLAD/redux/types/userTypes.tsx @@ -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" + } \ No newline at end of file diff --git a/src/FLAD/screens/InscriptionPage.tsx b/src/FLAD/screens/InscriptionPage.tsx index d466459..365f255 100644 --- a/src/FLAD/screens/InscriptionPage.tsx +++ b/src/FLAD/screens/InscriptionPage.tsx @@ -1,7 +1,16 @@ -import React, {Component, useState } from 'react'; -import { View, Image, StyleSheet, Text, ImageBackground, Button, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity } from 'react-native'; +import React, {Component, useEffect, useState } from 'react'; +import { View, Image, StyleSheet, Text, ImageBackground, Button, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity, Platform } from 'react-native'; import {useNavigation} from "@react-navigation/native"; 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 const DismissKeyboard = ({ children }) => ( @@ -10,13 +19,119 @@ const DismissKeyboard = ({ children }) => ( ) +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() { const [rememberMe, setRememberMe] = useState(false); 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 = () => { 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 ( @@ -28,22 +143,31 @@ export default function InscriptionPage() { S'INSCRIRE - + - + - + - + { + await getTokens(); + }} style={[styles.buttonSpotify, styles.shadow]}> Lier compte - console.log("Oui")}> + + submitForm()}> @@ -161,4 +285,4 @@ const styles = StyleSheet.create ({ borderRadius: 30, flexDirection: 'row' } -}) \ No newline at end of file +}) diff --git a/src/FLAD/screens/SpotDetailsPage.tsx b/src/FLAD/screens/SpotDetailsPage.tsx new file mode 100644 index 0000000..841ef4e --- /dev/null +++ b/src/FLAD/screens/SpotDetailsPage.tsx @@ -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 ( + + {/* */} + + + + */} - - - ))} + {cards.map((card, index) => ( + + + {hapti(card)}} > + {/* */} + {onSwipe(index, direction)}} + /> + {/* */} + + + )) + } + - - {/* */} - + + + + + + + + + + + + + + + + + ) + : ( + + + + + + 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. + + + ) + } + ); }; @@ -205,7 +293,36 @@ export default function Spot() { flex: 1, alignItems: 'center', justifyContent: 'center', + alignContent : 'center', + flexDirection : 'column', 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, + } }) diff --git a/src/FLAD/services/spotify/spotify.service.ts b/src/FLAD/services/spotify/spotify.service.ts index 3500dd8..3e7ce5f 100644 --- a/src/FLAD/services/spotify/spotify.service.ts +++ b/src/FLAD/services/spotify/spotify.service.ts @@ -1,11 +1,14 @@ import axios from "axios"; +import MusicFactory from "../../Model/factory/MusicFactory"; +import Music from "../../Model/Music"; +import { RequestHandler } from "./spotifyRequestHandler/utils"; - -export default class SpotifyService { - private readonly API_URL = "http://localhost:8080/api/spotify/exchange"; - - constructor() { - +export default class SpotifyService implements IspotifyService { + private readonly API_URL = "https://flad-api-production.up.railway.app/api/"; + private spotifyRequestHandler = new RequestHandler(); + private readonly token : string; + constructor(token : string) { + this.token = token; } // get id(){ // return this.identification; @@ -14,7 +17,46 @@ export default class SpotifyService { // async apiAuth(url : string) { // await this.identification.setCode(url); // // this.request = ApiSpotifyRequests(await this.identification.createToken()); - // } + // } + public async getMusicById(idMusic : string): Promise{ + 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{ + 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{ + 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{ + 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() { const res = await axios.get(this.API_URL) // then verify error diff --git a/src/FLAD/services/spotify/spotifyRequestHandler/IspotifyService.ts b/src/FLAD/services/spotify/spotifyRequestHandler/IspotifyService.ts new file mode 100644 index 0000000..8f3e829 --- /dev/null +++ b/src/FLAD/services/spotify/spotifyRequestHandler/IspotifyService.ts @@ -0,0 +1,4 @@ +interface IspotifyService { + getMusicById(idMusic : string): Promise; + +} \ No newline at end of file diff --git a/src/FLAD/services/spotify/spotifyRequestHandler/utils.tsx b/src/FLAD/services/spotify/spotifyRequestHandler/utils.tsx new file mode 100644 index 0000000..2addfb7 --- /dev/null +++ b/src/FLAD/services/spotify/spotifyRequestHandler/utils.tsx @@ -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; + /** The method type. */ + method?: Methods; + /** Search query parameters. */ + params?: Record; + /** The json body to send if available. */ + body?: Record; +} + +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(); + } +} \ No newline at end of file