Handle includes in the home page
This commit is contained in:
@@ -9,68 +9,75 @@ import {
|
||||
Query,
|
||||
Request,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
ApiCreatedResponse,
|
||||
ApiOkResponse,
|
||||
ApiOperation,
|
||||
ApiTags,
|
||||
ApiUnauthorizedResponse,
|
||||
} from '@nestjs/swagger';
|
||||
import { SearchHistory, SongHistory } from '@prisma/client';
|
||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
|
||||
import { SongHistoryDto } from './dto/SongHistoryDto';
|
||||
import { HistoryService } from './history.service';
|
||||
import { SearchHistoryDto } from './dto/SearchHistoryDto';
|
||||
import { SongHistory as _SongHistory } from 'src/_gen/prisma-class/song_history';
|
||||
import { SearchHistory as _SearchHistory } from 'src/_gen/prisma-class/search_history';
|
||||
} from "@nestjs/swagger";
|
||||
import { SearchHistory, SongHistory } from "@prisma/client";
|
||||
import { JwtAuthGuard } from "src/auth/jwt-auth.guard";
|
||||
import { SongHistoryDto } from "./dto/SongHistoryDto";
|
||||
import { HistoryService } from "./history.service";
|
||||
import { SearchHistoryDto } from "./dto/SearchHistoryDto";
|
||||
import { SongHistory as _SongHistory } from "src/_gen/prisma-class/song_history";
|
||||
import { SearchHistory as _SearchHistory } from "src/_gen/prisma-class/search_history";
|
||||
import { SongController } from "src/song/song.controller";
|
||||
import { mapInclude } from "src/utils/include";
|
||||
|
||||
@Controller('history')
|
||||
@ApiTags('history')
|
||||
@Controller("history")
|
||||
@ApiTags("history")
|
||||
export class HistoryController {
|
||||
constructor(private readonly historyService: HistoryService) {}
|
||||
constructor(private readonly historyService: HistoryService) { }
|
||||
|
||||
@Get()
|
||||
@HttpCode(200)
|
||||
@ApiOperation({ description: 'Get song history of connected user' })
|
||||
@ApiOperation({ description: "Get song history of connected user" })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOkResponse({ type: _SongHistory, isArray: true })
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid token' })
|
||||
@ApiUnauthorizedResponse({ description: "Invalid token" })
|
||||
async getHistory(
|
||||
@Request() req: any,
|
||||
@Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip: number,
|
||||
@Query('take', new DefaultValuePipe(20), ParseIntPipe) take: number,
|
||||
@Query("skip", new DefaultValuePipe(0), ParseIntPipe) skip: number,
|
||||
@Query("take", new DefaultValuePipe(20), ParseIntPipe) take: number,
|
||||
@Query("include") include: string,
|
||||
): Promise<SongHistory[]> {
|
||||
return this.historyService.getHistory(req.user.id, { skip, take });
|
||||
return this.historyService.getHistory(
|
||||
req.user.id,
|
||||
{ skip, take },
|
||||
mapInclude(include, req, SongController.includableFields),
|
||||
);
|
||||
}
|
||||
|
||||
@Get('search')
|
||||
@Get("search")
|
||||
@HttpCode(200)
|
||||
@ApiOperation({ description: 'Get search history of connected user' })
|
||||
@ApiOperation({ description: "Get search history of connected user" })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOkResponse({ type: _SearchHistory, isArray: true })
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid token' })
|
||||
@ApiUnauthorizedResponse({ description: "Invalid token" })
|
||||
async getSearchHistory(
|
||||
@Request() req: any,
|
||||
@Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip: number,
|
||||
@Query('take', new DefaultValuePipe(20), ParseIntPipe) take: number,
|
||||
@Query("skip", new DefaultValuePipe(0), ParseIntPipe) skip: number,
|
||||
@Query("take", new DefaultValuePipe(20), ParseIntPipe) take: number,
|
||||
): Promise<SearchHistory[]> {
|
||||
return this.historyService.getSearchHistory(req.user.id, { skip, take });
|
||||
}
|
||||
|
||||
@Post()
|
||||
@HttpCode(201)
|
||||
@ApiOperation({ description: 'Create a record of a song played by a user' })
|
||||
@ApiCreatedResponse({ description: 'Succesfully created a record' })
|
||||
@ApiOperation({ description: "Create a record of a song played by a user" })
|
||||
@ApiCreatedResponse({ description: "Succesfully created a record" })
|
||||
async create(@Body() record: SongHistoryDto): Promise<SongHistory> {
|
||||
return this.historyService.createSongHistoryRecord(record);
|
||||
}
|
||||
|
||||
@Post('search')
|
||||
@Post("search")
|
||||
@HttpCode(201)
|
||||
@ApiOperation({ description: 'Creates a search record in the users history' })
|
||||
@ApiOperation({ description: "Creates a search record in the users history" })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid token' })
|
||||
@ApiUnauthorizedResponse({ description: "Invalid token" })
|
||||
async createSearchHistory(
|
||||
@Request() req: any,
|
||||
@Body() record: SearchHistoryDto,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { SearchHistory, SongHistory } from '@prisma/client';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { SearchHistoryDto } from './dto/SearchHistoryDto';
|
||||
import { SongHistoryDto } from './dto/SongHistoryDto';
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Prisma, SearchHistory, SongHistory } from "@prisma/client";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { SearchHistoryDto } from "./dto/SearchHistoryDto";
|
||||
import { SongHistoryDto } from "./dto/SongHistoryDto";
|
||||
|
||||
@Injectable()
|
||||
export class HistoryService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(private prisma: PrismaService) { }
|
||||
|
||||
async createSongHistoryRecord({
|
||||
songID,
|
||||
@@ -45,13 +45,14 @@ export class HistoryService {
|
||||
async getHistory(
|
||||
playerId: number,
|
||||
{ skip, take }: { skip?: number; take?: number },
|
||||
include?: Prisma.SongInclude,
|
||||
): Promise<SongHistory[]> {
|
||||
return this.prisma.songHistory.findMany({
|
||||
where: { user: { id: playerId } },
|
||||
orderBy: { playDate: 'desc' },
|
||||
orderBy: { playDate: "desc" },
|
||||
skip,
|
||||
take,
|
||||
include: { song: true }
|
||||
include: { song: include ? { include } : true },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,7 +65,7 @@ export class HistoryService {
|
||||
}): Promise<{ best: number; history: SongHistory[] }> {
|
||||
const history = await this.prisma.songHistory.findMany({
|
||||
where: { user: { id: playerId }, song: { id: songId } },
|
||||
orderBy: { playDate: 'desc' },
|
||||
orderBy: { playDate: "desc" },
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -96,7 +97,7 @@ export class HistoryService {
|
||||
): Promise<SearchHistory[]> {
|
||||
return this.prisma.searchHistory.findMany({
|
||||
where: { user: { id: playerId } },
|
||||
orderBy: { searchDate: 'desc' },
|
||||
orderBy: { searchDate: "desc" },
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
|
||||
+16
-12
@@ -5,7 +5,7 @@ import Lesson from './models/Lesson';
|
||||
import Genre, { GenreHandler } from './models/Genre';
|
||||
import LessonHistory from './models/LessonHistory';
|
||||
import likedSong, { LikedSongHandler } from './models/LikedSong';
|
||||
import Song, { SongHandler } from './models/Song';
|
||||
import Song, { SongHandler, SongInclude } from './models/Song';
|
||||
import { SongHistoryHandler, SongHistoryItem, SongHistoryItemHandler } from './models/SongHistory';
|
||||
import User, { UserHandler } from './models/User';
|
||||
import store from './state/Store';
|
||||
@@ -277,13 +277,14 @@ export default class API {
|
||||
};
|
||||
}
|
||||
|
||||
public static getAllSongs(): Query<Song[]> {
|
||||
public static getAllSongs(include?: SongInclude[]): Query<Song[]> {
|
||||
include ??= [];
|
||||
return {
|
||||
key: 'songs',
|
||||
key: ['songs', include],
|
||||
exec: () =>
|
||||
API.fetch(
|
||||
{
|
||||
route: '/song',
|
||||
route: `/song?include=${include!.join(',')}`,
|
||||
},
|
||||
{
|
||||
handler: PlageHandler(SongHandler),
|
||||
@@ -332,13 +333,15 @@ export default class API {
|
||||
* @param genreId the id of the genre we're aiming
|
||||
* @returns a promise of an array of Songs
|
||||
*/
|
||||
public static getSongsByGenre(genreId: number): Query<Song[]> {
|
||||
public static getSongsByGenre(genreId: number, includes?: SongInclude[]): Query<Song[]> {
|
||||
includes ??= [];
|
||||
|
||||
return {
|
||||
key: ['genre', genreId, 'songs'],
|
||||
key: ['genre', genreId, 'songs', includes],
|
||||
exec: () =>
|
||||
API.fetch(
|
||||
{
|
||||
route: `/song?genreId=${genreId}`,
|
||||
route: `/song?genreId=${genreId}&includes=${includes!.join(',')}`,
|
||||
},
|
||||
{ handler: PlageHandler(SongHandler) }
|
||||
).then(({ data }) => data),
|
||||
@@ -605,21 +608,22 @@ export default class API {
|
||||
* Retrieve the authenticated user's recommendations
|
||||
* @returns an array of songs
|
||||
*/
|
||||
public static getSongSuggestions(): Query<Song[]> {
|
||||
return API.getAllSongs();
|
||||
public static getSongSuggestions(include?: SongInclude[]): Query<Song[]> {
|
||||
return API.getAllSongs(include);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the authenticated user's play history
|
||||
* * @returns an array of songs
|
||||
*/
|
||||
public static getUserPlayHistory(): Query<SongHistoryItem[]> {
|
||||
public static getUserPlayHistory(include?: SongInclude[]): Query<SongHistoryItem[]> {
|
||||
include ??= [];
|
||||
return {
|
||||
key: ['history'],
|
||||
key: ['history', include],
|
||||
exec: () =>
|
||||
API.fetch(
|
||||
{
|
||||
route: '/history',
|
||||
route: `/history?include=${include!.join(',')}`,
|
||||
},
|
||||
{ handler: ListHandler(SongHistoryItemHandler) }
|
||||
),
|
||||
|
||||
@@ -21,17 +21,17 @@ import GenreCard from './GenreCard';
|
||||
import SongCard from './SongCard';
|
||||
import CardGridCustom from './CardGridCustom';
|
||||
import SearchHistoryCard from './HistoryCard';
|
||||
import Song, { SongWithArtist } from '../models/Song';
|
||||
import Song from '../models/Song';
|
||||
import { useNavigation } from '../Navigation';
|
||||
import Artist from '../models/Artist';
|
||||
import SongRow from '../components/SongRow';
|
||||
import FavSongRow from './FavSongRow';
|
||||
import { LikedSongWithDetails } from '../models/LikedSong';
|
||||
|
||||
const swaToSongCardProps = (song: SongWithArtist) => ({
|
||||
const swaToSongCardProps = (song: Song) => ({
|
||||
songId: song.id,
|
||||
name: song.name,
|
||||
artistName: song.artist.name,
|
||||
artistName: song.artist!.name,
|
||||
cover: song.cover ?? 'https://picsum.photos/200',
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import { View, Image, TouchableOpacity } from 'react-native';
|
||||
import { Divider, Text, ScrollView, Row, useMediaQuery, useTheme } from 'native-base';
|
||||
import { useQuery, useQueries } from '../../Queries';
|
||||
import { useQuery } from '../../Queries';
|
||||
import API from '../../API';
|
||||
import Song from '../../models/Song';
|
||||
import ButtonBase from './ButtonBase';
|
||||
@@ -30,42 +30,36 @@ type ScaffoldDesktopCCProps = {
|
||||
|
||||
// TODO a tester avec un historique de plus de 3 musics différente mdr !!
|
||||
const SongHistory = (props: { quantity: number }) => {
|
||||
const playHistoryQuery = useQuery(API.getUserPlayHistory);
|
||||
const songHistory = useQueries(
|
||||
playHistoryQuery.data?.map(({ songID }) => API.getSong(songID)) ?? []
|
||||
);
|
||||
const history = useQuery(API.getUserPlayHistory);
|
||||
const navigation = useNavigation();
|
||||
|
||||
const musics = songHistory
|
||||
.map((h) => h.data)
|
||||
.filter((data): data is Song => data !== undefined)
|
||||
.filter((song, i, array) => array.map((s) => s.id).findIndex((id) => id == song.id) == i)
|
||||
?.slice(0, props.quantity)
|
||||
.map((song: Song) => (
|
||||
<View
|
||||
key={'short-history-tab' + song.id}
|
||||
style={{
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity onPress={() => navigation.navigate('Play', { songId: song.id })}>
|
||||
<Text numberOfLines={1}>{song.name}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
));
|
||||
|
||||
if (!playHistoryQuery.data || playHistoryQuery.isLoading || !songHistory) {
|
||||
if (!history.data || history.isLoading) {
|
||||
return <LoadingView />;
|
||||
}
|
||||
|
||||
const musics = history.data.map((h) => h.song)?.slice(0, props.quantity);
|
||||
|
||||
return (
|
||||
<View>
|
||||
{musics.length === 0 ? (
|
||||
<Text style={{ paddingHorizontal: 16 }}>{translate('menuNoSongsPlayedYet')}</Text>
|
||||
) : (
|
||||
musics
|
||||
musics.map((song) => (
|
||||
<View
|
||||
key={'short-history-tab' + song.id}
|
||||
style={{
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.navigate('Play', { songId: song.id })}
|
||||
>
|
||||
<Text numberOfLines={1}>{song.name}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
@@ -127,14 +121,14 @@ const ScaffoldDesktopCC = (props: ScaffoldDesktopCCProps) => {
|
||||
title={
|
||||
!isSmallScreen
|
||||
? translate(
|
||||
value.title as
|
||||
| 'menuDiscovery'
|
||||
| 'menuProfile'
|
||||
| 'menuMusic'
|
||||
| 'menuSearch'
|
||||
| 'menuLeaderBoard'
|
||||
| 'menuSettings'
|
||||
)
|
||||
value.title as
|
||||
| 'menuDiscovery'
|
||||
| 'menuProfile'
|
||||
| 'menuMusic'
|
||||
| 'menuSearch'
|
||||
| 'menuLeaderBoard'
|
||||
| 'menuSettings'
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
isDisabled={props.routeName === value.link}
|
||||
@@ -183,14 +177,14 @@ const ScaffoldDesktopCC = (props: ScaffoldDesktopCCProps) => {
|
||||
title={
|
||||
!isSmallScreen
|
||||
? translate(
|
||||
value.title as
|
||||
| 'menuDiscovery'
|
||||
| 'menuProfile'
|
||||
| 'menuMusic'
|
||||
| 'menuSearch'
|
||||
| 'menuLeaderBoard'
|
||||
| 'menuSettings'
|
||||
)
|
||||
value.title as
|
||||
| 'menuDiscovery'
|
||||
| 'menuProfile'
|
||||
| 'menuMusic'
|
||||
| 'menuSearch'
|
||||
| 'menuLeaderBoard'
|
||||
| 'menuSettings'
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
isDisabled={props.routeName === value.link}
|
||||
|
||||
@@ -186,7 +186,7 @@ const SongCardInfo = (props: SongCardInfoProps) => {
|
||||
fontWeight: 'normal',
|
||||
}}
|
||||
>
|
||||
{props.song.artistId}
|
||||
{props.song.artist?.name}
|
||||
</Text>
|
||||
</View>
|
||||
<Ionicons
|
||||
|
||||
@@ -4,7 +4,7 @@ import TabNavigationButton from './TabNavigationButton';
|
||||
import TabNavigationList from './TabNavigationList';
|
||||
import { useAssets } from 'expo-asset';
|
||||
import useColorScheme from '../../hooks/colorScheme';
|
||||
import { useQuery, useQueries } from '../../Queries';
|
||||
import { useQuery } from '../../Queries';
|
||||
import { NaviTab } from './TabNavigation';
|
||||
import API from '../../API';
|
||||
import Song from '../../models/Song';
|
||||
|
||||
@@ -29,6 +29,6 @@ export const PlageHandler = <A, R>(
|
||||
validator: PlageValidator(itemHandler.validator),
|
||||
transformer: (plage) => ({
|
||||
...plage,
|
||||
data: plage.data.map((item) => itemHandler.transformer(item)),
|
||||
data: plage.data.map((item) => itemHandler.transformer?.(item) ?? item as unknown as R),
|
||||
}),
|
||||
});
|
||||
|
||||
+18
-28
@@ -1,9 +1,13 @@
|
||||
import Model, { ModelValidator } from './Model';
|
||||
import SongDetails, { SongDetailsHandler, SongDetailsValidator } from './SongDetails';
|
||||
import Artist from './Artist';
|
||||
import Artist, { ArtistValidator } from './Artist';
|
||||
import * as yup from 'yup';
|
||||
import ResponseHandler from './ResponseHandler';
|
||||
import API from '../API';
|
||||
import { AlbumValidator } from './Album';
|
||||
import { GenreValidator } from './Genre';
|
||||
|
||||
export type SongInclude = 'artist' | 'album' | 'genre' | 'SongHistory' | 'likedByUsers';
|
||||
|
||||
export const SongValidator = yup
|
||||
.object({
|
||||
@@ -14,35 +18,21 @@ export const SongValidator = yup
|
||||
albumId: yup.number().required().nullable(),
|
||||
genreId: yup.number().required().nullable(),
|
||||
difficulties: SongDetailsValidator.required(),
|
||||
illustrationPath: yup.string().required(),
|
||||
cover: yup.string().required(),
|
||||
artist: yup.lazy(() => ArtistValidator.default(undefined)).optional(),
|
||||
album: yup.lazy(() => AlbumValidator.default(undefined)).optional(),
|
||||
genre: yup.lazy(() => GenreValidator.default(undefined)).optional(),
|
||||
})
|
||||
.concat(ModelValidator);
|
||||
|
||||
export const SongHandler: ResponseHandler<yup.InferType<typeof SongValidator>, Song> = {
|
||||
validator: SongValidator,
|
||||
transformer: (song) => ({
|
||||
id: song.id,
|
||||
name: song.name,
|
||||
artistId: song.artistId,
|
||||
albumId: song.albumId,
|
||||
genreId: song.genreId,
|
||||
details: SongDetailsHandler.transformer(song.difficulties),
|
||||
.concat(ModelValidator)
|
||||
.transform((song: Song) => ({
|
||||
...song,
|
||||
cover: `${API.baseUrl}/song/${song.id}/illustration`,
|
||||
}),
|
||||
}));
|
||||
|
||||
export type Song = yup.InferType<typeof SongValidator>;
|
||||
|
||||
export const SongHandler: ResponseHandler<Song> = {
|
||||
validator: SongValidator,
|
||||
};
|
||||
|
||||
interface Song extends Model {
|
||||
id: number;
|
||||
name: string;
|
||||
artistId: number;
|
||||
albumId: number | null;
|
||||
genreId: number | null;
|
||||
cover: string;
|
||||
details: SongDetails;
|
||||
}
|
||||
|
||||
export interface SongWithArtist extends Song {
|
||||
artist: Artist;
|
||||
}
|
||||
|
||||
export default Song;
|
||||
|
||||
@@ -17,28 +17,10 @@ export const SongDetailsValidator = yup.object({
|
||||
chordcomplexity: yup.number().required(),
|
||||
});
|
||||
|
||||
export const SongDetailsHandler: ResponseHandler<
|
||||
yup.InferType<typeof SongDetailsValidator>,
|
||||
SongDetails
|
||||
> = {
|
||||
export type SongDetails = yup.InferType<typeof SongDetailsValidator>;
|
||||
|
||||
export const SongDetailsHandler: ResponseHandler<SongDetails> = {
|
||||
validator: SongDetailsValidator,
|
||||
transformer: (value) => value,
|
||||
};
|
||||
|
||||
interface SongDetails {
|
||||
length: number;
|
||||
rhythm: number;
|
||||
arpeggio: number;
|
||||
distance: number;
|
||||
lefthand: number;
|
||||
righthand: number;
|
||||
twohands: number;
|
||||
notecombo: number;
|
||||
precision: number;
|
||||
pedalpoint: number;
|
||||
chordtiming: number;
|
||||
leadhandchange: number;
|
||||
chordcomplexity: number;
|
||||
}
|
||||
|
||||
export default SongDetails;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Flex, Heading, useBreakpointValue, ScrollView } from 'native-base';
|
||||
import { useQueries, useQuery } from '../Queries';
|
||||
import { useQuery } from '../Queries';
|
||||
import { LoadingView } from '../components/Loading';
|
||||
import { RouteProps, useNavigation } from '../Navigation';
|
||||
import API from '../API';
|
||||
@@ -13,19 +13,7 @@ type GenreDetailsViewProps = {
|
||||
|
||||
const GenreDetailsView = ({ genreId }: RouteProps<GenreDetailsViewProps>) => {
|
||||
const genreQuery = useQuery(API.getGenre(genreId));
|
||||
const songsQuery = useQuery(API.getSongsByGenre(genreId));
|
||||
const artistQueries = useQueries(
|
||||
songsQuery.data?.map((song) => song.artistId).map((artistId) => API.getArtist(artistId)) ??
|
||||
[]
|
||||
);
|
||||
// Here, .artist will always be defined
|
||||
const songWithArtist = songsQuery?.data
|
||||
?.map((song) => ({
|
||||
...song,
|
||||
artist: artistQueries.find((query) => query.data?.id == song.artistId)?.data,
|
||||
}))
|
||||
.filter((song) => song.artist !== undefined);
|
||||
|
||||
const songsQuery = useQuery(API.getSongsByGenre(genreId, ["artist"]));
|
||||
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });
|
||||
const isMobileView = screenSize == 'small';
|
||||
const navigation = useNavigation();
|
||||
@@ -34,7 +22,7 @@ const GenreDetailsView = ({ genreId }: RouteProps<GenreDetailsViewProps>) => {
|
||||
navigation.navigate('Error');
|
||||
return <></>;
|
||||
}
|
||||
if (!genreQuery.data || songsQuery.data === undefined || songWithArtist === undefined) {
|
||||
if (!genreQuery.data || songsQuery.data === undefined) {
|
||||
return <LoadingView />;
|
||||
}
|
||||
|
||||
@@ -54,7 +42,7 @@ const GenreDetailsView = ({ genreId }: RouteProps<GenreDetailsViewProps>) => {
|
||||
mt={4}
|
||||
>
|
||||
<CardGridCustom
|
||||
content={songWithArtist.map((songData) => ({
|
||||
content={songsQuery.data.map((songData) => ({
|
||||
name: songData.name,
|
||||
cover: songData.cover,
|
||||
artistName: songData.artist!.name,
|
||||
|
||||
+12
-46
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { useQueries, useQuery } from '../Queries';
|
||||
import { useQuery } from '../Queries';
|
||||
import API from '../API';
|
||||
import { LoadingView } from '../components/Loading';
|
||||
import { Box, Flex, Stack, Heading, VStack, HStack } from 'native-base';
|
||||
@@ -16,20 +16,10 @@ import ScaffoldCC from '../components/UI/ScaffoldCC';
|
||||
const HomeView = (props: RouteProps<{}>) => {
|
||||
const navigation = useNavigation();
|
||||
const userQuery = useQuery(API.getUserInfo);
|
||||
const playHistoryQuery = useQuery(API.getUserPlayHistory);
|
||||
const playHistoryQuery = useQuery(API.getUserPlayHistory(['artist']));
|
||||
const searchHistoryQuery = useQuery(API.getSearchHistory(0, 10));
|
||||
const skillsQuery = useQuery(API.getUserSkills);
|
||||
const nextStepQuery = useQuery(API.getSongSuggestions);
|
||||
const songHistory = useQueries(
|
||||
playHistoryQuery.data?.map(({ songID }) => API.getSong(songID)) ?? []
|
||||
);
|
||||
const artistsQueries = useQueries(
|
||||
songHistory
|
||||
.map((entry) => entry.data)
|
||||
.concat(nextStepQuery.data ?? [])
|
||||
.filter((s): s is Song => s !== undefined)
|
||||
.map((song) => API.getArtist(song.artistId))
|
||||
);
|
||||
const nextStepQuery = useQuery(API.getSongSuggestions(['artist']));
|
||||
|
||||
if (
|
||||
!userQuery.data ||
|
||||
@@ -46,20 +36,12 @@ const HomeView = (props: RouteProps<{}>) => {
|
||||
<SongCardGrid
|
||||
heading={<Translate translationKey="goNextStep" />}
|
||||
songs={
|
||||
nextStepQuery.data
|
||||
?.filter((song) =>
|
||||
artistsQueries.find(
|
||||
(artistQuery) => artistQuery.data?.id === song.artistId
|
||||
)
|
||||
)
|
||||
.map((song) => ({
|
||||
cover: song.cover,
|
||||
name: song.name,
|
||||
songId: song.id,
|
||||
artistName: artistsQueries.find(
|
||||
(artistQuery) => artistQuery.data?.id === song.artistId
|
||||
)!.data!.name,
|
||||
})) ?? []
|
||||
nextStepQuery.data?.map((song) => ({
|
||||
cover: song.cover,
|
||||
name: song.name,
|
||||
songId: song.id,
|
||||
artistName: song.artist!.name,
|
||||
})) ?? []
|
||||
}
|
||||
/>
|
||||
<Stack direction={{ base: 'column', lg: 'row' }}>
|
||||
@@ -75,29 +57,13 @@ const HomeView = (props: RouteProps<{}>) => {
|
||||
<SongCardGrid
|
||||
heading={<Translate translationKey="recentlyPlayed" />}
|
||||
songs={
|
||||
songHistory
|
||||
.map(({ data }) => data)
|
||||
.filter((data): data is Song => data !== undefined)
|
||||
.filter(
|
||||
(song, i, array) =>
|
||||
array
|
||||
.map((s) => s.id)
|
||||
.findIndex((id) => id == song.id) == i
|
||||
)
|
||||
.filter((song) =>
|
||||
artistsQueries.find(
|
||||
(artistQuery) =>
|
||||
artistQuery.data?.id === song.artistId
|
||||
)
|
||||
)
|
||||
playHistoryQuery.data
|
||||
?.map((x) => x.song)
|
||||
.map((song) => ({
|
||||
cover: song.cover,
|
||||
name: song.name,
|
||||
songId: song.id,
|
||||
artistName: artistsQueries.find(
|
||||
(artistQuery) =>
|
||||
artistQuery.data?.id === song.artistId
|
||||
)!.data!.name,
|
||||
artistName: song.artist!.name,
|
||||
})) ?? []
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -15,41 +15,20 @@ import { RouteProps, useNavigation } from '../Navigation';
|
||||
import { TranslationKey, translate } from '../i18n/i18n';
|
||||
import ScaffoldCC from '../components/UI/ScaffoldCC';
|
||||
import MusicList from '../components/UI/MusicList';
|
||||
import { useQueries, useQuery } from '../Queries';
|
||||
import { useQuery } from '../Queries';
|
||||
import API from '../API';
|
||||
import Song from '../models/Song';
|
||||
import { LoadingView } from '../components/Loading';
|
||||
|
||||
export const FavoritesMusic = () => {
|
||||
const navigation = useNavigation();
|
||||
const playHistoryQuery = useQuery(API.getUserPlayHistory);
|
||||
const nextStepQuery = useQuery(API.getSongSuggestions);
|
||||
const songHistory = useQueries(
|
||||
playHistoryQuery.data?.map(({ songID }) => API.getSong(songID)) ?? []
|
||||
);
|
||||
const artistsQueries = useQueries(
|
||||
songHistory
|
||||
.map((entry) => entry.data)
|
||||
.concat(nextStepQuery.data ?? [])
|
||||
.filter((s): s is Song => s !== undefined)
|
||||
.map((song) => API.getArtist(song.artistId))
|
||||
);
|
||||
|
||||
const isLoading =
|
||||
playHistoryQuery.isLoading ||
|
||||
nextStepQuery.isLoading ||
|
||||
songHistory.some((query) => query.isLoading) ||
|
||||
artistsQueries.some((query) => query.isLoading);
|
||||
const playHistoryQuery = useQuery(API.getUserPlayHistory(['artist']));
|
||||
|
||||
const musics =
|
||||
nextStepQuery.data
|
||||
?.filter((song: Song) =>
|
||||
artistsQueries.find((artistQuery) => artistQuery.data?.id === song.artistId)
|
||||
)
|
||||
playHistoryQuery.data
|
||||
?.map((x) => x.song)
|
||||
.map((song: Song) => ({
|
||||
artist: artistsQueries.find(
|
||||
(artistQuery) => artistQuery.data?.id === song.artistId
|
||||
)!.data!.name,
|
||||
artist: song.artist!.name,
|
||||
song: song.name,
|
||||
image: song.cover,
|
||||
level: 42,
|
||||
@@ -62,7 +41,7 @@ export const FavoritesMusic = () => {
|
||||
onPlay: () => navigation.navigate('Play', { songId: song.id }),
|
||||
})) ?? [];
|
||||
|
||||
if (isLoading) {
|
||||
if (playHistoryQuery.isLoading) {
|
||||
return <LoadingView />;
|
||||
}
|
||||
return (
|
||||
@@ -114,7 +93,7 @@ export const FavoritesMusic = () => {
|
||||
</View> */}
|
||||
<MusicList
|
||||
initialMusics={musics}
|
||||
// musicsPerPage={7}
|
||||
// musicsPerPage={7}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ import GoldenRatio from '../../components/V2/GoldenRatio';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const HomeView = (props: RouteProps<{}>) => {
|
||||
const songsQuery = useQuery(API.getSongSuggestions);
|
||||
const songsQuery = useQuery(API.getSongSuggestions(["artist"]));
|
||||
const navigation = useNavigation();
|
||||
const screenSize = useBreakpointValue({ base: 'small', md: 'big' });
|
||||
const isPhone = screenSize === 'small';
|
||||
|
||||
Reference in New Issue
Block a user