diff --git a/src/components/feedback/FeedbackDialog.tsx b/src/components/feedback/FeedbackDialog.tsx
index f3e1124c..e23eb073 100644
--- a/src/components/feedback/FeedbackDialog.tsx
+++ b/src/components/feedback/FeedbackDialog.tsx
@@ -215,7 +215,9 @@ export function FeedbackDialog({
aria-label={t(`common.feedback.${item.value}`, item.label)}
aria-pressed={sentiment === item.value}
isDisabled={sending}
- onPress={() => setSentiment(item.value)}
+ onPress={() =>
+ setSentiment(sentiment === item.value ? null : item.value)
+ }
>
@@ -305,7 +307,7 @@ export function FeedbackDialog({
+ {t("common.feedback.orEmail", "Or email")}{" "} + { + if (!window.electronAPI?.openExternalUrl) return; + event.preventDefault(); + void window.electronAPI.openExternalUrl( + "mailto:support@recordly.dev", + ); + }} + > + support@recordly.dev + +
+ )} ); diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index 1a1ff443..a998f3c8 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -36,13 +36,14 @@ "thanks": "Danke für dein Feedback", "signInRequired": "Melde dich an, um Feedback zu senden.", "openHome": "Öffne die Startseite, um dich anzumelden.", + "orEmail": "Oder per E-Mail an", "subject": "Betreff", "subjectPlaceholder": "Was möchtest du uns mitteilen?", "description": "Beschreibung", "descriptionPlaceholder": "Wir lesen jedes Feedback...", "attachFiles": "Dateien anhängen", "removeFile": "{{name}} entfernen", - "diagnostics": "Relevante Diagnoseprotokolle werden angehängt.", + "diagnostics": "Diagnoseprotokolle angehängt. Feedback an support@recordly.dev gesendet", "sending": "Wird gesendet…", "errors": { "unavailable": "Feedback ist erst verfügbar, wenn die Kontodienste eingerichtet sind.", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index 612434ed..7b40500d 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -36,6 +36,7 @@ "thanks": "Thanks for your feedback", "signInRequired": "Sign in to send feedback.", "openHome": "Open Home to sign in.", + "orEmail": "Or email", "missingMessage": "Tell us more before sending.", "sentiment": "How do you feel?", "loving": "Loving", @@ -47,7 +48,7 @@ "descriptionPlaceholder": "We read all feedback...", "attachFiles": "Attach files", "removeFile": "Remove {{name}}", - "diagnostics": "Relevant diagnostic logs will be attached.", + "diagnostics": "Diagnostic logs attached. Feedback sent to support@recordly.dev", "sending": "Sending…", "errors": { "unavailable": "Feedback is unavailable until account services are configured.", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index ad2a9270..8321a66c 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -36,13 +36,14 @@ "thanks": "Gracias por tus comentarios", "signInRequired": "Inicia sesión para enviar comentarios.", "openHome": "Abre Inicio para iniciar sesión.", + "orEmail": "O escribe a", "subject": "Asunto", "subjectPlaceholder": "¿Qué tienes en mente?", "description": "Descripción", "descriptionPlaceholder": "Leemos todos los comentarios...", "attachFiles": "Adjuntar archivos", "removeFile": "Eliminar {{name}}", - "diagnostics": "Se adjuntarán los registros de diagnóstico pertinentes.", + "diagnostics": "Registros de diagnóstico adjuntos. Comentarios enviados a support@recordly.dev", "sending": "Enviando…", "errors": { "unavailable": "Los comentarios no están disponibles hasta que se configuren los servicios de cuenta.", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index b7b8d863..436f98ac 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -36,13 +36,14 @@ "thanks": "Merci pour vos commentaires", "signInRequired": "Connectez-vous pour envoyer des commentaires.", "openHome": "Ouvrez l’accueil pour vous connecter.", + "orEmail": "Ou écrivez à", "subject": "Objet", "subjectPlaceholder": "Que souhaitez-vous nous dire ?", "description": "Description", "descriptionPlaceholder": "Nous lisons tous les commentaires...", "attachFiles": "Joindre des fichiers", "removeFile": "Retirer {{name}}", - "diagnostics": "Les journaux de diagnostic pertinents seront joints.", + "diagnostics": "Journaux de diagnostic joints. Commentaires envoyés à support@recordly.dev", "sending": "Envoi…", "errors": { "unavailable": "Les commentaires seront disponibles une fois les services de compte configurés.", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index a9de5582..b9e99ef4 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -36,13 +36,14 @@ "thanks": "Grazie per i tuoi commenti", "signInRequired": "Accedi per inviare commenti.", "openHome": "Apri Home per accedere.", + "orEmail": "Oppure scrivi a", "subject": "Oggetto", "subjectPlaceholder": "Cosa vorresti dirci?", "description": "Descrizione", "descriptionPlaceholder": "Leggiamo tutti i commenti...", "attachFiles": "Allega file", "removeFile": "Rimuovi {{name}}", - "diagnostics": "Verranno allegati i registri diagnostici pertinenti.", + "diagnostics": "Registri diagnostici allegati. Feedback inviato a support@recordly.dev", "sending": "Invio…", "errors": { "unavailable": "I commenti saranno disponibili dopo la configurazione dei servizi dell’account.", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index f63a21e5..a21cc6ee 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -36,6 +36,7 @@ "thanks": "フィードバックありがとうございます", "signInRequired": "フィードバックを送信するにはログインしてください。", "openHome": "ホームを開いてログインしてください。", + "orEmail": "またはメール", "missingMessage": "送信する前に内容を入力してください。", "sentiment": "使い心地はいかがですか?", "loving": "満足", @@ -47,7 +48,7 @@ "descriptionPlaceholder": "すべてのフィードバックに目を通しています…", "attachFiles": "ファイルを添付", "removeFile": "{{name}}を削除", - "diagnostics": "関連する診断ログが添付されます。", + "diagnostics": "診断ログを添付。フィードバックは support@recordly.dev に送信されます", "sending": "送信中…", "errors": { "unavailable": "アカウントサービスが設定されるまで、フィードバックは利用できません。", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index caa7bb3b..979bf400 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -36,13 +36,14 @@ "thanks": "피드백을 보내 주셔서 감사합니다", "signInRequired": "피드백을 보내려면 로그인하세요.", "openHome": "홈을 열어 로그인하세요.", + "orEmail": "또는 이메일", "subject": "제목", "subjectPlaceholder": "어떤 의견이 있으신가요?", "description": "설명", "descriptionPlaceholder": "모든 피드백을 읽고 있습니다...", "attachFiles": "파일 첨부", "removeFile": "{{name}} 제거", - "diagnostics": "관련 진단 로그가 첨부됩니다.", + "diagnostics": "진단 로그가 첨부되었습니다. 피드백이 support@recordly.dev로 전송됩니다", "sending": "전송 중…", "errors": { "unavailable": "계정 서비스가 구성되어야 피드백을 보낼 수 있습니다.", diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index 28e02247..09c1c808 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -36,13 +36,14 @@ "thanks": "Bedankt voor je feedback", "signInRequired": "Meld je aan om feedback te verzenden.", "openHome": "Open Start om je aan te melden.", + "orEmail": "Of mail naar", "subject": "Onderwerp", "subjectPlaceholder": "Wat wil je ons vertellen?", "description": "Beschrijving", "descriptionPlaceholder": "We lezen alle feedback...", "attachFiles": "Bestanden bijvoegen", "removeFile": "{{name}} verwijderen", - "diagnostics": "Relevante diagnostische logboeken worden bijgevoegd.", + "diagnostics": "Diagnostische logboeken bijgevoegd. Feedback verzonden naar support@recordly.dev", "sending": "Verzenden…", "errors": { "unavailable": "Feedback is pas beschikbaar nadat de accountdiensten zijn ingesteld.", diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index ad907d7b..5e971cc1 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -36,13 +36,14 @@ "thanks": "Obrigado pelos comentários", "signInRequired": "Entre para enviar comentários.", "openHome": "Abra a página inicial para entrar.", + "orEmail": "Ou envie um e-mail para", "subject": "Assunto", "subjectPlaceholder": "O que você gostaria de compartilhar?", "description": "Descrição", "descriptionPlaceholder": "Lemos todos os comentários...", "attachFiles": "Anexar arquivos", "removeFile": "Remover {{name}}", - "diagnostics": "Os registros de diagnóstico relevantes serão anexados.", + "diagnostics": "Registros de diagnóstico anexados. Comentários enviados para support@recordly.dev", "sending": "Enviando…", "errors": { "unavailable": "O envio de comentários ficará disponível após a configuração dos serviços da conta.", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index b15d85e3..0e0d56f8 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -36,13 +36,14 @@ "thanks": "感谢你的反馈", "signInRequired": "登录以发送反馈。", "openHome": "打开首页以登录。", + "orEmail": "或发送邮件至", "subject": "主题", "subjectPlaceholder": "你有什么想法?", "description": "说明", "descriptionPlaceholder": "我们会阅读所有反馈...", "attachFiles": "添加附件", "removeFile": "移除 {{name}}", - "diagnostics": "将附上相关诊断日志。", + "diagnostics": "已附上诊断日志。反馈发送至 support@recordly.dev", "sending": "正在发送…", "errors": { "unavailable": "配置账户服务后才能使用反馈功能。", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index 7199df74..bdd69415 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -36,13 +36,14 @@ "thanks": "感謝你的意見回饋", "signInRequired": "登入以傳送意見回饋。", "openHome": "開啟首頁以登入。", + "orEmail": "或寄送郵件至", "subject": "主旨", "subjectPlaceholder": "你有什麼想法?", "description": "說明", "descriptionPlaceholder": "我們會閱讀所有意見回饋...", "attachFiles": "附加檔案", "removeFile": "移除 {{name}}", - "diagnostics": "將附上相關診斷記錄。", + "diagnostics": "已附上診斷記錄。意見回饋傳送至 support@recordly.dev", "sending": "正在傳送…", "errors": { "unavailable": "設定帳號服務後才能使用意見回饋功能。", diff --git a/tests/ui/feedback.spec.ts b/tests/ui/feedback.spec.ts index 7d5515f5..5c58dfcb 100644 --- a/tests/ui/feedback.spec.ts +++ b/tests/ui/feedback.spec.ts @@ -1,6 +1,79 @@ import { expect, test } from "@playwright/test"; import { installDesktopBridge } from "./bridge"; +for (const clearSelection of [false, true]) { + test(`feedback submits without an emoji${clearSelection ? " after clearing a selection" : ""}`, async ({ + page, + }) => { + await installDesktopBridge(page); + let requestBody = ""; + await page.route("**/functions/v1/submit-feedback", async (route) => { + requestBody = route.request().postData() ?? ""; + await route.fulfill({ json: { success: true } }); + }); + await page.goto("/?windowType=editor"); + await page.getByRole("button", { name: "Recordly account", exact: true }).click(); + await page + .getByRole("dialog", { name: "Account menu", exact: true }) + .getByRole("button", { name: "Feedback", exact: true }) + .click(); + const modal = page.getByRole("dialog", { name: "Send feedback", exact: true }); + if (clearSelection) { + const loving = modal.getByRole("button", { name: "Loving", exact: true }); + await loving.click(); + await expect(loving).toHaveAttribute("aria-pressed", "true"); + await loving.click(); + } + await expect(modal.locator('[aria-pressed="true"]')).toHaveCount(0); + await modal.getByRole("button", { name: "Send feedback", exact: true }).click(); + await expect(modal).toBeVisible(); + expect(requestBody).toBe(""); + await modal + .getByLabel("Description", { exact: true }) + .fill("Please add more keyboard shortcuts."); + await modal.getByRole("button", { name: "Send feedback", exact: true }).click(); + await expect( + page.getByRole("dialog", { name: "Thanks for your feedback", exact: true }), + ).toBeVisible(); + expect(requestBody).toContain('name="title"\r\n\r\nPlease add more keyboard shortcuts.'); + expect(requestBody).not.toMatch(/🥰|😐|😠/u); + }); +} + +for (const signedIn of [false, true]) { + test(`feedback offers support email when signed ${signedIn ? "in" : "out"}`, async ({ + page, + }) => { + await installDesktopBridge(page, undefined, { signedIn }); + await page.addInitScript(() => { + window.electronAPI.openExternalUrl = async (url: string) => { + document.documentElement.dataset.openedExternalUrl = url; + return { success: true }; + }; + }); + await page.goto("/?windowType=editor"); + if (signedIn) { + await page.getByRole("button", { name: "Recordly account", exact: true }).click(); + await page + .getByRole("dialog", { name: "Account menu", exact: true }) + .getByRole("button", { name: "Feedback", exact: true }) + .click(); + } else { + await page.getByRole("button", { name: "Feedback", exact: true }).click(); + } + const modal = page.getByRole("dialog", { name: "Send feedback", exact: true }); + await expect(modal.getByText("Or email", { exact: false })).toBeVisible(); + const email = modal.getByRole("link", { name: "support@recordly.dev", exact: true }); + await expect(email).toHaveAttribute("href", "mailto:support@recordly.dev"); + await email.click(); + await expect(page.locator("html")).toHaveAttribute( + "data-opened-external-url", + "mailto:support@recordly.dev", + ); + await expect(modal).toBeVisible(); + }); +} + test("feedback preserves a draft on failure, supports attachments, and includes diagnostics automatically", async ({ page, }) => { @@ -31,7 +104,7 @@ test("feedback preserves a draft on failure, supports attachments, and includes await expect(modal.getByText("notes.txt", { exact: true })).toBeVisible(); await modal.getByRole("button", { name: "Remove notes.txt" }).click(); await expect(modal.getByText("notes.txt", { exact: true })).toHaveCount(0); - await expect(modal.getByText("Relevant diagnostic logs will be attached.")).toBeVisible(); + await expect(modal.getByText("Diagnostic logs attached. Feedback sent to support@recordly.dev")).toBeVisible(); await expect(modal.getByRole("checkbox")).toHaveCount(0); await expect(modal.getByLabel("Title", { exact: true })).toHaveCount(0); await modal.getByRole("button", { name: "Send feedback", exact: true }).click();