171 lines
4.6 KiB
TypeScript
171 lines
4.6 KiB
TypeScript
import { onMounted, onUnmounted, ref } from 'vue';
|
|
import { platformConfigs } from '@/config/platforms';
|
|
import type { CrawlTaskState } from '@/types';
|
|
import { sendBackgroundMessage } from '@/shared/message';
|
|
|
|
const CRAWL_TASK_STORAGE_KEY = 'crawlTaskState';
|
|
const ACTIVE_STATUSES = new Set(['running', 'paused']);
|
|
|
|
export const useScan = () => {
|
|
const selectedPlatformId = ref(platformConfigs[0]?.id ?? '');
|
|
const isScanning = ref<boolean>(false);
|
|
const crawlState = ref<CrawlTaskState | null>(null);
|
|
const elapsedSeconds = ref<number>(0);
|
|
|
|
let timer: number | undefined;
|
|
|
|
const handleScan = async () => {
|
|
if (isScanning.value) {
|
|
return;
|
|
}
|
|
|
|
isScanning.value = true;
|
|
|
|
try {
|
|
ensureElapsedTimer();
|
|
|
|
const response = await sendBackgroundMessage<CrawlTaskState>({
|
|
action: 'START_CRAWL',
|
|
payload: { platformId: selectedPlatformId.value },
|
|
});
|
|
|
|
if (response.ok) {
|
|
syncCrawlState(response.data ?? null);
|
|
} else {
|
|
console.error('[crawl] start failed', response.error);
|
|
}
|
|
} finally {
|
|
isScanning.value = false;
|
|
}
|
|
};
|
|
|
|
const handleCancelCrawl = async () => {
|
|
const response = await sendBackgroundMessage<CrawlTaskState | null>({ action: 'CANCEL_CRAWL' });
|
|
|
|
if (response.ok) {
|
|
syncCrawlState(response.data ?? null);
|
|
return;
|
|
}
|
|
|
|
console.error('[crawl] cancel failed', response.error);
|
|
await refreshCrawlState();
|
|
};
|
|
|
|
const handleResumeCrawl = async () => {
|
|
const response = await sendBackgroundMessage<CrawlTaskState | null>({ action: 'RESUME_CRAWL' });
|
|
|
|
if (response.ok) {
|
|
syncCrawlState(response.data ?? null);
|
|
return;
|
|
}
|
|
|
|
console.error('[crawl] resume failed', response.error);
|
|
await refreshCrawlState();
|
|
};
|
|
|
|
const handleDismissCrawl = async () => {
|
|
const response = await sendBackgroundMessage<CrawlTaskState | null>({ action: 'DISMISS_CRAWL' });
|
|
|
|
if (response.ok) {
|
|
syncCrawlState(response.data ?? null);
|
|
return;
|
|
}
|
|
|
|
console.error('[crawl] dismiss failed', response.error);
|
|
await refreshCrawlState();
|
|
};
|
|
|
|
function syncCrawlState(state: CrawlTaskState | null) {
|
|
crawlState.value = state;
|
|
updateSeconds();
|
|
|
|
if (state && ACTIVE_STATUSES.has(state.status)) {
|
|
ensureElapsedTimer();
|
|
return;
|
|
}
|
|
|
|
clearElapsedTimer();
|
|
}
|
|
|
|
function ensureElapsedTimer() {
|
|
if (timer !== undefined) {
|
|
return;
|
|
}
|
|
|
|
timer = window.setInterval(() => {
|
|
updateSeconds();
|
|
}, 1000);
|
|
}
|
|
|
|
function clearElapsedTimer() {
|
|
if (timer === undefined) {
|
|
return;
|
|
}
|
|
|
|
window.clearInterval(timer);
|
|
timer = undefined;
|
|
}
|
|
|
|
function updateSeconds() {
|
|
if (!crawlState.value) {
|
|
elapsedSeconds.value = 0;
|
|
return;
|
|
}
|
|
|
|
elapsedSeconds.value = Math.max(0, Math.floor((Date.now() - crawlState.value.startedAt) / 1000));
|
|
}
|
|
|
|
async function refreshCrawlState() {
|
|
const response = await sendBackgroundMessage<CrawlTaskState | null>({ action: 'GET_CRAWL_STATE' });
|
|
|
|
if (response.ok) {
|
|
syncCrawlState(response.data ?? null);
|
|
}
|
|
}
|
|
|
|
function handleStorageChanged(changes: Record<string, chrome.storage.StorageChange>, areaName: string) {
|
|
if (areaName !== 'local') {
|
|
return;
|
|
}
|
|
|
|
const change = changes[CRAWL_TASK_STORAGE_KEY];
|
|
|
|
if (!change) {
|
|
return;
|
|
}
|
|
|
|
syncCrawlState(isCrawlTaskState(change.newValue) ? change.newValue : null);
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await refreshCrawlState();
|
|
|
|
if (typeof chrome !== 'undefined' && chrome.storage?.onChanged) {
|
|
chrome.storage.onChanged.addListener(handleStorageChanged);
|
|
}
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
clearElapsedTimer();
|
|
|
|
if (typeof chrome !== 'undefined' && chrome.storage?.onChanged) {
|
|
chrome.storage.onChanged.removeListener(handleStorageChanged);
|
|
}
|
|
});
|
|
|
|
return {
|
|
selectedPlatformId,
|
|
isScanning,
|
|
crawlState,
|
|
handleScan,
|
|
handleCancelCrawl,
|
|
handleResumeCrawl,
|
|
handleDismissCrawl,
|
|
elapsedSeconds,
|
|
};
|
|
};
|
|
|
|
function isCrawlTaskState(value: unknown): value is CrawlTaskState {
|
|
return typeof value === 'object' && value !== null && 'id' in value && 'steps' in value;
|
|
}
|