ENG-1886 Send feedback button in Obsidian#1181
Conversation
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR size/scope checkThis PR is over our review-size guideline.
Please split this into smaller PRs unless there is a clear reason the changes need to land together. If keeping it as one PR, please add a brief justification covering:
|
803dfbf to
f9645dd
Compare
Adds a "Send feedback" button to the plugin's info section that opens a modal form. Users can attach a screenshot, fill in email, title, description, and feedback type, then submit — which creates a Linear triage issue via the website API route. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
f9645dd to
b0eeacb
Compare
Refactor feedback API helpers to use object params, declare LINEAR_API_KEY in turbo.json, and make OPTIONS sync to satisfy eslint. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 46fa9d0362
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| import { StrictMode, useState, useRef, useEffect } from "react"; | ||
| import type DiscourseGraphPlugin from "~/index"; | ||
|
|
||
| const FEEDBACK_ENDPOINT = `${process.env.NEXT_API_ROOT}/feedback`; |
There was a problem hiding this comment.
Add a production fallback for the endpoint
When the plugin is built through the documented Obsidian publish path, apps/obsidian/scripts/publish.ts invokes pnpm run build directly and the README only requires an Obsidian release token, so dbEnv.NEXT_API_ROOT is empty unless the publisher manually supplies extra env. This constant then compiles to /feedback; in Obsidian there is no website origin for that relative URL, so the new Send feedback flow fails in published builds. Please default to the production API URL or make the publish/build path set NEXT_API_ROOT.
Useful? React with 👍 / 👎.
| if (payload.screenshot) { | ||
| await uploadScreenshotToLinear(apiKey, issue.id, payload.screenshot); |
There was a problem hiding this comment.
Avoid reporting failure after creating the issue
If screenshot upload or attachment fails after createIssue succeeds, this await throws into the catch block and the API returns 500. The modal then tells the user the submission failed, but a Linear issue was already created, so retrying creates duplicate triage issues for the same feedback. Consider treating attachment failure separately or making the operation idempotent.
Useful? React with 👍 / 👎.
mdroidian
left a comment
There was a problem hiding this comment.
The two codex comments are worth addressing, especially the first one.
We'll need to add LINEAR_API_KEY to vercel env's, so could you create a follow-up ticket for that and tag me in it please?
Note: this makes POST /api/feedback an unauthenticated public endpoint that creates Linear tickets, with no server-side abuse mitigation.
Right now the route only checks that title is present before calling createIssue(...). The Obsidian modal has client-side email/title validation, but a bot can skip that and POST directly. CORS also does not protect this endpoint from non-browser clients.
I've created a linear ticket to further look into this, Otherwise someone can script this endpoint and flood Linear with tickets.
Right now it's fairly low risk, so I won't block on it.
| type FeedbackType = "feedback" | "bug_report" | "feature_request"; | ||
|
|
||
| const FEEDBACK_TYPE_LABELS: Record<FeedbackType, string> = { | ||
| bug_report: "Bug report", |
|
|
||
| const FEEDBACK_TYPE_LABELS: Record<FeedbackType, string> = { | ||
| bug_report: "Bug report", | ||
| feature_request: "Feature request", |
| pluginVersion: plugin.manifest.version, | ||
| }; | ||
|
|
||
| console.log("[FeedbackModal] POST", FEEDBACK_ENDPOINT, payload); |
| body: JSON.stringify(payload), | ||
| }); | ||
|
|
||
| console.log("[FeedbackModal] response status:", response.status); |
| }); | ||
|
|
||
| console.log("[FeedbackModal] response status:", response.status); | ||
| console.log("[FeedbackModal] response body:", response.json); |
| {/* Feedback type */} | ||
| <select | ||
| value={feedbackType} | ||
| onChange={(e) => setFeedbackType(e.target.value as FeedbackType)} |
There was a problem hiding this comment.
Why do we need to assert here?
| onChange={(e) => setFeedbackType(e.target.value as FeedbackType)} | ||
| className={selectClass} | ||
| > | ||
| {(Object.keys(FEEDBACK_TYPE_LABELS) as FeedbackType[]).map((type) => ( |
There was a problem hiding this comment.
Why do we need to assert here?
| const LINEAR_TEAM_ID = "e69757b7-976a-4567-836f-16f8a4d59df2"; // Engineering team | ||
| const LINEAR_TRIAGE_STATE_ID = "b4d95c83-3020-4f2a-9f38-5de042c66f6b"; // Triage status | ||
|
|
||
| type FeedbackType = "feedback" | "bug_report" | "feature_request"; |
https://www.loom.com/share/0c8218528a58485bbb717da9158f8745
Summary
FeedbackModal) with: screenshot attachment, email (with inline format validation), title (required), description (optional), and feedback type dropdownPOST /api/feedbackon the website, which creates a Linear triage issue and attaches any screenshot directly to the issue via Linear's file upload APINEXT_API_ROOTbuild-time injectionTest plan
LINEAR_API_KEY=lin_api_...toapps/website/.env🤖 Generated with Claude Code