+
+
{children}
- {!hideActions && (
-
- )}
+ {actionButtons}
);
}
diff --git a/components/waves/LinkPreviewContext.tsx b/components/waves/LinkPreviewContext.tsx
index a565eee9f3..3a5c657e89 100644
--- a/components/waves/LinkPreviewContext.tsx
+++ b/components/waves/LinkPreviewContext.tsx
@@ -22,6 +22,9 @@ type LinkPreviewContextValue = {
readonly hideActions: boolean;
readonly previewToggle?: LinkPreviewToggleControl | undefined;
readonly inlineShowControl?: LinkPreviewInlineShowControl | undefined;
+ readonly onCardActionsActiveChange?:
+ | ((href: string, active: boolean) => void)
+ | undefined;
};
const DEFAULT_CONTEXT: LinkPreviewContextValue = {
@@ -36,11 +39,15 @@ export const LinkPreviewProvider = ({
variant = "chat",
previewToggle,
inlineShowControl,
+ onCardActionsActiveChange,
children,
}: {
readonly variant?: LinkPreviewVariant | undefined;
readonly previewToggle?: LinkPreviewToggleControl | undefined;
readonly inlineShowControl?: LinkPreviewInlineShowControl | undefined;
+ readonly onCardActionsActiveChange?:
+ | ((href: string, active: boolean) => void)
+ | undefined;
readonly children: ReactNode;
}) => {
const value = useMemo(
@@ -49,8 +56,9 @@ export const LinkPreviewProvider = ({
hideActions: variant === "home",
previewToggle,
inlineShowControl,
+ onCardActionsActiveChange,
}),
- [variant, previewToggle, inlineShowControl]
+ [variant, previewToggle, inlineShowControl, onCardActionsActiveChange]
);
return (
diff --git a/components/waves/OpenGraphPreview.tsx b/components/waves/OpenGraphPreview.tsx
index 034e3226f3..0d21d49545 100644
--- a/components/waves/OpenGraphPreview.tsx
+++ b/components/waves/OpenGraphPreview.tsx
@@ -260,12 +260,16 @@ export function LinkPreviewCardLayout({
}
return (
-
-
+
+
{children}
{!hideActions && (
-
+
)}
);
diff --git a/components/waves/drops/WaveDrop.tsx b/components/waves/drops/WaveDrop.tsx
index f06ab5269f..ef3694d655 100644
--- a/components/waves/drops/WaveDrop.tsx
+++ b/components/waves/drops/WaveDrop.tsx
@@ -158,6 +158,9 @@ const WaveDrop = ({
const [activePartIndex, setActivePartIndex] = useState
(0);
const [isSlideUp, setIsSlideUp] = useState(false);
const [longPressTriggered, setLongPressTriggered] = useState(false);
+ const [activeLinkCardActionIds, setActiveLinkCardActionIds] = useState<
+ string[]
+ >([]);
const [boostAnimation, setBoostAnimation] =
useState(null);
const dropRef = useRef(null);
@@ -183,6 +186,7 @@ const WaveDrop = ({
const isMdUp = breakpoint === "MD";
const allowLongPress = hasTouch && !isMdUp;
const compact = useCompactMode();
+ const hasActiveLinkCardActions = activeLinkCardActionIds.length > 0;
const isProfileView = location === DropLocation.PROFILE;
const showAuthorInfo = !shouldGroupWithPreviousDrop || isProfileView;
@@ -343,6 +347,22 @@ const WaveDrop = ({
setBoostAnimation(null);
}, []);
+ const handleLinkCardActionsActiveChange = useCallback(
+ (actionId: string, active: boolean) => {
+ setActiveLinkCardActionIds((current) => {
+ const hasActionId = current.includes(actionId);
+ if (active) {
+ return hasActionId ? current : [...current, actionId];
+ }
+
+ return hasActionId
+ ? current.filter((item) => item !== actionId)
+ : current;
+ });
+ },
+ []
+ );
+
// Handler for mobile menu boost animation
const handleMobileBoostAnimation = useCallback(() => {
if (!dropRef.current) return;
@@ -429,6 +449,7 @@ const WaveDrop = ({
onSave={handleEditSave}
onCancel={handleEditCancel}
hasTouch={allowLongPress}
+ onLinkCardActionsActiveChange={handleLinkCardActionsActiveChange}
/>
@@ -439,6 +460,7 @@ const WaveDrop = ({
activePartIndex={activePartIndex}
onReply={handleOnReply}
onEdit={handleOnEdit}
+ suppressed={hasActiveLinkCardActions}
/>
)}
>
diff --git a/components/waves/drops/WaveDropActions.tsx b/components/waves/drops/WaveDropActions.tsx
index 22a4e1c890..2e55ecfec6 100644
--- a/components/waves/drops/WaveDropActions.tsx
+++ b/components/waves/drops/WaveDropActions.tsx
@@ -19,6 +19,7 @@ interface WaveDropActionsProps {
readonly showVoting?: boolean | undefined;
readonly onReply: () => void;
readonly onEdit?: (() => void) | undefined;
+ readonly suppressed?: boolean | undefined;
}
export default function WaveDropActions({
@@ -27,6 +28,7 @@ export default function WaveDropActions({
showVoting = true,
onReply,
onEdit,
+ suppressed = false,
}: WaveDropActionsProps) {
const { isMemesWave } = useSeizeSettings();
const compact = useCompactMode();
@@ -38,16 +40,20 @@ export default function WaveDropActions({
!(
drop.drop_type === ApiDropType.Participatory && isMemesWave(drop.wave.id)
);
+ let visibilityClasses =
+ "tw-pointer-events-none tw-opacity-0 desktop-hover:group-hover:tw-pointer-events-auto desktop-hover:group-hover:tw-opacity-100 desktop-hover:hover:tw-pointer-events-auto desktop-hover:hover:tw-opacity-100";
+
+ if (isMoreDropdownOpen) {
+ visibilityClasses = "tw-pointer-events-auto tw-opacity-100";
+ } else if (suppressed) {
+ visibilityClasses = "tw-pointer-events-none tw-opacity-0";
+ }
return (
diff --git a/components/waves/drops/WaveDropContent.tsx b/components/waves/drops/WaveDropContent.tsx
index 7923c36511..11785ee53b 100644
--- a/components/waves/drops/WaveDropContent.tsx
+++ b/components/waves/drops/WaveDropContent.tsx
@@ -28,6 +28,9 @@ interface WaveDropContentProps {
readonly isCompetitionDrop?: boolean | undefined;
readonly mediaImageScale?: ImageScale | undefined;
readonly hasTouch?: boolean | undefined;
+ readonly onLinkCardActionsActiveChange?:
+ | ((href: string, active: boolean) => void)
+ | undefined;
}
const WaveDropContent: React.FC = ({
@@ -45,6 +48,7 @@ const WaveDropContent: React.FC = ({
isCompetitionDrop = false,
mediaImageScale = ImageScale.AUTOx450,
hasTouch,
+ onLinkCardActionsActiveChange,
}) => {
const isTouchDevice = useIsTouchDevice();
const effectiveHasTouch = hasTouch ?? isTouchDevice;
@@ -65,6 +69,7 @@ const WaveDropContent: React.FC = ({
isCompetitionDrop={isCompetitionDrop}
mediaImageScale={mediaImageScale}
hasTouch={effectiveHasTouch}
+ onLinkCardActionsActiveChange={onLinkCardActionsActiveChange}
/>
);
};
diff --git a/components/waves/drops/WaveDropPart.tsx b/components/waves/drops/WaveDropPart.tsx
index e1d3dc4271..eaf9498aa4 100644
--- a/components/waves/drops/WaveDropPart.tsx
+++ b/components/waves/drops/WaveDropPart.tsx
@@ -29,6 +29,9 @@ interface WaveDropPartProps {
readonly isCompetitionDrop?: boolean | undefined;
readonly mediaImageScale?: ImageScale | undefined;
readonly hasTouch?: boolean | undefined;
+ readonly onLinkCardActionsActiveChange?:
+ | ((href: string, active: boolean) => void)
+ | undefined;
}
const LONG_PRESS_DURATION = 500; // milliseconds
@@ -50,6 +53,7 @@ const WaveDropPart: React.FC = memo(
isCompetitionDrop = false,
mediaImageScale = ImageScale.AUTOx450,
hasTouch = false,
+ onLinkCardActionsActiveChange,
}) => {
const activePart = drop.parts[activePartIndex];
@@ -146,6 +150,7 @@ const WaveDropPart: React.FC = memo(
onCancel={onCancel}
isCompetitionDrop={isCompetitionDrop}
mediaImageScale={mediaImageScale}
+ onLinkCardActionsActiveChange={onLinkCardActionsActiveChange}
/>
diff --git a/components/waves/drops/WaveDropPartContent.tsx b/components/waves/drops/WaveDropPartContent.tsx
index 679a47e9be..ff815da201 100644
--- a/components/waves/drops/WaveDropPartContent.tsx
+++ b/components/waves/drops/WaveDropPartContent.tsx
@@ -36,6 +36,9 @@ interface WaveDropPartContentProps {
readonly drop?: ApiDrop | undefined;
readonly isCompetitionDrop?: boolean | undefined;
readonly mediaImageScale?: ImageScale | undefined;
+ readonly onLinkCardActionsActiveChange?:
+ | ((href: string, active: boolean) => void)
+ | undefined;
}
const WaveDropPartContent: React.FC
= ({
@@ -57,6 +60,7 @@ const WaveDropPartContent: React.FC = ({
drop,
isCompetitionDrop = false,
mediaImageScale = ImageScale.AUTOx450,
+ onLinkCardActionsActiveChange,
}) => {
const contentRef = React.useRef(null);
@@ -146,6 +150,7 @@ const WaveDropPartContent: React.FC = ({
onSave={onSave}
onCancel={onCancel}
drop={drop}
+ onLinkCardActionsActiveChange={onLinkCardActionsActiveChange}
/>
{!!activePart.media.length && (
diff --git a/components/waves/drops/WaveDropPartContentMarkdown.tsx b/components/waves/drops/WaveDropPartContentMarkdown.tsx
index 5952cf6da1..1081733052 100644
--- a/components/waves/drops/WaveDropPartContentMarkdown.tsx
+++ b/components/waves/drops/WaveDropPartContentMarkdown.tsx
@@ -28,6 +28,9 @@ interface WaveDropPartContentMarkdownProps {
| undefined;
readonly onCancel?: (() => void) | undefined;
readonly drop?: ApiDrop | undefined; // Add drop to check for edited status
+ readonly onLinkCardActionsActiveChange?:
+ | ((href: string, active: boolean) => void)
+ | undefined;
}
const WaveDropPartContentMarkdown: React.FC<
@@ -44,6 +47,7 @@ const WaveDropPartContentMarkdown: React.FC<
onSave,
onCancel,
drop,
+ onLinkCardActionsActiveChange,
}) => {
const linkPreviewToggleControl = useDropLinkPreviewToggleControl(drop);
const currentQuotePath =
@@ -89,6 +93,7 @@ const WaveDropPartContentMarkdown: React.FC<
hideLinkPreviews={drop?.hide_link_preview}
quotePath={currentQuotePath}
linkPreviewToggleControl={linkPreviewToggleControl}
+ onLinkCardActionsActiveChange={onLinkCardActionsActiveChange}
/>
{typeof drop?.updated_at === "number" &&
drop.updated_at !== drop.created_at && (
@@ -111,6 +116,7 @@ const WaveDropPartContentMarkdown: React.FC<
embedPath={drop?.id ? [drop.id] : []}
quotePath={currentQuotePath}
embedDepth={1}
+ onLinkCardActionsActiveChange={onLinkCardActionsActiveChange}
/>
)}
diff --git a/components/waves/drops/WaveDropPartDrop.tsx b/components/waves/drops/WaveDropPartDrop.tsx
index 1a957f5107..b4049f1f4d 100644
--- a/components/waves/drops/WaveDropPartDrop.tsx
+++ b/components/waves/drops/WaveDropPartDrop.tsx
@@ -28,6 +28,9 @@ interface WaveDropPartDropProps {
readonly onCancel?: (() => void) | undefined;
isCompetitionDrop?: boolean | undefined;
mediaImageScale?: ImageScale | undefined;
+ readonly onLinkCardActionsActiveChange?:
+ | ((href: string, active: boolean) => void)
+ | undefined;
}
const WaveDropPartDrop: React.FC