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] start using rocksky xrpc api
tsiry-sandratraina.com
8 months ago
dd7a78d8
eb25963b
+93
-58
8 changed files
expand all
collapse all
unified
split
apps
web
src
api
charts.ts
index.ts
profile.ts
hooks
useChart.tsx
useFeed.tsx
useNowPlaying.tsx
pages
home
nowplayings
NowPlayings.tsx
turbo.json
+4
-4
apps/web/src/api/charts.ts
···
7
8
export const getSongChart = async (uri: string) => {
9
const response = await axios.get(
10
-
`${API_URL}/public/scrobbleschart?songuri=${uri}`
11
);
12
if (response.status !== 200) {
13
return [];
···
17
18
export const getArtistChart = async (uri: string) => {
19
const response = await axios.get(
20
-
`${API_URL}/public/scrobbleschart?artisturi=${uri}`
21
);
22
if (response.status !== 200) {
23
return [];
···
27
28
export const getAlbumChart = async (uri: string) => {
29
const response = await axios.get(
30
-
`${API_URL}/public/scrobbleschart?albumuri=${uri}`
31
);
32
if (response.status !== 200) {
33
return [];
···
37
38
export const getProfileChart = async (did: string) => {
39
const response = await axios.get(
40
-
`${API_URL}/public/scrobbleschart?did=${did}`
41
);
42
if (response.status !== 200) {
43
return [];
···
7
8
export const getSongChart = async (uri: string) => {
9
const response = await axios.get(
10
+
`${API_URL}/xrpc/app.rocksky.charts.getScrobblesChart?songuri=${uri}`
11
);
12
if (response.status !== 200) {
13
return [];
···
17
18
export const getArtistChart = async (uri: string) => {
19
const response = await axios.get(
20
+
`${API_URL}/xrpc/app.rocksky.charts.getScrobblesChart?artisturi=${uri}`
21
);
22
if (response.status !== 200) {
23
return [];
···
27
28
export const getAlbumChart = async (uri: string) => {
29
const response = await axios.get(
30
+
`${API_URL}/xrpc/app.rocksky.charts.getScrobblesChart?albumuri=${uri}`
31
);
32
if (response.status !== 200) {
33
return [];
···
37
38
export const getProfileChart = async (did: string) => {
39
const response = await axios.get(
40
+
`${API_URL}/xrpc/app.rocksky.charts.getScrobblesChart?did=${did}`
41
);
42
if (response.status !== 200) {
43
return [];
+6
apps/web/src/api/index.ts
···
0
0
0
0
0
0
···
1
+
import axios from "axios";
2
+
import { API_URL } from "../consts";
3
+
4
+
export const client = axios.create({
5
+
baseURL: API_URL,
6
+
});
+6
-2
apps/web/src/api/profile.ts
···
8
};
9
10
export const getProfileStatsByDid = async (did: string) => {
11
-
const response = await axios.get(`${API_URL}/users/${did}/stats`);
0
0
0
12
return response.data;
13
};
14
···
18
size = 10
19
): Promise<Scrobble[]> => {
20
const response = await axios.get<Scrobble[]>(
21
-
`${API_URL}/users/${did}/scrobbles?size=${size}&offset=${offset}`
0
22
);
23
return response.data;
24
};
···
8
};
9
10
export const getProfileStatsByDid = async (did: string) => {
11
+
const response = await axios.get(
12
+
`${API_URL}/xrpc/app.rocksky.stats.getStats`,
13
+
{ params: { did } }
14
+
);
15
return response.data;
16
};
17
···
21
size = 10
22
): Promise<Scrobble[]> => {
23
const response = await axios.get<Scrobble[]>(
24
+
`${API_URL}/users/${did}/scrobbles`,
25
+
{ params: { size, offset } }
26
);
27
return response.data;
28
};
+36
-22
apps/web/src/hooks/useChart.tsx
···
1
import { useQuery } from "@tanstack/react-query";
2
-
import axios from "axios";
3
import useSWR from "swr";
4
-
import { getArtistChart, getSongChart } from "../api/charts";
0
0
0
0
0
0
5
import { API_URL } from "../consts";
6
7
export const useScrobblesChartQuery = () =>
8
useQuery({
9
queryKey: ["scrobblesChart"],
10
-
queryFn: () =>
11
-
fetch(`${API_URL}/public/scrobbleschart`, {
12
-
method: "GET",
13
-
}).then((res) => res.json()),
14
});
15
16
export const useSongChartQuery = (uri: string) =>
17
useQuery({
18
queryKey: ["songChart", uri],
19
queryFn: () => getSongChart(uri),
0
20
});
21
22
export const useArtistChartQuery = (uri: string) =>
23
useQuery({
24
queryKey: ["artistChart", uri],
25
queryFn: () => getArtistChart(uri),
0
26
});
27
28
export const useAlbumChartQuery = (uri: string) =>
29
useQuery({
30
queryKey: ["albumChart", uri],
31
-
queryFn: () => getArtistChart(uri),
0
32
});
33
34
export const useProfileChartQuery = (did: string) =>
35
useQuery({
36
queryKey: ["profileChart", did],
37
-
queryFn: () => getArtistChart(did),
0
38
});
39
40
function useChart() {
···
43
method: "GET",
44
}).then((res) => res.json());
45
46
-
const { data: scrobblesChart } = useSWR("/public/scrobbleschart", fetcher);
0
0
0
47
48
const getScrobblesChart = () => {
49
-
return scrobblesChart || [];
50
};
51
52
const getSongChart = async (uri: string) => {
53
-
const response = await axios.get(
54
-
`${API_URL}/public/scrobbleschart?songuri=${uri}`
0
55
);
56
if (response.status !== 200) {
57
return [];
58
}
59
-
return response.data;
60
};
61
62
const getArtistChart = async (uri: string) => {
63
-
const response = await axios.get(
64
-
`${API_URL}/public/scrobbleschart?artisturi=${uri}`
0
65
);
66
if (response.status !== 200) {
67
return [];
68
}
69
-
return response.data;
70
};
71
72
const getAlbumChart = async (uri: string) => {
73
-
const response = await axios.get(
74
-
`${API_URL}/public/scrobbleschart?albumuri=${uri}`
0
75
);
76
if (response.status !== 200) {
77
return [];
78
}
79
-
return response.data;
80
};
81
82
const getProfileChart = async (did: string) => {
83
-
const response = await axios.get(
84
-
`${API_URL}/public/scrobbleschart?did=${did}`
0
85
);
86
if (response.status !== 200) {
87
return [];
88
}
89
-
return response.data;
90
};
91
92
return {
···
1
import { useQuery } from "@tanstack/react-query";
0
2
import useSWR from "swr";
3
+
import { client } from "../api";
4
+
import {
5
+
getAlbumChart,
6
+
getArtistChart,
7
+
getProfileChart,
8
+
getSongChart,
9
+
} from "../api/charts";
10
import { API_URL } from "../consts";
11
12
export const useScrobblesChartQuery = () =>
13
useQuery({
14
queryKey: ["scrobblesChart"],
15
+
queryFn: () => client.get("/xrpc/app.rocksky.charts.getScrobblesChart"),
16
+
select: ({ data }) => data.scrobbles || [],
0
0
17
});
18
19
export const useSongChartQuery = (uri: string) =>
20
useQuery({
21
queryKey: ["songChart", uri],
22
queryFn: () => getSongChart(uri),
23
+
select: (data) => data.scrobbles || [],
24
});
25
26
export const useArtistChartQuery = (uri: string) =>
27
useQuery({
28
queryKey: ["artistChart", uri],
29
queryFn: () => getArtistChart(uri),
30
+
select: (data) => data.scrobbles || [],
31
});
32
33
export const useAlbumChartQuery = (uri: string) =>
34
useQuery({
35
queryKey: ["albumChart", uri],
36
+
queryFn: () => getAlbumChart(uri),
37
+
select: (data) => data.scrobbles || [],
38
});
39
40
export const useProfileChartQuery = (did: string) =>
41
useQuery({
42
queryKey: ["profileChart", did],
43
+
queryFn: () => getProfileChart(did),
44
+
select: (data) => data.scrobbles || [],
45
});
46
47
function useChart() {
···
50
method: "GET",
51
}).then((res) => res.json());
52
53
+
const { data: scrobblesChart } = useSWR(
54
+
"/xrpc/app.rocksky.charts.getScrobblesChart",
55
+
fetcher
56
+
);
57
58
const getScrobblesChart = () => {
59
+
return scrobblesChart?.scrobbles || [];
60
};
61
62
const getSongChart = async (uri: string) => {
63
+
const response = await client.get(
64
+
"/xrpc/app.rocksky.charts.getScrobblesChart",
65
+
{ params: { songuri: uri } }
66
);
67
if (response.status !== 200) {
68
return [];
69
}
70
+
return response.data.scrobbles;
71
};
72
73
const getArtistChart = async (uri: string) => {
74
+
const response = await client.get(
75
+
"/xrpc/app.rocksky.charts.getScrobblesChart",
76
+
{ params: { artisturi: uri } }
77
);
78
if (response.status !== 200) {
79
return [];
80
}
81
+
return response.data.scrobbles;
82
};
83
84
const getAlbumChart = async (uri: string) => {
85
+
const response = await client.get(
86
+
"/xrpc/app.rocksky.charts.getScrobblesChart",
87
+
{ params: { albumuri: uri } }
88
);
89
if (response.status !== 200) {
90
return [];
91
}
92
+
return response.data.scrobbles;
93
};
94
95
const getProfileChart = async (did: string) => {
96
+
const response = await client.get(
97
+
"/xrpc/app.rocksky.charts.getScrobblesChart",
98
+
{ params: { did } }
99
);
100
if (response.status !== 200) {
101
return [];
102
}
103
+
return response.data.scrobbles;
104
};
105
106
return {
+6
-5
apps/web/src/hooks/useFeed.tsx
···
1
import { useQuery } from "@tanstack/react-query";
0
2
import { getFeedByUri } from "../api/feed";
3
-
import { API_URL } from "../consts";
4
5
-
export const useFeedQuery = (size = 114) =>
6
useQuery({
7
queryKey: ["feed"],
8
queryFn: () =>
9
-
fetch(`${API_URL}/public/scrobbles?size=${size}`, {
10
-
method: "GET",
11
-
}).then((res) => res.json()),
12
refetchInterval: 5000,
0
13
});
14
15
export const useFeedByUriQuery = (uri: string) =>
···
1
import { useQuery } from "@tanstack/react-query";
2
+
import { client } from "../api";
3
import { getFeedByUri } from "../api/feed";
0
4
5
+
export const useFeedQuery = (limit = 114) =>
6
useQuery({
7
queryKey: ["feed"],
8
queryFn: () =>
9
+
client.get("/xrpc/app.rocksky.scrobble.getScrobbles", {
10
+
params: { limit },
11
+
}),
12
refetchInterval: 5000,
13
+
select: (res) => res.data.scrobbles || [],
14
});
15
16
export const useFeedByUriQuery = (uri: string) =>
+12
-10
apps/web/src/hooks/useNowPlaying.tsx
···
1
import { useQuery } from "@tanstack/react-query";
2
-
import { API_URL } from "../consts";
3
4
export type NowPlayings = {
5
id: string;
6
title: string;
7
artist: string;
8
-
album_art: string;
9
-
artist_uri?: string;
10
uri: string;
11
avatar: string;
12
handle: string;
13
did: string;
14
-
created_at: string;
15
-
track_id: string;
16
-
track_uri: string;
17
}[];
18
19
export const useNowPlayingsQuery = () =>
20
-
useQuery<NowPlayings>({
21
queryKey: ["now-playings"],
22
queryFn: () =>
23
-
fetch(`${API_URL}/now-playings?size=7`, {
24
-
method: "GET",
25
-
}).then((res) => res.json()),
0
26
refetchInterval: 5000,
0
27
});
···
1
import { useQuery } from "@tanstack/react-query";
2
+
import { client } from "../api";
3
4
export type NowPlayings = {
5
id: string;
6
title: string;
7
artist: string;
8
+
albumArt: string;
9
+
artistUri?: string;
10
uri: string;
11
avatar: string;
12
handle: string;
13
did: string;
14
+
createdAt: string;
15
+
trackId: string;
16
+
trackUri: string;
17
}[];
18
19
export const useNowPlayingsQuery = () =>
20
+
useQuery({
21
queryKey: ["now-playings"],
22
queryFn: () =>
23
+
client.get<{ nowPlayings: NowPlayings }>(
24
+
"/xrpc/app.rocksky.feed.getNowPlayings",
25
+
{ params: { size: 7 } }
26
+
),
27
refetchInterval: 5000,
28
+
select: (res) => res.data.nowPlayings || [],
29
});
+15
-15
apps/web/src/pages/home/nowplayings/NowPlayings.tsx
···
94
id: string;
95
title: string;
96
artist: string;
97
-
album_art: string;
98
-
artist_uri?: string;
99
uri: string;
100
avatar: string;
101
handle: string;
102
did: string;
103
-
created_at: string;
104
-
track_id: string;
105
-
track_uri: string;
106
} | null>(null);
107
const [currentIndex, setCurrentIndex] = useState(0);
108
const [progress, setProgress] = useState(0);
···
211
</div>
212
</Link>
213
<span className="ml-[10px] text-[15px] text-[var(--color-text-muted)]">
214
-
{dayjs.utc(currentlyPlaying?.created_at).local().fromNow()}
215
</span>
216
</div>
217
</ModalHeader>
···
225
)}
226
</div>
227
<div className="flex flex-col items-center flex-1">
228
-
{currentlyPlaying?.track_uri && (
229
<Link
230
-
to={`/${currentlyPlaying?.track_uri.split("at://")[1]}`}
231
>
232
<Cover
233
-
src={currentlyPlaying?.album_art}
234
key={currentlyPlaying?.id}
235
/>
236
</Link>
237
)}
238
-
{currentlyPlaying?.track_uri && (
239
<Link
240
-
to={`/${currentlyPlaying?.track_uri.split("at://")[1]}`}
241
>
242
<TrackTitle>{currentlyPlaying?.title}</TrackTitle>
243
</Link>
244
)}
245
-
{!currentlyPlaying?.track_uri && (
246
<Cover
247
-
src={currentlyPlaying?.album_art}
248
key={currentlyPlaying?.id}
249
/>
250
)}
···
261
</div>
262
</div>
263
264
-
{!currentlyPlaying?.track_uri && (
265
<TrackTitle>{currentlyPlaying?.title}</TrackTitle>
266
)}
267
-
<Link to={`/${currentlyPlaying?.artist_uri?.split("at://")[1]}`}>
268
<TrackArtist>{currentlyPlaying?.artist}</TrackArtist>
269
</Link>
270
</ModalBody>
···
94
id: string;
95
title: string;
96
artist: string;
97
+
albumArt: string;
98
+
artistUri?: string;
99
uri: string;
100
avatar: string;
101
handle: string;
102
did: string;
103
+
createdAt: string;
104
+
trackId: string;
105
+
trackUri: string;
106
} | null>(null);
107
const [currentIndex, setCurrentIndex] = useState(0);
108
const [progress, setProgress] = useState(0);
···
211
</div>
212
</Link>
213
<span className="ml-[10px] text-[15px] text-[var(--color-text-muted)]">
214
+
{dayjs.utc(currentlyPlaying?.createdAt).local().fromNow()}
215
</span>
216
</div>
217
</ModalHeader>
···
225
)}
226
</div>
227
<div className="flex flex-col items-center flex-1">
228
+
{currentlyPlaying?.trackUri && (
229
<Link
230
+
to={`/${currentlyPlaying?.trackUri.split("at://")[1]}`}
231
>
232
<Cover
233
+
src={currentlyPlaying?.albumArt}
234
key={currentlyPlaying?.id}
235
/>
236
</Link>
237
)}
238
+
{currentlyPlaying?.trackUri && (
239
<Link
240
+
to={`/${currentlyPlaying?.trackUri.split("at://")[1]}`}
241
>
242
<TrackTitle>{currentlyPlaying?.title}</TrackTitle>
243
</Link>
244
)}
245
+
{!currentlyPlaying?.trackUri && (
246
<Cover
247
+
src={currentlyPlaying?.albumArt}
248
key={currentlyPlaying?.id}
249
/>
250
)}
···
261
</div>
262
</div>
263
264
+
{!currentlyPlaying?.trackUri && (
265
<TrackTitle>{currentlyPlaying?.title}</TrackTitle>
266
)}
267
+
<Link to={`/${currentlyPlaying?.artistUri?.split("at://")[1]}`}>
268
<TrackArtist>{currentlyPlaying?.artist}</TrackArtist>
269
</Link>
270
</ModalBody>
+8
turbo.json
···
14
"./dist/**"
15
]
16
},
0
0
0
0
0
0
0
0
17
"dev": {
18
"persistent": true,
19
"cache": false
···
14
"./dist/**"
15
]
16
},
17
+
"build:prod": {
18
+
"dependsOn": [
19
+
"^build:prod"
20
+
],
21
+
"outputs": [
22
+
"./dist/**"
23
+
]
24
+
},
25
"dev": {
26
"persistent": true,
27
"cache": false