147 Commits
v0.8.4 ... main

Author SHA1 Message Date
Clément Le Bihan
74f804bb9a Myatom 2024-01-18 10:59:48 +01:00
Clément Le Bihan
0b78772d0b Fix canva crash on mobile 2024-01-18 10:59:48 +01:00
Clément Le Bihan
9e3c2d1cca added playType correct check 2024-01-18 10:59:48 +01:00
Clément Le Bihan
1b097163a4 removed div 2024-01-18 10:59:48 +01:00
GitBluub
c61d17baa7 fix ? 2024-01-18 10:59:48 +01:00
GitBluub
be8867e12f scoro wrongs msgs 2024-01-18 05:30:21 +01:00
GitBluub
00f98151c1 list compreeeheeension 2024-01-18 04:04:55 +01:00
GitBluub
5d78d8b5dd use cursor to create partition 2024-01-18 04:04:55 +01:00
GitBluub
0bd12bbf34 pretty 2024-01-18 01:21:03 +01:00
GitBluub
88cb7b2b65 is working i swear 2024-01-18 01:21:03 +01:00
GitBluub
69329118f7 aaaaaa 2024-01-18 01:21:03 +01:00
GitBluub
2781276c12 fix prettier 2024-01-18 01:21:03 +01:00
GitBluub
a24a960184 fix practice mode no sound and score system during practice 2024-01-18 01:21:03 +01:00
GitBluub
9fd70d3110 fix normal scoro mode 2024-01-18 01:21:03 +01:00
GitBluub
c1d714e02a practice mode working, timestamp is delayed for some reason 2024-01-18 01:21:03 +01:00
GitBluub
c08a1a2c74 front practice basically working, need cleanup 2024-01-18 01:21:03 +01:00
GitBluub
23a1ff8d19 front practice listener and scorometer sending timestamp 2024-01-18 01:21:03 +01:00
GitBluub
b80167001f practice scoro rework 2024-01-18 01:21:03 +01:00
Clément Le Bihan
8c2a53aa41 Added profile view mobile 2024-01-16 16:20:56 +01:00
Clément Le Bihan
dcca780f2d pretty fix 2024-01-15 02:14:43 +01:00
Clément Le Bihan
9150817c05 Removed custom keyextractor of flatlist in musicList due to weird behaviours and still using special icon button for web 2024-01-15 02:14:43 +01:00
Clément Le Bihan
d57606dd53 Fixed flatlist keyextractor 2024-01-15 02:14:43 +01:00
Clément Le Bihan
52f2c94fb7 We can now see MusicItem IconButtons on Android (Play & Likes) 2024-01-15 02:14:43 +01:00
Clément Le Bihan
1952625098 Search results are now visible on android 2024-01-15 02:14:43 +01:00
Clément Le Bihan
10dbfda8a4 Fix search input bar wasn't visible on android 2024-01-15 02:14:43 +01:00
Clément Le Bihan
234335cf61 TextFormField now display error message without part of it being cut 2024-01-15 02:14:43 +01:00
Clément Le Bihan
52d40b43f0 Added mail in the ProfileView 2024-01-15 02:14:43 +01:00
Clément Le Bihan
50522bbe63 Fixed missing transflations in ScaffoldAuth and fontSize issues in Leaderboard view 2024-01-15 02:14:43 +01:00
Clément Le Bihan
ce927ea1a4 Added missing padding seen mostly on mobile and fixed some font rendering 2024-01-15 02:14:43 +01:00
Clément Le Bihan
aebf409cea Fixed dark glassmorphism theme on mobile 2024-01-15 02:14:43 +01:00
Clément Le Bihan
5f0ea41c04 Merge pull request #358 from Chroma-Case/feat/adc/search-view-v2
Feat/adc/search view v2
2024-01-14 18:26:32 +01:00
Clément Le Bihan
d3c7e4a0a1 Merge branch 'main' into feat/adc/search-view-v2 2024-01-14 17:55:07 +01:00
Amaury Danis Cousandier
a3893bdb2b yay 2024-01-14 16:21:26 +01:00
Amaury Danis Cousandier
4ba4303b1e fix(../V2/SearchView): actual music list used + minor fixes 2024-01-14 16:13:14 +01:00
e779876f54 Fix id parsing 2024-01-14 14:37:30 +01:00
bd9edaa60e Oups 2024-01-14 14:17:59 +01:00
Amaury Danis Cousandier
f2ad34c8ab feat(search view v2): update API.searchSongs 2024-01-13 10:00:59 +01:00
Amaury Danis Cousandier
131d7bf688 Merge branch 'main' into feat/adc/search-view-v2 2024-01-13 08:32:32 +01:00
Clément Le Bihan
d44e75a83a Added missing Int parses 2024-01-13 01:10:58 +01:00
e487d6d91e Fix total score increment 2024-01-13 01:10:58 +01:00
7a63a66da5 Rework music list 2024-01-13 01:10:58 +01:00
17f64cd849 Add isLiked bool on the front 2024-01-13 01:10:58 +01:00
ec17aa741f Allow search query to be empty 2024-01-13 01:10:58 +01:00
Amaury Danis Cousandier
38110d2840 merge dev NOT READY TO MERGE INTO MAIN 2024-01-12 19:28:20 +01:00
Amaury Danis Cousandier
fd60f2d171 artist and genre keys to refetch without changing the query 2024-01-12 17:56:42 +01:00
Amaury Danis Cousandier
86b2c1be50 histo 2024-01-12 17:53:39 +01:00
Amaury Danis Cousandier
627b8df658 css fixed 2024-01-12 16:54:54 +01:00
Amaury Danis Cousandier
3f0d0d523b like state 2024-01-12 09:19:06 +01:00
Amaury Danis Cousandier
29a9ffce74 pretty tsc lint 2024-01-11 17:56:32 +01:00
Amaury Danis Cousandier
a69e5ac009 fix artist name 2024-01-11 17:54:50 +01:00
Amaury Danis Cousandier
caa3322676 liked handled properly 2024-01-11 17:46:27 +01:00
Clément Le Bihan
358841abd5 Fixed misplaced translation 2024-01-10 21:27:12 +01:00
Clément Le Bihan
64e7dbc71e removed rate modification of the song since I have nothing to rely on 2024-01-10 21:27:12 +01:00
Clément Le Bihan
5a0809c1d0 removed SoundPlayerSlice 2024-01-10 21:27:12 +01:00
Clément Le Bihan
4b5e3d2b04 Removed piano keys sounds 2024-01-10 21:27:12 +01:00
Clément Le Bihan
5f24c6e7bd using smplr player on web and mp3 on mobile 2024-01-10 21:27:12 +01:00
Clément Le Bihan
8bdf8ce334 wip 2024-01-10 21:27:12 +01:00
Clément Le Bihan
9012a6a9d8 new script to generate mp3s 2024-01-10 21:27:12 +01:00
Clément Le Bihan
c5fd4aa7d5 modified mp3 generation & new mp3 generated 2024-01-10 21:27:12 +01:00
Clément Le Bihan
65cd04a494 moving partition magic from being timestamp controlled to be controlled by the audio 2024-01-10 21:27:12 +01:00
Clément Le Bihan
c79ae7c6e8 fix message display and now using the API to get the correct melody url 2024-01-10 21:27:12 +01:00
Clément Le Bihan
ddc97f0923 added route to get elody file of the song 2024-01-10 21:27:12 +01:00
Clément Le Bihan
a9b902a427 Moved score animation into its own component 2024-01-10 21:27:12 +01:00
Clément Le Bihan
96d8e649c8 removed dupliacte popupCC in dom and now using two function less 2024-01-10 21:27:12 +01:00
Clément Le Bihan
22c93b7571 Experiment with full mp3 2024-01-10 21:27:12 +01:00
Clément Le Bihan
0644d4b580 Added melodies 2024-01-10 21:27:12 +01:00
Clément Le Bihan
ee6a76cdd9 experiments with expo av 2024-01-10 21:27:12 +01:00
Amaury Danis Cousandier
934010a0c1 tsc pretty lint 2024-01-09 21:35:48 +01:00
Amaury Danis Cousandier
29b2bedae0 wip 2024-01-09 21:28:37 +01:00
Clément Le Bihan
5ba815590a ci fix 2024-01-09 17:34:35 +01:00
Clément Le Bihan
dd09827d08 minor fixes 2024-01-09 17:34:35 +01:00
b5b94adc83 Format code 2024-01-09 17:34:35 +01:00
3c04e8bb39 Fix types 2024-01-09 17:34:35 +01:00
17a4328af5 Better safe area handling everywhere 2024-01-09 17:34:35 +01:00
e81f2c1f75 Handle safe areas with tabs 2024-01-09 17:34:35 +01:00
f77874bec4 Fix desktop scafold background color 2024-01-09 17:34:35 +01:00
cfc72b8bc1 Fix margins with the desktop scaffold 2024-01-09 17:34:35 +01:00
359b20fc6d Add a screen wrapper in desktop mode 2024-01-09 17:34:35 +01:00
a3659618ea Add the desktop scaffold 2024-01-09 17:34:35 +01:00
fa60fc65a9 Steal the createBottomNavigaton from react-navigation 2024-01-09 17:34:35 +01:00
b1727b7838 Fix gradient in white mode 2024-01-09 17:34:35 +01:00
a3f4703dae Fix routes for logged out users 2024-01-09 17:34:35 +01:00
038918c212 Use a custom tabbar 2024-01-09 17:34:35 +01:00
42a947dfb0 Remove all scafoldcc instances 2024-01-09 17:34:35 +01:00
5525110d39 Add a bottom tab navigator 2024-01-09 17:34:35 +01:00
7160b77607 Fix .env.example 2024-01-09 17:34:35 +01:00
b5183f84b4 wip 2024-01-09 17:34:35 +01:00
Amaury Danis Cousandier
7a2b877714 feat(searchview2): wip 2024-01-08 23:31:15 +01:00
Amaury
9416393618 Update SearchView.tsx var name 2024-01-08 12:24:38 +01:00
Amaury
eb245118dc style(../V2/SearchView): function name change 2024-01-08 12:17:48 +01:00
mathysPaul
13050e52f9 [IMP] lint, prettier, tsc 2024-01-08 01:27:39 +01:00
mathysPaul
5ef3885f72 [FIX] MusicList, MusicItem, IconButton: Prevent double-add on consecutive likes
Fixes the issue where consecutive likes on a track mistakenly
added it twice to the liked list. Now ensures correct toggling
between like and unlike.
2024-01-08 01:27:39 +01:00
mathysPaul
a103666caf [REF] MusicView.tsx: Refactor & create MusicListCC 2024-01-08 01:27:39 +01:00
mathysPaul
29da5c2788 [ADD] Music view with Favorite, Last played and suggestion tabs 2024-01-08 01:27:39 +01:00
danis
40f16ab9ca lint 2024-01-08 00:00:40 +01:00
danis
a33d56bd61 fix(searchview2): fix types and remove deprecated search components 2024-01-07 23:54:35 +01:00
danis
c7c9250594 Merge branch 'main' into feat/adc/search-view-v2 2024-01-07 23:43:32 +01:00
danis
1b1659fe92 fix(../V2/SearchView): key 2024-01-07 23:34:52 +01:00
danis
3c9d71a757 feat(../V2/SearchView): artist name retrieval 2024-01-07 23:26:03 +01:00
danis
342099157e feat(../V2/SearchView): wip 2024-01-07 23:17:20 +01:00
danis
bb7a17fc22 feat(../V2/SearchView: copied working parts of music list 2024-01-07 17:22:02 +01:00
Arthur Jamet
1880b89b0c CI: Trigger Job if their source file has changes 2024-01-07 10:34:56 +01:00
Arthur Jamet
e769ff1f13 CI: Attempt to fix Action's trigger 2024-01-07 10:34:56 +01:00
danis
0ea8cb86bb fix(searchViewV2): the fat of the land 2024-01-06 15:17:18 +01:00
danis
90f9574a6f merge main 2024-01-06 10:53:59 +01:00
danis
f2f7ec3f8d fixed a thing or two 2024-01-06 10:50:57 +01:00
Clément Le Bihan
9e7873cdd7 Merge pull request #352 from Chroma-Case/feat/adc/search-history-V2
Feat/adc/search history v2
2024-01-04 22:24:34 +01:00
Clément Le Bihan
f46c2cfb4a fix ci 2024-01-04 22:22:56 +01:00
Clément Le Bihan
9f14061efd Now using european date format 2024-01-04 22:21:11 +01:00
danis
88b111529b Merge branch 'main' into feat/adc/search-view-v2 2024-01-04 21:02:40 +01:00
danis
851ee7420f fix(../V2/SearchHistory): fixed hard coded color + lightmode thing 2024-01-04 19:31:36 +01:00
danis
ef57eb752d fix(../V2/SearchHistory): fixed background width of history type prop 2024-01-04 18:41:44 +01:00
danis
fcb29ae484 Merge branch 'main' into feat/adc/search-history-V2 2024-01-02 20:52:57 +01:00
danis
5c4847ae2c style(searchBarV2): fixed coding style eslint error 2024-01-02 20:24:48 +01:00
Arthur Jamet
60a73781bd Front: Lint + format 2023-12-29 18:13:40 +01:00
Arthur Jamet
4e3b378d6a Front: Lint + format 2023-12-29 18:13:40 +01:00
Clément Le Bihan
2bf1e783a9 removed unused var 2023-12-29 18:13:40 +01:00
Clément Le Bihan
375d36f6c5 Fixed google logo for mobile 2023-12-29 18:13:40 +01:00
Clément Le Bihan
495380ec43 Fix CI 2023-12-29 18:13:40 +01:00
Clément Le Bihan
af0531bb0c Fixed the like button and now desactivated the click on card to go to song and changed default display for score from '?' to '-' 2023-12-29 18:13:40 +01:00
Arthur Jamet
c5124fa6ad Front: MusicView: Fix Wrong Mutation 2023-12-29 18:13:40 +01:00
Arthur Jamet
962cf58e77 Front: DiscoveryView: USe Like status 2023-12-29 18:13:40 +01:00
Arthur Jamet
60988dd599 Front: Use Mutations to update 'liked' state 2023-12-29 18:13:40 +01:00
Arthur Jamet
004a541302 Front: Lint + format 2023-12-28 12:07:35 +01:00
Arthur Jamet
f4cd9e18ea Front: Explain how to DL the APK 2023-12-28 12:07:35 +01:00
Arthur Jamet
2dc301addf Front: add Button to Download APK From Web 2023-12-28 12:07:35 +01:00
Arthur Jamet
e85a959c26 Front: remove Visible IDs 2023-12-22 17:37:21 +01:00
danis
5fc937d81b wip 2023-12-22 11:37:15 +01:00
danis
b3853646cb t1 2023-12-21 17:40:23 +01:00
Arthur Jamet
339e808d27 Front: SettingsView: Fox ordering of tabs 2023-12-21 17:17:47 +01:00
Arthur Jamet
22d1a97abd Front: SettingsView: Fox ordering of tabs 2023-12-21 17:17:47 +01:00
Arthur Jamet
ce4baa61dc Front: serve Google logo ourselves 2023-12-21 17:17:47 +01:00
Arthur Jamet
e90c7f05a8 Front: Remove use of external images for placeholders 2023-12-21 17:17:47 +01:00
Arthur Jamet
fb0e43af88 Front: Prettier 2023-12-21 17:17:47 +01:00
Arthur Jamet
4577997b1c Front :add spanish translations 2023-12-21 17:17:47 +01:00
Arthur Jamet
9bb256f2ee front: add missing translation components 2023-12-21 17:17:47 +01:00
Arthur Jamet
d3994ff26e Front: First Pass on translations + remove unused setting tabs 2023-12-21 17:17:47 +01:00
Clément Le Bihan
00d097f643 Fixes prettier 2023-12-20 12:01:55 +01:00
Arthur Jamet
99da77f23e Front: Fix cirular dependecy between validators 2023-12-20 12:01:55 +01:00
Arthur Jamet
7a6dc8b0c9 Front: Use history include to get best/last score for a song 2023-12-20 12:01:55 +01:00
Clément Le Bihan
b4f04f9b71 Fixed number of lignes on DiscoveryCard 2023-12-19 17:06:30 +01:00
Arthur Jamet
9df0c98100 Front: DiscoveryView: Remove Dummy Data 2023-12-19 15:03:18 +01:00
danis
dac9849ef5 bug fix 2023-12-07 20:17:18 +01:00
danis
11ed8f90fd stupid hooks rules 2023-12-07 20:08:21 +01:00
danis
5d103c6687 feat(search): proper data passing through handler 2023-12-07 17:18:00 +01:00
danis
be926dcaed feat(search): exchange between search bar and searchView. paella 2023-12-07 16:48:25 +01:00
danis
3353a17611 feat(search histo v2): created search history component + historyRow + fetching da things 2023-12-06 22:41:02 +01:00
199 changed files with 13208 additions and 2914 deletions

View File

@@ -16,9 +16,10 @@ GOOGLE_CALLBACK_URL=http://localhost:19006/logged/google
SMTP_TRANSPORT=smtps://toto:tata@relay
MAIL_AUTHOR='"Chromacase" <chromacase@octohub.app>'
IGNORE_MAILS=true
API_KEYS=SCOROTEST,ROBOTO,SCORO
API_KEYS=SCOROTEST,ROBOTO,SCORO,POPULATE
API_KEY_SCORO_TEST=SCOROTEST
API_KEY_ROBOT=ROBOTO
API_KEY_SCORO=SCORO
API_KEY_POPULATE=POPULATE
MEILI_MASTER_KEY="ghvjkgisbgkbgskegblfqbgjkebbhgwkjfb"
# vi: ft=sh

View File

@@ -12,27 +12,30 @@ jobs:
pull-requests: read
# Set job outputs to values from filter step
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
scoro: ${{ steps.filter.outputs.scoro }}
back: ${{ steps.filter.outputs.back }}
front: ${{ steps.filter.outputs.front }}
scorometer: ${{ steps.filter.outputs.scorometer }}
steps:
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'
scoro:
back:
- 'back/**'
- '.github/workflows/back.yml'
front:
- 'front/**'
- '.github/workflows/front.yml'
scorometer:
- 'scorometer/**'
- '.github/workflows/scoro.yml'
back_build:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
if: ${{ needs.changes.outputs.back == 'true' }}
defaults:
run:
working-directory: ./back
@@ -47,7 +50,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
needs: [ back_build ]
if: ${{ needs.changes.outputs.frontend == 'true' }}
if: ${{ needs.changes.outputs.back == 'true' }}
steps:
- uses: actions/checkout@v3

View File

@@ -12,28 +12,31 @@ jobs:
pull-requests: read
# Set job outputs to values from filter step
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
scoro: ${{ steps.filter.outputs.scoro }}
back: ${{ steps.filter.outputs.back }}
front: ${{ steps.filter.outputs.front }}
scorometer: ${{ steps.filter.outputs.scorometer }}
steps:
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'
scoro:
back:
- 'back/**'
- '.github/workflows/back.yml'
front:
- 'front/**'
- '.github/workflows/front.yml'
scorometer:
- 'scorometer/**'
- '.github/workflows/scoro.yml'
front_check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./front
needs: changes
if: ${{ needs.changes.outputs.frontend == 'true' }}
if: ${{ needs.changes.outputs.front == 'true' }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
@@ -54,7 +57,7 @@ jobs:
defaults:
run:
working-directory: ./front
if: ${{ needs.changes.outputs.frontend == 'true' }}
if: ${{ needs.changes.outputs.front == 'true' }}
needs: [ front_check ]
steps:
- uses: actions/checkout@v3

View File

@@ -11,25 +11,28 @@ jobs:
pull-requests: read
# Set job outputs to values from filter step
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
scoro: ${{ steps.filter.outputs.scoro }}
back: ${{ steps.filter.outputs.back }}
front: ${{ steps.filter.outputs.front }}
scorometer: ${{ steps.filter.outputs.scorometer }}
steps:
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'
scoro:
back:
- 'back/**'
- '.github/workflows/back.yml'
front:
- 'front/**'
- '.github/workflows/front.yml'
scorometer:
- 'scorometer/**'
- '.github/workflows/scoro.yml'
scoro_test:
runs-on: ubuntu-latest
needs: changes
if: ${{ needs.changes.outputs.scoro == 'true' }}
if: ${{ needs.changes.outputs.scorometer == 'true' }}
steps:
- uses: actions/checkout@v3

19
assets/create_melodies.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Iterate through subfolders
find . -type d | while read -r dir; do
# Check if .midi file exists in the subfolder
midi_file=$(find "$dir" -maxdepth 1 -type f -name '*.midi' | head -n 1)
if [ -n "$midi_file" ]; then
# Create output file name (melody.mp3) in the same subfolder
output_file="${dir}/melody.mp3"
# Run the given command
#timidity "$midi_file" -Ow -o - | ffmpeg -i - -acodec libmp3lame -ab 64k "$output_file"
fluidsynth -a alsa -T raw -F - "$midi_file" | ffmpeg -f s32le -i - "$output_file"
echo "Converted: $midi_file to $output_file"
fi
done

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +1,3 @@
FROM node:17
FROM node:18.10.0
WORKDIR /app
CMD npm i ; npx prisma generate ; npx prisma migrate dev ; npm run start:dev

10761
back/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,7 @@ import {
Response,
Query,
Param,
ParseIntPipe,
} from "@nestjs/common";
import { AuthService } from "./auth.service";
import { JwtAuthGuard } from "./jwt-auth.guard";
@@ -287,8 +288,8 @@ export class AuthController {
@ApiOkResponse({ description: "Successfully added liked song" })
@ApiUnauthorizedResponse({ description: "Invalid token" })
@Post("me/likes/:id")
addLikedSong(@Request() req: any, @Param("id") songId: number) {
return this.usersService.addLikedSong(+req.user.id, +songId);
addLikedSong(@Request() req: any, @Param("id", ParseIntPipe) songId: number) {
return this.usersService.addLikedSong(+req.user.id, songId);
}
@UseGuards(JwtAuthGuard)
@@ -296,8 +297,11 @@ export class AuthController {
@ApiOkResponse({ description: "Successfully removed liked song" })
@ApiUnauthorizedResponse({ description: "Invalid token" })
@Delete("me/likes/:id")
removeLikedSong(@Request() req: any, @Param("id") songId: number) {
return this.usersService.removeLikedSong(+req.user.id, +songId);
removeLikedSong(
@Request() req: any,
@Param("id", ParseIntPipe) songId: number,
) {
return this.usersService.removeLikedSong(+req.user.id, songId);
}
@UseGuards(JwtAuthGuard)
@@ -317,7 +321,7 @@ export class AuthController {
@ApiOkResponse({ description: "Successfully added score" })
@ApiUnauthorizedResponse({ description: "Invalid token" })
@Patch("me/score/:score")
addScore(@Request() req: any, @Param("id") score: number) {
addScore(@Request() req: any, @Param("score", ParseIntPipe) score: number) {
return this.usersService.addScore(+req.user.id, score);
}
}

View File

@@ -1,4 +1,4 @@
import { Controller, Get } from "@nestjs/common";
import { Controller, Get, Put } from "@nestjs/common";
import { ApiOkResponse, ApiTags } from "@nestjs/swagger";
import { ScoresService } from "./scores.service";
import { User } from "@prisma/client";
@@ -13,4 +13,10 @@ export class ScoresController {
getTopTwenty(): Promise<User[]> {
return this.scoresService.topTwenty();
}
// @ApiOkResponse{{description: "Successfully updated the user's total score"}}
// @Put("/add")
// addScore(): Promise<void> {
// return this.ScoresService.add()
// }
}

View File

@@ -2,9 +2,6 @@ import {
Controller,
DefaultValuePipe,
Get,
InternalServerErrorException,
NotFoundException,
Param,
ParseIntPipe,
Query,
Request,
@@ -16,15 +13,13 @@ import {
ApiTags,
ApiUnauthorizedResponse,
} from "@nestjs/swagger";
import { Artist, Genre, Song } from "@prisma/client";
import { Artist, Song } from "@prisma/client";
import { JwtAuthGuard } from "src/auth/jwt-auth.guard";
import { SearchService } from "./search.service";
import { Song as _Song } from "src/_gen/prisma-class/song";
import { Genre as _Genre } from "src/_gen/prisma-class/genre";
import { Artist as _Artist } from "src/_gen/prisma-class/artist";
import { mapInclude } from "src/utils/include";
import { SongController } from "src/song/song.controller";
import { GenreController } from "src/genre/genre.controller";
import { ArtistController } from "src/artist/artist.controller";
@ApiTags("search")
@@ -33,21 +28,21 @@ import { ArtistController } from "src/artist/artist.controller";
export class SearchController {
constructor(private readonly searchService: SearchService) {}
@Get("songs/:query")
@Get("songs")
@ApiOkResponse({ type: _Song, isArray: true })
@ApiOperation({ description: "Search a song" })
@ApiUnauthorizedResponse({ description: "Invalid token" })
async searchSong(
@Request() req: any,
@Param("query") query: string,
@Query("artistId") artistId: number,
@Query("genreId") genreId: number,
@Query("q") query: string | null,
@Query("artistId", new ParseIntPipe({ optional: true })) artistId: number,
@Query("genreId", new ParseIntPipe({ optional: true })) genreId: number,
@Query("include") include: string,
@Query("skip", new DefaultValuePipe(0), ParseIntPipe) skip: number,
@Query("take", new DefaultValuePipe(20), ParseIntPipe) take: number,
): Promise<Song[] | null> {
): Promise<Song[]> {
return await this.searchService.searchSong(
query,
query ?? "",
artistId,
genreId,
mapInclude(include, req, SongController.includableFields),
@@ -56,7 +51,7 @@ export class SearchController {
);
}
@Get("artists/:query")
@Get("artists")
@UseGuards(JwtAuthGuard)
@ApiOkResponse({ type: _Artist, isArray: true })
@ApiUnauthorizedResponse({ description: "Invalid token" })
@@ -64,12 +59,12 @@ export class SearchController {
async searchArtists(
@Request() req: any,
@Query("include") include: string,
@Param("query") query: string,
@Query("q") query: string | null,
@Query("skip", new DefaultValuePipe(0), ParseIntPipe) skip: number,
@Query("take", new DefaultValuePipe(20), ParseIntPipe) take: number,
): Promise<Artist[] | null> {
): Promise<Artist[]> {
return await this.searchService.searchArtists(
query,
query ?? "",
mapInclude(include, req, ArtistController.includableFields),
skip,
take,

View File

@@ -177,6 +177,31 @@ export class SongController {
}
}
@Get(":id/assets/melody")
@ApiOperation({
description: "Streams the mp3 file of the requested song",
})
@ApiNotFoundResponse({ description: "Song not found" })
@ApiOkResponse({ description: "Returns the mp3 file succesfully" })
@Header("Cache-Control", "max-age=86400")
@Header("Content-Type", "audio/mpeg")
@Public()
async getMelody(@Param("id", ParseIntPipe) id: number) {
const song = await this.songService.song({ id });
if (!song) throw new NotFoundException("Song not found");
const path = song.musicXmlPath;
// mp3 file is next to the musicXML file and called melody.mp3
const pathWithoutFile = path.substring(0, path.lastIndexOf("/"));
try {
const file = createReadStream(pathWithoutFile + "/melody.mp3");
return new StreamableFile(file, { type: "audio/mpeg" });
} catch {
throw new InternalServerErrorException();
}
}
@Post()
@ApiOperation({
description:

View File

@@ -122,7 +122,7 @@ export class UsersService {
return this.prisma.user.update({
where: { id: where },
data: {
partyPlayed: {
totalScore: {
increment: score,
},
},

View File

@@ -5,7 +5,6 @@ volumes:
scoro_logs:
meilisearch:
services:
back:
#platform: linux/amd64
@@ -67,6 +66,7 @@ services:
- NGINX_PORT=4567
ports:
- "19006:19006"
- "8081:8081"
volumes:
- ./front:/app
depends_on:
@@ -92,7 +92,7 @@ services:
- "4567:4567"
meilisearch:
image: getmeili/meilisearch:v1.4
image: getmeili/meilisearch:v1.5
volumes:
- meilisearch:/meili_data
env_file:
@@ -103,4 +103,3 @@ services:
interval: 10s
timeout: 10s
retries: 5

View File

@@ -1,4 +0,0 @@
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}

View File

@@ -1,5 +1,4 @@
import Artist, { ArtistHandler } from './models/Artist';
import Album from './models/Album';
import Chapter from './models/Chapter';
import Lesson from './models/Lesson';
import Genre, { GenreHandler } from './models/Genre';
@@ -24,6 +23,7 @@ import * as yup from 'yup';
import { base64ToBlob } from './utils/base64ToBlob';
import { ImagePickerAsset } from 'expo-image-picker';
import { SongCursorInfos, SongCursorInfosHandler } from './models/SongCursorInfos';
import { searchProps } from './views/V2/SearchView';
type AuthenticationInput = { username: string; password: string };
type RegistrationInput = AuthenticationInput & { email: string };
@@ -497,84 +497,6 @@ export default class API {
};
}
/**
* Search a song by its name
* @param query the string used to find the songs
*/
public static searchSongs(query: string): Query<Song[]> {
return {
key: ['search', 'song', query],
exec: () =>
API.fetch(
{
route: `/search/songs/${query}`,
},
{ handler: ListHandler(SongHandler) }
),
};
}
/**
* Search artists by name
* @param query the string used to find the artists
*/
public static searchArtists(query: string): Query<Artist[]> {
return {
key: ['search', 'artist', query],
exec: () =>
API.fetch(
{
route: `/search/artists/${query}`,
},
{ handler: ListHandler(ArtistHandler) }
),
};
}
/**
* Search Album by name
* @param query the string used to find the album
*/
public static searchAlbum(query: string): Query<Album[]> {
return {
key: ['search', 'album', query],
exec: async () => [
{
id: 1,
name: 'Super Trooper',
},
{
id: 2,
name: 'Kingdom Heart 365/2 OST',
},
{
id: 3,
name: 'The Legend Of Zelda Ocarina Of Time OST',
},
{
id: 4,
name: 'Random Access Memories',
},
],
};
}
/**
* Retrieve music genres
*/
public static searchGenres(query: string): Query<Genre[]> {
return {
key: ['search', 'genre', query],
exec: () =>
API.fetch(
{
route: `/search/genres/${query}`,
},
{ handler: ListHandler(GenreHandler) }
),
};
}
/**
* Retrieve a lesson
* @param lessonId the id to find the lesson
@@ -779,4 +701,31 @@ export default class API {
public static getPartitionSvgUrl(songId: number): string {
return `${API.baseUrl}/song/${songId}/assets/partition`;
}
public static searchSongs(query: searchProps, include?: SongInclude[]): Query<Song[]> {
const queryParams: string[] = [];
if (query.query) queryParams.push(`q=${encodeURIComponent(query.query)}`);
if (query.artist) queryParams.push(`artistId=${query.artist}`);
if (query.genre) queryParams.push(`genreId=${query.genre}`);
if (include) queryParams.push(`include=${include.join(',')}`);
const queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : '';
return {
key: ['search', query.query, query.artist, query.genre, include],
exec: () => {
return API.fetch(
{
route: `/search/songs${queryString}`,
},
{ handler: ListHandler(SongHandler) }
);
},
};
}
public static getPartitionMelodyUrl(songId: number): string {
return `${API.baseUrl}/song/${songId}/assets/melody`;
}
}

View File

@@ -1,11 +1,11 @@
/* eslint-disable @typescript-eslint/ban-types */
import { NativeStackScreenProps, createNativeStackNavigator } from '@react-navigation/native-stack';
import {
NavigationProp,
ParamListBase,
useNavigation as navigationHook,
} from '@react-navigation/native';
import React, { useEffect, useMemo } from 'react';
NativeStackNavigationProp,
NativeStackScreenProps,
createNativeStackNavigator,
} from '@react-navigation/native-stack';
import { ParamListBase, useNavigation as navigationHook } from '@react-navigation/native';
import React, { ComponentProps, ComponentType, useEffect, useMemo } from 'react';
import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
import { RootState, useSelector } from './state/Store';
import { useDispatch } from 'react-redux';
@@ -33,153 +33,201 @@ import ForgotPasswordView from './views/ForgotPasswordView';
import DiscoveryView from './views/V2/DiscoveryView';
import MusicView from './views/MusicView';
import Leaderboardiew from './views/LeaderboardView';
import { LinearGradient } from 'expo-linear-gradient';
import { createCustomNavigator } from './utils/navigator';
import { Cup, Discover, Music, SearchNormal1, Setting2, User } from 'iconsax-react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const Stack = createNativeStackNavigator<AppRouteParams>();
const Tab = createCustomNavigator<AppRouteParams>();
const Tabs = () => {
return (
<Tab.Navigator>
{Object.entries(tabRoutes).map(([name, route], routeIndex) => (
<Tab.Screen
key={'route-' + routeIndex}
name={name as keyof AppRouteParams}
options={{ ...route.options, headerTransparent: true }}
component={route.component}
/>
))}
</Tab.Navigator>
);
};
// Util function to hide route props in URL
const removeMe = () => '';
const protectedRoutes = () =>
({
Home: {
component: DiscoveryView,
options: { headerShown: false },
link: '/',
const tabRoutes = {
Home: {
component: DiscoveryView,
options: { headerShown: false, tabBarIcon: Discover },
link: '/',
},
User: {
component: ProfileView,
options: { headerShown: false, tabBarIcon: User },
link: '/user',
},
Music: {
component: MusicView,
options: { headerShown: false, tabBarIcon: Music },
link: '/music',
},
Search: {
component: SearchView,
options: { headerShown: false, tabBarIcon: SearchNormal1 },
link: '/search/:query?',
},
Leaderboard: {
component: Leaderboardiew,
options: { title: translate('leaderboardTitle'), headerShown: false, tabBarIcon: Cup },
link: '/leaderboard',
},
Settings: {
component: SettingsTab,
options: { headerShown: false, tabBarIcon: Setting2, subMenu: true },
link: '/settings/:screen?',
stringify: {
screen: removeMe,
},
Music: {
component: MusicView,
options: { headerShown: false },
link: '/music',
},
Play: {
component: PlayView,
options: { headerShown: false, title: translate('play') },
link: '/play/:songId',
},
Settings: {
component: SettingsTab,
options: { headerShown: false },
link: '/settings/:screen?',
stringify: {
screen: removeMe,
},
},
Artist: {
component: ArtistDetailsView,
options: { title: translate('artistFilter') },
link: '/artist/:artistId',
},
Genre: {
component: GenreDetailsView,
options: { title: translate('genreFilter') },
link: '/genre/:genreId',
},
Search: {
component: SearchView,
options: { headerShown: false },
link: '/search/:query?',
},
Leaderboard: {
component: Leaderboardiew,
options: { title: translate('leaderboardTitle'), headerShown: false },
link: '/leaderboard',
},
Error: {
component: ErrorView,
options: { title: translate('error'), headerLeft: null },
link: undefined,
},
User: { component: ProfileView, options: { headerShown: false }, link: '/user' },
Verified: {
component: VerifiedView,
options: { title: 'Verify email', headerShown: false },
link: '/verify',
},
}) as const;
},
};
const publicRoutes = () =>
({
Login: {
component: SigninView,
options: { title: translate('signInBtn'), headerShown: false },
link: '/login',
},
Signup: {
component: SignupView,
options: { title: translate('signUpBtn'), headerShown: false },
link: '/signup',
},
Google: {
component: GoogleView,
options: { title: 'Google signin', headerShown: false },
link: '/logged/google',
},
PasswordReset: {
component: PasswordResetView,
options: { title: 'Password reset form', headerShown: false },
link: '/password_reset',
},
ForgotPassword: {
component: ForgotPasswordView,
options: { title: 'Password reset form', headerShown: false },
link: '/forgot_password',
},
}) as const;
const protectedRoutes = {
Tabs: {
component: Tabs,
options: { headerShown: false, path: '' },
link: '',
childRoutes: tabRoutes,
},
Play: {
component: PlayView,
options: { headerShown: false, title: translate('play') },
link: '/play/:songId',
},
Artist: {
component: ArtistDetailsView,
options: { title: translate('artistFilter') },
link: '/artist/:artistId',
},
Genre: {
component: GenreDetailsView,
options: { title: translate('genreFilter') },
link: '/genre/:genreId',
},
Error: {
component: ErrorView,
options: { title: translate('error'), headerLeft: null },
link: undefined,
},
Verified: {
component: VerifiedView,
options: { title: 'Verify email', headerShown: false },
link: '/verify',
},
};
const publicRoutes = {
Login: {
component: SigninView,
options: { title: translate('signInBtn'), headerShown: false },
link: '/login',
},
Signup: {
component: SignupView,
options: { title: translate('signUpBtn'), headerShown: false },
link: '/signup',
},
Google: {
component: GoogleView,
options: { title: 'Google signin', headerShown: false },
link: '/logged/google',
},
PasswordReset: {
component: PasswordResetView,
options: { title: 'Password reset form', headerShown: false },
link: '/password_reset',
},
ForgotPassword: {
component: ForgotPasswordView,
options: { title: 'Password reset form', headerShown: false },
link: '/forgot_password',
},
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Route<Props = any> = {
component: (arg: RouteProps<Props>) => JSX.Element | (() => JSX.Element);
component: ComponentType<Props>;
options: object;
link?: string;
};
type OmitOrUndefined<T, K extends string> = T extends undefined ? T : Omit<T, K>;
// if the component has no props, ComponentProps return unknown so we remove those
type RemoveNonObjects<T> = [T] extends [{}] ? T : undefined;
type RouteParams<Routes extends Record<string, Route>> = {
[RouteName in keyof Routes]: OmitOrUndefined<
Parameters<Routes[RouteName]['component']>[0],
keyof NativeStackScreenProps<{}>
>;
[RouteName in keyof Routes]: RemoveNonObjects<ComponentProps<Routes[RouteName]['component']>>;
};
type PrivateRoutesParams = RouteParams<ReturnType<typeof protectedRoutes>>;
type PublicRoutesParams = RouteParams<ReturnType<typeof publicRoutes>>;
type AppRouteParams = PrivateRoutesParams & PublicRoutesParams;
type PrivateRoutesParams = RouteParams<typeof protectedRoutes>;
type PublicRoutesParams = RouteParams<typeof publicRoutes>;
type TabsRoutesParams = RouteParams<typeof tabRoutes>;
type AppRouteParams = Omit<PrivateRoutesParams, 'Tabs'> & {
Tabs: { screen: keyof TabsRoutesParams };
} & PublicRoutesParams &
TabsRoutesParams & { Oops: undefined };
const Stack = createNativeStackNavigator<AppRouteParams & { Loading: never; Oops: never }>();
const RouteToScreen = <T extends {}>(Component: Route<T>['component']) =>
function Route(props: NativeStackScreenProps<T & ParamListBase>) {
const colorScheme = useColorScheme();
const insets = useSafeAreaInsets();
const RouteToScreen =
<T extends {}>(component: Route<T>['component']) =>
// eslint-disable-next-line react/display-name
(props: NativeStackScreenProps<T & ParamListBase>) => (
<>
{component({ ...props.route.params, route: props.route } as Parameters<
Route<T>['component']
>[0])}
</>
);
return (
<LinearGradient
colors={colorScheme === 'dark' ? ['#101014', '#6075F9'] : ['#cdd4fd', '#cdd4fd']}
style={{
flex: 1,
paddingTop: insets.top,
paddingBottom: insets.bottom,
paddingLeft: insets.left,
paddingRight: insets.right,
}}
>
<Component {...(props.route.params as T)} route={props.route} />
</LinearGradient>
);
};
const routesToScreens = (routes: Partial<Record<keyof AppRouteParams, Route>>) =>
Object.entries(routes).map(([name, route], routeIndex) => (
<Stack.Screen
key={'route-' + routeIndex}
name={name as keyof AppRouteParams}
options={route.options}
options={{ ...route.options, headerTransparent: true }}
component={RouteToScreen(route.component)}
/>
));
const routesToLinkingConfig = (
routes: Partial<
Record<keyof AppRouteParams, { link?: string; stringify?: Record<string, () => string> }>
>
) => {
type RouteDescription = Record<
string,
{ link?: string; stringify?: Record<string, () => string>; childRoutes?: RouteDescription }
>;
const routesToLinkingConfig = (routes: RouteDescription) => {
// Too lazy to (find the) type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const pagesToRoute = {} as Record<keyof AppRouteParams, any>;
Object.keys(routes).forEach((route) => {
const index = route as keyof AppRouteParams;
if (routes[index]?.link) {
if (routes[index]?.link !== undefined) {
pagesToRoute[index] = {
path: routes[index]!.link!,
stringify: routes[index]!.stringify,
screens: routes[index]!.childRoutes
? routesToLinkingConfig(routes[index]!.childRoutes!).config.screens
: undefined,
};
}
});
@@ -239,12 +287,6 @@ export const Router = () => {
}
return 'noAuth';
}, [userProfile, accessToken]);
const routes = useMemo(() => {
if (authStatus == 'authed') {
return protectedRoutes();
}
return publicRoutes();
}, [authStatus]);
useEffect(() => {
if (accessToken) {
@@ -257,13 +299,14 @@ export const Router = () => {
return <LoadingView />;
}
const routes = authStatus == 'authed' ? { ...protectedRoutes } : publicRoutes;
return (
<NavigationContainer
linking={routesToLinkingConfig(routes)}
fallback={<LoadingView />}
theme={colorScheme == 'light' ? DefaultTheme : DarkTheme}
>
<Stack.Navigator>
<Stack.Navigator screenOptions={{ navigationBarColor: 'transparent' }}>
{authStatus == 'error' ? (
<>
<Stack.Screen
@@ -272,7 +315,7 @@ export const Router = () => {
<ProfileErrorView onTryAgain={() => userProfile.refetch()} />
))}
/>
{routesToScreens(publicRoutes())}
{routesToScreens(publicRoutes)}
</>
) : (
routesToScreens(routes)
@@ -282,6 +325,4 @@ export const Router = () => {
);
};
export type RouteProps<T> = T & Pick<NativeStackScreenProps<T & ParamListBase>, 'route'>;
export const useNavigation = () => navigationHook<NavigationProp<AppRouteParams>>();
export const useNavigation = () => navigationHook<NativeStackNavigationProp<AppRouteParams>>();

View File

@@ -30,9 +30,8 @@ const phoneLightGlassmorphism = {
900: 'rgb(248, 250, 254)',
1000: 'rgb(252, 254, 254)',
};
const lightGlassmorphism =
Platform.OS === 'web' ? defaultLightGlassmorphism : phoneLightGlassmorphism;
const darkGlassmorphism = {
const defaultDarkGlassmorphism = {
50: 'rgba(16,16,20,0.9)',
100: 'rgba(16,16,20,0.1)',
200: 'rgba(16,16,20,0.2)',
@@ -46,6 +45,24 @@ const darkGlassmorphism = {
1000: 'rgba(16,16,20,1)',
};
const phoneDarkGlassmorphism = {
50: 'rgb(10, 14, 38)',
100: 'rgb(14, 18, 42)',
200: 'rgb(18, 22, 46)',
300: 'rgb(22, 26, 50)',
400: 'rgb(26, 30, 54)',
500: 'rgb(10, 20, 54)',
600: 'rgb(14, 24, 58)',
700: 'rgb(18, 28, 62)',
800: 'rgb(22, 32, 66)',
900: 'rgb(26, 36, 70)',
1000: 'rgb(30, 40, 74)',
};
const lightGlassmorphism =
Platform.OS === 'web' ? defaultLightGlassmorphism : phoneLightGlassmorphism;
const darkGlassmorphism = Platform.OS === 'web' ? defaultDarkGlassmorphism : phoneDarkGlassmorphism;
const ThemeProvider = ({ children }: { children: JSX.Element }) => {
const colorScheme = useColorScheme();

BIN
front/assets/google.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More