feat: mention
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user