mirror of
https://github.com/Mail-0/Zero.git
synced 2026-07-01 08:16:28 +00:00
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:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface IGetThreadResponse {
|
||||
latest: ParsedMessage | undefined;
|
||||
hasUnread: boolean;
|
||||
totalReplies: number;
|
||||
labels: { id: string; name: string }[];
|
||||
}
|
||||
|
||||
export interface ParsedDraft {
|
||||
|
||||
@@ -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(
|
||||
() =>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -105,7 +105,7 @@ export const getListUnsubscribeAction = ({
|
||||
};
|
||||
|
||||
const FALLBACK_SENDER = {
|
||||
name: 'No Sender Name',
|
||||
name: '',
|
||||
email: 'no-sender@unknown',
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user