feat(mentions): support @all to mention all workspace members
Add @all mention type that notifies all workspace members (excluding agents). Includes backend parsing, notification expansion to all members, and frontend UI with autocomplete suggestion, rendering, and hover card.
This commit is contained in:
parent
e68091e4a8
commit
095b7f8185
7 changed files with 91 additions and 17 deletions
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { Users } from "lucide-react";
|
||||
import { HoverCard, HoverCardTrigger, HoverCardContent } from "@/components/ui/hover-card";
|
||||
import { ActorAvatar } from "@/components/common/actor-avatar";
|
||||
import { useWorkspaceStore } from "@/features/workspace";
|
||||
|
|
@ -15,6 +16,27 @@ function MentionHoverCard({ type, id, children }: MentionHoverCardProps) {
|
|||
const members = useWorkspaceStore((s) => s.members);
|
||||
const agents = useWorkspaceStore((s) => s.agents);
|
||||
|
||||
if (type === "all") {
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger render={<span />} className="cursor-default">
|
||||
{children}
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start" className="w-auto min-w-48 max-w-72">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary/10">
|
||||
<Users className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium">All members</p>
|
||||
<p className="text-xs text-muted-foreground">Notifies all workspace members</p>
|
||||
</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "member") {
|
||||
const member = members.find((m) => m.user_id === id);
|
||||
if (!member) return <>{children}</>;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Hash } from "lucide-react";
|
||||
import { Hash, Users } from "lucide-react";
|
||||
import { ReactRenderer } from "@tiptap/react";
|
||||
import { computePosition, offset, flip, shift } from "@floating-ui/dom";
|
||||
import { useWorkspaceStore } from "@/features/workspace";
|
||||
|
|
@ -23,7 +23,7 @@ import type { SuggestionOptions, SuggestionProps } from "@tiptap/suggestion";
|
|||
export interface MentionItem {
|
||||
id: string;
|
||||
label: string;
|
||||
type: "member" | "agent" | "issue";
|
||||
type: "member" | "agent" | "issue" | "all";
|
||||
/** Secondary text shown below the label (e.g. issue title) */
|
||||
description?: string;
|
||||
}
|
||||
|
|
@ -99,7 +99,11 @@ const MentionList = forwardRef<MentionListRef, MentionListProps>(
|
|||
}`}
|
||||
onClick={() => selectItem(index)}
|
||||
>
|
||||
{item.type === "issue" ? (
|
||||
{item.type === "all" ? (
|
||||
<span className="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||
<Users className="h-3 w-3" />
|
||||
</span>
|
||||
) : item.type === "issue" ? (
|
||||
<span className="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||
<Hash className="h-3 w-3" />
|
||||
</span>
|
||||
|
|
@ -137,6 +141,12 @@ export function createMentionSuggestion(): Omit<
|
|||
const { issues } = useIssueStore.getState();
|
||||
const q = query.toLowerCase();
|
||||
|
||||
// Show "All members" option when query is empty or matches "all"
|
||||
const allItem: MentionItem[] =
|
||||
"all members".includes(q) || "all".includes(q)
|
||||
? [{ id: "all", label: "All members", type: "all" as const, description: "Notify all members" }]
|
||||
: [];
|
||||
|
||||
const memberItems: MentionItem[] = members
|
||||
.filter((m) => m.name.toLowerCase().includes(q))
|
||||
.map((m) => ({
|
||||
|
|
@ -162,7 +172,7 @@ export function createMentionSuggestion(): Omit<
|
|||
description: i.title,
|
||||
}));
|
||||
|
||||
return [...memberItems, ...agentItems, ...issueItems].slice(0, 10);
|
||||
return [...allItem, ...memberItems, ...agentItems, ...issueItems].slice(0, 10);
|
||||
},
|
||||
|
||||
render: () => {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ const MentionExtension = Mention.configure({
|
|||
return {
|
||||
type: "mention",
|
||||
raw: match[0],
|
||||
attributes: { label: match[1], type: match[2], id: match[3] },
|
||||
attributes: { label: match[1], type: match[2] ?? "member", id: match[3] },
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue