diff --git a/src/main/ipc/feedback.ts b/src/main/ipc/feedback.ts index 82f61bd393b..663f24a4e3f 100644 --- a/src/main/ipc/feedback.ts +++ b/src/main/ipc/feedback.ts @@ -13,6 +13,13 @@ export type FeedbackSubmitArgs = { feedback: string githubLogin: string | null githubEmail: string | null + // Why: when the user submits anonymously they can still volunteer a GitHub + // handle (so we can @-mention them on the fix PR) or a contact (email / x + // handle for follow-up). These are separate fields rather than reusing + // githubLogin/githubEmail so the backend can tell verified gh identity + // apart from a self-typed handle. + anonymousGithubLogin?: string | null + anonymousContact?: string | null } type FeedbackSubmitBody = FeedbackSubmitArgs & { diff --git a/src/preload/api-types.ts b/src/preload/api-types.ts index a18aa14508e..7089df5acc9 100644 --- a/src/preload/api-types.ts +++ b/src/preload/api-types.ts @@ -506,6 +506,8 @@ export type PreloadApi = { feedback: string githubLogin: string | null githubEmail: string | null + anonymousGithubLogin?: string | null + anonymousContact?: string | null }) => Promise<{ ok: true } | { ok: false; status: number | null; error: string }> } export: ExportApi diff --git a/src/preload/index.ts b/src/preload/index.ts index 39cdc0e4a95..9bde36588c6 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -519,6 +519,8 @@ const api = { feedback: string githubLogin: string | null githubEmail: string | null + anonymousGithubLogin?: string | null + anonymousContact?: string | null }): Promise<{ ok: true } | { ok: false; status: number | null; error: string }> => ipcRenderer.invoke('feedback:submit', args) }, diff --git a/src/renderer/src/components/sidebar/SidebarToolbar.tsx b/src/renderer/src/components/sidebar/SidebarToolbar.tsx index 03d457bc281..4062baea3c2 100644 --- a/src/renderer/src/components/sidebar/SidebarToolbar.tsx +++ b/src/renderer/src/components/sidebar/SidebarToolbar.tsx @@ -42,6 +42,14 @@ function getSubmitIdentity(viewer: GitHubViewer | null, anonymous: boolean): Sub } } +// Why: when a user goes anonymous they lose the auto-attached gh identity, so +// the only way we can tag them on the fix PR or reach out when it lands is if +// they volunteer one of these. Strip the leading @ users tend to type. +function normalizeOptional(value: string): string | null { + const trimmed = value.trim().replace(/^@+/, '') + return trimmed.length > 0 ? trimmed : null +} + function FeedbackDialog({ open, onOpenChange @@ -54,6 +62,8 @@ function FeedbackDialog({ const [viewer, setViewer] = useState(null) const [isViewerLoading, setIsViewerLoading] = useState(false) const [submitAnonymously, setSubmitAnonymously] = useState(false) + const [anonGithubLogin, setAnonGithubLogin] = useState('') + const [anonContact, setAnonContact] = useState('') React.useEffect(() => { if (!open) { @@ -104,7 +114,12 @@ function FeedbackDialog({ const result = await window.api.feedback.submit({ feedback: trimmed, githubLogin: identity.githubLogin, - githubEmail: identity.githubEmail + githubEmail: identity.githubEmail, + // Why: only forward the opt-in fields when the user explicitly went + // anonymous. Otherwise the regular gh identity already covers tagging + // and contact, and we don't want to surprise non-anonymous submitters. + anonymousGithubLogin: submitAnonymously ? normalizeOptional(anonGithubLogin) : null, + anonymousContact: submitAnonymously ? normalizeOptional(anonContact) : null }) if (!result.ok) { @@ -114,6 +129,8 @@ function FeedbackDialog({ toast.success('Thanks for the feedback.') setFeedback('') setSubmitAnonymously(false) + setAnonGithubLogin('') + setAnonContact('') onOpenChange(false) } catch (err) { toast.error('Failed to submit feedback. Please try again.') @@ -216,6 +233,50 @@ function FeedbackDialog({ )} + {/* Why: grid-rows 0fr→1fr gives a smooth height animation without + measuring the DOM. Only shown when the user opts into anonymous so + the dialog stays compact in the common case. */} +
+
+
+
+ If we fix the issue, we can't let you know 🥲 +
+ + +
+
+