bulk-actions-bar.tsx7.2 KBView on GitHub
import {
  Archive,
  Trash,
  ArchiveX,
  Mail as MailIcon,
  MailOpen,
  Star,
  Inbox,
  Zap,
  X,
} from 'lucide-react';
import { BulkActionsLabelsPopover } from '@/modules/labels/components/bulk-actions-labels-popover';
import { useOptimisticActions } from '@/modules/threads/rendering/use-optimistic-actions';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { useMailActions } from '@/modules/store';
import { Button } from '@/components/ui/button';
import { m } from '@/paraglide/messages';
import { useCallback } from 'react';

interface BulkActionsBarProps {
  selectedThreadIds: string[];
  currentFolder: string;
  onExitSelection: () => void;
}

export function BulkActionsBar({
  selectedThreadIds,
  currentFolder,
  onExitSelection,
}: BulkActionsBarProps) {
  const {
    optimisticMarkAsRead,
    optimisticMarkAsUnread,
    optimisticToggleStar,
    optimisticMoveThreadsTo,
    optimisticDeleteThreads,
    optimisticToggleImportant,
  } = useOptimisticActions();
  const { clearBulkSelection } = useMailActions();

  // Determine which actions are available based on current folder
  const isInbox = currentFolder === 'inbox';
  const isSpam = currentFolder === 'spam';
  const isBin = currentFolder === 'bin' || currentFolder === 'trash';
  const isArchive = currentFolder === 'archive';
  const isSent = currentFolder === 'sent';

  const handleMarkAsRead = useCallback(() => {
    optimisticMarkAsRead(selectedThreadIds);
    clearBulkSelection();
  }, [optimisticMarkAsRead, selectedThreadIds, clearBulkSelection]);

  const handleMarkAsUnread = useCallback(() => {
    optimisticMarkAsUnread(selectedThreadIds);
    clearBulkSelection();
  }, [optimisticMarkAsUnread, selectedThreadIds, clearBulkSelection]);

  const handleArchive = useCallback(() => {
    optimisticMoveThreadsTo(selectedThreadIds, currentFolder, 'archive');
    clearBulkSelection();
  }, [optimisticMoveThreadsTo, selectedThreadIds, currentFolder, clearBulkSelection]);

  const handleMoveToInbox = useCallback(() => {
    optimisticMoveThreadsTo(selectedThreadIds, currentFolder, 'inbox');
    clearBulkSelection();
  }, [optimisticMoveThreadsTo, selectedThreadIds, currentFolder, clearBulkSelection]);

  const handleMoveToSpam = useCallback(() => {
    optimisticMoveThreadsTo(selectedThreadIds, currentFolder, 'spam');
    clearBulkSelection();
  }, [optimisticMoveThreadsTo, selectedThreadIds, currentFolder, clearBulkSelection]);

  const handleDelete = useCallback(() => {
    // Use optimisticDeleteThreads which calls the proper bulkDelete endpoint
    // This will remove INBOX, SPAM, and SENT labels and add TRASH
    optimisticDeleteThreads(selectedThreadIds);
    clearBulkSelection();
  }, [optimisticDeleteThreads, selectedThreadIds, clearBulkSelection]);

  const handleStar = useCallback(() => {
    optimisticToggleStar(selectedThreadIds, true);
    clearBulkSelection();
  }, [optimisticToggleStar, selectedThreadIds, clearBulkSelection]);

  const handleMarkImportant = useCallback(() => {
    optimisticToggleImportant(selectedThreadIds, true);
    clearBulkSelection();
  }, [optimisticToggleImportant, selectedThreadIds, clearBulkSelection]);

  return (
    <div className="flex flex-1 items-center justify-between gap-2">
      {/* Left side: Count and actions */}
      <div className="flex items-center gap-2">
        <div className="text-foreground text-sm font-medium">
          {selectedThreadIds.length} selected
        </div>

        {/* Mark as Read/Unread */}
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="ghost" size="sm" onClick={handleMarkAsRead} className="h-8 w-8 p-0">
              <MailOpen className="h-4 w-4" />
            </Button>
          </TooltipTrigger>
          <TooltipContent>Mark as read</TooltipContent>
        </Tooltip>

        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="ghost" size="sm" onClick={handleMarkAsUnread} className="h-8 w-8 p-0">
              <MailIcon className="h-4 w-4" />
            </Button>
          </TooltipTrigger>
          <TooltipContent>Mark as unread</TooltipContent>
        </Tooltip>

        {/* Star */}
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="ghost" size="sm" onClick={handleStar} className="h-8 w-8 p-0">
              <Star className="h-4 w-4" />
            </Button>
          </TooltipTrigger>
          <TooltipContent>Add to favorites</TooltipContent>
        </Tooltip>

        {/* Mark Important */}
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="ghost" size="sm" onClick={handleMarkImportant} className="h-8 w-8 p-0">
              <Zap className="h-4 w-4" />
            </Button>
          </TooltipTrigger>
          <TooltipContent>Mark as important</TooltipContent>
        </Tooltip>

        {/* Divider */}
        <div className="bg-border h-6 w-px" />

        {/* Archive (only show in inbox or sent) */}
        {(isInbox || isSent) && (
          <Tooltip>
            <TooltipTrigger asChild>
              <Button variant="ghost" size="sm" onClick={handleArchive} className="h-8 w-8 p-0">
                <Archive className="h-4 w-4" />
              </Button>
            </TooltipTrigger>
            <TooltipContent>Archive</TooltipContent>
          </Tooltip>
        )}

        {/* Move to Inbox (only show in archive, spam, or bin) */}
        {(isArchive || isSpam || isBin) && (
          <Tooltip>
            <TooltipTrigger asChild>
              <Button variant="ghost" size="sm" onClick={handleMoveToInbox} className="h-8 w-8 p-0">
                <Inbox className="h-4 w-4" />
              </Button>
            </TooltipTrigger>
            <TooltipContent>Move to inbox</TooltipContent>
          </Tooltip>
        )}

        {/* Move to Spam (only show in inbox) */}
        {isInbox && (
          <Tooltip>
            <TooltipTrigger asChild>
              <Button variant="ghost" size="sm" onClick={handleMoveToSpam} className="h-8 w-8 p-0">
                <ArchiveX className="h-4 w-4" />
              </Button>
            </TooltipTrigger>
            <TooltipContent>Move to spam</TooltipContent>
          </Tooltip>
        )}

        {/* Delete */}
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="ghost" size="sm" onClick={handleDelete} className="h-8 w-8 p-0">
              <Trash className="h-4 w-4" />
            </Button>
          </TooltipTrigger>
          <TooltipContent>Move to trash</TooltipContent>
        </Tooltip>

        {/* Divider */}
        <div className="bg-border h-6 w-px" />

        {/* Labels Popover */}
        <BulkActionsLabelsPopover
          selectedThreadIds={selectedThreadIds}
          onComplete={clearBulkSelection}
        />
      </div>

      {/* Right side: Exit button */}
      <Tooltip>
        <TooltipTrigger asChild>
          <Button
            variant="secondary"
            size="sm"
            onClick={onExitSelection}
            className="h-8 gap-2 rounded-lg"
          >
            <X className="h-3 w-3" />
            <span className="text-xs">ESC</span>
          </Button>
        </TooltipTrigger>
        <TooltipContent>{m['common.actions.exitSelectionModeEsc']()}</TooltipContent>
      </Tooltip>
    </div>
  );
}