import { proxyLazyWebpack } from "@webpack"; import { Flux, FluxDispatcher, PopoutActions, PopoutWindowStore, SnowflakeUtils } from "@webpack/common"; import DeckPopout from "./components/DeckPopout"; import { DataStore } from "@api/index"; export interface ChannelDeck { id: string; name: string; color?: number; columns: DeckColumn[]; open: boolean; } export interface DeckColumn { channelId: string, width: `${number}px` | `${number}%`; } export const ChannelDeckStore = proxyLazyWebpack(() => { class ChannelDeckStore extends Flux.Store { public _decks = new Map(); public loaded = false; public pageUnloading = false; public windowKeyPrefix = "DISCORD_VC_CHANNELDECK_"; public dataStoreKey = "ChannelDeck_Decks"; public createDeck(deckState: Partial>) { const deck = { id: SnowflakeUtils.fromTimestamp(Date.now()), name: "", columns: [], open: false, ...deckState }; this.setDeck(deck); return deck; } public getDeck(id: string) { return this._decks.get(id); } public setDeck(deck: ChannelDeck, noWrite?: boolean) { this._decks.set(deck.id, deck); const { id } = deck; const windowKey = this.windowKeyPrefix + id; // @ts-ignore if (deck?.open && !PopoutWindowStore.getWindowOpen(windowKey)) PopoutActions.open( windowKey, (key) => , { defaultWidth: 1200, defaultHeight: 960 }); // We cannot add a beforeunload event to the window here, it will be never fired as Discord has their own handler. // @ts-ignore if (!deck?.open && PopoutWindowStore.getWindowOpen(windowKey)) this.getDeckWindow(id).close(); if (noWrite || !this.loaded) return; this.writeData(); this.emitChange(); }; public deleteDeck(deck: ChannelDeck) { // Close window this.setDeck({ ...deck, open: false }, true); this._decks.delete(deck.id); this.writeData(); // @ts-ignore delete PopoutWindowStore.getState()[this.windowKeyPrefix + deck.id]; // @ts-ignore PopoutWindowStore.persist(); this.emitChange(); } public getDecks() { return this._decks.values(); } public getDeckWindow(id: string) { return PopoutWindowStore.getWindow(this.windowKeyPrefix + id); } // Workaround for Discord's beforeunload event public syncClosedWindows() { if (this.pageUnloading) return; this._decks.forEach((deck) => { // @ts-ignore if (deck.open && !PopoutWindowStore.getWindowOpen(this.windowKeyPrefix + deck.id)) this.setDeck({ ...deck, open: false }); }); } public async loadData() { this.loaded = true; const decks: Map | undefined = await DataStore.get(this.dataStoreKey); if (decks) { this._decks.clear(); decks.forEach(deck => this.setDeck(deck, true)); } } public async unloadData() { // closes all decks this.loaded = false; this._decks.forEach(deck => this.setDeck({ ...deck, open: false }, true)); this._decks.clear(); } public async writeData() { await DataStore.set(this.dataStoreKey, this._decks); } } const store = new ChannelDeckStore(FluxDispatcher, { }); window.addEventListener("beforeunload", () => store.pageUnloading = true); PopoutWindowStore.addChangeListener(() => store.syncClosedWindows()); return store; });;