mirror of
https://github.com/abhijithvijayan/web-extension-starter.git
synced 2026-01-30 09:48:12 +01:00
179 lines
6.4 KiB
TypeScript
179 lines
6.4 KiB
TypeScript
/**
|
|
* Popup Component
|
|
*
|
|
* This is the main UI that appears when the user clicks the extension icon.
|
|
* It communicates with both the content script and background script.
|
|
*
|
|
* Communication Flow:
|
|
* ┌─────────────────────────────────────────────────────────────────────┐
|
|
* │ POPUP │
|
|
* │ │
|
|
* │ On mount: │
|
|
* │ │
|
|
* │ 1. Popup ──GET_PAGE_INFO──► Content Script (via browser.tabs) │
|
|
* │ Popup ◄──PAGE_INFO_RESPONSE── Content Script │
|
|
* │ → Displays word count, link count, image count │
|
|
* │ │
|
|
* │ 2. Popup ──GET_VISIT_COUNT──► Background Script (via runtime) │
|
|
* │ Popup ◄──VISIT_COUNT_RESPONSE── Background Script │
|
|
* │ → Displays total pages tracked │
|
|
* │ │
|
|
* │ 3. Popup ──► browser.storage.local │
|
|
* │ → Reads username for greeting │
|
|
* └─────────────────────────────────────────────────────────────────────┘
|
|
*
|
|
* Note: browser.tabs.sendMessage() sends to content script in specific tab
|
|
* browser.runtime.sendMessage() sends to background script
|
|
*/
|
|
|
|
import {useEffect, useState} from 'react';
|
|
import type {FC} from 'react';
|
|
import browser, {Tabs} from 'webextension-polyfill';
|
|
import {getStorage} from '../utils/storage';
|
|
import {
|
|
PageInfo,
|
|
PageInfoResponseMessage,
|
|
VisitCountResponseMessage,
|
|
} from '../types/messages';
|
|
import {TabInfo} from './components/TabInfo/TabInfo';
|
|
import {FooterActions} from './components/FooterActions/FooterActions';
|
|
import styles from './Popup.module.scss';
|
|
|
|
function openWebPage(url: string): Promise<Tabs.Tab> {
|
|
return browser.tabs.create({url});
|
|
}
|
|
|
|
interface TabData {
|
|
title: string;
|
|
url: string;
|
|
favIconUrl?: string;
|
|
}
|
|
|
|
const Popup: FC = () => {
|
|
const [tabInfo, setTabInfo] = useState<TabData | null>(null);
|
|
const [pageInfo, setPageInfo] = useState<PageInfo | null>(null);
|
|
const [visitCount, setVisitCount] = useState<number>(0);
|
|
const [username, setUsername] = useState<string>('');
|
|
|
|
useEffect(() => {
|
|
// Get current tab info
|
|
browser.tabs.query({active: true, currentWindow: true}).then((tabs) => {
|
|
const tab = tabs[0];
|
|
if (tab) {
|
|
setTabInfo({
|
|
title: tab.title || 'Unknown',
|
|
url: tab.url || 'Unknown',
|
|
favIconUrl: tab.favIconUrl,
|
|
});
|
|
|
|
// Request page info from content script
|
|
if (tab.id) {
|
|
browser.tabs
|
|
.sendMessage(tab.id, {type: 'GET_PAGE_INFO'})
|
|
.then((response: unknown) => {
|
|
const res = response as PageInfoResponseMessage;
|
|
if (res?.data) {
|
|
setPageInfo(res.data);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// Content script might not be injected on this page
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
// Get visit count from background script
|
|
browser.runtime
|
|
.sendMessage({type: 'GET_VISIT_COUNT'})
|
|
.then((response: unknown) => {
|
|
const res = response as VisitCountResponseMessage;
|
|
if (res?.count !== undefined) {
|
|
setVisitCount(res.count);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// Background script might not be ready
|
|
});
|
|
|
|
// Get username from storage
|
|
getStorage(['username']).then(({username: storedUsername}) => {
|
|
setUsername(storedUsername);
|
|
});
|
|
}, []);
|
|
|
|
const handleReloadTab = async (): Promise<void> => {
|
|
const tabs = await browser.tabs.query({
|
|
active: true,
|
|
currentWindow: true,
|
|
});
|
|
const tab = tabs[0];
|
|
if (tab?.id) {
|
|
await browser.tabs.reload(tab.id);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<section className={styles.popup}>
|
|
<header className={styles.header}>
|
|
<h1 className={styles.title}>Web Extension Starter</h1>
|
|
{username && <p className={styles.greeting}>Hello, {username}!</p>}
|
|
</header>
|
|
|
|
{tabInfo && (
|
|
<div className={styles.tabCard}>
|
|
<TabInfo
|
|
title={tabInfo.title}
|
|
url={tabInfo.url}
|
|
favIconUrl={tabInfo.favIconUrl}
|
|
onReload={handleReloadTab}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Page Stats from Content Script */}
|
|
{pageInfo && (
|
|
<div className={styles.statsCard}>
|
|
<h3 className={styles.statsTitle}>Page Stats</h3>
|
|
<div className={styles.statsGrid}>
|
|
<div className={styles.statItem}>
|
|
<span className={styles.statValue}>{pageInfo.wordCount}</span>
|
|
<span className={styles.statLabel}>Words</span>
|
|
</div>
|
|
<div className={styles.statItem}>
|
|
<span className={styles.statValue}>{pageInfo.linkCount}</span>
|
|
<span className={styles.statLabel}>Links</span>
|
|
</div>
|
|
<div className={styles.statItem}>
|
|
<span className={styles.statValue}>{pageInfo.imageCount}</span>
|
|
<span className={styles.statLabel}>Images</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Visit Count from Background Script */}
|
|
<div className={styles.visitCard}>
|
|
<span className={styles.visitLabel}>Pages tracked:</span>
|
|
<span className={styles.visitCount}>{visitCount}</span>
|
|
</div>
|
|
|
|
<FooterActions
|
|
onSettings={(): Promise<Tabs.Tab> =>
|
|
openWebPage('/Options/options.html')
|
|
}
|
|
onGitHub={(): Promise<Tabs.Tab> =>
|
|
openWebPage(
|
|
'https://github.com/abhijithvijayan/web-extension-starter'
|
|
)
|
|
}
|
|
onSupport={(): Promise<Tabs.Tab> =>
|
|
openWebPage('https://www.buymeacoffee.com/abhijithvijayan')
|
|
}
|
|
/>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default Popup;
|