Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { formatNumberWithCommas } from "@/helpers/Helpers";
import type { ExtendedDrop } from "@/helpers/waves/drop.helpers";
import { useCallback, useEffect, useRef, useState } from "react";
import MemesQuickVoteActionBar from "./MemesQuickVoteActionBar";
import MemesQuickVoteDropHeader from "./MemesQuickVoteDropHeader";

Expand All @@ -27,6 +28,109 @@ interface MemesQuickVoteControlsProps {
readonly onVoteAmount: (amount: number) => void;
}

function MemesQuickVoteDescription({
description,
}: {
readonly description: string;
}) {
const [isExpanded, setIsExpanded] = useState(false);
const [isOverflowing, setIsOverflowing] = useState(false);
const visibleDescriptionRef = useRef<HTMLParagraphElement | null>(null);
const clampClass = isExpanded ? "tw-line-clamp-none" : "tw-line-clamp-4";
const descriptionClassName =
"tw-mb-0 tw-whitespace-pre-line tw-text-sm tw-font-medium tw-leading-relaxed tw-text-iron-400 md:tw-text-md";

const measureOverflow = useCallback(() => {
const visibleDescription = visibleDescriptionRef.current;

if (!visibleDescription) {
return;
}

const computedStyles = globalThis.getComputedStyle(visibleDescription);
const lineHeight = Number.parseFloat(computedStyles.lineHeight || "0");
const collapsedHeight = lineHeight * 4;

if (!Number.isFinite(collapsedHeight) || collapsedHeight <= 0) {
return;
}

const previousDisplay = visibleDescription.style.display;
const previousOverflow = visibleDescription.style.overflow;
const previousWebkitLineClamp = visibleDescription.style.webkitLineClamp;

visibleDescription.style.display = "block";
visibleDescription.style.overflow = "visible";
visibleDescription.style.webkitLineClamp = "unset";

const fullHeight = visibleDescription.getBoundingClientRect().height;

visibleDescription.style.display = previousDisplay;
visibleDescription.style.overflow = previousOverflow;
visibleDescription.style.webkitLineClamp = previousWebkitLineClamp;

const nextIsOverflowing = fullHeight > collapsedHeight + 1;

setIsOverflowing((current) =>
current === nextIsOverflowing ? current : nextIsOverflowing
);
}, []);

useEffect(() => {
const frameId = globalThis.requestAnimationFrame(() => {
measureOverflow();
});

if (typeof ResizeObserver === "undefined") {
const handleResize = () => {
measureOverflow();
};

globalThis.addEventListener("resize", handleResize);
return () => {
globalThis.removeEventListener("resize", handleResize);
globalThis.cancelAnimationFrame(frameId);
};
}

const observer = new ResizeObserver(() => {
measureOverflow();
});

if (visibleDescriptionRef.current) {
observer.observe(visibleDescriptionRef.current);
}

return () => {
observer.disconnect();
globalThis.cancelAnimationFrame(frameId);
};
}, [measureOverflow]);

return (
<div className="tw-space-y-1.5">
<p
ref={visibleDescriptionRef}
className={`${descriptionClassName} ${clampClass}`}
>
{description}
</p>
{isOverflowing && (
<button
type="button"
aria-expanded={isExpanded}
onClick={() => {
setIsExpanded((current) => !current);
}}
className="tw-inline-flex tw-w-fit tw-border-0 tw-bg-transparent tw-px-0 tw-py-1 tw-text-xs tw-font-medium tw-leading-none tw-text-iron-500 tw-transition-colors tw-duration-200 focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-white/15 desktop-hover:hover:tw-text-iron-300"
>
{isExpanded ? "See less" : "See more"}
</button>
)}
</div>
);
}

export default function MemesQuickVoteControls({
customValue,
drop,
Expand Down Expand Up @@ -65,7 +169,7 @@ export default function MemesQuickVoteControls({
</div>

<div className="tw-relative tw-hidden tw-min-h-0 tw-flex-1 md:tw-flex">
<div className="tw-min-h-0 tw-flex-1 tw-overflow-y-auto tw-pb-16 [-ms-overflow-style:none] [scrollbar-width:none] md:tw-px-8 [&::-webkit-scrollbar]:tw-hidden">
<div className="tw-[scrollbar-gutter:stable] tw-min-h-0 tw-flex-1 tw-overflow-y-auto tw-pb-16 tw-scrollbar-thin tw-scrollbar-track-transparent tw-scrollbar-thumb-iron-700/60 desktop-hover:hover:tw-scrollbar-thumb-iron-600/80 md:tw-px-8">
<div className="tw-flex tw-w-full tw-flex-col tw-gap-5">
<MemesQuickVoteDropHeader drop={drop} />

Expand All @@ -75,9 +179,10 @@ export default function MemesQuickVoteControls({
</h2>

{description && (
<p className="tw-mb-0 tw-line-clamp-4 tw-text-sm tw-font-medium tw-leading-relaxed tw-text-iron-400 md:tw-text-md">
{description}
</p>
<MemesQuickVoteDescription
key={drop.id}
description={description}
/>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,26 @@ function MemesQuickVoteCopySkeleton({

function MemesQuickVoteActionBarSkeleton() {
return (
<div className="tw-relative tw-border-t tw-border-solid tw-border-white/5 tw-bg-[linear-gradient(180deg,rgba(10,10,12,0.68),rgba(10,10,12,0.94))] tw-px-3 tw-pb-[calc(env(safe-area-inset-bottom,0px)+0.375rem)] tw-pt-1.5 tw-backdrop-blur-[28px] sm:tw-pb-[calc(env(safe-area-inset-bottom,0px)+0.5rem)] sm:tw-pt-2 md:tw-relative md:tw-z-20 md:tw-shrink-0 md:tw-p-0 md:tw-px-8 md:tw-pb-6 md:tw-pt-0">
<div className="tw-relative tw-bg-[linear-gradient(180deg,rgba(10,10,12,0.68),rgba(10,10,12,0.94))] tw-px-3 tw-pb-[calc(env(safe-area-inset-bottom,0px)+0.375rem)] tw-pt-1.5 tw-backdrop-blur-[28px] sm:tw-pb-[calc(env(safe-area-inset-bottom,0px)+0.5rem)] sm:tw-pt-2 md:tw-relative md:tw-z-20 md:tw-shrink-0 md:tw-p-0 md:tw-px-8 md:tw-pb-6 md:tw-pt-0">
<div className="tw-relative tw-z-10 tw-flex tw-flex-col tw-gap-2 sm:tw-gap-3">
<SkeletonBlock className="tw-bg-white/[0.08] tw-mx-auto tw-h-1 tw-w-10 tw-rounded-xl md:tw-hidden" />
<SkeletonBlock className="tw-mx-auto tw-h-1 tw-w-10 tw-rounded-xl tw-bg-iron-700/40 md:tw-hidden" />

<div className="tw-border-white/[0.06] tw-flex tw-flex-col tw-gap-2 tw-rounded-xl tw-border tw-border-solid tw-bg-[#151519]/95 tw-p-3 tw-shadow-[inset_0_1px_0_rgba(255,255,255,0.04),0_12px_28px_rgba(0,0,0,0.22)] sm:tw-gap-3 sm:tw-p-4 md:tw-p-5 md:tw-shadow-[0_20px_40px_rgba(0,0,0,0.18)]">
<div className="tw-flex tw-flex-col tw-gap-2 tw-rounded-xl tw-bg-iron-950/95 tw-p-3 tw-shadow-[0_12px_28px_rgba(0,0,0,0.22)] sm:tw-gap-3 sm:tw-p-4 md:tw-p-5 md:tw-shadow-[0_20px_40px_rgba(0,0,0,0.18)]">
<div className="tw-flex tw-flex-wrap tw-items-center tw-justify-between tw-gap-1.5 tw-px-0.5 sm:tw-gap-2 sm:tw-px-1 md:tw-flex-nowrap">
<SkeletonBlock className="tw-h-3 tw-w-20 tw-bg-iron-800/60" />
<div className="tw-border-white/[0.06] tw-flex tw-items-center tw-gap-1.5 tw-rounded-xl tw-border tw-border-solid tw-bg-white/[0.03] tw-px-3 tw-py-1 sm:tw-gap-2 sm:tw-py-1.5">
<SkeletonBlock className="tw-size-3.5 tw-rounded-full tw-bg-primary-500/20" />
<SkeletonBlock className="tw-h-4 tw-w-32 tw-bg-primary-500/15" />
<div className="tw-flex tw-items-center tw-gap-1.5 tw-rounded-xl tw-bg-iron-900/70 tw-px-3 tw-py-1 sm:tw-gap-2 sm:tw-py-1.5">
<SkeletonBlock className="tw-size-3.5 tw-rounded-full tw-bg-iron-700/50" />
<SkeletonBlock className="tw-h-4 tw-w-32 tw-bg-iron-700/40" />
</div>
</div>

<div className="tw-flex tw-h-11 tw-items-stretch tw-gap-1.5 sm:tw-h-12 sm:tw-gap-2 md:tw-h-12">
<SkeletonBlock className="tw-h-full tw-min-w-0 tw-flex-1 tw-rounded-xl tw-border tw-border-solid tw-border-white/10 tw-bg-black/80 md:tw-rounded-lg md:tw-border-iron-700 md:tw-bg-[#050505]" />
<SkeletonBlock className="tw-h-full tw-w-24 tw-rounded-xl tw-bg-primary-500/20 md:tw-w-20 lg:tw-w-24" />
<SkeletonBlock className="tw-h-full tw-min-w-0 tw-flex-1 tw-rounded-xl tw-bg-iron-900 md:tw-rounded-lg" />
<SkeletonBlock className="tw-h-full tw-w-24 tw-rounded-xl tw-bg-iron-800/80 md:tw-w-20 lg:tw-w-24" />
</div>
</div>

<SkeletonBlock className="tw-border-white/8 tw-h-11 tw-w-full tw-rounded-xl tw-border tw-border-solid tw-bg-[#16161b] sm:tw-h-12" />
<SkeletonBlock className="tw-h-11 tw-w-full tw-rounded-xl tw-bg-iron-950 sm:tw-h-12" />
</div>
</div>
);
Expand All @@ -80,12 +80,12 @@ export default function MemesQuickVoteDialogSkeleton() {
</p>

<div data-testid="quick-vote-preview-status" className="tw-hidden">
<SkeletonBlock className="tw-h-8 tw-w-32 tw-rounded-full tw-bg-iron-800/80" />
<SkeletonBlock className="tw-h-8 tw-w-32 tw-rounded-full tw-bg-iron-800/60" />
</div>

<div className="tw-flex tw-items-center tw-justify-between tw-border-b tw-border-solid tw-border-white/5 tw-bg-black/40 tw-px-4 tw-pb-3 tw-pt-[calc(env(safe-area-inset-top,0px)+0.75rem)] tw-backdrop-blur-xl md:tw-hidden">
<SkeletonBlock className="tw-size-10 tw-rounded-full tw-bg-iron-800/80" />
<SkeletonBlock className="tw-h-4 tw-w-28 tw-rounded-full tw-bg-iron-800/70" />
<SkeletonBlock className="tw-h-4 tw-w-28 tw-rounded-full tw-bg-iron-800/55" />
<div className="tw-size-10 tw-shrink-0" />
</div>

Expand Down Expand Up @@ -121,7 +121,7 @@ export default function MemesQuickVoteDialogSkeleton() {
className="tw-hidden tw-shrink-0 tw-flex-col tw-gap-0 md:tw-flex md:tw-h-full md:tw-min-h-0 md:tw-overflow-hidden md:tw-bg-[#0a0a0a]/30"
>
<div className="tw-hidden tw-shrink-0 tw-flex-wrap tw-gap-2 tw-px-8 md:tw-flex md:tw-pb-6 md:tw-pt-6">
<SkeletonBlock className="tw-h-8 tw-w-32 tw-rounded-full tw-bg-iron-800/80" />
<SkeletonBlock className="tw-h-8 tw-w-32 tw-rounded-full tw-bg-iron-800/60" />
</div>

<div className="tw-relative tw-hidden tw-min-h-0 tw-flex-1 md:tw-flex">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ export default function DropListItemContentMedia({
case MediaType.UNKNOWN:
return <></>;
default:
assertUnreachable(mediaType);
return;
return assertUnreachable(mediaType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function DropListItemContentMediaImage({
}, []);

const handleError = useCallback(() => {
if (errorCount >= maxRetries) return; // give up – show fallback
if (errorCount >= maxRetries) return;
const delay = 500 * 2 ** errorCount; // 0.5s, 1s, 2s …
setTimeout(() => {
setErrorCount((n) => n + 1);
Expand Down Expand Up @@ -105,7 +105,7 @@ function DropListItemContentMediaImage({
event.stopPropagation();
const fullscreenTarget = modalImageRef.current ?? imgRef.current;
if (fullscreenTarget) {
fullscreenTarget.requestFullscreen();
fullscreenTarget.requestFullscreen().catch(() => undefined);
}
},
[]
Expand Down Expand Up @@ -162,6 +162,7 @@ function DropListItemContentMediaImage({
wrapperClass="tw-w-full tw-h-full tw-flex tw-items-center tw-justify-center"
contentClass="tw-w-full tw-h-full tw-flex tw-items-center tw-justify-center"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
ref={modalImageRef}
src={src}
Expand Down
Loading