Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 161 additions & 77 deletions apps/mail/components/mail/mail-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,105 +135,189 @@ const Thread = memo(

return (
<div className="p-1" onClick={onClick ? onClick(message) : undefined}>
<Link
href={`/mail/${folder}?threadId=${message.threadId ?? message.id}`}
data-thread-id={message.threadId ?? message.id}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
key={message.threadId ?? message.id}
className={cn(
'hover:bg-offsetLight hover:bg-primary/5 group relative flex cursor-pointer flex-col items-start overflow-clip rounded-lg border border-transparent px-4 py-3 text-left text-sm transition-all hover:opacity-100',
isMailSelected || (!message.unread && 'opacity-50'),
(isMailSelected || isMailBulkSelected || isKeyboardFocused) &&
'border-border bg-primary/5 opacity-100',
isKeyboardFocused && 'ring-primary/50 ring-2',
)}
>
{demo ? (
<div
data-thread-id={message.threadId ?? message.id}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
key={message.threadId ?? message.id}
className={cn(
'bg-primary absolute inset-y-0 left-0 w-1 -translate-x-2 transition-transform ease-out',
isMailBulkSelected && 'translate-x-0',
'hover:bg-offsetLight hover:bg-primary/5 group relative flex cursor-pointer flex-col items-start overflow-clip rounded-lg border border-transparent px-4 py-3 text-left text-sm transition-all hover:opacity-100',
isMailSelected || (!message.unread && 'opacity-50'),
(isMailSelected || isMailBulkSelected || isKeyboardFocused) &&
'border-border bg-primary/5 opacity-100',
isKeyboardFocused && 'ring-primary/50 ring-2',
)}
/>
{/* <MailQuickActions
message={message}
isHovered={isHovered || isKeyboardFocused}
isInQuickActionMode={isInQuickActionMode}
selectedQuickActionIndex={selectedQuickActionIndex}
resetNavigation={resetNavigation}
/> */}
<div className="flex w-full items-center justify-between gap-4">
<Avatar className="h-8 w-8 rounded-full">
<AvatarImage src={getEmailLogo(message.sender.email)} className="rounded-full" />
<AvatarFallback className="rounded-full">{message.sender.name[0]}</AvatarFallback>
</Avatar>
<div className="flex w-full justify-between">
<div className="w-full">
<div className="flex w-full flex-row items-center justify-between">
<div className="flex flex-row items-center gap-1">
<p
className={cn(
message.unread && !isMailSelected ? 'font-bold' : 'font-medium',
'text-md flex items-baseline gap-1 group-hover:opacity-100',
)}
>
<span>{highlightText(message.sender.name, searchValue.highlight)}</span>{' '}
{message.unread && !isMailSelected ? (
<span className="size-2 rounded bg-[#006FFE]" />
>
<div
className={cn(
'bg-primary absolute inset-y-0 left-0 w-1 -translate-x-2 transition-transform ease-out',
isMailBulkSelected && 'translate-x-0',
)}
/>
<div className="flex w-full items-center justify-between gap-4">
<Avatar className="h-8 w-8 rounded-full">
<AvatarImage src={getEmailLogo(message.sender.email)} className="rounded-full" />
<AvatarFallback className="rounded-full">{message.sender.name[0]}</AvatarFallback>
</Avatar>
<div className="flex w-full justify-between">
<div className="w-full">
<div className="flex w-full flex-row items-center justify-between">
<div className="flex flex-row items-center gap-1">
<p
className={cn(
message.unread && !isMailSelected ? 'font-bold' : 'font-medium',
'text-md flex items-baseline gap-1 group-hover:opacity-100',
)}
>
<span>{highlightText(message.sender.name, searchValue.highlight)}</span>{' '}
{message.unread && !isMailSelected ? (
<span className="size-2 rounded bg-[#006FFE]" />
) : null}
</p>
<MailLabels labels={threadLabels} />
{message.totalReplies > 1 ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="rounded-md border border-dotted px-[5px] py-[1px] text-xs opacity-70">
{message.totalReplies}
</span>
</TooltipTrigger>
<TooltipContent className="px-1 py-0 text-xs">
{t('common.mail.replies', { count: message.totalReplies })}
</TooltipContent>
</Tooltip>
) : null}
</p>
<MailLabels labels={threadLabels} />
{message.totalReplies > 1 ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="rounded-md border border-dotted px-[5px] py-[1px] text-xs opacity-70">
{message.totalReplies}
</span>
</TooltipTrigger>
<TooltipContent className="px-1 py-0 text-xs">
{t('common.mail.replies', { count: message.totalReplies })}
</TooltipContent>
</Tooltip>
</div>
{message.receivedOn ? (
<p
className={cn(
'text-nowrap text-xs font-normal opacity-70 transition-opacity group-hover:opacity-100',
isMailSelected && 'opacity-100',
)}
>
{formatDate(message.receivedOn.split('.')[0] || '')}
</p>
) : null}
</div>
{message.receivedOn ? (
<p
className={cn(
'text-nowrap text-xs font-normal opacity-70 transition-opacity group-hover:opacity-100',
isMailSelected && 'opacity-100',
)}
>
{formatDate(message.receivedOn.split('.')[0] || '')}
</p>
) : null}
<p
className={cn(
'mt-1 line-clamp-1 text-xs opacity-70 transition-opacity',
mail.selected ? 'line-clamp-1' : 'line-clamp-2',
isMailSelected && 'opacity-100',
)}
>
{highlightText(message.subject, searchValue.highlight)}
</p>
</div>
<p
className={cn(
'mt-1 line-clamp-1 text-xs opacity-70 transition-opacity',
mail.selected ? 'line-clamp-1' : 'line-clamp-2',
isMailSelected && 'opacity-100',
)}
>
{highlightText(message.subject, searchValue.highlight)}
</p>
</div>
</div>
</div>
</Link>
) : (
<Link
href={`/mail/${folder}?threadId=${message.threadId ?? message.id}`}
data-thread-id={message.threadId ?? message.id}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
key={message.threadId ?? message.id}
className={cn(
'hover:bg-offsetLight hover:bg-primary/5 group relative flex cursor-pointer flex-col items-start overflow-clip rounded-lg border border-transparent px-4 py-3 text-left text-sm transition-all hover:opacity-100',
isMailSelected || (!message.unread && 'opacity-50'),
(isMailSelected || isMailBulkSelected || isKeyboardFocused) &&
'border-border bg-primary/5 opacity-100',
isKeyboardFocused && 'ring-primary/50 ring-2',
)}
>
<div
className={cn(
'bg-primary absolute inset-y-0 left-0 w-1 -translate-x-2 transition-transform ease-out',
isMailBulkSelected && 'translate-x-0',
)}
/>
<div className="flex w-full items-center justify-between gap-4">
<Avatar className="h-8 w-8 rounded-full">
<AvatarImage src={getEmailLogo(message.sender.email)} className="rounded-full" />
<AvatarFallback className="rounded-full">{message.sender.name[0]}</AvatarFallback>
</Avatar>
<div className="flex w-full justify-between">
<div className="w-full">
<div className="flex w-full flex-row items-center justify-between">
<div className="flex flex-row items-center gap-1">
<p
className={cn(
message.unread && !isMailSelected ? 'font-bold' : 'font-medium',
'text-md flex items-baseline gap-1 group-hover:opacity-100',
)}
>
<span>{highlightText(message.sender.name, searchValue.highlight)}</span>{' '}
{message.unread && !isMailSelected ? (
<span className="size-2 rounded bg-[#006FFE]" />
) : null}
</p>
<MailLabels labels={threadLabels} />
{message.totalReplies > 1 ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="rounded-md border border-dotted px-[5px] py-[1px] text-xs opacity-70">
{message.totalReplies}
</span>
</TooltipTrigger>
<TooltipContent className="px-1 py-0 text-xs">
{t('common.mail.replies', { count: message.totalReplies })}
</TooltipContent>
</Tooltip>
) : null}
</div>
{message.receivedOn ? (
<p
className={cn(
'text-nowrap text-xs font-normal opacity-70 transition-opacity group-hover:opacity-100',
isMailSelected && 'opacity-100',
)}
>
{formatDate(message.receivedOn.split('.')[0] || '')}
</p>
) : null}
</div>
<p
className={cn(
'mt-1 line-clamp-1 text-xs opacity-70 transition-opacity',
mail.selected ? 'line-clamp-1' : 'line-clamp-2',
isMailSelected && 'opacity-100',
)}
>
{highlightText(message.subject, searchValue.highlight)}
</p>
</div>
</div>
</div>
</Link>
)}
</div>
);
},
);

Thread.displayName = 'Thread';

export function MailListDemo({ items: filteredItems = items }) {
export function MailListDemo({ items: filteredItems = items, onSelectMail }: {
items?: typeof items;
onSelectMail?: (message: any) => void;
}) {
return (
<ScrollArea className="h-full pb-2" type="scroll">
<div className={cn('relative min-h-[calc(100vh-4rem)] w-full')}>
<div className="absolute left-0 top-0 w-full p-[8px]">
{filteredItems.map((item) => {
return item ? <Thread demo key={item.id} message={item} selectMode={'single'} /> : null;
return item ? (
<Thread
demo
key={item.id}
message={item}
selectMode={'single'}
onClick={(message) => () => onSelectMail && onSelectMail(message)}
/>
) : null;
})}
</div>
</div>
Expand Down
20 changes: 17 additions & 3 deletions apps/mail/components/mail/mail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ import { SearchBar } from './search-bar';
import items from './demo.json';
import { useAtom } from 'jotai';
import { toast } from 'sonner';
import { RefreshIcon } from '../icons/animated/refresh';

export function DemoMailLayout() {
const [mail, setMail] = useState({
selected: 'demo',
bulkSelected: [],
});
const [selectedMail, setSelectedMail] = useState<any>(null);
const isMobile = false;
const isValidating = false;
const isLoading = false;
Expand All @@ -84,6 +84,11 @@ export function DemoMailLayout() {
const [activeCategory, setActiveCategory] = useState('Primary');
const [filteredItems, setFilteredItems] = useState(items);

const handleSelectMail = useCallback((message: any) => {
setSelectedMail(message);
setMail(prev => ({ ...prev, selected: message.id }));
}, []);

useEffect(() => {
if (activeCategory === 'Primary') {
setFilteredItems(items);
Expand All @@ -101,6 +106,12 @@ export function DemoMailLayout() {
}
}, [activeCategory]);

useEffect(() => {
if (filteredItems.length > 0 && !selectedMail) {
handleSelectMail(filteredItems[0]);
}
}, [filteredItems, selectedMail, handleSelectMail]);

return (
<TooltipProvider delayDuration={0}>
<div className="rounded-inherit flex">
Expand Down Expand Up @@ -162,7 +173,10 @@ export function DemoMailLayout() {
))}
</div>
) : (
<MailListDemo items={filteredItems} />
<MailListDemo
items={filteredItems}
onSelectMail={handleSelectMail}
/>
)}
</div>
</div>
Expand All @@ -177,7 +191,7 @@ export function DemoMailLayout() {
minSize={25}
>
<div className="relative hidden h-[calc(100vh-(12px+14px))] max-h-[800px] flex-1 md:block">
<ThreadDemo mail={[filteredItems[0]]} />
<ThreadDemo mail={selectedMail ? [selectedMail] : [filteredItems[0]]} />
</div>
</ResizablePanel>
</>
Expand Down