mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-29 08:18:30 +02:00
refactor(post): improve PostViewer layout and editor previews
UI improvements: - Remove flex-1 from editor, use fixed min/max height - Move upload button next to publish button (icon-only) - Place relay selector below action buttons (not fixed) - Limit image/video previews to max-h-96 - Add pointer-events-none to event previews to prevent accidental interaction - Use EventDetailSkeleton for loading event previews Layout changes: - Single scrollable container with space-y-4 - Editor: 150-400px height range - Action buttons in single row (upload icon + publish button) - Relay list with max-h-64 scroll area - Better spacing and visual hierarchy
This commit is contained in:
@@ -249,9 +249,9 @@ export function PostViewer() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="h-full overflow-auto p-4 space-y-4">
|
||||
{/* Editor */}
|
||||
<div className="flex-1 overflow-auto p-4">
|
||||
<div>
|
||||
<RichEditor
|
||||
ref={editorRef}
|
||||
placeholder="What's on your mind?"
|
||||
@@ -260,111 +260,107 @@ export function PostViewer() {
|
||||
searchEmojis={searchEmojis}
|
||||
onFilePaste={handleFilePaste}
|
||||
autoFocus
|
||||
minHeight={200}
|
||||
maxHeight={600}
|
||||
minHeight={150}
|
||||
maxHeight={400}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Bottom section: Relay selection and publish button */}
|
||||
<div className="border-t border-border bg-muted/30 p-4 space-y-4">
|
||||
{/* Relay selection */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-sm font-medium">
|
||||
Relays ({selectedRelays.size} selected)
|
||||
</Label>
|
||||
{writeRelays.length > 0 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={updateRelayStates}
|
||||
disabled={isPublishing}
|
||||
className="h-6 text-xs"
|
||||
>
|
||||
<RefreshCw className="h-3 w-3 mr-1" />
|
||||
Reset
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{/* Action buttons */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => openUpload()}
|
||||
disabled={isPublishing}
|
||||
title="Upload image/video"
|
||||
>
|
||||
<Paperclip className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
{writeRelays.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No write relays configured. Please add relays in your profile
|
||||
settings.
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => editorRef.current?.submit()}
|
||||
disabled={isPublishing || selectedRelays.size === 0}
|
||||
className="gap-2 flex-1"
|
||||
>
|
||||
{isPublishing ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Publishing...
|
||||
</>
|
||||
) : (
|
||||
<div className="space-y-2 max-h-48 overflow-y-auto">
|
||||
{relayStates.map((relay) => (
|
||||
<div
|
||||
key={relay.url}
|
||||
className="flex items-center justify-between gap-3 rounded-md border border-border bg-background p-2"
|
||||
>
|
||||
<div className="flex items-center gap-2 flex-1 min-w-0">
|
||||
<Checkbox
|
||||
id={relay.url}
|
||||
checked={selectedRelays.has(relay.url)}
|
||||
onCheckedChange={() => toggleRelay(relay.url)}
|
||||
disabled={isPublishing}
|
||||
/>
|
||||
<label
|
||||
htmlFor={relay.url}
|
||||
className="text-sm cursor-pointer truncate flex-1"
|
||||
>
|
||||
{relay.url.replace(/^wss?:\/\//, "")}
|
||||
</label>
|
||||
</div>
|
||||
<>
|
||||
<Send className="h-4 w-4" />
|
||||
Publish
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Status indicator */}
|
||||
<div className="flex-shrink-0">
|
||||
{relay.status === "publishing" && (
|
||||
<Loader2 className="h-4 w-4 animate-spin text-blue-500" />
|
||||
)}
|
||||
{relay.status === "success" && (
|
||||
<Check className="h-4 w-4 text-green-500" />
|
||||
)}
|
||||
{relay.status === "error" && (
|
||||
<div title={relay.error || "Failed to publish"}>
|
||||
<X className="h-4 w-4 text-red-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Relay selection */}
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-sm font-medium">
|
||||
Relays ({selectedRelays.size} selected)
|
||||
</Label>
|
||||
{writeRelays.length > 0 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={updateRelayStates}
|
||||
disabled={isPublishing}
|
||||
className="h-6 text-xs"
|
||||
>
|
||||
<RefreshCw className="h-3 w-3 mr-1" />
|
||||
Reset
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Action buttons */}
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => openUpload()}
|
||||
disabled={isPublishing}
|
||||
className="gap-2"
|
||||
>
|
||||
<Paperclip className="h-4 w-4" />
|
||||
Upload
|
||||
</Button>
|
||||
{writeRelays.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No write relays configured. Please add relays in your profile
|
||||
settings.
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-2 max-h-64 overflow-y-auto">
|
||||
{relayStates.map((relay) => (
|
||||
<div
|
||||
key={relay.url}
|
||||
className="flex items-center justify-between gap-3 rounded-md border border-border bg-background p-2"
|
||||
>
|
||||
<div className="flex items-center gap-2 flex-1 min-w-0">
|
||||
<Checkbox
|
||||
id={relay.url}
|
||||
checked={selectedRelays.has(relay.url)}
|
||||
onCheckedChange={() => toggleRelay(relay.url)}
|
||||
disabled={isPublishing}
|
||||
/>
|
||||
<label
|
||||
htmlFor={relay.url}
|
||||
className="text-sm cursor-pointer truncate flex-1"
|
||||
>
|
||||
{relay.url.replace(/^wss?:\/\//, "")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
onClick={() => editorRef.current?.submit()}
|
||||
disabled={isPublishing || selectedRelays.size === 0}
|
||||
className="gap-2"
|
||||
>
|
||||
{isPublishing ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Publishing...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Send className="h-4 w-4" />
|
||||
Publish
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
{/* Status indicator */}
|
||||
<div className="flex-shrink-0">
|
||||
{relay.status === "publishing" && (
|
||||
<Loader2 className="h-4 w-4 animate-spin text-blue-500" />
|
||||
)}
|
||||
{relay.status === "success" && (
|
||||
<Check className="h-4 w-4 text-green-500" />
|
||||
)}
|
||||
{relay.status === "error" && (
|
||||
<div title={relay.error || "Failed to publish"}>
|
||||
<X className="h-4 w-4 text-red-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Upload dialog */}
|
||||
|
||||
@@ -33,7 +33,7 @@ export function BlobAttachmentRich({ node, deleteNode }: ReactNodeViewProps) {
|
||||
<img
|
||||
src={url}
|
||||
alt="attachment"
|
||||
className="max-w-full h-auto"
|
||||
className="max-w-full h-auto max-h-96 object-contain"
|
||||
draggable={false}
|
||||
/>
|
||||
{deleteNode && (
|
||||
@@ -53,7 +53,7 @@ export function BlobAttachmentRich({ node, deleteNode }: ReactNodeViewProps) {
|
||||
<video
|
||||
src={url}
|
||||
controls
|
||||
className="max-w-full h-auto"
|
||||
className="max-w-full h-auto max-h-96"
|
||||
preload="metadata"
|
||||
/>
|
||||
{deleteNode && (
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NodeViewWrapper, type ReactNodeViewProps } from "@tiptap/react";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { DetailKindRenderer } from "@/components/nostr/kinds";
|
||||
import type { EventPointer, AddressPointer } from "nostr-tools/nip19";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { EventDetailSkeleton } from "@/components/ui/skeleton";
|
||||
|
||||
/**
|
||||
* Rich preview component for Nostr events in the editor
|
||||
@@ -41,12 +41,9 @@ export function NostrEventPreviewRich({ node }: ReactNodeViewProps) {
|
||||
|
||||
return (
|
||||
<NodeViewWrapper className="my-2">
|
||||
<div className="rounded-lg border border-border bg-muted/30 p-3">
|
||||
<div className="rounded-lg border border-border bg-muted/30 p-3 pointer-events-none">
|
||||
{!event ? (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
<span>Loading event...</span>
|
||||
</div>
|
||||
<EventDetailSkeleton className="py-2" />
|
||||
) : (
|
||||
<DetailKindRenderer event={event} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user