Skip to content

Commit

Permalink
refactor: migrate list search to x's service
Browse files Browse the repository at this point in the history
  • Loading branch information
kawamataryo committed Nov 30, 2024
1 parent 4d917f0 commit 4229a39
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 70 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,5 @@ export const BSKY_DOMAIN =
export const BSKY_PROFILE_LABEL = {
IMPERSONATION: "impersonation",
} as const;

export const DEFAULT_LIST_NAME = "Imported List from X";
34 changes: 8 additions & 26 deletions src/lib/domHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import type { CrawledUserInfo } from "~types";
import { BSKY_DOMAIN } from "./constants";

export const getUserCells = ({
queryParam,
filterInsertedElement,
Expand All @@ -22,27 +19,12 @@ export const getUserCells = ({
return Array.from(userCells);
};

export const extractUserData = (userCell: Element): CrawledUserInfo => {
const anchors = Array.from(userCell.querySelectorAll("a"));
const [avatarEl, displayNameEl] = anchors;
const accountName = avatarEl?.getAttribute("href")?.replace("/", "");
const accountNameRemoveUnderscore = accountName.replaceAll("_", ""); // bsky does not allow underscores in handle, so remove them.
const accountNameReplaceUnderscore = accountName.replaceAll("_", "-");
const displayName = displayNameEl?.textContent;
const bskyHandle =
userCell.textContent?.match(
new RegExp(`([^/\\s]+\\.${BSKY_DOMAIN})`),
)?.[1] ??
userCell.textContent
?.match(/bsky\.app\/profile\/([^/\s]+)…?/)?.[1]
?.replace("…", "") ??
"";

return {
accountName,
displayName,
accountNameRemoveUnderscore,
accountNameReplaceUnderscore,
bskyHandle,
};
export const scrapeListNameFromPage = (): string => {
const listNameElement = document.querySelector(
'div[aria-label="Timeline: List"] span',
);
if (listNameElement) {
return listNameElement.textContent.trim();
}
return "Imported List from X";
};
17 changes: 7 additions & 10 deletions src/lib/hooks/useBskyUserManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BskyServiceWorkerClient } from "~lib/bskyServiceWorkerClient";
import {
ACTION_MODE,
BSKY_USER_MATCH_TYPE,
DEFAULT_LIST_NAME,
MESSAGE_NAME_TO_ACTION_MODE_MAP,
STORAGE_KEYS,
} from "~lib/constants";
Expand All @@ -21,13 +22,6 @@ export const useBskyUserManager = () => {
},
(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 [actionMode, setActionMode] = React.useState<
Expand Down Expand Up @@ -128,16 +122,20 @@ export const useBskyUserManager = () => {
// Import list
const importList = React.useCallback(async () => {
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({
name: listName,
name: listName || DEFAULT_LIST_NAME,
description: "List imported via Sky Follower Bridge",
userDids: filteredUsers.map((user) => user.did),
});
// TODO: Commented out temporarily due to failure in Firefox
// const myProfile = await bskyClient.current.getMyProfile();
// return `https://bsky.app/profile/${myProfile.handle}/lists/${listUri}`;
return "https://bsky.app/lists";
}, [filteredUsers, listName]);
}, [filteredUsers]);

// Follow All
const followAll = React.useCallback(async () => {
Expand Down Expand Up @@ -247,7 +245,6 @@ export const useBskyUserManager = () => {
return {
handleClickAction,
users,
listName,
actionMode,
matchTypeFilter,
changeMatchTypeFilter,
Expand Down
33 changes: 2 additions & 31 deletions src/lib/hooks/useRetrieveBskyUsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ 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 => {
const getService = (messageName: MessageName): AbstractService => {
return match(messageName)
.with(
P.when((name) =>
Expand All @@ -25,16 +25,6 @@ const getService = (messageName: string): AbstractService => {
.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 = () => {
const bskyClient = React.useRef<BskyServiceWorkerClient | null>(null);
const [users, setUsers] = useStorage<BskyUser[]>(
Expand All @@ -46,15 +36,6 @@ export const useRetrieveBskyUsers = () => {
},
(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 [errorMessage, setErrorMessage] = React.useState("");
const [isBottomReached, setIsBottomReached] = React.useState(false);
Expand Down Expand Up @@ -104,7 +85,7 @@ export const useRetrieveBskyUsers = () => {

const abortControllerRef = React.useRef<AbortController | null>(null);
const startRetrieveLoop = React.useCallback(
async (messageName: string) => {
async (messageName: MessageName) => {
abortControllerRef.current = new AbortController();
const signal = abortControllerRef.current.signal;

Expand Down Expand Up @@ -139,13 +120,6 @@ export const useRetrieveBskyUsers = () => {
[retrieveBskyUsers, isBottomReached],
);

React.useEffect(() => {
chrome.storage.local.set({
users: JSON.stringify(users),
listName: listName,
});
}, [users, listName]);

const stopRetrieveLoop = React.useCallback(() => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
Expand All @@ -169,8 +143,6 @@ export const useRetrieveBskyUsers = () => {

bskyClient.current = new BskyServiceWorkerClient(session);

setListName(scrapeListNameFromPage());

startRetrieveLoop(messageName).catch((e) => {
console.error(e);
setErrorMessage(e.message);
Expand Down Expand Up @@ -201,7 +173,6 @@ export const useRetrieveBskyUsers = () => {
return {
initialize,
users,
listName,
loading,
errorMessage,
isRateLimitError,
Expand Down
16 changes: 15 additions & 1 deletion src/lib/services/xService.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { Storage } from "@plasmohq/storage";
import { MESSAGE_NAMES } 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 type { CrawledUserInfo } from "~types";
import type { CrawledUserInfo, MessageName } from "~types";
import { AbstractService } from "./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 {
const anchors = Array.from(userCell.querySelectorAll("a"));
const [avatarEl, displayNameEl] = anchors;
Expand Down

0 comments on commit 4229a39

Please sign in to comment.