Skip to content

Commit

Permalink
feat(user): fix today time
Browse files Browse the repository at this point in the history
  • Loading branch information
stijnvdkolk committed Jan 2, 2024
1 parent a53870b commit 558dc6f
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 180 deletions.
19 changes: 19 additions & 0 deletions src/components/User/NotEnoughData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { PropsWithChildren } from 'react';

export const NotEnoughData = ({
data,
loading = false,
children,
}: PropsWithChildren<{ data: any[]; loading?: boolean }>) => {
if (data && data.length === 0 && !loading) {
return (
<div className="grid w-full place-items-center">
<p className="m-0 text-text-grey">
Not enough data to complete this list
</p>
</div>
);
}

return <>{children}</>;
};
88 changes: 49 additions & 39 deletions src/components/User/TopAlbums.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useApi, useAuth } from '@/hooks';
import formatter from '@/utils/formatter';
import type { Range, TopAlbum, UserPublic } from '@statsfm/statsfm.js';
import type { TopAlbum, UserPublic } from '@statsfm/statsfm.js';
import type { RefObject } from 'react';
import { useState, type FC, useEffect } from 'react';
import { event } from 'nextjs-google-analytics';
Expand All @@ -15,25 +15,32 @@ import {
} from '../Section';
import { ShareMenuItem } from '../ShareMenuItem';
import { AlbumCard, AlbumCardSkeleton } from '../Album';
import { ranges } from './utils';
import {
type TimeframeSelection,
getTimeframeOptions,
getTimeframeText,
} from './utils';
import { NotEnoughData } from './NotEnoughData';

export const TopAlbums: FC<{
range: Range;
timeframe: TimeframeSelection;
albumRef: RefObject<HTMLElement>;
userProfile: UserPublic;
activeCarousel: UserPageCarouselsWithGrid | null;
}> = ({ albumRef, userProfile, range, activeCarousel }) => {
}> = ({ albumRef, userProfile, timeframe, activeCarousel }) => {
const api = useApi();
const { user: currentUser } = useAuth();
const [topAlbums, setTopAlbums] = useState<TopAlbum[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
setTopAlbums([]);
api.users
.topAlbums(userProfile.id, { range })
.topAlbums(userProfile.id, getTimeframeOptions(timeframe))
.then(setTopAlbums)
.catch(() => []);
}, [range, userProfile]);
.catch(() => [])
.finally(() => setLoading(false));
}, [timeframe, userProfile]);

const gridModeCallback = (gridMode: boolean) => {
let newUrl = `/${userProfile.customId ?? userProfile.id}`;
Expand All @@ -59,18 +66,22 @@ export const TopAlbums: FC<{
title="Top albums"
description={`${
isCurrentUser ? 'Your' : formatter.nounify(userProfile.displayName)
} top albums ${ranges[range]}`}
} top albums ${getTimeframeText(timeframe)}`}
scope="topAlbums"
toolbar={
<div className="flex gap-1">
<SectionToolbarGridMode callback={gridModeCallback} />
<SectionToolbarCarouselNavigation
callback={() => event('USER_top_albums_previous')}
/>
<SectionToolbarCarouselNavigation
next
callback={() => event('USER_top_albums_next')}
/>
{topAlbums?.length > 0 && (
<>
<SectionToolbarGridMode callback={gridModeCallback} />
<SectionToolbarCarouselNavigation
callback={() => event('USER_top_albums_previous')}
/>
<SectionToolbarCarouselNavigation
next
callback={() => event('USER_top_albums_next')}
/>
</>
)}
<SectionToolbarInfoMenu>
<ShareMenuItem
path={`/${userProfile.customId ?? userProfile.id}/albums`}
Expand All @@ -80,29 +91,28 @@ export const TopAlbums: FC<{
}
>
<Scope value="topAlbums">
{/* <NotEnoughData data={topAlbums}> */}

<Carousel.Items>
{topAlbums?.length > 0
? topAlbums
.filter((topAlbum) => topAlbum.album?.id)
.map((item) => (
<Carousel.Item
key={item.album.id}
onClick={() => event('USER_top_album_click')}
>
<AlbumCard {...item} />
</Carousel.Item>
))
: Array(10)
.fill(null)
.map((_n, i) => (
<Carousel.Item key={i}>
<AlbumCardSkeleton />
</Carousel.Item>
))}
</Carousel.Items>
{/* </NotEnoughData> */}
<NotEnoughData data={topAlbums} loading={loading}>
<Carousel.Items>
{topAlbums?.length > 0
? topAlbums
.filter((topAlbum) => topAlbum.album?.id)
.map((item) => (
<Carousel.Item
key={item.album.id}
onClick={() => event('USER_top_album_click')}
>
<AlbumCard {...item} />
</Carousel.Item>
))
: Array(10)
.fill(null)
.map((_n, i) => (
<Carousel.Item key={i}>
<AlbumCardSkeleton />
</Carousel.Item>
))}
</Carousel.Items>
</NotEnoughData>
</Scope>
</Section>
</Carousel>
Expand Down
89 changes: 50 additions & 39 deletions src/components/User/TopArtists.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useApi, useAuth } from '@/hooks';
import formatter from '@/utils/formatter';
import type { Range, TopArtist, UserPublic } from '@statsfm/statsfm.js';
import type { TopArtist, UserPublic } from '@statsfm/statsfm.js';
import type { RefObject } from 'react';
import { useState, type FC, useEffect } from 'react';
import { event } from 'nextjs-google-analytics';
Expand All @@ -15,25 +15,32 @@ import {
} from '../Section';
import { ShareMenuItem } from '../ShareMenuItem';
import { ArtistCard, ArtistCardSkeleton } from '../Artist';
import { ranges } from './utils';
import {
getTimeframeOptions,
getTimeframeText,
type TimeframeSelection,
} from './utils';
import { NotEnoughData } from './NotEnoughData';

export const TopArtists: FC<{
range: Range;
timeframe: TimeframeSelection;
artistRef: RefObject<HTMLElement>;
userProfile: UserPublic;
activeCarousel: UserPageCarouselsWithGrid | null;
}> = ({ artistRef, userProfile, range, activeCarousel }) => {
}> = ({ artistRef, userProfile, timeframe, activeCarousel }) => {
const api = useApi();
const { user: currentUser } = useAuth();
const [topArtists, setTopArtists] = useState<TopArtist[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
setTopArtists([]);
api.users
.topArtists(userProfile.id, { range })
.topArtists(userProfile.id, getTimeframeOptions(timeframe))
.then(setTopArtists)
.catch(() => []);
}, [range, userProfile]);
.catch(() => [])
.finally(() => setLoading(false));
}, [timeframe, userProfile]);

const gridModeCallback = (gridMode: boolean) => {
let newUrl = `/${userProfile.customId ?? userProfile.id}`;
Expand All @@ -59,18 +66,23 @@ export const TopArtists: FC<{
title="Top artists"
description={`${
isCurrentUser ? 'Your' : formatter.nounify(userProfile.displayName)
} top artists ${ranges[range]}`}
} top artists ${getTimeframeText(timeframe)}`}
scope="topArtists"
toolbar={
<div className="flex gap-1">
<SectionToolbarGridMode callback={gridModeCallback} />
<SectionToolbarCarouselNavigation
callback={() => event('USER_top_artists_previous')}
/>
<SectionToolbarCarouselNavigation
next
callback={() => event('USER_top_artists_next')}
/>
{topArtists?.length > 0 && (
<>
{' '}
<SectionToolbarGridMode callback={gridModeCallback} />
<SectionToolbarCarouselNavigation
callback={() => event('USER_top_artists_previous')}
/>
<SectionToolbarCarouselNavigation
next
callback={() => event('USER_top_artists_next')}
/>
</>
)}
<SectionToolbarInfoMenu>
<ShareMenuItem
path={`/${userProfile.customId ?? userProfile.id}/artists`}
Expand All @@ -80,29 +92,28 @@ export const TopArtists: FC<{
}
>
<Scope value="topArtists">
{/* <NotEnoughData data={topArtists}> */}

<Carousel.Items>
{topArtists?.length > 0
? topArtists
.filter((topArtist) => topArtist.artist?.id)
.map((item) => (
<Carousel.Item
key={item.artist.id}
onClick={() => event('USER_top_artist_click')}
>
<ArtistCard {...item} />
</Carousel.Item>
))
: Array(10)
.fill(null)
.map((_n, i) => (
<Carousel.Item key={i}>
<ArtistCardSkeleton />
</Carousel.Item>
))}
</Carousel.Items>
{/* </NotEnoughData> */}
<NotEnoughData data={topArtists} loading={loading}>
<Carousel.Items>
{topArtists?.length > 0
? topArtists
.filter((topArtist) => topArtist.artist?.id)
.map((item) => (
<Carousel.Item
key={item.artist.id}
onClick={() => event('USER_top_artist_click')}
>
<ArtistCard {...item} />
</Carousel.Item>
))
: Array(10)
.fill(null)
.map((_n, i) => (
<Carousel.Item key={i}>
<ArtistCardSkeleton />
</Carousel.Item>
))}
</Carousel.Items>
</NotEnoughData>
</Scope>
</Section>
</Carousel>
Expand Down
60 changes: 33 additions & 27 deletions src/components/User/TopGenres.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import { useApi, useAuth } from '@/hooks';
import type { UserPublic, Range, TopGenre } from '@statsfm/statsfm.js';
import type { UserPublic, TopGenre } from '@statsfm/statsfm.js';
import clsx from 'clsx';
import Link from 'next/link';
import { useEffect, type FC, useState } from 'react';
import { event } from 'nextjs-google-analytics';
import { ChipGroup, Chip } from '../Chip';
import Scope from '../PrivacyScope';
import { Section } from '../Section';
import { ranges } from './utils';
import type { TimeframeSelection } from './utils';
import { getTimeframeOptions, getTimeframeText } from './utils';
import { NotEnoughData } from './NotEnoughData';

export const TopGenres: FC<{
range: Range;
timeframe: TimeframeSelection;
userProfile: UserPublic;
}> = ({ userProfile, range }) => {
}> = ({ userProfile, timeframe }) => {
const api = useApi();
const { user: currentUser } = useAuth();
const [topGenres, setTopGenres] = useState<TopGenre[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
setTopGenres([]);
api.users
.topGenres(userProfile.id, { range })
.topGenres(userProfile.id, getTimeframeOptions(timeframe))
.then(setTopGenres)
.catch(() => []);
}, [range, userProfile]);
.catch(() => [])
.finally(() => setLoading(false));
}, [timeframe, userProfile]);

const isCurrentUser = currentUser?.id === userProfile.id;

Expand All @@ -32,33 +36,35 @@ export const TopGenres: FC<{
title="Top genres"
description={`${
isCurrentUser ? 'Your' : `${userProfile.displayName}'s`
} top genres ${ranges[range]}`}
} top genres ${getTimeframeText(timeframe)}`}
scope="topGenres"
>
<Scope value="topGenres">
<ChipGroup
className={clsx(topGenres.length === 0 && '!overflow-x-hidden')}
>
{topGenres?.length > 0
? topGenres.map((genre, i) => (
<Chip key={i}>
<Link legacyBehavior href={`/genre/${genre.genre.tag}`}>
<a onClick={() => event('USER_top_genre_click')}>
{genre.genre.tag}
</a>
</Link>
</Chip>
))
: Array(8)
.fill(null)
.map((_v, i) => (
<Chip
className="shrink-0 animate-pulse text-transparent"
key={i}
>
{i.toString().repeat(i + (10 % 17))}
<NotEnoughData data={topGenres} loading={loading}>
{topGenres?.length > 0
? topGenres.map((genre, i) => (
<Chip key={i}>
<Link legacyBehavior href={`/genre/${genre.genre.tag}`}>
<a onClick={() => event('USER_top_genre_click')}>
{genre.genre.tag}
</a>
</Link>
</Chip>
))}
))
: Array(8)
.fill(null)
.map((_v, i) => (
<Chip
className="shrink-0 animate-pulse text-transparent"
key={i}
>
{i.toString().repeat(i + (10 % 17))}
</Chip>
))}
</NotEnoughData>
</ChipGroup>
</Scope>
</Section>
Expand Down
Loading

0 comments on commit 558dc6f

Please sign in to comment.