feat: scroll to post bubble from recommended card + back-to-top button
Some checks failed
Deploy to Frontend Servers / deploy (push) Failing after 14s
Some checks failed
Deploy to Frontend Servers / deploy (push) Failing after 14s
Recommended cards already routed to /browse#post-<id>, but the stream had no logic to scroll to the target bubble — and the post might not be paged in yet. MessageStream now resolves the #post-<id> hash, auto-loads more pages until the bubble renders, scrolls to it, and gives it a brief gold highlight. Bubbles get scroll-mt so they clear the sticky header. Also adds a global floating back-to-top button (BackToTop) mounted in PublicLayout, shown after scrolling past 400px. Bundles related staging UI work already present in the working tree. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Download, LoaderCircle } from "lucide-react";
|
||||
import { m } from "framer-motion";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { Resource } from "../api";
|
||||
import { assetUrl } from "../api";
|
||||
@@ -11,13 +12,20 @@ import {
|
||||
downloadAttachment,
|
||||
downloadFile,
|
||||
} from "./messageStream/utils/downloadFile";
|
||||
import { useToast } from "./Toast";
|
||||
|
||||
function isPlaceholderAsset(path: string | undefined | null) {
|
||||
return !path || path.includes("placeholder-cover");
|
||||
}
|
||||
|
||||
const CARD_BASE_CLASS =
|
||||
"group flex shrink-0 flex-col overflow-hidden rounded-xl border bg-[#1D1E23] transition hover:border-ark-gold/55";
|
||||
"group flex shrink-0 flex-col overflow-hidden rounded-xl border bg-[#1D1E23] transition hover:border-ark-gold/55 hover:shadow-lg hover:shadow-black/30";
|
||||
|
||||
const CARD_HOVER_SPRING = {
|
||||
type: "spring",
|
||||
stiffness: 380,
|
||||
damping: 26,
|
||||
} as const;
|
||||
|
||||
const CARD_CAROUSEL_SIZE_CLASS =
|
||||
"w-[208px] md:w-[240px] lg:w-[246.4px] min-[1100px]:max-xl:w-[273px] xl:w-[246.4px]";
|
||||
@@ -41,6 +49,7 @@ export function RecommendedCard({
|
||||
layout?: "carousel" | "grid";
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const { showToast } = useToast();
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const figmaCover =
|
||||
officialRecommendationCoverFallbacks[
|
||||
@@ -73,18 +82,21 @@ export function RecommendedCard({
|
||||
r.downloadAttachmentId,
|
||||
displayTitle,
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
await downloadFile(dl, displayTitle);
|
||||
}
|
||||
await downloadFile(dl, displayTitle);
|
||||
showToast(t("downloadOk"));
|
||||
} catch {
|
||||
/* ignore */
|
||||
showToast(t("downloadFail"), "error");
|
||||
} finally {
|
||||
setIsDownloading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<article
|
||||
<m.article
|
||||
whileHover={{ y: -4 }}
|
||||
transition={CARD_HOVER_SPRING}
|
||||
className={`${CARD_BASE_CLASS} ${
|
||||
layout === "grid" ? CARD_GRID_SIZE_CLASS : CARD_CAROUSEL_SIZE_CLASS
|
||||
} ${
|
||||
@@ -101,12 +113,9 @@ export function RecommendedCard({
|
||||
<img
|
||||
src={cover}
|
||||
alt=""
|
||||
className={`h-full w-full object-cover transition duration-300 ${
|
||||
useFigmaDesign
|
||||
? "group-hover:scale-[1.02]"
|
||||
: "group-hover:scale-[1.02]"
|
||||
}`}
|
||||
className="ark-img-fade h-full w-full object-cover transition duration-300 group-hover:scale-[1.02]"
|
||||
loading="lazy"
|
||||
onLoad={(e) => e.currentTarget.classList.add("is-loaded")}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-full w-full bg-gradient-to-br from-neutral-900 to-neutral-950" />
|
||||
@@ -164,8 +173,8 @@ export function RecommendedCard({
|
||||
type="button"
|
||||
className={
|
||||
useFigmaDesign
|
||||
? "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[#191921] text-ark-gold outline-none transition hover:bg-[#22232D] focus-visible:ring-2 focus-visible:ring-ark-gold/80 disabled:cursor-wait"
|
||||
: "shrink-0 rounded-lg p-1 text-ark-gold outline-none hover:bg-ark-gold/10 focus-visible:ring-2 focus-visible:ring-ark-gold/80 disabled:cursor-wait"
|
||||
? "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[#191921] text-ark-gold outline-none transition hover:bg-[#22232D] active:scale-95 focus-visible:ring-2 focus-visible:ring-ark-gold/80 disabled:cursor-wait"
|
||||
: "shrink-0 rounded-lg p-1 text-ark-gold outline-none transition hover:bg-ark-gold/10 active:scale-95 focus-visible:ring-2 focus-visible:ring-ark-gold/80 disabled:cursor-wait"
|
||||
}
|
||||
title={isDownloading ? t("downloading") : t("download")}
|
||||
aria-label={isDownloading ? t("downloading") : t("download")}
|
||||
@@ -179,11 +188,7 @@ export function RecommendedCard({
|
||||
>
|
||||
{isDownloading ? (
|
||||
<LoaderCircle
|
||||
className={
|
||||
useFigmaDesign
|
||||
? "h-5 w-5 animate-spin"
|
||||
: "h-5 w-5 animate-spin"
|
||||
}
|
||||
className="h-5 w-5 animate-spin"
|
||||
strokeWidth={2.2}
|
||||
/>
|
||||
) : useFigmaDesign ? (
|
||||
@@ -195,7 +200,7 @@ export function RecommendedCard({
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</m.article>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user