Compare commits

...

17 Commits

Author SHA1 Message Date
Lewis Crichton
5d1736d020 feat: rework consent 2023-10-28 18:07:19 +01:00
Lewis Crichton
3de02708a6 chore: add TODO comment as reminder 2023-10-28 13:23:50 +01:00
Lewis Crichton
49c331fcc9 feat: anonymous telemetry 2023-10-28 13:19:59 +01:00
Lewis Crichton
ba53acdca7 chore: dedupe platform consts 2023-10-28 12:46:48 +01:00
Nuckyz
bfb48b4faf BetterFolders: Option to choose whether to keep guild icons (#1918) 2023-10-28 04:00:17 +00:00
Hugo C
9dd8e72245 fix: PiP replacing video download button (#1910) 2023-10-28 02:22:04 +02:00
AutumnVN
aae790f1c1 vencordToolbox: correct hover color + oneko (#1913) 2023-10-28 02:21:35 +02:00
AutumnVN
7f17e70697 noSystemBadge: fix (#1914) 2023-10-28 02:20:29 +02:00
Vendicated
b3311c6f12 BetterGifAltText: Fix displaying undefined 2023-10-28 02:19:43 +02:00
Nuckyz
bc09225258 Add missing patches predicates to BetterFolders 2023-10-27 20:10:44 -03:00
Nuckyz
9ce923d4d7 Fix BetterFolders 2023-10-27 19:56:11 -03:00
Nuckyz
7845af0802 Remove hacks to support no longer active versions of Discord 2023-10-27 19:55:39 -03:00
Nuckyz
89672882b9 PermViewer: Fix incorrectly displaying some role names as Unknown Role 2023-10-27 14:33:18 -03:00
Nuckyz
e05c630a54 SHC: Make Chat Input Bar channel list include hidden channels 2023-10-27 14:29:25 -03:00
Nuckyz
38834ef7ac Fix git updater 2023-10-27 13:03:52 -03:00
Luna
98d49af728 Fix git updater for other branches (#1915) 2023-10-27 12:09:39 -03:00
Susheel Thapa
0afe319141 fix typo in multiple files (#1911) 2023-10-27 12:09:38 -03:00
28 changed files with 405 additions and 290 deletions

View File

@ -289,7 +289,7 @@ function runTime(token: string) {
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
}, 1000));
} catch (e) {
console.error("[PUP_DEBUG]", "A fatal error occured");
console.error("[PUP_DEBUG]", "A fatal error occurred");
console.error("[PUP_DEBUG]", e);
process.exit(1);
}

View File

@ -34,6 +34,7 @@ import { patches, PMLogger, startAllPlugins } from "./plugins";
import { localStorage } from "./utils/localStorage";
import { relaunch } from "./utils/native";
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
import { sendTelemetry } from "./utils/telemetry";
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
import { onceReady } from "./webpack";
import { SettingsRouter } from "./webpack/common";
@ -83,6 +84,8 @@ async function init() {
syncSettings();
sendTelemetry();
if (!IS_WEB) {
try {
const isOutdated = await checkForUpdates();

View File

@ -69,7 +69,7 @@ export function addGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback)
* Remove a context menu patch
* @param navId The navId(s) for the context menu(s) to remove the patch
* @param patch The patch to be removed
* @returns Wheter the patch was sucessfully removed from the context menu(s)
* @returns Whether the patch was successfully removed from the context menu(s)
*/
export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> {
const navIds = Array.isArray(navId) ? navId : [navId as string];
@ -82,7 +82,7 @@ export function removeContextMenuPatch<T extends string | Array<string>>(navId:
/**
* Remove a global context menu patch
* @param patch The patch to be removed
* @returns Wheter the patch was sucessfully removed
* @returns Whether the patch was successfully removed
*/
export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean {
return globalPatches.delete(patch);

View File

@ -61,6 +61,8 @@ export interface Settings {
settingsSync: boolean;
settingsSyncVersion: number;
};
telemetry?: boolean; // tri-state, undefined = ask
}
const DefaultSettings: Settings = {
@ -91,7 +93,9 @@ const DefaultSettings: Settings = {
url: "https://api.vencord.dev/",
settingsSync: false,
settingsSyncVersion: 0
}
},
telemetry: undefined
};
try {

View File

@ -46,7 +46,7 @@ function withDispatcher(dispatcher: React.Dispatch<React.SetStateAction<boolean>
if (code === "ENOENT")
var err = `Command \`${path}\` not found.\nPlease install it and try again`;
else {
var err = `An error occured while running \`${cmd}\`:\n`;
var err = `An error occurred while running \`${cmd}\`:\n`;
err += stderr || `Code \`${code}\`. See the console for more info`;
}

View File

@ -21,6 +21,7 @@ import { Settings, useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import DonateButton from "@components/DonateButton";
import { ErrorCard } from "@components/ErrorCard";
import { isMac, isWindows } from "@utils/constants";
import { Margins } from "@utils/margins";
import { identity } from "@utils/misc";
import { relaunch, showItemInFolder } from "@utils/native";
@ -46,9 +47,6 @@ function VencordSettings() {
const donateImage = React.useMemo(() => Math.random() > 0.5 ? DEFAULT_DONATE_IMAGE : SHIGGY_DONATE_IMAGE, []);
const isWindows = navigator.platform.toLowerCase().startsWith("win");
const isMac = navigator.platform.toLowerCase().startsWith("mac");
const Switches: Array<false | {
key: KeysOfType<typeof settings, boolean>;
title: string;
@ -93,6 +91,11 @@ function VencordSettings() {
key: "macosTranslucency",
title: "Enable translucent window",
note: "Requires a full restart"
},
{
key: "telemetry",
title: "Enable Telemetry",
note: "We only gather anonymous telemetry data. All data deleted after 3 days if you opt out."
}
];

View File

@ -49,7 +49,9 @@ async function getRepo() {
async function calculateGitChanges() {
await git("fetch");
const res = await git("log", "HEAD...origin/main", "--pretty=format:%an/%h/%s");
const branch = await git("branch", "--show-current");
const res = await git("log", `HEAD...origin/${branch.stdout.trim()}`, "--pretty=format:%an/%h/%s");
const commits = res.stdout.trim();
return commits ? commits.split("\n").map(line => {

View File

@ -16,56 +16,34 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { findByPropsLazy, findStoreLazy } from "@webpack";
import { i18n, React, useStateFromStores } from "@webpack/common";
import { LazyComponent } from "@utils/react";
import { find, findByPropsLazy } from "@webpack";
import { React, useStateFromStores } from "@webpack/common";
const cl = classNameFactory("vc-bf-");
const classes = findByPropsLazy("sidebar", "guilds");
import { ExpandedGuildFolderStore, settings } from ".";
const Animations = findByPropsLazy("a", "animated", "useTransition");
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")')));
function Guilds(props: {
className: string;
bfGuildFolders: any[];
}) {
// @ts-expect-error
const res = Vencord.Plugins.plugins.BetterFolders.Guilds(props);
// TODO: Make this better
const scrollerProps = res.props.children?.props?.children?.props?.children?.[1]?.props;
if (scrollerProps?.children) {
const servers = scrollerProps.children.find(c => c?.props?.["aria-label"] === i18n.Messages.SERVERS);
if (servers) scrollerProps.children = servers;
}
return res;
}
export default ErrorBoundary.wrap(() => {
export default ErrorBoundary.wrap(guildsBarProps => {
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
const fullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext());
const guilds = document.querySelector(`.${classes.guilds}`);
const visible = !!expandedFolders.size;
const className = cl("folder-sidebar", { fullscreen });
const Sidebar = (
<Guilds
className={classes.guilds}
bfGuildFolders={Array.from(expandedFolders)}
<GuildsBar
{...guildsBarProps}
isBetterFolders={true}
/>
);
if (!guilds || !Settings.plugins.BetterFolders.sidebarAnim)
const visible = !!expandedFolders.size;
const guilds = document.querySelector(guildsBarProps.className.split(" ").map(c => `.${c}`).join(""));
if (!guilds || !settings.store.sidebarAnim) {
return visible
? <div className={className}>{Sidebar}</div>
? <div style={{ display: "flex " }}>{Sidebar}</div>
: null;
}
return (
<Animations.Transition
@ -75,11 +53,13 @@ export default ErrorBoundary.wrap(() => {
leave={{ width: 0 }}
config={{ duration: 200 }}
>
{(style, show) => show && (
<Animations.animated.div style={style} className={className}>
{Sidebar}
</Animations.animated.div>
)}
{(style, show) =>
show && (
<Animations.animated.div style={{ ...style, display: "flex" }}>
{Sidebar}
</Animations.animated.div>
)
}
</Animations.Transition>
);
}, { noop: true });

View File

@ -1,17 +0,0 @@
.vc-bf-folder-sidebar [class*="wrapper-"] > [class*="listItem-"]:first-of-type,
.vc-bf-folder-sidebar [class*="unreadMentionsIndicator"] {
display: none;
}
.vc-bf-folder-sidebar [class*="expandedFolderBackground-"] {
background-color: transparent;
}
.vc-bf-folder-sidebar {
display: flex;
}
.vc-bf-fullscreen {
width: 0 !important;
visibility: hidden;
}

View File

@ -1,177 +0,0 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import "./betterFolders.css";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
import { FluxDispatcher } from "@webpack/common";
import FolderSideBar from "./FolderSideBar";
const GuildsTree = findLazy(m => m.prototype?.convertToFolder);
const GuildFolderStore = findStoreLazy("SortedGuildStore");
const ExpandedFolderStore = findStoreLazy("ExpandedGuildFolderStore");
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
const settings = definePluginSettings({
sidebar: {
type: OptionType.BOOLEAN,
description: "Display servers from folder on dedicated sidebar",
default: true,
},
sidebarAnim: {
type: OptionType.BOOLEAN,
description: "Animate opening the folder sidebar",
default: true,
},
closeAllFolders: {
type: OptionType.BOOLEAN,
description: "Close all folders when selecting a server not in a folder",
default: false,
},
closeAllHomeButton: {
type: OptionType.BOOLEAN,
description: "Close all folders when clicking on the home button",
default: false,
},
closeOthers: {
type: OptionType.BOOLEAN,
description: "Close other folders when opening a folder",
default: false,
},
forceOpen: {
type: OptionType.BOOLEAN,
description: "Force a folder to open when switching to a server of that folder",
default: false,
},
});
export default definePlugin({
name: "BetterFolders",
description: "Shows server folders on dedicated sidebar and adds folder related improvements",
authors: [Devs.juby, Devs.AutumnVN],
patches: [
{
find: '("guildsnav")',
predicate: () => settings.store.sidebar,
replacement: [
{
match: /(\i)\(\){return \i\(\(0,\i\.jsx\)\("div",{className:\i\(\)\.guildSeparator}\)\)}/,
replace: "$&$self.Separator=$1;"
},
// Folder component patch
{
match: /\i\(\(function\(\i,\i,\i\){var \i=\i\.key;return.+\(\i\)},\i\)}\)\)/,
replace: "arguments[0].bfHideServers?null:$&"
},
// BEGIN Guilds component patch
{
match: /(\i)\.themeOverride,(.{15,25}\(function\(\){var \i=)(\i\.\i\.getGuildsTree\(\))/,
replace: "$1.themeOverride,bfPatch=$1.bfGuildFolders,$2bfPatch?$self.getGuildsTree(bfPatch,$3):$3"
},
{
match: /return(\(0,\i\.jsx\))(\(\i,{)(folderNode:\i,setNodeRef:\i\.setNodeRef,draggable:!0,.+},\i\.id\));case/,
replace: "var bfHideServers=typeof bfPatch==='undefined',folder=$1$2bfHideServers,$3;return !bfHideServers&&arguments[1]?[$1($self.Separator,{}),folder]:folder;case"
},
// END
{
match: /\("guildsnav"\);return\(0,\i\.jsx\)\(.{1,6},{navigator:\i,children:\(0,\i\.jsx\)\(/,
replace: "$&$self.Guilds="
}
]
},
{
find: "APPLICATION_LIBRARY,render",
predicate: () => settings.store.sidebar,
replacement: {
match: /(\(0,\i\.jsx\))\(\i\..,{className:\i\(\)\.guilds,themeOverride:\i}\)/,
replace: "$&,$1($self.FolderSideBar,{})"
}
},
{
find: '("guildsnav")',
predicate: () => settings.store.closeAllHomeButton,
replacement: {
match: ",onClick:function(){if(!__OVERLAY__){",
replace: "$&$self.closeFolders();"
}
}
],
settings,
start() {
const getGuildFolder = (id: string) => GuildFolderStore.getGuildFolders().find(f => f.guildIds.includes(id));
FluxDispatcher.subscribe("CHANNEL_SELECT", this.onSwitch = data => {
if (!settings.store.closeAllFolders && !settings.store.forceOpen)
return;
if (this.lastGuildId !== data.guildId) {
this.lastGuildId = data.guildId;
const guildFolder = getGuildFolder(data.guildId);
if (guildFolder?.folderId) {
if (settings.store.forceOpen && !ExpandedFolderStore.isFolderExpanded(guildFolder.folderId))
FolderUtils.toggleGuildFolderExpand(guildFolder.folderId);
} else if (settings.store.closeAllFolders)
this.closeFolders();
}
});
FluxDispatcher.subscribe("TOGGLE_GUILD_FOLDER_EXPAND", this.onToggleFolder = e => {
if (settings.store.closeOthers && !this.dispatching)
FluxDispatcher.wait(() => {
const expandedFolders = ExpandedFolderStore.getExpandedFolders();
if (expandedFolders.size > 1) {
this.dispatching = true;
for (const id of expandedFolders) if (id !== e.folderId)
FolderUtils.toggleGuildFolderExpand(id);
this.dispatching = false;
}
});
});
},
stop() {
FluxDispatcher.unsubscribe("CHANNEL_SELECT", this.onSwitch);
FluxDispatcher.unsubscribe("TOGGLE_GUILD_FOLDER_EXPAND", this.onToggleFolder);
},
FolderSideBar,
getGuildsTree(folders, oldTree) {
const tree = new GuildsTree();
tree.root.children = oldTree.root.children.filter(e => folders.includes(e.id));
tree.nodes = folders.map(id => oldTree.nodes[id]);
return tree;
},
closeFolders() {
for (const id of ExpandedFolderStore.getExpandedFolders())
FolderUtils.toggleGuildFolderExpand(id);
},
});

View File

@ -0,0 +1,236 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findStoreLazy } from "@webpack";
import { FluxDispatcher, i18n } from "@webpack/common";
import FolderSideBar from "./FolderSideBar";
const GuildFolderStore = findStoreLazy("SortedGuildStore");
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
let lastGuildId = null as string | null;
let dispatchingFoldersClose = false;
function getGuildFolder(id: string) {
return GuildFolderStore.getGuildFolders().find(folder => folder.guildIds.includes(id));
}
function closeFolders() {
for (const id of ExpandedGuildFolderStore.getExpandedFolders())
FolderUtils.toggleGuildFolderExpand(id);
}
export const settings = definePluginSettings({
sidebar: {
type: OptionType.BOOLEAN,
description: "Display servers from folder on dedicated sidebar",
restartNeeded: true,
default: true
},
sidebarAnim: {
type: OptionType.BOOLEAN,
description: "Animate opening the folder sidebar",
restartNeeded: true,
default: true
},
closeAllFolders: {
type: OptionType.BOOLEAN,
description: "Close all folders when selecting a server not in a folder",
default: false
},
closeAllHomeButton: {
type: OptionType.BOOLEAN,
description: "Close all folders when clicking on the home button",
restartNeeded: true,
default: false
},
closeOthers: {
type: OptionType.BOOLEAN,
description: "Close other folders when opening a folder",
default: false
},
forceOpen: {
type: OptionType.BOOLEAN,
description: "Force a folder to open when switching to a server of that folder",
default: false
},
keepIcons: {
type: OptionType.BOOLEAN,
description: "Keep showing guild icons in the primary guild bar folder when it's open in the BetterFolders sidebar",
restartNeeded: true,
default: false
}
});
export default definePlugin({
name: "BetterFolders",
description: "Shows server folders on dedicated sidebar and adds folder related improvements",
authors: [Devs.juby, Devs.AutumnVN, Devs.Nuckyz],
settings,
patches: [
{
find: '("guildsnav")',
predicate: () => settings.store.sidebar,
replacement: [
// Create the isBetterFolders variable in the GuildsBar component
{
match: /(?<=let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?)(?=}=\i,)/,
replace: ",isBetterFolders"
},
// 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))'
},
// 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))'
},
// 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
{
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?GUILD:)/,
replace: 'if((typeof isBetterFolders!=="undefined"?isBetterFolders:false)&&$1.parentId==null)return null;'
}
]
},
{
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,"
},
// 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;`
},
// 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)&&'
},
// 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})&&`
}
]
},
{
find: "APPLICATION_LIBRARY,render",
predicate: () => settings.store.sidebar,
replacement: {
// Render the Better Folders sidebar
match: /(?<=({className:\i\.guilds,themeOverride:\i})\))/,
replace: ",$self.FolderSideBar($1)"
}
},
{
find: ".Messages.DISCODO_DISABLED",
predicate: () => settings.store.closeAllHomeButton,
replacement: {
// Close all folders when clicking the home button
match: /(?<=onClick:\(\)=>{)(?=.{0,200}"discodo")/,
replace: "$self.closeFolders();"
}
}
],
flux: {
CHANNEL_SELECT(data) {
if (!settings.store.closeAllFolders && !settings.store.forceOpen)
return;
if (lastGuildId !== data.guildId) {
lastGuildId = data.guildId;
const guildFolder = getGuildFolder(data.guildId);
if (guildFolder?.folderId) {
if (settings.store.forceOpen && !ExpandedGuildFolderStore.isFolderExpanded(guildFolder.folderId)) {
FolderUtils.toggleGuildFolderExpand(guildFolder.folderId);
}
} else if (settings.store.closeAllFolders) {
closeFolders();
}
}
},
TOGGLE_GUILD_FOLDER_EXPAND(data) {
if (settings.store.closeOthers && !dispatchingFoldersClose) {
dispatchingFoldersClose = true;
FluxDispatcher.wait(() => {
const expandedFolders = ExpandedGuildFolderStore.getExpandedFolders();
if (expandedFolders.size > 1) {
for (const id of expandedFolders) if (id !== data.folderId)
FolderUtils.toggleGuildFolderExpand(id);
}
dispatchingFoldersClose = false;
});
}
}
},
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
return child => {
if (isBetterFolders) {
return child?.props?.["aria-label"] === i18n.Messages.SERVERS;
}
return true;
};
},
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
return child => {
if (isBetterFolders) {
return "onScroll" in child.props;
}
return true;
};
},
FolderSideBar: guildsBarProps => <FolderSideBar {...guildsBarProps} />,
closeFolders
});

View File

@ -45,7 +45,8 @@ export default definePlugin({
],
altify(props: any) {
if (props.alt && props.alt !== "GIF") return props.alt;
props.alt ??= "GIF";
if (props.alt !== "GIF") return props.alt;
let url: string = props.original || props.src;
try {

View File

@ -395,7 +395,7 @@ export default definePlugin({
return (
<>
<Forms.FormText>
Go to <Link href="https://discord.com/developers/applications">Discord Deverloper Portal</Link> to create an application and
Go to <Link href="https://discord.com/developers/applications">Discord Developer Portal</Link> to create an application and
get the application ID.
</Forms.FormText>
<Forms.FormText>

View File

@ -19,7 +19,7 @@
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { ErrorCard } from "@components/ErrorCard";
import { Devs } from "@utils/constants";
import { Devs, isMac } from "@utils/constants";
import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
@ -96,9 +96,8 @@ export default definePlugin({
],
settingsAboutComponent: () => {
const isMacOS = navigator.platform.includes("Mac");
const modKey = isMacOS ? "cmd" : "ctrl";
const altKey = isMacOS ? "opt" : "alt";
const modKey = isMac ? "cmd" : "ctrl";
const altKey = isMac ? "opt" : "alt";
return (
<React.Fragment>
<Forms.FormTitle tag="h3">More Information</Forms.FormTitle>

View File

@ -25,15 +25,15 @@ export default definePlugin({
authors: [Devs.rushii],
patches: [
{
find: "setSystemTrayApplications:function",
find: ",setSystemTrayApplications",
replacement: [
{
match: /setBadge:function.+?},/,
replace: "setBadge:function(){},"
match: /setBadge\(\i\).+?},/,
replace: "setBadge(){},"
},
{
match: /setSystemTrayIcon:function.+?},/,
replace: "setSystemTrayIcon:function(){},"
match: /setSystemTrayIcon\(\i\).+?},/,
replace: "setSystemTrayIcon(){},"
}
]
}

View File

@ -135,9 +135,9 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
<Text variant="text-md/normal">
{
permission.type === PermissionType.Role
? role?.name || "Unknown Role"
? role?.name ?? "Unknown Role"
: permission.type === PermissionType.User
? (user && getUniqueUsername(user)) || "Unknown User"
? (user && getUniqueUsername(user)) ?? "Unknown User"
: (
<Flex style={{ gap: "0.2em", justifyItems: "center" }}>
@owner

View File

@ -28,8 +28,8 @@ export default definePlugin({
{
find: ".nonMediaAttachment]",
replacement: {
match: /\.nonMediaAttachment\].{0,10}children:\[\S/,
replace: "$&&&$self.renderPiPButton(),"
match: /\.nonMediaAttachment\].{0,10}children:\[(\S)/,
replace: "$&,$1&&$self.renderPiPButton(),"
},
},
],

View File

@ -17,7 +17,7 @@
*/
import { definePluginSettings, Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { Devs, isMac } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common";
@ -25,7 +25,6 @@ import { Message } from "discord-types/general";
const Kangaroo = findByPropsLazy("jumpToMessage");
const isMac = navigator.platform.includes("Mac"); // bruh
let replyIdx = -1;
let editIdx = -1;

View File

@ -414,6 +414,22 @@ export default definePlugin({
}
]
},
{
// Make the chat input bar channel list contain hidden channels
find: ",queryStaticRouteChannels(",
replacement: [
{
// Make the getChannels call to GuildChannelStore return hidden channels
match: /(?<=queryChannels\(\i\){.+?getChannels\(\i)(?=\))/,
replace: ",true"
},
{
// Avoid filtering out hidden channels from the channel list
match: /(?<=queryChannels\(\i\){.+?isGuildChannelType\)\((\i)\.type\))(?=&&!\i\.\i\.can\()/,
replace: "&&!$self.isHiddenChannel($1)"
}
]
},
{
find: "\"^/guild-stages/(\\\\d+)(?:/)?(\\\\d+)?\"",
replacement: {

View File

@ -2,7 +2,7 @@
padding: 0.375rem 0.5rem;
border-bottom: 1px solid var(--background-modifier-accent);
--vc-spotify-green: #1db954; /* so cusotm themes can easily change it */
--vc-spotify-green: #1db954; /* so custom themes can easily change it */
}
.theme-light #vc-spotify-player {
@ -167,7 +167,7 @@
}
#vc-spotify-progress-bar > [class^="slider"] [class^="grabber"] {
/* these importants are neccessary, it applies a width and height through inline styles */
/* these importants are necessary, it applies a width and height through inline styles */
height: 10px !important;
width: 10px !important;
background-color: var(--interactive-normal);

View File

@ -1,12 +1,16 @@
.vc-toolbox-btn,
.vc-toolbox-btn svg {
.vc-toolbox-btn>svg {
-webkit-app-region: no-drag;
}
.vc-toolbox-btn svg {
.vc-toolbox-btn>svg {
color: var(--interactive-normal);
}
:is(.vc-toolbox-btn:hover, .vc-toolbox-btn[class*="selected"]) svg {
.vc-toolbox-btn[class*="selected"]>svg {
color: var(--interactive-active);
}
.vc-toolbox-btn:hover>svg {
color: var(--interactive-hover);
}

View File

@ -89,10 +89,10 @@ function VencordPopout(onClose: () => void) {
);
}
function VencordPopoutIcon() {
function VencordPopoutIcon(isShown: boolean) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width={24} height={24}>
<path fill="currentColor" d="M53 10h7v1h-1v1h-1v1h-1v1h-1v1h-1v1h5v1h-7v-1h1v-1h1v-1h1v-1h1v-1h1v-1h-5m-43 1v32h2v2h2v2h2v2h2v2h2v2h2v2h2v2h2v2h8v-2h2V46h-2v2h-2v2h-4v-2h-2v-2h-2v-2h-2v-2h-2v-2h-2V12m24 0v27h-2v3h4v-6h2v-2h4V12m13 2h5v1h-1v1h-1v1h-1v1h3v1h-5v-1h1v-1h1v-1h1v-1h-3m8 5h1v5h1v-1h1v1h-1v1h1v-1h1v1h-1v3h-1v1h-2v1h-1v1h1v-1h2v-1h1v2h-1v1h-2v1h-1v-1h-1v1h-6v-1h-1v-1h-1v-2h1v1h2v1h3v1h1v-1h-1v-1h-3v-1h-4v-4h1v-2h1v-1h1v-1h1v2h1v1h1v-1h1v1h-1v1h2v-2h1v-2h1v-1h1m-13 4h2v1h-1v4h1v2h1v1h1v1h1v1h4v1h-6v-1h-6v-1h-1v-5h1v-1h1v-2h2m17 3h1v3h-1v1h-1v1h-1v2h-2v-2h2v-1h1v-1h1m1 0h1v3h-1v1h-2v-1h1v-1h1m-30 2v8h-8v32h8v8h32v-8h8v-8H70v8H54V44h16v8h16v-8h-8v-8h-1v1h-7v-1h-2v1h-8v-1" />
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" width={24} height={24}>
<path fill="currentColor" d={isShown ? "M9 0h1v1h1v2h1v2h3V3h1V1h1V0h1v2h1v2h1v7h-1v-1h-3V9h1V6h-1v4h-3v1h1v-1h2v1h3v1h-1v1h-3v2h1v1h1v1h1v3h-1v4h-2v-1h-1v-4h-1v4h-1v1h-2v-4H9v-3h1v-1h1v-1h1v-2H9v-1H8v-1h3V6h-1v3h1v1H8v1H7V4h1V2h1M5 19h2v1h1v1h1v3H4v-1h2v-1H4v-2h1m15-1h2v1h1v2h-2v1h2v1h-5v-3h1v-1h1m4 3h4v1h-4" : "M0 0h7v1H6v1H5v1H4v1H3v1H2v1h5v1H0V6h1V5h1V4h1V3h1V2h1V1H0m13 2h5v1h-1v1h-1v1h-1v1h3v1h-5V7h1V6h1V5h1V4h-3m8 5h1v5h1v-1h1v1h-1v1h1v-1h1v1h-1v3h-1v1h-2v1h-1v1h1v-1h2v-1h1v2h-1v1h-2v1h-1v-1h-1v1h-6v-1h-1v-1h-1v-2h1v1h2v1h3v1h1v-1h-1v-1h-3v-1h-4v-4h1v-2h1v-1h1v-1h1v2h1v1h1v-1h1v1h-1v1h2v-2h1v-2h1v-1h1M8 14h2v1H9v4h1v2h1v1h1v1h1v1h4v1h-6v-1H5v-1H4v-5h1v-1h1v-2h2m17 3h1v3h-1v1h-1v1h-1v2h-2v-2h2v-1h1v-1h1m1 0h1v3h-1v1h-2v-1h1v-1h1"} />
</svg>
);
}
@ -114,7 +114,7 @@ function VencordPopoutButton() {
className="vc-toolbox-btn"
onClick={() => setShow(v => !v)}
tooltip={isShown ? null : "Vencord Toolbox"}
icon={VencordPopoutIcon}
icon={() => VencordPopoutIcon(isShown)}
selected={isShown}
/>
)}

View File

@ -389,3 +389,10 @@ export const DevsById = /* #__PURE__*/ (() =>
.map(([_, v]) => [v.id, v] as const)
))
)() as Record<string, Dev>;
const { platform } = navigator;
export const isWindows = platform.startsWith("Win");
export const isMac = platform.startsWith("Mac");
export const isLinux = platform.startsWith("Linux");

69
src/utils/telemetry.tsx Normal file
View File

@ -0,0 +1,69 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Settings } from "@api/Settings";
import { Alerts } from "@webpack/common";
import { isPluginEnabled } from "../plugins";
import { Plugins } from "../Vencord";
import { isLinux, isMac, isWindows } from "./constants";
export function sendTelemetry() {
// TODO: READ THIS CHECK BEFORE RELEASING!!
// if (IS_DEV) return; // don't send on devbuilds, usually contains incorrect data
// if we have not yet told the user about the telemetry's existence, or they haven't agreed at all, DON'T send a
// probe now, but tell them and then let them decide if they want to opt in or not.
if (Settings.telemetry === undefined) {
Alerts.show({
title: "Telemetry Notice",
body: <>
<p>
Vencord has a telemetry feature that sends anonymous data to us, which we use to improve the mod. We
gather your operating system, the version of Vencord you're using and a list of enabled plugins, and
we can use this data to help improve it for yourself and everyone else.
</p>
<p>
If you don't want this, that's okay! We haven't sent anything yet. Please decide if you want to allow
us to gather a little bit of data. You can change this setting at any time in the future. If you
grant consent, we will start sending the data above the next time you reload or restart Discord.
</p>
</>,
confirmText: "Yes, that's fine",
cancelText: "No, I don't want that",
onConfirm() {
Settings.telemetry = true;
},
onCancel() {
Settings.telemetry = false;
}
});
return;
}
// if it's disabled in settings, obviously don't do anything
if (!Settings.telemetry) return;
const activePluginsList = Object.keys(Plugins.plugins)
.filter(p => isPluginEnabled(p));
let operatingSystem = "Unknown";
if (isWindows) operatingSystem = "Windows";
else if (isMac) operatingSystem = "macOS";
else if (isLinux) operatingSystem = "Linux";
const data = {
version: VERSION,
plugins: activePluginsList,
operatingSystem
};
navigator.sendBeacon("https://api.vencord.dev/v1/telemetry", JSON.stringify(data));
}

View File

@ -269,7 +269,7 @@ export interface DefinedSettings<
store: SettingsStore<Def> & PrivateSettings;
/**
* React hook for getting the settings for this plugin
* @param filter optional filter to avoid rerenders for irrelavent settings
* @param filter optional filter to avoid rerenders for irrelevent settings
*/
use<F extends Extract<keyof Def | keyof PrivateSettings, string>>(filter?: F[]): Pick<SettingsStore<Def> & PrivateSettings, F>;
/** Definitions of each setting */

View File

@ -20,7 +20,7 @@ import { proxyLazy } from "@utils/lazy";
import type * as Stores from "discord-types/stores";
// eslint-disable-next-line path-alias/no-relative
import { filters, findByCode, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
import { filters, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
import { waitForStore } from "./internal";
import * as t from "./types/stores";
@ -84,14 +84,7 @@ export const useStateFromStores: <T>(
idk?: any,
isEqual?: (old: T, newer: T) => boolean
) => T
// FIXME: hack to support old stable and new canary
= proxyLazy(() => {
try {
return findByProps("useStateFromStores").useStateFromStores;
} catch {
return findByCode('("useStateFromStores")');
}
});
= proxyLazy(() => findByProps("useStateFromStores").useStateFromStores);
waitForStore("DraftStore", s => DraftStore = s);
waitForStore("UserStore", s => UserStore = s);

View File

@ -136,11 +136,4 @@ waitFor("parseTopic", m => Parser = m);
export let SettingsRouter: any;
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
// FIXME: hack to support old stable and new canary
export const PermissionsBits: t.PermissionsBits = proxyLazy(() => {
try {
return find(m => m.Permissions?.ADMINISTRATOR).Permissions;
} catch {
return find(m => typeof m.ADMINISTRATOR === "bigint");
}
});
export const PermissionsBits: t.PermissionsBits = proxyLazy(() => find(m => typeof m.Permissions?.ADMINISTRATOR === "bigint").Permissions);

View File

@ -29,7 +29,7 @@ let webpackChunk: any[];
const logger = new Logger("WebpackInterceptor", "#8caaee");
if (window[WEBPACK_CHUNK]) {
logger.info(`Patching ${WEBPACK_CHUNK}.push (was already existant, likely from cache!)`);
logger.info(`Patching ${WEBPACK_CHUNK}.push (was already existent, likely from cache!)`);
_initWebpack(window[WEBPACK_CHUNK]);
patchPush(window[WEBPACK_CHUNK]);
} else {