Merge remote-tracking branch 'upstream/dev'

This commit is contained in:
thororen1234 2024-05-20 15:39:21 -04:00
commit 9d0550bf79
43 changed files with 644 additions and 248 deletions

View file

@ -55,7 +55,6 @@ export default definePlugin({
}
]
},
// Discord Canary
{
find: "Messages.ACTIVITY_SETTINGS",
replacement: {

View file

@ -0,0 +1,5 @@
# CustomIdle
Lets you change the time until your status gets automatically set to idle. You can also prevent idling altogether.
![Plugin Configuration](https://github.com/Vendicated/Vencord/assets/45801973/4e5259b2-18e0-42e5-b69f-efc672ce1e0b)

View file

@ -0,0 +1,94 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Notices } from "@api/index";
import { definePluginSettings } from "@api/Settings";
import { makeRange } from "@components/PluginSettings/components";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { FluxDispatcher } from "@webpack/common";
const settings = definePluginSettings({
idleTimeout: {
description: "Minutes before Discord goes idle (0 to disable auto-idle)",
type: OptionType.SLIDER,
markers: makeRange(0, 60, 5),
default: 10,
stickToMarkers: false,
restartNeeded: true // Because of the setInterval patch
},
remainInIdle: {
description: "When you come back to Discord, remain idle until you confirm you want to go online",
type: OptionType.BOOLEAN,
default: true
}
});
export default definePlugin({
name: "CustomIdle",
description: "Allows you to set the time before Discord goes idle (or disable auto-idle)",
authors: [Devs.newwares],
settings,
patches: [
{
find: "IDLE_DURATION:function(){return",
replacement: {
match: /(IDLE_DURATION:function\(\){return )\i/,
replace: "$1$self.getIdleTimeout()"
}
},
{
find: 'type:"IDLE",idle:',
replacement: [
{
match: /Math\.min\((\i\.AfkTimeout\.getSetting\(\)\*\i\.default\.Millis\.SECOND),\i\.IDLE_DURATION\)/,
replace: "$1" // Decouple idle from afk (phone notifications will remain at user setting or 10 min maximum)
},
{
match: /\i\.default\.dispatch\({type:"IDLE",idle:!1}\)/,
replace: "$self.handleOnline()"
},
{
match: /(setInterval\(\i,\.25\*)\i\.IDLE_DURATION/,
replace: "$1$self.getIntervalDelay()" // For web installs
}
]
}
],
getIntervalDelay() {
return Math.min(6e5, this.getIdleTimeout());
},
handleOnline() {
if (!settings.store.remainInIdle) {
FluxDispatcher.dispatch({
type: "IDLE",
idle: false
});
return;
}
const backOnlineMessage = "Welcome back! Click the button to go online. Click the X to stay idle until reload.";
if (
Notices.currentNotice?.[1] === backOnlineMessage ||
Notices.noticesQueue.some(([, noticeMessage]) => noticeMessage === backOnlineMessage)
) return;
Notices.showNotice(backOnlineMessage, "Exit idle", () => {
Notices.popNotice();
FluxDispatcher.dispatch({
type: "IDLE",
idle: false
});
});
},
getIdleTimeout() { // milliseconds, default is 6e5
const { idleTimeout } = settings.store;
return idleTimeout === 0 ? Infinity : idleTimeout * 60000;
}
});

View file

@ -9,7 +9,6 @@ import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger";
import { openModal } from "@utils/modal";
import { OAuth2AuthorizeModal, showToast, Toasts, UserStore, zustandCreate, zustandPersist } from "@webpack/common";
import type { StateStorage } from "zustand/middleware";
import { AUTHORIZE_URL, CLIENT_ID } from "../constants";
@ -23,7 +22,7 @@ interface AuthorizationState {
isAuthorized: () => boolean;
}
const indexedDBStorage: StateStorage = {
const indexedDBStorage = {
async getItem(name: string): Promise<string | null> {
return DataStore.get(name).then(v => v ?? null);
},
@ -36,9 +35,9 @@ const indexedDBStorage: StateStorage = {
};
// TODO: Move switching accounts subscription inside the store?
export const useAuthorizationStore = proxyLazy(() => zustandCreate<AuthorizationState>(
export const useAuthorizationStore = proxyLazy(() => zustandCreate(
zustandPersist(
(set, get) => ({
(set: any, get: any) => ({
token: null,
tokens: {},
init: () => { set({ token: get().tokens[UserStore.getCurrentUser().id] ?? null }); },
@ -91,7 +90,7 @@ export const useAuthorizationStore = proxyLazy(() => zustandCreate<Authorization
));
},
isAuthorized: () => !!get().token,
}),
} as AuthorizationState),
{
name: "decor-auth",
getStorage: () => indexedDBStorage,

View file

@ -21,7 +21,7 @@ interface UserDecorationsState {
clear: () => void;
}
export const useCurrentUserDecorationsStore = proxyLazy(() => zustandCreate<UserDecorationsState>((set, get) => ({
export const useCurrentUserDecorationsStore = proxyLazy(() => zustandCreate((set: any, get: any) => ({
decorations: [],
selectedDecoration: null,
async fetch() {
@ -53,4 +53,4 @@ export const useCurrentUserDecorationsStore = proxyLazy(() => zustandCreate<User
useUsersDecorationsStore.getState().set(UserStore.getCurrentUser().id, decoration ? decorationToAsset(decoration) : null);
},
clear: () => set({ decorations: [], selectedDecoration: null })
})));
} as UserDecorationsState)));

View file

@ -30,7 +30,7 @@ interface UsersDecorationsState {
set: (userId: string, decoration: string | null) => void;
}
export const useUsersDecorationsStore = proxyLazy(() => zustandCreate<UsersDecorationsState>((set, get) => ({
export const useUsersDecorationsStore = proxyLazy(() => zustandCreate((set: any, get: any) => ({
usersDecorations: new Map<string, UserDecorationData>(),
fetchQueue: new Set(),
bulkFetch: debounce(async () => {
@ -40,7 +40,7 @@ export const useUsersDecorationsStore = proxyLazy(() => zustandCreate<UsersDecor
set({ fetchQueue: new Set() });
const fetchIds = Array.from(fetchQueue);
const fetchIds = [...fetchQueue];
const fetchedUsersDecorations = await getUsersDecorations(fetchIds);
const newUsersDecorations = new Map(usersDecorations);
@ -92,7 +92,7 @@ export const useUsersDecorationsStore = proxyLazy(() => zustandCreate<UsersDecor
newUsersDecorations.set(userId, { asset: decoration, fetchedAt: new Date() });
set({ usersDecorations: newUsersDecorations });
}
})));
} as UsersDecorationsState)));
export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined {
const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null);

View file

@ -15,7 +15,7 @@ import { openChangeDecorationModal } from "../modals/ChangeDecorationModal";
const CustomizationSection = findByCodeLazy(".customizationSectionBackground");
interface DecorSectionProps {
export interface DecorSectionProps {
hideTitle?: boolean;
hideDivider?: boolean;
noMargin?: boolean;

View file

@ -7,6 +7,8 @@
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { getCurrentChannel } from "@utils/discord";
import { Logger } from "@utils/Logger";
import { classes } from "@utils/misc";
import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Heading, React, RelationshipStore, Text } from "@webpack/common";
@ -22,6 +24,7 @@ export default definePlugin({
description: "Shows when you became friends with someone in the user popout",
authors: [Devs.Elvyra],
patches: [
// User popup
{
find: ".AnalyticsSections.USER_PROFILE}};",
replacement: {
@ -29,16 +32,34 @@ export default definePlugin({
replace: "$&,$self.friendsSince({ userId: $1 })"
}
},
// User DMs "User Profile" popup in the right
{
find: ".UserPopoutUpsellSource.PROFILE_PANEL,",
replacement: {
match: /\i.default,\{userId:(\i)}\)/,
replace: "$&,$self.friendsSince({ userId: $1 })"
}
},
// User Profile Modal
{
find: ".userInfoSectionHeader,",
replacement: {
match: /(\.Messages\.USER_PROFILE_MEMBER_SINCE.+?userId:(.+?),textClassName:)(\i\.userInfoText)}\)/,
replace: (_, rest, userId, textClassName) => `${rest}!$self.getFriendSince(${userId}) ? ${textClassName} : void 0 }), $self.friendsSince({ userId: ${userId}, textClassName: ${textClassName} })`
}
}
],
friendsSince: ErrorBoundary.wrap(({ userId }: { userId: string; }) => {
getFriendSince(userId: string) {
try {
return RelationshipStore.getSince(userId);
} catch (err) {
new Logger("FriendsSince").error(err);
return null;
}
},
friendsSince: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => {
const friendsSince = RelationshipStore.getSince(userId);
if (!friendsSince) return null;
@ -61,7 +82,7 @@ export default definePlugin({
<path d="M3 5v-.75C3 3.56 3.56 3 4.25 3s1.24.56 1.33 1.25C6.12 8.65 9.46 12 13 12h1a8 8 0 0 1 8 8 2 2 0 0 1-2 2 .21.21 0 0 1-.2-.15 7.65 7.65 0 0 0-1.32-2.3c-.15-.2-.42-.06-.39.17l.25 2c.02.15-.1.28-.25.28H9a2 2 0 0 1-2-2v-2.22c0-1.57-.67-3.05-1.53-4.37A15.85 15.85 0 0 1 3 5Z" />
</svg>
)}
<Text variant="text-sm/normal" className={clydeMoreInfo.body}>
<Text variant="text-sm/normal" className={classes(clydeMoreInfo.body, textClassName)}>
{getCreatedAtDate(friendsSince, locale.getLocale())}
</Text>
</div>

View file

@ -114,6 +114,11 @@ const settings = definePluginSettings({
type: OptionType.BOOLEAN,
default: false,
},
shareSong: {
description: "show link to song on last.fm",
type: OptionType.BOOLEAN,
default: true,
},
hideWithSpotify: {
description: "hide last.fm presence if spotify is running",
type: OptionType.BOOLEAN,
@ -295,12 +300,7 @@ export default definePlugin({
large_text: trackData.album || undefined,
};
const buttons: ActivityButton[] = [
{
label: "View Song",
url: trackData.url,
},
];
const buttons: ActivityButton[] = [];
if (settings.store.shareUsername)
buttons.push({
@ -308,6 +308,12 @@ export default definePlugin({
url: `https://www.last.fm/user/${settings.store.username}`,
});
if (settings.store.shareSong)
buttons.push({
label: "View Song",
url: trackData.url,
});
const statusName = (() => {
switch (settings.store.nameFormat) {
case NameFormat.ArtistFirst:
@ -333,7 +339,7 @@ export default definePlugin({
state: trackData.artist,
assets,
buttons: buttons.map(v => v.label),
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
metadata: {
button_urls: buttons.map(v => v.url),
},

View file

@ -227,10 +227,8 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
const accessories = [] as (JSX.Element | null)[];
let match = null as RegExpMatchArray | null;
while ((match = messageLinkRegex.exec(message.content!)) !== null) {
const [_, channelID, messageID] = match;
if (embeddedBy.includes(messageID)) {
for (const [_, channelID, messageID] of message.content!.matchAll(messageLinkRegex)) {
if (embeddedBy.includes(messageID) || embeddedBy.length > 2) {
continue;
}
@ -378,9 +376,6 @@ export default definePlugin({
if (!messageLinkRegex.test(props.message.content))
return null;
// need to reset the regex because it's global
messageLinkRegex.lastIndex = 0;
return (
<ErrorBoundary>
<MessageEmbedAccessory

View file

@ -354,6 +354,15 @@ export default definePlugin({
if (location === "chat" && !settings.tagSettings[tag.name].showInChat) continue;
if (location === "not-chat" && !settings.tagSettings[tag.name].showInNotChat) continue;
// If the owner tag is disabled, and the user is the owner of the guild,
// avoid adding other tags because the owner will always match the condition for them
if (
tag.name !== "OWNER" &&
GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id &&
(location === "chat" && !settings.tagSettings.OWNER.showInChat) ||
(location === "not-chat" && !settings.tagSettings.OWNER.showInNotChat)
) continue;
if (
tag.permissions?.some(perm => perms.includes(perm)) ||
(tag.condition?.(message!, user, channel))

View file

@ -17,7 +17,7 @@
*/
import ErrorBoundary from "@components/ErrorBoundary";
import ExpandableHeader from "@components/ExpandableHeader";
import { ExpandableHeader } from "@components/ExpandableHeader";
import { classes } from "@utils/misc";
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common";

View file

@ -15,7 +15,7 @@ const DefaultEngines = {
DuckDuckGo: "https://duckduckgo.com/",
Bing: "https://www.bing.com/search?q=",
Yahoo: "https://search.yahoo.com/search?p=",
Github: "https://github.com/search?q=",
GitHub: "https://github.com/search?q=",
Kagi: "https://kagi.com/search?q=",
Yandex: "https://yandex.com/search/?text=",
AOL: "https://search.aol.com/aol/search?q=",

View file

@ -135,7 +135,7 @@ export default definePlugin({
find: '"MessageActionCreators"',
replacement: {
match: /(?<=focusMessage\(\i\){.+?)(?=focus:{messageId:(\i)})/,
replace: "before:$1,"
replace: "after:$1,"
}
},
// Force Server Home instead of Server Guide

View file

@ -20,7 +20,7 @@ import "./style.css";
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import ErrorBoundary from "@components/ErrorBoundary";
import ExpandableHeader from "@components/ExpandableHeader";
import { ExpandableHeader } from "@components/ExpandableHeader";
import { OpenExternalIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";

View file

@ -436,7 +436,7 @@ export default definePlugin({
},
},
{
find: ".shouldCloseDefaultModals",
find: 'className:"channelMention",children',
replacement: {
// Show inside voice channel instead of trying to join them when clicking on a channel mention
match: /(?<=getChannel\(\i\);if\(null!=(\i))(?=.{0,100}?selectVoiceChannel)/,

View file

@ -28,7 +28,7 @@ export default definePlugin({
patches: [{
find: "Messages.ACTIVITY_SETTINGS",
replacement: {
match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\))/,
match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\)}\))/,
replace: (_, commaOrSemi, settings, elements) => "" +
`${commaOrSemi}${settings}?.[0]==="CHANGELOG"` +
`&&${elements}.push({section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage})`

View file

@ -48,7 +48,7 @@ export default definePlugin({
})),
{
// channel mentions
find: ".shouldCloseDefaultModals",
find: 'className:"channelMention",children',
replacement: {
match: /onClick:(\i)(?=,.{0,30}className:"channelMention".+?(\i)\.inContent)/,
replace: (_, onClick, props) => ""

View file

@ -212,8 +212,7 @@ export default definePlugin({
},
// Group DMs top small & large icon
{
find: ".recipients.length>=2",
all: true,
find: /\.recipients\.length>=2(?!<isMultiUserDM.{0,50})/,
replacement: {
match: /null==\i\.icon\?.+?src:(\(0,\i\.getChannelIconURL\).+?\))(?=[,}])/,
replace: (m, iconUrl) => `${m},onClick:()=>$self.openImage(${iconUrl})`