add spotify service
continuous-integration/drone/push Build is passing Details

pull/8/head
David D'ALMEIDA 2 years ago
parent 897cbaa6a3
commit e1fe02e63c

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

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

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

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

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

@ -34,6 +34,14 @@
}, },
"web": { "web": {
"favicon": "./assets/icons/favicon.png" "favicon": "./assets/icons/favicon.png"
} },
"plugins": [
[
"expo-location",
{
"locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location."
}
]
]
} }
} }

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

@ -1,19 +1,28 @@
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 = { const initialState = {
spot: [], spot: [] as Spot[],
favoriteMusic: [], favoriteMusic: [] as Music [],
userCurrentMusic : null
} }
const appReducer = (state = initialState, action : any) => { const appReducer = (state = initialState, action : any) => {
switch (action.type) { switch (action.type) {
case ADD_FAVORITE_MUSICS: case favoritesTypes.ADD_FAVORITE_MUSICS:
return {...state, favoriteMusic: state.favoriteMusic.push(action.payload)}; return {...state, favoriteMusic: state.favoriteMusic.push(action.payload)};
case REMOVE_FAVORITE_MUSICS: case favoritesTypes.REMOVE_FAVORITE_MUSICS:
return {...state, favoriteMusic: state.favoriteMusic}; return {...state, favoriteMusic: state.favoriteMusic};
case FETCH_SPOT: case spotTypes.FETCH_SPOT:
return {...state, spot: action.payload}; return {...state, spot: action.payload};
case FETCH_DISCOVERIES: case discoveriesTypes.FETCH_DISCOVERIES:
return;
case spotifyTypes.GET_USER_CURRENT_MUSIC:
return {...state, userCurrentMusic: action.payload};
default: default:
return state; return state;
} }

@ -1,7 +1,8 @@
import { User } from "../../Model/User";
import { userTypes } from "../types/userTypes"; import { userTypes } from "../types/userTypes";
const initialState = { const initialState = {
loading: false, loading: false,
user: {}, // for user object user: User, // for user object
userFladToken: null, // for storing the JWT userFladToken: null, // for storing the JWT
userSpotifyToken : null, userSpotifyToken : null,
error: null, error: null,
@ -13,28 +14,29 @@ const initialState = {
// just for the navigation and speciafly use // just for the navigation and speciafly use
// and // and
case userTypes.RESTORE_TOKEN: case userTypes.RESTORE_TOKEN:
console.log(state.loading, "((((((((((((((((((((((((((((((((((((userRducer))))))))))))))))))))))))))))))))))))");
console.log(state.userFladToken, "userRducer");
console.log(state.loading, "((((((((((((((((((((((((((((((((((((userRducer))))))))))))))))))))))))))))))))))))");
return { return {
...state, ...state,
userFladToken : action.playload, userFladToken : action.playload,
loading: true, loading: true,
// isLogedIn: true, // isLogedIn: true,
}; };
case userTypes.LOGIN: case userTypes.LOGIN:
console.log("++++++++++++++++++++++++++++++++++++++userRducer+++++++++++++++++++++++++++++3");
console.log(action.playload, "LOOGGIIINN");
console.log("++++++++++++++++++++++++++++++++++++++userRducer+++++++++++++++++++++++++++++3");
return { return {
...state, ...state,
user :action.payload, user :action.playload,
isLogedIn: true isLogedIn: true
}; };
case userTypes.SIGNUP: case userTypes.SIGNUP:
console.log("++++++++++++++++++++++++++++++++++++++userRducer+++++++++++++++++++++++++++++3");
console.log(action.playload, "LOOGGIIINN");
console.log("++++++++++++++++++++++++++++++++++++++userRducer+++++++++++++++++++++++++++++3");
return { return {
...state, ...state,
user :action.payload, user :action.playload,
isLogedIn: true isLogedIn: true
}; };
// case USER_SIGNUP: // case USER_SIGNUP:

@ -6,6 +6,8 @@ import { useEffect } from "react";
import { API_URL } from "../../fladConfig"; import { API_URL } from "../../fladConfig";
import { Credentials, CredentialsRegister, restoreToken, setLoginState } from "../actions/userActions"; import { Credentials, CredentialsRegister, restoreToken, setLoginState } from "../actions/userActions";
import * as SecureStore from 'expo-secure-store'; import * as SecureStore from 'expo-secure-store';
import { User } from "../../Model/User";
import { UserFactory } from "../../Model/factory/UserFactory";
const key = 'userToken'; const key = 'userToken';
@ -36,7 +38,7 @@ export const registerUser = ( resgisterCredential : CredentialsRegister) => {
"https://flad-api-production.up.railway.app/api/users", "https://flad-api-production.up.railway.app/api/users",
{headers} {headers}
) )
dispatch(setLoginState(resp.data.user) ); // our action is called here dispatch(setLoginState( UserFactory.JsonToModel(user.data) )); // our action is called here
// console.log(user.data); // console.log(user.data);
// dispatch(setLoginState(user.data) ); // our action is called here // dispatch(setLoginState(user.data) ); // our action is called here
} else { } else {
@ -90,7 +92,7 @@ return async dispatch => {
{headers} {headers}
) )
// dispatch(setLoginState(resp.data.user) ); // our action is called here // dispatch(setLoginState(resp.data.user) ); // our action is called here
console.log(user.data);
dispatch(setLoginState(user.data) ); // our action is called here dispatch(setLoginState(user.data) ); // our action is called here
} else { } else {
console.log('Login Failed', 'Username or Password is incorrect'); console.log('Login Failed', 'Username or Password is incorrect');

@ -1,7 +1,14 @@
//Define your action creators that will be responsible for asynchronous operations //Define your action creators that will be responsible for asynchronous operations
import axios from "axios";
import { API_URL } from "../../fladConfig"; 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 = { export type CreateSpotReqBody = {
id : string; id : string;
@ -10,7 +17,80 @@ export type CreateSpotReqBody = {
linkCover : string; linkCover : string;
user : string; user : string;
} }
export const getSpotList = (resuestHandler : SpotifyService) => {
//@ts-ignore
return async dispatch => {
try {
// let { status } = await Location.requestForegroundPermissionsAsync();
// if (status !== 'granted') {
// setErrorMsg('Permission to access location was denied');
// return;
// }
// let location = await Location.getCurrentPositionAsync({});
// setLocation(location);
// const actualUser = MyApp.controller.getIdDafl();
// const actualSong = MyApp.controller.getCurrentMusic().id;
// const current = await new Promise<Position>((resolve, reject) => {
// Geolocation.getCurrentPosition(resolve, reject);
// });
//@ts-ignore
var userToken : string = await SecureStore.getItemAsync(key);
const headers = {
'Authorization': 'Bearer ' + userToken};
const data = await axios.get(
"https://flad-api-production.up.railway.app/api/users/nextTo",
{headers}
)
if (data.data.token) {
const spotsData: { [userId: string]: string } = {};
for (const item of data.data) {
spotsData[item.user] = item.music;
}
const spots = await Promise.all(
Object.entries(spotsData).map(async ([userId, value]) => {
const completeMusic = await resuestHandler.getMusicById(value);
return new Spot(userId, completeMusic);
})
);
dispatch(setSpotList(spots)); // our action is called here
} else {
console.log('Login Failed', 'Username or Password is incorrect');
}
} catch (error) {
console.log('Error---------', error);
}
}
}
export const getCurrentUserMusic = (resuestHandler : SpotifyService)=> {
//@ts-ignore
return async dispatch => {
try {
//@ts-ignore
var currentTrackResponse = await resuestHandler.getUserCurrentMusic();
if (!currentTrackResponse){
const recentlyTrackResponse = await resuestHandler.getUserRecentlyPlayedMusic();
if(!recentlyTrackResponse){
throw new Error;
}else{
currentTrackResponse = recentlyTrackResponse;
}
}
const completeMusic = await resuestHandler.getMusicById(currentTrackResponse);
dispatch(setUserCurrentMusic(completeMusic));
}
catch (error) {
console.log('Error---------', error);
}
}
}
// export const getSpotList = () => { // export const getSpotList = () => {
// return async dispatch => { // return async dispatch => {
// try { // try {

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

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

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

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

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

@ -9,6 +9,8 @@ import { Buffer } from 'buffer';
import { Audio } from 'expo-av'; import { Audio } from 'expo-av';
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { State, TapGestureHandler } from "react-native-gesture-handler"; 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 { interface SpotProps {
spot: { name: string, sourceUrl: string, index : number }; spot: { name: string, sourceUrl: string, index : number };
@ -149,27 +151,32 @@ const SpotDetailsPage = ({ route }) => {
}; };
var id = '0cFS3AMF9Lhj3CNoFvwjvY' var id = '0cFS3AMF9Lhj3CNoFvwjvY'
const requestor = new RequestHandler()
const getCurrentTrack = async () => { const getCurrentTrack = async () => {
try { try {
var GetTrackOptions = { const opt : FetchRequest ={headers : Record}
method: 'GET', requestor.spotifyFetch(`tracks${id}`,)
url: 'https://api.spotify.com/v1/tracks/'+id,
// var GetTrackOptions = {
headers: { // method: 'GET',
'Authorization': 'Bearer ' + spotify, // url: 'https://api.spotify.com/v1/tracks/'+id,
'Content-Type' : 'application/json',
'market' : 'FR', // headers: {
}, // 'Authorization': 'Bearer ' + spotify,
json: true // 'Content-Type' : 'application/json',
}; // 'market' : 'FR',
const resp = await axios(GetTrackOptions) // },
console.log("============"); // json: true
console.log(resp.data.href); // };
console.log("================================"+resp.data.album.images[0].url+ "================================"); // const resp = await axios(GetTrackOptions)
var tmp = currentspot; // console.log("============");
// console.log(resp.data.href);
tmp.sourceUrl = resp.data.album.images[0].url; // console.log("================================"+resp.data.album.images[0].url+ "================================");
setCurrentspot(tmp); // var tmp = currentspot;
// tmp.sourceUrl = resp.data.album.images[0].url;
// setCurrentspot(tmp);
// await axios(authOptions).then(async (response) =>{ // await axios(authOptions).then(async (response) =>{
// console.log(response.data.item.preview_url); // console.log(response.data.item.preview_url);
// const id = response.data.item.id; // const id = response.data.item.id;

@ -1,4 +1,4 @@
import React, {Component} from 'react'; import React, {Component, useState} from 'react';
import { Animated, Image,StyleSheet, Text, View, FlatList, ScrollView, TouchableOpacity } from 'react-native'; import { Animated, Image,StyleSheet, Text, View, FlatList, ScrollView, TouchableOpacity } from 'react-native';
import CardMusic from '../components/CardMusic'; import CardMusic from '../components/CardMusic';
import normalize from '../components/Normalize'; import normalize from '../components/Normalize';
@ -18,6 +18,25 @@ export default function favoritePage() {
new Music("Blanka", "PNL", require("../assets/images/pnl.png")), new Music("Blanka", "PNL", require("../assets/images/pnl.png")),
new Music("Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png"), new Music("Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png"),
] ]
// to do
const [filteredDataSource, setFilteredDataSource] = useState<Music[]>([]);
const [search, setSearch] = useState('');
const searchMusic = (text: string) => {
if (text) {
const newData = MUSIC_LIST.filter(function (item: Music) {
const search = item.title
? item.title.toUpperCase() : ''.toUpperCase();
const textsearch = text.toUpperCase();
return search.indexOf(textsearch) > -1;
});
setFilteredDataSource(newData);
setSearch(text);
} else {
setFilteredDataSource([]);
setSearch(text);
}
};
return ( return (
<View style={styles.body}> <View style={styles.body}>
<View style={styles.titleContainer}> <View style={styles.titleContainer}>

@ -113,6 +113,25 @@ export default function Spot() {
}, []) }, [])
// function addWatchLater(props: Movie) {
// dispatch(addMovieToWatchLater(props));
// dispatch(removeMovieTrending(props));
// if (displayIndex == trendingMovies.length - 1) {
// setdisplayIndex(0);
// swiper.swipeLeft();
// }
// }
// function addFavourite(props: Movie) {
// dispatch(addMovieToFavourite(props));
// dispatch(removeMovieTrending(props));
// if (displayIndex == trendingMovies.length - 1) {
// setdisplayIndex(0);
// swiper.swipeLeft();
// }
// }
// const hapti = (() => { // const hapti = (() => {
// Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy) // Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)
// getValueFor(MY_SECURE_AUTH_STATE_KEY) // getValueFor(MY_SECURE_AUTH_STATE_KEY)

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

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

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