2025-07-21 16:23:48 +08:00

91 lines
2.7 KiB
TypeScript

import { Tabs } from 'webextension-polyfill';
import { withErrorHandling } from '../utils/error-handler';
import { HomedepotDetailPageInjector } from '../web-injectors/homedepot';
import { BaseWorker } from './base';
import { taskOptionBase } from '../interfaces/common';
import { HomedepotEvents, HomedepotWorker } from '../interfaces/homedepot';
class HomedepotWorkerImpl
extends BaseWorker<HomedepotEvents & { interrupt: undefined }>
implements HomedepotWorker
{
private static _instance: HomedepotWorker | null = null;
public static getInstance() {
if (!HomedepotWorkerImpl._instance) {
HomedepotWorkerImpl._instance = new HomedepotWorkerImpl();
}
return HomedepotWorkerImpl._instance as HomedepotWorker;
}
protected constructor() {
super();
}
private async createNewTab(url?: string): Promise<Tabs.Tab> {
const tab = await browser.tabs.create({ url, active: true });
return tab;
}
@withErrorHandling
private async wanderingDetailPage(OSMID: string, review?: boolean) {
const url = `https://www.homedepot.com/p/${OSMID}`;
const tab = await this.createNewTab(url);
const injector = new HomedepotDetailPageInjector(tab);
const available = await injector.waitForPageLoad();
if (!available) {
setTimeout(() => {
browser.tabs.remove(tab.id!);
}, 1000);
return;
}
const info = await injector.getInfo();
const imageUrls = await injector.getImageUrls();
await this.emit('detail-item-collected', {
item: { OSMID, ...info, imageUrls, timestamp: dayjs().format('YYYY/M/D HH:mm:ss') },
});
if (!review) {
setTimeout(() => {
browser.tabs.remove(tab.id!);
}, 1000);
return;
}
await injector.waitForReviewLoad();
const reviews = await injector.getReviews();
await this.emit('review-collected', { reviews });
while (await injector.tryJumpToNextPage()) {
const reviews = await injector.getReviews();
await this.emit('review-collected', { reviews });
}
setTimeout(() => {
browser.tabs.remove(tab.id!);
}, 1000);
}
async runDetailPageTask(
OSMIDs: string[],
options: taskOptionBase & { review?: boolean } = {},
): Promise<void> {
const { progress, review } = options;
const remains = [...OSMIDs];
let interrupt = false;
const unsubscribe = this.on('interrupt', () => {
interrupt = true;
});
while (remains.length > 0 && !interrupt) {
const OSMIDs = remains.shift()!;
await this.wanderingDetailPage(OSMIDs, review);
progress && progress(remains);
}
unsubscribe();
}
stop(): Promise<void> {
return this.emit('interrupt');
}
}
export default {
getHomedepotWorker() {
return HomedepotWorkerImpl.getInstance();
},
};