Skip to content

Commit 43c5273

Browse files
authored
Merge branch 'staging' into fix/removed-buggy-hover-behaviour
2 parents b06be46 + 64c5480 commit 43c5273

File tree

2 files changed

+135
-109
lines changed

2 files changed

+135
-109
lines changed

apps/mail/components/mail/mail.tsx

Lines changed: 134 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable';
1111
import { useCommandPalette } from '../context/command-palette-context';
1212
import { useHotkeys, useHotkeysContext } from 'react-hotkeys-hook';
1313
import { ThreadDisplay } from '@/components/mail/thread-display';
14+
import { useCallback, useEffect, useRef, useState } from 'react';
1415
import { useActiveConnection } from '@/hooks/use-connections';
1516
import { Check, ChevronDown, RefreshCcw } from 'lucide-react';
1617
import { useMediaQuery } from '../../hooks/use-media-query';
@@ -22,7 +23,6 @@ import { useMail } from '@/components/mail/use-mail';
2223
import { SidebarToggle } from '../ui/sidebar-toggle';
2324
import { PricingDialog } from '../ui/pricing-dialog';
2425
import { clearBulkSelectionAtom } from './use-mail';
25-
import { useEffect, useRef, useState } from 'react';
2626
import AISidebar from '@/components/ui/ai-sidebar';
2727
import { useThreads } from '@/hooks/use-threads';
2828
import AIToggleButton from '../ai-toggle-button';
@@ -392,6 +392,26 @@ export function MailLayout() {
392392
const defaultCategoryId = useDefaultCategoryId();
393393
const [category] = useQueryState('category', { defaultValue: defaultCategoryId });
394394

395+
const handleClearFilters = useCallback(
396+
(e: React.MouseEvent) => {
397+
e.stopPropagation();
398+
clearAllFilters();
399+
},
400+
[clearAllFilters],
401+
);
402+
403+
const handleExitBulkSelection = useCallback(() => {
404+
setMail({ ...mail, bulkSelected: [] });
405+
}, [mail, setMail]);
406+
407+
const handleRefetchThreads = useCallback(() => {
408+
refetchThreads();
409+
}, [refetchThreads]);
410+
411+
const handleOpenCommandPalette = useCallback(() => {
412+
setIsCommandPaletteOpen('true');
413+
}, [setIsCommandPaletteOpen]);
414+
395415
return (
396416
<TooltipProvider delayDuration={0}>
397417
<PricingDialog />
@@ -413,113 +433,106 @@ export function MailLayout() {
413433
// onMouseLeave={handleMailListMouseLeave}
414434
>
415435
<div className="w-full md:h-[calc(100dvh-10px)]">
416-
<div
417-
className={cn(
418-
'z-15 sticky top-0 flex items-center justify-between gap-1.5 p-2 pb-0 transition-colors',
419-
)}
420-
>
421-
<div className="w-full">
422-
<div className="mt-1 grid grid-cols-12 gap-2">
423-
<SidebarToggle className="col-span-1 h-fit px-2" />
424-
{mail.bulkSelected.length === 0 ? (
425-
<div className="col-span-10 flex gap-2">
426-
<Button
427-
variant="outline"
428-
className={cn(
429-
'text-muted-foreground relative flex h-8 w-full select-none items-center justify-start overflow-hidden rounded-lg border bg-white pl-2 text-left text-sm font-normal shadow-none ring-0 focus-visible:ring-0 focus-visible:ring-offset-0 dark:border-none dark:bg-[#141414]',
430-
)}
431-
onClick={() => setIsCommandPaletteOpen('true')}
432-
>
433-
<Search className="fill-[#71717A] dark:fill-[#6F6F6F]" />
434-
435-
<span className="hidden truncate pr-20 lg:inline-block">
436-
{activeFilters.length > 0
437-
? activeFilters.map((f) => f.display).join(', ')
438-
: 'Search'}
439-
</span>
440-
<span className="inline-block truncate pr-20 lg:hidden">
441-
{activeFilters.length > 0
442-
? `${activeFilters.length} filter${activeFilters.length > 1 ? 's' : ''}`
443-
: 'Search'}
444-
</span>
445-
446-
<span className="absolute right-[0rem] flex items-center gap-1">
447-
{/* {activeFilters.length > 0 && (
436+
<div className="z-15 sticky top-0 p-4 pb-0">
437+
<div className="flex items-center gap-2">
438+
<SidebarToggle className="h-10 w-10" />
439+
440+
{mail.bulkSelected.length === 0 ? (
441+
<>
442+
<Button
443+
variant="outline"
444+
className={cn(
445+
'text-muted-foreground border-border/40 bg-background/50 hover:bg-accent/30 focus-visible:ring-ring dark:border-border/20 dark:bg-background/40 relative flex h-10 flex-1 select-none items-center justify-start overflow-hidden rounded-lg border pl-3 text-left text-sm font-normal shadow-none ring-0 backdrop-blur-sm transition-all focus-visible:ring-2 focus-visible:ring-offset-2',
446+
)}
447+
onClick={handleOpenCommandPalette}
448+
>
449+
<Search className="fill-muted-foreground h-4 w-4" />
450+
451+
<span className="ml-3 hidden truncate pr-20 lg:inline-block">
452+
{activeFilters.length > 0
453+
? activeFilters.map((f) => f.display).join(', ')
454+
: 'Search'}
455+
</span>
456+
<span className="ml-3 inline-block truncate pr-20 lg:hidden">
457+
{activeFilters.length > 0
458+
? `${activeFilters.length} filter${activeFilters.length > 1 ? 's' : ''}`
459+
: 'Search'}
460+
</span>
461+
462+
<div className="absolute right-2 flex items-center gap-2">
463+
{/* {activeFilters.length > 0 && (
448464
<Badge variant="secondary" className="ml-2 h-5 rounded px-1">
449465
{activeFilters.length}
450466
</Badge>
451467
)} */}
452-
{activeFilters.length > 0 && (
453-
<Button
454-
variant="ghost"
455-
size="sm"
456-
className="my-auto h-5 rounded-xl px-1.5 text-xs"
457-
onClick={(e) => {
458-
e.stopPropagation();
459-
clearAllFilters();
460-
}}
461-
>
462-
Clear
463-
</Button>
464-
)}
465-
<kbd className="bg-muted text-md leading-[0]! pointer-events-none mr-0.5 hidden h-7 select-none flex-row items-center gap-1 rounded-md border-none px-2 font-medium opacity-100 sm:flex dark:bg-[#262626] dark:text-[#929292]">
466-
<span
467-
className={cn(
468-
'leading-[0.2]! h-min',
469-
isMac ? 'mt-px text-lg' : 'text-sm',
470-
)}
471-
>
472-
{isMac ? '⌘' : 'Ctrl'}{' '}
473-
</span>
474-
<span className="leading-[0.2]! h-min text-sm"> K</span>
475-
</kbd>
476-
</span>
477-
</Button>
478-
{activeConnection?.providerId === 'google' && folder === 'inbox' && (
479-
<CategoryDropdown isMultiSelectMode={mail.bulkSelected.length > 0} />
480-
)}
481-
</div>
482-
) : null}
483-
<Button
484-
onClick={() => {
485-
refetchThreads();
486-
}}
487-
variant="ghost"
488-
className="md:h-fit md:px-2 hover:bg-accent/50"
489-
>
490-
<RefreshCcw className="text-muted-foreground h-4 w-4" />
491-
</Button>
492-
{mail.bulkSelected.length > 0 ? (
493-
<div className="flex items-center gap-2">
494-
<Tooltip>
495-
<TooltipTrigger asChild>
496-
<button
497-
onClick={() => {
498-
setMail({ ...mail, bulkSelected: [] });
499-
}}
500-
className="flex h-6 items-center gap-1 rounded-md bg-[#313131] px-2 text-xs text-[#A0A0A0] hover:bg-[#252525]"
468+
{activeFilters.length > 0 && (
469+
<Button
470+
variant="secondary"
471+
size="sm"
472+
className="h-6 rounded-md px-2 text-xs"
473+
onClick={handleClearFilters}
501474
>
502-
<X className="h-3 w-3 fill-[#A0A0A0]" />
503-
<span>esc</span>
504-
</button>
505-
</TooltipTrigger>
506-
<TooltipContent>
507-
{m['common.actions.exitSelectionModeEsc']()}
508-
</TooltipContent>
509-
</Tooltip>
475+
Clear
476+
</Button>
477+
)}
478+
<kbd className="bg-muted border-border/40 dark:bg-muted/40 pointer-events-none hidden h-6 select-none items-center gap-1 rounded border px-2 text-xs font-medium opacity-80 sm:flex">
479+
<span className={cn('text-xs', isMac ? 'text-sm' : 'text-xs')}>
480+
{isMac ? '⌘' : 'Ctrl'}
481+
</span>
482+
<span className="text-xs">K</span>
483+
</kbd>
484+
</div>
485+
</Button>
486+
487+
{activeConnection?.providerId === 'google' && folder === 'inbox' && (
488+
<CategoryDropdown isMultiSelectMode={mail.bulkSelected.length > 0} />
489+
)}
490+
</>
491+
) : (
492+
<div className="flex flex-1 items-center justify-between">
493+
<div className="text-foreground text-sm font-medium">
494+
{mail.bulkSelected.length} selected
510495
</div>
511-
) : null}
512-
</div>
496+
<Tooltip>
497+
<TooltipTrigger asChild>
498+
<Button
499+
variant="secondary"
500+
size="sm"
501+
onClick={handleExitBulkSelection}
502+
className="h-8 gap-2 rounded-lg"
503+
>
504+
<X className="h-3 w-3" />
505+
<span className="text-xs">ESC</span>
506+
</Button>
507+
</TooltipTrigger>
508+
<TooltipContent>
509+
{m['common.actions.exitSelectionModeEsc']()}
510+
</TooltipContent>
511+
</Tooltip>
512+
</div>
513+
)}
514+
515+
<Button
516+
onClick={handleRefetchThreads}
517+
variant="ghost"
518+
size="icon"
519+
className="border-none bg-transparent hover:bg-accent/50 h-10 w-10 rounded-lg backdrop-blur-sm"
520+
>
521+
<RefreshCcw className="text-muted-foreground h-4 w-4" />
522+
</Button>
513523
</div>
514524
</div>
515525

516-
<div
517-
className={cn(
518-
`${category === 'Important' ? 'bg-[#F59E0D]' : category === 'All Mail' ? 'bg-[#006FFE]' : category === 'Personal' ? 'bg-[#39ae4a]' : category === 'Updates' ? 'bg-[#8B5CF6]' : category === 'Promotions' ? 'bg-[#F43F5E]' : category === 'Unread' ? 'bg-[#FF4800]' : 'bg-[#F59E0D]'}`,
519-
'z-5 relative h-0.5 w-full transition-opacity',
520-
isFetching ? 'opacity-100' : 'opacity-0',
521-
)}
522-
/>
526+
<div className="px-4 pt-2">
527+
<div
528+
className={cn(
529+
`${category === 'Important' ? 'bg-[#F59E0D]' : category === 'All Mail' ? 'bg-[#006FFE]' : category === 'Personal' ? 'bg-[#39ae4a]' : category === 'Updates' ? 'bg-[#8B5CF6]' : category === 'Promotions' ? 'bg-[#F43F5E]' : category === 'Unread' ? 'bg-[#FF4800]' : 'bg-[#F59E0D]'}`,
530+
'h-0.5 w-full rounded-full transition-opacity',
531+
isFetching ? 'opacity-100' : 'opacity-0',
532+
)}
533+
/>
534+
</div>
535+
523536
<div className="z-1 relative h-[calc(100dvh-(2px+2px))] overflow-hidden pt-0 md:h-[calc(100dvh-4rem)]">
524537
<MailList />
525538
</div>
@@ -563,6 +576,14 @@ export function MailLayout() {
563576
);
564577
}
565578

579+
interface CategoryItem {
580+
id: string;
581+
name: string;
582+
searchValue: string;
583+
icon?: React.ReactNode;
584+
colors?: string;
585+
}
586+
566587
export const Categories = () => {
567588
const defaultCategoryIdInner = useDefaultCategoryId();
568589
const categorySettings = useCategorySettings();
@@ -661,11 +682,11 @@ export const Categories = () => {
661682
),
662683
};
663684
default:
664-
return base as any;
685+
return base;
665686
}
666687
});
667688

668-
return categories;
689+
return categories as CategoryItem[];
669690
};
670691
interface CategoryDropdownProps {
671692
isMultiSelectMode?: boolean;
@@ -737,32 +758,35 @@ function CategoryDropdown({ isMultiSelectMode }: CategoryDropdownProps) {
737758
<Button
738759
variant="outline"
739760
className={cn(
740-
'black:text-white text-muted-foreground flex h-8 min-w-fit items-center gap-1 rounded-md border-none px-2',
761+
'text-muted-foreground border-border/40 bg-background/50 hover:bg-accent/30 dark:border-border/20 dark:bg-background/40 flex h-10 min-w-fit items-center gap-2 rounded-lg border px-3 backdrop-blur-sm transition-all',
741762
)}
742763
aria-label="Filter by labels"
743764
aria-expanded={isOpen}
744765
aria-haspopup="menu"
745766
>
746-
<span className="text-xs font-medium">
767+
<span className="text-sm font-medium">
747768
{labels.length > 0
748769
? `${labels.length} View${labels.length > 1 ? 's' : ''}`
749770
: m['navigation.settings.categories']()}
750771
</span>
751772
<ChevronDown
752-
className={`black:text-white text-muted-foreground h-2 w-2 transition-transform duration-200 ${isOpen ? 'rotate-180' : 'rotate-0'}`}
773+
className={cn(
774+
'text-muted-foreground h-4 w-4 transition-transform duration-200',
775+
isOpen ? 'rotate-180' : 'rotate-0',
776+
)}
753777
/>
754778
</Button>
755779
</DropdownMenuTrigger>
756780
<DropdownMenuContent
757-
className="bg-muted w-48 font-medium dark:bg-[#2C2C2C]"
781+
className="border-border/50 bg-muted w-48 rounded-xl border p-2 dark:bg-[#232323]"
758782
align="start"
759783
role="menu"
760784
aria-label="Label filter options"
761785
>
762786
{categorySettings.map((category) => (
763787
<DropdownMenuItem
764788
key={category.id}
765-
className="flex cursor-pointer items-center gap-2 hover:bg-white/10"
789+
className="hover:bg-accent/50 flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2.5 text-sm transition-colors"
766790
onClick={(e) => {
767791
e.preventDefault();
768792
e.stopPropagation();
@@ -771,12 +795,14 @@ function CategoryDropdown({ isMultiSelectMode }: CategoryDropdownProps) {
771795
role="menuitemcheckbox"
772796
aria-checked={labels.includes(category.id)}
773797
>
774-
<span className="text-muted-foreground capitalize">{category.name.toLowerCase()}</span>
798+
<span className="text-foreground font-medium capitalize">
799+
{category.name.toLowerCase()}
800+
</span>
775801
{/* Special case: empty searchValue means "All Mail" - shows everything */}
776802
{(category.searchValue === ''
777803
? labels.length === 0
778804
: category.searchValue.split(',').some((val) => labels.includes(val))) && (
779-
<Check className="ml-auto h-3 w-3" />
805+
<Check className="text-primary ml-auto h-4 w-4" />
780806
)}
781807
</DropdownMenuItem>
782808
))}

apps/mail/components/ui/sidebar-toggle.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function SidebarToggle({ className }: ComponentProps<typeof SidebarTrigge
99
const { toggleSidebar } = useSidebar();
1010

1111
return (
12-
<Button onClick={toggleSidebar} variant="ghost" className={cn('md:h-fit md:px-2', className)}>
12+
<Button onClick={toggleSidebar} variant="ghost" className={cn('h-10 w-10 md:px-2', className)}>
1313
<PanelLeftOpen className="dark:fill-iconDark fill-iconLight" />
1414
</Button>
1515
);

0 commit comments

Comments
 (0)