From ee4876bd18dc385adc2e08d007d3bad8f56203f0 Mon Sep 17 00:00:00 2001 From: webadderall <131426131+webadderall@users.noreply.github.com> Date: Fri, 6 Nov 2026 21:00:07 +1100 Subject: [PATCH] Add Contact beside Socials in the Home sidebar --- electron/ipc/register/permissions.test.ts | 25 ++++ electron/ipc/register/permissions.ts | 9 +- src/components/video-editor/TutorialHelp.tsx | 119 +++++++++++++------ tests/ui/settings-fixed-sidebar.spec.ts | 26 +++- 4 files changed, 138 insertions(+), 41 deletions(-) diff --git a/electron/ipc/register/permissions.test.ts b/electron/ipc/register/permissions.test.ts index 208cd2d7..60866734 100644 --- a/electron/ipc/register/permissions.test.ts +++ b/electron/ipc/register/permissions.test.ts @@ -42,6 +42,31 @@ it("does not prompt or open settings merely by registering permission handlers", expect(mocks.open).not.toHaveBeenCalled(); }); +it.each([ + "mailto:support@recordly.dev", + "mailto:youngchen3442@gmail.com", +])("opens the fixed contact address %s in the default mail client", async (url) => { + expect(await mocks.handlers.get("open-external-url")!(undefined, url)).toEqual({ + success: true, + }); + expect(mocks.open).toHaveBeenCalledExactlyOnceWith(url); +}); + +it.each([ + "mailto:other@example.com", + "mailto:support@recordly.dev?bcc=other@example.com", + "mailto:support@recordly.dev.evil.test", + "mailto:youngchen3442@gmail.com?bcc=other@example.com", + "mailto:youngchen3442@gmail.com.evil.test", + "file:///tmp/example", + "custom:example", +])("blocks unsupported external URL %s", async (url) => { + expect(await mocks.handlers.get("open-external-url")!(undefined, url)).toMatchObject({ + success: false, + }); + expect(mocks.open).not.toHaveBeenCalled(); +}); + it("reports microphone and camera status without requesting access", async () => { mocks.status.mockImplementation((device: string) => device === "microphone" ? "denied" : "not-determined", diff --git a/electron/ipc/register/permissions.ts b/electron/ipc/register/permissions.ts index 4ae5b48e..f1a093de 100644 --- a/electron/ipc/register/permissions.ts +++ b/electron/ipc/register/permissions.ts @@ -20,9 +20,14 @@ export function registerPermissionHandlers() { ipcMain.handle("close-permission-accessory", ({ sender }) => dismissPermissionAccessory(sender)); ipcMain.handle("open-external-url", async (_, url: string) => { try { - // Security: only allow http/https URLs to prevent file:// or custom protocol abuse + // Only the two displayed contact addresses may launch a mail client; other protocols stay blocked. const parsed = new URL(url); - if (parsed.protocol !== "https:" && parsed.protocol !== "http:") { + if ( + parsed.protocol !== "https:" && + parsed.protocol !== "http:" && + url !== "mailto:support@recordly.dev" && + url !== "mailto:youngchen3442@gmail.com" + ) { return { success: false, error: `Blocked non-HTTP URL: ${parsed.protocol}` }; } await shell.openExternal(url); diff --git a/src/components/video-editor/TutorialHelp.tsx b/src/components/video-editor/TutorialHelp.tsx index 599d4fea..6d31b903 100644 --- a/src/components/video-editor/TutorialHelp.tsx +++ b/src/components/video-editor/TutorialHelp.tsx @@ -1,4 +1,4 @@ -import { GithubLogo, XLogo, YoutubeLogo } from "@phosphor-icons/react"; +import { EnvelopeSimple, GithubLogo, XLogo, YoutubeLogo } from "@phosphor-icons/react"; import { useEffect, useState } from "react"; import { UpgradeButton } from "@/components/billing/UpgradeButton"; import { Button } from "@/components/ui/button"; @@ -111,43 +111,86 @@ export function CommunityLinks() { truncateLabel className="h-8 w-full min-w-0 px-1.5 text-xs" /> - +