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
Add profile skeleton loader
tsiry-sandratraina.com
1 month ago
95b8bfe5
d7b01009
+118
-91
1 changed file
expand all
collapse all
unified
split
apps
web
src
pages
profile
Profile.tsx
+118
-91
apps/web/src/pages/profile/Profile.tsx
···
34
34
import { useArtistsQuery } from "../../hooks/useLibrary";
35
35
import { getLastDays } from "../../lib/date";
36
36
import { Link } from "@tanstack/react-router";
37
37
+
import ContentLoader from "react-content-loader";
37
38
38
39
const Group = styled.div`
39
40
display: flex;
···
204
205
<Main>
205
206
<div className="pb-[100px] pt-[75px]">
206
207
<div className="mb-[50px]">
207
207
-
<Group>
208
208
-
<ProfileInfo>
209
209
-
<div className="mr-[20px]">
210
210
-
<Avatar
211
211
-
name={profiles[did]?.displayName}
212
212
-
src={profiles[did]?.avatar}
213
213
-
size="150px"
214
214
-
/>
215
215
-
</div>
216
216
-
<div style={{ marginTop: profiles[did]?.displayName ? 10 : 30 }}>
217
217
-
<HeadingMedium
218
218
-
marginTop="0px"
219
219
-
marginBottom={0}
220
220
-
className="!text-[var(--color-text)]"
208
208
+
{profile.isLoading && (
209
209
+
<ContentLoader
210
210
+
width="100%"
211
211
+
height={200}
212
212
+
viewBox="0 0 800 200"
213
213
+
backgroundColor="var(--color-skeleton-background)"
214
214
+
foregroundColor="var(--color-skeleton-foreground)"
215
215
+
>
216
216
+
{/* Avatar circle */}
217
217
+
<circle cx="75" cy="75" r="75" />
218
218
+
{/* Display name */}
219
219
+
<rect x="180" y="30" rx="4" ry="4" width="250" height="24" />
220
220
+
{/* Handle */}
221
221
+
<rect x="180" y="70" rx="3" ry="3" width="180" height="16" />
222
222
+
{/* Scrobbling since text */}
223
223
+
<rect x="370" y="70" rx="3" ry="3" width="200" height="16" />
224
224
+
{/* View on PDSls button */}
225
225
+
<rect x="180" y="120" rx="8" ry="8" width="180" height="48" />
226
226
+
{/* Follow button */}
227
227
+
<rect x="680" y="30" rx="20" ry="20" width="120" height="40" />
228
228
+
</ContentLoader>
229
229
+
)}
230
230
+
{!profile.isLoading && (
231
231
+
<Group>
232
232
+
<ProfileInfo>
233
233
+
<div className="mr-[20px]">
234
234
+
<Avatar
235
235
+
name={profiles[did]?.displayName}
236
236
+
src={profiles[did]?.avatar}
237
237
+
size="150px"
238
238
+
/>
239
239
+
</div>
240
240
+
<div
241
241
+
style={{ marginTop: profiles[did]?.displayName ? 10 : 30 }}
221
242
>
222
222
-
{profiles[did]?.displayName}
223
223
-
</HeadingMedium>
224
224
-
<LabelLarge>
225
225
-
<a
226
226
-
href={`https://bsky.app/profile/${profiles[did]?.handle}`}
227
227
-
className="no-underline text-[var(--color-primary)]"
228
228
-
target="_blank"
229
229
-
>
230
230
-
@{profiles[did]?.handle}
231
231
-
</a>
232
232
-
<span className="text-[var(--color-text-muted)] text-[15px]">
233
233
-
{" "}
234
234
-
• scrobbling since{" "}
235
235
-
{dayjs(profiles[did]?.createdAt).format("DD MMM YYYY")}
236
236
-
</span>
237
237
-
</LabelLarge>
238
238
-
<div className="flex-1 mt-[30px] mr-[10px]">
239
239
-
<a
240
240
-
href={`https://pdsls.dev/at/${profiles[did]?.did}`}
241
241
-
target="_blank"
242
242
-
className="no-underline text-[var(--color-text)] bg-[var(--color-default-button)] p-[16px] rounded-[10px] pl-[25px] pr-[25px]"
243
243
+
<HeadingMedium
244
244
+
marginTop="0px"
245
245
+
marginBottom={0}
246
246
+
className="!text-[var(--color-text)]"
243
247
>
244
244
-
<ExternalLink size={24} style={{ marginRight: 10 }} />
245
245
-
View on PDSls
246
246
-
</a>
248
248
+
{profiles[did]?.displayName}
249
249
+
</HeadingMedium>
250
250
+
<LabelLarge>
251
251
+
<a
252
252
+
href={`https://bsky.app/profile/${profiles[did]?.handle}`}
253
253
+
className="no-underline text-[var(--color-primary)]"
254
254
+
target="_blank"
255
255
+
>
256
256
+
@{profiles[did]?.handle}
257
257
+
</a>
258
258
+
<span className="text-[var(--color-text-muted)] text-[15px]">
259
259
+
{" "}
260
260
+
• scrobbling since{" "}
261
261
+
{dayjs(profiles[did]?.createdAt).format("DD MMM YYYY")}
262
262
+
</span>
263
263
+
</LabelLarge>
264
264
+
<div className="flex-1 mt-[30px] mr-[10px]">
265
265
+
<a
266
266
+
href={`https://pdsls.dev/at/${profiles[did]?.did}`}
267
267
+
target="_blank"
268
268
+
className="no-underline text-[var(--color-text)] bg-[var(--color-default-button)] p-[16px] rounded-[10px] pl-[25px] pr-[25px]"
269
269
+
>
270
270
+
<ExternalLink size={24} style={{ marginRight: 10 }} />
271
271
+
View on PDSls
272
272
+
</a>
273
273
+
</div>
247
274
</div>
248
248
-
</div>
249
249
-
</ProfileInfo>
250
250
-
{(profile.data?.did !== localStorage.getItem("did") ||
251
251
-
!localStorage.getItem("did")) && (
252
252
-
<>
253
253
-
{!follows.has(profile.data?.did || "") && !isLoading && (
254
254
-
<Button
255
255
-
shape="pill"
256
256
-
size="compact"
257
257
-
startEnhancer={<IconPlus size={18} />}
258
258
-
onClick={onFollow}
259
259
-
overrides={{
260
260
-
BaseButton: {
261
261
-
style: {
262
262
-
marginTop: "12px",
263
263
-
minWidth: "120px",
264
264
-
backgroundColor: "#ff2876",
265
265
-
":hover": {
266
266
-
backgroundColor: "#ff2876",
267
267
-
},
268
268
-
":focus": {
275
275
+
</ProfileInfo>
276
276
+
{(profile.data?.did !== localStorage.getItem("did") ||
277
277
+
!localStorage.getItem("did")) && (
278
278
+
<>
279
279
+
{!follows.has(profile.data?.did || "") && !isLoading && (
280
280
+
<Button
281
281
+
shape="pill"
282
282
+
size="compact"
283
283
+
startEnhancer={<IconPlus size={18} />}
284
284
+
onClick={onFollow}
285
285
+
overrides={{
286
286
+
BaseButton: {
287
287
+
style: {
288
288
+
marginTop: "12px",
289
289
+
minWidth: "120px",
269
290
backgroundColor: "#ff2876",
291
291
+
":hover": {
292
292
+
backgroundColor: "#ff2876",
293
293
+
},
294
294
+
":focus": {
295
295
+
backgroundColor: "#ff2876",
296
296
+
},
270
297
},
271
298
},
272
272
-
},
273
273
-
}}
274
274
-
>
275
275
-
Follow
276
276
-
</Button>
277
277
-
)}
278
278
-
{follows.has(profile.data?.did || "") && !isLoading && (
279
279
-
<Button
280
280
-
shape="pill"
281
281
-
size="compact"
282
282
-
startEnhancer={<IconCheck size={18} />}
283
283
-
onClick={onUnfollow}
284
284
-
overrides={{
285
285
-
BaseButton: {
286
286
-
style: {
287
287
-
marginTop: "12px",
288
288
-
minWidth: "120px",
289
289
-
backgroundColor: "var(--color-default-button)",
290
290
-
color: "var(--color-text)",
291
291
-
":hover": {
292
292
-
backgroundColor: "var(--color-default-button)",
293
293
-
},
294
294
-
":focus": {
299
299
+
}}
300
300
+
>
301
301
+
Follow
302
302
+
</Button>
303
303
+
)}
304
304
+
{follows.has(profile.data?.did || "") && !isLoading && (
305
305
+
<Button
306
306
+
shape="pill"
307
307
+
size="compact"
308
308
+
startEnhancer={<IconCheck size={18} />}
309
309
+
onClick={onUnfollow}
310
310
+
overrides={{
311
311
+
BaseButton: {
312
312
+
style: {
313
313
+
marginTop: "12px",
314
314
+
minWidth: "120px",
295
315
backgroundColor: "var(--color-default-button)",
316
316
+
color: "var(--color-text)",
317
317
+
":hover": {
318
318
+
backgroundColor: "var(--color-default-button)",
319
319
+
},
320
320
+
":focus": {
321
321
+
backgroundColor: "var(--color-default-button)",
322
322
+
},
296
323
},
297
324
},
298
298
-
},
299
299
-
}}
300
300
-
>
301
301
-
Following
302
302
-
</Button>
303
303
-
)}
304
304
-
</>
305
305
-
)}
306
306
-
</Group>
325
325
+
}}
326
326
+
>
327
327
+
Following
328
328
+
</Button>
329
329
+
)}
330
330
+
</>
331
331
+
)}
332
332
+
</Group>
333
333
+
)}
307
334
{tags.length > 0 && (
308
335
<div className="mt-[30px] mb-[35px] flex flex-wrap">
309
336
{tags.map((genre) => (