Skip to content
7 changes: 7 additions & 0 deletions docs/COMMAND-WIKI.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,13 @@
- ``uwid``: The Quest ID of the user.
- **Subcommands:** None

## pg
- **Aliases:** `pagination`, `paginationtest`, `pgtest`
- **Description:** Test the pagination feature.
- **Examples:**<br> `.pg`<br> `.pagination`<br> `.pagination`<br> `.pgtest`
- **Options:** None
- **Subcommands:** None

## ping
- **Aliases:** `pong`
- **Description:** Ping the bot to see if it is alive. :ping_pong:
Expand Down
75 changes: 75 additions & 0 deletions src/commandDetails/miscellaneous/pagination-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { container } from '@sapphire/framework';
import { CodeyCommandDetails, getUserFromMessage } from '../../codeyCommand';
import { EmbedBuilder } from 'discord.js';
import { PaginationBuilder } from '../../utils/pagination';
import { SapphireMessageExecuteType, SapphireMessageResponse } from '../../codeyCommand';
import { Colors } from 'discord.js';

const PaginationTestExecuteCommand: SapphireMessageExecuteType = async (
_client,
messageFromUser,
_args,
): Promise<SapphireMessageResponse> => {
const message = messageFromUser;
const author = getUserFromMessage(message).id;

const embedData: { title: string; description: string; color: number }[] = [
{
title: 'Page 1',
description: 'Hey there! This is the content of Page 1 💙',
color: 0xff0000,
},
{
title: 'Page 2',
description: 'Hey there! This is the content of Page 2 🎉',
color: 0x00ff00,
},
{
title: 'Page 3',
description: 'Hey there! This is the content of Page 3 👽',
color: 0x0000ff,
},
{
title: 'Page 4',
description: 'Hey there! This is the content of Page 4 🎃',
color: 0xff00ff,
},
{
title: 'Page 5',
description: 'Hey there! This is the content of Page 5 🎄',
color: 0xffff00,
},
];

const embeds: EmbedBuilder[] = embedData.map((data) =>
new EmbedBuilder()
.setColor(Colors.Blue)
.setTitle(data.title)
.setDescription(data.description)
.setColor(data.color),
);

try {
await PaginationBuilder(message, author, embeds, 10000);
} catch (error) {
await message.reply('Error or timeout occurred during navigation.');
}
};

export const paginationTestCommandDetails: CodeyCommandDetails = {
name: 'pg',
aliases: ['pagination', 'paginationtest', 'pgtest'],
description: 'Test the pagination feature.',
detailedDescription: `**Examples:**
\`${container.botPrefix}pg\`
\`${container.botPrefix}pagination\`
\`${container.botPrefix}pagination\`
\`${container.botPrefix}pgtest\``,

isCommandResponseEphemeral: false,
messageWhenExecutingCommand: 'Testing pagination...',
executeCommand: PaginationTestExecuteCommand,
messageIfFailure: 'Could not test pagination.',
options: [],
subcommandDetails: {},
};
16 changes: 16 additions & 0 deletions src/commands/miscellaneous/pagination-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Command } from '@sapphire/framework';
import { CodeyCommand } from '../../codeyCommand';
import { paginationTestCommandDetails } from '../../commandDetails/miscellaneous/pagination-test';

export class MiscellaneousPaginationTestCommand extends CodeyCommand {
details = paginationTestCommandDetails;

public constructor(context: Command.Context, options: Command.Options) {
super(context, {
...options,
aliases: paginationTestCommandDetails.aliases,
description: paginationTestCommandDetails.description,
detailedDescription: paginationTestCommandDetails.detailedDescription,
});
}
}
123 changes: 123 additions & 0 deletions src/utils/pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {
EmbedBuilder,
ButtonBuilder,
ButtonStyle,
ActionRowBuilder,
ButtonInteraction,
ComponentType,
Message,
ChatInputCommandInteraction,
CacheType,
} from 'discord.js';

const COLLECTOR_TIMEOUT = 120000;

export const PaginationBuilder = async (
originalMessage: Message<boolean> | ChatInputCommandInteraction<CacheType>,
author: string,
embedPages: EmbedBuilder[],
timeout: number = COLLECTOR_TIMEOUT,
): Promise<Message<boolean> | undefined> => {
try {
if (!embedPages || !embedPages.length) {
await originalMessage.reply({
embeds: [new EmbedBuilder().setColor(0xff0000).setDescription('No pages to display.')],
});
return;
}
let currentPage = 0;
const firstButton = new ButtonBuilder()
.setCustomId('first')
.setEmoji('⏮️')
// .setLabel('First')
.setStyle(ButtonStyle.Primary)
.setDisabled(true);
const previousButton = new ButtonBuilder()
.setCustomId('previous')
.setEmoji('⬅️')
// .setLabel('Previous')
.setStyle(ButtonStyle.Primary)
.setDisabled(true);
const pageCount = new ButtonBuilder()
.setCustomId('pagecount')
.setLabel(`${currentPage + 1}/${embedPages.length}`)
.setStyle(ButtonStyle.Secondary)
.setDisabled(true);
const nextButton = new ButtonBuilder()
.setCustomId('next')
.setEmoji('➡️')
// .setLabel('Next')
.setStyle(ButtonStyle.Primary)
.setDisabled(embedPages.length <= 1);
const lastButton = new ButtonBuilder()
.setCustomId('last')
.setEmoji('⏭️')
// .setLabel('Last')
.setStyle(ButtonStyle.Primary)
.setDisabled(embedPages.length <= 1);
const actionRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
firstButton,
previousButton,
pageCount,
nextButton,
lastButton,
);

const message = await originalMessage.reply({
embeds: [embedPages[currentPage]],
components: [actionRow],
fetchReply: true,
});

await message.edit({
embeds: [embedPages[currentPage]],
components: [actionRow],
});

const collector = message.createMessageComponentCollector({
filter: (interaction) => interaction.user.id === author,
componentType: ComponentType.Button,
idle: timeout,
});

collector.on('collect', async (buttonInteraction: ButtonInteraction) => {
await buttonInteraction.deferUpdate();

switch (buttonInteraction.customId) {
case 'first':
currentPage = 0;
break;
case 'previous':
currentPage = Math.max(0, currentPage - 1);
break;
case 'next':
currentPage = Math.min(embedPages.length - 1, currentPage + 1);
break;
case 'last':
currentPage = embedPages.length - 1;
break;
}

firstButton.setDisabled(currentPage === 0);
previousButton.setDisabled(currentPage === 0);
nextButton.setDisabled(currentPage === embedPages.length - 1);
lastButton.setDisabled(currentPage === embedPages.length - 1);
pageCount.setLabel(`${currentPage + 1}/${embedPages.length}`);

await message.edit({
embeds: [embedPages[currentPage]],
components: [actionRow],
});
});

collector.on('end', async () => {
await message.edit({
components: [],
});
});

return message;
} catch (error) {
return undefined;
}
};