mirror of
https://gitlab.com/soapbox-pub/mkstack.git
synced 2025-08-27 04:59:22 +00:00
193 lines
6.7 KiB
TypeScript
193 lines
6.7 KiB
TypeScript
![]() |
import { useState } from 'react';
|
||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||
|
import { Textarea } from '@/components/ui/textarea';
|
||
|
import { Button } from '@/components/ui/button';
|
||
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||
|
import { Badge } from '@/components/ui/badge';
|
||
|
import { Send, Sparkles, Leaf } from 'lucide-react';
|
||
|
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||
|
import { useNostrPublish } from '@/hooks/useNostrPublish';
|
||
|
import { useAuthor } from '@/hooks/useAuthor';
|
||
|
import { genUserName } from '@/lib/genUserName';
|
||
|
import { translateToGnomeSpeak } from '@/lib/gnomeSpeak';
|
||
|
import { useToast } from '@/hooks/useToast';
|
||
|
|
||
|
export function GnomeComposer() {
|
||
|
const [content, setContent] = useState('');
|
||
|
const [isPreviewMode, setIsPreviewMode] = useState(false);
|
||
|
const { user } = useCurrentUser();
|
||
|
const { mutate: createEvent, isPending } = useNostrPublish();
|
||
|
const { toast } = useToast();
|
||
|
|
||
|
const author = useAuthor(user?.pubkey || '');
|
||
|
const metadata = author.data?.metadata;
|
||
|
const displayName = metadata?.name ?? (user?.pubkey ? genUserName(user.pubkey) : 'Anonymous Gnome');
|
||
|
const profileImage = metadata?.picture;
|
||
|
|
||
|
const handleSubmit = () => {
|
||
|
if (!content.trim()) {
|
||
|
toast({
|
||
|
title: "Empty woodland proclamation!",
|
||
|
description: "Even gnomes need something to say to the forest folk.",
|
||
|
variant: "destructive",
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!user) {
|
||
|
toast({
|
||
|
title: "Authentication required",
|
||
|
description: "You must be logged in to share woodland wisdom.",
|
||
|
variant: "destructive",
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
createEvent(
|
||
|
{
|
||
|
kind: 1,
|
||
|
content: content.trim(),
|
||
|
tags: [['t', 'gnome']] // Tag all posts as gnome content
|
||
|
},
|
||
|
{
|
||
|
onSuccess: () => {
|
||
|
setContent('');
|
||
|
setIsPreviewMode(false);
|
||
|
toast({
|
||
|
title: "Woodland proclamation sent! 🍄",
|
||
|
description: "Your message has been carried by the forest winds to all the message-mushrooms.",
|
||
|
});
|
||
|
},
|
||
|
onError: (error) => {
|
||
|
toast({
|
||
|
title: "Forest spirits are troubled",
|
||
|
description: `Failed to send your proclamation: ${error.message}`,
|
||
|
variant: "destructive",
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
};
|
||
|
|
||
|
const gnomeTranslatedContent = content ? translateToGnomeSpeak(content) : '';
|
||
|
|
||
|
if (!user) {
|
||
|
return (
|
||
|
<Card className="mb-6 border-2 border-dashed border-muted">
|
||
|
<CardContent className="py-8 text-center">
|
||
|
<div className="space-y-4">
|
||
|
<div className="text-4xl">🍄</div>
|
||
|
<div>
|
||
|
<h3 className="font-semibold mb-2">Join the Forest Council</h3>
|
||
|
<p className="text-muted-foreground">
|
||
|
Log in to share your woodland wisdom with fellow gnomes across the message-mushroom network!
|
||
|
</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
</CardContent>
|
||
|
</Card>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
<Card className="mb-6 border-2 border-primary/20 shadow-lg">
|
||
|
<CardHeader className="pb-3">
|
||
|
<div className="flex items-center space-x-3">
|
||
|
<Avatar className="h-10 w-10 border-2 border-primary/30">
|
||
|
<AvatarImage src={profileImage} alt={displayName} />
|
||
|
<AvatarFallback className="bg-primary text-primary-foreground font-bold">
|
||
|
{displayName.slice(0, 2).toUpperCase()}
|
||
|
</AvatarFallback>
|
||
|
</Avatar>
|
||
|
<div className="flex-1">
|
||
|
<div className="flex items-center space-x-2">
|
||
|
<CardTitle className="text-lg">Share Woodland Wisdom</CardTitle>
|
||
|
<Badge variant="secondary" className="gnome-wiggle">
|
||
|
🍄 Gnome Speak
|
||
|
</Badge>
|
||
|
</div>
|
||
|
<p className="text-sm text-muted-foreground">
|
||
|
Your words will be translated into the ancient gnome tongue
|
||
|
</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
</CardHeader>
|
||
|
|
||
|
<CardContent className="space-y-4">
|
||
|
<div className="space-y-2">
|
||
|
<div className="flex items-center justify-between">
|
||
|
<label className="text-sm font-medium">Your Message:</label>
|
||
|
<Button
|
||
|
variant="ghost"
|
||
|
size="sm"
|
||
|
onClick={() => setIsPreviewMode(!isPreviewMode)}
|
||
|
className="text-xs"
|
||
|
>
|
||
|
<Sparkles className="h-3 w-3 mr-1" />
|
||
|
{isPreviewMode ? 'Edit' : 'Preview Gnome Speak'}
|
||
|
</Button>
|
||
|
</div>
|
||
|
|
||
|
{!isPreviewMode ? (
|
||
|
<Textarea
|
||
|
value={content}
|
||
|
onChange={(e) => setContent(e.target.value)}
|
||
|
placeholder="Share your thoughts with the forest folk... (will be translated to gnome speak)"
|
||
|
className="min-h-[100px] resize-none border-2 border-border/50 focus:border-primary/50"
|
||
|
maxLength={280}
|
||
|
/>
|
||
|
) : (
|
||
|
<div className="min-h-[100px] p-3 border-2 border-accent/30 rounded-md bg-accent/5">
|
||
|
<p className="text-sm text-muted-foreground mb-2 font-medium">
|
||
|
🍄 Gnome Speak Translation:
|
||
|
</p>
|
||
|
<div className="text-sm leading-relaxed">
|
||
|
{gnomeTranslatedContent || (
|
||
|
<span className="text-muted-foreground italic">
|
||
|
Your gnome translation will appear here...
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
)}
|
||
|
|
||
|
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
||
|
<span>
|
||
|
{content.length}/280 characters
|
||
|
</span>
|
||
|
{content && (
|
||
|
<span className="text-accent">
|
||
|
✨ Will be auto-translated to gnome speak
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<div className="flex items-center justify-between pt-2">
|
||
|
<div className="flex items-center space-x-2 text-xs text-muted-foreground">
|
||
|
<Leaf className="h-3 w-3" />
|
||
|
<span>Broadcasting to message-mushroom network</span>
|
||
|
</div>
|
||
|
|
||
|
<Button
|
||
|
onClick={handleSubmit}
|
||
|
disabled={!content.trim() || isPending}
|
||
|
className="font-medium"
|
||
|
>
|
||
|
{isPending ? (
|
||
|
<>
|
||
|
<Sparkles className="h-4 w-4 mr-2 animate-spin" />
|
||
|
Sending to Forest...
|
||
|
</>
|
||
|
) : (
|
||
|
<>
|
||
|
<Send className="h-4 w-4 mr-2" />
|
||
|
Share Wisdom
|
||
|
</>
|
||
|
)}
|
||
|
</Button>
|
||
|
</div>
|
||
|
</CardContent>
|
||
|
</Card>
|
||
|
);
|
||
|
}
|