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
19 changes: 15 additions & 4 deletions apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,11 @@ export function useBulkArchive<T extends Row>({
froms: items.map((item) => item.name),
});

const displayNames = formatSenderNames(items);

toast.promise(promise, {
loading: "Archiving emails...",
success: "Bulk archive completed",
loading: `Archiving emails from ${displayNames}...`,
success: `Archived emails from ${displayNames}`,
error: (error) =>
error?.error?.serverError || "There was an error archiving the emails",
});
Expand Down Expand Up @@ -534,9 +536,11 @@ export function useBulkDelete<T extends Row>({

const promise = executeBulkTrash({ froms: items.map((item) => item.name) });

const displayNames = formatSenderNames(items);

toast.promise(promise, {
loading: "Moving emails to trash...",
success: "Bulk trash completed",
loading: `Deleting emails from ${displayNames}...`,
success: `Deleted emails from ${displayNames}`,
error: (error) =>
error?.error?.serverError || "There was an error trashing the emails",
});
Expand Down Expand Up @@ -675,3 +679,10 @@ export function useNewsletterFilter() {
setFilters,
};
}

function formatSenderNames<T extends Row>(items: T[]): string {
const senderNames = items.map((item) => item.name);
return senderNames.length > 3
? `${senderNames.slice(0, 3).join(", ")}...`
: senderNames.join(", ");
}
2 changes: 1 addition & 1 deletion apps/web/utils/cold-email/is-cold-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export async function isColdEmail({
const hasPreviousEmail =
email.date && email.id
? await provider.hasPreviousCommunicationsWithSenderOrDomain({
from: email.from,
from: extractEmailAddress(email.from) || email.from,
date: email.date,
messageId: email.id,
})
Expand Down
106 changes: 72 additions & 34 deletions apps/web/utils/email/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ export class GmailProvider implements EmailProvider {
}

async archiveMessage(messageId: string): Promise<void> {
const log = logger.with({
action: "archiveMessage",
messageId,
});

try {
await this.client.users.messages.modify({
userId: "me",
Expand All @@ -250,19 +255,21 @@ export class GmailProvider implements EmailProvider {
},
});

logger.info("Message archived successfully", {
messageId,
});
log.info("Message archived successfully");
} catch (error) {
logger.error("Failed to archive message", {
messageId,
log.error("Failed to archive message", {
error: error instanceof Error ? error.message : error,
});
throw error;
}
}

private async archiveMessagesBulk(messageIds: string[]): Promise<void> {
const log = logger.with({
action: "archiveMessagesBulk",
messageIds: messageIds,
});

try {
await this.client.users.messages.batchModify({
userId: "me",
Expand All @@ -272,8 +279,7 @@ export class GmailProvider implements EmailProvider {
},
});
} catch (error) {
logger.error("Failed to archive messages bulk", {
messageIds,
log.error("Failed to archive messages bulk", {
error: error instanceof Error ? error.message : error,
});
throw error;
Expand All @@ -286,6 +292,13 @@ export class GmailProvider implements EmailProvider {
ownerEmail: string,
emailAccountId: string,
): Promise<void> {
const log = logger.with({
action: "archiveMessagesFromSenders",
emailAccountId,
email: ownerEmail,
sendersCount: senders.length,
});

if (senders.length === 0) return;

for (const sender of senders) {
Expand Down Expand Up @@ -343,7 +356,7 @@ export class GmailProvider implements EmailProvider {

nextPageToken = token;
} catch (error) {
logger.error("Failed to archive messages from sender", {
log.error("Failed to archive messages from sender", {
sender,
error: error instanceof Error ? error.message : error,
});
Expand All @@ -353,14 +366,21 @@ export class GmailProvider implements EmailProvider {
} while (nextPageToken);
}

logger.info("Completed bulk archive from senders");
log.info("Completed bulk archive from senders");
}

private async trashThreadsFromSenders(
senders: string[],
ownerEmail: string,
emailAccountId: string,
): Promise<void> {
const log = logger.with({
action: "bulkTrashFromSenders",
emailAccountId,
email: ownerEmail,
sendersCount: senders.length,
});

if (senders.length === 0) {
return;
}
Expand Down Expand Up @@ -394,7 +414,7 @@ export class GmailProvider implements EmailProvider {

nextPageToken = token;
} catch (error) {
logger.error("Failed to get messages from sender", {
log.error("Failed to get messages from sender", {
sender,
error: error instanceof Error ? error.message : error,
});
Expand All @@ -412,7 +432,7 @@ export class GmailProvider implements EmailProvider {
await this.trashThread(threadId, ownerEmail, "automation");
successfullyTrashedThreadIds.add(threadId);
} catch (error) {
logger.error("Failed to trash thread for sender", {
log.error("Failed to trash thread for sender", {
sender,
threadId,
error: error instanceof Error ? error.message : error,
Expand Down Expand Up @@ -450,7 +470,7 @@ export class GmailProvider implements EmailProvider {

await Promise.all(promises);
} catch (error) {
logger.error("Failed to track trash operation for sender", {
log.error("Failed to track trash operation for sender", {
sender,
error: error instanceof Error ? error.message : error,
});
Expand All @@ -459,7 +479,7 @@ export class GmailProvider implements EmailProvider {
}
}

logger.info("Completed bulk trash from senders");
log.info("Completed bulk trash from senders");
}

async bulkArchiveFromSenders(
Expand Down Expand Up @@ -504,6 +524,13 @@ export class GmailProvider implements EmailProvider {
labelId: string;
labelName: string | null;
}): Promise<{ usedFallback?: boolean; actualLabelId?: string }> {
const log = logger.with({
action: "labelMessage",
messageId,
labelId,
labelName,
});

try {
await labelMessage({
gmail: this.client,
Expand All @@ -522,10 +549,7 @@ export class GmailProvider implements EmailProvider {
errorMessage.includes("labelId not found")) &&
labelName
) {
logger.warn("Label not found by ID, trying to get or create by name", {
labelId,
labelName,
});
log.warn("Label not found by ID, trying to get or create by name");

const label = await getOrCreateLabel({
gmail: this.client,
Expand Down Expand Up @@ -562,14 +586,22 @@ export class GmailProvider implements EmailProvider {
userEmail: string,
executedRule?: { id: string; threadId: string; emailAccountId: string },
): Promise<{ draftId: string }> {
const log = logger.with({
action: "draftEmail",
email: userEmail,
executedRuleId: executedRule?.id,
threadId: executedRule?.threadId,
messageId: email.id,
});

if (executedRule) {
// Run draft creation and previous draft deletion in parallel
const [result] = await Promise.all([
draftEmail(this.client, email, args, userEmail),
handlePreviousDraftDeletion({
client: this,
executedRule,
logger,
logger: log,
}),
]);
return { draftId: result.data.id || "" };
Expand Down Expand Up @@ -640,11 +672,16 @@ export class GmailProvider implements EmailProvider {
}

async blockUnsubscribedEmail(messageId: string): Promise<void> {
const log = logger.with({
action: "blockUnsubscribedEmail",
messageId,
});

const unsubscribeLabel =
await this.getOrCreateInboxZeroLabel("unsubscribed");

if (unsubscribeLabel?.id) {
logger.warn("Unsubscribe label not found", { messageId });
log.warn("Unsubscribe label not found");
}

await labelMessage({
Expand Down Expand Up @@ -927,19 +964,23 @@ export class GmailProvider implements EmailProvider {
}

async checkIfReplySent(senderEmail: string): Promise<boolean> {
const log = logger.with({
action: "checkIfReplySent",
sender: senderEmail,
});

try {
const query = `from:me to:${senderEmail} label:sent`;
const response = await getMessages(this.client, {
query,
maxResults: 1,
});
const sent = (response.messages?.length ?? 0) > 0;
logger.info("Checked for sent reply", { senderEmail, sent });
log.info("Checked for sent reply", { sent });
return sent;
} catch (error) {
logger.error("Error checking if reply was sent", {
log.error("Error checking if reply was sent", {
error,
senderEmail,
});
return true; // Default to true on error (safer for TO_REPLY filtering)
}
Expand All @@ -949,12 +990,15 @@ export class GmailProvider implements EmailProvider {
senderEmail: string,
threshold: number,
): Promise<number> {
const log = logger.with({
action: "countReceivedMessages",
sender: senderEmail,
threshold,
});

try {
const query = `from:${senderEmail}`;
logger.info("Checking received message count", {
senderEmail,
threshold,
});
log.info("Checking received message count");

// Fetch up to the threshold number of message IDs.
const response = await getMessages(this.client, {
Expand All @@ -963,16 +1007,10 @@ export class GmailProvider implements EmailProvider {
});
const count = response.messages?.length ?? 0;

logger.info("Received message count check result", {
senderEmail,
count,
});
log.info("Received message count check result", { count });
return count;
} catch (error) {
logger.error("Error counting received messages", {
error,
senderEmail,
});
log.error("Error counting received messages", { error });
return 0; // Default to 0 on error
}
}
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.18.13
v2.18.14
Loading