diff --git a/commands/message/search/anime.js b/commands/message/search/anime.js index f8e8b4a..c87d8f0 100644 --- a/commands/message/search/anime.js +++ b/commands/message/search/anime.js @@ -7,6 +7,8 @@ const { acknowledge } = require('#utils/interactions'); const { smallPill, link, pill, stringwrap, stringwrapPreserveWords} = require('#utils/markdown'); const { editOrReply } = require('#utils/message'); +const DynamicCardStack = require("../../../labscore/cardstack/DynamicCardStack"); + function renderAnimeResultsPage(context, res){ let result = createEmbed("default", context, { author: { @@ -57,7 +59,8 @@ function renderAnimeResultsPage(context, res){ } return page(result, {}, { - episodes_key: res.supplemental.episodes + episodes_key: res.supplemental.episodes, + name: res.title }); } @@ -94,9 +97,8 @@ module.exports = { if(!pages.length) return editOrReply(context, createEmbed("warning", context, `No results found.`)) - await paginator.createPaginator({ - context, - pages: formatPaginationEmbeds(pages), + new DynamicCardStack(context, { + cards: formatPaginationEmbeds(pages), interactive: { episodes_button: { // Button Label @@ -107,16 +109,36 @@ module.exports = { condition: (page)=>{ return (page.getState("episodes_key") !== null) }, + renderLoadingState: (pg) => { + return createEmbed("default", context, { + description: `-# ${pg.getState("name")} > **Episodes**`, + image: { + url: `https://bignutty.gitlab.io/webstorage4/v2/assets/loading/04_chat_loading.1zn1ocfb72tc.gif` + } + }) + }, // Will resolve all conditions at paginator creation time precompute_conditions: true, - resolvePage: (page)=>{ + resolvePage: (pg)=>{ // If an interactive button for this index hasn't been // resolved yet, run this code - page.getState("episodes_key"); // -> supplemental key + pg.getState("episodes_key"); // -> supplemental key /* Resolve supplemental key via api */ + console.log("get episodes for " + pg.getState("episodes_key")) - return [...cards]; + let i = 0; + return [ + createEmbed("default", context, { + description: `-# ${pg.getState("name")} > **Episodes**\n\nepisode page ${i++}`, + }), + createEmbed("default", context, { + description: `-# ${pg.getState("name")} > **Episodes**\n\nepisode page ${i++}`, + }), + createEmbed("default", context, { + description: `-# ${pg.getState("name")} > **Episodes**\n\nepisode page ${i++}`, + }) + ].map((p)=>page(p)); } }, characters_button: { diff --git a/labscore/cardstack/DynamicCardStack.js b/labscore/cardstack/DynamicCardStack.js index 0b5d218..771d2f1 100644 --- a/labscore/cardstack/DynamicCardStack.js +++ b/labscore/cardstack/DynamicCardStack.js @@ -34,6 +34,7 @@ class DynamicCardStack { * @param {Object} options.interactive Interactive Components * @param {Number} options.startingIndex Starting card index * @param {boolean} options.loop Wrap paging + * @param {number} options.expires CardStack timeout */ constructor(context, options){ this.context = context; @@ -44,22 +45,28 @@ class DynamicCardStack { this.index = options.startingIndex || 0; this.rootIndex = this.index; this.loopPages = options.loop || true; + this.expires = options.expires || 1*60*1000; + + this.uniqueId = (Date.now()*Math.random()).toString(36); this.pageState = []; this.subcategoryState = SUBCATEGORY_STATE_TYPES.SINGLE_PAGE; this.currentSelectedSubcategory = null; - console.log("now spawning") + console.log("now spawning " + this.uniqueId) this._spawn(); } /** * Kills the dynamic card stack. */ - kill(){ + async kill(clearComponents){ + console.log("killing " + this.uniqueId) clearTimeout(this.timeout); - // Remove reference to free the paginator for GC + if(clearComponents) await this._edit(this.currentPage(), []) + + // Remove reference to free the cardstack for GC activeStacks.delete(this.context.message?.id); } @@ -73,8 +80,9 @@ class DynamicCardStack { } _createDynamicCardStack(){ - // Kill any previously active paginators + // Kill any previously active cardstacks if(activeStacks.get(this.context.message?.id)){ + console.log(this.uniqueId + " is replacing " + this._getStackByMessageId(this.context.message?.id).uniqueId); this._getStackByMessageId(this.context.message?.id).kill(); } @@ -82,7 +90,7 @@ class DynamicCardStack { } /** - * Creates a new paginator in the given channel + * Creates a new cardstack in the given channel */ _spawn(){ try{ @@ -94,7 +102,6 @@ class DynamicCardStack { let i = 0; for(const ac of this.cards){ if(ac["_meta"]){ - console.log(ac) this.pageState[i] = Object.assign({}, ac["_meta"]); } i++; @@ -109,11 +116,15 @@ class DynamicCardStack { } }) + this.timeout = setTimeout(()=>{ + console.log(this.uniqueId + " timed out.") + this.kill(true); + }, this.expires) return this._edit({ ...this.getCurrentCard(), components: this.listener - }, true); + }); }catch(e){ console.log(e) } @@ -153,17 +164,22 @@ class DynamicCardStack { * Edits the cardstack message. * Automatically applies and rerenders components. * @param {Message} cardContent Card Content + * @param {boolean, Array} components Custom Components Array */ - async _edit(cardContent){ + async _edit(cardContent, components = false){ let message = Object.assign({}, cardContent); - this.listener.components = this._renderComponents(); - message.components = this.listener; + if(!components){ + this.listener.components = this._renderComponents(); + message.components = this.listener; + } else { + message.components = components; + } if(message["_meta"]) delete message["_meta"]; - console.log("GOING OUT:") - console.log(JSON.stringify(message, null, 2)) + //console.log("GOING OUT:") + //console.log(JSON.stringify(message, null, 2)) try{ return editOrReply(this.context, { @@ -177,7 +193,6 @@ class DynamicCardStack { } getCurrentCard(){ - console.log(this.activeCardStack[this.index]) return this.activeCardStack[this.index]; } @@ -193,6 +208,7 @@ class DynamicCardStack { */ getState(key){ if(!this.pageState[this.rootIndex]) return null; + if(!this.pageState[this.rootIndex][key]) return null; return this.pageState[this.rootIndex][key]; } @@ -209,8 +225,6 @@ class DynamicCardStack { // First Row always starts with built-in components for(const b of this.buttons){ - console.log("len: " + this.activeCardStack.length) - console.log(this.activeCardStack) let btn = { type: MessageComponentTypes.BUTTON, customId: b, @@ -237,6 +251,8 @@ class DynamicCardStack { disabled: disabled } + if(button.condition && typeof(button.condition) == "function") component.disabled = !button.condition(this); + if(button.label){ if(typeof(button.label) === "function") component.label = button.label(this); else component.label = button.label; @@ -250,7 +266,6 @@ class DynamicCardStack { else nComponentsSecondary.addButton(component); } - console.log(JSON.stringify(nComponents)) if(nComponentsSecondary.components.length >= 1) return [nComponents, nComponentsSecondary] return [nComponents]; } @@ -260,7 +275,6 @@ class DynamicCardStack { * @param {ComponentContext} ctx */ async _handleInteraction(ctx){ - console.log(ctx.data.customId) // should be a built-in button if(["next","previous"].includes(ctx.data.customId)){ @@ -309,22 +323,21 @@ class DynamicCardStack { data: Object.assign(processingEmbed, { components: this._renderComponents(true)}) }) - console.log("resolve trigger") try{ + // TODO: cache resolved stacks this.activeCardStack = await this.interactive_components[ctx.data.customId].resolvePage(this); } catch(e){ + this.activeCardStack = [ + page(createEmbed("error", ctx, "Stack rendering failed.")) + ] console.log("resolve failed:") console.log(e) } - console.log("resolve post") // TODO: allow overriding index this.index = 0; - console.log("stack resolved") - setTimeout(()=>{ - console.log(this.activeCardStack) - return this._edit(Object.assign(this.currentPage(), {components:this.listener}), true) + return this._edit(Object.assign(this.currentPage(), {components:this.listener})) }, 1500) return;