Skip to content

Commit 59aa23f

Browse files
committed
feat(user): more deeplinks on user page
1 parent 847f22f commit 59aa23f

File tree

7 files changed

+68
-31
lines changed

7 files changed

+68
-31
lines changed

next.config.js

+12
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ module.exports = withBundleAnalyzer({
102102
source: '/:id/tracks',
103103
destination: '/user/:id/tracks',
104104
},
105+
{
106+
source: '/:id/listeningClocks',
107+
destination: '/user/:id/listeningClocks',
108+
},
109+
{
110+
source: '/:id/genres',
111+
destination: '/user/:id/genres',
112+
},
113+
{
114+
source: '/:id/recentStreams',
115+
destination: '/user/:id/recentStreams',
116+
},
105117
];
106118
},
107119
generateBuildId: () => nextBuildId({ dir: __dirname }),

src/components/User/TopAlbums.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { TopAlbum, UserPublic } from '@statsfm/statsfm.js';
44
import type { RefObject } from 'react';
55
import { useState, type FC, useEffect } from 'react';
66
import { event } from 'nextjs-google-analytics';
7-
import type { UserPageCarouselsWithGrid } from '@/utils';
87
import { Carousel } from '../Carousel';
98
import Scope from '../PrivacyScope';
109
import {
@@ -26,7 +25,7 @@ export const TopAlbums: FC<{
2625
timeframe: TimeframeSelection;
2726
albumRef: RefObject<HTMLElement>;
2827
userProfile: UserPublic;
29-
activeCarousel: UserPageCarouselsWithGrid | null;
28+
activeCarousel: boolean;
3029
}> = ({ albumRef, userProfile, timeframe, activeCarousel }) => {
3130
const api = useApi();
3231
const { user: currentUser } = useAuth();
@@ -60,7 +59,7 @@ export const TopAlbums: FC<{
6059
const isCurrentUser = currentUser?.id === userProfile.id;
6160

6261
return (
63-
<Carousel gridMode={activeCarousel === 'albums'} itemHeight={255}>
62+
<Carousel gridMode={activeCarousel} itemHeight={255}>
6463
<Section
6564
ref={albumRef}
6665
title="Top albums"

src/components/User/TopArtists.tsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { useApi, useAuth } from '@/hooks';
22
import formatter from '@/utils/formatter';
33
import type { TopArtist, UserPublic } from '@statsfm/statsfm.js';
4-
import type { RefObject } from 'react';
5-
import { useState, type FC, useEffect } from 'react';
4+
import { useState, type FC, useEffect, type RefObject } from 'react';
65
import { event } from 'nextjs-google-analytics';
7-
import type { UserPageCarouselsWithGrid } from '@/utils';
86
import { Carousel } from '../Carousel';
97
import Scope from '../PrivacyScope';
108
import {
@@ -26,7 +24,7 @@ export const TopArtists: FC<{
2624
timeframe: TimeframeSelection;
2725
artistRef: RefObject<HTMLElement>;
2826
userProfile: UserPublic;
29-
activeCarousel: UserPageCarouselsWithGrid | null;
27+
activeCarousel: boolean;
3028
}> = ({ artistRef, userProfile, timeframe, activeCarousel }) => {
3129
const api = useApi();
3230
const { user: currentUser } = useAuth();
@@ -60,7 +58,7 @@ export const TopArtists: FC<{
6058
const isCurrentUser = currentUser?.id === userProfile.id;
6159

6260
return (
63-
<Carousel gridMode={activeCarousel === 'artists'} itemHeight={262}>
61+
<Carousel gridMode={activeCarousel} itemHeight={262}>
6462
<Section
6563
ref={artistRef}
6664
title="Top artists"

src/components/User/TopGenres.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import { NotEnoughData } from './NotEnoughData';
1414
export const TopGenres: FC<{
1515
timeframe: TimeframeSelection;
1616
userProfile: UserPublic;
17-
}> = ({ userProfile, timeframe }) => {
17+
topGenresRef: React.RefObject<HTMLElement>;
18+
}> = ({ userProfile, timeframe, topGenresRef: ref }) => {
1819
const api = useApi();
1920
const { user: currentUser } = useAuth();
2021
const [topGenres, setTopGenres] = useState<TopGenre[]>([]);
@@ -38,6 +39,7 @@ export const TopGenres: FC<{
3839
isCurrentUser ? 'Your' : `${userProfile.displayName}'s`
3940
} top genres ${getTimeframeText(timeframe)}`}
4041
scope="topGenres"
42+
ref={ref}
4143
>
4244
<Scope value="topGenres">
4345
<ChipGroup

src/components/User/TopTracks.tsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { useApi, useAuth } from '@/hooks';
22
import formatter from '@/utils/formatter';
33
import type { TopTrack, UserPublic } from '@statsfm/statsfm.js';
4-
import type { RefObject } from 'react';
5-
import { useState, type FC, useEffect } from 'react';
4+
import { useState, type FC, useEffect, type RefObject } from 'react';
65
import { event } from 'nextjs-google-analytics';
7-
import type { UserPageCarouselsWithGrid } from '@/utils';
86
import { Carousel } from '../Carousel';
97
import Scope from '../PrivacyScope';
108
import {
@@ -26,7 +24,7 @@ export const TopTracks: FC<{
2624
timeframe: TimeframeSelection;
2725
trackRef: RefObject<HTMLElement>;
2826
userProfile: UserPublic;
29-
activeCarousel: UserPageCarouselsWithGrid | null;
27+
activeCarousel: boolean;
3028
}> = ({ trackRef, userProfile, timeframe, activeCarousel }) => {
3129
const api = useApi();
3230
const { user: currentUser } = useAuth();
@@ -60,7 +58,7 @@ export const TopTracks: FC<{
6058
const isCurrentUser = currentUser?.id === userProfile.id;
6159

6260
return (
63-
<Carousel gridMode={activeCarousel === 'tracks'} itemHeight={276}>
61+
<Carousel gridMode={activeCarousel} itemHeight={276}>
6462
<Section
6563
ref={trackRef}
6664
title="Top tracks"

src/pages/user/[id]/[[...deeplink]].tsx

+38-16
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import {
4040
TopGenres,
4141
TopTracks,
4242
} from '@/components/User';
43-
import { clockProps, type UserPageCarouselsWithGrid } from '@/utils';
43+
import { clockProps, type UserScrollIntoView } from '@/utils';
4444
import dynamic from 'next/dynamic';
4545
import type { TimeframeSelection } from '@/components/User/utils';
4646
import { getTimeframeOptions, getTimeframeText } from '@/components/User/utils';
@@ -51,18 +51,25 @@ type Props = SSRProps & {
5151
userProfile: statsfm.UserPublic;
5252
friendStatus: statsfm.FriendStatus;
5353
friendCount: number;
54-
activeCarousel: UserPageCarouselsWithGrid | null;
54+
scrollIntoView: UserScrollIntoView | null;
5555
};
5656

57-
function activeGridModeFromDeepLink(
57+
function activeScrollIntoViewFromDeepLink(
5858
deeplink: string | string[] | undefined
59-
): UserPageCarouselsWithGrid | null {
59+
): UserScrollIntoView | null {
6060
if (typeof deeplink !== 'object') return null;
6161
if (deeplink.length !== 1) return null;
6262

6363
const [id] = deeplink;
64-
// TODO: this should rewrite or redirect
65-
if (id !== 'tracks' && id !== 'albums' && id !== 'artists') return null;
64+
if (
65+
id !== 'genres' &&
66+
id !== 'tracks' &&
67+
id !== 'albums' &&
68+
id !== 'artists' &&
69+
id !== 'listeningClocks' &&
70+
id !== 'recentStreams'
71+
)
72+
return null;
6673

6774
return id;
6875
}
@@ -77,10 +84,13 @@ export const getServerSideProps: GetServerSideProps<Props> = async (ctx) => {
7784
throw new Error('no param id recieved');
7885
}
7986

80-
const activeCarousel = activeGridModeFromDeepLink(deeplink);
87+
const scrollIntoView = activeScrollIntoViewFromDeepLink(deeplink);
8188

8289
const userProfile = await api.users.get(id).catch(() => {});
83-
if (!userProfile) return { notFound: true };
90+
if (!userProfile)
91+
return {
92+
notFound: true,
93+
};
8494

8595
const user = await fetchUser(ctx);
8696

@@ -105,11 +115,11 @@ export const getServerSideProps: GetServerSideProps<Props> = async (ctx) => {
105115

106116
return {
107117
props: {
108-
activeCarousel,
109118
userProfile,
110119
user,
111120
friendStatus,
112121
friendCount,
122+
scrollIntoView,
113123
},
114124
};
115125
};
@@ -199,7 +209,7 @@ const User: NextPage<Props> = ({
199209
userProfile: user,
200210
friendStatus,
201211
friendCount,
202-
activeCarousel,
212+
scrollIntoView,
203213
}) => {
204214
const api = useApi();
205215
const router = useRouter();
@@ -221,6 +231,9 @@ const User: NextPage<Props> = ({
221231
const topTracksRef = useRef<HTMLElement>(null);
222232
const topAlbumsRef = useRef<HTMLElement>(null);
223233
const topArtistsRef = useRef<HTMLElement>(null);
234+
const topGenresRef = useRef<HTMLElement>(null);
235+
const listeningClocksRef = useRef<HTMLElement>(null);
236+
const recentStreamsRef = useRef<HTMLElement>(null);
224237

225238
const isCurrentUser = currentUser?.id === user.id;
226239

@@ -268,13 +281,16 @@ const User: NextPage<Props> = ({
268281
}, [timeframe, user]);
269282

270283
useEffect(() => {
271-
const refs: Record<UserPageCarouselsWithGrid, RefObject<HTMLElement>> = {
284+
const refs: Record<UserScrollIntoView, RefObject<HTMLElement>> = {
272285
tracks: topTracksRef,
273286
albums: topAlbumsRef,
274287
artists: topArtistsRef,
288+
genres: topGenresRef,
289+
listeningClocks: listeningClocksRef,
290+
recentStreams: recentStreamsRef,
275291
};
276292

277-
if (activeCarousel) refs[activeCarousel].current?.scrollIntoView();
293+
if (scrollIntoView) refs[scrollIntoView].current?.scrollIntoView();
278294
}, []);
279295

280296
// TODO: improvements
@@ -499,28 +515,32 @@ const User: NextPage<Props> = ({
499515
)}
500516
</section>
501517

502-
<TopGenres timeframe={timeframe} userProfile={user} />
518+
<TopGenres
519+
timeframe={timeframe}
520+
userProfile={user}
521+
topGenresRef={topGenresRef}
522+
/>
503523

504524
<TopTracks
505525
timeframe={timeframe}
506526
userProfile={user}
507527
trackRef={topTracksRef}
508-
activeCarousel={activeCarousel}
528+
activeCarousel={scrollIntoView === 'tracks'}
509529
/>
510530

511531
<TopArtists
512532
timeframe={timeframe}
513533
userProfile={user}
514534
artistRef={topArtistsRef}
515-
activeCarousel={activeCarousel}
535+
activeCarousel={scrollIntoView === 'artists'}
516536
/>
517537

518538
{user.isPlus && (
519539
<TopAlbums
520540
timeframe={timeframe}
521541
userProfile={user}
522542
albumRef={topAlbumsRef}
523-
activeCarousel={activeCarousel}
543+
activeCarousel={scrollIntoView === 'albums'}
524544
/>
525545
)}
526546

@@ -535,6 +555,7 @@ const User: NextPage<Props> = ({
535555
} listening habits throughout the day ${getTimeframeText(
536556
timeframe
537557
)} `}
558+
ref={listeningClocksRef}
538559
>
539560
<Scope value="streamStats">
540561
<div className="flex-1 content-center text-center">
@@ -555,6 +576,7 @@ const User: NextPage<Props> = ({
555576
isCurrentUser ? 'Your' : `${user.displayName}'s`
556577
} recently played tracks`}
557578
scope="recentlyPlayed"
579+
ref={recentStreamsRef}
558580
>
559581
{({ headerRef }) => (
560582
<Scope value="recentlyPlayed">

src/utils/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,10 @@ export * from './AppConfig';
33
export * from './dayjs';
44
export * from './clocks';
55

6-
export type UserPageCarouselsWithGrid = 'tracks' | 'albums' | 'artists';
6+
export type UserScrollIntoView =
7+
| 'genres'
8+
| 'tracks'
9+
| 'albums'
10+
| 'artists'
11+
| 'listeningClocks'
12+
| 'recentStreams';

0 commit comments

Comments
 (0)