tangled
alpha
login
or
join now
t1c.dev
/
rocksky
forked from
rocksky.app/rocksky
2
fork
atom
A decentralized music tracking and discovery platform built on AT Protocol 🎵
2
fork
atom
overview
issues
pulls
pipelines
[web] migrate into rocksky xrpc api
tsiry-sandratraina.com
8 months ago
4f9cc9f5
25fad9fb
+197
-229
18 changed files
expand all
collapse all
unified
split
apps
api
src
context.ts
xrpc
app
rocksky
actor
getProfile.ts
web
src
api
feed.ts
library.ts
playlists.ts
components
Handle
Handle.tsx
hooks
useLibrary.tsx
useProfile.tsx
pages
album
Album.tsx
artist
Albums
Albums.tsx
Artist.tsx
profile
Profile.tsx
library
albums
Albums.tsx
lovedtracks
LovedTracks.tsx
overview
topalbums
TopAlbums.tsx
toptracks
TopTracks.tsx
song
PopularAlbums
PopularAlbums.tsx
Song.tsx
+1
apps/api/src/context.ts
···
26
26
export const ctx = {
27
27
oauthClient: await createClient(db),
28
28
resolver: createBidirectionalResolver(baseIdResolver),
29
29
+
baseIdResolver,
29
30
kv: new Map<string, string>(),
30
31
client,
31
32
db: drizzle.db,
+5
-2
apps/api/src/xrpc/app/rocksky/actor/getProfile.ts
···
62
62
return Effect.tryPromise({
63
63
try: async () => {
64
64
if (!params.did?.startsWith("did:plc:") && !!params.did) {
65
65
+
const handle = await ctx.baseIdResolver.handle.resolve(params.did);
65
66
return fetch(
66
67
`https://dns.google/resolve?name=_atproto.${params.did}&type=TXT`
67
68
)
68
69
.then((res) => res.json())
69
70
.then(
70
71
(data) =>
71
71
-
_.get(data, "Answer.0.data", "").replace(/"/g, "").split("=")[1]
72
72
+
_.get(data, "Answer.0.data", handle)
73
73
+
.replace(/"/g, "")
74
74
+
.split("=")[1]
72
75
)
73
76
.then((did) => ({
74
77
did,
···
98
101
return Effect.tryPromise({
99
102
try: async () => {
100
103
if (params.did) {
101
101
-
return fetch(`https://plc.directory/${did}`)
104
104
+
return fetch(`https://plc.directory/${params.did}`)
102
105
.then((res) => res.json())
103
106
.then((data) => ({
104
107
did,
+21
-17
apps/web/src/api/feed.ts
···
1
1
-
import axios from "axios";
2
2
-
import { API_URL } from "../consts";
1
1
+
import { client } from ".";
3
2
4
3
export const getFeed = () => {
5
4
return [];
6
5
};
7
6
8
7
export const getFeedByUri = async (uri: string) => {
9
9
-
const response = await axios.get(`${API_URL}/users/${uri}`);
8
8
+
if (uri.includes("app.rocksky.song")) {
9
9
+
return null;
10
10
+
}
11
11
+
const response = await client.get("/xrpc/app.rocksky.scrobble.getScrobble", {
12
12
+
params: { uri },
13
13
+
});
10
14
11
15
if (response.status !== 200) {
12
16
return null;
13
17
}
14
18
15
19
return {
16
16
-
id: response.data.track_id?.xata_id,
17
17
-
title: response.data.track_id?.title,
18
18
-
artist: response.data.track_id?.artist,
19
19
-
albumArtist: response.data.track_id?.album_artist,
20
20
-
album: response.data.track_id?.album,
21
21
-
cover: response.data.track_id?.album_art,
20
20
+
id: response.data?.id,
21
21
+
title: response.data?.title,
22
22
+
artist: response.data?.artist,
23
23
+
albumArtist: response.data?.albumArtist,
24
24
+
album: response.data?.album,
25
25
+
cover: response.data?.cover,
22
26
tags: [],
23
23
-
artistUri: response.data.track_id?.artist_uri,
24
24
-
albumUri: response.data.track_id?.album_uri,
25
25
-
listeners: response.data.listeners || 1,
26
26
-
scrobbles: response.data.scrobbles || 1,
27
27
-
lyrics: response.data.track_id?.lyrics,
28
28
-
spotifyLink: response.data.track_id?.spotify_link,
29
29
-
composer: response.data.track_id?.composer,
30
30
-
uri: response.data.track_id?.uri,
27
27
+
artistUri: response.data?.artistUri,
28
28
+
albumUri: response.data?.albumUri,
29
29
+
listeners: response.data?.listeners || 1,
30
30
+
scrobbles: response.data?.scrobbles || 1,
31
31
+
lyrics: response.data?.lyrics,
32
32
+
spotifyLink: response.data?.spotifyLink,
33
33
+
composer: response.data?.composer,
34
34
+
uri: response.data?.uri,
31
35
};
32
36
};
+50
-50
apps/web/src/api/library.ts
···
1
1
-
import axios from "axios";
2
2
-
import { API_URL } from "../consts";
1
1
+
import { client } from ".";
3
2
4
3
export const getSongByUri = async (uri: string) => {
5
5
-
const response = await axios.get(`${API_URL}/users/${uri}`);
4
4
+
const response = await client.get("/xrpc/app.rocksky.song.getSong", {
5
5
+
params: { uri },
6
6
+
});
6
7
return {
7
8
id: response.data?.id,
8
9
title: response.data?.title,
9
10
artist: response.data?.artist,
10
10
-
albumArtist: response.data?.album_artist,
11
11
+
albumArtist: response.data?.albumArtist,
11
12
album: response.data?.album,
12
12
-
cover: response.data?.album_art,
13
13
+
cover: response.data?.albumArt,
13
14
tags: [],
14
14
-
artistUri: response.data?.artist_uri,
15
15
-
albumUri: response.data?.album_uri,
16
16
-
listeners: response.data?.listeners || 1,
17
17
-
scrobbles: response.data?.scrobbles || 1,
15
15
+
artistUri: response.data?.artistUri,
16
16
+
albumUri: response.data?.albumUri,
17
17
+
listeners: response.data?.uniqueListeners || 1,
18
18
+
scrobbles: response.data?.playCount || 1,
18
19
lyrics: response.data?.lyrics,
19
19
-
spotifyLink: response.data?.spotify_link,
20
20
+
spotifyLink: response.data?.spotifyLink,
20
21
composer: response.data?.composer,
21
22
uri: response.data?.uri,
22
23
};
···
30
31
id: string;
31
32
title: string;
32
33
artist: string;
33
33
-
album_artist: string;
34
34
-
album_art: string;
34
34
+
albumArtist: string;
35
35
+
albumArt: string;
35
36
uri: string;
36
36
-
play_count: number;
37
37
-
album_uri?: string;
38
38
-
artist_uri?: string;
37
37
+
playCount: number;
38
38
+
albumUri?: string;
39
39
+
artistUri?: string;
39
40
}[]
40
41
> => {
41
41
-
const response = await axios.get(
42
42
-
`${API_URL}/users/${uri}/tracks?size=${limit}`
42
42
+
const response = await client.get(
43
43
+
"/xrpc/app.rocksky.artist.getArtistTracks",
44
44
+
{ params: { uri, limit } }
43
45
);
44
44
-
return response.data;
46
46
+
return response.data.tracks;
45
47
};
46
48
47
49
export const getArtistAlbums = async (
···
52
54
id: string;
53
55
title: string;
54
56
artist: string;
55
55
-
album_art: string;
56
56
-
artist_uri: string;
57
57
+
albumArt: string;
58
58
+
artistUri: string;
57
59
uri: string;
58
60
}[]
59
61
> => {
60
60
-
const response = await axios.get(
61
61
-
`${API_URL}/users/${uri}/albums?size=${limit}`
62
62
+
const response = await client.get(
63
63
+
"/xrpc/app.rocksky.artist.getArtistAlbums",
64
64
+
{ params: { uri, limit } }
62
65
);
63
63
-
return response.data;
66
66
+
return response.data.albums;
64
67
};
65
68
66
69
export const getArtists = async (did: string, offset = 0, limit = 30) => {
67
67
-
const response = await axios.get(
68
68
-
`${API_URL}/users/${did}/artists?size=${limit}&offset=${offset}`
69
69
-
);
70
70
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
71
71
-
return response.data.map((x: any) => ({ ...x, scrobbles: x.play_count }));
70
70
+
const response = await client.get("/xrpc/app.rocksky.actor.getActorArtists", {
71
71
+
params: { did, limit, offset },
72
72
+
});
73
73
+
return response.data;
72
74
};
73
75
74
76
export const getAlbums = async (did: string, offset = 0, limit = 12) => {
75
75
-
const response = await axios.get(
76
76
-
`${API_URL}/users/${did}/albums?size=${limit}&offset=${offset}`
77
77
-
);
78
78
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
79
79
-
return response.data.map((x: any) => ({
80
80
-
...x,
81
81
-
scrobbles: x.play_count,
82
82
-
}));
77
77
+
const response = await client.get("/xrpc/app.rocksky.actor.getActorAlbums", {
78
78
+
params: { did, limit, offset },
79
79
+
});
80
80
+
return response.data;
83
81
};
84
82
85
83
export const getTracks = async (did: string, offset = 0, limit = 20) => {
86
86
-
const response = await axios.get(
87
87
-
`${API_URL}/users/${did}/tracks?size=${limit}&offset=${offset}`
88
88
-
);
89
89
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
90
90
-
return response.data.map((x: any) => ({ ...x, scrobbles: x.play_count }));
84
84
+
const response = await client.get("/xrpc/app.rocksky.actor.getActorSongs", {
85
85
+
params: { did, limit, offset },
86
86
+
});
87
87
+
return response.data;
91
88
};
92
89
93
90
export const getLovedTracks = async (did: string, offset = 0, limit = 20) => {
94
94
-
const response = await axios.get(
95
95
-
`${API_URL}/users/${did}/likes?size=${limit}&offset=${offset}`
91
91
+
const response = await client.get(
92
92
+
"/xrpc/app.rocksky.actor.getActorLovedSongs",
93
93
+
{
94
94
+
params: { did, limit, offset },
95
95
+
}
96
96
);
97
97
-
return response.data;
97
97
+
return response.data.tracks;
98
98
};
99
99
100
100
export const getAlbum = async (did: string, rkey: string) => {
101
101
-
const response = await axios.get(
102
102
-
`${API_URL}/users/${did}/app.rocksky.album/${rkey}`
103
103
-
);
101
101
+
const response = await client.get("/xrpc/app.rocksky.album.getAlbum", {
102
102
+
params: { uri: `at://${did}/app.rocksky.album/${rkey}` },
103
103
+
});
104
104
return response.data;
105
105
};
106
106
107
107
export const getArtist = async (did: string, rkey: string) => {
108
108
-
const response = await axios.get(
109
109
-
`${API_URL}/users/${did}/app.rocksky.artist/${rkey}`
110
110
-
);
108
108
+
const response = await client.get("/xrpc/app.rocksky.artist.getArtist", {
109
109
+
params: { uri: `at://${did}/app.rocksky.artist/${rkey}` },
110
110
+
});
111
111
return response.data;
112
112
};
+13
-7
apps/web/src/api/playlists.ts
···
1
1
-
import axios from "axios";
2
2
-
import { API_URL } from "../consts";
1
1
+
import { client } from ".";
3
2
4
3
export const getPlaylists = async (
5
4
did: string
···
16
15
trackCount: number;
17
16
}[]
18
17
> => {
19
19
-
const response = await axios.get(`${API_URL}/users/${did}/playlists`);
20
20
-
return response.data;
18
18
+
const response = await client.get(
19
19
+
"/xrpc/app.rocksky.actor.getActorPlaylists",
20
20
+
{
21
21
+
params: { did },
22
22
+
}
23
23
+
);
24
24
+
return response.data.playlists;
21
25
};
22
26
23
27
export const getPlaylist = async (
···
56
60
discNumber: number;
57
61
}[];
58
62
}> => {
59
59
-
const response = await axios.get(
60
60
-
`${API_URL}/users/${did}/app.rocksky.playlist/${rkey}`
61
61
-
);
63
63
+
const response = await client.get("/xrpc/app.rocksky.playlist.getPlaylist", {
64
64
+
params: {
65
65
+
uri: `at://${did}/app.rocksky.playlist/${rkey}`,
66
66
+
},
67
67
+
});
62
68
return response.data;
63
69
};
+2
-2
apps/web/src/components/Handle/Handle.tsx
···
39
39
...profiles,
40
40
[did]: {
41
41
avatar: profile.data.avatar,
42
42
-
displayName: profile.data.display_name,
42
42
+
displayName: profile.data.displayName,
43
43
handle: profile.data.handle,
44
44
spotifyConnected: profile.data.spotifyConnected,
45
45
-
createdAt: profile.data.xata_createdat,
45
45
+
createdAt: profile.data.createdAt,
46
46
did,
47
47
},
48
48
}));
+18
apps/web/src/hooks/useLibrary.tsx
···
37
37
queryKey: ["artists", did, offset, limit],
38
38
queryFn: () => getArtists(did, offset, limit),
39
39
enabled: !!did,
40
40
+
select: (data) =>
41
41
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
42
42
+
data?.artists.map((x: any) => ({
43
43
+
...x,
44
44
+
scrobbles: x.playCount,
45
45
+
})),
40
46
});
41
47
42
48
export const useAlbumsQuery = (did: string, offset = 0, limit = 12) =>
···
44
50
queryKey: ["albums", did, offset, limit],
45
51
queryFn: () => getAlbums(did, offset, limit),
46
52
enabled: !!did,
53
53
+
select: (data) =>
54
54
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
55
55
+
data?.albums.map((x: any) => ({
56
56
+
...x,
57
57
+
scrobbles: x.playCount,
58
58
+
})),
47
59
});
48
60
49
61
export const useTracksQuery = (did: string, offset = 0, limit = 20) =>
···
51
63
queryKey: ["tracks", did, offset, limit],
52
64
queryFn: () => getTracks(did, offset, limit),
53
65
enabled: !!did,
66
66
+
select: (data) =>
67
67
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
68
68
+
data?.tracks.map((x: any) => ({
69
69
+
...x,
70
70
+
scrobbles: x.playCount,
71
71
+
})),
54
72
});
55
73
56
74
export const useLovedTracksQuery = (did: string, offset = 0, limit = 20) =>
+13
-11
apps/web/src/hooks/useProfile.tsx
···
43
43
44
44
const fetchProfile = async () => {
45
45
try {
46
46
-
const response = await fetch(`${API_URL}/profile`, {
47
47
-
headers: {
48
48
-
Authorization: `Bearer ${token}`,
49
49
-
},
50
50
-
}).then((res) => res.text());
46
46
+
const response = await fetch(
47
47
+
`${API_URL}/xrpc/app.rocksky.actor.getProfile`,
48
48
+
{
49
49
+
headers: {
50
50
+
Authorization: `Bearer ${token}`,
51
51
+
},
52
52
+
}
53
53
+
).then((res) => res.text());
51
54
setData(response);
52
55
setError(null);
53
56
} catch (e) {
···
62
65
if (data !== "Unauthorized" && data !== "Internal Server Error" && data) {
63
66
const profile = JSON.parse(data);
64
67
setProfile({
65
65
-
avatar: `https://cdn.bsky.app/img/avatar/plain/${localStorage.getItem(
66
66
-
"did"
67
67
-
)}/${profile.avatar.ref["$link"]}@jpeg`,
68
68
+
avatar: profile.avatar,
68
69
displayName: profile.displayName,
69
70
handle: profile.handle,
70
71
spotifyUser: {
71
71
-
isBeta: profile.spotifyUser?.is_beta_user,
72
72
+
isBeta: profile.spotifyUser?.isBetaUser,
72
73
},
73
74
spotifyConnected: profile.spotifyConnected,
74
75
did: profile.did,
75
76
googledriveUser: {
76
76
-
isBeta: profile.googledrive?.is_beta_user,
77
77
+
isBeta: profile.googledrive?.isBetaUser,
77
78
},
78
79
dropboxUser: {
79
79
-
isBeta: profile.dropbox?.is_beta_user,
80
80
+
isBeta: profile.dropbox?.isBetaUser,
80
81
},
81
82
});
82
83
}
83
84
84
85
if (
85
86
!data ||
87
87
+
Object.keys(data).length === 0 ||
86
88
data === "Unauthorized" ||
87
89
data === "Internal Server Error" ||
88
90
(error && localStorage.getItem("token"))
+18
-44
apps/web/src/pages/album/Album.tsx
···
66
66
artistUri?: string;
67
67
label?: string;
68
68
tracks: {
69
69
-
xata_id: string;
70
70
-
track_number: number;
69
69
+
id: string;
70
70
+
trackNumber: number;
71
71
album: string;
72
72
-
album_art: string;
73
73
-
album_artist: string;
72
72
+
albumArt: string;
73
73
+
albumArtist: string;
74
74
title: string;
75
75
artist: string;
76
76
-
xata_created: string;
76
76
+
createdAt: string;
77
77
uri: string;
78
78
-
album_uri: string;
79
79
-
artist_uri: string;
78
78
+
albumUri: string;
79
79
+
artistUri: string;
80
80
duration: number;
81
81
-
disc_number: number;
81
81
+
discNumber: number;
82
82
}[];
83
83
} | null>(null);
84
84
const uri = `${did}/app.rocksky.album/${rkey}`;
···
87
87
if (!isLoading && !isError) {
88
88
setAlbum({
89
89
id: data.id,
90
90
-
albumArt: data.album_art,
91
91
-
artistUri: data.artist_uri,
90
90
+
albumArt: data.albumArt,
91
91
+
artistUri: data.artistUri,
92
92
artist: data.artist,
93
93
title: data.title,
94
94
year: data.year,
95
95
uri: data.uri,
96
96
-
listeners: data.listeners,
97
97
-
scrobbles: data.scrobbles,
96
96
+
listeners: data.uniqueListeners,
97
97
+
scrobbles: data.playCount,
98
98
tracks: data.tracks,
99
99
-
releaseDate: data.release_date
100
100
-
? dayjs(data.release_date).format("MMMM D, YYYY")
99
99
+
releaseDate: data.releaseDate
100
100
+
? dayjs(data.releaseDate).format("MMMM D, YYYY")
101
101
: data.year?.toString(),
102
102
-
label: data.tracks[0].copyright_message || data.tracks[0].label,
102
102
+
label: data.tracks[0].copyrightMessage || data.tracks[0].label,
103
103
});
104
104
// eslint-disable-next-line @typescript-eslint/no-explicit-any
105
105
-
setDisc(Math.max(...data.tracks.map((track: any) => track.disc_number)));
105
105
+
setDisc(Math.max(...data.tracks.map((track: any) => track.discNumber)));
106
106
}
107
107
}, [data, isLoading, isError]);
108
108
···
199
199
<div className="mt-[20px]">
200
200
{disc < 2 && (
201
201
<TableBuilder
202
202
-
data={album?.tracks.map((x) => ({
203
203
-
id: x.xata_id,
204
204
-
trackNumber: x.track_number,
205
205
-
albumArt: x.album_art,
206
206
-
title: x.title,
207
207
-
artist: x.artist,
208
208
-
uri: x.uri,
209
209
-
albumUri: album?.uri,
210
210
-
artistUri: x.artist_uri,
211
211
-
albumArtist: x.album_artist,
212
212
-
duration: x.duration,
213
213
-
discNumber: x.disc_number,
214
214
-
}))}
202
202
+
data={album?.tracks}
215
203
emptyMessage="You haven't listened to any music yet."
216
204
divider="clean"
217
205
overrides={{
···
317
305
Volume {i + 1}
318
306
</LabelLarge>
319
307
<TableBuilder
320
320
-
data={album?.tracks
321
321
-
.filter((x) => x.disc_number == i + 1)
322
322
-
.map((x) => ({
323
323
-
id: x.xata_id,
324
324
-
trackNumber: x.track_number,
325
325
-
albumArt: x.album_art,
326
326
-
title: x.title,
327
327
-
artist: x.artist,
328
328
-
uri: x.uri,
329
329
-
albumUri: album?.uri,
330
330
-
artistUri: x.artist_uri,
331
331
-
albumArtist: x.album_artist,
332
332
-
duration: x.duration,
333
333
-
discNumber: x.disc_number,
334
334
-
}))}
308
308
+
data={album?.tracks.filter((x) => x.discNumber == i + 1)}
335
309
emptyMessage="You haven't listened to any music yet."
336
310
divider="clean"
337
311
overrides={{
+7
-7
apps/web/src/pages/artist/Albums/Albums.tsx
···
24
24
id: string;
25
25
title: string;
26
26
artist: string;
27
27
-
album_art: string;
28
28
-
artist_uri: string;
27
27
+
albumArt: string;
28
28
+
artistUri: string;
29
29
uri: string;
30
30
}[];
31
31
}
···
47
47
<FlexGridItem {...itemProps} key={album.id}>
48
48
{album.uri && (
49
49
<Link to={`/${album.uri.split("at://")[1]}`}>
50
50
-
<SongCover cover={album.album_art} size={230} />
50
50
+
<SongCover cover={album.albumArt} size={230} />
51
51
</Link>
52
52
)}
53
53
-
{!album.uri && <SongCover cover={album.album_art} size={230} />}
53
53
+
{!album.uri && <SongCover cover={album.albumArt} size={230} />}
54
54
{album.uri && (
55
55
<Link to={`/${album.uri.split("at://")[1]}`}>
56
56
<LabelMedium className="!text-[var(--color-text)]">
···
59
59
</Link>
60
60
)}
61
61
{!album.uri && <LabelMedium>{album.title}</LabelMedium>}
62
62
-
{album.artist_uri && (
63
63
-
<Link to={`/${album.artist_uri.split("at://")[1]}`}>
62
62
+
{album.artistUri && (
63
63
+
<Link to={`/${album.artistUri.split("at://")[1]}`}>
64
64
<LabelSmall className="!text-[var(--color-text-muted)]">
65
65
{album.artist}
66
66
</LabelSmall>
67
67
</Link>
68
68
)}
69
69
-
{!album.artist_uri && (
69
69
+
{!album.artistUri && (
70
70
<LabelSmall className="!text-[var(--color-text-muted)]">
71
71
{album.artist}
72
72
</LabelSmall>
+10
-23
apps/web/src/pages/artist/Artist.tsx
···
28
28
const Artist = () => {
29
29
const { did, rkey } = useParams<{ did: string; rkey: string }>();
30
30
31
31
-
const uri = `${did}/app.rocksky.artist/${rkey}`;
31
31
+
const uri = `at://${did}/app.rocksky.artist/${rkey}`;
32
32
const artistResult = useArtistQuery(did!, rkey!);
33
33
const artistTracksResult = useArtistTracksQuery(uri);
34
34
const artistAlbumsResult = useArtistAlbumsQuery(uri);
···
53
53
id: string;
54
54
title: string;
55
55
artist: string;
56
56
-
album_art: string;
57
57
-
artist_uri: string;
56
56
+
albumArt: string;
57
57
+
artistUri: string;
58
58
uri: string;
59
59
}[]
60
60
>([]);
···
72
72
id: artistResult.data.id,
73
73
name: artistResult.data.name,
74
74
born: artistResult.data.born,
75
75
-
bornIn: artistResult.data.born_in,
75
75
+
bornIn: artistResult.data.bornIn,
76
76
died: artistResult.data.died,
77
77
-
listeners: artistResult.data.listeners,
78
78
-
scrobbles: artistResult.data.scrobbles,
77
77
+
listeners: artistResult.data.uniqueListeners,
78
78
+
scrobbles: artistResult.data.playCount,
79
79
picture: artistResult.data.picture,
80
80
tags: artistResult.data.tags,
81
81
uri: artistResult.data.uri,
82
82
-
spotifyLink: artistResult.data.spotify_link,
82
82
+
spotifyLink: artistResult.data.spotifyLink,
83
83
});
84
84
// eslint-disable-next-line react-hooks/exhaustive-deps
85
85
}, [artistResult.data, artistResult.isLoading, artistResult.isError, did]);
···
94
94
}
95
95
96
96
setTopTracks(
97
97
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
98
98
-
artistTracksResult.data.map((track: any) => ({
97
97
+
artistTracksResult.data.map((track) => ({
99
98
...track,
100
100
-
albumArt: track.album_art,
101
101
-
albumArtist: track.album_artist,
102
102
-
albumUri: track.album_uri,
103
103
-
artistUri: track.artist_uri,
99
99
+
scrobbles: track.playCount || 1,
104
100
}))
105
101
);
106
102
}, [
···
119
115
return;
120
116
}
121
117
122
122
-
setTopAlbums(
123
123
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
124
124
-
artistAlbumsResult.data.map((album: any) => ({
125
125
-
...album,
126
126
-
albumArt: album.album_art,
127
127
-
albumArtist: album.album_artist,
128
128
-
albumUri: album.album_uri,
129
129
-
artistUri: album.artist_uri,
130
130
-
}))
131
131
-
);
118
118
+
setTopAlbums(artistAlbumsResult.data);
132
119
}, [
133
120
artistAlbumsResult.data,
134
121
artistAlbumsResult.isLoading,
+4
-4
apps/web/src/pages/profile/Profile.tsx
···
53
53
54
54
setUser({
55
55
avatar: profile.data.avatar,
56
56
-
displayName: profile.data.display_name,
56
56
+
displayName: profile.data.displayName,
57
57
handle: profile.data.handle,
58
58
spotifyUser: {
59
59
-
isBeta: profile.data.spotifyUser?.is_beta_user,
59
59
+
isBeta: profile.data.spotifyUser?.isBetaUser,
60
60
},
61
61
spotifyConnected: profile.data.spotifyConnected,
62
62
did: profile.data.did,
···
66
66
...profiles,
67
67
[did]: {
68
68
avatar: profile.data.avatar,
69
69
-
displayName: profile.data.display_name,
69
69
+
displayName: profile.data.displayName,
70
70
handle: profile.data.handle,
71
71
spotifyConnected: profile.data.spotifyConnected,
72
72
-
createdAt: profile.data.xata_createdat,
72
72
+
createdAt: profile.data.createdat,
73
73
did,
74
74
},
75
75
}));
+2
-2
apps/web/src/pages/profile/library/albums/Albums.tsx
···
82
82
id: x.id,
83
83
title: x.title,
84
84
artist: x.artist,
85
85
-
albumArt: x.album_art,
86
86
-
artistUri: x.artist_uri,
85
85
+
albumArt: x.albumArt,
86
86
+
artistUri: x.artistUri,
87
87
uri: x.uri,
88
88
scrobbles: x.scrobbles,
89
89
}))
+3
-7
apps/web/src/pages/profile/lovedtracks/LovedTracks.tsx
···
67
67
68
68
setLovedTracks(
69
69
// eslint-disable-next-line @typescript-eslint/no-explicit-any
70
70
-
lovedTracksResult.data.records.map((item: any) => ({
71
71
-
...item.track_id,
72
72
-
albumArt: item.track_id.album_art,
73
73
-
albumArtist: item.track_id.album_artist,
74
74
-
albumUri: item.track_id.album_uri,
75
75
-
artistUri: item.track_id.artist_uri,
76
76
-
date: item.xata_createdat,
70
70
+
lovedTracksResult.data.map((item: any) => ({
71
71
+
...item,
72
72
+
date: item.createdAt,
77
73
}))
78
74
);
79
75
// eslint-disable-next-line react-hooks/exhaustive-deps
+4
-4
apps/web/src/pages/profile/overview/topalbums/TopAlbums.tsx
···
76
76
topAlbums.map((album: any) => (
77
77
<FlexGridItem {...itemProps} key={album.id}>
78
78
<Link to={`/${album.uri?.split("at://")[1]}`}>
79
79
-
<SongCover cover={album.album_art} size={230} />
79
79
+
<SongCover cover={album.albumArt} size={230} />
80
80
</Link>
81
81
<Link to={`/${album.uri?.split("at://")[1]}`}>
82
82
<LabelMedium className="!text-[var(--color-text)]">
83
83
{album.title}
84
84
</LabelMedium>
85
85
</Link>
86
86
-
{album.artist_uri && (
87
87
-
<Link to={`/${album.artist_uri.split("at://")[1]}`}>
86
86
+
{album.artistUri && (
87
87
+
<Link to={`/${album.artistUri.split("at://")[1]}`}>
88
88
<LabelSmall className="!text-[var(--color-text-muted)]">
89
89
{album.artist}
90
90
</LabelSmall>
91
91
</Link>
92
92
)}
93
93
-
{!album.artist_uri && (
93
93
+
{!album.artistUri && (
94
94
<LabelSmall className="!text-[var(--color-text-muted)]">
95
95
{album.artist}
96
96
</LabelSmall>
+1
-10
apps/web/src/pages/profile/overview/toptracks/TopTracks.tsx
···
88
88
return;
89
89
}
90
90
91
91
-
setTopTracks(
92
92
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
93
93
-
tracksResult.data.map((track: any) => ({
94
94
-
...track,
95
95
-
albumArt: track.album_art,
96
96
-
albumArtist: track.album_artist,
97
97
-
albumUri: track.album_uri,
98
98
-
artistUri: track.artist_uri,
99
99
-
}))
100
100
-
);
91
91
+
setTopTracks(tracksResult.data);
101
92
// eslint-disable-next-line react-hooks/exhaustive-deps
102
93
}, [tracksResult.data, tracksResult.isLoading, tracksResult.isError, did]);
103
94
+7
-7
apps/web/src/pages/song/PopularAlbums/PopularAlbums.tsx
···
24
24
id: string;
25
25
title: string;
26
26
artist: string;
27
27
-
album_art: string;
28
28
-
artist_uri: string;
27
27
+
albumArt: string;
28
28
+
artistUri: string;
29
29
uri: string;
30
30
}[];
31
31
artist: string;
···
51
51
<FlexGridItem {...itemProps} key={album.id}>
52
52
{album.uri && (
53
53
<Link to={`/${album.uri.split("at://")[1]}`}>
54
54
-
<SongCover cover={album.album_art} size={230} />
54
54
+
<SongCover cover={album.albumArt} size={230} />
55
55
</Link>
56
56
)}
57
57
-
{!album.uri && <SongCover cover={album.album_art} size={230} />}
57
57
+
{!album.uri && <SongCover cover={album.albumArt} size={230} />}
58
58
{album.uri && (
59
59
<Link to={`/${album.uri.split("at://")[1]}`}>
60
60
<LabelMedium className="!text-[var(--color-text)]">
···
63
63
</Link>
64
64
)}
65
65
{!album.uri && <LabelMedium>{album.title}</LabelMedium>}
66
66
-
{album.artist_uri && (
67
67
-
<Link to={`/${album.artist_uri.split("at://")[1]}`}>
66
66
+
{album.artistUri && (
67
67
+
<Link to={`/${album.artistUri.split("at://")[1]}`}>
68
68
<LabelSmall className="!text-[var(--color-text-muted)]">
69
69
{album.artist}
70
70
</LabelSmall>
71
71
</Link>
72
72
)}
73
73
-
{!album.artist_uri && (
73
73
+
{!album.artistUri && (
74
74
<LabelSmall className="!text-[var(--color-text-muted)]">
75
75
{album.artist}
76
76
</LabelSmall>
+18
-32
apps/web/src/pages/song/Song.tsx
···
56
56
const Song = () => {
57
57
const { did, rkey } = useParams<{ did: string; rkey: string }>();
58
58
59
59
-
let uri = `${did}/app.rocksky.scrobble/${rkey}`;
59
59
+
let uri = `at://${did}/app.rocksky.scrobble/${rkey}`;
60
60
61
61
if (window.location.pathname.includes("app.rocksky.song")) {
62
62
-
uri = `${did}/app.rocksky.song/${rkey}`;
62
62
+
uri = `at://${did}/app.rocksky.song/${rkey}`;
63
63
}
64
64
if (window.location.pathname.includes("app.rocksky.scrobble")) {
65
65
-
uri = `${did}/app.rocksky.scrobble/${rkey}`;
65
65
+
uri = `at://${did}/app.rocksky.scrobble/${rkey}`;
66
66
}
67
67
68
68
const scrobbleResult = useFeedByUriQuery(uri);
69
69
const songResult = useSongByUriQuery(uri);
70
70
71
71
const artistTracksResult = useArtistTracksQuery(
72
72
-
songResult.data?.artistUri?.split("at://")[1] ||
73
73
-
scrobbleResult.data?.artistUri?.split("at://")[1],
72
72
+
songResult.data?.artistUri || scrobbleResult.data?.artistUri,
74
73
5
75
74
);
76
75
const artistAlbumResult = useArtistAlbumsQuery(
77
77
-
songResult.data?.artistUri?.split("at://")[1] ||
78
78
-
scrobbleResult.data?.artistUri?.split("at://")[1],
76
76
+
songResult.data?.artistUri || scrobbleResult.data?.artistUri,
79
77
10
80
78
);
81
79
···
100
98
id: string;
101
99
title: string;
102
100
artist: string;
103
103
-
album_art: string;
104
104
-
artist_uri: string;
101
101
+
albumArt: string;
102
102
+
artistUri: string;
105
103
uri: string;
106
104
}[]
107
105
>([]);
···
162
160
id: x.id,
163
161
title: x.title,
164
162
artist: x.artist,
165
165
-
albumArtist: x.album_artist,
166
166
-
albumArt: x.album_art,
163
163
+
albumArtist: x.albumArtist,
164
164
+
albumArt: x.albumArt,
167
165
uri: x.uri,
168
168
-
scrobbles: x.play_count,
169
169
-
albumUri: x.album_uri,
170
170
-
artistUri: x.artist_uri,
166
166
+
artistUri: x.artistUri,
167
167
+
scrobbles: x.playCount,
171
168
}))
172
169
);
173
170
// eslint-disable-next-line react-hooks/exhaustive-deps
···
182
179
return;
183
180
}
184
181
185
185
-
setTopAlbums(
186
186
-
artistAlbumResult.data.map((x) => ({
187
187
-
id: x.id,
188
188
-
title: x.title,
189
189
-
artist: x.artist,
190
190
-
album_art: x.album_art,
191
191
-
artist_uri: x.artist_uri!,
192
192
-
uri: x.uri,
193
193
-
}))
194
194
-
);
182
182
+
setTopAlbums(artistAlbumResult.data);
195
183
// eslint-disable-next-line react-hooks/exhaustive-deps
196
184
}, [artistAlbumResult.data, artistAlbumResult.isLoading]);
197
185
···
315
303
</div>
316
304
</Group>
317
305
318
318
-
{
319
319
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
320
320
-
song?.tags.map((tag: any) => (
321
321
-
<Tag closeable={false} kind={KIND.purple}>
322
322
-
{tag}
323
323
-
</Tag>
324
324
-
))
325
325
-
}
306
306
+
{// eslint-disable-next-line @typescript-eslint/no-explicit-any
307
307
+
song?.tags.map((tag: any) => (
308
308
+
<Tag closeable={false} kind={KIND.purple}>
309
309
+
{tag}
310
310
+
</Tag>
311
311
+
))}
326
312
327
313
{song?.lyrics && (
328
314
<>