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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26