Merge remote-tracking branch 'upstream/dev' into immediate-finds
This commit is contained in:
commit
e04dd85957
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.8.2",
|
"version": "1.8.3",
|
||||||
"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": {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* 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 { mergeDefaults } from "@utils/misc";
|
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||||
import { findByProps } from "@webpack";
|
import { findByProps } from "@webpack";
|
||||||
import { MessageActions, SnowflakeUtils } from "@webpack/common";
|
import { MessageActions, SnowflakeUtils } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { debounce } from "@shared/debounce";
|
||||||
import { SettingsStore as SettingsStoreClass } from "@shared/SettingsStore";
|
import { SettingsStore as SettingsStoreClass } from "@shared/SettingsStore";
|
||||||
import { localStorage } from "@utils/localStorage";
|
import { localStorage } from "@utils/localStorage";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { mergeDefaults } from "@utils/misc";
|
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||||
import { putCloudSettings } from "@utils/settingsSync";
|
import { putCloudSettings } from "@utils/settingsSync";
|
||||||
import { DefinedSettings, OptionType, SettingsChecks, SettingsDefinition } from "@utils/types";
|
import { DefinedSettings, OptionType, SettingsChecks, SettingsDefinition } from "@utils/types";
|
||||||
import { React } from "@webpack/common";
|
import { React } from "@webpack/common";
|
||||||
|
|
|
@ -21,7 +21,7 @@ import "./addonCard.css";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { Badge } from "@components/Badge";
|
import { Badge } from "@components/Badge";
|
||||||
import { Switch } from "@components/Switch";
|
import { Switch } from "@components/Switch";
|
||||||
import { Text } from "@webpack/common";
|
import { Text, useRef } from "@webpack/common";
|
||||||
import type { MouseEventHandler, ReactNode } from "react";
|
import type { MouseEventHandler, ReactNode } from "react";
|
||||||
|
|
||||||
const cl = classNameFactory("vc-addon-");
|
const cl = classNameFactory("vc-addon-");
|
||||||
|
@ -42,6 +42,8 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddonCard({ disabled, isNew, name, infoButton, footer, author, enabled, setEnabled, description, onMouseEnter, onMouseLeave }: Props) {
|
export function AddonCard({ disabled, isNew, name, infoButton, footer, author, enabled, setEnabled, description, onMouseEnter, onMouseLeave }: Props) {
|
||||||
|
const titleRef = useRef<HTMLDivElement>(null);
|
||||||
|
const titleContainerRef = useRef<HTMLDivElement>(null);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cl("card", { "card-disabled": disabled })}
|
className={cl("card", { "card-disabled": disabled })}
|
||||||
|
@ -51,7 +53,21 @@ export function AddonCard({ disabled, isNew, name, infoButton, footer, author, e
|
||||||
<div className={cl("header")}>
|
<div className={cl("header")}>
|
||||||
<div className={cl("name-author")}>
|
<div className={cl("name-author")}>
|
||||||
<Text variant="text-md/bold" className={cl("name")}>
|
<Text variant="text-md/bold" className={cl("name")}>
|
||||||
{name}{isNew && <Badge text="NEW" color="#ED4245" />}
|
<div ref={titleContainerRef} className={cl("title-container")}>
|
||||||
|
<div
|
||||||
|
ref={titleRef}
|
||||||
|
className={cl("title")}
|
||||||
|
onMouseOver={() => {
|
||||||
|
const title = titleRef.current!;
|
||||||
|
const titleContainer = titleContainerRef.current!;
|
||||||
|
|
||||||
|
title.style.setProperty("--offset", `${titleContainer.clientWidth - title.scrollWidth}px`);
|
||||||
|
title.style.setProperty("--duration", `${Math.max(0.5, (title.scrollWidth - titleContainer.clientWidth) / 7)}s`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
</div>{isNew && <Badge text="NEW" color="#ED4245" />}
|
||||||
</Text>
|
</Text>
|
||||||
{!!author && (
|
{!!author && (
|
||||||
<Text variant="text-md/normal" className={cl("author")}>
|
<Text variant="text-md/normal" className={cl("author")}>
|
||||||
|
|
|
@ -62,3 +62,36 @@
|
||||||
.vc-addon-author::before {
|
.vc-addon-author::before {
|
||||||
content: "by ";
|
content: "by ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-addon-title-container {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 1.25em;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-addon-title {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes vc-addon-title {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateX(var(--offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-addon-title:hover {
|
||||||
|
overflow: visible;
|
||||||
|
animation: vc-addon-title var(--duration) linear infinite;
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
import type { Settings } from "@api/Settings";
|
import type { Settings } from "@api/Settings";
|
||||||
import { IpcEvents } from "@shared/IpcEvents";
|
import { IpcEvents } from "@shared/IpcEvents";
|
||||||
import { SettingsStore } from "@shared/SettingsStore";
|
import { SettingsStore } from "@shared/SettingsStore";
|
||||||
|
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||||
|
|
||||||
|
@ -42,7 +43,22 @@ ipcMain.handle(IpcEvents.SET_SETTINGS, (_, data: Settings, pathToNotify?: string
|
||||||
RendererSettings.setData(data, pathToNotify);
|
RendererSettings.setData(data, pathToNotify);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const NativeSettings = new SettingsStore(readSettings("native", NATIVE_SETTINGS_FILE));
|
export interface NativeSettings {
|
||||||
|
plugins: {
|
||||||
|
[plugin: string]: {
|
||||||
|
[setting: string]: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultNativeSettings: NativeSettings = {
|
||||||
|
plugins: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nativeSettings = readSettings<NativeSettings>("native", NATIVE_SETTINGS_FILE);
|
||||||
|
mergeDefaults(nativeSettings, DefaultNativeSettings);
|
||||||
|
|
||||||
|
export const NativeSettings = new SettingsStore(nativeSettings);
|
||||||
|
|
||||||
NativeSettings.addGlobalChangeListener(() => {
|
NativeSettings.addGlobalChangeListener(() => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
|
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { Tooltip } from "@webpack/common";
|
import { Tooltip } from "@webpack/common";
|
||||||
import type { Component } from "react";
|
import type { Component } from "react";
|
||||||
|
|
||||||
|
@ -34,11 +35,19 @@ interface Props {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enum ReplaceElements {
|
||||||
|
ReplaceAllElements,
|
||||||
|
ReplaceTitlesOnly,
|
||||||
|
ReplaceThumbnailsOnly
|
||||||
|
}
|
||||||
|
|
||||||
const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/;
|
const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/;
|
||||||
|
|
||||||
async function embedDidMount(this: Component<Props>) {
|
async function embedDidMount(this: Component<Props>) {
|
||||||
try {
|
try {
|
||||||
const { embed } = this.props;
|
const { embed } = this.props;
|
||||||
|
const { replaceElements } = settings.store;
|
||||||
|
|
||||||
if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return;
|
if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return;
|
||||||
|
|
||||||
const videoId = embedUrlRe.exec(embed.video.url)?.[1];
|
const videoId = embedUrlRe.exec(embed.video.url)?.[1];
|
||||||
|
@ -58,12 +67,12 @@ async function embedDidMount(this: Component<Props>) {
|
||||||
enabled: true
|
enabled: true
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hasTitle) {
|
if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) {
|
||||||
embed.dearrow.oldTitle = embed.rawTitle;
|
embed.dearrow.oldTitle = embed.rawTitle;
|
||||||
embed.rawTitle = titles[0].title.replace(/ >(\S)/g, " $1");
|
embed.rawTitle = titles[0].title.replace(/ >(\S)/g, " $1");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasThumb) {
|
if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) {
|
||||||
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
||||||
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||||
}
|
}
|
||||||
|
@ -128,10 +137,30 @@ function DearrowButton({ component }: { component: Component<Props>; }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
hideButton: {
|
||||||
|
description: "Hides the Dearrow button from YouTube embeds",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false,
|
||||||
|
restartNeeded: true
|
||||||
|
},
|
||||||
|
replaceElements: {
|
||||||
|
description: "Choose which elements of the embed will be replaced",
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
restartNeeded: true,
|
||||||
|
options: [
|
||||||
|
{ label: "Everything (Titles & Thumbnails)", value: ReplaceElements.ReplaceAllElements, default: true },
|
||||||
|
{ label: "Titles", value: ReplaceElements.ReplaceTitlesOnly },
|
||||||
|
{ label: "Thumbnails", value: ReplaceElements.ReplaceThumbnailsOnly },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "Dearrow",
|
name: "Dearrow",
|
||||||
description: "Makes YouTube embed titles and thumbnails less sensationalist, powered by Dearrow",
|
description: "Makes YouTube embed titles and thumbnails less sensationalist, powered by Dearrow",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
|
settings,
|
||||||
|
|
||||||
embedDidMount,
|
embedDidMount,
|
||||||
renderButton(component: Component<Props>) {
|
renderButton(component: Component<Props>) {
|
||||||
|
@ -154,7 +183,8 @@ export default definePlugin({
|
||||||
// add dearrow button
|
// add dearrow button
|
||||||
{
|
{
|
||||||
match: /children:\[(?=null!=\i\?\i\.renderSuppressButton)/,
|
match: /children:\[(?=null!=\i\?\i\.renderSuppressButton)/,
|
||||||
replace: "children:[$self.renderButton(this),"
|
replace: "children:[$self.renderButton(this),",
|
||||||
|
predicate: () => !settings.store.hideButton
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -110,7 +110,7 @@ const hyperLinkRegex = /\[.+?\]\((https?:\/\/.+?)\)/;
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
enableEmojiBypass: {
|
enableEmojiBypass: {
|
||||||
description: "Allow sending fake emojis",
|
description: "Allows sending fake emojis (also bypasses missing permission to use custom emojis)",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true,
|
default: true,
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
|
@ -128,7 +128,7 @@ const settings = definePluginSettings({
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
},
|
},
|
||||||
enableStickerBypass: {
|
enableStickerBypass: {
|
||||||
description: "Allow sending fake stickers",
|
description: "Allows sending fake stickers (also bypasses missing permission to use stickers)",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true,
|
default: true,
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
|
|
|
@ -33,7 +33,7 @@ const PRONOUN_TOOLTIP_PATCH = {
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "PronounDB",
|
name: "PronounDB",
|
||||||
authors: [Devs.Tyman, Devs.TheKodeToad, Devs.Ven],
|
authors: [Devs.Tyman, Devs.TheKodeToad, Devs.Ven, Devs.Elvyra],
|
||||||
description: "Adds pronouns to user messages using pronoundb",
|
description: "Adds pronouns to user messages using pronoundb",
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { useAwaiter } from "@utils/react";
|
||||||
import { UserProfileStore, UserStore } from "@webpack/common";
|
import { UserProfileStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { PronounCode, PronounMapping, PronounsResponse } from "./types";
|
import { CachePronouns, PronounCode, PronounMapping, PronounsResponse } from "./types";
|
||||||
|
|
||||||
type PronounsWithSource = [string | null, string];
|
type PronounsWithSource = [string | null, string];
|
||||||
const EmptyPronouns: PronounsWithSource = [null, ""];
|
const EmptyPronouns: PronounsWithSource = [null, ""];
|
||||||
|
@ -40,9 +40,9 @@ export const enum PronounSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A map of cached pronouns so the same request isn't sent twice
|
// A map of cached pronouns so the same request isn't sent twice
|
||||||
const cache: Record<string, PronounCode> = {};
|
const cache: Record<string, CachePronouns> = {};
|
||||||
// A map of ids and callbacks that should be triggered on fetch
|
// A map of ids and callbacks that should be triggered on fetch
|
||||||
const requestQueue: Record<string, ((pronouns: PronounCode) => void)[]> = {};
|
const requestQueue: Record<string, ((pronouns: string) => void)[]> = {};
|
||||||
|
|
||||||
// Executes all queued requests and calls their callbacks
|
// Executes all queued requests and calls their callbacks
|
||||||
const bulkFetch = debounce(async () => {
|
const bulkFetch = debounce(async () => {
|
||||||
|
@ -50,7 +50,7 @@ const bulkFetch = debounce(async () => {
|
||||||
const pronouns = await bulkFetchPronouns(ids);
|
const pronouns = await bulkFetchPronouns(ids);
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
// Call all callbacks for the id
|
// Call all callbacks for the id
|
||||||
requestQueue[id]?.forEach(c => c(pronouns[id]));
|
requestQueue[id]?.forEach(c => c(pronouns[id] ? extractPronouns(pronouns[id].sets) : ""));
|
||||||
delete requestQueue[id];
|
delete requestQueue[id];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -78,8 +78,8 @@ export function useFormattedPronouns(id: string, useGlobalProfile: boolean = fal
|
||||||
if (settings.store.pronounSource === PronounSource.PreferDiscord && discordPronouns)
|
if (settings.store.pronounSource === PronounSource.PreferDiscord && discordPronouns)
|
||||||
return [discordPronouns, "Discord"];
|
return [discordPronouns, "Discord"];
|
||||||
|
|
||||||
if (result && result !== "unspecified")
|
if (result && result !== PronounMapping.unspecified)
|
||||||
return [formatPronouns(result), "PronounDB"];
|
return [result, "PronounDB"];
|
||||||
|
|
||||||
return [discordPronouns, "Discord"];
|
return [discordPronouns, "Discord"];
|
||||||
}
|
}
|
||||||
|
@ -98,8 +98,9 @@ const NewLineRe = /\n+/g;
|
||||||
|
|
||||||
// Gets the cached pronouns, if you're too impatient for a promise!
|
// Gets the cached pronouns, if you're too impatient for a promise!
|
||||||
export function getCachedPronouns(id: string): string | null {
|
export function getCachedPronouns(id: string): string | null {
|
||||||
const cached = cache[id];
|
const cached = cache[id] ? extractPronouns(cache[id].sets) : undefined;
|
||||||
if (cached && cached !== "unspecified") return cached;
|
|
||||||
|
if (cached && cached !== PronounMapping.unspecified) return cached;
|
||||||
|
|
||||||
return cached || null;
|
return cached || null;
|
||||||
}
|
}
|
||||||
|
@ -125,7 +126,7 @@ async function bulkFetchPronouns(ids: string[]): Promise<PronounsResponse> {
|
||||||
params.append("ids", ids.join(","));
|
params.append("ids", ids.join(","));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const req = await fetch("https://pronoundb.org/api/v1/lookup-bulk?" + params.toString(), {
|
const req = await fetch("https://pronoundb.org/api/v2/lookup?" + params.toString(), {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
|
@ -140,21 +141,24 @@ async function bulkFetchPronouns(ids: string[]): Promise<PronounsResponse> {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// If the request errors, treat it as if no pronouns were found for all ids, and log it
|
// If the request errors, treat it as if no pronouns were found for all ids, and log it
|
||||||
console.error("PronounDB fetching failed: ", e);
|
console.error("PronounDB fetching failed: ", e);
|
||||||
const dummyPronouns = Object.fromEntries(ids.map(id => [id, "unspecified"] as const));
|
const dummyPronouns = Object.fromEntries(ids.map(id => [id, { sets: {} }] as const));
|
||||||
Object.assign(cache, dummyPronouns);
|
Object.assign(cache, dummyPronouns);
|
||||||
return dummyPronouns;
|
return dummyPronouns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatPronouns(pronouns: string): 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.plugins.PronounDB as { pronounsFormat: PronounsFormat, enabled: boolean; };
|
||||||
// For capitalized pronouns, just return the mapping (it is by default capitalized)
|
|
||||||
if (pronounsFormat === PronounsFormat.Capitalized) return PronounMapping[pronouns];
|
if (pronouns.length === 1) {
|
||||||
// If it is set to lowercase and a special code (any, ask, avoid), then just return the capitalized text
|
// For capitalized pronouns or special codes (any, ask, avoid), we always return the normal (capitalized) string
|
||||||
else if (
|
if (pronounsFormat === PronounsFormat.Capitalized || ["any", "ask", "avoid", "other", "unspecified"].includes(pronouns[0]))
|
||||||
pronounsFormat === PronounsFormat.Lowercase
|
return PronounMapping[pronouns[0]];
|
||||||
&& ["any", "ask", "avoid", "other"].includes(pronouns)
|
else return PronounMapping[pronouns[0]].toLowerCase();
|
||||||
) return PronounMapping[pronouns];
|
}
|
||||||
// Otherwise (lowercase and not a special code), then convert the mapping to lowercase
|
const pronounString = pronouns.map(p => p[0].toUpperCase() + p.slice(1)).join("/");
|
||||||
else return PronounMapping[pronouns].toLowerCase();
|
return pronounsFormat === PronounsFormat.Capitalized ? pronounString : pronounString.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,31 +26,29 @@ export interface UserProfilePronounsProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PronounsResponse {
|
export interface PronounsResponse {
|
||||||
[id: string]: PronounCode;
|
[id: string]: {
|
||||||
|
sets?: {
|
||||||
|
[locale: string]: PronounCode[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CachePronouns {
|
||||||
|
sets?: {
|
||||||
|
[locale: string]: PronounCode[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PronounCode = keyof typeof PronounMapping;
|
export type PronounCode = keyof typeof PronounMapping;
|
||||||
|
|
||||||
export const PronounMapping = {
|
export const PronounMapping = {
|
||||||
hh: "He/Him",
|
he: "He/Him",
|
||||||
hi: "He/It",
|
it: "It/Its",
|
||||||
hs: "He/She",
|
she: "She/Her",
|
||||||
ht: "He/They",
|
they: "They/Them",
|
||||||
ih: "It/Him",
|
|
||||||
ii: "It/Its",
|
|
||||||
is: "It/She",
|
|
||||||
it: "It/They",
|
|
||||||
shh: "She/He",
|
|
||||||
sh: "She/Her",
|
|
||||||
si: "She/It",
|
|
||||||
st: "She/They",
|
|
||||||
th: "They/He",
|
|
||||||
ti: "They/It",
|
|
||||||
ts: "They/She",
|
|
||||||
tt: "They/Them",
|
|
||||||
any: "Any pronouns",
|
any: "Any pronouns",
|
||||||
other: "Other pronouns",
|
other: "Other pronouns",
|
||||||
ask: "Ask me my pronouns",
|
ask: "Ask me my pronouns",
|
||||||
avoid: "Avoid pronouns, use my name",
|
avoid: "Avoid pronouns, use my name",
|
||||||
unspecified: "Unspecified"
|
unspecified: "No pronouns specified.",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -40,9 +40,9 @@ export function TranslateIcon({ height = 24, width = 24, className }: { height?:
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
||||||
const { autoTranslate } = settings.use(["autoTranslate"]);
|
const { autoTranslate, showChatBarButton } = settings.use(["autoTranslate", "showChatBarButton"]);
|
||||||
|
|
||||||
if (!isMainChat) return null;
|
if (!isMainChat || !showChatBarButton) return null;
|
||||||
|
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
const newState = !autoTranslate;
|
const newState = !autoTranslate;
|
||||||
|
|
|
@ -48,6 +48,11 @@ export const settings = definePluginSettings({
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Automatically translate your messages before sending. You can also shift/right click the translate button to toggle this",
|
description: "Automatically translate your messages before sending. You can also shift/right click the translate button to toggle this",
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
showChatBarButton: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show translate button in chat bar",
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
}).withPrivateSettings<{
|
}).withPrivateSettings<{
|
||||||
showAutoTranslateAlert: boolean;
|
showAutoTranslateAlert: boolean;
|
||||||
|
|
|
@ -462,6 +462,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "Oleh Polisan",
|
name: "Oleh Polisan",
|
||||||
id: 242305263313485825n
|
id: 242305263313485825n
|
||||||
},
|
},
|
||||||
|
HAHALOSAH: {
|
||||||
|
name: "HAHALOSAH",
|
||||||
|
id: 903418691268513883n
|
||||||
|
},
|
||||||
GabiRP: {
|
GabiRP: {
|
||||||
name: "GabiRP",
|
name: "GabiRP",
|
||||||
id: 507955112027750401n
|
id: 507955112027750401n
|
||||||
|
|
24
src/utils/mergeDefaults.ts
Normal file
24
src/utils/mergeDefaults.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively merges defaults into an object and returns the same object
|
||||||
|
* @param obj Object
|
||||||
|
* @param defaults Defaults
|
||||||
|
* @returns obj
|
||||||
|
*/
|
||||||
|
export function mergeDefaults<T>(obj: T, defaults: T): T {
|
||||||
|
for (const key in defaults) {
|
||||||
|
const v = defaults[key];
|
||||||
|
if (typeof v === "object" && !Array.isArray(v)) {
|
||||||
|
obj[key] ??= {} as any;
|
||||||
|
mergeDefaults(obj[key], v);
|
||||||
|
} else {
|
||||||
|
obj[key] ??= v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
|
@ -20,25 +20,6 @@ import { Clipboard, Toasts } from "@webpack/common";
|
||||||
|
|
||||||
import { DevsById } from "./constants";
|
import { DevsById } from "./constants";
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively merges defaults into an object and returns the same object
|
|
||||||
* @param obj Object
|
|
||||||
* @param defaults Defaults
|
|
||||||
* @returns obj
|
|
||||||
*/
|
|
||||||
export function mergeDefaults<T>(obj: T, defaults: T): T {
|
|
||||||
for (const key in defaults) {
|
|
||||||
const v = defaults[key];
|
|
||||||
if (typeof v === "object" && !Array.isArray(v)) {
|
|
||||||
obj[key] ??= {} as any;
|
|
||||||
mergeDefaults(obj[key], v);
|
|
||||||
} else {
|
|
||||||
obj[key] ??= v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls .join(" ") on the arguments
|
* Calls .join(" ") on the arguments
|
||||||
* classes("one", "two") => "one two"
|
* classes("one", "two") => "one two"
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import { showNotification } from "@api/Notifications";
|
import { showNotification } from "@api/Notifications";
|
||||||
import { PlainSettings, Settings } from "@api/Settings";
|
import { PlainSettings, Settings } from "@api/Settings";
|
||||||
import { Toasts } from "@webpack/common";
|
import { moment, Toasts } from "@webpack/common";
|
||||||
import { deflateSync, inflateSync } from "fflate";
|
import { deflateSync, inflateSync } from "fflate";
|
||||||
|
|
||||||
import { getCloudAuth, getCloudUrl } from "./cloud";
|
import { getCloudAuth, getCloudUrl } from "./cloud";
|
||||||
|
@ -49,7 +49,7 @@ export async function exportSettings({ minify }: { minify?: boolean; } = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function downloadSettingsBackup() {
|
export async function downloadSettingsBackup() {
|
||||||
const filename = "vencord-settings-backup.json";
|
const filename = `vencord-settings-backup-${moment().format("YYYY-MM-DD")}.json`;
|
||||||
const backup = await exportSettings();
|
const backup = await exportSettings();
|
||||||
const data = new TextEncoder().encode(backup);
|
const data = new TextEncoder().encode(backup);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue