mirror of
https://github.com/snachodog/tok-to-insta-follower-bridge.git
synced 2025-04-10 14:11:22 -06:00
refacotr: create service
This commit is contained in:
parent
e07fbf91c3
commit
fb0b439137
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { BSKY_USER_MATCH_TYPE, MATCH_TYPE_LABEL_AND_COLOR } from "../constants";
|
||||
import type { MatchType } from "../../types";
|
||||
import { BSKY_USER_MATCH_TYPE, MATCH_TYPE_LABEL_AND_COLOR } from "../constants";
|
||||
|
||||
export type MatchTypeFilterValue = {
|
||||
[BSKY_USER_MATCH_TYPE.DESCRIPTION]: boolean;
|
||||
|
@ -3,14 +3,9 @@ import { Storage } from "@plasmohq/storage";
|
||||
import { useStorage } from "@plasmohq/storage/hook";
|
||||
import React from "react";
|
||||
import { BskyServiceWorkerClient } from "~lib/bskyServiceWorkerClient";
|
||||
import {
|
||||
type MESSAGE_NAMES,
|
||||
MESSAGE_NAME_TO_QUERY_PARAM_MAP,
|
||||
STORAGE_KEYS,
|
||||
} from "~lib/constants";
|
||||
import { extractUserData, getUserCells } from "~lib/domHelpers";
|
||||
import { type MESSAGE_NAMES, STORAGE_KEYS } from "~lib/constants";
|
||||
import { searchBskyUser } from "~lib/searchBskyUsers";
|
||||
import { wait } from "~lib/utils";
|
||||
import { XService } from "~lib/services/x";
|
||||
import type { CrawledUserInfo, MatchType } from "~types";
|
||||
|
||||
export type BskyUser = {
|
||||
@ -26,21 +21,8 @@ export type BskyUser = {
|
||||
blockingUri: string | null;
|
||||
};
|
||||
|
||||
const detectXUsers = (userCellQueryParam: string) => {
|
||||
const userCells = getUserCells({
|
||||
queryParam: userCellQueryParam,
|
||||
filterInsertedElement: true,
|
||||
});
|
||||
return userCells.map((userCell) => {
|
||||
return extractUserData(userCell);
|
||||
});
|
||||
};
|
||||
|
||||
export const useRetrieveBskyUsers = () => {
|
||||
const bskyClient = React.useRef<BskyServiceWorkerClient | null>(null);
|
||||
const [detectedXUsers, setDetectedXUsers] = React.useState<
|
||||
ReturnType<typeof detectXUsers>
|
||||
>([]);
|
||||
const [users, setUsers] = useStorage<BskyUser[]>(
|
||||
{
|
||||
key: STORAGE_KEYS.DETECTED_BSKY_USERS,
|
||||
@ -106,22 +88,7 @@ export const useRetrieveBskyUsers = () => {
|
||||
|
||||
let index = 0;
|
||||
|
||||
const queryParam = MESSAGE_NAME_TO_QUERY_PARAM_MAP[messageName];
|
||||
|
||||
let scrollElement: HTMLElement | Window;
|
||||
let modalScrollInterval: number;
|
||||
|
||||
if (messageName === "search_bsky_user_on_list_members_page") {
|
||||
// select the modal wrapper using viewport selector to avoid conflation with feed in the background
|
||||
scrollElement = document.querySelector(
|
||||
'div[data-viewportview="true"]',
|
||||
) as HTMLElement;
|
||||
// base interval off of intitial scroll height
|
||||
modalScrollInterval = scrollElement.scrollHeight;
|
||||
} else {
|
||||
// for other cases, use the window, no need to cache a scroll interval due to different window scroll logic
|
||||
scrollElement = window;
|
||||
}
|
||||
const xService = new XService(messageName);
|
||||
|
||||
// loop until we get to the bottom
|
||||
while (!isBottomReached) {
|
||||
@ -129,42 +96,15 @@ export const useRetrieveBskyUsers = () => {
|
||||
break;
|
||||
}
|
||||
|
||||
const data = detectXUsers(queryParam).filter((u) => {
|
||||
return !detectedXUsers.some((t) => t.accountName === u.accountName);
|
||||
});
|
||||
setDetectedXUsers((prev) => [...prev, ...data]);
|
||||
const data = xService.getCrawledUsers();
|
||||
await retrieveBskyUsers(data);
|
||||
|
||||
// handle scrolling pattern for both modal and window
|
||||
if (scrollElement instanceof HTMLElement) {
|
||||
scrollElement.scrollTop += modalScrollInterval;
|
||||
} else {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}
|
||||
const isEnd = await xService.performScrollAndCheckEnd();
|
||||
|
||||
// wait for fetching data by x
|
||||
await wait(3000);
|
||||
|
||||
// break if bottom is reached
|
||||
if (scrollElement instanceof HTMLElement) {
|
||||
if (
|
||||
scrollElement.scrollTop + scrollElement.clientHeight >=
|
||||
scrollElement.scrollHeight
|
||||
) {
|
||||
setIsBottomReached(true);
|
||||
setLoading(false);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const documentElement = document.documentElement;
|
||||
if (
|
||||
documentElement.scrollTop + documentElement.clientHeight >=
|
||||
documentElement.scrollHeight
|
||||
) {
|
||||
setIsBottomReached(true);
|
||||
setLoading(false);
|
||||
break;
|
||||
}
|
||||
if (isEnd) {
|
||||
setIsBottomReached(true);
|
||||
setLoading(false);
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
@ -174,13 +114,9 @@ export const useRetrieveBskyUsers = () => {
|
||||
}
|
||||
}
|
||||
},
|
||||
[retrieveBskyUsers, detectedXUsers, isBottomReached],
|
||||
[retrieveBskyUsers, isBottomReached],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
chrome.storage.local.set({ users: JSON.stringify(users) });
|
||||
}, [users]);
|
||||
|
||||
const stopRetrieveLoop = React.useCallback(() => {
|
||||
if (abortControllerRef.current) {
|
||||
abortControllerRef.current.abort();
|
||||
|
@ -0,0 +1,80 @@
|
||||
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";
|
||||
|
||||
export class XService {
|
||||
// 対象のdomを取得する処理
|
||||
messageName: MessageName;
|
||||
crawledUsers: Set<string>;
|
||||
|
||||
constructor(messageName: string) {
|
||||
// TODO: add type check
|
||||
this.messageName = messageName as MessageName;
|
||||
this.crawledUsers = new Set();
|
||||
}
|
||||
|
||||
private 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,
|
||||
};
|
||||
}
|
||||
|
||||
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<boolean> {
|
||||
const isListMembersPage =
|
||||
this.messageName === MESSAGE_NAMES.SEARCH_BSKY_USER_ON_LIST_MEMBERS_PAGE;
|
||||
|
||||
const scrollTarget = isListMembersPage
|
||||
? (document.querySelector('div[data-viewportview="true"]') as HTMLElement)
|
||||
: document.documentElement;
|
||||
|
||||
const initialScrollHeight = scrollTarget.scrollHeight;
|
||||
scrollTarget.scrollTop += initialScrollHeight;
|
||||
|
||||
await wait(3000);
|
||||
|
||||
const hasReachedEnd =
|
||||
scrollTarget.scrollTop + scrollTarget.clientHeight >=
|
||||
scrollTarget.scrollHeight;
|
||||
|
||||
return hasReachedEnd;
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
import type { BSKY_USER_MATCH_TYPE } from "~lib/constants";
|
||||
import type { BSKY_USER_MATCH_TYPE, MESSAGE_NAMES } from "~lib/constants";
|
||||
|
||||
export type MatchType =
|
||||
(typeof BSKY_USER_MATCH_TYPE)[keyof typeof BSKY_USER_MATCH_TYPE];
|
||||
|
||||
export type MESSAGE_NAME = (typeof MESSAGE_NAMES)[keyof typeof MESSAGE_NAMES];
|
||||
|
||||
export type BskyUser = {
|
||||
did: string;
|
||||
avatar: string;
|
||||
|
Loading…
x
Reference in New Issue
Block a user