feat(frontend): replace placeholder avatar with identicons

i don't actually know what the license on the kitten image is, and while
it's very unlikely, i don't want to get into legal trouble. it was only
ever supposed to be a temporary image, too.

identicons aren't the prettiest but at least they have a clear license
:3
This commit is contained in:
sam 2024-12-03 15:19:52 +01:00
parent 71d3b42330
commit 74222ead45
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
10 changed files with 37 additions and 6 deletions

View file

@ -42,6 +42,7 @@
"bootstrap-icons": "^1.11.3", "bootstrap-icons": "^1.11.3",
"luxon": "^3.5.0", "luxon": "^3.5.0",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"minidenticons": "^4.2.1",
"pretty-bytes": "^6.1.1", "pretty-bytes": "^6.1.1",
"sanitize-html": "^2.13.1", "sanitize-html": "^2.13.1",
"svelte-tippy": "^1.3.2", "svelte-tippy": "^1.3.2",

View file

@ -23,6 +23,9 @@ importers:
markdown-it: markdown-it:
specifier: ^14.1.0 specifier: ^14.1.0
version: 14.1.0 version: 14.1.0
minidenticons:
specifier: ^4.2.1
version: 4.2.1
pretty-bytes: pretty-bytes:
specifier: ^6.1.1 specifier: ^6.1.1
version: 6.1.1 version: 6.1.1
@ -1110,6 +1113,10 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'} engines: {node: '>=8.6'}
minidenticons@4.2.1:
resolution: {integrity: sha512-oWfFivA0lOx/V/bO/YIJbthB26lV8JXYvhnv9zM2hNd3fzsHTXQ6c6bWZPcvhD3nnOB+lQk/D9lF43BXixrN8g==}
engines: {node: '>=15.14.0'}
minimatch@3.1.2: minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@ -2369,6 +2376,8 @@ snapshots:
braces: 3.0.3 braces: 3.0.3
picomatch: 2.3.1 picomatch: 2.3.1
minidenticons@4.2.1: {}
minimatch@3.1.2: minimatch@3.1.2:
dependencies: dependencies:
brace-expansion: 1.1.11 brace-expansion: 1.1.11

View file

@ -1,16 +1,22 @@
<script lang="ts"> <script lang="ts">
import { DEFAULT_AVATAR } from "$lib"; import { minidenticon } from "minidenticons";
type Props = { url: string | null; alt: string; lazyLoad?: boolean; size?: number }; type Props = { url: string | null; alt: string; lazyLoad?: boolean; size?: number; name: string };
let { url, alt, lazyLoad, size }: Props = $props(); let { url, alt, lazyLoad, size, name }: Props = $props();
let width = $derived(size || 200); let width = $derived(size || 200);
let identicon: string | null = $derived.by(() => {
if (url) return null;
return "data:image/svg+xml;utf8," + encodeURIComponent(minidenticon(name, 50, 80));
});
</script> </script>
<img <img
class="rounded-circle img-fluid" class="rounded-circle img-fluid"
style="height: {width}px; width: {width}px" style="height: {width}px; width: {width}px"
src={url || DEFAULT_AVATAR} class:identicon={!url}
src={url || identicon}
{alt} {alt}
{width} {width}
loading={lazyLoad ? "lazy" : "eager"} loading={lazyLoad ? "lazy" : "eager"}
@ -20,4 +26,12 @@
img { img {
object-fit: cover; object-fit: cover;
} }
.identicon {
@media (prefers-color-scheme: dark) {
background-color: var(--bs-secondary-border-subtle);
}
background-color: var(--bs-light-border-subtle);
}
</style> </style>

View file

@ -7,12 +7,13 @@
import ShortNoscriptWarning from "./ShortNoscriptWarning.svelte"; import ShortNoscriptWarning from "./ShortNoscriptWarning.svelte";
type Props = { type Props = {
name: string;
current: string | null; current: string | null;
alt: string; alt: string;
update: (avatar: string) => Promise<void>; update: (avatar: string) => Promise<void>;
updated: boolean; updated: boolean;
}; };
let { current, alt, update: onclick, updated }: Props = $props(); let { name, current, alt, update: onclick, updated }: Props = $props();
const MAX_AVATAR_BYTES = 1_000_000; const MAX_AVATAR_BYTES = 1_000_000;
@ -40,7 +41,7 @@
</script> </script>
<p class="text-center"> <p class="text-center">
<Avatar url={avatarExists ? avatar : current} {alt} /> <Avatar {name} url={avatarExists ? avatar : current} {alt} />
</p> </p>
<InputGroup class="mb-2"> <InputGroup class="mb-2">

View file

@ -22,6 +22,7 @@
<div class="row"> <div class="row">
<div class="col-md-4 text-center"> <div class="col-md-4 text-center">
<Avatar <Avatar
name={"name" in profile ? profile.name : profile.username}
url={profile.avatar_url} url={profile.avatar_url}
alt={$t("avatar-tooltip", { name })} alt={$t("avatar-tooltip", { name })}
lazyLoad={lazyLoadAvatar} lazyLoad={lazyLoadAvatar}

View file

@ -36,6 +36,7 @@
<div> <div>
<a href="/@{username}/{member.name}"> <a href="/@{username}/{member.name}">
<Avatar <Avatar
name={member.name}
url={member.avatar_url} url={member.avatar_url}
lazyLoad lazyLoad
alt={$t("avatar-tooltip", { name: member.display_name })} alt={$t("avatar-tooltip", { name: member.display_name })}

View file

@ -55,6 +55,7 @@
<div class="col-md-3 text-center"> <div class="col-md-3 text-center">
<h5>{$t("settings.avatar")}</h5> <h5>{$t("settings.avatar")}</h5>
<Avatar <Avatar
name={data.user.username}
url={data.user.avatar_url} url={data.user.avatar_url}
alt={$t("avatar-tooltip", { name: "@" + data.user.username })} alt={$t("avatar-tooltip", { name: "@" + data.user.username })}
/> />

View file

@ -30,6 +30,7 @@
{#each data.members as member (member.id)} {#each data.members as member (member.id)}
<ListGroupItem tag="a" href="/settings/members/{member.id}" data-sveltekit-preload-data="tap"> <ListGroupItem tag="a" href="/settings/members/{member.id}" data-sveltekit-preload-data="tap">
<Avatar <Avatar
name={member.name}
url={member.avatar_url} url={member.avatar_url}
alt={$t("avatar-tooltip", { name: member.display_name })} alt={$t("avatar-tooltip", { name: member.display_name })}
size={20} size={20}

View file

@ -74,6 +74,7 @@
<div class="col-md"> <div class="col-md">
<h4>{$t("settings.avatar")}</h4> <h4>{$t("settings.avatar")}</h4>
<AvatarEditor <AvatarEditor
name={data.member.name}
current={data.member.avatar_url} current={data.member.avatar_url}
alt={$t("avatar-tooltip", { name: data.member.name })} alt={$t("avatar-tooltip", { name: data.member.name })}
update={updateAvatar} update={updateAvatar}

View file

@ -91,6 +91,7 @@
<div class="col-md"> <div class="col-md">
<h4>{$t("settings.avatar")}</h4> <h4>{$t("settings.avatar")}</h4>
<AvatarEditor <AvatarEditor
name={data.user.username}
current={data.user.avatar_url} current={data.user.avatar_url}
alt={$t("avatar-tooltip", { name: "@" + data.user.username })} alt={$t("avatar-tooltip", { name: "@" + data.user.username })}
update={updateAvatar} update={updateAvatar}