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
2 changes: 1 addition & 1 deletion apps/web/public/sw.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export function EntryPageBodyViewer({ entry }: Props) {
setSigningOperation(op);
},
TwitterComponent: Tweet,
images: entry.json_metadata?.image,
});
} catch (e) {
// Avoid breaking the page if enhancements fail, e.g. due to missing embeds or DOM structure issues
Expand All @@ -146,7 +147,7 @@ export function EntryPageBodyViewer({ entry }: Props) {
clearTimeout(timer);
cleanup?.();
};
}, [isRawContent, isEdit, editHistory]);
}, [isRawContent, isEdit, editHistory, entry.json_metadata?.image]);

return (
<EntryPageViewerManager>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
useAccountUpdateRecovery
} from "@ecency/sdk";
import { PrivateKey } from "@hiveio/dhive";
import { buildHsCallbackUrl } from "@/utils/hs-callback";
import { yupResolver } from "@hookform/resolvers/yup";
import { useQuery } from "@tanstack/react-query";
import { UilEditAlt } from "@tooni/iconscout-unicons-react";
Expand Down Expand Up @@ -70,7 +71,8 @@ export function AccountRecovery() {
getAccessToken(activeUser?.username ?? ""),
{
onError: (e) => error(...formatError(e)),
onSuccess: () => success(i18next.t("account-recovery.success-message"))
onSuccess: () => success(i18next.t("account-recovery.success-message")),
hsCallbackUrl: buildHsCallbackUrl(`/@${activeUser?.username}/permissions`)
},
getSdkAuthContext(getUser(activeUser?.username ?? ""))
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ProfilePreview } from "@/features/shared/profile-popover/profile-previe
import { Popover } from "@/features/ui";
import { getAccountFullQueryOptions, useAccountRevokePosting } from "@ecency/sdk";
import { useQuery } from "@tanstack/react-query";
import { buildHsCallbackUrl } from "@/utils/hs-callback";
import { Button } from "@ui/button";
import { Modal, ModalBody, ModalHeader } from "@ui/modal";
import i18next from "i18next";
Expand Down Expand Up @@ -35,7 +36,8 @@ export function ManageAuthorities() {
activeUser?.username,
{
onError: (err) => error((err as Error).message),
onSuccess: () => setKeyDialog(false)
onSuccess: () => setKeyDialog(false),
hsCallbackUrl: buildHsCallbackUrl(`/@${activeUser?.username}/permissions`)
},
getSdkAuthContext(getUser(activeUser?.username ?? ""))
);
Expand Down
66 changes: 66 additions & 0 deletions apps/web/src/app/auth/hs-callback/_page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"use client";

import { UilCheckCircle, UilTimesCircle } from "@tooni/iconscout-unicons-react";
import i18next from "i18next";
import { useRouter, useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";

export function HsCallbackPage() {
const params = useSearchParams();
const router = useRouter();

const txId = params?.get("id") ?? "";
const block = params?.get("block") ?? "";
const rawRedirect = params?.get("redirect") ?? "/";

// Sanitize redirect to prevent open-redirect attacks
const redirect = rawRedirect.startsWith("/") && !rawRedirect.startsWith("//")
? rawRedirect
: "/";

const isSuccess = txId.length > 0 || block.length > 0;
const [countdown, setCountdown] = useState(3);

useEffect(() => {
if (countdown <= 0) {
router.push(redirect);
return;
}
const timer = setTimeout(() => setCountdown((c) => c - 1), 1000);
return () => clearTimeout(timer);
}, [countdown, redirect, router]);

return (
<div className="flex items-center justify-center min-h-[60vh]">
<div className="flex flex-col items-center gap-4 p-8 bg-white dark:bg-dark-200 rounded-2xl shadow-lg max-w-md w-full mx-4">
{isSuccess ? (
<>
<UilCheckCircle className="text-green w-16 h-16" />
<h2 className="text-xl font-bold text-green">
{i18next.t("g.success")}
</h2>
<p className="text-sm text-gray-600 dark:text-gray-400 text-center">
{i18next.t("transactions.success-hint")}
</p>
{txId && (
<code className="text-xs text-gray-500 break-all">{txId}</code>
)}
</>
) : (
<>
<UilTimesCircle className="text-red w-16 h-16" />
<h2 className="text-xl font-bold text-red">
{i18next.t("g.error")}
</h2>
<p className="text-sm text-gray-600 dark:text-gray-400 text-center">
{i18next.t("transactions.error-hint")}
</p>
</>
)}
<p className="text-sm text-gray-500 mt-2">
{i18next.t("g.redirecting-in", { defaultValue: `Redirecting in ${countdown}s...`, n: countdown })}
</p>
</div>
</div>
);
Comment on lines +8 to +65
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if a test file exists for this component
fd -e test.tsx -e spec.tsx . apps/web/src/app/auth/hs-callback/ 2>/dev/null || echo "No test files found"

# Also check for any existing test patterns for similar callback pages
rg -l "HsCallbackPage" --type ts --type tsx 2>/dev/null || echo "No references found"

Repository: ecency/vision-next

Length of output: 81


🏁 Script executed:

# Search for similar auth-related tests
find apps/web/src/specs -name "*auth*" -o -name "*callback*" | head -20

# Check what auth tests exist
find apps/web/src/specs -type f -name "*.spec.tsx" | xargs grep -l "auth\|callback" 2>/dev/null || echo "No matching test files"

# Look for the test-utils file mentioned in learnings
find apps/web/src -name "test-utils.tsx"

# Check if there are any page component tests for reference
find apps/web/src/specs -type f -name "*page*.spec.tsx" | head -10

Repository: ecency/vision-next

Length of output: 726


🏁 Script executed:

# Check if this is a new file or existing
git log --oneline --all -- "apps/web/src/app/auth/hs-callback/_page.tsx" 2>/dev/null | head -5 || echo "File may be new"

# Look at the structure of an existing page test for reference
cat -n apps/web/src/specs/features/signup/invited-page.spec.tsx | head -50

# Check if there are any tests in the apps/web/src/specs/auth directory
find apps/web/src/specs -path "*auth*" -type f 2>/dev/null || echo "No auth specs directory found"

# Check the exact directory structure for app router pages
ls -la apps/web/src/app/auth/hs-callback/ 2>/dev/null || echo "Directory check"

Repository: ecency/vision-next

Length of output: 2347


🏁 Script executed:

# Get file status and check if it's new in this PR
git status --short apps/web/src/app/auth/hs-callback/_page.tsx 2>/dev/null || echo "Checking git status"

# Check git diff to see if this is a new file
git diff --name-status HEAD -- "apps/web/src/app/auth/hs-callback/_page.tsx" 2>/dev/null | head -1 || echo "No diff found"

# Look for other uses of i18next.t in page components to check pattern
rg "i18next\.t\(" apps/web/src/app --type tsx -B2 -A2 | head -30

# Check if i18n keys used are defined in en-US.json
rg "transactions\.success-hint|transactions\.error-hint|g\.redirecting-in" apps/web/public/locales/en-US.json

Repository: ecency/vision-next

Length of output: 164


🏁 Script executed:

# Find locale files
find apps/web -name "*en-US*" -o -name "en.json" -o -name "*locale*" -type f | grep -i "json\|locale" | head -10

# Simple git status check
git log --oneline -1 -- apps/web/src/app/auth/hs-callback/_page.tsx 2>/dev/null || echo "Could not get git log"

# Check if this file is new/modified
git status --porcelain 2>/dev/null | grep "hs-callback" || echo "Not in current changes"

# Search for where i18n translations are located
find apps/web -type f \( -name "*.json" \) | xargs grep -l "g\.success\|transactions\.success-hint" 2>/dev/null | head -5

Repository: ecency/vision-next

Length of output: 296


🏁 Script executed:

# Check if i18n keys are defined in en-US.json
cat apps/web/src/features/i18n/locales/en-US.json | jq '.["g"], .["transactions"]' 2>/dev/null | head -50

# Search specifically for the keys used
rg "success-hint|error-hint|redirecting-in" apps/web/src/features/i18n/locales/en-US.json

# Check if test file exists for this component (check various naming patterns)
find apps/web -path "*/hs-callback*" -name "*.spec.tsx" -o -path "*/hs-callback*" -name "*.test.tsx" 2>/dev/null | head -5

# Check overall spec directory structure for auth-related tests
find apps/web/src/specs -type d -name "*auth*" 2>/dev/null

Repository: ecency/vision-next

Length of output: 2074


🏁 Script executed:

# Final verification: Check g.success, g.error, g.redirecting-in keys explicitly
rg '"success"|"error"|"redirecting-in"' apps/web/src/features/i18n/locales/en-US.json | grep -E '"(success|error|redirecting-in)"' | head -10

Repository: ecency/vision-next

Length of output: 492


Add test coverage for new HsCallbackPage component.

Per coding guidelines, all new features in @ecency/web require tests. Create a test file at apps/web/src/specs/app/auth/hs-callback.spec.tsx with coverage for:

  • Success state rendering (when id or block params present)
  • Error state rendering (when neither param present)
  • Countdown timer decrements and redirect occurs after reaching 0

Use renderWithQueryClient from src/specs/test-utils.tsx and test user-visible behavior via screen.getByRole rather than implementation details.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/auth/hs-callback/_page.tsx` around lines 8 - 60, Add a new
test file named hs-callback.spec.tsx that exercises the HsCallbackPage
component: render HsCallbackPage via renderWithQueryClient while mocking
next/navigation hooks (useSearchParams to return params for success or empty for
error, and useRouter with a mocked push function), assert visible success vs
error UI using screen.getByRole/getByText (check the check or times icon
headings and the txId code when present), and verify the countdown text updates
by using jest.useFakeTimers()/advanceTimersByTime and that the mocked
router.push has been called when countdown reaches 0; reference the
HsCallbackPage component, renderWithQueryClient helper, screen.getByRole, and
the router.push mock in the tests.

}
15 changes: 15 additions & 0 deletions apps/web/src/app/auth/hs-callback/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Navbar } from "@/features/shared/navbar";
import { Theme } from "@/features/shared/theme";
import { HsCallbackPage } from "./_page";

export const dynamic = "force-dynamic";

export default function HsCallback() {
return (
<>
<Theme />
<Navbar />
<HsCallbackPage />
</>
);
}
Comment on lines +1 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add tests for the new HiveSigner callback page flow.

This route introduces new behavior (force-dynamic + callback UI composition) but the PR does not include tests for it. Please add coverage for at least rendering and redirect-state flow via the callback page component.

As per coding guidelines: apps/web/src/**/*.{ts,tsx}: All new features in @ecency/web require tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/auth/hs-callback/page.tsx` around lines 1 - 15, Add unit
tests that cover the new HsCallback route: write tests that render the
HsCallback component and assert Theme, Navbar and HsCallbackPage are present,
and add a test for the redirect-state flow by simulating the callback query
params/state and asserting the expected navigation or UI change from
HsCallbackPage (mock next/navigation hooks like useRouter/useSearchParams or
whatever HsCallbackPage consumes, and verify router.push or the component state
is called/updated). Ensure tests import HsCallback and HsCallbackPage and mock
any external dependencies (theme, navbar, or auth hooks) so you can assert
rendering and redirect behavior deterministically.

Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const DeckPostViewer = ({ entry: initialEntry, onClose, backTitle }: Prop
<EntryInfo entry={entry} />
</div>
<div className="px-3 pb-4">
<PostContentRenderer value={entry.body} />
<PostContentRenderer value={entry.body} images={entry.json_metadata?.image} />
</div>
<div className="bottom-actions p-3">
<EntryVoteBtn entry={entry} isPostSlider={false} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const DeckThreadItemBody = ({

return (
<div className="thread-item-body">
<PostContentRenderer value={entry.body} />
<PostContentRenderer value={entry.body} images={entry.json_metadata?.image} />
{currentViewingImage &&
portalContainer &&
createPortal(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export function WaveViewDetails({ entry: initialEntry }: Props) {
<div className="p-4">
<PostContentRenderer
value={entry?.body ?? ""}
images={entry?.json_metadata?.image}
renderOptions={{ embedVideosDirectly: true }}
onTagClick={tagFilter ? handleTagClick : undefined}
/>
{poll && <PollWidget entry={entry} poll={poll} isReadOnly={false} />}
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/app/waves/_components/waves-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ export const WavesListItem = React.memo(function WavesListItem({
) : (
<PostContentRenderer
value={entry?.body ?? ""}
images={entry?.json_metadata?.image}
renderOptions={{ embedVideosDirectly: true }}
onTagClick={tagFilter ? handleTagClick : undefined}
/>
)}
Expand Down
47 changes: 38 additions & 9 deletions apps/web/src/features/post-renderer/components/ecency-renderer.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
"use client";

import React, { HTMLProps, useRef } from "react";
import React, { HTMLProps, useEffect, useRef } from "react";
import { renderPostBody } from "@ecency/render-helper";
import type { SeoContext } from "@ecency/render-helper";
import type { RenderOptions, SeoContext } from "@ecency/render-helper";
import { clsx } from "clsx";
import "../ecency-renderer.scss";
import {
AuthorLinkExtension,
HiveOperationExtension,
HivePostLinkExtension,
ImageZoomExtension,
TagLinkExtension,
WaveLikePostExtension,
YoutubeVideoExtension,
} from "./extensions";
Expand All @@ -23,6 +21,8 @@ interface Props {
seoContext?: SeoContext;
onHiveOperationClick?: (op: string) => void;
TwitterComponent?: any;
images?: string[];
renderOptions?: RenderOptions;
}

export function EcencyRenderer({
Expand All @@ -31,10 +31,37 @@ export function EcencyRenderer({
seoContext,
onHiveOperationClick,
TwitterComponent = () => <div>No twitter component</div>,
images,
renderOptions,
...other
}: HTMLProps<HTMLDivElement> & Props) {
const ref = useRef<HTMLDivElement>(null);

// Lightweight postMessage listener for 3Speak orientation when videos are embedded directly
useEffect(() => {
if (!renderOptions?.embedVideosDirectly) return;

const handleMessage = (event: MessageEvent) => {
if (event.origin !== "https://play.3speak.tv" || event.data?.type !== "3speak-player-ready") return;

const iframes = ref.current?.querySelectorAll<HTMLIFrameElement>(".speak-iframe");
iframes?.forEach((iframe) => {
if (iframe.contentWindow !== event.source) return;
const container = iframe.closest(".markdown-video-link-speak");
if (!container) return;

if (event.data.isVertical) {
container.classList.add("speak-portrait");
} else if (event.data.aspectRatio && Math.abs(event.data.aspectRatio - 1) < 0.1) {
container.classList.add("speak-square");
}
Comment on lines +53 to +57
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Classes added but never cleaned up on component unmount or re-render.

When embedVideosDirectly becomes falsy or the component unmounts, the speak-portrait and speak-square classes remain on DOM elements. Consider tracking modified containers and removing classes in the cleanup function.

🧹 Suggested fix
   useEffect(() => {
     if (!renderOptions?.embedVideosDirectly) return;
+    const modifiedContainers: Element[] = [];

     const handleMessage = (event: MessageEvent) => {
       if (event.origin !== "https://play.3speak.tv" || event.data?.type !== "3speak-player-ready") return;

       const iframes = ref.current?.querySelectorAll<HTMLIFrameElement>(".speak-iframe");
       iframes?.forEach((iframe) => {
         if (iframe.contentWindow !== event.source) return;
         const container = iframe.closest(".markdown-video-link-speak");
         if (!container) return;

         if (event.data.isVertical) {
           container.classList.add("speak-portrait");
+          modifiedContainers.push(container);
         } else if (event.data.aspectRatio && Math.abs(event.data.aspectRatio - 1) < 0.1) {
           container.classList.add("speak-square");
+          modifiedContainers.push(container);
         }
       });
     };

     window.addEventListener("message", handleMessage);
-    return () => window.removeEventListener("message", handleMessage);
+    return () => {
+      window.removeEventListener("message", handleMessage);
+      modifiedContainers.forEach(c => c.classList.remove("speak-portrait", "speak-square"));
+    };
   }, [renderOptions?.embedVideosDirectly]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (event.data.isVertical) {
container.classList.add("speak-portrait");
} else if (event.data.aspectRatio && Math.abs(event.data.aspectRatio - 1) < 0.1) {
container.classList.add("speak-square");
}
useEffect(() => {
if (!renderOptions?.embedVideosDirectly) return;
const modifiedContainers: Element[] = [];
const handleMessage = (event: MessageEvent) => {
if (event.origin !== "https://play.3speak.tv" || event.data?.type !== "3speak-player-ready") return;
const iframes = ref.current?.querySelectorAll<HTMLIFrameElement>(".speak-iframe");
iframes?.forEach((iframe) => {
if (iframe.contentWindow !== event.source) return;
const container = iframe.closest(".markdown-video-link-speak");
if (!container) return;
if (event.data.isVertical) {
container.classList.add("speak-portrait");
modifiedContainers.push(container);
} else if (event.data.aspectRatio && Math.abs(event.data.aspectRatio - 1) < 0.1) {
container.classList.add("speak-square");
modifiedContainers.push(container);
}
});
};
window.addEventListener("message", handleMessage);
return () => {
window.removeEventListener("message", handleMessage);
modifiedContainers.forEach(c => c.classList.remove("speak-portrait", "speak-square"));
};
}, [renderOptions?.embedVideosDirectly]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/post-renderer/components/ecency-renderer.tsx` around
lines 53 - 57, The component adds "speak-portrait" and "speak-square" to a
container in the event handler but never removes them on unmount or when
embedVideosDirectly changes; update the effect that registers this handler (in
the EcencyRenderer component / the useEffect handling event.data) to track the
modified container(s) (e.g., store the last container reference or a Set of
containers) and in the effect cleanup remove both "speak-portrait" and
"speak-square" from those containers, and also ensure you remove classes when
embedVideosDirectly becomes falsy by running the same cleanup before toggling
behavior.

});
};

window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}, [renderOptions?.embedVideosDirectly]);
Comment on lines +40 to +63
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add tests for the new 3Speak postMessage orientation flow.

Line 42 introduces new runtime behavior (origin/type filtering + container class mutation), but no companion test coverage is included for this path. Please add a renderer spec for: valid 3Speak message handling, ignored non-matching messages, and portrait/square class outcomes.

As per coding guidelines, "All new features in @ecency/web require tests".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/post-renderer/components/ecency-renderer.tsx` around
lines 42 - 65, Add unit tests for the new 3Speak postMessage flow in the
ecency-renderer tests: simulate renderOptions.embedVideosDirectly=true and mount
the component, then dispatch window.postMessage events to exercise handleMessage
— include a valid message from origin "https://play.3speak.tv" with type
"3speak-player-ready" and both isVertical=true and aspectRatio≈1 cases and
assert the iframe's nearest .markdown-video-link-speak container gets
.speak-portrait or .speak-square respectively; also send non-matching messages
(wrong origin, wrong type, and unmatched event.source) and assert no classes are
added. Ensure tests locate iframes using the .speak-iframe selector and verify
behavior when iframe.contentWindow matches event.source.


return (
<>
<div
Expand All @@ -46,16 +73,18 @@ export function EcencyRenderer({
pure ? "markdown-view-pure" : "",
other.className
)}
dangerouslySetInnerHTML={{ __html: renderPostBody(value, false, false, 'ecency.com', seoContext) }}
dangerouslySetInnerHTML={{ __html: renderPostBody(value, false, false, 'ecency.com', seoContext, renderOptions) }}
/>
{!pure && (
<>
<ImageZoomExtension containerRef={ref} />
<HivePostLinkExtension containerRef={ref} />
<AuthorLinkExtension containerRef={ref} />
<TagLinkExtension containerRef={ref} />
<YoutubeVideoExtension containerRef={ref} />
<ThreeSpeakVideoExtension containerRef={ref} />
{!renderOptions?.embedVideosDirectly && (
<>
<YoutubeVideoExtension containerRef={ref} />
<ThreeSpeakVideoExtension containerRef={ref} images={images} />
</>
)}
Comment on lines +82 to +87
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not disable author/tag enhancements under embedVideosDirectly.

On Line 84, the condition currently suppresses AuthorLinkExtension and TagLinkExtension together with video extensions. That couples unrelated behaviors and can regress author/tag link enhancement when direct video embedding is enabled.

Suggested fix
-          {!renderOptions?.embedVideosDirectly && (
-            <>
-              <AuthorLinkExtension containerRef={ref} />
-              <TagLinkExtension containerRef={ref} />
-              <YoutubeVideoExtension containerRef={ref} />
-              <ThreeSpeakVideoExtension containerRef={ref} images={images} />
-            </>
-          )}
+          <AuthorLinkExtension containerRef={ref} />
+          <TagLinkExtension containerRef={ref} />
+          {!renderOptions?.embedVideosDirectly && (
+            <>
+              <YoutubeVideoExtension containerRef={ref} />
+              <ThreeSpeakVideoExtension containerRef={ref} images={images} />
+            </>
+          )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{!renderOptions?.embedVideosDirectly && (
<>
<AuthorLinkExtension containerRef={ref} />
<TagLinkExtension containerRef={ref} />
<YoutubeVideoExtension containerRef={ref} />
<ThreeSpeakVideoExtension containerRef={ref} images={images} />
</>
)}
<AuthorLinkExtension containerRef={ref} />
<TagLinkExtension containerRef={ref} />
{!renderOptions?.embedVideosDirectly && (
<>
<YoutubeVideoExtension containerRef={ref} />
<ThreeSpeakVideoExtension containerRef={ref} images={images} />
</>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/features/post-renderer/components/ecency-renderer.tsx` around
lines 84 - 91, The current JSX nests AuthorLinkExtension and TagLinkExtension
inside the !renderOptions?.embedVideosDirectly check which disables author/tag
enhancements when videos are embedded directly; change the rendering so
AuthorLinkExtension(containerRef={ref}) and TagLinkExtension(containerRef={ref})
are rendered unconditionally, and only wrap
YoutubeVideoExtension(containerRef={ref}) and
ThreeSpeakVideoExtension(containerRef={ref} images={images}) with the
!renderOptions?.embedVideosDirectly condition; locate these symbols in
ecency-renderer.tsx and move the two link extensions out of the conditional
block so author/tag behavior remains independent of
renderOptions.embedVideosDirectly.

<WaveLikePostExtension containerRef={ref} />
<TwitterExtension
containerRef={ref}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,46 @@
.ecency-renderer-author-extension {
&-link {
vertical-align: middle;
display: inline-flex;
align-items: center;
gap: 0.25rem;
text-decoration: none;
color: #357ce6;
border: 1px solid var(--border-color, #dee2e6);
padding: 0.125rem 0.675rem 0.125rem 0.125rem;
font-size: 0.875rem;
border-radius: 1rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
.er-author-link {
vertical-align: middle;
display: inline-flex !important;
align-items: center;
gap: 0.25rem;
text-decoration: none;
color: #357ce6;
border: 1px solid var(--border-color, #dee2e6);
padding: 0.125rem 0.675rem 0.125rem 0.125rem;
font-size: 0.875rem !important;
border-radius: 1rem;
max-width: 100%;

&-content {
display: flex;
flex-direction: column;
line-height: 1;
.er-author-link-content {
display: flex;
flex-direction: column;
line-height: 1;
min-width: 0;
}

&-label {
font-size: 0.5rem;
font-weight: 500;
letter-spacing: 0.05rem;
color: #8d8d8d;
text-transform: uppercase;
}
}
.er-author-link-label {
font-size: 0.5rem !important;
font-weight: 500;
letter-spacing: 0.05rem;
color: #8d8d8d;
text-transform: uppercase;
white-space: nowrap;
}

&-image {
border-radius: 1rem;
width: 1.5rem;
height: 1.5rem;
}
.er-author-link-image {
border-radius: 1rem;
width: 1.5rem !important;
height: 1.5rem !important;
min-width: 1.5rem;
aspect-ratio: auto !important;
object-fit: cover;
}

&::after {
content: none !important;
}
&::after {
content: none !important;
}

&:hover {
color: #1b68da;
}
&:hover {
color: #1b68da;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export function AuthorLinkRenderer({ author }: { author: string }) {
<>
<img
src={imageSrc}
className="ecency-renderer-author-extension-link-image"
className="er-author-link-image"
alt={author}
/>
<div className="ecency-renderer-author-extension-link-content">
<span className="ecency-renderer-author-extension-link-content-label">
<div className="er-author-link-content">
<span className="er-author-link-label">
Hive account
</span>
<span>{author.toLowerCase().replace("/", "")}</span>
Expand Down Expand Up @@ -60,8 +60,8 @@ export function AuthorLinkExtension({
container.setAttribute("target", "_blank");
container.setAttribute("rel", "noopener");

container.classList.add("ecency-renderer-author-extension");
container.classList.add("ecency-renderer-author-extension-link");
container.classList.add("er-author");
container.classList.add("er-author-link");

const root = createRoot(container);
rootsRef.current.push(root);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.ecency-renderer-hive-operation-extension {
.er-hive-op {
display: flex;
gap: 0.25rem;
text-decoration: none;
Expand Down
Loading