Fix Lint
This commit is contained in:
parent
1c2d328f5a
commit
fc5bfef589
7 changed files with 1 additions and 508 deletions
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { Link } from "@components/Link";
|
||||
import { CspBlockedUrls, useCspErrors } from "@utils/cspViolations";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { useForceUpdater } from "@utils/react";
|
||||
import { Alerts, Button, Forms } from "@webpack/common";
|
||||
|
||||
export function CspErrorCard() {
|
||||
if (IS_WEB) return null;
|
||||
|
||||
const errors = useCspErrors();
|
||||
const forceUpdate = useForceUpdater();
|
||||
|
||||
if (!errors.length) return null;
|
||||
|
||||
const isImgurHtmlDomain = (url: string) => url.startsWith("https://imgur.com/");
|
||||
|
||||
const allowUrl = async (url: string) => {
|
||||
const { origin: baseUrl, host } = new URL(url);
|
||||
|
||||
const result = await VencordNative.csp.requestAddOverride(baseUrl, ["connect-src", "img-src", "style-src", "font-src"], "Vencord Themes");
|
||||
if (result !== "ok") return;
|
||||
|
||||
CspBlockedUrls.forEach(url => {
|
||||
if (new URL(url).host === host) {
|
||||
CspBlockedUrls.delete(url);
|
||||
}
|
||||
});
|
||||
|
||||
forceUpdate();
|
||||
|
||||
Alerts.show({
|
||||
title: "Restart Required",
|
||||
body: "A restart is required to apply this change",
|
||||
confirmText: "Restart now",
|
||||
cancelText: "Later!",
|
||||
onConfirm: relaunch
|
||||
});
|
||||
};
|
||||
|
||||
const hasImgurHtmlDomain = errors.some(isImgurHtmlDomain);
|
||||
|
||||
return (
|
||||
<ErrorCard className="vc-settings-card">
|
||||
<Forms.FormTitle tag="h5">Blocked Resources</Forms.FormTitle>
|
||||
<Forms.FormText>Some images, styles, or fonts were blocked because they come from disallowed domains.</Forms.FormText>
|
||||
<Forms.FormText>It is highly recommended to move them to GitHub or Imgur. But you may also allow domains if you fully trust them.</Forms.FormText>
|
||||
<Forms.FormText>
|
||||
After allowing a domain, you have to fully close (from tray / task manager) and restart {IS_DISCORD_DESKTOP ? "Discord" : "Vesktop"} to apply the change.
|
||||
</Forms.FormText>
|
||||
|
||||
<Forms.FormTitle tag="h5" className={classes(Margins.top16, Margins.bottom8)}>Blocked URLs</Forms.FormTitle>
|
||||
<div className="vc-settings-csp-list">
|
||||
{errors.map((url, i) => (
|
||||
<div key={url}>
|
||||
{i !== 0 && <Forms.FormDivider className={Margins.bottom8} />}
|
||||
<div className="vc-settings-csp-row">
|
||||
<Link href={url}>{url}</Link>
|
||||
<Button color={Button.Colors.PRIMARY} onClick={() => allowUrl(url)} disabled={isImgurHtmlDomain(url)}>
|
||||
Allow
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{hasImgurHtmlDomain && (
|
||||
<>
|
||||
<Forms.FormDivider className={classes(Margins.top8, Margins.bottom16)} />
|
||||
<Forms.FormText>
|
||||
Imgur links should be direct links in the form of <code>https://i.imgur.com/...</code>
|
||||
</Forms.FormText>
|
||||
<Forms.FormText>To obtain a direct link, right-click the image and select "Copy image address".</Forms.FormText>
|
||||
</>
|
||||
)}
|
||||
</ErrorCard>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Settings, useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
|
||||
import { Link } from "@components/Link";
|
||||
import { QuickAction, QuickActionCard } from "@components/settings/QuickAction";
|
||||
import { openPluginModal } from "@components/settings/tabs/plugins/PluginModal";
|
||||
import { UserThemeHeader } from "@main/themes";
|
||||
import { findLazy } from "@webpack";
|
||||
import { Card, Forms, useEffect, useRef, useState } from "@webpack/common";
|
||||
import ClientThemePlugin from "plugins/clientTheme";
|
||||
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||
|
||||
import { ThemeCard } from "./ThemeCard";
|
||||
|
||||
const cl = classNameFactory("vc-settings-theme-");
|
||||
|
||||
type FileInput = ComponentType<{
|
||||
ref: Ref<HTMLInputElement>;
|
||||
onChange: (e: SyntheticEvent<HTMLInputElement>) => void;
|
||||
multiple?: boolean;
|
||||
filters?: { name?: string; extensions: string[]; }[];
|
||||
}>;
|
||||
|
||||
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
|
||||
|
||||
// When a local theme is enabled/disabled, update the settings
|
||||
function onLocalThemeChange(fileName: string, value: boolean) {
|
||||
if (value) {
|
||||
if (Settings.enabledThemes.includes(fileName)) return;
|
||||
Settings.enabledThemes = [...Settings.enabledThemes, fileName];
|
||||
} else {
|
||||
Settings.enabledThemes = Settings.enabledThemes.filter(f => f !== fileName);
|
||||
}
|
||||
}
|
||||
|
||||
async function onFileUpload(e: SyntheticEvent<HTMLInputElement>) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (!e.currentTarget?.files?.length) return;
|
||||
const { files } = e.currentTarget;
|
||||
|
||||
const uploads = Array.from(files, file => {
|
||||
const { name } = file;
|
||||
if (!name.endsWith(".css")) return;
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
VencordNative.themes.uploadTheme(name, reader.result as string)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
});
|
||||
|
||||
await Promise.all(uploads);
|
||||
}
|
||||
|
||||
export function LocalThemesTab() {
|
||||
const settings = useSettings(["enabledThemes"]);
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [userThemes, setUserThemes] = useState<UserThemeHeader[] | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
refreshLocalThemes();
|
||||
}, []);
|
||||
|
||||
async function refreshLocalThemes() {
|
||||
const themes = await VencordNative.themes.getThemesList();
|
||||
setUserThemes(themes);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="vc-settings-card">
|
||||
<Forms.FormTitle tag="h5">Find Themes:</Forms.FormTitle>
|
||||
<div style={{ marginBottom: ".5em", display: "flex", flexDirection: "column" }}>
|
||||
<Link style={{ marginRight: ".5em" }} href="https://betterdiscord.app/themes">
|
||||
BetterDiscord Themes
|
||||
</Link>
|
||||
<Link href="https://github.com/search?q=discord+theme">GitHub</Link>
|
||||
</div>
|
||||
<Forms.FormText>If using the BD site, click on "Download" and place the downloaded .theme.css file into your themes folder.</Forms.FormText>
|
||||
</Card>
|
||||
|
||||
<Card className="vc-settings-card">
|
||||
<Forms.FormTitle tag="h5">External Resources</Forms.FormTitle>
|
||||
<Forms.FormText>For security reasons, loading resources (styles, fonts, images, ...) from most sites is blocked.</Forms.FormText>
|
||||
<Forms.FormText>Make sure all your assets are hosted on GitHub, GitLab, Codeberg, Imgur, Discord or Google Fonts.</Forms.FormText>
|
||||
</Card>
|
||||
|
||||
<Forms.FormSection title="Local Themes">
|
||||
<QuickActionCard>
|
||||
<>
|
||||
{IS_WEB ?
|
||||
(
|
||||
<QuickAction
|
||||
text={
|
||||
<span style={{ position: "relative" }}>
|
||||
Upload Theme
|
||||
<FileInput
|
||||
ref={fileInputRef}
|
||||
onChange={async e => {
|
||||
await onFileUpload(e);
|
||||
refreshLocalThemes();
|
||||
}}
|
||||
multiple={true}
|
||||
filters={[{ extensions: ["css"] }]}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
Icon={PlusIcon}
|
||||
/>
|
||||
) : (
|
||||
<QuickAction
|
||||
text="Open Themes Folder"
|
||||
action={() => VencordNative.themes.openFolder()}
|
||||
Icon={FolderIcon}
|
||||
/>
|
||||
)}
|
||||
<QuickAction
|
||||
text="Load missing Themes"
|
||||
action={refreshLocalThemes}
|
||||
Icon={RestartIcon}
|
||||
/>
|
||||
<QuickAction
|
||||
text="Edit QuickCSS"
|
||||
action={() => VencordNative.quickCss.openEditor()}
|
||||
Icon={PaintbrushIcon}
|
||||
/>
|
||||
|
||||
{Vencord.Plugins.isPluginEnabled(ClientThemePlugin.name) && (
|
||||
<QuickAction
|
||||
text="Edit ClientTheme"
|
||||
action={() => openPluginModal(ClientThemePlugin)}
|
||||
Icon={PencilIcon}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</QuickActionCard>
|
||||
|
||||
<div className={cl("grid")}>
|
||||
{userThemes?.map(theme => (
|
||||
<ThemeCard
|
||||
key={theme.fileName}
|
||||
enabled={settings.enabledThemes.includes(theme.fileName)}
|
||||
onChange={enabled => onLocalThemeChange(theme.fileName, enabled)}
|
||||
onDelete={async () => {
|
||||
onLocalThemeChange(theme.fileName, false);
|
||||
await VencordNative.themes.deleteTheme(theme.fileName);
|
||||
refreshLocalThemes();
|
||||
}}
|
||||
theme={theme}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Forms.FormSection>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { Card, Forms, TextArea, useState } from "@webpack/common";
|
||||
|
||||
export function OnlineThemesTab() {
|
||||
const settings = useSettings(["themeLinks"]);
|
||||
|
||||
const [themeText, setThemeText] = useState(settings.themeLinks.join("\n"));
|
||||
|
||||
// When the user leaves the online theme textbox, update the settings
|
||||
function onBlur() {
|
||||
settings.themeLinks = [...new Set(
|
||||
themeText
|
||||
.trim()
|
||||
.split(/\n+/)
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean)
|
||||
)];
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className={classes("vc-warning-card", Margins.bottom16)}>
|
||||
<Forms.FormText>
|
||||
This section is for advanced users. If you are having difficulties using it, use the
|
||||
Local Themes tab instead.
|
||||
</Forms.FormText>
|
||||
</Card>
|
||||
<Card className="vc-settings-card">
|
||||
<Forms.FormTitle tag="h5">Paste links to css files here</Forms.FormTitle>
|
||||
<Forms.FormText>One link per line</Forms.FormText>
|
||||
<Forms.FormText>You can prefix lines with @light or @dark to toggle them based on your Discord theme</Forms.FormText>
|
||||
<Forms.FormText>Make sure to use direct links to files (raw or github.io)!</Forms.FormText>
|
||||
</Card>
|
||||
|
||||
<Forms.FormSection title="Online Themes" tag="h5">
|
||||
<TextArea
|
||||
value={themeText}
|
||||
onChange={setThemeText}
|
||||
className={"vc-settings-theme-links"}
|
||||
placeholder="Enter Theme Links..."
|
||||
spellCheck={false}
|
||||
onBlur={onBlur}
|
||||
rows={10}
|
||||
/>
|
||||
</Forms.FormSection>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Flex } from "@components/Flex";
|
||||
import { DeleteIcon } from "@components/Icons";
|
||||
import { Link } from "@components/Link";
|
||||
import { AddonCard } from "@components/settings/AddonCard";
|
||||
import { UserThemeHeader } from "@main/themes";
|
||||
import { openInviteModal } from "@utils/discord";
|
||||
import { showToast } from "@webpack/common";
|
||||
|
||||
interface ThemeCardProps {
|
||||
theme: UserThemeHeader;
|
||||
enabled: boolean;
|
||||
onChange: (enabled: boolean) => void;
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
export function ThemeCard({ theme, enabled, onChange, onDelete }: ThemeCardProps) {
|
||||
return (
|
||||
<AddonCard
|
||||
name={theme.name}
|
||||
description={theme.description}
|
||||
author={theme.author}
|
||||
enabled={enabled}
|
||||
setEnabled={onChange}
|
||||
infoButton={
|
||||
IS_WEB && (
|
||||
<div style={{ cursor: "pointer", color: "var(--status-danger" }} onClick={onDelete}>
|
||||
<DeleteIcon />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
footer={
|
||||
<Flex flexDirection="row" style={{ gap: "0.2em" }}>
|
||||
{!!theme.website && <Link href={theme.website}>Website</Link>}
|
||||
{!!(theme.website && theme.invite) && " • "}
|
||||
{!!theme.invite && (
|
||||
<Link
|
||||
href={`https://discord.gg/${theme.invite}`}
|
||||
onClick={async e => {
|
||||
e.preventDefault();
|
||||
theme.invite != null && openInviteModal(theme.invite).catch(() => showToast("Invalid or expired invite"));
|
||||
}}
|
||||
>
|
||||
Discord Server
|
||||
</Link>
|
||||
)}
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
import { Link } from "@components/Link";
|
||||
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { getStylusWebStoreUrl } from "@utils/web";
|
||||
import { Card, Forms, React, TabBar, useState } from "@webpack/common";
|
||||
|
||||
import { CspErrorCard } from "./CspErrorCard";
|
||||
import { LocalThemesTab } from "./LocalThemesTab";
|
||||
import { OnlineThemesTab } from "./OnlineThemesTab";
|
||||
|
||||
const enum ThemeTab {
|
||||
LOCAL,
|
||||
ONLINE
|
||||
}
|
||||
|
||||
function ThemesTab() {
|
||||
const [currentTab, setCurrentTab] = useState(ThemeTab.LOCAL);
|
||||
|
||||
return (
|
||||
<SettingsTab title="Themes">
|
||||
<TabBar
|
||||
type="top"
|
||||
look="brand"
|
||||
className="vc-settings-tab-bar"
|
||||
selectedItem={currentTab}
|
||||
onItemSelect={setCurrentTab}
|
||||
>
|
||||
<TabBar.Item
|
||||
className="vc-settings-tab-bar-item"
|
||||
id={ThemeTab.LOCAL}
|
||||
>
|
||||
Local Themes
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className="vc-settings-tab-bar-item"
|
||||
id={ThemeTab.ONLINE}
|
||||
>
|
||||
Online Themes
|
||||
</TabBar.Item>
|
||||
</TabBar>
|
||||
|
||||
<CspErrorCard />
|
||||
|
||||
{currentTab === ThemeTab.LOCAL && <LocalThemesTab />}
|
||||
{currentTab === ThemeTab.ONLINE && <OnlineThemesTab />}
|
||||
</SettingsTab>
|
||||
);
|
||||
}
|
||||
|
||||
function UserscriptThemesTab() {
|
||||
return (
|
||||
<SettingsTab title="Themes">
|
||||
<Card className="vc-settings-card">
|
||||
<Forms.FormTitle tag="h5">Themes are not supported on the Userscript!</Forms.FormTitle>
|
||||
|
||||
<Forms.FormText>
|
||||
You can instead install themes with the <Link href={getStylusWebStoreUrl()}>Stylus extension</Link>!
|
||||
</Forms.FormText>
|
||||
</Card>
|
||||
</SettingsTab>
|
||||
);
|
||||
}
|
||||
|
||||
export default IS_USERSCRIPT
|
||||
? wrapTab(UserscriptThemesTab, "Themes")
|
||||
: wrapTab(ThemesTab, "Themes");
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
.vc-settings-theme-grid {
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
}
|
||||
|
||||
.vc-settings-theme-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--background-base-lower-alt);
|
||||
color: var(--interactive-active);
|
||||
border-radius: 8px;
|
||||
padding: 1em;
|
||||
width: 100%;
|
||||
transition: 0.1s ease-out;
|
||||
transition-property: box-shadow, transform, background, opacity;
|
||||
}
|
||||
|
||||
.vc-settings-theme-card-text {
|
||||
text-overflow: ellipsis;
|
||||
height: 1.2em;
|
||||
margin-bottom: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vc-settings-theme-author::before {
|
||||
content: "by ";
|
||||
}
|
||||
|
||||
.vc-settings-csp-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.vc-settings-csp-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
|
||||
& a {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
--custom-button-button-md-height: 26px;
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ const settings = definePluginSettings({
|
|||
SavedIds: {
|
||||
description: "The amount of soundboard ids you want to save at a time (0 lets you save infinite)",
|
||||
type: OptionType.COMPONENT,
|
||||
component: ({ setValue, setError }) => {
|
||||
component: ({ setValue }) => {
|
||||
const value = settings.store.SavedIds ?? 50;
|
||||
const [state, setState] = useState(`${value}`);
|
||||
const [shouldShowWarning, setShouldShowWarning] = useState(false);
|
||||
|
|
@ -24,7 +24,6 @@ const settings = definePluginSettings({
|
|||
const changed = Number(newValue);
|
||||
|
||||
if (Number.isNaN(changed) || changed % 1 !== 0 || changed < 0) {
|
||||
setError(true);
|
||||
let errorMsg = "";
|
||||
errorMsg += Number.isNaN(changed) ? "The value is not a number.\n" : "";
|
||||
errorMsg += (changed % 1 !== 0) ? "The value can't be a decimal number.\n" : "";
|
||||
|
|
@ -32,7 +31,6 @@ const settings = definePluginSettings({
|
|||
setErrorMessage(errorMsg);
|
||||
return;
|
||||
} else {
|
||||
setError(false);
|
||||
setErrorMessage(null);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue