nuno revised this gist 4 weeks ago. Go to revision
1 file changed, 100 insertions
charSelect.ts(file created)
| @@ -0,0 +1,100 @@ | |||
| 1 | + | import { | |
| 2 | + | ButtonBuilder, | |
| 3 | + | ButtonStyle, | |
| 4 | + | ActionRowBuilder, | |
| 5 | + | } from "discord.js"; | |
| 6 | + | import { getCharacters, getCharacterByName } from "@systems/characters"; | |
| 7 | + | import { getClassEmoji } from "@systems/emojis"; | |
| 8 | + | import { format } from "@format"; | |
| 9 | + | import { Character } from "@types"; | |
| 10 | + | import fs from "fs"; | |
| 11 | + | import path from "path"; | |
| 12 | + | ||
| 13 | + | const CHARS_PATH = path.join(__dirname, "../../data/characters.json"); | |
| 14 | + | ||
| 15 | + | export interface CharSelectOptions { | |
| 16 | + | customIdPrefix: string; // e.g. "switch_after_reclaim:flash" | |
| 17 | + | excludeCharName?: string; // exclude this char from the list | |
| 18 | + | appendToCustomId?: string; // appended after charName e.g. ":yes" | |
| 19 | + | pageSize?: number; // default 4 | |
| 20 | + | page?: number; // default 0 | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | /** | |
| 24 | + | * Builds paginated character selection button rows for a given user. | |
| 25 | + | * Includes own characters + shared characters. | |
| 26 | + | * Returns up to 2 rows: one for char buttons, one for pagination if needed. | |
| 27 | + | */ | |
| 28 | + | export function buildCharSelectButtons( | |
| 29 | + | userKey: string, | |
| 30 | + | options: CharSelectOptions | |
| 31 | + | ): ActionRowBuilder<ButtonBuilder>[] { | |
| 32 | + | const { | |
| 33 | + | customIdPrefix, | |
| 34 | + | excludeCharName, | |
| 35 | + | appendToCustomId = "", | |
| 36 | + | pageSize = 4, | |
| 37 | + | page = 0, | |
| 38 | + | } = options; | |
| 39 | + | ||
| 40 | + | // Gather own + shared chars | |
| 41 | + | const ownChars = getCharacters(userKey); | |
| 42 | + | ||
| 43 | + | const sharedChars: Character[] = []; | |
| 44 | + | try { | |
| 45 | + | const chars = JSON.parse(fs.readFileSync(CHARS_PATH, "utf8")); | |
| 46 | + | for (const [ownerKey, data] of Object.entries(chars) as [string, any][]) { | |
| 47 | + | if (ownerKey === userKey) continue; | |
| 48 | + | for (const c of data.characters ?? []) { | |
| 49 | + | if (c.sharedWith?.includes(userKey)) sharedChars.push(c); | |
| 50 | + | } | |
| 51 | + | } | |
| 52 | + | } catch {} | |
| 53 | + | ||
| 54 | + | const allChars = [...ownChars, ...sharedChars] | |
| 55 | + | .filter((c) => c.name !== excludeCharName); | |
| 56 | + | ||
| 57 | + | const pageChars = allChars.slice(page * pageSize, (page + 1) * pageSize); | |
| 58 | + | const hasNext = allChars.length > (page + 1) * pageSize; | |
| 59 | + | const hasPrev = page > 0; | |
| 60 | + | const rows: ActionRowBuilder<ButtonBuilder>[] = []; | |
| 61 | + | ||
| 62 | + | // Char buttons | |
| 63 | + | if (pageChars.length > 0) { | |
| 64 | + | const btns = pageChars.map((c) => { | |
| 65 | + | const emojiStr = getClassEmoji(c.class); | |
| 66 | + | const emoji = format.emoji(emojiStr); | |
| 67 | + | const btn = new ButtonBuilder() | |
| 68 | + | .setCustomId(`${customIdPrefix}:${c.name}${appendToCustomId}`) | |
| 69 | + | .setStyle(ButtonStyle.Secondary) | |
| 70 | + | .setLabel(`${c.level} ${c.name}`); | |
| 71 | + | if (emoji) btn.setEmoji(emoji as any); | |
| 72 | + | return btn; | |
| 73 | + | }); | |
| 74 | + | rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(...btns)); | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | // Pagination row | |
| 78 | + | const navBtns: ButtonBuilder[] = []; | |
| 79 | + | if (hasPrev) { | |
| 80 | + | navBtns.push( | |
| 81 | + | new ButtonBuilder() | |
| 82 | + | .setCustomId(`${customIdPrefix}_page:${page - 1}`) | |
| 83 | + | .setLabel("← Prev") | |
| 84 | + | .setStyle(ButtonStyle.Primary) | |
| 85 | + | ); | |
| 86 | + | } | |
| 87 | + | if (hasNext) { | |
| 88 | + | navBtns.push( | |
| 89 | + | new ButtonBuilder() | |
| 90 | + | .setCustomId(`${customIdPrefix}_page:${page + 1}`) | |
| 91 | + | .setLabel("Next →") | |
| 92 | + | .setStyle(ButtonStyle.Primary) | |
| 93 | + | ); | |
| 94 | + | } | |
| 95 | + | if (navBtns.length > 0) { | |
| 96 | + | rows.push(new ActionRowBuilder<ButtonBuilder>().addComponents(...navBtns)); | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | return rows; | |
| 100 | + | } | |
Newer
Older