From a14e5395e566a10cae14d74dd2bc7621cbd27bbd Mon Sep 17 00:00:00 2001 From: kawamataryo Date: Wed, 20 Nov 2024 20:07:44 +0900 Subject: [PATCH] refactor: create abstract class --- src/lib/hooks/useRetrieveBskyUsers.ts | 26 +++++++++++++----- src/lib/services/abstractService.ts | 37 ++++++++++++++++++++++++++ src/lib/services/{x.ts => xService.ts} | 36 +++---------------------- 3 files changed, 61 insertions(+), 38 deletions(-) create mode 100644 src/lib/services/abstractService.ts rename src/lib/services/{x.ts => xService.ts} (64%) diff --git a/src/lib/hooks/useRetrieveBskyUsers.ts b/src/lib/hooks/useRetrieveBskyUsers.ts index 697e32f..c35ec24 100644 --- a/src/lib/hooks/useRetrieveBskyUsers.ts +++ b/src/lib/hooks/useRetrieveBskyUsers.ts @@ -2,11 +2,25 @@ import type { AtpSessionData } from "@atproto/api"; import { Storage } from "@plasmohq/storage"; import { useStorage } from "@plasmohq/storage/hook"; import React from "react"; +import { match, P } from "ts-pattern"; import { BskyServiceWorkerClient } from "~lib/bskyServiceWorkerClient"; -import { type MESSAGE_NAMES, STORAGE_KEYS } from "~lib/constants"; +import { MESSAGE_NAMES, STORAGE_KEYS } from "~lib/constants"; import { searchBskyUser } from "~lib/searchBskyUsers"; -import { XService } from "~lib/services/x"; -import type { BskyUser, CrawledUserInfo } from "~types"; +import type { AbstractService } from "~lib/services/abstractService"; +import { XService } from "~lib/services/xService"; +import type { BskyUser, CrawledUserInfo, MessageName } from "~types"; + +const getService = (messageName: string): AbstractService => { + return match(messageName) + .with(P.when((name) => [ + MESSAGE_NAMES.SEARCH_BSKY_USER_ON_FOLLOW_PAGE, + MESSAGE_NAMES.SEARCH_BSKY_USER_ON_LIST_MEMBERS_PAGE, + MESSAGE_NAMES.SEARCH_BSKY_USER_ON_BLOCK_PAGE, + ].includes(name as MessageName)), + () => new XService(messageName), + ) + .otherwise(() => new XService(messageName)); +}; export const useRetrieveBskyUsers = () => { const bskyClient = React.useRef(null); @@ -75,7 +89,7 @@ export const useRetrieveBskyUsers = () => { let index = 0; - const xService = new XService(messageName); + const service = getService(messageName); // loop until we get to the bottom while (!isBottomReached) { @@ -83,10 +97,10 @@ export const useRetrieveBskyUsers = () => { break; } - const data = xService.getCrawledUsers(); + const data = service.getCrawledUsers(); await retrieveBskyUsers(data); - const isEnd = await xService.performScrollAndCheckEnd(); + const isEnd = await service.performScrollAndCheckEnd(); if (isEnd) { setIsBottomReached(true); diff --git a/src/lib/services/abstractService.ts b/src/lib/services/abstractService.ts new file mode 100644 index 0000000..024463a --- /dev/null +++ b/src/lib/services/abstractService.ts @@ -0,0 +1,37 @@ +import { MESSAGE_NAME_TO_QUERY_PARAM_MAP } from "~lib/constants"; +import type { CrawledUserInfo, MessageName } from "~types"; + +export abstract class AbstractService { + messageName: MessageName; + crawledUsers: Set; + + constructor(messageName: string) { + this.messageName = messageName as MessageName; + this.crawledUsers = new Set(); + } + + abstract extractUserData(userCell: Element): CrawledUserInfo; + + getCrawledUsers(): CrawledUserInfo[] { + const userCells = Array.from( + document.querySelectorAll( + MESSAGE_NAME_TO_QUERY_PARAM_MAP[this.messageName], + ), + ); + + const users = Array.from(userCells).map((userCell) => + this.extractUserData(userCell), + ) + .filter((user) => { + const isNewUser = !this.crawledUsers.has(user.accountName); + if (isNewUser) { + this.crawledUsers.add(user.accountName); + } + return isNewUser; + }); + + return users; + } + + abstract performScrollAndCheckEnd(): Promise; +} diff --git a/src/lib/services/x.ts b/src/lib/services/xService.ts similarity index 64% rename from src/lib/services/x.ts rename to src/lib/services/xService.ts index 6b049db..206271e 100644 --- a/src/lib/services/x.ts +++ b/src/lib/services/xService.ts @@ -1,20 +1,11 @@ import { MESSAGE_NAMES } from "~lib/constants"; import { BSKY_DOMAIN, MESSAGE_NAME_TO_QUERY_PARAM_MAP } from "~lib/constants"; import { wait } from "~lib/utils"; -import type { CrawledUserInfo, MessageName } from "~types"; +import { AbstractService } from "./abstractService"; +import type { CrawledUserInfo } from "~types"; -export class XService { - // 対象のdomを取得する処理 - messageName: MessageName; - crawledUsers: Set; - - constructor(messageName: string) { - // TODO: add type check - this.messageName = messageName as MessageName; - this.crawledUsers = new Set(); - } - - private extractUserData(userCell: Element): CrawledUserInfo { +export class XService extends AbstractService { + extractUserData(userCell: Element): CrawledUserInfo { const anchors = Array.from(userCell.querySelectorAll("a")); const [avatarEl, displayNameEl] = anchors; const accountName = avatarEl?.getAttribute("href")?.replace("/", ""); @@ -39,25 +30,6 @@ export class XService { }; } - getCrawledUsers(): CrawledUserInfo[] { - const userCells = Array.from( - document.querySelectorAll( - MESSAGE_NAME_TO_QUERY_PARAM_MAP[this.messageName], - ), - ); - - const users = userCells - .map((userCell) => this.extractUserData(userCell)) - .filter((user) => !this.crawledUsers.has(user.accountName)); - - this.crawledUsers = new Set([ - ...this.crawledUsers, - ...users.map((user) => user.accountName), - ]); - - return users; - } - async performScrollAndCheckEnd(): Promise { const isListMembersPage = this.messageName === MESSAGE_NAMES.SEARCH_BSKY_USER_ON_LIST_MEMBERS_PAGE;