mirror of
https://gitlab.com/bignutty/labscore.git
synced 2025-06-08 06:03:04 -04:00
[nextgen/cardstack] big improvements
- Implement return types - page numbers - better subpage resolve delay - error handling and system error cards - code cleanup
This commit is contained in:
parent
7e850e5b5d
commit
18bd28591c
6 changed files with 249 additions and 125 deletions
|
@ -2,6 +2,7 @@ const { createEmbed, page } = require("#utils/embed");
|
||||||
const { acknowledge } = require("#utils/interactions");
|
const { acknowledge } = require("#utils/interactions");
|
||||||
|
|
||||||
const { DynamicCardStack } = require("../../../labscore/cardstack/DynamicCardStack");
|
const { DynamicCardStack } = require("../../../labscore/cardstack/DynamicCardStack");
|
||||||
|
const {CARD_STACK_CONSTANTS} = require("#cardstack");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
label: "text",
|
label: "text",
|
||||||
|
@ -25,6 +26,9 @@ module.exports = {
|
||||||
createEmbed("default", context, { description: "page 1"}),
|
createEmbed("default", context, { description: "page 1"}),
|
||||||
createEmbed("default", context, { description: "page 2. this has a conditional button."})
|
createEmbed("default", context, { description: "page 2. this has a conditional button."})
|
||||||
].map((p, index)=>page(p, {}, { key: `t_${index}` })),
|
].map((p, index)=>page(p, {}, { key: `t_${index}` })),
|
||||||
|
pageNumberGenerator: (pg)=>{
|
||||||
|
return `Test ${pg.index}`
|
||||||
|
},
|
||||||
interactive: {
|
interactive: {
|
||||||
always_active_button: {
|
always_active_button: {
|
||||||
label: "single sub page",
|
label: "single sub page",
|
||||||
|
@ -32,9 +36,12 @@ module.exports = {
|
||||||
visible: true,
|
visible: true,
|
||||||
disableCache: true,
|
disableCache: true,
|
||||||
resolvePage: ()=>{
|
resolvePage: ()=>{
|
||||||
return [
|
return {
|
||||||
createEmbed("success", context, "smiley")
|
type: CARD_STACK_CONSTANTS.RESOLVE_CALLBACK_TYPES.SUBSTACK,
|
||||||
].map((p)=>page(p))
|
cards: [
|
||||||
|
createEmbed("success", context, "smiley")
|
||||||
|
].map((p)=>page(p))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
conditional_button: {
|
conditional_button: {
|
||||||
|
@ -46,16 +53,20 @@ module.exports = {
|
||||||
return (page.getState("key") === "t_1")
|
return (page.getState("key") === "t_1")
|
||||||
},
|
},
|
||||||
resolvePage: (pg) => {
|
resolvePage: (pg) => {
|
||||||
return [
|
return {
|
||||||
createEmbed("default", context, { description: "this is a conditional sub page"}),
|
type: CARD_STACK_CONSTANTS.RESOLVE_CALLBACK_TYPES.SUBSTACK,
|
||||||
createEmbed("default", context, { description: "this is a conditional sub page two"})
|
cards: [
|
||||||
].map((p)=>page(p));
|
createEmbed("default", context, { description: "this is a conditional sub page"}),
|
||||||
|
createEmbed("default", context, { description: "this is a conditional sub page two"})
|
||||||
|
].map((p)=>page(p))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dynamic_button: {
|
dynamic_button: {
|
||||||
// Button Label
|
// Button Label
|
||||||
label: (page) => {
|
label: (page) => {
|
||||||
return page.getState("key");
|
console.log(page.getState("key"))
|
||||||
|
return page.getState("key") || "test";
|
||||||
},
|
},
|
||||||
// Next to pagination or new row
|
// Next to pagination or new row
|
||||||
inline: false,
|
inline: false,
|
||||||
|
@ -63,67 +74,19 @@ module.exports = {
|
||||||
// Renders the loading state card
|
// Renders the loading state card
|
||||||
renderLoadingState: (page) => {
|
renderLoadingState: (page) => {
|
||||||
return createEmbed("default", context, {
|
return createEmbed("default", context, {
|
||||||
description: "-# Subpage Loading :)",
|
description: "-# replacing papa card",
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
resolvePage: async (pg) => {
|
resolvePage: async (pg) => {
|
||||||
console.log("resolving page")
|
console.log("resolving page")
|
||||||
return [
|
return {
|
||||||
createEmbed("default", context, { description: "this is a dynamic sub page " + Math.random()}),
|
type: CARD_STACK_CONSTANTS.RESOLVE_CALLBACK_TYPES.REPLACE_PARENT_CARD,
|
||||||
createEmbed("default", context, { description: "this is a dynamic sub page " + Math.random()}),
|
card: page(createEmbed("default", context, { description: "this is the new over lord " + new Date()}), {}, {
|
||||||
createEmbed("default", context, { description: "this is a dynamic sub page " + Math.random()}),
|
key: Date.now()
|
||||||
createEmbed("default", context, { description: "this is a dynamic sub page " + Math.random()})
|
})
|
||||||
].map((p)=>page(p));
|
};
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
conditional_button_2: {
|
|
||||||
// Button Label
|
|
||||||
label: "Conditional",
|
|
||||||
// Next to pagination or new row
|
|
||||||
inline: false,
|
|
||||||
visible: true,
|
|
||||||
resolvePage: (pg) => {throw "a"}
|
|
||||||
},
|
|
||||||
conditional_button_3: {
|
|
||||||
// Button Label
|
|
||||||
label: "Conditional",
|
|
||||||
// Next to pagination or new row
|
|
||||||
inline: false,
|
|
||||||
visible: true,
|
|
||||||
resolvePage: (pg) => {throw "a"}
|
|
||||||
},
|
|
||||||
conditional_button_4: {
|
|
||||||
// Button Label
|
|
||||||
label: "Conditional",
|
|
||||||
// Next to pagination or new row
|
|
||||||
inline: false,
|
|
||||||
visible: true,
|
|
||||||
resolvePage: (pg) => {throw "a"}
|
|
||||||
},
|
|
||||||
conditional_button_5: {
|
|
||||||
// Button Label
|
|
||||||
label: "Conditional",
|
|
||||||
// Next to pagination or new row
|
|
||||||
inline: false,
|
|
||||||
visible: true,
|
|
||||||
resolvePage: (pg) => {throw "a"}
|
|
||||||
},
|
|
||||||
conditional_button_6: {
|
|
||||||
// Button Label
|
|
||||||
label: "Conditional",
|
|
||||||
// Next to pagination or new row
|
|
||||||
inline: false,
|
|
||||||
visible: true,
|
|
||||||
resolvePage: (pg) => {throw "a"}
|
|
||||||
},
|
|
||||||
conditional_button_7: {
|
|
||||||
// Button Label
|
|
||||||
label: "Conditional",
|
|
||||||
// Next to pagination or new row
|
|
||||||
inline: false,
|
|
||||||
visible: true,
|
|
||||||
resolvePage: (pg) => {throw "a"}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}catch(e){
|
}catch(e){
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const { anime, animeSupplemental} = require('#api');
|
const { anime, animeSupplemental} = require('#api');
|
||||||
const { PERMISSION_GROUPS, OMNI_ANIME_FORMAT_TYPES, COLORS_HEX} = require('#constants');
|
const { PERMISSION_GROUPS, OMNI_ANIME_FORMAT_TYPES, COLORS_HEX} = require('#constants');
|
||||||
|
|
||||||
const { createDynamicCardStack } = require("#cardstack");
|
const { createDynamicCardStack, CARD_STACK_CONSTANTS } = require("#cardstack");
|
||||||
|
|
||||||
const { hexToDecimalColor } = require("#utils/color");
|
const { hexToDecimalColor } = require("#utils/color");
|
||||||
const { createEmbed, formatPaginationEmbeds, page } = require('#utils/embed');
|
const { createEmbed, page } = require('#utils/embed');
|
||||||
const { acknowledge } = require('#utils/interactions');
|
const { acknowledge } = require('#utils/interactions');
|
||||||
const { smallPill, link, pill, stringwrapPreserveWords, timestamp, TIMESTAMP_FLAGS} = require('#utils/markdown');
|
const { smallPill, link, pill, stringwrapPreserveWords, timestamp, TIMESTAMP_FLAGS} = require('#utils/markdown');
|
||||||
const { editOrReply } = require('#utils/message');
|
const { editOrReply } = require('#utils/message');
|
||||||
|
@ -106,7 +106,7 @@ module.exports = {
|
||||||
if(!pages.length) return editOrReply(context, createEmbed("warning", context, `No results found.`))
|
if(!pages.length) return editOrReply(context, createEmbed("warning", context, `No results found.`))
|
||||||
|
|
||||||
createDynamicCardStack(context, {
|
createDynamicCardStack(context, {
|
||||||
cards: formatPaginationEmbeds(pages),
|
cards: pages,
|
||||||
interactive: {
|
interactive: {
|
||||||
episodes_button: {
|
episodes_button: {
|
||||||
label: "Episodes",
|
label: "Episodes",
|
||||||
|
@ -152,7 +152,15 @@ module.exports = {
|
||||||
return page(card)
|
return page(card)
|
||||||
})
|
})
|
||||||
|
|
||||||
return formatPaginationEmbeds(cards);
|
return {
|
||||||
|
type: CARD_STACK_CONSTANTS.RESOLVE_CALLBACK_TYPES.SUBSTACK,
|
||||||
|
cards: cards.length >= 1 ? cards : [
|
||||||
|
// This happens if the episode metadata resolver fails.
|
||||||
|
page(createEmbed("defaultNoFooter", context, {
|
||||||
|
description: `-# ${pg.getState("name")} › **Episodes**\n## Episodes Unavailable\n\nWe're unable to display episode details for this content.`
|
||||||
|
}))
|
||||||
|
],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
characters_button: {
|
characters_button: {
|
||||||
|
@ -190,7 +198,10 @@ module.exports = {
|
||||||
return page(card)
|
return page(card)
|
||||||
})
|
})
|
||||||
|
|
||||||
return formatPaginationEmbeds(cards);
|
return {
|
||||||
|
type: CARD_STACK_CONSTANTS.RESOLVE_CALLBACK_TYPES.SUBSTACK,
|
||||||
|
cards: cards
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
related_button: {
|
related_button: {
|
||||||
|
@ -214,9 +225,12 @@ module.exports = {
|
||||||
|
|
||||||
let cards = episodes.response.body.relations.map((e) => renderAnimeResultsPage(context, e, false))
|
let cards = episodes.response.body.relations.map((e) => renderAnimeResultsPage(context, e, false))
|
||||||
|
|
||||||
return formatPaginationEmbeds(cards);
|
return {
|
||||||
|
type: CARD_STACK_CONSTANTS.RESOLVE_CALLBACK_TYPES.SUBSTACK,
|
||||||
|
cards: cards
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}catch(e){
|
}catch(e){
|
||||||
|
|
33
labscore/cardstack/Constants.js
Normal file
33
labscore/cardstack/Constants.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
module.exports.BUILT_IN_BUTTON_TYPES = Object.freeze({
|
||||||
|
NEXT_PAGE: "next",
|
||||||
|
PREVIOUS_PAGE: "previous"
|
||||||
|
})
|
||||||
|
|
||||||
|
module.exports.DEFAULT_BUTTON_ICON_MAPPINGS = Object.freeze({
|
||||||
|
[this.BUILT_IN_BUTTON_TYPES.NEXT_PAGE]: "button_chevron_right",
|
||||||
|
[this.BUILT_IN_BUTTON_TYPES.PREVIOUS_PAGE]: "button_chevron_left"
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.STACK_CACHE_KEYS = Object.freeze({
|
||||||
|
RESULT_CARDS: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback Types for a Dynamic Card Stack
|
||||||
|
* Component resolve.
|
||||||
|
*
|
||||||
|
* - `SUBSTACK` - Creates a "submenu" with a brand new cardstack
|
||||||
|
* - `REPLACE_PARENT` - Replaces the parent card in the root stack
|
||||||
|
* - This callback type will also unselect the button
|
||||||
|
* - `REPLACE_ROOT_STACK` - Replaces the root stack
|
||||||
|
* - This callback type will also unselect the button
|
||||||
|
*/
|
||||||
|
module.exports.RESOLVE_CALLBACK_TYPES = Object.freeze({
|
||||||
|
SUBSTACK: 0,
|
||||||
|
REPLACE_PARENT_CARD: 1,
|
||||||
|
REPLACE_STACK: 2,
|
||||||
|
0: "SUBSTACK",
|
||||||
|
1: "REPLACE_PARENT_CARD",
|
||||||
|
2: "REPLACE_STACK",
|
||||||
|
})
|
|
@ -1,5 +1,5 @@
|
||||||
const { createEmbed, page } = require("#utils/embed");
|
const { createEmbed, page } = require("#utils/embed");
|
||||||
const { iconAsEmojiObject, icon, link} = require("#utils/markdown");
|
const { iconAsEmojiObject, icon, link, codeblock } = require("#utils/markdown");
|
||||||
const { editOrReply } = require("#utils/message");
|
const { editOrReply } = require("#utils/message");
|
||||||
const { STATIC_ASSETS } = require("#utils/statics");
|
const { STATIC_ASSETS } = require("#utils/statics");
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ const { MessageComponentTypes, InteractionCallbackTypes } = require("detritus-cl
|
||||||
const { Message } = require("detritus-client/lib/structures");
|
const { Message } = require("detritus-client/lib/structures");
|
||||||
const { ComponentContext, Components, ComponentActionRow} = require("detritus-client/lib/utils");
|
const { ComponentContext, Components, ComponentActionRow} = require("detritus-client/lib/utils");
|
||||||
const {DISCORD_INVITES} = require("#constants");
|
const {DISCORD_INVITES} = require("#constants");
|
||||||
|
const {DEFAULT_BUTTON_ICON_MAPPINGS, STACK_CACHE_KEYS, BUILT_IN_BUTTON_TYPES, RESOLVE_CALLBACK_TYPES} = require("./Constants");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores all active card stacks
|
* Stores all active card stacks
|
||||||
|
@ -15,20 +16,6 @@ const {DISCORD_INVITES} = require("#constants");
|
||||||
*/
|
*/
|
||||||
const activeStacks = new WeakMap();
|
const activeStacks = new WeakMap();
|
||||||
|
|
||||||
const BUILT_IN_BUTTON_TYPES = Object.freeze({
|
|
||||||
NEXT_PAGE: "next",
|
|
||||||
PREVIOUS_PAGE: "previous"
|
|
||||||
})
|
|
||||||
|
|
||||||
const DEFAULT_BUTTON_ICON_MAPPINGS = Object.freeze({
|
|
||||||
[BUILT_IN_BUTTON_TYPES.NEXT_PAGE]: "button_chevron_right",
|
|
||||||
[BUILT_IN_BUTTON_TYPES.PREVIOUS_PAGE]: "button_chevron_left"
|
|
||||||
});
|
|
||||||
|
|
||||||
const STACK_CACHE_KEYS = Object.freeze({
|
|
||||||
RESULT_CARDS: 0
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DynamicCardStack represents an interactive stacks
|
* DynamicCardStack represents an interactive stacks
|
||||||
* of cards (embeds) for the user to paginate through
|
* of cards (embeds) for the user to paginate through
|
||||||
|
@ -39,13 +26,15 @@ class DynamicCardStack {
|
||||||
* Creates a new DynamicCardStack
|
* Creates a new DynamicCardStack
|
||||||
* @param {Context} context Context
|
* @param {Context} context Context
|
||||||
* @param {Object} options DynamicCardStack Arguments
|
* @param {Object} options DynamicCardStack Arguments
|
||||||
* @param {Array<string>} options.buttons CardStack built-in navigation buttons
|
* @param {Array<string>} options.buttons Card Stack built-in navigation buttons
|
||||||
* @param {Array} options.cards Baseline CardStack
|
* @param {Array} options.cards Root Card Stack
|
||||||
* @param {Object} options.interactive Interactive Components
|
* @param {Object} options.interactive Interactive Components
|
||||||
* @param {Number} options.startingIndex Starting card index
|
* @param {Number} options.startingIndex Starting card index
|
||||||
* @param {boolean} options.loop Wrap paging
|
* @param {boolean} options.loop Wrap paging
|
||||||
* @param {number} options.expires CardStack timeout
|
* @param {number} options.expires Timeout for the Card Stack listener.
|
||||||
* @param {boolean} options.disableStackCache Allows disabling the stack result cache, meaning that every trigger will reevaluate a stack
|
* @param {boolean} options.disableStackCache Allows disabling the stack result cache, meaning that every trigger will reevaluate a stack
|
||||||
|
* @param {boolean} options.pageNumbers Renders Page Numbers in the footer of all embeds in cards.
|
||||||
|
* @param {Function} options.pageNumberGenerator Function that renders a page number. Default style is `Page <index>/<total>`
|
||||||
*/
|
*/
|
||||||
constructor(context, options){
|
constructor(context, options){
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
@ -54,9 +43,12 @@ class DynamicCardStack {
|
||||||
this.buttons = options.buttons || ["previous","next"]
|
this.buttons = options.buttons || ["previous","next"]
|
||||||
this.interactive_components = options.interactive || {};
|
this.interactive_components = options.interactive || {};
|
||||||
this.index = options.startingIndex || 0;
|
this.index = options.startingIndex || 0;
|
||||||
this.rootIndex = this.index;
|
|
||||||
this.loopPages = options.loop || true;
|
this.loopPages = options.loop || true;
|
||||||
this.expires = options.expires || 5*60*1000;
|
this.expires = options.expires || 5*60*1000;
|
||||||
|
this.pageNumbers = options.pageNumbers || true;
|
||||||
|
this.pageNumberGenerator = options.pageNumberGenerator || ((pg)=>`Page ${pg.index + 1}/${pg.activeCardStack.length}`);
|
||||||
|
|
||||||
|
this.rootIndex = this.index;
|
||||||
|
|
||||||
this.stackCache = {};
|
this.stackCache = {};
|
||||||
this.pageState = [];
|
this.pageState = [];
|
||||||
|
@ -138,15 +130,7 @@ class DynamicCardStack {
|
||||||
|
|
||||||
this.activeCardStack = [...this.cards];
|
this.activeCardStack = [...this.cards];
|
||||||
|
|
||||||
// Resolve page state for all
|
this.updatePageState()
|
||||||
// root pages.
|
|
||||||
let i = 0;
|
|
||||||
for(const ac of this.cards){
|
|
||||||
if(ac["_meta"]){
|
|
||||||
this.pageState[i] = Object.assign({}, ac["_meta"]);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create internal component listener
|
// Create internal component listener
|
||||||
this.listener = new Components({
|
this.listener = new Components({
|
||||||
|
@ -170,6 +154,20 @@ class DynamicCardStack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves page state for all root stack cards.
|
||||||
|
*/
|
||||||
|
updatePageState(){
|
||||||
|
let i = 0;
|
||||||
|
this.pageState = [];
|
||||||
|
for(const ac of this.cards){
|
||||||
|
if(ac["_meta"]){
|
||||||
|
this.pageState[i] = Object.assign({}, ac["_meta"]);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a card from the currently active
|
* Gets a card from the currently active
|
||||||
* stack by its index
|
* stack by its index
|
||||||
|
@ -177,11 +175,46 @@ class DynamicCardStack {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getCardByIndex(index){
|
getCardByIndex(index){
|
||||||
// TODO: remove this some time after launch
|
try{
|
||||||
let card = Object.assign({}, this.activeCardStack[index])
|
// TODO: remove this some time after launch
|
||||||
if(!card.content) card.content = "";
|
let card = structuredClone(this.activeCardStack[index]);
|
||||||
card.content += `\n-# ${icon("flask_mini")} You are using the new page system • Leave feedback or report bugs in our ${link(DISCORD_INVITES.feedback_cardstack, "Discord Server", "labsCore Support", false)}!`
|
|
||||||
return card;
|
// This creates an error card with debug information
|
||||||
|
// in case that our activeCardStack gets corrupted
|
||||||
|
// or lost somehow (bad implementation)
|
||||||
|
if(!this.activeCardStack[index]) card = page(createEmbed("errordetail", this.context, {
|
||||||
|
error: "Unable to resolve card.",
|
||||||
|
content: `Index: \`${this.index}\`, Stack Size: \`${this.index}\`\n` +
|
||||||
|
(Object.keys(this.getAllStateForPage(this.index)).length >= 1 ?
|
||||||
|
codeblock("json", [JSON.stringify(this.getAllStateForPage(this.index), null, 2)]).substr(0, 5000) : "")
|
||||||
|
}))
|
||||||
|
|
||||||
|
if(!card.content) card.content = "";
|
||||||
|
card.content += `\n-# ${icon("flask_mini")} You are using the new page system • Leave feedback or report bugs in our ${link(DISCORD_INVITES.feedback_cardstack, "Discord Server", "labsCore Support", false)}!`
|
||||||
|
|
||||||
|
// Render Page Numbers.
|
||||||
|
// Conditions:
|
||||||
|
// - We have more than one card in the active stack
|
||||||
|
// - We have embeds in the stack
|
||||||
|
if(this.pageNumbers && card.embeds?.length && this.activeCardStack.length >= 2){
|
||||||
|
card.embeds = card.embeds.map((e)=>{
|
||||||
|
if(!e.footer) e.footer = { text: this.pageNumberGenerator(this) }
|
||||||
|
else {
|
||||||
|
if(e.footer.text) e.footer.text += ` • ${this.pageNumberGenerator(this)}`;
|
||||||
|
else e.footer.text = this.pageNumberGenerator(this);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}catch(e){
|
||||||
|
console.log(e)
|
||||||
|
return page(createEmbed("errordetail", this.context, {
|
||||||
|
error: "Unable to render card:",
|
||||||
|
content: codeblock("js",[(e ? e.stack || e.message : e).replaceAll(process.cwd(), '')])
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -270,6 +303,16 @@ class DynamicCardStack {
|
||||||
return this.pageState;
|
return this.pageState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all state for a specific page.
|
||||||
|
* Only really intended for debugging purposes.
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
getAllStateForPage(index){
|
||||||
|
return this.pageState[index] || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders components and button states
|
* Renders components and button states
|
||||||
*/
|
*/
|
||||||
|
@ -433,8 +476,7 @@ class DynamicCardStack {
|
||||||
}
|
}
|
||||||
else this.currentSelectedSubcategory = ctx.data.customId;
|
else this.currentSelectedSubcategory = ctx.data.customId;
|
||||||
|
|
||||||
// Reset page index so the new stack starts on page 0
|
let resolveTime = Date.now();
|
||||||
this.index = 0;
|
|
||||||
|
|
||||||
try{
|
try{
|
||||||
// If we have a cached result, retrieve it
|
// If we have a cached result, retrieve it
|
||||||
|
@ -472,28 +514,82 @@ class DynamicCardStack {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the active cardstack.
|
// Compute the active cardstack.
|
||||||
this.activeCardStack = await this.interactive_components[ctx.data.customId].resolvePage(this);
|
let resolvedNewStack = await this.interactive_components[ctx.data.customId].resolvePage(this);
|
||||||
|
|
||||||
// TODO: this needs several modes/callback types.
|
if(!Object.values(RESOLVE_CALLBACK_TYPES).includes(resolvedNewStack.type))
|
||||||
// SUBSTACK - Creates a "submenu" with a brand new cardstack
|
throw new Error(`Invalid Stack Resolve Type (${resolvedNewStack.type})`);
|
||||||
// REPLACE_PARENT - Replaces the parent card in the root stack
|
|
||||||
// REPLACE_ROOT_STACK - Replaces the root stack
|
|
||||||
|
|
||||||
// Cache the computed cardstack for future accessing.
|
switch(resolvedNewStack.type){
|
||||||
// The cache can be disabled/bypassed if we either
|
/**
|
||||||
// a) have really big/complex results
|
* SUBSTACK
|
||||||
// b) want to ensure data is always fresh
|
*
|
||||||
if(!this.interactive_components[ctx.data.customId].disableCache){
|
* Replace the currently active paging
|
||||||
this._setCachedValue(this.rootIndex, ctx.data.customId, STACK_CACHE_KEYS.RESULT_CARDS, [...this.activeCardStack]);
|
* with a new, separate card stack to
|
||||||
|
* page through.
|
||||||
|
*/
|
||||||
|
case RESOLVE_CALLBACK_TYPES.SUBSTACK:
|
||||||
|
this.activeCardStack = resolvedNewStack.cards;
|
||||||
|
this.index = resolvedNewStack.index || 0;
|
||||||
|
|
||||||
|
// Cache the computed cardstack for future accessing.
|
||||||
|
// The cache can be disabled/bypassed if we either
|
||||||
|
// a) have really big/complex results
|
||||||
|
// b) want to ensure data is always fresh
|
||||||
|
|
||||||
|
// We currently only cache SUBSTACK responses, as the other
|
||||||
|
// types probably need revalidating/refetching since the parent
|
||||||
|
// has changed and might carry new data.
|
||||||
|
if(!this.interactive_components[ctx.data.customId].disableCache){
|
||||||
|
this._setCachedValue(this.rootIndex, ctx.data.customId, STACK_CACHE_KEYS.RESULT_CARDS, [...this.activeCardStack]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/**
|
||||||
|
* REPLACE_PARENT_CARD
|
||||||
|
*
|
||||||
|
* Replaces the parent card (the one this action
|
||||||
|
* was initiated from) with a new one.
|
||||||
|
*
|
||||||
|
* Re-resolves all page state.
|
||||||
|
* Unselects the button.
|
||||||
|
*/
|
||||||
|
case RESOLVE_CALLBACK_TYPES.REPLACE_PARENT_CARD:
|
||||||
|
this.cards[this.rootIndex] = resolvedNewStack.card;
|
||||||
|
this.activeCardStack = [...this.cards];
|
||||||
|
this.updatePageState();
|
||||||
|
this.index = resolvedNewStack.index || this.rootIndex;
|
||||||
|
this.currentSelectedSubcategory = null;
|
||||||
|
break;
|
||||||
|
/**
|
||||||
|
* REPLACE_STACK
|
||||||
|
*
|
||||||
|
* Replaces the entire parent
|
||||||
|
* card stack with a new set.
|
||||||
|
*
|
||||||
|
* Re-resolves all page state.
|
||||||
|
* Unselects the button.
|
||||||
|
*/
|
||||||
|
case RESOLVE_CALLBACK_TYPES.REPLACE_STACK:
|
||||||
|
this.activeCardStack = resolvedNewStack.cards;
|
||||||
|
this.updatePageState();
|
||||||
|
this.index = resolvedNewStack.index || this.rootIndex;
|
||||||
|
this.currentSelectedSubcategory = null;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch(e){
|
} catch(e){
|
||||||
this.activeCardStack = [
|
// Display an error if we're NOT
|
||||||
page(createEmbed("error", ctx, "Stack rendering failed."))
|
// in the root stack (that would break
|
||||||
]
|
// things badly).
|
||||||
|
if(this.currentSelectedSubcategory != null)
|
||||||
|
this.activeCardStack = [
|
||||||
|
page(createEmbed("errordetail", ctx, {
|
||||||
|
error: "Card stack rendering failed.",
|
||||||
|
content: codeblock("js",[(e ? e.stack || e.message : e).replaceAll(process.cwd(), '')])
|
||||||
|
}))
|
||||||
|
]
|
||||||
console.log("resolve failed:")
|
console.log("resolve failed:")
|
||||||
console.log(e)
|
console.log(e)
|
||||||
// TODO: better errors maybe?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the card stack with a card from the new stack.
|
// Update the card stack with a card from the new stack.
|
||||||
|
@ -503,9 +599,20 @@ class DynamicCardStack {
|
||||||
data: Object.assign(this.getCurrentCard(), { components: this._renderComponents()})
|
data: Object.assign(this.getCurrentCard(), { components: this._renderComponents()})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
setTimeout(()=>{
|
// This timeout exists 1. for cosmetic reasons so people can
|
||||||
return this._edit(Object.assign(this.getCurrentCard(), {components:this._renderComponents()}))
|
// see the skeleton state and 2. in order to avoid a really
|
||||||
}, 1500)
|
// annoying race condition with the media proxy reverting our
|
||||||
|
// embed to a prior state.
|
||||||
|
|
||||||
|
// If we've already waited at least 2 seconds during processing
|
||||||
|
// it *should* be safe to just edit the message now.
|
||||||
|
if((Date.now() - resolveTime) < 2000){
|
||||||
|
setTimeout(()=>{
|
||||||
|
return this._edit(Object.assign(this.getCurrentCard(), {components:this._renderComponents()}))
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
await this._edit(Object.assign(this.getCurrentCard(), {components:this._renderComponents()}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -3,5 +3,6 @@ const { DynamicCardStack } = require("./DynamicCardStack");
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createDynamicCardStack: (context, options)=>{
|
createDynamicCardStack: (context, options)=>{
|
||||||
return new DynamicCardStack(context, options)
|
return new DynamicCardStack(context, options)
|
||||||
}
|
},
|
||||||
|
CARD_STACK_CONSTANTS: require("./constants"),
|
||||||
}
|
}
|
|
@ -148,6 +148,12 @@ module.exports.createEmbed = function(type, context, content){
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds formatted page numbers to the embed footer
|
// Adds formatted page numbers to the embed footer
|
||||||
|
/**
|
||||||
|
* Formats embeds for pagination.
|
||||||
|
* @deprecated No longer necessary in DynamicCardStack.
|
||||||
|
* @param embeds Array of Messages
|
||||||
|
* @returns {Embed[]}
|
||||||
|
*/
|
||||||
module.exports.formatPaginationEmbeds = function(embeds){
|
module.exports.formatPaginationEmbeds = function(embeds){
|
||||||
// No formatting if we only have one page
|
// No formatting if we only have one page
|
||||||
if(embeds.length == 1) return embeds;
|
if(embeds.length == 1) return embeds;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue