diff --git a/.drone.yml b/.drone.yml index 09b34be..cf2cae6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,6 +1,6 @@ kind: pipeline type: docker -name: FLAD +name: FLAD-CLI trigger: event: @@ -13,7 +13,35 @@ steps: - cd ./src/FLAD/ - npm install - npm run - + + - name: code-analysis + image: node:latest + environment: + SONAR_TOKEN: + from_secret: SONAR_TOKEN + settings: + sources: ./src/FLAD/ + commands: + - export SONAR_SCANNER_VERSION=4.7.0.2747 + - export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux + - curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip + - unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ + - export PATH=$SONAR_SCANNER_HOME/bin:$PATH + - export SONAR_SCANNER_OPTS="-server" + - sonar-scanner -D sonar.projectKey=FLAD -D sonar.sources=./src/FLAD -D sonar.host.url=https://codefirst.iut.uca.fr/sonar + depends_on: [ app-build ] + +--- + +kind: pipeline +type: docker +name: FLAD-API-MQTT + +trigger: + event: + - push + +steps: - name: docker-build-and-push image: plugins/docker settings: @@ -25,25 +53,54 @@ steps: from_secret: SECRET_REGISTRY_USERNAME password: from_secret: SECRET_REGISTRY_PASSWORD + depends_on: [ app-build ] - #container deployment - name: deploy-container image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest environment: IMAGENAME: hub.codefirst.iut.uca.fr/emre.kartal/flad:latest - CONTAINERNAME: flady-container + CONTAINERNAME: flad + CODEFIRST_CLIENTDRONE_ENV_PORT: 80 + CODEFIRST_CLIENTDRONE_ENV_MONGO_PASSWORD: + from_secret: MONGO_PASSWORD + CODEFIRST_CLIENTDRONE_ENV_CLIENT_ID_SPOTIFY: + from_secret: CLIENT_ID_SPOTIFY + CODEFIRST_CLIENTDRONE_ENV_CLIENT_SECRET_SPOTIFY: + from_secret: CLIENT_SECRET_SPOTIFY COMMAND: create OVERWRITE: true ADMINS: emrekartal,davidd_almeida, depends_on: [ docker-build-and-push ] + - name: docker-build-and-push-mqtt + image: plugins/docker + settings: + dockerfile: src/Mqtt/Dockerfile + context: src/Mqtt + registry: hub.codefirst.iut.uca.fr + repo: hub.codefirst.iut.uca.fr/david.d_almeida/flad + username: + from_secret: SECRET_REGISTRY_USERNAME_MQTT + password: + from_secret: SECRET_REGISTRY_PASSWORD_MQTT + + - name: deploy-container-mqtt + image: hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest + environment: + IMAGENAME: hub.codefirst.iut.uca.fr/david.d_almeida/flad:latest + CONTAINERNAME: mqtt + COMMAND: create + OVERWRITE: true + ADMINS: emrekartal,davidd_almeida + depends_on: [ docker-build-and-push-mqtt ] + - name: code-analysis image: node:latest environment: SONAR_TOKEN: - from_secret: SONAR_TOKEN + from_secret: SONAR_TOKEN_API settings: - sources: ./src/FLAD/ + sources: ./src/Api/ commands: - export SONAR_SCANNER_VERSION=4.7.0.2747 - export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux @@ -51,5 +108,4 @@ steps: - unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ - export PATH=$SONAR_SCANNER_HOME/bin:$PATH - export SONAR_SCANNER_OPTS="-server" - - sonar-scanner -D sonar.projectKey=FLAD -D sonar.sources=. -D sonar.host.url=https://codefirst.iut.uca.fr/sonar - depends_on: [ app-build ] \ No newline at end of file + - sonar-scanner -D sonar.projectKey=FLAD-API -D sonar.sources=./src/Api -D sonar.host.url=https://codefirst.iut.uca.fr/sonar \ No newline at end of file diff --git a/README.md b/README.md index 39a00ee..b97f7b7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ --- -  ![Redux](https://img.shields.io/badge/Redux-593D88?style=for-the-badge&logo=redux&logoColor=white)   ![Docker](https://img.shields.io/badge/Docker-2496ED.svg?style=for-the-badge&logo=Docker&logoColor=white)   ![React Native](https://img.shields.io/badge/React_Native-20232A?style=for-the-badge&logo=react&logoColor=61DAFB)   ![Spotify Api](https://img.shields.io/badge/Spotify-1ED760?&style=for-the-badge&logo=spotify&logoColor=white) @@ -34,9 +33,9 @@ La racine de notre gitlab est composée de deux dossiers essentiels au projet: -[**src**](src) : **Toute la partie codage de l'application mobile** (contient un dossier API pour l'API FLAD qui effectue les requêtes vers l'API SPOTIFY et la base de données, ainsi qu'un dossier FLAD qui contient toute la partie côté client de l'application) +[**src**](src) : **Ensemble du code pour l'application mobile et les services web** (Application React Native, API Express en TypeScript, et messagerie MQTT) -[**doc**](doc) : **Documentation de l'application** (contient les maquettes) +[**doc**](doc) : **Documentation de l'application** (Inclut des diagrammes, des maquettes et des images) ## Fonctionnement @@ -62,11 +61,11 @@ Pour la suite, il suffit seulement de vérifier que node.js est à jour et insta Maintenant vous pouvez à tout moment lancer l'application grâce à la commande : **npx expo start :sunglasses:**
-:information_source: *Cliquer sur la touche 'w' si vous voulez le visualiser sur un navigateur (ce que je ne conseille pas) ou installer l'application 'Expo go' de votre téléphone et scanner le QR code proposer pour le visualiser (à noter que l'ordinateur dans lequel il se voit lancer doit être dans le même réseau local que votre téléphone)* +:information_source: *N'oubliez pas d'installer 'Expo Go' depuis le store de votre téléphone.* -- ### Comment le lancer à partir de l'iut d'Aubière ? +- ### Comment le lancer à partir de l'IUT d'Aubière ? -Cela est un peu plus difficile mais faisable !!! +Cela est un peu plus difficile mais faisable !
Tout d'abord aller dans votre compte scratch : **cd home/scratch/compte** @@ -89,36 +88,37 @@ Et entrer la commande : **export NODE_OPTIONS=--openssl-legacy-provider** Maintenant vous pouvez à tout moment lancer l'application grâce à la commande : **npx expo start :sunglasses:**
-:information_source: *Cliquer sur la touche 'w' si vous voulez le visualiser sur un navigateur (ce que je ne conseille pas) ou installer l'application 'Expo go' de votre téléphone et scanner le QR code proposer pour le visualiser (à noter que l'ordinateur dans lequel il se voit lancer doit être dans le même réseau local que votre téléphone)* - ### Comment s'inscrire sur l'application ? -Tout d'abord, il faut fournir votre *adresse e-mail* et votre *nom Spotify* aux **techniciens de l'application** (voir plus bas). Ils s'occuperont de vous ajouter définitivement à l'application. Une fois que cela est fait, inscrivez-vous via la **page d'inscription** de l'application en cliquant d'abord sur le bouton 'lier mon compte': +Tout d'abord, il faut fournir votre *adresse e-mail* et votre *nom Spotify* aux **techniciens de l'application** (voir plus bas). Ils s'occuperont de vous ajouter définitivement à l'application. Une fois que cela est fait, inscrivez-vous via la **page d'inscription** de l'application :
- +
-Vous serez normalement redirigé sur la page Spotify où vous devrez vous connecter. Une fois connecté, entrez votre nom, votre adresse e-mail et votre mot de passe en tant qu'utilisateur FLAD (n'oubliez pas ces informations car vous en aurez besoin pour vous connecter). Ensuite, cliquez sur le bouton ```suivant``` et bienvenue sur l'application ! +Une fois sur la page, saisissez votre nom, votre adresse e-mail, et votre mot de passe en tant qu'utilisateur FLAD (n'oubliez pas ces informations, vous en aurez besoin pour vous connecter). Pour lier votre compte à Spotify, vous serez automatiquement redirigé vers la page de connexion Spotify. Entrez vos identifiants Spotify, puis cliquez sur le bouton ```Suivant``` et bienvenue sur l'application !" ## Visuel de l'Application
- - - - +
:information_source: Lorsque vous entrez dans notre application, la page d'accueil (**home**) vous permet de découvrir les musiques :notes: des utilisateurs autour de vous. Vous pouvez valider une musique soit en cliquant sur le bouton, soit en la glissant vers la droite :point_up_2:. Cette musique sera alors ajoutée à la page **favoris** :heart: et vous pourrez entamer une discussion avec l'utilisateur dans la page **chat** :speech_balloon:. +
+ +Pour accéder aux détails d'une musique, maintenez votre doigt appuyé sur un Spot ou rendez-vous sur la page des favoris. Vous pourrez écouter la musique :arrow_forward:, obtenir des informations sur l'artiste et la chanson, découvrir des musiques similaires, et même l'ajouter à votre playlist Spotify ou la partager. +
Dans la page **settings** ⚙️, vous avez accès à toutes vos informations ```Spotify```, que vous pouvez modifier à votre guise. Toutefois, ces modifications ne seront prises en compte que dans notre application. Vous pouvez également choisir le mode sombre (dark mode) dans les paramètres pour une expérience de navigation plus confortable.
+ ### Voici un petit récapitulatif
@@ -128,9 +128,9 @@ Dans la page **settings** ⚙️, vous avez accès à toutes vos informations `` - - - + + +
Button 3
Suprimer de la pile un spotAjout Discover (pour l'instant il permet d'ajouter des spot suplémentaires dans la pile pour que vous puissiez vous amusez si il n'y aucun utilisateur à coté de vous)Like pour ajouter au favorieSupprimer de la pile un spotAjout dans une playlist de votre compte Spotify (créée spécialement par l'application)Ajouter à vos favoris
@@ -151,7 +151,7 @@ La composition pour le projet se voit réaliser par deux élèves de l'IUT d'Aub
- + @@ -159,9 +159,11 @@ La composition pour le projet se voit réaliser par deux élèves de l'IUT d'Aub
-© PM2 (Projet inspiré par nos très chers développeurs de la Dafl Team (S.O les Dafl dev)) +© FladDev
-
+
+ +Licence Creative Commons -Licence Creative Commons
Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 4.0 International. +
\ No newline at end of file diff --git a/doc/Images/Banner_App.png b/doc/Images/Banner_App.png index 09b4cbb..739cd15 100644 Binary files a/doc/Images/Banner_App.png and b/doc/Images/Banner_App.png differ diff --git a/doc/Images/Icon.png b/doc/Images/Icon.png deleted file mode 100644 index cc0dad0..0000000 Binary files a/doc/Images/Icon.png and /dev/null differ diff --git a/doc/Images/Overview.png b/doc/Images/Overview.png new file mode 100644 index 0000000..a1ebb2e Binary files /dev/null and b/doc/Images/Overview.png differ diff --git a/doc/Images/Real_ConversationPage.png b/doc/Images/Real_ConversationPage.png deleted file mode 100644 index 119225c..0000000 Binary files a/doc/Images/Real_ConversationPage.png and /dev/null differ diff --git a/doc/Images/Real_FavoritePage.png b/doc/Images/Real_FavoritePage.png deleted file mode 100644 index f2d1905..0000000 Binary files a/doc/Images/Real_FavoritePage.png and /dev/null differ diff --git a/doc/Images/Real_HomePage.png b/doc/Images/Real_HomePage.png deleted file mode 100644 index 1cafd36..0000000 Binary files a/doc/Images/Real_HomePage.png and /dev/null differ diff --git a/doc/Images/Real_LoginPage.png b/doc/Images/Real_LoginPage.png deleted file mode 100644 index b5a8dfe..0000000 Binary files a/doc/Images/Real_LoginPage.png and /dev/null differ diff --git a/doc/Images/Real_RegisterPage.png b/doc/Images/Real_RegisterPage.png deleted file mode 100644 index 8fc8b80..0000000 Binary files a/doc/Images/Real_RegisterPage.png and /dev/null differ diff --git a/doc/Images/Real_SettingPage.png b/doc/Images/Real_SettingPage.png deleted file mode 100644 index 28edc31..0000000 Binary files a/doc/Images/Real_SettingPage.png and /dev/null differ diff --git a/doc/Mascot/FLADYPerfect.png b/doc/Mascot/FLADYPerfect.png deleted file mode 100644 index 4eaaec4..0000000 Binary files a/doc/Mascot/FLADYPerfect.png and /dev/null differ diff --git a/doc/Mascot/FLADYPerfectRed.png b/doc/Mascot/FLADYPerfectRed.png deleted file mode 100644 index 33b82eb..0000000 Binary files a/doc/Mascot/FLADYPerfectRed.png and /dev/null differ diff --git a/doc/Mascot/FLADYPerfectShadow.png b/doc/Mascot/FLADYPerfectShadow.png deleted file mode 100644 index d834196..0000000 Binary files a/doc/Mascot/FLADYPerfectShadow.png and /dev/null differ diff --git a/doc/Mascot/Flady.png b/doc/Mascot/Flady.png new file mode 100644 index 0000000..bd520c2 Binary files /dev/null and b/doc/Mascot/Flady.png differ diff --git a/doc/Mascot/RedFlady.png b/doc/Mascot/RedFlady.png new file mode 100644 index 0000000..037e4f1 Binary files /dev/null and b/doc/Mascot/RedFlady.png differ diff --git a/src/.expo/README.md b/src/.expo/README.md deleted file mode 100644 index fd146b4..0000000 --- a/src/.expo/README.md +++ /dev/null @@ -1,15 +0,0 @@ -> Why do I have a folder named ".expo" in my project? - -The ".expo" folder is created when an Expo project is started using "expo start" command. - -> What do the files contain? - -- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds. -- "packager-info.json": contains port numbers and process PIDs that are used to serve the application to the mobile device/simulator. -- "settings.json": contains the server configuration that is used to serve the application manifest. - -> Should I commit the ".expo" folder? - -No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine. - -Upon project creation, the ".expo" folder is already added to your ".gitignore" file. diff --git a/src/.expo/settings.json b/src/.expo/settings.json deleted file mode 100644 index 92bc513..0000000 --- a/src/.expo/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "hostType": "lan", - "lanType": "ip", - "dev": true, - "minify": false, - "urlRandomness": null, - "https": false -} diff --git a/src/Api/.dockerignore b/src/Api/.dockerignore deleted file mode 100644 index e69de29..0000000 diff --git a/src/Api/.env b/src/Api/.env deleted file mode 100644 index c88bfa8..0000000 --- a/src/Api/.env +++ /dev/null @@ -1 +0,0 @@ -PORT=8080 \ No newline at end of file diff --git a/src/Api/.gitignore b/src/Api/.gitignore index a179c40..47537b1 100644 --- a/src/Api/.gitignore +++ b/src/Api/.gitignore @@ -21,6 +21,7 @@ DerivedData *.xcuserstate project.xcworkspace **/.xcode.env.local +**/src/config.ts # Gradle /build/ @@ -72,6 +73,8 @@ node_modules *.log .nvm package-lock.json +dist +**/.env # OS X .DS_Store diff --git a/src/Api/Dockerfile b/src/Api/Dockerfile index 5eadece..d849896 100644 --- a/src/Api/Dockerfile +++ b/src/Api/Dockerfile @@ -1,10 +1,8 @@ FROM node:latest WORKDIR /Api -ADD package.json /Api -ADD tsconfig.json /Api -ADD . /Api -RUN npm install -RUN npm run build -EXPOSE 8080 -CMD [ "node","." ] - +COPY package.json /Api +COPY tsconfig.json /Api +COPY . /Api +RUN npm install && npm run build +EXPOSE 80 +CMD ["node", "."] \ No newline at end of file diff --git a/src/Api/dist/app.js b/src/Api/dist/app.js deleted file mode 100644 index adae88d..0000000 --- a/src/Api/dist/app.js +++ /dev/null @@ -1,62 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_1 = __importDefault(require("express")); -// import compression from 'compression'; -const cors_1 = __importDefault(require("cors")); -// import ErrorMiddleware from './middleware/error.middleware'; -const body_parser_1 = __importDefault(require("body-parser")); -const mongoose_1 = __importDefault(require("mongoose")); -const cookie_parser_1 = __importDefault(require("cookie-parser")); -class App { - constructor(controllers, port) { - this.express = (0, express_1.default)(); - this.port = port; - this.dataBase = null; - this.initialiseDatabase(); - this.initialiseMiddleware(); - this.initialiseControllers(controllers); - // this.initialiseErrorHandling(); - } - initialiseMiddleware() { - // this.express.use(helmet()); - this.express.use((0, cors_1.default)()); - this.express.use((0, cookie_parser_1.default)()); - // this.express.use(morgan('dev')); - this.express.use(express_1.default.json()); - this.express.use(express_1.default.urlencoded({ extended: false })); - // this.express.use(compression()); - // mine - this.express.use(body_parser_1.default.json()); - this.express.use(body_parser_1.default.urlencoded({ - extended: true - })); - } - initialiseControllers(controllers) { - controllers.forEach((controller) => { - this.express.use('/api', controller.router); - this.express.get('/toto', (req, res) => { - res.send('Hello World!'); - }); - }); - } - // private initialiseErrorHandling(): void { - // this.express.use(ErrorMiddleware); - // } - listen() { - const server = this.express.listen(this.port, () => { - console.log(`⚡️[server] : App listening on the port ${this.port}`); - }); - } - initialiseDatabase() { - const { MONGO_USER, MONGO_PASSWORD, MONGO_PATH } = process.env; - const uri = "mongodb+srv://fladDevDb:ZslYlNRWIOUU7i6o@fladcluster.b29tytu.mongodb.net/?retryWrites=true&w=majority"; - mongoose_1.default.connect(uri) - .then(() => console.log("Connect to MongoDB database successfully")) - .catch(err => console.log("Error connecting : " + err)); - } -} -exports.default = App; -//# sourceMappingURL=app.js.map \ No newline at end of file diff --git a/src/Api/dist/app.js.map b/src/Api/dist/app.js.map deleted file mode 100644 index 0497168..0000000 --- a/src/Api/dist/app.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":";;;;;AAAA,sDAA+C;AAC/C,yCAAyC;AACzC,gDAAwB;AAIxB,+DAA+D;AAC/D,8DAAqC;AACrC,wDAAgC;AAMhC,kEAAyC;AAEzC,MAAM,GAAG;IAOL,YAAY,WAAyB,EAAE,IAAY;QAC/C,IAAI,CAAC,OAAO,GAAG,IAAA,iBAAO,GAAE,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,IAAI,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAExC,kCAAkC;IACtC,CAAC;IAEO,oBAAoB;QACxB,8BAA8B;QAC9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAA,cAAI,GAAE,CAAC,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAA,uBAAY,GAAE,CAAC,CAAC;QAGjC,mCAAmC;QACnC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAC1D,mCAAmC;QACnC,OAAO;QACP,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAU,CAAC,UAAU,CAAC;YACnC,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC,CAAC;IAEV,CAAC;IAEO,qBAAqB,CAAC,WAAyB;QACnD,WAAW,CAAC,OAAO,CAAC,CAAC,UAAsB,EAAE,EAAE;YAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBACnC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC3B,CAAC,CAAC,CAAA;QACR,CAAC,CAAC,CAAC;IACP,CAAC;IAED,4CAA4C;IAC5C,yCAAyC;IACzC,IAAI;IAEG,MAAM;QACT,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE;YAC/C,OAAO,CAAC,GAAG,CAAC,0CAA0C,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,kBAAkB;QACtB,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;QAC/D,MAAM,GAAG,GAAG,uGAAuG,CAAA;QACnH,kBAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;aACpB,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;aACnE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAE,GAAG,CAAE,CAAC,CAAC;IAC5D,CAAC;CAEJ;AAED,kBAAe,GAAG,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/controller/Icontroller.js b/src/Api/dist/controller/Icontroller.js deleted file mode 100644 index b804bba..0000000 --- a/src/Api/dist/controller/Icontroller.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=Icontroller.js.map \ No newline at end of file diff --git a/src/Api/dist/controller/Icontroller.js.map b/src/Api/dist/controller/Icontroller.js.map deleted file mode 100644 index b57d665..0000000 --- a/src/Api/dist/controller/Icontroller.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Icontroller.js","sourceRoot":"","sources":["../../src/controller/Icontroller.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/Api/dist/controller/TestCtrl.js b/src/Api/dist/controller/TestCtrl.js deleted file mode 100644 index b239e96..0000000 --- a/src/Api/dist/controller/TestCtrl.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_1 = require("express"); -class PingController { - constructor() { - this.path = '/ping'; - this.router = (0, express_1.Router)(); - this.initialiseRoutes(); - } - initialiseRoutes() { - this.router.get("/ping", (_req, res) => __awaiter(this, void 0, void 0, function* () { - const response = yield this.getMessage(); - return res.send(response); - })); - } - getMessage() { - return __awaiter(this, void 0, void 0, function* () { - return { - message: "pong", - }; - }); - } -} -exports.default = PingController; -//# sourceMappingURL=TestCtrl.js.map \ No newline at end of file diff --git a/src/Api/dist/controller/TestCtrl.js.map b/src/Api/dist/controller/TestCtrl.js.map deleted file mode 100644 index 782b7d8..0000000 --- a/src/Api/dist/controller/TestCtrl.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TestCtrl.js","sourceRoot":"","sources":["../../src/controller/TestCtrl.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,qCAAiC;AAOjC,MAAqB,cAAc;IAI/B;QAHO,SAAI,GAAG,OAAO,CAAC;QACf,WAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;QAGrB,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC5B,CAAC;IAEO,gBAAgB;QACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAO,IAAI,EAAE,GAAG,EAAE,EAAE;YACzC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACzC,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC,CAAA,CAAC,CAAC;IACT,CAAC;IACK,UAAU;;YACZ,OAAO;gBACP,OAAO,EAAE,MAAM;aACd,CAAC;QACN,CAAC;KAAA;CACJ;AAnBD,iCAmBC"} \ No newline at end of file diff --git a/src/Api/dist/controller/spotify-controller/crypt.js b/src/Api/dist/controller/spotify-controller/crypt.js deleted file mode 100644 index 2fdf1dc..0000000 --- a/src/Api/dist/controller/spotify-controller/crypt.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -class CryptString { - constructor(length) { - this.stringCrypt = this.generateRandomString(length); - } - generateRandomString(length) { - var text = ''; - var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (var i = 0; i < length; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; - } -} -exports.default = CryptString; -//# sourceMappingURL=crypt.js.map \ No newline at end of file diff --git a/src/Api/dist/controller/spotify-controller/crypt.js.map b/src/Api/dist/controller/spotify-controller/crypt.js.map deleted file mode 100644 index 76c08d1..0000000 --- a/src/Api/dist/controller/spotify-controller/crypt.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"crypt.js","sourceRoot":"","sources":["../../../src/controller/spotify-controller/crypt.ts"],"names":[],"mappings":";;AAAA,MAAqB,WAAW;IAI5B,YAAY,MAAe;QACvB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IACD,oBAAoB,CAAE,MAAe;QACjC,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,IAAI,QAAQ,GAAG,gEAAgE,CAAC;QAEhF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE;YAC/B,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;SACtE;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ;AAhBD,8BAgBC"} \ No newline at end of file diff --git a/src/Api/dist/controller/spotify-controller/request/authReqBody.js b/src/Api/dist/controller/spotify-controller/request/authReqBody.js deleted file mode 100644 index e9ba136..0000000 --- a/src/Api/dist/controller/spotify-controller/request/authReqBody.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=authReqBody.js.map \ No newline at end of file diff --git a/src/Api/dist/controller/spotify-controller/request/authReqBody.js.map b/src/Api/dist/controller/spotify-controller/request/authReqBody.js.map deleted file mode 100644 index 7e5a006..0000000 --- a/src/Api/dist/controller/spotify-controller/request/authReqBody.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authReqBody.js","sourceRoot":"","sources":["../../../../src/controller/spotify-controller/request/authReqBody.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/Api/dist/controller/spotify-controller/spotifyCtrl.js b/src/Api/dist/controller/spotify-controller/spotifyCtrl.js deleted file mode 100644 index 2b4b056..0000000 --- a/src/Api/dist/controller/spotify-controller/spotifyCtrl.js +++ /dev/null @@ -1,218 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_1 = require("express"); -const httpExeption_1 = __importDefault(require("../../middleware/exeption/httpExeption")); -const axios_1 = __importDefault(require("axios")); -const crypt_1 = __importDefault(require("./crypt")); -const qs_1 = __importDefault(require("qs")); -class SpotifyController { - constructor() { - this.path = '/spotify'; - this.router = (0, express_1.Router)(); - // need to put in ENvironement file - // private readonly CLIENT_CALLBACK_URL = "http://localhost:8080/callback"; - this.API_URL = "https://accounts.spotify.com/api/token"; - this.CLIENT_ID = "1f1e34e4b6ba48b388469dba80202b10"; - this.CLIENT_SECRET = "779371c6d4994a68b8dd6e84b0873c82"; - // private readonly CLIENT_CALLBACK_URL = "https://auth.expo.io/@thed47/FLAD//callback"; - this.CALLBACK_2 = 'https://flad-api-production.up.railway.app/api/spotify/callback'; - this.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'; - this.ENCRYPTION_SECRET = new crypt_1.default(16); - this.clientRedirect = 'spotify_final_redirect-uri-key'; - this.login = (req, res, next) => __awaiter(this, void 0, void 0, function* () { - console.log("useeeee== login"); - 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_2, - // // code: params.code - // }) - const redirectResponse = req.query.redirectUrl ? req.query.redirectUrl : req.headers.referer; - res.cookie(this.clientRedirect, redirectResponse); - console.log("aloorrr si c'est niquuuuuuuuuuuueeee" + this.CALLBACK_2 + "gennnnnnnnnrree vraiiiiiiiment "); - res.redirect('https://accounts.spotify.com/authorize?' + - qs_1.default.stringify({ - response_type: 'code', - client_id: this.CLIENT_ID, - scope: this.SCOPES, - redirect_uri: this.CALLBACK_2, - // state: this.ENCRYPTION_SECRET.stringCrypt - })); - // '?response_type=code' + - // '&client_id=' + - // "1f1e34e4b6ba48b388469dba80202b10" + - // (this.SCOPES ? '&scope=' + encodeURIComponent(this.SCOPES) : '') + - // '&redirect_uri=' + - // encodeURIComponent(this.CALLBACK_2) - // ); - // .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 httpExeption_1.default(400, 'Cannot create spot')); - } - }); - this.getRefreshToken = (req, res, next) => __awaiter(this, void 0, void 0, function* () { - 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_1.default.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 - // }); - // } - // }); - (0, axios_1.default)(authOptions) - .then(session => { - if (session.status === 200) { - console.log('### Information : responce ###' + JSON.stringify(session.data)); - console.log('### Information : refresh_token ###' + session.data.refresh_token); - res.send({ - "access_token": session.data.access_token, - "refresh_token": session.data.refresh_token, - "expires_in": session.data.expires_in - }); - } - }); - console.log("goood"); - } - catch (error) { - console.log("errur"); - next(new httpExeption_1.default(400, 'Cannot create post')); - } - }); - this.getSpot = (req, res, next) => __awaiter(this, void 0, void 0, function* () { - const spots = [ - { - name: "blue", - sourceUrl: "https://cdns-images.dzcdn.net/images/artist/399e7e760d8fedf3cc2891e9c0c41658/200x200-000000-80-0-0.jpg", - index: 3 - }, - { - name: "strange history", - sourceUrl: "https://images.genius.com/339dfe2a7c0adf9a5d08febf29a845f4.1000x1000x1.jpg", - index: 7 - }, - { - name: "oboy album", - sourceUrl: "https://i.pinimg.com/originals/ad/cc/d5/adccd58a0d0ff516a6114703cd05810e.jpg", - index: 1 - } - ]; - try { - res.send(spots); - } - catch (error) { - console.log('heuuuuuuuuuuuuuuuuuuuuubizzzaaarrreeee'); - console.log(error); - next(new httpExeption_1.default(400, 'On peut pas avoir darray mec')); - } - }); - this.getAccessToken = (req, res, next) => __awaiter(this, void 0, void 0, function* () { - console.log("useeeee== accesToken"); - var code = req.query.code; - var state = req.query.state || null; - var storedredirectUri = req.cookies ? req.cookies[this.clientRedirect] : null; - // var storedState = req.cookies ? req.cookies[stateKey] : null; - var authOptions = { - method: 'POST', - url: 'https://accounts.spotify.com/api/token', - data: qs_1.default.stringify({ - code: code, - redirect_uri: this.CALLBACK_2, - 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 { - var resp = yield (0, axios_1.default)(authOptions); - if (resp.status === 200) { - console.log('oon esttt laaa'); - var access_token = resp.data.access_token; - var expiration = resp.data.expires_in; - var refresh = resp.data.refresh_token; - console.log(access_token); - // res.send({ - // "access_token": access_token, - // "expires_in": expiration, - // "refresh" : refresh - // }); - res.clearCookie(this.clientRedirect); - res.redirect(`${storedredirectUri}?` + - qs_1.default.stringify({ - "access_token": access_token, - "expires_in": expiration, - "refresh_token": refresh - })); - } - } - catch (error) { - console.log('heuuuuuuuuuuuuuuuuuuuuubizzzaaarrreeee'); - console.log(error); - next(new httpExeption_1.default(400, 'On peut pas te connecter mec' + error.message)); - } - }); - 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.get(`${this.path}/refresh`, this.getRefreshToken); - this.router.get(`${this.path}/spot`, this.getSpot); - } -} -exports.default = SpotifyController; -//# sourceMappingURL=spotifyCtrl.js.map \ No newline at end of file diff --git a/src/Api/dist/controller/spotify-controller/spotifyCtrl.js.map b/src/Api/dist/controller/spotify-controller/spotifyCtrl.js.map deleted file mode 100644 index 362de6f..0000000 --- a/src/Api/dist/controller/spotify-controller/spotifyCtrl.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"spotifyCtrl.js","sourceRoot":"","sources":["../../../src/controller/spotify-controller/spotifyCtrl.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AACA,qCAAkF;AAElF,0FAAmE;AACnE,kDAA0B;AAC1B,oDAAkC;AAGlC,4CAAoB;AAGpB,MAAM,iBAAiB;IAInB;QAHO,SAAI,GAAG,UAAU,CAAC;QAClB,WAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;QAgBvB,mCAAmC;QACrC,2EAA2E;QACzD,YAAO,GAAG,wCAAwC,CAAC;QACnD,cAAS,GAAG,kCAAkC,CAAC;QAC/C,kBAAa,GAAG,kCAAkC,CAAC;QACrE,yFAAyF;QACvE,eAAU,GAAG,iEAAiE,CAAC;QAC/E,WAAM,GAAE,qLAAqL,CAAC;QAC9L,sBAAiB,GAAG,IAAI,eAAW,CAAC,EAAE,CAAC,CAAC;QACxC,mBAAc,GAAE,gCAAgC,CAAC;QAE3D,UAAK,GAAG,CACZ,GAAY,EACZ,GAAa,EACb,IAAkB,EACM,EAAE;YAE5B,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;YAC7B,IAAI;gBACA,2BAA2B;gBAC3B,+BAA+B;gBAC/B,sBAAsB;gBACtB,mCAAmC;gBACnC,QAAQ;gBACR,IAAI;gBAEJ,wBAAwB;gBACxB,sCAAsC;gBACtC,0CAA0C;gBAC1C,yBAAyB;gBACzB,KAAK;gBACL,MAAM,gBAAgB,GAAI,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC9F,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,sCAAsC,GAAE,IAAI,CAAC,UAAU,GAAE,iCAAiC,CAAC,CAAC;gBACxG,GAAG,CAAC,QAAQ,CAAC,yCAAyC;oBACtD,YAAE,CAAC,SAAS,CAAC;wBACX,aAAa,EAAE,MAAM;wBACrB,SAAS,EAAE,IAAI,CAAC,SAAS;wBACzB,KAAK,EAAE,IAAI,CAAC,MAAM;wBAClB,YAAY,EAAE,IAAI,CAAC,UAAU;wBAC7B,4CAA4C;qBAC7C,CAAC,CAAC,CAAC;gBAEJ,0BAA0B;gBAC1B,kBAAkB;gBAClB,uCAAuC;gBACvC,qEAAqE;gBACrE,qBAAqB;gBACrB,sCAAsC;gBACtC,KAAK;gBACL,qBAAqB;gBACrB,mBAAmB;gBACnB,4CAA4C;gBAC5C,wCAAwC;gBACxC,2DAA2D;gBAC3D,OAAO;gBACP,6BAA6B;gBAC7B,KAAK;gBACL,uBAAuB;gBACvB,+BAA+B;gBAC/B,MAAM;aACT;YAAC,OAAO,KAAK,EAAE;gBACZ,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC;aACtD;QAGL,CAAC,CAAA,CAAC;QAEM,oBAAe,GAAG,CACtB,GAAY,EACZ,GAAa,EACb,IAAkB,EACM,EAAE;YAE5B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAEnB,IAAI;gBACA,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC;gBAEvC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE;oBAC5B,OAAO,GAAG,CAAC,IAAI,CAAC;wBACd,OAAO,EAAE,iCAAiC;qBAC3C,CAAC,CAAC;iBACJ;gBACD,IAAI,WAAW,GAAG;oBAChB,MAAM,EAAE,MAAM;oBACd,GAAG,EAAE,wCAAwC;oBAC7C,IAAI,EAAE,YAAE,CAAC,SAAS,CAAC;wBACjB,UAAU,EAAE,eAAe;wBAC3B,aAAa,EAAE,MAAM;qBACtB,CAAC;oBACF,OAAO,EAAE;wBACP,eAAe,EAAE,QAAQ,GAAG,CAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;wBACxG,cAAc,EAAG,mCAAmC;qBACrD;oBACD,IAAI,EAAE,IAAI;iBACX,CAAC;gBAEF,8DAA8D;gBAC9D,iDAAiD;gBACjD,4CAA4C;gBAC5C,iBAAiB;gBACjB,qCAAqC;gBACrC,UAAU;gBACV,MAAM;gBACN,MAAM;gBACN,IAAA,eAAK,EAAC,WAAW,CAAC;qBACjB,IAAI,CAAC,OAAO,CAAC,EAAE;oBACd,IAAG,OAAO,CAAC,MAAM,KAAK,GAAG,EAAC;wBACxB,OAAO,CAAC,GAAG,CAAC,gCAAgC,GAAG,IAAI,CAAC,SAAS,CAAE,OAAO,CAAC,IAAI,CAAC,CAAE,CAAC;wBAC/E,OAAO,CAAC,GAAG,CAAC,qCAAqC,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;wBAEhF,GAAG,CAAC,IAAI,CAAC;4BACL,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY;4BACzC,eAAe,EAAE,OAAO,CAAC,IAAI,CAAC,aAAa;4BAC3C,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU;yBACxC,CAAC,CAAC;qBACJ;gBAAA,CAAC,CAAC,CAAC;gBACJ,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;aAClB;YAAC,OAAO,KAAK,EAAE;gBACtB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACnB,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC;aACtD;QAEH,CAAC,CAAA,CAAA;QAII,YAAO,GAAG,CACf,GAAY,EACZ,GAAa,EACb,IAAkB,EACM,EAAE;YAC5B,MAAM,KAAK,GAAG;gBACZ;oBACE,IAAI,EAAE,MAAM;oBACZ,SAAS,EAAE,wGAAwG;oBACnH,KAAK,EAAE,CAAC;iBACT;gBACD;oBACE,IAAI,EAAE,iBAAiB;oBACvB,SAAS,EAAE,4EAA4E;oBACvF,KAAK,EAAE,CAAC;iBACT;gBACD;oBACE,IAAI,EAAE,YAAY;oBAClB,SAAS,EAAE,8EAA8E;oBACzF,KAAK,EAAE,CAAC;iBACT;aACF,CAAC;YACF,IAAI;gBACF,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aAEjB;YAAC,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;gBACtD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACnB,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,8BAA8B,CAAC,CAAC,CAAC;aAC9D;QAAI,CAAC,CAAA,CAAA;QAGE,mBAAc,GAAG,CACvB,GAAY,EACZ,GAAa,EACb,IAAkB,EACM,EAAE;YAC5B,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;YAEpC,IAAI,IAAI,GAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAC3B,IAAI,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC;YACpC,IAAI,iBAAiB,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAE9E,gEAAgE;YAChE,IAAI,WAAW,GAAG;gBAChB,MAAM,EAAE,MAAM;gBACd,GAAG,EAAE,wCAAwC;gBAC7C,IAAI,EAAE,YAAE,CAAC,SAAS,CAAC;oBACjB,IAAI,EAAE,IAAI;oBACV,YAAY,EAAE,IAAI,CAAC,UAAU;oBAC7B,UAAU,EAAE,oBAAoB;iBACjC,CAAC;gBACF,OAAO,EAAE;oBACP,eAAe,EAAE,QAAQ,GAAG,CAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;oBACxG,cAAc,EAAG,mCAAmC;iBACrD;gBACD,IAAI,EAAE,IAAI;aACX,CAAC;YACF,IAAI;gBACJ,IAAI,IAAI,GAAG,MAAM,IAAA,eAAK,EAAC,WAAW,CAAC,CAAC;gBACpC,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE;oBACvB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;oBAC9B,IAAI,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC;oBAC1C,IAAI,UAAU,GAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;oBACrC,IAAI,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAA;oBACrC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;oBAC5B,eAAe;oBACf,oCAAoC;oBACpC,gCAAgC;oBAChC,0BAA0B;oBAC1B,MAAM;oBAEJ,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;oBACvC,GAAG,CAAC,QAAQ,CAAC,GAAG,iBAAiB,GAAG;wBACpC,YAAE,CAAC,SAAS,CAAC;4BACX,cAAc,EAAE,YAAY;4BAC5B,YAAY,EAAE,UAAU;4BACxB,eAAe,EAAG,OAAO;yBAC1B,CAAC,CAAC,CAAC;iBACH;aACA;YAAC,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;gBACtD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACnB,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,8BAA8B,GAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;aAC7E;QAIH,CAAC,CAAA,CAAC;QArOE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAErB,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC5B,CAAC;IACD,gBAAgB;QACZ,oDAAoD;QACpD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,WAAW,EAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,WAAW,EAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,UAAU,EAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAEvD,CAAC;CA6NJ;AACD,kBAAe,iBAAiB,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/controller/user-controller/userCtrl.js b/src/Api/dist/controller/user-controller/userCtrl.js deleted file mode 100644 index 9a94ef4..0000000 --- a/src/Api/dist/controller/user-controller/userCtrl.js +++ /dev/null @@ -1,193 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const express_1 = require("express"); -const httpExeption_1 = __importDefault(require("../../middleware/exeption/httpExeption")); -const UserService_1 = __importDefault(require("../../service/UserService")); -const UserValidation_1 = __importDefault(require("../../database/schema/User/UserValidation")); -const ValidatorMiddleware_1 = __importDefault(require("../../middleware/validation/ValidatorMiddleware")); -const authMiddleware_1 = __importDefault(require("../../middleware/authMiddleware")); -const LocationService_1 = __importDefault(require("../../service/LocationService")); -class UserController { - constructor() { - this.path = '/users'; - this.router = (0, express_1.Router)(); - this.userService = new UserService_1.default(); - this.locationService = new LocationService_1.default(); - // private createUser = async ( - // req: Request, - // res: Response, - // next: NextFunction - // ): Promise => { - // 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 => { - // 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 => { - // 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 => { - // 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 => { - // 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')); - // } - // }; - this.register = (req, res, next) => __awaiter(this, void 0, void 0, function* () { - try { - // the FladId should be created by the Userservice - const { name, email, password, idFlad, idSpotify } = req.body; - console.log(name, email, password, idFlad, idSpotify); - const token = yield this.userService.register(name, email, password, idFlad, idSpotify); - res.status(201).json({ token }); - } - catch (error) { - next(new httpExeption_1.default(400, error.message)); - } - }); - this.login = (req, res, next) => __awaiter(this, void 0, void 0, function* () { - try { - const { email, password } = req.body; - const token = yield this.userService.login(email, password); - res.status(200).json({ token }); - } - catch (error) { - next(new httpExeption_1.default(400, error.message)); - } - }); - this.getUser = (req, res, next) => { - if (!req.user) { - return next(new httpExeption_1.default(404, 'No logged in user')); - } - res.status(200).send({ data: req.user }); - }; - this.getUserNext = (req, res, next) => __awaiter(this, void 0, void 0, function* () { - try { - const longitude = Number(req.query.longitude); - const latitude = Number(req.query.latitude); - //verify::val_int(){ - console.log('woooooooooooooo' + req); - if (isNaN(longitude) || isNaN(latitude)) { - console.log('============' + longitude); - console.log('============' + latitude); - console.log('Impossible de convertir la chaîne en nombre'); - } - //} - const userId = req.user.idFlad; - const musicId = String(req.query.currentMusic); - console.log('============' + longitude); - console.log('============' + latitude); - console.log('daaaaaaaaaaaaaaaaaaaaaa' + musicId); - const data = yield this.locationService.getNearUser(userId, musicId, latitude, longitude); - console.log(data); - res.status(201).send(data); - } - catch (error) { - next(new httpExeption_1.default(400, 'Cannot create get netUser')); - } - }); - this.initialiseRoutes(); - } - initialiseRoutes() { - this.router.post(`${this.path}/register`, (0, ValidatorMiddleware_1.default)(UserValidation_1.default.register), this.register); - this.router.post(`${this.path}/login`, (0, ValidatorMiddleware_1.default)(UserValidation_1.default.login), this.login); - this.router.get(`${this.path}`, authMiddleware_1.default, this.getUser); - this.router.get(`${this.path}/nextTo`, authMiddleware_1.default, this.getUserNext); - // //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); - } -} -exports.default = UserController; -//# sourceMappingURL=userCtrl.js.map \ No newline at end of file diff --git a/src/Api/dist/controller/user-controller/userCtrl.js.map b/src/Api/dist/controller/user-controller/userCtrl.js.map deleted file mode 100644 index 5b9434b..0000000 --- a/src/Api/dist/controller/user-controller/userCtrl.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"userCtrl.js","sourceRoot":"","sources":["../../../src/controller/user-controller/userCtrl.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,qCAAkF;AAElF,0FAAmE;AAGnE,4EAAoD;AACpD,+FAAiE;AACjE,0GAAmF;AACnF,qFAA2D;AAC3D,oFAA4D;AAC5D,MAAM,cAAc;IAMhB;QALO,SAAI,GAAG,QAAQ,CAAC;QAChB,WAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;QACjB,gBAAW,GAAG,IAAI,qBAAW,EAAE,CAAC;QAChC,oBAAe,GAAG,IAAI,yBAAe,EAAE,CAAC;QAmChD,+BAA+B;QAC/B,oBAAoB;QACpB,qBAAqB;QACrB,yBAAyB;QACzB,mCAAmC;QACnC,YAAY;QAEZ,iCAAiC;QACjC,yEAAyE;QACzE,oDAAoD;QACpD,4DAA4D;QAC5D,aAAa;QAEb,2EAA2E;QAG3E,wBAAwB;QACxB,8DAA8D;QAC9D,QAAQ;QACR,KAAK;QACL,yDAAyD;QACzD,oBAAoB;QACpB,qBAAqB;QACrB,yBAAyB;QACzB,mCAAmC;QACnC,YAAY;QACZ,wCAAwC;QACxC,4CAA4C;QAE5C,uEAAuE;QACvE,sCAAsC;QAEtC,QAAQ;QACR,oBAAoB;QACpB,8DAA8D;QAC9D,QAAQ;QAER,IAAI;QACJ,yDAAyD;QACzD,oBAAoB;QACpB,qBAAqB;QACrB,yBAAyB;QACzB,mCAAmC;QACnC,YAAY;QACZ,4CAA4C;QAC5C,iEAAiE;QACjE,8EAA8E;QAC9E,2DAA2D;QAE3D,QAAQ;QACR,oBAAoB;QACpB,gEAAgE;QAChE,QAAQ;QAER,IAAI;QAEJ,+BAA+B;QAC/B,oBAAoB;QACpB,qBAAqB;QACrB,yBAAyB;QACzB,mCAAmC;QACnC,YAAY;QACZ,wCAAwC;QACxC,4CAA4C;QAC5C,yDAAyD;QACzD,mFAAmF;QACnF,wBAAwB;QACxB,8DAA8D;QAC9D,QAAQ;QACR,KAAK;QAEL,+BAA+B;QAC/B,oBAAoB;QACpB,qBAAqB;QACrB,yBAAyB;QACzB,mCAAmC;QACnC,YAAY;QAEZ,4CAA4C;QAC5C,4CAA4C;QAC5C,yFAAyF;QAEzF,iEAAiE;QACjE,gCAAgC;QAChC,sBAAsB;QACtB,sBAAsB;QACtB,0DAA0D;QAC1D,2BAA2B;QAC3B,mCAAmC;QACnC,4BAA4B;QAC5B,gCAAgC;QAChC,4BAA4B;QAC5B,+BAA+B;QAC/B,+BAA+B;QAC/B,2BAA2B;QAC3B,0BAA0B;QAC1B,iBAAiB;QACjB,0CAA0C;QAC1C,iDAAiD;QACjD,4EAA4E;QAC5E,wBAAwB;QACxB,8BAA8B;QAC9B,8DAA8D;QAC9D,QAAQ;QACR,KAAK;QAGG,aAAQ,GAAG,CACf,GAAY,EACZ,GAAa,EACb,IAAkB,EACM,EAAE;YAC1B,IAAI;gBACA,kDAAkD;gBAClD,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAG,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;gBAEtD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CACzC,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,CACZ,CAAC;gBAEF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;aACnC;YAAC,OAAO,KAAW,EAAE;gBAClB,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;aAC/C;QACL,CAAC,CAAA,CAAC;QAEM,UAAK,GAAG,CACZ,GAAY,EACZ,GAAa,EACb,IAAkB,EACM,EAAE;YAC1B,IAAI;gBACA,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;gBAErC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;gBAE5D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;aACnC;YAAC,OAAO,KAAW,EAAE;gBAClB,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;aAC/C;QACL,CAAC,CAAA,CAAC;QAEM,YAAO,GAAG,CACd,GAAY,EACZ,GAAa,EACb,IAAkB,EACH,EAAE;YACjB,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;gBACX,OAAO,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC,CAAC;aAC5D;YAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC,CAAC;QAEM,gBAAW,GAAG,CAClB,GAAY,EACZ,GAAa,EACb,IAAkB,EACM,EAAE;YAC1B,IAAI;gBACA,MAAM,SAAS,GAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBAC/C,MAAM,QAAQ,GAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC7C,oBAAoB;gBAChB,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,GAAG,CAAC,CAAC;gBACzC,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE;oBACrC,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,SAAS,CAAC,CAAA;oBACvC,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,QAAQ,CAAC,CAAA;oBACtC,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;iBAC9D;gBACD,GAAG;gBACH,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;gBAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBAC/C,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,SAAS,CAAC,CAAA;gBACvC,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,QAAQ,CAAC,CAAA;gBACtC,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,OAAO,CAAC,CAAC;gBACjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,MAAM,EAAC,OAAO,EAAC,QAAQ,EAAC,SAAS,CAAC,CAAC;gBACvF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAClB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aAE9B;YACD,OAAM,KAAW,EAAC;gBACd,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,2BAA2B,CAAC,CAAC,CAAC;aAC7D;QAEL,CAAC,CAAA,CAAA;QA7NG,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC5B,CAAC;IAEO,gBAAgB;QACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,IAAI,WAAW,EACvB,IAAA,6BAAoB,EAAC,wBAAS,CAAC,QAAQ,CAAC,EACxC,IAAI,CAAC,QAAQ,CAChB,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,IAAI,CACZ,GAAG,IAAI,CAAC,IAAI,QAAQ,EACpB,IAAA,6BAAoB,EAAC,wBAAS,CAAC,KAAK,CAAC,EACrC,IAAI,CAAC,KAAK,CACb,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,wBAAa,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,SAAS,EAAE,wBAAa,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAGxE,WAAW;QACX,oDAAoD;QAEpD,gBAAgB;QAChB,8DAA8D;QAC9D,gBAAgB;QAChB,sDAAsD;QACtD,eAAe;QACf,6DAA6D;QAC7D,eAAe;QACf,gEAAgE;IAEpE,CAAC;CAkMJ;AAED,kBAAe,cAAc,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/database/MongoDataBase.js b/src/Api/dist/database/MongoDataBase.js deleted file mode 100644 index ea520c6..0000000 --- a/src/Api/dist/database/MongoDataBase.js +++ /dev/null @@ -1,2 +0,0 @@ -// export default db = new MongoClient(uri); -//# sourceMappingURL=MongoDataBase.js.map \ No newline at end of file diff --git a/src/Api/dist/database/MongoDataBase.js.map b/src/Api/dist/database/MongoDataBase.js.map deleted file mode 100644 index 2d7c3f5..0000000 --- a/src/Api/dist/database/MongoDataBase.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"MongoDataBase.js","sourceRoot":"","sources":["../../src/database/MongoDataBase.ts"],"names":[],"mappings":"AACA,4CAA4C"} \ No newline at end of file diff --git a/src/Api/dist/database/StrategyDatabase.js b/src/Api/dist/database/StrategyDatabase.js deleted file mode 100644 index c376e83..0000000 --- a/src/Api/dist/database/StrategyDatabase.js +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=StrategyDatabase.js.map \ No newline at end of file diff --git a/src/Api/dist/database/StrategyDatabase.js.map b/src/Api/dist/database/StrategyDatabase.js.map deleted file mode 100644 index c04ad53..0000000 --- a/src/Api/dist/database/StrategyDatabase.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"StrategyDatabase.js","sourceRoot":"","sources":["../../src/database/StrategyDatabase.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/Api/dist/database/schema/LocationSchema.js b/src/Api/dist/database/schema/LocationSchema.js deleted file mode 100644 index 40dfc76..0000000 --- a/src/Api/dist/database/schema/LocationSchema.js +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const mongoose_1 = require("mongoose"); -const locationSchema = new mongoose_1.Schema({ - idFlad: { - type: String, - required: true, - unique: true, - }, - musicId: { - type: String, - required: true, - }, - latitude: { - type: Number, - required: true, - }, - longitude: { - type: Number, - required: true, - }, -}, { timestamps: true }); -// fladDevDb -// ZslYlNRWIOUU7i6o -exports.default = (0, mongoose_1.model)('Location', locationSchema); -//# sourceMappingURL=LocationSchema.js.map \ No newline at end of file diff --git a/src/Api/dist/database/schema/LocationSchema.js.map b/src/Api/dist/database/schema/LocationSchema.js.map deleted file mode 100644 index 38457b6..0000000 --- a/src/Api/dist/database/schema/LocationSchema.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"LocationSchema.js","sourceRoot":"","sources":["../../../src/database/schema/LocationSchema.ts"],"names":[],"mappings":";;AAAA,uCAAkD;AAGlD,MAAM,cAAc,GAAG,IAAI,iBAAM,CAC7B;IAEI,MAAM,EAAE;QACJ,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,IAAI;KACf;IACD,OAAO,EAAE;QACL,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;KACjB;IACD,QAAQ,EAAE;QACN,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;KACjB;IACD,SAAS,EAAE;QACP,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;KACjB;CAGJ,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,CACvB,CAAC;AAEF,YAAY;AACZ,mBAAmB;AACnB,kBAAe,IAAA,gBAAK,EAAY,UAAU,EAAE,cAAc,CAAC,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/database/schema/NotificationSchema.js b/src/Api/dist/database/schema/NotificationSchema.js deleted file mode 100644 index bb42953..0000000 --- a/src/Api/dist/database/schema/NotificationSchema.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const mongoose_1 = require("mongoose"); -const notificationSchema = new mongoose_1.Schema({ - type: { type: String, required: true }, - content: { type: String, required: true } -}); -exports.default = { Notification: (0, mongoose_1.model)("nofitication", notificationSchema) }; -//# sourceMappingURL=NotificationSchema.js.map \ No newline at end of file diff --git a/src/Api/dist/database/schema/NotificationSchema.js.map b/src/Api/dist/database/schema/NotificationSchema.js.map deleted file mode 100644 index ed407fc..0000000 --- a/src/Api/dist/database/schema/NotificationSchema.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NotificationSchema.js","sourceRoot":"","sources":["../../../src/database/schema/NotificationSchema.ts"],"names":[],"mappings":";;AAAA,uCAAyC;AAEzC,MAAM,kBAAkB,GAAG,IAAI,iBAAM,CAAC;IAClC,IAAI,EAAE,EAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAC;IACpC,OAAO,EAAE,EAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAC;CAC1C,CAAC,CAAC;AAEH,kBAAe,EAAC,YAAY,EAAE,IAAA,gBAAK,EAAC,cAAc,EAAE,kBAAkB,CAAC,EAAC,CAAA"} \ No newline at end of file diff --git a/src/Api/dist/database/schema/Token/IToken.js b/src/Api/dist/database/schema/Token/IToken.js deleted file mode 100644 index af0fc56..0000000 --- a/src/Api/dist/database/schema/Token/IToken.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=IToken.js.map \ No newline at end of file diff --git a/src/Api/dist/database/schema/Token/IToken.js.map b/src/Api/dist/database/schema/Token/IToken.js.map deleted file mode 100644 index c2c2353..0000000 --- a/src/Api/dist/database/schema/Token/IToken.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"IToken.js","sourceRoot":"","sources":["../../../../src/database/schema/Token/IToken.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/Api/dist/database/schema/User/UserInterface.js b/src/Api/dist/database/schema/User/UserInterface.js deleted file mode 100644 index 01d113f..0000000 --- a/src/Api/dist/database/schema/User/UserInterface.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=UserInterface.js.map \ No newline at end of file diff --git a/src/Api/dist/database/schema/User/UserInterface.js.map b/src/Api/dist/database/schema/User/UserInterface.js.map deleted file mode 100644 index 5d59c31..0000000 --- a/src/Api/dist/database/schema/User/UserInterface.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"UserInterface.js","sourceRoot":"","sources":["../../../../src/database/schema/User/UserInterface.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/Api/dist/database/schema/User/UserSchema.js b/src/Api/dist/database/schema/User/UserSchema.js deleted file mode 100644 index 5af36a4..0000000 --- a/src/Api/dist/database/schema/User/UserSchema.js +++ /dev/null @@ -1,82 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const mongoose_1 = require("mongoose"); -const bcrypt_1 = __importDefault(require("bcrypt")); -// const userSchema: Schema = new Schema({ -// 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] } -// }); -const userSchema = new mongoose_1.Schema({ - idFlad: { - type: String, - required: true, - unique: true, - }, - idSpotify: { - type: String, - required: true, - unique: true, - }, - name: { - type: String, - required: true, - }, - email: { - type: String, - required: true, - // this mean that we identify user by email - unique: true, - // delete the whitespace - trim: true, - }, - password: { - type: String, - }, -}, { timestamps: true }); -// this means that we hash the user password before saving it to the database -userSchema.pre('save', function (next) { - return __awaiter(this, void 0, void 0, function* () { - if (!this.isModified('password')) { - //just had that to be sure that the api still going - return next(); - } - const hash = yield bcrypt_1.default.hash(this.password, 8); - this.password = hash; - next(); - }); -}); -userSchema.methods.isValidPassword = function (password) { - return __awaiter(this, void 0, void 0, function* () { - return yield bcrypt_1.default.compare(password, this.password); - }); -}; -// fladDevDb -// ZslYlNRWIOUU7i6o -exports.default = (0, mongoose_1.model)('User', userSchema); -// export const User: Model = model('User', userSchema); -//# sourceMappingURL=UserSchema.js.map \ No newline at end of file diff --git a/src/Api/dist/database/schema/User/UserSchema.js.map b/src/Api/dist/database/schema/User/UserSchema.js.map deleted file mode 100644 index 80388e0..0000000 --- a/src/Api/dist/database/schema/User/UserSchema.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"UserSchema.js","sourceRoot":"","sources":["../../../../src/database/schema/User/UserSchema.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAEA,uCAAyC;AACzC,oDAA4B;AAC5B,iDAAiD;AACjD,uDAAuD;AACvD,6BAA6B;AAC7B,uDAAuD;AACvD,iCAAiC;AACjC,gCAAgC;AAChC,2CAA2C;AAC3C,gDAAgD;AAChD,wCAAwC;AACxC,0CAA0C;AAC1C,iCAAiC;AACjC,oBAAoB;AACpB,yBAAyB;AACzB,kCAAkC;AAClC,mBAAmB;AACnB,uCAAuC;AACvC,MAAM;AAEN,MAAM,UAAU,GAAG,IAAI,iBAAM,CACzB;IAEI,MAAM,EAAE;QACJ,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,IAAI;KACf;IACD,SAAS,EAAE;QACP,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,IAAI;KACf;IACD,IAAI,EAAE;QACF,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;KACjB;IACD,KAAK,EAAE;QACH,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,IAAI;QACd,2CAA2C;QAC3C,MAAM,EAAE,IAAI;QACZ,wBAAwB;QACxB,IAAI,EAAE,IAAI;KACb;IACD,QAAQ,EAAE;QACN,IAAI,EAAE,MAAM;KACf;CAEJ,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,CACvB,CAAC;AAEF,6EAA6E;AAC7E,UAAU,CAAC,GAAG,CAAQ,MAAM,EAAE,UAAgB,IAAI;;QAC9C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE;YAC9B,mDAAmD;YACnD,OAAO,IAAI,EAAE,CAAC;SACjB;QAED,MAAM,IAAI,GAAG,MAAM,gBAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAEjD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErB,IAAI,EAAE,CAAC;IACX,CAAC;CAAA,CAAC,CAAC;AAEH,UAAU,CAAC,OAAO,CAAC,eAAe,GAAG,UACjC,QAAgB;;QAEhB,OAAO,MAAM,gBAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;CAAA,CAAC;AAEF,YAAY;AACZ,mBAAmB;AACnB,kBAAe,IAAA,gBAAK,EAAQ,MAAM,EAAE,UAAU,CAAC,CAAC;AAChD,+DAA+D"} \ No newline at end of file diff --git a/src/Api/dist/database/schema/User/UserValidation.js b/src/Api/dist/database/schema/User/UserValidation.js deleted file mode 100644 index cd0111a..0000000 --- a/src/Api/dist/database/schema/User/UserValidation.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const joi_1 = __importDefault(require("joi")); -const register = joi_1.default.object({ - name: joi_1.default.string().max(30).required(), - email: joi_1.default.string().email().required(), - password: joi_1.default.string().min(6).required(), - // can add an field like confimPassword and cheked that the password is equal to the confirmPassword - idSpotify: joi_1.default.string(), - idFlad: joi_1.default.string(), -}); -const login = joi_1.default.object({ - email: joi_1.default.string().email().required(), - password: joi_1.default.string().required(), -}); -exports.default = { register, login }; -//# sourceMappingURL=UserValidation.js.map \ No newline at end of file diff --git a/src/Api/dist/database/schema/User/UserValidation.js.map b/src/Api/dist/database/schema/User/UserValidation.js.map deleted file mode 100644 index 34d24e7..0000000 --- a/src/Api/dist/database/schema/User/UserValidation.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"UserValidation.js","sourceRoot":"","sources":["../../../../src/database/schema/User/UserValidation.ts"],"names":[],"mappings":";;;;;AAAA,8CAAsB;AAEtB,MAAM,QAAQ,GAAG,aAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,aAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAErC,KAAK,EAAE,aAAG,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IAEtC,QAAQ,EAAE,aAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxC,oGAAoG;IACpG,SAAS,EAAE,aAAG,CAAC,MAAM,EAAE;IACvB,MAAM,EAAG,aAAG,CAAC,MAAM,EAAE;CACxB,CAAC,CAAC;AAEH,MAAM,KAAK,GAAG,aAAG,CAAC,MAAM,CAAC;IACrB,KAAK,EAAE,aAAG,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;IACtC,QAAQ,EAAE,aAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAEH,kBAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/index.js b/src/Api/dist/index.js deleted file mode 100644 index 632b661..0000000 --- a/src/Api/dist/index.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const app_1 = __importDefault(require("./app")); -const spotifyCtrl_1 = __importDefault(require("./controller/spotify-controller/spotifyCtrl")); -const TestCtrl_1 = __importDefault(require("./controller/TestCtrl")); -const userCtrl_1 = __importDefault(require("./controller/user-controller/userCtrl")); -const dotenv_1 = __importDefault(require("dotenv")); -dotenv_1.default.config(); -const app = new app_1.default([new TestCtrl_1.default(), new spotifyCtrl_1.default(), new userCtrl_1.default()], Number(process.env.PORT)); -app.listen(); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/src/Api/dist/index.js.map b/src/Api/dist/index.js.map deleted file mode 100644 index 3d14e47..0000000 --- a/src/Api/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA,gDAAwB;AACxB,8FAA4E;AAC5E,qEAAmD;AACnD,qFAAmE;AACnE,oDAA2B;AAC3B,gBAAM,CAAC,MAAM,EAAE,CAAC;AAChB,MAAM,GAAG,GAAG,IAAI,aAAG,CACf,CAAC,IAAI,kBAAc,EAAE,EAAE,IAAI,qBAAiB,EAAE,EAAE,IAAI,kBAAc,EAAE,CAAC,EACrE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAE3B,CAAC;AAEF,GAAG,CAAC,MAAM,EAAE,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/middleware/authMiddleware.js b/src/Api/dist/middleware/authMiddleware.js deleted file mode 100644 index 24ac50e..0000000 --- a/src/Api/dist/middleware/authMiddleware.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -const UserSchema_1 = __importDefault(require("../database/schema/User/UserSchema")); -const token_1 = __importDefault(require("../model/token")); -const httpExeption_1 = __importDefault(require("./exeption/httpExeption")); -function authenticatedMiddleware(req, res, next) { - return __awaiter(this, void 0, void 0, function* () { - const bearer = req.headers.authorization; - if (!bearer || !bearer.startsWith('Bearer ')) { - return next(new httpExeption_1.default(401, 'Unauthorised')); - } - const accessToken = bearer.split('Bearer ')[1].trim(); - try { - const payload = yield token_1.default.verifyToken(accessToken); - if (payload instanceof jsonwebtoken_1.default.JsonWebTokenError) { - return next(new httpExeption_1.default(401, 'Unauthorised')); - } - const user = yield UserSchema_1.default.findById(payload.id) - .select('-password') - .exec(); - if (!user) { - return next(new httpExeption_1.default(401, 'Unauthorised')); - } - req.user = user; - return next(); - } - catch (error) { - return next(new httpExeption_1.default(401, 'Unauthorised')); - } - }); -} -exports.default = authenticatedMiddleware; -//# sourceMappingURL=authMiddleware.js.map \ No newline at end of file diff --git a/src/Api/dist/middleware/authMiddleware.js.map b/src/Api/dist/middleware/authMiddleware.js.map deleted file mode 100644 index 77d6a84..0000000 --- a/src/Api/dist/middleware/authMiddleware.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../src/middleware/authMiddleware.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AACA,gEAA+B;AAE/B,oFAA4D;AAC5D,2DAAmC;AACnC,2EAAoD;AAEpD,SAAe,uBAAuB,CAClC,GAAY,EACZ,GAAa,EACb,IAAkB;;QAElB,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;QAEzC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;YAC1C,OAAO,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;SACvD;QAED,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtD,IAAI;YACA,MAAM,OAAO,GAAmC,MAAM,eAAK,CAAC,WAAW,CACnE,WAAW,CACd,CAAC;YAEF,IAAI,OAAO,YAAY,sBAAG,CAAC,iBAAiB,EAAE;gBAC1C,OAAO,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;aACvD;YAED,MAAM,IAAI,GAAG,MAAM,oBAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;iBAC7C,MAAM,CAAC,WAAW,CAAC;iBACnB,IAAI,EAAE,CAAC;YAEZ,IAAI,CAAC,IAAI,EAAE;gBACP,OAAO,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;aACvD;YAED,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YAEhB,OAAO,IAAI,EAAE,CAAC;SACjB;QAAC,OAAO,KAAK,EAAE;YACZ,OAAO,IAAI,CAAC,IAAI,sBAAa,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;SACvD;IACL,CAAC;CAAA;AAED,kBAAe,uBAAuB,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/middleware/exeption/httpExeption.js b/src/Api/dist/middleware/exeption/httpExeption.js deleted file mode 100644 index bfed40e..0000000 --- a/src/Api/dist/middleware/exeption/httpExeption.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -class HttpException extends Error { - constructor(status, message) { - super(message); - this.status = status; - this.message = message; - } -} -// en fontion de l'exeption firebas,etc une bonne exeption -exports.default = HttpException; -//# sourceMappingURL=httpExeption.js.map \ No newline at end of file diff --git a/src/Api/dist/middleware/exeption/httpExeption.js.map b/src/Api/dist/middleware/exeption/httpExeption.js.map deleted file mode 100644 index 47983ff..0000000 --- a/src/Api/dist/middleware/exeption/httpExeption.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"httpExeption.js","sourceRoot":"","sources":["../../../src/middleware/exeption/httpExeption.ts"],"names":[],"mappings":";;AAAA,MAAM,aAAc,SAAQ,KAAK;IAI7B,YAAY,MAAc,EAAE,OAAe;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;CACJ;AACD,0DAA0D;AAE1D,kBAAe,aAAa,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/middleware/validation/ValidatorMiddleware.js b/src/Api/dist/middleware/validation/ValidatorMiddleware.js deleted file mode 100644 index 98ad7de..0000000 --- a/src/Api/dist/middleware/validation/ValidatorMiddleware.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -function validationMiddleware(schema) { - return (req, res, next) => __awaiter(this, void 0, void 0, function* () { - const validationOptions = { - abortEarly: false, - allowUnknown: true, - stripUnknown: true, - }; - try { - const value = yield schema.validateAsync(req.body, validationOptions); - req.body = value; - next(); - } - catch (e) { - const errors = []; - e.details.forEach((error) => { - errors.push(error.message); - }); - res.status(400).send({ errors: errors }); - } - }); -} -exports.default = validationMiddleware; -//# sourceMappingURL=ValidatorMiddleware.js.map \ No newline at end of file diff --git a/src/Api/dist/middleware/validation/ValidatorMiddleware.js.map b/src/Api/dist/middleware/validation/ValidatorMiddleware.js.map deleted file mode 100644 index 80fc94e..0000000 --- a/src/Api/dist/middleware/validation/ValidatorMiddleware.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ValidatorMiddleware.js","sourceRoot":"","sources":["../../../src/middleware/validation/ValidatorMiddleware.ts"],"names":[],"mappings":";;;;;;;;;;;AAGA,SAAS,oBAAoB,CAAC,MAAkB;IAC5C,OAAO,CACH,GAAY,EACZ,GAAa,EACb,IAAkB,EACL,EAAE;QACf,MAAM,iBAAiB,GAAG;YACtB,UAAU,EAAE,KAAK;YACjB,YAAY,EAAE,IAAI;YAClB,YAAY,EAAE,IAAI;SACrB,CAAC;QAEF,IAAI;YACA,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,aAAa,CACpC,GAAG,CAAC,IAAI,EACR,iBAAiB,CACpB,CAAC;YACF,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC;YACjB,IAAI,EAAE,CAAC;SACV;QAAC,OAAO,CAAM,EAAE;YACb,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAA8B,EAAE,EAAE;gBACjD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;SAC5C;IACL,CAAC,CAAA,CAAC;AACN,CAAC;AAED,kBAAe,oBAAoB,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/middleware/winston.js b/src/Api/dist/middleware/winston.js deleted file mode 100644 index 79ab948..0000000 --- a/src/Api/dist/middleware/winston.js +++ /dev/null @@ -1,9 +0,0 @@ -// 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 }) -// ), -// }; -//# sourceMappingURL=winston.js.map \ No newline at end of file diff --git a/src/Api/dist/middleware/winston.js.map b/src/Api/dist/middleware/winston.js.map deleted file mode 100644 index 7a79c49..0000000 --- a/src/Api/dist/middleware/winston.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"winston.js","sourceRoot":"","sources":["../../src/middleware/winston.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,sDAAsD;AACtD,sCAAsC;AACtC,iCAAiC;AACjC,wCAAwC;AACxC,iDAAiD;AACjD,SAAS;AACT,KAAK"} \ No newline at end of file diff --git a/src/Api/dist/model/IUser.js b/src/Api/dist/model/IUser.js deleted file mode 100644 index 6d13f68..0000000 --- a/src/Api/dist/model/IUser.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -//# sourceMappingURL=IUser.js.map \ No newline at end of file diff --git a/src/Api/dist/model/IUser.js.map b/src/Api/dist/model/IUser.js.map deleted file mode 100644 index 1281d2b..0000000 --- a/src/Api/dist/model/IUser.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"IUser.js","sourceRoot":"","sources":["../../src/model/IUser.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/src/Api/dist/model/locationModel.js b/src/Api/dist/model/locationModel.js deleted file mode 100644 index 679d00b..0000000 --- a/src/Api/dist/model/locationModel.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Place = exports.UserLocation = exports.PlacePosition = void 0; -class PlacePosition { - constructor(timestamp, latitude, longitude) { - this.timestamp = timestamp; - this.coords = { latitude, longitude }; - } -} -exports.PlacePosition = PlacePosition; -class UserLocation { - constructor(uuid, musicId, latitude, longitude) { - this.uuid = uuid; - this.musicId = musicId; - this.latitude = latitude; - this.longitude = longitude; - } -} -exports.UserLocation = UserLocation; -class Place { - constructor(address, position) { - this.position = position; - this.address = address; - } -} -exports.Place = Place; -//# sourceMappingURL=locationModel.js.map \ No newline at end of file diff --git a/src/Api/dist/model/locationModel.js.map b/src/Api/dist/model/locationModel.js.map deleted file mode 100644 index 835a347..0000000 --- a/src/Api/dist/model/locationModel.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"locationModel.js","sourceRoot":"","sources":["../../src/model/locationModel.ts"],"names":[],"mappings":";;;AAmBA,MAAa,aAAa;IAMtB,YAAY,SAAiB,EAAC,QAAiB,EAAE,SAAiB;QAC9D,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,EAAC,QAAQ,EAAE,SAAS,EAAC,CAAC;IACxC,CAAC;CACJ;AAVD,sCAUC;AACD,MAAa,YAAY;IAKrB,YAAY,IAAY,EAAE,OAAgB,EAAC,QAAgB,EAAE,SAAiB;QAC1E,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;CACJ;AAXD,oCAWC;AAED,MAAa,KAAK;IAGlB,YAAY,OAAgB,EAAC,QAAkB;QAC3C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;CACA;AAPD,sBAOC"} \ No newline at end of file diff --git a/src/Api/dist/model/token.js b/src/Api/dist/model/token.js deleted file mode 100644 index f63afa4..0000000 --- a/src/Api/dist/model/token.js +++ /dev/null @@ -1,34 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.verifyToken = exports.createToken = void 0; -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -const createToken = (user) => { - return jsonwebtoken_1.default.sign({ id: user._id }, "foo", { - expiresIn: '100d', - }); -}; -exports.createToken = createToken; -const verifyToken = (token) => __awaiter(void 0, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - jsonwebtoken_1.default.verify(token, "foo", (err, payload) => { - if (err) - return reject(err); - resolve(payload); - }); - }); -}); -exports.verifyToken = verifyToken; -exports.default = { createToken: exports.createToken, verifyToken: exports.verifyToken }; -//# sourceMappingURL=token.js.map \ No newline at end of file diff --git a/src/Api/dist/model/token.js.map b/src/Api/dist/model/token.js.map deleted file mode 100644 index 87b1e0c..0000000 --- a/src/Api/dist/model/token.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/model/token.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,gEAA+B;AAIxB,MAAM,WAAW,GAAG,CAAC,IAAW,EAAU,EAAE;IAC/C,OAAO,sBAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,KAAmB,EAAE;QACnD,SAAS,EAAE,MAAM;KACpB,CAAC,CAAC;AACP,CAAC,CAAC;AAJW,QAAA,WAAW,eAItB;AAEK,MAAM,WAAW,GAAG,CACvB,KAAa,EACqB,EAAE;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,sBAAG,CAAC,MAAM,CACN,KAAK,EACL,KAAmB,EACnB,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;YACb,IAAI,GAAG;gBAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;YAE5B,OAAO,CAAC,OAAiB,CAAC,CAAC;QAC/B,CAAC,CACJ,CAAC;IACN,CAAC,CAAC,CAAC;AACP,CAAC,CAAA,CAAC;AAdW,QAAA,WAAW,eActB;AAEF,kBAAe,EAAE,WAAW,EAAX,mBAAW,EAAE,WAAW,EAAX,mBAAW,EAAE,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/service/LocationService.js b/src/Api/dist/service/LocationService.js deleted file mode 100644 index a1eaed7..0000000 --- a/src/Api/dist/service/LocationService.js +++ /dev/null @@ -1,116 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -// import db from '../database'; -const locationModel_1 = require("../model/locationModel"); -const LocationSchema_1 = __importDefault(require("../database/schema/LocationSchema")); -class LocationService { - constructor() { - this.locationCollection = LocationSchema_1.default; - this.toRad = (value) => (value * Math.PI) / 180; - this.toDeg = (value) => (value * 180) / Math.PI; - // getCenter(coords) - } - // private API_KEY : string = "AIzaSyBFCEAtmhZ8jvw84UTQvX3Aqpr66GVqB_A"; - getNearUser(idFlad, musicId, latitude, longitude) { - return __awaiter(this, void 0, void 0, function* () { - yield this.locationCollection.findOneAndUpdate({ idFlad }, { idFlad, musicId, latitude, longitude }, { upsert: true }); - const snapshot = yield this.locationCollection.find({ idFlad: { $ne: idFlad } }); - if (snapshot.length === 0) { - console.log('No matching documents.'); - return; - } - let dbUsersList = []; - snapshot.forEach(doc => { - dbUsersList.push(new locationModel_1.UserLocation(doc.idFlad, doc.musicId, doc.latitude, doc.longitude)); - console.log(doc.idFlad, '=>', doc); - }); - // missing the curent music - let listUser = []; - const listUser2 = {}; - dbUsersList.forEach(user => { - console.log(user); - const dist = this.distanceBetween(latitude, longitude, user.latitude, user.longitude); - console.log(user.uuid, dist); - if (dist <= 100) { - listUser.push(user.uuid); - listUser2[user.uuid] = user.musicId; - } - }); - return { listUser, listUser2 }; - // $listUser[] = {userID,idMusic}; - }); - } - getCenter(points) { - 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))), - }; - } - ; - // sa c'est un utils du coup mettre dans une calss utils - // resulta en km - distanceBetween(lat1, lon1, lat2, lon2) { - 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; - } - } - distanceBetweenPosition(first, second) { - 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 - findNearest(main, list) { - this.orderByDistance(main, list)[0]; - } - //distanceFn: DistanceFn = getDistance est param sa serrait cool de lui passer un fonction - orderByDistance(mainPos, coords) { - return coords - .slice() - .sort((a, b) => this.distanceBetweenPosition(mainPos, a) - this.distanceBetweenPosition(mainPos, b)); - } - ; -} -exports.default = LocationService; -//# sourceMappingURL=LocationService.js.map \ No newline at end of file diff --git a/src/Api/dist/service/LocationService.js.map b/src/Api/dist/service/LocationService.js.map deleted file mode 100644 index 1f6b0d5..0000000 --- a/src/Api/dist/service/LocationService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"LocationService.js","sourceRoot":"","sources":["../../src/service/LocationService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,gCAAgC;AAChC,0DAAsF;AAEtF,uFAA+D;AAE/D,MAAM,eAAe;IAArB;QACY,uBAAkB,GAAG,wBAAc,CAAC;QAuErC,UAAK,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;QACnD,UAAK,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;QA2C1D,oBAAoB;IAKxB,CAAC;IAvHG,wEAAwE;IAC3D,WAAW,CAAC,MAAe,EAAE,OAAgB,EAAC,QAAiB,EAAE,SAAkB;;YAE5F,MAAM,IAAI,CAAC,kBAAkB,CAAC,gBAAgB,CAC1C,EAAE,MAAM,EAAE,EACV,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,EACxC,EAAE,MAAM,EAAE,IAAI,EAAE,CACnB,CAAC;YAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACjF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;gBAC3B,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;gBACtC,OAAO;aACN;YAED,IAAI,WAAW,GAAkB,EAAE,CAAC;YACpC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;gBACnB,WAAW,CAAC,IAAI,CAAC,IAAI,4BAAY,CAAC,GAAG,CAAC,MAAM,EAAC,GAAG,CAAC,OAAO,EAAC,GAAG,CAAC,QAAQ,EAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;gBACxF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACrC,CAAC,CAAC,CAAC;YACC,2BAA2B;YAC3B,IAAI,QAAQ,GAAa,EAAE,CAAC;YAC5B,MAAM,SAAS,GAA2B,EAAE,CAAC;YAC7C,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBACvB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAClB,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAG,SAAS,EAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;gBACxF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAC,IAAI,CAAC,CAAC;gBAC5B,IAAI,IAAI,IAAI,GAAG,EAAE;oBAEb,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACzB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;iBACvC;YACL,CAAC,CAAC,CAAC;YAGH,OAAM,EAAE,QAAQ,EAAE,SAAS,EAAC,CAAC;YAC7B,+CAA+C;QAEvD,CAAC;KAAA;IAEM,SAAS,CAAE,MAAkB;QAChC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;YACxD,OAAO,KAAK,CAAC;SAChB;QAED,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;QAErC,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CACrB,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YACX,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACpD,OAAO;gBACH,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAClD,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAClD,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;aAChC,CAAC;QACN,CAAC,EACD,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CACvB,CAAC;QAEF,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC;QACjC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC;QACjC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC;QAEjC,OAAO;YACH,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACvC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SAChE,CAAC;IACN,CAAC;IAAA,CAAC;IAKF,yDAAyD;IACzD,gBAAgB;IACR,eAAe,CAAE,IAAa,EAAE,IAAa,EAAE,IAAY,EAAE,IAAa;QAC9E,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE;YAClC,OAAO,CAAC,CAAC;SACZ;aACI;YACD,IAAI,OAAO,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,GAAC,GAAG,CAAC;YACjC,IAAI,OAAO,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,GAAC,GAAG,CAAC;YACjC,IAAI,KAAK,GAAG,IAAI,GAAC,IAAI,CAAC;YACtB,IAAI,QAAQ,GAAG,IAAI,CAAC,EAAE,GAAG,KAAK,GAAC,GAAG,CAAC;YACnC,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAE9G,IAAI,IAAI,GAAG,CAAC,EAAE;gBACV,IAAI,GAAG,CAAC,CAAC;aACZ;YAED,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,GAAG,IAAI,GAAG,GAAG,GAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,IAAI,GAAG,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC;YAC1B,IAAI,GAAG,IAAI,GAAG,QAAQ,CAAC;YAEvB,OAAO,IAAI,CAAC;SACf;IACL,CAAC;IACO,uBAAuB,CAAC,KAAgB,EAAE,MAAiB;QAC/D,OAAO,IAAI,CAAC,eAAe,CAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IAChI,CAAC;IAED,mEAAmE;IAC3D,WAAW,CAAC,IAAe,EAAE,IAAiB;QAClD,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IACvC,CAAC;IAED,0FAA0F;IAClF,eAAe,CAAE,OAAiB,EAAC,MAAkB;QACzD,OAAO,MAAM;aACZ,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,uBAAuB,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,uBAAuB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACzG,CAAC;IAAA,CAAC;CAOL;AAID,kBAAe,eAAe,CAAC"} \ No newline at end of file diff --git a/src/Api/dist/service/UserService.js b/src/Api/dist/service/UserService.js deleted file mode 100644 index 8e0e51e..0000000 --- a/src/Api/dist/service/UserService.js +++ /dev/null @@ -1,69 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const UserSchema_1 = __importDefault(require("../database/schema/User/UserSchema")); -const token_1 = __importDefault(require("../model/token")); -class UserService { - constructor() { - this.user = UserSchema_1.default; - } - /** - * Register a new user - */ - register(name, email, password, idFlad, idSpotify) { - return __awaiter(this, void 0, void 0, function* () { - try { - const user = yield this.user.create({ - name, - email, - password, - idFlad, - idSpotify - }); - const accessToken = token_1.default.createToken(user); - return accessToken; - } - catch (error) { - throw new Error(error.message); - } - }); - } - /** - * Attempt to login a user - */ - login(email, password) { - return __awaiter(this, void 0, void 0, function* () { - try { - // should maybe creat a method base on id and other information for better security - // need to view with Emre - const user = yield this.user.findOne({ email }); - // const user = await this.user.findById(idFlad); - if (!user) { - throw new Error('Unable to find user with that email address'); - } - if (yield user.isValidPassword(password)) { - return token_1.default.createToken(user); - } - else { - throw new Error('Wrong credentials given'); - } - } - catch (error) { - throw new Error('Unable to create user'); - } - }); - } -} -exports.default = UserService; -//# sourceMappingURL=UserService.js.map \ No newline at end of file diff --git a/src/Api/dist/service/UserService.js.map b/src/Api/dist/service/UserService.js.map deleted file mode 100644 index cb002da..0000000 --- a/src/Api/dist/service/UserService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"UserService.js","sourceRoot":"","sources":["../../src/service/UserService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAAA,oFAA4D;AAC5D,2DAAmC;AAGnC,MAAM,WAAW;IAAjB;QACY,SAAI,GAAG,oBAAU,CAAC;IAuD9B,CAAC;IArDG;;OAEG;IACU,QAAQ,CACjB,IAAY,EACZ,KAAa,EACb,QAAgB,EAChB,MAAe,EACf,SAAkB;;YAElB,IAAI;gBACA,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;oBAChC,IAAI;oBACJ,KAAK;oBACL,QAAQ;oBACR,MAAM;oBACN,SAAS;iBACZ,CAAC,CAAC;gBAEH,MAAM,WAAW,GAAG,eAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBAE5C,OAAO,WAAW,CAAC;aACtB;YAAC,OAAO,KAAW,EAAE;gBAClB,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;aAClC;QACL,CAAC;KAAA;IAED;;OAEG;IACU,KAAK,CACd,KAAa,EACb,QAAgB;;YAEhB,IAAI;gBACA,mFAAmF;gBACnF,yBAAyB;gBACzB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;gBAChD,iDAAiD;gBAEjD,IAAI,CAAC,IAAI,EAAE;oBACP,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;iBAClE;gBAED,IAAI,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE;oBACtC,OAAO,eAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;iBAClC;qBAAM;oBACH,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;iBAC9C;aACJ;YAAC,OAAO,KAAK,EAAE;gBACZ,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;aAC5C;QACL,CAAC;KAAA;CACJ;AAED,kBAAe,WAAW,CAAC"} \ No newline at end of file diff --git a/src/Api/nodemon.json b/src/Api/nodemon.json new file mode 100644 index 0000000..7d833c9 --- /dev/null +++ b/src/Api/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["src"], + "ext": ".ts.js", + "exec": "ts-node ./src/index.ts" +} \ No newline at end of file diff --git a/src/Api/package.json b/src/Api/package.json index 3626db0..e744c55 100644 --- a/src/Api/package.json +++ b/src/Api/package.json @@ -1,13 +1,11 @@ { - "name": "api", + "name": "flad_api", "version": "1.0.0", "description": "", "main": "dist/index.js", "scripts": { "build": "tsc", - "dave": "nodemon ./src/index.ts", - "dev": "tsc -w & nodemon .", - "start": "tsc & node .", + "dev": "nodemon", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -15,36 +13,30 @@ "license": "ISC", "devDependencies": { "@types/bcrypt": "^5.0.0", + "@types/body-parser": "^1.19.2", "@types/cors": "^2.8.13", - "@types/debug": "^4.1.7", "@types/express": "^4.17.16", "@types/jsonwebtoken": "^9.0.1", - "@types/morgan": "^1.9.4", - "@types/node": "^18.14.0", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.6", "nodemon": "^2.0.20", - "pre-commit": "^1.2.2", "ts-node": "^10.9.1", "typescript": "^4.9.5" }, "dependencies": { "@types/cookie-parser": "^1.4.3", - "@types/crypto-js": "^4.1.1", "@types/mongoose": "^5.11.97", - "@types/request": "^2.48.8", "axios": "^1.2.6", "bcrypt": "^5.1.0", + "build": "^0.1.4", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "dotenv": "^16.0.3", - "express-winston": "^4.2.0", + "express": "^4.18.2", "joi": "^17.8.1", "jsonwebtoken": "^9.0.0", - "mongodb": "^5.0.0", "mongoose": "^6.9.0", - "morgan": "^1.10.0", - "request": "^2.88.2", - "rimraf": "^4.1.2", - "swagger-ui-express": "^4.6.0", - "winston": "^3.8.2" + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.0" } } diff --git a/src/Api/src/app.ts b/src/Api/src/app.ts index f665b4b..90e16e8 100644 --- a/src/Api/src/app.ts +++ b/src/Api/src/app.ts @@ -1,68 +1,60 @@ import express, { Application } from 'express'; import cors from 'cors'; -import Controller from './controller/Icontroller'; +import cookieParser from 'cookie-parser'; import bodyParser from 'body-parser'; +import IController from './controllers/interfaces/IController'; import mongoose from 'mongoose'; - -import cookieParser from 'cookie-parser'; +import swaggerUi from "swagger-ui-express"; +import { specs } from './utils/swagger'; class App { public express: Application; public port: number; - public dataBase: null; - public server : any; - - constructor(controllers: Controller[], port: number) { + constructor(controllers: IController[], port: number) { this.express = express(); this.port = port; - this.dataBase = null; - - this.initialiseDatabase(); - this.initialiseMiddleware(); - this.initialiseControllers(controllers); - + + this.initDatabase(); + this.initMiddleware(); + this.initControllers(controllers); + this.initSwagger(); } - private initialiseMiddleware(): void { + private initMiddleware(): void { this.express.use(cors()); this.express.use(cookieParser()); - - this.express.use(express.json()); this.express.use(express.urlencoded({ extended: false })); - this.express.use(bodyParser.json()); this.express.use(bodyParser.urlencoded({ extended: true - })); - + })); } - private initialiseControllers(controllers: Controller[]): void { - controllers.forEach((controller: Controller) => { + private initControllers(controllers: IController[]): void { + controllers.forEach((controller: IController) => { this.express.use('/api', controller.router); - this.express.get('/toto', (req, res) => { - res.send('Hello World!'); - }) }); } - - public listen(): void { - const server = this.express.listen(this.port, () => { + this.express.listen(this.port, () => { console.log(`⚡️[server] : App listening on the port ${this.port}`); }); } - - private initialiseDatabase(): void { - const uri = "mongodb+srv://fladDevDb:ZslYlNRWIOUU7i6o@fladcluster.b29tytu.mongodb.net/?retryWrites=true&w=majority" - mongoose.connect(uri) - .then(() => console.log("Connect to MongoDB database successfully")) - .catch(err => console.log("Error connecting : "+ err )); + + private initDatabase(): void { + const MONGO_URL = `mongodb+srv://FladDev:${process.env.MONGO_PASSWORD}@flad.mliekr2.mongodb.net/?retryWrites=true&w=majority`; + mongoose.connect(MONGO_URL) + .then(() => console.log("Connect to MongoDB database successfully")) + .catch(error => console.log("Error connecting : " + error)); + } + + public initSwagger(): void { + this.express.use("/swagger", swaggerUi.serve, swaggerUi.setup(specs),); + console.log(`Docs available at /${this.port}/swagger`); } - } export default App; \ No newline at end of file diff --git a/src/Api/src/assets/images/default_user.png b/src/Api/src/assets/images/default_user.png new file mode 100644 index 0000000..41b21ff Binary files /dev/null and b/src/Api/src/assets/images/default_user.png differ diff --git a/src/Api/src/controller/Icontroller.ts b/src/Api/src/controller/Icontroller.ts deleted file mode 100644 index dc34151..0000000 --- a/src/Api/src/controller/Icontroller.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Router } from 'express'; - -interface Controller { - path: string; - router: Router; - // constructor() { - // this.initialiseRoutes(); - // } - - // initialiseRoutes(): void ; - -} -// il y a un truc inject - -export default Controller; \ No newline at end of file diff --git a/src/Api/src/controller/TestCtrl.ts b/src/Api/src/controller/TestCtrl.ts deleted file mode 100644 index 4058fc5..0000000 --- a/src/Api/src/controller/TestCtrl.ts +++ /dev/null @@ -1,27 +0,0 @@ -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 { - return { - message: "pong", - }; - } -} \ No newline at end of file diff --git a/src/Api/src/controller/spotify-controller/request/authReqBody.ts b/src/Api/src/controller/spotify-controller/request/authReqBody.ts deleted file mode 100644 index 6799b65..0000000 --- a/src/Api/src/controller/spotify-controller/request/authReqBody.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type AuthReqBody = { - grant_type: string, - redirect_uri?: string, - code?: string, - refresh_token?: string, -} \ No newline at end of file diff --git a/src/Api/src/controller/spotify-controller/spotifyCtrl.ts b/src/Api/src/controller/spotify-controller/spotifyCtrl.ts deleted file mode 100644 index 2c9e220..0000000 --- a/src/Api/src/controller/spotify-controller/spotifyCtrl.ts +++ /dev/null @@ -1,198 +0,0 @@ -import Controller from '../Icontroller'; -import { Router, Request, Response, NextFunction } from 'express'; -import HttpException from '../../middleware/exeption/httpExeption'; -import axios from 'axios'; -import qs from 'qs'; - -class SpotifyController implements Controller { - public path = '/spotify'; - public router = Router(); - - constructor() { - console.log("useeeee"); - - this.initialiseRoutes(); - } - initialiseRoutes() { - this.router.get(`${this.path}/exchange`,this.login); - this.router.get(`${this.path}/callback`,this.getAccessToken); - this.router.get(`${this.path}/refresh`,this.getRefreshToken); - this.router.get(`${this.path}/spot`, this.getSpot); - - } - - - private readonly API_URL = "https://accounts.spotify.com/api/token"; - private readonly CLIENT_ID = "1f1e34e4b6ba48b388469dba80202b10"; - private readonly CLIENT_SECRET = "779371c6d4994a68b8dd6e84b0873c82"; - private readonly CALLBACK_2 = 'https://flad-api-production.up.railway.app/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 clientRedirect= 'spotify_final_redirect-uri-key'; - - private login = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - - console.log("useeeee== login"); - try { - - const redirectResponse = req.query.redirectUrl ? req.query.redirectUrl : req.headers.referer; - res.cookie(this.clientRedirect, redirectResponse); - console.log("aloorrr si c'est niquuuuuuuuuuuueeee" +this.CALLBACK_2+ "gennnnnnnnnrree vraiiiiiiiment "); - res.redirect('https://accounts.spotify.com/authorize?' + - qs.stringify({ - response_type: 'code', - client_id: this.CLIENT_ID, - scope: this.SCOPES, - redirect_uri: this.CALLBACK_2, - })); - - - } catch (error) { - next(new HttpException(400, 'Cannot create spot')); - } - - - }; - - private getRefreshToken = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - - console.log('UUse2'); - - try { - const params = req.query.refresh_token; - - if (!req.query.refresh_token) { - return res.json({ - "error": "Parameter refresh_token missing" - }); - } - let 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 - }; - - axios(authOptions) - .then(session => { - if(session.status === 200){ - console.log('### Information : responce ###' + JSON.stringify( session.data) ); - console.log('### Information : refresh_token ###' + session.data.refresh_token); - - res.send({ - "access_token": session.data.access_token, - "refresh_token": session.data.refresh_token, - "expires_in": session.data.expires_in - }); - }}); - console.log("goood"); - } catch (error) { - console.log("errur"); - next(new HttpException(400, 'Cannot create post')); - } - - } - - - - public getSpot = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - const spots = [ - { - name: "blue", - sourceUrl: "https://cdns-images.dzcdn.net/images/artist/399e7e760d8fedf3cc2891e9c0c41658/200x200-000000-80-0-0.jpg", - index: 3 - }, - { - name: "strange history", - sourceUrl: "https://images.genius.com/339dfe2a7c0adf9a5d08febf29a845f4.1000x1000x1.jpg", - index: 7 - }, - { - name: "oboy album", - sourceUrl: "https://i.pinimg.com/originals/ad/cc/d5/adccd58a0d0ff516a6114703cd05810e.jpg", - index: 1 - } - ]; - try { - res.send(spots); - - } catch (error) { - console.log('heuuuuuuuuuuuuuuuuuuuuubizzzaaarrreeee'); - console.log(error); - next(new HttpException(400, 'On peut pas avoir darray mec')); - } } - - - private getAccessToken = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - console.log("useeeee== accesToken"); - - let code = req.query.code; - let state = req.query.state || null; - let storedredirectUri = req.cookies ? req.cookies[this.clientRedirect] : null; - - var authOptions = { - method: 'POST', - url: 'https://accounts.spotify.com/api/token', - data: qs.stringify({ - code: code, - redirect_uri: this.CALLBACK_2, - 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 { - var resp = await axios(authOptions); - if (resp.status === 200) { - console.log('oon esttt laaa'); - let access_token = resp.data.access_token; - let expiration =resp.data.expires_in; - let refresh = resp.data.refresh_token - console.log(access_token); - - res.clearCookie(this.clientRedirect); - res.redirect(`${storedredirectUri}?` + - qs.stringify({ - "access_token": access_token, - "expires_in": expiration, - "refresh_token" : refresh - })); - } - } catch (error) { - console.log('heuuuuuuuuuuuuuuuuuuuuubizzzaaarrreeee'); - console.log(error); - next(new HttpException(400, 'On peut pas te connecter mec'+ error.message)); - } - - - - }; - - -} -export default SpotifyController; diff --git a/src/Api/src/controller/user-controller/userCtrl.ts b/src/Api/src/controller/user-controller/userCtrl.ts deleted file mode 100644 index 3ea4efe..0000000 --- a/src/Api/src/controller/user-controller/userCtrl.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { Router, Request, Response, NextFunction, RequestHandler } from 'express'; -import Controller from '../Icontroller'; -import HttpException from '../../middleware/exeption/httpExeption'; -// import LocationService from '../../service/LocationService'; -import IUser from '../../database/schema/User/UserInterface'; -import UserService from '../../service/UserService'; -import validator from '../../database/schema/User/UserValidation' -import validationMiddleware from '../../middleware/validation/ValidatorMiddleware'; -import authenticator from '../../middleware/authMiddleware' -import LocationService from '../../service/LocationService'; -class UserController implements Controller { - public path = '/users'; - public router = Router(); - private userService = new UserService(); - private locationService = new LocationService(); - - constructor() { - this.initialiseRoutes(); - } - - private initialiseRoutes(): void { - this.router.post( - `${this.path}/register`, - validationMiddleware(validator.register), - this.register - ); - this.router.post( - `${this.path}/login`, - validationMiddleware(validator.login), - this.login - ); - this.router.get(`${this.path}`, authenticator, this.getUser); - this.router.get(`${this.path}/nextTo`, authenticator, this.getUserNext); - - - // //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 => { - // 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 => { - // 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 => { - // 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 => { - // 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 => { - // 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 register = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - try { - // the FladId should be created by the Userservice - const { name, email, password , idFlad, idSpotify } = req.body; - console.log(name, email, password, idFlad, idSpotify); - - const token = await this.userService.register( - name, - email, - password, - idFlad, - idSpotify - ); - - res.status(201).json({ token }); - } catch (error : any) { - next(new HttpException(400, error.message)); - } - }; - - private login = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - try { - const { email, password } = req.body; - - const token = await this.userService.login(email, password); - - res.status(200).json({ token }); - } catch (error : any) { - next(new HttpException(400, error.message)); - } - }; - - private getUser = ( - req: Request, - res: Response, - next: NextFunction - ): Response | void => { - if (!req.user) { - return next(new HttpException(404, 'No logged in user')); - } - - res.status(200).send({ data: req.user }); - }; - - private getUserNext = async ( - req: Request, - res: Response, - next: NextFunction - ): Promise => { - try { - const longitude = Number(req.query.longitude); - const latitude = Number(req.query.latitude); - //verify::val_int(){ - console.log('woooooooooooooo' + req); - if (isNaN(longitude) || isNaN(latitude)) { - console.log('============' + longitude) - console.log('============' + latitude) - console.log('Impossible de convertir la chaîne en nombre'); - } - //} - const userId = req.user.idFlad; - const musicId = String(req.query.currentMusic); - console.log('============' + longitude) - console.log('============' + latitude) - console.log('daaaaaaaaaaaaaaaaaaaaaa' + musicId); - const data = await this.locationService.getNearUser(userId,musicId,latitude,longitude); - console.log(data); - res.status(201).send(data); - - } - catch(error : any){ - next(new HttpException(400, 'Cannot create get netUser')); - } - - } - - -} - -export default UserController; - - - -declare global { - namespace Express { - export interface Request { - user: IUser; - } - } -} diff --git a/src/Api/src/controllers/interfaces/IController.ts b/src/Api/src/controllers/interfaces/IController.ts new file mode 100644 index 0000000..76dd956 --- /dev/null +++ b/src/Api/src/controllers/interfaces/IController.ts @@ -0,0 +1,8 @@ +import { Router } from 'express'; + +interface IController { + path: string; + router: Router; +} + +export default IController; \ No newline at end of file diff --git a/src/Api/src/controllers/spotifyController.ts b/src/Api/src/controllers/spotifyController.ts new file mode 100644 index 0000000..42a75b0 --- /dev/null +++ b/src/Api/src/controllers/spotifyController.ts @@ -0,0 +1,196 @@ +import IController from './interfaces/IController'; +import { Router, Request, Response } from 'express'; +import axios from 'axios'; +import qs from 'qs'; + +class SpotifyController implements IController { + public path = '/spotify'; + public router = Router(); + public readonly CLIENT_ID_SPOTIFY = process.env.CLIENT_ID_SPOTIFY; + public readonly CLIENT_SECRET_SPOTIFY = process.env.CLIENT_SECRET_SPOTIFY; + private readonly API_URL = "https://accounts.spotify.com/api/token"; + private readonly CALLBACK = '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'; + + constructor() { + this.initRoutes(); + } + + initRoutes() { + /** + * @swagger + * /api/spotify/exchange: + * get: + * summary: Initiate the Spotify login flow + * description: Redirect the user to the Spotify login page for authorization + * tags: + * - Spotify + * parameters: + * - in: query + * name: redirectUrl + * schema: + * type: string + * description: The URL to redirect the user after Spotify authorization (optional) + * responses: + * 302: + * description: Redirecting to Spotify login page + * 400: + * description: Bad request - Cannot connect to Spotify + */ + this.router.get(`${this.path}/exchange`, this.login); + + /** + * @swagger + * /api/spotify/callback: + * get: + * summary: Handle Spotify callback and exchange code for access token + * description: Handle Spotify callback and exchange the received code for an access token + * tags: + * - Spotify + * responses: + * 302: + * description: Redirecting with access token information + * 400: + * description: Bad request - Error connecting to Spotify + */ + this.router.get(`${this.path}/callback`, this.getAccessToken); + + /** + * @swagger + * /api/spotify/refresh: + * get: + * summary: Refresh the Spotify access token using a refresh token + * description: Refresh the Spotify access token using a refresh token + * tags: + * - Spotify + * parameters: + * - in: query + * name: refresh_token + * schema: + * type: string + * required: true + * description: The refresh token obtained during the initial authorization + * responses: + * 200: + * description: Successfully refreshed access token + * content: + * application/json: + * schema: + * type: object + * properties: + * access_token: + * type: string + * description: The new access token + * refresh_token: + * type: string + * description: The new refresh token + * expires_in: + * type: number + * description: The time until the access token expires (in seconds) + * 400: + * description: Bad request - Cannot refresh the access token + */ + this.router.get(`${this.path}/refresh`, this.getRefreshToken); + } + + private readonly clientRedirect = 'spotify_final_redirect-uri-key'; + + private login = async ( + req: Request, + res: Response + ): Promise => { + try { + const redirectResponse = req.query.redirectUrl ? req.query.redirectUrl : req.headers.referer; + res.cookie(this.clientRedirect, redirectResponse); + res.redirect('https://accounts.spotify.com/authorize?' + + qs.stringify({ + response_type: 'code', + client_id: this.CLIENT_ID_SPOTIFY, + scope: this.SCOPES, + redirect_uri: this.CALLBACK, + })); + } catch (error) { + res.status(400).send('Cannot connect: ' + error.message); + } + }; + + private getRefreshToken = async ( + req: Request, + res: Response + ): Promise => { + const params = req.query.refresh_token; + if (!req.query.refresh_token) { + return res.json({ + "error": "Parameter refresh_token missing" + }); + } + const 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_SPOTIFY + ':' + this.CLIENT_SECRET_SPOTIFY).toString('base64')), + 'Content-Type': 'application/x-www-form-urlencoded' + }, + json: true + }; + + axios(authOptions) + .then(session => { + if (session.status === 200) { + res.send({ + "access_token": session.data.access_token, + "refresh_token": session.data.refresh_token, + "expires_in": session.data.expires_in + }); + } + }) + .catch(error => { + res.status(400).send("Cannot get a new refresh token"); + }); + } + + private getAccessToken = async ( + req: Request, + res: Response + ): Promise => { + let code = req.query.code; + let storedRedirectUri = req.cookies ? req.cookies[this.clientRedirect] : null; + let authOptions = { + method: 'POST', + url: this.API_URL, + data: qs.stringify({ + code: code, + redirect_uri: this.CALLBACK, + grant_type: 'authorization_code' + }), + headers: { + 'Authorization': 'Basic ' + (Buffer.from(this.CLIENT_ID_SPOTIFY + ':' + this.CLIENT_SECRET_SPOTIFY).toString('base64')), + 'Content-Type': 'application/x-www-form-urlencoded' + }, + json: true + }; + try { + const resp = await axios(authOptions); + if (resp.status === 200) { + let access_token = resp.data.access_token; + let expiration = resp.data.expires_in; + let refresh = resp.data.refresh_token + res.clearCookie(this.clientRedirect); + res.redirect(`${storedRedirectUri}?` + + qs.stringify({ + "access_token": access_token, + "expires_in": expiration, + "refresh_token": refresh + })); + } + } catch (error) { + res.status(400).send('Error connection: ' + error.message); + } + }; +} + +export default SpotifyController; \ No newline at end of file diff --git a/src/Api/src/controllers/userController.ts b/src/Api/src/controllers/userController.ts new file mode 100644 index 0000000..7e3866c --- /dev/null +++ b/src/Api/src/controllers/userController.ts @@ -0,0 +1,803 @@ +import { Router, Request, Response } from 'express'; +import IController from './interfaces/IController'; +import User from '../models/User'; +import UserService from '../services/UserService'; +import validator from '../middlewares/UserValidation' +import validationMiddleware from '../middlewares/validationMiddleware'; +import authenticator from '../middlewares/authMiddleware' +import LocationService from '../services/LocationService'; +import axios from 'axios'; +import { IMusic } from '../models/Music'; +import * as fs from 'fs'; +import * as base64js from 'base64-js'; + +class UserController implements IController { + public path = '/user'; + public authPath = '/auth'; + public router = Router(); + private userService = new UserService(); + private locationService = new LocationService(); + + constructor() { + this.initRoutes(); + } + + private initRoutes(): void { + /** + * @swagger + * /api/auth/register: + * post: + * summary: Register a new user + * description: Register a new user with the provided details + * tags: + * - Authentication + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * email: + * type: string + * default: john.doe@example.com + * password: + * type: string + * default: stringPassword123 + * name: + * type: string + * default: john_doe + * tokenSpotify: + * type: string + * responses: + * 201: + * description: User registered successfully + * 400: + * description: Bad request - Invalid input data + * 401: + * description: Unauthorized - Spotify token is invalid + * 409: + * description: Conflict - Email or username is already in use + * 500: + * description: Internal Server Error - Spotify account not authorized or not found + */ + this.router.post( + `${this.authPath}/register`, + validationMiddleware(validator.register), + this.register + ); + + /** + * @swagger + * /api/auth/login: + * post: + * summary: Login a user + * description: Login with the provided email and password + * tags: + * - Authentication + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * email: + * type: string + * default: john.doe@example.com + * password: + * type: string + * default: stringPassword123 + * responses: + * 200: + * description: User logged in successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * token: + * type: string + * 400: + * description: Bad request - Invalid input data + */ + this.router.post( + `${this.authPath}/login`, + validationMiddleware(validator.login), + this.login + ); + + /** + * @swagger + * /api/user: + * get: + * summary: Get user information + * description: Get information about the authenticated user + * tags: + * - User + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: User logged in successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + */ + this.router.get(`${this.path}`, authenticator, this.getUser); + + + /** + * @swagger + * /api/users: + * get: + * summary: Get information about multiple users + * description: Get information about multiple users based on provided user ids + * tags: + * - User + * security: + * - bearerAuth: [] + * parameters: + * - in: query + * name: ids + * schema: + * type: string + * description: Comma-separated list of user ids + * responses: + * 200: + * description: Users information retrieved successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + */ + this.router.get(`${this.path}s`, authenticator, this.getUsers); + + /** + * @swagger + * /api/user: + * delete: + * summary: Delete the authenticated user + * description: Delete the authenticated user and associated data + * tags: + * - User + * security: + * - bearerAuth: [] + * responses: + * 204: + * description: User deleted successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 404: + * description: User not found + */ + this.router.delete(`${this.path}`, authenticator, this.deleteUser); + + /** + * @swagger + * /api/user/nextTo: + * get: + * summary: Get users near the authenticated user + * description: Get information about users near the authenticated user based on location + * tags: + * - User + * security: + * - bearerAuth: [] + * parameters: + * - in: query + * name: longitude + * schema: + * type: number + * description: Longitude of the user's current location + * - in: query + * name: latitude + * schema: + * type: number + * description: Latitude of the user's current location + * - in: query + * name: currentMusic + * schema: + * type: string + * description: The ID of the currently playing music + * responses: + * 201: + * description: Users near the authenticated user retrieved successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * data: + * type: array + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 400: + * description: Bad request - Invalid input data + */ + this.router.get(`${this.path}/nextTo`, authenticator, this.getUserNext); + + /** + * @swagger + * /api/user/musics/{id}: + * delete: + * summary: Delete a music from the authenticated user's liked list + * description: Delete a music from the authenticated user's liked list by music id + * tags: + * - User + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * schema: + * type: string + * description: The ID of the music to delete + * responses: + * 200: + * description: Music deleted successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 404: + * description: Music not found + */ + this.router.delete(`${this.path}/musics/:id`, authenticator, this.deleteMusic); + + /** + * @swagger + * /api/user/musics: + * post: + * summary: Add a music to the authenticated user's liked list + * description: Add a music to the authenticated user's liked list + * tags: + * - User + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * musicId: + * type: string + * description: The ID of the music to add + * userId: + * type: string + * description: The ID of the user who liked the music + * responses: + * 201: + * description: Music added to liked list successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 400: + * description: Bad request - Invalid input data + */ + this.router.post(`${this.path}/musics`, authenticator, this.addMusic); + + /** + * @swagger + * /api/user/musics: + * get: + * summary: Get the list of musics liked by the authenticated user + * description: Get the list of musics liked by the authenticated user + * tags: + * - User + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: List of musics retrieved successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * musics: + * type: array + * 401: + * description: Unauthorized - Invalid or missing authentication token + */ + this.router.get(`${this.path}/musics`, authenticator, this.getMusics); + + /** + * @swagger + * /api/user/name: + * put: + * summary: Update the name of the authenticated user + * description: Update the name of the authenticated user + * tags: + * - User + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * name: + * type: string + * description: The new name for the user + * responses: + * 200: + * description: User name updated successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 400: + * description: Bad request - Invalid input data + * 409: + * description: Conflict - The provided name is already in use by another user + */ + this.router.put(`${this.path}/name`, authenticator, this.setName); + + /** + * @swagger + * /api/user/email: + * put: + * summary: Update the email of the authenticated user + * description: Update the email of the authenticated user + * tags: + * - User + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * email: + * type: string + * format: email + * description: The new email for the user + * responses: + * 200: + * description: User email updated successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 400: + * description: Bad request - Invalid input data + * 409: + * description: Conflict - The provided email is already in use by another user + */ + this.router.put(`${this.path}/email`, authenticator, this.setEmail); + + /** + * @swagger + * /api/user/spotify: + * put: + * summary: Update the spotify account + * description: Update the spotify account of the authenticated user + * tags: + * - User + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * tokenSpotify: + * type: string + * description: Spotify token + * responses: + * 200: + * description: Spotify account updated successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 409: + * description: Conflict - The provided token is already in use by another user + * 500: + * description: Internal Server Error - Spotify account not authorized or not found + */ + this.router.put(`${this.path}/spotify`, authenticator, this.setSpotify); + + /** + * @swagger + * /api/user/image: + * put: + * summary: Update the profile image of the authenticated user + * description: Update the profile image of the authenticated user + * tags: + * - User + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * image: + * type: string + * format: base64 + * description: The new profile image for the user (base64 encoded) + * responses: + * 200: + * description: User profile image updated successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 500: + * description: Internal Server Error - Unable to update the profile image + */ + this.router.put(`${this.path}/image`, authenticator, this.setImage); + + /** + * @swagger + * /api/user/password: + * put: + * summary: Update the password of the authenticated user + * description: Update the password of the authenticated user + * tags: + * - User + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * oldPassword: + * type: string + * description: The current password of the user + * newPassword: + * type: string + * description: The new password for the user + * responses: + * 200: + * description: User password updated successfully + * 401: + * description: Unauthorized - Invalid or missing authentication token + * 500: + * description: Internal Server Error - Unable to update the password + */ + this.router.put(`${this.path}/password`, authenticator, this.setPassword); + } + + private register = async ( + req: Request, + res: Response + ): Promise => { + + let access_token; + let idSpotify: string; + let image: string; + const { name, email, password, tokenSpotify } = req.body; + const apiBaseUrl = process.env.API_BASE_URL || 'http://localhost:8080/api'; + const refreshUrl = `${apiBaseUrl}/spotify/refresh?refresh_token=${tokenSpotify}`; + try { + const authOptions = { + method: 'GET', + url: refreshUrl, + json: true + }; + const authResponse = await axios(authOptions); + if (authResponse.status === 200) { + access_token = authResponse.data.access_token; + const headers = { + Authorization: `Bearer ${access_token}`, + }; + const resp = await axios.get('https://api.spotify.com/v1/me', { headers }); + if (resp.status == 200) { + const images = resp.data.images; + idSpotify = resp.data.id; + if (images && images.length > 0) { + images.sort((a: any, b: any) => b.height - a.height); + image = images[0].url; + } + else { + const imagePath = './src/assets/images/default_user.png'; + const imageBuffer = fs.readFileSync(imagePath); + const base64Image = 'data:image/png;base64,' + base64js.fromByteArray(imageBuffer); + image = base64Image + } + } + } + } catch (error: any) { + console.log(error); + if (error.response.status === 400) { + res.status(401).send("Unauthorized: Spotify token is invalid"); + return; + } + res.status(500).send("Internal Server Error: Unable to authenticate with Spotify"); + return; + } + + try { + const token = await this.userService.register( + name.toLowerCase(), + email.toLowerCase(), + password, + idSpotify, + tokenSpotify, + image + ); + res.status(201).json({ token }); + } catch (error: any) { + res.status(409).json(error.message); + } + }; + + private login = async ( + req: Request, + res: Response + ): Promise => { + try { + const { email, password } = req.body; + const token = await this.userService.login(email, password); + res.status(200).json({ token }); + } catch (error: any) { + res.status(400).json(error.message) + } + }; + + private getUser = ( + req: Request, + res: Response + ): Response | void => { + res.status(200).send({ data: req.user }); + }; + + private getUsers = async ( + req: Request, + res: Response + ): Promise => { + const userIds = req.query.ids as string; + + if (!userIds) { + return res.status(200).json([]); + } + + const userIdArray = userIds.split('&'); + + try { + const users = await this.userService.getUsers(userIdArray); + res.json(users); + } catch (error) { + res.status(500).json({ message: error.message }); + } + }; + + private deleteUser = async ( + req: Request, + res: Response + ): Promise => { + try { + const { _id } = req.user; + await this.userService.delete(_id); + await this.locationService.delete(_id); + res.status(204).send(); + } catch (error: any) { + res.status(404).json(error.message) + } + }; + + private getUserNext = async ( + req: Request, + res: Response + ): Promise => { + try { + const longitude = Number(req.query.longitude); + const latitude = Number(req.query.latitude); + if (isNaN(longitude) || isNaN(latitude)) { + console.log('Unable to convert string to number'); + throw new Error('Unable to convert string to number'); + } + const userId = req.user.id; + const musicId = String(req.query.currentMusic); + const data = await this.locationService.getNearUser(userId, musicId, latitude, longitude); + res.status(201).send(data); + } + catch (error: any) { + res.status(400).json(error.message) + } + } + + private deleteMusic = async ( + req: Request, + res: Response + ): Promise => { + try { + const { _id } = req.user; + const musicId: string = req.params.id; + if (!musicId) { + return res.status(400).json({ error: 'musicId are required fields.' }); + } + + const deleted = await this.userService.deleteMusic(_id, musicId); + + if (deleted) { + res.status(200).send({ message: 'Music deleted successfully.' }); + } else { + res.status(404).json({ error: 'Music not found.' }); + } + + } catch (error: any) { + res.status(404).json(error.message) + } + } + + private addMusic = async ( + req: Request, + res: Response + ): Promise => { + try { + const { _id } = req.user; + const { musicId, userId } = req.body; + if (!musicId || !userId) { + return res.status(400).json({ error: 'musicId and userId are required fields.' }); + } + const music: IMusic = { + musicId, + userId, + date: new Date(), + }; + await this.userService.addMusic(_id, music); + res.status(201).send({ music }); + } catch (error: any) { + res.status(400).json(error.message) + } + } + + private getMusics = async ( + req: Request, + res: Response + ): Promise => { + try { + const userId: string = req.user.id; + const musics = await this.userService.getMusics(userId); + return res.status(200).json({ musics }); + } catch (error: any) { + res.status(400).json(error.message) + } + } + + private setName = async ( + req: Request, + res: Response + ): Promise => { + try { + const { _id } = req.user; + const { name } = req.body; + + const regex = /^\w+$/; + if (!regex.test(name) || !name) { + return res.status(400).json({ error: "Name should only contain alphanumeric characters (letters, numbers, and underscores)" }); + } + + await this.userService.setName(_id, name.toLowerCase()); + + res.status(200).json({ message: 'Name updated successfully' }); + } catch (error: any) { + res.status(409).json(error.message) + } + } + + private setEmail = async ( + req: Request, + res: Response + ): Promise => { + try { + const { _id } = req.user; + const { email } = req.body; + + const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; + if (!regex.test(email) || !email) { + return res.status(400).json({ error: "Invalid email" }); + } + + await this.userService.setEmail(_id, email.toLowerCase()); + + res.status(200).json({ message: 'Email updated successfully' }); + } catch (error: any) { + res.status(409).json(error.message) + } + } + + private setSpotify = async ( + req: Request, + res: Response + ): Promise => { + let access_token; + let idAccount: string; + let image: string; + const { _id, idSpotify } = req.user; + const { tokenSpotify } = req.body; + + if (!tokenSpotify) { + return res.status(400).json({ error: 'TokenSpotify is missing in the request.' }); + } + + const apiBaseUrl = process.env.API_BASE_URL || 'http://localhost:8080/api'; + const refreshUrl = `${apiBaseUrl}/spotify/refresh?refresh_token=${tokenSpotify}`; + try { + const authOptions = { + method: 'GET', + url: refreshUrl, + json: true + }; + const authResponse = await axios(authOptions); + if (authResponse.status === 200) { + access_token = authResponse.data.access_token; + const headers = { + Authorization: `Bearer ${access_token}`, + }; + const resp = await axios.get('https://api.spotify.com/v1/me', { headers }); + if (resp.status == 200) { + const images = resp.data.images; + idAccount = resp.data.id; + if (idSpotify === idAccount) { + return res.status(400).json({ error: 'idSpotify cannot be the same as idAccount.' }); + } + if (images && images.length > 0) { + images.sort((a: any, b: any) => b.height - a.height); + image = images[0].url; + } + else { + const imagePath = './src/assets/images/default_user.png'; + const imageBuffer = fs.readFileSync(imagePath); + const base64Image = 'data:image/png;base64,' + base64js.fromByteArray(imageBuffer); + image = base64Image + } + } + } + } catch (error: any) { + console.log(error); + res.status(500).send("Internal Server Error: Unable to authenticate with Spotify"); + return; + } + try { + await this.userService.setSpotify(_id, tokenSpotify, idAccount, image); + + res.status(200).json({ message: 'Spotify token updated successfully' }); + } catch (error: any) { + res.status(409).json(error.message) + } + } + + private setImage = async ( + req: Request, + res: Response + ): Promise => { + try { + const { _id } = req.user; + const { image } = req.body; + + await this.userService.setImage(_id, image); + + res.status(200).json({ message: 'Image updated successfully' }); + } catch (error: any) { + res.status(500).json(error.message) + } + } + + private setPassword = async ( + req: Request, + res: Response + ): Promise => { + try { + const { _id } = req.user; + const { oldPassword, newPassword } = req.body; + + await this.userService.setPassword(_id, oldPassword, newPassword); + + res.status(200).json({ message: 'Password updated successfully' }); + } catch (error: any) { + res.status(500).json(error.message) + } + } +} +export default UserController; + +declare global { + namespace Express { + export interface Request { + user: User; + } + } +} diff --git a/src/Api/src/database/LocationSchema.ts b/src/Api/src/database/LocationSchema.ts new file mode 100644 index 0000000..1145d17 --- /dev/null +++ b/src/Api/src/database/LocationSchema.ts @@ -0,0 +1,26 @@ +import { Schema, model } from 'mongoose'; +import { Location } from '../models/Location'; + +const locationSchema = new Schema({ + userId: { + type: String, + required: true, + unique: true, + }, + musicId: { + type: String, + required: true, + }, + latitude: { + type: Number, + required: true, + }, + longitude: { + type: Number, + required: true, + } +}, + { timestamps: true } +); + +export default model('Location', locationSchema); \ No newline at end of file diff --git a/src/Api/src/database/MongoDataBase.ts b/src/Api/src/database/MongoDataBase.ts deleted file mode 100644 index dd8f5d3..0000000 --- a/src/Api/src/database/MongoDataBase.ts +++ /dev/null @@ -1,4 +0,0 @@ - -// export default db = new MongoClient(uri); - - diff --git a/src/Api/src/database/StrategyDatabase.ts b/src/Api/src/database/StrategyDatabase.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/Api/src/database/UserSchema.ts b/src/Api/src/database/UserSchema.ts new file mode 100644 index 0000000..1d90764 --- /dev/null +++ b/src/Api/src/database/UserSchema.ts @@ -0,0 +1,62 @@ +import User from "../models/User"; +import { Schema, model } from 'mongoose'; +import bcrypt from 'bcrypt'; + +const userSchema = new Schema({ + idSpotify: { + type: String, + required: true, + unique: true + }, + tokenSpotify: { + type: String, + required: true + }, + name: { + type: String, + required: true, + unique: true + }, + email: { + type: String, + required: true, + unique: true, + trim: true + }, + password: { + type: String, + required: true + } + , + image: { + type: String, + required: true + }, + musics_likes: { + type: [{ + musicId: String, + userId: String, + date: Date + }], + default: [] + } +}, + { timestamps: true } +); + +userSchema.pre('save', async function (next) { + if (!this.isModified('password')) { + return next(); + } + const hash = await bcrypt.hash(this.password, 8); + this.password = hash; + next(); +}); + +userSchema.methods.isValidPassword = async function ( + password: string +): Promise { + return await bcrypt.compare(password, this.password); +}; + +export default model('User', userSchema); \ No newline at end of file diff --git a/src/Api/src/database/schema/LocationSchema.ts b/src/Api/src/database/schema/LocationSchema.ts deleted file mode 100644 index 66bce58..0000000 --- a/src/Api/src/database/schema/LocationSchema.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Schema, model,Document } from 'mongoose'; - - -const locationSchema = new Schema( - { - - idFlad: { - type: String, - required: true, - unique: true, - }, - musicId: { - type: String, - required: true, - }, - latitude: { - type: Number, - required: true, - }, - longitude: { - type: Number, - required: true, - }, - - - }, - { timestamps: true } -); - -// fladDevDb -// ZslYlNRWIOUU7i6o -export default model('Location', locationSchema); - -export interface ILocation extends Document { - idFlad: string; - musicId : string; - latitude : number; - longitude: number; -} \ No newline at end of file diff --git a/src/Api/src/database/schema/NotificationSchema.ts b/src/Api/src/database/schema/NotificationSchema.ts deleted file mode 100644 index 7207503..0000000 --- a/src/Api/src/database/schema/NotificationSchema.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Schema, model } from 'mongoose'; - -const notificationSchema = new Schema({ - type: {type: String, required: true}, - content: {type: String, required: true} -}); - -export default {Notification: model("nofitication", notificationSchema)} \ No newline at end of file diff --git a/src/Api/src/database/schema/User/UserInterface.ts b/src/Api/src/database/schema/User/UserInterface.ts deleted file mode 100644 index 2308f88..0000000 --- a/src/Api/src/database/schema/User/UserInterface.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Document } from 'mongoose'; - -export default interface IUser extends Document { - email: string; - name: string; - password: string; - idFlad : string; - idSpotify : string; - - isValidPassword(password: string): Promise; -} \ No newline at end of file diff --git a/src/Api/src/database/schema/User/UserSchema.ts b/src/Api/src/database/schema/User/UserSchema.ts deleted file mode 100644 index 8f8cdd5..0000000 --- a/src/Api/src/database/schema/User/UserSchema.ts +++ /dev/null @@ -1,79 +0,0 @@ -// maye this file should me the UserModel like we had in php cause it's here we verrify the password -import IUser from "./UserInterface"; -import { Schema, model } from 'mongoose'; -import bcrypt from 'bcrypt'; -// const userSchema: Schema = new Schema({ -// 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] } -// }); - -const userSchema = new Schema( - { - - idFlad: { - type: String, - required: true, - unique: true, - }, - idSpotify: { - type: String, - required: true, - unique: true, - }, - name: { - type: String, - required: true, - }, - email: { - type: String, - required: true, - // this mean that we identify user by email - unique: true, - // delete the whitespace - trim: true, - }, - password: { - type: String, - }, - - }, - { timestamps: true } -); - -// this means that we hash the user password before saving it to the database -userSchema.pre('save', async function (next) { - if (!this.isModified('password')) { - //just had that to be sure that the api still going - return next(); - } - - const hash = await bcrypt.hash(this.password, 8); - - this.password = hash; - - next(); -}); - -userSchema.methods.isValidPassword = async function ( - password: string -): Promise< boolean | Error> { - return await bcrypt.compare(password, this.password); -}; - -// fladDevDb -// ZslYlNRWIOUU7i6o -export default model('User', userSchema); -// export const User: Model = model('User', userSchema); diff --git a/src/Api/src/middleware/exeption/httpExeption.ts b/src/Api/src/exception/HttpException.ts similarity index 73% rename from src/Api/src/middleware/exeption/httpExeption.ts rename to src/Api/src/exception/HttpException.ts index ae972b5..d4e9d9b 100644 --- a/src/Api/src/middleware/exeption/httpExeption.ts +++ b/src/Api/src/exception/HttpException.ts @@ -8,6 +8,5 @@ class HttpException extends Error { this.message = message; } } -// en fontion de l'exeption firebas,etc une bonne exeption export default HttpException; \ No newline at end of file diff --git a/src/Api/src/index.ts b/src/Api/src/index.ts index 54ec830..c2f022e 100644 --- a/src/Api/src/index.ts +++ b/src/Api/src/index.ts @@ -1,16 +1,12 @@ import App from "./app"; -import SpotifyController from "./controller/spotify-controller/spotifyCtrl"; -import PingController from "./controller/TestCtrl"; -import UserController from "./controller/user-controller/userCtrl"; +import SpotifyController from "./controllers/spotifyController"; +import UserController from "./controllers/userController"; import dotenv from 'dotenv' + dotenv.config(); const app = new App( - [new PingController(), new SpotifyController(), new UserController()], + [new SpotifyController(), new UserController()], Number(process.env.PORT) - ); -app.listen(); - - - +app.listen(); \ No newline at end of file diff --git a/src/Api/src/middleware/authMiddleware.ts b/src/Api/src/middleware/authMiddleware.ts deleted file mode 100644 index bb0f875..0000000 --- a/src/Api/src/middleware/authMiddleware.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; -import jwt from 'jsonwebtoken'; -import IToken from '../database/schema/Token/IToken'; -import UserSchema from '../database/schema/User/UserSchema'; -import token from '../model/token'; -import HttpException from './exeption/httpExeption'; - -async function authenticatedMiddleware( - req: Request, - res: Response, - next: NextFunction -): Promise { - const bearer = req.headers.authorization; - - if (!bearer || !bearer.startsWith('Bearer ')) { - return next(new HttpException(401, 'Unauthorised')); - } - - const accessToken = bearer.split('Bearer ')[1].trim(); - try { - const payload: IToken | jwt.JsonWebTokenError = await token.verifyToken( - accessToken - ); - - if (payload instanceof jwt.JsonWebTokenError) { - return next(new HttpException(401, 'Unauthorised')); - } - - const user = await UserSchema.findById(payload.id) - .select('-password') - .exec(); - - if (!user) { - return next(new HttpException(401, 'Unauthorised')); - } - - req.user = user; - - return next(); - } catch (error) { - return next(new HttpException(401, 'Unauthorised')); - } -} - -export default authenticatedMiddleware; \ No newline at end of file diff --git a/src/Api/src/middleware/winston.ts b/src/Api/src/middleware/winston.ts deleted file mode 100644 index 87a8e70..0000000 --- a/src/Api/src/middleware/winston.ts +++ /dev/null @@ -1,8 +0,0 @@ -// 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 }) -// ), -// }; \ No newline at end of file diff --git a/src/Api/src/database/schema/User/UserValidation.ts b/src/Api/src/middlewares/UserValidation.ts similarity index 52% rename from src/Api/src/database/schema/User/UserValidation.ts rename to src/Api/src/middlewares/UserValidation.ts index e00e988..f900307 100644 --- a/src/Api/src/database/schema/User/UserValidation.ts +++ b/src/Api/src/middlewares/UserValidation.ts @@ -1,14 +1,11 @@ import Joi from 'joi'; const register = Joi.object({ - name: Joi.string().max(30).required(), - + name: Joi.string().max(30).required().regex(/^\w+$/) + .message("Name should only contain alphanumeric characters (letters, numbers, and underscores)"), email: Joi.string().email().required(), - 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(), + tokenSpotify: Joi.string().required() }); const login = Joi.object({ diff --git a/src/Api/src/middlewares/authMiddleware.ts b/src/Api/src/middlewares/authMiddleware.ts new file mode 100644 index 0000000..a9b5550 --- /dev/null +++ b/src/Api/src/middlewares/authMiddleware.ts @@ -0,0 +1,45 @@ +import { Request, Response, NextFunction } from 'express'; +import jwt from 'jsonwebtoken'; +import Token from '../models/Token'; +import UserSchema from '../database/UserSchema'; +import token from '../services/TokenService'; +import HttpException from '../exception/HttpException'; + +async function authMiddleware( + req: Request, + res: Response, + next: NextFunction +): Promise { + const bearer = req.headers.authorization; + + if (!bearer || !bearer.startsWith('Bearer ')) { + return next(new HttpException(401, 'Unauthorized')); + } + + const accessToken = bearer.split('Bearer')[1].trim(); + try { + const payload: Token | jwt.JsonWebTokenError = await token.verifyToken( + accessToken + ); + + if (payload instanceof jwt.JsonWebTokenError) { + return next(new HttpException(401, 'Unauthorized')); + } + + const user = await UserSchema.findById(payload.id) + .select('-password') + .exec(); + + if (!user) { + return next(new HttpException(401, 'Unauthorized')); + } + + req.user = user; + + return next(); + } catch (error) { + return next(new HttpException(401, 'Unauthorized')); + } +} + +export default authMiddleware; \ No newline at end of file diff --git a/src/Api/src/middleware/validation/ValidatorMiddleware.ts b/src/Api/src/middlewares/validationMiddleware.ts similarity index 100% rename from src/Api/src/middleware/validation/ValidatorMiddleware.ts rename to src/Api/src/middlewares/validationMiddleware.ts diff --git a/src/Api/src/model/IUser.ts b/src/Api/src/model/IUser.ts deleted file mode 100644 index 2084e48..0000000 --- a/src/Api/src/model/IUser.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default interface IUser{ - name: string; - email: string; - avatar?: string; - } \ No newline at end of file diff --git a/src/Api/src/model/locationModel.ts b/src/Api/src/model/locationModel.ts deleted file mode 100644 index 0674db8..0000000 --- a/src/Api/src/model/locationModel.ts +++ /dev/null @@ -1,59 +0,0 @@ -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; - musicId : string; - constructor(uuid: string, musicId : string,latitude: number, longitude: number){ - this.uuid = uuid; - this.musicId = musicId; - 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; -} \ No newline at end of file diff --git a/src/Api/src/model/token.ts b/src/Api/src/model/token.ts deleted file mode 100644 index ebc6f65..0000000 --- a/src/Api/src/model/token.ts +++ /dev/null @@ -1,27 +0,0 @@ -import jwt from 'jsonwebtoken'; -import IUser from '../database/schema/User/UserInterface'; -import IToken from '../database/schema/Token/IToken'; - -export const createToken = (user: IUser): string => { - return jwt.sign({ id: user._id }, "foo" as jwt.Secret, { - expiresIn: '100d', - }); -}; - -export const verifyToken = async ( - token: string -): Promise => { - return new Promise((resolve, reject) => { - jwt.verify( - token, - "foo" as jwt.Secret, - (err, payload) => { - if (err) return reject(err); - - resolve(payload as IToken); - } - ); - }); -}; - -export default { createToken, verifyToken }; \ No newline at end of file diff --git a/src/Api/src/models/Location.ts b/src/Api/src/models/Location.ts new file mode 100644 index 0000000..39a9251 --- /dev/null +++ b/src/Api/src/models/Location.ts @@ -0,0 +1,24 @@ +import { Document } from 'mongoose'; + +export class UserLocation { + _id: string; + userId: string; + musicId: string; + distance: number; + date: Date; + constructor(id: string, userId: string, musicId: string, distance: number, date: Date) { + this._id = id; + this.userId = userId; + this.musicId = musicId; + this.distance = distance; + this.date = date; + } +} + +export class Location extends Document { + userId: string; + musicId: string; + latitude: number; + longitude: number; + updatedAt: Date; +} \ No newline at end of file diff --git a/src/Api/src/models/Music.ts b/src/Api/src/models/Music.ts new file mode 100644 index 0000000..9da69eb --- /dev/null +++ b/src/Api/src/models/Music.ts @@ -0,0 +1,5 @@ +export interface IMusic { + musicId: string; + userId: string; + date: Date; +} \ No newline at end of file diff --git a/src/Api/src/models/Person.ts b/src/Api/src/models/Person.ts new file mode 100644 index 0000000..22abca9 --- /dev/null +++ b/src/Api/src/models/Person.ts @@ -0,0 +1,5 @@ +export interface IPerson { + _id: string; + name: string; + image: string; +} \ No newline at end of file diff --git a/src/Api/src/database/schema/Token/IToken.ts b/src/Api/src/models/Token.ts similarity index 60% rename from src/Api/src/database/schema/Token/IToken.ts rename to src/Api/src/models/Token.ts index ff6e4af..943439e 100644 --- a/src/Api/src/database/schema/Token/IToken.ts +++ b/src/Api/src/models/Token.ts @@ -1,8 +1,6 @@ import { Schema } from 'mongoose'; -interface IToken extends Object { +export default interface Token extends Object { id: Schema.Types.ObjectId; expiresIn: number; -} - -export default IToken; \ No newline at end of file +} \ No newline at end of file diff --git a/src/Api/src/models/User.ts b/src/Api/src/models/User.ts new file mode 100644 index 0000000..1065844 --- /dev/null +++ b/src/Api/src/models/User.ts @@ -0,0 +1,13 @@ +import { Document } from 'mongoose'; +import { IMusic } from './Music'; + +export default interface User extends Document { + idSpotify: string; + tokenSpotify: string; + name: string; + email: string; + password: string; + isValidPassword(password: string): Promise; + image: string; + musics_likes: IMusic[]; +} \ No newline at end of file diff --git a/src/Api/src/service/LocationService.ts b/src/Api/src/service/LocationService.ts deleted file mode 100644 index 52e65b9..0000000 --- a/src/Api/src/service/LocationService.ts +++ /dev/null @@ -1,139 +0,0 @@ -// import db from '../database'; -import { Place, PlacePosition, Position, UserLocation } from '../model/locationModel'; -import axios from 'axios'; -import LocationSchema from "../database/schema/LocationSchema"; - -class LocationService { - private locationCollection = LocationSchema; - // private API_KEY : string = "AIzaSyBFCEAtmhZ8jvw84UTQvX3Aqpr66GVqB_A"; - public async getNearUser(idFlad : string, musicId : string,latitude : number, longitude : number) - { - await this.locationCollection.findOneAndUpdate( - { idFlad }, - { idFlad, musicId, latitude, longitude }, - { upsert: true } - ); - - const snapshot = await this.locationCollection.find({ idFlad: { $ne: idFlad } }); - if (snapshot.length === 0) { - console.log('No matching documents.'); - return; - } - - let dbUsersList:UserLocation[] = []; - snapshot.forEach(doc => { - dbUsersList.push(new UserLocation(doc.idFlad,doc.musicId,doc.latitude,doc.longitude)); - console.log(doc.idFlad, '=>', doc); - }); - // missing the curent music - let listUser: string[] = []; - const listUser2: Record = {}; - dbUsersList.forEach(user => { - console.log(user); - const dist = this.distanceBetween(latitude , longitude , user.latitude, user.longitude); - console.log(user.uuid,dist); - if (dist <= 100) { - - listUser.push(user.uuid); - listUser2[user.uuid] = user.musicId; - } - }); - - - return{ listUser, listUser2}; - // $listUser[] = {userID,idMusic}; - - } - - 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[]){ - const nearest = this.orderByDistance(main, list)[0] - return nearest; - } - - //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; - - - - - - - \ No newline at end of file diff --git a/src/Api/src/service/UserService.ts b/src/Api/src/service/UserService.ts deleted file mode 100644 index 682a963..0000000 --- a/src/Api/src/service/UserService.ts +++ /dev/null @@ -1,63 +0,0 @@ -import UserSchema from "../database/schema/User/UserSchema"; -import token from "../model/token"; - - -class UserService { - private user = UserSchema; - - /** - * Register a new user - */ - public async register( - name: string, - email: string, - password: string, - idFlad : string, - idSpotify : string - ): Promise { - try { - const user = await this.user.create({ - name, - email, - password, - idFlad, - idSpotify - }); - - const accessToken = token.createToken(user); - - return accessToken; - } catch (error : any) { - throw new Error(error.message); - } - } - - /** - * Attempt to login a user - */ - public async login( - 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 }); - // const user = await this.user.findById(idFlad); - - if (!user) { - throw new Error('Unable to find user with that email address'); - } - - if (await user.isValidPassword(password)) { - return token.createToken(user); - } else { - throw new Error('Wrong credentials given'); - } - } catch (error) { - throw new Error('Unable to create user'); - } - } -} - -export default UserService; \ No newline at end of file diff --git a/src/Api/src/services/LocationService.ts b/src/Api/src/services/LocationService.ts new file mode 100644 index 0000000..0bc4aeb --- /dev/null +++ b/src/Api/src/services/LocationService.ts @@ -0,0 +1,66 @@ +import { UserLocation } from '../models/Location'; +import LocationSchema from "../database/LocationSchema"; + +class LocationService { + private location = LocationSchema; + public async getNearUser(userId: string, musicId: string, latitude: number, longitude: number) { + await this.location.findOneAndUpdate( + { userId }, + { userId, musicId, latitude, longitude }, + { upsert: true } + ); + + const snapshot = await this.location.find({ userId: { $ne: userId } }); + if (!snapshot.length) { + console.log('No matching documents.'); + return; + } + + let usersLocation: UserLocation[] = []; + + snapshot.forEach(location => { + const distance = this.distanceBetween(latitude, longitude, location.latitude, location.longitude); + if (distance <= 1000) { + usersLocation.push(new UserLocation(location._id, location.userId, location.musicId, Math.ceil(distance + 0.1 / 200) * 200, location.updatedAt)); + } + }); + return { data: usersLocation }; + } + + private distanceBetween(lat1: number, lon1: number, lat2: number, lon2: number): number { + if ((lat1 == lat2) && (lon1 == lon2)) { + return 0; + } + else { + const radlat1 = Math.PI * lat1 / 180; + const radlat2 = Math.PI * lat2 / 180; + const theta = lon1 - lon2; + const radtheta = Math.PI * theta / 180; + let 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 * 1000; + + return dist; + } + } + + public async delete( + id: string + ): Promise { + try { + await this.location.findByIdAndRemove(id); + } catch (error: any) { + throw new Error(error.message); + } + } + +} + +export default LocationService; \ No newline at end of file diff --git a/src/Api/src/services/TokenService.ts b/src/Api/src/services/TokenService.ts new file mode 100644 index 0000000..c43cbc2 --- /dev/null +++ b/src/Api/src/services/TokenService.ts @@ -0,0 +1,26 @@ +import jwt from 'jsonwebtoken'; +import User from '../models/User'; +import Token from '../models/Token'; + +export const createToken = (user: User): string => { + return jwt.sign({ id: user._id }, process.env.SECRET_JWT as jwt.Secret, { + expiresIn: '15d', + }); +}; + +export const verifyToken = async ( + token: string +): Promise => { + return new Promise((resolve, reject) => { + jwt.verify( + token, + process.env.SECRET_JWT as jwt.Secret, + (err, payload) => { + if (err) return reject(err); + resolve(payload as Token); + } + ); + }); +}; + +export default { createToken, verifyToken }; \ No newline at end of file diff --git a/src/Api/src/services/UserService.ts b/src/Api/src/services/UserService.ts new file mode 100644 index 0000000..61636bc --- /dev/null +++ b/src/Api/src/services/UserService.ts @@ -0,0 +1,175 @@ +import { IMusic } from "../models/Music"; +import UserSchema from "../database/UserSchema"; +import token from "./TokenService"; +import { IPerson } from "../models/Person"; +import mongoose from "mongoose"; + +class UserService { + private user = UserSchema; + + public async register( + name: string, + email: string, + password: string, + idSpotify: string, + tokenSpotify: string, + image: string + ): Promise { + try { + const user = await this.user.create({ + name, + email, + password, + tokenSpotify, + idSpotify, + image + }); + return token.createToken(user); + } catch (error: any) { + throw new Error(error.message); + } + } + + public async login( + email: string, + password: string + ): Promise { + const user = await this.user.findOne({ email }); + if (!user) { + throw new Error('Wrong credentials given'); + } + if (await user.isValidPassword(password)) { + return token.createToken(user); + } else { + throw new Error('Wrong credentials given'); + } + } + + public async delete( + id: string + ): Promise { + try { + await this.user.findByIdAndRemove(id); + } catch (error: any) { + throw new Error(error.message); + } + } + + public async getUsers( + ids: string[] + ): Promise { + try { + const validIds = ids.filter(id => mongoose.Types.ObjectId.isValid(id)); + + if (validIds.length === 0) { + return []; + } + + return await this.user.find({ _id: { $in: validIds } }) + .select('_id name image') + } catch (error: any) { + throw new Error(error.message); + } + } + + public async addMusic(userId: string, music: IMusic): Promise { + try { + return await this.user.findByIdAndUpdate(userId, { + $push: { musics_likes: music }, + }); + } catch (error: any) { + throw new Error(error.message); + } + } + + public async deleteMusic(userId: string, musicId: string): Promise { + try { + const userOld = await this.user.findById(userId); + const userNew = await this.user.findByIdAndUpdate(userId, { + $pull: { musics_likes: { _id: musicId } }, + }, { new: true }); + + if (userOld.musics_likes.length === userNew.musics_likes.length) { + return false; + } + return true; + } catch (error) { + throw new Error(error.message); + } + } + + public async getMusics(userId: string): Promise { + try { + const user = await this.user.findById(userId); + return user?.musics_likes || []; + } catch (error) { + throw new Error(error.message); + } + } + + public async setName(userId: string, newName: string): Promise { + try { + await this.user.findByIdAndUpdate( + userId, + { name: newName } + ); + } catch (error) { + throw new Error(error.message); + } + } + + public async setEmail(userId: string, newEmail: string): Promise { + try { + await this.user.findByIdAndUpdate( + userId, + { email: newEmail } + ); + } catch (error) { + throw new Error(error.message); + } + } + + public async setSpotify(userId: string, tokenSpotify: string, idSpotify: string, image: string): Promise { + try { + await this.user.findByIdAndUpdate( + userId, + { + tokenSpotify: tokenSpotify, + idSpotify: idSpotify, + image: image + } + ); + } catch (error) { + throw new Error(error.message); + } + } + + public async setImage(userId: string, newImage: string): Promise { + try { + await this.user.findByIdAndUpdate( + userId, + { image: newImage } + ); + } catch (error) { + throw new Error(error.message); + } + } + + public async setPassword(userId: string, oldPassword: string, newPassword: string): Promise { + try { + + const user = await this.user.findById(userId); + + if (await user.isValidPassword(oldPassword)) { + user.password = newPassword; + await user.save(); + } else { + throw new Error('Old password does not match.'); + } + } catch (error) { + throw new Error(error.message); + } + } +} + +export default UserService; \ No newline at end of file diff --git a/src/Api/src/utils/swagger.ts b/src/Api/src/utils/swagger.ts new file mode 100644 index 0000000..e4f8a22 --- /dev/null +++ b/src/Api/src/utils/swagger.ts @@ -0,0 +1,34 @@ +import swaggerJsdoc from "swagger-jsdoc"; + +const options = { + definition: { + openapi: "3.0.1", + info: { + title: "FLAD API", + version: "1.0.0", + description: + "This is the Express API for the Flad project.", + contact: { + name: "Flad Dev", + url: "code", + email: "fladdevpro@gmail.com", + }, + }, + components: { + securitySchemes: { + bearerAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + } + } + }, + security: [{ + bearerAuth: ["read"] + }] + }, + apis: ["./dist/**/*.js"], +}; + + +export const specs = swaggerJsdoc(options); \ No newline at end of file diff --git a/src/FLAD/.gitignore b/src/FLAD/.gitignore index ec8a36a..040e2c3 100644 --- a/src/FLAD/.gitignore +++ b/src/FLAD/.gitignore @@ -9,6 +9,7 @@ npm-debug.* *.mobileprovision *.orig.* web-build/ +constants/config.ts # macOS .DS_Store diff --git a/src/FLAD/App.tsx b/src/FLAD/App.tsx index 444c82f..2d797b6 100644 --- a/src/FLAD/App.tsx +++ b/src/FLAD/App.tsx @@ -7,8 +7,7 @@ SplashScreen.preventAutoHideAsync(); export default function App() { return ( - + ); -} - +} \ No newline at end of file diff --git a/src/FLAD/Model/Artist.tsx b/src/FLAD/Model/Artist.tsx deleted file mode 100644 index dac88f0..0000000 --- a/src/FLAD/Model/Artist.tsx +++ /dev/null @@ -1,17 +0,0 @@ -export default class Artist { - private id: string; - private name: string; - private _url: string; // Image.source - - constructor(id: string, name: string, url: string) { - this.id = id; - this.name = name; - this._url = url; - } - - get url(): string { - return this.url; - } - - -} \ No newline at end of file diff --git a/src/FLAD/Model/Spot.tsx b/src/FLAD/Model/Spot.tsx deleted file mode 100644 index 714e99c..0000000 --- a/src/FLAD/Model/Spot.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import Music from "./Music"; - -export class Spot { - private _userId: string; - private _music: Music; - constructor(userId: string, music: Music) { - this._userId = userId; - this._music = music; - } - get userSpotifyId(): string { - return this._userId; - } - set userSpotifyId(value: string) { - this._userId = value; - } - get music(): Music { - return this._music; - } - set music(value: Music) { - this._music = value; - } -} \ No newline at end of file diff --git a/src/FLAD/Model/User.tsx b/src/FLAD/Model/User.tsx deleted file mode 100644 index 52c8896..0000000 --- a/src/FLAD/Model/User.tsx +++ /dev/null @@ -1,43 +0,0 @@ -export class User { - //attributes from DAFL - 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(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 deleted file mode 100644 index 9b548e0..0000000 --- a/src/FLAD/Model/factory/MusicFactory.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Music from "../Music"; - -export default class MusicFactory { - static mapFromSpotifyTrack(jsonMusic: any): Music { - const music = new Music( - jsonMusic.id, - jsonMusic.name, - jsonMusic.artists[0].name, - jsonMusic.album.images[0].url, - jsonMusic.preview_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 deleted file mode 100644 index 105b4b0..0000000 --- a/src/FLAD/Model/factory/UserFactory.tsx +++ /dev/null @@ -1,12 +0,0 @@ -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 2d685f4..424b35e 100644 --- a/src/FLAD/app.json +++ b/src/FLAD/app.json @@ -4,10 +4,10 @@ "slug": "FLAD", "version": "1.0.0", "orientation": "portrait", - "icon": "./assets/icons/icon.png", + "icon": "./assets/images/icon.png", "userInterfaceStyle": "automatic", "splash": { - "image": "./assets/icons/splash.png", + "image": "./assets/images/splash.png", "resizeMode": "contain", "backgroundColor": "#1d2129" }, @@ -28,12 +28,12 @@ }, "android": { "adaptiveIcon": { - "foregroundImage": "./assets/icons/adaptive-icon.png", + "foregroundImage": "./assets/images/adaptive-icon.png", "backgroundColor": "#FFFFFF" } }, "web": { - "favicon": "./assets/icons/favicon.png" + "favicon": "./assets/images/favicon.png" }, "plugins": [ [ diff --git a/src/FLAD/assets/GraphicalCharterDark.tsx b/src/FLAD/assets/GraphicalCharterDark.tsx deleted file mode 100644 index 9095c3a..0000000 --- a/src/FLAD/assets/GraphicalCharterDark.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export const GraphicalCharterDark = { - "body": "#141414", - "Text": "white", - "Card": "#232123", - "Line": "#403F3F" -} \ No newline at end of file diff --git a/src/FLAD/assets/GraphicalCharterLight.tsx b/src/FLAD/assets/GraphicalCharterLight.tsx deleted file mode 100644 index 0d748b1..0000000 --- a/src/FLAD/assets/GraphicalCharterLight.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export const GraphicalCharterLight = { - "body": "#f2f2f6", - "Text": "black", - "Card": "#fff", - "Line": "#e2e2e3" -} \ No newline at end of file diff --git a/src/FLAD/assets/icon.ts b/src/FLAD/assets/icon.ts new file mode 100644 index 0000000..da64377 --- /dev/null +++ b/src/FLAD/assets/icon.ts @@ -0,0 +1,9 @@ +const Icons = { + discovery: require('./images/icon_discovery.png'), + like: require('./images/icon_like.png'), + dislike: require('./images/icon_dislike.png'), + bookmark : require('./images/icon_bookmark.svg'), + share : require('./images/Vector.png'), +} + +export default Icons; diff --git a/src/FLAD/assets/icons/3869-8114-no-background-like.riv b/src/FLAD/assets/icons/3869-8114-no-background-like.riv deleted file mode 100644 index 6deffd5..0000000 Binary files a/src/FLAD/assets/icons/3869-8114-no-background-like.riv and /dev/null differ diff --git a/src/FLAD/assets/icons/Spotify_-_Animation_1.gif b/src/FLAD/assets/icons/Spotify_-_Animation_1.gif deleted file mode 100644 index 8df51f0..0000000 Binary files a/src/FLAD/assets/icons/Spotify_-_Animation_1.gif and /dev/null differ diff --git a/src/FLAD/assets/icons/colors/colors.tsx b/src/FLAD/assets/icons/colors/colors.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/FLAD/assets/icons/colors/theme.tsx b/src/FLAD/assets/icons/colors/theme.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/FLAD/assets/icons/config-dev.ts b/src/FLAD/assets/icons/config-dev.ts deleted file mode 100644 index 88a9750..0000000 --- a/src/FLAD/assets/icons/config-dev.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const spotifyCredentials = { - clientId: 'a5cb39302b6e4c64957de1df50742d71', - clientSecret: 'e23db1cd77ee4b589ee99525e282b2e8', - redirectUri: 'Your Redirect URI' -} diff --git a/src/FLAD/assets/icons/icons/IconProfil.png b/src/FLAD/assets/icons/icons/IconProfil.png deleted file mode 100644 index b9c6183..0000000 Binary files a/src/FLAD/assets/icons/icons/IconProfil.png and /dev/null differ diff --git a/src/FLAD/assets/icons/icons/Spotify.png b/src/FLAD/assets/icons/icons/Spotify.png deleted file mode 100644 index 011863b..0000000 Binary files a/src/FLAD/assets/icons/icons/Spotify.png and /dev/null differ diff --git a/src/FLAD/assets/icons/icons/Union-1.svg b/src/FLAD/assets/icons/icons/Union-1.svg deleted file mode 100644 index 9a91058..0000000 --- a/src/FLAD/assets/icons/icons/Union-1.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/FLAD/assets/icons/icons/Union-2.svg b/src/FLAD/assets/icons/icons/Union-2.svg deleted file mode 100644 index e61196d..0000000 --- a/src/FLAD/assets/icons/icons/Union-2.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/FLAD/assets/icons/icons/Union.svg b/src/FLAD/assets/icons/icons/Union.svg deleted file mode 100644 index 1efd231..0000000 --- a/src/FLAD/assets/icons/icons/Union.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/FLAD/assets/icons/icons/Vector.png b/src/FLAD/assets/icons/icons/Vector.png deleted file mode 100644 index 34364fb..0000000 Binary files a/src/FLAD/assets/icons/icons/Vector.png and /dev/null differ diff --git a/src/FLAD/assets/icons/icons/buttonProfil.png b/src/FLAD/assets/icons/icons/buttonProfil.png deleted file mode 100644 index 91a7086..0000000 Binary files a/src/FLAD/assets/icons/icons/buttonProfil.png and /dev/null differ diff --git a/src/FLAD/assets/icons/icons/buttonProfil_Inverse.png b/src/FLAD/assets/icons/icons/buttonProfil_Inverse.png deleted file mode 100644 index a9a6110..0000000 Binary files a/src/FLAD/assets/icons/icons/buttonProfil_Inverse.png and /dev/null differ diff --git a/src/FLAD/assets/icons/icons/discovery.svg b/src/FLAD/assets/icons/icons/discovery.svg deleted file mode 100644 index cfc5c6e..0000000 --- a/src/FLAD/assets/icons/icons/discovery.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/FLAD/assets/icons/icons/edit.png b/src/FLAD/assets/icons/icons/edit.png deleted file mode 100644 index 5f13961..0000000 Binary files a/src/FLAD/assets/icons/icons/edit.png and /dev/null differ diff --git a/src/FLAD/assets/icons/icons/icon.ts b/src/FLAD/assets/icons/icons/icon.ts deleted file mode 100644 index c34f7bc..0000000 --- a/src/FLAD/assets/icons/icons/icon.ts +++ /dev/null @@ -1,12 +0,0 @@ - -const Icons = { - discovery: require('./icon_discovery.png'), - like: require('./icon_like.png'), - dislike: require('./icon_dislike.png'), - bookmark : require('./icon_bookmark.svg'), - share : require('./Vector.png'), - - // riveLike : require('./light_like.riv'), -} - -export default Icons; diff --git a/src/FLAD/assets/images/Background.png b/src/FLAD/assets/images/Background.png deleted file mode 100644 index 66bbe21..0000000 Binary files a/src/FLAD/assets/images/Background.png and /dev/null differ diff --git a/src/FLAD/assets/images/FLADYCry.png b/src/FLAD/assets/images/FLADYCry.png deleted file mode 100644 index e7079fb..0000000 Binary files a/src/FLAD/assets/images/FLADYCry.png and /dev/null differ diff --git a/src/FLAD/assets/images/FLADYHate.png b/src/FLAD/assets/images/FLADYHate.png deleted file mode 100644 index da322f7..0000000 Binary files a/src/FLAD/assets/images/FLADYHate.png and /dev/null differ diff --git a/src/FLAD/assets/images/FLADYLove.png b/src/FLAD/assets/images/FLADYLove.png deleted file mode 100644 index c03e03d..0000000 Binary files a/src/FLAD/assets/images/FLADYLove.png and /dev/null differ diff --git a/src/FLAD/assets/images/FLADYStar.png b/src/FLAD/assets/images/FLADYStar.png deleted file mode 100644 index 01dc134..0000000 Binary files a/src/FLAD/assets/images/FLADYStar.png and /dev/null differ diff --git a/src/FLAD/assets/images/Flady.png b/src/FLAD/assets/images/Flady.png deleted file mode 100644 index 172ff00..0000000 Binary files a/src/FLAD/assets/images/Flady.png and /dev/null differ diff --git a/src/FLAD/assets/images/FladyShadow.png b/src/FLAD/assets/images/FladyShadow.png deleted file mode 100644 index f4c532e..0000000 Binary files a/src/FLAD/assets/images/FladyShadow.png and /dev/null differ diff --git a/src/FLAD/assets/images/RedFlady.png b/src/FLAD/assets/images/RedFlady.png deleted file mode 100644 index c6c3b40..0000000 Binary files a/src/FLAD/assets/images/RedFlady.png and /dev/null differ diff --git a/src/FLAD/assets/icons/adaptive-icon.png b/src/FLAD/assets/images/adaptive-icon.png similarity index 100% rename from src/FLAD/assets/icons/adaptive-icon.png rename to src/FLAD/assets/images/adaptive-icon.png diff --git a/src/FLAD/assets/icons/icons/next.png b/src/FLAD/assets/images/arrow_forward.png similarity index 100% rename from src/FLAD/assets/icons/icons/next.png rename to src/FLAD/assets/images/arrow_forward.png diff --git a/src/FLAD/assets/images/background.png b/src/FLAD/assets/images/background.png new file mode 100644 index 0000000..65fba59 Binary files /dev/null and b/src/FLAD/assets/images/background.png differ diff --git a/src/FLAD/assets/images/Background_Start_Page.png b/src/FLAD/assets/images/background_start.png similarity index 100% rename from src/FLAD/assets/images/Background_Start_Page.png rename to src/FLAD/assets/images/background_start.png diff --git a/src/FLAD/assets/images/Board_Image.png b/src/FLAD/assets/images/board_1.png similarity index 100% rename from src/FLAD/assets/images/Board_Image.png rename to src/FLAD/assets/images/board_1.png diff --git a/src/FLAD/assets/images/Board_Image2.png b/src/FLAD/assets/images/board_2.png similarity index 100% rename from src/FLAD/assets/images/Board_Image2.png rename to src/FLAD/assets/images/board_2.png diff --git a/src/FLAD/assets/images/Board_Image3.png b/src/FLAD/assets/images/board_3.png similarity index 100% rename from src/FLAD/assets/images/Board_Image3.png rename to src/FLAD/assets/images/board_3.png diff --git a/src/FLAD/assets/icons/icons/icon_bookmark.svg b/src/FLAD/assets/images/bookmark_icon.svg similarity index 100% rename from src/FLAD/assets/icons/icons/icon_bookmark.svg rename to src/FLAD/assets/images/bookmark_icon.svg diff --git a/src/FLAD/assets/icons/Check.png b/src/FLAD/assets/images/check_icon.png similarity index 100% rename from src/FLAD/assets/icons/Check.png rename to src/FLAD/assets/images/check_icon.png diff --git a/src/FLAD/assets/images/chevron_left_icon.png b/src/FLAD/assets/images/chevron_left_icon.png new file mode 100644 index 0000000..c13af20 Binary files /dev/null and b/src/FLAD/assets/images/chevron_left_icon.png differ diff --git a/src/FLAD/assets/images/chevron_right_icon.png b/src/FLAD/assets/images/chevron_right_icon.png new file mode 100644 index 0000000..3fba365 Binary files /dev/null and b/src/FLAD/assets/images/chevron_right_icon.png differ diff --git a/src/FLAD/assets/images/confirm_icon.png b/src/FLAD/assets/images/confirm_icon.png new file mode 100644 index 0000000..04fd411 Binary files /dev/null and b/src/FLAD/assets/images/confirm_icon.png differ diff --git a/src/FLAD/assets/icons/icons/croix.png b/src/FLAD/assets/images/cross_icon.png similarity index 100% rename from src/FLAD/assets/icons/icons/croix.png rename to src/FLAD/assets/images/cross_icon.png diff --git a/src/FLAD/assets/icons/icons/icon_discovery.png b/src/FLAD/assets/images/discovery_icon.png similarity index 100% rename from src/FLAD/assets/icons/icons/icon_discovery.png rename to src/FLAD/assets/images/discovery_icon.png diff --git a/src/FLAD/assets/icons/icons/icon_discovery_no_text.png b/src/FLAD/assets/images/discovery_icon_no_text.png similarity index 100% rename from src/FLAD/assets/icons/icons/icon_discovery_no_text.png rename to src/FLAD/assets/images/discovery_icon_no_text.png diff --git a/src/FLAD/assets/icons/icons/icon_dislike.png b/src/FLAD/assets/images/dislike_icon.png similarity index 100% rename from src/FLAD/assets/icons/icons/icon_dislike.png rename to src/FLAD/assets/images/dislike_icon.png diff --git a/src/FLAD/assets/icons/icons/icon_dislike_no_text.png b/src/FLAD/assets/images/dislike_icon_no_text.png similarity index 100% rename from src/FLAD/assets/icons/icons/icon_dislike_no_text.png rename to src/FLAD/assets/images/dislike_icon_no_text.png diff --git a/src/FLAD/assets/images/explicit_icon.png b/src/FLAD/assets/images/explicit_icon.png new file mode 100644 index 0000000..f1cef16 Binary files /dev/null and b/src/FLAD/assets/images/explicit_icon.png differ diff --git a/src/FLAD/assets/icons/favicon.png b/src/FLAD/assets/images/favicon.png similarity index 100% rename from src/FLAD/assets/icons/favicon.png rename to src/FLAD/assets/images/favicon.png diff --git a/src/FLAD/assets/icons/Logo_White_Flad.png b/src/FLAD/assets/images/flad_logo.png similarity index 100% rename from src/FLAD/assets/icons/Logo_White_Flad.png rename to src/FLAD/assets/images/flad_logo.png diff --git a/src/FLAD/assets/images/Flady.gif b/src/FLAD/assets/images/flady.gif similarity index 100% rename from src/FLAD/assets/images/Flady.gif rename to src/FLAD/assets/images/flady.gif diff --git a/src/FLAD/assets/images/flady.png b/src/FLAD/assets/images/flady.png new file mode 100644 index 0000000..bd520c2 Binary files /dev/null and b/src/FLAD/assets/images/flady.png differ diff --git a/src/FLAD/assets/images/flady_angry.png b/src/FLAD/assets/images/flady_angry.png new file mode 100644 index 0000000..5cb25dc Binary files /dev/null and b/src/FLAD/assets/images/flady_angry.png differ diff --git a/src/FLAD/assets/images/flady_cry.png b/src/FLAD/assets/images/flady_cry.png new file mode 100644 index 0000000..5f3744e Binary files /dev/null and b/src/FLAD/assets/images/flady_cry.png differ diff --git a/src/FLAD/assets/images/flady_icon.png b/src/FLAD/assets/images/flady_icon.png new file mode 100644 index 0000000..037e4f1 Binary files /dev/null and b/src/FLAD/assets/images/flady_icon.png differ diff --git a/src/FLAD/assets/images/flady_love.png b/src/FLAD/assets/images/flady_love.png new file mode 100644 index 0000000..53a2fd8 Binary files /dev/null and b/src/FLAD/assets/images/flady_love.png differ diff --git a/src/FLAD/assets/images/flady_shadow.png b/src/FLAD/assets/images/flady_shadow.png new file mode 100644 index 0000000..7fa3992 Binary files /dev/null and b/src/FLAD/assets/images/flady_shadow.png differ diff --git a/src/FLAD/assets/images/flady_star.png b/src/FLAD/assets/images/flady_star.png new file mode 100644 index 0000000..792e071 Binary files /dev/null and b/src/FLAD/assets/images/flady_star.png differ diff --git a/src/FLAD/assets/icons/icon.png b/src/FLAD/assets/images/icon.png similarity index 100% rename from src/FLAD/assets/icons/icon.png rename to src/FLAD/assets/images/icon.png diff --git a/src/FLAD/assets/images/jul.png b/src/FLAD/assets/images/jul.png deleted file mode 100644 index 9ef32e1..0000000 Binary files a/src/FLAD/assets/images/jul.png and /dev/null differ diff --git a/src/FLAD/assets/icons/icons/icon_like.png b/src/FLAD/assets/images/like_icon.png similarity index 100% rename from src/FLAD/assets/icons/icons/icon_like.png rename to src/FLAD/assets/images/like_icon.png diff --git a/src/FLAD/assets/images/like_icon_no_text.png b/src/FLAD/assets/images/like_icon_no_text.png new file mode 100644 index 0000000..95d8c84 Binary files /dev/null and b/src/FLAD/assets/images/like_icon_no_text.png differ diff --git a/src/FLAD/assets/icons/icons/lock.png b/src/FLAD/assets/images/lock_icon.png similarity index 100% rename from src/FLAD/assets/icons/icons/lock.png rename to src/FLAD/assets/images/lock_icon.png diff --git a/src/FLAD/assets/icons/icons/icon_messages.png b/src/FLAD/assets/images/message_icon.png similarity index 100% rename from src/FLAD/assets/icons/icons/icon_messages.png rename to src/FLAD/assets/images/message_icon.png diff --git a/src/FLAD/assets/images/ok_icon.png b/src/FLAD/assets/images/ok_icon.png new file mode 100644 index 0000000..7a2927b Binary files /dev/null and b/src/FLAD/assets/images/ok_icon.png differ diff --git a/src/FLAD/assets/images/pause_icon.png b/src/FLAD/assets/images/pause_icon.png new file mode 100644 index 0000000..8adabf9 Binary files /dev/null and b/src/FLAD/assets/images/pause_icon.png differ diff --git a/src/FLAD/assets/images/play_icon.png b/src/FLAD/assets/images/play_icon.png new file mode 100644 index 0000000..54bfff9 Binary files /dev/null and b/src/FLAD/assets/images/play_icon.png differ diff --git a/src/FLAD/assets/images/pnl.png b/src/FLAD/assets/images/pnl.png deleted file mode 100644 index 73bfc5d..0000000 Binary files a/src/FLAD/assets/images/pnl.png and /dev/null differ diff --git a/src/FLAD/assets/images/save_icon.png b/src/FLAD/assets/images/save_icon.png new file mode 100644 index 0000000..7891b53 Binary files /dev/null and b/src/FLAD/assets/images/save_icon.png differ diff --git a/src/FLAD/assets/images/save_icon_full.png b/src/FLAD/assets/images/save_icon_full.png new file mode 100644 index 0000000..9287096 Binary files /dev/null and b/src/FLAD/assets/images/save_icon_full.png differ diff --git a/src/FLAD/assets/images/share_icon.png b/src/FLAD/assets/images/share_icon.png new file mode 100644 index 0000000..a100e16 Binary files /dev/null and b/src/FLAD/assets/images/share_icon.png differ diff --git a/src/FLAD/assets/icons/splash.png b/src/FLAD/assets/images/splash.png similarity index 100% rename from src/FLAD/assets/icons/splash.png rename to src/FLAD/assets/images/splash.png diff --git a/src/FLAD/assets/images/spotify_icon.png b/src/FLAD/assets/images/spotify_icon.png new file mode 100644 index 0000000..3c62ae8 Binary files /dev/null and b/src/FLAD/assets/images/spotify_icon.png differ diff --git a/src/FLAD/assets/icons/icons/User.png b/src/FLAD/assets/images/user_icon.png similarity index 100% rename from src/FLAD/assets/icons/icons/User.png rename to src/FLAD/assets/images/user_icon.png diff --git a/src/FLAD/assets/lottie/Lottie.tsx b/src/FLAD/assets/lottie/Lottie.tsx index 30a1482..fc2616a 100644 --- a/src/FLAD/assets/lottie/Lottie.tsx +++ b/src/FLAD/assets/lottie/Lottie.tsx @@ -1,6 +1,5 @@ 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/sounds/Click.mp3 b/src/FLAD/assets/sounds/click.mp3 similarity index 100% rename from src/FLAD/assets/sounds/Click.mp3 rename to src/FLAD/assets/sounds/click.mp3 diff --git a/src/FLAD/components/AdjustSize.tsx b/src/FLAD/components/AdjustSize.tsx deleted file mode 100644 index 32ae5ed..0000000 --- a/src/FLAD/components/AdjustSize.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export default function AdjustSize(Text: string) { - const titleLength = Text.length; - const minFontSize = 23; - const maxFontSize = 48; - const fontRatio = 1.1; - const fontSize = Math.max(minFontSize, maxFontSize - (titleLength * fontRatio)); - return fontSize; -} \ No newline at end of file diff --git a/src/FLAD/components/AnimatedParalax.tsx b/src/FLAD/components/AnimatedParalax.tsx deleted file mode 100644 index bba9f4a..0000000 --- a/src/FLAD/components/AnimatedParalax.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { View } from "react-native"; -import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle } from "react-native-reanimated"; - -const halfPi = Math.PI / 2; - -export default function AnimatedParalax() { - const sensor = useAnimatedSensor(SensorType.ROTATION); - const styleAniamatedImage = useAnimatedStyle(() => { - const { 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 ( - - - - - ); -}; diff --git a/src/FLAD/components/Artist.tsx b/src/FLAD/components/Artist.tsx deleted file mode 100644 index e2b67c5..0000000 --- a/src/FLAD/components/Artist.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { View, StyleSheet, Dimensions, Image, TouchableOpacity } from "react-native"; -import Animated, { - Layout, - 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 { 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; - 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/ArtistChip.tsx b/src/FLAD/components/ArtistChip.tsx deleted file mode 100644 index 8decee0..0000000 --- a/src/FLAD/components/ArtistChip.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useCallback } from 'react'; -import { View, Text, Image, Pressable, Linking, Alert } from 'react-native' -import Artist from '../Model/Artist'; - -interface ArtistChipProps { - backgroundColor: string; - artist: Artist; -} - -const ArtistChip = ({ artist }: ArtistChipProps) => { - const handlePress = useCallback(async () => { - const supported = await Linking.canOpenURL(artist.url); - - if (supported) { - await Linking.openURL(artist.url); - } else { - Alert.alert(`Don't know how to open this URL: ${artist.url}`); - } - }, [artist.url]); - - return ( - - - - - - - - ii - - - - ); -}; - - -export default ArtistChip; \ No newline at end of file diff --git a/src/FLAD/components/Card.tsx b/src/FLAD/components/Card.tsx deleted file mode 100644 index cf6903f..0000000 --- a/src/FLAD/components/Card.tsx +++ /dev/null @@ -1,228 +0,0 @@ -import { View, Image, Dimensions, StyleSheet } from 'react-native' -import React from 'react' -import Animated, { interpolate, runOnJS, useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated'; -import normalize from '../components/Normalize'; -import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler'; - -const SCREEN_WIDTH = Dimensions.get('window').width -const SCREEN_HEIGHT = Dimensions.get('window').height - -interface CardProps { - title: string; - image: any; - onSwipe: (direction: "left" | "right" | "down") => void; -} -type ContextType = { - translateX: number; - translateY: number; -}; - -const Card = ({ image, onSwipe }: CardProps) => { - - const translateX = useSharedValue(0); - const translateY = useSharedValue(0); - const scale = useSharedValue(1); - 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; - translateY.value = event.translationY + context.translateY; - - }, - onEnd: () => { - - if (translateX.value > 160) { - console.log("translateX2"); - runOnJS(onSwipe)("right"); - } else if (translateX.value < -160) { - runOnJS(onSwipe)("left"); - } else if (translateY.value > 250) { - runOnJS(onSwipe)("down"); - } - - 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 / 4, 0, SCREEN_WIDTH / 4], - [0.35, 0, 0.35]); - - - return { - opacity: opacityl, - }; - }); - const opacCStyle2 = useAnimatedStyle(() => { - const opacityl = interpolate - (translateY.value, - [0, SCREEN_HEIGHT / 4], - [0, 0.35]); - - - return { - opacity: opacityl, - }; - }); - - const opacDStyle = useAnimatedStyle(() => { - const opacityl = interpolate - (translateY.value, - [100, 300], - [0, 1]); - - return { - opacity: opacityl, - }; - }); - - const horizontalThreshold = SCREEN_WIDTH * 0.65; - - 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` }, - ] - - }; - }); - - return ( - - - - - - - - - - <> - - - - - - - - - - - - - - - ); -}; - - -const styles = StyleSheet.create({ - card: { - justifyContent: 'center', - alignItems: 'center', - }, - image: { - borderRadius: 24, - resizeMode: 'stretch', - height: normalize(420), - width: normalize(420), - }, - container: { - flex: 1, - width: '100%', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - } -}) - -export default Card; - diff --git a/src/FLAD/components/CardComponent.tsx b/src/FLAD/components/CardComponent.tsx new file mode 100644 index 0000000..a567cf0 --- /dev/null +++ b/src/FLAD/components/CardComponent.tsx @@ -0,0 +1,212 @@ +import { View, Image, Dimensions, StyleSheet } from 'react-native' +import React from 'react' +import Animated, { interpolate, runOnJS, useAnimatedGestureHandler, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated'; +import normalize from './Normalize'; +import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler'; + +const SCREEN_WIDTH = Dimensions.get('window').width; +const SCREEN_HEIGHT = Dimensions.get('window').height; + +interface CardProps { + image: any; + onSwipe: (direction: "left" | "right" | "down") => void; +} + +type ContextType = { + translateX: number; + translateY: number; +}; + +export default function CardComponent(props: CardProps) { + + const translateX = useSharedValue(0); + const translateY = useSharedValue(0); + const scale = useSharedValue(1); + 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; + translateY.value = event.translationY + context.translateY; + }, + onEnd: () => { + + if (translateX.value > SCREEN_WIDTH / 2) { + runOnJS(props.onSwipe)("right"); + } else if (translateX.value < -SCREEN_WIDTH / 2) { + runOnJS(props.onSwipe)("left"); + } else if (translateY.value > SCREEN_HEIGHT / 2) { + runOnJS(props.onSwipe)("down"); + } else { + translateX.value = withSpring(0); + translateY.value = withSpring(0); + } + }, + }); + + const opacityRightIcon = useAnimatedStyle(() => { + const horizontal = interpolate + (translateX.value, + [-SCREEN_WIDTH / 4, 20, SCREEN_WIDTH / 4], + [0, 0, 1]); + + const vertical = interpolate + (translateY.value, + [20, SCREEN_HEIGHT / 4], + [1, 0.2]); + + return { + opacity: horizontal * vertical, + }; + }); + + const opacityLeftIcon = useAnimatedStyle(() => { + const horizontal = interpolate + (translateX.value, + [-SCREEN_WIDTH / 4, -20, SCREEN_WIDTH / 4], + [1, 0, 0]); + + const vertical = interpolate + (translateY.value, + [20, SCREEN_HEIGHT / 4], + [1, 0.2]); + + return { + opacity: horizontal * vertical, + }; + }); + + const opacityDownIcon = useAnimatedStyle(() => { + const vertical = interpolate + (translateY.value, + [20, SCREEN_HEIGHT / 2], + [0, 1]); + + const horizontal = interpolate + (translateX.value, + [-SCREEN_WIDTH / 4, 0, SCREEN_WIDTH / 4], + [0.5, 1, 0.5]); + + return { + opacity: vertical * horizontal, + }; + }); + + const opacityHorizontalBackground = useAnimatedStyle(() => { + const value = interpolate + (translateX.value, + [-SCREEN_WIDTH / 4, 0, SCREEN_WIDTH / 4], + [0.27, 0, 0.27]); + return { + opacity: value, + }; + }); + + const opacityDownBackground = useAnimatedStyle(() => { + const value = interpolate + (translateY.value, + [0, SCREEN_HEIGHT / 5], + [0, 0.28]); + return { + opacity: value, + }; + }); + + const horizontalThreshold = SCREEN_WIDTH * 0.65; + + 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` }, + ] + }; + }); + + return ( + + + + + + + + + + + + + + + + + + + ); +}; + + +const styles = StyleSheet.create({ + image: { + borderRadius: 24, + resizeMode: 'stretch', + height: normalize(420), + width: normalize(420), + }, + backgroundEffect: { + backgroundColor: 'black', + elevation: 100, + position: "absolute", + borderWidth: 8, + borderColor: '#FFF', + zIndex: 1 + }, + container: { + flex: 1, + width: '100%', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + shadowColor: "#000", + shadowOffset: { + width: 0, + height: 4, + }, + shadowOpacity: 0.39, + shadowRadius: 8.30, + }, + iconContainer: { + width: '100%', + height: '100%', + position: "absolute", + justifyContent: "center", + alignContent: "center", + elevation: 100, + zIndex: 1 + }, + icon: { + alignSelf: "center", + width: 126.27, + height: 118.64, + } +}); \ No newline at end of file diff --git a/src/FLAD/components/CardMusic.tsx b/src/FLAD/components/CardMusicComponent.tsx similarity index 58% rename from src/FLAD/components/CardMusic.tsx rename to src/FLAD/components/CardMusicComponent.tsx index 7b16f4e..a921345 100644 --- a/src/FLAD/components/CardMusic.tsx +++ b/src/FLAD/components/CardMusicComponent.tsx @@ -1,25 +1,20 @@ import React from 'react'; import { StyleSheet, Text, View, Image } from 'react-native'; import { useSelector } from 'react-redux'; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; +import normalize from './Normalize'; +import Music from '../models/Music'; +import Artist from '../models/Artist'; -import normalize from '../components/Normalize'; - -type CustomCardMusic = { //Props - image: string; - title: string; - description: string; - id: string; +type CardMusicProps = { + music: Music } - -export default function CardMusic(CBP: CustomCardMusic) { +export default function CardMusic(props: CardMusicProps) { // @ts-ignore const isDark = useSelector(state => state.userReducer.dark); - const style = isDark ? GraphicalCharterDark : GraphicalCharterLight; + const style: Theme = isDark ? DarkTheme : LightTheme; - const source = typeof CBP.image === 'string' ? { uri: CBP.image } : CBP.image; const styles = StyleSheet.create({ container: { flexDirection: 'row', @@ -28,8 +23,8 @@ export default function CardMusic(CBP: CustomCardMusic) { marginBottom: 15 }, imageContainer: { - width: normalize(92), - height: normalize(92), + width: normalize(82), + height: normalize(82), alignItems: 'center', justifyContent: 'center', marginRight: 20, @@ -39,7 +34,6 @@ export default function CardMusic(CBP: CustomCardMusic) { width: '100%', height: '100%', borderRadius: 10 - }, textContainer: { flex: 1, @@ -64,12 +58,11 @@ export default function CardMusic(CBP: CustomCardMusic) { - + - {/* currentMusic.id === CBP.id && styles.currentMusic */} - {CBP.title} - {CBP.description} + {props.music.name} + {props.music.artists.map((artist: Artist) => artist.name).join(', ')} ); diff --git a/src/FLAD/components/CircularProgressBar.tsx b/src/FLAD/components/CircularProgressBar.tsx deleted file mode 100644 index 28f2b34..0000000 --- a/src/FLAD/components/CircularProgressBar.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { View, StyleSheet } from 'react-native' -import Animated, { lessThan, multiply } 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 theta = multiply(progress, 2 * PI); - const opacity = lessThan(theta, PI); - - return ( - <> - - - - - - - - - - - - - - ); -}; - -export default FladInput; \ No newline at end of file diff --git a/src/FLAD/components/CurrentMusic.tsx b/src/FLAD/components/CurrentMusic.tsx deleted file mode 100644 index e120c56..0000000 --- a/src/FLAD/components/CurrentMusic.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { View } from 'react-native'; -import Animated from 'react-native-reanimated'; - -export default function CurrentMusic() { - - return ( - - - - - - - ); -} - diff --git a/src/FLAD/components/FladyComponent.tsx b/src/FLAD/components/FladyComponent.tsx index 4dee542..9e1900f 100644 --- a/src/FLAD/components/FladyComponent.tsx +++ b/src/FLAD/components/FladyComponent.tsx @@ -17,15 +17,16 @@ export default function FladyComponent(monFlady: Flady) { const styles = StyleSheet.create({ container: { - width: normalize(152), - height: normalize(152), - borderRadius: 90, - marginHorizontal: normalize(15), + width: normalize(132), + height: normalize(132), + borderRadius: 30, + backgroundColor: 'white', + marginHorizontal: normalize(12), overflow: 'hidden', }, image: { - width: normalize(220), - height: normalize(220), + width: normalize(180), + height: normalize(180), marginLeft: -1 } }) \ No newline at end of file diff --git a/src/FLAD/components/Friend.tsx b/src/FLAD/components/FriendComponent.tsx similarity index 52% rename from src/FLAD/components/Friend.tsx rename to src/FLAD/components/FriendComponent.tsx index 80e1d65..3355cec 100644 --- a/src/FLAD/components/Friend.tsx +++ b/src/FLAD/components/FriendComponent.tsx @@ -1,24 +1,23 @@ import React from 'react'; import { StyleSheet, Text, View, Image } from 'react-native'; -import { color } from 'react-native-reanimated'; import { useSelector } from 'react-redux'; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; import normalize from './Normalize'; +import Message from '../models/Message'; type FriendProps = { image: string; name: string; - lastMessage: string; + lastMessage: Message; } -export default function Friend(friend: FriendProps) { +export default function Friend(props: FriendProps) { // @ts-ignore const isDark = useSelector(state => state.userReducer.dark); - const style = isDark ? GraphicalCharterDark : GraphicalCharterLight; + const style: Theme = isDark ? DarkTheme : LightTheme; - const source = typeof friend.image === 'string' ? { uri: friend.image } : friend.image; + const source = typeof props.image === 'string' ? { uri: props.image } : props.image; const styles = StyleSheet.create({ container: { @@ -27,7 +26,7 @@ export default function Friend(friend: FriendProps) { paddingVertical: 9, }, image: { - marginLeft: 15, + marginLeft: "7%", marginRight: 12, width: 55, height: 55, @@ -52,7 +51,7 @@ export default function Friend(friend: FriendProps) { }, time: { fontSize: normalize(18), - color: '#989898' + color: style.Text }, profilContainer: { marginTop: 5, @@ -62,23 +61,50 @@ export default function Friend(friend: FriendProps) { justifyContent: 'center', }, button: { - width: normalize(13), - height: normalize(13), + width: normalize(9), + height: normalize(15), marginRight: 42 } }) + const getTimeDifferenceString = (date: Date): string => { + const now = new Date(); + const differenceInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000); + + const intervals = { + an: 31536000, + mois: 2592000, + sem: 604800, + jour: 86400, + heure: 3600, + min: 60, + }; + + for (const [intervalName, seconds] of Object.entries(intervals)) { + const intervalCount = Math.floor(differenceInSeconds / seconds); + if (intervalCount > 0) { + if (intervalName === 'mois' || intervalName === 'min') { + return `il y a ${intervalCount} ${intervalName}`; + } else { + return `il y a ${intervalCount} ${intervalName}${intervalCount !== 1 ? 's' : ''}`; + } + } + } + + return 'À l’instant'; + }; + return ( - {friend.name} + {props.name} - {friend.lastMessage} - · 1sem + {props.lastMessage.content} + · {getTimeDifferenceString(props.lastMessage.date)} - + ) } diff --git a/src/FLAD/components/Genre.tsx b/src/FLAD/components/Genre.tsx deleted file mode 100644 index 6bf423a..0000000 --- a/src/FLAD/components/Genre.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useState } from "react"; -import { ScrollView, StyleSheet } from "react-native"; -import Music from "../Model/Music"; -import { Artist } from "./Artist"; - -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); - - return ( - - {artists.map((artist, i) => ( - { - const tmppArtist = new Music("Kratos", "PNL", "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png","",""); - - artists.push(tmppArtist); - setArtists([...artists]); - }} - /> - ))} - - - - ); -}; - - - -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 deleted file mode 100644 index f3d98d3..0000000 --- a/src/FLAD/components/HalfCircle.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { View } from 'react-native' - -interface HalfCirlceProps { - backgroundColor: string; -} - -const HalfCirlce = ({ backgroundColor }: HalfCirlceProps) => { - - return ( - - - - - - ); -}; - - -export default HalfCirlce; \ No newline at end of file diff --git a/src/FLAD/components/HorizontalFlatList.tsx b/src/FLAD/components/HorizontalFlatList.tsx deleted file mode 100644 index 5df0a3d..0000000 --- a/src/FLAD/components/HorizontalFlatList.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { View, StyleSheet, Text, FlatList } from "react-native"; -import { RenderCellProps } from "./littleCard"; - - -interface HorizontalFlatListProps { - children: (props: RenderCellProps) => React.ReactElement - title: string; - data: any[]; -} -export const HorizontalFlatList = ({ title, data, children: RenderCell }: HorizontalFlatListProps) => { - - return ( - - {title} - item.id} - renderItem={({ item }) => RenderCell(item)} /> - ); -}; -const styles = StyleSheet.create({ - similarSection: { - paddingTop: 16 - }, - similarTitle: { - color: "#FFF", - paddingLeft: 8, - fontSize: 24, - fontWeight: "600", - paddingBottom: 16 - } - -}); \ No newline at end of file diff --git a/src/FLAD/components/FladLoadingScreen.tsx b/src/FLAD/components/LoadingComponent.tsx similarity index 59% rename from src/FLAD/components/FladLoadingScreen.tsx rename to src/FLAD/components/LoadingComponent.tsx index 7043aa9..8da06d3 100644 --- a/src/FLAD/components/FladLoadingScreen.tsx +++ b/src/FLAD/components/LoadingComponent.tsx @@ -2,21 +2,19 @@ import { View } from 'react-native' import React, { useEffect } from 'react' import Animated, { interpolate, useAnimatedStyle, useSharedValue, withRepeat, withTiming } from 'react-native-reanimated'; -const size = 100 -const FladLoading = () => { - - const progresse = useSharedValue(1); +export default function Loading() { + const size = 100 + const progress = useSharedValue(1); useEffect(() => { - // withTiming, withSpring - progresse.value = withRepeat(withTiming(0.01, { duration: 750 }), -1, true); - }, [progresse]); + progress.value = withRepeat(withTiming(0.01, { duration: 750 }), -1, true); + }, [progress]); const breatheStyleSquare = useAnimatedStyle(() => { const borderRange = interpolate - (progresse.value, + (progress.value, [0, 1], [(size + 20), (size)], ); @@ -28,22 +26,16 @@ const FladLoading = () => { borderWidth: size / 10, borderColor: "#F80404", shadowColor: "#F40C1C", - shadowOffset: { width: 0, height: 0 }, shadowOpacity: 1, shadowRadius: 10, }; }); return ( - - + - + - ); -}; - -export default FladLoading; - +}; \ No newline at end of file diff --git a/src/FLAD/components/Messaging.tsx b/src/FLAD/components/Messaging.tsx deleted file mode 100644 index 566b5dd..0000000 --- a/src/FLAD/components/Messaging.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Messaging() { - -} \ No newline at end of file diff --git a/src/FLAD/components/NextButton.tsx b/src/FLAD/components/NextButtonComponent.tsx similarity index 90% rename from src/FLAD/components/NextButton.tsx rename to src/FLAD/components/NextButtonComponent.tsx index 3090ef7..1b9abfc 100644 --- a/src/FLAD/components/NextButton.tsx +++ b/src/FLAD/components/NextButtonComponent.tsx @@ -2,13 +2,12 @@ import React, { useRef, useEffect } from 'react'; import { View, StyleSheet, TouchableOpacity, Animated, useColorScheme } from 'react-native'; import Svg, { G, Circle } from 'react-native-svg'; import { AntDesign } from '@expo/vector-icons'; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; -import normalize from '../components/Normalize'; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; +import normalize from './Normalize'; // @ts-ignore export default function NextButton({ percentage, scrollTo }) { - const style = useColorScheme() == 'light' ? GraphicalCharterLight : GraphicalCharterDark; + const style: Theme = useColorScheme() == 'light' ? LightTheme : DarkTheme; const size = normalize(148); const strokeWidth = 2; diff --git a/src/FLAD/components/OnboardingItem.tsx b/src/FLAD/components/OnboardingComponent.tsx similarity index 80% rename from src/FLAD/components/OnboardingItem.tsx rename to src/FLAD/components/OnboardingComponent.tsx index 9feb16f..5d22aa7 100644 --- a/src/FLAD/components/OnboardingItem.tsx +++ b/src/FLAD/components/OnboardingComponent.tsx @@ -1,12 +1,11 @@ import React from 'react'; import { View, StyleSheet, Text, Image, useWindowDimensions, useColorScheme } from 'react-native'; -import normalize from '../components/Normalize'; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; +import normalize from './Normalize'; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; // @ts-ignore -export default function Onboarding({ item }) { - const style = useColorScheme() == 'light' ? GraphicalCharterLight : GraphicalCharterDark; +export default function OnboardingComponent({ item }) { + const style: Theme = useColorScheme() == 'light' ? LightTheme : DarkTheme; const { width, height } = useWindowDimensions(); const styles = StyleSheet.create({ container: { diff --git a/src/FLAD/components/Paginator.tsx b/src/FLAD/components/PaginatorComponent.tsx similarity index 94% rename from src/FLAD/components/Paginator.tsx rename to src/FLAD/components/PaginatorComponent.tsx index c622b68..66bd7f0 100644 --- a/src/FLAD/components/Paginator.tsx +++ b/src/FLAD/components/PaginatorComponent.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { View, StyleSheet, Animated, useWindowDimensions } from 'react-native'; - -import normalize from '../components/Normalize'; +import normalize from './Normalize'; // @ts-ignore export default function Paginator({ data, scrollX }) { @@ -9,7 +8,7 @@ export default function Paginator({ data, scrollX }) { return ( - {data.map((_, i) => { + { data.map((_ : any, i : any) => { const inputRange = [(i - 1) * width, i * width, (i + 1) * width]; const dotWidth = scrollX.interpolate({ diff --git a/src/FLAD/components/SelectedCard.tsx b/src/FLAD/components/SelectedCard.tsx deleted file mode 100644 index 8ec8557..0000000 --- a/src/FLAD/components/SelectedCard.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { View, StyleSheet, Dimensions, TouchableOpacity } from "react-native"; -import Animated, { - Layout, - 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 { useState } from "react"; - - -interface SelectedCardProps { - artist: Music; - onPress: () => void; -} -export const SelectedCard = ({ artist, onPress }: SelectedCardProps) => { - 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/SimilarMusicComponent.tsx b/src/FLAD/components/SimilarMusicComponent.tsx new file mode 100644 index 0000000..dd42465 --- /dev/null +++ b/src/FLAD/components/SimilarMusicComponent.tsx @@ -0,0 +1,36 @@ +import { View, Text, StyleSheet, Image } from 'react-native'; +import Music from '../models/Music'; +import normalize from './Normalize'; + +export interface RenderCellProps { + music: Music; +} +export const SimilarMusic = (props: RenderCellProps) => { + return ( + + + {props.music.name} + + + ) +} + +const styles = StyleSheet.create({ + + similarContainer: { + marginHorizontal: 6 + }, + similarTitle: { + color: "#DADADA", + paddingTop: 5, + paddingLeft: 5, + fontWeight: "600", + maxWidth: normalize(130), + fontSize: normalize(14) + }, + similarPoster: { + height: normalize(130), + width: normalize(130), + borderRadius: 12 + } +}) diff --git a/src/FLAD/components/UserInfoBadgeComponent.tsx b/src/FLAD/components/UserInfoBadgeComponent.tsx new file mode 100644 index 0000000..5afe566 --- /dev/null +++ b/src/FLAD/components/UserInfoBadgeComponent.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { StyleSheet, Text, View, Image } from 'react-native'; +import normalize from './Normalize'; + +type UserInfoProps = { + image: string, + date: Date, + distance: string, +} + +export default function UserInfoBadge(props: UserInfoProps) { + const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + alignItems: 'center', + height: 30, + paddingHorizontal: normalize(10), + backgroundColor: '#3F1DC3', + alignSelf: 'flex-start', + borderRadius: 10, + paddingRight: 20, + }, + section: { + flexDirection: 'row', + alignItems: 'center', + }, + image: { + width: normalize(26), + height: normalize(26), + borderRadius: 30, + marginRight: normalize(10), + }, + text: { + fontSize: normalize(14), + color: '#FFFFFF', + }, + boldText: { + fontWeight: 'bold', + }, + }); + + const date = formatDate(props.date); + + let timeSection; + + if (date.days !== 0) { + timeSection = ( + + {date.days} + j + + ); + } else if (date.hours !== 0) { + timeSection = ( + + {date.hours} + h + + ); + } else { + timeSection = ( + + {date.minutes} + min + + ); + } + + return ( + + + Il y a + {timeSection} + {'<'} + {props.distance} + m + + ); +} + +const formatDate = (date: Date) => { + const now = new Date(); + const diffInMilliseconds = now.getTime() - date.getTime(); + const diffInSeconds = Math.floor(diffInMilliseconds / 1000); + const diffInMinutes = Math.floor(diffInSeconds / 60); + const diffInHours = Math.floor(diffInMinutes / 60); + const diffInDays = Math.floor(diffInHours / 24); + + const days = Math.floor(diffInDays); + const hours = Math.floor(diffInHours % 24); + const minutes = Math.floor(diffInMinutes % 60); + + return { days, hours, minutes }; +}; diff --git a/src/FLAD/components/button/button.tsx b/src/FLAD/components/button/button.tsx deleted file mode 100644 index fbeedb1..0000000 --- a/src/FLAD/components/button/button.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Image , StyleSheet, Pressable } from 'react-native' -import React from 'react' -import Icons from '../../assets/icons/icons/icon'; -import { RiveRef } from 'rive-react-native'; - -interface ButtonProps { - name : string; -} - -const FladButton = ({name} : ButtonProps) => { - const riveRef = React.useRef(null); - - const handlePlay = () => { riveRef.current?.play() }; - return ( - - - - ); - }; - -const styles = StyleSheet.create({ - button : { - justifyContent : 'center', - alignItems : 'center', - }, - image : { - borderRadius : 24, - resizeMode: 'cover', - width: 65, - height: 65, - backgroundColor: 'black', -} -}) - -export default FladButton; - - - - diff --git a/src/FLAD/components/littleCard.tsx b/src/FLAD/components/littleCard.tsx deleted file mode 100644 index 70c972a..0000000 --- a/src/FLAD/components/littleCard.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { View, Text, StyleSheet, Image } from 'react-native'; - -export interface RenderCellProps { - data : any; -} -export const LittleCard = (props: RenderCellProps) => { - return ( - - - {props.data.title} - - - ) -} - -const styles = StyleSheet.create({ - - similarContainer: { - marginHorizontal: 7 - }, - similarTitleFilm: { - color: "#DADADA", - paddingTop: 5, - fontWeight: "300" - }, - similarPoster: { - height: 160, - width: 160, - borderRadius: 16 - } -}) diff --git a/src/FLAD/constants/colors.ts b/src/FLAD/constants/colors.ts new file mode 100644 index 0000000..2d7f8ea --- /dev/null +++ b/src/FLAD/constants/colors.ts @@ -0,0 +1,20 @@ +export interface Theme { + body: string, + Text: string, + Card: string, + Line: string +} + +export const LightTheme: Theme = { + body: "#f2f2f6", + Text: "black", + Card: "#fff", + Line: "#e2e2e3" +} + +export const DarkTheme: Theme = { + body: "#141414", + Text: "white", + Card: "#232123", + Line: "#403F3F" +} \ No newline at end of file diff --git a/src/FLAD/data/data.ts b/src/FLAD/data/data.ts deleted file mode 100644 index 1902083..0000000 --- a/src/FLAD/data/data.ts +++ /dev/null @@ -1,105 +0,0 @@ -import Music from "../Model/Music"; -import { Spot } from "../Model/Spot"; - -export const cards = [{ - name: "blue", - sourceUrl: "https://th.bing.com/th/id/R.dbf87f0d8cbfd078ab6a589a5d921994?rik=1%2f6KliMpOAeh8A&pid=ImgRaw&r=0", - index: 4 -}, -{ - musicName: "Breathin", - name: "Ariana Grande", - sourceUrl: "https://i.ebayimg.com/images/g/rY0AAOSw97djEo2C/s-l500.jpg", - index: 9 -}, -{ - musicName: "Zombies", - name: "gambino", - sourceUrl: "https://th.bing.com/th/id/R.0b2d1a59bfda9b1a49ecb561e08535a8?rik=Xyc35OZU%2f6VOVw&pid=ImgRaw&r=0", - index: 3 -}, -{ - musicName: "Bambina", - name: "PNL", - sourceUrl: "https://upload.wikimedia.org/wikipedia/en/a/a0/PNL_-_Dans_la_l%C3%A9gende.png", - index: 10 -}, -{ - musicName: "Freeze Raël", - name: "Freeze Corleone", - sourceUrl: "https://intrld.com/wp-content/uploads/2020/08/freeze-corleone-la-menace-fanto%CC%82me.png", - index: 23 -}, -{ - musicName: "Autobahn", - name: "Sch", - sourceUrl: "https://images.genius.com/83b6c98680d38bde1571f6b4093244b5.1000x1000x1.jpg", - index: 44 -}, -{ - musicName: "Lakehouse", - name: "Stratos", - sourceUrl: "https://images.genius.com/ddc9cadedd1d4cef0860aaa85af9cd46.705x705x1.png", - index: 89 -}, - -] - -const spotArray: Spot[] = [ - new Spot("1", new Music("1", "Title 1", "Bio 1", "Image 1", "TrackPreviewUrl 1")), - new Spot("2", new Music("2", "Title 2", "Bio 2", "Image 2", "TrackPreviewUrl 2")), - new Spot("3", new Music("3", "Title 3", "Bio 3", "Image 3", "TrackPreviewUrl 3")), - new Spot("4", new Music("4", "Title 4", "Bio 4", "Image 4", "TrackPreviewUrl 4")), - new Spot("5", new Music("5", "Title 5", "Bio 5", "Image 5", "TrackPreviewUrl 5")), -]; -export const spotArray2: Spot[] = [ - new Spot("1", new Music("6KNw3UKRp3QRsO7Cf4ASVE", - "MOLLY - A COLORS SHOW", - "Tame Impala", - "https://i.scdn.co/image/ab67616d0000b2734299eb40408fc73ce8bf490a", - "https://p.scdn.co/mp3-preview/4faf99856f15e03a09d50b91006efd3205606866?cid=774b29d4f13844c495f206cafdad9c86") - ), - new Spot("2", new Music("5yHoANSze7sGzhn9MUarH3", - "Passat", - "Silk Sonic, Bruno Mars, Anderson .Paak", - "https://i.scdn.co/image/ab67616d0000b273e9df9b5a7df491536c51c922", - "https://p.scdn.co/mp3-preview/0bb7472026a00790950fc231fe61963ef7cc867b?cid=774b29d4f13844c495f206cafdad9c86") - ), - new Spot("3", new Music("7suNqxRED5CrwyZSzYC0nT", - "Extendo", - "Kali Uchis", - "https://i.scdn.co/image/ab67616d0000b273b856464c40a062d1723a21f2", - "https://p.scdn.co/mp3-preview/5398121f6295965e3c7cad8a6dca5667ba7f4713?cid=774b29d4f13844c495f206cafdad9c86") - ), - new Spot("4", new Music("07JqNLmPUJSlcouGQoJlzq", - "Addiction", - "Harry Styles", - "https://i.scdn.co/image/ab67616d0000b2739297f238f237431d56c67460", - "https://p.scdn.co/mp3-preview/33d12e9e5a3dd3394b1649d515912260b01579dd?cid=774b29d4f13844c495f206cafdad9c86") - ), - new Spot("5", new Music("5Ylp75kdffyJSwISRPqEiL", - "La Vidéo", - "Harry Styles", - "https://i.scdn.co/image/ab67616d0000b2738900d48677696015bf325b8b", - "https://p.scdn.co/mp3-preview/4fff3f8d76a422f42cea39f001836a3d54937fc4?cid=774b29d4f13844c495f206cafdad9c86") - ), - new Spot("6", new Music("30d0q6kt1BIfwAQUCAfxVQ", - "Calme toi", - "Kerchack", - "https://i.scdn.co/image/ab67616d0000b273b4f73fb5c5ea299c7ebfbf60", - "https://p.scdn.co/mp3-preview/5de1103b9528c1e47e03d32b0aa5dbfe797191a2?cid=774b29d4f13844c495f206cafdad9c86") - ), - new Spot("7", new Music("7IXQrRgmHxWYWitSlyFY7z", - "Peur (feat. Ziak)", - "Ziak", - "https://i.scdn.co/image/ab67616d0000b273b533a3a5625bc6ce1be56a2e", - "https://p.scdn.co/mp3-preview/2c613f31b11375980aba80a5b535bf87ddb6211b?cid=774b29d4f13844c495f206cafdad9c86") - ), - new Spot("8", new Music("7wBoSW48q4ZFe8qSdozqqi", - "Blue", - "Kerchack", - "https://i.scdn.co/image/ab67616d0000b273cc4e66af40292c9d92146909", - "https://p.scdn.co/mp3-preview/401b51374dd3f2a15466a0b415a9ac7d2114a54b?cid=774b29d4f13844c495f206cafdad9c86") - ) - -]; diff --git a/src/FLAD/data/slides.tsx b/src/FLAD/data/slides.tsx index 610d95e..97cc078 100644 --- a/src/FLAD/data/slides.tsx +++ b/src/FLAD/data/slides.tsx @@ -1,20 +1,20 @@ export default [ { id: '1', - title: 'Bienvenue sur Flad', + title: 'Bienvenue sur Flad 🎶', description: 'L\'application pour découvrir de nouvelles musiques et vous faire de nouveaux amis', - image: require('../assets/images/Board_Image.png') + image: require('../assets/images/board_1.png') }, { id: '2', title: 'Tous les jours de nouvelles musiques qui peuvent vous plaire', - description: 'L\'application apprends de vous et de vos amis pour vous suggérer des albums et des musics', - image: require('../assets/images/Board_Image2.png') + description: 'L\'application apprend de vous et de vos amis pour vous suggérer des albums et des musiques', + image: require('../assets/images/board_2.png') }, { id: '3', title: 'La musique ça se partage', description: 'Faites connaissances avec de nouvelles personnes et partagez vos critiques', - image: require('../assets/images/Board_Image3.png') + image: require('../assets/images/board_3.png') } ] \ No newline at end of file diff --git a/src/FLAD/fladConfig.tsx b/src/FLAD/fladConfig.tsx deleted file mode 100644 index 0c6f028..0000000 --- a/src/FLAD/fladConfig.tsx +++ /dev/null @@ -1 +0,0 @@ -export const API_URL = "https://flad-api-production.up.railway.app" diff --git a/src/FLAD/lib/index.js b/src/FLAD/lib/index.js deleted file mode 100644 index 14f24b2..0000000 --- a/src/FLAD/lib/index.js +++ /dev/null @@ -1,7 +0,0 @@ -Object.defineProperty(exports, '__esModule', {value: true}); -require('./mqttLib'); -const storage = require('./storage'); -function initialize() { - global.localStorage = storage; -} -exports.default = initialize; \ No newline at end of file diff --git a/src/FLAD/lib/mqtt.js b/src/FLAD/lib/mqtt.js deleted file mode 100644 index 612845e..0000000 --- a/src/FLAD/lib/mqtt.js +++ /dev/null @@ -1,2395 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Andrew Banks - initial API and implementation and initial documentation - *******************************************************************************/ - - -// Only expose a single object name in the global namespace. -// Everything must go through this module. Global Paho module -// only has a single public function, client, which returns -// a Paho client object given connection details. - -/** - * Send and receive messages using web browsers. - *

- * This programming interface lets a JavaScript client application use the MQTT V3.1 or - * V3.1.1 protocol to connect to an MQTT-supporting messaging server. - * - * The function supported includes: - *

    - *
  1. Connecting to and disconnecting from a server. The server is identified by its host name and port number. - *
  2. Specifying options that relate to the communications link with the server, - * for example the frequency of keep-alive heartbeats, and whether SSL/TLS is required. - *
  3. Subscribing to and receiving messages from MQTT Topics. - *
  4. Publishing messages to MQTT Topics. - *
- *

- * The API consists of two main objects: - *

- *
{@link Paho.Client}
- *
This contains methods that provide the functionality of the API, - * including provision of callbacks that notify the application when a message - * arrives from or is delivered to the messaging server, - * or when the status of its connection to the messaging server changes.
- *
{@link Paho.Message}
- *
This encapsulates the payload of the message along with various attributes - * associated with its delivery, in particular the destination to which it has - * been (or is about to be) sent.
- *
- *

- * The programming interface validates parameters passed to it, and will throw - * an Error containing an error message intended for developer use, if it detects - * an error with any parameter. - *

- * Example: - * - *

-var client = new Paho.MQTT.Client(location.hostname, Number(location.port), "clientId");
-client.onConnectionLost = onConnectionLost;
-client.onMessageArrived = onMessageArrived;
-client.connect({onSuccess:onConnect});
-
-function onConnect() {
-  // Once a connection has been made, make a subscription and send a message.
-  console.log("onConnect");
-  client.subscribe("/World");
-  var message = new Paho.MQTT.Message("Hello");
-  message.destinationName = "/World";
-  client.send(message);
-};
-function onConnectionLost(responseObject) {
-  if (responseObject.errorCode !== 0)
-	console.log("onConnectionLost:"+responseObject.errorMessage);
-};
-function onMessageArrived(message) {
-  console.log("onMessageArrived:"+message.payloadString);
-  client.disconnect();
-};
- * 
- * @namespace Paho - */ - -/* jshint shadow:true */ -(function ExportLibrary(root, factory) { - if(typeof exports === "object" && typeof module === "object"){ - module.exports = factory(); - } else if (typeof define === "function" && define.amd){ - define(factory); - } else if (typeof exports === "object"){ - exports = factory(); - } else { - //if (typeof root.Paho === "undefined"){ - // root.Paho = {}; - //} - root.Paho = factory(); - } -})(this, function LibraryFactory(){ - - - var PahoMQTT = (function (global) { - - // Private variables below, these are only visible inside the function closure - // which is used to define the module. - var version = "@VERSION@-@BUILDLEVEL@"; - - /** - * @private - */ - var localStorage = global.localStorage || (function () { - var data = {}; - - return { - setItem: function (key, item) { data[key] = item; }, - getItem: function (key) { return data[key]; }, - removeItem: function (key) { delete data[key]; }, - }; - })(); - - /** - * Unique message type identifiers, with associated - * associated integer values. - * @private - */ - var MESSAGE_TYPE = { - CONNECT: 1, - CONNACK: 2, - PUBLISH: 3, - PUBACK: 4, - PUBREC: 5, - PUBREL: 6, - PUBCOMP: 7, - SUBSCRIBE: 8, - SUBACK: 9, - UNSUBSCRIBE: 10, - UNSUBACK: 11, - PINGREQ: 12, - PINGRESP: 13, - DISCONNECT: 14 - }; - - // Collection of utility methods used to simplify module code - // and promote the DRY pattern. - - /** - * Validate an object's parameter names to ensure they - * match a list of expected variables name for this option - * type. Used to ensure option object passed into the API don't - * contain erroneous parameters. - * @param {Object} obj - User options object - * @param {Object} keys - valid keys and types that may exist in obj. - * @throws {Error} Invalid option parameter found. - * @private - */ - var validate = function(obj, keys) { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - if (keys.hasOwnProperty(key)) { - if (typeof obj[key] !== keys[key]) - throw new Error(format(ERROR.INVALID_TYPE, [typeof obj[key], key])); - } else { - var errorStr = "Unknown property, " + key + ". Valid properties are:"; - for (var validKey in keys) - if (keys.hasOwnProperty(validKey)) - errorStr = errorStr+" "+validKey; - throw new Error(errorStr); - } - } - } - }; - - /** - * Return a new function which runs the user function bound - * to a fixed scope. - * @param {function} User function - * @param {object} Function scope - * @return {function} User function bound to another scope - * @private - */ - var scope = function (f, scope) { - return function () { - return f.apply(scope, arguments); - }; - }; - - /** - * Unique message type identifiers, with associated - * associated integer values. - * @private - */ - var ERROR = { - OK: {code:0, text:"AMQJSC0000I OK."}, - CONNECT_TIMEOUT: {code:1, text:"AMQJSC0001E Connect timed out."}, - SUBSCRIBE_TIMEOUT: {code:2, text:"AMQJS0002E Subscribe timed out."}, - UNSUBSCRIBE_TIMEOUT: {code:3, text:"AMQJS0003E Unsubscribe timed out."}, - PING_TIMEOUT: {code:4, text:"AMQJS0004E Ping timed out."}, - INTERNAL_ERROR: {code:5, text:"AMQJS0005E Internal error. Error Message: {0}, Stack trace: {1}"}, - CONNACK_RETURNCODE: {code:6, text:"AMQJS0006E Bad Connack return code:{0} {1}."}, - SOCKET_ERROR: {code:7, text:"AMQJS0007E Socket error:{0}."}, - SOCKET_CLOSE: {code:8, text:"AMQJS0008I Socket closed."}, - MALFORMED_UTF: {code:9, text:"AMQJS0009E Malformed UTF data:{0} {1} {2}."}, - UNSUPPORTED: {code:10, text:"AMQJS0010E {0} is not supported by this browser."}, - INVALID_STATE: {code:11, text:"AMQJS0011E Invalid state {0}."}, - INVALID_TYPE: {code:12, text:"AMQJS0012E Invalid type {0} for {1}."}, - INVALID_ARGUMENT: {code:13, text:"AMQJS0013E Invalid argument {0} for {1}."}, - UNSUPPORTED_OPERATION: {code:14, text:"AMQJS0014E Unsupported operation."}, - INVALID_STORED_DATA: {code:15, text:"AMQJS0015E Invalid data in local storage key={0} value={1}."}, - INVALID_MQTT_MESSAGE_TYPE: {code:16, text:"AMQJS0016E Invalid MQTT message type {0}."}, - MALFORMED_UNICODE: {code:17, text:"AMQJS0017E Malformed Unicode string:{0} {1}."}, - BUFFER_FULL: {code:18, text:"AMQJS0018E Message buffer is full, maximum buffer size: {0}."}, - }; - - /** CONNACK RC Meaning. */ - var CONNACK_RC = { - 0:"Connection Accepted", - 1:"Connection Refused: unacceptable protocol version", - 2:"Connection Refused: identifier rejected", - 3:"Connection Refused: server unavailable", - 4:"Connection Refused: bad user name or password", - 5:"Connection Refused: not authorized" - }; - - /** - * Format an error message text. - * @private - * @param {error} ERROR value above. - * @param {substitutions} [array] substituted into the text. - * @return the text with the substitutions made. - */ - var format = function(error, substitutions) { - var text = error.text; - if (substitutions) { - var field,start; - for (var i=0; i 0) { - var part1 = text.substring(0,start); - var part2 = text.substring(start+field.length); - text = part1+substitutions[i]+part2; - } - } - } - return text; - }; - - //MQTT protocol and version 6 M Q I s d p 3 - var MqttProtoIdentifierv3 = [0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70,0x03]; - //MQTT proto/version for 311 4 M Q T T 4 - var MqttProtoIdentifierv4 = [0x00,0x04,0x4d,0x51,0x54,0x54,0x04]; - - /** - * Construct an MQTT wire protocol message. - * @param type MQTT packet type. - * @param options optional wire message attributes. - * - * Optional properties - * - * messageIdentifier: message ID in the range [0..65535] - * payloadMessage: Application Message - PUBLISH only - * connectStrings: array of 0 or more Strings to be put into the CONNECT payload - * topics: array of strings (SUBSCRIBE, UNSUBSCRIBE) - * requestQoS: array of QoS values [0..2] - * - * "Flag" properties - * cleanSession: true if present / false if absent (CONNECT) - * willMessage: true if present / false if absent (CONNECT) - * isRetained: true if present / false if absent (CONNECT) - * userName: true if present / false if absent (CONNECT) - * password: true if present / false if absent (CONNECT) - * keepAliveInterval: integer [0..65535] (CONNECT) - * - * @private - * @ignore - */ - var WireMessage = function (type, options) { - this.type = type; - for (var name in options) { - if (options.hasOwnProperty(name)) { - this[name] = options[name]; - } - } - }; - - WireMessage.prototype.encode = function() { - // Compute the first byte of the fixed header - var first = ((this.type & 0x0f) << 4); - - /* - * Now calculate the length of the variable header + payload by adding up the lengths - * of all the component parts - */ - - var remLength = 0; - var topicStrLength = []; - var destinationNameLength = 0; - var willMessagePayloadBytes; - - // if the message contains a messageIdentifier then we need two bytes for that - if (this.messageIdentifier !== undefined) - remLength += 2; - - switch(this.type) { - // If this a Connect then we need to include 12 bytes for its header - case MESSAGE_TYPE.CONNECT: - switch(this.mqttVersion) { - case 3: - remLength += MqttProtoIdentifierv3.length + 3; - break; - case 4: - remLength += MqttProtoIdentifierv4.length + 3; - break; - } - - remLength += UTF8Length(this.clientId) + 2; - if (this.willMessage !== undefined) { - remLength += UTF8Length(this.willMessage.destinationName) + 2; - // Will message is always a string, sent as UTF-8 characters with a preceding length. - willMessagePayloadBytes = this.willMessage.payloadBytes; - if (!(willMessagePayloadBytes instanceof Uint8Array)) - willMessagePayloadBytes = new Uint8Array(payloadBytes); - remLength += willMessagePayloadBytes.byteLength +2; - } - if (this.userName !== undefined) - remLength += UTF8Length(this.userName) + 2; - if (this.password !== undefined) - remLength += UTF8Length(this.password) + 2; - break; - - // Subscribe, Unsubscribe can both contain topic strings - case MESSAGE_TYPE.SUBSCRIBE: - first |= 0x02; // Qos = 1; - for ( var i = 0; i < this.topics.length; i++) { - topicStrLength[i] = UTF8Length(this.topics[i]); - remLength += topicStrLength[i] + 2; - } - remLength += this.requestedQos.length; // 1 byte for each topic's Qos - // QoS on Subscribe only - break; - - case MESSAGE_TYPE.UNSUBSCRIBE: - first |= 0x02; // Qos = 1; - for ( var i = 0; i < this.topics.length; i++) { - topicStrLength[i] = UTF8Length(this.topics[i]); - remLength += topicStrLength[i] + 2; - } - break; - - case MESSAGE_TYPE.PUBREL: - first |= 0x02; // Qos = 1; - break; - - case MESSAGE_TYPE.PUBLISH: - if (this.payloadMessage.duplicate) first |= 0x08; - first = first |= (this.payloadMessage.qos << 1); - if (this.payloadMessage.retained) first |= 0x01; - destinationNameLength = UTF8Length(this.payloadMessage.destinationName); - remLength += destinationNameLength + 2; - var payloadBytes = this.payloadMessage.payloadBytes; - remLength += payloadBytes.byteLength; - if (payloadBytes instanceof ArrayBuffer) - payloadBytes = new Uint8Array(payloadBytes); - else if (!(payloadBytes instanceof Uint8Array)) - payloadBytes = new Uint8Array(payloadBytes.buffer); - break; - - case MESSAGE_TYPE.DISCONNECT: - break; - - default: - break; - } - - // Now we can allocate a buffer for the message - - var mbi = encodeMBI(remLength); // Convert the length to MQTT MBI format - var pos = mbi.length + 1; // Offset of start of variable header - var buffer = new ArrayBuffer(remLength + pos); - var byteStream = new Uint8Array(buffer); // view it as a sequence of bytes - - //Write the fixed header into the buffer - byteStream[0] = first; - byteStream.set(mbi,1); - - // If this is a PUBLISH then the variable header starts with a topic - if (this.type == MESSAGE_TYPE.PUBLISH) - pos = writeString(this.payloadMessage.destinationName, destinationNameLength, byteStream, pos); - // If this is a CONNECT then the variable header contains the protocol name/version, flags and keepalive time - - else if (this.type == MESSAGE_TYPE.CONNECT) { - switch (this.mqttVersion) { - case 3: - byteStream.set(MqttProtoIdentifierv3, pos); - pos += MqttProtoIdentifierv3.length; - break; - case 4: - byteStream.set(MqttProtoIdentifierv4, pos); - pos += MqttProtoIdentifierv4.length; - break; - } - var connectFlags = 0; - if (this.cleanSession) - connectFlags = 0x02; - if (this.willMessage !== undefined ) { - connectFlags |= 0x04; - connectFlags |= (this.willMessage.qos<<3); - if (this.willMessage.retained) { - connectFlags |= 0x20; - } - } - if (this.userName !== undefined) - connectFlags |= 0x80; - if (this.password !== undefined) - connectFlags |= 0x40; - byteStream[pos++] = connectFlags; - pos = writeUint16 (this.keepAliveInterval, byteStream, pos); - } - - // Output the messageIdentifier - if there is one - if (this.messageIdentifier !== undefined) - pos = writeUint16 (this.messageIdentifier, byteStream, pos); - - switch(this.type) { - case MESSAGE_TYPE.CONNECT: - pos = writeString(this.clientId, UTF8Length(this.clientId), byteStream, pos); - if (this.willMessage !== undefined) { - pos = writeString(this.willMessage.destinationName, UTF8Length(this.willMessage.destinationName), byteStream, pos); - pos = writeUint16(willMessagePayloadBytes.byteLength, byteStream, pos); - byteStream.set(willMessagePayloadBytes, pos); - pos += willMessagePayloadBytes.byteLength; - - } - if (this.userName !== undefined) - pos = writeString(this.userName, UTF8Length(this.userName), byteStream, pos); - if (this.password !== undefined) - pos = writeString(this.password, UTF8Length(this.password), byteStream, pos); - break; - - case MESSAGE_TYPE.PUBLISH: - // PUBLISH has a text or binary payload, if text do not add a 2 byte length field, just the UTF characters. - byteStream.set(payloadBytes, pos); - - break; - - // case MESSAGE_TYPE.PUBREC: - // case MESSAGE_TYPE.PUBREL: - // case MESSAGE_TYPE.PUBCOMP: - // break; - - case MESSAGE_TYPE.SUBSCRIBE: - // SUBSCRIBE has a list of topic strings and request QoS - for (var i=0; i> 4; - var messageInfo = first &= 0x0f; - pos += 1; - - - // Decode the remaining length (MBI format) - - var digit; - var remLength = 0; - var multiplier = 1; - do { - if (pos == input.length) { - return [null,startingPos]; - } - digit = input[pos++]; - remLength += ((digit & 0x7F) * multiplier); - multiplier *= 128; - } while ((digit & 0x80) !== 0); - - var endPos = pos+remLength; - if (endPos > input.length) { - return [null,startingPos]; - } - - var wireMessage = new WireMessage(type); - switch(type) { - case MESSAGE_TYPE.CONNACK: - var connectAcknowledgeFlags = input[pos++]; - if (connectAcknowledgeFlags & 0x01) - wireMessage.sessionPresent = true; - wireMessage.returnCode = input[pos++]; - break; - - case MESSAGE_TYPE.PUBLISH: - var qos = (messageInfo >> 1) & 0x03; - - var len = readUint16(input, pos); - pos += 2; - var topicName = parseUTF8(input, pos, len); - pos += len; - // If QoS 1 or 2 there will be a messageIdentifier - if (qos > 0) { - wireMessage.messageIdentifier = readUint16(input, pos); - pos += 2; - } - - var message = new Message(input.subarray(pos, endPos)); - if ((messageInfo & 0x01) == 0x01) - message.retained = true; - if ((messageInfo & 0x08) == 0x08) - message.duplicate = true; - message.qos = qos; - message.destinationName = topicName; - wireMessage.payloadMessage = message; - break; - - case MESSAGE_TYPE.PUBACK: - case MESSAGE_TYPE.PUBREC: - case MESSAGE_TYPE.PUBREL: - case MESSAGE_TYPE.PUBCOMP: - case MESSAGE_TYPE.UNSUBACK: - wireMessage.messageIdentifier = readUint16(input, pos); - break; - - case MESSAGE_TYPE.SUBACK: - wireMessage.messageIdentifier = readUint16(input, pos); - pos += 2; - wireMessage.returnCode = input.subarray(pos, endPos); - break; - - default: - break; - } - - return [wireMessage,endPos]; - } - - function writeUint16(input, buffer, offset) { - buffer[offset++] = input >> 8; //MSB - buffer[offset++] = input % 256; //LSB - return offset; - } - - function writeString(input, utf8Length, buffer, offset) { - offset = writeUint16(utf8Length, buffer, offset); - stringToUTF8(input, buffer, offset); - return offset + utf8Length; - } - - function readUint16(buffer, offset) { - return 256*buffer[offset] + buffer[offset+1]; - } - - /** - * Encodes an MQTT Multi-Byte Integer - * @private - */ - function encodeMBI(number) { - var output = new Array(1); - var numBytes = 0; - - do { - var digit = number % 128; - number = number >> 7; - if (number > 0) { - digit |= 0x80; - } - output[numBytes++] = digit; - } while ( (number > 0) && (numBytes<4) ); - - return output; - } - - /** - * Takes a String and calculates its length in bytes when encoded in UTF8. - * @private - */ - function UTF8Length(input) { - var output = 0; - for (var i = 0; i 0x7FF) - { - // Surrogate pair means its a 4 byte character - if (0xD800 <= charCode && charCode <= 0xDBFF) - { - i++; - output++; - } - output +=3; - } - else if (charCode > 0x7F) - output +=2; - else - output++; - } - return output; - } - - /** - * Takes a String and writes it into an array as UTF8 encoded bytes. - * @private - */ - function stringToUTF8(input, output, start) { - var pos = start; - for (var i = 0; i>6 & 0x1F | 0xC0; - output[pos++] = charCode & 0x3F | 0x80; - } else if (charCode <= 0xFFFF) { - output[pos++] = charCode>>12 & 0x0F | 0xE0; - output[pos++] = charCode>>6 & 0x3F | 0x80; - output[pos++] = charCode & 0x3F | 0x80; - } else { - output[pos++] = charCode>>18 & 0x07 | 0xF0; - output[pos++] = charCode>>12 & 0x3F | 0x80; - output[pos++] = charCode>>6 & 0x3F | 0x80; - output[pos++] = charCode & 0x3F | 0x80; - } - } - return output; - } - - function parseUTF8(input, offset, length) { - var output = ""; - var utf16; - var pos = offset; - - while (pos < offset+length) - { - var byte1 = input[pos++]; - if (byte1 < 128) - utf16 = byte1; - else - { - var byte2 = input[pos++]-128; - if (byte2 < 0) - throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16),""])); - if (byte1 < 0xE0) // 2 byte character - utf16 = 64*(byte1-0xC0) + byte2; - else - { - var byte3 = input[pos++]-128; - if (byte3 < 0) - throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16)])); - if (byte1 < 0xF0) // 3 byte character - utf16 = 4096*(byte1-0xE0) + 64*byte2 + byte3; - else - { - var byte4 = input[pos++]-128; - if (byte4 < 0) - throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); - if (byte1 < 0xF8) // 4 byte character - utf16 = 262144*(byte1-0xF0) + 4096*byte2 + 64*byte3 + byte4; - else // longer encodings are not supported - throw new Error(format(ERROR.MALFORMED_UTF, [byte1.toString(16), byte2.toString(16), byte3.toString(16), byte4.toString(16)])); - } - } - } - - if (utf16 > 0xFFFF) // 4 byte character - express as a surrogate pair - { - utf16 -= 0x10000; - output += String.fromCharCode(0xD800 + (utf16 >> 10)); // lead character - utf16 = 0xDC00 + (utf16 & 0x3FF); // trail character - } - output += String.fromCharCode(utf16); - } - return output; - } - - /** - * Repeat keepalive requests, monitor responses. - * @ignore - */ - var Pinger = function(client, keepAliveInterval) { - this._client = client; - this._keepAliveInterval = keepAliveInterval*1000; - this.isReset = false; - - var pingReq = new WireMessage(MESSAGE_TYPE.PINGREQ).encode(); - - var doTimeout = function (pinger) { - return function () { - return doPing.apply(pinger); - }; - }; - - /** @ignore */ - var doPing = function() { - if (!this.isReset) { - this._client._trace("Pinger.doPing", "Timed out"); - this._client._disconnected( ERROR.PING_TIMEOUT.code , format(ERROR.PING_TIMEOUT)); - } else { - this.isReset = false; - this._client._trace("Pinger.doPing", "send PINGREQ"); - this._client.socket.send(pingReq); - this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); - } - }; - - this.reset = function() { - this.isReset = true; - clearTimeout(this.timeout); - if (this._keepAliveInterval > 0) - this.timeout = setTimeout(doTimeout(this), this._keepAliveInterval); - }; - - this.cancel = function() { - clearTimeout(this.timeout); - }; - }; - - /** - * Monitor request completion. - * @ignore - */ - var Timeout = function(client, timeoutSeconds, action, args) { - if (!timeoutSeconds) - timeoutSeconds = 30; - - var doTimeout = function (action, client, args) { - return function () { - return action.apply(client, args); - }; - }; - this.timeout = setTimeout(doTimeout(action, client, args), timeoutSeconds * 1000); - - this.cancel = function() { - clearTimeout(this.timeout); - }; - }; - - /** - * Internal implementation of the Websockets MQTT V3.1 client. - * - * @name Paho.ClientImpl @constructor - * @param {String} host the DNS nameof the webSocket host. - * @param {Number} port the port number for that host. - * @param {String} clientId the MQ client identifier. - */ - var ClientImpl = function (uri, host, port, path, clientId) { - // Check dependencies are satisfied in this browser. - if (!("WebSocket" in global && global.WebSocket !== null)) { - throw new Error(format(ERROR.UNSUPPORTED, ["WebSocket"])); - } - if (!("ArrayBuffer" in global && global.ArrayBuffer !== null)) { - throw new Error(format(ERROR.UNSUPPORTED, ["ArrayBuffer"])); - } - this._trace("Paho.Client", uri, host, port, path, clientId); - - this.host = host; - this.port = port; - this.path = path; - this.uri = uri; - this.clientId = clientId; - this._wsuri = null; - - // Local storagekeys are qualified with the following string. - // The conditional inclusion of path in the key is for backward - // compatibility to when the path was not configurable and assumed to - // be /mqtt - this._localKey=host+":"+port+(path!="/mqtt"?":"+path:"")+":"+clientId+":"; - - // Create private instance-only message queue - // Internal queue of messages to be sent, in sending order. - this._msg_queue = []; - this._buffered_msg_queue = []; - - // Messages we have sent and are expecting a response for, indexed by their respective message ids. - this._sentMessages = {}; - - // Messages we have received and acknowleged and are expecting a confirm message for - // indexed by their respective message ids. - this._receivedMessages = {}; - - // Internal list of callbacks to be executed when messages - // have been successfully sent over web socket, e.g. disconnect - // when it doesn't have to wait for ACK, just message is dispatched. - this._notify_msg_sent = {}; - - // Unique identifier for SEND messages, incrementing - // counter as messages are sent. - this._message_identifier = 1; - - // Used to determine the transmission sequence of stored sent messages. - this._sequence = 0; - - - // Load the local state, if any, from the saved version, only restore state relevant to this client. - for (var key in localStorage) - if ( key.indexOf("Sent:"+this._localKey) === 0 || key.indexOf("Received:"+this._localKey) === 0) - this.restore(key); - }; - - // Messaging Client public instance members. - ClientImpl.prototype.host = null; - ClientImpl.prototype.port = null; - ClientImpl.prototype.path = null; - ClientImpl.prototype.uri = null; - ClientImpl.prototype.clientId = null; - - // Messaging Client private instance members. - ClientImpl.prototype.socket = null; - /* true once we have received an acknowledgement to a CONNECT packet. */ - ClientImpl.prototype.connected = false; - /* The largest message identifier allowed, may not be larger than 2**16 but - * if set smaller reduces the maximum number of outbound messages allowed. - */ - ClientImpl.prototype.maxMessageIdentifier = 65536; - ClientImpl.prototype.connectOptions = null; - ClientImpl.prototype.hostIndex = null; - ClientImpl.prototype.onConnected = null; - ClientImpl.prototype.onConnectionLost = null; - ClientImpl.prototype.onMessageDelivered = null; - ClientImpl.prototype.onMessageArrived = null; - ClientImpl.prototype.traceFunction = null; - ClientImpl.prototype._msg_queue = null; - ClientImpl.prototype._buffered_msg_queue = null; - ClientImpl.prototype._connectTimeout = null; - /* The sendPinger monitors how long we allow before we send data to prove to the server that we are alive. */ - ClientImpl.prototype.sendPinger = null; - /* The receivePinger monitors how long we allow before we require evidence that the server is alive. */ - ClientImpl.prototype.receivePinger = null; - ClientImpl.prototype._reconnectInterval = 1; // Reconnect Delay, starts at 1 second - ClientImpl.prototype._reconnecting = false; - ClientImpl.prototype._reconnectTimeout = null; - ClientImpl.prototype.disconnectedPublishing = false; - ClientImpl.prototype.disconnectedBufferSize = 5000; - - ClientImpl.prototype.receiveBuffer = null; - - ClientImpl.prototype._traceBuffer = null; - ClientImpl.prototype._MAX_TRACE_ENTRIES = 100; - - ClientImpl.prototype.connect = function (connectOptions) { - var connectOptionsMasked = this._traceMask(connectOptions, "password"); - this._trace("Client.connect", connectOptionsMasked, this.socket, this.connected); - - if (this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); - if (this.socket) - throw new Error(format(ERROR.INVALID_STATE, ["already connected"])); - - if (this._reconnecting) { - // connect() function is called while reconnect is in progress. - // Terminate the auto reconnect process to use new connect options. - this._reconnectTimeout.cancel(); - this._reconnectTimeout = null; - this._reconnecting = false; - } - - this.connectOptions = connectOptions; - this._reconnectInterval = 1; - this._reconnecting = false; - if (connectOptions.uris) { - this.hostIndex = 0; - this._doConnect(connectOptions.uris[0]); - } else { - this._doConnect(this.uri); - } - - }; - - ClientImpl.prototype.subscribe = function (filter, subscribeOptions) { - this._trace("Client.subscribe", filter, subscribeOptions); - - if (!this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - - var wireMessage = new WireMessage(MESSAGE_TYPE.SUBSCRIBE); - wireMessage.topics = filter.constructor === Array ? filter : [filter]; - if (subscribeOptions.qos === undefined) - subscribeOptions.qos = 0; - wireMessage.requestedQos = []; - for (var i = 0; i < wireMessage.topics.length; i++) - wireMessage.requestedQos[i] = subscribeOptions.qos; - - if (subscribeOptions.onSuccess) { - wireMessage.onSuccess = function(grantedQos) {subscribeOptions.onSuccess({invocationContext:subscribeOptions.invocationContext,grantedQos:grantedQos});}; - } - - if (subscribeOptions.onFailure) { - wireMessage.onFailure = function(errorCode) {subscribeOptions.onFailure({invocationContext:subscribeOptions.invocationContext,errorCode:errorCode, errorMessage:format(errorCode)});}; - } - - if (subscribeOptions.timeout) { - wireMessage.timeOut = new Timeout(this, subscribeOptions.timeout, subscribeOptions.onFailure, - [{invocationContext:subscribeOptions.invocationContext, - errorCode:ERROR.SUBSCRIBE_TIMEOUT.code, - errorMessage:format(ERROR.SUBSCRIBE_TIMEOUT)}]); - } - - // All subscriptions return a SUBACK. - this._requires_ack(wireMessage); - this._schedule_message(wireMessage); - }; - - /** @ignore */ - ClientImpl.prototype.unsubscribe = function(filter, unsubscribeOptions) { - this._trace("Client.unsubscribe", filter, unsubscribeOptions); - - if (!this.connected) - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - - var wireMessage = new WireMessage(MESSAGE_TYPE.UNSUBSCRIBE); - wireMessage.topics = filter.constructor === Array ? filter : [filter]; - - if (unsubscribeOptions.onSuccess) { - wireMessage.callback = function() {unsubscribeOptions.onSuccess({invocationContext:unsubscribeOptions.invocationContext});}; - } - if (unsubscribeOptions.timeout) { - wireMessage.timeOut = new Timeout(this, unsubscribeOptions.timeout, unsubscribeOptions.onFailure, - [{invocationContext:unsubscribeOptions.invocationContext, - errorCode:ERROR.UNSUBSCRIBE_TIMEOUT.code, - errorMessage:format(ERROR.UNSUBSCRIBE_TIMEOUT)}]); - } - - // All unsubscribes return a SUBACK. - this._requires_ack(wireMessage); - this._schedule_message(wireMessage); - }; - - ClientImpl.prototype.send = function (message) { - this._trace("Client.send", message); - - var wireMessage = new WireMessage(MESSAGE_TYPE.PUBLISH); - wireMessage.payloadMessage = message; - - if (this.connected) { - // Mark qos 1 & 2 message as "ACK required" - // For qos 0 message, invoke onMessageDelivered callback if there is one. - // Then schedule the message. - if (message.qos > 0) { - this._requires_ack(wireMessage); - } else if (this.onMessageDelivered) { - this._notify_msg_sent[wireMessage] = this.onMessageDelivered(wireMessage.payloadMessage); - } - this._schedule_message(wireMessage); - } else { - // Currently disconnected, will not schedule this message - // Check if reconnecting is in progress and disconnected publish is enabled. - if (this._reconnecting && this.disconnectedPublishing) { - // Check the limit which include the "required ACK" messages - var messageCount = Object.keys(this._sentMessages).length + this._buffered_msg_queue.length; - if (messageCount > this.disconnectedBufferSize) { - throw new Error(format(ERROR.BUFFER_FULL, [this.disconnectedBufferSize])); - } else { - if (message.qos > 0) { - // Mark this message as "ACK required" - this._requires_ack(wireMessage); - } else { - wireMessage.sequence = ++this._sequence; - // Add messages in fifo order to array, by adding to start - this._buffered_msg_queue.unshift(wireMessage); - } - } - } else { - throw new Error(format(ERROR.INVALID_STATE, ["not connected"])); - } - } - }; - - ClientImpl.prototype.disconnect = function () { - this._trace("Client.disconnect"); - - if (this._reconnecting) { - // disconnect() function is called while reconnect is in progress. - // Terminate the auto reconnect process. - this._reconnectTimeout.cancel(); - this._reconnectTimeout = null; - this._reconnecting = false; - } - - if (!this.socket) - throw new Error(format(ERROR.INVALID_STATE, ["not connecting or connected"])); - - var wireMessage = new WireMessage(MESSAGE_TYPE.DISCONNECT); - - // Run the disconnected call back as soon as the message has been sent, - // in case of a failure later on in the disconnect processing. - // as a consequence, the _disconected call back may be run several times. - this._notify_msg_sent[wireMessage] = scope(this._disconnected, this); - - this._schedule_message(wireMessage); - }; - - ClientImpl.prototype.getTraceLog = function () { - if ( this._traceBuffer !== null ) { - this._trace("Client.getTraceLog", new Date()); - this._trace("Client.getTraceLog in flight messages", this._sentMessages.length); - for (var key in this._sentMessages) - this._trace("_sentMessages ",key, this._sentMessages[key]); - for (var key in this._receivedMessages) - this._trace("_receivedMessages ",key, this._receivedMessages[key]); - - return this._traceBuffer; - } - }; - - ClientImpl.prototype.startTrace = function () { - if ( this._traceBuffer === null ) { - this._traceBuffer = []; - } - this._trace("Client.startTrace", new Date(), version); - }; - - ClientImpl.prototype.stopTrace = function () { - delete this._traceBuffer; - }; - - ClientImpl.prototype._doConnect = function (wsurl) { - // When the socket is open, this client will send the CONNECT WireMessage using the saved parameters. - if (this.connectOptions.useSSL) { - var uriParts = wsurl.split(":"); - uriParts[0] = "wss"; - wsurl = uriParts.join(":"); - } - this._wsuri = wsurl; - this.connected = false; - - - - if (this.connectOptions.mqttVersion < 4) { - this.socket = new WebSocket(wsurl, ["mqttv3.1"]); - } else { - this.socket = new WebSocket(wsurl, ["mqtt"]); - } - this.socket.binaryType = "arraybuffer"; - this.socket.onopen = scope(this._on_socket_open, this); - this.socket.onmessage = scope(this._on_socket_message, this); - this.socket.onerror = scope(this._on_socket_error, this); - this.socket.onclose = scope(this._on_socket_close, this); - - this.sendPinger = new Pinger(this, this.connectOptions.keepAliveInterval); - this.receivePinger = new Pinger(this, this.connectOptions.keepAliveInterval); - if (this._connectTimeout) { - this._connectTimeout.cancel(); - this._connectTimeout = null; - } - this._connectTimeout = new Timeout(this, this.connectOptions.timeout, this._disconnected, [ERROR.CONNECT_TIMEOUT.code, format(ERROR.CONNECT_TIMEOUT)]); - }; - - - // Schedule a new message to be sent over the WebSockets - // connection. CONNECT messages cause WebSocket connection - // to be started. All other messages are queued internally - // until this has happened. When WS connection starts, process - // all outstanding messages. - ClientImpl.prototype._schedule_message = function (message) { - // Add messages in fifo order to array, by adding to start - this._msg_queue.unshift(message); - // Process outstanding messages in the queue if we have an open socket, and have received CONNACK. - if (this.connected) { - this._process_queue(); - } - }; - - ClientImpl.prototype.store = function(prefix, wireMessage) { - var storedMessage = {type:wireMessage.type, messageIdentifier:wireMessage.messageIdentifier, version:1}; - - switch(wireMessage.type) { - case MESSAGE_TYPE.PUBLISH: - if(wireMessage.pubRecReceived) - storedMessage.pubRecReceived = true; - - // Convert the payload to a hex string. - storedMessage.payloadMessage = {}; - var hex = ""; - var messageBytes = wireMessage.payloadMessage.payloadBytes; - for (var i=0; i= 2) { - var x = parseInt(hex.substring(0, 2), 16); - hex = hex.substring(2, hex.length); - byteStream[i++] = x; - } - var payloadMessage = new Message(byteStream); - - payloadMessage.qos = storedMessage.payloadMessage.qos; - payloadMessage.destinationName = storedMessage.payloadMessage.destinationName; - if (storedMessage.payloadMessage.duplicate) - payloadMessage.duplicate = true; - if (storedMessage.payloadMessage.retained) - payloadMessage.retained = true; - wireMessage.payloadMessage = payloadMessage; - - break; - - default: - throw Error(format(ERROR.INVALID_STORED_DATA, [key, value])); - } - - if (key.indexOf("Sent:"+this._localKey) === 0) { - wireMessage.payloadMessage.duplicate = true; - this._sentMessages[wireMessage.messageIdentifier] = wireMessage; - } else if (key.indexOf("Received:"+this._localKey) === 0) { - this._receivedMessages[wireMessage.messageIdentifier] = wireMessage; - } - }; - - ClientImpl.prototype._process_queue = function () { - var message = null; - - // Send all queued messages down socket connection - while ((message = this._msg_queue.pop())) { - this._socket_send(message); - // Notify listeners that message was successfully sent - if (this._notify_msg_sent[message]) { - this._notify_msg_sent[message](); - delete this._notify_msg_sent[message]; - } - } - }; - - /** - * Expect an ACK response for this message. Add message to the set of in progress - * messages and set an unused identifier in this message. - * @ignore - */ - ClientImpl.prototype._requires_ack = function (wireMessage) { - var messageCount = Object.keys(this._sentMessages).length; - if (messageCount > this.maxMessageIdentifier) - throw Error ("Too many messages:"+messageCount); - - while(this._sentMessages[this._message_identifier] !== undefined) { - this._message_identifier++; - } - wireMessage.messageIdentifier = this._message_identifier; - this._sentMessages[wireMessage.messageIdentifier] = wireMessage; - if (wireMessage.type === MESSAGE_TYPE.PUBLISH) { - this.store("Sent:", wireMessage); - } - if (this._message_identifier === this.maxMessageIdentifier) { - this._message_identifier = 1; - } - }; - - /** - * Called when the underlying websocket has been opened. - * @ignore - */ - ClientImpl.prototype._on_socket_open = function () { - // Create the CONNECT message object. - var wireMessage = new WireMessage(MESSAGE_TYPE.CONNECT, this.connectOptions); - wireMessage.clientId = this.clientId; - this._socket_send(wireMessage); - }; - - /** - * Called when the underlying websocket has received a complete packet. - * @ignore - */ - ClientImpl.prototype._on_socket_message = function (event) { - this._trace("Client._on_socket_message", event.data); - var messages = this._deframeMessages(event.data); - for (var i = 0; i < messages.length; i+=1) { - this._handleMessage(messages[i]); - } - }; - - ClientImpl.prototype._deframeMessages = function(data) { - var byteArray = new Uint8Array(data); - var messages = []; - if (this.receiveBuffer) { - var newData = new Uint8Array(this.receiveBuffer.length+byteArray.length); - newData.set(this.receiveBuffer); - newData.set(byteArray,this.receiveBuffer.length); - byteArray = newData; - delete this.receiveBuffer; - } - try { - var offset = 0; - while(offset < byteArray.length) { - var result = decodeMessage(byteArray,offset); - var wireMessage = result[0]; - offset = result[1]; - if (wireMessage !== null) { - messages.push(wireMessage); - } else { - break; - } - } - if (offset < byteArray.length) { - this.receiveBuffer = byteArray.subarray(offset); - } - } catch (error) { - var errorStack = ((error.hasOwnProperty("stack") == "undefined") ? error.stack.toString() : "No Error Stack Available"); - this._disconnected(ERROR.INTERNAL_ERROR.code , format(ERROR.INTERNAL_ERROR, [error.message,errorStack])); - return; - } - return messages; - }; - - ClientImpl.prototype._handleMessage = function(wireMessage) { - - this._trace("Client._handleMessage", wireMessage); - - try { - switch(wireMessage.type) { - case MESSAGE_TYPE.CONNACK: - this._connectTimeout.cancel(); - if (this._reconnectTimeout) - this._reconnectTimeout.cancel(); - - // If we have started using clean session then clear up the local state. - if (this.connectOptions.cleanSession) { - for (var key in this._sentMessages) { - var sentMessage = this._sentMessages[key]; - localStorage.removeItem("Sent:"+this._localKey+sentMessage.messageIdentifier); - } - this._sentMessages = {}; - - for (var key in this._receivedMessages) { - var receivedMessage = this._receivedMessages[key]; - localStorage.removeItem("Received:"+this._localKey+receivedMessage.messageIdentifier); - } - this._receivedMessages = {}; - } - // Client connected and ready for business. - if (wireMessage.returnCode === 0) { - - this.connected = true; - // Jump to the end of the list of uris and stop looking for a good host. - - if (this.connectOptions.uris) - this.hostIndex = this.connectOptions.uris.length; - - } else { - this._disconnected(ERROR.CONNACK_RETURNCODE.code , format(ERROR.CONNACK_RETURNCODE, [wireMessage.returnCode, CONNACK_RC[wireMessage.returnCode]])); - break; - } - - // Resend messages. - var sequencedMessages = []; - for (var msgId in this._sentMessages) { - if (this._sentMessages.hasOwnProperty(msgId)) - sequencedMessages.push(this._sentMessages[msgId]); - } - - // Also schedule qos 0 buffered messages if any - if (this._buffered_msg_queue.length > 0) { - var msg = null; - while ((msg = this._buffered_msg_queue.pop())) { - sequencedMessages.push(msg); - if (this.onMessageDelivered) - this._notify_msg_sent[msg] = this.onMessageDelivered(msg.payloadMessage); - } - } - - // Sort sentMessages into the original sent order. - var sequencedMessages = sequencedMessages.sort(function(a,b) {return a.sequence - b.sequence;} ); - for (var i=0, len=sequencedMessages.length; i - * Most applications will create just one Client object and then call its connect() method, - * however applications can create more than one Client object if they wish. - * In this case the combination of host, port and clientId attributes must be different for each Client object. - *

- * The send, subscribe and unsubscribe methods are implemented as asynchronous JavaScript methods - * (even though the underlying protocol exchange might be synchronous in nature). - * This means they signal their completion by calling back to the application, - * via Success or Failure callback functions provided by the application on the method in question. - * Such callbacks are called at most once per method invocation and do not persist beyond the lifetime - * of the script that made the invocation. - *

- * In contrast there are some callback functions, most notably onMessageArrived, - * that are defined on the {@link Paho.Client} object. - * These may get called multiple times, and aren't directly related to specific method invocations made by the client. - * - * @name Paho.Client - * - * @constructor - * - * @param {string} host - the address of the messaging server, as a fully qualified WebSocket URI, as a DNS name or dotted decimal IP address. - * @param {number} port - the port number to connect to - only required if host is not a URI - * @param {string} path - the path on the host to connect to - only used if host is not a URI. Default: '/mqtt'. - * @param {string} clientId - the Messaging client identifier, between 1 and 23 characters in length. - * - * @property {string} host - read only the server's DNS hostname or dotted decimal IP address. - * @property {number} port - read only the server's port. - * @property {string} path - read only the server's path. - * @property {string} clientId - read only used when connecting to the server. - * @property {function} onConnectionLost - called when a connection has been lost. - * after a connect() method has succeeded. - * Establish the call back used when a connection has been lost. The connection may be - * lost because the client initiates a disconnect or because the server or network - * cause the client to be disconnected. The disconnect call back may be called without - * the connectionComplete call back being invoked if, for example the client fails to - * connect. - * A single response object parameter is passed to the onConnectionLost callback containing the following fields: - *

    - *
  1. errorCode - *
  2. errorMessage - *
- * @property {function} onMessageDelivered - called when a message has been delivered. - * All processing that this Client will ever do has been completed. So, for example, - * in the case of a Qos=2 message sent by this client, the PubComp flow has been received from the server - * and the message has been removed from persistent storage before this callback is invoked. - * Parameters passed to the onMessageDelivered callback are: - *
    - *
  1. {@link Paho.Message} that was delivered. - *
- * @property {function} onMessageArrived - called when a message has arrived in this Paho.client. - * Parameters passed to the onMessageArrived callback are: - *
    - *
  1. {@link Paho.Message} that has arrived. - *
- * @property {function} onConnected - called when a connection is successfully made to the server. - * after a connect() method. - * Parameters passed to the onConnected callback are: - *
    - *
  1. reconnect (boolean) - If true, the connection was the result of a reconnect.
  2. - *
  3. URI (string) - The URI used to connect to the server.
  4. - *
- * @property {boolean} disconnectedPublishing - if set, will enable disconnected publishing in - * in the event that the connection to the server is lost. - * @property {number} disconnectedBufferSize - Used to set the maximum number of messages that the disconnected - * buffer will hold before rejecting new messages. Default size: 5000 messages - * @property {function} trace - called whenever trace is called. TODO - */ - var Client = function (host, port, path, clientId) { - - var uri; - - if (typeof host !== "string") - throw new Error(format(ERROR.INVALID_TYPE, [typeof host, "host"])); - - if (arguments.length == 2) { - // host: must be full ws:// uri - // port: clientId - clientId = port; - uri = host; - var match = uri.match(/^(wss?):\/\/((\[(.+)\])|([^\/]+?))(:(\d+))?(\/.*)$/); - if (match) { - host = match[4]||match[2]; - port = parseInt(match[7]); - path = match[8]; - } else { - throw new Error(format(ERROR.INVALID_ARGUMENT,[host,"host"])); - } - } else { - if (arguments.length == 3) { - clientId = path; - path = "/mqtt"; - } - if (typeof port !== "number" || port < 0) - throw new Error(format(ERROR.INVALID_TYPE, [typeof port, "port"])); - if (typeof path !== "string") - throw new Error(format(ERROR.INVALID_TYPE, [typeof path, "path"])); - - var ipv6AddSBracket = (host.indexOf(":") !== -1 && host.slice(0,1) !== "[" && host.slice(-1) !== "]"); - uri = "ws://"+(ipv6AddSBracket?"["+host+"]":host)+":"+port+path; - } - - var clientIdLength = 0; - for (var i = 0; i 65535) - throw new Error(format(ERROR.INVALID_ARGUMENT, [clientId, "clientId"])); - - var client = new ClientImpl(uri, host, port, path, clientId); - - //Public Properties - Object.defineProperties(this,{ - "host":{ - get: function() { return host; }, - set: function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); } - }, - "port":{ - get: function() { return port; }, - set: function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); } - }, - "path":{ - get: function() { return path; }, - set: function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); } - }, - "uri":{ - get: function() { return uri; }, - set: function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); } - }, - "clientId":{ - get: function() { return client.clientId; }, - set: function() { throw new Error(format(ERROR.UNSUPPORTED_OPERATION)); } - }, - "onConnected":{ - get: function() { return client.onConnected; }, - set: function(newOnConnected) { - if (typeof newOnConnected === "function") - client.onConnected = newOnConnected; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnected, "onConnected"])); - } - }, - "disconnectedPublishing":{ - get: function() { return client.disconnectedPublishing; }, - set: function(newDisconnectedPublishing) { - client.disconnectedPublishing = newDisconnectedPublishing; - } - }, - "disconnectedBufferSize":{ - get: function() { return client.disconnectedBufferSize; }, - set: function(newDisconnectedBufferSize) { - client.disconnectedBufferSize = newDisconnectedBufferSize; - } - }, - "onConnectionLost":{ - get: function() { return client.onConnectionLost; }, - set: function(newOnConnectionLost) { - if (typeof newOnConnectionLost === "function") - client.onConnectionLost = newOnConnectionLost; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnConnectionLost, "onConnectionLost"])); - } - }, - "onMessageDelivered":{ - get: function() { return client.onMessageDelivered; }, - set: function(newOnMessageDelivered) { - if (typeof newOnMessageDelivered === "function") - client.onMessageDelivered = newOnMessageDelivered; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageDelivered, "onMessageDelivered"])); - } - }, - "onMessageArrived":{ - get: function() { return client.onMessageArrived; }, - set: function(newOnMessageArrived) { - if (typeof newOnMessageArrived === "function") - client.onMessageArrived = newOnMessageArrived; - else - throw new Error(format(ERROR.INVALID_TYPE, [typeof newOnMessageArrived, "onMessageArrived"])); - } - }, - "trace":{ - get: function() { return client.traceFunction; }, - set: function(trace) { - if(typeof trace === "function"){ - client.traceFunction = trace; - }else{ - throw new Error(format(ERROR.INVALID_TYPE, [typeof trace, "onTrace"])); - } - } - }, - }); - - /** - * Connect this Messaging client to its server. - * - * @name Paho.Client#connect - * @function - * @param {object} connectOptions - Attributes used with the connection. - * @param {number} connectOptions.timeout - If the connect has not succeeded within this - * number of seconds, it is deemed to have failed. - * The default is 30 seconds. - * @param {string} connectOptions.userName - Authentication username for this connection. - * @param {string} connectOptions.password - Authentication password for this connection. - * @param {Paho.Message} connectOptions.willMessage - sent by the server when the client - * disconnects abnormally. - * @param {number} connectOptions.keepAliveInterval - the server disconnects this client if - * there is no activity for this number of seconds. - * The default value of 60 seconds is assumed if not set. - * @param {boolean} connectOptions.cleanSession - if true(default) the client and server - * persistent state is deleted on successful connect. - * @param {boolean} connectOptions.useSSL - if present and true, use an SSL Websocket connection. - * @param {object} connectOptions.invocationContext - passed to the onSuccess callback or onFailure callback. - * @param {function} connectOptions.onSuccess - called when the connect acknowledgement - * has been received from the server. - * A single response object parameter is passed to the onSuccess callback containing the following fields: - *
    - *
  1. invocationContext as passed in to the onSuccess method in the connectOptions. - *
- * @param {function} connectOptions.onFailure - called when the connect request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext as passed in to the onFailure method in the connectOptions. - *
  2. errorCode a number indicating the nature of the error. - *
  3. errorMessage text describing the error. - *
- * @param {array} connectOptions.hosts - If present this contains either a set of hostnames or fully qualified - * WebSocket URIs (ws://iot.eclipse.org:80/ws), that are tried in order in place - * of the host and port paramater on the construtor. The hosts are tried one at at time in order until - * one of then succeeds. - * @param {array} connectOptions.ports - If present the set of ports matching the hosts. If hosts contains URIs, this property - * is not used. - * @param {boolean} connectOptions.reconnect - Sets whether the client will automatically attempt to reconnect - * to the server if the connection is lost. - *
    - *
  • If set to false, the client will not attempt to automatically reconnect to the server in the event that the - * connection is lost.
  • - *
  • If set to true, in the event that the connection is lost, the client will attempt to reconnect to the server. - * It will initially wait 1 second before it attempts to reconnect, for every failed reconnect attempt, the delay - * will double until it is at 2 minutes at which point the delay will stay at 2 minutes.
  • - *
- * @param {number} connectOptions.mqttVersion - The version of MQTT to use to connect to the MQTT Broker. - *
    - *
  • 3 - MQTT V3.1
  • - *
  • 4 - MQTT V3.1.1
  • - *
- * @param {boolean} connectOptions.mqttVersionExplicit - If set to true, will force the connection to use the - * selected MQTT Version or will fail to connect. - * @param {array} connectOptions.uris - If present, should contain a list of fully qualified WebSocket uris - * (e.g. ws://iot.eclipse.org:80/ws), that are tried in order in place of the host and port parameter of the construtor. - * The uris are tried one at a time in order until one of them succeeds. Do not use this in conjunction with hosts as - * the hosts array will be converted to uris and will overwrite this property. - * @throws {InvalidState} If the client is not in disconnected state. The client must have received connectionLost - * or disconnected before calling connect for a second or subsequent time. - */ - this.connect = function (connectOptions) { - connectOptions = connectOptions || {} ; - validate(connectOptions, {timeout:"number", - userName:"string", - password:"string", - willMessage:"object", - keepAliveInterval:"number", - cleanSession:"boolean", - useSSL:"boolean", - invocationContext:"object", - onSuccess:"function", - onFailure:"function", - hosts:"object", - ports:"object", - reconnect:"boolean", - mqttVersion:"number", - mqttVersionExplicit:"boolean", - uris: "object"}); - - // If no keep alive interval is set, assume 60 seconds. - if (connectOptions.keepAliveInterval === undefined) - connectOptions.keepAliveInterval = 60; - - if (connectOptions.mqttVersion > 4 || connectOptions.mqttVersion < 3) { - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.mqttVersion, "connectOptions.mqttVersion"])); - } - - if (connectOptions.mqttVersion === undefined) { - connectOptions.mqttVersionExplicit = false; - connectOptions.mqttVersion = 4; - } else { - connectOptions.mqttVersionExplicit = true; - } - - //Check that if password is set, so is username - if (connectOptions.password !== undefined && connectOptions.userName === undefined) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.password, "connectOptions.password"])); - - if (connectOptions.willMessage) { - if (!(connectOptions.willMessage instanceof Message)) - throw new Error(format(ERROR.INVALID_TYPE, [connectOptions.willMessage, "connectOptions.willMessage"])); - // The will message must have a payload that can be represented as a string. - // Cause the willMessage to throw an exception if this is not the case. - connectOptions.willMessage.stringPayload = null; - - if (typeof connectOptions.willMessage.destinationName === "undefined") - throw new Error(format(ERROR.INVALID_TYPE, [typeof connectOptions.willMessage.destinationName, "connectOptions.willMessage.destinationName"])); - } - if (typeof connectOptions.cleanSession === "undefined") - connectOptions.cleanSession = true; - if (connectOptions.hosts) { - - if (!(connectOptions.hosts instanceof Array) ) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); - if (connectOptions.hosts.length <1 ) - throw new Error(format(ERROR.INVALID_ARGUMENT, [connectOptions.hosts, "connectOptions.hosts"])); - - var usingURIs = false; - for (var i = 0; i - * @param {object} subscribeOptions - used to control the subscription - * - * @param {number} subscribeOptions.qos - the maximum qos of any publications sent - * as a result of making this subscription. - * @param {object} subscribeOptions.invocationContext - passed to the onSuccess callback - * or onFailure callback. - * @param {function} subscribeOptions.onSuccess - called when the subscribe acknowledgement - * has been received from the server. - * A single response object parameter is passed to the onSuccess callback containing the following fields: - *
    - *
  1. invocationContext if set in the subscribeOptions. - *
- * @param {function} subscribeOptions.onFailure - called when the subscribe request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext - if set in the subscribeOptions. - *
  2. errorCode - a number indicating the nature of the error. - *
  3. errorMessage - text describing the error. - *
- * @param {number} subscribeOptions.timeout - which, if present, determines the number of - * seconds after which the onFailure calback is called. - * The presence of a timeout does not prevent the onSuccess - * callback from being called when the subscribe completes. - * @throws {InvalidState} if the client is not in connected state. - */ - this.subscribe = function (filter, subscribeOptions) { - if (typeof filter !== "string" && filter.constructor !== Array) - throw new Error("Invalid argument:"+filter); - subscribeOptions = subscribeOptions || {} ; - validate(subscribeOptions, {qos:"number", - invocationContext:"object", - onSuccess:"function", - onFailure:"function", - timeout:"number" - }); - if (subscribeOptions.timeout && !subscribeOptions.onFailure) - throw new Error("subscribeOptions.timeout specified with no onFailure callback."); - if (typeof subscribeOptions.qos !== "undefined" && !(subscribeOptions.qos === 0 || subscribeOptions.qos === 1 || subscribeOptions.qos === 2 )) - throw new Error(format(ERROR.INVALID_ARGUMENT, [subscribeOptions.qos, "subscribeOptions.qos"])); - client.subscribe(filter, subscribeOptions); - }; - - /** - * Unsubscribe for messages, stop receiving messages sent to destinations described by the filter. - * - * @name Paho.Client#unsubscribe - * @function - * @param {string} filter - describing the destinations to receive messages from. - * @param {object} unsubscribeOptions - used to control the subscription - * @param {object} unsubscribeOptions.invocationContext - passed to the onSuccess callback - or onFailure callback. - * @param {function} unsubscribeOptions.onSuccess - called when the unsubscribe acknowledgement has been received from the server. - * A single response object parameter is passed to the - * onSuccess callback containing the following fields: - *
    - *
  1. invocationContext - if set in the unsubscribeOptions. - *
- * @param {function} unsubscribeOptions.onFailure called when the unsubscribe request has failed or timed out. - * A single response object parameter is passed to the onFailure callback containing the following fields: - *
    - *
  1. invocationContext - if set in the unsubscribeOptions. - *
  2. errorCode - a number indicating the nature of the error. - *
  3. errorMessage - text describing the error. - *
- * @param {number} unsubscribeOptions.timeout - which, if present, determines the number of seconds - * after which the onFailure callback is called. The presence of - * a timeout does not prevent the onSuccess callback from being - * called when the unsubscribe completes - * @throws {InvalidState} if the client is not in connected state. - */ - this.unsubscribe = function (filter, unsubscribeOptions) { - if (typeof filter !== "string" && filter.constructor !== Array) - throw new Error("Invalid argument:"+filter); - unsubscribeOptions = unsubscribeOptions || {} ; - validate(unsubscribeOptions, {invocationContext:"object", - onSuccess:"function", - onFailure:"function", - timeout:"number" - }); - if (unsubscribeOptions.timeout && !unsubscribeOptions.onFailure) - throw new Error("unsubscribeOptions.timeout specified with no onFailure callback."); - client.unsubscribe(filter, unsubscribeOptions); - }; - - /** - * Send a message to the consumers of the destination in the Message. - * - * @name Paho.Client#send - * @function - * @param {string|Paho.Message} topic - mandatory The name of the destination to which the message is to be sent. - * - If it is the only parameter, used as Paho.Message object. - * @param {String|ArrayBuffer} payload - The message data to be sent. - * @param {number} qos The Quality of Service used to deliver the message. - *
- *
0 Best effort (default). - *
1 At least once. - *
2 Exactly once. - *
- * @param {Boolean} retained If true, the message is to be retained by the server and delivered - * to both current and future subscriptions. - * If false the server only delivers the message to current subscribers, this is the default for new Messages. - * A received message has the retained boolean set to true if the message was published - * with the retained boolean set to true - * and the subscrption was made after the message has been published. - * @throws {InvalidState} if the client is not connected. - */ - this.send = function (topic,payload,qos,retained) { - var message ; - - if(arguments.length === 0){ - throw new Error("Invalid argument."+"length"); - - }else if(arguments.length == 1) { - - if (!(topic instanceof Message) && (typeof topic !== "string")) - throw new Error("Invalid argument:"+ typeof topic); - - message = topic; - if (typeof message.destinationName === "undefined") - throw new Error(format(ERROR.INVALID_ARGUMENT,[message.destinationName,"Message.destinationName"])); - client.send(message); - - }else { - //parameter checking in Message object - message = new Message(payload); - message.destinationName = topic; - if(arguments.length >= 3) - message.qos = qos; - if(arguments.length >= 4) - message.retained = retained; - client.send(message); - } - }; - - /** - * Publish a message to the consumers of the destination in the Message. - * Synonym for Paho.Mqtt.Client#send - * - * @name Paho.Client#publish - * @function - * @param {string|Paho.Message} topic - mandatory The name of the topic to which the message is to be published. - * - If it is the only parameter, used as Paho.Message object. - * @param {String|ArrayBuffer} payload - The message data to be published. - * @param {number} qos The Quality of Service used to deliver the message. - *
- *
0 Best effort (default). - *
1 At least once. - *
2 Exactly once. - *
- * @param {Boolean} retained If true, the message is to be retained by the server and delivered - * to both current and future subscriptions. - * If false the server only delivers the message to current subscribers, this is the default for new Messages. - * A received message has the retained boolean set to true if the message was published - * with the retained boolean set to true - * and the subscrption was made after the message has been published. - * @throws {InvalidState} if the client is not connected. - */ - this.publish = function(topic,payload,qos,retained) { - var message ; - - if(arguments.length === 0){ - throw new Error("Invalid argument."+"length"); - - }else if(arguments.length == 1) { - - if (!(topic instanceof Message) && (typeof topic !== "string")) - throw new Error("Invalid argument:"+ typeof topic); - - message = topic; - if (typeof message.destinationName === "undefined") - throw new Error(format(ERROR.INVALID_ARGUMENT,[message.destinationName,"Message.destinationName"])); - client.send(message); - - }else { - //parameter checking in Message object - message = new Message(payload); - message.destinationName = topic; - if(arguments.length >= 3) - message.qos = qos; - if(arguments.length >= 4) - message.retained = retained; - client.send(message); - } - }; - - /** - * Normal disconnect of this Messaging client from its server. - * - * @name Paho.Client#disconnect - * @function - * @throws {InvalidState} if the client is already disconnected. - */ - this.disconnect = function () { - client.disconnect(); - }; - - /** - * Get the contents of the trace log. - * - * @name Paho.Client#getTraceLog - * @function - * @return {Object[]} tracebuffer containing the time ordered trace records. - */ - this.getTraceLog = function () { - return client.getTraceLog(); - }; - - /** - * Start tracing. - * - * @name Paho.Client#startTrace - * @function - */ - this.startTrace = function () { - client.startTrace(); - }; - - /** - * Stop tracing. - * - * @name Paho.Client#stopTrace - * @function - */ - this.stopTrace = function () { - client.stopTrace(); - }; - - this.isConnected = function() { - return client.connected; - }; - }; - - /** - * An application message, sent or received. - *

- * All attributes may be null, which implies the default values. - * - * @name Paho.Message - * @constructor - * @param {String|ArrayBuffer} payload The message data to be sent. - *

- * @property {string} payloadString read only The payload as a string if the payload consists of valid UTF-8 characters. - * @property {ArrayBuffer} payloadBytes read only The payload as an ArrayBuffer. - *

- * @property {string} destinationName mandatory The name of the destination to which the message is to be sent - * (for messages about to be sent) or the name of the destination from which the message has been received. - * (for messages received by the onMessage function). - *

- * @property {number} qos The Quality of Service used to deliver the message. - *

- *
0 Best effort (default). - *
1 At least once. - *
2 Exactly once. - *
- *

- * @property {Boolean} retained If true, the message is to be retained by the server and delivered - * to both current and future subscriptions. - * If false the server only delivers the message to current subscribers, this is the default for new Messages. - * A received message has the retained boolean set to true if the message was published - * with the retained boolean set to true - * and the subscrption was made after the message has been published. - *

- * @property {Boolean} duplicate read only If true, this message might be a duplicate of one which has already been received. - * This is only set on messages received from the server. - * - */ - var Message = function (newPayload) { - var payload; - if ( typeof newPayload === "string" || - newPayload instanceof ArrayBuffer || - (ArrayBuffer.isView(newPayload) && !(newPayload instanceof DataView)) - ) { - payload = newPayload; - } else { - throw (format(ERROR.INVALID_ARGUMENT, [newPayload, "newPayload"])); - } - - var destinationName; - var qos = 0; - var retained = false; - var duplicate = false; - - Object.defineProperties(this,{ - "payloadString":{ - enumerable : true, - get : function () { - if (typeof payload === "string") - return payload; - else - return parseUTF8(payload, 0, payload.length); - } - }, - "payloadBytes":{ - enumerable: true, - get: function() { - if (typeof payload === "string") { - var buffer = new ArrayBuffer(UTF8Length(payload)); - var byteStream = new Uint8Array(buffer); - stringToUTF8(payload, byteStream, 0); - - return byteStream; - } else { - return payload; - } - } - }, - "destinationName":{ - enumerable: true, - get: function() { return destinationName; }, - set: function(newDestinationName) { - if (typeof newDestinationName === "string") - destinationName = newDestinationName; - else - throw new Error(format(ERROR.INVALID_ARGUMENT, [newDestinationName, "newDestinationName"])); - } - }, - "qos":{ - enumerable: true, - get: function() { return qos; }, - set: function(newQos) { - if (newQos === 0 || newQos === 1 || newQos === 2 ) - qos = newQos; - else - throw new Error("Invalid argument:"+newQos); - } - }, - "retained":{ - enumerable: true, - get: function() { return retained; }, - set: function(newRetained) { - if (typeof newRetained === "boolean") - retained = newRetained; - else - throw new Error(format(ERROR.INVALID_ARGUMENT, [newRetained, "newRetained"])); - } - }, - "topic":{ - enumerable: true, - get: function() { return destinationName; }, - set: function(newTopic) {destinationName=newTopic;} - }, - "duplicate":{ - enumerable: true, - get: function() { return duplicate; }, - set: function(newDuplicate) {duplicate=newDuplicate;} - } - }); - }; - - // Module contents. - return { - Client: Client, - Message: Message - }; - // eslint-disable-next-line no-nested-ternary - })(typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}); - return PahoMQTT; -}); \ No newline at end of file diff --git a/src/FLAD/lib/storage.js b/src/FLAD/lib/storage.js deleted file mode 100644 index 809ba35..0000000 --- a/src/FLAD/lib/storage.js +++ /dev/null @@ -1,10 +0,0 @@ -const storage = { - setItem: (key, item) => { - storage[key] = item; - }, - getItem: key => storage[key], - removeItem: key => { - delete storage[key]; - }, - }; - export default storage; \ No newline at end of file diff --git a/src/FLAD/model/Music.ts b/src/FLAD/model/Music.ts deleted file mode 100644 index 639257f..0000000 --- a/src/FLAD/model/Music.ts +++ /dev/null @@ -1,55 +0,0 @@ -export default class Music{ - private _id: string; - private _title: string; - private _bio: string; - private _image: string; - private _trackPreviewUrl: string; - - constructor(id: string, title: string, bio: string, image: string, trackPreviewUrl: string) { - this._title = title; - this._bio = bio; - this._image = image; - this._id = id; - this._trackPreviewUrl = trackPreviewUrl; - } - - get title(): string { - return this._title; - } - - set title(value: string) { - this._title = value; - } - - get bio(): string { - return this._bio; - } - - set bio(value: string) { - this._bio = value; - } - - get image(): string { - return this._image; - } - - set image(value: string) { - this._image = value; - } - - get id(): string { - return this._id; - } - - set id(value: string) { - this._id = value; - } - - get trackPreviewUrl(): string { - return this._trackPreviewUrl; - } - - set trackPreviewUrl(value: string) { - this._trackPreviewUrl = value; - } -} diff --git a/src/FLAD/models/Artist.ts b/src/FLAD/models/Artist.ts new file mode 100644 index 0000000..991c22d --- /dev/null +++ b/src/FLAD/models/Artist.ts @@ -0,0 +1,35 @@ +export default class Artist { + private _id: string; + private _name: string; + private _url: string; + + constructor(id: string, name: string, url: string) { + this._id = id; + this._name = name; + this._url = url; + } + + get id(): string { + return this._id; + } + + set id(value: string) { + this._id = value; + } + + get name(): string { + return this._name; + } + + set name(value: string) { + this._name = value; + } + + get url(): string { + return this._url; + } + + set url(value: string) { + this._url = value; + } +} \ No newline at end of file diff --git a/src/FLAD/models/Conversation.ts b/src/FLAD/models/Conversation.ts new file mode 100644 index 0000000..a972af9 --- /dev/null +++ b/src/FLAD/models/Conversation.ts @@ -0,0 +1,47 @@ +import Message from "./Message"; + +export default class Conversation { + private _id: string; + private _name: string; + private _image: string; + private _lastMessage: Message; + + constructor(id: string, name: string, image: string, lastMessage: Message) { + this._id = id; + this._name = name; + this._image = image; + this._lastMessage = lastMessage; + } + + get id(): string { + return this._id; + } + + set id(value: string) { + this._id = value; + } + + get name(): string { + return this._name; + } + + set name(value: string) { + this._name = value; + } + + get image(): string { + return this._image; + } + + set image(value: string) { + this._image = value; + } + + get lastMessage(): Message { + return this._lastMessage; + } + + set lastMessage(value: Message) { + this._lastMessage = value; + } +} \ No newline at end of file diff --git a/src/FLAD/models/Message.ts b/src/FLAD/models/Message.ts new file mode 100644 index 0000000..4e8b6e5 --- /dev/null +++ b/src/FLAD/models/Message.ts @@ -0,0 +1,55 @@ +export default class Message { + private _id: string; + private _content: string; + private _sender: string; + private _date: Date; + private _audio: string; + + constructor(id: string, content: string, sender: string, date: Date, audio: string = '') { + this._id = id; + this._content = content; + this._sender = sender; + this._date = date; + this._audio = audio; + } + + get id(): string { + return this._id; + } + + set id(value: string) { + this._id = value; + } + + get content(): string { + return this._content; + } + + set content(value: string) { + this._content = value; + } + + get sender(): string { + return this._sender; + } + + set sender(value: string) { + this._sender = value; + } + + get date(): Date { + return this._date; + } + + set date(value: Date) { + this._date = value; + } + + get audio(): string { + return this._audio; + } + + set audio(value: string) { + this._audio = value; + } +} \ No newline at end of file diff --git a/src/FLAD/models/Music.ts b/src/FLAD/models/Music.ts new file mode 100644 index 0000000..83df62a --- /dev/null +++ b/src/FLAD/models/Music.ts @@ -0,0 +1,119 @@ +import Artist from "./Artist"; + +export default class Music { + private _id: string; + private _name: string; + private _url: string; + private _artists: Artist[]; + private _cover: string; + private _littleCover: string; + private _date: number; + private _duration: number; + private _explicit: boolean = false; + private _trackPreviewUrl: string; + + constructor( + id: string, + name: string, + url: string, + artists: Artist[], + cover: string, + littleCover: string, + date: number, + duration: number, + explicit: boolean, + trackPreviewUrl: string + ) { + this._id = id; + this._name = name; + this._url = url; + this._artists = artists; + this._cover = cover; + this._littleCover = littleCover; + this._date = date; + this._duration = duration; + this._explicit = explicit; + this._trackPreviewUrl = trackPreviewUrl; + } + + get id(): string { + return this._id; + } + + set id(value: string) { + this._id = value; + } + + get name(): string { + return this._name; + } + + set name(value: string) { + this._name = value; + } + + get url(): string { + return this._url; + } + + set url(value: string) { + this._url = value; + } + + get artists(): Artist[] { + return this._artists; + } + + set artists(value: Artist[]) { + this._artists = value; + } + + get cover(): string { + return this._cover; + } + + set cover(value: string) { + this._cover = value; + } + + get littleCover(): string { + return this._littleCover; + } + + set littleCover(value: string) { + this._littleCover = value; + } + + get date(): number { + return this._date; + } + + set date(value: number) { + this._date = value; + } + + get duration(): number { + return this._duration; + } + + set duration(value: number) { + this._duration = value; + } + + get explicit(): boolean { + return this._explicit; + } + + set explicit(value: boolean) { + this._explicit = value; + } + + get trackPreviewUrl(): string { + return this._trackPreviewUrl; + } + + set trackPreviewUrl(value: string) { + this._trackPreviewUrl = value; + } +} + diff --git a/src/FLAD/models/MusicServiceProvider.ts b/src/FLAD/models/MusicServiceProvider.ts new file mode 100644 index 0000000..20ca4a3 --- /dev/null +++ b/src/FLAD/models/MusicServiceProvider.ts @@ -0,0 +1,15 @@ +import EmptyMusicService from "../services/musics/EmptyMusicService"; +import IMusicService from "../services/musics/interfaces/IMusicService"; +import SpotifyService from "../services/musics/spotify/SpotifyService"; + +export class MusicServiceProvider { + static musicService: IMusicService; + + static initSpotify(refreshToken: string, idSpotify: string) { + this.musicService = new SpotifyService(refreshToken, idSpotify); + } + + static resetService() { + this.musicService = new EmptyMusicService(); + } +} \ No newline at end of file diff --git a/src/FLAD/models/Person.ts b/src/FLAD/models/Person.ts new file mode 100644 index 0000000..aa22acb --- /dev/null +++ b/src/FLAD/models/Person.ts @@ -0,0 +1,27 @@ +export class Person { + private _id: string; + private _name: string; + public image: string; + + constructor(id: string, name: string, image: string) { + this._id = id; + this._name = name; + this.image = image; + } + + get id(): string { + return this._id; + } + + set id(value: string) { + this._id = value; + } + + get name(): string { + return this._name; + } + + set name(value: string) { + this._name = value; + } +} \ No newline at end of file diff --git a/src/FLAD/models/Spot.ts b/src/FLAD/models/Spot.ts new file mode 100644 index 0000000..c531422 --- /dev/null +++ b/src/FLAD/models/Spot.ts @@ -0,0 +1,50 @@ +import Music from "./Music"; +import { Person } from "./Person"; + +export class Spot { + private _id: string; + private _user: Person; + private _music: Music; + public distance: string; + private _date: Date; + + constructor(id: string, user: Person, music: Music, distance: string, date: Date) { + this._id = id; + this._user = user; + this._music = music; + this.distance = distance; + this._date = date; + } + + get id(): string { + return this._id; + } + + set id(value: string) { + this._id = value; + } + + get user(): Person { + return this._user; + } + + set user(value: Person) { + this._user = value; + } + + get music(): Music { + return this._music; + } + + set music(value: Music) { + this._music = value; + } + + get date(): Date { + return this._date; + } + + set date(value: Date) { + this._date = value; + } +} \ No newline at end of file diff --git a/src/FLAD/models/User.ts b/src/FLAD/models/User.ts new file mode 100644 index 0000000..57bac03 --- /dev/null +++ b/src/FLAD/models/User.ts @@ -0,0 +1,33 @@ +export class User { + private _id: string; + private _idSpotify: string; + private _name: string; + private _email: string; + private _creationDate: Date; + public image: string; + + constructor(id: string, idSpotify: string, name: string, email: string, creationDate: Date, image: string) { + this._id = id; + this._idSpotify = idSpotify; + this._name = name; + this._email = email; + this._creationDate = creationDate; + this.image = image; + } + + get id(): string { + return this._id; + } + get idSpotify(): string { + return this._idSpotify; + } + get name(): string { + return this._name; + } + get email(): string { + return this._email; + } + get creationDate(): Date { + return this._creationDate; + } +} \ No newline at end of file diff --git a/src/FLAD/models/mapper/ArtistMapper.ts b/src/FLAD/models/mapper/ArtistMapper.ts new file mode 100644 index 0000000..d644463 --- /dev/null +++ b/src/FLAD/models/mapper/ArtistMapper.ts @@ -0,0 +1,7 @@ +import Artist from "../Artist"; + +export default class ArtistMapper { + static toModel(artist: any): Artist { + return new Artist(artist.id, artist.name, artist.external_urls.spotify); + } +} \ No newline at end of file diff --git a/src/FLAD/models/mapper/MusicMapper.ts b/src/FLAD/models/mapper/MusicMapper.ts new file mode 100644 index 0000000..c8f0e01 --- /dev/null +++ b/src/FLAD/models/mapper/MusicMapper.ts @@ -0,0 +1,21 @@ +import Music from "../Music"; +import ArtistMapper from "./ArtistMapper"; + +export default class MusicMapper { + static toModel(music: any): Music { + const artists = music.artists.map((artist: any) => ArtistMapper.toModel(artist)); + const last = music.album.images.length - 1; + return new Music( + music.id, + music.name, + music.external_urls.spotify, + artists, + music.album.images[0].url, + music.album.images[last].url, + music.album.release_date.split('-')[0], + music.duration_ms / 1000, + music.explicit, + music.preview_url + ); + } +} \ No newline at end of file diff --git a/src/FLAD/models/mapper/PersonMapper.ts b/src/FLAD/models/mapper/PersonMapper.ts new file mode 100644 index 0000000..bb857e6 --- /dev/null +++ b/src/FLAD/models/mapper/PersonMapper.ts @@ -0,0 +1,7 @@ +import { Person } from "../Person"; + +export class PersonMapper { + public static toModel(person: any): Person { + return new Person(person._id, person.name, person.image); + } +} \ No newline at end of file diff --git a/src/FLAD/models/mapper/SpotMapper.ts b/src/FLAD/models/mapper/SpotMapper.ts new file mode 100644 index 0000000..267501d --- /dev/null +++ b/src/FLAD/models/mapper/SpotMapper.ts @@ -0,0 +1,7 @@ +import { Spot } from "../Spot"; + +export class SpotMapper { + public static toModel(spot: any): Spot { + return new Spot(spot._id, spot.user, spot.music, spot.distance, new Date(spot.date)); + } +} \ No newline at end of file diff --git a/src/FLAD/models/mapper/UserMapper.ts b/src/FLAD/models/mapper/UserMapper.ts new file mode 100644 index 0000000..0bfb37d --- /dev/null +++ b/src/FLAD/models/mapper/UserMapper.ts @@ -0,0 +1,7 @@ +import { User } from "../User"; + +export class UserMapper { + public static toModel(user: any): User { + return new User(user._id, user.idSpotify, user.name, user.email, new Date(user.createdAt), user.image); + } +} \ No newline at end of file diff --git a/src/FLAD/navigation/AuthNavigation.tsx b/src/FLAD/navigation/AuthNavigation.tsx index 479e0a9..417160e 100644 --- a/src/FLAD/navigation/AuthNavigation.tsx +++ b/src/FLAD/navigation/AuthNavigation.tsx @@ -1,57 +1,57 @@ -import Navigation from './Navigation'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { SafeAreaProvider } from 'react-native-safe-area-context'; -import StartNavigation from './StartNavigation'; import { useDispatch, useSelector } from 'react-redux'; import { useEffect, useState } from 'react'; import * as SplashScreen from 'expo-splash-screen'; -import { ChangeMode, getRefreshToken } from '../redux/thunk/authThunk'; +import { getRefreshToken } from '../redux/thunk/authThunk'; +import { darkMode } from '../redux/thunk/userThunk'; +import HomeNavigation from './HomeNavigation'; +import StartNavigation from './StartNavigation'; export default function AuthNavigation() { //@ts-ignore - const tokenProcesed: boolean = useSelector(state => state.userReducer.loading); + const tokenProcessed: boolean = useSelector(state => state.userReducer.loading); //@ts-ignore - const isLogin: boolean = useSelector(state => state.userReducer.isLogedIn); + const isLogin: boolean = useSelector(state => state.userReducer.isLogedIn); const [appIsReady, setAppIsReady] = useState(false); const dispatch = useDispatch(); - async function prepare() { - //@ts-ignore - await dispatch(getRefreshToken()) - if (tokenProcesed && appIsReady) { + + async function check() { + if (tokenProcessed && appIsReady) { await SplashScreen.hideAsync(); } } - async function ChangeDarkMode() { - try { - const currentValue = await AsyncStorage.getItem('dark'); - if (currentValue !== null) { - const newValue = JSON.stringify(JSON.parse(currentValue)); - //@ts-ignore - dispatch(ChangeMode(JSON.parse(newValue))) - } - } catch (error) { - console.log(`Une erreur s'est produite lors de la mise à jour de la valeur booléenne pour la clé 'dark': `, error); + async function prepare() { + //@ts-ignore + dispatch(getRefreshToken()) + } + + async function initDarkMode() { + const currentValue: string | null = await AsyncStorage.getItem('dark'); + if (currentValue) { + const newValue = JSON.stringify(JSON.parse(currentValue)); + //@ts-ignore + dispatch(darkMode(JSON.parse(newValue))) } } - useEffect(() => { - ChangeDarkMode(); - prepare(); - }, [appIsReady, tokenProcesed]); + useEffect(() => { + check(); + }, [appIsReady, tokenProcessed]); - - if (tokenProcesed == false) { - return null; - } + useEffect(() => { + prepare(); + initDarkMode(); + }, []); return ( - + setAppIsReady(true)}> {isLogin ? ( - + ) : - + } ) diff --git a/src/FLAD/navigation/FavoriteNavigation.tsx b/src/FLAD/navigation/FavoriteNavigation.tsx index 29a671b..d723d7a 100644 --- a/src/FLAD/navigation/FavoriteNavigation.tsx +++ b/src/FLAD/navigation/FavoriteNavigation.tsx @@ -1,23 +1,22 @@ import React from 'react'; -import Favorite from '../screens/Favorite'; -import MusicDetail from '../screens/MusicDetail'; +import Favorite from '../screens/FavoriteScreen'; +import DetailScreen from '../screens/DetailScreen'; import { createSharedElementStackNavigator } from 'react-navigation-shared-element'; -import CurrentMusic from '../components/CurrentMusic'; -const Stack = createSharedElementStackNavigator(); +const FavoriteStack = createSharedElementStackNavigator(); export default function MusicNavigation() { return ( - - + - { return [route.params.music.id] }} + - + ) } \ No newline at end of file diff --git a/src/FLAD/navigation/HomeNavigation.tsx b/src/FLAD/navigation/HomeNavigation.tsx new file mode 100644 index 0000000..7765a1d --- /dev/null +++ b/src/FLAD/navigation/HomeNavigation.tsx @@ -0,0 +1,184 @@ +import React, { useEffect, useState } from 'react'; +import { View, Alert, Platform } from 'react-native'; +import { faUser, faEnvelope, faHeart, faMusic } from "@fortawesome/free-solid-svg-icons" +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import { NavigationContainer, getFocusedRouteNameFromRoute } from '@react-navigation/native'; +import FavoriteNavigation from './FavoriteNavigation'; +import SettingNavigation from './SettingNavigation'; +import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome"; +import SpotNavigation from './SpotNavigation'; +import MessagingNavigation from './MessagingNavigation'; +import { useDispatch, useSelector } from 'react-redux'; +import { getUserCurrentMusic } from '../redux/thunk/appThunk'; +import { logout } from '../redux/thunk/authThunk'; +import { setAccessError, setErrorEmptyMusic } from '../redux/actions/userActions'; +import * as Location from 'expo-location'; +import { getSpotList } from '../redux/thunk/spotThunk'; +import Music from '../models/Music'; +import normalize from '../components/Normalize'; +import { BlurView } from 'expo-blur'; + +export default function HomeNavigation() { + //@ts-ignore + const favoritesMusicLength = useSelector(state => state.appReducer.nbAddedFavoriteMusic); + //@ts-ignore + const accessError = useSelector(state => state.userReducer.accessError); + //@ts-ignore + const errorEmptyMusic = useSelector(state => state.userReducer.errorEmptyMusic); + // @ts-ignore + const isDark = useSelector(state => state.userReducer.dark); + // @ts-ignore + const currentMusic: Music = useSelector(state => state.appReducer.userCurrentMusic); + const [locationPermission, setLocationPermission] = useState(false); + + const BottomTabNavigator = createBottomTabNavigator(); + + const dispatch = useDispatch(); + + const requestLocationPermission = async () => { + const { status } = await Location.requestForegroundPermissionsAsync(); + if (status !== 'granted') { + alert( + "Oups ! Il semble que l'accès à votre localisation soit désactivé. Pour découvrir la musique des personnes autour de vous, veuillez autoriser l'accès à la localisation dans les paramètres de votre appareil." + ); + } else { + setLocationPermission(true); + } + } + + useEffect(() => { + requestLocationPermission(); + }, []); + + useEffect(() => { + const getSpots = async () => { + //@ts-ignore + dispatch(getUserCurrentMusic()); + } + + getSpots(); + + const interval = setInterval(getSpots, 30000); + return () => { + clearInterval(interval); + }; + }, []); + + useEffect(() => { + getSpots(); + }, [currentMusic]); + + const getSpots = async () => { + if (currentMusic && locationPermission) { + const location = await Location.getCurrentPositionAsync({ + accuracy: Location.Accuracy.Low, + }); + //@ts-ignore + dispatch(getSpotList(location.coords.longitude, location.coords.latitude, currentMusic._id)); + } + } + + useEffect(() => { + if (accessError) { + Alert.alert( + "Problème lié à votre compte", + "Votre compte ne fait plus partie des utilisateurs ayant accès à l'application. Pour plus d'informations, veuillez contacter l'équipe de support à l'adresse suivante : fladdevpro@gmail.com.", + [ + { + text: 'Réessayer plus tard', + onPress: () => { + dispatch(setAccessError(false)) + //@ts-ignore + dispatch(logout()); + }, + }, + ], + { cancelable: false } + ); + } + }, [accessError]); + + useEffect(() => { + if (errorEmptyMusic) { + Alert.alert( + "Bienvenue sur FLAD 🎵", + "Votre compte Spotify semble tout neuf, donc pour le moment, vous ne pouvez pas encore partager de musique.\n\n" + + "Pas encore de playlist secrète ? Aucun morceau honteux ? Nous attendons impatiemment vos découvertes musicales !", + [ + { + text: "D'accord", + onPress: () => dispatch(setErrorEmptyMusic(false)), + } + ] + ); + } + }, [errorEmptyMusic]); + + const MenuBlur = () => { + return ( + + ); + }; + + return ( + + : undefined, + tabBarActiveTintColor: isDark ? "white" : "rgb(255, 45, 85)", + tabBarStyle: { + position: 'absolute', + borderTopColor: isDark ? 'rgba(255, 255, 255, 0.25)' : 'rgba(50, 50, 50, 0.07)', + }, + }}> + , + }} /> + , + }} /> + ({ + headerShown: false, + tabBarStyle: { + display: getFocusedRouteNameFromRoute(route) !== "Chat" ? "flex" : "none", + position: "absolute", + borderTopColor: isDark ? 'rgba(255, 255, 255, 0.25)' : 'rgba(50, 50, 50, 0.07)', + }, + tabBarIcon: ({ color }) => , + })} /> + , + }} /> + + + ) +} + +function TabBarIcon(props: { + name: any; + color: string; + size: number; +}) { + return ; +} \ No newline at end of file diff --git a/src/FLAD/navigation/MessagingNavigation.tsx b/src/FLAD/navigation/MessagingNavigation.tsx index 09ac13d..d204415 100644 --- a/src/FLAD/navigation/MessagingNavigation.tsx +++ b/src/FLAD/navigation/MessagingNavigation.tsx @@ -1,24 +1,69 @@ import React from 'react'; +import ConversationScreen from '../screens/ConversationScreen' +import { Image, View, Text, StyleSheet } from 'react-native'; +import ChatScreen from '../screens/ChatScreen'; import { createStackNavigator } from '@react-navigation/stack'; -import Conversation from '../screens/Conversation' -import Chat from '../screens/Chat'; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; +import { useSelector } from 'react-redux'; export default function MessagingNavigation() { - const Stack = createStackNavigator(); + // @ts-ignore + const isDark = useSelector(state => state.userReducer.dark); + const style: Theme = isDark ? DarkTheme : LightTheme; + const MessagingStack = createStackNavigator(); + + const styles = StyleSheet.create({ + headerContainer: { + flexDirection: 'row', + alignItems: 'center' + }, + headerImage: { + width: 30, + height: 30, + borderRadius: 20, + marginRight: 8 + }, + headerText: { + color: style.Text, + fontSize: 16, + fontWeight: 'bold' + } + }); return ( - - + - ({ + headerShown: true, + headerBackTitleVisible: false, + headerStyle: { + backgroundColor: style.Card + }, + headerTitle: () => ( + + + + {/* @ts-ignore */} + {route.params.username} + + + ), + headerTitleStyle: { + color: style.Text + }, + })} /> - + ) } \ No newline at end of file diff --git a/src/FLAD/navigation/Navigation.tsx b/src/FLAD/navigation/Navigation.tsx deleted file mode 100644 index 6eab3b0..0000000 --- a/src/FLAD/navigation/Navigation.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { View, StyleSheet, Platform } from 'react-native'; -import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; -import { NavigationContainer } from '@react-navigation/native'; -import FavoriteNavigation from './FavoriteNavigation'; -import SettingNavigation from './SettingNavigation'; - -import normalize from '../components/Normalize'; -// @ts-ignore -import FontAwesome from 'react-native-vector-icons/FontAwesome'; -import SpotNavigation from './SpotNavigation'; -import MessagingNavigation from './MessagingNavigation'; -import { useDispatch, useSelector } from 'react-redux'; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; -import { getCurrentUserMusic, getSpotList } from '../redux/thunk/spotThunk'; -import SpotifyService from '../services/spotify/spotify.service'; -import * as SecureStore from 'expo-secure-store'; -import { MY_SECURE_AUTH_STATE_KEY } from '../screens/Register'; -import * as Location from 'expo-location'; -import axios from 'axios'; -import qs from 'qs'; - -export default function Navigation() { - const [setErrorMsg] = useState(''); - const [location, setLocation] = useState(); -//@ts-ignore -const tokenSend: string = useSelector(state => state.userReducer.userFladToken); - //@ts-ignore - const currentMusic: Music = useSelector(state => state.appReducer.userCurrentMusic); - - const dispatch = useDispatch(); - - const requestLocationPermission = async () => { - const { status } = await Location.requestForegroundPermissionsAsync(); - if (status !== 'granted') { - console.log('Permission to access location was denied'); - } else { - console.log('Permission to access location was granted'); - } - } - - useEffect(() => { - requestLocationPermission(); - const sendLocationUpdate = async () => { - try { - - let tmpKey: string = await SecureStore.getItemAsync(MY_SECURE_AUTH_STATE_KEY) ; - //@ts-ignore - dispatch(getCurrentUserMusic(new SpotifyService(tmpKey))) - let { status } = await Location.requestForegroundPermissionsAsync(); - if (status == 'granted') { - // should app is ready - const locationresp = await Location.getCurrentPositionAsync({}); - // send location to server - if(currentMusic){ - const body: Record = { - longitude: locationresp.coords.longitude, - latitude: locationresp.coords.latitude, - currentMusic: currentMusic.id - } - const resp = await axios({ - url: 'https://flad-api-production.up.railway.app/api/users/nextTo?' + qs.stringify(body), - method: 'GET', - headers: { - Authorization: `Bearer ${tokenSend}`, - }, - }); - const datat: Record = resp.data.listUser2; - //@ts-ignore - dispatch(getSpotList(datat, new SpotifyService(tmpKey))) - } - else{ - return; - } - - - } - else { - //@ts-ignore - let {status} = Location.requestForegroundPermissionsAsync(); - if (status !== 'granted') { - setErrorMsg('Permission to access location was denied'); - return; - } - return; - - } - } catch (error) { - console.log(error); - } - }; - const interval = setInterval(sendLocationUpdate, 30000); - return () => { - clearInterval(interval); - }; - }, [currentMusic]); - - // @ts-ignore - const isDark = useSelector(state => state.userReducer.dark); - const style = isDark ? GraphicalCharterDark : GraphicalCharterLight; - const BottomTabNavigator = createBottomTabNavigator(); - const MyTheme = { - dark: false, - colors: { - primary: 'rgb(255, 45, 85)', - card: style.Card, - border: style.Card, - text: 'rgb(138, 138, 138)', - } - }; - //@ts-ignore - const favoritesMusicLength: number = useSelector(state => state.appReducer.favoriteMusic.length); - return ( - // @ts-ignore - - - , - }} /> - , - }} /> - , - }} /> - , - }} /> - - - ) -} - -function TabBarIcon(props: { - name: React.ComponentProps['name']; - color: string; -}) { - return ; -} - -const styles = StyleSheet.create({ - tabBar: { - height: 60, - position: 'absolute', - bottom: normalize(50), - borderRadius: 30, - marginHorizontal: 25 - }, - IconContainer: { - position: 'absolute', - top: 5, - } -}) diff --git a/src/FLAD/navigation/SettingNavigation.tsx b/src/FLAD/navigation/SettingNavigation.tsx index e7a27c8..9c8dfd9 100644 --- a/src/FLAD/navigation/SettingNavigation.tsx +++ b/src/FLAD/navigation/SettingNavigation.tsx @@ -1,22 +1,22 @@ import React from 'react'; -import Setting from '../screens/Setting'; -import SettingProfil from '../screens/SettingProfil'; +import SettingScreen from '../screens/SettingScreen'; +import ProfilScreen from '../screens/ProfilScreen'; import { createStackNavigator } from '@react-navigation/stack'; export default function SettingNavigation() { - const Stack = createStackNavigator(); + const SettingStack = createStackNavigator(); return ( - - + - - + ) } \ No newline at end of file diff --git a/src/FLAD/navigation/SpotNavigation.tsx b/src/FLAD/navigation/SpotNavigation.tsx index af8a5ac..d8c6694 100644 --- a/src/FLAD/navigation/SpotNavigation.tsx +++ b/src/FLAD/navigation/SpotNavigation.tsx @@ -1,28 +1,22 @@ import React from 'react'; +import SpotScreen from '../screens/SpotScreen' +import DetailScreen from '../screens/DetailScreen'; import { createStackNavigator } from '@react-navigation/stack'; -import SpotPage from '../screens/spot' -import MusicDetail from '../screens/MusicDetail'; - export default function SpotNavigation() { - const Stack = createStackNavigator(); - + const SpotStack = createStackNavigator(); return ( - - + - - + ) } \ No newline at end of file diff --git a/src/FLAD/navigation/StartNavigation.tsx b/src/FLAD/navigation/StartNavigation.tsx index c0c755a..aeed323 100644 --- a/src/FLAD/navigation/StartNavigation.tsx +++ b/src/FLAD/navigation/StartNavigation.tsx @@ -1,31 +1,31 @@ import React from 'react'; -import Login from '../screens/LoginPage'; -import Register from '../screens/Register'; -import Onboarding from '../components/Onboarding'; +import LoginScreen from '../screens/LoginScreen'; +import RegisterScreen from '../screens/RegisterScreen'; +import StartScreen from '../screens/StartScreen'; import { createStackNavigator } from '@react-navigation/stack'; import { NavigationContainer } from '@react-navigation/native'; export default function StartNavigation() { - const Stack = createStackNavigator(); + const StartStack = createStackNavigator(); return ( - - + - - - + ) diff --git a/src/FLAD/package.json b/src/FLAD/package.json index 0cc7b14..0460782 100644 --- a/src/FLAD/package.json +++ b/src/FLAD/package.json @@ -1,61 +1,54 @@ { "name": "flad", "version": "1.0.0", - "main": "node_modules/expo/AppEntry.js", "scripts": { - "start": "expo start", + "start": "expo start --dev-client", "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web" }, "dependencies": { + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/react-native-fontawesome": "^0.3.0", "@react-native-async-storage/async-storage": "~1.17.3", "@react-navigation/bottom-tabs": "^6.5.4", "@react-navigation/native": "^6.1.4", - "@react-navigation/native-stack": "^6.9.8", "@react-navigation/stack": "^6.3.12", "@reduxjs/toolkit": "^1.9.2", "@types/react-redux": "^7.1.25", - "axios": "^1.2.6", - "buffer": "^6.0.3", + "axios": "^1.6.0", "expo": "~47.0.12", - "expo-asset": "~8.7.0", "expo-auth-session": "~3.8.0", "expo-av": "~13.0.3", "expo-blur": "~12.0.1", - "expo-cli": "^6.3.0", "expo-haptics": "~12.0.1", "expo-image-picker": "~14.0.2", "expo-linear-gradient": "~12.0.1", "expo-location": "~15.0.1", - "expo-random": "~13.0.0", + "expo-random": "^13.0.0", "expo-secure-store": "~12.0.0", "expo-splash-screen": "~0.17.5", - "expo-status-bar": "~1.4.2", - "expo-web-browser": "~12.0.0", - "lottie-react-native": "5.1.4", "react": "18.1.0", "react-dom": "18.1.0", - "react-native": "0.70.5", + "react-native": "0.70.8", "react-native-gesture-handler": "~2.8.0", "react-native-gifted-chat": "^2.0.1", "react-native-modal": "^13.0.1", "react-native-reanimated": "~2.12.0", "react-native-safe-area-context": "4.4.1", "react-native-screens": "~3.18.0", - "react-native-shared-element": "0.8.4", "react-native-svg": "13.4.0", - "react-native-vector-icons": "^9.2.0", + "react-native-vector-icons": "^10.0.3", "react-native-web": "~0.18.9", "react-navigation-shared-element": "^3.1.3", "react-redux": "^8.0.5", - "redux": "^4.2.1", - "expo-linking": "~3.3.1" + "redux": "^4.2.1" }, "devDependencies": { "@babel/core": "^7.12.9", "@types/react": "~18.0.14", - "@types/react-native": "~0.70.6", + "@types/react-native": "~0.70.8", + "@types/react-native-vector-icons": "^6.4.18", "typescript": "^4.6.3" }, "private": true diff --git a/src/FLAD/redux/actions/appActions.ts b/src/FLAD/redux/actions/appActions.ts index 40e617b..241d176 100644 --- a/src/FLAD/redux/actions/appActions.ts +++ b/src/FLAD/redux/actions/appActions.ts @@ -1,15 +1,48 @@ -import Music from "../../Model/Music"; +import Conversation from "../../models/Conversation"; +import Message from "../../models/Message"; +import Music from "../../models/Music"; +import { Spot } from "../../models/Spot"; +import { chatTypes } from "../types/chatTypes"; import { favoritesTypes } from "../types/favoritesTypes"; +import { spotifyTypes } from "../types/spotifyTypes"; -export const getFavoritesMusic = (music: Music[]) => { +export const setUserCurrentMusic = (music: Music | null) => { return { - type: favoritesTypes.GET_FAVORITE_MUSICS, + type: spotifyTypes.GET_USER_CURRENT_MUSIC, payload: music, }; } -export const addFavoritesMusic = (music: Music) => { + +export const setFavoriteMusic = (spots: Spot[]) => { + return { + type: favoritesTypes.GET_FAVORITE_MUSICS, + payload: spots, + }; +} + +export const addFavoriteMusic = (spot: Spot) => { return { type: favoritesTypes.ADD_FAVORITE_MUSICS, - payload: music, + payload: spot, }; } + +export const resetNbAddedFavoriteMusic = () => { + return { + type: favoritesTypes.RESET_NB_ADDED_FAVORITE_MUSIC + }; +} + +export const setConversations = (conversations: Conversation[]) => { + return { + type: chatTypes.FETCH_CONVERSATIONS, + payload: conversations, + }; +} + +export const setMessages = (messages: Message[]) => { + return { + type: chatTypes.FETCH_MESSAGES, + payload: messages, + }; +} \ No newline at end of file diff --git a/src/FLAD/redux/actions/spotActions.tsx b/src/FLAD/redux/actions/spotActions.tsx index 6276b10..9a064ca 100644 --- a/src/FLAD/redux/actions/spotActions.tsx +++ b/src/FLAD/redux/actions/spotActions.tsx @@ -1,6 +1,4 @@ -import Music from "../../Model/Music"; -import { Spot } from "../../Model/Spot"; -import { spotifyTypes } from "../types/spotifyTypes"; +import { Spot } from "../../models/Spot"; import { spotTypes } from "../types/spotTypes"; export const setSpotList = (spotList: Spot[]) => { @@ -9,23 +7,10 @@ export const setSpotList = (spotList: Spot[]) => { payload: spotList, }; } + export const removeFromSpotList = (spot: Spot) => { return { type: spotTypes.REMOVE_SPOT, payload: spot } -} - -export const addSpotListMock = (spotList: Spot[]) => { - return { - type: spotTypes.ADD_SPOT_MOCK, - payload: spotList, - }; -} - -export const setUserCurrentMusic = (currentMusic: Music) => { - return { - type: spotifyTypes.GET_USER_CURRENT_MUSIC, - payload: currentMusic, - }; } \ No newline at end of file diff --git a/src/FLAD/redux/actions/userActions.tsx b/src/FLAD/redux/actions/userActions.tsx index 605e8be..e8ec712 100644 --- a/src/FLAD/redux/actions/userActions.tsx +++ b/src/FLAD/redux/actions/userActions.tsx @@ -1,60 +1,89 @@ -import { User } from "../../Model/User"; +import { User } from "../../models/User"; import { userTypes } from "../types/userTypes"; - -export interface Credentials { +export interface LoginCredentials { email: string, password: string } -export interface CredentialsRegister { + +export interface RegisterCredentials { email: string, password: string, name: string, - idFlad: string, - idSpotify: string + tokenSpotify: string } -export const setLoginState = (userJson: any) => { - const user = new User(userJson.data.idFlad, userJson.data.idSpotify, userJson.data.email, new Date(), userJson.data.name, require('../../assets/images/jul.png')); + +export const userLogin = (user: User) => { return { type: userTypes.LOGIN, payload: user }; } -export const restoreToken = (token: string) => { +export const restoreToken = () => { return { - type: userTypes.RESTORE_TOKEN, - payload: token + type: userTypes.RESTORE_TOKEN }; } -export const userSignUp = (user: User) => { + +export const userLogout = () => { return { - type: userTypes.LOGIN, - payload: user + type: userTypes.USER_LOGOUT, }; } -export const UserLogout = () => { +export const setDarkMode = (value: boolean) => { return { - type: userTypes.USER_LOGOUT, + type: userTypes.SET_DARK_MODE, + payload: value + }; +} + +export const setErrorLogin = (value: boolean) => { + return { + type: userTypes.SET_ERROR_LOGIN, + payload: value }; } -export const userChangeMode = (value: boolean) => { +export const setErrorSignup = (value: string) => { return { - type: userTypes.CHANGE_MODE, + type: userTypes.SET_ERROR_SIGNUP, payload: value }; } -export const ChangeErrorLogin = () => { +export const setErrorNetwork = (value: boolean) => { return { - type: userTypes.CHANGE_ERROR_LOGIN, + type: userTypes.SET_ERROR_NETWORK, + payload: value }; } -export const ChangeErrorSignup = () => { +export const setErrorEmptyMusic = (value: boolean) => { return { - type: userTypes.CHANGE_ERROR_SIGNUP, + type: userTypes.SET_ERROR_EMPTY_MUSIC, + payload: value + }; +} + +export const setAccessError = (value: boolean) => { + return { + type: userTypes.SET_ERROR_ACCESS, + payload: value + }; +} + +export const setErrorUpdate = (value: boolean) => { + return { + type: userTypes.SET_ERROR_UPDATE, + payload: value + }; +} + +export const setErrorUpdateMessage = (value: string) => { + return { + type: userTypes.SET_ERROR_UPDATE_MESSAGE, + payload: value }; } \ No newline at end of file diff --git a/src/FLAD/redux/reducers/appReducer.tsx b/src/FLAD/redux/reducers/appReducer.tsx index 95db813..7fbdfb2 100644 --- a/src/FLAD/redux/reducers/appReducer.tsx +++ b/src/FLAD/redux/reducers/appReducer.tsx @@ -1,14 +1,20 @@ -import Music from "../../Model/Music"; -import { Spot } from "../../Model/Spot"; -import { discoveriesTypes } from "../types/discoverieTypes"; +import Conversation from "../../models/Conversation"; +import Message from "../../models/Message"; +import { Spot } from "../../models/Spot"; +import { chatTypes } from "../types/chatTypes"; import { favoritesTypes } from "../types/favoritesTypes"; import { spotifyTypes } from "../types/spotifyTypes"; import { spotTypes } from "../types/spotTypes"; +import { userTypes } from "../types/userTypes"; const initialState = { spot: [] as Spot[], - favoriteMusic: [] as Music[], - userCurrentMusic: null + favoriteMusic: [] as Spot[], + userCurrentMusic: null, + nbAddedFavoriteMusic: 0, + oldSpot: [] as string[], + conversations: [] as Conversation[], + messages: [] as Message[] } const appReducer = (state = initialState, action: any) => { @@ -16,21 +22,32 @@ const appReducer = (state = initialState, action: any) => { case favoritesTypes.GET_FAVORITE_MUSICS: return { ...state, favoriteMusic: action.payload }; case favoritesTypes.ADD_FAVORITE_MUSICS: - return { ...state, favoriteMusic: [action.payload, ...state.favoriteMusic] }; - case favoritesTypes.REMOVE_FAVORITE_MUSICS: - return { ...state, favoriteMusic: state.favoriteMusic }; + return { + ...state, favoriteMusic: [...state.favoriteMusic, action.payload], + nbAddedFavoriteMusic: state.nbAddedFavoriteMusic + 1 + }; + case favoritesTypes.RESET_NB_ADDED_FAVORITE_MUSIC: + return { ...state, nbAddedFavoriteMusic: 0 }; + case chatTypes.FETCH_CONVERSATIONS: + return { ...state, conversations: action.payload }; + case chatTypes.FETCH_MESSAGES: + return { ...state, messages: action.payload }; case spotTypes.FETCH_SPOT: - const uniqueSpots = action.payload.filter((spot) => { - return !state.spot.some((s) => s.userSpotifyId === spot.userSpotifyId && s.music.id === spot.music.id); + const uniqueSpots = action.payload.filter((spot: Spot) => { + const spotKey = `${spot.user}_${spot.music.id}`; + return !state.oldSpot.includes(spotKey); }); + const updatedSpotList = [...uniqueSpots, ...state.spot]; - return { ...state, spot: updatedSpotList }; + const updatedOldSpotList = [...state.oldSpot, ...uniqueSpots.map((spot: Spot) => `${spot.user}_${spot.music.id}`)]; + + return { ...state, spot: updatedSpotList, oldSpot: updatedOldSpotList }; case spotTypes.REMOVE_SPOT: - return { ...state, spot: state.spot.filter((spot) => spot.userSpotifyId !== action.payload.userSpotifyId && spot.music.id !== action.payload.music.id) }; - case discoveriesTypes.FETCH_DISCOVERIES: - return; + return { ...state, spot: state.spot.filter((spot) => spot.user !== action.payload.user && spot.music.id !== action.payload.music.id) }; case spotifyTypes.GET_USER_CURRENT_MUSIC: return { ...state, userCurrentMusic: action.payload }; + case userTypes.USER_LOGOUT: + return { ...state, spot: [], favoriteMusic: [], userCurrentMusic: null, nbAddedFavoriteMusic: 0, oldSpot: [] }; default: return state; } diff --git a/src/FLAD/redux/reducers/userReducer.tsx b/src/FLAD/redux/reducers/userReducer.tsx index f177d98..874aa54 100644 --- a/src/FLAD/redux/reducers/userReducer.tsx +++ b/src/FLAD/redux/reducers/userReducer.tsx @@ -1,68 +1,71 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; -import { User } from "../../Model/User"; +import { User } from "../../models/User"; import { userTypes } from "../types/userTypes"; const initialState = { - loading: false, user: User, - userFladToken: "", - userSpotifyToken: null, - error: null, isLogedIn: false, + loading: false, failedLogin: false, failedSignup: false, - dark: null + errorMessage: null, + errorNetwork: false, + dark: null, + errorEmptyMusic: false, + accessError: false, + errorUpdateMessage: null, + errorUpdate: false, } const userReducer = (state = initialState, action: any) => { switch (action.type) { case userTypes.RESTORE_TOKEN: - const resp = (action.payload == "" ? false : true) return { ...state, - userFladToken: action.payload, loading: true, - isLogedIn: resp, }; case userTypes.LOGIN: - AsyncStorage.setItem('dark', JSON.stringify(false)).then(() => { }); return { ...state, user: action.payload, - failedLogin: false, isLogedIn: true, - dark: false - }; - case userTypes.SIGNUP: - AsyncStorage.setItem('dark', JSON.stringify(false)).then(() => { }); - return { - ...state, - user: action.payload, + failedLogin: false, failedSignup: false, - isLogedIn: true, - dark: false + errorNetwork: false }; case userTypes.USER_LOGOUT: - AsyncStorage.removeItem('dark').then(() => { }); + AsyncStorage.removeItem('dark'); return { ...state, user: null, - isLogedIn: false + isLogedIn: false, + dark: null } case userTypes.SAVE_SPOTIFY: return { ...state, - userSpotifyToken: action.payload, + userSpotifyToken: action.payload }; - case userTypes.CHANGE_ERROR_LOGIN: - return { ...state, failedLogin: true } - case userTypes.CHANGE_ERROR_SIGNUP: - return { ...state, failedSignup: true } - case userTypes.CHANGE_MODE: + case userTypes.SET_ERROR_LOGIN: + return { ...state, failedLogin: action.payload } + case userTypes.SET_ERROR_SIGNUP: + return { ...state, failedSignup: true, errorMessage: action.payload } + case userTypes.SET_DARK_MODE: return { ...state, dark: action.payload } + case userTypes.SET_ERROR_NETWORK: + return { ...state, errorNetwork: action.payload } + case userTypes.SET_ERROR_EMPTY_MUSIC: + return { ...state, errorEmptyMusic: action.payload } + case userTypes.SET_ERROR_ACCESS: + return { ...state, accessError: action.payload } + case userTypes.SET_ERROR_UPDATE: + return { ...state, errorUpdate: action.payload } + case userTypes.SET_ERROR_UPDATE_MESSAGE: + return { ...state, errorUpdateMessage: action.payload, errorUpdate: true } default: return state; } } -export default userReducer + +export default userReducer; \ No newline at end of file diff --git a/src/FLAD/redux/store.tsx b/src/FLAD/redux/store.tsx index 663d697..724a5ef 100644 --- a/src/FLAD/redux/store.tsx +++ b/src/FLAD/redux/store.tsx @@ -1,10 +1,6 @@ import { configureStore } from '@reduxjs/toolkit' import appReducer from './reducers/appReducer'; import userReducer from './reducers/userReducer'; -import { spotTypes } from './types/spotTypes'; -import { userTypes } from './types/userTypes'; -import { spotifyTypes } from './types/spotifyTypes'; -import { favoritesTypes } from './types/favoritesTypes'; // Reference here all your application reducers const reducer = { @@ -13,14 +9,13 @@ const reducer = { } const store = configureStore({ + // @ts-ignore reducer: reducer, - middleware: (getDefaultMiddleware) => getDefaultMiddleware({ - serializableCheck: { - ignoredActions: [spotTypes.FETCH_SPOT, spotifyTypes.GET_USER_CURRENT_MUSIC, favoritesTypes.ADD_FAVORITE_MUSICS, favoritesTypes.REMOVE_FAVORITE_MUSICS, spotTypes.REMOVE_SPOT, userTypes.LOGIN, userTypes.SIGNUP ], - ignoredActionPaths: ['appReducer'], - ignoredPaths: ['appReducer', 'userReducer'] - } - }) + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + immutableCheck: false, + serializableCheck: false, + }) },); export default store; \ No newline at end of file diff --git a/src/FLAD/redux/thunk/appThunk.tsx b/src/FLAD/redux/thunk/appThunk.tsx new file mode 100644 index 0000000..2933b71 --- /dev/null +++ b/src/FLAD/redux/thunk/appThunk.tsx @@ -0,0 +1,107 @@ +import axios from "axios"; +import * as SecureStore from 'expo-secure-store'; +import { Spot } from "../../models/Spot"; +import configs from "../../constants/config"; +import { MusicServiceProvider } from "../../models/MusicServiceProvider"; +import { addFavoriteMusic, setFavoriteMusic, setUserCurrentMusic } from "../actions/appActions"; +import { setAccessError, setErrorEmptyMusic } from "../actions/userActions"; +import { SpotMapper } from "../../models/mapper/SpotMapper"; +import { logout } from "./authThunk"; +import { removeFromSpotList } from "../actions/spotActions"; + +export const getUserCurrentMusic = () => { + //@ts-ignore + return async dispatch => { + try { + let idTrack; + const resp = await MusicServiceProvider.musicService.getCurrentlyPlayingMusic(); + if (resp === null) { + idTrack = await MusicServiceProvider.musicService.getRecentlyPlayedMusic(); + if (idTrack === null) { + dispatch(setErrorEmptyMusic(true)); + dispatch(setUserCurrentMusic(null)); + return; + } + } else { + idTrack = resp; + } + const music = await MusicServiceProvider.musicService.getMusicById(idTrack); + dispatch(setUserCurrentMusic(music)) + } catch (error: any) { + console.log(error); + switch (error.response.status) { + case 401: + dispatch(logout); + break; + case 403: + dispatch(setAccessError(true)); + break; + default: + console.error("Error retrieving music currently listened : " + error); + dispatch(setAccessError(true)); + break; + } + } + } +} + +export const getFavoriteMusic = () => { + //@ts-ignore + return async dispatch => { + try { + let token: string | null = await SecureStore.getItemAsync(configs.key); + const headers = { + 'Authorization': 'Bearer ' + token + }; + const resp = await axios.get( + configs.API_URL + '/user/musics', + { headers } + ) + + const musicIds = resp.data.musics.map((music: any) => music.musicId); + const musics = await MusicServiceProvider.musicService.getMusicsWithIds(musicIds); + const result = resp.data.musics + .filter((music: any) => musics.some((m: any) => m.id === music.musicId)) + .map((music: any) => { + const matchingMusic = musics.find((m: any) => m.id === music.musicId); + return { + ...music, + music: matchingMusic, + }; + }); + + dispatch(setFavoriteMusic(result.map((item: any) => SpotMapper.toModel(item)))); + + } catch (error: any) { + console.error(error); + dispatch(setAccessError(true)); + } + }; +} + +export const addMusicToFavorite = (spot: Spot) => { + //@ts-ignore + return async dispatch => { + try { + dispatch(removeFromSpotList(spot)); + let token: string | null = await SecureStore.getItemAsync(configs.key); + const headers = { + 'Authorization': 'Bearer ' + token + }; + const body = { + "musicId": spot.music.id, + "userId": spot.user + } + await axios.post(configs.API_URL + '/user/musics', body, { headers }); + + spot.date = new Date(Date.now()); + dispatch(addFavoriteMusic(spot)); + } catch (error: any) { + switch (error.response.status) { + default: + console.error("Error like spot : " + error); + break; + } + } + }; +} \ No newline at end of file diff --git a/src/FLAD/redux/thunk/authThunk.tsx b/src/FLAD/redux/thunk/authThunk.tsx index 82ee547..74114e0 100644 --- a/src/FLAD/redux/thunk/authThunk.tsx +++ b/src/FLAD/redux/thunk/authThunk.tsx @@ -1,58 +1,73 @@ import axios from "axios"; -import { API_URL } from "../../fladConfig"; -import { Credentials, CredentialsRegister, restoreToken, setLoginState, UserLogout, userChangeMode, userSignUp, ChangeErrorLogin, ChangeErrorSignup } from "../actions/userActions"; +import configs from "../../constants/config"; +import { LoginCredentials, RegisterCredentials, restoreToken, userLogin, userLogout, setErrorLogin, setErrorSignup, setErrorNetwork } from "../actions/userActions"; import * as SecureStore from 'expo-secure-store'; -import { UserFactory } from "../../Model/factory/UserFactory"; +import { UserMapper } from "../../models/mapper/UserMapper"; +import { MusicServiceProvider } from "../../models/MusicServiceProvider"; -const key = 'userToken'; +const keyRemember = 'rememberUser'; -export const registerUser = (resgisterCredential: CredentialsRegister) => { +export const register = (registerCredential: RegisterCredentials) => { //@ts-ignore return async dispatch => { try { - console.log("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr") - - console.log(resgisterCredential); - console.log("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr") const config = { headers: { 'Content-Type': 'application/json', }, } const resp = await axios.post( - 'https://flad-api-production.up.railway.app/api/users/register', - resgisterCredential, + configs.API_URL + '/auth/register', + registerCredential, config ) - - if (resp.data.token) { - console.log(resp.data.token); - const token = resp.data.token; - const headers = { - 'Authorization': 'Bearer ' + token - }; - const user = await axios.get( - "https://flad-api-production.up.railway.app/api/users", - { headers } - ) - dispatch(userSignUp(UserFactory.JsonToModel(user.data))); - } else { - console.log('Login Failed', 'Username or Password is incorrect'); + const token = resp.data.token; + await SecureStore.setItemAsync(configs.key, token); + await SecureStore.setItemAsync(keyRemember, 'true'); + const headers = { + 'Authorization': 'Bearer ' + token + }; + const user = await axios.get( + configs.API_URL + '/user', + { headers } + ) + MusicServiceProvider.initSpotify(user.data.data.tokenSpotify, user.data.data.idSpotify); + dispatch(userLogin(UserMapper.toModel(user.data.data))); + } catch (error: any) { + switch (error.response.status) { + case 400: + dispatch(setErrorSignup("Email non valide !")); + break; + case 409: + const duplicateFields = error.response.data || []; + let errorMessage = "Email, Spotify ou nom déjà utilisé !"; + if (duplicateFields.includes('idSpotify')) { + errorMessage = "Compte Spotify déjà utilisé !"; + } + if (duplicateFields.includes('name')) { + errorMessage = "Nom déjà utilisé !"; + } + if (duplicateFields.includes('email')) { + errorMessage = "Email déjà utilisé !"; + } + dispatch(setErrorSignup(errorMessage)); + break; + case 500: + dispatch(setErrorSignup("Compte Spotify non autorisé !")); + break; + default: + console.error("Error : " + error.message); + dispatch(setErrorSignup("Erreur lors de l'inscription !")); + break; } - - } catch (error) { - console.log('Login Failed'+ error.message + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - dispatch(ChangeErrorSignup()) } } } -export const userLogin = (loginCredential: Credentials) => { +export const login = (loginCredential: LoginCredentials, remember: boolean) => { //@ts-ignore return async dispatch => { try { - console.log(loginCredential); - const config = { headers: { 'Content-Type': 'application/json', @@ -60,30 +75,37 @@ export const userLogin = (loginCredential: Credentials) => { } const resp = await axios.post( - "https://flad-api-production.up.railway.app/api/users/login", + configs.API_URL + '/auth/login', loginCredential, config ) - if (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 } - ) - console.log(user.data); - - dispatch(setLoginState(user.data)); - } else { - console.log('Login Failed', 'Username or Password is incorrect'); + const token = resp.data.token; + await SecureStore.setItemAsync(configs.key, token); + if (remember) { + await SecureStore.setItemAsync(keyRemember, remember.toString()); } - } catch (error) { - dispatch(ChangeErrorLogin()) + const headers = { + 'Authorization': 'Bearer ' + token + }; + + const user = await axios.get( + configs.API_URL + '/user', + { headers } + ) + MusicServiceProvider.initSpotify(user.data.data.tokenSpotify, user.data.data.idSpotify); + dispatch(userLogin(UserMapper.toModel(user.data.data))); + } catch (error: any) { + switch (error.response.status) { + case 400: + dispatch(setErrorLogin(true)); + break; + default: + console.error("Error : " + error.message); + dispatch(setErrorNetwork(true)); + break; + } } } } @@ -91,45 +113,59 @@ export const userLogin = (loginCredential: Credentials) => { export const getRefreshToken = () => { //@ts-ignore return async dispatch => { - try { - let userToken: string | null = await SecureStore.getItemAsync(key); - - if (userToken) { - dispatch(restoreToken(userToken)); + let remember: string | null = await SecureStore.getItemAsync(keyRemember); + let token: string | null = await SecureStore.getItemAsync(configs.key); + if (token) { + if (remember) { + const headers = { + 'Authorization': 'Bearer ' + token + }; + try { + const user = await axios.get( + configs.API_URL + '/user', + { headers } + ) + MusicServiceProvider.initSpotify(user.data.data.tokenSpotify, user.data.data.idSpotify); + await dispatch(userLogin(UserMapper.toModel(user.data.data))); + } catch (error: any) { + dispatch(logout()); + } } else { - const empty = ""; - dispatch(restoreToken(empty)); + dispatch(logout()); } - } catch (e) { - console.log('Error :', e); } - } -} + dispatch(restoreToken()); - -export const DeleteToken = () => { - //@ts-ignore - return async dispatch => { - try { - await SecureStore.deleteItemAsync(key); - dispatch(UserLogout()); - } catch (e) { - console.log('Error deleting token', e); - } } } -export const ChangeMode = (value: boolean) => { +export const deleteUser = () => { //@ts-ignore return async dispatch => { - dispatch(userChangeMode(value)); + let token: string | null = await SecureStore.getItemAsync(configs.key); + if (token) { + const headers = { + 'Authorization': 'Bearer ' + token + }; + try { + await axios.delete( + configs.API_URL + '/user', + { headers } + ) + dispatch(logout()); + } catch (error: any) { + console.error("Error deleting account : " + error.message); + } + } } } -export const ChangeImageUserCurrent = (value: ImagePicker) => { +export const logout = () => { //@ts-ignore return async dispatch => { - //@ts-ignore - dispatch(userChangeImage(value)); + await SecureStore.deleteItemAsync(configs.key); + await SecureStore.deleteItemAsync(keyRemember); + MusicServiceProvider.resetService(); + dispatch(userLogout()); } } \ No newline at end of file diff --git a/src/FLAD/redux/thunk/chatThunk.tsx b/src/FLAD/redux/thunk/chatThunk.tsx new file mode 100644 index 0000000..f3d847e --- /dev/null +++ b/src/FLAD/redux/thunk/chatThunk.tsx @@ -0,0 +1,42 @@ +import Message from "../../models/Message"; +import IMessageService from "../../services/messages/interfaces/IMessageService" +import StubMessageService from "../../services/messages/stub/StubMessageService"; +import { setConversations, setMessages } from "../actions/appActions"; + +const chatService: IMessageService = new StubMessageService(); + +export const getConversations = () => { + //@ts-ignore + return async dispatch => { + try { + const conversations = await chatService.getConversations(); + dispatch(setConversations(conversations)); + } catch (error: any) { + + } + } +} + +export const getMessages = (id: string) => { + //@ts-ignore + return async dispatch => { + try { + const messages = await chatService.getMessagesWithIdConversation(id); + dispatch(setMessages(messages)); + } catch (error: any) { + + } + } +} + +export const sendMessage = (id: string, message: Message) => { + //@ts-ignore + return async dispatch => { + try { + await chatService.sendMessage(id, message); + dispatch(getMessages(id)); + } catch (error: any) { + + } + } +} \ No newline at end of file diff --git a/src/FLAD/redux/thunk/socialThunk.tsx b/src/FLAD/redux/thunk/socialThunk.tsx deleted file mode 100644 index 558cc9f..0000000 --- a/src/FLAD/redux/thunk/socialThunk.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import axios from "axios"; -import { Spot } from "../../Model/Spot"; - -export const likeSpot = async (spot: Spot) => { - return async (dispatch) => { - axios.post("osdj").then(responce => { - if (responce.status == 200) { - dispatch(true); - } - }).catch(error => { - console.log("something goes wrong while searching : " + error); - ; - }) - - - }; -} \ No newline at end of file diff --git a/src/FLAD/redux/thunk/spotThunk.tsx b/src/FLAD/redux/thunk/spotThunk.tsx index 4451c06..4e38282 100644 --- a/src/FLAD/redux/thunk/spotThunk.tsx +++ b/src/FLAD/redux/thunk/spotThunk.tsx @@ -1,69 +1,98 @@ import axios from "axios"; +import configs from "../../constants/config"; import * as SecureStore from 'expo-secure-store'; -import { Spot } from "../../Model/Spot"; -import SpotifyService from "../../services/spotify/spotify.service"; -import { setSpotList, setUserCurrentMusic } from "../actions/spotActions"; -const key = 'userToken'; +import qs from "qs"; +import { removeFromSpotList, setSpotList } from "../actions/spotActions"; +import { MusicServiceProvider } from "../../models/MusicServiceProvider"; +import { SpotMapper } from "../../models/mapper/SpotMapper"; +import { Spot } from "../../models/Spot"; +import { Person } from "../../models/Person"; +import Music from "../../models/Music"; +import { PersonMapper } from "../../models/mapper/PersonMapper"; -export type CreateSpotReqBody = { - id: string; - name: string; - artist: string; - linkCover: string; - user: string; -} -export const getSpotList = (spotsData : Record , resuestHandler: SpotifyService) => { +export const getSpotList = (longitude: string, latitude: string, music: string) => { //@ts-ignore return async dispatch => { try { - //@ts-ignore - if (spotsData) { - - 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'); + let token: string | null = await SecureStore.getItemAsync(configs.key); + const body: Record = { + longitude: longitude, + latitude: latitude, + currentMusic: music } + const resp = await axios({ + url: configs.API_URL + '/user/nextTo?' + qs.stringify(body), + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + const musicIds = resp.data.data.map((music: any) => music.musicId); + const musics = await MusicServiceProvider.musicService.getMusicsWithIds(musicIds); + + const userIds = resp.data.data.map((user: any) => user.userId); + + const users = await getUsersInfo(userIds); + + const result = resp.data.data + .filter((spot: any) => musics.some((m: Music) => m.id === spot.musicId)) + .filter((spot: any) => users.some((u: Person) => u.id === spot.userId)) + .map((spot: any) => { + const matchingMusic = musics.find((m: any) => m.id === spot.musicId); + const matchingUser = users.find((user: any) => user.id === spot.userId); + return { + ...spot, + music: matchingMusic, + user: matchingUser, + }; + }); + dispatch(setSpotList(result.map((item: any) => SpotMapper.toModel(item)))); - } catch (error) { - console.log('Error---------', error); + } catch (error: any) { + console.log(error); + switch (error.response.status) { + default: + console.error("Error retrieving spots : " + error); + break; + } } + } } -export const getCurrentUserMusic = (resuestHandler: SpotifyService) => { + +const getUsersInfo = async (userIds: string[]) => { + try { + const token: string | null = await SecureStore.getItemAsync(configs.key); + + const ids = userIds.join('&'); + + const resp = await axios({ + url: configs.API_URL + `/users?ids=${ids}`, + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return resp.data.map((item: any) => PersonMapper.toModel(item)); + } catch (error) { + console.error("Error retrieving user information: " + error); + return []; + } +} + +export const removeSpot = (spot: Spot) => { //@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); - if(!completeMusic){ - return; - } - dispatch(setUserCurrentMusic(completeMusic)); - } - catch (error) { - console.log('Error---------', error); - } + dispatch(removeFromSpotList(spot)); } } -export const searchMusic = async (resuestHandler: SpotifyService, search: string) => { - return async (dispatch) => { - return resuestHandler.searchMusic(search).then(musics => dispatch((musics))).catch(err => console.log("something goes wrong while searching : " + err)); - }; -} \ No newline at end of file +export const addToPlaylist = (spot: Spot) => { + //@ts-ignore + return async dispatch => { + MusicServiceProvider.musicService.addToPlaylist(spot.music.id); + dispatch(removeFromSpotList(spot)); + } +} \ No newline at end of file diff --git a/src/FLAD/redux/thunk/userThunk.tsx b/src/FLAD/redux/thunk/userThunk.tsx new file mode 100644 index 0000000..206de67 --- /dev/null +++ b/src/FLAD/redux/thunk/userThunk.tsx @@ -0,0 +1,158 @@ +import axios from "axios"; +import configs from "../../constants/config"; +import { setDarkMode, setErrorUpdateMessage, userLogin } from "../actions/userActions"; +import * as SecureStore from 'expo-secure-store'; +import { UserMapper } from "../../models/mapper/UserMapper"; +import { MusicServiceProvider } from "../../models/MusicServiceProvider"; + +export const darkMode = (value: boolean) => { + //@ts-ignore + return async dispatch => { + dispatch(setDarkMode(value)); + } +} + +export const setName = (name: string) => { + //@ts-ignore + return async dispatch => { + try { + let token: string | null = await SecureStore.getItemAsync(configs.key); + const headers = { + 'Authorization': 'Bearer ' + token + }; + await axios.put(configs.API_URL + '/user/name', { name }, { headers }); + + const user = await axios.get( + configs.API_URL + '/user', + { headers } + ) + dispatch(userLogin(UserMapper.toModel(user.data.data))); + + } catch (error: any) { + switch (error.response.status) { + case 409: + dispatch(setErrorUpdateMessage("Nom déjà utilisé.")) + break; + default: + console.error("Error : " + error.message); + break; + } + } + } +} + +export const setMail = (email: string) => { + //@ts-ignore + return async dispatch => { + try { + let token: string | null = await SecureStore.getItemAsync(configs.key); + const headers = { + 'Authorization': 'Bearer ' + token + }; + + await axios.put(configs.API_URL + '/user/email', { email }, { headers }); + + const user = await axios.get( + configs.API_URL + '/user', + { headers } + ) + dispatch(userLogin(UserMapper.toModel(user.data.data))); + } catch (error: any) { + switch (error.response.status) { + case 409: + dispatch(setErrorUpdateMessage("Email déjà utilisé.")) + break; + default: + console.error("Error : " + error.message); + break; + } + } + } +} + +export const setPassword = (oldPassword: string, newPassword: string) => { + //@ts-ignore + return async dispatch => { + try { + let token: string | null = await SecureStore.getItemAsync(configs.key); + const headers = { + 'Authorization': 'Bearer ' + token + }; + await axios.put(configs.API_URL + '/user/password', { oldPassword, newPassword }, { headers }); + + } catch (error: any) { + switch (error.response.status) { + case 500: + dispatch(setErrorUpdateMessage("Mot de passe incorrect.")) + break; + default: + console.error("Error : " + error.message); + break; + } + } + } +} + +export const setImage = (image: string) => { + //@ts-ignore + return async dispatch => { + try { + let token: string | null = await SecureStore.getItemAsync(configs.key); + const headers = { + 'Authorization': 'Bearer ' + token + }; + + await axios.put(configs.API_URL + '/user/image', { image }, { headers }); + + const user = await axios.get( + configs.API_URL + '/user', + { headers } + ) + dispatch(userLogin(UserMapper.toModel(user.data.data))); + } catch (error: any) { + switch (error.response.status) { + case 413: + dispatch(setErrorUpdateMessage("Taille de l'image trop grande :\n" + image.length / 1000 + " Ko. Max 100 Ko.")) + break; + default: + console.error("Error : " + error.message); + break; + } + } + } +} + +export const setSpotify = (tokenSpotify: string) => { + //@ts-ignore + return async dispatch => { + try { + let token: string | null = await SecureStore.getItemAsync(configs.key); + const headers = { + 'Authorization': 'Bearer ' + token + }; + await axios.put(configs.API_URL + '/user/spotify', { tokenSpotify }, { headers }); + + const user = await axios.get( + configs.API_URL + '/user', + { headers } + ) + MusicServiceProvider.initSpotify(user.data.data.tokenSpotify, user.data.data.idSpotify); + dispatch(userLogin(UserMapper.toModel(user.data.data))); + } catch (error: any) { + switch (error.response.status) { + case 400: + dispatch(setErrorUpdateMessage("Ce compte Spotify est déjà associé à votre compte.")) + break; + case 409: + dispatch(setErrorUpdateMessage("Compte Spotify déjà utilisé !")) + break; + case 500: + dispatch(setErrorUpdateMessage("Compte Spotify non autorisé !")); + break; + default: + console.error("Error : " + error.message); + break; + } + } + } +} \ No newline at end of file diff --git a/src/FLAD/redux/types/chatTypes.tsx b/src/FLAD/redux/types/chatTypes.tsx new file mode 100644 index 0000000..9c832e5 --- /dev/null +++ b/src/FLAD/redux/types/chatTypes.tsx @@ -0,0 +1,4 @@ +export const chatTypes = { + FETCH_CONVERSATIONS: 'FETCH_CONVERSATIONS', + FETCH_MESSAGES: 'FETCH_MESSAGES', +} \ No newline at end of file diff --git a/src/FLAD/redux/types/favoritesTypes.tsx b/src/FLAD/redux/types/favoritesTypes.tsx index 7d747f5..2654c64 100644 --- a/src/FLAD/redux/types/favoritesTypes.tsx +++ b/src/FLAD/redux/types/favoritesTypes.tsx @@ -1,5 +1,5 @@ export const favoritesTypes = { GET_FAVORITE_MUSICS: 'GET_FAVORITE_MUSICS', ADD_FAVORITE_MUSICS: 'ADD_FAVORITE_MUSICS', - REMOVE_FAVORITE_MUSICS: 'REMOVE_FAVORITE_MUSICS', + RESET_NB_ADDED_FAVORITE_MUSIC: 'RESET_NB_ADDED_FAVORITE_MUSIC', } \ No newline at end of file diff --git a/src/FLAD/redux/types/playlistTypes.tsx b/src/FLAD/redux/types/playlistTypes.tsx deleted file mode 100644 index 4d8c3e4..0000000 --- a/src/FLAD/redux/types/playlistTypes.tsx +++ /dev/null @@ -1,5 +0,0 @@ -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 index 8a8f70b..8c8024e 100644 --- a/src/FLAD/redux/types/spotTypes.tsx +++ b/src/FLAD/redux/types/spotTypes.tsx @@ -1,5 +1,4 @@ export const spotTypes = { FETCH_SPOT: 'FETCH_SPOT', - ADD_SPOT_MOCK: 'ADD_SPOT_MOCK', - REMOVE_SPOT: 'REMOVE_SPOT', + REMOVE_SPOT: 'REMOVE_SPOT' } \ No newline at end of file diff --git a/src/FLAD/redux/types/spotifyTypes.ts b/src/FLAD/redux/types/spotifyTypes.ts index 0b84ebe..a6e9a79 100644 --- a/src/FLAD/redux/types/spotifyTypes.ts +++ b/src/FLAD/redux/types/spotifyTypes.ts @@ -1,3 +1,3 @@ export const spotifyTypes = { - GET_USER_CURRENT_MUSIC: 'GET_USER_CURRENT_MUSIC', + 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 index 8ced6fe..8e5eb34 100644 --- a/src/FLAD/redux/types/userTypes.tsx +++ b/src/FLAD/redux/types/userTypes.tsx @@ -1,12 +1,14 @@ export const userTypes = { + RESTORE_TOKEN: "RESTORE_TOKEN", LOGIN: 'LOGIN', - SIGNUP: 'SIGNUP', - SAVE_SPOTIFY: 'SAVE_SPOTIFY', - UPDATE_USER: 'UPDATE_USER', - UPDATE_PROFILE_PICTURE: 'UPDATE_PROFILE_PICTURE', USER_LOGOUT: 'USER_LOGOUT', - RESTORE_TOKEN: "RESTORE_TOKEN", - CHANGE_MODE: "CHANGE_MODE", - CHANGE_ERROR_LOGIN: "CHANGE_ERROR_LOGIN", - CHANGE_ERROR_SIGNUP: "CHANGE_ERROR_SIGNUP" + SAVE_SPOTIFY: 'SAVE_SPOTIFY', + SET_ERROR_LOGIN: "SET_ERROR_LOGIN", + SET_ERROR_SIGNUP: "SET_ERROR_SIGNUP", + SET_DARK_MODE: "SET_DARK_MODE", + SET_ERROR_NETWORK: "SET_ERROR_NETWORK", + SET_ERROR_ACCESS: "SET_ERROR_ACCESS", + SET_ERROR_EMPTY_MUSIC: "SET_ERROR_EMPTY_MUSIC", + SET_ERROR_UPDATE: "SET_ERROR_UPDATE", + SET_ERROR_UPDATE_MESSAGE: "SET_ERROR_UPDATE_MESSAGE" } \ No newline at end of file diff --git a/src/FLAD/screens/Chat.tsx b/src/FLAD/screens/Chat.tsx deleted file mode 100644 index 053a21f..0000000 --- a/src/FLAD/screens/Chat.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useNavigation } from "@react-navigation/native"; -import React, { useEffect } from "react"; -import { GiftedChat } from "react-native-gifted-chat"; - -export default function Chat() { - - const navigation = useNavigation(); - - useEffect(() => { - navigation.getParent()?.setOptions({ - tabBarStyle: { - display: "none" - } - }); - return () => navigation.getParent()?.setOptions({ - tabBarStyle: undefined - }); - }, [navigation]); - return ( - - ) -} \ No newline at end of file diff --git a/src/FLAD/screens/ChatScreen.tsx b/src/FLAD/screens/ChatScreen.tsx new file mode 100644 index 0000000..7264722 --- /dev/null +++ b/src/FLAD/screens/ChatScreen.tsx @@ -0,0 +1,177 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { Bubble, GiftedChat, IMessage, InputToolbar, Send } from "react-native-gifted-chat"; +import { faFileImage, faMicrophone } from "@fortawesome/free-solid-svg-icons" +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; +import { useDispatch, useSelector } from 'react-redux'; +import { SafeAreaView, TouchableOpacity, View } from "react-native"; +import Icon from 'react-native-vector-icons/Ionicons'; +import FontAwesome from 'react-native-vector-icons/FontAwesome'; +import Message from "../models/Message"; +import { getMessages, sendMessage } from "../redux/thunk/chatThunk"; +import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome"; + +//@ts-ignore +export default function Chat({ route }) { + const item: string = route.params.conversation; + const [messages, setMessages] = useState(); + //@ts-ignore + const conversations: Message[] = useSelector(state => state.appReducer.messages); + + // @ts-ignore + const isDark = useSelector(state => state.userReducer.dark); + const style: Theme = isDark ? DarkTheme : LightTheme; + + const dispatch = useDispatch(); + + useEffect(() => { + // @ts-ignore + dispatch(getMessages(item)); + }, []); + + useEffect(() => { + const mappedMessages = conversations.map((msg: Message) => ({ + _id: msg.id, + text: msg.content, + createdAt: msg.date, + user: { + _id: msg.sender, + name: msg.sender, + avatar: 'https://picsum.photos/536/354', + }, + audio: msg.audio + })); + + mappedMessages.reverse(); + + mappedMessages.push({ + _id: "0", + text: 'Vous avez matché !!!', + system: true, + }); + + setMessages(mappedMessages); + }, [conversations]); + + const onSend = useCallback((messages: any = []) => { + + const newMessage = new Message( + "-1", + messages[0].text, + "User1", + new Date() + ); + + // @ts-ignore + dispatch(sendMessage(item, newMessage)); + }, []) + + const renderBubble = (props: any) => { + return ( + + ); + }; + + const renderInputToolbar = (props: any) => { + return ( + + ); + }; + + function handleImageIconPress(): void { + console.log("Image"); + } + + function handleMicrophoneIconPress(): void { + console.log("Audio"); + } + + const renderActions = (props: any) => { + return ( + + + + + + + + + + ); + }; + + + const renderSend = (props: any) => { + return ( + + + + + + ); + }; + + const scrollToBottomComponent = () => { + return ( + + ) + } + + return ( + + onSend(messages)} + user={{ + _id: "User1", + }} + listViewProps={{ + style: { + backgroundColor: style.body + }, + }} + // @ts-ignore + textInputStyle={{ + backgroundColor: style.Line, + borderRadius: 20, + paddingHorizontal: 15, + paddingTop: 10, + marginTop: 5, + color: style.Text + }} + renderActions={renderActions} + maxInputLength={255} + renderBubble={renderBubble} + renderInputToolbar={renderInputToolbar} + renderSend={renderSend} + scrollToBottom + scrollToBottomComponent={scrollToBottomComponent} + placeholder="Chat" + /> + + ); +} \ No newline at end of file diff --git a/src/FLAD/screens/Conversation.tsx b/src/FLAD/screens/Conversation.tsx deleted file mode 100644 index dd979c5..0000000 --- a/src/FLAD/screens/Conversation.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useNavigation } from "@react-navigation/native"; -import { SafeAreaView, StyleSheet, Text, View, FlatList, TouchableOpacity } from "react-native"; -import { useSelector } from "react-redux"; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; -import Friend from "../components/Friend"; -import normalize from '../components/Normalize'; - -export default function ConversationList() { - - // @ts-ignore - const isDark = useSelector(state => state.userReducer.dark); - - const navigation = useNavigation(); - - const friends = [ - { id: 1, name: "Lucas", lastMessage: "J'en ai marre de provot", source: require('../assets/images/jul.png') }, - { id: 2, name: "Louison", lastMessage: "Tu vien piscine ?", source: require('../assets/images/jul.png') }, - { id: 3, name: "Dave", lastMessage: "Ok c noté !", source: require('../assets/images/pnl.png') }, - { id: 4, name: "Valentin", lastMessage: "Haha react native c incroyable !!!", source: require('../assets/images/jul.png') }, - ]; - - const style = isDark ? GraphicalCharterDark : GraphicalCharterLight; - - const styles = StyleSheet.create({ - mainSafeArea: { - flex: 1, - backgroundColor: style.body, - }, - titleContainer: { - marginTop: 30, - marginLeft: 20, - }, - title: { - fontSize: normalize(28), - fontWeight: 'bold', - color: style.Text, - }, - description: { - marginTop: 10, - fontSize: normalize(20), - color: '#787878', - marginBottom: 20 - } - }) - - return ( - - - Messages - Retrouvez ici tous vos amis! - - item.id.toString()} - renderItem={({ item }) => ( - navigation.navigate('Chat')}> - - - )} - /> - - ) -} \ No newline at end of file diff --git a/src/FLAD/screens/ConversationScreen.tsx b/src/FLAD/screens/ConversationScreen.tsx new file mode 100644 index 0000000..aecc80a --- /dev/null +++ b/src/FLAD/screens/ConversationScreen.tsx @@ -0,0 +1,110 @@ +import { useNavigation } from "@react-navigation/native"; +import { StyleSheet, Text, View, FlatList, TouchableOpacity, RefreshControl } from "react-native"; +import { useSelector, useDispatch } from "react-redux"; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; +import Friend from "../components/FriendComponent"; +import normalize from '../components/Normalize'; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useEffect, useState } from "react"; +import { getConversations } from "../redux/thunk/chatThunk"; + +export default function ConversationScreen() { + + //@ts-ignore + const friends = useSelector(state => state.appReducer.conversations); + // @ts-ignore + const isDark = useSelector(state => state.userReducer.dark); + const [refreshing, setRefreshing] = useState(false); + + const dispatch = useDispatch(); + + useEffect(() => { + // @ts-ignore + dispatch(getConversations()); + }, []); + + const handleRefresh = () => { + setRefreshing(true); + //@ts-ignore + dispatch(getConversations()); + setTimeout(() => { + setRefreshing(false); + }, 700); + }; + + const navigation = useNavigation(); + + const style: Theme = isDark ? DarkTheme : LightTheme; + + const insets = useSafeAreaInsets(); + + const styles = StyleSheet.create({ + mainSafeArea: { + flex: 1, + backgroundColor: style.body, + paddingTop: insets.top + }, + titleContainer: { + marginLeft: "7%", + }, + title: { + fontSize: normalize(28), + fontWeight: 'bold', + color: style.Text, + }, + description: { + marginTop: 3, + fontSize: normalize(20), + color: '#787878', + marginBottom: 5 + }, + body: { + alignItems: 'center', + justifyContent: 'center', + flex: 1, + marginHorizontal: "7%" + }, + text: { + color: style.Text, + fontSize: normalize(18), + opacity: 0.8, + textAlign: 'center' + } + }) + + return ( + + + Messages + Retrouvez ici les discussions + + {friends.length === 0 ? ( + + + Pas de conversations pour le moment. 🥲{'\n'} + Va liker des musiques pour créer des conversations avec des gens dans le monde ! 🔥🎆 + + + ) : ( + b.lastMessage.date - a.lastMessage.date)} + keyExtractor={(item) => item.id.toString()} + renderItem={({ item }) => ( + // @ts-ignore + navigation.navigate('Chat', { username: item.name, image: item.image, conversation: item.id })}> + + + )} + refreshControl={ + + } + /> + )} + + ) +} \ No newline at end of file diff --git a/src/FLAD/screens/DetailScreen.tsx b/src/FLAD/screens/DetailScreen.tsx new file mode 100644 index 0000000..dbdd212 --- /dev/null +++ b/src/FLAD/screens/DetailScreen.tsx @@ -0,0 +1,374 @@ +import { useIsFocused, useNavigation } from "@react-navigation/native"; +import { View, Text, Image, StyleSheet, TouchableOpacity, ScrollView, Share, Alert, Linking, FlatList, ActivityIndicator, Platform } from "react-native"; +import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, withSpring } from "react-native-reanimated"; +import { Audio } from 'expo-av'; +import { useEffect, useState } from "react"; +import normalize from '../components/Normalize'; +import Music from "../models/Music"; +import { LinearGradient } from "expo-linear-gradient"; +import { MusicServiceProvider } from "../models/MusicServiceProvider"; +import { SimilarMusic } from "../components/SimilarMusicComponent"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import Artist from "../models/Artist"; +import { BlurView } from 'expo-blur'; + +const halfPi = Math.PI / 2; + +//@ts-ignore +export default function DetailScreen({ route }) { + const item: Music = route.params.music; + const [simularMusic, setSimularMusic] = useState([]); + const [artistImage, setArtistImage] = useState(null); + const [isPlaying, setIsPlaying] = useState(false); + const [addedToPlaylist, setAddedToPlaylist] = useState(false); + const [sound, setSound] = useState(); + const [loading, setLoading] = useState(true); + + const navigator = useNavigation(); + + useEffect(() => { + getSimilarTrack(); + getArtistImage(); + + if (item.trackPreviewUrl) { + loadMusic(); + } + + return () => { + if (sound) { + sound.unloadAsync(); + } + }; + }, []); + + const isFocused = useIsFocused(); + + useEffect(() => { + if (!isFocused && sound) { + sound.stopAsync(); + setIsPlaying(false); + } + }, [isFocused]); + + const loadMusic = async () => { + const { sound } = await Audio.Sound.createAsync( + { uri: item.trackPreviewUrl }, + { shouldPlay: isPlaying }, + onPlaybackStatusUpdate + ); + setSound(sound); + }; + + const getArtistImage = async () => { + const image = await MusicServiceProvider.musicService.getImageArtistWithId(item.artists[0].id); + setArtistImage(image); + }; + + const onPlaybackStatusUpdate = (status: any) => { + if (status.didJustFinish) { + setIsPlaying(false); + } + }; + + const play = async () => { + if (sound) { + if (isPlaying) { + await sound.pauseAsync(); + } else { + await sound.replayAsync(); + await sound.playAsync(); + } + setIsPlaying(!isPlaying); + } + }; + + const getSimilarTrack = async () => { + try { + const simularMusic = await MusicServiceProvider.musicService.getSimilarTracks(item.id); + setSimularMusic(simularMusic); + } finally { + setLoading(false); + } + } + + const onShare = async () => { + try { + await Share.share({ + message: + item.url, + }); + } catch (error: any) { + Alert.alert(error.message); + } + }; + + const addToPlaylist = async () => { + MusicServiceProvider.musicService.addToPlaylist(item.id); + setAddedToPlaylist(true); + }; + + const sensor = useAnimatedSensor(SensorType.ROTATION); + const styleAnimatedImage = useAnimatedStyle(() => { + const { pitch, roll } = sensor.sensor.value; + const verticalAxis = interpolate( + pitch, + [-halfPi * 2, halfPi * 2], + [-45, 45] + ) + const horizontalAxis = interpolate( + roll, + [-halfPi * 2, halfPi * 2], + [-45, 45] + ) + return { + top: withSpring(verticalAxis), + left: withSpring(horizontalAxis), + }; + + }) + + const insets = useSafeAreaInsets(); + + const styles = StyleSheet.create({ + mainSafeArea: { + height: '100%', + width: '100%', + }, + backgroundSection: { + height: "100%", + width: "100%", + position: "absolute", + }, + back_drop: { + height: "100%", + width: '100%', + position: "absolute", + }, + gradientFade: { + height: "100%", + }, + card: { + alignItems: 'center', + paddingTop: insets.top + }, + cardCover: { + width: normalize(390), + height: normalize(390), + borderRadius: 16, + resizeMode: 'stretch', + }, + section1: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + marginHorizontal: "10%", + marginBottom: "4%", + marginTop: "5%" + }, + section2: { + flex: 1 + }, + section3: { + flexDirection: "row", + }, + similarTitle: { + color: "#FFF", + paddingLeft: "8%", + fontSize: normalize(28), + fontWeight: "600", + paddingBottom: normalize(15) + }, + title: { + maxWidth: "90%", + fontSize: normalize(30), + fontWeight: "bold", + color: "white" + }, + playButton: { + height: normalize(60), + width: normalize(60), + backgroundColor: "#E70E0E", + borderRadius: 30 + }, + imagePlayButton: { + width: normalize(40), + height: normalize(40) + }, + bodyPlayButton: { + flex: 1, + justifyContent: 'center', + alignItems: 'center' + }, + artist: { + maxWidth: "40%", + fontWeight: "bold", + color: "white", + fontSize: normalize(17), + paddingLeft: "2%" + }, + date: { + fontWeight: "400", + color: "white", + fontSize: normalize(17) + }, + buttonArtist: { + flexDirection: "row", + alignItems: "center", + }, + saveButton: { + backgroundColor: '#3F3F3F', + width: normalize(180), + height: normalize(50), + padding: 10, + borderRadius: 8, + marginRight: normalize(10), + flexDirection: "row", + alignItems: "center" + }, + shareButton: { + backgroundColor: '#3F3F3F', + width: normalize(180), + height: normalize(50), + marginLeft: normalize(10), + padding: 10, + borderRadius: 8, + flexDirection: "row", + alignItems: "center" + }, + saveIcon: { + width: normalize(14.7), + height: normalize(21), + marginLeft: normalize(9) + }, + shareIcon: { + width: normalize(25), + height: normalize(25), + marginBottom: normalize(5) + }, + saveText: { + fontSize: normalize(11), + paddingLeft: normalize(9), + color: "white", + fontWeight: "bold" + }, + shareText: { + fontSize: normalize(11), + paddingLeft: normalize(5), + color: "white", + fontWeight: "bold" + }, + explicitImage: { + marginLeft: normalize(5), + width: normalize(16), + height: normalize(16) + }, + options: { + flexDirection: "row", + alignItems: "center", + paddingTop: normalize(15), + justifyContent: "center", + paddingHorizontal: normalize(20) + }, + overlay: { + ...StyleSheet.absoluteFillObject, + backgroundColor: 'rgba(0, 0, 0, 0.1)', + }, + + }); + + return ( + + + + + + + + + + + + { Linking.openURL(item.url); }}> + + + + + + { Linking.openURL(item.url); }}> + {item.name} + {item.explicit && ( + + + )} + + + { Linking.openURL(item.artists[0].url); }}> + {artistImage && ( + + )} + {item.artists.map((artist: Artist) => artist.name).join(', ')} + - {item.date} - {Math.floor(item.duration / 60)} min {Math.floor(item.duration % 60)} s + + + + + {!!item.trackPreviewUrl && ( + + + + + + )} + + + + + + Dans ma collection + + + + + + + Partager cette music + + + + + + Similaire + {loading ? ( + + ) : + item.id} + renderItem={({ item }) => + { + // @ts-ignore + navigator.replace("Detail", { "music": item }) + }} > + + + } + />} + + + + + ); +}; \ No newline at end of file diff --git a/src/FLAD/screens/Favorite.tsx b/src/FLAD/screens/Favorite.tsx deleted file mode 100644 index 1da3675..0000000 --- a/src/FLAD/screens/Favorite.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React from 'react'; -import { Image, StyleSheet, Text, View, FlatList, TouchableOpacity, TouchableHighlight, SafeAreaView } from 'react-native'; -import CardMusic from '../components/CardMusic'; -import normalize from '../components/Normalize'; -import Music from '../Model/Music' -import FladyComponent from '../components/FladyComponent'; -import { useNavigation } from "@react-navigation/native"; -import { useSelector } from 'react-redux'; -import { SharedElement } from 'react-navigation-shared-element'; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; - -export default function FavoritePage() { - - // @ts-ignore - const isDark = useSelector(state => state.userReducer.dark); - const style = isDark ? GraphicalCharterDark : GraphicalCharterLight; - - const navigation = useNavigation(); - //@ts-ignore - const favoritesMusic = useSelector(state => state.appReducer.favoriteMusic); - const images = [ - { id: 1, source: require('../assets/images/FLADYLove.png') }, - { id: 2, source: require('../assets/images/FLADYStar.png') }, - { id: 3, source: require('../assets/images/FLADYHate.png') }, - { id: 4, source: require('../assets/images/FLADYCry.png') }, - ]; - const navigueToDetail = (music: any) => { - // @ts-ignore - navigation.navigate("MusicDetail", { "music": music }) - }; - const styles = StyleSheet.create({ - mainSafeArea: { - flex: 1, - backgroundColor: style.body, - }, - titleContainer: { - marginTop: 30, - marginLeft: 20, - }, - title: { - fontSize: normalize(28), - fontWeight: 'bold', - color: style.Text, - }, - description: { - marginTop: 10, - fontSize: normalize(20), - color: '#787878', - marginBottom: 20 - }, - button: { - marginTop: '10%', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - alignSelf: 'center', - backgroundColor: 'white', - width: normalize(100), - height: normalize(100), - borderRadius: 21 - }, - buttonImage: { - width: normalize(46), - height: normalize(46), - }, - shadow: { - shadowColor: '#000', - shadowOffset: { - width: 2, - height: 3, - }, - shadowOpacity: 0.50, - shadowRadius: 3.84, - } - }); - - return ( - - - Favoris - Retrouvez ici vos musiques favorites - - ( - { navigueToDetail(item) }}> - - - - - )} - keyExtractor={(item: Music) => item.title} - ListFooterComponent={ - <> - What's your mood? - item.id.toString()} - horizontal - showsHorizontalScrollIndicator={false} - renderItem={({ item }) => ( - - )} - /> - - } - nestedScrollEnabled={true} - /> - - ); -}; \ No newline at end of file diff --git a/src/FLAD/screens/FavoriteScreen.tsx b/src/FLAD/screens/FavoriteScreen.tsx new file mode 100644 index 0000000..a39d716 --- /dev/null +++ b/src/FLAD/screens/FavoriteScreen.tsx @@ -0,0 +1,179 @@ +import React, { useEffect, useState } from 'react'; +import { StyleSheet, Text, View, FlatList, SectionList, TouchableOpacity, RefreshControl } from 'react-native'; +import CardMusic from '../components/CardMusicComponent'; +import normalize from '../components/Normalize'; +import { Svg, Path } from 'react-native-svg'; +import FladyComponent from '../components/FladyComponent'; +import { useNavigation, useFocusEffect } from "@react-navigation/native"; +import { useSelector, useDispatch } from 'react-redux'; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; +import { getFavoriteMusic } from '../redux/thunk/appThunk'; +import { Spot } from '../models/Spot'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { resetNbAddedFavoriteMusic } from '../redux/actions/appActions'; + +export default function FavoriteScreen() { + + // @ts-ignore + const isDark = useSelector(state => state.userReducer.dark); + const style: Theme = isDark ? DarkTheme : LightTheme; + const [refreshing, setRefreshing] = useState(false); + + const images = [ + { id: 1, source: require('../assets/images/flady_love.png') }, + { id: 2, source: require('../assets/images/flady_star.png') }, + { id: 3, source: require('../assets/images/flady_angry.png') }, + { id: 4, source: require('../assets/images/flady_cry.png') }, + ]; + const navigation = useNavigation(); + //@ts-ignore + const favoriteMusic = useSelector(state => state.appReducer.favoriteMusic); + + const dispatch = useDispatch(); + + useEffect(() => { + //@ts-ignore + dispatch(getFavoriteMusic()); + }, []); + + const handleRefresh = () => { + setRefreshing(true); + //@ts-ignore + dispatch(getFavoriteMusic()); + setTimeout(() => { + setRefreshing(false); + }, 900); + }; + + useFocusEffect( + React.useCallback(() => { + dispatch(resetNbAddedFavoriteMusic()); + }, []) + ); + + const groupByDate = (data: Spot[]) => { + const groupedData: { [key: string]: Spot[] } = {}; + + const sortedData = data.sort((a, b) => b.date.getTime() - a.date.getTime()); + + sortedData.forEach((item) => { + const formattedDate = formatDate(item.date); + + if (groupedData[formattedDate]) { + groupedData[formattedDate].push(item); + } else { + groupedData[formattedDate] = [item]; + } + }); + + return Object.keys(groupedData).map((date) => ({ + title: date, + data: groupedData[date], + })); + }; + + const formatDate = (date: Date): string => { + const day = String(date.getDate()).padStart(2, '0'); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const year = date.getFullYear(); + + return `${day}/${month}/${year}`; + }; + + + const insets = useSafeAreaInsets(); + + const styles = StyleSheet.create({ + mainSafeArea: { + flex: 1, + backgroundColor: style.body, + paddingTop: insets.top + }, + titleContainer: { + marginTop: 5, + marginLeft: "7%", + }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingRight: 20 + }, + title: { + fontSize: normalize(28), + fontWeight: 'bold', + color: style.Text, + }, + description: { + fontSize: normalize(20), + color: '#787878', + marginBottom: 5 + }, + titleSection: { + fontSize: normalize(20), + color: style.Text, + fontWeight: '500', + marginLeft: "7%", + marginBottom: 10 + }, + collection: { + marginTop: "3%", + marginLeft: "1%", + } + }); + + return ( + + + + Favoris + + + + + + Retrouvez ici vos musiques favorites + + + } + keyExtractor={(item: Spot) => item.music.id + item.user + item.date} + renderItem={({ item }) => ( + { + //@ts-ignore + navigation.navigate('Detail', { music: item.music }); + }}> + + + )} + renderSectionHeader={({ section: { title } }) => ( + //@ts-ignore + {title} + )} + ListFooterComponent={ + <> + What's your mood? + item.id.toString()} + horizontal + showsHorizontalScrollIndicator={false} + renderItem={({ item }) => } + /> + + } + nestedScrollEnabled={true} + + /> + + ); +}; \ No newline at end of file diff --git a/src/FLAD/screens/LoginPage.tsx b/src/FLAD/screens/LoginScreen.tsx similarity index 71% rename from src/FLAD/screens/LoginPage.tsx rename to src/FLAD/screens/LoginScreen.tsx index 8dd07df..00e2384 100644 --- a/src/FLAD/screens/LoginPage.tsx +++ b/src/FLAD/screens/LoginScreen.tsx @@ -1,11 +1,11 @@ -import React, { useState } from 'react'; -import { View, Image, StyleSheet, Text, ImageBackground, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity } from 'react-native'; +import React, { useEffect, useState } from 'react'; +import { Alert, View, Image, StyleSheet, Text, ImageBackground, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity } from 'react-native'; +import { setErrorNetwork, LoginCredentials } from "../redux/actions/userActions"; import { useNavigation } from "@react-navigation/native"; import normalize from '../components/Normalize'; -import { userLogin } from '../redux/thunk/authThunk'; +import { login } from '../redux/thunk/authThunk'; import { useDispatch, useSelector } from 'react-redux'; import { Audio } from 'expo-av'; -import { Credentials } from '../redux/actions/userActions'; // @ts-ignore const DismissKeyboard = ({ children }) => ( @@ -14,35 +14,52 @@ const DismissKeyboard = ({ children }) => ( ) -export default function LoginPage() { - const [sound, setSound] = useState(); +export default function LoginScreen() { const [rememberMe, setRememberMe] = useState(false); const navigation = useNavigation(); // @ts-ignore const failedLogin = useSelector(state => state.userReducer.failedLogin); - + // @ts-ignore + const networkError = useSelector(state => state.userReducer.errorNetwork); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); + const dispatch = useDispatch(); + async function playSound() { - console.log('Loading Sound'); const { sound } = await Audio.Sound.createAsync( - require('../assets/sounds/Click.mp3') + require('../assets/sounds/click.mp3') ); - setSound(sound); await sound.playAsync(); } - const dispatch = useDispatch(); const submitForm = () => { - const credentials: Credentials = { - email: username, + const credentials: LoginCredentials = { + email: username.toLowerCase().trim(), password: password }; //@ts-ignore - dispatch(userLogin(credentials)) + dispatch(login(credentials, rememberMe)) playSound() } + useEffect(() => { + if (networkError) { + Alert.alert( + 'Erreur réseau', + 'Une erreur réseau s\'est produite. Veuillez vérifier votre connexion internet et réessayer.', + [ + { + text: 'OK', + onPress: () => { + dispatch(setErrorNetwork(false)); + }, + }, + ], + { cancelable: false } + ); + } + }, [networkError]); + const toggleRememberMe = () => { setRememberMe(!rememberMe); } @@ -50,11 +67,11 @@ export default function LoginPage() { return ( - + v2.0 - + SE CONNECTER {failedLogin && ( Email ou mot de passe incorrect! @@ -64,7 +81,7 @@ export default function LoginPage() { placeholderTextColor="#B8B4B8" value={username} onChangeText={setUsername} style={[styles.input, styles.shadow]} /> - + - + - + + {rememberMe && ( + + )} + SE SOUVENIR DE MOI - + Tu n'as pas de compte? @@ -124,15 +145,19 @@ const styles = StyleSheet.create({ borderRadius: 21 }, textError: { - fontSize: 15, - alignSelf: "center", - color: "red", + fontSize: 15, + alignSelf: "center", + color: "red", fontWeight: 'bold' }, buttonImage: { width: normalize(46), height: normalize(46), }, + checkBoxImage: { + width: normalize(14), + height: normalize(11), + }, iconUser: { position: 'absolute', width: 20, @@ -204,7 +229,12 @@ const styles = StyleSheet.create({ color: 'white' }, checkboxChecked: { - backgroundColor: 'white' + backgroundColor: '#5C1DC3', + borderColor: '#5C1DC3', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + alignSelf: 'center' }, inscriptionText: { flexDirection: 'row', diff --git a/src/FLAD/screens/MusicDetail.tsx b/src/FLAD/screens/MusicDetail.tsx deleted file mode 100644 index f1226b5..0000000 --- a/src/FLAD/screens/MusicDetail.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import { useNavigation } from "@react-navigation/native"; -import { View, Text, Image, StyleSheet, TouchableOpacity, ScrollView, Pressable } from "react-native"; -import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, withSpring } from "react-native-reanimated"; - -import { Audio } from 'expo-av'; -import { useEffect, useState } from "react"; -import normalize from '../components/Normalize'; -import Music from "../Model/Music"; -import SpotifyService from "../services/spotify/spotify.service"; -import { LinearGradient } from "expo-linear-gradient"; -import FontAwesome from 'react-native-vector-icons/FontAwesome'; -import { Feather as Icon } from "@expo/vector-icons"; -import { HorizontalFlatList } from "../components/HorizontalFlatList"; -import { LittleCard } from "../components/littleCard"; -import * as SecureStore from 'expo-secure-store'; -import { MY_SECURE_AUTH_STATE_KEY } from "./Register"; - -const halfPi = Math.PI / 2; - - -//@ts-ignore -const MusicDetail = ({ route }) => { - const music: Music = route.params.music; - const [currentspot] = useState(music); - const [simularMusic, setSimularMusic] = useState([]); - const [isPlaying, setIsPlaying] = useState(false); - const [sound, setSound] = useState(null); - - const navigator = useNavigation(); - - - useEffect(() => { - getSimilarTrack(); - }, []); - const getSimilarTrack = async () => { - try { - let token = await SecureStore.getItemAsync(MY_SECURE_AUTH_STATE_KEY); - const service = new SpotifyService(token); - const simularMusic = await service.getSimilarTrack(currentspot.id, 5, 'FR'); - setSimularMusic(simularMusic); - } catch (error) { - console.error('Error ================ in getSimilarTrack', error); - } - } - - const handlePlaySound = async () => { - if (sound === null) { - const { sound: newSound } = await Audio.Sound.createAsync( - { uri: music.trackPreviewUrl }, - { shouldPlay: true } - ); - setSound(newSound); - setIsPlaying(true); - - } else { - setIsPlaying(true); - //@ts-ignore - await sound.playAsync(); - } - }; - - const handleStopSound = async () => { - if (sound !== null) { - setIsPlaying(false); - - //@ts-ignore - await sound.stopAsync(); - } - }; - useEffect(() => { - return sound ? () => { - console.log('Unloading Sound'); - //@ts-ignore - sound.unloadAsync(); - } - : undefined; - }, [sound]); - - const sensor = useAnimatedSensor(SensorType.ROTATION); - const styleAniamatedImage = useAnimatedStyle(() => { - const { pitch, roll } = sensor.sensor.value; - const verticalAxis = interpolate( - pitch, - [-halfPi * 2, halfPi * 2], - [-45, 45] - ) - const horizontalAxis = interpolate( - roll, - [-halfPi * 2, halfPi * 2], - [-45, 45] - ) - return { - top: withSpring(verticalAxis), - left: withSpring(horizontalAxis), - }; - - }) - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dans ma collection - - - - {/* */} - Partager cette music - - - - {simularMusic.length !== 0 && ( - - {(props) => ( - { - // @ts-ignore - navigator.replace("DetailsSpot", { "music": props }) }} > - - - )} - - )} - - - - - ); -}; - -export default MusicDetail; - -const styles = StyleSheet.create({ - mainSafeArea: { - flex: 1, - backgroundColor: "#141414", - }, - body: { - backgroundColor: "#0E0E0E" - }, - backgroundSection: { - height: "100%", - width: "100%", - position: "absolute" - }, - back_drop: { - height: "160%", - width: '430%', - position: "absolute", - }, - gradientFade: { - height: "100%", - }, - background1: { - height: '100%', - width: '100%', - }, - list: { - height: "100%" - }, - section1: { - paddingHorizontal: 25 - } -}) \ No newline at end of file diff --git a/src/FLAD/screens/ProfilScreen.tsx b/src/FLAD/screens/ProfilScreen.tsx new file mode 100644 index 0000000..09b187f --- /dev/null +++ b/src/FLAD/screens/ProfilScreen.tsx @@ -0,0 +1,575 @@ +import React, { useEffect, useState } from 'react'; +import { Alert, View, Text, StyleSheet, TouchableWithoutFeedback, Keyboard, ScrollView, Image } from 'react-native'; +import { TextInput, TouchableOpacity } from 'react-native-gesture-handler'; +import { Svg, Path } from 'react-native-svg'; +import Modal from "react-native-modal"; +import { useNavigation } from "@react-navigation/native"; +import { useDispatch, useSelector } from 'react-redux'; +import * as AuthSession from 'expo-auth-session'; +import normalize from '../components/Normalize'; +import * as ImagePicker from 'expo-image-picker'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; +import { deleteUser } from '../redux/thunk/authThunk'; +import { setImage, setMail, setName, setPassword, setSpotify } from '../redux/thunk/userThunk'; +import { setErrorUpdate } from '../redux/actions/userActions'; +import * as FileSystem from 'expo-file-system'; +import configs from '../constants/config'; + +// @ts-ignore +const DismissKeyboard = ({ children }) => ( + Keyboard.dismiss()}> + {children} + +) + +export default function ProfilScreen() { + // @ts-ignore + const isDark = useSelector(state => state.userReducer.dark); + // @ts-ignore + const errorUpdateMessage = useSelector(state => state.userReducer.errorUpdateMessage); + // @ts-ignore + const errorUpdate = useSelector(state => state.userReducer.errorUpdate); + // @ts-ignore + const userCurrent = useSelector(state => state.userReducer.user); + const [username, setUsername] = useState(''); + const [email, setEmail] = useState(''); + const [oldPassword, setOldPassword] = React.useState(''); + const [newPassword, setNewPassword] = React.useState(''); + const [confirmPassword, setConfirmPassword] = React.useState(''); + const style: Theme = isDark ? DarkTheme : LightTheme; + const navigation = useNavigation(); + const [isModalVisible, setIsModalVisible] = React.useState(false); + const dispatch = useDispatch(); + + const handleModal = () => setIsModalVisible(() => !isModalVisible); + + const deleteAccount = () => { + Alert.alert( + 'Confirmation', + 'Êtes-vous sûr de vouloir supprimer votre compte ?', + [ + { + text: 'Annuler', + style: 'cancel' + }, + { + text: 'Oui', + //@ts-ignore + onPress: () => dispatch(deleteUser()), + style: 'destructive' + }, + ], + { cancelable: false } + ); + }; + + const pickImage = async () => { + const result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.Images, + allowsEditing: true, + aspect: [3, 3], + quality: 0.2, + }); + + if (result.assets !== null) { + const base64Image = await convertImageToBase64(result.assets[0].uri); + //@ts-ignore + dispatch(setImage(base64Image)); + } + }; + + const convertImageToBase64 = async (imageUri: any) => { + const base64 = await FileSystem.readAsStringAsync(imageUri, { + encoding: FileSystem.EncodingType.Base64, + }); + return `data:image/jpg;base64,${base64}`; + }; + + const submitSpotify = async () => { + const token = await getSpotifyToken(); + if (token !== undefined) { + //@ts-ignore + dispatch(setSpotify(token)); + } + }; + + const getSpotifyToken = async () => { + try { + const redirectUri = AuthSession.makeRedirectUri(); + const result: any = await AuthSession.startAsync({ + authUrl: configs.API_URL + '/spotify/exchange?' + 'redirectUrl=' + + encodeURIComponent(redirectUri) + }) + const { + refresh_token: refresh_token, + } = result.params + return refresh_token; + } catch (error) { + Alert.alert("Erreur modification", "La connexion à Spotify à échouer."); + return; + } + } + + const submitUsername = () => { + const isUsernameValid = /^\w+$/.test(username); + + if (username.length > 30) { + Alert.alert("Erreur modification", "Le nom d'utilisateur ne peut pas être plus grand que 30 caractères."); + return; + } + if (!isUsernameValid) { + Alert.alert("Erreur modification", "Le nom d'utilisateur ne peut pas posséder de caractères spéciaux."); + return; + } + Alert.alert( + 'Confirmation', + 'Êtes-vous sûr de vouloir changer de nom d\'utilisateur ?', + [ + { + text: 'Annuler', + style: 'cancel' + }, + { + text: 'Oui', + onPress: () => { + //@ts-ignore + dispatch(setName(username)); + setUsername(""); + }, + style: 'destructive' + }, + ], + { cancelable: false } + ); + } + + const submitEmail = () => { + const isEmailValid = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email); + + if (email.length > 100) { + Alert.alert("Erreur modification", "L'adresse e-mail ne peut pas être plus grand que 100 caractères."); + return; + } + if (!isEmailValid) { + Alert.alert("Erreur modification", "L'adresse e-mail n\'est pas valide."); + return; + } + Alert.alert( + 'Confirmation', + 'Êtes-vous sûr de vouloir changer l\'adresse e-mail ?', + [ + { + text: 'Annuler', + style: 'cancel' + }, + { + text: 'Oui', + onPress: () => { + //@ts-ignore + dispatch(setMail(email)); + setEmail(""); + }, + style: 'destructive' + }, + ], + { cancelable: false } + ); + } + + const submitPassword = () => { + //@ts-ignore + dispatch(setPassword(oldPassword, newPassword)); + setOldPassword(""); + setNewPassword(""); + setConfirmPassword(""); + } + + useEffect(() => { + if (errorUpdate) { + Alert.alert( + "Erreur modification", + errorUpdateMessage, + [ + { + text: 'Ok', + onPress: () => { + dispatch(setErrorUpdate(false)) + }, + }, + ], + { cancelable: false } + ); + } + }, [errorUpdate]); + + const insets = useSafeAreaInsets(); + + const styles = StyleSheet.create({ + mainSafeArea: { + flex: 1, + backgroundColor: style.body, + paddingTop: insets.top + }, + container: { + marginTop: 20, + marginHorizontal: normalize(25), + flex: 1, + backgroundColor: style.body, + }, + buttonSetting: { + width: normalize(11), + height: normalize(18), + marginRight: 5 + }, + modalContent: { + position: 'absolute', + top: '5%', + left: '-5%', + right: '-5%', + height: '100%', + backgroundColor: style.body, + borderRadius: 12 + }, + modalView: { + flexDirection: 'row', + marginTop: 20, + marginLeft: 30, + marginBottom: normalize(45) + }, + exit: { + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center' + }, + textExit: { + fontSize: normalize(20), + color: '#454545', + fontWeight: 'bold' + }, + profilHead: { + alignItems: 'center', + }, + title: { + fontSize: normalize(30), + fontWeight: 'bold', + color: style.Text, + }, + imageWrapper: { + width: 126, + height: 126, + borderRadius: 63, + borderWidth: 3, + borderColor: style.Text, + overflow: 'hidden', + marginVertical: 20, + alignItems: 'center', + justifyContent: 'center', + }, + imageProfil: { + width: 120, + height: 120, + }, + body: { + paddingVertical: 9, + paddingLeft: normalize(10), + backgroundColor: style.Card, + borderRadius: 13, + alignItems: 'flex-start', + marginBottom: normalize(35) + }, + textOption: { + fontSize: normalize(18), + color: style.Text, + fontWeight: 'bold', + marginLeft: 12 + }, + deleteOption: { + paddingVertical: 9, + paddingLeft: 5, + backgroundColor: style.Card, + borderRadius: 13, + flexDirection: 'row', + alignItems: 'center', + }, + textOptionPassword: { + fontSize: normalize(18), + color: '#1c77fb', + marginLeft: 12 + }, + textOptionSpotify: { + fontSize: normalize(18), + color: '#1DB954', + marginLeft: 12 + }, + buttonDeleteOption: { + backgroundColor: '#DF0404', + padding: 5, + borderRadius: 10, + marginLeft: 15 + }, + textDeleteOption: { + fontSize: normalize(18), + color: '#F80404', + marginLeft: 12 + }, + optionId: { + flexDirection: 'row', + marginBottom: 20, + }, + optionMail: { + flexDirection: 'row', + }, + textInputId: { + marginLeft: 50, + width: '50%', + color: style.Text, + fontSize: normalize(18), + }, + textIdSpotify: { + marginLeft: 50, + width: '57%', + color: style.Text, + fontWeight: 'bold', + fontSize: normalize(18), + }, + textInputMail: { + marginLeft: 100, + color: style.Text, + width: '50%', + fontSize: normalize(18) + }, + passwordOption: { + paddingVertical: 9, + paddingLeft: normalize(10), + backgroundColor: style.Card, + borderRadius: 13, + alignItems: 'flex-start', + marginBottom: normalize(30) + }, + spotifyOption: { + paddingVertical: 9, + paddingLeft: normalize(10), + backgroundColor: style.Card, + borderRadius: 13, + alignItems: 'flex-start', + marginBottom: normalize(45) + }, + spotifyView: { + backgroundColor: '#1DB954', + padding: 5, + paddingHorizontal: 5, + borderRadius: 10, + marginLeft: 10 + }, + passwordIcon: { + backgroundColor: '#8e8d92', + padding: 5, + paddingHorizontal: 8, + borderRadius: 10, + marginLeft: 10 + }, + spotifyIcon: { + width: 20, + height: 20 + }, + optionView: { + flexDirection: 'row', + marginTop: 5 + }, + cancelText: { + fontSize: normalize(18), + color: '#1c77fb' + }, + updateText: { + marginLeft: 60, + fontWeight: 'bold', + fontSize: normalize(18), + color: '#404040' + }, + titlePassword: { + fontWeight: 'bold', + fontSize: normalize(20), + color: style.Text, + marginLeft: 65 + }, + warning: { + color: '#98989f', + fontSize: normalize(15) + }, + warningView: { + marginTop: 10, + paddingHorizontal: 40 + }, + bodyModal: { + paddingVertical: 12, + paddingLeft: 30, + marginHorizontal: normalize(25), + backgroundColor: style.Card, + borderRadius: 13, + alignItems: 'flex-start' + }, + optionModalWithUnderline: { + flexDirection: 'row', + borderBottomWidth: 1, + borderColor: style.Line, + paddingBottom: 10, + marginBottom: 10 + }, + optionModal: { + flexDirection: 'row' + }, + textOptionModal: { + fontSize: normalize(18), + color: style.Text, + fontWeight: 'bold', + }, + textInputNewModal: { + marginLeft: 40, + color: style.Text, + width: '67.5%', + fontSize: normalize(18) + }, + textInputConfirmModal: { + marginLeft: 30, + color: style.Text, + width: '67.5%', + fontSize: normalize(18) + }, + textInputOldModal: { + marginLeft: 55, + color: style.Text, + width: '67.5%', + fontSize: normalize(18) + } + }) + + return ( + + + + + navigation.navigate('Setting')}> + + + Exit + + + + Profil + + + + + + + + + Id. Spotify + {userCurrent.idSpotify} + + + Identifiant + + {username.length >= 5 && ( + + + + )} + + + Mail + + {email.length >= 7 && ( + + + + )} + + + + + + + + + + + + Modifier le mot de passe + + + + + + + + + + + Changer de compte Spotify + + + + + + + + + + + + Supprimer le compte + + + + + + + + Annuler + + + Mot de passe + + + = 6 && newPassword === confirmPassword && oldPassword.length >= 6 ? '#1c77fb' : '#404040', + }]}>Modifier + + + + + + Ancien + + + + Nouveau + + + + Confirmer + + + + + Votre mot de passe doit comporter au moins 6 caractères. + + + + + + + + ); +}; \ No newline at end of file diff --git a/src/FLAD/screens/Register.tsx b/src/FLAD/screens/RegisterScreen.tsx similarity index 62% rename from src/FLAD/screens/Register.tsx rename to src/FLAD/screens/RegisterScreen.tsx index 3d1d85e..8dc64a5 100644 --- a/src/FLAD/screens/Register.tsx +++ b/src/FLAD/screens/RegisterScreen.tsx @@ -1,17 +1,13 @@ -import React, { useEffect, useState } from 'react'; -import { View, Image, StyleSheet, Text, ImageBackground, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity, Platform } from 'react-native'; +import React, { useState } from 'react'; +import { Alert, View, Image, StyleSheet, Text, ImageBackground, TextInput, TouchableWithoutFeedback, Keyboard, TouchableOpacity } 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 { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; -import { registerUser } from '../redux/thunk/authThunk'; +import { register } from '../redux/thunk/authThunk'; import { useDispatch, useSelector } from 'react-redux'; import { Audio } from 'expo-av'; -import { CredentialsRegister } from '../redux/actions/userActions'; -import * as WebBrowser from 'expo-web-browser'; -import { spotArray2 } from '../data/data'; -import { setSpotList } from '../redux/actions/spotActions'; +import { RegisterCredentials } from '../redux/actions/userActions'; +import configs from '../constants/config'; // @ts-ignore const DismissKeyboard = ({ children }) => ( @@ -20,130 +16,133 @@ const DismissKeyboard = ({ children }) => ( ) -export const MY_SECURE_AUTH_STATE_KEY = 'MySecureAuthStateKeySpotify'; -export const MY_SECURE_AUTH_STATE_KEY_REFRESH = 'MySecureAuthStateKeySpotifyREFRESH'; - -WebBrowser.maybeCompleteAuthSession(); - -// save the spotifyToken -async function save(key: string, value: string) { - await SecureStore.setItemAsync(key, value); -} -export default function InscriptionPage() { - const [sound, setSound] = useState(); +export default function RegisterScreen() { const navigation = useNavigation(); - const [spotifyToken, setSpotifyToken] = useState(''); + const [spotifyToken, setSpotifyToken] = useState(); + const [username, setUsername] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const dispatch = useDispatch(); // @ts-ignore const failedSignup = useSelector(state => state.userReducer.failedSignup); + // @ts-ignore + const errorMessage = useSelector(state => state.userReducer.errorMessage); async function playSound() { const { sound } = await Audio.Sound.createAsync( - require('../assets/sounds/Click.mp3') + require('../assets/sounds/click.mp3') ); - setSound(sound); await sound.playAsync(); } - const [username, setUsername] = useState(''); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); + const submitForm = () => { + const isUsernameValid = /^\w+$/.test(username); + const isEmailValid = /^\w+@\w+\.[^\s@]+$/.test(email); - const dispatch = useDispatch(); + if (username.length > 30) { + Alert.alert("Erreur inscription", "Le nom d'utilisateur ne peut pas être plus grand que 30 caractères."); + return; + } + if (username == "" || username == null) { + Alert.alert("Erreur inscription", "Le nom d'utilisateur ne peut pas être vide."); + return; + } + if (!isUsernameValid) { + Alert.alert("Erreur inscription", "Le nom d'utilisateur ne peut pas posséder de caractères spéciaux."); + return; + } + if (email.length > 100) { + Alert.alert("Erreur inscription", "L'adresse e-mail ne peut pas être plus grand que 100 caractères."); + return; + } + if (!isEmailValid) { + Alert.alert("Erreur inscription", "L'adresse e-mail n\'est pas valide."); + return; + } + if (password.length < 6) { + Alert.alert("Erreur inscription", "Le mot de passe doit avoir au moins 6 caractères"); + return; + } - function addMockSpots() { - dispatch(setSpotList(spotArray2)) - } + if (spotifyToken == null || spotifyToken == "") { + Alert.alert("Erreur inscription", "Pour vous inscrire, veuillez vous connecter à Spotify."); + return; + } - const submitForm = () => { - const credentials: CredentialsRegister = { + const credentials: RegisterCredentials = { email: email, password: password, - idSpotify: spotifyToken, - name: username, - idFlad: generateRandomString() + tokenSpotify: spotifyToken, + name: username }; //@ts-ignore - dispatch(registerUser(credentials)) - addMockSpots() + dispatch(register(credentials)) playSound() } - function generateRandomString(): string { - const alphabet = 'abcdefghijklmnopqrstuvwxyz'; - let randomString = ''; - - for (let i = 0; i < 8; i++) { - const randomIndex = Math.floor(Math.random() * alphabet.length); - const randomChar = alphabet[randomIndex]; - randomString += randomChar; - } - - return randomString; - } - - const getTokens2 = async () => { + const getSpotifyToken = async () => { try { const redirectUri = AuthSession.makeRedirectUri(); - const result = await AuthSession.startAsync({ - authUrl: 'https://flad-api-production.up.railway.app/api/spotify/exchange?' + '&redirectUrl=' + - encodeURIComponent(redirectUri) + const result: any = await AuthSession.startAsync({ + authUrl: configs.API_URL + '/spotify/exchange?' + 'redirectUrl=' + + encodeURIComponent(redirectUri) }) const { - access_token: access_token, refresh_token: refresh_token, } = result.params - save(MY_SECURE_AUTH_STATE_KEY, access_token); - setSpotifyToken(access_token) - save(MY_SECURE_AUTH_STATE_KEY_REFRESH, refresh_token); - } catch (err) { - console.error(err); + setSpotifyToken(refresh_token) + } catch (error) { + Alert.alert("Erreur inscription", "La connexion à Spotify à échouer."); + return; } } - return ( - + v2.0 - + S'INSCRIRE {failedSignup && ( - Email ou mot de passe incorrect! + {errorMessage} )} - + - + - + { - await getTokens2(); + await getSpotifyToken(); }} style={[styles.buttonSpotify, styles.shadow]}> Lier compte - + {spotifyToken == null ? ( + + ) : + + } - submitForm()}> - + Tu as déjà un compte? diff --git a/src/FLAD/screens/SettingProfil.tsx b/src/FLAD/screens/SettingProfil.tsx deleted file mode 100644 index 81d75e8..0000000 --- a/src/FLAD/screens/SettingProfil.tsx +++ /dev/null @@ -1,364 +0,0 @@ -import React, { useEffect } from 'react'; -import { View, Text, StyleSheet, TouchableWithoutFeedback, Keyboard, ScrollView, Image } from 'react-native'; -import { TextInput, TouchableOpacity } from 'react-native-gesture-handler'; -import { Svg, Path } from 'react-native-svg'; -import Modal from "react-native-modal"; -import { useNavigation } from "@react-navigation/native"; -import { useSelector } from 'react-redux'; - -import normalize from '../components/Normalize'; -import * as ImagePicker from 'expo-image-picker'; -import { SafeAreaView } from 'react-native-safe-area-context'; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; - -// @ts-ignore -const DismissKeyboard = ({ children }) => ( - Keyboard.dismiss()}> - {children} - -) - - -export default function SettingProfil() { - // @ts-ignore - const isDark = useSelector(state => state.userReducer.dark); - // @ts-ignore - const UserCurrent = useSelector(state => state.userReducer.user); - const style = isDark ? GraphicalCharterDark : GraphicalCharterLight; - const navigation = useNavigation(); - const [isModalVisible, setIsModalVisible] = React.useState(false); - - useEffect(() => { - console.log(UserCurrent.image); - }); - - const handleModal = () => setIsModalVisible(() => !isModalVisible); - - - const pickImage = async () => { - await ImagePicker.launchImageLibraryAsync({ - mediaTypes: ImagePicker.MediaTypeOptions.All, - allowsEditing: true, - aspect: [4, 3], - quality: 1, - }); - }; - - const styles = StyleSheet.create({ - mainSafeArea: { - flex: 1, - backgroundColor: style.body, - }, - container: { - marginTop: 20, - marginHorizontal: normalize(25), - flex: 1, - backgroundColor: style.body, - }, - buttonSetting: { - width: normalize(17), - height: normalize(17), - marginRight: 5 - }, - modalContent: { - position: 'absolute', - top: '5%', - left: '-5%', - right: '-5%', - height: '100%', - backgroundColor: style.body, - borderRadius: 12 - }, - modalView: { - flexDirection: 'row', - marginTop: 20, - marginLeft: 30, - marginBottom: normalize(45) - }, - exit: { - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center' - }, - textExit: { - fontSize: normalize(20), - color: '#454545', - fontWeight: 'bold' - }, - profilHead: { - alignItems: 'center', - }, - title: { - fontSize: normalize(30), - fontWeight: 'bold', - color: style.Text, - }, - imageWrapper: { - width: 126, - height: 126, - borderRadius: 63, - borderWidth: 3, - borderColor: style.Text, - overflow: 'hidden', - marginVertical: 20, - alignItems: 'center', - justifyContent: 'center', - }, - imageProfil: { - width: 120, - height: 120, - }, - editButton: { - width: 50, - height: 50, - borderRadius: 25, - backgroundColor: '#7C7C7C', - alignItems: 'center', - justifyContent: 'center' - }, - body: { - paddingVertical: 9, - paddingLeft: normalize(10), - backgroundColor: style.Card, - borderRadius: 13, - alignItems: 'flex-start', - marginBottom: normalize(45) - }, - textOption: { - fontSize: normalize(18), - color: style.Text, - fontWeight: 'bold', - marginLeft: 12 - }, - deleteOption: { - paddingVertical: 9, - paddingLeft: 5, - backgroundColor: style.Card, - borderRadius: 13, - flexDirection: 'row', - alignItems: 'center', - }, - textOptionPassword: { - fontSize: normalize(18), - color: '#1c77fb', - marginLeft: 12 - }, - buttonDeleteOption: { - backgroundColor: '#DF0404', - padding: 5, - borderRadius: 10, - marginLeft: 15 - }, - textDeleteOption: { - fontSize: normalize(18), - color: '#F80404', - marginLeft: 12 - }, - optionId: { - flexDirection: 'row', - marginBottom: 20, - }, - optionMail: { - flexDirection: 'row', - }, - textInputId: { - marginLeft: 50, - width: '57%', - color: style.Text, - fontSize: normalize(18), - }, - textInputMail: { - marginLeft: 100, - color: style.Text, - width: '57%', - fontSize: normalize(18) - }, - passwordOption: { - paddingVertical: 9, - paddingLeft: normalize(10), - backgroundColor: style.Card, - borderRadius: 13, - alignItems: 'flex-start', - marginBottom: normalize(45) - }, - passwordIcon: { - backgroundColor: '#8e8d92', - padding: 5, - paddingHorizontal: 8, - borderRadius: 10, - marginLeft: 10 - }, - optionView: { - flexDirection: 'row', - marginTop: 5 - }, - cancelText: { - fontSize: normalize(20), - color: '#1c77fb' - }, - updateText: { - marginLeft: 60, - fontSize: normalize(20), - color: '#404040' - }, - titlePassword: { - fontSize: normalize(22), - color: style.Text, - marginLeft: 50 - }, - warning: { - color: '#98989f', - fontSize: normalize(15) - }, - warningView: { - marginTop: 10, - paddingHorizontal: 40 - }, - bodyModal: { - paddingVertical: 12, - paddingLeft: 30, - marginHorizontal: normalize(25), - backgroundColor: style.Card, - borderRadius: 13, - alignItems: 'flex-start' - }, - optionModalWithUnderline: { - flexDirection: 'row', - borderBottomWidth: 1, - borderColor: style.Line, - paddingBottom: 10, - marginBottom: 10 - }, - optionModal: { - flexDirection: 'row' - }, - textOptionModal: { - fontSize: normalize(18), - color: style.Text, - fontWeight: 'bold', - }, - textInputNewModal: { - marginLeft: 40, - color: style.Text, - width: '67.5%', - fontSize: normalize(18) - }, - textInputConfirmModal: { - marginLeft: 30, - color: style.Text, - fontSize: normalize(18) - }, - textInputOldModal: { - marginLeft: 55, - color: style.Text, - width: '67.5%', - fontSize: normalize(18) - } - }) - - return ( - - - - - navigation.navigate('Setting')}> - - - Exit - - - - Profil - - - - - - - - - - - - Identifiant - - - - Mail - - - - - - - - - - - - - Modifier le mot de passe - - - - - - - - - - - - - - - - console.log("Tkt t deconnecter")}> - Supprimer le compte - - - - - - - - Annuler - - - Mot de passe - - - Modifier - - - - - - Ancien - - - - Nouveau - - - - Confirmer - - - - - Votre mot de passe doit comporter au moins 8 caractères, dont au moins un chiffre, une majuscule et une minuscule. - - - - - - - - ); -}; \ No newline at end of file diff --git a/src/FLAD/screens/Setting.tsx b/src/FLAD/screens/SettingScreen.tsx similarity index 80% rename from src/FLAD/screens/Setting.tsx rename to src/FLAD/screens/SettingScreen.tsx index 75ec7fd..95b33a8 100644 --- a/src/FLAD/screens/Setting.tsx +++ b/src/FLAD/screens/SettingScreen.tsx @@ -1,15 +1,18 @@ -import React, { useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import { View, StyleSheet, Text, Image, TouchableWithoutFeedback, Keyboard, TouchableOpacity, SafeAreaView } from 'react-native'; +import { View, StyleSheet, Text, Image, TouchableWithoutFeedback, Keyboard, TouchableOpacity, Alert } from 'react-native'; import { Svg, Path } from 'react-native-svg'; import { useNavigation } from "@react-navigation/native"; import { useDispatch, useSelector } from 'react-redux'; import normalize from '../components/Normalize'; import { ScrollView, Switch, TextInput } from 'react-native-gesture-handler'; -import CardMusic from '../components/CardMusic'; -import { ChangeMode, DeleteToken } from '../redux/thunk/authThunk'; -import { GraphicalCharterDark } from '../assets/GraphicalCharterDark'; -import { GraphicalCharterLight } from '../assets/GraphicalCharterLight'; +import CardMusic from '../components/CardMusicComponent'; +import { logout } from '../redux/thunk/authThunk'; +import { darkMode } from '../redux/thunk/userThunk'; +import { LightTheme, DarkTheme, Theme } from "../constants/colors"; +import { User } from '../models/User'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import * as Location from 'expo-location'; // @ts-ignore const DismissKeyboard = ({ children }) => ( @@ -18,7 +21,7 @@ const DismissKeyboard = ({ children }) => ( ) -export default function Setting() { +export default function SettingScreen() { const textInputRef = useRef(null); const dispatch = useDispatch(); const navigation = useNavigation(); @@ -31,50 +34,60 @@ export default function Setting() { const currentMusic = useSelector(state => state.appReducer.userCurrentMusic); // @ts-ignore - const isDark = useSelector(state => state.userReducer.dark); + const currentUser: User = useSelector(state => state.userReducer.user); - const style = isDark ? GraphicalCharterDark : GraphicalCharterLight; + // @ts-ignore + const isDark = useSelector(state => state.userReducer.dark); + const [locationPermission, setLocationPermission] = useState(false); + const style: Theme = isDark ? DarkTheme : LightTheme; async function ChangeDarkMode() { - try { - const currentValue = await AsyncStorage.getItem('dark'); - if (currentValue !== null) { - const newValue = JSON.stringify(!JSON.parse(currentValue)); - await AsyncStorage.setItem('dark', newValue); - // @ts-ignore - dispatch(ChangeMode(JSON.parse(newValue))) - } - } catch (error) { - console.log(`Une erreur s'est produite lors de la mise à jour de la valeur booléenne pour la clé 'dark': `, error); + const newValue = !isDark; + await AsyncStorage.setItem('dark', newValue.toString()); + // @ts-ignore + dispatch(darkMode(JSON.parse(newValue))) + } + + const checkLocationPermission = async () => { + const { status } = await Location.getForegroundPermissionsAsync(); + if (status !== 'granted') { + setLocationPermission(false); + } else { + setLocationPermission(true); } } - //Notification + useEffect(() => { + checkLocationPermission(); + }, []); + const [isCheckedNotif, setIsCheckedNotif] = useState(false); const toggleNotif = () => setIsCheckedNotif(value => !value); - //Deconnection const Deconnection = () => { //@ts-ignore - dispatch(DeleteToken()) + dispatch(logout()); } - //Localisation - const [isCheckedLocalisation, setIsCheckedLocalisation] = useState(false); const toggleLocalisation = - () => setIsCheckedLocalisation(value => !value); + () => { + Alert.alert( + 'Localisation', + "Pour changer la permission de localisation, veuillez aller dans les paramètres de votre téléphone et ajuster les autorisations pour l'application." + ); + }; + const insets = useSafeAreaInsets(); - //Style const styles = StyleSheet.create({ mainSafeArea: { flex: 1, backgroundColor: style.body, }, container: { - marginTop: 30, + marginTop: insets.top + 30, marginHorizontal: normalize(25), paddingBottom: normalize(400), flex: 1, @@ -97,7 +110,7 @@ export default function Setting() { inputSearch: { placeholderTextColor: 'red', color: style.Text, - width: normalize(350), + width: "80%", }, profil: { paddingVertical: 9, @@ -110,6 +123,7 @@ export default function Setting() { imageProfil: { marginLeft: 15, marginRight: 7, + borderRadius: 8, width: 50, height: 50 }, @@ -129,8 +143,8 @@ export default function Setting() { justifyContent: 'center', }, buttonSetting: { - width: normalize(17), - height: normalize(17), + width: normalize(11), + height: normalize(18), marginRight: 22 }, body: { @@ -239,17 +253,24 @@ export default function Setting() { marginBottom: 5 }, mascot: { - width: normalize(90), - height: normalize(90), + width: normalize(70), + height: normalize(70), position: 'absolute', right: normalize(0), - top: normalize(10) + top: normalize(20) + }, + creationDateText: { + marginTop: 10, + fontSize: normalize(13), + fontWeight: '700', + color: style.Text, + opacity: 0.4 } }) return ( - + Réglages @@ -260,7 +281,7 @@ export default function Setting() { - + @@ -268,25 +289,24 @@ export default function Setting() { navigation.navigate('SettingProfil')} + onPress={() => navigation.navigate('Account')} > - + - Emre KARTAL + {currentUser.name.charAt(0).toUpperCase() + currentUser.name.slice(1)} id. Spotify, mail et mot de passe - + - - - - + + + @@ -296,7 +316,7 @@ export default function Setting() { - + @@ -309,14 +329,14 @@ export default function Setting() { - + Localisation - + @@ -334,15 +354,15 @@ export default function Setting() { - - + + ) : <>} - + @@ -351,9 +371,18 @@ export default function Setting() { Se deconnecter + + Compte créer le {currentUser.creationDate.toLocaleString('fr-FR', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + })} + - + ); }; diff --git a/src/FLAD/screens/SpotDetailsPage.tsx b/src/FLAD/screens/SpotDetailsPage.tsx deleted file mode 100644 index 6ab1a62..0000000 --- a/src/FLAD/screens/SpotDetailsPage.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { View, Text, StyleSheet, Button, TouchableOpacity, SafeAreaView } from "react-native"; -import Animated, { interpolate, SensorType, useAnimatedSensor, useAnimatedStyle, useSharedValue, withSpring } from "react-native-reanimated"; -import { Audio } from 'expo-av'; -import { useEffect, useState } from "react"; -import { theService } from "../App"; - -const halfPi = Math.PI / 2; - -//@ts-ignore -const SpotDetailsPage = ({ route }) => { - console.log(route); - - const spot: { name: string, sourceUrl: string, index: number } = route.params.spot; - const [currentspot] = 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 trackPreviewUrl = 'https://p.scdn.co/mp3-preview/08ef3b9d6dbd6bab233f5e9ca564091902767f71?cid=774b29d4f13844c495f206cafdad9c86'; - - - 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 ? () => { - //@ts-ignore - sound.unloadAsync(); - } - : undefined; - }, [sound]); - - const sensor = useAnimatedSensor(SensorType.ROTATION); - const styleAniamatedImage = useAnimatedStyle(() => { - const { 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 id = '0cFS3AMF9Lhj3CNoFvwjvY' - - const getCurrentTrack = async () => { - theService.getMusicById(id); - } - - return ( - - - - - -