Skip to content

Conversation

@Asraye
Copy link
Contributor

@Asraye Asraye commented Oct 4, 2025

Introduces a Toast UI Component

image

This update adds a new toast component for displaying notifications in the UI. The file changed included serves primarily as an example and a foundation for editing on the toast system. The hope for it is to eventually replace existing alerts with a more user-friendly system

Summary by CodeRabbit

  • New Features

    • Introduced non-blocking toast notifications in server publish/bump flows, replacing browser alerts.
    • Auto-dismiss toasts with progress indicator and configurable duration.
    • Interactive controls: drag/swipe to dismiss, close button, and optional action button.
    • Supports stacking multiple toasts; responsive behavior for mobile with smooth entry animations.
  • Style

    • Added a polished, theme-aware toast UI with refined layout, shadows, and responsiveness for various screen sizes.

Asraye added 2 commits October 4, 2025 21:04
This is mostly just as an example on how to use them :P
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 4, 2025

Walkthrough

Introduces a reusable toast system (hook, container, styles) and integrates it into PublishServerSettings. Replaces a browser alert with toast notifications for bump cooldown and errors. Adds per-toast timers, drag-to-dismiss, and optional actions. Updates JSX structure for ServerBumpModal without changing logic.

Changes

Cohort / File(s) Summary
Toast system: hook + container
src/components/ui/toasts/useToast.ts, src/components/ui/toasts/ToastContainer.tsx
Adds useToast hook managing toast list with push/remove and auto-expiry; adds ToastContainer rendering toasts with timers, progress, drag-to-dismiss, close button, optional action, and removal sequencing.
Toast styles
src/components/ui/toasts/ToastStyles.module.scss
Adds styles for toast wrapper, card, content, title, message, action, progress bar, close button, and slideDown animation with responsive adjustments.
Publish settings integration
src/components/servers/settings/PublishServerSettings.tsx
Imports useToast and ToastContainer, instantiates store, replaces alert with pushToast(...), and renders ToastContainer. Adjusts ServerBumpModal JSX formatting without logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant PublishServerSettings
  participant useToast as Toast Store
  participant ToastContainer

  User->>PublishServerSettings: Click "Bump" / "Publish"
  PublishServerSettings->>PublishServerSettings: Check cooldown / errors
  alt Cooldown or Error
    PublishServerSettings->>useToast: pushToast({ title, message, color, duration })
    useToast-->>ToastContainer: toasts()
    ToastContainer->>ToastContainer: Start timer, show progress
    opt User closes or drags past threshold
      ToastContainer->>useToast: removeToast(id)
    end
    alt Timer expires
      ToastContainer->>useToast: removeToast(id)
    end
  else Allowed
    PublishServerSettings->>PublishServerSettings: Proceed with normal flow
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A bunny taps “publish” with cheerful delight,
Cooldowns now whisper in toasts taking flight.
A swipe, a blink—progress bars glow,
No jarring alerts, just soft status flow.
Hop, hop, hooray—signals align,
Carrots and components, perfectly fine. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 toasts” succinctly and accurately summarizes the primary change of introducing toast notifications, aligning with the pull request’s main objective without unnecessary detail or ambiguity.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b2dfd2 and 9145f0f.

📒 Files selected for processing (4)
  • src/components/servers/settings/PublishServerSettings.tsx (4 hunks)
  • src/components/ui/toasts/ToastContainer.tsx (1 hunks)
  • src/components/ui/toasts/ToastStyles.module.scss (1 hunks)
  • src/components/ui/toasts/useToast.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/ui/toasts/ToastContainer.tsx (1)
src/components/ui/toasts/useToast.ts (1)
  • useToast (12-29)
src/components/servers/settings/PublishServerSettings.tsx (2)
src/components/ui/toasts/useToast.ts (1)
  • useToast (12-29)
src/components/ui/toasts/ToastContainer.tsx (1)
  • ToastContainer (5-117)

Comment on lines +18 to +63
const startTimer = () => {
const start = Date.now();
const duration = toast.duration ?? 4000;
interval = window.setInterval(() => {
const elapsed = Date.now() - start;
const remaining = Math.max(duration - elapsed, 0);
setProgress(remaining / duration);
if (remaining <= 0) {
clearInterval(interval);
triggerRemove();
}
}, 50);
};

startTimer();
onCleanup(() => clearInterval(interval));

const triggerRemove = () => {
clearInterval(interval);
setIsRemoving(true);
setTimeout(() => removeToast(toast.id), 300);
};

const onPointerDown = (e: PointerEvent) => {
const target = e.target as HTMLElement;
if (target.closest(`.${style.close}`) || target.closest(`.${style.action}`)) return;

startX = e.clientX;
clearInterval(interval);
};

const onPointerMove = (e: PointerEvent) => {
if (startX !== null) {
const delta = e.clientX - startX;
setDragX(delta);
}
};

const onPointerUp = () => {
if (Math.abs(dragX()) > 120) {
setDragX(dragX() > 0 ? 500 : -500);
triggerRemove();
} else {
setDragX(0);
startTimer();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Duration 0 toasts are getting auto-dismissed and corrupting the progress bar

pushToast treats duration: 0 as “stick around”, but ToastContainer still starts the countdown: it divides by zero (progress becomes NaN%) and calls triggerRemove, so sticky toasts disappear almost instantly. Guard against duration === 0 whenever starting/restarting the timer.

-          const [progress, setProgress] = createSignal(1);
-          const [dragX, setDragX] = createSignal(0);
-          const [isRemoving, setIsRemoving] = createSignal(false);
-          let interval: number;
+          const [progress, setProgress] = createSignal(1);
+          const [dragX, setDragX] = createSignal(0);
+          const [isRemoving, setIsRemoving] = createSignal(false);
+          let interval: number | undefined;
...
-          const startTimer = () => {
-            const start = Date.now();
-            const duration = toast.duration ?? 4000;
+          const startTimer = () => {
+            const duration = toast.duration ?? 4000;
+            if (duration === 0) return;
+            const start = Date.now();
...
-          startTimer();
-          onCleanup(() => clearInterval(interval));
+          if (toast.duration !== 0) {
+            startTimer();
+          }
+          onCleanup(() => interval !== undefined && clearInterval(interval));
...
-            clearInterval(interval);
+            if (interval !== undefined) clearInterval(interval);
...
-            clearInterval(interval); 
+            if (interval !== undefined) clearInterval(interval);
...
-              setDragX(0);
-              startTimer();
+              setDragX(0);
+              if (toast.duration !== 0) {
+                startTimer();
+              }
🤖 Prompt for AI Agents
In src/components/ui/toasts/ToastContainer.tsx around lines 18 to 63, the timer
logic doesn't guard for toast.duration === 0 so sticky toasts start a countdown
(causing division-by-zero NaN progress and auto-dismiss). Fix by making
startTimer return immediately when duration === 0 (and setProgress(1) or leave
progress full), and only call startTimer again (e.g., after pointerUp) if
toast.duration !== 0; also ensure any progress calculation divides by duration
only when duration > 0 to avoid NaN.

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.

1 participant