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 {CommandMessage} from "../../../core/types/commands";
|
||||||
import {listVariables} from "../../../core/lib/vars";
|
import {listVariables} from "../../../core/lib/vars";
|
||||||
import type Amayo from "../../../core/client";
|
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 type {DisplayComponentContainer} from "../../../core/types/displayComponents";
|
||||||
import { hasManageGuildOrStaff } from "../../../core/lib/permissions";
|
import { hasManageGuildOrStaff } from "../../../core/lib/permissions";
|
||||||
|
|
||||||
@@ -103,7 +111,7 @@ export const command: CommandMessage = {
|
|||||||
coverImage: undefined,
|
coverImage: undefined,
|
||||||
components: [
|
components: [
|
||||||
{ type: 14, divider: false, spacing: 1 },
|
{ 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);
|
await handleAddImage(interaction, editorMessage, originalMessage, blockState);
|
||||||
break;
|
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":
|
case "cover_image":
|
||||||
await handleCoverImage(interaction, editorMessage, originalMessage, blockState);
|
await handleCoverImage(interaction, editorMessage, originalMessage, blockState);
|
||||||
break;
|
break;
|
||||||
@@ -356,7 +507,7 @@ async function handleButtonInteraction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function awaitModalWithDeferredReply(
|
async function awaitModalWithDeferredReply(
|
||||||
interaction: ButtonInteraction,
|
interaction: ButtonInteraction | MessageComponentInteraction,
|
||||||
options: Parameters<ButtonInteraction['awaitModalSubmit']>[0] = { time: 300000 }
|
options: Parameters<ButtonInteraction['awaitModalSubmit']>[0] = { time: 300000 }
|
||||||
): Promise<ModalSubmitInteraction | null> {
|
): Promise<ModalSubmitInteraction | null> {
|
||||||
try {
|
try {
|
||||||
@@ -465,9 +616,13 @@ async function handleEditDescription(
|
|||||||
modalInteraction = await awaitModalWithDeferredReply(interaction);
|
modalInteraction = await awaitModalWithDeferredReply(interaction);
|
||||||
if (!modalInteraction) return;
|
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, {
|
await updateEditor(editorMessage, {
|
||||||
display: await DisplayComponentUtils.renderPreview(blockState, originalMessage.member!, originalMessage.guild!),
|
display: await DisplayComponentUtils.renderPreview(blockState, originalMessage.member!, originalMessage.guild!),
|
||||||
components: DisplayComponentUtils.createEditorButtons(false)
|
components: DisplayComponentUtils.createEditorButtons(false)
|
||||||
|
|||||||
Reference in New Issue
Block a user