diff --git a/package-lock.json b/package-lock.json index 916be75..5cbc420 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,21 @@ { "name": "sky-follower-bridge", - "version": "0.1.2", + "version": "0.1.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "sky-follower-bridge", - "version": "0.1.2", + "version": "0.1.3", "dependencies": { "@atproto/api": "^0.3.3", "@plasmohq/messaging": "^0.3.0", "@plasmohq/storage": "^1.6.0", "plasmo": "0.67.4", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "ts-pattern": "^5.0.5", + "vanjs-core": "^1.0.2" }, "devDependencies": { "@plasmohq/prettier-plugin-sort-imports": "3.6.4", @@ -8602,6 +8604,11 @@ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, + "node_modules/ts-pattern": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.0.5.tgz", + "integrity": "sha512-tL0w8U/pgaacOmkb9fRlYzWEUDCfVjjv9dD4wHTgZ61MjhuMt46VNWTG747NqW6vRzoWIKABVhFSOJ82FvXrfA==" + }, "node_modules/tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", @@ -8740,6 +8747,11 @@ "node": ">= 4" } }, + "node_modules/vanjs-core": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vanjs-core/-/vanjs-core-1.0.2.tgz", + "integrity": "sha512-c/ding4r+NRMobYJTLjVYyrqee51Zi1Er+8k1QV1vTZW4+e30/728DQOQh8yQc2VdZSrNJHYRWhW02C4NoiJgw==" + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -14533,6 +14545,11 @@ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, + "ts-pattern": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-5.0.5.tgz", + "integrity": "sha512-tL0w8U/pgaacOmkb9fRlYzWEUDCfVjjv9dD4wHTgZ61MjhuMt46VNWTG747NqW6vRzoWIKABVhFSOJ82FvXrfA==" + }, "tslib": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", @@ -14621,6 +14638,11 @@ "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" }, + "vanjs-core": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vanjs-core/-/vanjs-core-1.0.2.tgz", + "integrity": "sha512-c/ding4r+NRMobYJTLjVYyrqee51Zi1Er+8k1QV1vTZW4+e30/728DQOQh8yQc2VdZSrNJHYRWhW02C4NoiJgw==" + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/package.json b/package.json index 3969bd8..e2932fb 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ "@plasmohq/storage": "^1.6.0", "plasmo": "0.67.4", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "ts-pattern": "^5.0.5", + "vanjs-core": "^1.0.2" }, "devDependencies": { "@plasmohq/prettier-plugin-sort-imports": "3.6.4", @@ -26,10 +28,10 @@ "@types/react": "18.0.27", "@types/react-dom": "18.0.10", "autoprefixer": "^10.4.14", + "daisyui": "^2.51.6", "postcss": "^8.4.23", "prettier": "2.8.3", "tailwindcss": "^3.3.2", - "daisyui": "^2.51.6", "typescript": "4.9.4" }, "manifest": { diff --git a/src/content.ts b/src/content.ts index ea53c2f..0e76362 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,135 +1,71 @@ -import { isOutOfTopViewport } from './lib/domHelpers'; -import { BskyClient } from "./lib/bskyClient"; +import { BskyClient, BskyLoginParams } from "./lib/bskyClient"; import type { PlasmoCSConfig } from "plasmo" -import { MESSAGE_NAMES } from "~lib/constants"; -import { getAccountNameAndDisplayName, getUserCells, insertBskyProfileEl, insertNotFoundEl, insertReloadEl, removeReloadElIfExists } from "~lib/domHelpers"; -import { isSimilarUser } from "~lib/bskyHelpers"; +import { MESSAGE_NAMES, VIEWER_STATE } from "~lib/constants"; import "./style.content.css" -import { debugLog } from "~lib/utils"; +import { initialize, searchBskyUsers } from '~lib/searchAndInsertBskyUsers'; export const config: PlasmoCSConfig = { matches: ["https://twitter.com/*", "https://x.com/*"], all_frames: true } -let abortController = new AbortController(); - -const notFoundUserCache = new Set() - -const followerUrlMap = new Map() - -const initialize = async () => { - abortController.abort() - abortController = new AbortController() -} - - -const searchBskyUsers = async ({ - userId, - password -}) => { - removeReloadElIfExists() +const searchAndShowBskyUsers = async ({ + identifier, + password, + messageName, +}: BskyLoginParams & { messageName: string }) => { const agent = await BskyClient.createAgent({ - identifier: userId, - password: password, - }); - - const userCells = getUserCells() - debugLog(`userCells length: ${userCells.length}`) - - let index = 0 - for (const userCell of userCells) { - if(isOutOfTopViewport(userCell)) { - continue - } - const { twAccountName, twDisplayName } = getAccountNameAndDisplayName(userCell) - if (notFoundUserCache.has(twAccountName)) { - insertNotFoundEl(userCell) - continue - } - - const [searchResultByAccountName] = await agent.searchUser({ - term: twAccountName, - limit: 1, - }) - - // TODO: Refactor, this is duplicated - // first, search by account name - if (isSimilarUser(twDisplayName, searchResultByAccountName) || isSimilarUser(twAccountName, searchResultByAccountName)) { - insertBskyProfileEl({ - dom: userCell, - profile: searchResultByAccountName, - abortController, - followAction: async () => { - const result = await agent.follow(searchResultByAccountName.did); - followerUrlMap.set(searchResultByAccountName.did, result.uri) - }, - unfollowAction: async () => { - if(searchResultByAccountName?.viewer?.following) { - await agent.unfollow(searchResultByAccountName?.viewer?.following); - } else { - await agent.unfollow(followerUrlMap.get(searchResultByAccountName.did)); - } - }, - }) - } else { - // if not found, search by display name - const [searchResultByDisplayName] = await agent.searchUser({ - term: twDisplayName, - limit: 1, - }) - if (isSimilarUser(twDisplayName, searchResultByDisplayName) || isSimilarUser(twAccountName, searchResultByDisplayName)) { - insertBskyProfileEl({ - dom: userCell, - profile: searchResultByDisplayName, - abortController, - followAction: async () => { - const result = await agent.follow(searchResultByDisplayName.did); - followerUrlMap.set(searchResultByDisplayName.did, result.uri) - }, - unfollowAction: async () => { - if(searchResultByDisplayName?.viewer?.following) { - await agent.unfollow(searchResultByDisplayName?.viewer?.following); - } else { - await agent.unfollow(followerUrlMap.get(searchResultByDisplayName.did)); - } - }, - }) - } else { - insertNotFoundEl(userCell) - notFoundUserCache.add(twAccountName) - } - } - - index++ - if (process.env.NODE_ENV === "development" && index > 5) { - break - } - } - - // TODO: if there are more users, insert reload button - insertReloadEl(async () => { - await searchBskyUsers({ - userId, - password, - }) + identifier, + password, }) + switch (messageName) { + case MESSAGE_NAMES.SEARCH_BSKY_USER_ON_FOLLOW_PAGE: + await searchBskyUsers({ + agent, + btnLabel: { + add: "Follow", + remove: "Unfollow", + progressive: "Following", + }, + statusKey: VIEWER_STATE.FOLLOWING, + userCellQueryParam: '[data-testid="primaryColumn"] [data-testid="UserCell"]', + addQuery: async (arg: string) => await agent.follow(arg), + removeQuery: async (arg: string) => await agent.unfollow(arg), + }) + break + case MESSAGE_NAMES.SEARCH_BSKY_USER_ON_BLOCK_PAGE: + // TODO: If already blocked, don't show blocking state. because blocking user can't find. + await searchBskyUsers({ + agent, + btnLabel: { + add: "Block", + remove: "Unblock", + progressive: "Blocking", + }, + statusKey: VIEWER_STATE.BLOCKING, + userCellQueryParam: '[data-testid="UserCell"]', + addQuery: async (arg: string) => await agent.block(arg), + removeQuery: async (arg: string) => await agent.unblock(arg), + }) + break + } } - chrome.runtime.onMessage.addListener((message, _, sendResponse) => { - if (message.name === MESSAGE_NAMES.SEARCH_BSKY_USER) { + if (Object.values(MESSAGE_NAMES).includes(message.name)) { initialize() - - searchBskyUsers({ - userId: message.body.userId, - password: message.body.password - }).then(() => { - sendResponse({ hasError: false }) - }).catch((e) => { - sendResponse({ hasError: true, message: e.toString() }) + searchAndShowBskyUsers({ + identifier: message.body.userId, + password: message.body.password, + messageName: message.name, }) + .then(() => { + sendResponse({ hasError: false }) + }) + .catch((e) => { + sendResponse({ hasError: true, message: e.toString() }) + }); return true } return false diff --git a/src/lib/bskyClient.ts b/src/lib/bskyClient.ts index 4d12554..773e65f 100644 --- a/src/lib/bskyClient.ts +++ b/src/lib/bskyClient.ts @@ -1,8 +1,19 @@ -import { BskyAgent } from "@atproto/api"; +import { AtUri, BskyAgent } from "@atproto/api"; + +export type BskyLoginParams = { + identifier: string; + password: string; +} export class BskyClient { private service = "https://bsky.social"; + me: { + did: string; + handle: string; + email: string; + }; agent: BskyAgent; + private constructor() { this.agent = new BskyAgent({ service: this.service }); } @@ -10,12 +21,14 @@ export class BskyClient { public static async createAgent({ identifier, password, - }: { - identifier: string; - password: string; - }): Promise { + }: BskyLoginParams): Promise { const client = new BskyClient(); - await client.agent.login({ identifier, password }); + const {data} = await client.agent.login({ identifier, password }); + client.me = { + did: data.did, + handle: data.handle, + email: data.email, + } return client; } @@ -40,4 +53,25 @@ export class BskyClient { public unfollow = async (followUri: string) => { return await this.agent.deleteFollow(followUri); } + + public block = async (subjectDid: string) => { + return await this.agent.app.bsky.graph.block.create({ + repo: this.me.did, + collection: "app.bsky.graph.block", + }, + { + subject: subjectDid, + createdAt: new Date().toISOString(), + }) + } + + public unblock = async (blockUri: string) => { + // TODO: unblock is not working. Need to fix it. + const {rkey} = new AtUri(blockUri) + return await this.agent.app.bsky.graph.block.delete({ + repo: this.me.did, + collection: "app.bsky.graph.block", + rkey, + }); + } } diff --git a/src/lib/constants.ts b/src/lib/constants.ts index e999572..9a05a9d 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,21 +1,25 @@ export const MESSAGE_NAMES = { - SEARCH_BSKY_USER: "search_bsky_user" + SEARCH_BSKY_USER_ON_FOLLOW_PAGE: "search_bsky_user_on_follow_page", + SEARCH_BSKY_USER_ON_BLOCK_PAGE: "search_bsky_user_on_block_page", } const STORAGE_PREFIX = "sky_follower_bridge_storage" export const STORAGE_KEYS = { BSKY_USER_ID: `${STORAGE_PREFIX}_bsky_password`, BSKY_PASSWORD: `${STORAGE_PREFIX}_bsky_user`, -} +} as const -export const TARGET_URLS_REGEX = [ - /^https:\/\/twitter\.com\/[^/]+\/following$/, - /^https:\/\/twitter\.com\/[^/]+\/followers$/, - /^https:\/\/x\.com\/[^/]+\/following$/, - /^https:\/\/x\.com\/[^/]+\/followers$/, -] +export const TARGET_URLS_REGEX = { + FOLLOW: /^https:\/\/(twitter|x)\.com\/[^/]+\/follow/, + BLOCK: /^https:\/\/(twitter|x)\.com\/settings\/blocked/, +} as const export const MESSAGE_TYPE = { ERROR: "error", SUCCESS: "success", } as const + +export const VIEWER_STATE = { + BLOCKING: "blocking", + FOLLOWING: "following", +} as const diff --git a/src/lib/domHelpers.ts b/src/lib/domHelpers.ts index a25b372..aab02ec 100644 --- a/src/lib/domHelpers.ts +++ b/src/lib/domHelpers.ts @@ -1,7 +1,13 @@ -import type { ProfileView } from "@atproto/api/dist/client/types/app/bsky/actor/defs" +import type { ProfileView, ViewerState } from "@atproto/api/dist/client/types/app/bsky/actor/defs" -export const getUserCells = ({ filterInsertedElement }: { filterInsertedElement: boolean } = { filterInsertedElement: true }) => { - const userCells = document.querySelectorAll('[data-testid="primaryColumn"] [data-testid="UserCell"]'); +export type UserCellBtnLabel = { + add: string, + remove: string, + progressive: string, +} + +export const getUserCells = ({ queryParam, filterInsertedElement }: { queryParam: string, filterInsertedElement: boolean }) => { + const userCells = document.querySelectorAll(queryParam); // filter out already inserted elements if (filterInsertedElement) { @@ -46,9 +52,19 @@ export const getAccountNameAndDisplayName = (userCell: Element) => { const twDisplayName = displayNameEl?.textContent return { twAccountName, twDisplayName } } -export const insertBskyProfileEl = ({ dom, profile, abortController, followAction, unfollowAction }: { dom: Element, profile: ProfileView, abortController: AbortController, followAction: () => void, unfollowAction: () => void }) => { + +// TODO: vanjsを使ってdom操作を描き直したい +export const insertBskyProfileEl = ({ dom, profile, statusKey, btnLabel, abortController, followAction, unfollowAction }: { + dom: Element, + profile: ProfileView, + statusKey: keyof ViewerState, + btnLabel: UserCellBtnLabel, + abortController: AbortController, + followAction: () => void, + unfollowAction: () => void +}) => { const avatarEl = profile.avatar ? `` : "
" - const followButtonEl = profile.viewer?.following ? "" : "" + const actionBtnEl = profile.viewer[statusKey] ? `` : `` dom.insertAdjacentHTML('afterend', `
@@ -63,7 +79,7 @@ export const insertBskyProfileEl = ({ dom, profile, abortController, followActio

@${profile.handle}

- ${followButtonEl} + ${actionBtnEl}
${profile.description ? `

${profile.description}

` : ""} @@ -82,9 +98,10 @@ export const insertBskyProfileEl = ({ dom, profile, abortController, followActio target.textContent = "processing..." target.classList.add('follow-button__processing') await followAction() - target.textContent = "Following on Bluesky" + target.textContent = `${btnLabel.progressive} on Bluesky` target.classList.remove('follow-button__processing') target.classList.add('follow-button__following') + target.classList.add('follow-button__just-followed') return } @@ -93,22 +110,20 @@ export const insertBskyProfileEl = ({ dom, profile, abortController, followActio target.textContent = "processing..." target.classList.add('follow-button__processing') await unfollowAction() - target.textContent = "Follow on Bluesky" + target.textContent = `${btnLabel.add} on Bluesky` target.classList.remove('follow-button__processing') target.classList.remove('follow-button__following') - target.classList.add('follow-button__just-followed') return } }, { signal: abortController.signal }) - // register a hover action bskyUserContentDom?.addEventListener('mouseover', async (e) => { const target = e.target as Element const classList = target.classList if (classList.contains('follow-button') && classList.contains('follow-button__following')) { - target.textContent = "Unfollow on Bluesky" + target.textContent = `${btnLabel.remove} on Bluesky` } }, { signal: abortController.signal @@ -116,11 +131,11 @@ export const insertBskyProfileEl = ({ dom, profile, abortController, followActio bskyUserContentDom?.addEventListener('mouseout', async (e) => { const target = e.target as Element const classList = target.classList - if(classList.contains('follow-button__just-followed')) { + if (classList.contains('follow-button__just-followed')) { target.classList.remove('follow-button__just-followed') } if (classList.contains('follow-button') && classList.contains('follow-button__following')) { - target.textContent = "Following on Bluesky" + target.textContent = `${btnLabel.progressive} on Bluesky` } }, { signal: abortController.signal diff --git a/src/lib/searchAndInsertBskyUsers.ts b/src/lib/searchAndInsertBskyUsers.ts new file mode 100644 index 0000000..2ff86ea --- /dev/null +++ b/src/lib/searchAndInsertBskyUsers.ts @@ -0,0 +1,129 @@ +import { UserCellBtnLabel, isOutOfTopViewport } from './domHelpers'; +import { getAccountNameAndDisplayName, getUserCells, insertBskyProfileEl, insertNotFoundEl, insertReloadEl, removeReloadElIfExists } from "~lib/domHelpers"; +import { isSimilarUser } from "~lib/bskyHelpers"; +import { debugLog } from "~lib/utils"; +import type { BskyClient } from './bskyClient'; +import type { ViewerState } from '@atproto/api/dist/client/types/app/bsky/actor/defs'; + + +let abortController = new AbortController(); + +const notFoundUserCache = new Set() + +const followerUrlMap = new Map() + +export const initialize = async () => { + abortController.abort() + abortController = new AbortController() +} + +export const searchBskyUsers = async ( + { + agent, + btnLabel, + userCellQueryParam, + statusKey, + addQuery, + removeQuery, + }: { + agent: BskyClient, + userCellQueryParam: string, + btnLabel: UserCellBtnLabel, + statusKey: keyof ViewerState, + addQuery: (arg: string) => Promise, + removeQuery: (arg: string) => Promise, + }) => { + removeReloadElIfExists() + + const userCells = getUserCells({ + queryParam: userCellQueryParam, + filterInsertedElement: true, + }) + debugLog(`userCells length: ${userCells.length}`) + + let index = 0 + for (const userCell of userCells) { + if (isOutOfTopViewport(userCell)) { + continue + } + const { twAccountName, twDisplayName } = getAccountNameAndDisplayName(userCell) + if (notFoundUserCache.has(twAccountName)) { + insertNotFoundEl(userCell) + continue + } + + const [searchResultByAccountName] = await agent.searchUser({ + term: twAccountName, + limit: 1, + }) + + // TODO: Refactor, this is duplicated + // first, search by account name + if (isSimilarUser(twDisplayName, searchResultByAccountName) || isSimilarUser(twAccountName, searchResultByAccountName)) { + insertBskyProfileEl({ + dom: userCell, + profile: searchResultByAccountName, + statusKey, + btnLabel, + abortController, + followAction: async () => { + const result = await addQuery(searchResultByAccountName.did); + followerUrlMap.set(searchResultByAccountName.did, result.uri) + }, + unfollowAction: async () => { + if (searchResultByAccountName?.viewer?.following) { + await removeQuery(searchResultByAccountName?.viewer?.following); + } else { + await removeQuery(followerUrlMap.get(searchResultByAccountName.did)); + } + }, + }) + } else { + // if not found, search by display name + const [searchResultByDisplayName] = await agent.searchUser({ + term: twDisplayName, + limit: 1, + }) + if (isSimilarUser(twDisplayName, searchResultByDisplayName) || isSimilarUser(twAccountName, searchResultByDisplayName)) { + insertBskyProfileEl({ + dom: userCell, + profile: searchResultByDisplayName, + abortController, + statusKey, + btnLabel, + followAction: async () => { + const result = await addQuery(searchResultByDisplayName.did); + followerUrlMap.set(searchResultByDisplayName.did, result.uri) + }, + unfollowAction: async () => { + if (searchResultByDisplayName?.viewer?.following) { + await removeQuery(searchResultByDisplayName?.viewer?.following); + } else { + await removeQuery(followerUrlMap.get(searchResultByDisplayName.did)); + } + }, + }) + } else { + insertNotFoundEl(userCell) + notFoundUserCache.add(twAccountName) + } + } + + index++ + if (process.env.NODE_ENV === "development" && index > 5) { + break + } + } + + // TODO: if there are more users, insert reload button + insertReloadEl(async () => { + await searchBskyUsers({ + agent, + btnLabel, + userCellQueryParam, + statusKey, + addQuery, + removeQuery, + }) + }) +} diff --git a/src/popup.tsx b/src/popup.tsx index 7f8a5c8..4774f61 100644 --- a/src/popup.tsx +++ b/src/popup.tsx @@ -1,13 +1,18 @@ import { FormEvent, useState } from "react" -import iconSrc from "data-base64:~assets/icon.popup.svg" - +import { P, match } from "ts-pattern" import "./style.css" import { sendToContentScript } from "@plasmohq/messaging" - -import { MESSAGE_NAMES, MESSAGE_TYPE, STORAGE_KEYS, TARGET_URLS_REGEX } from "~lib/constants" import { useStorage } from "@plasmohq/storage/hook" + +import { + MESSAGE_NAMES, + MESSAGE_TYPE, + STORAGE_KEYS, + TARGET_URLS_REGEX +} from "~lib/constants" + import { debugLog } from "./lib/utils" function IndexPopup() { @@ -15,46 +20,68 @@ function IndexPopup() { const [password, setPassword] = useStorage(STORAGE_KEYS.BSKY_PASSWORD, "") const [userId, setUserId] = useStorage(STORAGE_KEYS.BSKY_USER_ID, "") const [message, setMessage] = useState(null) const isDisabled = !password || !userId || isLoading const isShowErrorMessage = message?.type === MESSAGE_TYPE.ERROR const isShowSuccessMessage = message?.type === MESSAGE_TYPE.SUCCESS - const setErrorMessage = (message: string) => { setMessage({ type: MESSAGE_TYPE.ERROR, message }) } - - const isExecutablePage = async () => { - const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }) - return TARGET_URLS_REGEX.some((r) => r.test(tab.url)) + const setErrorMessage = (message: string) => { + setMessage({ type: MESSAGE_TYPE.ERROR, message }) } const searchBskyUser = async (e: FormEvent) => { e.preventDefault() - if(!await isExecutablePage()) { - setErrorMessage("Error: Invalid page. please open the Twitter followers or following page.") - return; + const [{ url: currentUrl }] = await chrome.tabs.query({ + active: true, + currentWindow: true + }) + + if (!Object.values(TARGET_URLS_REGEX).some((r) => r.test(currentUrl))) { + setErrorMessage( + "Error: Invalid page. please open the Twitter following or blocking page." + ) + return } + const messageName = match(currentUrl) + .with( + P.when((url) => TARGET_URLS_REGEX.FOLLOW.test(url)), + () => MESSAGE_NAMES.SEARCH_BSKY_USER_ON_FOLLOW_PAGE + ) + .with( + P.when((url) => TARGET_URLS_REGEX.BLOCK.test(url)), + () => MESSAGE_NAMES.SEARCH_BSKY_USER_ON_BLOCK_PAGE + ) + .run() + setMessage(null) setIsLoading(true) + try { - const res: { hasError: boolean, message: string } = await sendToContentScript({ - name: MESSAGE_NAMES.SEARCH_BSKY_USER, - body: { - password, - userId, - } - }) - if(res.hasError) { + const res: { hasError: boolean; message: string } = + await sendToContentScript({ + name: messageName, + body: { + password, + userId + } + }) + if (res.hasError) { setErrorMessage(res.message) } else { - setMessage({ type: MESSAGE_TYPE.SUCCESS, message: "Completed. Try again if no results found.”" }) + setMessage({ + type: MESSAGE_TYPE.SUCCESS, + message: "Completed. Try again if no results found.”" + }) } - } catch(e) { - setErrorMessage('Error: Something went wrong. Please reload the web page and try again.') - console.error(e) + } catch (e) { + setErrorMessage( + "Error: Something went wrong. Please reload the web page and try again." + ) + console.error(e) } finally { setIsLoading(false) } @@ -62,7 +89,27 @@ function IndexPopup() { return (
-

Sky Follower Bridge

+

+ + + + + + + Sky Follower Bridge +

{isShowErrorMessage && (
- + + + {message.message}
@@ -129,7 +189,18 @@ function IndexPopup() { {isShowSuccessMessage && (
- + + + Success. Try again if no results found.