mirror of
https://github.com/snachodog/tok-to-insta-follower-bridge.git
synced 2025-04-10 14:11:22 -06:00
192 lines
5.3 KiB
TypeScript
192 lines
5.3 KiB
TypeScript
import { useBskyUserManager } from "~lib/hooks/useBskyUserManager";
|
|
import "./style.css";
|
|
import { ToastContainer, toast } from "react-toastify";
|
|
import useConfirm from "~lib/components/ConfirmDialog";
|
|
import Sidebar from "~lib/components/Sidebar";
|
|
import "react-toastify/dist/ReactToastify.css";
|
|
import type { ProfileView } from "@atproto/api/dist/client/types/app/bsky/actor/defs";
|
|
import React from "react";
|
|
import ReSearchModal from "~components/ReSearchModal";
|
|
import DetectedUserListItem from "~lib/components/DetectedUserListItem";
|
|
|
|
const Option = () => {
|
|
const {
|
|
users,
|
|
filteredUsers,
|
|
matchTypeFilter,
|
|
changeMatchTypeFilter,
|
|
handleClickAction,
|
|
actionMode,
|
|
matchTypeStats,
|
|
importList,
|
|
followAll,
|
|
blockAll,
|
|
reSearch,
|
|
reSearchResults,
|
|
changeDetectedUser,
|
|
clearReSearchResults,
|
|
} = useBskyUserManager();
|
|
|
|
const {
|
|
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",
|
|
});
|
|
|
|
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",
|
|
});
|
|
|
|
const handleFollowAll = async () => {
|
|
if (!(await followAllConfirm())) {
|
|
return;
|
|
}
|
|
toast.promise(followAll, {
|
|
pending: "Processing...",
|
|
success: {
|
|
render({ data }) {
|
|
return <span className="font-bold">Followed {data} users🎉</span>;
|
|
},
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleBlockAll = async () => {
|
|
if (!(await followAllConfirm())) {
|
|
return;
|
|
}
|
|
toast.promise(blockAll, {
|
|
pending: "Processing...",
|
|
success: {
|
|
render({ data }) {
|
|
return <span className="font-bold">Blocked {data} users🎉</span>;
|
|
},
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleImportList = async () => {
|
|
if (!(await importListConfirm())) {
|
|
return;
|
|
}
|
|
toast.promise(importList, {
|
|
pending: "Processing...",
|
|
success: {
|
|
render({ data }) {
|
|
return (
|
|
<>
|
|
<span className="font-bold">List imported successfully🎉</span>
|
|
<br />
|
|
<a href={data} target="_blank" rel="noreferrer" className="link">
|
|
View Imported List
|
|
</a>
|
|
</>
|
|
);
|
|
},
|
|
},
|
|
error: {
|
|
render({ data }) {
|
|
console.log(data);
|
|
return `Failed to import list: ${data}`;
|
|
},
|
|
},
|
|
});
|
|
};
|
|
|
|
const [showReSearchModal, setShowReSearchModal] = React.useState(false);
|
|
const handleReSearch = async (user: {
|
|
sourceDid: string;
|
|
accountName: string;
|
|
displayName: string;
|
|
}) => {
|
|
reSearch({
|
|
sourceDid: user.sourceDid,
|
|
accountName: user.accountName,
|
|
displayName: user.displayName,
|
|
});
|
|
setShowReSearchModal(true);
|
|
};
|
|
|
|
const handleClickReSearchResult = ({
|
|
sourceDid,
|
|
user,
|
|
}: {
|
|
sourceDid: string;
|
|
user: ProfileView;
|
|
}) => {
|
|
changeDetectedUser(sourceDid, user);
|
|
setShowReSearchModal(false);
|
|
clearReSearchResults();
|
|
};
|
|
|
|
const handleCloseReSearchModal = () => {
|
|
setShowReSearchModal(false);
|
|
clearReSearchResults();
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="flex h-screen">
|
|
<div className="fixed top-0 left-0 h-full">
|
|
<Sidebar
|
|
detectedCount={users.length}
|
|
filterValue={matchTypeFilter}
|
|
onChangeFilter={changeMatchTypeFilter}
|
|
actionMode={actionMode}
|
|
matchTypeStats={matchTypeStats}
|
|
importList={handleImportList}
|
|
followAll={handleFollowAll}
|
|
blockAll={handleBlockAll}
|
|
/>
|
|
</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>
|
|
</div>
|
|
<div className="flex flex-col gap-4">
|
|
<div className="divide-y divide-gray-500">
|
|
{filteredUsers.map((user) => (
|
|
<DetectedUserListItem
|
|
key={user.handle}
|
|
user={user}
|
|
clickAction={handleClickAction}
|
|
actionMode={actionMode}
|
|
reSearch={handleReSearch}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<ReSearchModal
|
|
open={showReSearchModal}
|
|
onClose={handleCloseReSearchModal}
|
|
reSearchResults={reSearchResults}
|
|
handleClickReSearchResult={handleClickReSearchResult}
|
|
/>
|
|
<ToastContainer
|
|
position="top-right"
|
|
autoClose={5000}
|
|
className="text-sm"
|
|
/>
|
|
<FollowAllConfirmationDialog />
|
|
<ImportListConfirmationDialog />
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Option;
|