From 3cef92e82ffb2967ea283bf2ededaa50170426de Mon Sep 17 00:00:00 2001
From: Kalle <38327916+Sendouc@users.noreply.github.com>
Date: Thu, 9 Jul 2026 21:13:19 +0300
Subject: [PATCH] Show identicons for breadcrumbs to match with user profile
avatar
---
app/components/Avatar.tsx | 2 +-
app/components/layout/index.tsx | 20 +++++++++++++++++--
.../user-page/routes/u.$identifier.tsx | 1 +
app/utils/remix.server.ts | 2 ++
4 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/app/components/Avatar.tsx b/app/components/Avatar.tsx
index dec646b98..005469895 100644
--- a/app/components/Avatar.tsx
+++ b/app/components/Avatar.tsx
@@ -42,7 +42,7 @@ function generateColors(hash: number) {
};
}
-function generateIdenticon(input: string, size = 128, gridSize = 5) {
+export function generateIdenticon(input: string, size = 128, gridSize = 5) {
const cacheKey = `${input}-${size}-${gridSize}`;
const cached = identiconCache.get(cacheKey);
if (cached) return cached;
diff --git a/app/components/layout/index.tsx b/app/components/layout/index.tsx
index e1c634261..b8dee259b 100644
--- a/app/components/layout/index.tsx
+++ b/app/components/layout/index.tsx
@@ -40,7 +40,7 @@ import {
SETTINGS_PAGE,
userPage,
} from "~/utils/urls";
-import { Avatar } from "../Avatar";
+import { Avatar, generateIdenticon } from "../Avatar";
import { SendouButton } from "../elements/Button";
import { SendouPopover } from "../elements/Popover";
import { FuseZone } from "../fuse/Fuse";
@@ -601,6 +601,9 @@ function SideNavCollapseButton({
}
function PageIcon({ crumb }: { crumb: Breadcrumb }) {
+ const [isErrored, setIsErrored] = React.useState(false);
+ const isClient = useHydrated();
+
if (crumb.type !== "IMAGE") {
return null;
}
@@ -609,15 +612,28 @@ function PageIcon({ crumb }: { crumb: Breadcrumb }) {
const isExternal = lastPathSegment.includes(".");
const iconClass = clsx(styles.pageIcon, "rounded");
+ // an can finish loading (and fail) before React hydrates and attaches onError, so that
+ // error is missed — re-check on mount and fall back manually so SSR'd icons still heal
+ const checkAlreadyErrored = (img: HTMLImageElement | null) => {
+ if (img?.complete && img.naturalWidth === 0) setIsErrored(true);
+ };
+
+ const identiconSrc =
+ isErrored && isClient && crumb.identiconInput
+ ? generateIdenticon(crumb.identiconInput, 28, 7)
+ : null;
+
return (