Compare commits
29 Commits
serverProf
...
v1.4.7
Author | SHA1 | Date | |
---|---|---|---|
|
25a1d934c6 | ||
|
620319a4bc | ||
|
4c9996d620 | ||
|
885c75fdaa | ||
|
f2a22c5e57 | ||
|
5e3a485edc | ||
|
452bf72e56 | ||
|
afa47addd7 | ||
|
8ecee3d09f | ||
|
d2aac5edc5 | ||
|
fc45510053 | ||
|
77bfaf38a5 | ||
|
a4cadc03d8 | ||
|
315fcf1972 | ||
|
f1b3b3c0a3 | ||
|
4c805d08be | ||
|
9b987d1e56 | ||
|
774318d583 | ||
|
7d954f9ade | ||
|
860d6edc7b | ||
|
223b0366c6 | ||
|
69cb7593eb | ||
|
8614e17633 | ||
|
aecd9d8fda | ||
|
faeb4fb585 | ||
|
0d18b44ba7 | ||
|
d671bd65ac | ||
|
8e952c630b | ||
|
2c758ccdf8 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
|||||||
run: pnpm build --standalone
|
run: pnpm build --standalone
|
||||||
|
|
||||||
- name: Generate plugin list
|
- name: Generate plugin list
|
||||||
run: pnpm generatePluginJson dist/plugins.json
|
run: pnpm generatePluginJson dist/plugins.json dist/plugin-readmes.json
|
||||||
|
|
||||||
- name: Clean up obsolete files
|
- name: Clean up obsolete files
|
||||||
run: |
|
run: |
|
||||||
|
@ -65,7 +65,7 @@ Also pay attention to the following:
|
|||||||
- Match any but a guaranteed terminating character: `[^;]+`, for example to match the entire assigned value in `var a=b||c||func();`,
|
- Match any but a guaranteed terminating character: `[^;]+`, for example to match the entire assigned value in `var a=b||c||func();`,
|
||||||
`var .{1,2}=([^;]+);`
|
`var .{1,2}=([^;]+);`
|
||||||
- If you don't care about that part, just match a bunch of chars: `.{0,50}`, for example to extract the variable "b" in `createElement("div",{a:"foo",c:"bar"},b)`, `createElement\("div".{0,30},(.{1,2})\),`. Note the `.{0,30}`, this is essentially the same as `.+`, but safer as you can't end up accidently eating thousands of characters
|
- If you don't care about that part, just match a bunch of chars: `.{0,50}`, for example to extract the variable "b" in `createElement("div",{a:"foo",c:"bar"},b)`, `createElement\("div".{0,30},(.{1,2})\),`. Note the `.{0,30}`, this is essentially the same as `.+`, but safer as you can't end up accidently eating thousands of characters
|
||||||
- Additionally, as you might have noticed, all of the appove approaches use regex groups (`(...)`) to capture the variable name. You can then use those groups in your replacement to access those variables dynamically
|
- Additionally, as you might have noticed, all of the above approaches use regex groups (`(...)`) to capture the variable name. You can then use those groups in your replacement to access those variables dynamically
|
||||||
|
|
||||||
#### "replace"
|
#### "replace"
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ Then fully close Discord from your taskbar or task manager, and restart it. Venc
|
|||||||
|
|
||||||
If you're using Discord already, go into the `Updater` tab in settings.
|
If you're using Discord already, go into the `Updater` tab in settings.
|
||||||
|
|
||||||
Sometimes it may be neccessary to manually update if the GUI updater fails.
|
Sometimes it may be necessary to manually update if the GUI updater fails.
|
||||||
|
|
||||||
To pull latest changes:
|
To pull latest changes:
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.4.5",
|
"version": "1.4.7",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
@ -165,7 +165,11 @@ async function parseFile(fileName: string) {
|
|||||||
data.target = target as any;
|
data.target = target as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
let readme = "";
|
||||||
|
try {
|
||||||
|
readme = readFileSync(join(fileName, "..", "README.md"), "utf-8");
|
||||||
|
} catch { }
|
||||||
|
return [data, readme] as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw fail("no default export called 'definePlugin' found");
|
throw fail("no default export called 'definePlugin' found");
|
||||||
@ -194,18 +198,24 @@ function isPluginFile({ name }: { name: string; }) {
|
|||||||
(async () => {
|
(async () => {
|
||||||
parseDevs();
|
parseDevs();
|
||||||
|
|
||||||
const plugins = ["src/plugins", "src/plugins/_core"].flatMap(dir =>
|
const plugins = [] as PluginData[];
|
||||||
|
const readmes = {} as Record<string, string>;
|
||||||
|
|
||||||
|
await Promise.all(["src/plugins", "src/plugins/_core"].flatMap(dir =>
|
||||||
readdirSync(dir, { withFileTypes: true })
|
readdirSync(dir, { withFileTypes: true })
|
||||||
.filter(isPluginFile)
|
.filter(isPluginFile)
|
||||||
.map(async dirent =>
|
.map(async dirent => {
|
||||||
parseFile(await getEntryPoint(dir, dirent))
|
const [data, readme] = await parseFile(await getEntryPoint(dir, dirent));
|
||||||
)
|
plugins.push(data);
|
||||||
);
|
if (readme) readmes[data.name] = readme;
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
const data = JSON.stringify(await Promise.all(plugins));
|
const data = JSON.stringify(plugins);
|
||||||
|
|
||||||
if (process.argv.length > 2) {
|
if (process.argv.length > 3) {
|
||||||
writeFileSync(process.argv[2], data);
|
writeFileSync(process.argv[2], data);
|
||||||
|
writeFileSync(process.argv[3], JSON.stringify(readmes));
|
||||||
} else {
|
} else {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ page.on("console", async e => {
|
|||||||
plugin,
|
plugin,
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
match: regex,
|
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"),
|
||||||
error: cause
|
error: cause
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
21
src/components/CodeBlock.tsx
Normal file
21
src/components/CodeBlock.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
import { Parser } from "@webpack/common";
|
||||||
|
|
||||||
|
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders code in a Discord codeblock
|
||||||
|
*/
|
||||||
|
export function CodeBlock(props: { content?: string, lang: string; }) {
|
||||||
|
return (
|
||||||
|
<div className={CodeContainerClasses.markup}>
|
||||||
|
{Parser.defaultRules.codeBlock.react(props, null, {})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
113
src/components/PluginSettings/ContributorModal.tsx
Normal file
113
src/components/PluginSettings/ContributorModal.tsx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./contributorModal.css";
|
||||||
|
|
||||||
|
import { useSettings } from "@api/Settings";
|
||||||
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { DevsById } from "@utils/constants";
|
||||||
|
import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
|
||||||
|
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
|
||||||
|
import { Forms, MaskedLink, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
|
||||||
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
|
import Plugins from "~plugins";
|
||||||
|
|
||||||
|
import { PluginCard } from ".";
|
||||||
|
|
||||||
|
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
|
||||||
|
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
|
||||||
|
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
|
||||||
|
const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
|
||||||
|
|
||||||
|
const cl = classNameFactory("vc-author-modal-");
|
||||||
|
|
||||||
|
export function openContributorModal(user: User) {
|
||||||
|
openModal(modalProps =>
|
||||||
|
<ModalRoot {...modalProps}>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<ModalContent className={cl("root")}>
|
||||||
|
<ContributorModal user={user} />
|
||||||
|
</ModalContent>
|
||||||
|
</ErrorBoundary>
|
||||||
|
</ModalRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GithubIcon() {
|
||||||
|
const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark;
|
||||||
|
return <img src={src} alt="GitHub" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function WebsiteIcon() {
|
||||||
|
const src = getTheme() === Theme.Light ? WebsiteIconLight : WebsiteIconDark;
|
||||||
|
return <img src={src} alt="Website" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ContributorModal({ user }: { user: User; }) {
|
||||||
|
useSettings();
|
||||||
|
|
||||||
|
const profile = useStateFromStores([UserProfileStore], () => UserProfileStore.getUserProfile(user.id));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!profile && !user.bot && user.id)
|
||||||
|
fetchUserProfile(user.id);
|
||||||
|
}, [user.id]);
|
||||||
|
|
||||||
|
const githubName = profile?.connectedAccounts?.find(a => a.type === "github")?.name;
|
||||||
|
const website = profile?.connectedAccounts?.find(a => a.type === "domain")?.name;
|
||||||
|
|
||||||
|
const plugins = useMemo(() => {
|
||||||
|
const allPlugins = Object.values(Plugins);
|
||||||
|
const pluginsByAuthor = DevsById[user.id]
|
||||||
|
? allPlugins.filter(p => p.authors.includes(DevsById[user.id]))
|
||||||
|
: allPlugins.filter(p => p.authors.some(a => a.name === user.username));
|
||||||
|
|
||||||
|
return pluginsByAuthor
|
||||||
|
.filter(p => !p.name.endsWith("API"))
|
||||||
|
.sort((a, b) => Number(a.required ?? false) - Number(b.required ?? false));
|
||||||
|
}, [user.id, user.username]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={cl("header")}>
|
||||||
|
<img
|
||||||
|
className={cl("avatar")}
|
||||||
|
src={user.getAvatarURL(void 0, 512, true)}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
<Forms.FormTitle tag="h2" className={cl("name")}>{user.username}</Forms.FormTitle>
|
||||||
|
|
||||||
|
<div className={cl("links")}>
|
||||||
|
{website && (
|
||||||
|
<MaskedLink
|
||||||
|
href={"https://" + website}
|
||||||
|
>
|
||||||
|
<WebsiteIcon />
|
||||||
|
</MaskedLink>
|
||||||
|
)}
|
||||||
|
{githubName && (
|
||||||
|
<MaskedLink href={`https://github.com/${githubName}`}>
|
||||||
|
<GithubIcon />
|
||||||
|
</MaskedLink>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cl("plugins")}>
|
||||||
|
{plugins.map(p =>
|
||||||
|
<PluginCard
|
||||||
|
key={p.name}
|
||||||
|
plugin={p}
|
||||||
|
disabled={p.required ?? false}
|
||||||
|
onRestartNeeded={() => showToast("Restart to apply changes!")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -18,17 +18,16 @@
|
|||||||
|
|
||||||
import { generateId } from "@api/Commands";
|
import { generateId } from "@api/Commands";
|
||||||
import { useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes, isObjectEmpty } from "@utils/misc";
|
||||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
||||||
import { LazyComponent } from "@utils/react";
|
import { LazyComponent } from "@utils/react";
|
||||||
import { OptionType, Plugin } from "@utils/types";
|
import { OptionType, Plugin } from "@utils/types";
|
||||||
import { findByCode, findByPropsLazy } from "@webpack";
|
import { findByCode, findByPropsLazy } from "@webpack";
|
||||||
import { Button, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
import { Constructor } from "type-fest";
|
import { Constructor } from "type-fest";
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ import {
|
|||||||
SettingSliderComponent,
|
SettingSliderComponent,
|
||||||
SettingTextComponent
|
SettingTextComponent
|
||||||
} from "./components";
|
} from "./components";
|
||||||
import hideBotTagStyle from "./userPopoutHideBotTag.css?managed";
|
import { openContributorModal } from "./ContributorModal";
|
||||||
|
|
||||||
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
|
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
|
||||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||||
@ -89,30 +88,19 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
|
|
||||||
const canSubmit = () => Object.values(errors).every(e => !e);
|
const canSubmit = () => Object.values(errors).every(e => !e);
|
||||||
|
|
||||||
const hasSettings = Boolean(pluginSettings && plugin.options);
|
const hasSettings = Boolean(pluginSettings && plugin.options && !isObjectEmpty(plugin.options));
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
enableStyle(hideBotTagStyle);
|
|
||||||
|
|
||||||
let originalUser: User;
|
|
||||||
(async () => {
|
(async () => {
|
||||||
for (const user of plugin.authors.slice(0, 6)) {
|
for (const user of plugin.authors.slice(0, 6)) {
|
||||||
const author = user.id
|
const author = user.id
|
||||||
? await UserUtils.fetchUser(`${user.id}`)
|
? await UserUtils.fetchUser(`${user.id}`)
|
||||||
// only show name & pfp and no actions so users cannot harass plugin devs for support (send dms, add as friend, etc)
|
|
||||||
.then(u => (originalUser = u, makeDummyUser(u)))
|
|
||||||
.catch(() => makeDummyUser({ username: user.name }))
|
.catch(() => makeDummyUser({ username: user.name }))
|
||||||
: makeDummyUser({ username: user.name });
|
: makeDummyUser({ username: user.name });
|
||||||
|
|
||||||
setAuthors(a => [...a, author]);
|
setAuthors(a => [...a, author]);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return () => {
|
|
||||||
disableStyle(hideBotTagStyle);
|
|
||||||
if (originalUser)
|
|
||||||
FluxDispatcher.dispatch({ type: "USER_UPDATE", user: originalUser });
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function saveAndClose() {
|
async function saveAndClose() {
|
||||||
@ -214,6 +202,19 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
showDefaultAvatarsForNullUsers
|
showDefaultAvatarsForNullUsers
|
||||||
showUserPopout
|
showUserPopout
|
||||||
renderMoreUsers={renderMoreUsers}
|
renderMoreUsers={renderMoreUsers}
|
||||||
|
renderUser={(user: User) => (
|
||||||
|
<Clickable
|
||||||
|
className={AvatarStyles.clickableAvatar}
|
||||||
|
onClick={() => openContributorModal(user)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className={AvatarStyles.avatar}
|
||||||
|
src={user.getAvatarURL(void 0, 80, true)}
|
||||||
|
alt={user.username}
|
||||||
|
title={user.username}
|
||||||
|
/>
|
||||||
|
</Clickable>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Forms.FormSection>
|
</Forms.FormSection>
|
||||||
|
57
src/components/PluginSettings/contributorModal.css
Normal file
57
src/components/PluginSettings/contributorModal.css
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
.vc-author-modal-root {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-name {
|
||||||
|
text-transform: none;
|
||||||
|
flex-grow: 0;
|
||||||
|
background: var(--background-tertiary);
|
||||||
|
border-radius: 0 9999px 9999px 0;
|
||||||
|
padding: 6px 0.8em 6px 0.5em;
|
||||||
|
font-size: 20px;
|
||||||
|
height: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-name::before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 16px;
|
||||||
|
background: var(--background-tertiary);
|
||||||
|
z-index: -1;
|
||||||
|
left: -16px;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-avatar {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-links {
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-links img {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 4px solid var(--background-tertiary);
|
||||||
|
box-sizing: border-box
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-author-modal-plugins {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
@ -28,7 +28,7 @@ import { SettingsTab } from "@components/VencordSettings/shared";
|
|||||||
import { ChangeList } from "@utils/ChangeList";
|
import { ChangeList } from "@utils/ChangeList";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes, isObjectEmpty } from "@utils/misc";
|
||||||
import { openModalLazy } from "@utils/modal";
|
import { openModalLazy } from "@utils/modal";
|
||||||
import { LazyComponent, useAwaiter } from "@utils/react";
|
import { LazyComponent, useAwaiter } from "@utils/react";
|
||||||
import { Plugin } from "@utils/types";
|
import { Plugin } from "@utils/types";
|
||||||
@ -91,7 +91,7 @@ interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
|
|||||||
isNew?: boolean;
|
isNew?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
||||||
const settings = Settings.plugins[plugin.name];
|
const settings = Settings.plugins[plugin.name];
|
||||||
|
|
||||||
const isEnabled = () => settings.enabled ?? false;
|
const isEnabled = () => settings.enabled ?? false;
|
||||||
@ -161,7 +161,7 @@ function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLe
|
|||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
infoButton={
|
infoButton={
|
||||||
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
|
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
|
||||||
{plugin.options
|
{plugin.options && !isObjectEmpty(plugin.options)
|
||||||
? <CogWheel />
|
? <CogWheel />
|
||||||
: <InfoIcon width="24" height="24" />}
|
: <InfoIcon width="24" height="24" />}
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
[class|="userPopoutOuter"] [class*="botTag"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||||
|
import { CodeBlock } from "@components/CodeBlock";
|
||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||||
@ -299,7 +300,7 @@ function PatchHelper() {
|
|||||||
{!!(find && match && replacement) && (
|
{!!(find && match && replacement) && (
|
||||||
<>
|
<>
|
||||||
<Forms.FormTitle className={Margins.top20}>Code</Forms.FormTitle>
|
<Forms.FormTitle className={Margins.top20}>Code</Forms.FormTitle>
|
||||||
<div style={{ userSelect: "text" }}>{Parser.parse(makeCodeblock(code, "ts"))}</div>
|
<CodeBlock lang="js" content={code} />
|
||||||
<Button onClick={() => Clipboard.copy(code)}>Copy to Clipboard</Button>
|
<Button onClick={() => Clipboard.copy(code)}>Copy to Clipboard</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
transition: 0.1s ease-out;
|
transition: 0.1s ease-out;
|
||||||
transition-property: box-shadow, transform, background, opacity;
|
transition-property: box-shadow, transform, background, opacity;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-addon-card-disabled {
|
.vc-addon-card-disabled {
|
||||||
|
@ -49,7 +49,7 @@ export default definePlugin({
|
|||||||
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
|
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
|
||||||
}, {
|
}, {
|
||||||
match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g,
|
match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g,
|
||||||
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
|
replace: "$1,vcProps=$2$3+(vcProps.nsfw?' vc-nsfw-img':'')"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -136,4 +136,5 @@ export const defaultRules = [
|
|||||||
"utm_term",
|
"utm_term",
|
||||||
"si@open.spotify.com",
|
"si@open.spotify.com",
|
||||||
"igshid",
|
"igshid",
|
||||||
|
"share_id@reddit.com",
|
||||||
];
|
];
|
||||||
|
@ -49,7 +49,7 @@ export default definePlugin({
|
|||||||
await this.buildCss();
|
await this.buildCss();
|
||||||
|
|
||||||
addButton("HideAttachments", msg => {
|
addButton("HideAttachments", msg => {
|
||||||
if (!msg.attachments.length && !msg.embeds.length) return null;
|
if (!msg.attachments.length && !msg.embeds.length && !msg.stickerItems.length) return null;
|
||||||
|
|
||||||
const isHidden = hiddenMessages.has(msg.id);
|
const isHidden = hiddenMessages.has(msg.id);
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ export default definePlugin({
|
|||||||
async buildCss() {
|
async buildCss() {
|
||||||
const elements = [...hiddenMessages].map(id => `#message-accessories-${id}`).join(",");
|
const elements = [...hiddenMessages].map(id => `#message-accessories-${id}`).join(",");
|
||||||
style.textContent = `
|
style.textContent = `
|
||||||
:is(${elements}) [class*="embedWrapper"] {
|
:is(${elements}) :is([class*="embedWrapper"], [class*="clickableSticker"]) {
|
||||||
/* important is not necessary, but add it to make sure bad themes won't break it */
|
/* important is not necessary, but add it to make sure bad themes won't break it */
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,8 @@ const quotes = [
|
|||||||
"hd{b${",
|
"hd{b${",
|
||||||
"<;vqkijbq33271:56<3799?24944:",
|
"<;vqkijbq33271:56<3799?24944:",
|
||||||
"Thof$lu'ofdn,!qsefc'az*bnrcma+&Om{o+iu\"`khct$)bnrd\"bcdoi&",
|
"Thof$lu'ofdn,!qsefc'az*bnrcma+&Om{o+iu\"`khct$)bnrd\"bcdoi&",
|
||||||
"snofplkb{)c'r\"lod'|f*aurv#cpno`abchijklmno"
|
"snofplkb{)c'r\"lod'|f*aurv#cpno`abchijklmno",
|
||||||
|
"Wdn`khc'|f*eghl{%"
|
||||||
];
|
];
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
|
@ -30,6 +30,9 @@ const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
|
|||||||
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
|
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sharedIntlNumberFormat = new Intl.NumberFormat();
|
||||||
|
const numberFormat = (value: number) => sharedIntlNumberFormat.format(value);
|
||||||
|
|
||||||
function MemberCount() {
|
function MemberCount() {
|
||||||
const { id: channelId, guild_id: guildId } = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
|
const { id: channelId, guild_id: guildId } = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
|
||||||
const { groups } = useStateFromStores(
|
const { groups } = useStateFromStores(
|
||||||
@ -57,7 +60,7 @@ function MemberCount() {
|
|||||||
alignContent: "center",
|
alignContent: "center",
|
||||||
gap: 0
|
gap: 0
|
||||||
}}>
|
}}>
|
||||||
<Tooltip text={`${online} Online in this Channel`} position="bottom">
|
<Tooltip text={`${numberFormat(online)} online in this channel`} position="bottom">
|
||||||
{props => (
|
{props => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<span
|
<span
|
||||||
@ -70,11 +73,11 @@ function MemberCount() {
|
|||||||
marginRight: "0.5em"
|
marginRight: "0.5em"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span style={{ color: "var(--green-360)" }}>{online}</span>
|
<span style={{ color: "var(--green-360)" }}>{numberFormat(online)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip text={`${total} Total Server Members`} position="bottom">
|
<Tooltip text={`${numberFormat(total)} total server members`} position="bottom">
|
||||||
{props => (
|
{props => (
|
||||||
<div {...props}>
|
<div {...props}>
|
||||||
<span
|
<span
|
||||||
@ -88,7 +91,7 @@ function MemberCount() {
|
|||||||
marginLeft: "1em"
|
marginLeft: "1em"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span style={{ color: "var(--primary-400)" }}>{total}</span>
|
<span style={{ color: "var(--primary-400)" }}>{numberFormat(total)}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -169,21 +169,14 @@ export default definePlugin({
|
|||||||
try {
|
try {
|
||||||
if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
|
if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
|
||||||
|
|
||||||
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds } = Settings.plugins.MessageLogger;
|
const mutate = (id: string) => {
|
||||||
const myId = UserStore.getCurrentUser().id;
|
|
||||||
|
|
||||||
function mutate(id: string) {
|
|
||||||
const msg = cache.get(id);
|
const msg = cache.get(id);
|
||||||
if (!msg) return;
|
if (!msg) return;
|
||||||
|
|
||||||
const EPHEMERAL = 64;
|
const EPHEMERAL = 64;
|
||||||
const shouldIgnore = data.mlDeleted ||
|
const shouldIgnore = data.mlDeleted ||
|
||||||
(msg.flags & EPHEMERAL) === EPHEMERAL ||
|
(msg.flags & EPHEMERAL) === EPHEMERAL ||
|
||||||
ignoreBots && msg.author?.bot ||
|
this.shouldIgnore(msg);
|
||||||
ignoreSelf && msg.author?.id === myId ||
|
|
||||||
ignoreUsers.includes(msg.author?.id) ||
|
|
||||||
ignoreChannels.includes(msg.channel_id) ||
|
|
||||||
ignoreGuilds.includes(ChannelStore.getChannel(msg.channel_id)?.guild_id);
|
|
||||||
|
|
||||||
if (shouldIgnore) {
|
if (shouldIgnore) {
|
||||||
cache = cache.remove(id);
|
cache = cache.remove(id);
|
||||||
@ -192,7 +185,7 @@ export default definePlugin({
|
|||||||
.set("deleted", true)
|
.set("deleted", true)
|
||||||
.set("attachments", m.attachments.map(a => (a.deleted = true, a))));
|
.set("attachments", m.attachments.map(a => (a.deleted = true, a))));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (isBulk) {
|
if (isBulk) {
|
||||||
data.ids.forEach(mutate);
|
data.ids.forEach(mutate);
|
||||||
@ -205,6 +198,18 @@ export default definePlugin({
|
|||||||
return cache;
|
return cache;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldIgnore(message: any) {
|
||||||
|
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds } = Settings.plugins.MessageLogger;
|
||||||
|
const myId = UserStore.getCurrentUser().id;
|
||||||
|
|
||||||
|
return ignoreBots && message.author?.bot ||
|
||||||
|
ignoreSelf && message.author?.id === myId ||
|
||||||
|
ignoreUsers.includes(message.author?.id) ||
|
||||||
|
ignoreChannels.includes(message.channel_id) ||
|
||||||
|
ignoreChannels.includes(ChannelStore.getChannel(message.channel_id)?.parent_id) ||
|
||||||
|
ignoreGuilds.includes(ChannelStore.getChannel(message.channel_id)?.guild_id);
|
||||||
|
},
|
||||||
|
|
||||||
// Based on canary 9ab8626bcebceaea6da570b9c586172d02b9c996
|
// Based on canary 9ab8626bcebceaea6da570b9c586172d02b9c996
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
@ -237,7 +242,7 @@ export default definePlugin({
|
|||||||
match: /(MESSAGE_UPDATE:function\((\w)\).+?)\.update\((\w)/,
|
match: /(MESSAGE_UPDATE:function\((\w)\).+?)\.update\((\w)/,
|
||||||
replace: "$1" +
|
replace: "$1" +
|
||||||
".update($3,m =>" +
|
".update($3,m =>" +
|
||||||
" (($2.message.flags & 64) === 64 || (Vencord.Settings.plugins.MessageLogger.ignoreBots && $2.message.author?.bot) || (Vencord.Settings.plugins.MessageLogger.ignoreSelf && $2.message.author?.id === Vencord.Webpack.Common.UserStore.getCurrentUser().id)) ? m :" +
|
" (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message)) ? m :" +
|
||||||
" $2.message.content !== m.editHistory?.[0]?.content && $2.message.content !== m.content ?" +
|
" $2.message.content !== m.editHistory?.[0]?.content && $2.message.content !== m.content ?" +
|
||||||
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" +
|
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" +
|
||||||
" m" +
|
" m" +
|
||||||
|
83
src/plugins/pictureInPicture.tsx
Normal file
83
src/plugins/pictureInPicture.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
import { React, Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
loop: {
|
||||||
|
description: "Whether to make the PiP video loop or not",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "PictureInPicture",
|
||||||
|
description: "Adds picture in picture to videos (next to the Download button)",
|
||||||
|
authors: [Devs.Lumap],
|
||||||
|
settings,
|
||||||
|
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".onRemoveAttachment,",
|
||||||
|
replacement: {
|
||||||
|
match: /\.nonMediaAttachment.{0,10}children:\[(\i),/,
|
||||||
|
replace: "$&$1&&$self.renderPiPButton(),"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
renderPiPButton: ErrorBoundary.wrap(() => {
|
||||||
|
return (
|
||||||
|
<Tooltip text="Toggle Picture in Picture">
|
||||||
|
{tooltipProps => (
|
||||||
|
<div
|
||||||
|
{...tooltipProps}
|
||||||
|
role="button"
|
||||||
|
style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
paddingTop: "4px",
|
||||||
|
paddingLeft: "4px",
|
||||||
|
paddingRight: "4px",
|
||||||
|
}}
|
||||||
|
onClick={e => {
|
||||||
|
const video = e.currentTarget.parentNode!.parentNode!.querySelector("video")!;
|
||||||
|
const videoClone = document.body.appendChild(video.cloneNode(true)) as HTMLVideoElement;
|
||||||
|
|
||||||
|
videoClone.loop = settings.store.loop;
|
||||||
|
videoClone.style.display = "none";
|
||||||
|
videoClone.onleavepictureinpicture = () => videoClone.remove();
|
||||||
|
|
||||||
|
function launchPiP() {
|
||||||
|
videoClone.currentTime = video.currentTime;
|
||||||
|
videoClone.requestPictureInPicture();
|
||||||
|
video.pause();
|
||||||
|
videoClone.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoClone.readyState === 4 /* HAVE_ENOUGH_DATA */)
|
||||||
|
launchPiP();
|
||||||
|
else
|
||||||
|
videoClone.onloadedmetadata = launchPiP;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg width="24px" height="24px" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="var(--interactive-normal)"
|
||||||
|
d="M21 3a1 1 0 0 1 1 1v7h-2V5H4v14h6v2H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h18zm0 10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-8a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h8zm-1 2h-6v4h6v-4z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}, { noop: true })
|
||||||
|
});
|
@ -122,6 +122,14 @@ export default definePlugin({
|
|||||||
// ....concat(pins).concat(toArray(channelIds).filter(c => !isPinned(c)))
|
// ....concat(pins).concat(toArray(channelIds).filter(c => !isPinned(c)))
|
||||||
replace: ".concat($self.getSnapshot()).concat($2.filter(c=>!$self.isPinned(c)))"
|
replace: ".concat($self.getSnapshot()).concat($2.filter(c=>!$self.isPinned(c)))"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
// fix alt+shift+up/down
|
||||||
|
{
|
||||||
|
find: '"alt+shift+down"',
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=return \i===\i\.ME\?)\i\.\i\.getPrivateChannelIds\(\)/,
|
||||||
|
replace: "$self.getSnapshot().concat($&.filter(c=>!$self.isPinned(c)))"
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,7 @@ import { sendBotMessage } from "@api/Commands";
|
|||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { Button, ButtonLooks, ButtonWrapperClasses, DraftStore, DraftType, SelectedChannelStore, Tooltip, UserStore } from "@webpack/common";
|
import { Button, ButtonLooks, ButtonWrapperClasses, DraftStore, DraftType, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type: {
|
type: {
|
||||||
@ -31,10 +31,9 @@ interface Props {
|
|||||||
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
|
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
|
||||||
|
|
||||||
export function PreviewButton(chatBoxProps: Props) {
|
export function PreviewButton(chatBoxProps: Props) {
|
||||||
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
|
||||||
const channelId = SelectedChannelStore.getChannelId();
|
const channelId = SelectedChannelStore.getChannelId();
|
||||||
const draft = getDraft(channelId);
|
const draft = useStateFromStores([DraftStore], () => getDraft(channelId));
|
||||||
|
if (chatBoxProps.type.analyticsName !== "normal") return null;
|
||||||
if (!draft) return null;
|
if (!draft) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -21,14 +21,11 @@ import { VENCORD_USER_AGENT } from "@utils/constants";
|
|||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import { getCurrentChannel } from "@utils/discord";
|
import { getCurrentChannel } from "@utils/discord";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { findStoreLazy } from "@webpack";
|
import { UserProfileStore, UserStore } from "@webpack/common";
|
||||||
import { UserStore } from "@webpack/common";
|
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { PronounCode, PronounMapping, PronounsResponse } from "./types";
|
import { PronounCode, PronounMapping, PronounsResponse } from "./types";
|
||||||
|
|
||||||
const UserProfileStore = findStoreLazy("UserProfileStore");
|
|
||||||
|
|
||||||
type PronounsWithSource = [string | null, string];
|
type PronounsWithSource = [string | null, string];
|
||||||
const EmptyPronouns: PronounsWithSource = [null, ""];
|
const EmptyPronouns: PronounsWithSource = [null, ""];
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ export default definePlugin({
|
|||||||
find: "window.webkitAudioContext",
|
find: "window.webkitAudioContext",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\i\.acquire=function\((\i)\)\{return )navigator\.mediaDevices\.getUserMedia\(\1\)(?=\})/,
|
match: /(?<=\i\.acquire=function\((\i)\)\{return )navigator\.mediaDevices\.getUserMedia\(\1\)(?=\})/,
|
||||||
replace: m => `${m}.then(stream => $self.connectRnnoise(stream))`
|
replace: "$&.then(stream => $self.connectRnnoise(stream, $1.audio))"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -182,7 +182,8 @@ export default definePlugin({
|
|||||||
|
|
||||||
setEnabled,
|
setEnabled,
|
||||||
isEnabled: () => settings.store.isEnabled,
|
isEnabled: () => settings.store.isEnabled,
|
||||||
async connectRnnoise(stream: MediaStream): Promise<MediaStream> {
|
async connectRnnoise(stream: MediaStream, isAudio: boolean): Promise<MediaStream> {
|
||||||
|
if (!isAudio) return stream;
|
||||||
if (!settings.store.isEnabled) return stream;
|
if (!settings.store.isEnabled) return stream;
|
||||||
|
|
||||||
const audioCtx = new AudioContext();
|
const audioCtx = new AudioContext();
|
||||||
|
@ -27,13 +27,12 @@ import { copyWithToast } from "@utils/misc";
|
|||||||
import { LazyComponent } from "@utils/react";
|
import { LazyComponent } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCode, findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack";
|
import { findByCode, findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { Text, Tooltip } from "@webpack/common";
|
import { Text, Tooltip, UserProfileStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
import { VerifiedIcon } from "./VerifiedIcon";
|
import { VerifiedIcon } from "./VerifiedIcon";
|
||||||
|
|
||||||
const Section = LazyComponent(() => findByCode("().lastSection"));
|
const Section = LazyComponent(() => findByCode("().lastSection"));
|
||||||
const UserProfileStore = findStoreLazy("UserProfileStore");
|
|
||||||
const ThemeStore = findStoreLazy("ThemeStore");
|
const ThemeStore = findStoreLazy("ThemeStore");
|
||||||
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
|
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
|
||||||
const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"');
|
const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"');
|
||||||
|
35
src/plugins/showTimeouts.ts
Normal file
35
src/plugins/showTimeouts.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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 { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "ShowTimeouts",
|
||||||
|
description: "Display member timeout icons in chat regardless of permissions.",
|
||||||
|
authors: [Devs.Dolfies],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: "showCommunicationDisabledStyles",
|
||||||
|
replacement: {
|
||||||
|
match: /&&\i\.\i\.canManageUser\(\i\.\i\.MODERATE_MEMBERS,\i\.author,\i\)/,
|
||||||
|
replace: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
@ -48,7 +48,7 @@ export default definePlugin({
|
|||||||
name: "vencord-debug",
|
name: "vencord-debug",
|
||||||
description: "Send Vencord Debug info",
|
description: "Send Vencord Debug info",
|
||||||
predicate: ctx => AllowedChannelIds.includes(ctx.channel.id),
|
predicate: ctx => AllowedChannelIds.includes(ctx.channel.id),
|
||||||
execute() {
|
async execute() {
|
||||||
const { RELEASE_CHANNEL } = window.GLOBAL_ENV;
|
const { RELEASE_CHANNEL } = window.GLOBAL_ENV;
|
||||||
|
|
||||||
const client = (() => {
|
const client = (() => {
|
||||||
@ -75,6 +75,10 @@ export default definePlugin({
|
|||||||
OpenAsar: "openasar" in window,
|
OpenAsar: "openasar" in window,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (IS_DISCORD_DESKTOP) {
|
||||||
|
info["Last Crash Reason"] = (await DiscordNative.processUtils.getLastCrash())?.rendererCrashReason ?? "N/A";
|
||||||
|
}
|
||||||
|
|
||||||
const debugInfo = `
|
const debugInfo = `
|
||||||
**Vencord Debug Info**
|
**Vencord Debug Info**
|
||||||
>>> ${Object.entries(info).map(([k, v]) => `${k}: ${v}`).join("\n")}
|
>>> ${Object.entries(info).map(([k, v]) => `${k}: ${v}`).join("\n")}
|
||||||
|
@ -80,6 +80,9 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
||||||
|
data,
|
||||||
|
|
||||||
settingsAboutComponent: () => {
|
settingsAboutComponent: () => {
|
||||||
return (
|
return (
|
||||||
<Link href="https://github.com/AutumnVN/usrbg#how-to-request-your-own-usrbg-banner">CLICK HERE TO GET YOUR OWN BANNER</Link>
|
<Link href="https://github.com/AutumnVN/usrbg#how-to-request-your-own-usrbg-banner">CLICK HERE TO GET YOUR OWN BANNER</Link>
|
||||||
@ -116,7 +119,9 @@ export default definePlugin({
|
|||||||
enableStyle(style);
|
enableStyle(style);
|
||||||
|
|
||||||
const res = await fetch(BASE_URL);
|
const res = await fetch(BASE_URL);
|
||||||
if (res.ok)
|
if (res.ok) {
|
||||||
data = await res.json();
|
data = await res.json();
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -16,8 +16,10 @@
|
|||||||
* 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 { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||||
import { addButton, removeButton } from "@api/MessagePopover";
|
import { addButton, removeButton } from "@api/MessagePopover";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import { CodeBlock } from "@components/CodeBlock";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
@ -25,12 +27,12 @@ import { Margins } from "@utils/margins";
|
|||||||
import { copyWithToast } from "@utils/misc";
|
import { copyWithToast } from "@utils/misc";
|
||||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { Button, ChannelStore, Forms, Parser, Text } from "@webpack/common";
|
import { Button, ChannelStore, Forms, Menu, Text } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
|
|
||||||
const CopyIcon = () => {
|
const CopyIcon = () => {
|
||||||
return <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" width="22" height="22">
|
return <svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" width="18" height="18">
|
||||||
<path d="M12.9297 3.25007C12.7343 3.05261 12.4154 3.05226 12.2196 3.24928L11.5746 3.89824C11.3811 4.09297 11.3808 4.40733 11.5739 4.60245L16.5685 9.64824C16.7614 9.84309 16.7614 10.1569 16.5685 10.3517L11.5739 15.3975C11.3808 15.5927 11.3811 15.907 11.5746 16.1017L12.2196 16.7507C12.4154 16.9477 12.7343 16.9474 12.9297 16.7499L19.2604 10.3517C19.4532 10.1568 19.4532 9.84314 19.2604 9.64832L12.9297 3.25007Z" />
|
<path d="M12.9297 3.25007C12.7343 3.05261 12.4154 3.05226 12.2196 3.24928L11.5746 3.89824C11.3811 4.09297 11.3808 4.40733 11.5739 4.60245L16.5685 9.64824C16.7614 9.84309 16.7614 10.1569 16.5685 10.3517L11.5739 15.3975C11.3808 15.5927 11.3811 15.907 11.5746 16.1017L12.2196 16.7507C12.4154 16.9477 12.7343 16.9474 12.9297 16.7499L19.2604 10.3517C19.4532 10.1568 19.4532 9.84314 19.2604 9.64832L12.9297 3.25007Z" />
|
||||||
<path d="M8.42616 4.60245C8.6193 4.40733 8.61898 4.09297 8.42545 3.89824L7.78047 3.24928C7.58466 3.05226 7.26578 3.05261 7.07041 3.25007L0.739669 9.64832C0.5469 9.84314 0.546901 10.1568 0.739669 10.3517L7.07041 16.7499C7.26578 16.9474 7.58465 16.9477 7.78047 16.7507L8.42545 16.1017C8.61898 15.907 8.6193 15.5927 8.42616 15.3975L3.43155 10.3517C3.23869 10.1569 3.23869 9.84309 3.43155 9.64824L8.42616 4.60245Z" />
|
<path d="M8.42616 4.60245C8.6193 4.40733 8.61898 4.09297 8.42545 3.89824L7.78047 3.24928C7.58466 3.05226 7.26578 3.05261 7.07041 3.25007L0.739669 9.64832C0.5469 9.84314 0.546901 10.1568 0.739669 10.3517L7.07041 16.7499C7.26578 16.9474 7.58465 16.9477 7.78047 16.7507L8.42545 16.1017C8.61898 15.907 8.6193 15.5927 8.42616 15.3975L3.43155 10.3517C3.23869 10.1569 3.23869 9.84309 3.43155 9.64824L8.42616 4.60245Z" />
|
||||||
</svg>;
|
</svg>;
|
||||||
@ -58,19 +60,7 @@ function cleanMessage(msg: Message) {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CodeBlock(props: { content: string, lang: string; }) {
|
function openViewRawModal(json: string, type: string, msgContent?: string) {
|
||||||
return (
|
|
||||||
// make text selectable
|
|
||||||
<div style={{ userSelect: "text" }}>
|
|
||||||
{Parser.defaultRules.codeBlock.react(props, null, {})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openViewRawModal(msg: Message) {
|
|
||||||
msg = cleanMessage(msg);
|
|
||||||
const msgJson = JSON.stringify(msg, null, 4);
|
|
||||||
|
|
||||||
const key = openModal(props => (
|
const key = openModal(props => (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<ModalRoot {...props} size={ModalSize.LARGE}>
|
<ModalRoot {...props} size={ModalSize.LARGE}>
|
||||||
@ -80,26 +70,28 @@ function openViewRawModal(msg: Message) {
|
|||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<div style={{ padding: "16px 0" }}>
|
<div style={{ padding: "16px 0" }}>
|
||||||
{!!msg.content && (
|
{!!msgContent && (
|
||||||
<>
|
<>
|
||||||
<Forms.FormTitle tag="h5">Content</Forms.FormTitle>
|
<Forms.FormTitle tag="h5">Content</Forms.FormTitle>
|
||||||
<CodeBlock content={msg.content} lang="" />
|
<CodeBlock content={msgContent} lang="" />
|
||||||
<Forms.FormDivider className={Margins.bottom20} />
|
<Forms.FormDivider className={Margins.bottom20} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Forms.FormTitle tag="h5">Message Data</Forms.FormTitle>
|
<Forms.FormTitle tag="h5">{type} Data</Forms.FormTitle>
|
||||||
<CodeBlock content={msgJson} lang="json" />
|
<CodeBlock content={json} lang="json" />
|
||||||
</div>
|
</div>
|
||||||
</ModalContent >
|
</ModalContent >
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Flex cellSpacing={10}>
|
<Flex cellSpacing={10}>
|
||||||
<Button onClick={() => copyWithToast(msgJson, "Message data copied to clipboard!")}>
|
<Button onClick={() => copyWithToast(json, `${type} data copied to clipboard!`)}>
|
||||||
Copy Message JSON
|
Copy {type} JSON
|
||||||
</Button>
|
|
||||||
<Button onClick={() => copyWithToast(msg.content, "Content copied to clipboard!")}>
|
|
||||||
Copy Raw Content
|
|
||||||
</Button>
|
</Button>
|
||||||
|
{!!msgContent && (
|
||||||
|
<Button onClick={() => copyWithToast(msgContent, "Content copied to clipboard!")}>
|
||||||
|
Copy Raw Content
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalRoot >
|
</ModalRoot >
|
||||||
@ -107,6 +99,13 @@ function openViewRawModal(msg: Message) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openViewRawModalMessage(msg: Message) {
|
||||||
|
msg = cleanMessage(msg);
|
||||||
|
const msgJson = JSON.stringify(msg, null, 4);
|
||||||
|
|
||||||
|
return openViewRawModal(msgJson, "Message", msg.content);
|
||||||
|
}
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
clickMethod: {
|
clickMethod: {
|
||||||
description: "Change the button to view the raw content/data of any message.",
|
description: "Change the button to view the raw content/data of any message.",
|
||||||
@ -118,10 +117,34 @@ const settings = definePluginSettings({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function MakeContextCallback(name: string) {
|
||||||
|
const callback: NavContextMenuPatchCallback = (children, props) => () => {
|
||||||
|
if (name === "Guild" && !props.guild) return;
|
||||||
|
const lastChild = children.at(-1);
|
||||||
|
if (lastChild?.key === "developer-actions") {
|
||||||
|
const p = lastChild.props;
|
||||||
|
if (!Array.isArray(p.children))
|
||||||
|
p.children = [p.children];
|
||||||
|
({ children } = p);
|
||||||
|
}
|
||||||
|
|
||||||
|
children.splice(-1, 0,
|
||||||
|
<Menu.MenuItem
|
||||||
|
id={`vc-view-${name.toLowerCase()}-raw`}
|
||||||
|
label="View Raw"
|
||||||
|
action={() => openViewRawModal(JSON.stringify(props[name.toLowerCase()], null, 4), name)}
|
||||||
|
icon={CopyIcon}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ViewRaw",
|
name: "ViewRaw",
|
||||||
description: "Copy and view the raw content/data of any message.",
|
description: "Copy and view the raw content/data of any message, channel or guild",
|
||||||
authors: [Devs.KingFish, Devs.Ven, Devs.rad],
|
authors: [Devs.KingFish, Devs.Ven, Devs.rad, Devs.ImLvna],
|
||||||
dependencies: ["MessagePopoverAPI"],
|
dependencies: ["MessagePopoverAPI"],
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
@ -131,7 +154,7 @@ export default definePlugin({
|
|||||||
if (settings.store.clickMethod === "Right") {
|
if (settings.store.clickMethod === "Right") {
|
||||||
copyWithToast(msg.content);
|
copyWithToast(msg.content);
|
||||||
} else {
|
} else {
|
||||||
openViewRawModal(msg);
|
openViewRawModalMessage(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -143,7 +166,7 @@ export default definePlugin({
|
|||||||
} else {
|
} else {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
openViewRawModal(msg);
|
openViewRawModalMessage(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,9 +183,16 @@ export default definePlugin({
|
|||||||
onContextMenu: handleContextMenu
|
onContextMenu: handleContextMenu
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addContextMenuPatch("guild-context", MakeContextCallback("Guild"));
|
||||||
|
addContextMenuPatch("channel-context", MakeContextCallback("Channel"));
|
||||||
|
addContextMenuPatch("user-context", MakeContextCallback("User"));
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
removeButton("CopyRawMessage");
|
removeButton("CopyRawMessage");
|
||||||
|
removeContextMenuPatch("guild-context", MakeContextCallback("Guild"));
|
||||||
|
removeContextMenuPatch("channel-context", MakeContextCallback("Channel"));
|
||||||
|
removeContextMenuPatch("user-context", MakeContextCallback("User"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||||
import { Flex } from "@components/Flex";
|
|
||||||
import { Microphone } from "@components/Icons";
|
import { Microphone } from "@components/Icons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||||
@ -39,6 +38,7 @@ import { VoiceRecorderWeb } from "./WebRecorder";
|
|||||||
const CloudUpload = findLazy(m => m.prototype?.uploadFileToCloud);
|
const CloudUpload = findLazy(m => m.prototype?.uploadFileToCloud);
|
||||||
const MessageCreator = findByPropsLazy("getSendMessageOptionsForReply", "sendMessage");
|
const MessageCreator = findByPropsLazy("getSendMessageOptionsForReply", "sendMessage");
|
||||||
const PendingReplyStore = findStoreLazy("PendingReplyStore");
|
const PendingReplyStore = findStoreLazy("PendingReplyStore");
|
||||||
|
const OptionClasses = findByPropsLazy("optionName", "optionIcon", "optionLabel");
|
||||||
|
|
||||||
export type VoiceRecorder = ComponentType<{
|
export type VoiceRecorder = ComponentType<{
|
||||||
setAudioBlob(blob: Blob): void;
|
setAudioBlob(blob: Blob): void;
|
||||||
@ -226,12 +226,10 @@ const ctxMenuPatch: NavContextMenuPatchCallback = (children, props) => () => {
|
|||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
id="vc-send-vmsg"
|
id="vc-send-vmsg"
|
||||||
label={
|
label={
|
||||||
<>
|
<div className={OptionClasses.optionLabel}>
|
||||||
<Flex flexDirection="row" style={{ alignItems: "center", gap: 8 }}>
|
<Microphone className={OptionClasses.optionIcon} height={24} width={24} />
|
||||||
<Microphone height={24} width={24} />
|
<div className={OptionClasses.optionName}>Send voice message</div>
|
||||||
Send voice message
|
</div>
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
action={() => openModal(modalProps => <Modal modalProps={modalProps} />)}
|
action={() => openModal(modalProps => <Modal modalProps={modalProps} />)}
|
||||||
/>
|
/>
|
||||||
|
@ -355,10 +355,18 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||||||
name: "bb010g",
|
name: "bb010g",
|
||||||
id: 72791153467990016n,
|
id: 72791153467990016n,
|
||||||
},
|
},
|
||||||
|
Lumap: {
|
||||||
|
name: "lumap",
|
||||||
|
id: 635383782576357407n
|
||||||
|
},
|
||||||
|
Dolfies: {
|
||||||
|
name: "Dolfies",
|
||||||
|
id: 852892297661906993n,
|
||||||
|
},
|
||||||
RuukuLada: {
|
RuukuLada: {
|
||||||
name: "RuukuLada",
|
name: "RuukuLada",
|
||||||
id: 119705748346241027n,
|
id: 119705748346241027n,
|
||||||
},
|
}
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
// iife so #__PURE__ works correctly
|
// iife so #__PURE__ works correctly
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
import { MessageObject } from "@api/MessageEvents";
|
import { MessageObject } from "@api/MessageEvents";
|
||||||
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
|
||||||
import { ChannelStore, ComponentDispatch, GuildStore, MaskedLink, ModalImageClasses, PrivateChannelsStore, SelectedChannelStore, SelectedGuildStore, UserUtils } from "@webpack/common";
|
import { ChannelStore, ComponentDispatch, FluxDispatcher, GuildStore, MaskedLink, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileStore, UserUtils } from "@webpack/common";
|
||||||
import { Guild, Message, User } from "discord-types/general";
|
import { Guild, Message, User } from "discord-types/general";
|
||||||
|
|
||||||
import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal";
|
import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal";
|
||||||
@ -118,6 +118,41 @@ export async function openUserProfile(id: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FetchUserProfileOptions {
|
||||||
|
friend_token?: string;
|
||||||
|
connections_role_id?: string;
|
||||||
|
guild_id?: string;
|
||||||
|
with_mutual_guilds?: boolean;
|
||||||
|
with_mutual_friends_count?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a user's profile
|
||||||
|
*/
|
||||||
|
export async function fetchUserProfile(id: string, options?: FetchUserProfileOptions) {
|
||||||
|
const cached = UserProfileStore.getUserProfile(id);
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
FluxDispatcher.dispatch({ type: "USER_PROFILE_FETCH_START", userId: id });
|
||||||
|
|
||||||
|
const { body } = await RestAPI.get({
|
||||||
|
url: `/users/${id}/profile`,
|
||||||
|
query: {
|
||||||
|
with_mutual_guilds: false,
|
||||||
|
with_mutual_friends_count: false,
|
||||||
|
...options
|
||||||
|
},
|
||||||
|
oldFormErrors: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
FluxDispatcher.dispatch({ type: "USER_UPDATE", user: body.user });
|
||||||
|
await FluxDispatcher.dispatch({ type: "USER_PROFILE_FETCH_SUCCESS", ...body });
|
||||||
|
if (options?.guild_id && body.guild_member)
|
||||||
|
FluxDispatcher.dispatch({ type: "GUILD_MEMBER_PROFILE_UPDATE", guildId: options.guild_id, guildMember: body.guild_member });
|
||||||
|
|
||||||
|
return UserProfileStore.getUserProfile(id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the unique username for a user. Returns user.username for pomelo people, user.tag otherwise
|
* Get the unique username for a user. Returns user.username for pomelo people, user.tag otherwise
|
||||||
*/
|
*/
|
||||||
|
@ -74,6 +74,16 @@ export function isObject(obj: unknown): obj is object {
|
|||||||
return typeof obj === "object" && obj !== null && !Array.isArray(obj);
|
return typeof obj === "object" && obj !== null && !Array.isArray(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an object is empty or in other words has no own properties
|
||||||
|
*/
|
||||||
|
export function isObjectEmpty(obj: object) {
|
||||||
|
for (const k in obj)
|
||||||
|
if (Object.hasOwn(obj, k)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns null if value is not a URL, otherwise return URL object.
|
* Returns null if value is not a URL, otherwise return URL object.
|
||||||
* Avoids having to wrap url checks in a try/catch
|
* Avoids having to wrap url checks in a try/catch
|
||||||
|
@ -48,6 +48,7 @@ export let PoggerModeSettingsStore: GenericStore;
|
|||||||
|
|
||||||
export let GuildStore: Stores.GuildStore & t.FluxStore;
|
export let GuildStore: Stores.GuildStore & t.FluxStore;
|
||||||
export let UserStore: Stores.UserStore & t.FluxStore;
|
export let UserStore: Stores.UserStore & t.FluxStore;
|
||||||
|
export let UserProfileStore: GenericStore;
|
||||||
export let SelectedChannelStore: Stores.SelectedChannelStore & t.FluxStore;
|
export let SelectedChannelStore: Stores.SelectedChannelStore & t.FluxStore;
|
||||||
export let SelectedGuildStore: t.FluxStore & Record<string, any>;
|
export let SelectedGuildStore: t.FluxStore & Record<string, any>;
|
||||||
export let ChannelStore: Stores.ChannelStore & t.FluxStore;
|
export let ChannelStore: Stores.ChannelStore & t.FluxStore;
|
||||||
@ -86,6 +87,7 @@ export const useStateFromStores: <T>(
|
|||||||
|
|
||||||
waitForStore("DraftStore", s => DraftStore = s);
|
waitForStore("DraftStore", s => DraftStore = s);
|
||||||
waitForStore("UserStore", s => UserStore = s);
|
waitForStore("UserStore", s => UserStore = s);
|
||||||
|
waitForStore("UserProfileStore", m => UserProfileStore = m);
|
||||||
waitForStore("ChannelStore", m => ChannelStore = m);
|
waitForStore("ChannelStore", m => ChannelStore = m);
|
||||||
waitForStore("SelectedChannelStore", m => SelectedChannelStore = m);
|
waitForStore("SelectedChannelStore", m => SelectedChannelStore = m);
|
||||||
waitForStore("SelectedGuildStore", m => SelectedGuildStore = m);
|
waitForStore("SelectedGuildStore", m => SelectedGuildStore = m);
|
||||||
|
16
src/webpack/common/types/components.d.ts
vendored
16
src/webpack/common/types/components.d.ts
vendored
@ -398,12 +398,18 @@ export type Paginator = ComponentType<{
|
|||||||
hideMaxPage?: boolean;
|
hideMaxPage?: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type MaskedLink = ComponentType<{
|
export type MaskedLink = ComponentType<PropsWithChildren<{
|
||||||
onClick(): void;
|
|
||||||
trusted: boolean;
|
|
||||||
title: string,
|
|
||||||
href: string;
|
href: string;
|
||||||
}>;
|
rel?: string;
|
||||||
|
target?: string;
|
||||||
|
title?: string,
|
||||||
|
className?: string;
|
||||||
|
tabIndex?: number;
|
||||||
|
onClick?(): void;
|
||||||
|
trusted?: boolean;
|
||||||
|
messageId?: string;
|
||||||
|
channelId?: string;
|
||||||
|
}>>;
|
||||||
|
|
||||||
export type ScrollerThin = ComponentType<PropsWithChildren<{
|
export type ScrollerThin = ComponentType<PropsWithChildren<{
|
||||||
className?: string;
|
className?: string;
|
||||||
|
Reference in New Issue
Block a user