Add hashtag suggestions and tagging to PostComposer
cgen-496aa33ab637443da87cf0baa6131285
This commit is contained in:
parent
54a50701a2
commit
fe99e2874e
1 changed files with 42 additions and 3 deletions
|
|
@ -17,18 +17,26 @@ function readFileAsDataURL(file: File): Promise<string> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PostComposer({ onPosted }: { onPosted?: () => void }) {
|
export default function PostComposer({
|
||||||
|
onPosted,
|
||||||
|
suggestedTags = [],
|
||||||
|
}: {
|
||||||
|
onPosted?: () => void;
|
||||||
|
suggestedTags?: string[];
|
||||||
|
}) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState("");
|
||||||
const [mediaFile, setMediaFile] = useState<File | null>(null);
|
const [mediaFile, setMediaFile] = useState<File | null>(null);
|
||||||
const [mediaUrlInput, setMediaUrlInput] = useState("");
|
const [mediaUrlInput, setMediaUrlInput] = useState("");
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
setText("");
|
setText("");
|
||||||
setMediaFile(null);
|
setMediaFile(null);
|
||||||
setMediaUrlInput("");
|
setMediaUrlInput("");
|
||||||
|
setSelectedTags([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadToStorage = async (file: File): Promise<string | null> => {
|
const uploadToStorage = async (file: File): Promise<string | null> => {
|
||||||
|
|
@ -104,12 +112,18 @@ export default function PostComposer({ onPosted }: { onPosted?: () => void }) {
|
||||||
? "New photo"
|
? "New photo"
|
||||||
: "Update");
|
: "Update");
|
||||||
|
|
||||||
|
const inlineTags = Array.from((text.match(/#[\p{L}0-9_]+/gu) || []).map((t) => t.replace(/^#/, "").toLowerCase()));
|
||||||
|
const baseTags = mediaType === "none" ? ["update"] : [mediaType, "feed"];
|
||||||
|
const combinedTags = Array.from(
|
||||||
|
new Set([...baseTags, ...selectedTags.map((t) => t.toLowerCase()), ...inlineTags]).values(),
|
||||||
|
);
|
||||||
|
|
||||||
await communityService.createPost({
|
await communityService.createPost({
|
||||||
author_id: user.id,
|
author_id: user.id,
|
||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
category: mediaType === "none" ? "text" : mediaType,
|
category: mediaType === "none" ? "text" : mediaType,
|
||||||
tags: mediaType === "none" ? ["update"] : [mediaType, "feed"],
|
tags: combinedTags,
|
||||||
is_published: true,
|
is_published: true,
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
|
|
@ -131,11 +145,36 @@ export default function PostComposer({ onPosted }: { onPosted?: () => void }) {
|
||||||
<Card className="bg-background/70 border-border/40">
|
<Card className="bg-background/70 border-border/40">
|
||||||
<CardContent className="p-4 space-y-3">
|
<CardContent className="p-4 space-y-3">
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder="Share an update…"
|
placeholder="Share an update… Use #hashtags to tag topics"
|
||||||
value={text}
|
value={text}
|
||||||
onChange={(e) => setText(e.target.value)}
|
onChange={(e) => setText(e.target.value)}
|
||||||
className="min-h-[80px]"
|
className="min-h-[80px]"
|
||||||
/>
|
/>
|
||||||
|
{suggestedTags.length > 0 && (
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{suggestedTags.map((tag) => {
|
||||||
|
const active = selectedTags.includes(tag);
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={tag}
|
||||||
|
type="button"
|
||||||
|
variant={active ? "default" : "outline"}
|
||||||
|
size="sm"
|
||||||
|
className={active ? "bg-aethex-500/80 text-white" : ""}
|
||||||
|
onClick={() =>
|
||||||
|
setSelectedTags((prev) =>
|
||||||
|
prev.includes(tag)
|
||||||
|
? prev.filter((t) => t !== tag)
|
||||||
|
: [...prev, tag],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
#{tag}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="flex flex-col sm:flex-row gap-3 items-start sm:items-center">
|
<div className="flex flex-col sm:flex-row gap-3 items-start sm:items-center">
|
||||||
<Input
|
<Input
|
||||||
type="file"
|
type="file"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue