From 6d6ebedc5e3a5773fb5eb84d152bb680360f978f Mon Sep 17 00:00:00 2001 From: shni Date: Sun, 5 Oct 2025 16:49:22 -0500 Subject: [PATCH] feat: improve handling of description and thumbnail editing in alliances display component creation --- .../alliaces/createDisplayComponent.ts | 165 +++++++++++++++++- 1 file changed, 160 insertions(+), 5 deletions(-) diff --git a/src/commands/messages/alliaces/createDisplayComponent.ts b/src/commands/messages/alliaces/createDisplayComponent.ts index 22c2840..a351f81 100644 --- a/src/commands/messages/alliaces/createDisplayComponent.ts +++ b/src/commands/messages/alliaces/createDisplayComponent.ts @@ -11,7 +11,15 @@ import logger from "../../../core/lib/logger"; import {CommandMessage} from "../../../core/types/commands"; import {listVariables} from "../../../core/lib/vars"; import type Amayo from "../../../core/client"; -import {BlockState, DisplayComponentUtils, EditorActionRow} from "../../../core/types/displayComponentEditor"; +import { + BlockState, + DisplayComponentUtils, + EditorActionRow, + DESCRIPTION_PLACEHOLDER, + syncDescriptionComponent, + ensureDescriptionTextComponent, + normalizeDisplayContent +} from "../../../core/types/displayComponentEditor"; import type {DisplayComponentContainer} from "../../../core/types/displayComponents"; import { hasManageGuildOrStaff } from "../../../core/lib/permissions"; @@ -103,7 +111,7 @@ export const command: CommandMessage = { coverImage: undefined, components: [ { type: 14, divider: false, spacing: 1 }, - { type: 10, content: "Usa los botones para configurar.", thumbnail: null } + { type: 10, content: DESCRIPTION_PLACEHOLDER, thumbnail: null } ] }; @@ -219,6 +227,149 @@ async function handleButtonInteraction( await handleAddImage(interaction, editorMessage, originalMessage, blockState); break; + case "edit_thumbnail": { + ensureDescriptionTextComponent(blockState, { placeholder: DESCRIPTION_PLACEHOLDER }); + + const descriptionNormalized = normalizeDisplayContent(blockState.description); + const textDisplays = blockState.components + .map((component: any, idx: number) => ({ component, idx })) + .filter(({ component }) => component?.type === 10); + + if (textDisplays.length === 0) { + await interaction.deferReply({ flags: MessageFlags.Ephemeral }).catch(() => {}); + await interaction.editReply({ content: '❌ No hay bloques de texto disponibles para añadir thumbnail.' }).catch(() => {}); + break; + } + + const options = textDisplays.map(({ component, idx }) => ({ + label: descriptionNormalized && normalizeDisplayContent(component.content) === descriptionNormalized + ? 'Descripción principal' + : `Texto #${idx + 1}: ${component.content?.slice(0, 30) || '...'}`, + value: String(idx), + description: component.thumbnail ? 'Con thumbnail' : component.linkButton ? 'Con botón link' : 'Sin accesorio' + })); + + try { + await interaction.reply({ + flags: MessageFlags.Ephemeral, + content: 'Selecciona el bloque de texto al que quieres editar el thumbnail:', + components: [ + { + type: 1, + components: [ + { + type: 3, + custom_id: 'choose_text_for_thumbnail', + placeholder: 'Selecciona un bloque de texto', + options + } + ] + } + ] + }); + } catch (error) { + logger.error({ err: error }, 'Error enviando selector de thumbnails'); + break; + } + + let replyMsg: Message | null = null; + try { + replyMsg = await interaction.fetchReply(); + } catch {} + + if (!replyMsg) break; + + const selCollector = replyMsg.createMessageComponentCollector({ + componentType: ComponentType.StringSelect, + max: 1, + time: 60000, + filter: (it: any) => it.user.id === originalMessage.author.id + }); + + selCollector.on('collect', async (sel: any) => { + selCollector.stop('collected'); + + const idx = parseInt(sel.values[0], 10); + if (Number.isNaN(idx)) { + try { + if (!sel.replied && !sel.deferred) { + await sel.reply({ content: '❌ Selección inválida.', flags: MessageFlags.Ephemeral }); + } + } catch {} + return; + } + + const textComp = blockState.components[idx]; + if (!textComp || textComp.type !== 10) { + try { + if (!sel.replied && !sel.deferred) { + await sel.reply({ content: '❌ El bloque seleccionado ya no existe.', flags: MessageFlags.Ephemeral }); + } + } catch {} + return; + } + + const modal = { + title: '📎 Editar Thumbnail', + customId: `edit_thumbnail_modal_${idx}`, + components: [ + { + type: ComponentType.Label, + label: 'URL del Thumbnail', + component: { + type: ComponentType.TextInput, + customId: 'thumbnail_input', + style: TextInputStyle.Short, + placeholder: 'https://ejemplo.com/thumbnail.png (vacío para eliminar)', + value: textComp.thumbnail || '', + maxLength: 512, + required: false + } + } + ] + } as const; + + try { + await sel.showModal(modal); + } catch (error) { + logger.error({ err: error }, 'No se pudo mostrar el modal de thumbnail'); + return; + } + + const modalInteraction = await awaitModalWithDeferredReply(sel); + if (!modalInteraction) return; + + const rawInput = modalInteraction.components.getTextInputValue('thumbnail_input').trim(); + + if (rawInput.length === 0) { + textComp.thumbnail = null; + await modalInteraction.editReply({ content: '✅ Thumbnail eliminado.' }).catch(() => {}); + } else if (!DisplayComponentUtils.isValidUrl(rawInput)) { + await modalInteraction.editReply({ content: '❌ URL de thumbnail inválida.' }).catch(() => {}); + return; + } else if (textComp.linkButton) { + await modalInteraction.editReply({ content: '❌ Este bloque tiene un botón link. Elimínalo antes de añadir un thumbnail.' }).catch(() => {}); + return; + } else { + textComp.thumbnail = rawInput; + await modalInteraction.editReply({ content: '✅ Thumbnail actualizado.' }).catch(() => {}); + } + + await updateEditor(editorMessage, { + display: await DisplayComponentUtils.renderPreview(blockState, originalMessage.member!, originalMessage.guild!), + components: DisplayComponentUtils.createEditorButtons(false) + }); + }); + + selCollector.on('end', async () => { + try { + await replyMsg!.edit({ components: [] }); + } catch {} + }); + + break; + } + case "cover_image": await handleCoverImage(interaction, editorMessage, originalMessage, blockState); break; @@ -356,7 +507,7 @@ async function handleButtonInteraction( } async function awaitModalWithDeferredReply( - interaction: ButtonInteraction, + interaction: ButtonInteraction | MessageComponentInteraction, options: Parameters[0] = { time: 300000 } ): Promise { try { @@ -465,9 +616,13 @@ async function handleEditDescription( modalInteraction = await awaitModalWithDeferredReply(interaction); if (!modalInteraction) return; - const newDescription = modalInteraction.components.getTextInputValue("description_input").trim(); + const rawDescription = modalInteraction.components.getTextInputValue("description_input"); + const previousDescription = typeof blockState.description === "string" ? blockState.description : null; + syncDescriptionComponent(blockState, rawDescription, { + previousDescription, + placeholder: DESCRIPTION_PLACEHOLDER + }); - blockState.description = newDescription || undefined; await updateEditor(editorMessage, { display: await DisplayComponentUtils.renderPreview(blockState, originalMessage.member!, originalMessage.guild!), components: DisplayComponentUtils.createEditorButtons(false)