Refactor mail components to include labels in thread data and improve layout structure. Updated type definitions and adjusted state management in MailDisplay and MailList components for better handling of email labels.

This commit is contained in:
Aj Wazzan
2025-04-28 13:34:53 -07:00
parent ca72e603e8
commit 2ee601031c
7 changed files with 49 additions and 21 deletions

View File

@@ -9,10 +9,10 @@ export default function Layout({ children }: { children: React.ReactNode }) {
return (
<HotkeyProviderWrapper>
<CommandPaletteProvider>
<div className="relative flex overflow-hidden max-h-screen w-full">
<div className="relative flex max-h-screen w-full overflow-hidden">
<SWRConfig
value={{
provider: typeof window !== 'undefined' ? dexieStorageProvider : undefined,
// provider: typeof window !== 'undefined' ? dexieStorageProvider : undefined,
revalidateOnFocus: false,
revalidateIfStale: false,
shouldRetryOnError: false,

View File

@@ -1,10 +1,8 @@
import { parseAddressList, parseFrom, wasSentWithTLS } from '@/lib/email-utils';
import { IGetThreadResponse, type IConfig, type MailManager } from './types';
import { deleteActiveConnection, FatalErrors } from '@/actions/utils';
import { IOutgoingMessage, type ParsedMessage } from '@/types';
import { type IConfig, type MailManager } from './types';
import { type gmail_v1, google } from 'googleapis';
import { filterSuggestions } from '@/lib/filter';
import { GMAIL_COLORS } from '@/lib/constants';
import { cleanSearchValue } from '@/lib/utils';
import { createMimeMessage } from 'mimetext';
import { toByteArray } from 'base64-js';
@@ -661,8 +659,15 @@ export const driver = async (config: IConfig): Promise<MailManager> => {
});
if (!res.data.messages)
return { messages: [], latest: undefined, hasUnread: false, totalReplies: 0 };
return {
messages: [],
latest: undefined,
hasUnread: false,
totalReplies: 0,
labels: [],
};
let hasUnread = false;
const labels = new Set<string>();
const messages: ParsedMessage[] = await Promise.all(
res.data.messages.map(async (message) => {
const bodyData =
@@ -725,6 +730,14 @@ export const driver = async (config: IConfig): Promise<MailManager> => {
}
const parsedData = parse(message);
if (parsedData.tags) {
parsedData.tags.forEach((tag) => {
if (tag.id) {
if (labels.has(tag.id)) return;
labels.add(tag.id);
}
});
}
const attachments = await Promise.all(
message.payload?.parts
@@ -787,6 +800,7 @@ export const driver = async (config: IConfig): Promise<MailManager> => {
}),
);
return {
labels: Array.from(labels).map((id) => ({ id, name: id })),
messages,
latest: messages[messages.length - 1],
hasUnread,

View File

@@ -6,6 +6,7 @@ export interface IGetThreadResponse {
latest: ParsedMessage | undefined;
hasUnread: boolean;
totalReplies: number;
labels: { id: string; name: string }[];
}
export interface ParsedDraft {

View File

@@ -286,12 +286,14 @@ const MailDisplay = ({ emailData, index, totalEmails, demo }: Props) => {
const t = useTranslations();
const [activeReplyId, setActiveReplyId] = useQueryState('activeReplyId');
console.warn(emailData, 'emailData');
useEffect(() => {
if (!demo) {
if (activeReplyId === emailData.id) {
setIsCollapsed(false);
} else {
setIsCollapsed(activeReplyId ? true : totalEmails ? index !== totalEmails - 1 : false);
}
// Set all emails to collapsed by default except the last one
setIsCollapsed(totalEmails ? index !== totalEmails - 1 : false);
if (totalEmails && index === totalEmails - 1) {
if (totalEmails > 5) {
setTimeout(() => {
@@ -301,7 +303,7 @@ const MailDisplay = ({ emailData, index, totalEmails, demo }: Props) => {
}
}
}
}, [index, emailData.id, totalEmails, demo]);
}, [index, activeReplyId, emailData.id, totalEmails, demo]);
const listUnsubscribeAction = useMemo(
() =>

View File

@@ -149,14 +149,17 @@ const Thread = memo(
const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const isHovering = useRef<boolean>(false);
const hasPrefetched = useRef<boolean>(false);
const { data: getThreadData, isLoading, isGroupThread } = useThread(demo ? null : message.id);
const {
data: getThreadData,
labels,
isLoading,
isGroupThread,
} = useThread(demo ? null : message.id);
const latestMessage = demo ? demoMessage : getThreadData?.latest;
const emailContent = demo ? demoMessage?.body : getThreadData?.latest?.body;
const { labels: threadLabels } = useThreadLabels(
latestMessage ? latestMessage.tags?.map((t) => t.id!) : [],
);
const { labels: threadLabels } = useThreadLabels(labels ? labels.map((l) => l.id) : []);
const mainSearchTerm = useMemo(() => {
if (!searchValue.highlight) return '';
@@ -308,7 +311,7 @@ const Thread = memo(
<span className="size-2 rounded bg-[#006FFE]" />
) : null}
</p>
<MailLabels labels={latestMessage.tags} />
{/* <MailLabels labels={latestMessage.tags} /> */}
{Math.random() > 0.5 &&
(() => {
const count = Math.floor(Math.random() * 10) + 1;
@@ -419,7 +422,7 @@ const Thread = memo(
'text-md flex items-baseline gap-1 group-hover:opacity-100',
)}
>
<span className={cn('max-w-[30ch] truncate text-sm')}>
<span className={cn('max-w-[18ch] truncate text-sm')}>
{highlightText(
cleanNameDisplay(latestMessage.sender.name) || '',
searchValue.highlight,
@@ -436,7 +439,7 @@ const Thread = memo(
[{getThreadData.totalReplies}]
</span>
</TooltipTrigger>
<TooltipContent className="px-1 py-0 text-xs">
<TooltipContent className="p-1 text-xs">
{t('common.mail.replies', { count: getThreadData.totalReplies })}
</TooltipContent>
</Tooltip>
@@ -461,7 +464,7 @@ const Thread = memo(
>
{highlightText(latestMessage.subject, searchValue.highlight)}
</p>
<MailLabels labels={latestMessage.tags} />
{labels ? <MailLabels labels={labels} /> : null}
</div>
{emailContent && (
<div className="text-muted-foreground mt-2 line-clamp-2 text-xs">
@@ -771,7 +774,7 @@ export const MailList = memo(({ isCompact }: MailListProps) => {
MailList.displayName = 'MailList';
export const MailLabels = memo(
({ labels }: { labels: Label[] }) => {
({ labels }: { labels: { id: string; name: string }[] }) => {
const t = useTranslations();
if (!labels?.length) return null;

View File

@@ -159,5 +159,13 @@ export const useThread = (threadId: string | null) => {
return totalRecipients > 1;
}, [data]);
return { data, isLoading, error, isGroupThread, hasUnread: data?.hasUnread, mutate };
return {
data,
isLoading,
labels: data?.labels,
error,
isGroupThread,
hasUnread: data?.hasUnread,
mutate,
};
};

View File

@@ -105,7 +105,7 @@ export const getListUnsubscribeAction = ({
};
const FALLBACK_SENDER = {
name: 'No Sender Name',
name: '',
email: 'no-sender@unknown',
};