diff --git a/index.js b/index.js index 75fc31e..e672dd6 100644 --- a/index.js +++ b/index.js @@ -56,9 +56,11 @@ client.once('clientReady', async () => { }); client.on(Events.InteractionCreate, async interaction => { - if (interaction.isStringSelectMenu()) { - await handlePinboardSelection(interaction); - return; + if (interaction.isStringSelectMenu() || interaction.isButton()) { + const handled = await handlePinboardSelection(interaction); + if (handled) { + return; + } } if (!interaction.isChatInputCommand()) return; diff --git a/src/pterodactyl/displayer.js b/src/pterodactyl/displayer.js index 407e280..c4a97ae 100644 --- a/src/pterodactyl/displayer.js +++ b/src/pterodactyl/displayer.js @@ -1,8 +1,11 @@ -const { EmbedBuilder, ActionRowBuilder, StringSelectMenuBuilder, AttachmentBuilder } = require('discord.js'); +const { EmbedBuilder, ActionRowBuilder, StringSelectMenuBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); const fs = require('node:fs'); const path = require('node:path'); const PINBOARD_SELECT_CUSTOM_ID = 'pinboard-image-select'; +const PINBOARD_PREV_CUSTOM_ID = 'pinboard-image-prev'; +const PINBOARD_NEXT_CUSTOM_ID = 'pinboard-image-next'; +const PINBOARD_INDEX_CUSTOM_ID = 'pinboard-image-index'; const selectedImageIndexByMessage = new Map(); const preloadedImagesByUrl = new Map(); const warmedMessageIds = new Set(); @@ -99,6 +102,12 @@ const clampIndex = (index, length) => { return index; }; +const shiftIndex = (currentIndex, delta, length) => { + if (length <= 0) return 0; + const normalizedCurrent = clampIndex(currentIndex, length); + return (normalizedCurrent + delta + length) % length; +}; + const sanitizeAttachmentName = (name) => ( (name || 'pinboard-image') .toLowerCase() @@ -217,7 +226,7 @@ const buildSelectedImageEmbed = (item, imageReference, options = {}) => { return new EmbedBuilder() .setColor('#f59e0b') - .setTitle(item.name) + .setTitle('Stats du Serveur') .setImage(imageUrl); }; @@ -236,6 +245,31 @@ const buildImageSelectorRow = (items, selectedIndex) => { return new ActionRowBuilder().addComponents(selectMenu); }; +const buildImageNavigationRow = (items, selectedIndex) => { + const hasItems = items.length > 0; + const indexLabel = hasItems ? `${selectedIndex + 1}/${items.length}` : '0/0'; + + const previousButton = new ButtonBuilder() + .setCustomId(PINBOARD_PREV_CUSTOM_ID) + .setLabel('<<') + .setStyle(ButtonStyle.Secondary) + .setDisabled(!hasItems); + + const indexButton = new ButtonBuilder() + .setCustomId(PINBOARD_INDEX_CUSTOM_ID) + .setLabel(indexLabel) + .setStyle(ButtonStyle.Secondary) + .setDisabled(true); + + const nextButton = new ButtonBuilder() + .setCustomId(PINBOARD_NEXT_CUSTOM_ID) + .setLabel('>>') + .setStyle(ButtonStyle.Secondary) + .setDisabled(!hasItems); + + return new ActionRowBuilder().addComponents(previousButton, indexButton, nextButton); +}; + const getMinecraftStatus = async () => { const address = process.env.MINECRAFT_SERVER_ADDRESS; @@ -296,6 +330,7 @@ const buildPanelPayload = (status, messageId, pinboardItems = null, options = {} disableCacheBust: options.disableCacheBustForSelected }); const selectorRow = buildImageSelectorRow(items, currentIndex); + const navigationRow = buildImageNavigationRow(items, currentIndex); const shouldIncludeFiles = options.includeFiles !== false; const files = shouldIncludeFiles && cachedImage?.buffer @@ -305,7 +340,7 @@ const buildPanelPayload = (status, messageId, pinboardItems = null, options = {} return { content: '', embeds: [panelEmbed, selectedImageEmbed], - components: [selectorRow], + components: [selectorRow, navigationRow], files }; }; @@ -372,7 +407,11 @@ const update = async (client) => { }; const handlePinboardSelection = async (interaction) => { - if (!interaction.isStringSelectMenu() || interaction.customId !== PINBOARD_SELECT_CUSTOM_ID) { + const isImageSelect = interaction.isStringSelectMenu() && interaction.customId === PINBOARD_SELECT_CUSTOM_ID; + const isImageNavButton = interaction.isButton() + && (interaction.customId === PINBOARD_PREV_CUSTOM_ID || interaction.customId === PINBOARD_NEXT_CUSTOM_ID); + + if (!isImageSelect && !isImageNavButton) { return false; } @@ -384,8 +423,18 @@ const handlePinboardSelection = async (interaction) => { return true; } - const selectedRaw = Number.parseInt(interaction.values?.[0] || '0', 10); - const selectedIndex = clampIndex(selectedRaw, pinboardItems.length); + let selectedIndex = clampIndex(selectedImageIndexByMessage.get(interaction.message.id) ?? 0, pinboardItems.length); + + if (isImageSelect) { + const selectedRaw = Number.parseInt(interaction.values?.[0] || '0', 10); + selectedIndex = clampIndex(selectedRaw, pinboardItems.length); + } + + if (isImageNavButton) { + const delta = interaction.customId === PINBOARD_PREV_CUSTOM_ID ? -1 : 1; + selectedIndex = shiftIndex(selectedIndex, delta, pinboardItems.length); + } + selectedImageIndexByMessage.set(interaction.message.id, selectedIndex); const status = lastKnownStatus || await getMinecraftStatus();