Unificar endpoint de envío y agregar soporte para stickers
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m28s

- Consolidar send.post.ts y send-media.post.ts en un único endpoint /send
- Agregar servicio sticker-processor.ts para convertir imágenes a WebP 512x512
- Agregar toggle Imagen/Sticker en MediaPreview para enviar imágenes como stickers
- Actualizar MessageInput y página de mensajes para usar endpoint unificado
- Instalar dependencia sharp para procesamiento de imágenes
This commit is contained in:
2025-12-04 09:33:03 -06:00
parent 09d3c5398a
commit ec40cd6826
8 changed files with 458 additions and 291 deletions

View File

@@ -7,15 +7,27 @@
:key="index"
class="relative group"
>
<!-- Image preview -->
<!-- Image preview with sticker toggle -->
<div
v-if="isImage(file)"
class="relative"
>
<img
:src="getPreviewUrl(file)"
class="h-20 w-20 object-cover rounded-lg"
class="h-20 w-20 object-cover rounded-lg transition-all"
:class="{ 'ring-2 ring-[var(--wa-green)]': stickerModes[index] }"
/>
<!-- Sticker toggle button -->
<button
class="absolute bottom-0 left-0 right-0 py-0.5 text-[10px] font-medium transition-colors"
:class="stickerModes[index]
? 'bg-[var(--wa-green)] text-white rounded-b-lg'
: 'bg-black/60 text-white/80 rounded-b-lg hover:bg-black/80'"
@click="toggleStickerMode(index)"
>
{{ stickerModes[index] ? 'Sticker' : 'Imagen' }}
</button>
<!-- Remove button -->
<button
class="absolute -top-2 -right-2 w-5 h-5 rounded-full bg-red-500 text-white flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity"
@click="$emit('remove', index)"
@@ -81,16 +93,21 @@
</div>
</div>
<!-- Caption input -->
<div v-if="showCaption && files.length > 0">
<!-- Caption input (hide if all files are stickers) -->
<div v-if="showCaption && files.length > 0 && !allStickers">
<UInput
:model-value="caption"
placeholder="Agregar descripción..."
placeholder="Agregar descripcion..."
size="sm"
class="bg-[var(--wa-bg-light)]"
@update:model-value="$emit('update:caption', $event)"
/>
</div>
<!-- Info text for stickers -->
<p v-if="hasStickers" class="text-xs text-[var(--wa-text-muted)]">
Los stickers se convertiran a formato 512x512 WebP
</p>
</div>
</template>
@@ -101,16 +118,45 @@ interface Props {
showCaption?: boolean
}
withDefaults(defineProps<Props>(), {
const props = withDefaults(defineProps<Props>(), {
caption: '',
showCaption: true
})
defineEmits<{
const emit = defineEmits<{
remove: [index: number]
'update:caption': [value: string]
'update:stickerModes': [modes: boolean[]]
}>()
// Track which images should be sent as stickers
const stickerModes = ref<boolean[]>([])
// Initialize sticker modes when files change
watch(() => props.files, (files) => {
// Preserve existing modes and add false for new files
const newModes = files.map((_, i) => stickerModes.value[i] ?? false)
stickerModes.value = newModes
emit('update:stickerModes', [...newModes])
}, { immediate: true })
const toggleStickerMode = (index: number) => {
stickerModes.value[index] = !stickerModes.value[index]
emit('update:stickerModes', [...stickerModes.value])
}
// Check if all files are stickers (images marked as sticker)
const allStickers = computed(() => {
return props.files.length > 0 && props.files.every((f, i) =>
isImage(f) && stickerModes.value[i]
)
})
// Check if any file is marked as sticker
const hasStickers = computed(() => {
return props.files.some((f, i) => isImage(f) && stickerModes.value[i])
})
// Preview URL cache
const previewUrls = new Map<File, string>()