/[sudobot]/trunk/src/utils/Pagination.ts
ViewVC logotype

Annotation of /trunk/src/utils/Pagination.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 484 - (hide annotations)
Mon Jul 29 17:30:27 2024 UTC (8 months, 1 week ago) by rakin
File MIME type: application/typescript
File size: 7228 byte(s)
feat: add pagination button emojis
1 rakin 446 import DiscordClient from "../client/Client";
2     import MessageEmbed from "../client/MessageEmbed";
3     import { v4 as uuid } from 'uuid';
4 rakin 447 import { ButtonInteraction, InteractionCollector, InteractionReplyOptions, Message, MessageActionRow, MessageButton, MessageEditOptions, MessageOptions, ReplyMessageOptions } from "discord.js";
5 rakin 484 import { emoji } from "./Emoji";
6 rakin 446
7     export interface EmbedBuilderOptions<T> {
8     data: Array<T>;
9     currentPage: number;
10     maxPages: number;
11     }
12    
13     export interface PaginationOptions<T> {
14     limit: number;
15     guild_id: string;
16     channel_id: string;
17     user_id?: string;
18 rakin 467 timeout?: number;
19 rakin 446 embedBuilder: (options: EmbedBuilderOptions<T>) => MessageEmbed;
20     actionRowBuilder?: (options: { first: boolean, last: boolean, next: boolean, back: boolean }) => MessageActionRow<MessageButton>;
21     }
22    
23     export default class Pagination<T> {
24     protected readonly client = DiscordClient.client;
25     protected readonly id: string;
26     protected currentPage: number = 1;
27    
28     constructor(protected readonly data: Array<T> = [], protected readonly options: PaginationOptions<T>) {
29     this.id = uuid();
30     }
31    
32     getOffset(page: number = 1) {
33     return (page - 1) * this.options.limit;
34     }
35    
36     getPaginatedData(page: number = 1) {
37     console.log(page, this.getOffset(page));
38     return this.data.slice(this.getOffset(page), this.getOffset(page) + this.options.limit);
39     }
40    
41     getEmbed(page: number = 1): MessageEmbed {
42     return this.options.embedBuilder({
43     data: this.getPaginatedData(page),
44     currentPage: this.currentPage,
45     maxPages: Math.ceil(this.data.length / this.options.limit),
46     });
47     }
48    
49     getMessageOptions(page: number = 1, actionRowOptions: { first: boolean, last: boolean, next: boolean, back: boolean } | undefined = undefined, optionsToMerge: ReplyMessageOptions & MessageOptions & InteractionReplyOptions & MessageEditOptions = {}) {
50     const options = {...optionsToMerge};
51     const actionRowOptionsDup = actionRowOptions ? {...actionRowOptions} : { first: true, last: true, next: true, back: true };
52    
53     if (actionRowOptionsDup && page <= 1) {
54     actionRowOptionsDup.back = false;
55     actionRowOptionsDup.first = false;
56     }
57    
58     if (actionRowOptionsDup && page >= Math.ceil(this.data.length / this.options.limit)) {
59     actionRowOptionsDup.last = false
60     actionRowOptionsDup.next = false;
61     }
62    
63     options.embeds ??= [];
64     options.embeds.push(this.getEmbed(page));
65 rakin 467
66 rakin 446 options.components ??= [];
67     options.components.push(this.getActionRow(actionRowOptionsDup));
68    
69     return options;
70     }
71    
72     getActionRow({ first, last, next, back }: { first: boolean, last: boolean, next: boolean, back: boolean } = { first: true, last: true, next: true, back: true }) {
73     if (this.options.actionRowBuilder) {
74     return this.options.actionRowBuilder({ first, last, next, back });
75     }
76    
77 rakin 467 const actionRow = new MessageActionRow<MessageButton>();
78 rakin 446
79     actionRow.addComponents(
80     new MessageButton()
81     .setCustomId(`pagination_first_${this.id}`)
82     .setStyle("PRIMARY")
83 rakin 484 .setDisabled(!first)
84     .setEmoji(emoji('ChevronLeft')!),
85 rakin 446 new MessageButton()
86     .setCustomId(`pagination_back_${this.id}`)
87     .setStyle("PRIMARY")
88 rakin 484 .setDisabled(!back)
89     .setEmoji(emoji('ArrowLeft')!),
90 rakin 446 new MessageButton()
91     .setCustomId(`pagination_next_${this.id}`)
92     .setStyle("PRIMARY")
93 rakin 484 .setDisabled(!next)
94     .setEmoji(emoji('ArrowRight')!),
95 rakin 446 new MessageButton()
96     .setCustomId(`pagination_last_${this.id}`)
97     .setStyle("PRIMARY")
98     .setDisabled(!last)
99 rakin 484 .setEmoji(emoji('ChevronRight')!)
100 rakin 446 );
101    
102     return actionRow;
103     }
104    
105     async start(message: Message) {
106     const collector = new InteractionCollector(this.client, {
107     guild: this.options.guild_id,
108     channel: this.options.channel_id,
109     interactionType: 'MESSAGE_COMPONENT',
110     componentType: 'BUTTON',
111     message,
112 rakin 467 time: this.options.timeout ?? 60_000,
113 rakin 446 filter: interaction => {
114     if (interaction.inGuild() && (!this.options.user_id || interaction.user.id === this.options.user_id)) {
115     return true;
116     }
117    
118     if (interaction.isRepliable()) {
119     interaction.reply({ content: 'That\'t not under your control or the button controls are expired', ephemeral: true });
120     }
121    
122     return false;
123     },
124     });
125    
126     collector.on("collect", async (interaction: ButtonInteraction) => {
127     if (!interaction.customId.endsWith(this.id)) {
128     return;
129     }
130    
131 rakin 467 // await interaction.deferUpdate();
132    
133 rakin 446 const maxPage = Math.ceil(this.data.length / this.options.limit);
134     const componentOptions = { first: true, last: true, next: true, back: true };
135    
136     if ([`pagination_next_${this.id}`, `pagination_back_${this.id}`].includes(interaction.customId)) {
137     console.log('here');
138    
139     if (this.currentPage >= maxPage && interaction.customId === `pagination_next_${this.id}`) {
140     console.log('here');
141     await interaction.reply({ content: maxPage === 1 ? "This is the only page!" : "You've reached the last page!", ephemeral: true });
142     return;
143     }
144    
145     if (this.currentPage <= 1 && interaction.customId === `pagination_back_${this.id}`) {
146     console.log('here');
147     await interaction.reply({ content: maxPage === 1 ? "This is the only page!" : "You're in the very first page!", ephemeral: true });
148     return;
149     }
150     }
151    
152     if (interaction.customId === `pagination_first_${this.id}`)
153     this.currentPage = 1;
154     else if (interaction.customId === `pagination_last_${this.id}`)
155     this.currentPage = maxPage;
156    
157     await interaction.update(this.getMessageOptions(
158     interaction.customId === `pagination_first_${this.id}` ? 1 :
159     interaction.customId === `pagination_last_${this.id}` ? maxPage :
160     (interaction.customId === `pagination_next_${this.id}` ? (this.currentPage >= maxPage ? this.currentPage : ++this.currentPage) : --this.currentPage),
161     componentOptions,
162     {
163     embeds: []
164     }
165     ));
166     });
167    
168     collector.on("end", async () => {
169 rakin 467 const component = message.components[0]; // this.getActionRow({ first: false, last: false, next: false, back: false })
170    
171     for (const i in component.components) {
172     component.components[i].disabled = true;
173     }
174    
175     await message.edit({ components: [component] });
176 rakin 446 });
177     }
178     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26