3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-03-15 05:41:51 +00:00

Switch pagination to buttons

This commit is contained in:
Dark 2021-06-21 01:36:35 +02:00
parent f9dd82f201
commit 6e22687bf6
No known key found for this signature in database
GPG key ID: 384C4B4F5B1E25A8
4 changed files with 131 additions and 82 deletions

View file

@ -1,4 +1,4 @@
import { GuildMember } from "discord.js";
import { GuildMember, MessageActionRow, MessageButton, MessageComponentInteraction } from "discord.js";
import moment from "moment-timezone";
import { commandTypeHelpers as ct } from "../../../commandTypes";
import { humanizeDurationShort } from "../../../humanizeDurationShort";
@ -27,10 +27,9 @@ export const MutesCmd = mutesCmd({
let totalMutes = 0;
let hasFilters = false;
let hasReactions = false;
let clearReactionsFn;
let clearReactionsTimeout;
const clearReactionsDebounce = 5 * MINUTES;
let stopCollectionFn;
let stopCollectionTimeout;
const stopCollectionDebounce = 5 * MINUTES;
let lines: string[] = [];
@ -167,13 +166,12 @@ export const MutesCmd = mutesCmd({
message += "\n\n" + pageLines.join("\n");
listMessage.edit(message);
bumpClearReactionsTimeout();
bumpCollectionTimeout();
};
const bumpClearReactionsTimeout = () => {
if (!hasReactions) return;
clearTimeout(clearReactionsTimeout);
clearReactionsTimeout = setTimeout(clearReactionsFn, clearReactionsDebounce);
const bumpCollectionTimeout = () => {
clearTimeout(stopCollectionTimeout);
stopCollectionTimeout = setTimeout(stopCollectionFn, stopCollectionDebounce);
};
if (totalMutes === 0) {
@ -194,35 +192,55 @@ export const MutesCmd = mutesCmd({
drawListPage(1);
if (totalPages > 1) {
hasReactions = true;
listMessage.react("⬅");
listMessage.react("➡");
const idMod = `${listMessage.id}:muteList`;
const buttons: MessageButton[] = [];
const paginationReactionListener = pluginData.events.on(
"messageReactionAdd",
async ({ args: { reaction, user } }) => {
const rMsg = reaction.message;
const member = await pluginData.guild.members.fetch(user.id);
if (!isFullMessage(rMsg)) return;
if (rMsg.id !== listMessage.id) return;
if (member.id !== msg.author.id) return;
if (!["⬅", "➡"].includes(reaction.emoji.name!)) return;
if (reaction.emoji.name === "⬅" && currentPage > 1) {
drawListPage(currentPage - 1);
} else if (reaction.emoji.name === "➡" && currentPage < totalPages) {
drawListPage(currentPage + 1);
}
reaction.remove().catch(noop);
},
buttons.push(
new MessageButton()
.setStyle("SECONDARY")
.setEmoji("⬅")
.setType("BUTTON")
.setCustomID(`previousButton:${idMod}`),
);
clearReactionsFn = () => {
listMessage.reactions.removeAll().catch(noop);
pluginData.events.off("messageReactionAdd", paginationReactionListener);
buttons.push(
new MessageButton()
.setStyle("SECONDARY")
.setEmoji("➡")
.setType("BUTTON")
.setCustomID(`nextButton:${idMod}`),
);
const row = new MessageActionRow().addComponents(buttons);
await listMessage.edit({ components: [row] });
const filter = (iac: MessageComponentInteraction) => iac.message.id === listMessage.id;
const collector = listMessage.createMessageComponentInteractionCollector(filter, {
time: stopCollectionDebounce,
});
collector.on("collect", async (interaction: MessageComponentInteraction) => {
if (msg.author.id !== interaction.user.id) {
interaction.reply(`You are not permitted to use these buttons.`, { ephemeral: true });
} else {
collector.resetTimer();
if (interaction.customID === `previousButton:${idMod}` && currentPage > 1) {
await interaction.deferUpdate();
await drawListPage(currentPage - 1);
} else if (interaction.customID === `nextButton:${idMod}` && currentPage < totalPages) {
await interaction.deferUpdate();
await drawListPage(currentPage + 1);
} else {
await interaction.deferUpdate();
}
}
});
stopCollectionFn = async () => {
collector.stop();
await listMessage.edit({ content: listMessage.content, components: [] });
};
bumpClearReactionsTimeout();
bumpCollectionTimeout();
}
}
},

View file

@ -21,6 +21,8 @@ export const ButtonInteractionEvt = reactionRolesEvt({
? (meta.args.interaction as MessageComponentInteraction)
: null;
if (!int) return;
const allOnMessage = await meta.pluginData.state.buttonRoles.getAllForMessageId(int.message.id);
if (allOnMessage.length === 0) return;
const cfg = meta.pluginData.config.get();
const split = int.customID.split(BUTTON_CONTEXT_SEPARATOR);
const context = (await resolveStatefulCustomId(meta.pluginData, int.customID)) ?? {

View file

@ -1,11 +1,20 @@
import { GuildMember, Message, Permissions, TextChannel, User } from "discord.js";
import {
GuildMember,
Message,
MessageActionRow,
MessageButton,
MessageComponentInteraction,
Permissions,
TextChannel,
User,
} from "discord.js";
import escapeStringRegexp from "escape-string-regexp";
import { GuildPluginData } from "knub";
import { ArgsFromSignatureOrArray } from "knub/dist/commands/commandUtils";
import moment from "moment-timezone";
import { getBaseUrl, sendErrorMessage } from "../../pluginUtils";
import { allowTimeout, RegExpRunner } from "../../RegExpRunner";
import { isFullMessage, MINUTES, multiSorter, noop, sorter, trimLines } from "../../utils";
import { MINUTES, multiSorter, sorter, trimLines } from "../../utils";
import { asyncFilter } from "../../utils/async";
import { hasDiscordPermissions } from "../../utils/hasDiscordPermissions";
import { inputPatternToRegExp, InvalidRegexError } from "../../validatorUtils";
@ -75,9 +84,8 @@ export async function displaySearch(
let originalSearchMsg: Message;
let searching = false;
let currentPage = args.page || 1;
let hasReactions = false;
let clearReactionsFn: () => void;
let clearReactionsTimeout: Timeout;
let stopCollectionFn: () => void;
let stopCollectionTimeout: Timeout;
const perPage = args.ids ? SEARCH_ID_RESULTS_PER_PAGE : SEARCH_RESULTS_PER_PAGE;
@ -155,47 +163,78 @@ export async function displaySearch(
}
}
searchMsg.edit(result);
currentPage = searchResult.page;
// Set up pagination reactions if needed. The reactions are cleared after a timeout.
if (searchResult.totalResults > perPage) {
if (!hasReactions) {
hasReactions = true;
searchMsg.react("⬅");
searchMsg.react("➡");
searchMsg.react("🔄");
const idMod = `${searchMsg.id}:${moment.utc().valueOf()}`;
const buttons: MessageButton[] = [];
const listenerFn = pluginData.events.on("messageReactionAdd", async ({ args: { reaction, user } }) => {
const rMsg = reaction.message;
const member = await pluginData.guild.members.fetch(user.id);
if (rMsg.id !== searchMsg.id) return;
if (member.user.id !== msg.author.id) return;
if (!["⬅", "➡", "🔄"].includes(reaction.emoji.name!)) return;
buttons.push(
new MessageButton()
.setStyle("SECONDARY")
.setEmoji("⬅")
.setType("BUTTON")
.setCustomID(`previousButton:${idMod}`)
.setDisabled(currentPage === 1),
);
if (reaction.emoji.name === "⬅" && currentPage > 1) {
loadSearchPage(currentPage - 1);
} else if (reaction.emoji.name === "➡" && currentPage < searchResult.lastPage) {
loadSearchPage(currentPage + 1);
} else if (reaction.emoji.name === "🔄") {
loadSearchPage(currentPage);
buttons.push(
new MessageButton()
.setStyle("SECONDARY")
.setEmoji("➡")
.setType("BUTTON")
.setCustomID(`nextButton:${idMod}`)
.setDisabled(currentPage === searchResult.lastPage),
);
buttons.push(
new MessageButton()
.setStyle("SECONDARY")
.setEmoji("🔄")
.setType("BUTTON")
.setCustomID(`reloadButton:${idMod}`),
);
const row = new MessageActionRow().addComponents(buttons);
await searchMsg.edit({ content: result, components: [row] });
const filter = (iac: MessageComponentInteraction) => iac.message.id === searchMsg.id;
const collector = searchMsg.createMessageComponentInteractionCollector(filter, { time: 2 * MINUTES });
collector.on("collect", async (interaction: MessageComponentInteraction) => {
if (msg.author.id !== interaction.user.id) {
interaction.reply(`You are not permitted to use these buttons.`, { ephemeral: true });
} else {
if (interaction.customID === `previousButton:${idMod}` && currentPage > 1) {
collector.stop();
await interaction.deferUpdate();
await loadSearchPage(currentPage - 1);
} else if (interaction.customID === `nextButton:${idMod}` && currentPage < searchResult.lastPage) {
collector.stop();
await interaction.deferUpdate();
await loadSearchPage(currentPage + 1);
} else if (interaction.customID === `reloadButton:${idMod}`) {
collector.stop();
await interaction.deferUpdate();
await loadSearchPage(currentPage);
} else {
await interaction.deferUpdate();
}
}
});
if (isFullMessage(rMsg)) {
reaction.remove();
}
});
stopCollectionFn = async () => {
collector.stop();
await searchMsg.edit({ content: searchMsg.content, components: [] });
};
clearReactionsFn = async () => {
searchMsg.reactions.removeAll().catch(noop);
pluginData.events.off("messageReactionAdd", listenerFn);
};
}
clearTimeout(clearReactionsTimeout);
clearReactionsTimeout = setTimeout(clearReactionsFn, 5 * MINUTES);
clearTimeout(stopCollectionTimeout);
stopCollectionTimeout = setTimeout(stopCollectionFn, 2 * MINUTES);
} else {
searchMsg.edit(result);
}
currentPage = searchResult.page;
searching = false;
};
@ -370,7 +409,7 @@ async function performMemberSearch(
} else {
matchingMembers.sort(
multiSorter([
[m => m.username.toLowerCase(), realSortDir],
[m => m.user.username.toLowerCase(), realSortDir],
[m => m.discriminator, realSortDir],
]),
);

View file

@ -1,17 +1,7 @@
import { MessageActionRow, MessageButton, MessageComponentInteraction, MessageOptions, TextChannel } from "discord.js";
import { PluginError } from "knub";
import { noop } from "knub/dist/utils";
import moment from "moment";
export async function waitForComponent(
channel: TextChannel,
toPost: MessageOptions,
components: MessageActionRow[],
options?: WaitForOptions,
) {
throw new PluginError("Unimplemented method waitForComponent called.");
}
export async function waitForButtonConfirm(
channel: TextChannel,
toPost: MessageOptions,