Switch pagination to buttons
This commit is contained in:
parent
f9dd82f201
commit
6e22687bf6
4 changed files with 131 additions and 82 deletions
|
@ -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();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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)) ?? {
|
||||
|
|
|
@ -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],
|
||||
]),
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue