mirror of
https://github.com/snachodog/tok-to-insta-follower-bridge.git
synced 2025-09-13 07:23:32 -06:00
feat: add i18n
This commit is contained in:
@@ -26,12 +26,14 @@ const ReSearchModal: React.FC<ReSearchModalProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<Modal open={open} width={600} onClose={onClose}>
|
||||
<h2 className="text-lg font-bold text-center py-2">Search Results</h2>
|
||||
<h2 className="text-lg font-bold text-center py-2">
|
||||
{chrome.i18n.getMessage("re_search_modal_title")}
|
||||
</h2>
|
||||
{reSearchResults.users.length === 0 && (
|
||||
<div className="text-center flex justify-center items-center flex-col gap-4 mt-5">
|
||||
<span className="loading loading-spinner loading-lg" />
|
||||
<div className="text-center flex justify-center items-center text-sm">
|
||||
Loading...
|
||||
{chrome.i18n.getMessage("loading")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@@ -108,12 +108,18 @@ const App = () => {
|
||||
<div className="flex flex-col gap-2 items-center">
|
||||
{loading && (
|
||||
<p className="text-lg font-bold">
|
||||
Scanning {serviceName} users to find bsky users...
|
||||
{chrome.i18n.getMessage("scanning_users", [serviceName])}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-2xl font-bold">
|
||||
Detected <span className="text-4xl">{users.length}</span> users
|
||||
</p>
|
||||
<p
|
||||
className="text-2xl font-bold"
|
||||
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: chrome.i18n.getMessage("detected_users", [
|
||||
users.length.toString(),
|
||||
]),
|
||||
}}
|
||||
/>
|
||||
{errorMessage && <AlertError>{errorMessage}</AlertError>}
|
||||
{loading && (
|
||||
<>
|
||||
@@ -122,7 +128,7 @@ const App = () => {
|
||||
className="btn btn-primary mt-5 btn-ghost"
|
||||
onClick={stopAndShowDetectedUsers}
|
||||
>
|
||||
Stop Scanning and View Results
|
||||
{chrome.i18n.getMessage("stop_scanning_and_view_results")}
|
||||
</button>
|
||||
<LoadingCards />
|
||||
</>
|
||||
@@ -133,7 +139,7 @@ const App = () => {
|
||||
className="btn btn-primary mt-5"
|
||||
onClick={restart}
|
||||
>
|
||||
Resume Scanning
|
||||
{chrome.i18n.getMessage("resume_scanning")}
|
||||
</button>
|
||||
)}
|
||||
{!loading && isBottomReached && (
|
||||
@@ -143,14 +149,14 @@ const App = () => {
|
||||
className="btn btn-primary mt-5"
|
||||
onClick={openOptionPage}
|
||||
>
|
||||
View Detected Users
|
||||
{chrome.i18n.getMessage("view_detected_users")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary mt-5 btn-ghost"
|
||||
onClick={restart}
|
||||
>
|
||||
Resume Scanning
|
||||
{chrome.i18n.getMessage("resume_scanning")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
@@ -22,7 +22,7 @@ const AsyncButton = ({ onClick, label, className }: Props) => {
|
||||
onClick={handleClick}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "Processing..." : label}
|
||||
{loading ? chrome.i18n.getMessage("loading") : label}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
@@ -29,16 +29,16 @@ const DetectedUserListItem = ({
|
||||
match(actionMode)
|
||||
.with(ACTION_MODE.FOLLOW, ACTION_MODE.IMPORT_LIST, () => {
|
||||
const follow = {
|
||||
label: "Follow on Bluesky",
|
||||
label: chrome.i18n.getMessage("button_follow_on_bluesky"),
|
||||
class: "btn-primary",
|
||||
};
|
||||
const following = {
|
||||
label: "Following on Bluesky",
|
||||
label: chrome.i18n.getMessage("button_following_on_bluesky"),
|
||||
class:
|
||||
"btn-outline hover:bg-transparent hover:border hover:bg-transparent hover:text-base-content",
|
||||
};
|
||||
const unfollow = {
|
||||
label: "Unfollow on Bluesky",
|
||||
label: chrome.i18n.getMessage("button_unfollow_on_bluesky"),
|
||||
class:
|
||||
"text-red-500 hover:bg-transparent hover:border hover:border-red-500",
|
||||
};
|
||||
@@ -52,16 +52,16 @@ const DetectedUserListItem = ({
|
||||
})
|
||||
.with(ACTION_MODE.BLOCK, () => {
|
||||
const block = {
|
||||
label: "Block on Bluesky",
|
||||
label: chrome.i18n.getMessage("button_block_on_bluesky"),
|
||||
class: "btn-primary",
|
||||
};
|
||||
const blocking = {
|
||||
label: "Blocking on Bluesky",
|
||||
label: chrome.i18n.getMessage("button_blocking_on_bluesky"),
|
||||
class:
|
||||
"btn-outline hover:bg-transparent hover:border hover:bg-transparent hover:text-base-content",
|
||||
};
|
||||
const unblock = {
|
||||
label: "Unblock on Bluesky",
|
||||
label: chrome.i18n.getMessage("button_unblock_on_bluesky"),
|
||||
class:
|
||||
"text-red-500 hover:bg-transparent hover:border hover:border-red-500",
|
||||
};
|
||||
@@ -106,12 +106,12 @@ const DetectedUserListItem = ({
|
||||
<div>
|
||||
<div className={`w-full border-l-8 border-${matchTypeColor}`}>
|
||||
<div
|
||||
className={`w-full border-t border-gray-500 text-${matchTypeColor} grid grid-cols-[22%_1fr]`}
|
||||
className={`w-full border-t border-gray-500 text-${matchTypeColor} grid grid-cols-[22%_1fr] text-xs`}
|
||||
>
|
||||
<div className="px-3 bg-slate-100 dark:bg-slate-800">
|
||||
<div className="px-3 bg-slate-100 dark:bg-slate-800" />
|
||||
<div className="px-3">
|
||||
{MATCH_TYPE_LABEL_AND_COLOR[user.matchType].label}
|
||||
</div>
|
||||
<div className="px-3" />
|
||||
</div>
|
||||
<div className="bg-base-100 w-full relative grid grid-cols-[22%_1fr] gap-5">
|
||||
<DetectedUserSource user={user} />
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import { match } from "ts-pattern";
|
||||
import { getMessageWithLink } from "~lib/utils";
|
||||
import type { MatchType, MatchTypeFilterValue } from "../../types";
|
||||
import {
|
||||
ACTION_MODE,
|
||||
@@ -78,18 +79,19 @@ const Sidebar = ({
|
||||
</svg>
|
||||
</div>
|
||||
<div className="stat-title text-lg text-base-content font-bold">
|
||||
Detected users
|
||||
{chrome.i18n.getMessage("sidebar_detected_users")}
|
||||
</div>
|
||||
<div className="stat-value text-base-content">{detectedCount}</div>
|
||||
<div className="stat-desc">
|
||||
Same handle name: {matchTypeStats[BSKY_USER_MATCH_TYPE.HANDLE]}
|
||||
{chrome.i18n.getMessage("same_handle_name")}:{" "}
|
||||
{matchTypeStats[BSKY_USER_MATCH_TYPE.HANDLE]}
|
||||
</div>
|
||||
<div className="stat-desc">
|
||||
Same display name:{" "}
|
||||
{chrome.i18n.getMessage("same_display_name")}:{" "}
|
||||
{matchTypeStats[BSKY_USER_MATCH_TYPE.DISPLAY_NAME]}
|
||||
</div>
|
||||
<div className="stat-desc">
|
||||
Included handle in description:{" "}
|
||||
{chrome.i18n.getMessage("included_handle_in_description")}:{" "}
|
||||
{matchTypeStats[BSKY_USER_MATCH_TYPE.DESCRIPTION]}
|
||||
</div>
|
||||
</div>
|
||||
@@ -118,7 +120,7 @@ const Sidebar = ({
|
||||
<span className="text-sm">
|
||||
{key === BSKY_USER_MATCH_TYPE.FOLLOWING &&
|
||||
actionMode === ACTION_MODE.BLOCK
|
||||
? "Blocked users"
|
||||
? chrome.i18n.getMessage("blocked_user")
|
||||
: MATCH_TYPE_LABEL_AND_COLOR[key].label}
|
||||
</span>
|
||||
<input
|
||||
@@ -151,30 +153,39 @@ const Sidebar = ({
|
||||
</div>
|
||||
{match(actionMode)
|
||||
.with(ACTION_MODE.FOLLOW, () => (
|
||||
<AsyncButton onClick={followAll} label="Follow All" />
|
||||
<AsyncButton
|
||||
onClick={followAll}
|
||||
label={chrome.i18n.getMessage("follow_all")}
|
||||
/>
|
||||
))
|
||||
.with(ACTION_MODE.BLOCK, () => (
|
||||
<AsyncButton onClick={blockAll} label="Block All" />
|
||||
<AsyncButton
|
||||
onClick={blockAll}
|
||||
label={chrome.i18n.getMessage("block_all")}
|
||||
/>
|
||||
))
|
||||
.with(ACTION_MODE.IMPORT_LIST, () => (
|
||||
<AsyncButton onClick={importList} label="Import List" />
|
||||
<AsyncButton
|
||||
onClick={importList}
|
||||
label={chrome.i18n.getMessage("import_list")}
|
||||
/>
|
||||
))
|
||||
.otherwise(() => null)}
|
||||
<p className="text-xs">
|
||||
⚠️ User detection is not perfect and may include false positives.
|
||||
⚠️ {chrome.i18n.getMessage("warning_user_detection")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-auto">
|
||||
<div className="divider" />
|
||||
<p className="mb-2">
|
||||
If you find this tool helpful, I'd appreciate{" "}
|
||||
<a href="https://ko-fi.com/X8X315UWFN" className="link">
|
||||
your support
|
||||
</a>{" "}
|
||||
to help me maintain and improve it ☕
|
||||
</p>
|
||||
<p
|
||||
className="mb-2 text-xs"
|
||||
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: getMessageWithLink("donate_message"),
|
||||
}}
|
||||
/>
|
||||
<a
|
||||
href="https://ko-fi.com/X8X315UWFN"
|
||||
href="https://ko-fi.com/kawamataryo"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
style={{ display: "inline-block" }}
|
||||
|
@@ -75,19 +75,19 @@ export const MAX_RELOAD_COUNT = 1;
|
||||
|
||||
export const MATCH_TYPE_LABEL_AND_COLOR = {
|
||||
[BSKY_USER_MATCH_TYPE.HANDLE]: {
|
||||
label: "Same handle name",
|
||||
label: chrome.i18n.getMessage("same_handle_name"),
|
||||
color: "info",
|
||||
},
|
||||
[BSKY_USER_MATCH_TYPE.DISPLAY_NAME]: {
|
||||
label: "Same display name",
|
||||
label: chrome.i18n.getMessage("same_display_name"),
|
||||
color: "warning",
|
||||
},
|
||||
[BSKY_USER_MATCH_TYPE.DESCRIPTION]: {
|
||||
label: "Included handle in description",
|
||||
label: chrome.i18n.getMessage("included_handle_in_description"),
|
||||
color: "secondary",
|
||||
},
|
||||
[BSKY_USER_MATCH_TYPE.FOLLOWING]: {
|
||||
label: "Followed users",
|
||||
label: chrome.i18n.getMessage("followed_users"),
|
||||
color: "success",
|
||||
},
|
||||
};
|
||||
@@ -95,6 +95,9 @@ export const MATCH_TYPE_LABEL_AND_COLOR = {
|
||||
export const AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE =
|
||||
"AuthFactorTokenRequiredError";
|
||||
|
||||
export const INVALID_IDENTIFIER_OR_PASSWORD_ERROR_MESSAGE =
|
||||
"Invalid identifier or password";
|
||||
|
||||
export const RATE_LIMIT_ERROR_MESSAGE = "Rate limit";
|
||||
|
||||
export const DOCUMENT_LINK = {
|
||||
|
@@ -28,10 +28,7 @@ export class ThreadsService extends AbstractService {
|
||||
'[role="dialog"] [role="tab"]>[role="button"]',
|
||||
);
|
||||
if (!isTargetPage) {
|
||||
return [
|
||||
false,
|
||||
"Invalid page. please open the following or followers view.",
|
||||
];
|
||||
return [false, chrome.i18n.getMessage("error_invalid_page_in_threads")];
|
||||
}
|
||||
return [true, ""];
|
||||
}
|
||||
|
@@ -36,3 +36,21 @@ export const findFirstScrollableElements = (
|
||||
|
||||
return scrollableElements[0] ?? null;
|
||||
};
|
||||
|
||||
export const getMessageWithLink = (
|
||||
key: string,
|
||||
placeholders: string[] = [],
|
||||
) => {
|
||||
const linkPattern = /\[(.*?)\]\((.*?)\)/g;
|
||||
let message = chrome.i18n.getMessage(key, placeholders);
|
||||
const links = message.matchAll(linkPattern);
|
||||
for (const link of links) {
|
||||
const [fullMatch, text, url] = link;
|
||||
message = message.replace(
|
||||
fullMatch,
|
||||
`<a href="${url}" target="_blank" class="link" rel="noreferrer">${text}</a>`,
|
||||
);
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
@@ -31,22 +31,20 @@ const Option = () => {
|
||||
confirm: followAllConfirm,
|
||||
ConfirmationDialog: FollowAllConfirmationDialog,
|
||||
} = useConfirm({
|
||||
title: "Proceed with Execution?",
|
||||
message:
|
||||
"User detection is not perfect and may include false positives. Do you still want to proceed?",
|
||||
cancelText: "Cancel",
|
||||
okText: "OK",
|
||||
title: chrome.i18n.getMessage("follow_all_confirmation_title"),
|
||||
message: chrome.i18n.getMessage("follow_all_confirmation_message"),
|
||||
cancelText: chrome.i18n.getMessage("confirmation_cancel"),
|
||||
okText: chrome.i18n.getMessage("confirmation_ok"),
|
||||
});
|
||||
|
||||
const {
|
||||
confirm: importListConfirm,
|
||||
ConfirmationDialog: ImportListConfirmationDialog,
|
||||
} = useConfirm({
|
||||
title: "Proceed with Execution?",
|
||||
message:
|
||||
"Importing a list will create a new list and add all detected users to it. This feature is experimental and may not work as expected. Do you still want to proceed?",
|
||||
cancelText: "Cancel",
|
||||
okText: "OK",
|
||||
title: chrome.i18n.getMessage("import_list_confirmation_title"),
|
||||
message: chrome.i18n.getMessage("import_list_confirmation_message"),
|
||||
cancelText: chrome.i18n.getMessage("confirmation_cancel"),
|
||||
okText: chrome.i18n.getMessage("confirmation_ok"),
|
||||
});
|
||||
|
||||
const handleFollowAll = async () => {
|
||||
@@ -54,10 +52,16 @@ const Option = () => {
|
||||
return;
|
||||
}
|
||||
toast.promise(followAll, {
|
||||
pending: "Processing...",
|
||||
pending: chrome.i18n.getMessage("toast_pending"),
|
||||
success: {
|
||||
render({ data }) {
|
||||
return <span className="font-bold">Followed {data} users🎉</span>;
|
||||
return (
|
||||
<span className="font-bold">
|
||||
{chrome.i18n.getMessage("toast_follow_all_success", [
|
||||
data.toString(),
|
||||
])}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -68,10 +72,16 @@ const Option = () => {
|
||||
return;
|
||||
}
|
||||
toast.promise(blockAll, {
|
||||
pending: "Processing...",
|
||||
pending: chrome.i18n.getMessage("toast_pending"),
|
||||
success: {
|
||||
render({ data }) {
|
||||
return <span className="font-bold">Blocked {data} users🎉</span>;
|
||||
return (
|
||||
<span className="font-bold">
|
||||
{chrome.i18n.getMessage("toast_block_all_success", [
|
||||
data.toString(),
|
||||
])}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -82,15 +92,17 @@ const Option = () => {
|
||||
return;
|
||||
}
|
||||
toast.promise(importList, {
|
||||
pending: "Processing...",
|
||||
pending: chrome.i18n.getMessage("toast_pending"),
|
||||
success: {
|
||||
render({ data }) {
|
||||
return (
|
||||
<>
|
||||
<span className="font-bold">List imported successfully🎉</span>
|
||||
<span className="font-bold">
|
||||
{chrome.i18n.getMessage("toast_import_list_success")}
|
||||
</span>
|
||||
<br />
|
||||
<a href={data} target="_blank" rel="noreferrer" className="link">
|
||||
View Imported List
|
||||
{chrome.i18n.getMessage("toast_import_list_success_view_list")}
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
@@ -98,8 +110,9 @@ const Option = () => {
|
||||
},
|
||||
error: {
|
||||
render({ data }) {
|
||||
console.log(data);
|
||||
return `Failed to import list: ${data}`;
|
||||
return chrome.i18n.getMessage("toast_import_list_error", [
|
||||
data as string,
|
||||
]);
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -153,8 +166,12 @@ const Option = () => {
|
||||
</div>
|
||||
<div className="flex-1 ml-80 p-6 pt-0 overflow-y-auto">
|
||||
<div className="grid grid-cols-[22%_1fr] sticky top-0 z-10 bg-base-100 border-b-[1px] border-gray-500">
|
||||
<h2 className="text-lg font-bold text-center py-2">Source</h2>
|
||||
<h2 className="text-lg font-bold text-center py-2">Detected</h2>
|
||||
<h2 className="text-lg font-bold text-center py-2">
|
||||
{chrome.i18n.getMessage("source")}
|
||||
</h2>
|
||||
<h2 className="text-lg font-bold text-center py-2">
|
||||
{chrome.i18n.getMessage("detected")}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex flex-col border-b-[1px] border-gray-500">
|
||||
{filteredUsers.map((user) => (
|
||||
|
@@ -10,6 +10,7 @@ import {
|
||||
AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE,
|
||||
BSKY_DOMAIN,
|
||||
DOCUMENT_LINK,
|
||||
INVALID_IDENTIFIER_OR_PASSWORD_ERROR_MESSAGE,
|
||||
MAX_RELOAD_COUNT,
|
||||
MESSAGE_NAMES,
|
||||
MESSAGE_TYPE,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
STORAGE_KEYS,
|
||||
TARGET_URLS_REGEX,
|
||||
} from "~lib/constants";
|
||||
import { getMessageWithLink } from "~lib/utils";
|
||||
|
||||
function IndexPopup() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -81,19 +83,21 @@ function IndexPopup() {
|
||||
|
||||
const validateForm = () => {
|
||||
if (!password && !identifier) {
|
||||
setErrorMessage("Error: Please enter your password and identifier.");
|
||||
setErrorMessage(
|
||||
chrome.i18n.getMessage("error_enter_identifier_and_password"),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!password) {
|
||||
setErrorMessage("Error: Please enter your password.");
|
||||
setErrorMessage(chrome.i18n.getMessage("error_enter_password"));
|
||||
return false;
|
||||
}
|
||||
if (!identifier) {
|
||||
setErrorMessage("Error: Please enter your identifier.");
|
||||
setErrorMessage(chrome.i18n.getMessage("error_enter_identifier"));
|
||||
return false;
|
||||
}
|
||||
if (isShowAuthFactorTokenInput && !authFactorToken) {
|
||||
setErrorMessage("Error: Please enter your auth factor token.");
|
||||
setErrorMessage(chrome.i18n.getMessage("error_enter_auth_factor_token"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -115,7 +119,7 @@ function IndexPopup() {
|
||||
|
||||
if (!Object.values(TARGET_URLS_REGEX).some((r) => r.test(currentUrl))) {
|
||||
setErrorMessage(
|
||||
"Error: Invalid page. please open the target page.",
|
||||
chrome.i18n.getMessage("error_invalid_page"),
|
||||
DOCUMENT_LINK.PAGE_ERROR,
|
||||
);
|
||||
return;
|
||||
@@ -166,6 +170,13 @@ function IndexPopup() {
|
||||
await saveShowAuthFactorTokenInputToStorage(true);
|
||||
} else if (error.message.includes(RATE_LIMIT_ERROR_MESSAGE)) {
|
||||
setErrorMessage(error.message, DOCUMENT_LINK.RATE_LIMIT_ERROR);
|
||||
} else if (
|
||||
error.message.includes(INVALID_IDENTIFIER_OR_PASSWORD_ERROR_MESSAGE)
|
||||
) {
|
||||
setErrorMessage(
|
||||
chrome.i18n.getMessage("error_invalid_identifier_or_password"),
|
||||
DOCUMENT_LINK.LOGIN_ERROR,
|
||||
);
|
||||
} else {
|
||||
setErrorMessage(error.message, DOCUMENT_LINK.LOGIN_ERROR);
|
||||
}
|
||||
@@ -202,7 +213,7 @@ function IndexPopup() {
|
||||
await searchBskyUser();
|
||||
} else {
|
||||
setErrorMessage(
|
||||
"Error: Something went wrong. Please reload the web page and try again.",
|
||||
chrome.i18n.getMessage("error_something_went_wrong"),
|
||||
DOCUMENT_LINK.OTHER_ERROR,
|
||||
);
|
||||
}
|
||||
@@ -258,7 +269,7 @@ function IndexPopup() {
|
||||
d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
</svg>
|
||||
Handle or Email
|
||||
{chrome.i18n.getMessage("handle_or_email")}
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
@@ -286,20 +297,17 @@ function IndexPopup() {
|
||||
/>
|
||||
</svg>
|
||||
<p>
|
||||
Password
|
||||
{chrome.i18n.getMessage("password")}
|
||||
<br />
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-xs">
|
||||
We recommend using the{" "}
|
||||
<a
|
||||
href="https://bsky.app/settings/app-passwords"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="link"
|
||||
>
|
||||
App Password.
|
||||
</a>
|
||||
<span
|
||||
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: getMessageWithLink("recommended_to_use_app_password"),
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<input
|
||||
type="password"
|
||||
@@ -322,8 +330,8 @@ function IndexPopup() {
|
||||
className="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M16.5 6v.75m0 3v.75m0 3v.75m0 3V18m-9-5.25h5.25M7.5 15h3M3.375 5.25c-.621 0-1.125.504-1.125 1.125v3.026a2.999 2.999 0 0 1 0 5.198v3.026c0 .621.504 1.125 1.125 1.125h17.25c.621 0 1.125-.504 1.125-1.125v-3.026a2.999 2.999 0 0 1 0-5.198V6.375c0-.621-.504-1.125-1.125-1.125H3.375Z"
|
||||
/>
|
||||
</svg>
|
||||
@@ -350,10 +358,12 @@ function IndexPopup() {
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading && <span className="w-4 loading loading-spinner" />}
|
||||
{isLoading ? "Finding Bluesky Users" : "Find Bluesky Users"}
|
||||
{isLoading
|
||||
? chrome.i18n.getMessage("finding_bluesky_users")
|
||||
: chrome.i18n.getMessage("find_bluesky_users")}
|
||||
</button>
|
||||
{isShowErrorMessage && (
|
||||
<div className="flex gap-2 items-center text-red-600 border border-red-600 p-2 rounded-md mt-2">
|
||||
<div className="flex gap-2 items-center text-red-600 border border-red-600 p-2 rounded-md mt-2 text-xs">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="stroke-current flex-shrink-0 h-6 w-6"
|
||||
@@ -376,10 +386,9 @@ function IndexPopup() {
|
||||
rel="noreferrer"
|
||||
className="link ml-2"
|
||||
>
|
||||
Learn more
|
||||
{chrome.i18n.getMessage("learn_more")}
|
||||
</a>
|
||||
)}
|
||||
.
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
Reference in New Issue
Block a user