refactor: migrate list search to x's service

This commit is contained in:
kawamataryo 2024-11-30 16:42:43 +09:00
parent 4d917f0e98
commit 4229a39aae
6 changed files with 36 additions and 70 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "sky-follower-bridge", "name": "sky-follower-bridge",
"version": "1.3.0", "version": "1.4.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "sky-follower-bridge", "name": "sky-follower-bridge",
"version": "1.3.0", "version": "1.4.1",
"dependencies": { "dependencies": {
"@atproto/api": "^0.13.12", "@atproto/api": "^0.13.12",
"@changesets/cli": "^2.27.1", "@changesets/cli": "^2.27.1",

View File

@ -110,3 +110,5 @@ export const BSKY_DOMAIN =
export const BSKY_PROFILE_LABEL = { export const BSKY_PROFILE_LABEL = {
IMPERSONATION: "impersonation", IMPERSONATION: "impersonation",
} as const; } as const;
export const DEFAULT_LIST_NAME = "Imported List from X";

View File

@ -1,6 +1,3 @@
import type { CrawledUserInfo } from "~types";
import { BSKY_DOMAIN } from "./constants";
export const getUserCells = ({ export const getUserCells = ({
queryParam, queryParam,
filterInsertedElement, filterInsertedElement,
@ -22,27 +19,12 @@ export const getUserCells = ({
return Array.from(userCells); return Array.from(userCells);
}; };
export const extractUserData = (userCell: Element): CrawledUserInfo => { export const scrapeListNameFromPage = (): string => {
const anchors = Array.from(userCell.querySelectorAll("a")); const listNameElement = document.querySelector(
const [avatarEl, displayNameEl] = anchors; 'div[aria-label="Timeline: List"] span',
const accountName = avatarEl?.getAttribute("href")?.replace("/", ""); );
const accountNameRemoveUnderscore = accountName.replaceAll("_", ""); // bsky does not allow underscores in handle, so remove them. if (listNameElement) {
const accountNameReplaceUnderscore = accountName.replaceAll("_", "-"); return listNameElement.textContent.trim();
const displayName = displayNameEl?.textContent; }
const bskyHandle = return "Imported List from X";
userCell.textContent?.match(
new RegExp(`([^/\\s]+\\.${BSKY_DOMAIN})`),
)?.[1] ??
userCell.textContent
?.match(/bsky\.app\/profile\/([^/\s]+)…?/)?.[1]
?.replace("…", "") ??
"";
return {
accountName,
displayName,
accountNameRemoveUnderscore,
accountNameReplaceUnderscore,
bskyHandle,
};
}; };

View File

@ -5,6 +5,7 @@ import { BskyServiceWorkerClient } from "~lib/bskyServiceWorkerClient";
import { import {
ACTION_MODE, ACTION_MODE,
BSKY_USER_MATCH_TYPE, BSKY_USER_MATCH_TYPE,
DEFAULT_LIST_NAME,
MESSAGE_NAME_TO_ACTION_MODE_MAP, MESSAGE_NAME_TO_ACTION_MODE_MAP,
STORAGE_KEYS, STORAGE_KEYS,
} from "~lib/constants"; } from "~lib/constants";
@ -21,13 +22,6 @@ export const useBskyUserManager = () => {
}, },
(v) => (v === undefined ? [] : v), (v) => (v === undefined ? [] : v),
); );
const [listName, setListName] = React.useState<string>("");
React.useEffect(() => {
chrome.storage.local.get("listName", (result) => {
const name = result.listName || "Imported List from X";
setListName(name);
});
}, []);
const bskyClient = React.useRef<BskyServiceWorkerClient | null>(null); const bskyClient = React.useRef<BskyServiceWorkerClient | null>(null);
const [actionMode, setActionMode] = React.useState< const [actionMode, setActionMode] = React.useState<
@ -128,8 +122,12 @@ export const useBskyUserManager = () => {
// Import list // Import list
const importList = React.useCallback(async () => { const importList = React.useCallback(async () => {
if (!bskyClient.current) return; if (!bskyClient.current) return;
const storage = new Storage({
area: "local",
});
const listName = await storage.get(STORAGE_KEYS.LIST_NAME);
const listUri = await bskyClient.current.createListAndAddUsers({ const listUri = await bskyClient.current.createListAndAddUsers({
name: listName, name: listName || DEFAULT_LIST_NAME,
description: "List imported via Sky Follower Bridge", description: "List imported via Sky Follower Bridge",
userDids: filteredUsers.map((user) => user.did), userDids: filteredUsers.map((user) => user.did),
}); });
@ -137,7 +135,7 @@ export const useBskyUserManager = () => {
// const myProfile = await bskyClient.current.getMyProfile(); // const myProfile = await bskyClient.current.getMyProfile();
// return `https://bsky.app/profile/${myProfile.handle}/lists/${listUri}`; // return `https://bsky.app/profile/${myProfile.handle}/lists/${listUri}`;
return "https://bsky.app/lists"; return "https://bsky.app/lists";
}, [filteredUsers, listName]); }, [filteredUsers]);
// Follow All // Follow All
const followAll = React.useCallback(async () => { const followAll = React.useCallback(async () => {
@ -247,7 +245,6 @@ export const useBskyUserManager = () => {
return { return {
handleClickAction, handleClickAction,
users, users,
listName,
actionMode, actionMode,
matchTypeFilter, matchTypeFilter,
changeMatchTypeFilter, changeMatchTypeFilter,

View File

@ -10,7 +10,7 @@ import type { AbstractService } from "~lib/services/abstractService";
import { XService } from "~lib/services/xService"; import { XService } from "~lib/services/xService";
import type { BskyUser, CrawledUserInfo, MessageName } from "~types"; import type { BskyUser, CrawledUserInfo, MessageName } from "~types";
const getService = (messageName: string): AbstractService => { const getService = (messageName: MessageName): AbstractService => {
return match(messageName) return match(messageName)
.with( .with(
P.when((name) => P.when((name) =>
@ -25,16 +25,6 @@ const getService = (messageName: string): AbstractService => {
.otherwise(() => new XService(messageName)); .otherwise(() => new XService(messageName));
}; };
const scrapeListNameFromPage = (): string => {
const listNameElement = document.querySelector(
'div[aria-label="Timeline: List"] span',
);
if (listNameElement) {
return listNameElement.textContent.trim();
}
return "Imported List from X";
};
export const useRetrieveBskyUsers = () => { export const useRetrieveBskyUsers = () => {
const bskyClient = React.useRef<BskyServiceWorkerClient | null>(null); const bskyClient = React.useRef<BskyServiceWorkerClient | null>(null);
const [users, setUsers] = useStorage<BskyUser[]>( const [users, setUsers] = useStorage<BskyUser[]>(
@ -46,15 +36,6 @@ export const useRetrieveBskyUsers = () => {
}, },
(v) => (v === undefined ? [] : v), (v) => (v === undefined ? [] : v),
); );
const [listName, setListName] = useStorage<string>(
{
key: STORAGE_KEYS.LIST_NAME,
instance: new Storage({
area: "local",
}),
},
(v) => (v === undefined ? "" : v),
);
const [loading, setLoading] = React.useState(true); const [loading, setLoading] = React.useState(true);
const [errorMessage, setErrorMessage] = React.useState(""); const [errorMessage, setErrorMessage] = React.useState("");
const [isBottomReached, setIsBottomReached] = React.useState(false); const [isBottomReached, setIsBottomReached] = React.useState(false);
@ -104,7 +85,7 @@ export const useRetrieveBskyUsers = () => {
const abortControllerRef = React.useRef<AbortController | null>(null); const abortControllerRef = React.useRef<AbortController | null>(null);
const startRetrieveLoop = React.useCallback( const startRetrieveLoop = React.useCallback(
async (messageName: string) => { async (messageName: MessageName) => {
abortControllerRef.current = new AbortController(); abortControllerRef.current = new AbortController();
const signal = abortControllerRef.current.signal; const signal = abortControllerRef.current.signal;
@ -139,13 +120,6 @@ export const useRetrieveBskyUsers = () => {
[retrieveBskyUsers, isBottomReached], [retrieveBskyUsers, isBottomReached],
); );
React.useEffect(() => {
chrome.storage.local.set({
users: JSON.stringify(users),
listName: listName,
});
}, [users, listName]);
const stopRetrieveLoop = React.useCallback(() => { const stopRetrieveLoop = React.useCallback(() => {
if (abortControllerRef.current) { if (abortControllerRef.current) {
abortControllerRef.current.abort(); abortControllerRef.current.abort();
@ -169,8 +143,6 @@ export const useRetrieveBskyUsers = () => {
bskyClient.current = new BskyServiceWorkerClient(session); bskyClient.current = new BskyServiceWorkerClient(session);
setListName(scrapeListNameFromPage());
startRetrieveLoop(messageName).catch((e) => { startRetrieveLoop(messageName).catch((e) => {
console.error(e); console.error(e);
setErrorMessage(e.message); setErrorMessage(e.message);
@ -201,7 +173,6 @@ export const useRetrieveBskyUsers = () => {
return { return {
initialize, initialize,
users, users,
listName,
loading, loading,
errorMessage, errorMessage,
isRateLimitError, isRateLimitError,

View File

@ -1,10 +1,24 @@
import { Storage } from "@plasmohq/storage";
import { MESSAGE_NAMES } from "~lib/constants"; import { MESSAGE_NAMES } from "~lib/constants";
import { BSKY_DOMAIN } from "~lib/constants"; import { BSKY_DOMAIN } from "~lib/constants";
import { STORAGE_KEYS } from "~lib/constants";
import { scrapeListNameFromPage } from "~lib/domHelpers";
import { wait } from "~lib/utils"; import { wait } from "~lib/utils";
import type { CrawledUserInfo } from "~types"; import type { CrawledUserInfo, MessageName } from "~types";
import { AbstractService } from "./abstractService"; import { AbstractService } from "./abstractService";
export class XService extends AbstractService { export class XService extends AbstractService {
constructor(messageName: MessageName) {
// Set the list name in the storage if it's a list members page
if (messageName === MESSAGE_NAMES.SEARCH_BSKY_USER_ON_LIST_MEMBERS_PAGE) {
const listName = scrapeListNameFromPage();
new Storage({
area: "local",
}).set(STORAGE_KEYS.LIST_NAME, listName);
}
super(messageName);
}
extractUserData(userCell: Element): CrawledUserInfo { extractUserData(userCell: Element): CrawledUserInfo {
const anchors = Array.from(userCell.querySelectorAll("a")); const anchors = Array.from(userCell.querySelectorAll("a"));
const [avatarEl, displayNameEl] = anchors; const [avatarEl, displayNameEl] = anchors;