feat: improve handling of description and thumbnail editing in alliances display component creation
This commit is contained in:
@@ -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<ButtonInteraction['awaitModalSubmit']>[0] = { time: 300000 }
|
||||
): Promise<ModalSubmitInteraction | null> {
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user