From 4b958d17fd28ce39917c006c614efb4189fa4dd0 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 28 Oct 2023 17:18:00 -0300 Subject: [PATCH] Fix BetterFolders freeze and add new options (#1923) --- src/plugins/betterFolders/FolderSideBar.tsx | 1 + src/plugins/betterFolders/index.tsx | 119 ++++++++++++++++---- 2 files changed, 96 insertions(+), 24 deletions(-) diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index f3884701..5504e1d5 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -33,6 +33,7 @@ export default ErrorBoundary.wrap(guildsBarProps => { ); diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index a3b20c6c..8f40d90f 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -18,13 +18,21 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; +import { proxyLazy } from "@utils/lazy"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByProps, findByPropsLazy, findStoreLazy } from "@webpack"; import { FluxDispatcher, i18n } from "@webpack/common"; import FolderSideBar from "./FolderSideBar"; -const GuildFolderStore = findStoreLazy("SortedGuildStore"); +enum FolderIconDisplay { + Never, + Always, + MoreThanOneFolderExpanded +} + +const GuildsTree = proxyLazy(() => findByProps("GuildsTree").GuildsTree); +const SortedGuildStore = findStoreLazy("SortedGuildStore"); export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore"); const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand"); @@ -32,7 +40,7 @@ let lastGuildId = null as string | null; let dispatchingFoldersClose = false; function getGuildFolder(id: string) { - return GuildFolderStore.getGuildFolders().find(folder => folder.guildIds.includes(id)); + return SortedGuildStore.getGuildFolders().find(folder => folder.guildIds.includes(id)); } function closeFolders() { @@ -50,7 +58,6 @@ export const settings = definePluginSettings({ sidebarAnim: { type: OptionType.BOOLEAN, description: "Animate opening the folder sidebar", - restartNeeded: true, default: true }, closeAllFolders: { @@ -79,6 +86,16 @@ export const settings = definePluginSettings({ description: "Keep showing guild icons in the primary guild bar folder when it's open in the BetterFolders sidebar", restartNeeded: true, default: false + }, + showFolderIcon: { + type: OptionType.SELECT, + description: "Show the folder icon above the folder guilds in the BetterFolders sidebar", + options: [ + { label: "Never", value: FolderIconDisplay.Never }, + { label: "Always", value: FolderIconDisplay.Always, default: true }, + { label: "When more than one folder is expanded", value: FolderIconDisplay.MoreThanOneFolderExpanded } + ], + restartNeeded: true } }); @@ -99,25 +116,45 @@ export default definePlugin({ match: /(?<=let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?)(?=}=\i,)/, replace: ",isBetterFolders" }, + // If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders + { + match: /(useStateFromStoresArray\).{0,25}let \i)=(\i\.\i.getGuildsTree\(\))/, + replace: (_, rest, guildsTree) => `${rest}=$self.getGuildTree(!!arguments[0].isBetterFolders,${guildsTree},arguments[0].betterFoldersExpandedIds)` + }, // If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children { match: /lastTargetNode:\i\[\i\.length-1\].+?Fragment.+?\]}\)\]/, - replace: '$&.filter($self.makeGuildsBarGuildListFilter(typeof isBetterFolders!=="undefined"?isBetterFolders:false))' + replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0].isBetterFolders))" }, // If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children { match: /unreadMentionsIndicatorBottom,barClassName.+?}\)\]/, - replace: '$&.filter($self.makeGuildsBarTreeFilter(typeof isBetterFolders!=="undefined"?isBetterFolders:false))' + replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0].isBetterFolders))" }, // Export the isBetterFolders variable to the folders component { match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,)/, replace: 'isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,' - }, - // Avoid rendering servers that are not in folders in the Better Folders sidebar + } + ] + }, + { + // This is the parent folder component + find: ".MAX_GUILD_FOLDER_NAME_LENGTH,", + predicate: () => settings.store.sidebar && settings.store.showFolderIcon !== FolderIconDisplay.Always, + replacement: [ { - match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?GUILD:)/, - replace: 'if((typeof isBetterFolders!=="undefined"?isBetterFolders:false)&&$1.parentId==null)return null;' + // Modify the expanded state to instead return the list of expanded folders + match: /(useStateFromStores\).{0,20}=>)(\i\.\i)\.isFolderExpanded\(\i\)/, + replace: (_, rest, ExpandedGuildFolderStore) => `${rest}${ExpandedGuildFolderStore}.getExpandedFolders()`, + }, + { + // Modify the expanded prop to use the boolean if the above patch fails, or check if the folder is expanded from the list if it succeeds + // Also export the list of expanded folders to the child folder component if the patch above succeeds, else export undefined + match: /(?<=folderNode:(\i),expanded:)\i(?=,)/, + replace: (isExpandedOrExpandedIds, folderNote) => "" + + `typeof ${isExpandedOrExpandedIds}==="boolean"?${isExpandedOrExpandedIds}:${isExpandedOrExpandedIds}.has(${folderNote}.id),` + + `betterFoldersExpandedIds:${isExpandedOrExpandedIds} instanceof Set?${isExpandedOrExpandedIds}:void 0` } ] }, @@ -125,33 +162,37 @@ export default definePlugin({ find: ".FOLDER_ITEM_GUILD_ICON_MARGIN);", predicate: () => settings.store.sidebar, replacement: [ - // Create the isBetterFolders variable in the nested folders component (the parent exports all the props so we don't have to patch it) - { - match: /(?<=let{folderNode:\i,setNodeRef:\i,)/, - replace: "isBetterFolders," - }, + // We use arguments[0] to access the isBetterFolders variable in this nested folder component (the parent exports all the props so we don't have to patch it) + // If we are rendering the normal GuildsBar sidebar, we make Discord think the folder is always collapsed to show better icons (the mini guild icons) and avoid transitions { predicate: () => settings.store.keepIcons, - match: /(?<=let{folderNode:\i,setNodeRef:\i,.+?expanded:(\i).+?;)(?=let)/, - replace: '$1=(typeof isBetterFolders!=="undefined"?isBetterFolders:false)?$1:false;' - }, - // If we are rendering the Better Folders sidebar, we filter out folders that are not expanded - { - match: /(?=return\(0,\i.\i\)\("div")(?<=selected:\i,expanded:(\i),.+?)/, - replace: (_, expanded) => `if((typeof isBetterFolders!=="undefined"?isBetterFolders:false)&&!${expanded})return null;` + match: /(?<=let{folderNode:\i,setNodeRef:\i,.+?expanded:(\i),.+?;)(?=let)/, + replace: (_, isExpanded) => `${isExpanded}=!!arguments[0].isBetterFolders&&${isExpanded};` }, // Disable expanding and collapsing folders transition in the normal GuildsBar sidebar { predicate: () => !settings.store.keepIcons, match: /(?<=\.Messages\.SERVER_FOLDER_PLACEHOLDER.+?useTransition\)\()/, - replace: '(typeof isBetterFolders!=="undefined"?isBetterFolders:false)&&' + replace: "!!arguments[0].isBetterFolders&&" }, // If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded { predicate: () => !settings.store.keepIcons, match: /expandedFolderBackground,.+?,(?=\i\(\(\i,\i,\i\)=>{let{key.{0,45}ul)(?<=selected:\i,expanded:(\i),.+?)/, - replace: (m, expanded) => `${m}((typeof isBetterFolders!=="undefined"?isBetterFolders:false)||!${expanded})&&` + replace: (m, isExpanded) => `${m}!arguments[0].isBetterFolders&&${isExpanded}?null:` + }, + { + // Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar + predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always, + match: /(?<=\.wrapper,children:\[)/, + replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)&&" + }, + { + // Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar + predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always, + match: /(?<=\.expandedFolderBackground.+?}\),)(?=\i,)/, + replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)?null:" } ] }, @@ -212,6 +253,21 @@ export default definePlugin({ } }, + getGuildTree(isBetterFolders: boolean, oldTree: any, expandedFolderIds?: Set) { + if (!isBetterFolders || expandedFolderIds == null) return oldTree; + + const newTree = new GuildsTree(); + // Children is every folder and guild which is not in a folder, this filters out only the expanded folders + newTree.root.children = oldTree.root.children.filter(guildOrFolder => expandedFolderIds.has(guildOrFolder.id)); + // Nodes is every folder and guild, even if it's in a folder, this filters out only the expanded folders and guilds inside them + newTree.nodes = Object.fromEntries( + Object.entries(oldTree.nodes) + .filter(([_, guildOrFolder]: any[]) => expandedFolderIds.has(guildOrFolder.id) || expandedFolderIds.has(guildOrFolder.parentId)) + ); + + return newTree; + }, + makeGuildsBarGuildListFilter(isBetterFolders: boolean) { return child => { if (isBetterFolders) { @@ -230,6 +286,21 @@ export default definePlugin({ }; }, + shouldShowFolderIconAndBackground(isBetterFolders: boolean, expandedFolderIds?: Set) { + if (!isBetterFolders) return true; + + switch (settings.store.showFolderIcon) { + case FolderIconDisplay.Never: + return false; + case FolderIconDisplay.Always: + return true; + case FolderIconDisplay.MoreThanOneFolderExpanded: + return (expandedFolderIds?.size ?? 0) > 1; + default: + return true; + } + }, + FolderSideBar: guildsBarProps => , closeFolders