feat: Add toast notification system for bounty actions (Closes #825)#1254
feat: Add toast notification system for bounty actions (Closes #825)#1254sepulchralvoid666 wants to merge 2 commits into
Conversation
- New Toast.tsx: context-based toast system with success/error/warning/info variants - Auto-dismiss after 5 seconds, manual close button, slide-in animation from top-right - Multiple toasts stack properly with AnimatePresence - Accessible with role="alert" on each toast - ToastProvider wired into main.tsx wrapping App - SubmissionForm: toast on bounty submission success/failure - BountyCreateWizard: toast on bounty creation and publish success/failure Closes SolFoundry#825 **Wallet:** 2JHa4QQWNV7AGSGsVeX1cwjZtFcmTTomb1TrJmQthoh3
📝 WalkthroughWalkthroughThis PR implements a complete toast notification system for the app. It creates a React context-based Toast component with auto-dismiss and animations, integrates it into the app root, and wires toast success/error notifications into the bounty creation wizard and submission form flows. ChangesToast Notification System
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment Warning |
There was a problem hiding this comment.
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 `@frontend/src/components/bounty/BountyCreateWizard.tsx`:
- Around line 442-445: verifyEscrowDeposit is awaited but its return value is
ignored, so even non-verified escrow responses will mark the bounty published;
update the success flow in the async block that calls verifyEscrowDeposit({
bounty_id: state.bounty_id, tx_signature: state.tx_signature }) to capture its
result, check the result.verified boolean, and only call setSuccess(true) and
addToast(...) when verified is true; if verified is false (or response indicates
failure) then do not set success, instead call addToast with an error variant
and an explanatory message (and optionally set an error state) so the UI
reflects the failed verification.
In `@frontend/src/components/ui/Toast.tsx`:
- Around line 37-41: The auto-dismiss timers started for toasts are never
cleared, so update the implementation to track timeouts in a ref (replace or
supplement counter useRef with a timersRef Map or Record keyed by toast id),
store each timeout id when scheduling, and call clearTimeout for that id inside
removeToast (and remove it from timersRef) so manual dismissal cancels the
timer; also add a cleanup in the provider's useEffect/unmount to iterate
timersRef and clearTimeout all remaining timers. Ensure you update places that
schedule timers to write into timersRef and that removeToast and the unmount
cleanup both clear and delete entries.
🪄 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
Run ID: 99b14938-11b1-46c2-9d21-376c6cc5abf6
📒 Files selected for processing (4)
frontend/src/components/bounty/BountyCreateWizard.tsxfrontend/src/components/bounty/SubmissionForm.tsxfrontend/src/components/ui/Toast.tsxfrontend/src/main.tsx
| await verifyEscrowDeposit({ bounty_id: state.bounty_id, tx_signature: state.tx_signature }); | ||
| setSuccess(true); | ||
| addToast({ variant: 'success', title: 'Bounty published!', message: 'Your bounty is now live and visible to contributors.' }); | ||
| } catch (e: unknown) { |
There was a problem hiding this comment.
Handle non-verified escrow responses before showing publish success.
At Line 442, verifyEscrowDeposit is awaited but its result is ignored. If it resolves with verified: false, the code still sets success state/toast.
Suggested fix
- await verifyEscrowDeposit({ bounty_id: state.bounty_id, tx_signature: state.tx_signature });
+ const result = await verifyEscrowDeposit({ bounty_id: state.bounty_id, tx_signature: state.tx_signature });
+ if (!result.verified) {
+ throw new Error(result.error ?? 'Failed to publish bounty. Escrow deposit is not verified.');
+ }
setSuccess(true);
addToast({ variant: 'success', title: 'Bounty published!', message: 'Your bounty is now live and visible to contributors.' });📝 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.
| await verifyEscrowDeposit({ bounty_id: state.bounty_id, tx_signature: state.tx_signature }); | |
| setSuccess(true); | |
| addToast({ variant: 'success', title: 'Bounty published!', message: 'Your bounty is now live and visible to contributors.' }); | |
| } catch (e: unknown) { | |
| const result = await verifyEscrowDeposit({ bounty_id: state.bounty_id, tx_signature: state.tx_signature }); | |
| if (!result.verified) { | |
| throw new Error(result.error ?? 'Failed to publish bounty. Escrow deposit is not verified.'); | |
| } | |
| setSuccess(true); | |
| addToast({ variant: 'success', title: 'Bounty published!', message: 'Your bounty is now live and visible to contributors.' }); | |
| } catch (e: unknown) { |
🤖 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 `@frontend/src/components/bounty/BountyCreateWizard.tsx` around lines 442 -
445, verifyEscrowDeposit is awaited but its return value is ignored, so even
non-verified escrow responses will mark the bounty published; update the success
flow in the async block that calls verifyEscrowDeposit({ bounty_id:
state.bounty_id, tx_signature: state.tx_signature }) to capture its result,
check the result.verified boolean, and only call setSuccess(true) and
addToast(...) when verified is true; if verified is false (or response indicates
failure) then do not set success, instead call addToast with an error variant
and an explanatory message (and optionally set an error state) so the UI
reflects the failed verification.
| const counter = useRef(0); | ||
|
|
||
| const removeToast = useCallback((id: string) => { | ||
| setToasts((prev) => prev.filter((t) => t.id !== id)); | ||
| }, []); |
There was a problem hiding this comment.
Clear auto-dismiss timers on remove/unmount.
Line 50 schedules timers, but they aren’t cleared when a toast is manually dismissed or when the provider unmounts.
Suggested fix
-import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
+import React, { createContext, useContext, useState, useCallback, useRef, useEffect } from 'react';
export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([]);
const counter = useRef(0);
+ const timers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());
const removeToast = useCallback((id: string) => {
+ const timer = timers.current.get(id);
+ if (timer) {
+ clearTimeout(timer);
+ timers.current.delete(id);
+ }
setToasts((prev) => prev.filter((t) => t.id !== id));
}, []);
+ useEffect(() => {
+ return () => {
+ timers.current.forEach((t) => clearTimeout(t));
+ timers.current.clear();
+ };
+ }, []);
+
const addToast = useCallback(
(toast: Omit<Toast, 'id'>) => {
const id = `toast-${++counter.current}`;
const duration = toast.duration ?? 5000;
setToasts((prev) => [...prev, { ...toast, id }]);
if (duration > 0) {
- setTimeout(() => removeToast(id), duration);
+ const t = setTimeout(() => removeToast(id), duration);
+ timers.current.set(id, t);
}
},
[removeToast],
);Also applies to: 43-51
🤖 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 `@frontend/src/components/ui/Toast.tsx` around lines 37 - 41, The auto-dismiss
timers started for toasts are never cleared, so update the implementation to
track timeouts in a ref (replace or supplement counter useRef with a timersRef
Map or Record keyed by toast id), store each timeout id when scheduling, and
call clearTimeout for that id inside removeToast (and remove it from timersRef)
so manual dismissal cancels the timer; also add a cleanup in the provider's
useEffect/unmount to iterate timersRef and clearTimeout all remaining timers.
Ensure you update places that schedule timers to write into timersRef and that
removeToast and the unmount cleanup both clear and delete entries.
…check - Toast.tsx: Track auto-dismiss timers in ref Map, clear on manual dismiss and unmount - BountyCreateWizard.tsx: Check verifyEscrowDeposit result before showing success toast
What this does
Adds a toast notification system for bounty status changes, errors, and success messages across the site.
New Component:
Toast.tsxToastProvider+useToasthook — React context-based, works anywhere in the component treeAnimatePresencerole="alert"and dismiss button hasaria-labelIntegration Points
ToastProviderwraps the entire appHow to test
Closes #825
Wallet: 2JHa4QQWNV7AGSGsVeX1cwjZtFcmTTomb1TrJmQthoh3
Summary by CodeRabbit