fix(posts): clearer read-only composer for viewers

Drop the 60% opacity / pointer-events lock on the editor's left column so
the client sees the draft clearly and can still click an image to open the
lightbox. Instead the composer renders read-only: the add-image tile,
remove/drag controls and the formatting toolbar are hidden and the caption
textarea is read-only, while the media click-to-preview stays active.
This commit is contained in:
Paulo Castellano 2026-06-22 17:08:29 -03:00
parent 1c9ab462d0
commit 04f6a60ee7
2 changed files with 13 additions and 10 deletions

View file

@ -46,9 +46,11 @@ const props = withDefaults(
platformLimits: PlatformLimit[];
mediaIssues: Record<string, MediaIssue[]>;
allowAiRegenerate?: boolean;
readOnly?: boolean;
}>(),
{
allowAiRegenerate: true,
readOnly: false,
},
);
@ -186,6 +188,7 @@ const onMediaDragEnd = () => {
const GRID_COLS = 4;
const onMediaKeydown = async (event: KeyboardEvent, index: number) => {
if (props.readOnly) return;
if (media.value.length < 2) return;
if (! event.altKey) return;
@ -215,7 +218,7 @@ const canRegenerateWithAi = (item: MediaItem): boolean => props.allowAiRegenerat
<div class="mx-auto max-w-2xl px-6 py-10">
<div class="relative">
<!-- Media grid (top) — always shown so "Add" tile is discoverable -->
<div class="mb-6">
<div v-if="!readOnly || media.length" class="mb-6">
<div class="grid grid-cols-4 gap-2">
<div
v-for="(item, index) in media"
@ -228,7 +231,7 @@ const canRegenerateWithAi = (item: MediaItem): boolean => props.allowAiRegenerat
mediaIssues[item.id] ? '!border-rose-500' : '',
]"
tabindex="0"
:draggable="media.length > 1"
:draggable="!readOnly && media.length > 1"
@click="openPreview(item)"
@dragstart="onMediaDragStart($event, index)"
@dragover="onMediaDragOver($event, index)"
@ -289,7 +292,7 @@ const canRegenerateWithAi = (item: MediaItem): boolean => props.allowAiRegenerat
</TooltipProvider>
<span
v-if="media.length > 1"
v-if="media.length > 1 && !readOnly"
class="absolute left-1.5 top-1.5 inline-flex size-6 cursor-grab items-center justify-center rounded-md border-2 border-foreground bg-card text-foreground opacity-0 shadow-2xs transition-opacity group-hover:opacity-100 group-focus:opacity-100"
>
<IconGripVertical class="size-3.5" />
@ -306,6 +309,7 @@ const canRegenerateWithAi = (item: MediaItem): boolean => props.allowAiRegenerat
</button>
<button
v-if="!readOnly"
type="button"
class="absolute right-1.5 top-1.5 inline-flex size-6 cursor-pointer items-center justify-center rounded-md border-2 border-foreground bg-card text-foreground opacity-0 shadow-2xs transition-all hover:bg-rose-100 hover:text-rose-700 group-hover:opacity-100 group-focus:opacity-100"
@click.stop="removeMedia(item.id)"
@ -315,6 +319,7 @@ const canRegenerateWithAi = (item: MediaItem): boolean => props.allowAiRegenerat
</div>
<button
v-if="!readOnly"
type="button"
class="flex aspect-square cursor-pointer flex-col items-center justify-center gap-1 rounded-xl border-2 border-dashed border-foreground/25 text-foreground/60 transition-colors hover:border-foreground hover:bg-foreground/5 hover:text-foreground"
@click="mediaPickerDialog?.open()"
@ -326,7 +331,7 @@ const canRegenerateWithAi = (item: MediaItem): boolean => props.allowAiRegenerat
</div>
<!-- Toolbar: between photos and textarea -->
<div class="mb-4 flex items-center gap-2">
<div v-if="!readOnly" class="mb-4 flex items-center gap-2">
<Popover v-model:open="emojiOpen">
<PopoverAnchor as-child>
<TooltipProvider>
@ -432,7 +437,8 @@ const canRegenerateWithAi = (item: MediaItem): boolean => props.allowAiRegenerat
</div>
<textarea
v-model="content"
:placeholder="$t('posts.edit.caption_placeholder')"
:readonly="readOnly"
:placeholder="readOnly ? '' : $t('posts.edit.caption_placeholder')"
class="relative block w-full resize-none border-0 bg-transparent p-0 font-sans text-base leading-[1.7] shadow-none outline-none placeholder:text-foreground/40"
style="min-height: 280px; field-sizing: content;"
/>

View file

@ -394,11 +394,7 @@ usePostEcho(post.value.id, '.post.comment.created', (e: any) => {
class="flex h-full"
:class="{ 'pointer-events-none select-none opacity-60': isScheduled }"
>
<div
class="w-full overflow-y-auto lg:w-2/3 lg:border-r-2 lg:border-foreground"
:class="{ 'pointer-events-none select-none opacity-60': !canCreatePost }"
:inert="!canCreatePost"
>
<div class="w-full overflow-y-auto lg:w-2/3 lg:border-r-2 lg:border-foreground">
<PostEditorComposer
v-model:content="content"
v-model:media="media"
@ -406,6 +402,7 @@ usePostEcho(post.value.id, '.post.comment.created', (e: any) => {
:platform-limits="platformLimits"
:media-issues="mediaIssues"
:allow-ai-regenerate="!isLocked"
:read-only="!canCreatePost"
@open-ai-generate="isAiGenerateOpen = true"
@open-ai-review="isAiReviewOpen = true"
@open-ai-regenerate-image="onOpenAiRegenerateImage"