A decentralized music tracking and discovery platform built on AT Protocol 🎵

Render generic avatar for @jpeg placeholders

+101 -36
+13 -6
apps/web/src/components/Handle/Handle.tsx
··· 14 14 import Stats from "../Stats"; 15 15 import NowPlaying from "./NowPlaying"; 16 16 import { followsAtom } from "../../atoms/follows"; 17 - import { IconCheck, IconPlus } from "@tabler/icons-react"; 17 + import { IconCheck, IconPlus, IconUser } from "@tabler/icons-react"; 18 18 import { Button } from "baseui/button"; 19 19 import SignInModal from "../SignInModal"; 20 20 import { ··· 176 176 <div className="flex flex-row items-start justify-between"> 177 177 <div className="flex flex-row items-center"> 178 178 <Link to={link} className="no-underline"> 179 - <Avatar 180 - src={profiles[did]?.avatar} 181 - name={profiles[did]?.displayName} 182 - size={"60px"} 183 - /> 179 + {!profiles[did]?.avatar.endsWith("/@jpeg") && ( 180 + <Avatar 181 + src={profiles[did]?.avatar} 182 + name={profiles[did]?.displayName} 183 + size={"60px"} 184 + /> 185 + )} 186 + {profiles[did]?.avatar.endsWith("/@jpeg") && ( 187 + <div className="w-[60px] h-[60px] rounded-full bg-[var(--color-avatar-background)] flex items-center justify-center"> 188 + <IconUser size={30} color="#fff" /> 189 + </div> 190 + )} 184 191 </Link> 185 192 <div className="ml-[16px]"> 186 193 <Link to={link} className="no-underline">
+2
apps/web/src/index.css
··· 92 92 --color-skeleton-background: #f3f3f3; 93 93 --color-skeleton-foreground: #ecebeb; 94 94 --color-genre: #3f03fb; 95 + --color-avatar-background: #8d2dffed; 95 96 } 96 97 97 98 .dark { ··· 115 116 --color-skeleton-background: #221538; 116 117 --color-skeleton-foreground: #2b1a4c; 117 118 --color-genre: #00f1f3; 119 + --color-avatar-background: #8d2dffed; 118 120 } 119 121 120 122 ::placeholder {
+25 -10
apps/web/src/layouts/Navbar/Navbar.tsx
··· 21 21 import { useProfileStatsByDidQuery } from "../../hooks/useProfile"; 22 22 import LogoDark from "../../assets/rocksky-logo-dark.png"; 23 23 import LogoLight from "../../assets/rocksky-logo-light.png"; 24 + import { IconUser } from "@tabler/icons-react"; 24 25 25 26 const Container = styled.div` 26 27 position: fixed; ··· 174 175 <div className="flex flex-col items-center"> 175 176 <div className="mb-[5px]"> 176 177 <Link to="/profile/$did" params={{ did: profile.handle }}> 177 - <Avatar 178 - src={profile.avatar} 179 - name={profile.displayName} 180 - size="80px" 181 - /> 178 + {!profile?.avatar?.endsWith("/@jpeg") && ( 179 + <Avatar 180 + src={profile.avatar} 181 + name={profile.displayName} 182 + size="80px" 183 + /> 184 + )} 185 + {profile?.avatar?.endsWith("/@jpeg") && ( 186 + <div className="w-[80px] h-[80px] rounded-full bg-[var(--color-avatar-background)] flex items-center justify-center"> 187 + <IconUser size={40} color="#fff" /> 188 + </div> 189 + )} 182 190 </Link> 183 191 </div> 184 192 ··· 339 347 )} 340 348 > 341 349 <button className="ml-[15px] border-none bg-transparent cursor-pointer"> 342 - <Avatar 343 - src={profile.avatar} 344 - name={profile.displayName} 345 - size="scale1200" 346 - /> 350 + {!profile?.avatar?.endsWith("/@jpeg") && ( 351 + <Avatar 352 + src={profile.avatar} 353 + name={profile.displayName} 354 + size="scale1200" 355 + /> 356 + )} 357 + {profile?.avatar?.endsWith("/@jpeg") && ( 358 + <div className="w-[48px] h-[48px] rounded-full bg-[var(--color-avatar-background)] flex items-center justify-center"> 359 + <IconUser size={24} color="#fff" /> 360 + </div> 361 + )} 347 362 </button> 348 363 </StatefulPopover> 349 364 )}
+14 -6
apps/web/src/layouts/Search/Search.tsx
··· 13 13 import Disc from "../../components/Icons/Disc"; 14 14 import Track from "../../components/Icons/Track"; 15 15 import { useSearchMutation } from "../../hooks/useSearch"; 16 + import { IconUser } from "@tabler/icons-react"; 16 17 17 18 const Link = styled(DefaultLink)` 18 19 color: initial; ··· 84 85 {item._federation.indexUid === "users" && ( 85 86 <Link to={`/profile/${item.handle}`} key={item.id}> 86 87 <div className="flex flex-row mb-[10px]"> 87 - <img 88 - key={item.did} 89 - src={item.avatar} 90 - alt={item.displayName} 91 - className="w-[50px] h-[50px] mr-[12px] rounded-full" 92 - /> 88 + {!item.avatar?.endsWith("/@jpeg") && ( 89 + <img 90 + key={item.did} 91 + src={item.avatar} 92 + alt={item.displayName} 93 + className="w-[50px] h-[50px] mr-[12px] rounded-full" 94 + /> 95 + )} 96 + {item.avatar?.endsWith("/@jpeg") && ( 97 + <div className="w-[50px] h-[50px] rounded-full bg-[var(--color-avatar-background)] flex items-center justify-center mr-[12px]"> 98 + <IconUser size={25} color="#fff" /> 99 + </div> 100 + )} 93 101 <div> 94 102 <div className="overflow-hidden"> 95 103 <div className="overflow-hidden text-ellipsis whitespace-nowrap text-[var(--color-text)]">
+13 -5
apps/web/src/pages/home/feed/Feed.tsx
··· 22 22 import FeedGenerators from "./FeedGenerators"; 23 23 import { consola } from "consola"; 24 24 import { Link } from "@tanstack/react-router"; 25 + import { IconUser } from "@tabler/icons-react"; 25 26 26 27 dayjs.extend(relativeTime); 27 28 ··· 219 220 220 221 <div className="flex"> 221 222 <div className="mr-[8px]"> 222 - <Avatar 223 - src={song.userAvatar} 224 - name={song.userDisplayName} 225 - size={"20px"} 226 - /> 223 + {!song.userAvatar.endsWith("/@jpeg") && ( 224 + <Avatar 225 + src={song.userAvatar} 226 + name={song.userDisplayName} 227 + size={"20px"} 228 + /> 229 + )} 230 + {song.userAvatar.endsWith("/@jpeg") && ( 231 + <div className="w-[20px] h-[20px] rounded-full bg-[var(--color-avatar-background)] flex items-center justify-center"> 232 + <IconUser size={10} color="#fff" /> 233 + </div> 234 + )} 227 235 </div> 228 236 <Handle 229 237 link={`/profile/${song.user}`}
+21 -3
apps/web/src/pages/home/stories/Stories.tsx
··· 8 8 import relativeTime from "dayjs/plugin/relativeTime"; 9 9 import utc from "dayjs/plugin/utc"; 10 10 import { useEffect, useState, useRef } from "react"; 11 - import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react"; 11 + import { 12 + IconChevronLeft, 13 + IconChevronRight, 14 + IconUser, 15 + } from "@tabler/icons-react"; 12 16 import { useStoriesQuery } from "../../../hooks/useStories"; 13 17 import styles, { getModalStyles } from "./styles"; 14 18 import _ from "lodash"; ··· 340 344 setIsOpen(true); 341 345 }} 342 346 > 343 - <Story src={item.avatar} /> 347 + {!item.avatar?.endsWith("/@jpeg") && ( 348 + <Story src={item.avatar} /> 349 + )} 350 + {item.avatar?.endsWith("/@jpeg") && ( 351 + <div className="w-[64px] h-[64px] rounded-full bg-[var(--color-avatar-background)] flex items-center justify-center mr-[12px]"> 352 + <IconUser size={24} color="#fff" /> 353 + </div> 354 + )} 344 355 <StatefulTooltip 345 356 content={item.handle} 346 357 returnFocus ··· 384 395 </div> 385 396 <div className="flex flex-row items-center"> 386 397 <Link to={`/profile/${currentlyPlaying?.handle}`}> 387 - <Avatar src={currentlyPlaying?.avatar} /> 398 + {!currentlyPlaying?.avatar?.endsWith("/@jpeg") && ( 399 + <Avatar src={currentlyPlaying?.avatar} /> 400 + )} 401 + {currentlyPlaying?.avatar?.endsWith("/@jpeg") && ( 402 + <div className="w-[48px] h-[48px] rounded-full bg-[var(--color-avatar-background)] flex items-center justify-center mr-[12px]"> 403 + <IconUser size={24} color="#fff" /> 404 + </div> 405 + )} 388 406 </Link> 389 407 <Link to={`/profile/${currentlyPlaying?.handle}`}> 390 408 <div className="text-[#fff] no-underline text-[15px]">
+13 -6
apps/web/src/pages/profile/Profile.tsx
··· 18 18 import Overview from "./overview"; 19 19 import Playlists from "./playlists"; 20 20 import { Button } from "baseui/button"; 21 - import { IconPlus, IconCheck } from "@tabler/icons-react"; 21 + import { IconPlus, IconCheck, IconUser } from "@tabler/icons-react"; 22 22 import { followsAtom } from "../../atoms/follows"; 23 23 import SignInModal from "../../components/SignInModal"; 24 24 import { ··· 231 231 <Group> 232 232 <ProfileInfo> 233 233 <div className="mr-[20px]"> 234 - <Avatar 235 - name={profiles[did]?.displayName} 236 - src={profiles[did]?.avatar} 237 - size="150px" 238 - /> 234 + {!profiles[did]?.avatar?.endsWith("/@jpeg") && ( 235 + <Avatar 236 + name={profiles[did]?.displayName} 237 + src={profiles[did]?.avatar} 238 + size="150px" 239 + /> 240 + )} 241 + {profiles[did]?.avatar?.endsWith("/@jpeg") && ( 242 + <div className="w-[150px] h-[150px] rounded-full bg-[var(--color-avatar-background)] flex items-center justify-center"> 243 + <IconUser size={80} color="#fff" /> 244 + </div> 245 + )} 239 246 </div> 240 247 <div 241 248 style={{ marginTop: profiles[did]?.displayName ? 10 : 30 }}