Skip to content

Commit

Permalink
Viewing who reacted to posts
Browse files Browse the repository at this point in the history
  • Loading branch information
mybearworld committed Jul 27, 2024
1 parent e7486b1 commit 4f2c2a7
Show file tree
Hide file tree
Showing 4 changed files with 317 additions and 31 deletions.
24 changes: 15 additions & 9 deletions src/components/Post.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Markdown } from "./Markdown";
import { Mention } from "./Mention";
import { MarkdownInput } from "./MarkdownInput";
import { ProfilePicture, ProfilePictureBase } from "./ProfilePicture";
import { ReactionUsers } from "./ReactionUsers";
import { RelativeTime } from "./RelativeTime";
import { twMerge } from "tailwind-merge";
import { EmojiPicker } from "./EmojiPicker";
Expand Down Expand Up @@ -271,15 +272,6 @@ const PostBase = memo((props: PostBaseProps) => {
: "View source"}
</MenuItem>
: undefined}
<MenuItem
onClick={() => {
navigator.clipboard.writeText(
`https://mybearworld.github.io/roarer-2?post=${props.post.post_id}`,
);
}}
>
Copy link
</MenuItem>
{credentials.username === props.post.u ?
<>
<MenuItem
Expand All @@ -294,6 +286,20 @@ const PostBase = memo((props: PostBaseProps) => {
<MenuItem onClick={handleDelete}>Delete</MenuItem>
</>
: undefined}
<MenuItem
onClick={() => {
navigator.clipboard.writeText(
`https://mybearworld.github.io/roarer-2?post=${props.post.post_id}`,
);
}}
>
Copy link
</MenuItem>
{props.post.reactions.length ?
<ReactionUsers post={props.post.post_id}>
<MenuItem dontClose>Reactions</MenuItem>
</ReactionUsers>
: undefined}
</Menu>
</>
: undefined}
Expand Down
156 changes: 156 additions & 0 deletions src/components/ReactionUsers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import * as Dialog from "@radix-ui/react-dialog";
import * as Tabs from "@radix-ui/react-tabs";
import { ReactNode, useState } from "react";
import { useShallow } from "zustand/react/shallow";
import { useAPI } from "../lib/api";
import { Popup } from "./Popup";
import { UserView } from "./UserView";
import { Button } from "./Button";

export type ReactionUsersProps = {
post: string;
children: ReactNode;
};
export const ReactionUsers = (props: ReactionUsersProps) => {
const [posts, loadPost] = useAPI(
useShallow((state) => [state.posts, state.loadPost]),
);
loadPost(props.post);
const post = posts[props.post];

return (
<Popup trigger={props.children} triggerAsChild size="extend">
{!post ?
<Dialog.Title>Loading post...</Dialog.Title>
: post.error ?
<Dialog.Title>
Failed to get post.
<br />
Message: {post.message}
</Dialog.Title>
: post.isDeleted ?
<Dialog.Title>This was post was deleted.</Dialog.Title>
: !post.reactions.length ?
<Dialog.Title>
This post doesn't have any reactions yet. Be the first to react!
</Dialog.Title>
: <div>
<Dialog.Title className="text-xl font-bold">Reactions</Dialog.Title>
<Tabs.Root
className="flex gap-2"
defaultValue={post.reactions[0]?.emoji}
>
<Tabs.List className="flex flex-col gap-2">
{post.reactions.map((reaction) => (
<Tabs.Trigger
value={reaction.emoji}
className="border-b-2 border-transparent text-xl aria-selected:border-lime-500 dark:aria-selected:border-lime-600"
key={reaction.emoji}
>
{reaction.emoji}
</Tabs.Trigger>
))}
</Tabs.List>
{post.reactions.map((reaction) => (
<Tabs.Content
className="grow"
value={reaction.emoji}
key={reaction.emoji}
>
<IndividualReactionUsers
post={props.post}
emoji={reaction.emoji}
/>
</Tabs.Content>
))}
</Tabs.Root>
</div>
}
</Popup>
);
};

type IndividualReactionUsersProps = {
post: string;
emoji: string;
};
const IndividualReactionUsers = (props: IndividualReactionUsersProps) => {
const [
credentials,
reactionUsers,
loadReactionUsers,
loadMoreReactionUsers,
reactToPost,
] = useAPI(
useShallow((state) => [
state.credentials,
state.reactionUsers,
state.loadReactionUsers,
state.loadMoreReactionUsers,
state.reactToPost,
]),
);
const [error, setError] = useState<string>();
const [loadingMore, setLoadingMore] = useState(false);
loadReactionUsers(props.post, props.emoji);
const users = reactionUsers[`${props.post}/${props.emoji}`];

const handleRemove = async () => {
const response = await reactToPost(props.post, props.emoji, "delete");
if (response.error) {
setError(response.message);
}
};
const handleLoadMore = async () => {
setLoadingMore(true);
const response = await loadMoreReactionUsers(props.post, props.emoji);
setLoadingMore(false);
if (response.error) {
setError(response.message);
}
};

return (
<div>
{!users ?
"Loading..."
: users.error ?
<div>
Failed getting users.
<br />
Message: {users.message}
</div>
: <div className="flex flex-col gap-2">
{error ?
<div className="text-red-400">{error}</div>
: undefined}
<div className="flex flex-col">
{users.users.map((user) => (
<UserView
username={user}
key={user}
secondary
disabled={user !== credentials?.username}
text={user === credentials?.username ? "You" : undefined}
rightText={
user === credentials?.username ? "Remove" : undefined
}
onClick={handleRemove}
/>
))}
</div>
{users.stopLoadingMore ?
undefined
: <Button
className="w-full"
onClick={handleLoadMore}
disabled={loadingMore}
>
Load more
</Button>
}
</div>
}
</div>
);
};
29 changes: 18 additions & 11 deletions src/components/UserView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type UserViewProps = {
secondary?: boolean;
className?: string;
force?: boolean;
rightText?: string;
};
export const UserView = forwardRef<HTMLButtonElement, UserViewProps>(
(props: UserViewProps, ref) => {
Expand All @@ -22,29 +23,35 @@ export const UserView = forwardRef<HTMLButtonElement, UserViewProps>(
<button
ref={ref}
className={twMerge(
"flex w-full items-center gap-2 bg-white px-2 py-1 dark:bg-gray-950",
"flex items-center bg-white px-2 py-1 dark:bg-gray-950",
props.secondary ?
"bg-white dark:bg-gray-900"
: "bg-white dark:bg-gray-950",
props.disabled ? ""
: props.secondary ? "hover:bg-gray-100 dark:hover:bg-gray-800"
: "hover:bg-gray-100 dark:hover:bg-gray-900",
props.disabled ? "" : "group",
props.className,
)}
onClick={props.onClick}
disabled={props.disabled}
>
<ProfilePicture
className="inline-block"
username={props.username}
size="h-8 min-h-8 w-8 min-w-8"
/>
<div>
{props.username}{" "}
{props.text ?
<span className="text-sm">({props.text})</span>
: undefined}
<div className="flex w-full grow items-center gap-2">
<ProfilePicture
className="inline-block"
username={props.username}
size="h-8 min-h-8 w-8 min-w-8"
/>
<div>
{props.username}{" "}
{props.text ?
<span className="text-sm">({props.text})</span>
: undefined}
</div>
</div>
{props.rightText ?
<div className="text-sm">{props.rightText}</div>
: undefined}
</button>
);
},
Expand Down
Loading

0 comments on commit 4f2c2a7

Please sign in to comment.