feat: mention

This commit is contained in:
gosangam
2026-06-10 16:29:44 +05:30
parent 482a845f37
commit 909ae67320
10 changed files with 628 additions and 66 deletions
+47 -1
View File
@@ -24,7 +24,8 @@ import {
DrawerHeader,
DrawerTitle,
} from "@/components/ui/drawer";
import { posts, SocialError } from "@/lib/social";
import { posts, SocialError, type UserSummary } from "@/lib/social";
import { MentionAutocomplete } from "@/components/social/mention-autocomplete";
import { buildHighlightUrl } from "@/lib/text-highlight";
import { getSourceReference } from "@/lib/source-reference";
import { compressImage } from "@/lib/compress-image";
@@ -79,6 +80,8 @@ export function ComposePost({
);
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
// Users tagged on this post (separate from the locked caption).
const [mentions, setMentions] = useState<UserSummary[]>([]);
async function onFiles(e: React.ChangeEvent<HTMLInputElement>) {
const files = Array.from(e.target.files ?? []);
@@ -149,6 +152,9 @@ export function ComposePost({
image_urls: images
.filter((img) => !img.uploading && img.url)
.map((img) => img.url),
mention_user_ids: mentions
.map((m) => m.id)
.filter((id): id is string => !!id),
});
onPosted?.();
onClose();
@@ -249,6 +255,46 @@ export function ComposePost({
) : null}
</div>
<div>
<p className="text-xs uppercase tracking-[0.18em] text-muted-foreground">
Mention people
</p>
{mentions.length > 0 ? (
<div className="mt-2 flex flex-wrap gap-2">
{mentions.map((u) => (
<span
key={u.id ?? u.handle}
className="inline-flex items-center gap-1.5 rounded-full bg-muted px-2.5 py-1 text-sm text-foreground"
>
@{u.handle}
<button
type="button"
onClick={() =>
setMentions((prev) => prev.filter((m) => m.id !== u.id))
}
aria-label={`Remove @${u.handle}`}
className="text-muted-foreground hover:text-foreground"
>
<X className="size-3.5" />
</button>
</span>
))}
</div>
) : null}
<div className="mt-2">
<MentionAutocomplete
excludeIds={mentions
.map((m) => m.id)
.filter((id): id is string => !!id)}
onSelect={(u) =>
setMentions((prev) =>
prev.some((m) => m.id === u.id) ? prev : [...prev, u],
)
}
/>
</div>
</div>
{error ? <p className="text-sm text-destructive">{error}</p> : null}
<button