From cdca0d4942405bd01621109aa64fe7d2f8f63d4b Mon Sep 17 00:00:00 2001 From: Louis Auzuret Date: Mon, 20 Jun 2022 17:10:07 +0200 Subject: [PATCH 01/15] fix: auth robot test --- back/src/auth/auth.controller.ts | 12 ++++- back/src/auth/auth.service.ts | 2 +- back/test/robot/.gitignore | 4 ++ back/test/robot/auth/auth.robot | 83 +++++++++++++++++++++++++++++++ back/test/robot/rest.resource | 2 +- back/test/robot/users/users.robot | 14 ++++++ docker-compose.yml | 2 +- 7 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 back/test/robot/.gitignore create mode 100644 back/test/robot/auth/auth.robot create mode 100644 back/test/robot/users/users.robot diff --git a/back/src/auth/auth.controller.ts b/back/src/auth/auth.controller.ts index ea7e3a0..59a4430 100644 --- a/back/src/auth/auth.controller.ts +++ b/back/src/auth/auth.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Request, Post, Get, UseGuards, Res, Body } from '@nestjs/common'; +import { Controller, Request, Post, Get, UseGuards, Res, Body, Delete } from '@nestjs/common'; import { AuthService } from './auth.service'; import { JwtAuthGuard } from './jwt-auth.guard'; import { LocalAuthGuard } from './local-auth.guard'; @@ -40,4 +40,14 @@ export class AuthController { getProfile(@Request() req) { return req.user; } + + @UseGuards(JwtAuthGuard) + @ApiBearerAuth() + @ApiOkResponse({ description: 'Successfully deleted' }) + @ApiUnauthorizedResponse({ description: 'Invalid token' }) + @Delete('me') + deleteSelf(@Request() req) { + return this.usersService.deleteUser({"id": req.user.id}) + } + } diff --git a/back/src/auth/auth.service.ts b/back/src/auth/auth.service.ts index 44831f1..7cca4b1 100644 --- a/back/src/auth/auth.service.ts +++ b/back/src/auth/auth.service.ts @@ -12,7 +12,7 @@ export class AuthService { ) {} async validateUser(username: string, password: string): Promise { - const user = await this.userService.user({username}); + const user = await this.userService.user({username}); if (user && bcrypt.compareSync(password, user.password)) { return { username: user.username, diff --git a/back/test/robot/.gitignore b/back/test/robot/.gitignore new file mode 100644 index 0000000..73a37df --- /dev/null +++ b/back/test/robot/.gitignore @@ -0,0 +1,4 @@ +log.html +output.xml +report.html +env diff --git a/back/test/robot/auth/auth.robot b/back/test/robot/auth/auth.robot new file mode 100644 index 0000000..ccb9fb2 --- /dev/null +++ b/back/test/robot/auth/auth.robot @@ -0,0 +1,83 @@ +*** Settings *** +Documentation Tests of the /auth route. +... Ensures that the user can authenticate on kyoo. +Resource ../rest.resource + + +*** Keywords *** +Login + [Documentation] Shortcut to login with the given username for future requests + [Arguments] ${username} + &{res}= POST /auth/login {"username": "${username}", "password": "password-${username}"} + Output + Integer response status 201 + String response body access_token + Set Headers {"Authorization": "Bearer ${res.body.access_token}"} + +Register + [Documentation] Shortcut to register with the given username for future requests + [Arguments] ${username} + &{res}= POST + ... /auth/register + ... {"username": "${username}", "password": "password-${username}", "email": "${username}@chromacase.moe"} + Output + Integer response status 200 + +Logout + [Documentation] Logout the current user, only the local client is affected. + Set Headers {"Authorization": ""} + + +*** Test Cases *** +Me cant be accessed without an account + Get /auth/me + Output + Integer response status 401 + +Bad Account + [Documentation] Login fails if user does not exist + POST /auth/login {"username": "i-don-t-exist", "password": "pass"} + Output + Integer response status 401 + +RegisterAndLogin + [Documentation] Create a new user and login in it + Register user-1 + Login user-1 + [Teardown] DELETE /auth/me + +Register Duplicates + [Documentation] If two users tries to register with the same username, it fails + Register user-duplicate + # We can't use the `Register` keyword because it assert for success + POST /auth/register {"username": "user-duplicate", "password": "pass", "email": "mail@kyoo.moe"} + Output + Integer response status 400 + Login user-duplicate + [Teardown] DELETE /auth/me + +Delete Account + [Documentation] Check if a user can delete it's account + Register I-should-be-deleted + Login I-should-be-deleted + DELETE /auth/me + Output + Integer response status 200 + +Login + [Documentation] Create a new user and login in it + Register login-user + Login login-user + ${res}= GET /auth/me + Output + Integer response status 200 + String response body username login-user + + Logout + Login login-user + ${me}= Get /auth/me + Output + Output ${me} + Should Be Equal As Strings ${res["body"]} ${me["body"]} + + [Teardown] DELETE /auth/me diff --git a/back/test/robot/rest.resource b/back/test/robot/rest.resource index 41db3c9..42db9ed 100644 --- a/back/test/robot/rest.resource +++ b/back/test/robot/rest.resource @@ -1,4 +1,4 @@ *** Settings *** Documentation Common things to handle rest requests -Library REST http://localhost:3000/api +Library REST http://localhost:3000 diff --git a/back/test/robot/users/users.robot b/back/test/robot/users/users.robot new file mode 100644 index 0000000..45135e6 --- /dev/null +++ b/back/test/robot/users/users.robot @@ -0,0 +1,14 @@ +*** Settings *** +Documentation Tests of the /users route. +... Ensures that the users CRUD works corectly. +Resource ../rest.resource + + +*** Keywords *** +*** Test Cases *** +Create a user + [Documentation] Create a user + POST /users {"username": "i-don-t-exist", "password": "pass", "email": "wow@gmail.com"} + Output + Integer response status 201 + [Teardown] DELETE /users/1 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index ea0af46..a80b7bf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: back: - build: ./back + build: ./back ports: - "3000:3000" depends_on: From a897d7693c177ecb4b885636e019a473a80f7b05 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 26 Sep 2022 22:48:26 +0900 Subject: [PATCH 02/15] Add lessons on the API. (#78) --- .editorconfig | 9 ++ .env.example | 6 + .gitignore | 5 +- back/Dockerfile.dev | 3 - back/nest-cli.json | 15 ++- back/package-lock.json | 35 +++++- back/package.json | 3 +- .../migrations/20220924080202_/migration.sql | 26 +++++ .../migrations/20220924081654_/migration.sql | 28 +++++ .../migrations/20220926065731_/migration.sql | 10 ++ back/prisma/migrations/migration_lock.toml | 3 + back/prisma/schema.prisma | 45 +++++++- back/src/app.module.ts | 7 +- back/src/auth/auth.service.ts | 16 +-- back/src/auth/local.strategy.ts | 26 +++-- back/src/lesson/lesson.controller.spec.ts | 18 +++ back/src/lesson/lesson.controller.ts | 103 ++++++++++++++++++ back/src/lesson/lesson.module.ts | 11 ++ back/src/lesson/lesson.service.spec.ts | 18 +++ back/src/lesson/lesson.service.ts | 41 +++++++ back/src/main.ts | 12 +- back/src/models/plage.ts | 51 +++++++++ back/src/users/users.service.ts | 91 ++++++++-------- back/test/robot/lesson/lesson.robot | 80 ++++++++++++++ back/test/robot/users/users.robot | 6 +- back/tsconfig.json | 4 +- node_modules/.yarn-integrity | 12 -- package-lock.json | 6 + 28 files changed, 591 insertions(+), 99 deletions(-) create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 back/prisma/migrations/20220924080202_/migration.sql create mode 100644 back/prisma/migrations/20220924081654_/migration.sql create mode 100644 back/prisma/migrations/20220926065731_/migration.sql create mode 100644 back/prisma/migrations/migration_lock.toml create mode 100644 back/src/lesson/lesson.controller.spec.ts create mode 100644 back/src/lesson/lesson.controller.ts create mode 100644 back/src/lesson/lesson.module.ts create mode 100644 back/src/lesson/lesson.service.spec.ts create mode 100644 back/src/lesson/lesson.service.ts create mode 100644 back/src/models/plage.ts create mode 100644 back/test/robot/lesson/lesson.robot delete mode 100644 node_modules/.yarn-integrity create mode 100644 package-lock.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b696824 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = tab +indent_size = tab diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7f48820 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_NAME= +POSTGRES_HOST= +DATABASE_URL= +JWT_SECRET= diff --git a/.gitignore b/.gitignore index c7564a0..0af2a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ pyvenv.cfg include .env prisma/migrations/* -.vscode \ No newline at end of file +.vscode +output.xml +report.html +log.html diff --git a/back/Dockerfile.dev b/back/Dockerfile.dev index d86196a..1d7d470 100644 --- a/back/Dockerfile.dev +++ b/back/Dockerfile.dev @@ -1,6 +1,3 @@ FROM node:17 WORKDIR /app -COPY ./package.json ./ -RUN npm install -COPY . . CMD npx prisma generate ; npx prisma migrate dev ; npm run start:dev diff --git a/back/nest-cli.json b/back/nest-cli.json index 2566481..a427513 100644 --- a/back/nest-cli.json +++ b/back/nest-cli.json @@ -1,5 +1,14 @@ { - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src" + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "plugins": [{ + "name": "@nestjs/swagger", + "options": { + "introspectComments": true, + "dtoFileNameSuffix": [] + } + }] + } } diff --git a/back/package-lock.json b/back/package-lock.json index c9fb02a..c4714a8 100644 --- a/back/package-lock.json +++ b/back/package-lock.json @@ -27,7 +27,8 @@ "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", - "rxjs": "^7.2.0" + "rxjs": "^7.2.0", + "swagger-ui-express": "^4.5.0" }, "devDependencies": { "@nestjs/cli": "^8.0.0", @@ -8194,6 +8195,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.14.0.tgz", + "integrity": "sha512-TBzhheU15s+o54Cgk9qxuYcZMiqSm/SkvKnapoGHOF66kz0Y5aGjpzj5BT/vpBbn6rTPJ9tUYXQxuDWfsjiGMw==" + }, + "node_modules/swagger-ui-express": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.5.0.tgz", + "integrity": "sha512-DHk3zFvsxrkcnurGvQlAcLuTDacAVN1JHKDgcba/gr2NFRE4HGwP1YeHIXMiGznkWR4AeS7X5vEblNn4QljuNA==", + "dependencies": { + "swagger-ui-dist": ">=4.11.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0" + } + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -15400,6 +15420,19 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "swagger-ui-dist": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.14.0.tgz", + "integrity": "sha512-TBzhheU15s+o54Cgk9qxuYcZMiqSm/SkvKnapoGHOF66kz0Y5aGjpzj5BT/vpBbn6rTPJ9tUYXQxuDWfsjiGMw==" + }, + "swagger-ui-express": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.5.0.tgz", + "integrity": "sha512-DHk3zFvsxrkcnurGvQlAcLuTDacAVN1JHKDgcba/gr2NFRE4HGwP1YeHIXMiGznkWR4AeS7X5vEblNn4QljuNA==", + "requires": { + "swagger-ui-dist": ">=4.11.0" + } + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", diff --git a/back/package.json b/back/package.json index ac6a010..773af23 100644 --- a/back/package.json +++ b/back/package.json @@ -39,7 +39,8 @@ "passport-local": "^1.0.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", - "rxjs": "^7.2.0" + "rxjs": "^7.2.0", + "swagger-ui-express": "^4.5.0" }, "devDependencies": { "@nestjs/cli": "^8.0.0", diff --git a/back/prisma/migrations/20220924080202_/migration.sql b/back/prisma/migrations/20220924080202_/migration.sql new file mode 100644 index 0000000..c51015f --- /dev/null +++ b/back/prisma/migrations/20220924080202_/migration.sql @@ -0,0 +1,26 @@ +-- CreateEnum +CREATE TYPE "DifficultyPoint" AS ENUM ('TwoHands', 'Rhythm', 'NoteCombo', 'Arpeggio', 'Distance', 'LeftHand', 'RightHand', 'LeadHandChange', 'ChordComplexity', 'ChordTiming', 'Length', 'PedalPoint', 'Precision'); + +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "username" TEXT NOT NULL, + "password" TEXT NOT NULL, + "email" TEXT NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Lesson" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL, + "requiredLvl" INTEGER NOT NULL, + "difficulyPoint" "DifficultyPoint" NOT NULL, + + CONSTRAINT "Lesson_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); diff --git a/back/prisma/migrations/20220924081654_/migration.sql b/back/prisma/migrations/20220924081654_/migration.sql new file mode 100644 index 0000000..0bc5d2e --- /dev/null +++ b/back/prisma/migrations/20220924081654_/migration.sql @@ -0,0 +1,28 @@ +/* + Warnings: + + - You are about to drop the column `difficulyPoint` on the `Lesson` table. All the data in the column will be lost. + - You are about to drop the column `requiredLvl` on the `Lesson` table. All the data in the column will be lost. + - Added the required column `mainSkill` to the `Lesson` table without a default value. This is not possible if the table is not empty. + - Added the required column `requiredLevel` to the `Lesson` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Lesson" DROP COLUMN "difficulyPoint", +DROP COLUMN "requiredLvl", +ADD COLUMN "mainSkill" "DifficultyPoint" NOT NULL, +ADD COLUMN "requiredLevel" INTEGER NOT NULL; + +-- CreateTable +CREATE TABLE "LessonHistory" ( + "lessonID" INTEGER NOT NULL, + "userID" INTEGER NOT NULL, + + CONSTRAINT "LessonHistory_pkey" PRIMARY KEY ("lessonID","userID") +); + +-- AddForeignKey +ALTER TABLE "LessonHistory" ADD CONSTRAINT "LessonHistory_userID_fkey" FOREIGN KEY ("userID") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LessonHistory" ADD CONSTRAINT "LessonHistory_lessonID_fkey" FOREIGN KEY ("lessonID") REFERENCES "Lesson"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926065731_/migration.sql b/back/prisma/migrations/20220926065731_/migration.sql new file mode 100644 index 0000000..5d85f48 --- /dev/null +++ b/back/prisma/migrations/20220926065731_/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `title` on the `Lesson` table. All the data in the column will be lost. + - Added the required column `name` to the `Lesson` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Lesson" DROP COLUMN "title", +ADD COLUMN "name" TEXT NOT NULL; diff --git a/back/prisma/migrations/migration_lock.toml b/back/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/back/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/back/prisma/schema.prisma b/back/prisma/schema.prisma index 3f22507..579d951 100644 --- a/back/prisma/schema.prisma +++ b/back/prisma/schema.prisma @@ -10,8 +10,45 @@ datasource db { } model User { - id Int @default(autoincrement()) @id - username String @unique - password String - email String + id Int @id @default(autoincrement()) + username String @unique + password String + email String + LessonHistory LessonHistory[] +} + +model Lesson { + id Int @id @default(autoincrement()) + name String + description String + requiredLevel Int + mainSkill Skill + LessonHistory LessonHistory[] +} + +model LessonHistory { + lesson Lesson @relation(fields: [lessonID], references: [id]) + lessonID Int + user User @relation(fields: [userID], references: [id]) + userID Int + + @@id([lessonID, userID]) +} + +enum Skill { + TwoHands + Rhythm + NoteCombo + Arpeggio + Distance + LeftHand + RightHand + LeadHandChange + ChordComplexity + ChordTiming + Length + PedalPoint + Precision + + @@map("DifficultyPoint") } diff --git a/back/src/app.module.ts b/back/src/app.module.ts index 6b93fd1..d87e0cb 100644 --- a/back/src/app.module.ts +++ b/back/src/app.module.ts @@ -5,10 +5,11 @@ import { PrismaService } from './prisma/prisma.service'; import { UsersModule } from './users/users.module'; import { PrismaModule } from './prisma/prisma.module'; import { AuthModule } from './auth/auth.module'; +import { LessonModule } from './lesson/lesson.module'; @Module({ - imports: [UsersModule, PrismaModule, AuthModule], - controllers: [AppController], - providers: [AppService, PrismaService], + imports: [UsersModule, PrismaModule, AuthModule, LessonModule], + controllers: [AppController], + providers: [AppService, PrismaService], }) export class AppModule {} diff --git a/back/src/auth/auth.service.ts b/back/src/auth/auth.service.ts index 7cca4b1..1568cd8 100644 --- a/back/src/auth/auth.service.ts +++ b/back/src/auth/auth.service.ts @@ -1,22 +1,24 @@ -import { Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable } from '@nestjs/common'; import { UsersService } from '../users/users.service'; import { JwtService } from '@nestjs/jwt'; import * as bcrypt from 'bcryptjs'; import PayloadInterface from './interface/payload.interface'; @Injectable() export class AuthService { - constructor( private userService: UsersService, - private jwtService: JwtService + private jwtService: JwtService, ) {} - async validateUser(username: string, password: string): Promise { - const user = await this.userService.user({username}); + async validateUser( + username: string, + password: string, + ): Promise { + const user = await this.userService.user({ username }); if (user && bcrypt.compareSync(password, user.password)) { return { username: user.username, - id: user.id + id: user.id, }; } return null; @@ -26,7 +28,7 @@ export class AuthService { const payload = { username: user.username, id: user.id }; const access_token = this.jwtService.sign(payload); return { - access_token + access_token, }; } } diff --git a/back/src/auth/local.strategy.ts b/back/src/auth/local.strategy.ts index efa05fb..c9d1cce 100644 --- a/back/src/auth/local.strategy.ts +++ b/back/src/auth/local.strategy.ts @@ -1,4 +1,3 @@ - import { Strategy } from 'passport-local'; import { PassportStrategy } from '@nestjs/passport'; import { Injectable, UnauthorizedException } from '@nestjs/common'; @@ -7,15 +6,18 @@ import PayloadInterface from './interface/payload.interface'; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { - constructor(private authService: AuthService) { - super(); - } + constructor(private authService: AuthService) { + super(); + } - async validate(username: string, password: string): Promise { - const user = await this.authService.validateUser(username, password); - if (!user) { - throw new UnauthorizedException(); - } - return user; - } -} \ No newline at end of file + async validate( + username: string, + password: string, + ): Promise { + const user = await this.authService.validateUser(username, password); + if (!user) { + throw new UnauthorizedException(); + } + return user; + } +} diff --git a/back/src/lesson/lesson.controller.spec.ts b/back/src/lesson/lesson.controller.spec.ts new file mode 100644 index 0000000..9f87a9d --- /dev/null +++ b/back/src/lesson/lesson.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { LessonController } from './lesson.controller'; + +describe('LessonController', () => { + let controller: LessonController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [LessonController], + }).compile(); + + controller = module.get(LessonController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/back/src/lesson/lesson.controller.ts b/back/src/lesson/lesson.controller.ts new file mode 100644 index 0000000..1b54bff --- /dev/null +++ b/back/src/lesson/lesson.controller.ts @@ -0,0 +1,103 @@ +import { + Controller, + Get, + Res, + Query, + Req, + Request, + Param, + ParseIntPipe, + DefaultValuePipe, + BadRequestException, + Post, + Body, + Delete, + NotFoundException, +} from '@nestjs/common'; +import { Plage } from 'src/models/plage'; +import { LessonService } from './lesson.service'; +import { ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger'; +import { Prisma, Skill } from '@prisma/client'; + +export class Lesson { + @ApiProperty() + id: number; + @ApiProperty() + name: string; + @ApiProperty() + description: string; + @ApiProperty() + requiredLevel: number; + @ApiProperty() + mainSkill: Skill; +} + +@ApiTags('lessons') +@Controller('lesson') +export class LessonController { + constructor(private lessonService: LessonService) {} + + @ApiOperation({ + summary: 'Get all lessons', + }) + @Get() + async getAll( + @Req() request: Request, + @Query() filter: Prisma.LessonWhereInput, + @Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip: number, + @Query('take', new DefaultValuePipe(20), ParseIntPipe) take: number, + ): Promise> { + try { + const ret = await this.lessonService.getAll({ + skip, + take, + where: { + ...filter, + requiredLevel: filter.requiredLevel + ? +filter.requiredLevel + : undefined, + }, + }); + return new Plage(ret, request); + } catch (e) { + console.log(e); + throw new BadRequestException(null, e?.toString()); + } + } + + @ApiOperation({ + summary: 'Get a particular lessons', + }) + @Get(':id') + async get(@Param('id', ParseIntPipe) id: number): Promise { + const ret = await this.lessonService.get(id); + if (!ret) throw new NotFoundException(); + return ret; + } + + @ApiOperation({ + summary: 'Create a lessons', + }) + @Post() + async post(@Body() lesson: Lesson): Promise { + try { + return await this.lessonService.create(lesson); + } catch (e) { + console.log(e); + throw new BadRequestException(null, e.toString()); + } + } + + @ApiOperation({ + summary: 'Delete a lessons', + }) + @Delete(':id') + async delete(@Param('id', ParseIntPipe) id: number): Promise { + try { + return await this.lessonService.delete(id); + } catch (e) { + console.log(e); + throw new BadRequestException(null, e.toString()); + } + } +} diff --git a/back/src/lesson/lesson.module.ts b/back/src/lesson/lesson.module.ts new file mode 100644 index 0000000..66799da --- /dev/null +++ b/back/src/lesson/lesson.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { LessonController } from './lesson.controller'; +import { LessonService } from './lesson.service'; + +@Module({ + imports: [PrismaModule], + controllers: [LessonController], + providers: [LessonService], +}) +export class LessonModule {} diff --git a/back/src/lesson/lesson.service.spec.ts b/back/src/lesson/lesson.service.spec.ts new file mode 100644 index 0000000..c857a0f --- /dev/null +++ b/back/src/lesson/lesson.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { LessonService } from './lesson.service'; + +describe('LessonService', () => { + let service: LessonService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [LessonService], + }).compile(); + + service = module.get(LessonService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/back/src/lesson/lesson.service.ts b/back/src/lesson/lesson.service.ts new file mode 100644 index 0000000..aed4a1e --- /dev/null +++ b/back/src/lesson/lesson.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import { Lesson, Prisma } from '@prisma/client'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Injectable() +export class LessonService { + constructor(private prisma: PrismaService) {} + + async getAll(params: { + skip?: number; + take?: number; + cursor?: Prisma.LessonWhereUniqueInput; + where?: Prisma.LessonWhereInput; + orderBy?: Prisma.LessonOrderByWithRelationInput; + }): Promise { + const { skip, take, cursor, where, orderBy } = params; + return this.prisma.lesson.findMany({ + skip, + take, + cursor, + where, + orderBy, + }); + } + + async get(id: number): Promise { + return this.prisma.lesson.findFirst({ + where: { + id: id, + }, + }); + } + + async create(lesson: Lesson): Promise { + return this.prisma.lesson.create({ data: lesson }); + } + + async delete(id: number): Promise { + return this.prisma.lesson.delete({ where: { id: id } }); + } +} diff --git a/back/src/main.ts b/back/src/main.ts index 0f304a2..8a0ebd4 100644 --- a/back/src/main.ts +++ b/back/src/main.ts @@ -1,11 +1,21 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { PrismaService } from './prisma/prisma.service'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; async function bootstrap() { const app = await NestFactory.create(AppModule); const prismaService = app.get(PrismaService); - await prismaService.enableShutdownHooks(app) + await prismaService.enableShutdownHooks(app); + + const config = new DocumentBuilder() + .setTitle('Chromacase') + .setDescription('The chromacase API') + .setVersion('1.0') + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); + await app.listen(3000); } bootstrap(); diff --git a/back/src/models/plage.ts b/back/src/models/plage.ts new file mode 100644 index 0000000..1da0f13 --- /dev/null +++ b/back/src/models/plage.ts @@ -0,0 +1,51 @@ +/* + * Thanks to https://github.com/Arthi-chaud/Meelo/blob/master/src/pagination/models/paginated-response.ts + */ + +import { ApiProperty } from '@nestjs/swagger'; + +export class Plage { + @ApiProperty() + metadata: { + this: string; + next: string | null; + previous: string | null; + }; + @ApiProperty() + data: T[]; + + constructor(data: T[], request: Request | any) { + this.data = data; + let take = Number(request.query['take'] ?? 20).valueOf(); + if (take == 0) take = 20; + let skipped: number = Number(request.query['skip'] ?? 0).valueOf(); + if (skipped % take) { + skipped += take - (skipped % take); + } + this.metadata = { + this: this.buildUrl(request.path, request.query), + next: + data.length >= take + ? this.buildUrl(request.path, { + ...request.query, + skip: skipped + take, + }) + : null, + previous: skipped + ? this.buildUrl(request.path, { + ...request.query, + skip: Math.max(0, skipped - take), + }) + : null, + }; + } + + private buildUrl(route: string, queryParameters: any) { + if (queryParameters.skip == 0) delete queryParameters.skip; + const builtQueryParameters = new URLSearchParams( + queryParameters, + ).toString(); + if (builtQueryParameters.length) return `${route}?${builtQueryParameters}`; + return route; + } +} diff --git a/back/src/users/users.service.ts b/back/src/users/users.service.ts index c167c87..df3a75d 100644 --- a/back/src/users/users.service.ts +++ b/back/src/users/users.service.ts @@ -7,55 +7,54 @@ import * as bcrypt from 'bcryptjs'; @Injectable() export class UsersService { - [x: string]: any; - constructor(private prisma: PrismaService) {} + constructor(private prisma: PrismaService) {} - async user( - userWhereUniqueInput: Prisma.UserWhereUniqueInput, - ): Promise { - return this.prisma.user.findUnique({ - where: userWhereUniqueInput, - }); - } + async user( + userWhereUniqueInput: Prisma.UserWhereUniqueInput, + ): Promise { + return this.prisma.user.findUnique({ + where: userWhereUniqueInput, + }); + } - async users(params: { - skip?: number; - take?: number; - cursor?: Prisma.UserWhereUniqueInput; - where?: Prisma.UserWhereInput; - orderBy?: Prisma.UserOrderByWithRelationInput; - }): Promise { - const { skip, take, cursor, where, orderBy } = params; - return this.prisma.user.findMany({ - skip, - take, - cursor, - where, - orderBy, - }); - } + async users(params: { + skip?: number; + take?: number; + cursor?: Prisma.UserWhereUniqueInput; + where?: Prisma.UserWhereInput; + orderBy?: Prisma.UserOrderByWithRelationInput; + }): Promise { + const { skip, take, cursor, where, orderBy } = params; + return this.prisma.user.findMany({ + skip, + take, + cursor, + where, + orderBy, + }); + } - async createUser(data: Prisma.UserCreateInput): Promise { - data.password = await bcrypt.hash(data.password, 8) - return this.prisma.user.create({ - data, - }); - } + async createUser(data: Prisma.UserCreateInput): Promise { + data.password = await bcrypt.hash(data.password, 8); + return this.prisma.user.create({ + data, + }); + } - async updateUser(params: { - where: Prisma.UserWhereUniqueInput; - data: Prisma.UserUpdateInput; - }): Promise { - const { where, data } = params; - return this.prisma.user.update({ - data, - where, - }); - } + async updateUser(params: { + where: Prisma.UserWhereUniqueInput; + data: Prisma.UserUpdateInput; + }): Promise { + const { where, data } = params; + return this.prisma.user.update({ + data, + where, + }); + } - async deleteUser(where: Prisma.UserWhereUniqueInput): Promise { - return this.prisma.user.delete({ - where, - }); - } + async deleteUser(where: Prisma.UserWhereUniqueInput): Promise { + return this.prisma.user.delete({ + where, + }); + } } diff --git a/back/test/robot/lesson/lesson.robot b/back/test/robot/lesson/lesson.robot new file mode 100644 index 0000000..9c72ff7 --- /dev/null +++ b/back/test/robot/lesson/lesson.robot @@ -0,0 +1,80 @@ +*** Settings *** +Documentation Tests of the /lesson route. +... Ensures that the lesson CRUD works corectly. + +Resource ../rest.resource + + +*** Test Cases *** +Post a lesson + [Documentation] Get a lesson + &{res}= POST + ... /lesson + ... {"name": "toto", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + [Teardown] DELETE /lesson/${res.body.id} + +Get a lesson + [Documentation] Get a lesson + &{res}= POST + ... /lesson + ... {"name": "toto", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + &{get}= GET /lesson/${res.body.id} + Output + Should Be Equal ${res.body} ${get.body} + [Teardown] DELETE /lesson/${res.body.id} + +Get a non-lesson + [Documentation] Get a lesson + &{get}= GET /lesson/toto + Output + Integer response status 400 + +Get a not-existing-lesson + [Documentation] Get a lesson + &{get}= GET /lesson/99999999 + Output + Integer response status 404 + +Get all lessons + [Documentation] Get a lesson + &{res}= POST + ... /lesson + ... {"name": "toto", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + &{res2}= POST + ... /lesson + ... {"name": "tata", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + &{get}= GET /lesson + Output + Should Contain ${get.body.data} ${res.body} + Should Contain ${get.body.data} ${res2.body} + + [Teardown] Run Keywords DELETE /lesson/${res.body.id} + ... AND DELETE /lesson/${res2.body.id} + +Get all lessons filtered + [Documentation] Get a lesson + &{res}= POST + ... /lesson + ... {"name": "toto", "requiredLevel": 3, "mainSkill": "TwoHands", "description": "What am i doing"} + Output + Integer response status 201 + &{res2}= POST + ... /lesson + ... {"name": "tata", "requiredLevel": 3, "mainSkill": "Distance", "description": "What am i doing"} + Output + Integer response status 201 + &{get}= GET /lesson?mainSkill=Distance + Output + Should Not Contain ${get.body.data} ${res.body} + Should Contain ${get.body.data} ${res2.body} + + [Teardown] Run Keywords DELETE /lesson/${res.body.id} + ... AND DELETE /lesson/${res2.body.id} diff --git a/back/test/robot/users/users.robot b/back/test/robot/users/users.robot index 45135e6..034d07d 100644 --- a/back/test/robot/users/users.robot +++ b/back/test/robot/users/users.robot @@ -1,14 +1,14 @@ *** Settings *** Documentation Tests of the /users route. ... Ensures that the users CRUD works corectly. + Resource ../rest.resource -*** Keywords *** *** Test Cases *** Create a user [Documentation] Create a user - POST /users {"username": "i-don-t-exist", "password": "pass", "email": "wow@gmail.com"} + &{res}= POST /users {"username": "louis-boufon", "password": "pass", "email": "wow@gmail.com"} Output Integer response status 201 - [Teardown] DELETE /users/1 \ No newline at end of file + [Teardown] DELETE /users/${res.body.id} diff --git a/back/tsconfig.json b/back/tsconfig.json index adb614c..cf106ab 100644 --- a/back/tsconfig.json +++ b/back/tsconfig.json @@ -12,10 +12,10 @@ "baseUrl": "./", "incremental": true, "skipLibCheck": true, - "strictNullChecks": false, + "strictNullChecks": true, "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": true } } diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity deleted file mode 100644 index 19ce570..0000000 --- a/node_modules/.yarn-integrity +++ /dev/null @@ -1,12 +0,0 @@ -{ - "systemParams": "linux-x64-108", - "modulesFolders": [ - "node_modules" - ], - "flags": [], - "linkedModules": [], - "topLevelPatterns": [], - "lockfileEntries": {}, - "files": [], - "artifacts": {} -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..64e05ed --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Chromacase", + "lockfileVersion": 2, + "requires": true, + "packages": {} +} From 014c64d52da555765dc1a8b970d7473195d39b1d Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 26 Sep 2022 23:12:19 +0900 Subject: [PATCH 03/15] Add a swagger (#79) --- back/src/auth/auth.controller.ts | 56 ++++++++++++++-------- back/src/auth/models/jwt.ts | 6 +++ back/src/models/user.ts | 12 +++++ back/src/users/dto/create-user.dto.ts | 11 +++-- back/src/users/entities/user.entity.ts | 1 - back/src/users/users.controller.ts | 65 +++++++++++++++++--------- back/test/robot/auth/auth.robot | 4 +- 7 files changed, 108 insertions(+), 47 deletions(-) create mode 100644 back/src/auth/models/jwt.ts create mode 100644 back/src/models/user.ts delete mode 100644 back/src/users/entities/user.entity.ts diff --git a/back/src/auth/auth.controller.ts b/back/src/auth/auth.controller.ts index 59a4430..1722424 100644 --- a/back/src/auth/auth.controller.ts +++ b/back/src/auth/auth.controller.ts @@ -1,53 +1,71 @@ -import { Controller, Request, Post, Get, UseGuards, Res, Body, Delete } from '@nestjs/common'; +import { + Controller, + Request, + Post, + Get, + UseGuards, + Body, + Delete, + BadRequestException, + HttpCode, +} from '@nestjs/common'; import { AuthService } from './auth.service'; import { JwtAuthGuard } from './jwt-auth.guard'; import { LocalAuthGuard } from './local-auth.guard'; import { RegisterDto } from './dto/register.dto'; -import { Response } from 'express'; import { UsersService } from 'src/users/users.service'; -import { ApiBearerAuth, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'; -import { ConfigService } from '@nestjs/config'; +import { + ApiBearerAuth, + ApiBody, + ApiOkResponse, + ApiParam, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; +import { User } from '../models/user'; +import { JwtToken } from './models/jwt'; +import { LoginDto } from './dto/login.dto'; +@ApiTags('auth') @Controller('auth') export class AuthController { constructor( private authService: AuthService, private usersService: UsersService, - private configService: ConfigService ) {} @Post('register') - async register(@Body() registerDto: RegisterDto, @Res() res: Response) { + async register(@Body() registerDto: RegisterDto): Promise { try { await this.usersService.createUser(registerDto); - return res.status(200).json({"status": "user created"}); } catch { - return res.status(400).json({"status": "user not created"}); + throw new BadRequestException(); } } + @ApiBody({ type: LoginDto }) + @HttpCode(200) @UseGuards(LocalAuthGuard) @Post('login') - async login(@Request() req) { + async login(@Request() req: any): Promise { return this.authService.login(req.user); } @UseGuards(JwtAuthGuard) @ApiBearerAuth() - @ApiOkResponse({ description: 'Successfully logged in' }) + @ApiOkResponse({ description: 'Successfully logged in', type: User }) @ApiUnauthorizedResponse({ description: 'Invalid token' }) @Get('me') - getProfile(@Request() req) { - return req.user; - } + getProfile(@Request() req: any): User { + return req.user; + } - @UseGuards(JwtAuthGuard) + @UseGuards(JwtAuthGuard) @ApiBearerAuth() - @ApiOkResponse({ description: 'Successfully deleted' }) + @ApiOkResponse({ description: 'Successfully deleted', type: User}) @ApiUnauthorizedResponse({ description: 'Invalid token' }) @Delete('me') - deleteSelf(@Request() req) { - return this.usersService.deleteUser({"id": req.user.id}) - } - + deleteSelf(@Request() req: any): Promise { + return this.usersService.deleteUser({ id: req.user.id }); + } } diff --git a/back/src/auth/models/jwt.ts b/back/src/auth/models/jwt.ts new file mode 100644 index 0000000..bc06aeb --- /dev/null +++ b/back/src/auth/models/jwt.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class JwtToken { + @ApiProperty() + access_token: string; +} diff --git a/back/src/models/user.ts b/back/src/models/user.ts new file mode 100644 index 0000000..d650ae0 --- /dev/null +++ b/back/src/models/user.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class User { + @ApiProperty() + id: number; + @ApiProperty() + username: string; + @ApiProperty() + password: string; + @ApiProperty() + email: string; +} diff --git a/back/src/users/dto/create-user.dto.ts b/back/src/users/dto/create-user.dto.ts index b8eef8f..2466a61 100644 --- a/back/src/users/dto/create-user.dto.ts +++ b/back/src/users/dto/create-user.dto.ts @@ -1,5 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; + export class CreateUserDto { - email: string; - username: string; - password: string; + @ApiProperty() + email: string; + @ApiProperty() + username: string; + @ApiProperty() + password: string; } diff --git a/back/src/users/entities/user.entity.ts b/back/src/users/entities/user.entity.ts deleted file mode 100644 index 4f82c14..0000000 --- a/back/src/users/entities/user.entity.ts +++ /dev/null @@ -1 +0,0 @@ -export class User {} diff --git a/back/src/users/users.controller.ts b/back/src/users/users.controller.ts index ed2c15d..ca5b5f2 100644 --- a/back/src/users/users.controller.ts +++ b/back/src/users/users.controller.ts @@ -1,34 +1,55 @@ -import { Controller, Get, Post, Body, Patch, Param, Delete, Put } from '@nestjs/common'; +import { + Controller, + Get, + Post, + Body, + Patch, + Param, + Delete, + NotFoundException, +} from '@nestjs/common'; import { UsersService } from './users.service'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; +import { ApiNotFoundResponse, ApiTags } from '@nestjs/swagger'; +import { User } from 'src/models/user'; +@ApiTags('users') @Controller('users') export class UsersController { - constructor(private readonly usersService: UsersService) {} + constructor(private readonly usersService: UsersService) {} - @Post() - create(@Body() createUserDto: CreateUserDto) { - return this.usersService.createUser(createUserDto); - } + @Post() + create(@Body() createUserDto: CreateUserDto): Promise { + return this.usersService.createUser(createUserDto); + } - @Get() - findAll() { - return this.usersService.users({}); - } + @Get() + findAll(): Promise { + return this.usersService.users({}); + } - @Get(':id') - findOne(@Param('id') id: number) { - return this.usersService.user({"id": +id}); - } + @Get(':id') + @ApiNotFoundResponse() + async findOne(@Param('id') id: number): Promise { + const ret = await this.usersService.user({ id: +id }); + if (!ret) throw new NotFoundException(); + return ret; + } - @Patch(':id') - update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { - return this.usersService.updateUser({where: {"id": +id}, data: updateUserDto}); - } + @Patch(':id') + update( + @Param('id') id: string, + @Body() updateUserDto: UpdateUserDto, + ): Promise { + return this.usersService.updateUser({ + where: { id: +id }, + data: updateUserDto, + }); + } - @Delete(':id') - remove(@Param('id') id: string) { - return this.usersService.deleteUser({"id": +id}); - } + @Delete(':id') + remove(@Param('id') id: string): Promise { + return this.usersService.deleteUser({ id: +id }); + } } diff --git a/back/test/robot/auth/auth.robot b/back/test/robot/auth/auth.robot index ccb9fb2..15d8176 100644 --- a/back/test/robot/auth/auth.robot +++ b/back/test/robot/auth/auth.robot @@ -10,7 +10,7 @@ Login [Arguments] ${username} &{res}= POST /auth/login {"username": "${username}", "password": "password-${username}"} Output - Integer response status 201 + Integer response status 200 String response body access_token Set Headers {"Authorization": "Bearer ${res.body.access_token}"} @@ -21,7 +21,7 @@ Register ... /auth/register ... {"username": "${username}", "password": "password-${username}", "email": "${username}@chromacase.moe"} Output - Integer response status 200 + Integer response status 201 Logout [Documentation] Logout the current user, only the local client is affected. From 38ddea057d6a4c56ace2dc7a84a37f29517bae03 Mon Sep 17 00:00:00 2001 From: Bluub <46396559+GitBluub@users.noreply.github.com> Date: Mon, 26 Sep 2022 23:21:55 +0900 Subject: [PATCH 04/15] feat: add song crd and tests (#80) Co-authored-by: Zoe Roux --- back/.gitignore | 7 +- .../migrations/20220924084638_/migration.sql | 61 ++++++++++++++ .../migrations/20220926080922_/migration.sql | 8 ++ .../migrations/20220926082047_/migration.sql | 8 ++ .../migrations/20220926084154_/migration.sql | 8 ++ back/prisma/schema.prisma | 33 ++++++++ back/src/app.controller.spec.ts | 26 +++--- back/src/app.controller.ts | 10 +-- back/src/app.module.ts | 3 +- back/src/app.service.ts | 6 +- back/src/auth/auth.controller.spec.ts | 20 ++--- back/src/auth/auth.controller.ts | 4 +- back/src/auth/auth.module.ts | 26 +++--- back/src/auth/auth.service.spec.ts | 20 ++--- back/src/auth/constants.ts | 11 ++- back/src/auth/dto/login.dto.ts | 2 +- back/src/auth/dto/register.dto.ts | 2 +- back/src/auth/interface/payload.interface.ts | 2 +- back/src/auth/jwt-auth.guard.ts | 2 +- back/src/auth/jwt.strategy.ts | 23 +++-- back/src/auth/local-auth.guard.ts | 1 - back/src/lesson/lesson.controller.spec.ts | 20 ++--- back/src/lesson/lesson.service.spec.ts | 20 ++--- back/src/main.ts | 22 ++--- back/src/prisma/prisma.module.ts | 4 +- back/src/prisma/prisma.service.spec.ts | 20 ++--- back/src/prisma/prisma.service.ts | 16 ++-- back/src/song/dto/create-song.dto.ts | 12 +++ back/src/song/song.controller.spec.ts | 18 ++++ back/src/song/song.controller.ts | 65 ++++++++++++++ back/src/song/song.module.ts | 11 +++ back/src/song/song.service.spec.ts | 18 ++++ back/src/song/song.service.ts | 45 ++++++++++ back/src/users/users.controller.spec.ts | 22 ++--- back/src/users/users.module.ts | 8 +- back/src/users/users.service.spec.ts | 20 ++--- back/test/app.e2e-spec.ts | 28 +++---- back/test/robot/env/lib64 | 1 + back/test/robot/songs/songs.robot | 84 +++++++++++++++++++ 39 files changed, 548 insertions(+), 169 deletions(-) create mode 100644 back/prisma/migrations/20220924084638_/migration.sql create mode 100644 back/prisma/migrations/20220926080922_/migration.sql create mode 100644 back/prisma/migrations/20220926082047_/migration.sql create mode 100644 back/prisma/migrations/20220926084154_/migration.sql create mode 100644 back/src/song/dto/create-song.dto.ts create mode 100644 back/src/song/song.controller.spec.ts create mode 100644 back/src/song/song.controller.ts create mode 100644 back/src/song/song.module.ts create mode 100644 back/src/song/song.service.spec.ts create mode 100644 back/src/song/song.service.ts create mode 120000 back/test/robot/env/lib64 create mode 100644 back/test/robot/songs/songs.robot diff --git a/back/.gitignore b/back/.gitignore index 22f55ad..69122cb 100644 --- a/back/.gitignore +++ b/back/.gitignore @@ -32,4 +32,9 @@ lerna-debug.log* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json -!.vscode/extensions.json \ No newline at end of file +!.vscode/extensions.json + +# Robots tests +log.html +output.xml +report.html diff --git a/back/prisma/migrations/20220924084638_/migration.sql b/back/prisma/migrations/20220924084638_/migration.sql new file mode 100644 index 0000000..07bf1b5 --- /dev/null +++ b/back/prisma/migrations/20220924084638_/migration.sql @@ -0,0 +1,61 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "username" TEXT NOT NULL, + "password" TEXT NOT NULL, + "email" TEXT NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Song" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "artistId" INTEGER NOT NULL, + "albumId" INTEGER, + "genreId" INTEGER NOT NULL, + "difficulties" JSONB NOT NULL, + + CONSTRAINT "Song_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Genre" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "Genre_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Artist" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "Artist_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Album" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "Album_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "Song_name_key" ON "Song"("name"); + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_genreId_fkey" FOREIGN KEY ("genreId") REFERENCES "Genre"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_artistId_fkey" FOREIGN KEY ("artistId") REFERENCES "Artist"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_albumId_fkey" FOREIGN KEY ("albumId") REFERENCES "Album"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926080922_/migration.sql b/back/prisma/migrations/20220926080922_/migration.sql new file mode 100644 index 0000000..41fdc96 --- /dev/null +++ b/back/prisma/migrations/20220926080922_/migration.sql @@ -0,0 +1,8 @@ +-- DropForeignKey +ALTER TABLE "Song" DROP CONSTRAINT "Song_genreId_fkey"; + +-- AlterTable +ALTER TABLE "Song" ALTER COLUMN "genreId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_genreId_fkey" FOREIGN KEY ("genreId") REFERENCES "Genre"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926082047_/migration.sql b/back/prisma/migrations/20220926082047_/migration.sql new file mode 100644 index 0000000..6bb82d6 --- /dev/null +++ b/back/prisma/migrations/20220926082047_/migration.sql @@ -0,0 +1,8 @@ +-- DropForeignKey +ALTER TABLE "Song" DROP CONSTRAINT "Song_artistId_fkey"; + +-- AlterTable +ALTER TABLE "Song" ALTER COLUMN "artistId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "Song" ADD CONSTRAINT "Song_artistId_fkey" FOREIGN KEY ("artistId") REFERENCES "Artist"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926084154_/migration.sql b/back/prisma/migrations/20220926084154_/migration.sql new file mode 100644 index 0000000..21b258d --- /dev/null +++ b/back/prisma/migrations/20220926084154_/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to drop the column `description` on the `Song` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Song" DROP COLUMN "description"; diff --git a/back/prisma/schema.prisma b/back/prisma/schema.prisma index 579d951..f18c7b7 100644 --- a/back/prisma/schema.prisma +++ b/back/prisma/schema.prisma @@ -17,6 +17,39 @@ model User { LessonHistory LessonHistory[] } +model Song { + id Int @id @default(autoincrement()) + name String @unique + artistId Int? + artist Artist? @relation(fields: [artistId], references: [id]) + albumId Int? + album Album? @relation(fields: [albumId], references: [id]) + genreId Int? + genre Genre? @relation(fields: [genreId], references: [id]) + difficulties Json +} + +model Genre { + id Int @id @default(autoincrement()) + name String + + Song Song[] +} + +model Artist { + id Int @id @default(autoincrement()) + name String + + Song Song[] +} + +model Album { + id Int @id @default(autoincrement()) + name String + + Song Song[] +} + model Lesson { id Int @id @default(autoincrement()) name String diff --git a/back/src/app.controller.spec.ts b/back/src/app.controller.spec.ts index d22f389..e2c0215 100644 --- a/back/src/app.controller.spec.ts +++ b/back/src/app.controller.spec.ts @@ -3,20 +3,20 @@ import { AppController } from './app.controller'; import { AppService } from './app.service'; describe('AppController', () => { - let appController: AppController; + let appController: AppController; - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); - appController = app.get(AppController); - }); + appController = app.get(AppController); + }); - describe('root', () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe('Hello World!'); - }); - }); + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); }); diff --git a/back/src/app.controller.ts b/back/src/app.controller.ts index cce879e..6509517 100644 --- a/back/src/app.controller.ts +++ b/back/src/app.controller.ts @@ -3,10 +3,10 @@ import { AppService } from './app.service'; @Controller() export class AppController { - constructor(private readonly appService: AppService) {} + constructor(private readonly appService: AppService) {} - @Get() - getHello(): string { - return this.appService.getHello(); - } + @Get() + getHello(): string { + return this.appService.getHello(); + } } diff --git a/back/src/app.module.ts b/back/src/app.module.ts index d87e0cb..23e15a7 100644 --- a/back/src/app.module.ts +++ b/back/src/app.module.ts @@ -5,10 +5,11 @@ import { PrismaService } from './prisma/prisma.service'; import { UsersModule } from './users/users.module'; import { PrismaModule } from './prisma/prisma.module'; import { AuthModule } from './auth/auth.module'; +import { SongModule } from './song/song.module'; import { LessonModule } from './lesson/lesson.module'; @Module({ - imports: [UsersModule, PrismaModule, AuthModule, LessonModule], + imports: [UsersModule, PrismaModule, AuthModule, SongModule, LessonModule], controllers: [AppController], providers: [AppService, PrismaService], }) diff --git a/back/src/app.service.ts b/back/src/app.service.ts index 927d7cc..7e99928 100644 --- a/back/src/app.service.ts +++ b/back/src/app.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; @Injectable() export class AppService { - getHello(): string { - return 'Hello World!'; - } + getHello(): string { + return 'Hello World!'; + } } diff --git a/back/src/auth/auth.controller.spec.ts b/back/src/auth/auth.controller.spec.ts index 27a31e6..fa86936 100644 --- a/back/src/auth/auth.controller.spec.ts +++ b/back/src/auth/auth.controller.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthController } from './auth.controller'; describe('AuthController', () => { - let controller: AuthController; + let controller: AuthController; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AuthController], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AuthController], + }).compile(); - controller = module.get(AuthController); - }); + controller = module.get(AuthController); + }); - it('should be defined', () => { - expect(controller).toBeDefined(); - }); + it('should be defined', () => { + expect(controller).toBeDefined(); + }); }); diff --git a/back/src/auth/auth.controller.ts b/back/src/auth/auth.controller.ts index 1722424..446ee6b 100644 --- a/back/src/auth/auth.controller.ts +++ b/back/src/auth/auth.controller.ts @@ -7,7 +7,7 @@ import { Body, Delete, BadRequestException, - HttpCode, + HttpCode, } from '@nestjs/common'; import { AuthService } from './auth.service'; import { JwtAuthGuard } from './jwt-auth.guard'; @@ -62,7 +62,7 @@ export class AuthController { @UseGuards(JwtAuthGuard) @ApiBearerAuth() - @ApiOkResponse({ description: 'Successfully deleted', type: User}) + @ApiOkResponse({ description: 'Successfully deleted', type: User }) @ApiUnauthorizedResponse({ description: 'Invalid token' }) @Delete('me') deleteSelf(@Request() req: any): Promise { diff --git a/back/src/auth/auth.module.ts b/back/src/auth/auth.module.ts index 4c721b4..3f09593 100644 --- a/back/src/auth/auth.module.ts +++ b/back/src/auth/auth.module.ts @@ -10,16 +10,20 @@ import { ConfigService } from '@nestjs/config'; import { JwtStrategy } from './jwt.strategy'; @Module({ - imports: [ConfigModule, UsersModule, PassportModule, - JwtModule.registerAsync({ - imports: [ConfigModule], - useFactory: async (configService: ConfigService) => ({ - secret: configService.get('JWT_SECRET'), - signOptions: { expiresIn: '1h' }, - }), - inject: [ConfigService], - })], - providers: [AuthService, LocalStrategy, JwtStrategy], - controllers: [AuthController] + imports: [ + ConfigModule, + UsersModule, + PassportModule, + JwtModule.registerAsync({ + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + secret: configService.get('JWT_SECRET'), + signOptions: { expiresIn: '1h' }, + }), + inject: [ConfigService], + }), + ], + providers: [AuthService, LocalStrategy, JwtStrategy], + controllers: [AuthController], }) export class AuthModule {} diff --git a/back/src/auth/auth.service.spec.ts b/back/src/auth/auth.service.spec.ts index 800ab66..a80b4b6 100644 --- a/back/src/auth/auth.service.spec.ts +++ b/back/src/auth/auth.service.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthService } from './auth.service'; describe('AuthService', () => { - let service: AuthService; + let service: AuthService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [AuthService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AuthService], + }).compile(); - service = module.get(AuthService); - }); + service = module.get(AuthService); + }); - it('should be defined', () => { - expect(service).toBeDefined(); - }); + it('should be defined', () => { + expect(service).toBeDefined(); + }); }); diff --git a/back/src/auth/constants.ts b/back/src/auth/constants.ts index 6f4f3ba..38163cb 100644 --- a/back/src/auth/constants.ts +++ b/back/src/auth/constants.ts @@ -1,12 +1,11 @@ -import { Injectable } from "@nestjs/common"; -import { ConfigService } from "@nestjs/config"; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class Constants { - constructor(private configService: ConfigService) {} getSecret = () => { - return this.configService.get("JWT_SECRET"); - } -} \ No newline at end of file + return this.configService.get('JWT_SECRET'); + }; +} diff --git a/back/src/auth/dto/login.dto.ts b/back/src/auth/dto/login.dto.ts index 78d3526..2282784 100644 --- a/back/src/auth/dto/login.dto.ts +++ b/back/src/auth/dto/login.dto.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty } from "class-validator"; +import { IsNotEmpty } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class LoginDto { diff --git a/back/src/auth/dto/register.dto.ts b/back/src/auth/dto/register.dto.ts index d8dd9a8..58529e4 100644 --- a/back/src/auth/dto/register.dto.ts +++ b/back/src/auth/dto/register.dto.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty } from "class-validator"; +import { IsNotEmpty } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class RegisterDto { diff --git a/back/src/auth/interface/payload.interface.ts b/back/src/auth/interface/payload.interface.ts index e414f74..2fbce8a 100644 --- a/back/src/auth/interface/payload.interface.ts +++ b/back/src/auth/interface/payload.interface.ts @@ -1,4 +1,4 @@ export default interface PayloadInterface { username: string; id: number; -} \ No newline at end of file +} diff --git a/back/src/auth/jwt-auth.guard.ts b/back/src/auth/jwt-auth.guard.ts index 18588a5..2155290 100644 --- a/back/src/auth/jwt-auth.guard.ts +++ b/back/src/auth/jwt-auth.guard.ts @@ -2,4 +2,4 @@ import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() -export class JwtAuthGuard extends AuthGuard('jwt') {} \ No newline at end of file +export class JwtAuthGuard extends AuthGuard('jwt') {} diff --git a/back/src/auth/jwt.strategy.ts b/back/src/auth/jwt.strategy.ts index a03932e..844062c 100644 --- a/back/src/auth/jwt.strategy.ts +++ b/back/src/auth/jwt.strategy.ts @@ -1,4 +1,3 @@ - import { ExtractJwt, Strategy } from 'passport-jwt'; import { PassportStrategy } from '@nestjs/passport'; import { Injectable } from '@nestjs/common'; @@ -6,15 +5,15 @@ import { ConfigService } from '@nestjs/config'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { - constructor(private configService: ConfigService) { - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - secretOrKey: configService.get('JWT_SECRET'), - }); - } + constructor(private configService: ConfigService) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: configService.get('JWT_SECRET'), + }); + } - async validate(payload: any) { - return { id: payload.id, username: payload.username }; - } -} \ No newline at end of file + async validate(payload: any) { + return { id: payload.id, username: payload.username }; + } +} diff --git a/back/src/auth/local-auth.guard.ts b/back/src/auth/local-auth.guard.ts index a3a4bf3..ccf962b 100644 --- a/back/src/auth/local-auth.guard.ts +++ b/back/src/auth/local-auth.guard.ts @@ -1,4 +1,3 @@ - import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; diff --git a/back/src/lesson/lesson.controller.spec.ts b/back/src/lesson/lesson.controller.spec.ts index 9f87a9d..9350d52 100644 --- a/back/src/lesson/lesson.controller.spec.ts +++ b/back/src/lesson/lesson.controller.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LessonController } from './lesson.controller'; describe('LessonController', () => { - let controller: LessonController; + let controller: LessonController; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [LessonController], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [LessonController], + }).compile(); - controller = module.get(LessonController); - }); + controller = module.get(LessonController); + }); - it('should be defined', () => { - expect(controller).toBeDefined(); - }); + it('should be defined', () => { + expect(controller).toBeDefined(); + }); }); diff --git a/back/src/lesson/lesson.service.spec.ts b/back/src/lesson/lesson.service.spec.ts index c857a0f..3713a08 100644 --- a/back/src/lesson/lesson.service.spec.ts +++ b/back/src/lesson/lesson.service.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LessonService } from './lesson.service'; describe('LessonService', () => { - let service: LessonService; + let service: LessonService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [LessonService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [LessonService], + }).compile(); - service = module.get(LessonService); - }); + service = module.get(LessonService); + }); - it('should be defined', () => { - expect(service).toBeDefined(); - }); + it('should be defined', () => { + expect(service).toBeDefined(); + }); }); diff --git a/back/src/main.ts b/back/src/main.ts index 8a0ebd4..f82b632 100644 --- a/back/src/main.ts +++ b/back/src/main.ts @@ -4,18 +4,18 @@ import { PrismaService } from './prisma/prisma.service'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; async function bootstrap() { - const app = await NestFactory.create(AppModule); - const prismaService = app.get(PrismaService); - await prismaService.enableShutdownHooks(app); + const app = await NestFactory.create(AppModule); + const prismaService = app.get(PrismaService); + await prismaService.enableShutdownHooks(app); - const config = new DocumentBuilder() - .setTitle('Chromacase') - .setDescription('The chromacase API') - .setVersion('1.0') - .build(); - const document = SwaggerModule.createDocument(app, config); - SwaggerModule.setup('api', app, document); + const config = new DocumentBuilder() + .setTitle('Chromacase') + .setDescription('The chromacase API') + .setVersion('1.0') + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); - await app.listen(3000); + await app.listen(3000); } bootstrap(); diff --git a/back/src/prisma/prisma.module.ts b/back/src/prisma/prisma.module.ts index e569e2d..3e6d1b6 100644 --- a/back/src/prisma/prisma.module.ts +++ b/back/src/prisma/prisma.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'; import { PrismaService } from './prisma.service'; @Module({ - providers: [PrismaService], - exports: [PrismaService] + providers: [PrismaService], + exports: [PrismaService], }) export class PrismaModule {} diff --git a/back/src/prisma/prisma.service.spec.ts b/back/src/prisma/prisma.service.spec.ts index a68cb9e..d694c29 100644 --- a/back/src/prisma/prisma.service.spec.ts +++ b/back/src/prisma/prisma.service.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { PrismaService } from './prisma.service'; describe('PrismaService', () => { - let service: PrismaService; + let service: PrismaService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [PrismaService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [PrismaService], + }).compile(); - service = module.get(PrismaService); - }); + service = module.get(PrismaService); + }); - it('should be defined', () => { - expect(service).toBeDefined(); - }); + it('should be defined', () => { + expect(service).toBeDefined(); + }); }); diff --git a/back/src/prisma/prisma.service.ts b/back/src/prisma/prisma.service.ts index edf6532..6feee25 100644 --- a/back/src/prisma/prisma.service.ts +++ b/back/src/prisma/prisma.service.ts @@ -3,13 +3,13 @@ import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { - async onModuleInit() { - await this.$connect(); - } + async onModuleInit() { + await this.$connect(); + } - async enableShutdownHooks(app: INestApplication) { - this.$on('beforeExit', async () => { - await app.close(); - }); - } + async enableShutdownHooks(app: INestApplication) { + this.$on('beforeExit', async () => { + await app.close(); + }); + } } diff --git a/back/src/song/dto/create-song.dto.ts b/back/src/song/dto/create-song.dto.ts new file mode 100644 index 0000000..02b4a2d --- /dev/null +++ b/back/src/song/dto/create-song.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty } from 'class-validator'; + +export class CreateSongDto { + @IsNotEmpty() + @ApiProperty() + name: string; + + @IsNotEmpty() + @ApiProperty() + difficulties: object; +} diff --git a/back/src/song/song.controller.spec.ts b/back/src/song/song.controller.spec.ts new file mode 100644 index 0000000..bbe129b --- /dev/null +++ b/back/src/song/song.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SongController } from './song.controller'; + +describe('SongController', () => { + let controller: SongController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SongController], + }).compile(); + + controller = module.get(SongController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/back/src/song/song.controller.ts b/back/src/song/song.controller.ts new file mode 100644 index 0000000..cb06efe --- /dev/null +++ b/back/src/song/song.controller.ts @@ -0,0 +1,65 @@ +import { + BadRequestException, + Body, + Controller, + DefaultValuePipe, + Delete, + Get, + NotFoundException, + Param, + ParseIntPipe, + Post, + Query, + Req, +} from '@nestjs/common'; +import { Plage } from 'src/models/plage'; +import { CreateSongDto } from './dto/create-song.dto'; +import { SongService } from './song.service'; +import { Request } from 'express'; +import { Prisma, Song } from '@prisma/client'; + +@Controller('song') +export class SongController { + constructor(private readonly songService: SongService) {} + + @Post() + async create(@Body() createSongDto: CreateSongDto) { + return await this.songService.createSong(createSongDto); + } + + @Delete(':id') + async remove(@Param('id', ParseIntPipe) id: number) { + return await this.songService.deleteSong({ id }); + } + + @Get() + async findAll( + @Req() req: Request, + @Query() filter: Prisma.SongWhereInput, + @Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip: number, + @Query('take', new DefaultValuePipe(20), ParseIntPipe) take: number, + ): Promise> { + try { + const ret = await this.songService.songs({ + skip, + take, + where: { + ...filter, + id: filter.id ? +filter.id : undefined, + }, + }); + return new Plage(ret, req); + } catch (e) { + console.log(e); + throw new BadRequestException(null, e?.toString()); + } + } + + @Get(':id') + async findOne(@Param('id', ParseIntPipe) id: number) { + let res = await this.songService.song({ id }); + + if (res === null) throw new NotFoundException('Song not found'); + return res; + } +} diff --git a/back/src/song/song.module.ts b/back/src/song/song.module.ts new file mode 100644 index 0000000..1cf9780 --- /dev/null +++ b/back/src/song/song.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { SongService } from './song.service'; +import { SongController } from './song.controller'; +import { PrismaModule } from 'src/prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + providers: [SongService], + controllers: [SongController], +}) +export class SongModule {} diff --git a/back/src/song/song.service.spec.ts b/back/src/song/song.service.spec.ts new file mode 100644 index 0000000..dd86410 --- /dev/null +++ b/back/src/song/song.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SongService } from './song.service'; + +describe('SongService', () => { + let service: SongService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SongService], + }).compile(); + + service = module.get(SongService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/back/src/song/song.service.ts b/back/src/song/song.service.ts new file mode 100644 index 0000000..4a7a285 --- /dev/null +++ b/back/src/song/song.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@nestjs/common'; +import { Prisma, Song } from '@prisma/client'; +import { PrismaService } from 'src/prisma/prisma.service'; + +@Injectable() +export class SongService { + constructor(private prisma: PrismaService) {} + + async createSong(data: Prisma.SongCreateInput): Promise { + return this.prisma.song.create({ + data, + }); + } + + async song( + songWhereUniqueInput: Prisma.SongWhereUniqueInput, + ): Promise { + return this.prisma.song.findUnique({ + where: songWhereUniqueInput, + }); + } + + async songs(params: { + skip?: number; + take?: number; + cursor?: Prisma.SongWhereUniqueInput; + where?: Prisma.SongWhereInput; + orderBy?: Prisma.SongOrderByWithRelationInput; + }): Promise { + const { skip, take, cursor, where, orderBy } = params; + return this.prisma.song.findMany({ + skip, + take, + cursor, + where, + orderBy, + }); + } + + async deleteSong(where: Prisma.SongWhereUniqueInput): Promise { + return this.prisma.song.delete({ + where, + }); + } +} diff --git a/back/src/users/users.controller.spec.ts b/back/src/users/users.controller.spec.ts index a76d310..7721e48 100644 --- a/back/src/users/users.controller.spec.ts +++ b/back/src/users/users.controller.spec.ts @@ -3,18 +3,18 @@ import { UsersController } from './users.controller'; import { UsersService } from './users.service'; describe('UsersController', () => { - let controller: UsersController; + let controller: UsersController; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [UsersController], - providers: [UsersService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UsersController], + providers: [UsersService], + }).compile(); - controller = module.get(UsersController); - }); + controller = module.get(UsersController); + }); - it('should be defined', () => { - expect(controller).toBeDefined(); - }); + it('should be defined', () => { + expect(controller).toBeDefined(); + }); }); diff --git a/back/src/users/users.module.ts b/back/src/users/users.module.ts index 89332aa..d1e76e7 100644 --- a/back/src/users/users.module.ts +++ b/back/src/users/users.module.ts @@ -4,9 +4,9 @@ import { UsersController } from './users.controller'; import { PrismaModule } from 'src/prisma/prisma.module'; @Module({ - imports: [PrismaModule], - controllers: [UsersController], - providers: [UsersService], - exports: [UsersService] + imports: [PrismaModule], + controllers: [UsersController], + providers: [UsersService], + exports: [UsersService], }) export class UsersModule {} diff --git a/back/src/users/users.service.spec.ts b/back/src/users/users.service.spec.ts index 62815ba..38b96e7 100644 --- a/back/src/users/users.service.spec.ts +++ b/back/src/users/users.service.spec.ts @@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UsersService } from './users.service'; describe('UsersService', () => { - let service: UsersService; + let service: UsersService; - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [UsersService], - }).compile(); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [UsersService], + }).compile(); - service = module.get(UsersService); - }); + service = module.get(UsersService); + }); - it('should be defined', () => { - expect(service).toBeDefined(); - }); + it('should be defined', () => { + expect(service).toBeDefined(); + }); }); diff --git a/back/test/app.e2e-spec.ts b/back/test/app.e2e-spec.ts index 50cda62..5c49beb 100644 --- a/back/test/app.e2e-spec.ts +++ b/back/test/app.e2e-spec.ts @@ -4,21 +4,21 @@ import * as request from 'supertest'; import { AppModule } from './../src/app.module'; describe('AppController (e2e)', () => { - let app: INestApplication; + let app: INestApplication; - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); - app = moduleFixture.createNestApplication(); - await app.init(); - }); + app = moduleFixture.createNestApplication(); + await app.init(); + }); - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); }); diff --git a/back/test/robot/env/lib64 b/back/test/robot/env/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/back/test/robot/env/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/back/test/robot/songs/songs.robot b/back/test/robot/songs/songs.robot new file mode 100644 index 0000000..73379c6 --- /dev/null +++ b/back/test/robot/songs/songs.robot @@ -0,0 +1,84 @@ +*** Settings *** +Documentation Tests of the /song route. +... Ensures that the songs CRUD works corectly. +Resource ../rest.resource + + +*** Keywords *** +*** Test Cases *** +Create a song + [Documentation] Create a song + &{res}= POST /song {"name": "Mama mia", "difficulties": {}} + Output + Integer response status 201 + [Teardown] DELETE /song/${res.body.id} + +Find a song + [Documentation] Create a song and find it + &{res}= POST /song {"name": "Mama mia", "difficulties": {}} + Output + Integer response status 201 + &{get}= GET /song/${res.body.id} + Output + Integer response status 200 + Should Be Equal ${res.body} ${get.body} + [Teardown] DELETE /song/${res.body.id} + + +Find a song non existant + [Documentation] Find non existant song + &{get}= GET /song/9999 + Integer response status 404 + +Find multiples songs + [Documentation] Create two songs and find them + &{res}= POST /song {"name": "Mama mia", "difficulties": {}} + Output + Integer response status 201 + &{res2}= POST /song {"name": "Here we go again", "difficulties": {}} + Output + Integer response status 201 + + &{get}= GET /song + Output + Integer response status 200 + Should Contain ${get.body.data} ${res.body} + Should Contain ${get.body.data} ${res2.body} + [Teardown] Run Keywords DELETE /song/${res.body.id} + ... AND DELETE /song/${res2.body.id} + +Find multiples songs filtered + [Documentation] Create two songs and find them + &{res}= POST /song {"name": "Mamamia", "difficulties": {}} + Output + Integer response status 201 + &{res2}= POST /song {"name": "Here we go again", "difficulties": {}} + Output + Integer response status 201 + + &{get}= GET /song?name=Mamamia + Output + Integer response status 200 + Should Contain ${get.body.data} ${res.body} + Should Not Contain ${get.body.data} ${res2.body} + [Teardown] Run Keywords DELETE /song/${res.body.id} + ... AND DELETE /song/${res2.body.id} + + + +Find multiples songs filtered by type + [Documentation] Create two songs and find them + &{res}= POST /song {"name": "Mamamia", "difficulties": {}} + Output + Integer response status 201 + &{res2}= POST /song {"name": "Here we go again", "difficulties": {}} + Output + Integer response status 201 + + &{get}= GET /song?id=${res.body.id} + Output + Integer response status 200 + Should Contain ${get.body.data} ${res.body} + Should Not Contain ${get.body.data} ${res2.body} + [Teardown] Run Keywords DELETE /song/${res.body.id} + ... AND DELETE /song/${res2.body.id} From 5cdd06a5bde4a3b36450930f5e1cec4a2b570916 Mon Sep 17 00:00:00 2001 From: GitBluub Date: Mon, 26 Sep 2022 23:33:31 +0900 Subject: [PATCH 05/15] fix: prisma migrations --- .../migrations/20220924080202_/migration.sql | 26 ------------- .../migrations/20220924081654_/migration.sql | 28 -------------- .../migrations/20220926065731_/migration.sql | 10 ----- .../migrations/20220926080922_/migration.sql | 8 ---- .../migrations/20220926082047_/migration.sql | 8 ---- .../migrations/20220926084154_/migration.sql | 8 ---- .../migration.sql | 37 ++++++++++++++++--- 7 files changed, 32 insertions(+), 93 deletions(-) delete mode 100644 back/prisma/migrations/20220924080202_/migration.sql delete mode 100644 back/prisma/migrations/20220924081654_/migration.sql delete mode 100644 back/prisma/migrations/20220926065731_/migration.sql delete mode 100644 back/prisma/migrations/20220926080922_/migration.sql delete mode 100644 back/prisma/migrations/20220926082047_/migration.sql delete mode 100644 back/prisma/migrations/20220926084154_/migration.sql rename back/prisma/migrations/{20220924084638_ => 20220926143147_}/migration.sql (53%) diff --git a/back/prisma/migrations/20220924080202_/migration.sql b/back/prisma/migrations/20220924080202_/migration.sql deleted file mode 100644 index c51015f..0000000 --- a/back/prisma/migrations/20220924080202_/migration.sql +++ /dev/null @@ -1,26 +0,0 @@ --- CreateEnum -CREATE TYPE "DifficultyPoint" AS ENUM ('TwoHands', 'Rhythm', 'NoteCombo', 'Arpeggio', 'Distance', 'LeftHand', 'RightHand', 'LeadHandChange', 'ChordComplexity', 'ChordTiming', 'Length', 'PedalPoint', 'Precision'); - --- CreateTable -CREATE TABLE "User" ( - "id" SERIAL NOT NULL, - "username" TEXT NOT NULL, - "password" TEXT NOT NULL, - "email" TEXT NOT NULL, - - CONSTRAINT "User_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Lesson" ( - "id" SERIAL NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT NOT NULL, - "requiredLvl" INTEGER NOT NULL, - "difficulyPoint" "DifficultyPoint" NOT NULL, - - CONSTRAINT "Lesson_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); diff --git a/back/prisma/migrations/20220924081654_/migration.sql b/back/prisma/migrations/20220924081654_/migration.sql deleted file mode 100644 index 0bc5d2e..0000000 --- a/back/prisma/migrations/20220924081654_/migration.sql +++ /dev/null @@ -1,28 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `difficulyPoint` on the `Lesson` table. All the data in the column will be lost. - - You are about to drop the column `requiredLvl` on the `Lesson` table. All the data in the column will be lost. - - Added the required column `mainSkill` to the `Lesson` table without a default value. This is not possible if the table is not empty. - - Added the required column `requiredLevel` to the `Lesson` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Lesson" DROP COLUMN "difficulyPoint", -DROP COLUMN "requiredLvl", -ADD COLUMN "mainSkill" "DifficultyPoint" NOT NULL, -ADD COLUMN "requiredLevel" INTEGER NOT NULL; - --- CreateTable -CREATE TABLE "LessonHistory" ( - "lessonID" INTEGER NOT NULL, - "userID" INTEGER NOT NULL, - - CONSTRAINT "LessonHistory_pkey" PRIMARY KEY ("lessonID","userID") -); - --- AddForeignKey -ALTER TABLE "LessonHistory" ADD CONSTRAINT "LessonHistory_userID_fkey" FOREIGN KEY ("userID") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "LessonHistory" ADD CONSTRAINT "LessonHistory_lessonID_fkey" FOREIGN KEY ("lessonID") REFERENCES "Lesson"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926065731_/migration.sql b/back/prisma/migrations/20220926065731_/migration.sql deleted file mode 100644 index 5d85f48..0000000 --- a/back/prisma/migrations/20220926065731_/migration.sql +++ /dev/null @@ -1,10 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `title` on the `Lesson` table. All the data in the column will be lost. - - Added the required column `name` to the `Lesson` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Lesson" DROP COLUMN "title", -ADD COLUMN "name" TEXT NOT NULL; diff --git a/back/prisma/migrations/20220926080922_/migration.sql b/back/prisma/migrations/20220926080922_/migration.sql deleted file mode 100644 index 41fdc96..0000000 --- a/back/prisma/migrations/20220926080922_/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ --- DropForeignKey -ALTER TABLE "Song" DROP CONSTRAINT "Song_genreId_fkey"; - --- AlterTable -ALTER TABLE "Song" ALTER COLUMN "genreId" DROP NOT NULL; - --- AddForeignKey -ALTER TABLE "Song" ADD CONSTRAINT "Song_genreId_fkey" FOREIGN KEY ("genreId") REFERENCES "Genre"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926082047_/migration.sql b/back/prisma/migrations/20220926082047_/migration.sql deleted file mode 100644 index 6bb82d6..0000000 --- a/back/prisma/migrations/20220926082047_/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ --- DropForeignKey -ALTER TABLE "Song" DROP CONSTRAINT "Song_artistId_fkey"; - --- AlterTable -ALTER TABLE "Song" ALTER COLUMN "artistId" DROP NOT NULL; - --- AddForeignKey -ALTER TABLE "Song" ADD CONSTRAINT "Song_artistId_fkey" FOREIGN KEY ("artistId") REFERENCES "Artist"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/back/prisma/migrations/20220926084154_/migration.sql b/back/prisma/migrations/20220926084154_/migration.sql deleted file mode 100644 index 21b258d..0000000 --- a/back/prisma/migrations/20220926084154_/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `description` on the `Song` table. All the data in the column will be lost. - -*/ --- AlterTable -ALTER TABLE "Song" DROP COLUMN "description"; diff --git a/back/prisma/migrations/20220924084638_/migration.sql b/back/prisma/migrations/20220926143147_/migration.sql similarity index 53% rename from back/prisma/migrations/20220924084638_/migration.sql rename to back/prisma/migrations/20220926143147_/migration.sql index 07bf1b5..2e548f1 100644 --- a/back/prisma/migrations/20220924084638_/migration.sql +++ b/back/prisma/migrations/20220926143147_/migration.sql @@ -1,3 +1,6 @@ +-- CreateEnum +CREATE TYPE "DifficultyPoint" AS ENUM ('TwoHands', 'Rhythm', 'NoteCombo', 'Arpeggio', 'Distance', 'LeftHand', 'RightHand', 'LeadHandChange', 'ChordComplexity', 'ChordTiming', 'Length', 'PedalPoint', 'Precision'); + -- CreateTable CREATE TABLE "User" ( "id" SERIAL NOT NULL, @@ -12,10 +15,9 @@ CREATE TABLE "User" ( CREATE TABLE "Song" ( "id" SERIAL NOT NULL, "name" TEXT NOT NULL, - "description" TEXT NOT NULL, - "artistId" INTEGER NOT NULL, + "artistId" INTEGER, "albumId" INTEGER, - "genreId" INTEGER NOT NULL, + "genreId" INTEGER, "difficulties" JSONB NOT NULL, CONSTRAINT "Song_pkey" PRIMARY KEY ("id") @@ -45,6 +47,25 @@ CREATE TABLE "Album" ( CONSTRAINT "Album_pkey" PRIMARY KEY ("id") ); +-- CreateTable +CREATE TABLE "Lesson" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT NOT NULL, + "requiredLevel" INTEGER NOT NULL, + "mainSkill" "DifficultyPoint" NOT NULL, + + CONSTRAINT "Lesson_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "LessonHistory" ( + "lessonID" INTEGER NOT NULL, + "userID" INTEGER NOT NULL, + + CONSTRAINT "LessonHistory_pkey" PRIMARY KEY ("lessonID","userID") +); + -- CreateIndex CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); @@ -52,10 +73,16 @@ CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); CREATE UNIQUE INDEX "Song_name_key" ON "Song"("name"); -- AddForeignKey -ALTER TABLE "Song" ADD CONSTRAINT "Song_genreId_fkey" FOREIGN KEY ("genreId") REFERENCES "Genre"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "Song" ADD CONSTRAINT "Song_genreId_fkey" FOREIGN KEY ("genreId") REFERENCES "Genre"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE "Song" ADD CONSTRAINT "Song_artistId_fkey" FOREIGN KEY ("artistId") REFERENCES "Artist"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "Song" ADD CONSTRAINT "Song_artistId_fkey" FOREIGN KEY ("artistId") REFERENCES "Artist"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "Song" ADD CONSTRAINT "Song_albumId_fkey" FOREIGN KEY ("albumId") REFERENCES "Album"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LessonHistory" ADD CONSTRAINT "LessonHistory_userID_fkey" FOREIGN KEY ("userID") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LessonHistory" ADD CONSTRAINT "LessonHistory_lessonID_fkey" FOREIGN KEY ("lessonID") REFERENCES "Lesson"("id") ON DELETE RESTRICT ON UPDATE CASCADE; From d4a114dca779f10f43d018af02ba134f2472a34f Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 27 Sep 2022 11:40:53 +0900 Subject: [PATCH 06/15] Fix back dockerfile --- back/.dockerignore | 10 ++++++++++ back/Dockerfile | 7 ++++--- 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 back/.dockerignore diff --git a/back/.dockerignore b/back/.dockerignore new file mode 100644 index 0000000..ff2cba8 --- /dev/null +++ b/back/.dockerignore @@ -0,0 +1,10 @@ +node_modules +Dockerfile +Dockerfile.dev +dist +test +.dockerignore +.gitignore +.eslintrc.json +.pretiierrc +README.MD diff --git a/back/Dockerfile b/back/Dockerfile index 33225cc..99a6920 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -1,7 +1,8 @@ FROM node:17 WORKDIR /app -COPY ./package.json ./ -RUN npm install +COPY ./package.json ./package-lock.json ./ +RUN npm install --frozen-lockfile COPY . . +RUN npx prisma generate RUN npm run build -CMD npx prisma generate ; npx prisma migrate dev ; npm run start:prod +CMD npx prisma migrate ; npm run start:prod From 71c35d40166a9e1ea56320579f4e52da8c984f06 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 27 Sep 2022 11:43:31 +0900 Subject: [PATCH 07/15] Fix back prisma migrations on docker --- back/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/Dockerfile b/back/Dockerfile index 99a6920..65bba41 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -5,4 +5,4 @@ RUN npm install --frozen-lockfile COPY . . RUN npx prisma generate RUN npm run build -CMD npx prisma migrate ; npm run start:prod +CMD npx prisma migrate dev; npm run start:prod From c6fe245245f0f929925444c1102e7d9a2868cdf9 Mon Sep 17 00:00:00 2001 From: Arthi-chaud Date: Thu, 6 Oct 2022 09:20:15 +0100 Subject: [PATCH 08/15] Front: Native-Base Migration: apply to AuthenticationView --- front/components/loading.tsx | 1 - front/views/AuthenticationView.tsx | 22 ++++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/front/components/loading.tsx b/front/components/loading.tsx index c35e304..dcf1f5c 100644 --- a/front/components/loading.tsx +++ b/front/components/loading.tsx @@ -1,4 +1,3 @@ -import { useTheme } from "@react-navigation/native"; import { ActivityIndicator } from "react-native-paper"; const LoadingComponent = () => { return diff --git a/front/views/AuthenticationView.tsx b/front/views/AuthenticationView.tsx index abd012c..1091191 100644 --- a/front/views/AuthenticationView.tsx +++ b/front/views/AuthenticationView.tsx @@ -1,21 +1,19 @@ import React from "react"; -import { useTranslation } from "react-i18next"; -import { Text, View } from 'react-native'; -import { Button } from "react-native-paper"; import { useDispatch } from "react-redux"; import { translate } from "../i18n/i18n"; import { setUserToken } from "../state/UserSlice"; +import { Center, Button, Text } from 'native-base'; const AuthenticationView = () => { - const dispatch = useDispatch(); - return ( - - { translate('welcome') } - - - ); + const dispatch = useDispatch(); + return ( +
+ {translate('welcome')} + +
+ ); } From 5c4d29c5de2cf77c120b8278d505f4dda54e369b Mon Sep 17 00:00:00 2001 From: Arthi-chaud Date: Thu, 6 Oct 2022 20:05:47 +0100 Subject: [PATCH 09/15] Front: Loading component uses theme --- front/components/loading.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/front/components/loading.tsx b/front/components/loading.tsx index dcf1f5c..775479c 100644 --- a/front/components/loading.tsx +++ b/front/components/loading.tsx @@ -1,6 +1,8 @@ +import { useTheme } from "native-base"; import { ActivityIndicator } from "react-native-paper"; const LoadingComponent = () => { - return + const theme = useTheme(); + return } export default LoadingComponent; \ No newline at end of file From 048264588dfa01450d4e1cf5ece85ff6a02d4786 Mon Sep 17 00:00:00 2001 From: Arthi-chaud Date: Thu, 6 Oct 2022 20:06:14 +0100 Subject: [PATCH 10/15] Front: Format Theme for Native Base --- front/Theme.tsx | 136 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 120 insertions(+), 16 deletions(-) diff --git a/front/Theme.tsx b/front/Theme.tsx index fa016c4..62725e9 100644 --- a/front/Theme.tsx +++ b/front/Theme.tsx @@ -1,25 +1,129 @@ +import { extendTheme } from 'native-base'; /** * Color theme to use thoughout the application * Using the Material Color guidelines */ - -import { DefaultTheme } from 'react-native-paper'; - -const Theme = { - ...DefaultTheme, +const Theme = extendTheme({ roundness: 10, colors: { - ...DefaultTheme.colors, - primary: '#5db075', - background: '#F0F0F0', - surface: '#F6F6F6', - accent: '#00bdbd', - error: '#B00020', - text: '#000000', - onSurface: '#000000', - placeholder: '#C9C9C9', - notification: '#FF0000' + primary: + { + 50: '#e6faea', + 100: '#c8e7d0', + 200: '#a7d6b5', + 300: '#86c498', + 400: '#65b47c', + 500: '#4b9a62', + 600: '#3a784b', + 700: '#275635', + 800: '#14341f', + 900: '#001405', + }, + background: + { + 50: '#f2f2f2', + 100: '#d9d9d9', + 200: '#bfbfbf', + 300: '#a6a6a6', + 400: '#8c8c8c', + 500: '#737373', + 600: '#595959', + 700: '#404040', + 800: '#262626', + 900: '#0d0d0d', + }, + surface: + { + 50: '#f2f2f2', + 100: '#d9d9d9', + 200: '#bfbfbf', + 300: '#a6a6a6', + 400: '#8c8c8c', + 500: '#737373', + 600: '#595959', + 700: '#404040', + 800: '#262626', + 900: '#0d0d0d', + }, + accent: + { + 50: '#d8ffff', + 100: '#acffff', + 200: '#7dffff', + 300: '#4dffff', + 400: '#28ffff', + 500: '#18e5e6', + 600: '#00b2b3', + 700: '#007f80', + 800: '#004d4e', + 900: '#001b1d', + }, + error: + { + 50: '#ffe2e9', + 100: '#ffb1bf', + 200: '#ff7f97', + 300: '#ff4d6d', + 400: '#fe1d43', + 500: '#e5062b', + 600: '#b30020', + 700: '#810017', + 800: '#4f000c', + 900: '#200004', + }, + text: + { + 50: '#f2f2f2', + 100: '#d9d9d9', + 200: '#bfbfbf', + 300: '#a6a6a6', + 400: '#8c8c8c', + 500: '#737373', + 600: '#595959', + 700: '#404040', + 800: '#262626', + 900: '#0d0d0d', + }, + onSurface: + { + 50: '#f2f2f2', + 100: '#d9d9d9', + 200: '#bfbfbf', + 300: '#a6a6a6', + 400: '#8c8c8c', + 500: '#737373', + 600: '#595959', + 700: '#404040', + 800: '#262626', + 900: '#0d0d0d', + }, + placeholder: + { + 50: '#fbf0f2', + 100: '#dcd8d9', + 200: '#bfbfbf', + 300: '#a6a6a6', + 400: '#8c8c8c', + 500: '#737373', + 600: '#595959', + 700: '#404040', + 800: '#282626', + 900: '#150a0d', + }, + notification: + { + 50: '#ffe1e1', + 100: '#ffb1b1', + 200: '#ff7f7f', + 300: '#ff4c4c', + 400: '#ff1a1a', + 500: '#e60000', + 600: '#b40000', + 700: '#810000', + 800: '#500000', + 900: '#210000', + } } -}; +}); export default Theme; \ No newline at end of file From 99e68fcc4ced5df18c591925796e951910239240 Mon Sep 17 00:00:00 2001 From: Arthi-chaud Date: Thu, 6 Oct 2022 20:06:31 +0100 Subject: [PATCH 11/15] Front: Add Native Base Dependency --- front/package.json | 2 + front/yarn.lock | 1093 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1088 insertions(+), 7 deletions(-) diff --git a/front/package.json b/front/package.json index 5ebe312..7f2a397 100644 --- a/front/package.json +++ b/front/package.json @@ -29,6 +29,7 @@ "i18next": "^21.8.16", "jest": "^26.6.3", "jest-expo": "^45.0.1", + "native-base": "^3.4.17", "react": "17.0.2", "react-dom": "17.0.2", "react-i18next": "^11.18.3", @@ -36,6 +37,7 @@ "react-native-paper": "^4.12.4", "react-native-safe-area-context": "4.2.4", "react-native-screens": "~3.11.1", + "react-native-svg": "12.3.0", "react-native-testing-library": "^6.0.0", "react-native-web": "0.17.7", "react-redux": "^8.0.2" diff --git a/front/yarn.lock b/front/yarn.lock index a159fcf..d1fdf2f 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -1040,6 +1040,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.8.7": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" + integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" @@ -1438,6 +1445,45 @@ find-up "^5.0.0" js-yaml "^4.1.0" +"@formatjs/ecma402-abstract@1.12.0": + version "1.12.0" + resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-1.12.0.tgz#2fb5e8983d5fae2fad9ec6c77aec1803c2b88d8e" + integrity sha512-0/wm9b7brUD40kx7KSE0S532T8EfH06Zc41rGlinoNyYXnuusR6ull2x63iFJgVXgwahm42hAW7dcYdZ+llZzA== + dependencies: + "@formatjs/intl-localematcher" "0.2.31" + tslib "2.4.0" + +"@formatjs/fast-memoize@1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-1.2.6.tgz#a442970db7e9634af556919343261a7bbe5e88c3" + integrity sha512-9CWZ3+wCkClKHX+i5j+NyoBVqGf0pIskTo6Xl6ihGokYM2yqSSS68JIgeo+99UIHc+7vi9L3/SDSz/dWI9SNlA== + dependencies: + tslib "2.4.0" + +"@formatjs/icu-messageformat-parser@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.8.tgz#45cb678da4c760009b52fe6ca2c13aedbc200fee" + integrity sha512-T1R/UtPSCdznwjlfIJDl9XnjZdcFap+rPJrKC9uATr/sUdziVad3SfRQFf50JOuHptbk6knz+VdiYdApek4Sag== + dependencies: + "@formatjs/ecma402-abstract" "1.12.0" + "@formatjs/icu-skeleton-parser" "1.3.13" + tslib "2.4.0" + +"@formatjs/icu-skeleton-parser@1.3.13": + version "1.3.13" + resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.13.tgz#f7e186e72ed73c3272d22a3aacb646e77368b099" + integrity sha512-qb1kxnA4ep76rV+d9JICvZBThBpK5X+nh1dLmmIReX72QyglicsaOmKEcdcbp7/giCWfhVs6CXPVA2JJ5/ZvAw== + dependencies: + "@formatjs/ecma402-abstract" "1.12.0" + tslib "2.4.0" + +"@formatjs/intl-localematcher@0.2.31": + version "0.2.31" + resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.31.tgz#aada2b1e58211460cedba56889e3c489117eb6eb" + integrity sha512-9QTjdSBpQ7wHShZgsNzNig5qT3rCPvmZogS/wXZzKotns5skbXgs0I7J8cuN0PPqXyynvNVuN+iOKhNS2eb+ZA== + dependencies: + tslib "2.4.0" + "@gar/promisify@^1.0.1": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -1460,6 +1506,35 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@internationalized/date@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.0.1.tgz#66332e9ca8f59b7be010ca65d946bca430ba4b66" + integrity sha512-E/3lASs4mAeJ2Z2ye6ab7eUD0bPUfTeNVTAv6IS+ne9UtMu9Uepb9A1U2Ae0hDr6WAlBuvUtrakaxEdYB9TV6Q== + dependencies: + "@babel/runtime" "^7.6.2" + +"@internationalized/message@^3.0.9": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@internationalized/message/-/message-3.0.9.tgz#52bc20debe5296375d66ffcf56c3df5d8118a37d" + integrity sha512-yHQggKWUuSvj1GznVtie4tcYq+xMrkd/lTKCFHp6gG18KbIliDw+UI7sL9+yJPGuWiR083xuLyyhzqiPbNOEww== + dependencies: + "@babel/runtime" "^7.6.2" + intl-messageformat "^10.1.0" + +"@internationalized/number@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.1.1.tgz#160584316741de4381689ab759001603ee17b595" + integrity sha512-dBxCQKIxvsZvW2IBt3KsqrCfaw2nV6o6a8xsloJn/hjW0ayeyhKuiiMtTwW3/WGNPP7ZRyDbtuiUEjMwif1ENQ== + dependencies: + "@babel/runtime" "^7.6.2" + +"@internationalized/string@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@internationalized/string/-/string-3.0.0.tgz#de563871e1b19e4d0ce3246ec18d25da1a73db73" + integrity sha512-NUSr4u+mNu5BysXFeVWZW4kvjXylPkU/YYqaWzdNuz1eABfehFiZTEYhWAAMzI3U8DTxfqF9PM3zyhk5gcfz6w== + dependencies: + "@babel/runtime" "^7.6.2" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1790,6 +1865,404 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@react-aria/checkbox@^3.2.1": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@react-aria/checkbox/-/checkbox-3.6.0.tgz#5c09f7be3de2d91e2f6ab0f14d2e588793afc1da" + integrity sha512-E2MZoMZhtHtlS1mYjxTI29JRq4s3Y6d92KHj7tzvcC308d4Bzz0IHJd0bMz/8whoNteU02pQyZ3rDAcGf92bHQ== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/label" "^3.4.2" + "@react-aria/toggle" "^3.4.0" + "@react-aria/utils" "^3.14.0" + "@react-stately/checkbox" "^3.3.0" + "@react-stately/toggle" "^3.4.2" + "@react-types/checkbox" "^3.4.0" + "@react-types/shared" "^3.15.0" + +"@react-aria/combobox@^3.0.0-alpha.1": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@react-aria/combobox/-/combobox-3.4.2.tgz#ad24c7a3c7425f1f668051dbe970e79b60ca0735" + integrity sha512-LFB0L2WLR7hc5J2c36jdgekHigLv4PwZfNRlh+B4VX474pS9Zt33F0FIrCKsYZA8xXzvM+lXr4POjwsoBhmnJw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/i18n" "^3.6.1" + "@react-aria/interactions" "^3.12.0" + "@react-aria/listbox" "^3.7.0" + "@react-aria/live-announcer" "^3.1.1" + "@react-aria/menu" "^3.6.2" + "@react-aria/overlays" "^3.11.0" + "@react-aria/selection" "^3.11.0" + "@react-aria/textfield" "^3.7.2" + "@react-aria/utils" "^3.14.0" + "@react-stately/collections" "^3.4.4" + "@react-stately/combobox" "^3.2.2" + "@react-stately/layout" "^3.8.0" + "@react-types/button" "^3.6.2" + "@react-types/combobox" "^3.5.4" + "@react-types/shared" "^3.15.0" + +"@react-aria/focus@3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.2.3.tgz#3e4137498a7fb5235d056c30fd94ab4a82e73aea" + integrity sha512-+OWmJMivrq3f8ApWihH1KJYqYj3rZV34YJORacBohcAsF1Qd1V1/P+w3dMkf24kV0wqAiWePCF1FwgnrL/rYzQ== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/interactions" "^3.3.0" + "@react-aria/utils" "^3.4.0" + "@react-types/shared" "^3.3.0" + clsx "^1.1.1" + +"@react-aria/focus@^3.2.3", "@react-aria/focus@^3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.9.0.tgz#fa4478eebdc3c199a0529470f1d7b36608ef0d10" + integrity sha512-DwesjEjWjFfwAwzv9qeqkyKZNPAYmPa3UrygxzmXeKEg2JpaACGZPxRcmT2EFJFEDbX8daQDEeRGyLO49o5agg== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/interactions" "^3.12.0" + "@react-aria/utils" "^3.14.0" + "@react-types/shared" "^3.15.0" + clsx "^1.1.1" + +"@react-aria/i18n@^3.2.0", "@react-aria/i18n@^3.3.0", "@react-aria/i18n@^3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@react-aria/i18n/-/i18n-3.6.1.tgz#b97e78e3ec040a5ca08d166033f2d358ef1af4c5" + integrity sha512-kAetWsj9HOqwaqLhmFU2udhZ+4QGGYkQOgGBJYdrB7GfLZQ1GPBlZjv3QFdkX4oSf/k9cFqgftxvVQQDYZLOew== + dependencies: + "@babel/runtime" "^7.6.2" + "@internationalized/date" "^3.0.1" + "@internationalized/message" "^3.0.9" + "@internationalized/number" "^3.1.1" + "@internationalized/string" "^3.0.0" + "@react-aria/ssr" "^3.3.0" + "@react-aria/utils" "^3.14.0" + "@react-types/shared" "^3.15.0" + +"@react-aria/interactions@^3.12.0", "@react-aria/interactions@^3.3.0", "@react-aria/interactions@^3.3.2": + version "3.12.0" + resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.12.0.tgz#b16a392c3dc23351c8fd33a16cef0ef93dc4682d" + integrity sha512-KcKurjPZwme9ggvGQjbjqZtZtuyXipTBVMHUah9a3+Dz7vXxhRg5vFaEdM79oQnNsrGFW5xS6SKBehl/JG6BMw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/utils" "^3.14.0" + "@react-types/shared" "^3.15.0" + +"@react-aria/label@^3.1.1", "@react-aria/label@^3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@react-aria/label/-/label-3.4.2.tgz#c66761891aa7aabe1893608f367c1bab0b8b2d28" + integrity sha512-ctygLrA4yY31440UMZdw21Iz4ogXupLJB5xpTU1KFXcvybSu+H3gvVCOJu1jFwR12bTKnkVogTWT6wwVYH2nFw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/utils" "^3.14.0" + "@react-types/label" "^3.7.0" + "@react-types/shared" "^3.15.0" + +"@react-aria/listbox@^3.2.4", "@react-aria/listbox@^3.7.0": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@react-aria/listbox/-/listbox-3.7.0.tgz#8155dd485981658b44c756b45172974534f4a69a" + integrity sha512-XUWg+ll9LmzJB3WzAklwJY5A/YuRTWd5UM/WSL34aV1O6P/IdLW697Xvr6fpHWMPtkIUeNB6teAAT2M98iWx5Q== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/focus" "^3.9.0" + "@react-aria/interactions" "^3.12.0" + "@react-aria/label" "^3.4.2" + "@react-aria/selection" "^3.11.0" + "@react-aria/utils" "^3.14.0" + "@react-stately/collections" "^3.4.4" + "@react-stately/list" "^3.5.4" + "@react-types/listbox" "^3.3.4" + "@react-types/shared" "^3.15.0" + +"@react-aria/live-announcer@^3.0.0-alpha.0", "@react-aria/live-announcer@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@react-aria/live-announcer/-/live-announcer-3.1.1.tgz#40f340f6794fca42682fb308fe750ff56bf7c07f" + integrity sha512-e7b+dRh1SUTla42vzjdbhGYkeLD7E6wIYjYaHW9zZ37rBkSqLHUhTigh3eT3k5NxFlDD/uRxTYuwaFnWQgR+4g== + dependencies: + "@babel/runtime" "^7.6.2" + +"@react-aria/menu@^3.6.2": + version "3.6.2" + resolved "https://registry.yarnpkg.com/@react-aria/menu/-/menu-3.6.2.tgz#9061a603075dcb25bc916843a697d6c5dcb5c69b" + integrity sha512-uBZHGuFAtOdoocBVZzoBpZQFJmkt5axlEUKjBgh2BuR5JX8aZiSxyKnfgAeb3aDEi9PZpOp6RWxHzOMBRg4TsA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/i18n" "^3.6.1" + "@react-aria/interactions" "^3.12.0" + "@react-aria/overlays" "^3.11.0" + "@react-aria/selection" "^3.11.0" + "@react-aria/utils" "^3.14.0" + "@react-stately/collections" "^3.4.4" + "@react-stately/menu" "^3.4.2" + "@react-stately/tree" "^3.3.4" + "@react-types/button" "^3.6.2" + "@react-types/menu" "^3.7.2" + "@react-types/shared" "^3.15.0" + +"@react-aria/overlays@^3.11.0", "@react-aria/overlays@^3.6.1", "@react-aria/overlays@^3.7.0": + version "3.11.0" + resolved "https://registry.yarnpkg.com/@react-aria/overlays/-/overlays-3.11.0.tgz#9ecab7fbaf88b7c315215d55a3eb1ae1aa5b78f2" + integrity sha512-NqLqxSiEW9AuUPcEHCIp2lHH1moNxlkP0CkuUMkT2/T5MCPm/Iq+uD53VSR+NyeCWU/aGH3ykj2kq9NSITJkOA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/focus" "^3.9.0" + "@react-aria/i18n" "^3.6.1" + "@react-aria/interactions" "^3.12.0" + "@react-aria/ssr" "^3.3.0" + "@react-aria/utils" "^3.14.0" + "@react-aria/visually-hidden" "^3.5.0" + "@react-stately/overlays" "^3.4.2" + "@react-types/button" "^3.6.2" + "@react-types/overlays" "^3.6.4" + "@react-types/shared" "^3.15.0" + +"@react-aria/radio@^3.1.2": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@react-aria/radio/-/radio-3.4.0.tgz#4cc930dee71b3bae70f9ceaed7085f7db4d448b4" + integrity sha512-DUccHQxfI0PikXXQKarh/hLS/G+ZzfdQ00/sd57jzWsuRyukb+WywQhud29p5uO3wT33/MH9LZgSmb9dlvfabQ== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/focus" "^3.9.0" + "@react-aria/i18n" "^3.6.1" + "@react-aria/interactions" "^3.12.0" + "@react-aria/label" "^3.4.2" + "@react-aria/utils" "^3.14.0" + "@react-stately/radio" "^3.6.0" + "@react-types/radio" "^3.3.0" + "@react-types/shared" "^3.15.0" + +"@react-aria/selection@^3.11.0", "@react-aria/selection@^3.3.1", "@react-aria/selection@^3.3.2": + version "3.11.0" + resolved "https://registry.yarnpkg.com/@react-aria/selection/-/selection-3.11.0.tgz#5d3457e9ea2a5aae4f8abf799da92c723d04172d" + integrity sha512-2Qcv0PxXqOrYYT1oL+TOaB+lE/jhIPzVEPHVmf8HYzEMP5WBYP8Q+R9no5s8x++b1W0DsbUVwmk9szY48O9Bmw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/focus" "^3.9.0" + "@react-aria/i18n" "^3.6.1" + "@react-aria/interactions" "^3.12.0" + "@react-aria/utils" "^3.14.0" + "@react-stately/collections" "^3.4.4" + "@react-stately/selection" "^3.11.0" + "@react-types/shared" "^3.15.0" + +"@react-aria/slider@^3.0.1": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@react-aria/slider/-/slider-3.2.2.tgz#85884a33f9012e2487acac7c11387566eae87c9c" + integrity sha512-jSzCEcHJEhLoirjJed1u9gr4WTKvZRVjgcpfTfYeV+wMDlINBawxglJ6zBUsv6OW7REPCS+N7TGxX714+oR6Hw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/focus" "^3.9.0" + "@react-aria/i18n" "^3.6.1" + "@react-aria/interactions" "^3.12.0" + "@react-aria/label" "^3.4.2" + "@react-aria/utils" "^3.14.0" + "@react-stately/radio" "^3.6.0" + "@react-stately/slider" "^3.2.2" + "@react-types/radio" "^3.3.0" + "@react-types/shared" "^3.15.0" + "@react-types/slider" "^3.3.0" + +"@react-aria/ssr@^3.0.1", "@react-aria/ssr@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.3.0.tgz#25e81daf0c7a270a4a891159d8d984578e4512d8" + integrity sha512-yNqUDuOVZIUGP81R87BJVi/ZUZp/nYOBXbPsRe7oltJOfErQZD+UezMpw4vM2KRz18cURffvmC8tJ6JTeyDtaQ== + dependencies: + "@babel/runtime" "^7.6.2" + +"@react-aria/tabs@3.0.0-alpha.2": + version "3.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/@react-aria/tabs/-/tabs-3.0.0-alpha.2.tgz#3b931d9c752c2dca4c2a1b975248b0ee751077a2" + integrity sha512-yHpz1HujxBcMq8e4jrHkkowzrJwuVyssCB+DuA91kt6LC0eIMZsDZY9tEhhOq+TyOhI3nbyXaDKJG6y1qB0A5A== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/i18n" "^3.2.0" + "@react-aria/interactions" "^3.3.2" + "@react-aria/selection" "^3.3.1" + "@react-aria/utils" "^3.4.1" + "@react-stately/list" "^3.2.2" + "@react-stately/tabs" "3.0.0-alpha.0" + "@react-types/shared" "^3.2.1" + "@react-types/tabs" "3.0.0-alpha.2" + +"@react-aria/textfield@^3.7.2": + version "3.7.2" + resolved "https://registry.yarnpkg.com/@react-aria/textfield/-/textfield-3.7.2.tgz#07602775964aa7b78fedb817c42142c899e46aba" + integrity sha512-GSzjSEMdLMcQmjxOKmTaphbfMan91J/VhYGkFYEYAwcSoVMjBsIl8VUOjZQ3+7LowQ0mOS4gE7ADigRj1w2f/Q== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/focus" "^3.9.0" + "@react-aria/label" "^3.4.2" + "@react-aria/utils" "^3.14.0" + "@react-types/shared" "^3.15.0" + "@react-types/textfield" "^3.6.0" + +"@react-aria/toggle@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@react-aria/toggle/-/toggle-3.4.0.tgz#70f6a3be688130c31dad63dfb59b3027ccbebea6" + integrity sha512-kQ/CuStB64QcQtT5Kovj4cJ234CotH5et77CP9ctsT37w5lc/t4iDWDTJxf2ju9atPeMh+efqsnRY34lhK2cBA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/focus" "^3.9.0" + "@react-aria/interactions" "^3.12.0" + "@react-aria/utils" "^3.14.0" + "@react-stately/toggle" "^3.4.2" + "@react-types/checkbox" "^3.4.0" + "@react-types/shared" "^3.15.0" + "@react-types/switch" "^3.2.4" + +"@react-aria/utils@^3.14.0", "@react-aria/utils@^3.3.0", "@react-aria/utils@^3.4.0", "@react-aria/utils@^3.4.1", "@react-aria/utils@^3.6.0": + version "3.14.0" + resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.14.0.tgz#87877e89e959c8b6299da953ec3a7167de2192c3" + integrity sha512-DHgmwNBNEhnb6DEYYAfbt99wprBqJJOBBeIpQ2g3+pxwlw4BZ+v4Qr+rDD0ZibWV0mYzt8zOhZ9StpId7iTF0Q== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/ssr" "^3.3.0" + "@react-stately/utils" "^3.5.1" + "@react-types/shared" "^3.15.0" + clsx "^1.1.1" + +"@react-aria/visually-hidden@^3.2.1", "@react-aria/visually-hidden@^3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@react-aria/visually-hidden/-/visually-hidden-3.5.0.tgz#aa8669545464cdb6a4b2ba47c9695d1405864a06" + integrity sha512-tF/kCZCGv1yebwgH21cKbhjSV5CmB5/SAHOUM5YkO5V/lIFjaPtywcamIPI8F0JSfrwGF/Z9EqvqBxvIYGRlCA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/interactions" "^3.12.0" + "@react-aria/utils" "^3.14.0" + "@react-types/shared" "^3.15.0" + clsx "^1.1.1" + +"@react-native-aria/button@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@react-native-aria/button/-/button-0.2.4.tgz#ce0c994449011f4b852a222afd90e027fb839de0" + integrity sha512-wlu6SXI20U+N4fbPX8oh9pkL9hx8W41+cra3fa3s2xfQ6czT4KAkyvSsr1ALUBH4dRkoxxSPOcGJMGnq2K3djw== + dependencies: + "@react-aria/utils" "^3.6.0" + "@react-native-aria/interactions" "^0.2.3" + "@react-stately/toggle" "^3.2.1" + "@react-types/checkbox" "^3.2.1" + +"@react-native-aria/checkbox@^0.2.2": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@react-native-aria/checkbox/-/checkbox-0.2.3.tgz#b6c99c215677df872f1bb4e596b54573f1c7a5f0" + integrity sha512-YtWtXGg5tvOaV6v1CmbusXoOZvGRAVYygms9qNeUF7/B8/iDNGSKjlxHE5LVOLRtJO/B9ndZnr6RkL326ceyng== + dependencies: + "@react-aria/checkbox" "^3.2.1" + "@react-aria/utils" "^3.6.0" + "@react-native-aria/toggle" "^0.2.3" + "@react-native-aria/utils" "^0.2.6" + "@react-stately/toggle" "^3.2.1" + +"@react-native-aria/combobox@^0.2.4-alpha.0": + version "0.2.4-alpha.1" + resolved "https://registry.yarnpkg.com/@react-native-aria/combobox/-/combobox-0.2.4-alpha.1.tgz#2ec7c5c2e87aba4dda62e494a4c3538066275e2b" + integrity sha512-MOxKMKVus9MsOL3l+mNRDYHeVr5kj5fYnretLofWh/dHBO2W5H7H70ZfOPDEr9s+vgaBBjHCtbbfOiimKRk6Kg== + dependencies: + "@react-aria/combobox" "^3.0.0-alpha.1" + "@react-aria/live-announcer" "^3.0.0-alpha.0" + "@react-aria/overlays" "^3.6.1" + "@react-aria/utils" "^3.6.0" + "@react-native-aria/utils" "^0.2.6" + "@react-types/button" "^3.3.1" + +"@react-native-aria/focus@^0.2.6": + version "0.2.7" + resolved "https://registry.yarnpkg.com/@react-native-aria/focus/-/focus-0.2.7.tgz#fd339d5ec8384cee6afe0c0115a528f360d04a27" + integrity sha512-7Ol8AoTzEN7qC4t4AzclPzjQZ0oRkNBePmVBm2lAQwOnmkKwa+TdiVGtU7MgvsQxUV3aTTMY2Nu1Z5YwCwhUkA== + dependencies: + "@react-aria/focus" "^3.2.3" + +"@react-native-aria/interactions@^0.2.2", "@react-native-aria/interactions@^0.2.3", "@react-native-aria/interactions@^0.2.7": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@react-native-aria/interactions/-/interactions-0.2.8.tgz#5ced4bd3391c647699fa79275472f784a44c2488" + integrity sha512-+LsLghBnp1fEVdLdIZGfE2izbZS0GPwc7eyiLHndnAXwXdLmyDRw71UCEjsUuNh7SO7BBR5QjHlk0cTHmyynQg== + dependencies: + "@react-aria/interactions" "^3.3.2" + "@react-aria/utils" "^3.6.0" + "@react-native-aria/utils" "^0.2.6" + +"@react-native-aria/listbox@^0.2.4-alpha.3": + version "0.2.4-alpha.3" + resolved "https://registry.yarnpkg.com/@react-native-aria/listbox/-/listbox-0.2.4-alpha.3.tgz#1a8df0de6c932c8143ea73e43713a5d37070203c" + integrity sha512-e/y+Wdoyy/PbpFj4DVYDYMsKI+uUqnZ/0yLByqHQvzs8Ys8o69CQkyEYzHhxvFT5lCLegkLbuQN2cJd8bYNQsA== + dependencies: + "@react-aria/interactions" "^3.3.2" + "@react-aria/label" "^3.1.1" + "@react-aria/listbox" "^3.2.4" + "@react-aria/selection" "^3.3.2" + "@react-aria/utils" "^3.6.0" + "@react-native-aria/interactions" "^0.2.2" + "@react-native-aria/utils" "^0.2.6" + "@react-types/listbox" "^3.1.1" + "@react-types/shared" "^3.4.0" + +"@react-native-aria/overlays@0.3.3-rc.0": + version "0.3.3-rc.0" + resolved "https://registry.yarnpkg.com/@react-native-aria/overlays/-/overlays-0.3.3-rc.0.tgz#9041ddd6f151e6edb50c971d29920c458aa41459" + integrity sha512-RgaIYIHMltt0RdMrVwfXLAVxc22TIUY1Yx07HbQRMdt4LcSmU8pyp5CEtJ/MQCXceuqocnXfsUxyHOSnfhmfpA== + dependencies: + "@react-aria/interactions" "^3.3.2" + "@react-aria/overlays" "^3.7.0" + "@react-native-aria/utils" "^0.2.8" + "@react-stately/overlays" "^3.1.1" + "@react-types/overlays" "^3.4.0" + dom-helpers "^5.0.0" + +"@react-native-aria/radio@^0.2.4": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@react-native-aria/radio/-/radio-0.2.5.tgz#436d3abdbb48bcaf6e9c5c045ff9c5bf87b71248" + integrity sha512-kTfCjRMZH+Z2C70VxjomPO8eXBcHPa5zcuOUotyhR10WsrKZJlwwnA75t2xDq8zsxKnABJRfThv7rSlAjkFSeg== + dependencies: + "@react-aria/radio" "^3.1.2" + "@react-aria/utils" "^3.6.0" + "@react-native-aria/interactions" "^0.2.3" + "@react-native-aria/utils" "^0.2.6" + "@react-stately/radio" "^3.2.1" + "@react-types/radio" "^3.1.1" + +"@react-native-aria/slider@^0.2.5-alpha.1": + version "0.2.5-alpha.2" + resolved "https://registry.yarnpkg.com/@react-native-aria/slider/-/slider-0.2.5-alpha.2.tgz#e613c2ac338de8d8ef8999ed664ea743174ee8da" + integrity sha512-eYCAGEgcmgs2x5yC1q3edq/VpZWd8P9x1ZoB6uhiyIpDViTDFTz82IWTK0jrbHC70WxWfoY+876VjiKzbjyNxw== + dependencies: + "@react-aria/focus" "^3.2.3" + "@react-aria/interactions" "^3.3.2" + "@react-aria/label" "^3.1.1" + "@react-aria/slider" "^3.0.1" + "@react-aria/utils" "^3.6.0" + "@react-native-aria/utils" "^0.2.6" + "@react-stately/slider" "^3.0.1" + +"@react-native-aria/tabs@^0.2.7": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@react-native-aria/tabs/-/tabs-0.2.8.tgz#8b721261a277fe0459154f70c25b81e23217c864" + integrity sha512-coAiaj9NFFh8vYr/kiugqLwip8IhB6m2dL/GXPcmbK0WH531pIPXKSwgePjniETJtEP84L4PYCTZ705pRlVN8A== + dependencies: + "@react-aria/selection" "^3.3.1" + "@react-aria/tabs" "3.0.0-alpha.2" + "@react-native-aria/interactions" "^0.2.7" + "@react-native-aria/utils" "^0.2.7" + "@react-stately/tabs" "3.0.0-alpha.1" + "@react-types/tabs" "3.0.0-alpha.2" + +"@react-native-aria/toggle@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@react-native-aria/toggle/-/toggle-0.2.3.tgz#a387f03480aa0d97dc0191acbcae66122f7bcf7f" + integrity sha512-3aOlchMxpR0b2h3Z7V0aYZaQMVJD6uKOWKWJm82VsLrni4iDnDX/mLv30ujuuK3+LclUhVlJd2kRuCl+xnf3XQ== + dependencies: + "@react-aria/focus" "^3.2.3" + "@react-aria/utils" "^3.6.0" + "@react-native-aria/interactions" "^0.2.3" + "@react-native-aria/utils" "^0.2.6" + "@react-stately/toggle" "^3.2.1" + "@react-types/checkbox" "^3.2.1" + +"@react-native-aria/utils@^0.2.6", "@react-native-aria/utils@^0.2.7", "@react-native-aria/utils@^0.2.8": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@react-native-aria/utils/-/utils-0.2.8.tgz#da433606506125483080f18dbcd97b526ca46fd5" + integrity sha512-x375tG1itv3irLFRnURLsdK2djuvhFJHizSDUtLCo8skQwfjslED5t4sUkQ49di4G850gaVJz0fCcCx/pHX7CA== + dependencies: + "@react-aria/ssr" "^3.0.1" + "@react-aria/utils" "^3.3.0" + "@react-native-community/cli-debugger-ui@^7.0.3": version "7.0.3" resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-7.0.3.tgz#3eeeacc5a43513cbcae56e5e965d77726361bcb4" @@ -2016,6 +2489,364 @@ dependencies: nanoid "^3.1.23" +"@react-stately/checkbox@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@react-stately/checkbox/-/checkbox-3.0.3.tgz#18ee6bd3b544334b6f853bb5c5f7017ac3bb9c37" + integrity sha512-amT889DTLdbjAVjZ9j9TytN73PszynGIspKi1QSUCvXeA2OVyCwShxhV0Pn7yYX8cMinvGXrjhWdhn0nhYeMdg== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/toggle" "^3.2.3" + "@react-stately/utils" "^3.2.2" + "@react-types/checkbox" "^3.2.3" + +"@react-stately/checkbox@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@react-stately/checkbox/-/checkbox-3.3.0.tgz#31cb5dedbb10b9ab7358f79228f34d09e0f1bf5f" + integrity sha512-hYFJzEoreAmUKkcgd3ErDXtEqp65pfawfcygOr/3pe7MUGzl+MaauVUOg6Dh02Bxt+mdSX4mQXbJSfvm+8bmfA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/toggle" "^3.4.2" + "@react-stately/utils" "^3.5.1" + "@react-types/checkbox" "^3.4.0" + "@react-types/shared" "^3.15.0" + +"@react-stately/collections@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@react-stately/collections/-/collections-3.3.0.tgz#d1e66077b47a8b6a9abcac66f1052d4b8851ce47" + integrity sha512-Y8Pfugw/tYbcR9F6GTiTkd9O4FiXErxi5aDLSZ/knS6v0pvr3EHsC3T7jLW+48dSNrwl+HkMe5ECMhWSUA1jRQ== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-types/shared" "^3.2.1" + +"@react-stately/collections@^3.4.4": + version "3.4.4" + resolved "https://registry.yarnpkg.com/@react-stately/collections/-/collections-3.4.4.tgz#9df0b690bac0d3a95bc01352937ec74160c6bd29" + integrity sha512-gryUYCe6uzqE0ea5frTwOxOPpx/6Z42PRk7KetOh3ddN3Ts0j8XQP08jP1IB/7BC1QidrkHWvDCqGHxRiEjiIg== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-types/shared" "^3.15.0" + +"@react-stately/combobox@3.0.0-alpha.1": + version "3.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/@react-stately/combobox/-/combobox-3.0.0-alpha.1.tgz#d3240ba528b021965998950a615e715c2eccbcee" + integrity sha512-v0DNGLx0KGvNgBbXoSKzfHGcy65eP0Wx4uY3dqj+u9k3ru2BEvIqB8fo6CWhQqu8VHBX4AlhoxcyrloIKvjD/g== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/list" "^3.2.2" + "@react-stately/menu" "^3.1.0" + "@react-stately/select" "^3.1.0" + "@react-stately/utils" "^3.2.0" + "@react-types/combobox" "3.0.0-alpha.1" + "@react-types/shared" "^3.4.0" + +"@react-stately/combobox@^3.2.2": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@react-stately/combobox/-/combobox-3.2.2.tgz#fe331d75f6dca24fb03057b6657114f51043511f" + integrity sha512-kUMxgXskrtwdeEExZkJ9CjF4EJANetj+7970cOevCpy6ePCdrvdgO6+0cMrVvSgZeMaMDZXDIbbF7fqA6w0uXQ== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/list" "^3.5.4" + "@react-stately/menu" "^3.4.2" + "@react-stately/select" "^3.3.2" + "@react-stately/utils" "^3.5.1" + "@react-types/combobox" "^3.5.4" + "@react-types/shared" "^3.15.0" + +"@react-stately/layout@^3.8.0": + version "3.8.0" + resolved "https://registry.yarnpkg.com/@react-stately/layout/-/layout-3.8.0.tgz#f57a7c7b02783a5983c3902bbb26977408665cd9" + integrity sha512-0cUlkjY42YaGa0u8Z8CgNx8PIsm06zr0cr3HESCfHmvuOOR2q707PcqAd6kKuF/4NjHU4MVfuGp5z9Sm/lnjlA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/virtualizer" "^3.3.1" + "@react-types/grid" "^3.1.4" + "@react-types/shared" "^3.15.0" + "@react-types/table" "^3.3.2" + +"@react-stately/list@^3.2.2", "@react-stately/list@^3.5.4": + version "3.5.4" + resolved "https://registry.yarnpkg.com/@react-stately/list/-/list-3.5.4.tgz#0282c1925aaed33bb0e32b90d0e1524ee82399d8" + integrity sha512-AB0r2RevKVAP2AOQM1JRuCVjS2UUxMJFshA53t8pV+OSVWeyirYccsMR49mWwU60ZnxYqBWVMN+Pl7NTPTnT8w== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/collections" "^3.4.4" + "@react-stately/selection" "^3.11.0" + "@react-stately/utils" "^3.5.1" + "@react-types/shared" "^3.15.0" + +"@react-stately/menu@^3.1.0", "@react-stately/menu@^3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@react-stately/menu/-/menu-3.4.2.tgz#5bb6847c9bf4a6140d561114b5f8709a4df12a51" + integrity sha512-vFC8EloVEcqf6sgiP6ABIkC41ytjoJiGtj7Ws5OS7PvZNyxxDgJr4V0O3Pxd1T0AjlHCloBbojnvoTRwZiSr/A== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/overlays" "^3.4.2" + "@react-stately/utils" "^3.5.1" + "@react-types/menu" "^3.7.2" + "@react-types/shared" "^3.15.0" + +"@react-stately/overlays@^3.1.1", "@react-stately/overlays@^3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@react-stately/overlays/-/overlays-3.4.2.tgz#c6df94a65551137075263eeef70beba8b90b52a0" + integrity sha512-UTCnn0aT+JL4ZhYPQYUWHwhmuR2T3vKTFUEZeZN9sTuDCctg08VfGoASJx8qofqkLwYJXeb8D5PMhhTDPiUQPw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/utils" "^3.5.1" + "@react-types/overlays" "^3.6.4" + +"@react-stately/radio@3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@react-stately/radio/-/radio-3.2.1.tgz#d3fb0b28c2e7accdee47912c9802ab4a886a3092" + integrity sha512-WGYMWCDJQOicFLf+bW2CbAnlRWaqsUd028WpsS41GWyIx/w7DVpUeGFwTSvyCXC5SCQZuambsWHgXNz8Ng5WIA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/utils" "^3.1.1" + "@react-types/radio" "^3.1.1" + +"@react-stately/radio@^3.2.1", "@react-stately/radio@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@react-stately/radio/-/radio-3.6.0.tgz#85846c0d3bd906cddccfb897eb900a5fd527d962" + integrity sha512-hzNwIapDSnbk5mCim/AgHQTtHRgy2QiW95okfVnGflzO7nnws8WH/s2Va4f7UupWObPv8XTqHADUEng86mVBJA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/utils" "^3.5.1" + "@react-types/radio" "^3.3.0" + "@react-types/shared" "^3.15.0" + +"@react-stately/select@^3.1.0", "@react-stately/select@^3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@react-stately/select/-/select-3.3.2.tgz#c2e1e72a6f67e7a79a9e6ffbed75ca1847248d98" + integrity sha512-/C9fW7HT+V9XnmSTiZZqH5cn+ifY9vdXvIKDbUyna98lFHtDgn7i/UvD5edunOGY0qqSMIO3kJ6/BiNg+gpn6Q== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/collections" "^3.4.4" + "@react-stately/list" "^3.5.4" + "@react-stately/menu" "^3.4.2" + "@react-stately/selection" "^3.11.0" + "@react-stately/utils" "^3.5.1" + "@react-types/select" "^3.6.4" + "@react-types/shared" "^3.15.0" + +"@react-stately/selection@^3.11.0": + version "3.11.0" + resolved "https://registry.yarnpkg.com/@react-stately/selection/-/selection-3.11.0.tgz#50945d87dadd0d08505b37f1bb319d0c783d2037" + integrity sha512-cBgDzH+AY+bMEROJbcZFdhbMk0vgiwyqBB8ZKLtCL7EOHs2xeynTAohRM+/t27U/tF91o4qHPFo67Tkxrd16Bg== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/collections" "^3.4.4" + "@react-stately/utils" "^3.5.1" + "@react-types/shared" "^3.15.0" + +"@react-stately/slider@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@react-stately/slider/-/slider-3.0.1.tgz#076c149947ae45f5eda30178b368ad0c4052f2a3" + integrity sha512-gGpfdVbTmdsOvrmZvFx4hJ5b7nczvAWdHR/tFFJKfxH0/V8NudZ5hGnawY84R3x+OvgV+tKUfifEUKA+oJyG5w== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/i18n" "^3.3.0" + "@react-aria/utils" "^3.6.0" + "@react-stately/utils" "^3.2.0" + "@react-types/slider" "^3.0.1" + +"@react-stately/slider@^3.0.1", "@react-stately/slider@^3.2.2": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@react-stately/slider/-/slider-3.2.2.tgz#3da9175453fc2b49e72910e8f0f0f8038d6e680d" + integrity sha512-LIgS+6L9PSUanj0stWql5KEEdm28LBTscm3cxBq177aaKhoKu+MvplItmoMeZjXnjwpRBliiZ7hWAJHyCDFE+g== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/i18n" "^3.6.1" + "@react-aria/utils" "^3.14.0" + "@react-stately/utils" "^3.5.1" + "@react-types/shared" "^3.15.0" + "@react-types/slider" "^3.3.0" + +"@react-stately/tabs@3.0.0-alpha.0": + version "3.0.0-alpha.0" + resolved "https://registry.yarnpkg.com/@react-stately/tabs/-/tabs-3.0.0-alpha.0.tgz#41451c7957ab2773fc4edb78ec02fcb94c6ab226" + integrity sha512-QJZ9N7DT89RkP18btvQhJvxWuv/JkSwtm14ftfk+5LBbzyxyLsD2KP6jDrNhXgmkRMmIyEaMt2w2VmI6fQ6UAA== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/list" "^3.2.2" + "@react-stately/utils" "^3.0.0-alpha.1" + "@react-types/tabs" "3.0.0-alpha.2" + +"@react-stately/tabs@3.0.0-alpha.1": + version "3.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/@react-stately/tabs/-/tabs-3.0.0-alpha.1.tgz#b166ca9733ebebcc3bb2223116b8b070af104812" + integrity sha512-aEG5lVLqmfx7A/dS5gkPXmD2ERAo69RtC0aHPo/Dw1XjzalYyo6QbQ5WtiuQxsCVx/naWGEJCcMEAD5/vt+cUQ== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/list" "^3.2.2" + "@react-stately/utils" "^3.2.0" + "@react-types/tabs" "3.0.0-alpha.2" + +"@react-stately/toggle@3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@react-stately/toggle/-/toggle-3.2.1.tgz#8b10b5eb99c3c4df2c36d17a5f23b77773ed7722" + integrity sha512-gZVuJ8OYoATUoXzdprsyx6O1w3wCrN+J0KnjhrjjKTrBG68n3pZH0p6dM0XpsaCzlSv0UgNa4fhHS3dYfr/ovw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/utils" "^3.1.1" + "@react-types/checkbox" "^3.2.1" + "@react-types/shared" "^3.2.1" + +"@react-stately/toggle@^3.2.1", "@react-stately/toggle@^3.2.3", "@react-stately/toggle@^3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@react-stately/toggle/-/toggle-3.4.2.tgz#8c70922ad559d9ef32ecf3cc3d122a66eb858f0d" + integrity sha512-+pO13Ap/tj4optu6VjQrEAaAoZvJAEwarMUaZvrkc0kdvMTNPdiT/2vhN32yvsSW0ssuFqToa3jMrTylCbpo8w== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/utils" "^3.5.1" + "@react-types/checkbox" "^3.4.0" + "@react-types/shared" "^3.15.0" + +"@react-stately/tree@^3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@react-stately/tree/-/tree-3.3.4.tgz#2b71436dd7ed3bd42983f4fd29a9417f947876f9" + integrity sha512-CBgXvwa9qYBsJuxrAiVgGnm48eSxLe/6OjPMwH1pWf4s383Mx73MbbN4fS0oWDeXBVgdqz5/Xg/p8nvPIvl3WQ== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-stately/collections" "^3.4.4" + "@react-stately/selection" "^3.11.0" + "@react-stately/utils" "^3.5.1" + "@react-types/shared" "^3.15.0" + +"@react-stately/utils@^3.0.0-alpha.1", "@react-stately/utils@^3.1.1", "@react-stately/utils@^3.2.0", "@react-stately/utils@^3.2.2", "@react-stately/utils@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.5.1.tgz#502de762e5d33e892347c5f58053674e06d3bc92" + integrity sha512-INeQ5Er2Jm+db8Py4upKBtgfzp3UYgwXYmbU/XJn49Xw27ktuimH9e37qP3bgHaReb5L3g8IrGs38tJUpnGPHA== + dependencies: + "@babel/runtime" "^7.6.2" + +"@react-stately/virtualizer@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@react-stately/virtualizer/-/virtualizer-3.3.1.tgz#1e41f4beeb879b4445b662c1a21d23dd18dbb5ef" + integrity sha512-1bjvrLLto3TewJRfe4bCrRKYUAdE6lPB9fn3kQhpxbWb4KW1Xl7ar/waL7JDzpwxTBbwzbxCS6nL03YxSt5tIw== + dependencies: + "@babel/runtime" "^7.6.2" + "@react-aria/utils" "^3.14.0" + "@react-types/shared" "^3.15.0" + +"@react-types/button@^3.3.1", "@react-types/button@^3.6.2": + version "3.6.2" + resolved "https://registry.yarnpkg.com/@react-types/button/-/button-3.6.2.tgz#72d617deb0f76bd01a570ef28306ac1482c58a67" + integrity sha512-qgrYT6yiGVuZSPbzeDT6kTREQVxzJ9p5chV+JX7G5Rpjl2vyUDkEhZ5V/AHLKguBALgFaWJvjtwejHQ7FtycTw== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/checkbox@^3.2.1", "@react-types/checkbox@^3.2.3", "@react-types/checkbox@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@react-types/checkbox/-/checkbox-3.4.0.tgz#f2d6acabdf953cf2f7b8b874dab9ac8ae1c020fa" + integrity sha512-ZDqbtAYWWSGPjL4ydinaWHrD65Qft9yEGA6BCKQTxdJCgxiXxgGkA3pI7Sxwk+OulR+O0CYJ1JROExM9cSJyyQ== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/combobox@3.0.0-alpha.1": + version "3.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/@react-types/combobox/-/combobox-3.0.0-alpha.1.tgz#4c390d11bc52c248fda92dc7e755f6dc2dc71b82" + integrity sha512-td8pZmzZx5L32DuJ5iQk0Y4DNPerHWc2NXjx88jiQGxtorzvfrIQRKh3sy13PH7AMplGSEdAxG0llfCKrIy0Ow== + dependencies: + "@react-types/shared" "^3.4.0" + +"@react-types/combobox@^3.5.4": + version "3.5.4" + resolved "https://registry.yarnpkg.com/@react-types/combobox/-/combobox-3.5.4.tgz#a911302f49c24629fb86105cd82a7b18172a7520" + integrity sha512-boVrxvARZBuhZSmhcaShty/JXaJRKNm3fx0zKcqyfB8iFpszltMfv2w23cRzc8BVA5bA6RRTsiKf8hEjkqFnxg== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/grid@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@react-types/grid/-/grid-3.1.4.tgz#2275b2f83716e4a0f8aab3e53c5cbc27734b7531" + integrity sha512-i9f2VEnlex5BFV/AdSUGg71xoukn2i/XT2VLxUXUagy23gFxKJk9Xr3BT9bw+pRRLPwm/Ib+h9ELUdgi8lUAKA== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/label@^3.7.0": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@react-types/label/-/label-3.7.0.tgz#ecdff8ea9e37cc6c3b8e8446d2d46095e36ca000" + integrity sha512-33iQQ3aC34+yKECvSHJ8DDWwd32rm2TZhABX513DYwuCupIxs+BrgHvcfp2YLmz2Fh5UTMSfJXDA74Tbd0XwLg== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/listbox@^3.1.1", "@react-types/listbox@^3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@react-types/listbox/-/listbox-3.3.4.tgz#4ebf1158c882f87b504b6f0947dff95729789f1e" + integrity sha512-/wKUxIj/F7JekEFpsxQS4Za3OqwKop3orD67tmJh9xD8Abcj+YjGVNrjQuBvbq1hzTPgefNrb42Y4aqnqZojIg== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/menu@^3.7.2": + version "3.7.2" + resolved "https://registry.yarnpkg.com/@react-types/menu/-/menu-3.7.2.tgz#04a0447f791a7ffa0a6c8dc160cbff3bbeeedefd" + integrity sha512-BXMWrT3VCP6NTf0y7v1YYqRJNXkUKLzGXI+n7Qv9+aiZZfd3NNMyk20byHczhFoT2yuCcU5xhyOXzkxSo6ew3A== + dependencies: + "@react-types/overlays" "^3.6.4" + "@react-types/shared" "^3.15.0" + +"@react-types/overlays@^3.4.0", "@react-types/overlays@^3.6.4": + version "3.6.4" + resolved "https://registry.yarnpkg.com/@react-types/overlays/-/overlays-3.6.4.tgz#4ae4d7b3b38c45d122b0ca2dc88a57f08e89fd0e" + integrity sha512-REC4IyDUHS5WhwxMxcvTo+LdrvlSYpJKjyYkPFtJoDBpM3gmXfakTY3KW6K5eZkFv3TnmXjDF9Q2yboEk2u6WQ== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/radio@^3.1.1", "@react-types/radio@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@react-types/radio/-/radio-3.3.0.tgz#618f836c87d07b0828a660c0565b5149c049039c" + integrity sha512-aF4OpGjd9/xuRnDSDJnmwzLvvOENUWSHQc//wp8rViCWf1uinY4wHI/J3uEhodhsUPAKmrqvUagphRoyXWZA8A== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/select@^3.6.4": + version "3.6.4" + resolved "https://registry.yarnpkg.com/@react-types/select/-/select-3.6.4.tgz#47ddd3d114b030089e96fc97d6895b1408f85c43" + integrity sha512-lbiv7XUEdByluILjEAYVZic2uq4pU0BzW+qZ+sBCYn7v8q2hxXP7T0C6OamzfgXokQHhJ6247JDk7kugqXKL4Q== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/shared@^3.15.0", "@react-types/shared@^3.2.1", "@react-types/shared@^3.3.0", "@react-types/shared@^3.4.0": + version "3.15.0" + resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.15.0.tgz#a4a78f36bc8daaefe6e9a9df1f453271639c2233" + integrity sha512-hwuE4BmgswqP+HRDSLMj7DcnYOCCK+ZRuKnc9AVhXS4LBrwRSkdUkNvXhgvqF5tav7IqTpG9pBYMR9wedehuhA== + +"@react-types/slider@^3.0.1", "@react-types/slider@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@react-types/slider/-/slider-3.3.0.tgz#95d5b8bc73aadbd8b8a09eb936e8b2b4a5b412d5" + integrity sha512-GOYcPhFW4XCHflUlBzZAVVza3Jv2q4xUHrF26Mo1x/7L6/aeABR4Pu/Jjh47pKG7g2MvDYaUv5L1Cui2VywuFA== + dependencies: + "@react-types/shared" "^3.15.0" + +"@react-types/switch@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@react-types/switch/-/switch-3.2.4.tgz#6853793032da50415be1abbac1374fca08ea5e44" + integrity sha512-LFrt8fbEu2QXoZ9FLYLmorCMTrQ3WmvkKpRYaMSj81COxXwIHbByZlH/nzL278fU40GkZGXz2f6ffEYeuc9Vcg== + dependencies: + "@react-types/checkbox" "^3.4.0" + "@react-types/shared" "^3.15.0" + +"@react-types/table@^3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@react-types/table/-/table-3.3.2.tgz#58491920d76d5436bad0193f269743b842b115c5" + integrity sha512-BIYehWSfvPRkneKKKB7YEWD4wZAVVLBf2N0M2jjsVdshK9ZpjQPgOMI6YKjiWGC/ZLZFrAysKRploaIw4Cb+TQ== + dependencies: + "@react-types/grid" "^3.1.4" + "@react-types/shared" "^3.15.0" + +"@react-types/tabs@3.0.0-alpha.2": + version "3.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/@react-types/tabs/-/tabs-3.0.0-alpha.2.tgz#f18c71f4843ae2117b41fdb012f89cc2dd43adf4" + integrity sha512-HQNS2plzuNhKPo88OGEW2Ja9aLeiWqgNqEemSxh0KAjkA8IsvDGaoQEpr9ZQIyBZ3PQIljvOpEJ/IwHU5LztrQ== + dependencies: + "@react-types/shared" "^3.2.1" + +"@react-types/textfield@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@react-types/textfield/-/textfield-3.6.0.tgz#87c7166a045443067a33c56c50962e6899d505c8" + integrity sha512-fEa/lTqpWBn7ZE0cDsc700OhTBAxiy8ifm8AQcLYfZuRX4zqRoMpkXMWN0XMjYfoZzNrS0LJJ6pLBHTwrlXWWA== + dependencies: + "@react-types/shared" "^3.15.0" + "@reduxjs/toolkit@^1.8.3": version "1.8.5" resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.8.5.tgz#c14bece03ee08be88467f22dc0ecf9cf875527cd" @@ -2171,6 +3002,18 @@ expect "^28.0.0" pretty-format "^28.0.0" +"@types/lodash.has@^4.5.6": + version "4.5.7" + resolved "https://registry.yarnpkg.com/@types/lodash.has/-/lodash.has-4.5.7.tgz#9e1c3da3ee67f68fadc3d168ffdf8e11a3f3ccd3" + integrity sha512-nfbAzRbsZBdzSAkL9iiLy4SQk89uuFcXBFwZ7pf6oZhBgPvNys8BY5Twp/w8XvZKGt1o6cAa85wX4QhqO3uQ7A== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.186" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.186.tgz#862e5514dd7bd66ada6c70ee5fce844b06c8ee97" + integrity sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw== + "@types/node@*": version "18.7.14" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.14.tgz#0fe081752a3333392d00586d815485a17c2cf3c9" @@ -2238,6 +3081,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/use-subscription@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/use-subscription/-/use-subscription-1.0.0.tgz#d146f8d834f70f50d48bd8246a481d096f11db19" + integrity sha512-0WWZ5GUDKMXUY/1zy4Ur5/zsC0s/B+JjXfHdkvx6JgDNZzZV5eW+KKhDqsTGyqX56uh99gwGwbsKbVwkcVIKQA== + "@types/use-sync-external-store@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" @@ -2771,6 +3619,11 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + bplist-creator@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" @@ -3115,6 +3968,11 @@ clone@^2.1.2: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== +clsx@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -3363,6 +4221,30 @@ css-in-js-utils@^2.0.0: hyphenate-style-name "^1.0.2" isobject "^3.0.1" +css-select@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-tree@^1.0.0-alpha.39: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" @@ -3585,6 +4467,28 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dom-helpers@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -3592,6 +4496,22 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -3624,6 +4544,11 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + env-editor@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861" @@ -4696,6 +5621,16 @@ internal-ip@4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" +intl-messageformat@^10.1.0: + version "10.1.5" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.1.5.tgz#ec053e0367ee5e4d40a9448ddbb9fff143266a9d" + integrity sha512-bDQz81oQ6TkQp1pSnlBK36ahGL5/tLb0+3hSiG/1/SnVbz5NWPQTqaPx2cT7nmGujDrPn9pQ0ik8RWE8v1lzEw== + dependencies: + "@formatjs/ecma402-abstract" "1.12.0" + "@formatjs/fast-memoize" "1.2.6" + "@formatjs/icu-messageformat-parser" "2.1.8" + tslib "2.4.0" + invariant@*, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -5831,11 +6766,66 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.has@^4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" + integrity sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g== + +lodash.isempty@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" + integrity sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isnil@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lodash.isnil/-/lodash.isnil-4.0.0.tgz#49e28cd559013458c814c5479d3c663a21bfaa6c" + integrity sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash.omit@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" + integrity sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg== + +lodash.omitby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.omitby/-/lodash.omitby-4.6.0.tgz#5c15ff4754ad555016b53c041311e8f079204791" + integrity sha512-5OrRcIVR75M288p4nbI2WLAf3ndw2GD9fyNv3Bc15+WCxJDdZ4lYndSxGd7hnG6PVjiJTeJE2dHEGhIuKGicIQ== + +lodash.pick@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q== + lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" @@ -5956,6 +6946,11 @@ md5hex@^1.0.0: resolved "https://registry.yarnpkg.com/md5hex/-/md5hex-1.0.0.tgz#ed74b477a2ee9369f75efee2f08d5915e52a42e8" integrity sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ== +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -6428,6 +7423,50 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +native-base@^3.4.17: + version "3.4.17" + resolved "https://registry.yarnpkg.com/native-base/-/native-base-3.4.17.tgz#d2ce41cc43175b9d6df6c63c9eb7e0e88b83bf8e" + integrity sha512-tAySi0e42xRTEJcNWH61+B8FS+OyC1891joSBivE3DjZQLxbMHnhYbY7BXIqJnisIuNs1RFjl5MLWzTRxB4RZA== + dependencies: + "@react-aria/focus" "3.2.3" + "@react-aria/utils" "^3.6.0" + "@react-aria/visually-hidden" "^3.2.1" + "@react-native-aria/button" "^0.2.4" + "@react-native-aria/checkbox" "^0.2.2" + "@react-native-aria/combobox" "^0.2.4-alpha.0" + "@react-native-aria/focus" "^0.2.6" + "@react-native-aria/interactions" "^0.2.2" + "@react-native-aria/listbox" "^0.2.4-alpha.3" + "@react-native-aria/overlays" "0.3.3-rc.0" + "@react-native-aria/radio" "^0.2.4" + "@react-native-aria/slider" "^0.2.5-alpha.1" + "@react-native-aria/tabs" "^0.2.7" + "@react-native-aria/utils" "^0.2.8" + "@react-stately/checkbox" "3.0.3" + "@react-stately/collections" "3.3.0" + "@react-stately/combobox" "3.0.0-alpha.1" + "@react-stately/radio" "3.2.1" + "@react-stately/slider" "3.0.1" + "@react-stately/tabs" "3.0.0-alpha.1" + "@react-stately/toggle" "3.2.1" + "@types/lodash.has" "^4.5.6" + "@types/use-subscription" "^1.0.0" + lodash.clonedeep "^4.5.0" + lodash.get "^4.4.2" + lodash.has "^4.5.2" + lodash.isempty "^4.4.0" + lodash.isequal "^4.5.0" + lodash.isnil "^4.0.0" + lodash.merge "^4.6.2" + lodash.mergewith "^4.6.2" + lodash.omit "^4.5.0" + lodash.omitby "^4.6.0" + lodash.pick "^4.4.0" + react-native-keyboard-aware-scroll-view "^0.9.5" + stable-hash "^0.0.2" + tinycolor2 "^1.4.2" + use-subscription "^1.8.0" + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -6560,6 +7599,13 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -7016,7 +8062,7 @@ prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2, prompts@^2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@*, prop-types@^15.6.0: +prop-types@*, prop-types@^15.6.0, prop-types@^15.6.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -7158,11 +8204,19 @@ react-native-gradle-plugin@^0.0.6: resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.6.tgz#b61a9234ad2f61430937911003cddd7e15c72b45" integrity sha512-eIlgtsmDp1jLC24dRn43hB3kEcZVqx6DUQbR0N1ABXGnMEafm9I3V3dUUeD1vh+Dy5WqijSoEwLNUPLgu5zDMg== -react-native-iphone-x-helper@^1.3.1: +react-native-iphone-x-helper@^1.0.3, react-native-iphone-x-helper@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010" integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg== +react-native-keyboard-aware-scroll-view@^0.9.5: + version "0.9.5" + resolved "https://registry.yarnpkg.com/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.9.5.tgz#e2e9665d320c188e6b1f22f151b94eb358bf9b71" + integrity sha512-XwfRn+T/qBH9WjTWIBiJD2hPWg0yJvtaEw6RtPCa5/PYHabzBaWxYBOl0usXN/368BL1XktnZPh8C2lmTpOREA== + dependencies: + prop-types "^15.6.2" + react-native-iphone-x-helper "^1.0.3" + react-native-paper@^4.12.4: version "4.12.4" resolved "https://registry.yarnpkg.com/react-native-paper/-/react-native-paper-4.12.4.tgz#c04ce40d225dc267b7ee32be705dd553f44e0c0c" @@ -7185,6 +8239,14 @@ react-native-screens@~3.11.1: react-freeze "^1.0.0" warn-once "^0.1.0" +react-native-svg@12.3.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-12.3.0.tgz#40f657c5d1ee366df23f3ec8dae76fd276b86248" + integrity sha512-ESG1g1j7/WLD7X3XRFTQHVv0r6DpbHNNcdusngAODIxG88wpTWUZkhcM3A2HJTb+BbXTFDamHv7FwtRKWQ/ALg== + dependencies: + css-select "^4.2.1" + css-tree "^1.0.0-alpha.39" + react-native-testing-library@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/react-native-testing-library/-/react-native-testing-library-6.0.0.tgz#b6e1c2213008dc62bdf28bb1474dfaca9cece058" @@ -7967,6 +9029,11 @@ ssri@^8.0.1: dependencies: minipass "^3.1.1" +stable-hash@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.2.tgz#a909deaa5b9d430b100ca0a10132a533f2665e94" + integrity sha512-tPwQ3c1rLIwbJpq59duoznegEbmgfV630C2n4R4G96LKBFljgK8j+O9AxjqB6cAzu4gE7s4pByrLWtZel8E+Mg== + stack-utils@^2.0.2, stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" @@ -8261,6 +9328,11 @@ through@2: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tinycolor2@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" + integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -8352,16 +9424,16 @@ ts-interface-checker@^0.1.9: resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== +tslib@2.4.0, tslib@^2.0.1, tslib@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^1.10.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1, tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -8582,6 +9654,13 @@ use-latest-callback@^0.1.5: dependencies: object-assign "^4.1.1" +use-subscription@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.8.0.tgz#f118938c29d263c2bce12fc5585d3fe694d4dbce" + integrity sha512-LISuG0/TmmoDoCRmV5XAqYkd3UCBNM0ML3gGBndze65WITcsExCD3DTvXXTLyNcOC0heFQZzluW88bN/oC1DQQ== + dependencies: + use-sync-external-store "^1.2.0" + use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" From 599e785969ef4143451bb3b99acc891ad9dce743 Mon Sep 17 00:00:00 2001 From: Arthi-chaud Date: Thu, 6 Oct 2022 20:06:49 +0100 Subject: [PATCH 12/15] Front: Add Theme Provider at root of app --- front/App.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/front/App.tsx b/front/App.tsx index 9921778..d6dda24 100644 --- a/front/App.tsx +++ b/front/App.tsx @@ -1,4 +1,3 @@ -import { Provider as PaperProvider } from 'react-native-paper'; import Theme from './Theme'; import React from 'react'; import { QueryClient, QueryClientProvider } from 'react-query'; @@ -6,16 +5,18 @@ import { Provider } from 'react-redux'; import store from './state/Store'; import { Router } from './Navigation'; import './i18n/i18n'; +import { NativeBaseProvider, extendTheme } from "native-base"; + const queryClient = new QueryClient(); export default function App() { - return ( - - - - - - - - ); + return ( + + + + + + + + ); } From ccb0e16c04737e2b16400e563d25e392ea91396b Mon Sep 17 00:00:00 2001 From: Arthi-chaud Date: Thu, 6 Oct 2022 20:07:18 +0100 Subject: [PATCH 13/15] Front: Apply Native Base on Auth and home view --- front/views/AuthenticationView.tsx | 2 +- front/views/HomeView.tsx | 47 +++++++++++++++--------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/front/views/AuthenticationView.tsx b/front/views/AuthenticationView.tsx index 1091191..89d630c 100644 --- a/front/views/AuthenticationView.tsx +++ b/front/views/AuthenticationView.tsx @@ -9,7 +9,7 @@ const AuthenticationView = () => { return (
{translate('welcome')} -
diff --git a/front/views/HomeView.tsx b/front/views/HomeView.tsx index d17afb2..3d542ef 100644 --- a/front/views/HomeView.tsx +++ b/front/views/HomeView.tsx @@ -1,35 +1,34 @@ import { useNavigation } from "@react-navigation/native"; import React from "react"; -import { View } from 'react-native'; -import { Button, Text } from "react-native-paper"; +import { Center, Button, Text } from "native-base"; import { useDispatch, useSelector } from "react-redux"; import { AvailableLanguages, DefaultLanguage, translate } from "../i18n/i18n"; import { useLanguage } from "../state/LanguageSlice"; import { unsetUserToken } from "../state/UserSlice"; const HomeView = () => { - const dispatch = useDispatch(); - const navigation = useNavigation(); - const language: AvailableLanguages = useSelector((state) => state.language.value); - return ( - - This is the Home Screen - - - - Current language: { language } - - ); + const dispatch = useDispatch(); + const navigation = useNavigation(); + const language: AvailableLanguages = useSelector((state) => state.language.value); + return ( +
+ This is the Home Screen + + + + Current language: {language} +
+ ); } export default HomeView; From e29a5b700c919a84d8ac1aaa9ece57a91071e23c Mon Sep 17 00:00:00 2001 From: Arthi-chaud Date: Fri, 7 Oct 2022 09:24:27 +0100 Subject: [PATCH 14/15] Front: Mograte Style for SongLobby View --- front/views/SongLobbyView.tsx | 92 +++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/front/views/SongLobbyView.tsx b/front/views/SongLobbyView.tsx index f369ef3..3845430 100644 --- a/front/views/SongLobbyView.tsx +++ b/front/views/SongLobbyView.tsx @@ -1,6 +1,5 @@ import { useRoute } from "@react-navigation/native"; -import { Image, View } from "react-native" -import { Button, Divider, IconButton, List, Surface, Text } from "react-native-paper"; +import { Button, Divider, Box, Center, Image, Text, VStack, HStack, PresenceTransition, Icon } from "native-base"; import API from "../API"; import { useQuery } from 'react-query'; import LoadingComponent from "../components/loading"; @@ -8,6 +7,7 @@ import React, { useEffect, useState } from "react"; import logo from '../assets/cover.png'; import { translate } from "../i18n/i18n"; import formatDuration from "format-duration"; +import { Ionicons, FontAwesome } from '@expo/vector-icons'; interface SongLobbyProps { // The unique identifier to find a song @@ -27,54 +27,60 @@ const SongLobbyView = () => { }, [chaptersOpen]); useEffect(() => {}, [songQuery.isLoading]); if (songQuery.isLoading || scoresQuery.isLoading) - return + return
- +
return ( - - - + + + - - - - - {songQuery.data!.title} + + + + + {songQuery.data!.title} {'3:20'} - {translate('level')} { chaptersQuery.data!.reduce((a, b) => a + b.difficulty, 0) / chaptersQuery.data!.length } - - - - - - - {translate('bestScore') } + + + + + + + {translate('bestScore') } {scoresQuery.data!.sort()[0]?.score} - - - {translate('lastScore') } + + + {translate('lastScore') } {scoresQuery.data!.slice(-1)[0]!.score} - - + + {songQuery.data!.description} - setChaptersOpen(!chaptersOpen)}> - { chaptersQuery.isLoading && } - { !chaptersQuery.isLoading && chaptersQuery.data!.map((chapter) => - <> - - - - )} - - + + + + + { chaptersQuery.isLoading && } + { !chaptersQuery.isLoading && + }> + { chaptersQuery.data!.map((chapter) => + + {chapter.name} + + {`${translate('level')} ${chapter.difficulty} - ${formatDuration((chapter.end - chapter.start) * 1000)}`} + + + )} + + } + + ) } From 6a31062336e1345961b0767376155d3f81c4550a Mon Sep 17 00:00:00 2001 From: Arthi-chaud Date: Fri, 7 Oct 2022 09:29:58 +0100 Subject: [PATCH 15/15] Front: Override redux functions for type safety --- front/App.tsx | 2 +- front/Navigation.tsx | 2 +- front/state/Store.ts | 15 +++++++++++++-- front/views/AuthenticationView.tsx | 2 +- front/views/HomeView.tsx | 2 +- front/views/SongLobbyView.tsx | 4 ++-- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/front/App.tsx b/front/App.tsx index d6dda24..4cca4bf 100644 --- a/front/App.tsx +++ b/front/App.tsx @@ -5,7 +5,7 @@ import { Provider } from 'react-redux'; import store from './state/Store'; import { Router } from './Navigation'; import './i18n/i18n'; -import { NativeBaseProvider, extendTheme } from "native-base"; +import { NativeBaseProvider } from "native-base"; const queryClient = new QueryClient(); diff --git a/front/Navigation.tsx b/front/Navigation.tsx index 9755f88..ff04104 100644 --- a/front/Navigation.tsx +++ b/front/Navigation.tsx @@ -3,7 +3,7 @@ import React from 'react'; import AuthenticationView from './views/AuthenticationView'; import HomeView from './views/HomeView'; import { NavigationContainer } from '@react-navigation/native'; -import { useSelector } from 'react-redux'; +import { useSelector } from './state/Store'; import SongLobbyView from './views/SongLobbyView'; import { translate } from './i18n/i18n'; diff --git a/front/state/Store.ts b/front/state/Store.ts index 253f578..9a2d390 100644 --- a/front/state/Store.ts +++ b/front/state/Store.ts @@ -1,10 +1,21 @@ import userReducer from '../state/UserSlice'; import { configureStore } from '@reduxjs/toolkit'; import languageReducer from './LanguageSlice'; +import { TypedUseSelectorHook, useDispatch as reduxDispatch, useSelector as reduxSelector } from 'react-redux' -export default configureStore({ +const store = configureStore({ reducer: { user: userReducer, language: languageReducer }, -}) \ No newline at end of file +}) + +// Infer the `RootState` and `AppDispatch` types from the store itself +export type RootState = ReturnType +// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} +export type AppDispatch = typeof store.dispatch; +// Use throughout your app instead of plain `useDispatch` and `useSelector` +export const useDispatch: () => AppDispatch = reduxDispatch +export const useSelector: TypedUseSelectorHook = reduxSelector + +export default store \ No newline at end of file diff --git a/front/views/AuthenticationView.tsx b/front/views/AuthenticationView.tsx index 89d630c..416c8ff 100644 --- a/front/views/AuthenticationView.tsx +++ b/front/views/AuthenticationView.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useDispatch } from "react-redux"; +import { useDispatch } from '../state/Store'; import { translate } from "../i18n/i18n"; import { setUserToken } from "../state/UserSlice"; import { Center, Button, Text } from 'native-base'; diff --git a/front/views/HomeView.tsx b/front/views/HomeView.tsx index 3d542ef..3a3d0e3 100644 --- a/front/views/HomeView.tsx +++ b/front/views/HomeView.tsx @@ -1,7 +1,7 @@ import { useNavigation } from "@react-navigation/native"; import React from "react"; import { Center, Button, Text } from "native-base"; -import { useDispatch, useSelector } from "react-redux"; +import { useDispatch, useSelector } from "../state/Store"; import { AvailableLanguages, DefaultLanguage, translate } from "../i18n/i18n"; import { useLanguage } from "../state/LanguageSlice"; import { unsetUserToken } from "../state/UserSlice"; diff --git a/front/views/SongLobbyView.tsx b/front/views/SongLobbyView.tsx index 3845430..7b0d3d3 100644 --- a/front/views/SongLobbyView.tsx +++ b/front/views/SongLobbyView.tsx @@ -1,5 +1,5 @@ import { useRoute } from "@react-navigation/native"; -import { Button, Divider, Box, Center, Image, Text, VStack, HStack, PresenceTransition, Icon } from "native-base"; +import { Button, Divider, Box, Center, Image, Text, VStack, PresenceTransition, Icon } from "native-base"; import API from "../API"; import { useQuery } from 'react-query'; import LoadingComponent from "../components/loading"; @@ -7,7 +7,7 @@ import React, { useEffect, useState } from "react"; import logo from '../assets/cover.png'; import { translate } from "../i18n/i18n"; import formatDuration from "format-duration"; -import { Ionicons, FontAwesome } from '@expo/vector-icons'; +import { Ionicons } from '@expo/vector-icons'; interface SongLobbyProps { // The unique identifier to find a song