Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
//plane
import { cn } from "@plane/editor";
// components
import { IssueEmojiReactions, IssueVotes } from "@/components/issues/reactions";
// hooks
import { usePublish } from "@/hooks/store";

type Props = {
issueId: string;
};
export const BlockReactions = observer((props: Props) => {
const { issueId } = props;
const { anchor } = useParams();
const { canVote, canReact } = usePublish(anchor.toString());

// if the user cannot vote or react then return empty
if (!canVote && !canReact) return <></>;

return (
<div
className={cn(
"flex flex-wrap border-t-[1px] outline-transparent w-full border-t-custom-border-200 bg-custom-background-90 rounded-b"
)}
>
<div className="py-2 px-3 flex flex-wrap items-center gap-2">
{canVote && (
<div
className={cn(`flex items-center gap-2 pr-1`, {
"after:h-6 after:ml-1 after:w-[1px] after:bg-custom-border-200": canReact,
})}
>
<IssueVotes anchor={anchor.toString()} issueIdFromProps={issueId} size="sm" />
</div>
)}
{canReact && (
<div className="flex flex-wrap items-center gap-2">
<IssueEmojiReactions anchor={anchor.toString()} issueIdFromProps={issueId} size="sm" />
</div>
)}
</div>
</div>
);
});
22 changes: 14 additions & 8 deletions space/core/components/issues/issue-layouts/kanban/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import { WithDisplayPropertiesHOC } from "@/components/issues/issue-layouts/with
import { queryParamGenerator } from "@/helpers/query-param-generator";
// hooks
import { useIssueDetails, usePublish } from "@/hooks/store";

//
import { IIssue } from "@/types/issue";
import { IssueProperties } from "../properties/all-properties";
import { getIssueBlockId } from "../utils";
import { BlockReactions } from "./block-reactions";

interface IssueBlockProps {
issueId: string;
Expand Down Expand Up @@ -83,17 +84,22 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = observer((props) => {

return (
<div className={cn("group/kanban-block relative p-1.5")}>
<Link
id={getIssueBlockId(issueId, groupId, subGroupId)}
<div
className={cn(
"block rounded border-[1px] outline-[0.5px] outline-transparent w-full border-custom-border-200 bg-custom-background-100 text-sm transition-all hover:border-custom-border-400",
"relative block rounded border-[1px] outline-[0.5px] outline-transparent w-full border-custom-border-200 bg-custom-background-100 text-sm transition-all hover:border-custom-border-400",
{ "border border-custom-primary-70 hover:border-custom-primary-70": getIsIssuePeeked(issue.id) }
)}
href={`?${queryParam}`}
onClick={handleIssuePeekOverview}
>
<KanbanIssueDetailsBlock issue={issue} displayProperties={displayProperties} />
</Link>
<Link
id={getIssueBlockId(issueId, groupId, subGroupId)}
className="w-full"
href={`?${queryParam}`}
onClick={handleIssuePeekOverview}
>
<KanbanIssueDetailsBlock issue={issue} displayProperties={displayProperties} />
</Link>
<BlockReactions issueId={issueId} />
</div>
</div>
);
});
Expand Down
2 changes: 0 additions & 2 deletions space/core/components/issues/peek-overview/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ export * from "./issue-properties";
export * from "./layout";
export * from "./side-peek-view";
export * from "./issue-reaction";
export * from "./issue-vote-reactions";
export * from "./issue-emoji-reactions";
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { observer } from "mobx-react";
import { IssueEmojiReactions, IssueVotes } from "@/components/issues/peek-overview";
import { IssueEmojiReactions, IssueVotes } from "@/components/issues/reactions";
// hooks
import { usePublish } from "@/hooks/store";
import useIsInIframe from "@/hooks/use-is-in-iframe";
Expand Down
2 changes: 2 additions & 0 deletions space/core/components/issues/reactions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./issue-emoji-reactions";
export * from "./issue-vote-reactions";
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { useIssueDetails, useUser } from "@/hooks/store";

type IssueEmojiReactionsProps = {
anchor: string;
issueIdFromProps?: string;
size?: "md" | "sm";
};

export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer((props) => {
const { anchor } = props;
const { anchor, issueIdFromProps, size = "md" } = props;
// router
const router = useRouter();
const pathName = usePathname();
Expand All @@ -31,7 +33,7 @@ export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer(
const issueDetailsStore = useIssueDetails();
const { data: user } = useUser();

const issueId = issueDetailsStore.peekId;
const issueId = issueIdFromProps ?? issueDetailsStore.peekId;
const reactions = issueDetailsStore.details[issueId ?? ""]?.reaction_items ?? [];
const groupedReactions = groupReactions(reactions, "reaction");

Expand All @@ -55,6 +57,7 @@ export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer(

// derived values
const { queryParam } = queryParamGenerator({ peekId, board, state, priority, labels });
const reactionDimensions = size === "sm" ? "h-6 px-2 py-1" : "h-full px-2 py-1";

return (
<>
Expand All @@ -64,54 +67,52 @@ export const IssueEmojiReactions: React.FC<IssueEmojiReactionsProps> = observer(
else router.push(`/?next_path=${pathName}?${queryParam}`);
}}
selected={userReactions?.map((r) => r.reaction)}
size="md"
size={size}
/>
<div className="flex flex-wrap items-center gap-2">
{Object.keys(groupedReactions || {}).map((reaction) => {
const reactions = groupedReactions?.[reaction] ?? [];
const REACTIONS_LIMIT = 1000;
{Object.keys(groupedReactions || {}).map((reaction) => {
const reactions = groupedReactions?.[reaction] ?? [];
const REACTIONS_LIMIT = 1000;

if (reactions.length > 0)
return (
<Tooltip
key={reaction}
tooltipContent={
<div>
{reactions
?.map((r) => r?.actor_details?.display_name)
?.splice(0, REACTIONS_LIMIT)
?.join(", ")}
{reactions.length > REACTIONS_LIMIT && " and " + (reactions.length - REACTIONS_LIMIT) + " more"}
</div>
}
if (reactions.length > 0)
return (
<Tooltip
key={reaction}
tooltipContent={
<div>
{reactions
?.map((r) => r?.actor_details?.display_name)
?.splice(0, REACTIONS_LIMIT)
?.join(", ")}
{reactions.length > REACTIONS_LIMIT && " and " + (reactions.length - REACTIONS_LIMIT) + " more"}
</div>
}
>
<button
type="button"
onClick={() => {
if (user) handleReactionClick(reaction);
else router.push(`/?next_path=${pathName}?${queryParam}`);
}}
className={`flex items-center gap-1 rounded-md text-sm text-custom-text-100 ${
reactions.some((r) => r?.actor_details?.id === user?.id && r.reaction === reaction)
? "bg-custom-primary-100/10"
: "bg-custom-background-80"
} ${reactionDimensions}`}
>
<button
type="button"
onClick={() => {
if (user) handleReactionClick(reaction);
else router.push(`/?next_path=${pathName}?${queryParam}`);
}}
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
<span>{renderEmoji(reaction)}</span>
<span
className={
reactions.some((r) => r?.actor_details?.id === user?.id && r.reaction === reaction)
? "bg-custom-primary-100/10"
: "bg-custom-background-80"
}`}
? "text-custom-primary-100"
: ""
}
>
<span>{renderEmoji(reaction)}</span>
<span
className={
reactions.some((r) => r?.actor_details?.id === user?.id && r.reaction === reaction)
? "text-custom-primary-100"
: ""
}
>
{groupedReactions?.[reaction].length}{" "}
</span>
</button>
</Tooltip>
);
})}
</div>
{groupedReactions?.[reaction].length}{" "}
</span>
</button>
</Tooltip>
);
})}
</>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import useIsInIframe from "@/hooks/use-is-in-iframe";

type TIssueVotes = {
anchor: string;
issueIdFromProps?: string;
size?: "md" | "sm";
};

export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {
const { anchor } = props;
const { anchor, issueIdFromProps, size = "md" } = props;
// states
const [isSubmitting, setIsSubmitting] = useState(false);
// router
Expand All @@ -35,7 +37,7 @@ export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {

const isInIframe = useIsInIframe();

const issueId = issueDetailsStore.peekId;
const issueId = issueIdFromProps ?? issueDetailsStore.peekId;

const votes = issueDetailsStore.details[issueId ?? ""]?.vote_items ?? [];

Expand Down Expand Up @@ -66,6 +68,7 @@ export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {

// derived values
const { queryParam } = queryParamGenerator({ peekId, board, state, priority, labels });
const votingDimensions = size === "sm" ? "px-1 h-6 min-w-9" : "px-2 h-7";

return (
<div className="flex items-center gap-2">
Expand Down Expand Up @@ -96,7 +99,8 @@ export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {
else router.push(`/?next_path=${pathName}?${queryParam}`);
}}
className={cn(
"flex items-center justify-center gap-x-1 overflow-hidden rounded border px-2 h-7 focus:outline-none",
"flex items-center justify-center gap-x-1 overflow-hidden rounded border focus:outline-none bg-custom-background-100",
votingDimensions,
{
"border-custom-primary-200 text-custom-primary-200": isUpVotedByUser,
"border-custom-border-300": !isUpVotedByUser,
Expand Down Expand Up @@ -136,7 +140,8 @@ export const IssueVotes: React.FC<TIssueVotes> = observer((props) => {
else router.push(`/?next_path=${pathName}?${queryParam}`);
}}
className={cn(
"flex items-center justify-center gap-x-1 h-7 overflow-hidden rounded border px-2 focus:outline-none",
"flex items-center justify-center gap-x-1 overflow-hidden rounded border focus:outline-none bg-custom-background-100",
votingDimensions,
{
"border-red-600 text-red-600": isDownVotedByUser,
"border-custom-border-300": !isDownVotedByUser,
Expand Down