feat: add delete button

chore: fix design
This commit is contained in:
kawamataryo 2024-12-11 14:27:36 +09:00
parent dd8ce838b6
commit 5efee32eea
5 changed files with 93 additions and 28 deletions

View File

@ -1,6 +1,6 @@
{
"name": "sky-follower-bridge",
"displayName": "__MSG_extension_name__",
"displayName": "Sky Follower Bridge",
"version": "2.1.1",
"description": "__MSG_extension_description__",
"author": "kawamataryou",

View File

@ -14,6 +14,7 @@ export type Props = {
accountName: string;
displayName: string;
}) => Promise<void>;
deleteUser: (did: string) => Promise<void>;
};
const DetectedUserListItem = ({
@ -21,6 +22,7 @@ const DetectedUserListItem = ({
actionMode,
clickAction,
reSearch,
deleteUser,
}: Props) => {
const [isBtnHovered, setIsBtnHovered] = React.useState(false);
const [isJustClicked, setIsJustClicked] = React.useState(false);
@ -100,6 +102,10 @@ const DetectedUserListItem = ({
});
};
const handleDeleteClick = () => {
deleteUser(user.did);
};
const matchTypeColor = MATCH_TYPE_LABEL_AND_COLOR[user.matchType].color;
return (
@ -123,6 +129,7 @@ const DetectedUserListItem = ({
setIsBtnHovered={setIsBtnHovered}
setIsJustClicked={setIsJustClicked}
handleReSearchClick={handleReSearchClick}
handleDeleteClick={handleDeleteClick}
/>
</div>
</div>

View File

@ -4,14 +4,33 @@ import ActionButton from "./ActionButton";
import UserInfo from "./UserInfo";
import UserProfile from "./UserProfile";
export type UserCardProps = {
user: Pick<BskyUser, "avatar" | "handle" | "displayName" | "description">;
loading: boolean;
actionBtnLabelAndClass: { label: string; class: string };
handleActionButtonClick: () => void;
setIsBtnHovered: (value: boolean) => void;
setIsJustClicked: (value: boolean) => void;
handleReSearchClick: () => void;
const DeleteButton = ({
onClick,
}: {
onClick: () => void;
}) => {
return (
<button
type="button"
className="btn-outline w-7 h-7 border rounded-full flex items-center justify-center"
onClick={onClick}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-4 w-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
);
};
const ReSearchButton = ({
@ -43,6 +62,17 @@ const ReSearchButton = ({
);
};
export type UserCardProps = {
user: Pick<BskyUser, "avatar" | "handle" | "displayName" | "description">;
loading: boolean;
actionBtnLabelAndClass: { label: string; class: string };
handleActionButtonClick: () => void;
setIsBtnHovered: (value: boolean) => void;
setIsJustClicked: (value: boolean) => void;
handleReSearchClick: () => void;
handleDeleteClick: () => void;
};
const UserCard = ({
user,
loading,
@ -51,6 +81,7 @@ const UserCard = ({
setIsBtnHovered,
setIsJustClicked,
handleReSearchClick,
handleDeleteClick = () => {},
}: UserCardProps) => {
return (
<div className="relative py-3 pt-1 pl-0 pr-2 grid grid-cols-[50px_1fr]">
@ -59,23 +90,28 @@ const UserCard = ({
url={`https://bsky.app/profile/${user.handle}`}
/>
<div className="flex flex-col gap-2">
<div className="flex justify-between items-center gap-2">
<div className="flex justify-between items-start gap-2">
<div className="flex items-start gap-4">
<UserInfo
handle={user.handle}
displayName={user.displayName}
url={`https://bsky.app/profile/${user.handle}`}
/>
<ReSearchButton onClick={handleReSearchClick} />
</div>
<div className="card-actions flex items-center gap-4">
<ActionButton
loading={loading}
actionBtnLabelAndClass={actionBtnLabelAndClass}
handleActionButtonClick={handleActionButtonClick}
setIsBtnHovered={setIsBtnHovered}
setIsJustClicked={setIsJustClicked}
/>
<div className="card-actions flex items-center gap-6">
<div className="flex items-center gap-2">
<ReSearchButton onClick={handleReSearchClick} />
<DeleteButton onClick={handleDeleteClick} />
</div>
<div className="w-[170px]">
<ActionButton
loading={loading}
actionBtnLabelAndClass={actionBtnLabelAndClass}
handleActionButtonClick={handleActionButtonClick}
setIsBtnHovered={setIsBtnHovered}
setIsJustClicked={setIsJustClicked}
/>
</div>
</div>
</div>
<p className="text-sm break-all">{user.description}</p>

View File

@ -271,6 +271,13 @@ export const useBskyUserManager = () => {
});
}, []);
const deleteUser = React.useCallback(
async (did: string) => {
await setUsers((prev) => prev.filter((user) => user.did !== did));
},
[setUsers],
);
const changeDetectedUser = React.useCallback(
(fromDid: string, toUser: ProfileView) => {
setUsers((prev) =>
@ -305,5 +312,6 @@ export const useBskyUserManager = () => {
reSearchResults,
changeDetectedUser,
clearReSearchResults,
deleteUser,
};
};

View File

@ -5,6 +5,7 @@ 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 { AnimatePresence, motion } from "framer-motion";
import React from "react";
import ReSearchModal from "~components/ReSearchModal";
import DetectedUserListItem from "~lib/components/DetectedUserListItem";
@ -26,6 +27,7 @@ const Option = () => {
reSearchResults,
changeDetectedUser,
clearReSearchResults,
deleteUser,
} = useBskyUserManager();
const {
@ -175,15 +177,27 @@ const Option = () => {
</h2>
</div>
<div className="flex flex-col border-b-[1px] border-gray-500">
{filteredUsers.map((user) => (
<DetectedUserListItem
key={user.handle}
user={user}
clickAction={handleClickAction}
actionMode={actionMode}
reSearch={handleReSearch}
/>
))}
<AnimatePresence>
{filteredUsers.map((user) => (
<motion.div
key={user.did}
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
<DetectedUserListItem
key={user.handle}
user={user}
clickAction={handleClickAction}
actionMode={actionMode}
reSearch={handleReSearch}
deleteUser={deleteUser}
/>
</motion.div>
))}
</AnimatePresence>
</div>
</div>
<div className="fixed bottom-5 right-5">