-
Notifications
You must be signed in to change notification settings - Fork 6
Thumbnail Waves and hivesigner fix #728
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b6ef242
4666b00
60b6a78
461068e
52266b5
1dafaa6
a48ce88
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| 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> | ||
| ); | ||
| } | ||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add tests for the new HiveSigner callback page flow. This route introduces new behavior ( As per coding guidelines: 🤖 Prompt for AI Agents |
||
| 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"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -23,6 +21,8 @@ interface Props { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| seoContext?: SeoContext; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onHiveOperationClick?: (op: string) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TwitterComponent?: any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| images?: string[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| renderOptions?: RenderOptions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function EcencyRenderer({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Classes added but never cleaned up on component unmount or re-render. When 🧹 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.addEventListener("message", handleMessage); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => window.removeEventListener("message", handleMessage); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [renderOptions?.embedVideosDirectly]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+40
to
+63
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add tests for the new 3Speak 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 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not disable author/tag enhancements under On Line 84, the condition currently suppresses 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <WaveLikePostExtension containerRef={ref} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <TwitterExtension | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| containerRef={ref} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| .ecency-renderer-hive-operation-extension { | ||
| .er-hive-op { | ||
| display: flex; | ||
| gap: 0.25rem; | ||
| text-decoration: none; | ||
|
|
||
There was a problem hiding this comment.
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:
Repository: ecency/vision-next
Length of output: 81
🏁 Script executed:
Repository: ecency/vision-next
Length of output: 726
🏁 Script executed:
Repository: ecency/vision-next
Length of output: 2347
🏁 Script executed:
Repository: ecency/vision-next
Length of output: 164
🏁 Script executed:
Repository: ecency/vision-next
Length of output: 296
🏁 Script executed:
Repository: ecency/vision-next
Length of output: 2074
🏁 Script executed:
Repository: ecency/vision-next
Length of output: 492
Add test coverage for new HsCallbackPage component.
Per coding guidelines, all new features in
@ecency/webrequire tests. Create a test file atapps/web/src/specs/app/auth/hs-callback.spec.tsxwith coverage for:idorblockparams present)Use
renderWithQueryClientfromsrc/specs/test-utils.tsxand test user-visible behavior viascreen.getByRolerather than implementation details.🤖 Prompt for AI Agents