Skip to content

feat: add share menu for doubts#620

Open
bhumishah2411 wants to merge 6 commits into
knoxiboy:mainfrom
bhumishah2411:DoubtShareButton
Open

feat: add share menu for doubts#620
bhumishah2411 wants to merge 6 commits into
knoxiboy:mainfrom
bhumishah2411:DoubtShareButton

Conversation

@bhumishah2411

@bhumishah2411 bhumishah2411 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

User description

Description

Added a Share Menu to doubt cards that allows users to quickly share doubts through WhatsApp, Telegram, or by copying a direct link.

Features Added

  • Share button on doubt cards
  • Copy Link functionality using Clipboard API
  • Share on WhatsApp with prefilled doubt URL
  • Share on Telegram with prefilled doubt URL
    This improves collaboration and makes it easier for students to share doubts with classmates, friends, and study groups.

Related Issue

Closes #610

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Documentation update (README, guides, comments)
  • Style / UI change (no logic change)
  • Code refactor (no behavior change)
  • Test addition or update
  • Breaking change (fix or feature that would cause existing functionality to change)

Screenshots (if UI change)

image

How Has This Been Tested?

  • Tested locally with npm run dev
  • Verified on mobile viewport (375px)
  • Verified on desktop viewport (1440px)

Manual Testing

  • Verified that the share menu opens correctly.
  • Verified that Copy Link copies the correct doubt URL.
  • Verified that WhatsApp sharing opens with the correct prefilled link.
  • Verified that Telegram sharing opens with the correct prefilled link.
  • Verified that existing doubt card functionality remains unaffected.

Checklist

  • I have tested my changes locally (npm run dev)
  • My code follows the existing code style (TypeScript, Tailwind, no any types)
  • I have not introduced unrelated changes (each PR should address one issue)
  • I have added comments where necessary
  • My branch is up to date with main
  • I have linked the related issue above
  • Screenshots are included (if this is a UI change)

Summary by CodeRabbit

  • New Features

    • Enhanced sharing: dropdown menu offering Copy Link, WhatsApp, and Telegram options.
  • Bug Fixes

    • More reliable reply and total counts in listings.
    • Improved teacher reply access checks to prevent incorrect permissions and handle missing data gracefully.
    • Removed the onboarding tour from the Home page.
  • Chores

    • Database migration adding an embeddings column and updated schema snapshot.
    • Safer handling when the database URL is missing to avoid runtime crashes.

CodeAnt-AI Description

Add sharing options for doubts and fix reply access checks

What Changed

  • The doubt card share button now opens a menu instead of copying a link immediately.
  • Users can now copy the doubt link or share it directly through WhatsApp or Telegram.
  • Copy/share actions now show a clear success or failure message.
  • Reply access checks were tightened so classroom and teacher-only replies are only shown to users who are allowed to see them.

Impact

✅ Easier sharing of doubts
✅ Faster sharing to WhatsApp and Telegram
✅ Fewer access errors for classroom replies

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai

codeant-ai Bot commented Jun 8, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.

@vercel

vercel Bot commented Jun 8, 2026

Copy link
Copy Markdown

@bhumishah2411 is attempting to deploy a commit to the Karan Mani Tripathi 's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added size/m gssoc'26 GSSoC program issue level:intermediate Intermediate level task type:bug Bug fix type:feature New feature review-needed labels Jun 8, 2026
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a share-dropdown to DoubtCard (copy, WhatsApp, Telegram). Backend tweaks: use drizzle count (remove ::int casts) for doubts pagination counts, tighten replies authorization to require membership and role for teacher doubts. Adds pgvector column and updates schema snapshot; removes Home onboarding.

Changes

Sharing Enhancements

Layer / File(s) Summary
Share menu imports and constants
src/components/DoubtCard.tsx
Lucide-react icon imports updated and SHARE_MESSAGES constant introduced.
Share URL and handler functions
src/components/DoubtCard.tsx
getShareUrl() plus handlers for Copy Link (clipboard + toast), WhatsApp, and Telegram sharing implemented.
Share dropdown menu UI
src/components/DoubtCard.tsx
Standalone share button replaced by a DropdownMenu trigger with three DropdownMenuItem entries wired to the new handlers.

Doubts API (counting/pagination)

Layer / File(s) Summary
drizzle count import and SQL aggregation
src/app/api/doubts/route.ts
Import count from drizzle-orm; update replyCountSql and totalCountRow to use count(*) / count() without explicit ::int casts.

Replies API (membership gating)

Layer / File(s) Summary
Conditional membership fetch and auth flags (GET)
src/app/api/replies/route.ts
Only query membershipsTable when email and doubt.classroomId exist; treat membership as optional and derive isTeacher/isOwner with presence guards.
Teacher permission check (POST)
src/app/api/replies/route.ts
Require classroom membership existence and canTeach(membership.role) to allow teacher replies.

Database migration & schema snapshot

Layer / File(s) Summary
Migration and Drizzle snapshot
drizzle/0009_marvelous_vin_gonzales.sql, drizzle/meta/0009_snapshot.json, drizzle/meta/_journal.json
Add doubts.embedding column of type vector(1536) and include the generated Drizzle snapshot and journal entry for migration 0009.

Home page cleanup

Layer / File(s) Summary
Remove onboarding tour
src/app/page.tsx
Removed onboarding-tour state, mount effect, completion/skip handlers, and OnboardingTour rendering from Home.`

Config fallback

Layer / File(s) Summary
Database URL fallback change
src/configs/database-url.ts
When DATABASE_URL is missing, warn and return a dummy PostgreSQL URL instead of throwing in test env.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

frontend, backend, database, quality:clean

Suggested reviewers

  • knoxiboy
🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Multiple significant changes appear unrelated to the stated PR objective (share menu for doubts): database embedding migration, API reply authorization refactoring, home page onboarding removal, and database URL config changes. Move database schema changes, authorization refactoring, onboarding removal, and config modifications to separate PRs focused on their respective objectives to maintain scope clarity.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The PR primarily addresses share menu functionality as required by #610, but includes additional changes (database embedding column, API authorization refactoring, onboarding removal) that lack corresponding issue references. Clarify whether all changes—especially the embedding column addition, reply authorization tightening, and onboarding removal—are related to issue #610 or should be scoped separately into distinct PRs.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add share menu for doubts' directly and clearly describes the main change—adding a dropdown share menu to doubt cards as specified in issue #610.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot requested a review from knoxiboy June 8, 2026 13:08
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

@coderabbitai review

@codeant-ai codeant-ai Bot added the size:M This PR changes 30-99 lines, ignoring generated files label Jun 8, 2026
@github-actions github-actions Bot removed the size:M This PR changes 30-99 lines, ignoring generated files label Jun 8, 2026
Comment on lines +327 to +330
<button
className="flex items-center justify-center p-3 rounded-2xl bg-white/5 hover:bg-white/10 text-slate-400 hover:text-white border border-white/5 transition-all outline-none"
title="Share doubt"
aria-label="Share doubt"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 Design Review — HIGH

Do you think removing the trigger's default outline (outline-none) without adding a replacement focus style might make keyboard focus hard to perceive for the new Share menu button?

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a **Design Review** comment — a question about the UX/design of frontend code. It is intentionally framed as a question, not a prescription. The author may agree or disagree.

**Path:** src/components/DoubtCard.tsx
**Line:** 327:330
**Comment:**
	*HIGH: Do you think removing the trigger's default outline (`outline-none`) without adding a replacement focus style might make keyboard focus hard to perceive for the new Share menu button?

- If you agree with the proposal, apply a small, localized change (swap a color token, bump a font size, adjust spacing, add an aria-label, etc.).
- If you disagree, or the answer depends on a design decision a human should make, explain your reasoning and ask the user how to proceed.
Do NOT refactor surrounding components or apply other design changes that weren't asked about.

Comment thread src/components/DoubtCard.tsx Outdated
Comment on lines 176 to 177
navigator.clipboard.writeText(getShareUrl());
toast.success("Link copied!");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The copy action shows success immediately without waiting for clipboard.writeText to resolve, so users can see "Link copied!" even when permission is denied or clipboard access fails. Make this handler async, await the write call, and show an error toast on rejection. [logic error]

Severity Level: Major ⚠️
- ⚠️ Share menu misreports copy success on clipboard failure.
- ⚠️ Users may rely on uncopied doubt links.
Steps of Reproduction ✅
1. Navigate to any page that renders `DoubtCard`, for example `/bookmarks` implemented in
`src/app/bookmarks/page.tsx` lines 116-120, where each `bookmarks.map` item renders
`<DoubtCard key={doubt.id} doubt={doubt} onUpdate={fetchBookmarks} role={appUser?.role}
/>`.

2. In the UI, on any doubt card, click the share icon button defined in
`src/components/DoubtCard.tsx` lines 66-73 (footer actions share button inside the
`DropdownMenuTrigger`), which opens the share dropdown.

3. Click the "Copy Link" menu item wired to `onClick={handleShare}` in
`src/components/DoubtCard.tsx` lines 77-80, which invokes `handleShare`.

4. When `handleShare` executes (defined at `src/components/DoubtCard.tsx` lines 175-178),
it calls `navigator.clipboard.writeText(getShareUrl());` (line 176) but does not `await`
or wrap it in `try/catch`, and immediately calls `toast.success("Link copied!");` (line
177); in any environment where `navigator.clipboard.writeText` rejects or throws (e.g.,
clipboard permission denied or unsupported), the user still sees a success toast even
though the link was never copied, and the error is unhandled.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/components/DoubtCard.tsx
**Line:** 176:177
**Comment:**
	*Logic Error: The copy action shows success immediately without waiting for `clipboard.writeText` to resolve, so users can see "Link copied!" even when permission is denied or clipboard access fails. Make this handler async, await the write call, and show an error toast on rejection.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment thread src/components/DoubtCard.tsx Outdated

const handleWhatsAppShare = () => {
const text = encodeURIComponent(`Check out this doubt on DoubtDesk:\n\n${getShareUrl()}`);
window.open(`https://api.whatsapp.com/send?text=${text}`, "_blank");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Opening an external URL with window.open(..., "_blank") without noopener/noreferrer lets the new tab access window.opener, which enables reverse-tabnabbing attacks. Open the window with noopener,noreferrer (or equivalent safe link behavior) to prevent opener access. [security]

Severity Level: Major ⚠️
- ❌ Malicious WhatsApp page can redirect DoubtDesk tab.
- ⚠️ Users sharing doubts risk phishing via tab takeover.
Steps of Reproduction ✅
1. Navigate to any page that renders `DoubtCard`, such as `/public-rooms` implemented in
`src/app/public-rooms/page.tsx` lines 7-11, where `filteredDoubts.map` renders `<DoubtCard
key={`${doubt.id}-${index}`} doubt={doubt} onUpdate={() => mutate()} />`.

2. On a doubt card, click the share icon button defined in `src/components/DoubtCard.tsx`
lines 66-73 to open the share dropdown.

3. Click the "Share on WhatsApp" menu item defined in `src/components/DoubtCard.tsx` lines
81-84, which has `onClick={handleWhatsAppShare}`.

4. The `handleWhatsAppShare` function in `src/components/DoubtCard.tsx` lines 180-183
builds a `text` query string and calls
`window.open(\`https://api.whatsapp.com/send?text=${text}\`, "_blank");` (line 182)
without specifying `noopener`/`noreferrer` or nulling `window.opener`; this means the
newly opened WhatsApp page keeps a reference to `window.opener` and can programmatically
navigate or manipulate the original DoubtDesk tab (reverse-tabnabbing) if that external
page or its scripts are compromised.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/components/DoubtCard.tsx
**Line:** 182:182
**Comment:**
	*Security: Opening an external URL with `window.open(..., "_blank")` without `noopener`/`noreferrer` lets the new tab access `window.opener`, which enables reverse-tabnabbing attacks. Open the window with `noopener,noreferrer` (or equivalent safe link behavior) to prevent opener access.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment thread src/components/DoubtCard.tsx Outdated
const handleTelegramShare = () => {
const url = encodeURIComponent(getShareUrl());
const text = encodeURIComponent(`Check out this doubt on DoubtDesk:`);
window.open(`https://t.me/share/url?url=${url}&text=${text}`, "_blank");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This _blank popup also omits noopener/noreferrer, so the destination page can control the opener tab and potentially redirect it. Use a safe open pattern that disables opener access. [security]

Severity Level: Major ⚠️
- ❌ Malicious Telegram share page can hijack origin tab.
- ⚠️ Doubt sharing flow exposes reverse-tabnabbing risk.
Steps of Reproduction ✅
1. Navigate to any page that renders `DoubtCard`, such as the doubt permalink page
implemented in `src/app/doubts/[id]/DoubtPermalinkClient.tsx` lines 10-17, where a single
`<DoubtCard>` is rendered with `doubt={doubt}`.

2. On the doubt card, click the share icon button (footer share button in
`src/components/DoubtCard.tsx` lines 66-73) to open the share dropdown.

3. Click the "Share on Telegram" menu item defined in `src/components/DoubtCard.tsx` lines
85-88, which is wired with `onClick={handleTelegramShare}`.

4. The `handleTelegramShare` function in `src/components/DoubtCard.tsx` lines 185-188
builds `url` and `text` and then calls
`window.open(\`https://t.me/share/url?url=${url}&text=${text}\`, "_blank");` (line 188)
without a `noopener,noreferrer` window feature or clearing `window.opener`, so the opened
Telegram share page retains access to the originating DoubtDesk tab and can perform
reverse-tabnabbing by changing `window.opener.location` if that external page or embedded
scripts are compromised.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/components/DoubtCard.tsx
**Line:** 188:188
**Comment:**
	*Security: This `_blank` popup also omits `noopener`/`noreferrer`, so the destination page can control the opener tab and potentially redirect it. Use a safe open pattern that disables opener access.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codeant-ai

codeant-ai Bot commented Jun 8, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/DoubtCard.tsx`:
- Line 177: The share UI strings (e.g., "Link copied!" used in toast.success and
the hardcoded share labels/messages in the JSX) must be moved into constants to
avoid inline UI copy; add descriptive constants such as SHARE_COPY_SUCCESS and
SHARE_LABEL_* at the top of the DoubtCard.tsx (or import from a shared constants
file) and replace all hardcoded occurrences in the DoubtCard component (handlers
like the copy/share function and the JSX render blocks around lines referenced)
with those constants so the toast.success call and the share button/label text
read from the new constants.
- Around line 182-183: The external-share calls in the DoubtCard component (the
handlers that call window.open(..., "_blank"), e.g., the WhatsApp/Twitter share
handlers) leave window.opener exposed; replace those window.open calls with
creating a temporary anchor element, set its href to the share URL, set
target="_blank" and rel="noopener noreferrer", append/click/remove it (or
alternatively capture the return value from window.open and set newWindow.opener
= null and ensure referrer is suppressed), so that shared windows include
noopener and noreferrer protections.
- Around line 175-178: Make handleShare async and await
navigator.clipboard.writeText(getShareUrl()) inside a try/catch: on success call
toast.success("Link copied!"), on failure call toast.error(...) with the caught
error message and implement a simple fallback (e.g., select/copy from a hidden
input or notify the user to copy manually). Update references to handleShare
where used if necessary and ensure getShareUrl and toast are still referenced
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a8cd8a75-6caa-4078-8eca-8f35b8f8d3f0

📥 Commits

Reviewing files that changed from the base of the PR and between 82aab9b and 2076abb.

📒 Files selected for processing (1)
  • src/components/DoubtCard.tsx

Comment thread src/components/DoubtCard.tsx Outdated
Comment thread src/components/DoubtCard.tsx Outdated
Comment thread src/components/DoubtCard.tsx Outdated
@bhumishah2411

Copy link
Copy Markdown
Contributor Author

hi @knoxiboy, i have made pr for this issue. Please review it and merge whenever possible. Thankyou!!

@knoxiboy

Copy link
Copy Markdown
Owner

Hi! This PR currently has merge conflicts with the main branch. Please rebase/merge main and resolve conflicts so we can review and merge it. Thank you!

@codeant-ai

codeant-ai Bot commented Jun 11, 2026

Copy link
Copy Markdown

CodeAnt AI is running Incremental review

@codeant-ai codeant-ai Bot added the size:M This PR changes 30-99 lines, ignoring generated files label Jun 11, 2026
@github-actions github-actions Bot removed the size:M This PR changes 30-99 lines, ignoring generated files label Jun 11, 2026
@codeant-ai

codeant-ai Bot commented Jun 11, 2026

Copy link
Copy Markdown

CodeAnt AI Incremental review completed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/doubts/route.ts`:
- Around line 155-156: Duplicate declaration of replyCountSql causes a compile
error; remove the obsolete declaration. Delete the older line that redeclares
const replyCountSql using count(*)::int (the second declaration) so only one
replyCountSql remains (the version using coalesce((SELECT count(*) FROM
${repliesTable} WHERE ${repliesTable.doubtId} = ${doubtsTable.id}),
0).mapWith(Number)). Ensure no other references rely on the removed variant.
- Around line 13-15: The file has a duplicate import from "drizzle-orm" causing
duplicate identifier errors; remove the redundant import line that re-imports
and, eq, inArray, isNull, or, not, sql, SQL, ilike, desc, getTableColumns (the
older import) so only the consolidated import that includes count remains;
update the import block near the top of src/app/api/doubts/route.ts to keep the
single import containing count and drop the duplicate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 006be762-95af-40e9-a1aa-c6f581c315a7

📥 Commits

Reviewing files that changed from the base of the PR and between 958346c and 5696496.

📒 Files selected for processing (3)
  • src/app/api/doubts/route.ts
  • src/app/api/replies/route.ts
  • src/components/DoubtCard.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/api/replies/route.ts
  • src/components/DoubtCard.tsx

Comment thread src/app/api/doubts/route.ts Outdated
Comment thread src/app/api/doubts/route.ts Outdated
@knoxiboy

Copy link
Copy Markdown
Owner

Hi @bhumishah2411! The CI checks are currently failing due to a compilation syntax error in \src/app/api/replies/route.ts\ where the \membership\ variable is declared twice (around lines 61 and 74).

Specifically, you have:
\\ ypescript
if (doubt.type === 'teacher') {
let membership;
if (email && doubt.classroomId) {
...
membership = res[0];
}
const [membership] = await db // <--- Duplicate declaration error
...
\\

Please resolve this duplicate declaration to fix the build and test suite. Thank you!

@codeant-ai

codeant-ai Bot commented Jun 12, 2026

Copy link
Copy Markdown

CodeAnt AI is running Incremental review


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added the size:M This PR changes 30-99 lines, ignoring generated files label Jun 12, 2026
@github-actions github-actions Bot removed the size:M This PR changes 30-99 lines, ignoring generated files label Jun 12, 2026
@codeant-ai

codeant-ai Bot commented Jun 12, 2026

Copy link
Copy Markdown

CodeAnt AI Incremental review completed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/api/replies/route.ts (1)

153-166: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce teacher authorization before using doubt.classroomId in POST.

Line 154 performs a membership query with doubt.classroomId! before validating it, while Lines 164-168 only enforce teacher-role checks when doubt.classroomId is truthy. This leaves a path where teacher-doubt replies can bypass role enforcement.

🔒 Suggested fix
 if (doubt.type === "teacher") {
-    const [teacherMembership] = await db
-    .select()
-    .from(membershipsTable)
-    .where(
-        and(
-            eq(membershipsTable.userEmail, email),
-            eq(membershipsTable.classroomId, doubt.classroomId!)
-        )
-    );
-
-    if (doubt.classroomId) {
-        if (!teacherMembership || !canTeach(teacherMembership.role)) {
-            return errorResponse("Insufficient permissions to reply to this doubt", 403);
-        }
-    }
+    if (!doubt.classroomId) {
+        return errorResponse("Insufficient permissions to reply to this doubt", 403);
+    }
+
+    const [teacherMembership] = await db
+        .select()
+        .from(membershipsTable)
+        .where(
+            and(
+                eq(membershipsTable.userEmail, email),
+                eq(membershipsTable.classroomId, doubt.classroomId)
+            )
+        );
+
+    if (!teacherMembership || !canTeach(teacherMembership.role)) {
+        return errorResponse("Insufficient permissions to reply to this doubt", 403);
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/replies/route.ts` around lines 153 - 166, The handler queries
membershipsTable using doubt.classroomId! before ensuring classroomId exists and
authorizing the user; change the flow in the replies route so that inside the
doubt.type === "teacher" branch you first validate doubt.classroomId (return an
error if missing), then perform the DB select for teacherMembership and enforce
canTeach(teacherMembership.role) (return 403 if not allowed), and remove the
non-null assertion by using the validated doubt.classroomId when calling the
query; use the existing symbols doubt, teacherMembership, membershipsTable,
canTeach, and errorResponse to locate and update the logic.
🧹 Nitpick comments (1)
src/app/api/replies/route.ts (1)

61-73: ⚡ Quick win

Avoid the duplicate membership query in GET teacher gating.

Line 63 re-queries membershipsTable for the same (email, classroomId) pair already queried at Line 50, adding an unnecessary DB round-trip on teacher-doubt fetches.

♻️ Suggested refactor
-        if (doubt.classroomId && email) {
-            const [membership] = await db.select().from(membershipsTable).where(
+        let membership;
+        if (doubt.classroomId && email) {
+            const [resolvedMembership] = await db.select().from(membershipsTable).where(
                 and(eq(membershipsTable.userEmail, email), eq(membershipsTable.classroomId, doubt.classroomId))
             );
-            if (!membership) {
+            membership = resolvedMembership;
+            if (!membership) {
                 return errorResponse("Access denied to this classroom's doubt replies", 403);
             }
         } else if (doubt.classroomId && !email) {
             return errorResponse("Access denied to this classroom's doubt replies", 403);
         }
 
         if (doubt.type === 'teacher') {
-            let membership;
-            if (email && doubt.classroomId) {
-                const res = await db
-                .select()
-                .from(membershipsTable)
-                .where(
-                    and(
-                        eq(membershipsTable.userEmail, email),
-                        eq(membershipsTable.classroomId, doubt.classroomId)
-                    )
-                );
-                membership = res[0];
-            }
-
             const isTeacher = membership ? canTeach(membership.role) : false;
             const isOwner = email ? doubt.userEmail === email : false;
             if (!isTeacher && !isOwner) {
                 return errorResponse("Access denied", 403);
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/replies/route.ts` around lines 61 - 73, Duplicate DB lookup:
remove the second membershipsTable query and reuse the already-fetched
membership for the same (email, doubt.classroomId) pair; specifically, in the
GET teacher gating code that currently runs
db.select().from(membershipsTable).where(and(eq(membershipsTable.userEmail,
email), eq(membershipsTable.classroomId, doubt.classroomId))) assign or
reference the previously obtained membership variable/result instead of
re-querying; ensure the logic still handles the case where the first query
returned no rows (membership may be undefined) and remove the redundant
db.select() call to eliminate the extra round-trip.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/app/api/replies/route.ts`:
- Around line 153-166: The handler queries membershipsTable using
doubt.classroomId! before ensuring classroomId exists and authorizing the user;
change the flow in the replies route so that inside the doubt.type === "teacher"
branch you first validate doubt.classroomId (return an error if missing), then
perform the DB select for teacherMembership and enforce
canTeach(teacherMembership.role) (return 403 if not allowed), and remove the
non-null assertion by using the validated doubt.classroomId when calling the
query; use the existing symbols doubt, teacherMembership, membershipsTable,
canTeach, and errorResponse to locate and update the logic.

---

Nitpick comments:
In `@src/app/api/replies/route.ts`:
- Around line 61-73: Duplicate DB lookup: remove the second membershipsTable
query and reuse the already-fetched membership for the same (email,
doubt.classroomId) pair; specifically, in the GET teacher gating code that
currently runs
db.select().from(membershipsTable).where(and(eq(membershipsTable.userEmail,
email), eq(membershipsTable.classroomId, doubt.classroomId))) assign or
reference the previously obtained membership variable/result instead of
re-querying; ensure the logic still handles the case where the first query
returned no rows (membership may be undefined) and remove the redundant
db.select() call to eliminate the extra round-trip.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 89e2e226-7480-4f1b-ad4b-bf6ed623264f

📥 Commits

Reviewing files that changed from the base of the PR and between 5696496 and 939ef38.

📒 Files selected for processing (3)
  • src/app/api/doubts/route.ts
  • src/app/api/replies/route.ts
  • src/components/DoubtCard.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/api/doubts/route.ts
  • src/components/DoubtCard.tsx

@knoxiboy

Copy link
Copy Markdown
Owner

Hi @bhumishah2411! Thanks for submitting this PR. Here is a detailed review of your changes:

🤖 Bot Review Feedback to Resolve

CodeRabbit and CodeAnt have highlighted some important issues that need to be addressed. Please prioritize fixing these:

  • File src/components/DoubtCard.tsx (Line 373):
    🎨 Design Review — HIGH

Do you think removing the trigger's default outline (outline-none) without adding a replacement focus style might make keyboard focus hard to perceive for the new Share menu button?

[Fix in Cursor](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=0ec49845d95a45...

  • File src/components/DoubtCard.tsx (Line 177):
    Suggestion: The copy action shows success immediately without waiting for clipboard.writeText to resolve, so users can see "Link copied!" even when permission is denied or clipboard access fails. Make this handler async, await the write call, and show an error toast on rejection. [logic error]...

  • File src/components/DoubtCard.tsx (Line 182):
    Suggestion: Opening an external URL with window.open(..., "_blank") without noopener/noreferrer lets the new tab access window.opener, which enables reverse-tabnabbing attacks. Open the window with noopener,noreferrer (or equivalent safe link behavior) to prevent opener access. [securi...

  • File src/components/DoubtCard.tsx (Line 188):
    Suggestion: This _blank popup also omits noopener/noreferrer, so the destination page can control the opener tab and potentially redirect it. Use a safe open pattern that disables opener access. [security]

Severity Level: Major ⚠️
- ❌ Malicious ...

- **File `src/components/DoubtCard.tsx` (Line 178)**:
  _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**Handle clipboard failures and show an error toast path.**

On Line 176, `navigator.clipboard.writeText(...)` is not awaited and has no failure handling, so blocked permissions/insecure contexts silently fail.

<details>
<summary>Suggested patch</su...

### ⚙️ CI/CD Pipeline
Your PR currently has some failing checks:
- `Vercel`

**Note:** We have recently fixed database configuration and `ask-ai` syntax/compilation issues on the `main` branch. Please pull/merge the latest `main` branch into your branch to get these fixes, which should resolve common test and Vercel build failures.

---
*This is an automated review assessment. Please address the feedback above to move your PR forward.*

@knoxiboy

Copy link
Copy Markdown
Owner

⚠️ Merge Conflicts Detected

Hi @bhumishah2411, your branch has merge conflicts with the base branch. Please resolve them by running the following commands in your terminal:

git checkout DoubtShareButton
git pull origin main
# Resolve the conflicts in your files, then stage them
git add .
git commit -m "chore: resolve merge conflicts with main"
git push origin DoubtShareButton

If you do not have write permissions to push directly, please push to your fork branch instead.

@knoxiboy knoxiboy added level:advanced Advanced level task merge-conflict PR has merge conflicts that need resolution and removed level:intermediate Intermediate level task labels Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc'26 GSSoC program issue level:advanced Advanced level task merge-conflict PR has merge conflicts that need resolution review-needed size/xxl type:bug Bug fix type:feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add Share Menu for Doubts (WhatsApp, Telegram, and Copy Link Support)

2 participants