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
4 changes: 2 additions & 2 deletions packages/frontend/src/Applications/Feedback/Feedback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import type React from "react";
import { useCallback, useMemo, useState } from "react";
import { FeedbackForm } from "./FeedbackForm";
import { FeedbackSuccess } from "./FeedbackSuccess";
import { useFeedback } from "./useFeedback";
import { FEEDBACK_APP_ID, useFeedback } from "./useFeedback";
import type { FeedbackFields } from "./useFeedback";

const appId = "Feedback.app";
const appId = FEEDBACK_APP_ID;
const appName = "Feedback";

// This app's own icon, registered into the shared registry at
Expand Down
36 changes: 36 additions & 0 deletions packages/frontend/src/Applications/Feedback/useFeedback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,42 @@ describe("useFeedback", () => {

document.body.removeChild(root);
});

it("ignoreElements skips the Feedback app's own windows so they don't appear in the shot", async () => {
const mockCanvas = {
toBlob: (cb: (b: Blob | null) => void) =>
cb(new Blob(["png"], { type: "image/png" })),
} as unknown as HTMLCanvasElement;
mockHtml2canvas.mockResolvedValue(mockCanvas);

const root = document.createElement("div");
root.id = "root";
document.body.appendChild(root);

const { result } = renderHook(() => useFeedback());
await act(async () => {
await result.current.captureScreenshot();
});

const { ignoreElements } = mockHtml2canvas.mock.calls[0][1] as {
ignoreElements: (el: Element) => boolean;
};

// Classicy renders each window with DOM id `${appId}_${windowId}`.
const mainWindow = document.createElement("div");
mainWindow.id = "Feedback.app_feedback_main";
expect(ignoreElements(mainWindow)).toBe(true);

const aboutWindow = document.createElement("div");
aboutWindow.id = "Feedback.app_about";
expect(ignoreElements(aboutWindow)).toBe(true);

const otherAppWindow = document.createElement("div");
otherAppWindow.id = "RadioScanner.app_radioscanner_main";
expect(ignoreElements(otherAppWindow)).toBe(false);

document.body.removeChild(root);
});
});

describe("submit", () => {
Expand Down
8 changes: 8 additions & 0 deletions packages/frontend/src/Applications/Feedback/useFeedback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import html2canvas from "html2canvas-pro";
import { useCallback, useState } from "react";
import { getSessionURL } from "../../openreplay";

// Also the `id` of <ClassicyApp> in Feedback.tsx. Classicy gives every window
// a DOM id of `${appId}_${windowId}`, which is how captureScreenshot excludes
// this app's own windows from the shot.
export const FEEDBACK_APP_ID = "Feedback.app";

export interface FeedbackFields {
name: string;
email: string;
Expand All @@ -27,6 +32,9 @@ export function useFeedback(): {
const canvas = await html2canvas(root, {
useCORS: true,
ignoreElements: (el) => {
// html2canvas re-renders the DOM rather than grabbing the real
// screen, so skipping this app's windows is what "hides" them.
if (el.id.startsWith(`${FEEDBACK_APP_ID}_`)) return true;
if (el.tagName !== "IFRAME") return false;
try {
return new URL((el as HTMLIFrameElement).src).origin !== window.location.origin;
Expand Down
Loading