Reglage merge API And Navigation

pull/7/head
root 2 years ago
commit 18b3951aef

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

41494
FLAD/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -2,17 +2,35 @@
"name": "api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"main": "src/server.ts",
"scripts": {
"start": "nodemon src/server.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/cors": "^2.8.13",
"@types/debug": "^4.1.7",
"@types/express": "^4.17.16",
"@types/morgan": "^1.9.4",
"nodemon": "^2.0.20",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
"typescript": "^4.9.5"
},
"dependencies": {
"@types/crypto-js": "^4.1.1",
"@types/mongoose": "^5.11.97",
"@types/request": "^2.48.8",
"axios": "^1.2.6",
"cors": "^2.8.5",
"express-winston": "^4.2.0",
"mongodb": "^5.0.0",
"mongoose": "^6.9.0",
"morgan": "^1.10.0",
"request": "^2.88.2",
"swagger-ui-express": "^4.6.0",
"winston": "^3.8.2"
}
}

@ -0,0 +1,68 @@
import express, { Application } from 'express';
// import compression from 'compression';
import cors from 'cors';
// import db from './database';
// import morgan from 'morgan';
import Controller from './controller/Icontroller';
// import ErrorMiddleware from './middleware/error.middleware';
import bodyParser from 'body-parser';
// to secure
// import helmet from 'helmet';
import http from 'http';
class App {
public express: Application;
public port: number;
public dataBase: null;
public server : any;
constructor(controllers: Controller[], port: number) {
this.express = express();
this.port = port;
this.dataBase = null;
// this.initialiseDatabase();
this.initialiseMiddleware();
this.initialiseControllers(controllers);
// this.initialiseErrorHandling();
}
private initialiseMiddleware(): void {
// this.express.use(helmet());
this.express.use(cors());
// this.express.use(morgan('dev'));
this.express.use(express.json());
this.express.use(express.urlencoded({ extended: false }));
// this.express.use(compression());
// mine
this.express.use(bodyParser.json());
this.express.use(bodyParser.urlencoded({
extended: true
}));
}
private initialiseControllers(controllers: Controller[]): void {
controllers.forEach((controller: Controller) => {
this.express.use('/api', controller.router);
});
}
// private initialiseErrorHandling(): void {
// this.express.use(ErrorMiddleware);
// }
public listen(): void {
const server = this.express.listen(this.port, () => {
console.log(`⚡️[server] : App listening on the port ${this.port}`);
});
}
}
export default App;

@ -0,0 +1,15 @@
import { Router } from 'express';
interface Controller {
path: string;
router: Router;
// constructor() {
// this.initialiseRoutes();
// }
// initialiseRoutes(): void ;
}
// il y a un truc inject
export default Controller;

@ -0,0 +1,27 @@
import { Router } from "express";
import Controller from "./Icontroller";
type PingResponse = {
message: string;
}
export default class PingController implements Controller {
public path = '/ping';
public router = Router();
constructor() {
this.initialiseRoutes();
}
private initialiseRoutes(): void {
this.router.get("/ping", async (_req, res) => {
const response = await this.getMessage();
return res.send(response);
});
}
async getMessage(): Promise<PingResponse> {
return {
message: "pong",
};
}
}

@ -0,0 +1,19 @@
export default class CryptString{
stringCrypt: string;
constructor(length : number){
this.stringCrypt = this.generateRandomString(length);
}
generateRandomString (length : number){
var text = '';
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
}

@ -0,0 +1,6 @@
export type AuthReqBody = {
grant_type: string,
redirect_uri?: string,
code?: string,
refresh_token?: string,
}

@ -0,0 +1,287 @@
import Controller from '../Icontroller';
import { Router, Request, Response, NextFunction, RequestHandler } from 'express';
import { AuthReqBody } from './request/authReqBody';
import HttpException from '../../middleware/exeption/httpExeption';
import axios from 'axios';
import CryptString from './crypt';
import AES from 'crypto-js'
import querystring from 'querystring';
import qs from 'qs';
class SpotifyController implements Controller {
public path = '/spotify';
public router = Router();
constructor() {
console.log("useeeee");
this.initialiseRoutes();
}
initialiseRoutes() {
// this.router.post(`${this.path}`,this.createTask);
this.router.get(`${this.path}/exchange`,this.login);
this.router.get(`${this.path}/callback`,this.getAccessToken);
// this.router.post(`${this.path}/refresh`,this.getRefreshToken);
this.router.get(`${this.path}/play/:musicId`, this.getMusic);
}
// need to put in ENvironement file
// private readonly CLIENT_CALLBACK_URL = "http://localhost:8080/callback";
private readonly API_URL = "https://accounts.spotify.com/api/token";
private readonly CLIENT_ID = "1f1e34e4b6ba48b388469dba80202b10";
private readonly CLIENT_SECRET = "779371c6d4994a68b8dd6e84b0873c82";
private readonly CLIENT_CALLBACK_URL = "https://auth.expo.io/@thed47/FLAD//callback";
private readonly CALLBACK_URL = "http://localhost:8080/api/spotify/callback";
private readonly 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';
private readonly ENCRYPTION_SECRET = new CryptString(16);
private login = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
console.log("useeeee");
try {
// const params = req.body;
// if (!params.refresh_token) {
// return res.json({
// "error": "Parameter missing"
// });
// }
// this.spotifyRequest({
// grant_type: "authorization_code",
// redirect_uri: this.CLIENT_CALLBACK_URL,
// // code: params.code
// })
res.redirect('https://accounts.spotify.com/authorize?' +
querystring.stringify({
response_type: 'code',
client_id: this.CLIENT_ID,
scope: this.SCOPES,
redirect_uri: this.CALLBACK_URL,
state: this.ENCRYPTION_SECRET.stringCrypt
}));
// .then(session => {
// let result = {
// "access_token": session.access_token,
// "expires_in": session.expires_in,
// "refresh_token": this.encrypt(session.refresh_token)
// };
// return res.send(result);
// })
// .catch(response => {
// return res.json(response);
// });
} catch (error) {
next(new HttpException(400, 'Cannot create spot'));
}
};
private getRefreshToken = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
console.log('UUse2');
try {
const params = req.query.refresh_token;
if (!req.query.refresh_token) {
return res.json({
"error": "Parameter refresh_token missing"
});
}
var authOptions = {
method: 'POST',
url: 'https://accounts.spotify.com/api/token',
data: qs.stringify({
grant_type: 'refresh_token',
refresh_token: params
}),
headers: {
'Authorization': 'Basic ' + ( Buffer.from(this.CLIENT_ID + ':' + this.CLIENT_SECRET).toString('base64')),
'Content-Type' : 'application/x-www-form-urlencoded'
},
json: true
};
// request.post(authOptions, function(error, response, body) {
// if (!error && response.statusCode === 200) {
// var access_token = body.access_token;
// res.send({
// 'access_token': access_token
// });
// }
// });
axios(authOptions)
.then(session => {
if(session.status === 200){
res.send({
"access_token": session.data.access_token,
"expires_in": session.data.expires_in
});
}});
console.log("goood");
} catch (error) {
console.log("errur");
next(new HttpException(400, 'Cannot create post'));
}
}
// };
public getMusic(){
return null;
}
// private spotifyRequest = (params : AuthReqBody) => {
// return new Promise<any>(() => {
// console.log("============ on est laaaa sa mer");
// var code = req.query.code || null;
// var state = req.query.state || null;
// axios.post(this.API_URL, {
// form: params,
// headers: {
// "Authorization": "Basic " + new Buffer(this.CLIENT_ID + ":" + this.CLIENT_SECRET).toString('base64')
// },
// json: true
// });
// }).then(resp => {
// if (resp.statusCode != 200) {
// return Promise.reject({
// statusCode: resp.statusCode,
// body: resp.body
// });
// }
// return Promise.resolve(resp.body);
// })
// .catch(err => {
// return Promise.reject({
// statusCode: 500,
// body: err.stringify({})
// });
// });
// };
private getAccessToken = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
// console.log("heheh");
// try {
// var code = req.query.code;
// var state = req.query.state;
// console.log("================================================================================================================================");
// console.log(req);
// console.log("================================================================================================================================");
// if (state === null) {
// next(new HttpException(400, 'Cannot create twerk'));
// } else {
// const resp : any = await axios.post('https://accounts.spotify.com/api/token',{
// form: {
// code: code,
// redirect_uri: this.CALLBACK_URL,
// // code_verifier : this.ENCRYPTION_SECRET.stringCrypt,
// grant_type: 'authorization_code'
// },
// headers: {
// 'Authorization': 'Basic ' + (new Buffer(this.CLIENT_ID + ':' + this.CLIENT_SECRET).toString('base64')),
// },json: true}
// );
// if (resp.statusCode != 200) {
// console.log(resp.statusCode, resp.body);
// }
// else{
// console.log("error");
// console.log(resp.statusCode, resp.body);
// }
// }
// // });
// } catch (error) {
// console.log(error);
// next(new HttpException(400, 'Cannot create spot'));
// }
var code = req.query.code;
var state = req.query.state || null;
// var storedState = req.cookies ? req.cookies[stateKey] : null;
var authOptions = {
method: 'POST',
url: 'https://accounts.spotify.com/api/token',
data: qs.stringify({
code: code,
redirect_uri: this.CALLBACK_URL,
grant_type: 'authorization_code'
}),
headers: {
'Authorization': 'Basic ' + ( Buffer.from(this.CLIENT_ID + ':' + this.CLIENT_SECRET).toString('base64')),
'Content-Type' : 'application/x-www-form-urlencoded'
},
json: true
};
try {
console.log('presssquuueee');
var resp = await axios(authOptions);
if (resp.status === 200) {
console.log(resp);
console.log('oon esttt laaa');
var access_token = resp.data.access_token;
console.log(access_token);
// should redirect res.redirect('/')
res.json("ok");
}
} catch (error) {
console.log('heuuuuuuuuuuuuuuuuuuuuubizzzaaarrreeee');
console.log(error);
next(new HttpException(400, 'On peut pas te connecter mec'));
}
// axios({
// method: 'post',
// url: 'https://accounts.spotify.com/api/token',
// data: {
// firstName: 'Fred',
// lastName: 'Flintstone'
// },
// headers: {
// 'Authorization': 'Basic ' + ( Buffer.from(this.CLIENT_ID + ':' + this.CLIENT_SECRET).toString('base64')),
// 'Content-Type' : 'application/x-www-form-urlencoded'
// },
// });
// request.post(authOptions, function(error, response, body) {
// if (!error && response.statusCode === 200) {
// var access_token = body.access_token;
// console.log(access_token);
// res.redirect(200, '/')
// }
// console.log(error);
// });
};
private encrypt(text :any){
return CryptoJS.AES.encrypt(text, this.ENCRYPTION_SECRET.stringCrypt).toString();
};
private decrypt(text: any) {
console.log("errer");
var bytes = CryptoJS.AES.decrypt(text, this.ENCRYPTION_SECRET.stringCrypt);
return bytes.toString(CryptoJS.enc.Utf8);
};
}
export default SpotifyController;

@ -0,0 +1,169 @@
import { Router, Request, Response, NextFunction, RequestHandler } from 'express';
import Controller from '../Icontroller';
import HttpException from '../../middleware/exeption/httpExeption';
import LocationService from '../../service/LocationService';
class TaskController implements Controller {
public path = '/task';
public router = Router();
private userService = new UserService();
private locationService = new LocationService();
constructor() {
this.initialiseRoutes();
}
private initialiseRoutes(): void {
//create
this.router.post(`${this.path}`,this.createUser);
// // get One
this.router.get (`${this.path}/:userId`, this.getUserById);
// // get All
this.router.get (`${this.path}`, this.getAllUsers);
//update One
this.router.put (`${this.path}/:userId`, this.updateUser);
//Delete One
this.router.delete (`${this.path}/:userId`, this.deleteUser);
}
private createUser = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
try {
console.log(req.body);
const reqBody:CreateTaskReqBody = Object.assign({}, req.body);
checkIfIsValidCreateTaskReqBody(reqBody);
await this.userService.createUserById(reqBody.fin
);
res.status(200).send({ status: "Success", msg: "Success add" });
} catch (error) {
next(new HttpException(400, 'Cannot create post'));
}
};
private readonly getUserById: RequestHandler = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
try {
const id = req.params.taskId;
const userId = req.params.userId;
const data = await this.userService.getUserById(id, userId);
res.status(201).send(data);
}
catch(error){
next(new HttpException(400, 'Cannot create post'));
}
}
private readonly getAllUsers: RequestHandler = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
try {
const userId = req.params.userId;
const tasks = await this.userService.getUsers(userId);
const responseList = tasks.map(task => new TaskResumedRes(task));
res.status(201).send(responseList);
}
catch(error){
next(new HttpException(400, 'Cannot get user task'));
}
}
private deleteUser = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
try {
const id = req.params.taskId;
const userId = req.params.userId;
await this.userService.DeleteUser(id, userId);
return res.status(200).send({ status: "Success", msg: "Data Removed" });
} catch (error) {
next(new HttpException(400, 'Cannot create post'));
}
};
private updateUser = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
try {
const taskId = req.params.taskId;
const userId = req.params.userId;
const reqBody:CreateTaskReqBody = Object.assign({}, req.body);
const updatedTask = await this.userService.UpdateTask(
// req.auth!.uid,
taskId,
userId,
// firebase.auth().currentUser.getIdToken()
reqBody.nom,
reqBody.description,
reqBody.logo,
reqBody.duration,
reqBody.done,
// reqBody.tags,
reqBody.repepat,
reqBody.deb,
reqBody.fin
);
// res.send('Success add');
// res.status(201).json({ task });
res.status(204).send(`Update a new contact: ${updatedTask}`);
} catch (error) {
console.log(error);
next(new HttpException(403, 'Cannot create post'));
}
};
private getUserNext: RequestHandler = async (
req: Request,
res: Response,
next: NextFunction
): Promise<Response | void> => {
try {
const longitude = Number(req.body.longitude);
const latitude = Number(req.body.latitude);
//verify::val_int(){
if (isNaN(longitude) || isNaN(latitude)) {
console.log('Impossible de convertir la chaîne en nombre');
}
//}
const userId = req.body.userId;
const data = await this.locationService.getNearUser(userId,latitude,longitude);
console.log(data);
res.status(201).send(data);
}
catch(error){
next(new HttpException(400, 'Cannot create post'));
}
}
}
export default TaskController;

@ -0,0 +1,3 @@
const uri = "mongodb+srv://fladDevDb:ZslYlNRWIOUU7i6o@fladcluster.b29tytu.mongodb.net/?retryWrites=true&w=majority"
export default db = new MongoClient(uri);

@ -0,0 +1,6 @@
const notificationSchema = new mongoose.Schema({
type: {type: String, required: true},
content: {type: String, required: true}
});
export default {Annonce: mongoose.model("nofitication", notificationSchema)}

@ -0,0 +1,21 @@
const userSchema: Schema = new mongoose.Schema<IUser>({
pseudo: {type: String, index: { unique: true }},
email: {type: String},
idDafl: {type: String, index: { unique: true }},
idSpotify: {type: String},
password: {type: String},
prenom: {type: String, default: ""},
description: {type: String, default: ""},
nom: {type: String, default: ""},
ville: {type: String, default: ""},
profilPic: {type: String},
noteList: [],
notifications: [],
friends: {type: [String] },
favoris: [],
conversations: {type: [String] }
});
// fladDevDb
// ZslYlNRWIOUU7i6o
export const User: Model<IUser> = model('User', userSchema);

@ -0,0 +1,13 @@
class HttpException extends Error {
public status: number;
public message: string;
constructor(status: number, message: string) {
super(message);
this.status = status;
this.message = message;
}
}
// en fontion de l'exeption firebas,etc une bonne exeption
export default HttpException;

@ -0,0 +1,8 @@
// export const loggerOptions: expressWinston.LoggerOptions = {
// transports: [new winston.transports.Console()],
// format: winston.format.combine(
// winston.format.json(),
// winston.format.prettyPrint(),
// winston.format.colorize({ all: true })
// ),
// };

@ -0,0 +1,5 @@
export default interface IUser{
name: string;
email: string;
avatar?: string;
}

@ -0,0 +1,57 @@
export interface Position {
/**
* Creation timestamp for coords
*/
timestamp: number;
/**
* The GPS coordinates along with the accuracy of the data
*/
coords: {
/**
* Latitude in decimal degrees
*/
latitude: number;
/**
* longitude in decimal degrees
*/
longitude: number;
};
}
export class PlacePosition implements Position {
timestamp: number;
coords: {
latitude: number;
longitude: number;
};
constructor(timestamp: number,latitude : number ,longitude: number){
this.timestamp = timestamp;
this.coords = {latitude, longitude};
}
}
export class UserLocation {
uuid: string;
latitude : number;
longitude: number;
constructor(uuid: string, latitude: number, longitude: number){
this.uuid = uuid;
this.latitude = latitude;
this.longitude = longitude;
}
}
export class Place{
position: Position;
address: Address;
constructor(address: Address,position: Position){
this.position = position;
this.address = address;
}
}
export type Address = {
street : string;
city : string;
state : string;
zip : string;
}

@ -0,0 +1,15 @@
import App from "./app";
import SpotifyController from "./controller/spotify-controller/spotifyCtrl";
import PingController from "./controller/TestCtrl";
const app = new App(
[new PingController(), new SpotifyController()],
Number(8080)
// Number(process.env.PORT)
);
app.listen();

@ -0,0 +1,136 @@
// import db from '../database';
import { Place, PlacePosition, Position, UserLocation } from '../model/locationModel';
import axios from 'axios';
class LocationService {
private API_KEY : string = "AIzaSyBFCEAtmhZ8jvw84UTQvX3Aqpr66GVqB_A";
public async getNearUser(uuid : string, latitude : number, longitude : number)
{
const UserCollectionref = db.collection('UserPosition');
db.collection('UserPosition').doc(uuid).set({uuid : uuid, latitude : latitude, longitude : longitude});
// const newPosition = {
// name: 'Los Angeles',
// state: 'CA',
// country: 'USA'
// };
const snapshot = await UserCollectionref.where("uuid", "!=", uuid).get();
if (snapshot.empty) {
console.log('No matching documents.');
return;
}
let dbUsersList:UserLocation[] = [];
snapshot.forEach(doc => {
dbUsersList.push(new UserLocation(doc.data().uuid,doc.data().latitude,doc.data().longitude));
console.log(doc.id, '=>', doc.data());
});
let listUser: string[] = []; //Set the listUser to an empty list
dbUsersList.forEach(user => {
console.log(user);
const dist = this.distanceBetween(latitude , longitude , user.latitude, user.longitude); //With the function meters, determinate the distance between the current user and the user who is in the actual row
console.log(user.uuid,dist);
if (dist <= 5) { //If the user in the actual row is less than 100 meters away of the current user
listUser.push(user.uuid); //Add the username and the ID of the song that user who is in the actual row is listening
}
});
return listUser; //Return an array
// $listUser[] = ['user' => $userID, 'music' => $idMusic]; //Add the username and the ID of the song that user who is in the actual row is listening
}
public getCenter (points: Position[]) {
if (Array.isArray(points) === false || points.length === 0) {
return false;
}
const numberOfPoints = points.length;
const sum = points.reduce(
(acc, point) => {
const pointLat = this.toRad(point.coords.latitude);
const pointLon = this.toRad(point.coords.longitude);
return {
X: acc.X + Math.cos(pointLat) * Math.cos(pointLon),
Y: acc.Y + Math.cos(pointLat) * Math.sin(pointLon),
Z: acc.Z + Math.sin(pointLat),
};
},
{ X: 0, Y: 0, Z: 0 }
);
const X = sum.X / numberOfPoints;
const Y = sum.Y / numberOfPoints;
const Z = sum.Z / numberOfPoints;
return {
longitude: this.toDeg(Math.atan2(Y, X)),
latitude: this.toDeg(Math.atan2(Z, Math.sqrt(X * X + Y * Y))),
};
};
public toRad = (value: number) => (value * Math.PI) / 180;
public toDeg = (value: number) => (value * 180) / Math.PI;
// sa c'est un utils du coup mettre dans une calss utils
// resulta en km
private distanceBetween (lat1 : number, lon1 : number, lat2: number, lon2 : number) : number {
if ((lat1 == lat2) && (lon1 == lon2)) {
return 0;
}
else {
var radlat1 = Math.PI * lat1/180;
var radlat2 = Math.PI * lat2/180;
var theta = lon1-lon2;
var radtheta = Math.PI * theta/180;
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
if (dist > 1) {
dist = 1;
}
dist = Math.acos(dist);
dist = dist * 180/Math.PI;
dist = dist * 60 * 1.1515;
dist = dist * 1.609344;
return dist;
}
}
private distanceBetweenPosition(first : Position, second : Position) : number {
return this.distanceBetween (first.coords.latitude, first.coords.longitude, second.coords.latitude, second.coords.longitude)
}
// give a array of position sorted by distance and return the first
private findNearest(main : Position, list : Position[]){
this.orderByDistance(main, list)[0]
}
//distanceFn: DistanceFn = getDistance est param sa serrait cool de lui passer un fonction
private orderByDistance (mainPos: Position,coords: Position[]){
return coords
.slice()
.sort((a, b) => this.distanceBetweenPosition(mainPos, a) - this.distanceBetweenPosition(mainPos, b));
};
// getCenter(coords)
}
export default LocationService;

@ -1,67 +1,26 @@
import { LinearGradient } from 'expo-linear-gradient';
import { StatusBar } from 'expo-status-bar';
import { useState, useTransition } from 'react';
import { Animated, StyleSheet, Text, View } from 'react-native';
import FavoritePage from './screens/favoritePage';
import { cards as cardArray } from './data/data'
import Navigation from './navigation/Navigation';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Animated, Dimensions, ImageBackground, StyleSheet, Text, View } from 'react-native';
import Card from './components/Card';
import Login from './pages/login';
import Spot from './pages/spot';
export default function App() {
// const [currentCard, setCurrentCard] = useState(cardArray);
// const aIndex = useTransition(currentCard)
// ;
const [cards, setCards] = useState(cardArray);
const aIndex = useTransition();
const onSwipe = (index: number, direction: 'left' | 'right') => {
if (direction === 'right') {
// Swiped right
console.log('Swiped right');
} else if (direction === 'left') {
console.log('Swiped left');
}
// update the state of the card or the app
setCards(cards.filter((_, i) => i !== index));
};
// const [currentCard, setCurrentCard] = useState(0);
const Stack = createBottomTabNavigator();
return (
/*<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', position : 'absolute', backgroundColor : '' }}>
{cards.map((card, index) => (
<View key={card.name}>
<Card
title={card.name}
image={card.sourceUrl}
onSwipe={(direction) => onSwipe(index, direction)}
/>
</View>
))}
</View>*/
<Navigation/>
export default function App() {
// <View style={styles.container}>
// <Text>Open up App.tsx to start working on your app!</Text>
// {/* <View>
// <Animated.View>
const Stack = createBottomTabNavigator();
// </Animated.View>
// {cardArray.map( ({index}) => currentCard < index && step + step && (
// <Card card={card} ></Card>
return (
// ) )}
<Navigation/>
// </View> */}
// <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
// <Card title="Swipe me left or right" />
// </View>
// <StatusBar style="auto" />
// </View>
);
}

@ -0,0 +1,113 @@
import { useState } from "react";
import SpotifyService from "../services/spotify/spotify.service";
class Manager{
// injection de dépences
spotifyService = new SpotifyService();
userService = new userService();
private currentUser = useState(null);
constructor() {
}
// spotify methods
apiAuthorization(url : string) {
this.spotifyService.apiAuthorization(url);
}
getCompleteMusic = async (id : string) :Promise<Music> =>{
// Map info = await spotifyService.getTrackInfo(id);
// return Music(id, info['name'], info['artist'], info['cover']);
}
removeFromPlaylist(id : string) {
this.spotifyService.removeFromPlaylist(id);
}
addToPlaylist(id : string) {
this.spotifyService.addToPlaylist(id);
}
playTrack(id : string) {
this.spotifyService.playTrack(id);
}
////////////////////////////////////////////
// private readonly getTaskById: RequestHandler = async (
// req: Request,
// res: Response,
// next: NextFunction
// ): Promise<Response | void> => {
// try {
// const id = req.params.taskId;
// const userId = req.params.userId;
// const data = await this.Taskservice.getTaskById(id, userId);
// res.status(201).send(data);
// }
// catch(error){
// next(new HttpException(400, 'Cannot create post'));
// }
// }
// private delete = async (
// req: Request,
// res: Response,
// next: NextFunction
// ): Promise<Response | void> => {
// try {
// const id = req.params.taskId;
// const userId = req.params.userId;
// await this.Taskservice.DeleteTask(id, userId);
// return res.status(200).send({ status: "Success", msg: "Data Removed" });
// } catch (error) {
// next(new HttpException(400, 'Cannot create post'));
// }
// };
// private updateTask = async (
// req: Request,
// res: Response,
// next: NextFunction
// ): Promise<Response | void> => {
// try {
// const taskId = req.params.taskId;
// const userId = req.params.userId;
// const reqBody:CreateTaskReqBody = Object.assign({}, req.body);
// const updatedTask = await this.Taskservice.UpdateTask(
// // req.auth!.uid,
// taskId,
// userId,
// // firebase.auth().currentUser.getIdToken()
// reqBody.nom,
// reqBody.description,
// reqBody.logo,
// reqBody.duration,
// reqBody.done,
// // reqBody.tags,
// reqBody.repepat,
// reqBody.deb,
// reqBody.fin
// );
// // res.send('Success add');
// // res.status(201).json({ task });
// res.status(204).send(`Update a new contact: ${updatedTask}`);
// } catch (error) {
// console.log(error);
// next(new HttpException(403, 'Cannot create post'));
// }
// };
}
export default Manager;

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

@ -0,0 +1,8 @@
class Spot {
private userId : string;
public music : Music;;
constructor(userId : string, music : Music){
this.userId = userId;
this.music = music;
}
}

@ -0,0 +1,129 @@
// import { useState } from "react";
// import SpotifyService from "../services/spotify/spotify.service";
// import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
// class StubManager{
// // injection de dépences
// spotifyService = new SpotifyService();
// // userService = new userService();
// private currentUser = useState(null);
// private discovery = {
// authorizationEndpoint: 'https://accounts.spotify.com/authorize',
// tokenEndpoint: 'https://accounts.spotify.com/api/token',
// };
// constructor() {
// }
// // spotify methods
// apiAuthorization(url : string){
// const [, response, promptAsync] = useAuthRequest(
// {
// clientId: '1f1e34e4b6ba48b388469dba80202b10',
// scopes: ['user-read-email', 'playlist-modify-public'],
// // In order to follow the "Authorization Code Flow" to fetch token after authorizationEndpoint
// // this must be set to false
// usePKCE: false,
// redirectUri: makeRedirectUri({
// scheme: 'flad'
// }),
// },
// this.discovery
// );
// }
// getCompleteMusic = async (id : string) :Promise<Music> =>{
// // Map info = await spotifyService.getTrackInfo(id);
// // return Music(id, info['name'], info['artist'], info['cover']);
// }
// removeFromPlaylist(id : string) {
// this.spotifyService.removeFromPlaylist(id);
// }
// addToPlaylist(id : string) {
// this.spotifyService.addToPlaylist(id);
// }
// playTrack(id : string) {
// this.spotifyService.playTrack(id);
// }
// ////////////////////////////////////////////
// // private readonly getTaskById: RequestHandler = async (
// // req: Request,
// // res: Response,
// // next: NextFunction
// // ): Promise<Response | void> => {
// // try {
// // const id = req.params.taskId;
// // const userId = req.params.userId;
// // const data = await this.Taskservice.getTaskById(id, userId);
// // res.status(201).send(data);
// // }
// // catch(error){
// // next(new HttpException(400, 'Cannot create post'));
// // }
// // }
// // private delete = async (
// // req: Request,
// // res: Response,
// // next: NextFunction
// // ): Promise<Response | void> => {
// // try {
// // const id = req.params.taskId;
// // const userId = req.params.userId;
// // await this.Taskservice.DeleteTask(id, userId);
// // return res.status(200).send({ status: "Success", msg: "Data Removed" });
// // } catch (error) {
// // next(new HttpException(400, 'Cannot create post'));
// // }
// // };
// // private updateTask = async (
// // req: Request,
// // res: Response,
// // next: NextFunction
// // ): Promise<Response | void> => {
// // try {
// // const taskId = req.params.taskId;
// // const userId = req.params.userId;
// // const reqBody:CreateTaskReqBody = Object.assign({}, req.body);
// // const updatedTask = await this.Taskservice.UpdateTask(
// // // req.auth!.uid,
// // taskId,
// // userId,
// // // firebase.auth().currentUser.getIdToken()
// // reqBody.nom,
// // reqBody.description,
// // reqBody.logo,
// // reqBody.duration,
// // reqBody.done,
// // // reqBody.tags,
// // reqBody.repepat,
// // reqBody.deb,
// // reqBody.fin
// // );
// // // res.send('Success add');
// // // res.status(201).json({ task });
// // res.status(204).send(`Update a new contact: ${updatedTask}`);
// // } catch (error) {
// // console.log(error);
// // next(new HttpException(403, 'Cannot create post'));
// // }
// // };
// }
// export default StubManager;

@ -0,0 +1,9 @@
class User {
//attributes from DAFL
private idDafl : any;;
private idSpotify : any;
//constructors
constructor(){
}
}

@ -9,8 +9,9 @@
"splash": {
"image": "./assets/icons/splash.png",
"resizeMode": "contain",
"backgroundColor": "#141414"
"backgroundColor": "#1d2129"
},
"scheme": "flad",
"updates": {
"fallbackToCacheTimeout": 0
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 142 KiB

@ -0,0 +1,5 @@
export const spotifyCredentials = {
clientId: 'a5cb39302b6e4c64957de1df50742d71',
clientSecret: 'e23db1cd77ee4b589ee99525e282b2e8',
redirectUri: 'Your Redirect URI'
}

@ -0,0 +1,3 @@
<svg width="21" height="19" viewBox="0 0 21 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.85029 4.158C1.85877 3.01308 2.68386 2.08791 3.70044 2.08791H17.2996C18.3214 2.08791 19.1498 3.0227 19.1498 4.17582V4.18139L11.7151 8.74107C11.183 9.06737 10.5432 9.07922 10.0019 8.77281L1.85029 4.158ZM1.85022 6.49388V14.8242C1.85022 15.9773 2.67859 16.9121 3.70044 16.9121H17.2996C18.3214 16.9121 19.1498 15.9773 19.1498 14.8242V6.55774L12.5986 10.5756C11.5345 11.2282 10.2548 11.2519 9.17228 10.639L1.85022 6.49388ZM3.70044 0C1.65674 0 0 1.86958 0 4.17582V14.8242C0 17.1304 1.65674 19 3.70044 19H17.2996C19.3433 19 21 17.1304 21 14.8242V4.17582C21 1.86958 19.3433 0 17.2996 0H3.70044Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 757 B

@ -0,0 +1,6 @@
<svg width="20" height="17" viewBox="0 0 20 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1_70_52" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.12142 8.63961C1.28389 8.84583 1.46152 9.03956 1.65263 9.21911L8.09971 16.2523C8.8973 17.1224 10.271 17.1161 11.0606 16.2388L17.9089 8.62951L17.8988 8.62763C18.674 7.71412 19.1417 6.53135 19.1417 5.23935C19.1417 2.34574 16.7959 0 13.9023 0C12.0669 0 10.4518 0.943814 9.51612 2.3726C8.56693 1.03408 7.00516 0.16054 5.23935 0.16054C2.34574 0.16054 0 2.50628 0 5.39989C0 6.61822 0.415838 7.73942 1.11332 8.6293L1.11217 8.62951L1.12142 8.63961Z"/>
</mask>
<path d="M1.12142 8.63961L2.69245 7.40192L2.6462 7.34322L2.59571 7.28813L1.12142 8.63961ZM1.65263 9.21911L3.12694 7.86766L3.07648 7.81261L3.02206 7.76148L1.65263 9.21911ZM8.09971 16.2523L9.57402 14.9008L9.57402 14.9008L8.09971 16.2523ZM11.0606 16.2388L12.5472 17.5767L12.5472 17.5767L11.0606 16.2388ZM17.9089 8.62951L19.3955 9.96744L21.7842 7.31332L18.2732 6.66296L17.9089 8.62951ZM17.8988 8.62763L16.3739 7.33352L14.1402 9.96545L17.5345 10.5942L17.8988 8.62763ZM9.51612 2.3726L7.88468 3.5295L9.58181 5.92274L11.1892 3.46835L9.51612 2.3726ZM1.11332 8.6293L1.47049 10.5972L4.73275 10.0051L2.68743 7.39553L1.11332 8.6293ZM1.11217 8.62951L0.755003 6.66166L-2.81154 7.30899L-0.362119 9.98099L1.11217 8.62951ZM-0.449608 9.87729C-0.225421 10.1619 0.0196126 10.4291 0.283205 10.6767L3.02206 7.76148C2.90343 7.65003 2.7932 7.52981 2.69245 7.40192L-0.449608 9.87729ZM9.57402 14.9008L3.12694 7.86766L0.178324 10.5706L6.6254 17.6037L9.57402 14.9008ZM9.57402 14.9008L9.57402 14.9008L6.6254 17.6037C8.22058 19.3439 10.968 19.3314 12.5472 17.5767L9.57402 14.9008ZM16.4223 7.29158L9.57402 14.9008L12.5472 17.5767L19.3955 9.96744L16.4223 7.29158ZM17.5345 10.5942L17.5446 10.5961L18.2732 6.66296L18.263 6.66108L17.5345 10.5942ZM19.4236 9.92175C20.4941 8.6604 21.1417 7.02293 21.1417 5.23935H17.1417C17.1417 6.03977 16.8539 6.76784 16.3739 7.33352L19.4236 9.92175ZM21.1417 5.23935C21.1417 1.24117 17.9005 -2 13.9023 -2V2C15.6914 2 17.1417 3.45031 17.1417 5.23935H21.1417ZM13.9023 -2C11.3642 -2 9.13258 -0.692242 7.843 1.27684L11.1892 3.46835C11.7711 2.57987 12.7695 2 13.9023 2V-2ZM11.1476 1.21569C9.83945 -0.628956 7.68082 -1.83946 5.23935 -1.83946V2.16054C6.32949 2.16054 7.29441 2.69712 7.88468 3.5295L11.1476 1.21569ZM5.23935 -1.83946C1.24117 -1.83946 -2 1.40171 -2 5.39989H2C2 3.61085 3.45031 2.16054 5.23935 2.16054V-1.83946ZM-2 5.39989C-2 7.08121 -1.42438 8.63367 -0.460783 9.86307L2.68743 7.39553C2.25605 6.84516 2 6.15522 2 5.39989H-2ZM1.46933 10.5974L1.47049 10.5972L0.756159 6.66145L0.755003 6.66166L1.46933 10.5974ZM2.59571 7.28813L2.58645 7.27804L-0.362119 9.98099L-0.352865 9.99108L2.59571 7.28813Z" fill="white" mask="url(#path-1-inside-1_70_52)"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@ -0,0 +1,3 @@
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.88862 0.324035C1.45657 -0.108012 0.756083 -0.108012 0.324036 0.324035C-0.108011 0.756082 -0.108013 1.45657 0.324034 1.88861L5.75471 7.31929L0.324038 12.75C-0.108009 13.182 -0.108009 13.8825 0.324038 14.3145C0.756085 14.7466 1.45657 14.7466 1.88862 14.3145L7.31929 8.88387L12.75 14.3145C13.182 14.7466 13.8825 14.7466 14.3145 14.3145C14.7466 13.8825 14.7466 13.182 14.3145 12.75L8.88387 7.31929L14.3145 1.88863C14.7466 1.45658 14.7466 0.756092 14.3145 0.324045C13.8825 -0.108002 13.182 -0.108004 12.75 0.324043L7.31929 5.75471L1.88862 0.324035Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 715 B

@ -0,0 +1,3 @@
<svg width="26" height="18" viewBox="0 0 26 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.837607 0C1.3002 0 1.67521 0.400086 1.67521 0.893617V17.1064C1.67521 17.5999 1.3002 18 0.837607 18C0.375009 18 0 17.5999 0 17.1064V0.893617C0 0.400086 0.375009 0 0.837607 0ZM4.90598 0C5.36858 0 5.74359 0.400086 5.74359 0.893617V17.1064C5.74359 17.5999 5.36858 18 4.90598 18C4.44339 18 4.06838 17.5999 4.06838 17.1064V0.893617C4.06838 0.400086 4.44339 0 4.90598 0ZM9.93162 2.53621V16.2128H12.3248V12.1941C12.62 12.367 12.9968 12.5 13.4468 12.5H14V16.8511C14 17.4856 13.5178 18 12.9231 18H9.33333C8.72658 18 8.25641 17.4699 8.25641 16.8482V1.3651C8.25641 0.464709 9.19429 -0.101539 9.9188 0.39861L13.5077 2.87616C13.813 3.08688 14 3.44946 14 3.84103V6.5H13.4468C12.9968 6.5 12.62 6.63296 12.3248 6.80589V4.18828L9.93162 2.53621ZM13 9.5C13 10.0523 13.2 10.5 13.4468 10.5H14H16.5V13.5532C16.5 13.8 16.9477 14 17.5 14C18.0523 14 18.5 13.8 18.5 13.5532V10.5H21.5532C21.8 10.5 22 10.0523 22 9.5C22 8.94772 21.8 8.5 21.5532 8.5H18.5V5.44681C18.5 5.20004 18.0523 5 17.5 5C16.9477 5 16.5 5.20004 16.5 5.44681V8.5H14H13.4468C13.2 8.5 13 8.94771 13 9.5Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1,7 @@
const Icons = {
discovery: require('./icon_discovery.png'),
// riveLike : require('./light_like.riv'),
}
export default Icons;

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -2,5 +2,8 @@ module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
// always the last plugin
plugins: ['react-native-reanimated/plugin'],
};
};

@ -1,77 +1,202 @@
import { View, Text, Image, Animated ,PanResponder, Dimensions, StyleSheet } from 'react-native'
import { View, Text, Image , Dimensions, StyleSheet } from 'react-native'
import React, { useRef, useState } from 'react'
import { eventMethod } from '@ionic/core/dist/types/utils/overlays';
import Animated,{ Extrapolate, interpolate, useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withSpring } 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 width = wWidht *0.75;
const height = wWidht * (465/264);
const borderRadius = 24;
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;
interface CardProps {
title: string;
image: any;
onSwipe: (direction: "left" | "right") => void;
}
type ContextType = {
translateX: number;
translateY: number;
};
// const [loading, setLoading] = useState(true);
// const [cards, setCards] = useState(cardArray);
// useEffect(()=>{
// setLoading(true);
// eventMethod().then(
// setLoading(false);
// )
// })
const Card: React.FC<CardProps> = ({ title, image, onSwipe }) => {
const pan = useRef(new Animated.ValueXY()).current;
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
// Only set the pan responder if the user has moved the card more than a certain threshold
if (Math.abs(gestureState.dx) > 10) {
return true;
}
return false;
},
onPanResponderMove: (evt, gestureState) => {
// Update the position of the card as the user swipes it
pan.setValue({ x: gestureState.dx, y: 0 });
console.log("Card");
},
onPanResponderRelease: (evt, gestureState) => {
// Handle the swipe action based on the swipe direction
if (gestureState.dx > 50) {
onSwipe("right");
} else if (gestureState.dx < -50) {
onSwipe("left");
} else {
// Swiped a small amount, reset the position of the card
Animated.spring(pan, {
toValue: { x: 0, y: 0 },
useNativeDriver: true,
}).start();
}
},
})
).current;
const Card = ({ title, image, onSwipe} : CardProps) => {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const hapti = (() => {
// Haptics.NotificationFeedbackType.Success
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)
});
const onGestureEvent = useAnimatedGestureHandler<
PanGestureHandlerGestureEvent,
ContextType
>({
onStart : (event, context) => {
context.translateX = translateX.value;
context.translateY = translateY.value;
},
onActive : (event, context) => {
translateX.value = event.translationX + context.translateX -5;
translateY.value = event.translationY + context.translateY;
},
onEnd : (event, context) => {
// translateX.value = withSpring(0);
// translateY.value = withSpring(snapPoint(translateY.value,velocityY, snapPoints ))
if (translateX.value > 160) {
hapti()
onSwipe("right");
} else if (translateX.value < -160) {
onSwipe("left");
} else {
translateX.value = withSpring(0);
translateY.value = withSpring(0);
}
},
});
//better to have 2 listerner => 2 useAnimatedStyle ou faire une ftc qui retourne l'verse de une useAnimatedStyle
const opacLStyle = useAnimatedStyle(() => {
const opacityl = interpolate
( translateX.value,
[-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4],
[0, 0, 1]);
return {
opacity : opacityl,
};
});
const opacRStyle = useAnimatedStyle(() => {
const opacityl = interpolate
( translateX.value,
[-SCREEN_WIDTH / 4, 0, SCREEN_WIDTH / 2],
[1, 0, 0]);
return {
opacity : opacityl,
};
});
const opacCStyle = useAnimatedStyle(() => {
const opacityl = interpolate
( translateX.value,
[-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 4],
[0.85, 1, 1]);
return {
opacity : opacityl,
};
});
const opacDStyle = useAnimatedStyle(() => {
const opacityl = interpolate
( translateY.value,
[-SCREEN_HEIGHT / 4, 0, SCREEN_HEIGHT / 4],
[0, 0, 1]);
return {
opacity : opacityl,
};
});
const rotateStyle = useAnimatedStyle(() => {
const rot = interpolate
( translateX.value,
[-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2],
[30, 0, 30],
Extrapolate.CLAMP);
return {
transform: [{ rotate: `${rot}deg` },{
translateX: withSpring(translateX.value),
} ],
};
});
const rStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: translateX.value,
},
{
translateY: translateY.value,
},
],
};
});
return (
<View>
<PanGestureHandler onGestureEvent={onGestureEvent}>
<Animated.View style={[ rStyle, styles.card,opacCStyle, ]} >
<Animated.View
style={{ transform: [{ translateX: pan.x }], backgroundColor : 'red'}}
{...panResponder.panHandlers}
>
<Image source={{uri : image}} style={{ width: 200, height: 200 }} />
style={[{
// transform: [{ rotate: "30deg" }],
position: "absolute",
zIndex: 1000,
},opacRStyle]}
>
<Image style={[{alignSelf : "center"}]}
source={require('../assets/icons/icon_dislike.png')}
/>
</Animated.View>
<Animated.View
style={[{
position: "absolute",
justifyContent : "center",
alignContent : "center",
zIndex: 1000,
},opacLStyle]}
>
<Image style={[{alignSelf : "center"}]}
source={require('../assets/icons/icon_like.png')}
/>
</Animated.View>
<Animated.View
style={[{
position: "absolute",
justifyContent : "center",
alignContent : "center",
zIndex: 1000,
},opacDStyle]}
>
<Image style={[{alignSelf : "center",width: 126.27,
height: 118.64, }]}
source={require('../assets/icons/icon_discovery.png')}
/>
</Animated.View>
<Image source={{uri : image}} style={[styles.image]} />
</Animated.View>
</PanGestureHandler>
</View>
);
};
const nournous = StyleSheet.create({
const styles = StyleSheet.create({
card : {
justifyContent : 'center',
alignItems : 'center',
borderColor : 'red',
borderWidth : 3,
},
image : {
borderRadius : 24,
resizeMode: 'cover',
height: 529,
width: 312,
backgroundColor: 'black',
}
})

@ -0,0 +1,49 @@
import { View, Text, Image , Dimensions, StyleSheet, Pressable } from 'react-native'
import React, { useRef, useState } from 'react'
import Animated,{ Extrapolate, interpolate, useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
import * as Haptics from 'expo-haptics';
import Icons from '../../assets/icons/icon';
import Rive, { Fit, RiveRef } from 'rive-react-native';
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;
interface ButtonProps {
name : string;
}
const FladButton = ({name} : ButtonProps) => {
const riveRef = React.useRef<RiveRef>(null);
const handlePlay = () => { riveRef.current?.play() };
return (
<Pressable onPress={handlePlay}>
<Image source={Icons.discovery} style={[styles.image]} />
</Pressable>
);
};
const styles = StyleSheet.create({
button : {
justifyContent : 'center',
alignItems : 'center',
},
image : {
borderRadius : 24,
resizeMode: 'cover',
width: 65,
height: 65,
backgroundColor: 'black',
}
})
export default FladButton;

@ -0,0 +1,30 @@
import { useState } from 'react';
import { View, Text, Image, Animated ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, TextInput } from 'react-native'
interface InputProps {
name : string;
placeholder : string;
}
const FladInput = ({name, placeholder} : InputProps) => {
const [focused, setFocused] = useState<boolean>(false);
return (
<View>
<Text></Text>
<TextInput style={[styles.input]}>
</TextInput>
</View>
);
};
const styles = StyleSheet.create({
input : {
justifyContent : 'center',
alignItems : 'center',
placeholder : "placeholde"
},
})
export default FladInput;

@ -1,17 +1,12 @@
export const cards = [{
name : "blue",
sourceUrl : "https://images.unsplash.com/photo-1484589065579-248aad0d8b13?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1359&q=80",
sourceUrl : "https://cdns-images.dzcdn.net/images/artist/399e7e760d8fedf3cc2891e9c0c41658/200x200-000000-80-0-0.jpg",
index : 3
},
{
name : "red",
sourceUrl : "https://images.unsplash.com/photo-1614613535308-eb5fbd3d2c17?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=870&q=80",
index : 2
},
{
name : "green",
sourceUrl : "https://images.unsplash.com/photo-1584679109597-c656b19974c9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=580&q=80",
index : 1
name : "her",
sourceUrl : "https://images.unsplash.com/photo-1484589065579-248aad0d8b13?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1359&q=80",
index : 3
}
]

@ -15,13 +15,23 @@
"@react-navigation/stack": "^6.3.12",
"expo": "~47.0.12",
"expo-cli": "^6.2.1",
"axios": "^1.2.6",
"expo-auth-session": "~3.8.0",
"expo-haptics": "~12.0.1",
"expo-linear-gradient": "~12.0.1",
"expo-location": "~15.0.1",
"expo-random": "~13.0.0",
"expo-secure-store": "~12.0.0",
"expo-status-bar": "~1.4.2",
"react": "18.1.0",
"react-dom": "18.1.0",
"react-native": "0.70.5",
"react-native-gesture-handler": "~2.8.0",
"react-native-reanimated": "~2.12.0",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.0",
"react-native-web": "~0.18.9"
"react-native-web": "~0.18.9",
"rive-react-native": "^3.0.41"
},
"devDependencies": {
"@babel/core": "^7.12.9",

@ -0,0 +1,137 @@
import { View, Text, Image, Animated ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable, Touchable, TouchableOpacity, Modal, SafeAreaView, TextInput, Platform } from 'react-native'
import React, { useCallback, useEffect, useRef, useState, useTransition } from 'react'
import { LinearGradient } from 'expo-linear-gradient';
import * as Haptics from 'expo-haptics';
import * as AuthSession from 'expo-auth-session';
import Card from '../components/Card';
import axios from 'axios';
import { cards as cardArray } from '../FakeData/data'
import FladButton from '../components/button/button';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
const SCREEN_WIDTH = Dimensions.get('window').width
import * as SecureStore from 'expo-secure-store';
interface LoginProps {
}
interface Params {
[key: string]: string;
}
interface Profile {
display_name: string;
email: string;
id: string;
}
//generate random string
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',
};
async function save(key : string, value : string) {
await SecureStore.setItemAsync(key, value);
}
export default function Login() {
// const [advice, setAdvice] = useState("dd");
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
);
const getAdvice = async () => { axios.get("http://localhost:8080/api/spotify/exchange")
.then(response => {
console.log(response.data.message);
// setAdvice(response.data.message);
}).catch(function (error) {
console.log(error);
});
};
React.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]);
return (
<View style={styles.centeredView}>
<Text style={styles.textStyle}>Hello flad test logIn</Text>
<Button disabled={!request} title="Login"
onPress={() => {
promptAsync();
}}
/>
</View>
);
};
const styles = StyleSheet.create({
centeredView: {
justifyContent: 'center',
alignItems: 'center',
marginTop: 22,
},
modalView: {
margin: 20,
backgroundColor: 'white',
borderRadius: 20,
padding: 35,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 4,
},
header :{
alignItems : 'flex-end',
justifyContent: 'center',
},
button: {
borderRadius: 20,
padding: 10,
elevation: 2,
},
buttonOpen: {
backgroundColor: '#F194FF',
},
textStyle: {
color: 'white',
fontWeight: 'bold',
textAlign: 'center',
},
close :{
alignSelf : 'flex-end',
backgroundColor : 'red',
justifyContent : 'center'
}
})

@ -1,73 +1,215 @@
import { View, Text, Image, Animated ,PanResponder, Dimensions } from 'react-native'
import React, { useRef, useState, useTransition } from 'react'
import { View, Text, Image, Animated ,PanResponder, Dimensions, StyleSheet, ImageBackground, Button, Pressable } from 'react-native'
import React, { useCallback, useEffect, useRef, useState, useTransition } from 'react'
import { LinearGradient } from 'expo-linear-gradient';
import * as Haptics from 'expo-haptics';
import Card from '../components/Card';
import { cards as cardArray } from '../FakeData/data'
import FladButton from '../components/button/button';
import axios from 'axios';
import * as SecureStore from 'expo-secure-store';
import { MY_SECURE_AUTH_STATE_KEY } from './login';
import * as Location from 'expo-location';
const {width : wWidht} = Dimensions.get("window");
const width = wWidht *0.75;
const height = wWidht * (465/264);
const borderRadius = 24;
interface SpotProps {
title: string;
image: any;
onSwipe: (direction: "left" | "right") => void;
}
}
type LocationData = {
latitude: number;
longitude: number;
timestamp: number;
}
interface NearbyUser {
id: number;
name: string;
latitude: number;
longitude: number;
}
async function getUserData(accessToken : string) {
axios.get("https://api.spotify.com/v1/me",
{
headers: {
'Authorization': 'Bearer ' + accessToken,
"Content-Type" : "application/json"
}})
.then(response =>
{
if (response && response.statusText === 'success') {
console.log(response.data.message);
const userData = JSON.stringify(response.data);
const userId = response.data.id;
return {userId, userData}
}
})
.catch(function (error) {
console.log(error);
});
};
// async function sendUserLoc(accessToken : string) {
// axios.get("https://api.spotify.com/v1/me",
// {
// headers: {
// 'Authorization': 'Bearer ' + accessToken,
// "Content-Type" : "application/json"
// }})
// .then(response =>
// {
// if (response && response.statusText === 'success') {
// console.log(response.data.message);
// const userData = JSON.stringify(response.data);
// const userId = response.data.id;}
// })
// .catch(function (error) {
// console.log(error);
// });
// };
async function getValueFor(key:string) :Promise<string | null> {
let result = await SecureStore.getItemAsync(key);
if (result) {
alert("🔐 Here's your value 🔐 \n" + result);
} else {
alert('No values stored under that key.');
}
return result;
}
const Spot: React.FC<SpotProps> = ({ title, image, onSwipe }) => {
const [cards, setCards] = useState(cardArray);
const aIndex = useTransition();
const onSwipe = (index: number, direction: 'left' | 'right') => {
export default function Spot() {
const [cards, setCards] = useState(cardArray);
const [currentCard, setcurrentCard] = useState(cards[cards.length - 1]);
const onSwipe = (index: number, direction: 'left' | 'right') => {
if (direction === 'right') {
// Swiped right
console.log('Swiped right');
// Swiped right
console.log("===================")
} else if (direction === 'left') {
console.log('Swiped left');
// Swiped left
}
// update the state of the card or the app
// update the state of the cards state when it remove thisy
setCards(cards.filter((_, i) => i !== index));
};
setcurrentCard(cards[cards.length -1]);
};
// const [currentCard, setCurrentCard] = useState(0);
const hapti = (() => {
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)
getValueFor(MY_SECURE_AUTH_STATE_KEY)
.then(key => { (key != null) ? getUserData(key) :console.log("error key is nullll") } ) ;
// Haptics.NotificationFeedbackType.Success
});
////////////////////////////////////////////////////////////////
const [locationData, setLocationData] = useState<LocationData>();
const [prevLocationData, setPrevLocationData] = useState<LocationData>();
const [nearbyUsers, setNearbyUsers] = useState<NearbyUser[]>([]);
const [currentMusic, setCurrentMusic] = useState<string>("");
async function getLocation() {
var { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
console.log('Permission to access location was denied');
return;
}
let currentLocation = await Location.getCurrentPositionAsync({});
setLocationData({
latitude: currentLocation.coords.latitude,
longitude: currentLocation.coords.longitude,
timestamp: currentLocation.timestamp
});
};
async function sendLocationToServer() {
getLocation();
if (!locationData) return;
if (prevLocationData && locationData.latitude === prevLocationData.latitude && locationData.longitude === prevLocationData.longitude) {
return;
}
try {
const response = await axios.post(
'http://localhost/api/users/david/nextToMe',
locationData
);
if (response.status !== 200) {
throw new Error('Failed to send location to server');
}
setPrevLocationData(locationData);
setNearbyUsers(response.data);
} catch (error) {
console.error(error);
}
};
setInterval(sendLocationToServer, 30000)
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', position : 'absolute', backgroundColor : '' }}>
<View style={styles.spot}>
<ImageBackground blurRadius={20}
style={{
position: 'absolute',
width: "120%",
height: "120%",
justifyContent: "center",
alignItems: "center",
opacity: 0.28
}}
source={{
uri:currentCard.sourceUrl ,
}}
></ImageBackground>
{cards.map((card, index) => (
<View key={card.name}>
<View key={card.name} style = {{ position:'absolute'
}} >
<Pressable onLongPress={hapti}>
<Card
title={card.name}
image={card.sourceUrl}
onSwipe={(direction) => onSwipe(index, direction)}
/>
</Pressable>
{/* <Button
title="Success"
onPress={
() =>
Haptics.notificationAsync(
Haptics.NotificationFeedbackType.Success
)
}
></Button> */}
<FladButton name="discovery"/>
</View>
))}
</View>
// <View style={styles.container}>
// <Text>Open up App.tsx to start working on your app!</Text>
// {/* <View>
// <Animated.View>
{/* <LinearGradient
// Background Linear Gradient
colors={['rgba(0,0,0,0.8)', 'transparent']}
// </Animated.View>
// {cardArray.map( ({index}) => currentCard < index && step + step && (
// <Card card={card} ></Card>
/> */}
// ) )}
</View>
// </View> */}
// <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
// <Card title="Swipe me left or right" />
// </View>
// <StatusBar style="auto" />
// </View>
);
};
};
const styles = StyleSheet.create({
spot : {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}
})
export default Spot;

@ -0,0 +1,33 @@
export class AuthentificationService {
constructor(private auth: Auth, private http: HttpClient) { }
name = "toto";
async register({ email, password }) {
try {
const user = await createUserWithEmailAndPassword(this.auth, email, password);
return user;
} catch (e) {
return null;
}
}
async login({ email, password }) {
// should for all method use a cloud function to creata user
try {
const user = await signInWithEmailAndPassword(this.auth, email, password);
return user;
} catch (e) {
return null;
}
}
logout() {
return signOut(this.auth);
}
}

@ -1,23 +1,25 @@
import axios from "axios";
export class SpotifyService {
private identification : ApiSpotifyIdentification;
public request : SpotifyRequest;
export default class SpotifyService {
private readonly API_URL = "http://localhost:8080/api/spotify/exchange";
constructor() {
constructor() {
}
}
// get id(){
// return this.identification;
// }
get identification{
}
async uploadName() {
}
async apiAuth(url : string) {
await this.identification.setCode(url);
this.request = ApiSpotifyRequests(await this.identification.createToken());
}
// async apiAuth(url : string) {
// await this.identification.setCode(url);
// // this.request = ApiSpotifyRequests(await this.identification.createToken());
// }
async getSpotifyCredentials() {
const res = await axios.get(this.API_URL)
// then verify error
const spotifyCredentials = res.data;
return spotifyCredentials
}
}

@ -0,0 +1,14 @@
// import { Linking } from 'react-native';
// export class SpotifyRequest {
// constructor() {
// }
// }

@ -0,0 +1,27 @@
import { AuthentificationService } from "../Auth/authentificationService.service";
export class UserService {
constructor(private auth: AuthentificationService) {}
getUserProfile() {
const user = this.auth.currentUser;
const userDocRef = doc(this.firestore, `User/${user.uid}`);
return docData(userDocRef);
}
async uploadName(name: string, email : string) {
const user = this.auth.currentUser;
try {
const userDocRef = doc(this.firestore, `User/${user.uid}`);
await setDoc(userDocRef, {
name,
email
});
return true;
} catch (e) {
return null;
}
}
}
Loading…
Cancel
Save