77 lines · 2.8 KB
import { useState } from "react";
import { toggleReaction } from "~/fns/reactions";
import { useAction } from "~/hooks/use-action";
import { REACTION_CONTENTS, REACTION_EMOJI } from "~/lib/reactions";
import type { ReactionSubject, ReactionSummary } from "~/lib/reactions";
import { cn } from "~/utils/cn";

type Props = {
  owner: string;
  repo: string;
  subjectType: ReactionSubject;
  subjectId: string;
  reactions: ReactionSummary[];
  canReact: boolean;
};

/** Reaction pills plus an emoji picker; reusable on issues, PRs, comments, review comments and discussions. */
export function ReactionBar({ owner, repo, subjectType, subjectId, reactions, canReact }: Props) {
  const [open, setOpen] = useState(false);
  const toggle = useAction(toggleReaction);
  const react = (content: (typeof REACTION_CONTENTS)[number]) => {
    setOpen(false);
    void toggle.run({ data: { owner, repo, subjectType, subjectId, content } });
  };
  if (!canReact && reactions.length === 0) return null;
  return (
    <div className="relative flex flex-wrap items-center gap-1.5">
      {reactions.map((reaction) => (
        <button
          key={reaction.content}
          type="button"
          disabled={!canReact || toggle.pending}
          onClick={() => react(reaction.content)}
          aria-pressed={reaction.viewerReacted}
          className={cn(
            "inline-flex h-6 items-center gap-1 rounded-full border px-2 text-xs",
            reaction.viewerReacted
              ? "border-accent-600/70 bg-accent-950 text-accent-300"
              : "border-gray-700 text-gray-400 hover:border-gray-500",
          )}