|
1 | | -import {ActionRowBuilder, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, ContainerBuilder, MessageFlags, SectionBuilder, SeparatorBuilder, SeparatorSpacingSize, StringSelectMenuBuilder, StringSelectMenuInteraction, StringSelectMenuOptionBuilder, TextDisplayBuilder, ThumbnailBuilder} from "discord.js"; |
| 1 | +import { |
| 2 | + ButtonStyle, ChatInputCommandInteraction, ComponentType, MessageFlags, SeparatorSpacingSize, |
| 3 | + StringSelectMenuInteraction, |
| 4 | + type ActionRowData, type ComponentInContainerData, type ContainerComponentData, |
| 5 | + type MessageActionRowComponentData, type SectionComponentData, type TextDisplayComponentData |
| 6 | +} from "discord.js"; |
| 7 | +import {container, row, text} from "../framework"; |
2 | 8 | import type {BdWebAddon} from "../types"; |
3 | 9 |
|
4 | 10 | import Web from "../util/web"; |
@@ -72,86 +78,91 @@ export function sortAddons(addons: BdWebAddon[], sortBy: "likes" | "downloads" | |
72 | 78 | } |
73 | 79 |
|
74 | 80 |
|
75 | | -export function createAddonComponent(addon: BdWebAddon) { |
| 81 | +const separator = (spacing: SeparatorSpacingSize, divider: boolean): ComponentInContainerData => |
| 82 | + ({type: ComponentType.Separator, spacing, divider}); |
76 | 83 |
|
77 | | - const buttons = new ActionRowBuilder<ButtonBuilder>() |
78 | | - .addComponents( |
79 | | - new ButtonBuilder() |
80 | | - .setStyle(ButtonStyle.Link) |
81 | | - .setLabel("View Online") |
82 | | - .setURL(Web.pages[addon.type](addon.name)), |
83 | | - new ButtonBuilder() |
84 | | - .setStyle(ButtonStyle.Link) |
85 | | - .setLabel("Download Now") |
86 | | - .setURL(Web.redirects.download(addon.id.toString())), |
87 | | - ); |
| 84 | +const thumbnail = (url: string) => ({type: ComponentType.Thumbnail as const, media: {url}}); |
| 85 | + |
| 86 | +/** The link buttons every addon carries, plus a support server when there is one. */ |
| 87 | +function addonLinks(addon: BdWebAddon): MessageActionRowComponentData[] { |
| 88 | + const buttons: MessageActionRowComponentData[] = [ |
| 89 | + {type: ComponentType.Button, style: ButtonStyle.Link, label: "View Online", url: Web.pages[addon.type](addon.name)}, |
| 90 | + {type: ComponentType.Button, style: ButtonStyle.Link, label: "Download Now", url: Web.redirects.download(addon.id.toString())} |
| 91 | + ]; |
88 | 92 |
|
89 | 93 | if (addon.author.guild?.invite_link) { |
90 | | - buttons.addComponents( |
91 | | - new ButtonBuilder() |
92 | | - .setStyle(ButtonStyle.Link) |
93 | | - .setLabel("Support Server") |
94 | | - .setURL(addon.author.guild.invite_link), |
95 | | - ); |
| 94 | + buttons.push({type: ComponentType.Button, style: ButtonStyle.Link, label: "Support Server", url: addon.author.guild.invite_link}); |
96 | 95 | } |
97 | 96 |
|
98 | | - const page = new ContainerBuilder() |
99 | | - .addSectionComponents( |
100 | | - new SectionBuilder() |
101 | | - .setThumbnailAccessory( |
102 | | - new ThumbnailBuilder().setURL(Web.resources.thumbnail(addon.thumbnail_url)) |
103 | | - ) |
104 | | - .addTextDisplayComponents( |
105 | | - new TextDisplayBuilder().setContent(`# ${addon.name} v${addon.version}`), |
106 | | - new TextDisplayBuilder().setContent(addon.description ?? "No description provided."), |
107 | | - new TextDisplayBuilder().setContent(addon.tags.map(tag => `\`${tag}\``).join(" ")), |
108 | | - ), |
109 | | - ) |
110 | | - .addSeparatorComponents(new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Small).setDivider(false)) |
111 | | - .addTextDisplayComponents(new TextDisplayBuilder().setContent(`👍 ${addon.likes.toLocaleString()} Likes ⬇️ ${addon.downloads.toLocaleString()} Downloads`)) |
112 | | - .addSeparatorComponents(new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Large).setDivider(true)) |
113 | | - .addActionRowComponents(buttons) |
114 | | - .addTextDisplayComponents(new TextDisplayBuilder().setContent(`-# Updated ${new Date(addon.latest_release_date).toLocaleDateString()} • Released ${new Date(addon.initial_release_date).toLocaleDateString()}`)); |
115 | | - |
116 | | - return page; |
| 97 | + return buttons; |
117 | 98 | } |
118 | 99 |
|
119 | | -export function createAddonSection(addon: BdWebAddon) { |
120 | 100 |
|
| 101 | +/** One addon rendered in full, as its own page. */ |
| 102 | +export function createAddonComponent(addon: BdWebAddon): ContainerComponentData { |
| 103 | + const details: TextDisplayComponentData[] = [ |
| 104 | + {type: ComponentType.TextDisplay, content: `# ${addon.name} v${addon.version}`}, |
| 105 | + {type: ComponentType.TextDisplay, content: addon.description ?? "No description provided."}, |
| 106 | + {type: ComponentType.TextDisplay, content: addon.tags.map(tag => `\`${tag}\``).join(" ")} |
| 107 | + ]; |
| 108 | + |
| 109 | + return container([ |
| 110 | + { |
| 111 | + type: ComponentType.Section, |
| 112 | + components: details, |
| 113 | + accessory: thumbnail(Web.resources.thumbnail(addon.thumbnail_url)) |
| 114 | + }, |
| 115 | + separator(SeparatorSpacingSize.Small, false), |
| 116 | + text(`👍 ${addon.likes.toLocaleString()} Likes ⬇️ ${addon.downloads.toLocaleString()} Downloads`), |
| 117 | + separator(SeparatorSpacingSize.Large, true), |
| 118 | + row(...addonLinks(addon)), |
| 119 | + text(`-# Updated ${new Date(addon.latest_release_date).toLocaleDateString()} • Released ${new Date(addon.initial_release_date).toLocaleDateString()}`) |
| 120 | + ]); |
| 121 | +} |
| 122 | + |
| 123 | + |
| 124 | +/** One addon as a compact row within a list. */ |
| 125 | +export function createAddonSection(addon: BdWebAddon): SectionComponentData { |
121 | 126 | const links = [ |
122 | 127 | `[View Online](${Web.pages[addon.type](addon.name)})`, |
123 | 128 | `[Download Now](${Web.redirects.download(addon.id.toString())})`, |
124 | | - addon.author.guild?.invite_link && `[Support Server](${addon.author.guild?.invite_link})` |
| 129 | + addon.author.guild?.invite_link && `[Support Server](${addon.author.guild.invite_link})` |
125 | 130 | ].filter(Boolean).join(" • "); |
126 | 131 |
|
127 | | - const section = new SectionBuilder() |
128 | | - .setThumbnailAccessory(new ThumbnailBuilder().setURL(Web.resources.thumbnail(addon.thumbnail_url))) |
129 | | - .addTextDisplayComponents( |
130 | | - new TextDisplayBuilder().setContent(`### ${addon.name}`), |
131 | | - new TextDisplayBuilder().setContent(addon.description ?? "No description provided."), |
132 | | - new TextDisplayBuilder().setContent(links), |
133 | | - ); |
134 | | - |
135 | | - return section; |
| 132 | + return { |
| 133 | + type: ComponentType.Section, |
| 134 | + components: [ |
| 135 | + {type: ComponentType.TextDisplay, content: `### ${addon.name}`}, |
| 136 | + {type: ComponentType.TextDisplay, content: addon.description ?? "No description provided."}, |
| 137 | + {type: ComponentType.TextDisplay, content: links} |
| 138 | + ], |
| 139 | + accessory: thumbnail(Web.resources.thumbnail(addon.thumbnail_url)) |
| 140 | + }; |
136 | 141 | } |
137 | 142 |
|
138 | | -export function createAddonList(title: string, addons: BdWebAddon[]) { |
139 | | - const page = new ContainerBuilder(); |
| 143 | + |
| 144 | +export function createAddonList(title: string, addons: BdWebAddon[]): [TextDisplayComponentData, ContainerComponentData] { |
| 145 | + const body: ComponentInContainerData[] = []; |
140 | 146 | for (const [index, addon] of addons.entries()) { |
141 | | - page.addSectionComponents(createAddonSection(addon)); |
142 | | - if (index < addons.length - 1) page.addSeparatorComponents(new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Large).setDivider(true)); |
| 147 | + body.push(createAddonSection(addon)); |
| 148 | + if (index < addons.length - 1) body.push(separator(SeparatorSpacingSize.Large, true)); |
143 | 149 | } |
144 | | - return [new TextDisplayBuilder().setContent(`## ${title}`), page]; |
| 150 | + |
| 151 | + return [{type: ComponentType.TextDisplay, content: `## ${title}`}, container(body)]; |
145 | 152 | } |
146 | 153 |
|
147 | | -export function createNavigation(addons: BdWebAddon[], selectedIndex = 0, disabled = false) { |
148 | | - const navigation = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents( |
149 | | - new StringSelectMenuBuilder().setCustomId(`addons-navigation`).addOptions( |
150 | | - ...addons.map((addon, index) => new StringSelectMenuOptionBuilder().setLabel(`${index + 1}. ${addon.name}`).setValue(addon.name).setDefault(index === selectedIndex)) |
151 | | - ) |
152 | | - .setDisabled(disabled) |
153 | | - ); |
154 | | - return navigation; |
| 154 | + |
| 155 | +export function createNavigation(addons: BdWebAddon[], selectedIndex = 0, disabled = false): ActionRowData<MessageActionRowComponentData> { |
| 156 | + return row({ |
| 157 | + type: ComponentType.StringSelect, |
| 158 | + customId: "addons-navigation", |
| 159 | + disabled, |
| 160 | + options: addons.map((addon, index) => ({ |
| 161 | + "label": `${index + 1}. ${addon.name}`, |
| 162 | + "value": addon.name, |
| 163 | + "default": index === selectedIndex |
| 164 | + })) |
| 165 | + }); |
155 | 166 | } |
156 | 167 |
|
157 | 168 |
|
|
0 commit comments