Merge branch 'dev'

This commit is contained in:
thororen1234 2024-08-20 11:09:35 -04:00
commit 5020f3b456
11 changed files with 343 additions and 104 deletions

View file

@ -4,8 +4,6 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend
![image](https://github.com/Equicord/Equicord/assets/78185467/81707ad9-3a04-4f76-88a0-60ee70684f81)
## WOMP WOMP
## Features
- Third-party plugins implemented into the main build.
@ -23,7 +21,7 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend
- Request for plugins from Discord.
<details>
<summary>Extra included plugins (122 additional plugins)</summary>
<summary>Extra included plugins (123 additional plugins)</summary>
- AllCallTimers by MaxHerbold and D3SOX
- AltKrispSwitch by newwares
@ -129,6 +127,7 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend
- TeX by Kyuuhachi
- TextToSpeech by Samwich
- ThemeLibrary by Fafa
- Timezones by Aria
- Title by Kyuuhachi
- TosuRPC by AutumnVN
- Translate+ by Prince527 (Using Translate by Ven)

View file

@ -6,7 +6,7 @@
import "./style.css";
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin from "@utils/types";
@ -38,6 +38,10 @@ export default definePlugin({
description: "Group your commonly visited channels in tabs, like a browser",
authors: [Devs.TheSun, Devs.TheKodeToad, EquicordDevs.keifufu, Devs.Nickyux],
dependencies: ["ContextMenuAPI"],
contextMenus: {
"channel-mention-context": contextMenuPatch,
"channel-context": contextMenuPatch
},
patches: [
// add the channel tab container at the top
{
@ -91,16 +95,6 @@ export default definePlugin({
settings,
start() {
addContextMenuPatch("channel-mention-context", contextMenuPatch);
addContextMenuPatch("channel-context", contextMenuPatch);
},
stop() {
removeContextMenuPatch("channel-mention-context", contextMenuPatch);
removeContextMenuPatch("channel-context", contextMenuPatch);
},
containerHeight: 0,
render({ currentChannel, children }: {

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin from "@utils/types";
import { Menu } from "@webpack/common";
@ -26,11 +26,9 @@ export default definePlugin({
name: "EmojiDumper",
description: "Context menu to dump and download a server's emojis.",
authors: [EquicordDevs.Cortex, Devs.Samwich, EquicordDevs.Woosh],
start() {
addContextMenuPatch(["guild-context", "guild-header-popout"], Patch);
},
stop() {
removeContextMenuPatch(["guild-context", "guild-header-popout"], Patch);
contextMenus: {
"guild-context": Patch,
"guild-header-popout": Patch
}
});

View file

@ -8,7 +8,6 @@ import { addChatBarButton, removeChatBarButton } from "@api/ChatButtons";
import { disableStyle, enableStyle } from "@api/Styles";
import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin from "@utils/types";
import { findExportedComponentLazy } from "@webpack";
import { FluxDispatcher } from "@webpack/common";
import { ChatBarIcon } from "./components/Icons";
@ -17,8 +16,6 @@ import { updateLoggedSounds } from "./store";
import styles from "./styles.css?managed";
import { getListeners } from "./utils";
const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider");
export default definePlugin({
name: "SoundBoardLogger",
authors: [Devs.Moxxie, EquicordDevs.Fres, Devs.echo, EquicordDevs.thororen],

View file

@ -0,0 +1,84 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import * as DataStore from "@api/DataStore";
import { classNameFactory } from "@api/Styles";
import { Margins } from "@utils/margins";
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
import { Button, Forms, SearchableSelect, useMemo, useState } from "@webpack/common";
import { DATASTORE_KEY, timezones } from ".";
export async function setUserTimezone(userId: string, timezone: string | null) {
timezones[userId] = timezone;
await DataStore.set(DATASTORE_KEY, timezones);
}
const cl = classNameFactory("vc-timezone-");
export function SetTimezoneModal({ userId, modalProps }: { userId: string, modalProps: ModalProps; }) {
const [currentValue, setCurrentValue] = useState<string | null>(timezones[userId] ?? null);
const options = useMemo(() => {
return Intl.supportedValuesOf("timeZone").map(timezone => {
const offset = new Intl.DateTimeFormat(undefined, { timeZone: timezone, timeZoneName: "short" })
.formatToParts(new Date())
.find(part => part.type === "timeZoneName")!.value;
return { label: `${timezone} (${offset})`, value: timezone };
});
}, []);
return (
<ModalRoot {...modalProps}>
<ModalHeader className={cl("modal-header")}>
<Forms.FormTitle tag="h2">
Timezones
</Forms.FormTitle>
<ModalCloseButton onClick={modalProps.onClose} />
</ModalHeader>
<ModalContent className={cl("modal-content")}>
<section className={Margins.bottom16}>
<Forms.FormTitle tag="h3">
Select Timezone
</Forms.FormTitle>
<SearchableSelect
options={options}
value={options.find(o => o.value === currentValue)}
placeholder={"Select a Timezone"}
maxVisibleItems={5}
closeOnSelect={true}
onChange={v => setCurrentValue(v)}
/>
</section>
</ModalContent>
<ModalFooter className={cl("modal-footer")}>
<Button
color={Button.Colors.RED}
onClick={async () => {
await setUserTimezone(userId, null);
modalProps.onClose();
}}
>
Delete Timezone
</Button>
<Button
color={Button.Colors.BRAND}
disabled={currentValue === null}
onClick={async () => {
await setUserTimezone(userId, currentValue!);
modalProps.onClose();
}}
>
Save
</Button>
</ModalFooter>
</ModalRoot>
);
}

View file

@ -0,0 +1,189 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./styles.css";
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import * as DataStore from "@api/DataStore";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { openModal } from "@utils/modal";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { i18n, Menu, Tooltip, useEffect, useState } from "@webpack/common";
import { Message, User } from "discord-types/general";
import { SetTimezoneModal } from "./TimezoneModal";
export const DATASTORE_KEY = "vencord-timezones";
export let timezones: Record<string, string | null> = {};
(async () => {
timezones = await DataStore.get<Record<string, string>>(DATASTORE_KEY) || {};
})();
const classes = findByPropsLazy("timestamp", "compact", "contentOnly");
export const settings = definePluginSettings({
"24h Time": {
type: OptionType.BOOLEAN,
description: "Show time in 24h format",
default: false
},
showMessageHeaderTime: {
type: OptionType.BOOLEAN,
description: "Show time in message headers",
default: true
},
showProfileTime: {
type: OptionType.BOOLEAN,
description: "Show time in profiles",
default: true
}
});
function getTime(timezone: string, timestamp: string | number, props: Intl.DateTimeFormatOptions = {}) {
const date = new Date(timestamp);
const formatter = new Intl.DateTimeFormat(i18n?.getLocale?.() ?? "en-US", {
hour12: !settings.store["24h Time"],
timeZone: timezone,
...props
});
return formatter.format(date);
}
interface Props {
userId: string;
timestamp?: string;
type: "message" | "profile";
}
const TimestampComponent = ErrorBoundary.wrap(({ userId, timestamp, type }: Props) => {
const [currentTime, setCurrentTime] = useState(timestamp || Date.now());
const timezone = timezones[userId];
useEffect(() => {
let timer: NodeJS.Timeout;
if (type === "profile") {
setCurrentTime(Date.now());
const now = new Date();
const delay = (60 - now.getSeconds()) * 1000 + 1000 - now.getMilliseconds();
timer = setTimeout(() => {
setCurrentTime(Date.now());
}, delay);
}
return () => timer && clearTimeout(timer);
}, [type, currentTime]);
if (!timezone) return null;
const shortTime = getTime(timezone, currentTime, { hour: "numeric", minute: "numeric" });
const longTime = getTime(timezone, currentTime, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
});
return (
<Tooltip
position="top"
// @ts-ignore
delay={750}
allowOverflow={false}
spacing={8}
hideOnClick={true}
tooltipClassName="timezone-tooltip"
text={longTime}
>
{toolTipProps => {
return (
<span
{...toolTipProps}
className={type === "message" ? `timezone-message-item ${classes.timestamp}` : "timezone-profile-item"}
>
{
type === "message" ? `(${shortTime})` : shortTime
}
</span>
);
}}
</Tooltip>
);
}, { noop: true });
const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { user: User; }) => {
if (user?.id == null) return;
const setTimezoneItem = (
<Menu.MenuItem
label="Set Timezone"
id="set-timezone"
action={() => openModal(modalProps => <SetTimezoneModal userId={user.id} modalProps={modalProps} />)}
/>
);
children.push(<Menu.MenuSeparator />, setTimezoneItem);
};
export default definePlugin({
name: "Timezone",
authors: [Devs.Aria],
description: "Shows the local time of users in profiles and message headers",
contextMenus: {
"user-context": userContextMenuPatch
},
patches: [
// stolen from ViewIcons
...[".NITRO_BANNER,", "=!1,canUsePremiumCustomization:"].map(find => ({
find,
replacement: {
match: /(?<=hasProfileEffect.+?)children:\[/,
replace: "$&$self.renderProfileTimezone(arguments[0]),"
}
})),
{
find: '"Message Username"',
replacement: {
// thanks https://github.com/Syncxv/vc-timezones/pull/4
match: /(?<=isVisibleOnlyOnHover.+?)id:.{1,11},timestamp.{1,50}}\),/,
replace: "$&,$self.renderMessageTimezone(arguments[0]),"
}
}
],
settings,
getTime,
renderProfileTimezone: (props?: { user?: User; }) => {
if (!settings.store.showProfileTime || !props?.user?.id) return null;
return <TimestampComponent
userId={props.user.id}
type="profile"
/>;
},
renderMessageTimezone: (props?: { message?: Message; }) => {
if (!settings.store.showMessageHeaderTime || !props?.message) return null;
return <TimestampComponent
userId={props.message.author.id}
timestamp={props.message.timestamp.toISOString()}
type="message"
/>;
}
});

View file

@ -0,0 +1,41 @@
.timezone-profile-item {
position: absolute;
right: 0;
bottom: 0;
margin: 28px 16px 4px;
background: var(--profile-body-background-color, var(--background-primary));
border-radius: 4px;
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
color: var(--text-normal);
}
[class*="topSection"] .timezone-profile-item {
margin: 16px;
}
.timezone-message-item {
margin-left: 4px;
}
.vc-timezone-modal-header {
place-content: center;
justify-content: space-between;
}
.vc-timezone-modal-header h1 {
margin: 0;
}
.vc-timezone-modal-content {
padding: 1em;
}
.vc-timezone-modal-footer {
gap: 16px;
}
.timezone-tooltip {
max-width: none!important;
white-space: nowrap
}

View file

@ -1,73 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Button, ChannelStore, Text } from "@webpack/common";
const { selectChannel } = findByPropsLazy("selectChannel", "selectVoiceChannel");
function jumpToMessage(channelId: string, messageId: string) {
const guildId = ChannelStore.getChannel(channelId)?.guild_id;
selectChannel({
guildId,
channelId,
messageId,
jumpType: "INSTANT"
});
}
function findChannelId(message: any): string | null {
const { embeds: [embed] } = message;
const channelField = embed.fields.find(({ rawName }) => rawName === "channel_id");
if (!channelField) {
return null;
}
return channelField.rawValue;
}
export default definePlugin({
name: "AutomodContext",
description: "Allows you to jump to the messages surrounding an automod hit.",
authors: [Devs.JohnyTheCarrot],
patches: [
{
find: ".Messages.GUILD_AUTOMOD_REPORT_ISSUES",
replacement: {
match: /\.Messages\.ACTIONS.+?}\)(?=,(\(0.{0,40}\.dot.*?}\)),)/,
replace: (m, dot) => `${m},${dot},$self.renderJumpButton({message:arguments[0].message})`
}
}
],
renderJumpButton: ErrorBoundary.wrap(({ message }: { message: any; }) => {
const channelId = findChannelId(message);
if (!channelId) {
return null;
}
return (
<Button
style={{ padding: "2px 8px" }}
look={Button.Looks.LINK}
size={Button.Sizes.SMALL}
color={Button.Colors.LINK}
onClick={() => jumpToMessage(channelId, message.id)}
>
<Text color="text-link" variant="text-xs/normal">
Jump to Surrounding
</Text>
</Button>
);
}, { noop: true })
});

View file

@ -35,6 +35,7 @@ export default definePlugin({
if (hasCtrl) switch (e.key) {
case "t":
case "T":
if (!IS_VESKTOP) return;
e.preventDefault();
if (e.shiftKey) {
if (SelectedGuildStore.getGuildId()) NavigationRouter.transitionToGuild("@me");
@ -47,14 +48,15 @@ export default definePlugin({
});
}
break;
case "Tab":
if (!IS_VESKTOP) return;
const handler = e.shiftKey ? KeyBinds.SERVER_PREV : KeyBinds.SERVER_NEXT;
handler.action(e);
break;
case ",":
e.preventDefault();
SettingsRouter.open("My Account");
break;
case "Tab":
const handler = e.shiftKey ? KeyBinds.SERVER_PREV : KeyBinds.SERVER_NEXT;
handler.action(e);
break;
default:
if (e.key >= "1" && e.key <= "9") {
e.preventDefault();

View file

@ -522,7 +522,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({
},
nyx: {
name: "verticalsync",
id: 328165170536775680n
id: 1207087393929171095n
},
nekohaxx: {
name: "nekohaxx",
@ -557,7 +557,7 @@ export const EquicordDevs = Object.freeze({
},
nyx: {
name: "verticalsync",
id: 328165170536775680n,
id: 1207087393929171095n,
},
Cortex: {
name: "Cortex",

View file

@ -355,8 +355,16 @@ function patchFactories(factories: Record<string, (module: any, exports: any, re
if (!patch.all) patches.splice(i--, 1);
}
if (mod !== originalMod) {
factory.$$vencordPatchedSource = String(mod);
if (IS_DEV) {
if (mod !== originalMod) {
factory.$$vencordPatchedSource = String(mod);
} else if (wreq != null) {
const existingFactory = wreq.m[id];
if (existingFactory != null) {
factory.$$vencordPatchedSource = existingFactory.$$vencordPatchedSource;
}
}
}
}
}