Add image grid to the series view command (#294)
- Add the image grid to the series view command - Moved the image generation logic to its own class so we can have common logic between them - Fixed a bug where paginated commands that deferred (series view, inventory) were creating new messages, rather than updating #279 Reviewed-on: #294 Reviewed-by: VylpesTester <tester@vylpes.com> Co-authored-by: Ethan Lane <ethan@vylpes.com> Co-committed-by: Ethan Lane <ethan@vylpes.com>
This commit is contained in:
parent
1b9857dfe5
commit
acfdcb17f2
6 changed files with 72 additions and 51 deletions
|
@ -31,7 +31,7 @@ export default class Inventory extends ButtonEvent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.followUp({
|
await interaction.editReply({
|
||||||
files: [ embed.image ],
|
files: [ embed.image ],
|
||||||
embeds: [ embed.embed ],
|
embeds: [ embed.embed ],
|
||||||
components: [ embed.row ],
|
components: [ embed.row ],
|
||||||
|
|
|
@ -24,11 +24,14 @@ export default class Series extends ButtonEvent {
|
||||||
const seriesid = interaction.customId.split(" ")[2];
|
const seriesid = interaction.customId.split(" ")[2];
|
||||||
const page = interaction.customId.split(" ")[3];
|
const page = interaction.customId.split(" ")[3];
|
||||||
|
|
||||||
const embed = SeriesHelper.GenerateSeriesViewPage(Number(seriesid), Number(page));
|
await interaction.deferUpdate();
|
||||||
|
|
||||||
await interaction.update({
|
const embed = await SeriesHelper.GenerateSeriesViewPage(Number(seriesid), Number(page));
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
embeds: [ embed!.embed ],
|
embeds: [ embed!.embed ],
|
||||||
components: [ embed!.row ],
|
components: [ embed!.row ],
|
||||||
|
files: [ embed!.image ],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,4 +45,4 @@ export default class Series extends ButtonEvent {
|
||||||
components: [ embed!.row ],
|
components: [ embed!.row ],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ export default class Series extends Command {
|
||||||
|
|
||||||
AppLogger.LogSilly("Commands/Series/View", `Parameters: id=${id?.value}`);
|
AppLogger.LogSilly("Commands/Series/View", `Parameters: id=${id?.value}`);
|
||||||
|
|
||||||
|
await interaction.deferReply();
|
||||||
|
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
|
|
||||||
const series = CoreClient.Cards.find(x => x.id == id.value);
|
const series = CoreClient.Cards.find(x => x.id == id.value);
|
||||||
|
@ -54,13 +56,17 @@ export default class Series extends Command {
|
||||||
if (!series) {
|
if (!series) {
|
||||||
AppLogger.LogVerbose("Commands/Series/View", "Series not found.");
|
AppLogger.LogVerbose("Commands/Series/View", "Series not found.");
|
||||||
|
|
||||||
await interaction.reply("Series not found.");
|
await interaction.followUp("Series not found.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const embed = SeriesHelper.GenerateSeriesViewPage(series.id, 0);
|
const embed = await SeriesHelper.GenerateSeriesViewPage(series.id, 0);
|
||||||
|
|
||||||
await interaction.reply({ embeds: [ embed!.embed ], components: [ embed!.row ]});
|
await interaction.followUp({
|
||||||
|
embeds: [ embed!.embed ],
|
||||||
|
components: [ embed!.row ],
|
||||||
|
files: [ embed!.image ],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ListSeries(interaction: CommandInteraction) {
|
private async ListSeries(interaction: CommandInteraction) {
|
||||||
|
@ -68,4 +74,4 @@ export default class Series extends Command {
|
||||||
|
|
||||||
await interaction.reply({ embeds: [ embed!.embed ], components: [ embed!.row ]});
|
await interaction.reply({ embeds: [ embed!.embed ], components: [ embed!.row ]});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
43
src/helpers/ImageHelper.ts
Normal file
43
src/helpers/ImageHelper.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import {createCanvas, loadImage} from "canvas";
|
||||||
|
import path from "path";
|
||||||
|
import AppLogger from "../client/appLogger";
|
||||||
|
import {existsSync} from "fs";
|
||||||
|
|
||||||
|
export default class ImageHelper {
|
||||||
|
public static async GenerateCardImageGrid(paths: string[]): Promise<Buffer> {
|
||||||
|
const gridWidth = 3;
|
||||||
|
const gridHeight = Math.ceil(paths.length / gridWidth);
|
||||||
|
|
||||||
|
const imageWidth = 526;
|
||||||
|
const imageHeight = 712;
|
||||||
|
|
||||||
|
const canvasWidth = imageWidth * gridWidth;
|
||||||
|
const canvasHeight = imageHeight * gridHeight;
|
||||||
|
|
||||||
|
const canvas = createCanvas(canvasWidth, canvasHeight);
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
for (let i = 0; i < paths.length; i++) {
|
||||||
|
const filePath = path.join(process.env.DATA_DIR!, "cards", paths[i]);
|
||||||
|
|
||||||
|
const exists = existsSync(filePath);
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
AppLogger.LogError("ImageHelper/GenerateCardImageGrid", `Failed to load image from path ${paths[i]}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = await loadImage(filePath);
|
||||||
|
|
||||||
|
const x = i % gridWidth;
|
||||||
|
const y = Math.floor(i / gridWidth);
|
||||||
|
|
||||||
|
const imageX = imageWidth * x;
|
||||||
|
const imageY = imageHeight * y;
|
||||||
|
|
||||||
|
ctx.drawImage(image, imageX, imageY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas.toBuffer();
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,8 +5,7 @@ import EmbedColours from "../constants/EmbedColours";
|
||||||
import { CardRarity, CardRarityToString } from "../constants/CardRarity";
|
import { CardRarity, CardRarityToString } from "../constants/CardRarity";
|
||||||
import cloneDeep from "clone-deep";
|
import cloneDeep from "clone-deep";
|
||||||
import AppLogger from "../client/appLogger";
|
import AppLogger from "../client/appLogger";
|
||||||
import { createCanvas, loadImage } from "canvas";
|
import ImageHelper from "./ImageHelper";
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
interface InventoryPage {
|
interface InventoryPage {
|
||||||
id: number,
|
id: number,
|
||||||
|
@ -113,44 +112,9 @@ export default class InventoryHelper {
|
||||||
.setStyle(ButtonStyle.Primary)
|
.setStyle(ButtonStyle.Primary)
|
||||||
.setDisabled(page + 1 == pages.length));
|
.setDisabled(page + 1 == pages.length));
|
||||||
|
|
||||||
const buffer = await this.GenerateInventoryImage(currentPage);
|
const buffer = await ImageHelper.GenerateCardImageGrid(currentPage.cards.map(x => x.path));
|
||||||
const image = new AttachmentBuilder(buffer, { name: "page.png" });
|
const image = new AttachmentBuilder(buffer, { name: "page.png" });
|
||||||
|
|
||||||
return { embed, row, image };
|
return { embed, row, image };
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async GenerateInventoryImage(page: InventoryPage): Promise<Buffer> {
|
|
||||||
const gridWidth = 3;
|
|
||||||
const gridHeight = Math.ceil(page.cards.length / gridWidth);
|
|
||||||
|
|
||||||
const imageWidth = 526;
|
|
||||||
const imageHeight = 712;
|
|
||||||
|
|
||||||
const canvasWidth = imageWidth * gridWidth;
|
|
||||||
const canvasHeight = imageHeight * gridHeight;
|
|
||||||
|
|
||||||
const canvas = createCanvas(canvasWidth, canvasHeight);
|
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
|
|
||||||
for (let i = 0; i < page.cards.length; i++) {
|
|
||||||
const card = page.cards[i];
|
|
||||||
|
|
||||||
const image = await loadImage(path.join(process.env.DATA_DIR!, "cards", card.path));
|
|
||||||
|
|
||||||
if (!image) {
|
|
||||||
AppLogger.LogError("InventoryHelper/GenerateInventoryImage", `Failed to load image for card ${card.id}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const x = i % gridWidth;
|
|
||||||
const y = Math.floor(i / gridWidth);
|
|
||||||
|
|
||||||
const imageX = imageWidth * x;
|
|
||||||
const imageY = imageHeight * y;
|
|
||||||
|
|
||||||
ctx.drawImage(image, imageX, imageY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return canvas.toBuffer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
|
import { ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
|
||||||
import AppLogger from "../client/appLogger";
|
import AppLogger from "../client/appLogger";
|
||||||
import cloneDeep from "clone-deep";
|
import cloneDeep from "clone-deep";
|
||||||
import { CoreClient } from "../client/client";
|
import { CoreClient } from "../client/client";
|
||||||
import EmbedColours from "../constants/EmbedColours";
|
import EmbedColours from "../constants/EmbedColours";
|
||||||
import { CardRarityToString } from "../constants/CardRarity";
|
import { CardRarityToString } from "../constants/CardRarity";
|
||||||
|
import ImageHelper from "./ImageHelper";
|
||||||
|
|
||||||
export default class SeriesHelper {
|
export default class SeriesHelper {
|
||||||
public static GenerateSeriesViewPage(seriesId: number, page: number): { embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> } | null {
|
public static async GenerateSeriesViewPage(seriesId: number, page: number): Promise<{ embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder>, image: AttachmentBuilder } | null> {
|
||||||
AppLogger.LogSilly("Helpers/SeriesHelper", `Parameters: seriesId=${seriesId}, page=${page}`);
|
AppLogger.LogSilly("Helpers/SeriesHelper", `Parameters: seriesId=${seriesId}, page=${page}`);
|
||||||
|
|
||||||
const itemsPerPage = 15;
|
const itemsPerPage = 9;
|
||||||
|
|
||||||
const series = cloneDeep(CoreClient.Cards)
|
const series = cloneDeep(CoreClient.Cards)
|
||||||
.find(x => x.id == seriesId);
|
.find(x => x.id == seriesId);
|
||||||
|
@ -37,7 +38,8 @@ export default class SeriesHelper {
|
||||||
.setTitle(series.name)
|
.setTitle(series.name)
|
||||||
.setColor(EmbedColours.Ok)
|
.setColor(EmbedColours.Ok)
|
||||||
.setDescription(description)
|
.setDescription(description)
|
||||||
.setFooter({ text: `${series.id} · ${totalCards} cards · Page ${page + 1} of ${totalPages}` });
|
.setFooter({ text: `${series.id} · ${totalCards} cards · Page ${page + 1} of ${totalPages}` })
|
||||||
|
.setImage("attachment://page.png");
|
||||||
|
|
||||||
const row = new ActionRowBuilder<ButtonBuilder>()
|
const row = new ActionRowBuilder<ButtonBuilder>()
|
||||||
.addComponents(
|
.addComponents(
|
||||||
|
@ -52,7 +54,10 @@ export default class SeriesHelper {
|
||||||
.setStyle(ButtonStyle.Primary)
|
.setStyle(ButtonStyle.Primary)
|
||||||
.setDisabled(page + 1 > totalPages));
|
.setDisabled(page + 1 > totalPages));
|
||||||
|
|
||||||
return { embed, row };
|
const buffer = await ImageHelper.GenerateCardImageGrid(cardsOnPage.map(x => x.path));
|
||||||
|
const image = new AttachmentBuilder(buffer, { name: "page.png" });
|
||||||
|
|
||||||
|
return { embed, row, image };
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GenerateSeriesListPage(page: number): { embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> } | null {
|
public static GenerateSeriesListPage(page: number): { embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> } | null {
|
||||||
|
|
Loading…
Reference in a new issue