From c20244af3e0b750040d31c26341145876b8ea71a Mon Sep 17 00:00:00 2001 From: shlok sushil chorge Date: Wed, 19 Nov 2025 21:25:14 +0530 Subject: [PATCH] added archieve button and its function to it u just recheck it i am not --- public/background.js | 41 +++++ src/data/content_scripts/archiveAutomation.ts | 164 ++++++++++++++++++ src/data/content_scripts/content.ts | 19 +- src/presentation/apps/sidebar/App.tsx | 47 +++++ .../apps/sidebar/components/actionButton.tsx | 9 + src/presentation/providers/app_provider.tsx | 5 + 6 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 src/data/content_scripts/archiveAutomation.ts diff --git a/public/background.js b/public/background.js index c53b9f8..b772947 100644 --- a/public/background.js +++ b/public/background.js @@ -44,3 +44,44 @@ chrome.runtime.onInstalled.addListener(function (object) { // Shows an uninstall survey when extension is removed chrome.runtime.setUninstallURL("https://tally.so/r/w4yg5X"); + + + +// adding archiveAutomation content script +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === 'archiveEmails') { + handleArchiveEmails(message.data) + .then(result => sendResponse({ success: true, result })) + .catch(error => sendResponse({ success: false, error: error.message })); + return true; // Required for async response + } + // ... existing handlers +}); + +async function handleArchiveEmails(data: { sender: string }) { + const { sender } = data; + + try { + // Query the active Gmail tab + const tabs = await chrome.tabs.query({ + active: true, + currentWindow: true, + url: "*://mail.google.com/*" + }); + + if (!tabs[0]?.id) { + throw new Error('No active Gmail tab found'); + } + + // Execute archive operation in content script + const result = await chrome.tabs.sendMessage(tabs[0].id, { + action: 'performArchive', + sender: sender + }); + + return result; + } catch (error) { + console.error('Archive error:', error); + throw error; + } +} \ No newline at end of file diff --git a/src/data/content_scripts/archiveAutomation.ts b/src/data/content_scripts/archiveAutomation.ts new file mode 100644 index 0000000..e20c43c --- /dev/null +++ b/src/data/content_scripts/archiveAutomation.ts @@ -0,0 +1,164 @@ +export class ArchiveAutomation { +private readonly SELECTORS = { + searchBox: 'input[name="q"]', + searchButton: 'button[aria-label="Search Mail"]', + selectAllCheckbox: 'div[data-tooltip="Select"]', + archiveButton: 'div[data-tooltip="Archive"]', + emailRow: 'tr.zA', + loadingIndicator: 'div[role="progressbar"]' +}; + +async archiveEmailsFromSender(senderEmail: string): Promise { + try { + // Navigate to All Mail view to ensure we see all emails + await this.navigateToAllMail(); + + // Search for emails from sender + await this.searchBySender(senderEmail); + + // Wait for results to load + await this.waitForSearchResults(); + + // Count emails found + const emailCount = this.countEmailsInView(); + + if (emailCount === 0) { + return { + success: true, + archivedCount: 0, + error: 'No emails found from this sender' + }; + } + + // Select all emails + await this.selectAllEmails(); + + // Click archive button + await this.clickArchiveButton(); + + // Wait for archive to complete + await this.waitForArchiveComplete(); + + return { + success: true, + archivedCount: emailCount + }; + } catch (error) { + console.error('Archive automation error:', error); + return { + success: false, + archivedCount: 0, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +} + +private async navigateToAllMail(): Promise { + const allMailLink = document.querySelector('a[href="#all"]'); + if (allMailLink instanceof HTMLElement) { + allMailLink.click(); + await this.wait(1000); + } +} + +private async searchBySender(senderEmail: string): Promise { + const searchBox = document.querySelector(this.SELECTORS.searchBox) as HTMLInputElement; + if (!searchBox) { + throw new Error('Search box not found'); + } + + // Clear existing search + searchBox.value = ''; + searchBox.focus(); + + // Type search query + const searchQuery = `from:${senderEmail}`; + searchBox.value = searchQuery; + + // Trigger search + const event = new KeyboardEvent('keydown', { + key: 'Enter', + code: 'Enter', + keyCode: 13, + bubbles: true + }); + searchBox.dispatchEvent(event); + + await this.wait(1500); +} + +private async waitForSearchResults(): Promise { + // Wait for loading indicator to disappear + let attempts = 0; + const maxAttempts = 20; + + while (attempts < maxAttempts) { + const loading = document.querySelector(this.SELECTORS.loadingIndicator); + if (!loading) { + // Wait a bit more for results to stabilize + await this.wait(500); + return; + } + await this.wait(500); + attempts++; + } +} + +private countEmailsInView(): number { + const emailRows = document.querySelectorAll(this.SELECTORS.emailRow); + return emailRows.length; +} + +private async selectAllEmails(): Promise { + // Click the select all checkbox + const selectAllBtn = document.querySelector(this.SELECTORS.selectAllCheckbox) as HTMLElement; + if (!selectAllBtn) { + throw new Error('Select all button not found'); + } + + selectAllBtn.click(); + await this.wait(300); + + // Check if "Select all conversations" banner appears + const selectAllConversationsLink = Array.from(document.querySelectorAll('span')) + .find(el => el.textContent?.includes('Select all')); + + if (selectAllConversationsLink instanceof HTMLElement) { + selectAllConversationsLink.click(); + await this.wait(500); + } +} + +private async clickArchiveButton(): Promise { + const archiveBtn = document.querySelector(this.SELECTORS.archiveButton) as HTMLElement; + if (!archiveBtn) { + throw new Error('Archive button not found'); + } + + archiveBtn.click(); + await this.wait(500); +} + +private async waitForArchiveComplete(): Promise { + // Wait for the "archived" confirmation banner + let attempts = 0; + const maxAttempts = 20; + + while (attempts < maxAttempts) { + const banner = Array.from(document.querySelectorAll('span')) + .find(el => el.textContent?.toLowerCase().includes('archived')); + + if (banner) { + await this.wait(500); + return; + } + + await this.wait(500); + attempts++; + } +} + +private wait(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} diff --git a/src/data/content_scripts/content.ts b/src/data/content_scripts/content.ts index 00c0ff0..3fb90cd 100644 --- a/src/data/content_scripts/content.ts +++ b/src/data/content_scripts/content.ts @@ -1,6 +1,23 @@ -// src/data/content_scripts/content.ts + import { BrowserEmailService } from "../services/browser_email_service"; import { PageInteractionService } from "../services/page_interaction_service"; +import { ArchiveAutomation } from './archiveAutomation'; + +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (message.action === 'performArchive') { + const archiver = new ArchiveAutomation(); + archiver.archiveEmailsFromSender(message.sender) + .then(result => sendResponse(result)) + .catch(error => sendResponse({ + success: false, + archivedCount: 0, + error: error.message + })); + return true; // Required for async response + } + // ... existing handlers +}); + let currentAbortController: AbortController | null = null; diff --git a/src/presentation/apps/sidebar/App.tsx b/src/presentation/apps/sidebar/App.tsx index b61258c..ea9aeb2 100644 --- a/src/presentation/apps/sidebar/App.tsx +++ b/src/presentation/apps/sidebar/App.tsx @@ -1,4 +1,5 @@ import "./App.css"; +import { useState } from 'react'; import { useTheme } from "../../providers/theme_provider.tsx"; import { ActionButton } from "./components/actionButton.tsx"; import { ReloadButton } from "./components/reloadButton.tsx"; @@ -22,6 +23,52 @@ function App() { ); } +const handleArchive = async (sender: string) => { + setIsArchiving(true); + setArchiveStatus(''); + + try { + const response = await chrome.runtime.sendMessage({ + action: 'archiveEmails', + data: { sender } + }); + + if (response.success) { + setArchiveStatus(`Successfully archived ${response.result.archivedCount} emails`); + // Optionally refresh the sender list + await refreshSenders(); + } else { + setArchiveStatus(`Error: ${response.error}`); + } + } catch (error) { + console.error('Archive error:', error); + setArchiveStatus('Failed to archive emails'); + } finally { + setIsArchiving(false); + } +}; + +// Add Archive button in your table row JSX +// (Next to your Delete/Unsubscribe buttons) +