+73
-46
src/App.tsx
+73
-46
src/App.tsx
···
8
8
Suspense,
9
9
} from "solid-js";
10
10
11
+
const LB_API_URL = `https://api.listenbrainz.org`;
12
+
const COVERARTARCHIVE_URL = "https://coverartarchive.org";
13
+
const MB_API_URL = "https://musicbrainz.org";
14
+
11
15
export type Settings = {
12
16
username: string;
13
17
range: Range;
18
+
count: number;
14
19
};
15
20
16
21
export default () => {
17
22
const [settings, setSettings] = createSignal<Settings>({
18
23
username: "karitham",
19
-
range: "all_time",
24
+
range: "this_month",
25
+
count: 10,
20
26
});
21
27
const [artistFetcher] = makeCache(
22
-
(set: Settings) => artists(set.username, undefined, set.range),
28
+
(set: Settings) => artists(set.username, undefined, set.range, set.count),
23
29
{
24
30
storage: localStorage,
25
31
sourceHash(source) {
···
29
35
);
30
36
const [artistRes] = createResource(settings, artistFetcher);
31
37
const [groupsFetcher] = makeCache(
32
-
(set: Settings) => releaseGroups(set.username, undefined, set.range),
38
+
(set: Settings) =>
39
+
releaseGroups(set.username, undefined, set.range, set.count),
33
40
{
34
41
storage: localStorage,
35
42
sourceHash(source) {
···
40
47
const [groupsRes] = createResource(settings, groupsFetcher);
41
48
42
49
return (
43
-
<div class="bg-black min-h-screen text-white p-8">
44
-
<h1 class="text-4xl font-bold mb-8 text-center">
45
-
Your ListenBrainz Stats
46
-
</h1>
47
-
<div class="flex flex-col sm:flex-row items-center justify-center gap-4 mb-8">
48
-
<input
49
-
type="text"
50
-
value={settings().username}
51
-
onChange={(e) => {
52
-
setSettings(() => ({
53
-
...settings(),
54
-
username: e.target.value,
55
-
}));
56
-
}}
57
-
placeholder="Enter username"
58
-
class="p-4 rounded-lg bg-gray-800 text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-green-500 w-full sm:w-auto"
59
-
/>
60
-
<select
61
-
value={settings().range}
62
-
onChange={(e) => {
63
-
setSettings(() => ({
64
-
...settings(),
65
-
range: e.target.value as Range,
66
-
}));
67
-
}}
68
-
class="p-4 rounded-lg bg-gray-800 text-white focus:outline-none focus:ring-2 focus:ring-green-500 w-full sm:w-auto"
50
+
<main class="bg-black min-h-screen text-white p-8">
51
+
<div class="m-auto max-w-4xl flex flex-col align-center">
52
+
<h1 class="text-4xl font-bold mb-8 text-center">
53
+
Your ListenBrainz Stats
54
+
</h1>
55
+
<div class="flex flex-col sm:flex-row items-center justify-center gap-4 mb-8">
56
+
<input
57
+
type="text"
58
+
value={settings().username}
59
+
onChange={(e) => {
60
+
setSettings(() => ({
61
+
...settings(),
62
+
username: e.target.value,
63
+
}));
64
+
}}
65
+
placeholder="Enter username"
66
+
class="p-4 rounded-lg bg-gray-800 text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-green-500 w-full sm:w-auto"
67
+
/>
68
+
<select
69
+
value={settings().range}
70
+
onChange={(e) => {
71
+
setSettings(() => ({
72
+
...settings(),
73
+
range: e.target.value as Range,
74
+
}));
75
+
}}
76
+
class="p-4 rounded-lg bg-gray-800 text-white focus:outline-none focus:ring-2 focus:ring-green-500 w-full sm:w-auto"
77
+
>
78
+
{ranges.map((r) => (
79
+
<option value={r}>{r}</option>
80
+
))}
81
+
</select>
82
+
</div>
83
+
<Suspense
84
+
fallback={<p class="text-center text-gray-400">Loading...</p>}
69
85
>
70
-
{ranges.map((r) => (
71
-
<option value={r}>{r}</option>
72
-
))}
73
-
</select>
86
+
<div>
87
+
<div>
88
+
<h2 class="text-2xl font-semibold my-6 text-center">
89
+
Top Artists
90
+
</h2>
91
+
<Artists artists={artistRes()?.artists || []} />
92
+
</div>
93
+
<div>
94
+
<h2 class="text-2xl font-semibold my-6 text-center">
95
+
Top Albums
96
+
</h2>
97
+
<ReleaseGroups groups={groupsRes()?.release_groups || []} />
98
+
</div>
99
+
</div>
100
+
</Suspense>
74
101
</div>
75
-
<Suspense fallback={<p class="text-center text-gray-400">Loading...</p>}>
76
-
<h2 class="text-2xl font-semibold my-6 text-center">Top Artists</h2>
77
-
<Artists artists={artistRes()?.artists || []} />
78
-
<h2 class="text-2xl font-semibold my-6 text-center">Top Albums</h2>
79
-
<ReleaseGroups groups={groupsRes()?.release_groups || []} />
80
-
</Suspense>
81
-
</div>
102
+
</main>
82
103
);
83
104
};
84
105
···
127
148
/>
128
149
</Show>
129
150
<p class="text-white text-center font-bold text-base truncate mb-1">
130
-
{props.artist.artist_name}
151
+
<a
152
+
target="_blank"
153
+
href={`${MB_API_URL}/artist/${props.artist.artist_mbid}`}
154
+
>
155
+
{props.artist.artist_name}
156
+
</a>
131
157
</p>
132
158
<p class="text-gray-500 text-base truncate mb-1">
133
159
{props.artist.listen_count} listens
···
182
208
/>
183
209
</Show>
184
210
<p class="text-white text-center font-bold text-base truncate mb-1">
185
-
{props.group.release_group_name}
211
+
<a
212
+
target="_blank"
213
+
href={`${MB_API_URL}/release/${props.group.caa_release_mbid}`}
214
+
>
215
+
{props.group.release_group_name}
216
+
</a>
186
217
</p>
187
218
<p class="text-gray-500 text-base truncate mb-1">
188
219
{props.group.listen_count} listens
···
190
221
</div>
191
222
);
192
223
};
193
-
194
-
const LB_API_URL = `https://api.listenbrainz.org`;
195
-
const COVERARTARCHIVE_URL = "https://coverartarchive.org";
196
-
const MB_API_URL = "https://musicbrainz.org";
197
224
198
225
const ranges = ["this_week", "this_month", "this_year", "all_time"] as const;
199
226
export type Range = (typeof ranges)[number];