Compare commits
17 Commits
v1.6.1
...
feat/telem
Author | SHA1 | Date | |
---|---|---|---|
|
5d1736d020 | ||
|
3de02708a6 | ||
|
49c331fcc9 | ||
|
ba53acdca7 | ||
|
bfb48b4faf | ||
|
9dd8e72245 | ||
|
aae790f1c1 | ||
|
7f17e70697 | ||
|
b3311c6f12 | ||
|
bc09225258 | ||
|
9ce923d4d7 | ||
|
7845af0802 | ||
|
89672882b9 | ||
|
e05c630a54 | ||
|
38834ef7ac | ||
|
98d49af728 | ||
|
0afe319141 |
@ -289,7 +289,7 @@ function runTime(token: string) {
|
|||||||
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
|
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
|
||||||
}, 1000));
|
}, 1000));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[PUP_DEBUG]", "A fatal error occured");
|
console.error("[PUP_DEBUG]", "A fatal error occurred");
|
||||||
console.error("[PUP_DEBUG]", e);
|
console.error("[PUP_DEBUG]", e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import { patches, PMLogger, startAllPlugins } from "./plugins";
|
|||||||
import { localStorage } from "./utils/localStorage";
|
import { localStorage } from "./utils/localStorage";
|
||||||
import { relaunch } from "./utils/native";
|
import { relaunch } from "./utils/native";
|
||||||
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
|
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
|
||||||
|
import { sendTelemetry } from "./utils/telemetry";
|
||||||
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
|
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
|
||||||
import { onceReady } from "./webpack";
|
import { onceReady } from "./webpack";
|
||||||
import { SettingsRouter } from "./webpack/common";
|
import { SettingsRouter } from "./webpack/common";
|
||||||
@ -83,6 +84,8 @@ async function init() {
|
|||||||
|
|
||||||
syncSettings();
|
syncSettings();
|
||||||
|
|
||||||
|
sendTelemetry();
|
||||||
|
|
||||||
if (!IS_WEB) {
|
if (!IS_WEB) {
|
||||||
try {
|
try {
|
||||||
const isOutdated = await checkForUpdates();
|
const isOutdated = await checkForUpdates();
|
||||||
|
@ -69,7 +69,7 @@ export function addGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback)
|
|||||||
* Remove a context menu patch
|
* Remove a context menu patch
|
||||||
* @param navId The navId(s) for the context menu(s) to remove the patch
|
* @param navId The navId(s) for the context menu(s) to remove the patch
|
||||||
* @param patch The patch to be removed
|
* @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> {
|
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];
|
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
|
* Remove a global context menu patch
|
||||||
* @param patch The patch to be removed
|
* @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 {
|
export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean {
|
||||||
return globalPatches.delete(patch);
|
return globalPatches.delete(patch);
|
||||||
|
@ -61,6 +61,8 @@ export interface Settings {
|
|||||||
settingsSync: boolean;
|
settingsSync: boolean;
|
||||||
settingsSyncVersion: number;
|
settingsSyncVersion: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
telemetry?: boolean; // tri-state, undefined = ask
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultSettings: Settings = {
|
const DefaultSettings: Settings = {
|
||||||
@ -91,7 +93,9 @@ const DefaultSettings: Settings = {
|
|||||||
url: "https://api.vencord.dev/",
|
url: "https://api.vencord.dev/",
|
||||||
settingsSync: false,
|
settingsSync: false,
|
||||||
settingsSyncVersion: 0
|
settingsSyncVersion: 0
|
||||||
}
|
},
|
||||||
|
|
||||||
|
telemetry: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -46,7 +46,7 @@ function withDispatcher(dispatcher: React.Dispatch<React.SetStateAction<boolean>
|
|||||||
if (code === "ENOENT")
|
if (code === "ENOENT")
|
||||||
var err = `Command \`${path}\` not found.\nPlease install it and try again`;
|
var err = `Command \`${path}\` not found.\nPlease install it and try again`;
|
||||||
else {
|
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`;
|
err += stderr || `Code \`${code}\`. See the console for more info`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import { Settings, useSettings } from "@api/Settings";
|
|||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import DonateButton from "@components/DonateButton";
|
import DonateButton from "@components/DonateButton";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
|
import { isMac, isWindows } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { identity } from "@utils/misc";
|
import { identity } from "@utils/misc";
|
||||||
import { relaunch, showItemInFolder } from "@utils/native";
|
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 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 | {
|
const Switches: Array<false | {
|
||||||
key: KeysOfType<typeof settings, boolean>;
|
key: KeysOfType<typeof settings, boolean>;
|
||||||
title: string;
|
title: string;
|
||||||
@ -93,6 +91,11 @@ function VencordSettings() {
|
|||||||
key: "macosTranslucency",
|
key: "macosTranslucency",
|
||||||
title: "Enable translucent window",
|
title: "Enable translucent window",
|
||||||
note: "Requires a full restart"
|
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."
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -49,7 +49,9 @@ async function getRepo() {
|
|||||||
async function calculateGitChanges() {
|
async function calculateGitChanges() {
|
||||||
await git("fetch");
|
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();
|
const commits = res.stdout.trim();
|
||||||
return commits ? commits.split("\n").map(line => {
|
return commits ? commits.split("\n").map(line => {
|
||||||
|
@ -16,56 +16,34 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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 ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
import { LazyComponent } from "@utils/react";
|
||||||
import { i18n, React, useStateFromStores } from "@webpack/common";
|
import { find, findByPropsLazy } from "@webpack";
|
||||||
|
import { React, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
const cl = classNameFactory("vc-bf-");
|
import { ExpandedGuildFolderStore, settings } from ".";
|
||||||
const classes = findByPropsLazy("sidebar", "guilds");
|
|
||||||
|
|
||||||
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
||||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")')));
|
||||||
const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
|
||||||
|
|
||||||
function Guilds(props: {
|
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||||
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(() => {
|
|
||||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
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 = (
|
const Sidebar = (
|
||||||
<Guilds
|
<GuildsBar
|
||||||
className={classes.guilds}
|
{...guildsBarProps}
|
||||||
bfGuildFolders={Array.from(expandedFolders)}
|
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
|
return visible
|
||||||
? <div className={className}>{Sidebar}</div>
|
? <div style={{ display: "flex " }}>{Sidebar}</div>
|
||||||
: null;
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animations.Transition
|
<Animations.Transition
|
||||||
@ -75,11 +53,13 @@ export default ErrorBoundary.wrap(() => {
|
|||||||
leave={{ width: 0 }}
|
leave={{ width: 0 }}
|
||||||
config={{ duration: 200 }}
|
config={{ duration: 200 }}
|
||||||
>
|
>
|
||||||
{(style, show) => show && (
|
{(style, show) =>
|
||||||
<Animations.animated.div style={style} className={className}>
|
show && (
|
||||||
|
<Animations.animated.div style={{ ...style, display: "flex" }}>
|
||||||
{Sidebar}
|
{Sidebar}
|
||||||
</Animations.animated.div>
|
</Animations.animated.div>
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
</Animations.Transition>
|
</Animations.Transition>
|
||||||
);
|
);
|
||||||
}, { noop: true });
|
}, { noop: true });
|
||||||
|
@ -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;
|
|
||||||
}
|
|
@ -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);
|
|
||||||
},
|
|
||||||
});
|
|
236
src/plugins/betterFolders/index.tsx
Normal file
236
src/plugins/betterFolders/index.tsx
Normal 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
|
||||||
|
});
|
@ -45,7 +45,8 @@ export default definePlugin({
|
|||||||
],
|
],
|
||||||
|
|
||||||
altify(props: any) {
|
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;
|
let url: string = props.original || props.src;
|
||||||
try {
|
try {
|
||||||
|
@ -395,7 +395,7 @@ export default definePlugin({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Forms.FormText>
|
<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.
|
get the application ID.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs, isMac } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
@ -96,9 +96,8 @@ export default definePlugin({
|
|||||||
],
|
],
|
||||||
|
|
||||||
settingsAboutComponent: () => {
|
settingsAboutComponent: () => {
|
||||||
const isMacOS = navigator.platform.includes("Mac");
|
const modKey = isMac ? "cmd" : "ctrl";
|
||||||
const modKey = isMacOS ? "cmd" : "ctrl";
|
const altKey = isMac ? "opt" : "alt";
|
||||||
const altKey = isMacOS ? "opt" : "alt";
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Forms.FormTitle tag="h3">More Information</Forms.FormTitle>
|
<Forms.FormTitle tag="h3">More Information</Forms.FormTitle>
|
||||||
|
@ -25,15 +25,15 @@ export default definePlugin({
|
|||||||
authors: [Devs.rushii],
|
authors: [Devs.rushii],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "setSystemTrayApplications:function",
|
find: ",setSystemTrayApplications",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /setBadge:function.+?},/,
|
match: /setBadge\(\i\).+?},/,
|
||||||
replace: "setBadge:function(){},"
|
replace: "setBadge(){},"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /setSystemTrayIcon:function.+?},/,
|
match: /setSystemTrayIcon\(\i\).+?},/,
|
||||||
replace: "setSystemTrayIcon:function(){},"
|
replace: "setSystemTrayIcon(){},"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -135,9 +135,9 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||||||
<Text variant="text-md/normal">
|
<Text variant="text-md/normal">
|
||||||
{
|
{
|
||||||
permission.type === PermissionType.Role
|
permission.type === PermissionType.Role
|
||||||
? role?.name || "Unknown Role"
|
? role?.name ?? "Unknown Role"
|
||||||
: permission.type === PermissionType.User
|
: permission.type === PermissionType.User
|
||||||
? (user && getUniqueUsername(user)) || "Unknown User"
|
? (user && getUniqueUsername(user)) ?? "Unknown User"
|
||||||
: (
|
: (
|
||||||
<Flex style={{ gap: "0.2em", justifyItems: "center" }}>
|
<Flex style={{ gap: "0.2em", justifyItems: "center" }}>
|
||||||
@owner
|
@owner
|
||||||
|
@ -28,8 +28,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".nonMediaAttachment]",
|
find: ".nonMediaAttachment]",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.nonMediaAttachment\].{0,10}children:\[\S/,
|
match: /\.nonMediaAttachment\].{0,10}children:\[(\S)/,
|
||||||
replace: "$&&&$self.renderPiPButton(),"
|
replace: "$&,$1&&$self.renderPiPButton(),"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { definePluginSettings, Settings } from "@api/Settings";
|
import { definePluginSettings, Settings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs, isMac } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
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 Kangaroo = findByPropsLazy("jumpToMessage");
|
||||||
|
|
||||||
const isMac = navigator.platform.includes("Mac"); // bruh
|
|
||||||
let replyIdx = -1;
|
let replyIdx = -1;
|
||||||
let editIdx = -1;
|
let editIdx = -1;
|
||||||
|
|
||||||
|
@ -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+)?\"",
|
find: "\"^/guild-stages/(\\\\d+)(?:/)?(\\\\d+)?\"",
|
||||||
replacement: {
|
replacement: {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
padding: 0.375rem 0.5rem;
|
padding: 0.375rem 0.5rem;
|
||||||
border-bottom: 1px solid var(--background-modifier-accent);
|
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 {
|
.theme-light #vc-spotify-player {
|
||||||
@ -167,7 +167,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#vc-spotify-progress-bar > [class^="slider"] [class^="grabber"] {
|
#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;
|
height: 10px !important;
|
||||||
width: 10px !important;
|
width: 10px !important;
|
||||||
background-color: var(--interactive-normal);
|
background-color: var(--interactive-normal);
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
.vc-toolbox-btn,
|
.vc-toolbox-btn,
|
||||||
.vc-toolbox-btn svg {
|
.vc-toolbox-btn>svg {
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-toolbox-btn svg {
|
.vc-toolbox-btn>svg {
|
||||||
color: var(--interactive-normal);
|
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);
|
color: var(--interactive-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-toolbox-btn:hover>svg {
|
||||||
|
color: var(--interactive-hover);
|
||||||
|
}
|
||||||
|
@ -89,10 +89,10 @@ function VencordPopout(onClose: () => void) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function VencordPopoutIcon() {
|
function VencordPopoutIcon(isShown: boolean) {
|
||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width={24} height={24}>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" 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" />
|
<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>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ function VencordPopoutButton() {
|
|||||||
className="vc-toolbox-btn"
|
className="vc-toolbox-btn"
|
||||||
onClick={() => setShow(v => !v)}
|
onClick={() => setShow(v => !v)}
|
||||||
tooltip={isShown ? null : "Vencord Toolbox"}
|
tooltip={isShown ? null : "Vencord Toolbox"}
|
||||||
icon={VencordPopoutIcon}
|
icon={() => VencordPopoutIcon(isShown)}
|
||||||
selected={isShown}
|
selected={isShown}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -389,3 +389,10 @@ export const DevsById = /* #__PURE__*/ (() =>
|
|||||||
.map(([_, v]) => [v.id, v] as const)
|
.map(([_, v]) => [v.id, v] as const)
|
||||||
))
|
))
|
||||||
)() as Record<string, Dev>;
|
)() 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
69
src/utils/telemetry.tsx
Normal 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));
|
||||||
|
}
|
@ -269,7 +269,7 @@ export interface DefinedSettings<
|
|||||||
store: SettingsStore<Def> & PrivateSettings;
|
store: SettingsStore<Def> & PrivateSettings;
|
||||||
/**
|
/**
|
||||||
* React hook for getting the settings for this plugin
|
* 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>;
|
use<F extends Extract<keyof Def | keyof PrivateSettings, string>>(filter?: F[]): Pick<SettingsStore<Def> & PrivateSettings, F>;
|
||||||
/** Definitions of each setting */
|
/** Definitions of each setting */
|
||||||
|
@ -20,7 +20,7 @@ import { proxyLazy } from "@utils/lazy";
|
|||||||
import type * as Stores from "discord-types/stores";
|
import type * as Stores from "discord-types/stores";
|
||||||
|
|
||||||
// eslint-disable-next-line path-alias/no-relative
|
// 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 { waitForStore } from "./internal";
|
||||||
import * as t from "./types/stores";
|
import * as t from "./types/stores";
|
||||||
|
|
||||||
@ -84,14 +84,7 @@ export const useStateFromStores: <T>(
|
|||||||
idk?: any,
|
idk?: any,
|
||||||
isEqual?: (old: T, newer: T) => boolean
|
isEqual?: (old: T, newer: T) => boolean
|
||||||
) => T
|
) => T
|
||||||
// FIXME: hack to support old stable and new canary
|
= proxyLazy(() => findByProps("useStateFromStores").useStateFromStores);
|
||||||
= proxyLazy(() => {
|
|
||||||
try {
|
|
||||||
return findByProps("useStateFromStores").useStateFromStores;
|
|
||||||
} catch {
|
|
||||||
return findByCode('("useStateFromStores")');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
waitForStore("DraftStore", s => DraftStore = s);
|
waitForStore("DraftStore", s => DraftStore = s);
|
||||||
waitForStore("UserStore", s => UserStore = s);
|
waitForStore("UserStore", s => UserStore = s);
|
||||||
|
@ -136,11 +136,4 @@ waitFor("parseTopic", m => Parser = m);
|
|||||||
export let SettingsRouter: any;
|
export let SettingsRouter: any;
|
||||||
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
|
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
|
||||||
|
|
||||||
// FIXME: hack to support old stable and new canary
|
export const PermissionsBits: t.PermissionsBits = proxyLazy(() => find(m => typeof m.Permissions?.ADMINISTRATOR === "bigint").Permissions);
|
||||||
export const PermissionsBits: t.PermissionsBits = proxyLazy(() => {
|
|
||||||
try {
|
|
||||||
return find(m => m.Permissions?.ADMINISTRATOR).Permissions;
|
|
||||||
} catch {
|
|
||||||
return find(m => typeof m.ADMINISTRATOR === "bigint");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
@ -29,7 +29,7 @@ let webpackChunk: any[];
|
|||||||
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
const logger = new Logger("WebpackInterceptor", "#8caaee");
|
||||||
|
|
||||||
if (window[WEBPACK_CHUNK]) {
|
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]);
|
_initWebpack(window[WEBPACK_CHUNK]);
|
||||||
patchPush(window[WEBPACK_CHUNK]);
|
patchPush(window[WEBPACK_CHUNK]);
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user