diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a0a7fcd7..6a1c8ee8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,7 +59,7 @@ jobs: cp desktop/* release for file in equibop/*; do filename=$(basename "$file") - cp "$file" "release/equicordDesktop${filename^}" + cp "$file" "release/equibop${filename^}" done find release -size 0 -delete diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index bc675503..b4e3050f 100644 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -191,8 +191,8 @@ const buildConfigs = ([ globalName: "Vencord", sourcemap, plugins: [ - globPlugins("equicordDesktop"), - ...commonOpts.plugins + globPlugins("equibop"), + ...commonRendererPlugins ], define: { ...defines, diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index cfa46865..fd2ff6a7 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -132,7 +132,7 @@ export const makeAllPackagesExternalPlugin = { }; /** - * @type {(kind: "web" | "discordDesktop" | "vencordDesktop" | "equicordDesktop") => import("esbuild").Plugin} + * @type {(kind: "web" | "discordDesktop" | "vesktop" | "equibop") => import("esbuild").Plugin} */ export const globPlugins = kind => ({ name: "glob-plugins", @@ -171,8 +171,8 @@ export const globPlugins = kind => ({ (target === "web" && kind === "discordDesktop") || (target === "desktop" && kind === "web") || (target === "discordDesktop" && kind !== "discordDesktop") || - (target === "vencordDesktop" && kind !== "vencordDesktop" && kind !== "equicordDesktop") || - (target === "equicordDesktop" && kind !== "equicordDesktop" && kind !== "vencordDesktop"); + (target === "vesktop" && kind !== "vesktop" && kind !== "equibop") || + (target === "equibop" && kind !== "equibop" && kind !== "vesktop"); if (excluded) { const name = await resolvePluginName(fullDir, file); @@ -358,10 +358,6 @@ export const commonOpts = { jsxFragment: "VencordFragment" }; -const escapedBuiltinModules = builtinModules - .map(m => m.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")) - .join("|"); - export const commonRendererPlugins = [ banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"), banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"), diff --git a/scripts/generateEquicordPluginList.ts b/scripts/generateEquicordPluginList.ts index 2a349f58..6fbd61bf 100644 --- a/scripts/generateEquicordPluginList.ts +++ b/scripts/generateEquicordPluginList.ts @@ -45,7 +45,7 @@ interface PluginData { commands: Command[]; required: boolean; enabledByDefault: boolean; - target: "discordDesktop" | "vencordDesktop" | "equicordDesktop" | "desktop" | "web" | "dev"; + target: "discordDesktop" | "vencordDesktop" | "equibop" | "desktop" | "web" | "dev"; filePath: string; } @@ -215,7 +215,7 @@ async function parseFile(fileName: string) { const target = getPluginTarget(fileName); if (target) { - if (!["web", "discordDesktop", "vencordDesktop", "equicordDesktop", "desktop", "dev"].includes(target)) throw fail(`invalid target ${target}`); + if (!["web", "discordDesktop", "vencordDesktop", "equibop", "desktop", "dev"].includes(target)) throw fail(`invalid target ${target}`); data.target = target as any; } diff --git a/scripts/generatePluginList.ts b/scripts/generatePluginList.ts index 7f0dac58..e8af78b4 100644 --- a/scripts/generatePluginList.ts +++ b/scripts/generatePluginList.ts @@ -45,7 +45,7 @@ interface PluginData { commands: Command[]; required: boolean; enabledByDefault: boolean; - target: "discordDesktop" | "vencordDesktop" | "equicordDesktop" | "desktop" | "web" | "dev"; + target: "discordDesktop" | "vesktop" | "equibop" | "desktop" | "web" | "dev"; filePath: string; } @@ -215,7 +215,7 @@ async function parseFile(fileName: string) { const target = getPluginTarget(fileName); if (target) { - if (!["web", "discordDesktop", "vencordDesktop", "equicordDesktop", "desktop", "dev"].includes(target)) throw fail(`invalid target ${target}`); + if (!["web", "discordDesktop", "vesktop", "equibop", "desktop", "dev"].includes(target)) throw fail(`invalid target ${target}`); data.target = target as any; } diff --git a/src/api/Notifications/styles.css b/src/api/Notifications/styles.css index 98dff6df..ad5c9cbc 100644 --- a/src/api/Notifications/styles.css +++ b/src/api/Notifications/styles.css @@ -11,6 +11,10 @@ width: 100%; } +.visual-refresh .vc-notification-root { + background-color: var(--bg-overlay-floating, var(--background-base-low)); +} + .vc-notification-root:not(.vc-notification-log-wrapper > .vc-notification-root) { position: absolute; z-index: 2147483647; diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index 7f1cc583..bbb17d27 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -192,11 +192,11 @@ function ExcludedPluginsList({ search }: { search: string; }) { const matchingExcludedPlugins = Object.entries(ExcludedPlugins) .filter(([name]) => name.toLowerCase().includes(search)); - const ExcludedReasons: Record<"web" | "discordDesktop" | "vencordDesktop" | "equicordDesktop" | "desktop" | "dev", string> = { + const ExcludedReasons: Record<"web" | "discordDesktop" | "vesktop" | "equibop" | "desktop" | "dev", string> = { desktop: "Discord Desktop app or Vesktop", discordDesktop: "Discord Desktop app", - vencordDesktop: "Vesktop Equibop apps", - equicordDesktop: "Vesktop & Equibop apps", + vesktop: "Vesktop Equibop apps", + equibop: "Vesktop & Equibop apps", web: "Vesktop & Equibop apps as well as the Web version of Discord", dev: "Developer version of Equicord" }; diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx index 10904e14..87af25e2 100644 --- a/src/components/Switch.tsx +++ b/src/components/Switch.tsx @@ -27,7 +27,7 @@ interface SwitchProps { disabled?: boolean; } -const SWITCH_ON = "var(--green-360)"; +const SWITCH_ON = "var(--brand-500)"; const SWITCH_OFF = "var(--primary-400)"; const SwitchClasses = findByPropsLazy("slider", "input", "container"); diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index 9e825152..be141df3 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -83,8 +83,8 @@ export default definePlugin({ // this also affects name headers in chats outside of servers find: '="SYSTEM_TAG"', replacement: { - match: /(?<=\i.gradientClassName]\),style:.{0,80}:void 0\}\)\(\),)/, - replace: "style:{color:$self.colorIfServer(arguments[0])}," + match: /\i.gradientClassName]\),style:/, + replace: "$&{color:$self.colorIfServer(arguments[0])},_style:" }, predicate: () => !Settings.plugins.IrcColors.enabled }, diff --git a/src/modules.d.ts b/src/modules.d.ts index 698ff2ab..ca7db7f6 100644 --- a/src/modules.d.ts +++ b/src/modules.d.ts @@ -25,7 +25,7 @@ declare module "~plugins" { folderName: string; userPlugin: boolean; }>; - export const ExcludedPlugins: Record; + export const ExcludedPlugins: Record; } declare module "~pluginNatives" { diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index 406f32b4..2b212d34 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -81,8 +81,8 @@ export default definePlugin({ replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalRenderPopout:()=>{${originalPopout}}})` }, { - match: /(\.AVATAR,children:.+?onRequestClose:\(\)=>\{)/, - replace: "$&onRequestClose:$self.onPopoutClose();" + match: /\.AVATAR,children:.+?onRequestClose:\(\)=>\{/, + replace: "$&$self.onPopoutClose();" }, { match: /(?<=#{intl::SET_STATUS}\),)/, diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index 63a04ca3..d2ffe6bb 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -45,8 +45,8 @@ export default ErrorBoundary.wrap(guildsBarProps => { // Also display flex otherwise to fix scrolling const barStyle = { display: isFullscreen ? "none" : "flex", - gridArea: "sidebar" - } as CSSProperties; + gridArea: "betterFoldersSidebar" + } satisfies CSSProperties; if (!guilds || !settings.store.sidebarAnim) { return visible diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 10357383..2d8bf360 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +import "./sidebarFix.css"; + import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { getIntlMessage } from "@utils/discord"; @@ -104,7 +106,7 @@ export const settings = definePluginSettings({ } }); -let cssMade = false; +const cssMade = false; const cssElementId = "VC-BetterFolders"; @@ -202,6 +204,13 @@ export default definePlugin({ predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always, match: /(?<=\.expandedFolderBackground.+?}\),)(?=\i,)/, replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)?null:" + }, + { + // Discord adds a slight bottom margin of 4px when it's expanded + // Which looks off when there's nothing open in the folder + predicate: () => !settings.store.keepIcons, + match: /(?=className:.{0,50}folderIcon)/, + replace: "style:arguments[0]?.isBetterFolders?{}:{marginBottom:0}," } ] }, @@ -212,12 +221,17 @@ export default definePlugin({ replacement: [ { // Render the Better Folders sidebar + // Discord has two different places where they render the sidebar. + // One is for visual refresh, one is not, + // and each has a bunch of conditions &&ed in front of it. + // Add the betterFolders sidebar to both, keeping the conditions Discord uses. match: /(?<=[[,])((?:!?\i&&)+)\(.{0,50}({className:\i\.guilds,themeOverride:\i})\)/g, - replace: (_, conditions, props) => `${_},${conditions}$self.FolderSideBar({...${props}})` + replace: (m, conditions, props) => `${m},${conditions}$self.FolderSideBar(${props})` }, { + // Add grid styles to fix aligment with other visual refresh elements match: /(?<=className:)(\i\.base)(?=,)/, - replace: "($self.makePatchedBaseCSS($1))" + replace: "`${$self.gridStyle} ${$1}`" } ] }, @@ -276,46 +290,7 @@ export default definePlugin({ } }, - gridStyle: "vc-BetterFolders-sidebar-grid", - makePatchedBaseCSS(className: string) { - done: try { - if (cssMade) break done; - const rule = [...document.styleSheets] - .flatMap(x => [...x.cssRules]) - // cant do includes because they have a `not ((grid-template-columns` - // dumb type inference - .filter((x): x is CSSSupportsRule => x instanceof CSSSupportsRule && x.conditionText.startsWith("(grid-template-columns")) - .flatMap(x => [...x.cssRules]) - .filter(x => x instanceof CSSStyleRule) - .find(x => x.selectorText.endsWith(`.${className}`)); - if (!rule) { - console.error("Failed to find css rule for betterFolders"); - break done; - } - const areas = rule.style.gridTemplateAreas - .split('" "') - .map(x => x.replace(/"/g, "").split(" ")); - areas[0].splice(1, 0, areas[0][0]); - areas[1].splice(1, 0, "sidebar"); - areas[2].splice(1, 0, "sidebar"); - const css = ` - .visual-refresh .${this.gridStyle} { - grid-template-areas: ${areas.map(x => `"${x.join(" ")}"`).join(" ")}; - grid-template-columns: ${rule.style.gridTemplateColumns.replace(/(?<=guildsEnd\])/, " min-content [sidebarEnd]")}; - } - `; - const element = document.createElement("style"); - element.id = cssElementId; - element.textContent = css; - document.getElementById(cssElementId)?.remove(); - document.head.appendChild(element); - cssMade = true; - } catch (e) { - console.error(e); - return className; - } - return `${className} ${this.gridStyle}`; - }, + gridStyle: "vc-betterFolders-sidebar-grid", getGuildTree(isBetterFolders: boolean, originalTree: any, expandedFolderIds?: Set) { return useMemo(() => { diff --git a/src/plugins/betterFolders/sidebarFix.css b/src/plugins/betterFolders/sidebarFix.css new file mode 100644 index 00000000..7a048eb7 --- /dev/null +++ b/src/plugins/betterFolders/sidebarFix.css @@ -0,0 +1,9 @@ +/* These area names need to be hardcoded. Only betterFoldersSidebar is added by the plugin. */ + +.visual-refresh .vc-betterFolders-sidebar-grid { + grid-template-columns: [start] min-content [guildsEnd] min-content [sidebarEnd] min-content [channelsEnd] 1fr [end]; /* stylelint-disable-line value-keyword-case */ + grid-template-areas: + "titleBar titleBar titleBar titleBar" + "guildsList betterFoldersSidebar notice notice" + "guildsList betterFoldersSidebar channelsList page"; +} diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index e8d00043..72fc4429 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -130,12 +130,15 @@ export default definePlugin({ } }); -const variableRegex = /(--neutral-\d{1,3}-hsl):.*?(\S*)%;/g; +const visualRefreshVariableRegex = /(--neutral-\d{1,3}-hsl):.*?(\S*)%;/g; +const oldVariableRegex = /(--primary-\d{3}-hsl):.*?(\S*)%;/g; +const lightVariableRegex = /^--primary-[1-5]\d{2}-hsl/g; +const darkVariableRegex = /^--primary-[5-9]\d{2}-hsl/g; // generates variables per theme by: // - offset from specified center (light/dark theme get different offsets because light uses 100 for background-primary, while dark uses 600) -function genThemeSpecificOffsets(variableLightness: Record, centerVariable: string): string { - return Object.entries(variableLightness) +function genThemeSpecificOffsets(variableLightness: Record, regex: RegExp | null, centerVariable: string): string { + return Object.entries(variableLightness).filter(([key]) => regex == null || key.search(regex) > -1) .map(([key, lightness]) => { const lightnessOffset = lightness - variableLightness[centerVariable]; const plusOrMinus = lightnessOffset >= 0 ? "+" : "-"; @@ -145,23 +148,65 @@ function genThemeSpecificOffsets(variableLightness: Record, cent } function generateColorOffsets(styles) { - const variableLightness = {} as Record; + const oldVariableLightness = {} as Record; + const visualRefreshVariableLightness = {} as Record; // Get lightness values of --primary variables - let variableMatch = variableRegex.exec(styles); - while (variableMatch !== null) { - const [, variable, lightness] = variableMatch; - variableLightness[variable] = parseFloat(lightness); - variableMatch = variableRegex.exec(styles); + for (const [, variable, lightness] of styles.matchAll(oldVariableRegex)) { + oldVariableLightness[variable] = parseFloat(lightness); + } + + for (const [, variable, lightness] of styles.matchAll(visualRefreshVariableRegex)) { + visualRefreshVariableLightness[variable] = parseFloat(lightness); } createStyleSheet("clientThemeOffsets", [ - // you can determine the "center color" by looking at the variable used by `--background-primary` - `.theme-light {\n ${genThemeSpecificOffsets(variableLightness, "--neutral-2-hsl")} \n}`, - `.theme-dark {\n ${genThemeSpecificOffsets(variableLightness, "--neutral-69-hsl")} \n}`, + `.theme-light {\n ${genThemeSpecificOffsets(oldVariableLightness, lightVariableRegex, "--primary-345-hsl")} \n}`, + `.theme-dark {\n ${genThemeSpecificOffsets(oldVariableLightness, darkVariableRegex, "--primary-600-hsl")} \n}`, + `.visual-refresh.theme-light {\n ${genThemeSpecificOffsets(visualRefreshVariableLightness, null, "--neutral-2-hsl")} \n}`, + `.visual-refresh.theme-dark {\n ${genThemeSpecificOffsets(visualRefreshVariableLightness, null, "--neutral-69-hsl")} \n}`, ].join("\n\n")); } +function generateLightModeFixes(styles: string) { + const groupLightUsesW500Regex = /\.theme-light[^{]*\{[^}]*var\(--white-500\)[^}]*}/gm; + // get light capturing groups that mention --white-500 + const relevantStyles = [...styles.matchAll(groupLightUsesW500Regex)].flat(); + + const groupBackgroundRegex = /^([^{]*)\{background:var\(--white-500\)/m; + const groupBackgroundColorRegex = /^([^{]*)\{background-color:var\(--white-500\)/m; + // find all capturing groups that assign background or background-color directly to w500 + const backgroundGroups = mapReject(relevantStyles, entry => captureOne(entry, groupBackgroundRegex)).join(",\n"); + const backgroundColorGroups = mapReject(relevantStyles, entry => captureOne(entry, groupBackgroundColorRegex)).join(",\n"); + // create css to reassign them to --primary-100 + const reassignBackgrounds = `${backgroundGroups} {\n background: var(--primary-100) \n}`; + const reassignBackgroundColors = `${backgroundColorGroups} {\n background-color: var(--primary-100) \n}`; + + const groupBgVarRegex = /\.theme-light\{([^}]*--[^:}]*(?:background|bg)[^:}]*:var\(--white-500\)[^}]*)\}/m; + const bgVarRegex = /^(--[^:]*(?:background|bg)[^:]*):var\(--white-500\)/m; + // get all global variables used for backgrounds + const lightVars = mapReject(relevantStyles, style => captureOne(style, groupBgVarRegex)) // get the insides of capture groups that have at least one background var with w500 + .map(str => str.split(";")).flat(); // captureGroupInsides[] -> cssRule[] + const lightBgVars = mapReject(lightVars, variable => captureOne(variable, bgVarRegex)); // remove vars that aren't for backgrounds or w500 + // create css to reassign every var + const reassignVariables = `.theme-light {\n ${lightBgVars.map(variable => `${variable}: var(--primary-100);`).join("\n")} \n}`; + + createStyleSheet("clientThemeLightModeFixes", [ + reassignBackgrounds, + reassignBackgroundColors, + reassignVariables, + ].join("\n\n")); +} + +function captureOne(str, regex) { + const result = str.match(regex); + return (result === null) ? null : result[1]; +} + +function mapReject(arr, mapFunc) { + return arr.map(mapFunc).filter(Boolean); +} + function updateColorVars(color: string) { const { hue, saturation, lightness } = hexToHSL(color); diff --git a/src/plugins/consoleJanitor/index.tsx b/src/plugins/consoleJanitor/index.tsx index 12fe231c..d32f525e 100644 --- a/src/plugins/consoleJanitor/index.tsx +++ b/src/plugins/consoleJanitor/index.tsx @@ -117,6 +117,7 @@ export default definePlugin({ this.settings.store.whitelistedLoggers?.split(";").map(x => x.trim()).forEach(logAllow.add.bind(logAllow)); }, + Noop, NoopLogger: () => NoopLogger, shouldLog(logger: string, level: keyof AllowLevels) { @@ -149,14 +150,14 @@ export default definePlugin({ find: "is not a valid locale.", replacement: { match: /\i\.error(?=\(""\.concat\(\i," is not a valid locale."\)\))/, - replace: "$self.NoopLogger" + replace: "$self.Noop" } }, { find: '"AppCrashedFatalReport: getLastCrash not supported."', replacement: { match: /console\.log(?=\("AppCrashedFatalReport: getLastCrash not supported\."\))/, - replace: "$self.NoopLogger" + replace: "$self.Noop" } }, { diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 900b481b..10371315 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -155,6 +155,7 @@ function makeShortcuts() { Stores: Webpack.fluxStores, + // e.g. "2024-05_desktop_visual_refresh", 0 setExperiment: (id: string, bucket: number) => { Common.FluxDispatcher.dispatch({ type: "EXPERIMENT_OVERRIDE_BUCKET", diff --git a/src/plugins/customidle/index.ts b/src/plugins/customidle/index.ts index 77e7d4e9..2d8b5d3d 100644 --- a/src/plugins/customidle/index.ts +++ b/src/plugins/customidle/index.ts @@ -47,19 +47,11 @@ export default definePlugin({ { match: /\i\.\i\.dispatch\({type:"IDLE",idle:!1}\)/, replace: "$self.handleOnline()" - }, - { - match: /(setInterval\(\i,30\*)\i\.\i\.Millis\.SECOND/, - replace: "$1$self.getIntervalDelay()" // For web installs } ] } ], - getIntervalDelay() { - return Math.min(6e5, this.getIdleTimeout()); - }, - handleOnline() { if (!settings.store.remainInIdle) { FluxDispatcher.dispatch({ diff --git a/src/plugins/disableDeepLinks.vesktop/index.ts b/src/plugins/disableDeepLinks.vesktop/index.ts new file mode 100644 index 00000000..c917181b --- /dev/null +++ b/src/plugins/disableDeepLinks.vesktop/index.ts @@ -0,0 +1,23 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "DisableDeepLinks", + description: "Disables Discord stupid DeepLinks experiment which makes the app unusable", + authors: [Devs.Ven], + required: true, + + patches: [{ + find: "2025-03_desktop_deeplinks", + replacement: { + match: /config:{enabled:!0/, + replace: "config:{enabled:!1", + } + }] +}); diff --git a/src/plugins/implicitRelationships/index.ts b/src/plugins/implicitRelationships/index.ts index f0be9ee5..13c12ed9 100644 --- a/src/plugins/implicitRelationships/index.ts +++ b/src/plugins/implicitRelationships/index.ts @@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findStoreLazy } from "@webpack"; -import { ChannelStore, Constants, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common"; +import { Constants, FluxDispatcher, GuildStore, RelationshipStore, RestAPI, SnowflakeUtils, UserStore } from "@webpack/common"; import { Settings } from "Vencord"; const UserAffinitiesStore = findStoreLazy("UserAffinitiesStore"); @@ -121,55 +121,61 @@ export default definePlugin({ : comparator(row); }, + async refreshUserAffinities() { + try { + await RestAPI.get({ url: "/users/@me/affinities/users", retries: 3 }).then(({ body }) => { + FluxDispatcher.dispatch({ + type: "LOAD_USER_AFFINITIES_SUCCESS", + affinities: body, + }); + }); + } catch (e) { + // Not a critical error if this fails for some reason + } + }, + async fetchImplicitRelationships() { // Implicit relationships are defined as users that you: // 1. Have an affinity for - // 2. Do not have a relationship with // TODO: Check how this works with pending/blocked relationships - // 3. Have a mutual guild with + // 2. Do not have a relationship with + await this.refreshUserAffinities(); const userAffinities: Set = UserAffinitiesStore.getUserAffinitiesUserIds(); + const relationships = RelationshipStore.getRelationships(); const nonFriendAffinities = Array.from(userAffinities).filter( id => !RelationshipStore.getRelationshipType(id) ); + nonFriendAffinities.forEach(id => { + relationships[id] = 5; + }); + RelationshipStore.emitChange(); - // I would love to just check user cache here (falling back to the gateway of course) - // However, users in user cache may just be there because they share a DM or group DM with you - // So there's no guarantee that a user being in user cache means they have a mutual with you - // To get around this, we request users we have DMs with, and ignore them below if we don't get them back - const dmUserIds = new Set( - Object.values(ChannelStore.getSortedPrivateChannels()).flatMap(c => c.recipients) - ); - const toRequest = nonFriendAffinities.filter(id => !UserStore.getUser(id) || dmUserIds.has(id)); + const toRequest = nonFriendAffinities.filter(id => !UserStore.getUser(id)); const allGuildIds = Object.keys(GuildStore.getGuilds()); const sentNonce = SnowflakeUtils.fromTimestamp(Date.now()); let count = allGuildIds.length * Math.ceil(toRequest.length / 100); // OP 8 Request Guild Members allows 100 user IDs at a time - const ignore = new Set(toRequest); - const relationships = RelationshipStore.getRelationships(); + // Note: As we are using OP 8 here, implicit relationships who we do not share a guild + // with will not be fetched; so, if they're not otherwise cached, they will not be shown + // This should not be a big deal as these should be rare const callback = ({ chunks }) => { - for (const chunk of chunks) { - const { nonce, members } = chunk; - if (nonce !== sentNonce) return; - members.forEach(member => { - ignore.delete(member.user.id); - }); + const chunkCount = chunks.filter(chunk => chunk.nonce === sentNonce).length; + if (chunkCount === 0) return; - nonFriendAffinities.map(id => UserStore.getUser(id)).filter(user => user && !ignore.has(user.id)).forEach(user => relationships[user.id] = 5); - RelationshipStore.emitChange(); - if (--count === 0) { - // @ts-ignore - FluxDispatcher.unsubscribe("GUILD_MEMBERS_CHUNK_BATCH", callback); - } + count -= chunkCount; + RelationshipStore.emitChange(); + if (count <= 0) { + FluxDispatcher.unsubscribe("GUILD_MEMBERS_CHUNK_BATCH", callback); } }; - // @ts-ignore FluxDispatcher.subscribe("GUILD_MEMBERS_CHUNK_BATCH", callback); for (let i = 0; i < toRequest.length; i += 100) { FluxDispatcher.dispatch({ type: "GUILD_MEMBERS_REQUEST", guildIds: allGuildIds, userIds: toRequest.slice(i, i + 100), + presences: true, nonce: sentNonce, }); } diff --git a/src/plugins/invisibleChat.desktop/index.tsx b/src/plugins/invisibleChat.desktop/index.tsx index f98ab176..f5e8cbb5 100644 --- a/src/plugins/invisibleChat.desktop/index.tsx +++ b/src/plugins/invisibleChat.desktop/index.tsx @@ -80,8 +80,8 @@ const ChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => { diff --git a/src/plugins/ircColors/index.ts b/src/plugins/ircColors/index.ts index 237286e7..3d531459 100644 --- a/src/plugins/ircColors/index.ts +++ b/src/plugins/ircColors/index.ts @@ -67,8 +67,8 @@ export default definePlugin({ { find: '="SYSTEM_TAG"', replacement: { - match: /(?<=\i.gradientClassName]\),style:.{0,80}:void 0\}\)\(\),)/, - replace: "style:{color:$self.calculateNameColorForMessageContext(arguments[0])}," + match: /\i.gradientClassName]\),style:/, + replace: "$&{color:$self.calculateNameColorForMessageContext(arguments[0])},_style:" } }, { diff --git a/src/plugins/oneko/index.ts b/src/plugins/oneko/index.ts index 32c7e917..ff0193e1 100644 --- a/src/plugins/oneko/index.ts +++ b/src/plugins/oneko/index.ts @@ -54,7 +54,7 @@ export default definePlugin({ settings, start() { - fetch("https://raw.githubusercontent.com/adryd325/oneko.js/8fa8a1864aa71cd7a794d58bc139e755e96a236c/oneko.js") + fetch("https://raw.githubusercontent.com/adryd325/oneko.js/c4ee66353b11a44e4a5b7e914a81f8d33111555e/oneko.js") .then(x => x.text()) .then(s => s.replace("const nekoSpeed = 10;", `const nekoSpeed = ${settings.store.speed};`)) .then(s => s.replace("./oneko.gif", "https://raw.githubusercontent.com/adryd325/oneko.js/14bab15a755d0e35cd4ae19c931d96d306f99f42/oneko.gif") diff --git a/src/plugins/previewMessage/index.tsx b/src/plugins/previewMessage/index.tsx index 7b03e31d..ded408f9 100644 --- a/src/plugins/previewMessage/index.tsx +++ b/src/plugins/previewMessage/index.tsx @@ -105,8 +105,8 @@ const PreviewButton: ChatBarButtonFactory = ({ isMainChat, isEmpty, type: { atta diff --git a/src/plugins/sendTimestamps/index.tsx b/src/plugins/sendTimestamps/index.tsx index 57b9de0d..6bfac736 100644 --- a/src/plugins/sendTimestamps/index.tsx +++ b/src/plugins/sendTimestamps/index.tsx @@ -144,8 +144,8 @@ const ChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => {