From 2004b1e700f3f9a646b99b08d18e0347571e5b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C4=81rti=C5=86=C5=A1=20Bru=C5=86enieks?= Date: Wed, 20 Nov 2024 20:50:26 +0200 Subject: [PATCH 01/30] Update README.md fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10394d9..077dbef 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ https://github.com/kawamataryo/sky-follower-bridge/assets/11070996/67bdd228-dc67 ## 🚨 Limitations -- User search may fail due to late limit in Bluesky's API. In this case, please wait for 2 to 3 minutes and execute the search again. +- User search may fail due to rate limit in Bluesky's API. In this case, please wait for 2 to 3 minutes and execute the search again. ## Development From a14e5395e566a10cae14d74dd2bc7621cbd27bbd Mon Sep 17 00:00:00 2001 From: kawamataryo Date: Wed, 20 Nov 2024 20:07:44 +0900 Subject: [PATCH 02/30] 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; From 90ce635799714dfd2cc1494140b3555104441a18 Mon Sep 17 00:00:00 2001 From: kawamataryo Date: Fri, 22 Nov 2024 20:15:20 +0900 Subject: [PATCH 03/30] fix: bug on block all --- src/lib/hooks/useBskyUserManager.ts | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/hooks/useBskyUserManager.ts b/src/lib/hooks/useBskyUserManager.ts index 481083a..60e1c72 100644 --- a/src/lib/hooks/useBskyUserManager.ts +++ b/src/lib/hooks/useBskyUserManager.ts @@ -153,21 +153,21 @@ export const useBskyUserManager = () => { } const result = await bskyClient.current.block(user.did); resultUri = result.uri; + await setUsers((prev) => + prev.map((prevUser) => { + if (prevUser.did === user.did) { + return { + ...prevUser, + isBlocking: !prevUser.isBlocking, + blockingUri: resultUri ?? prevUser.blockingUri, + }; + } + return prevUser; + }), + ); + await wait(300); + actionCount++; } - await setUsers((prev) => - prev.map((prevUser) => { - if (prevUser.did === user.did) { - return { - ...prevUser, - isBlocking: !prevUser.isBlocking, - blockingUri: resultUri ?? prevUser.blockingUri, - }; - } - return prevUser; - }), - ); - await wait(300); - actionCount++; } return actionCount; }, [filteredUsers, actionMode, setUsers]); @@ -180,7 +180,7 @@ export const useBskyUserManager = () => { bskyClient.current = new BskyServiceWorkerClient(session); setActionMode( MESSAGE_NAME_TO_ACTION_MODE_MAP[ - result[STORAGE_KEYS.BSKY_MESSAGE_NAME] + result[STORAGE_KEYS.BSKY_MESSAGE_NAME] ], ); }, From b2620dd28c2ccb1be1a47275fce084f6f8fa9e24 Mon Sep 17 00:00:00 2001 From: kawamataryo Date: Fri, 22 Nov 2024 20:24:39 +0900 Subject: [PATCH 04/30] chore: display version --- src/popup.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/popup.tsx b/src/popup.tsx index 26e588e..9617ffc 100644 --- a/src/popup.tsx +++ b/src/popup.tsx @@ -1,5 +1,6 @@ import { type FormEvent, useCallback, useEffect, useState } from "react"; import { P, match } from "ts-pattern"; +import packageJson from "../package.json"; import "./style.css"; @@ -213,7 +214,8 @@ function IndexPopup() { - Sky Follower Bridge + Sky Follower Bridge{" "} + v{packageJson.version}