Merge branch 'immediate-finds' into immediate-finds-modules-proxy

This commit is contained in:
Nuckyz 2024-06-13 00:03:39 -03:00
commit bb22355a57
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
22 changed files with 353 additions and 354 deletions

View file

@ -40,7 +40,7 @@ import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextI
import Plugins from "~plugins";
// Avoid circular dependency
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins"));
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins")) as typeof import("../../plugins");
const cl = classNameFactory("vc-plugins-");
const logger = new Logger("PluginSettings", "#a6d189");

View file

@ -250,7 +250,7 @@ function ThemesTab() {
Edit QuickCSS
</Button>
{Vencord.Settings.plugins.ClientTheme.enabled && (
{Vencord.Plugins.isPluginEnabled("ClientTheme") && (
<Button
onClick={() => openModal(modalProps => (
<PluginModal

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab";
import CloudTab from "@components/VencordSettings/CloudTab";
import PatchHelperTab from "@components/VencordSettings/PatchHelperTab";
@ -33,11 +33,27 @@ import gitHash from "~git-hash";
type SectionType = "HEADER" | "DIVIDER" | "CUSTOM";
type SectionTypes = Record<SectionType, SectionType>;
const settings = definePluginSettings({
settingsLocation: {
type: OptionType.SELECT,
description: "Where to put the Vencord settings section",
options: [
{ label: "At the very top", value: "top" },
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
{ label: "Below the Nitro section", value: "belowNitro" },
{ label: "Above Activity Settings", value: "aboveActivity" },
{ label: "Below Activity Settings", value: "belowActivity" },
{ label: "At the very bottom", value: "bottom" },
]
}
});
export default definePlugin({
name: "Settings",
description: "Adds Settings UI and debug info",
authors: [Devs.Ven, Devs.Megu],
required: true,
settings,
patches: [
{
@ -63,7 +79,7 @@ export default definePlugin({
noWarn: true,
replacement: {
get match() {
switch (Settings.plugins.Settings.settingsLocation) {
switch (settings.store.settingsLocation) {
case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS/;
case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS/;
case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS/;
@ -158,12 +174,12 @@ export default definePlugin({
].filter(Boolean);
},
isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) {
const firstChild = settings?.[0];
isRightSpot({ header, settingsChilds }: { header?: string; settingsChilds?: string[]; }) {
const firstChild = settingsChilds?.[0];
// lowest two elements... sanity backup
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
const { settingsLocation } = Settings.plugins.Settings;
const { settingsLocation } = settings.store;
if (settingsLocation === "bottom") return firstChild === "LOGOUT";
if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG";
@ -203,21 +219,6 @@ export default definePlugin({
};
},
options: {
settingsLocation: {
type: OptionType.SELECT,
description: "Where to put the Vencord settings section",
options: [
{ label: "At the very top", value: "top" },
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
{ label: "Below the Nitro section", value: "belowNitro" },
{ label: "Above Activity Settings", value: "aboveActivity" },
{ label: "Below Activity Settings", value: "belowActivity" },
{ label: "At the very bottom", value: "bottom" },
]
},
},
get electronVersion() {
return VencordNative.native.getVersions().electron || window.armcord?.electron || null;
},

View file

@ -16,6 +16,7 @@
* 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";
@ -28,16 +29,17 @@ export default definePlugin({
find: "BAN_CONFIRM_TITLE.",
replacement: {
match: /src:\i\("\d+"\)/g,
replace: "src: Vencord.Settings.plugins.BANger.source"
replace: "src: $self.settings.store.source"
}
}
],
options: {
settings: definePluginSettings({
source: {
description: "Source to replace ban GIF with (Video or Gif)",
type: OptionType.STRING,
default: "https://i.imgur.com/wp5q52C.mp4",
restartNeeded: true,
}
}
})
});

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { canonicalizeMatch } from "@utils/patches";
@ -25,10 +25,26 @@ import { findByProps } from "@webpack";
const UserPopoutSectionCssClasses = findByProps("section", "lastSection");
const settings = definePluginSettings({
hide: {
type: OptionType.BOOLEAN,
description: "Hide notes",
default: false,
restartNeeded: true
},
noSpellCheck: {
type: OptionType.BOOLEAN,
description: "Disable spellcheck in notes",
disabled: () => settings.store.hide,
default: false
}
});
export default definePlugin({
name: "BetterNotesBox",
description: "Hide notes or disable spellcheck (Configure in settings!!)",
authors: [Devs.Ven],
settings,
patches: [
{
@ -36,7 +52,7 @@ export default definePlugin({
all: true,
// Some modules match the find but the replacement is returned untouched
noWarn: true,
predicate: () => Vencord.Settings.plugins.BetterNotesBox.hide,
predicate: () => settings.store.hide,
replacement: {
match: /hideNote:.+?(?=([,}].*?\)))/g,
replace: (m, rest) => {
@ -54,7 +70,7 @@ export default definePlugin({
find: "Messages.NOTE_PLACEHOLDER",
replacement: {
match: /\.NOTE_PLACEHOLDER,/,
replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck,"
replace: "$&spellCheck:!$self.settings.store.noSpellCheck,"
}
},
{
@ -66,21 +82,6 @@ export default definePlugin({
}
],
options: {
hide: {
type: OptionType.BOOLEAN,
description: "Hide notes",
default: false,
restartNeeded: true
},
noSpellCheck: {
type: OptionType.BOOLEAN,
description: "Disable spellcheck in notes",
disabled: () => Settings.plugins.BetterNotesBox.hide,
default: false
}
},
patchPadding: ErrorBoundary.wrap(({ lastSection }) => {
if (!lastSection) return null;
return (

View file

@ -16,16 +16,32 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { Clipboard, Toasts } from "@webpack/common";
const settings = definePluginSettings({
bothStyles: {
type: OptionType.BOOLEAN,
description: "Show both role dot and coloured names",
restartNeeded: true,
default: false,
},
copyRoleColorInProfilePopout: {
type: OptionType.BOOLEAN,
description: "Allow click on role dot in profile popout to copy role color",
restartNeeded: true,
default: false
}
});
export default definePlugin({
name: "BetterRoleDot",
authors: [Devs.Ven, Devs.AutumnVN],
description:
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
settings,
patches: [
{
@ -39,7 +55,7 @@ export default definePlugin({
find: '"dot"===',
all: true,
noWarn: true,
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
predicate: () => settings.store.bothStyles,
replacement: {
match: /"(?:username|dot)"===\i(?!\.\i)/g,
replace: "true",
@ -49,7 +65,7 @@ export default definePlugin({
{
find: ".ADD_ROLE_A11Y_LABEL",
all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
noWarn: true,
replacement: {
match: /"dot"===\i/,
@ -59,7 +75,7 @@ export default definePlugin({
{
find: ".roleVerifiedIcon",
all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
noWarn: true,
replacement: {
match: /"dot"===\i/,
@ -68,21 +84,6 @@ export default definePlugin({
}
],
options: {
bothStyles: {
type: OptionType.BOOLEAN,
description: "Show both role dot and coloured names",
restartNeeded: true,
default: false,
},
copyRoleColorInProfilePopout: {
type: OptionType.BOOLEAN,
description: "Allow click on role dot in profile popout to copy role color",
restartNeeded: true,
default: false
}
},
copyToClipBoard(color: string) {
Clipboard.copy(color);
Toasts.show({

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
@ -26,7 +26,7 @@ function setCss() {
style.textContent = `
.vc-nsfw-img [class^=imageWrapper] img,
.vc-nsfw-img [class^=wrapperPaused] video {
filter: blur(${Settings.plugins.BlurNSFW.blurAmount}px);
filter: blur(${settings.store.blurAmount}px);
transition: filter 0.2s;
}
.vc-nsfw-img [class^=imageWrapper]:hover img,
@ -36,10 +36,20 @@ function setCss() {
`;
}
const settings = definePluginSettings({
blurAmount: {
type: OptionType.NUMBER,
description: "Blur Amount",
default: 10,
onChange: setCss
}
});
export default definePlugin({
name: "BlurNSFW",
description: "Blur attachments in NSFW channels until hovered",
authors: [Devs.Ven],
settings,
patches: [
{
@ -51,15 +61,6 @@ export default definePlugin({
}
],
options: {
blurAmount: {
type: OptionType.NUMBER,
description: "Blur Amount",
default: 10,
onChange: setCss
}
},
start() {
style = document.createElement("style");
style.id = "VcBlurNsfw";

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { useTimer } from "@utils/react";
@ -25,7 +25,7 @@ import { React } from "@webpack/common";
function formatDuration(ms: number) {
// here be dragons (moment fucking sucks)
const human = Settings.plugins.CallTimer.format === "human";
const human = settings.store.format === "human";
const format = (n: number) => human ? n : n.toString().padStart(2, "0");
const unit = (s: string) => human ? s : "";
@ -46,15 +46,7 @@ function formatDuration(ms: number) {
return res;
}
export default definePlugin({
name: "CallTimer",
description: "Adds a timer to vcs",
authors: [Devs.Ven],
startTime: 0,
interval: void 0 as NodeJS.Timeout | undefined,
options: {
const settings = definePluginSettings({
format: {
type: OptionType.SELECT,
description: "The timer format. This can be any valid moment.js format",
@ -70,7 +62,16 @@ export default definePlugin({
}
]
}
},
});
export default definePlugin({
name: "CallTimer",
description: "Adds a timer to vcs",
authors: [Devs.Ven],
settings,
startTime: 0,
interval: void 0 as NodeJS.Timeout | undefined,
patches: [{
find: "renderConnectionStatus(){",

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings, Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { ErrorCard } from "@components/ErrorCard";
import { Link } from "@components/Link";
import { Devs } from "@utils/constants";
@ -258,7 +258,7 @@ const settings = definePluginSettings({
function onChange() {
setRpc(true);
if (Settings.plugins.CustomRPC.enabled) setRpc();
if (Vencord.Plugins.isPluginEnabled("CustomRPC")) setRpc();
}
function isStreamLinkDisabled() {

View file

@ -21,20 +21,25 @@ import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByProps, findStore } from "@webpack";
import { ChannelStore, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common";
import { Settings } from "Vencord";
const UserAffinitiesStore = findStore("UserAffinitiesStore");
const { FriendsSections } = findByProps("FriendsSections");
interface UserAffinity {
user_id: string;
affinity: number;
const settings = definePluginSettings({
sortByAffinity: {
type: OptionType.BOOLEAN,
default: true,
description: "Whether to sort implicit relationships by their affinity to you.",
restartNeeded: true
}
});
export default definePlugin({
name: "ImplicitRelationships",
description: "Shows your implicit relationships in the Friends tab.",
authors: [Devs.Dolfies],
settings,
patches: [
// Counts header
{
@ -81,7 +86,7 @@ export default definePlugin({
{
find: "getRelationshipCounts(){",
replacement: {
predicate: () => Settings.plugins.ImplicitRelationships.sortByAffinity,
predicate: () => settings.store.sortByAffinity,
match: /\}\)\.sortBy\((.+?)\)\.value\(\)/,
replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()"
}
@ -110,16 +115,6 @@ export default definePlugin({
},
}
],
settings: definePluginSettings(
{
sortByAffinity: {
type: OptionType.BOOLEAN,
default: true,
description: "Whether to sort implicit relationships by their affinity to you.",
restartNeeded: true
},
}
),
wrapSort(comparator: Function, row: any) {
return row.type === 5

View file

@ -20,7 +20,7 @@ import "./messageLogger.css";
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { updateMessage } from "@api/MessageUpdater";
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
@ -41,7 +41,7 @@ interface MLMessage extends Message {
const styles = findByProps("edited", "communicationDisabled", "isSystemMessage");
function addDeleteStyle() {
if (Settings.plugins.MessageLogger.deleteStyle === "text") {
if (settings.store.deleteStyle === "text") {
enableStyle(textStyle);
disableStyle(overlayStyle);
} else {
@ -125,57 +125,7 @@ const patchChannelContextMenu: NavContextMenuPatchCallback = (children, { channe
);
};
export default definePlugin({
name: "MessageLogger",
description: "Temporarily logs deleted and edited messages.",
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux],
dependencies: ["MessageUpdaterAPI"],
contextMenus: {
"message": patchMessageContextMenu,
"channel-context": patchChannelContextMenu,
"user-context": patchChannelContextMenu,
"gdm-context": patchChannelContextMenu
},
start() {
addDeleteStyle();
},
renderEdits: ErrorBoundary.wrap(({ message: { id: messageId, channel_id: channelId } }: { message: Message; }) => {
const message = useStateFromStores(
[MessageStore],
() => MessageStore.getMessage(channelId, messageId) as MLMessage,
null,
(oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory
);
return (
<>
{message.editHistory?.map(edit => (
<div className="messagelogger-edited">
{Parser.parse(edit.content)}
<Timestamp
timestamp={edit.timestamp}
isEdited={true}
isInline={false}
>
<span className={styles.edited}>{" "}({i18n.Messages.MESSAGE_EDITED})</span>
</Timestamp>
</div>
))}
</>
);
}, { noop: true }),
makeEdit(newMessage: any, oldMessage: any): any {
return {
timestamp: new Date(newMessage.edited_timestamp),
content: oldMessage.content
};
},
options: {
const settings = definePluginSettings({
deleteStyle: {
type: OptionType.SELECT,
description: "The style of deleted messages",
@ -221,6 +171,57 @@ export default definePlugin({
description: "Comma-separated list of guild IDs to ignore",
default: ""
},
});
export default definePlugin({
name: "MessageLogger",
description: "Temporarily logs deleted and edited messages.",
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux],
dependencies: ["MessageUpdaterAPI"],
settings,
contextMenus: {
"message": patchMessageContextMenu,
"channel-context": patchChannelContextMenu,
"user-context": patchChannelContextMenu,
"gdm-context": patchChannelContextMenu
},
start() {
addDeleteStyle();
},
renderEdits: ErrorBoundary.wrap(({ message: { id: messageId, channel_id: channelId } }: { message: Message; }) => {
const message = useStateFromStores(
[MessageStore],
() => MessageStore.getMessage(channelId, messageId) as MLMessage,
null,
(oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory
);
return (
<>
{message.editHistory?.map(edit => (
<div className="messagelogger-edited">
{Parser.parse(edit.content)}
<Timestamp
timestamp={edit.timestamp}
isEdited={true}
isInline={false}
>
<span className={styles.edited}>{" "}({i18n.Messages.MESSAGE_EDITED})</span>
</Timestamp>
</div>
))}
</>
);
}, { noop: true }),
makeEdit(newMessage: any, oldMessage: any): any {
return {
timestamp: new Date(newMessage.edited_timestamp),
content: oldMessage.content
};
},
handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) {
@ -257,7 +258,7 @@ export default definePlugin({
},
shouldIgnore(message: any, isEdit = false) {
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = Settings.plugins.MessageLogger;
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = settings.store;
const myId = UserStore.getCurrentUser().id;
return ignoreBots && message.author?.bot ||

View file

@ -18,7 +18,7 @@
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands";
import * as DataStore from "@api/DataStore";
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
@ -60,7 +60,7 @@ function createTagCommand(tag: Tag) {
return { content: `/${tag.name}` };
}
if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, {
if (settings.store.clyde) sendBotMessage(ctx.channel.id, {
content: `${EMOTE} The tag **${tag.name}** has been sent!`
});
return { content: tag.message.replaceAll("\\n", "\n") };
@ -69,19 +69,21 @@ function createTagCommand(tag: Tag) {
}, "CustomTags");
}
export default definePlugin({
name: "MessageTags",
description: "Allows you to save messages and to use them with a simple command.",
authors: [Devs.Luna],
options: {
const settings = definePluginSettings({
clyde: {
name: "Clyde message on send",
description: "If enabled, clyde will send you an ephemeral message when a tag was used.",
type: OptionType.BOOLEAN,
default: true
}
},
});
export default definePlugin({
name: "MessageTags",
description: "Allows you to save messages and to use them with a simple command.",
authors: [Devs.Luna],
settings,
dependencies: ["CommandsAPI"],
async start() {

View file

@ -16,17 +16,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByProps } from "@webpack";
const RelationshipStore = findByProps("getRelationships", "isBlocked");
const settings = definePluginSettings({
ignoreBlockedMessages: {
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true,
},
});
export default definePlugin({
name: "NoBlockedMessages",
description: "Hides all blocked messages from chat completely.",
authors: [Devs.rushii, Devs.Samu],
settings,
patches: [
{
find: "Messages.BLOCKED_MESSAGES_HIDE",
@ -42,7 +53,7 @@ export default definePlugin({
'"displayName","ReadStateStore")'
].map(find => ({
find,
predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true,
predicate: () => settings.store.ignoreBlockedMessages === true,
replacement: [
{
match: /(?<=MESSAGE_CREATE:function\((\i)\){)/,
@ -51,14 +62,6 @@ export default definePlugin({
]
}))
],
options: {
ignoreBlockedMessages: {
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true,
},
},
isBlocked: message =>
RelationshipStore.isBlocked(message.author.id)
});

View file

@ -19,7 +19,7 @@
import { addBadge, BadgePosition, ProfileBadge, removeBadge } from "@api/Badges";
import { addDecorator, removeDecorator } from "@api/MemberListDecorators";
import { addDecoration, removeDecoration } from "@api/MessageDecorations";
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
@ -162,27 +162,34 @@ const indicatorLocations = {
}
};
const settings = definePluginSettings({
...Object.fromEntries(
Object.entries(indicatorLocations).map(([key, value]) => {
return [key, {
type: OptionType.BOOLEAN,
description: `Show indicators ${value.description.toLowerCase()}`,
// onChange doesn't give any way to know which setting was changed, so restart required
restartNeeded: true,
default: true
}];
})
) as Record<"list" | "badges" | "messages", { type: OptionType.BOOLEAN; description: string; restartNeeded: boolean; default: boolean; }>,
colorMobileIndicator: {
type: OptionType.BOOLEAN,
description: "Whether to make the mobile indicator match the color of the user status.",
default: true,
restartNeeded: true
}
});
export default definePlugin({
name: "PlatformIndicators",
description: "Adds platform indicators (Desktop, Mobile, Web...) to users",
authors: [Devs.kemo, Devs.TheSun, Devs.Nuckyz, Devs.Ven],
dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"],
settings,
start() {
const settings = Settings.plugins.PlatformIndicators;
const { displayMode } = settings;
// transfer settings from the old ones, which had a select menu instead of booleans
if (displayMode) {
if (displayMode !== "both") settings[displayMode] = true;
else {
settings.list = true;
settings.badges = true;
}
settings.messages = true;
delete settings.displayMode;
}
Object.entries(indicatorLocations).forEach(([key, value]) => {
if (settings[key]) value.onEnable();
});
@ -197,7 +204,7 @@ export default definePlugin({
patches: [
{
find: ".Masks.STATUS_ONLINE_MOBILE",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator,
predicate: () => settings.store.colorMobileIndicator,
replacement: [
{
// Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status
@ -213,7 +220,7 @@ export default definePlugin({
},
{
find: ".AVATAR_STATUS_MOBILE_16;",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator,
predicate: () => settings.store.colorMobileIndicator,
replacement: [
{
// Return the AVATAR_STATUS_MOBILE size mask if the user is on mobile, no matter the status
@ -234,32 +241,12 @@ export default definePlugin({
},
{
find: "}isMobileOnline(",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator,
predicate: () => settings.store.colorMobileIndicator,
replacement: {
// Make isMobileOnline return true no matter what is the user status
match: /(?<=\i\[\i\.\i\.MOBILE\])===\i\.\i\.ONLINE/,
replace: "!= null"
}
}
],
options: {
...Object.fromEntries(
Object.entries(indicatorLocations).map(([key, value]) => {
return [key, {
type: OptionType.BOOLEAN,
description: `Show indicators ${value.description.toLowerCase()}`,
// onChange doesn't give any way to know which setting was changed, so restart required
restartNeeded: true,
default: true
}];
})
),
colorMobileIndicator: {
type: OptionType.BOOLEAN,
description: "Whether to make the mobile indicator match the color of the user status.",
default: true,
restartNeeded: true
}
}
]
});

View file

@ -16,7 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { debounce } from "@shared/debounce";
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
import { getCurrentChannel } from "@utils/discord";
@ -147,11 +146,11 @@ async function bulkFetchPronouns(ids: string[]): Promise<PronounsResponse> {
}
}
export function extractPronouns(pronounSet?: { [locale: string]: PronounCode[] }): string {
export function extractPronouns(pronounSet?: { [locale: string]: PronounCode[]; }): string {
if (!pronounSet || !pronounSet.en) return PronounMapping.unspecified;
// PronounDB returns an empty set instead of {sets: {en: ["unspecified"]}}.
const pronouns = pronounSet.en;
const { pronounsFormat } = Settings.plugins.PronounDB as { pronounsFormat: PronounsFormat, enabled: boolean; };
const { pronounsFormat } = settings.store;
if (pronouns.length === 1) {
// For capitalized pronouns or special codes (any, ask, avoid), we always return the normal (capitalized) string

View file

@ -17,7 +17,7 @@
*/
import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList";
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { useForceUpdater } from "@utils/react";
@ -89,13 +89,7 @@ function handleGuildUpdate() {
forceUpdateGuildCount?.();
}
export default definePlugin({
name: "ServerListIndicators",
description: "Add online friend count or server count in the server list",
authors: [Devs.dzshn],
dependencies: ["ServerListAPI"],
options: {
const settings = definePluginSettings({
mode: {
description: "mode",
type: OptionType.SELECT,
@ -105,10 +99,17 @@ export default definePlugin({
{ label: "Both server and online friend counts", value: IndicatorType.BOTH },
]
}
},
});
export default definePlugin({
name: "ServerListIndicators",
description: "Add online friend count or server count in the server list",
authors: [Devs.dzshn],
dependencies: ["ServerListAPI"],
settings,
renderIndicator: () => {
const { mode } = Settings.plugins.ServerListIndicators;
const { mode } = settings.store;
return <ErrorBoundary noop>
<div style={{ marginBottom: "4px" }}>
{!!(mode & IndicatorType.FRIEND) && <FriendsIndicator />}

View file

@ -16,7 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { formatDuration } from "@utils/text";
import { findByProps, findComponentByCode } from "@webpack";
@ -157,7 +156,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
});
}
if (Settings.plugins.PermissionsViewer.enabled) {
if (Vencord.Plugins.isPluginEnabled("PermissionsViewer")) {
setPermissions(sortPermissionOverwrites(Object.values(permissionOverwrites).map(overwrite => ({
type: overwrite.type as PermissionType,
id: overwrite.id,
@ -274,7 +273,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
}
<div className="shc-lock-screen-allowed-users-and-roles-container">
<div className="shc-lock-screen-allowed-users-and-roles-container-title">
{Settings.plugins.PermissionsViewer.enabled && (
{Vencord.Plugins.isPluginEnabled("PermissionsViewer") && (
<Tooltip text="Permission Details">
{({ onMouseLeave, onMouseEnter }) => (
<button

View file

@ -16,10 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { proxyLazy } from "@utils/lazy";
import { findByProps, webpackDependantLazy } from "@webpack";
import { Flux, FluxDispatcher } from "@webpack/common";
// Avoid circular dependency
const { settings } = proxyLazy(() => require(".")) as typeof import(".");
export interface Track {
id: string;
name: string;
@ -88,7 +91,7 @@ export const SpotifyStore = webpackDependantLazy(() => {
public isSettingPosition = false;
public openExternal(path: string) {
const url = Settings.plugins.SpotifyControls.useSpotifyUris || Vencord.Plugins.isPluginEnabled("OpenInApp")
const url = settings.store.useSpotifyUris || Vencord.Plugins.isPluginEnabled("OpenInApp")
? "spotify:" + path.replaceAll("/", (_, idx) => idx === 0 ? "" : ":")
: "https://open.spotify.com" + path;

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
@ -29,11 +29,7 @@ function toggleHoverControls(value: boolean) {
(value ? enableStyle : disableStyle)(hoverOnlyStyle);
}
export default definePlugin({
name: "SpotifyControls",
description: "Adds a Spotify player above the account panel",
authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000],
options: {
export const settings = definePluginSettings({
hoverControls: {
description: "Show controls on hover",
type: OptionType.BOOLEAN,
@ -45,7 +41,14 @@ export default definePlugin({
description: "Open Spotify URIs instead of Spotify URLs. Will only work if you have Spotify installed and might not work on all platforms",
default: false
}
},
});
export default definePlugin({
name: "SpotifyControls",
description: "Adds a Spotify player above the account panel",
authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000],
settings,
patches: [
{
find: "showTaglessAccountPanel:",
@ -87,7 +90,7 @@ export default definePlugin({
}
],
start: () => toggleHoverControls(Settings.plugins.SpotifyControls.hoverControls),
start: () => toggleHoverControls(settings.store.hoverControls),
PanelWrapper({ VencordOriginal, ...props }) {
return (

View file

@ -18,7 +18,7 @@
import "./style.css";
import { definePluginSettings, Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
@ -87,7 +87,7 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
break;
}
default: {
tooltipText = Settings.plugins.TypingTweaks.enabled
tooltipText = Vencord.Plugins.isPluginEnabled("TypingTweaks")
? buildSeveralUsers({ a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]), count: typingUsersArray.length - 2 })
: i18n.Messages.SEVERAL_USERS_TYPING;
break;

View file

@ -16,13 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/Settings";
import { definePluginSettings } from "@api/Settings";
import { ErrorCard } from "@components/ErrorCard";
import { Devs } from "@utils/constants";
import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { wordsToTitle } from "@utils/text";
import definePlugin, { OptionType, PluginOptionsItem, ReporterTestable } from "@utils/types";
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
import { findByProps } from "@webpack";
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
@ -42,25 +43,25 @@ const VoiceStateStore = findByProps("getVoiceStatesForChannel", "getCurrentClien
// Filtering out events is not as simple as just dropping duplicates, as otherwise mute, unmute, mute would
// not say the second mute, which would lead you to believe they're unmuted
function speak(text: string, settings: any = Settings.plugins.VcNarrator) {
function speak(text: string, mergedSettings: typeof settings.store = settings.store) {
if (!text) return;
const speech = new SpeechSynthesisUtterance(text);
let voice = speechSynthesis.getVoices().find(v => v.voiceURI === settings.voice);
let voice = speechSynthesis.getVoices().find(v => v.voiceURI === mergedSettings.voice);
if (!voice) {
new Logger("VcNarrator").error(`Voice "${settings.voice}" not found. Resetting to default.`);
new Logger("VcNarrator").error(`Voice "${mergedSettings.voice}" not found. Resetting to default.`);
voice = speechSynthesis.getVoices().find(v => v.default);
settings.voice = voice?.voiceURI;
mergedSettings.voice = voice?.voiceURI as string;
if (!voice) return; // This should never happen
}
speech.voice = voice!;
speech.volume = settings.volume;
speech.rate = settings.rate;
speech.voice = voice;
speech.volume = mergedSettings.volume;
speech.rate = mergedSettings.rate;
speechSynthesis.speak(speech);
}
function clean(str: string) {
const replacer = Settings.plugins.VcNarrator.latinOnly
const replacer = settings.store.latinOnly
? /[^\p{Script=Latin}\p{Number}\p{Punctuation}\s]/gu
: /[^\p{Letter}\p{Number}\p{Punctuation}\s]/gu;
@ -143,92 +144,23 @@ function updateStatuses(type: string, { deaf, mute, selfDeaf, selfMute, userId,
}
*/
function playSample(tempSettings: any, type: string) {
const settings = Object.assign({}, Settings.plugins.VcNarrator, tempSettings);
function playSample(tempSettings: typeof settings.store, type: string) {
const mergedSettings = Object.assign({}, settings.store, tempSettings);
const currentUser = UserStore.getCurrentUser();
const myGuildId = SelectedGuildStore.getGuildId();
speak(formatText(settings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username, GuildMemberStore.getNick(myGuildId, currentUser.id) ?? currentUser.username), settings);
speak(formatText(mergedSettings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username, GuildMemberStore.getNick(myGuildId, currentUser.id) ?? currentUser.username), mergedSettings);
}
export default definePlugin({
name: "VcNarrator",
description: "Announces when users join, leave, or move voice channels via narrator",
authors: [Devs.Ven],
reporterTestable: ReporterTestable.None,
flux: {
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
const myGuildId = SelectedGuildStore.getGuildId();
const myChanId = SelectedChannelStore.getVoiceChannelId();
const myId = UserStore.getCurrentUser().id;
if (ChannelStore.getChannel(myChanId!)?.type === 13 /* Stage Channel */) return;
for (const state of voiceStates) {
const { userId, channelId, oldChannelId } = state;
const isMe = userId === myId;
if (!isMe) {
if (!myChanId) continue;
if (channelId !== myChanId && oldChannelId !== myChanId) continue;
}
const [type, id] = getTypeAndChannelId(state, isMe);
if (!type) continue;
const template = Settings.plugins.VcNarrator[type + "Message"];
const user = isMe && !Settings.plugins.VcNarrator.sayOwnName ? "" : UserStore.getUser(userId).username;
const displayName = user && ((UserStore.getUser(userId) as any).globalName ?? user);
const nickname = user && (GuildMemberStore.getNick(myGuildId, userId) ?? user);
const channel = ChannelStore.getChannel(id).name;
speak(formatText(template, user, channel, displayName, nickname));
// updateStatuses(type, state, isMe);
}
},
AUDIO_TOGGLE_SELF_MUTE() {
const chanId = SelectedChannelStore.getVoiceChannelId()!;
const s = VoiceStateStore.getVoiceStateForChannel(chanId) as VoiceState;
if (!s) return;
const event = s.mute || s.selfMute ? "unmute" : "mute";
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
},
AUDIO_TOGGLE_SELF_DEAF() {
const chanId = SelectedChannelStore.getVoiceChannelId()!;
const s = VoiceStateStore.getVoiceStateForChannel(chanId) as VoiceState;
if (!s) return;
const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen";
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
}
},
start() {
if (typeof speechSynthesis === "undefined" || speechSynthesis.getVoices().length === 0) {
new Logger("VcNarrator").warn(
"SpeechSynthesis not supported or no Narrator voices found. Thus, this plugin will not work. Check my Settings for more info"
);
return;
}
},
optionsCache: null as Record<string, PluginOptionsItem> | null,
get options() {
return this.optionsCache ??= {
const settings = definePluginSettings({
voice: {
type: OptionType.SELECT,
description: "Narrator Voice",
options: window.speechSynthesis?.getVoices().map(v => ({
options: proxyLazy(() => window.speechSynthesis?.getVoices().map(v => ({
label: v.name,
value: v.voiceURI,
default: v.default
})) ?? []
})) ?? [])
},
volume: {
type: OptionType.SLIDER,
@ -289,7 +221,73 @@ export default definePlugin({
description: "Undeafen Message (only self for now)",
default: "{{USER}} undeafened"
}
};
});
export default definePlugin({
name: "VcNarrator",
description: "Announces when users join, leave, or move voice channels via narrator",
authors: [Devs.Ven],
reporterTestable: ReporterTestable.None,
settings,
flux: {
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
const myGuildId = SelectedGuildStore.getGuildId();
const myChanId = SelectedChannelStore.getVoiceChannelId();
const myId = UserStore.getCurrentUser().id;
if (ChannelStore.getChannel(myChanId!)?.type === 13 /* Stage Channel */) return;
for (const state of voiceStates) {
const { userId, channelId, oldChannelId } = state;
const isMe = userId === myId;
if (!isMe) {
if (!myChanId) continue;
if (channelId !== myChanId && oldChannelId !== myChanId) continue;
}
const [type, id] = getTypeAndChannelId(state, isMe);
if (!type) continue;
const template = settings.store[type + "Message"];
const user = isMe && !settings.store.sayOwnName ? "" : UserStore.getUser(userId).username;
const displayName = user && ((UserStore.getUser(userId) as any).globalName ?? user);
const nickname = user && (GuildMemberStore.getNick(myGuildId, userId) ?? user);
const channel = ChannelStore.getChannel(id).name;
speak(formatText(template, user, channel, displayName, nickname));
// updateStatuses(type, state, isMe);
}
},
AUDIO_TOGGLE_SELF_MUTE() {
const chanId = SelectedChannelStore.getVoiceChannelId()!;
const s = VoiceStateStore.getVoiceStateForChannel(chanId) as VoiceState;
if (!s) return;
const event = s.mute || s.selfMute ? "unmute" : "mute";
speak(formatText(settings.store[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
},
AUDIO_TOGGLE_SELF_DEAF() {
const chanId = SelectedChannelStore.getVoiceChannelId()!;
const s = VoiceStateStore.getVoiceStateForChannel(chanId) as VoiceState;
if (!s) return;
const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen";
speak(formatText(settings.store[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
}
},
start() {
if (typeof speechSynthesis === "undefined" || speechSynthesis.getVoices().length === 0) {
new Logger("VcNarrator").warn(
"SpeechSynthesis not supported or no Narrator voices found. Thus, this plugin will not work. Check my Settings for more info"
);
return;
}
},
settingsAboutComponent({ tempSettings: s }) {
@ -299,7 +297,7 @@ export default definePlugin({
}, []);
const types = useMemo(
() => Object.keys(Vencord.Plugins.plugins.VcNarrator.options!).filter(k => k.endsWith("Message")).map(k => k.slice(0, -7)),
() => Object.keys(settings.def).filter(k => k.endsWith("Message")).map(k => k.slice(0, -7)),
[],
);
@ -335,7 +333,7 @@ export default definePlugin({
className={"vc-narrator-buttons"}
>
{types.map(t => (
<Button key={t} onClick={() => playSample(s, t)}>
<Button key={t} onClick={() => playSample(s as typeof settings.store, t)}>
{wordsToTitle([t])}
</Button>
))}

View file

@ -124,6 +124,7 @@ export type Permissions = "ADD_REACTIONS"
| "USE_APPLICATION_COMMANDS"
| "USE_CLYDE_AI"
| "USE_EMBEDDED_ACTIVITIES"
| "USE_EXTERNAL_APPS"
| "USE_EXTERNAL_EMOJIS"
| "USE_EXTERNAL_SOUNDS"
| "USE_EXTERNAL_STICKERS"