Skip to content

Fix/study timer completion feedback#998

Open
OmanshiRaj wants to merge 5 commits into
durdana3105:mainfrom
OmanshiRaj:fix/study-timer-completion-feedback
Open

Fix/study timer completion feedback#998
OmanshiRaj wants to merge 5 commits into
durdana3105:mainfrom
OmanshiRaj:fix/study-timer-completion-feedback

Conversation

@OmanshiRaj

@OmanshiRaj OmanshiRaj commented Jun 15, 2026

Copy link
Copy Markdown

Closes #924

Added else if (isRunning && seconds === 0) branch to stop the timer and fire a toast.success when countdown completes
Merged separate Start/Pause buttons into a single toggle button (handleToggle) — prevents duplicate setInterval calls if Start was clicked while already running
Added a progress bar that fills as time elapses (blue → red in last 60s → green on completion)
Timer display and border flash red during the last 60 seconds
Toggle button is disabled at seconds === 0 to prevent interaction after completion
handleReset now also clears hasCompleted

Summary by CodeRabbit

Release Notes

  • New Features
    • Added profile navigation from peer cards
    • Added copy and clear buttons for code execution output
    • Timer now displays completion status with enhanced visual feedback
    • Added 500-character limit for doubt posts with live character counter
    • Improved upvote synchronization and error handling

@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

@OmanshiRaj is attempting to deploy a commit to the durdana3105's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Four unrelated UI improvements: PeerCard adds React Router programmatic navigation to the "View Profile" button; StudyTimer adds completion detection with a toast, a progress bar, warning styling, and a merged Start/Pause toggle; LiveCodeRunner adds output copy and code clear actions with toast feedback; AnonymousDoubts adds a 500-character limit with a live counter and rewrites upvote to use optimistic Supabase persistence with rollback.

Changes

UI Feature Additions

Layer / File(s) Summary
PeerCard profile navigation
src/components/PeerCard.tsx
Adds useNavigate import and hook initialization; wires the "View Profile" button onClick to navigate to /profile/${peer.id}.
StudyTimer completion detection and progress UI
src/components/studyroom/StudyTimer.tsx
Imports sonner toast and adds hasCompleted state; useEffect now fires a success toast and stops the timer at zero; derives TOTAL/progress/warning values; consolidates Start/Pause into a single handleToggle button (disabled at zero); updates border, text, and progress-bar styling for warning and completed states.
LiveCodeRunner copy output and clear code actions
src/components/studyroom/LiveCodeRunner.tsx
Adds Clipboard and Trash2 imports; introduces handleCopyOutput (clipboard write with success/error toasts) and handleClearCode (clears code and output); adds a disabled-while-running Clear button in the dialog header and a conditional Copy button in the Output panel header.
AnonymousDoubts character limit and optimistic upvote
src/pages/AnonymousDoubts.tsx
Adds MAX_CHARS = 500; addDoubt rejects over-limit submissions with an error toast; upvote is rewritten to apply an optimistic increment, persist via Supabase, and roll back on error; a live character counter with conditional coloring is added to the form; submit button disabled condition extended to cover over-limit state.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #926PeerCard.tsx changes directly implement the fix described: adding useNavigate, initializing navigate, and wiring the "View Profile" button to /profile/${peer.id}.
  • #927AnonymousDoubts.tsx changes directly implement the feature requested: MAX_CHARS = 500, live character counter with conditional coloring, and error toast on over-limit submit.
  • #928LiveCodeRunner.tsx changes directly implement the requested features: handleCopyOutput and handleClearCode, Clipboard and Trash2 icons, and wired Clear/Copy buttons with toast feedback.
  • #924StudyTimer.tsx changes directly implement all fixes described: completion toast at zero, merged Start/Pause toggle button, progress bar, and last-60-seconds warning styling.

Suggested labels

type:bug

🐇 Four little fixes hopped into view,
A timer that toasts when the countdown is through,
Copy and clear for the code that you write,
A doubt with a limit, an upvote done right,
And "View Profile" now leaps to the route in a click —
This rabbit approves every last coding trick! 🌟

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes changes to PeerCard, LiveCodeRunner, and AnonymousDoubts components that are unrelated to the timer completion feedback objectives specified in issue #924. Remove out-of-scope changes to PeerCard, LiveCodeRunner, and AnonymousDoubts components; focus only on StudyTimer.tsx modifications to stay aligned with issue #924.
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.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix/study timer completion feedback' directly addresses the main objective from issue #924 by highlighting the completion feedback improvement.
Linked Issues check ✅ Passed The PR implementation covers all primary objectives from #924: timer completion detection with toast notification, button consolidation to prevent duplicate intervals, progress bar visualization, warning state during final 60 seconds, and completion state management.

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

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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.4.16)
src/components/studyroom/StudyTimer.tsx

File contains syntax errors that prevent linting: Line 77: expected } but instead the file ends


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

@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: 4

🤖 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/PeerCard.tsx`:
- Line 183: The onClick handler in the PeerCard component uses
navigate(`/profile/${peer.id}`) which attempts to navigate to a route that does
not exist in the current router configuration. The router only recognizes the
/profile path (without a dynamic ID segment), and Profile.tsx currently renders
the authenticated session user's profile rather than accepting a user ID from
the URL. Change the navigate call from the dynamic route pattern to simply
navigate to /profile, or if peer-specific profile viewing is needed, coordinate
with the router and Profile.tsx page to support the dynamic route pattern by
updating both the route definition and the Profile component to accept and use
the URL parameter.

In `@src/components/studyroom/StudyTimer.tsx`:
- Line 77: The StudyTimer component is missing its closing brace at the end of
the file. After the final `);` statement at line 77, add a closing brace `}` to
properly close the StudyTimer function definition. This will resolve the parse
error that prevents the component from compiling.

In `@src/pages/AnonymousDoubts.tsx`:
- Around line 96-105: The character counter display is present but the actual
textarea or input field that captures user input is missing from this div
element with className="relative". Add a textarea control inside this div
(before the character counter span) that accepts user input, has an onChange
handler to update the `text` state variable, includes appropriate placeholder
text and styling to be visible and usable, and respects the MAX_CHARS limit to
prevent exceeding the character boundary. This will allow users to actually
enter doubt content and make the "Post Doubt" button reachable. The same
textarea control needs to be added at the other affected location as well.
- Around line 75-80: Replace the non-atomic read-modify-write pattern in the
upvote handler with an atomic database operation. Instead of reading the current
upvotes value from client state with doubts.find((d) => d.id === id)!.upvotes +
1 and sending that absolute value to the database, create a Supabase RPC
function (or use the rpc() interface) that performs the increment atomically at
the database level using SQL like update doubts set upvotes = upvotes + 1 where
id = id. This ensures concurrent upvotes do not overwrite each other and
eliminates the brittle non-null assertion. Follow the same pattern already
implemented for resource votes in your codebase.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6eee2936-9e78-4bff-b115-7fc5ed6993cc

📥 Commits

Reviewing files that changed from the base of the PR and between 7200d81 and 5e46ff7.

📒 Files selected for processing (4)
  • src/components/PeerCard.tsx
  • src/components/studyroom/LiveCodeRunner.tsx
  • src/components/studyroom/StudyTimer.tsx
  • src/pages/AnonymousDoubts.tsx

<Button
size="sm"
variant="outline"
onClick={() => navigate(`/profile/${peer.id}`)}

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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

View Profile navigates to a path that is not routable today.

navigate(\/profile/${peer.id}`) does not match the current router contract (/profile), and src/pages/Profile.tsx` currently reads the authenticated session user, not a URL id. This makes the button behavior incorrect at runtime.

🤖 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/components/PeerCard.tsx` at line 183, The onClick handler in the PeerCard
component uses navigate(`/profile/${peer.id}`) which attempts to navigate to a
route that does not exist in the current router configuration. The router only
recognizes the /profile path (without a dynamic ID segment), and Profile.tsx
currently renders the authenticated session user's profile rather than accepting
a user ID from the URL. Change the navigate call from the dynamic route pattern
to simply navigate to /profile, or if peer-specific profile viewing is needed,
coordinate with the router and Profile.tsx page to support the dynamic route
pattern by updating both the route definition and the Profile component to
accept and use the URL parameter.

</div>
);
} No newline at end of file
); No newline at end of file

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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing closing brace breaks component parsing.

The component ends after ); without a final } for StudyTimer, which causes a compile-time parse failure.

Proposed fix
   return (
     <div className={`bg-slate-900 border rounded-xl p-4 transition-colors ${isWarning ? "border-red-500" : "border-slate-700"}`}>
       ...
     </div>
   );
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
);
);
}
🧰 Tools
🪛 Biome (2.4.16)

[error] 77-77: expected } but instead the file ends

(parse)

🤖 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/components/studyroom/StudyTimer.tsx` at line 77, The StudyTimer component
is missing its closing brace at the end of the file. After the final `);`
statement at line 77, add a closing brace `}` to properly close the StudyTimer
function definition. This will resolve the parse error that prevents the
component from compiling.

Source: Linters/SAST tools

Comment on lines +75 to +80
setDoubts((prev) => prev.map((d) => (d.id === id ? { ...d, upvotes: d.upvotes + 1 } : d)));

const { error } = await (supabase as any)
.from("doubts")
.update({ upvotes: doubts.find((d) => d.id === id)!.upvotes + 1 })
.eq("id", id);

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for Supabase SQL functions/migrations that implement atomic upvote increment.
fd -HI -t f --extension sql
rg -n -i "create\\s+function|rpc|upvote|increment|doubts"

# Look for existing client-side RPC usage patterns that could be reused.
rg -n --type ts --type tsx "\\.rpc\\("

# Locate all doubts upvote writes for consistency review.
rg -n --type ts --type tsx "from\\(\"doubts\"\\).*update\\(|upvotes\\s*:"

Repository: durdana3105/peer-learning

Length of output: 31858


🏁 Script executed:

cat -n supabase/migrations/20260608000001_anonymous_doubts.sql

Repository: durdana3105/peer-learning

Length of output: 1176


🏁 Script executed:

sed -n '71,90p' src/pages/AnonymousDoubts.tsx

Repository: durdana3105/peer-learning

Length of output: 850


🏁 Script executed:

rg -n "upvote.*doubts|doubts.*upvote" --type ts --type tsx -A 5 -B 5

Repository: durdana3105/peer-learning

Length of output: 97


🏁 Script executed:

wc -l supabase/migrations/20260608000001_anonymous_doubts.sql

Repository: durdana3105/peer-learning

Length of output: 128


🏁 Script executed:

cat supabase/migrations/20260608000001_anonymous_doubts.sql

Repository: durdana3105/peer-learning

Length of output: 973


🏁 Script executed:

rg -n "upvote.*rpc|rpc.*upvote" --type ts --type tsx --type sql

Repository: durdana3105/peer-learning

Length of output: 97


🏁 Script executed:

rg -n "doubts.*update|update.*doubts" supabase/migrations/ --type sql -A 3 -B 1

Repository: durdana3105/peer-learning

Length of output: 51


🏁 Script executed:

rg -n "update.*doubt|doubt.*update" supabase/migrations/ -A 2 -B 1

Repository: durdana3105/peer-learning

Length of output: 51


🏁 Script executed:

rg -n "create.*function.*upvote|CREATE FUNCTION.*upvote" supabase/migrations/ -i

Repository: durdana3105/peer-learning

Length of output: 51


🏁 Script executed:

rg -n "policy.*update.*doubt|policy.*doubt.*update" supabase/migrations/ -i -A 3

Repository: durdana3105/peer-learning

Length of output: 51


🏁 Script executed:

# Check if there are other doubt-related migrations after the initial one
fd -t f --extension sql supabase/migrations/ | grep -i doubt

Repository: durdana3105/peer-learning

Length of output: 455


🏁 Script executed:

rg -n "UPDATE|policy" supabase/migrations/20260608000001_anonymous_doubts.sql

Repository: durdana3105/peer-learning

Length of output: 168


🏁 Script executed:

cat -n src/pages/AnonymousDoubts.tsx | head -100

Repository: durdana3105/peer-learning

Length of output: 3853


🏁 Script executed:

# Check if there's a bypass or special RLS that allows updates
rg -n "on doubts for update" supabase/migrations/ --type sql -i

Repository: durdana3105/peer-learning

Length of output: 51


🏁 Script executed:

# Compare with resource voting which was mentioned as using atomic SQL
cat -n supabase/migrations/20260601000000_resource_votes_and_saves.sql | grep -A 10 -B 5 "upvotes"

Repository: durdana3105/peer-learning

Length of output: 1906


Use an atomic database operation to increment upvotes, not client-derived absolute values.

The current upvote implementation reads the upvotes value from client state (doubts.find(...).upvotes + 1) and writes it back to the database. This read-modify-write pattern is non-atomic and vulnerable to concurrent vote loss: when multiple users upvote simultaneously, later writes overwrite earlier increments with stale values. Additionally, the non-null assertion is brittle if the item disappears from local state.

Implement atomic upvote increments using SQL-level operations, following the pattern used by resource votes: create an RPC function with a trigger that performs update doubts set upvotes = upvotes + 1 where id = ? as a single atomic database operation, or use Supabase's rpc() interface to delegate the increment to the database.

🤖 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/pages/AnonymousDoubts.tsx` around lines 75 - 80, Replace the non-atomic
read-modify-write pattern in the upvote handler with an atomic database
operation. Instead of reading the current upvotes value from client state with
doubts.find((d) => d.id === id)!.upvotes + 1 and sending that absolute value to
the database, create a Supabase RPC function (or use the rpc() interface) that
performs the increment atomically at the database level using SQL like update
doubts set upvotes = upvotes + 1 where id = id. This ensures concurrent upvotes
do not overwrite each other and eliminates the brittle non-null assertion.
Follow the same pattern already implemented for resource votes in your codebase.

Comment on lines +96 to +105
<div className="relative">

<span className={`absolute bottom-2 right-2 text-xs select-none ${
text.length > MAX_CHARS ? "text-red-500 font-semibold"
: text.length >= MAX_CHARS * 0.9 ? "text-amber-500"
: "text-slate-400"}`}
>
{text.length}/{MAX_CHARS}
</span>
</div>

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Doubt content input appears removed, which blocks posting.

The form now shows a character counter but no visible control updates text; with disabled={... || !text.trim() ...} the “Post Doubt” action becomes effectively unreachable.

Also applies to: 122-122

🤖 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/pages/AnonymousDoubts.tsx` around lines 96 - 105, The character counter
display is present but the actual textarea or input field that captures user
input is missing from this div element with className="relative". Add a textarea
control inside this div (before the character counter span) that accepts user
input, has an onChange handler to update the `text` state variable, includes
appropriate placeholder text and styling to be visible and usable, and respects
the MAX_CHARS limit to prevent exceeding the character boundary. This will allow
users to actually enter doubt content and make the "Post Doubt" button
reachable. The same textarea control needs to be added at the other affected
location as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: StudyTimer silently stops at zero with no feedback`

1 participant