mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-09 22:53:02 -04:00
Updates
This commit is contained in:
parent
418568097c
commit
b26faf2e37
10 changed files with 956 additions and 479 deletions
|
@ -8,6 +8,7 @@ import "./style.css";
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { EquicordDevs } from "@utils/constants";
|
import { EquicordDevs } from "@utils/constants";
|
||||||
|
import { getCurrentChannel } from "@utils/discord";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { waitFor } from "@webpack";
|
import { waitFor } from "@webpack";
|
||||||
import { UserStore } from "@webpack/common";
|
import { UserStore } from "@webpack/common";
|
||||||
|
@ -15,6 +16,7 @@ import { UserStore } from "@webpack/common";
|
||||||
let ChannelTextAreaClasses;
|
let ChannelTextAreaClasses;
|
||||||
let shouldShowColorEffects: boolean;
|
let shouldShowColorEffects: boolean;
|
||||||
let position: boolean;
|
let position: boolean;
|
||||||
|
let forceLeft = false;
|
||||||
|
|
||||||
waitFor(["buttonContainer", "channelTextArea"], m => (ChannelTextAreaClasses = m));
|
waitFor(["buttonContainer", "channelTextArea"], m => (ChannelTextAreaClasses = m));
|
||||||
|
|
||||||
|
@ -68,7 +70,7 @@ export default definePlugin({
|
||||||
charCounterDiv = document.createElement("div");
|
charCounterDiv = document.createElement("div");
|
||||||
charCounterDiv.classList.add("char-counter");
|
charCounterDiv.classList.add("char-counter");
|
||||||
|
|
||||||
if (position) charCounterDiv.classList.add("left");
|
if (position || forceLeft) charCounterDiv.classList.add("left");
|
||||||
|
|
||||||
charCounterDiv.innerHTML = `<span class="char-count">0</span>/<span class="char-max">${charMax}</span>`;
|
charCounterDiv.innerHTML = `<span class="char-count">0</span>/<span class="char-max">${charMax}</span>`;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +122,10 @@ export default definePlugin({
|
||||||
const observeDOMChanges = () => {
|
const observeDOMChanges = () => {
|
||||||
const observer = new MutationObserver(() => {
|
const observer = new MutationObserver(() => {
|
||||||
const chatTextArea = document.querySelector(`.${ChannelTextAreaClasses?.channelTextArea}`);
|
const chatTextArea = document.querySelector(`.${ChannelTextAreaClasses?.channelTextArea}`);
|
||||||
if (chatTextArea) {
|
if (chatTextArea && !document.querySelector(".char-counter")) {
|
||||||
|
const currentChannel = getCurrentChannel();
|
||||||
|
forceLeft = currentChannel?.rateLimitPerUser !== 0;
|
||||||
|
|
||||||
addCharCounter();
|
addCharCounter();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: -15px;
|
||||||
right: 0;
|
right: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
|
@ -5,16 +5,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as DataStore from "@api/DataStore";
|
import * as DataStore from "@api/DataStore";
|
||||||
import { Logger } from "@utils/Logger";
|
|
||||||
import { Button, useEffect, useRef, UserStore, useState } from "@webpack/common";
|
import { Button, useEffect, useRef, UserStore, useState } from "@webpack/common";
|
||||||
import type { User } from "discord-types/general";
|
import type { User } from "discord-types/general";
|
||||||
|
|
||||||
import type { Theme, ThemeLikeProps } from "../types";
|
import type { Theme, ThemeLikeProps } from "../types";
|
||||||
import { isAuthorized } from "../utils/auth";
|
import { isAuthorized } from "../utils/auth";
|
||||||
import { LikeIcon } from "../utils/Icons";
|
import { LikeIcon } from "../utils/Icons";
|
||||||
import { themeRequest } from "./ThemeTab";
|
import { logger, themeRequest } from "./ThemeTab";
|
||||||
|
|
||||||
export const logger = new Logger("ThemeLibrary", "#e5c890");
|
|
||||||
|
|
||||||
export const LikesComponent = ({ themeId, likedThemes: initialLikedThemes }: { themeId: Theme["id"], likedThemes: ThemeLikeProps | undefined; }) => {
|
export const LikesComponent = ({ themeId, likedThemes: initialLikedThemes }: { themeId: Theme["id"], likedThemes: ThemeLikeProps | undefined; }) => {
|
||||||
const [likesCount, setLikesCount] = useState(0);
|
const [likesCount, setLikesCount] = useState(0);
|
||||||
|
@ -35,7 +32,7 @@ export const LikesComponent = ({ themeId, likedThemes: initialLikedThemes }: { t
|
||||||
if (!isAuthorized()) return;
|
if (!isAuthorized()) return;
|
||||||
const theme = likedThemes?.likes.find(like => like.themeId === themeId as unknown as Number);
|
const theme = likedThemes?.likes.find(like => like.themeId === themeId as unknown as Number);
|
||||||
const currentUser: User = UserStore.getCurrentUser();
|
const currentUser: User = UserStore.getCurrentUser();
|
||||||
const hasLiked: boolean = theme?.userIds.includes(currentUser.id) ?? false;
|
const hasLiked: boolean = (theme?.userIds.includes(currentUser.id) || themeId === "preview") ?? false;
|
||||||
const endpoint = hasLiked ? "/likes/remove" : "/likes/add";
|
const endpoint = hasLiked ? "/likes/remove" : "/likes/add";
|
||||||
const token = await DataStore.get("ThemeLibrary_uniqueToken");
|
const token = await DataStore.get("ThemeLibrary_uniqueToken");
|
||||||
|
|
||||||
|
@ -83,9 +80,10 @@ export const LikesComponent = ({ themeId, likedThemes: initialLikedThemes }: { t
|
||||||
size={Button.Sizes.MEDIUM}
|
size={Button.Sizes.MEDIUM}
|
||||||
color={Button.Colors.PRIMARY}
|
color={Button.Colors.PRIMARY}
|
||||||
look={Button.Looks.OUTLINED}
|
look={Button.Looks.OUTLINED}
|
||||||
|
disabled={themeId === "preview"}
|
||||||
style={{ marginLeft: "8px" }}
|
style={{ marginLeft: "8px" }}
|
||||||
>
|
>
|
||||||
{LikeIcon(hasLiked)} {likesCount}
|
{LikeIcon(hasLiked || themeId === "preview")} {themeId === "preview" ? 143 : likesCount}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
180
src/equicordplugins/themeLibrary/components/ThemeCard.tsx
Normal file
180
src/equicordplugins/themeLibrary/components/ThemeCard.tsx
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { generateId } from "@api/Commands";
|
||||||
|
import { Settings } from "@api/Settings";
|
||||||
|
import { OpenExternalIcon } from "@components/Icons";
|
||||||
|
import { proxyLazy } from "@utils/lazy";
|
||||||
|
import { Margins } from "@utils/margins";
|
||||||
|
import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
|
import { Button, Card, FluxDispatcher, Forms, Parser, React, UserStore, UserUtils } from "@webpack/common";
|
||||||
|
import { User } from "discord-types/general";
|
||||||
|
import { Constructor } from "type-fest";
|
||||||
|
|
||||||
|
import type { Theme, ThemeLikeProps } from "../types";
|
||||||
|
import { LikesComponent } from "./LikesComponent";
|
||||||
|
import { ThemeInfoModal } from "./ThemeInfoModal";
|
||||||
|
import { apiUrl } from "./ThemeTab";
|
||||||
|
|
||||||
|
interface ThemeCardProps {
|
||||||
|
theme: Theme;
|
||||||
|
themeLinks: string[];
|
||||||
|
likedThemes?: ThemeLikeProps;
|
||||||
|
setThemeLinks: (links: string[]) => void;
|
||||||
|
removePreview?: boolean;
|
||||||
|
removeButtons?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
||||||
|
|
||||||
|
function makeDummyUser(user: { username: string; id?: string; avatar?: string; }) {
|
||||||
|
const newUser = new UserRecord({
|
||||||
|
username: user.username,
|
||||||
|
id: user.id ?? generateId(),
|
||||||
|
avatar: user.avatar,
|
||||||
|
bot: true,
|
||||||
|
});
|
||||||
|
FluxDispatcher.dispatch({
|
||||||
|
type: "USER_UPDATE",
|
||||||
|
user: newUser,
|
||||||
|
});
|
||||||
|
return newUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ThemeCard: React.FC<ThemeCardProps> = ({ theme, themeLinks, likedThemes, setThemeLinks, removeButtons, removePreview }) => {
|
||||||
|
|
||||||
|
const getUser = (id: string, username: string) => UserUtils.getUser(id) ?? makeDummyUser({ username, id });
|
||||||
|
|
||||||
|
const handleAddRemoveTheme = () => {
|
||||||
|
const onlineThemeLinks = themeLinks.includes(`${apiUrl}/${theme.name}`)
|
||||||
|
? themeLinks.filter(link => link !== `${apiUrl}/${theme.name}`)
|
||||||
|
: [...themeLinks, `${apiUrl}/${theme.name}`];
|
||||||
|
|
||||||
|
setThemeLinks(onlineThemeLinks);
|
||||||
|
Vencord.Settings.themeLinks = onlineThemeLinks;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleThemeAttributesCheck = () => {
|
||||||
|
const requiresThemeAttributes = theme.requiresThemeAttributes ?? false;
|
||||||
|
|
||||||
|
if (requiresThemeAttributes && !Settings.plugins.ThemeAttributes.enabled) {
|
||||||
|
openModal(modalProps => (
|
||||||
|
<ModalRoot {...modalProps} size={ModalSize.SMALL}>
|
||||||
|
<ModalHeader>
|
||||||
|
<Forms.FormTitle tag="h4">Hold on!</Forms.FormTitle>
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalContent>
|
||||||
|
<Forms.FormText style={{ padding: "8px" }}>
|
||||||
|
<p>This theme requires the <b>ThemeAttributes</b> plugin to work properly!</p>
|
||||||
|
<p>Do you want to enable it?</p>
|
||||||
|
</Forms.FormText>
|
||||||
|
</ModalContent>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
look={Button.Looks.FILLED}
|
||||||
|
color={Button.Colors.GREEN}
|
||||||
|
onClick={() => {
|
||||||
|
Settings.plugins.ThemeAttributes.enabled = true;
|
||||||
|
modalProps.onClose();
|
||||||
|
handleAddRemoveTheme();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Enable Plugin
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color={Button.Colors.RED}
|
||||||
|
look={Button.Looks.FILLED}
|
||||||
|
className={Margins.right8}
|
||||||
|
onClick={() => modalProps.onClose()}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalRoot>
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
handleAddRemoveTheme();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleViewSource = () => {
|
||||||
|
const content = window.atob(theme.content);
|
||||||
|
const metadata = content.match(/\/\*\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g)?.[0] || "";
|
||||||
|
const source = metadata.match(/@source\s+(.+)/)?.[1] || "";
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
VencordNative.native.openExternal(source);
|
||||||
|
} else {
|
||||||
|
VencordNative.native.openExternal(`${apiUrl}/${theme.name}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card style={{ padding: ".5rem", marginBottom: ".5em", marginTop: ".5em", display: "flex", flexDirection: "column", backgroundColor: "var(--background-secondary-alt)" }} key={theme.id}>
|
||||||
|
<Forms.FormTitle tag="h2" style={{ overflowWrap: "break-word", marginTop: 8 }} className="vce-theme-text">
|
||||||
|
{theme.name}
|
||||||
|
</Forms.FormTitle>
|
||||||
|
<Forms.FormText className="vce-theme-text">
|
||||||
|
{Parser.parse(theme.description)}
|
||||||
|
</Forms.FormText>
|
||||||
|
{!removePreview && (
|
||||||
|
<img role="presentation" src={theme.thumbnail_url} loading="lazy" alt={theme.name} className="vce-theme-info-preview" />
|
||||||
|
)}
|
||||||
|
<div className="vce-theme-info">
|
||||||
|
<div style={{ justifyContent: "flex-start", flexDirection: "column" }}>
|
||||||
|
{theme.tags && (
|
||||||
|
<Forms.FormText>
|
||||||
|
{theme.tags.map(tag => (
|
||||||
|
<span className="vce-theme-info-tag" key={tag}>
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</Forms.FormText>
|
||||||
|
)}
|
||||||
|
{!removeButtons && (
|
||||||
|
< div style={{ marginTop: "8px", display: "flex", flexDirection: "row" }}>
|
||||||
|
<Button
|
||||||
|
onClick={handleThemeAttributesCheck}
|
||||||
|
size={Button.Sizes.MEDIUM}
|
||||||
|
color={themeLinks.includes(`${apiUrl}/${theme.name}`) ? Button.Colors.RED : Button.Colors.GREEN}
|
||||||
|
look={Button.Looks.FILLED}
|
||||||
|
className={Margins.right8}
|
||||||
|
disabled={!theme.content || theme.id === "preview"}
|
||||||
|
>
|
||||||
|
{themeLinks.includes(`${apiUrl}/${theme.name}`) ? "Remove Theme" : "Add Theme"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
const authors = Array.isArray(theme.author)
|
||||||
|
? await Promise.all(theme.author.map(author => getUser(author.discord_snowflake, author.discord_name)))
|
||||||
|
: [await getUser(theme.author.discord_snowflake, theme.author.discord_name)];
|
||||||
|
|
||||||
|
openModal(props => <ThemeInfoModal {...props} author={authors} theme={theme} />);
|
||||||
|
}}
|
||||||
|
size={Button.Sizes.MEDIUM}
|
||||||
|
color={Button.Colors.BRAND}
|
||||||
|
look={Button.Looks.FILLED}
|
||||||
|
>
|
||||||
|
Theme Info
|
||||||
|
</Button>
|
||||||
|
<LikesComponent themeId={theme.id} likedThemes={likedThemes} />
|
||||||
|
<Button
|
||||||
|
onClick={handleViewSource}
|
||||||
|
size={Button.Sizes.MEDIUM}
|
||||||
|
color={Button.Colors.LINK}
|
||||||
|
look={Button.Looks.LINK}
|
||||||
|
disabled={!theme.content || theme.id === "preview"}
|
||||||
|
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
|
||||||
|
>
|
||||||
|
View Source <OpenExternalIcon height={16} width={16} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card >
|
||||||
|
);
|
||||||
|
};
|
|
@ -16,7 +16,7 @@ import { Button, Clipboard, Forms, Parser, React, showToast, Toasts } from "@web
|
||||||
|
|
||||||
import { Theme, ThemeInfoModalProps } from "../types";
|
import { Theme, ThemeInfoModalProps } from "../types";
|
||||||
import { ClockIcon, DownloadIcon, WarningIcon } from "../utils/Icons";
|
import { ClockIcon, DownloadIcon, WarningIcon } from "../utils/Icons";
|
||||||
import { logger } from "./LikesComponent";
|
import { logger } from "./ThemeTab";
|
||||||
|
|
||||||
const Native = VencordNative.pluginHelpers.ThemeLibrary as PluginNative<typeof import("../native")>;
|
const Native = VencordNative.pluginHelpers.ThemeLibrary as PluginNative<typeof import("../native")>;
|
||||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||||
|
@ -25,7 +25,7 @@ async function downloadTheme(themesDir: string, theme: Theme) {
|
||||||
try {
|
try {
|
||||||
await Native.downloadTheme(themesDir, theme);
|
await Native.downloadTheme(themesDir, theme);
|
||||||
showToast(`Downloaded ${theme.name}!`, Toasts.Type.SUCCESS);
|
showToast(`Downloaded ${theme.name}!`, Toasts.Type.SUCCESS);
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
showToast(`Failed to download ${theme.name}! (check console)`, Toasts.Type.FAILURE);
|
showToast(`Failed to download ${theme.name}! (check console)`, Toasts.Type.FAILURE);
|
||||||
}
|
}
|
||||||
|
@ -121,34 +121,36 @@ export const ThemeInfoModal: React.FC<ThemeInfoModalProps> = ({ author, theme, .
|
||||||
)}
|
)}
|
||||||
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Source</Forms.FormTitle>
|
<Forms.FormTitle tag="h5" style={{ marginTop: "10px" }}>Source</Forms.FormTitle>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
<Button onClick={() => openModal(modalProps => (
|
<Button
|
||||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
disabled={!theme.content || theme.id === "preview"}
|
||||||
<ModalHeader>
|
onClick={() => openModal(modalProps => (
|
||||||
<Forms.FormTitle tag="h4">Theme Source</Forms.FormTitle>
|
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||||
</ModalHeader>
|
<ModalHeader>
|
||||||
<ModalContent>
|
<Forms.FormTitle tag="h4">Theme Source</Forms.FormTitle>
|
||||||
<Forms.FormText style={{
|
</ModalHeader>
|
||||||
padding: "8px",
|
<ModalContent>
|
||||||
}}>
|
<Forms.FormText style={{
|
||||||
<CodeBlock lang="css" content={themeContent} />
|
padding: "8px",
|
||||||
</Forms.FormText>
|
}}>
|
||||||
</ModalContent>
|
<CodeBlock lang="css" content={themeContent} />
|
||||||
<ModalFooter>
|
</Forms.FormText>
|
||||||
<Button
|
</ModalContent>
|
||||||
color={Button.Colors.RED}
|
<ModalFooter>
|
||||||
look={Button.Looks.OUTLINED}
|
<Button
|
||||||
onClick={() => modalProps.onClose()}
|
color={Button.Colors.RED}
|
||||||
>
|
look={Button.Looks.OUTLINED}
|
||||||
Close
|
onClick={() => modalProps.onClose()}
|
||||||
</Button>
|
>
|
||||||
<Button className={Margins.right8}
|
Close
|
||||||
onClick={() => {
|
</Button>
|
||||||
Clipboard.copy(themeContent);
|
<Button className={Margins.right8}
|
||||||
showToast("Copied to Clipboard", Toasts.Type.SUCCESS);
|
onClick={() => {
|
||||||
}}>Copy to Clipboard</Button>
|
Clipboard.copy(themeContent);
|
||||||
</ModalFooter>
|
showToast("Copied to Clipboard", Toasts.Type.SUCCESS);
|
||||||
</ModalRoot>
|
}}>Copy to Clipboard</Button>
|
||||||
))}
|
</ModalFooter>
|
||||||
|
</ModalRoot>
|
||||||
|
))}
|
||||||
>
|
>
|
||||||
View Theme Source
|
View Theme Source
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -190,6 +192,7 @@ export const ThemeInfoModal: React.FC<ThemeInfoModalProps> = ({ author, theme, .
|
||||||
color={Button.Colors.GREEN}
|
color={Button.Colors.GREEN}
|
||||||
look={Button.Looks.OUTLINED}
|
look={Button.Looks.OUTLINED}
|
||||||
className={classes("vce-button", Margins.right8)}
|
className={classes("vce-button", Margins.right8)}
|
||||||
|
disabled={!theme.content || theme.id === "preview"}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const themesDir = await VencordNative.themes.getThemesDir();
|
const themesDir = await VencordNative.themes.getThemesDir();
|
||||||
const exists = await Native.themeExists(themesDir, theme);
|
const exists = await Native.themeExists(themesDir, theme);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -89,3 +89,41 @@
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vce-image-paste {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 3px dashed var(--background-modifier-accent);
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 20px;
|
||||||
|
transition: border 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vce-image-paste:hover {
|
||||||
|
border: 3px dashed var(--brand-500);
|
||||||
|
transition: border 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vce-styled-list {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vce-styled-list li {
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: thin solid var(--background-modifier-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vce-styled-list li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vce-divider-border {
|
||||||
|
border-bottom: thin solid var(--background-modifier-accent);
|
||||||
|
padding: 10px 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
|
@ -4,51 +4,74 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EquicordDevs } from "@utils/constants";
|
import { ModalProps } from "@utils/modal";
|
||||||
import definePlugin from "@utils/types";
|
import { User } from "discord-types/general";
|
||||||
import { SettingsRouter } from "@webpack/common";
|
|
||||||
|
|
||||||
import { settings } from "./utils/settings";
|
type Author = {
|
||||||
|
github_name?: string;
|
||||||
|
discord_name: string;
|
||||||
|
discord_snowflake: string;
|
||||||
|
};
|
||||||
|
|
||||||
export default definePlugin({
|
export interface Theme {
|
||||||
name: "ThemeLibrary",
|
id: string;
|
||||||
description: "A library of themes for Vencord.",
|
name: string;
|
||||||
authors: [EquicordDevs.Fafa],
|
content: string;
|
||||||
settings,
|
type: string | "theme" | "snippet";
|
||||||
toolboxActions: {
|
description: string;
|
||||||
"Open Theme Library": () => {
|
version: string;
|
||||||
SettingsRouter.open("ThemeLibrary");
|
author: Author | Author[];
|
||||||
},
|
likes: number;
|
||||||
},
|
tags: string[];
|
||||||
|
thumbnail_url: string;
|
||||||
|
release_date: Date;
|
||||||
|
last_updated?: Date;
|
||||||
|
guild?: {
|
||||||
|
name: string;
|
||||||
|
snowflake: string;
|
||||||
|
invite_link: string;
|
||||||
|
};
|
||||||
|
source?: string;
|
||||||
|
requiresThemeAttributes?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
start() {
|
export interface ThemeInfoModalProps extends ModalProps {
|
||||||
const customSettingsSections = (
|
author: User | User[];
|
||||||
Vencord.Plugins.plugins.Settings as any as {
|
theme: Theme;
|
||||||
customSections: ((ID: Record<string, unknown>) => any)[];
|
}
|
||||||
}
|
|
||||||
).customSections;
|
|
||||||
|
|
||||||
const ThemeSection = () => ({
|
export const enum TabItem {
|
||||||
section: "ThemeLibrary",
|
THEMES,
|
||||||
label: "Theme Library",
|
SUBMIT_THEMES,
|
||||||
element: require("./components/ThemeTab").default,
|
}
|
||||||
id: "ThemeSection",
|
|
||||||
});
|
|
||||||
|
|
||||||
customSettingsSections.push(ThemeSection);
|
export interface LikesComponentProps {
|
||||||
},
|
theme: Theme;
|
||||||
|
userId: User["id"];
|
||||||
|
}
|
||||||
|
|
||||||
stop() {
|
export const enum SearchStatus {
|
||||||
const customSettingsSections = (
|
ALL,
|
||||||
Vencord.Plugins.plugins.Settings as any as {
|
ENABLED,
|
||||||
customSections: ((ID: Record<string, unknown>) => any)[];
|
DISABLED,
|
||||||
}
|
THEME,
|
||||||
).customSections;
|
SNIPPET,
|
||||||
|
DARK,
|
||||||
|
LIGHT,
|
||||||
|
LIKED,
|
||||||
|
}
|
||||||
|
|
||||||
const i = customSettingsSections.findIndex(
|
export type ThemeLikeProps = {
|
||||||
section => section({}).id === "ThemeSection"
|
status: number;
|
||||||
);
|
likes: [{
|
||||||
|
themeId: number;
|
||||||
|
userIds: User["id"][];
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
if (i !== -1) customSettingsSections.splice(i, 1);
|
export interface Contributor {
|
||||||
},
|
username: User["username"];
|
||||||
});
|
github_username: string;
|
||||||
|
id: User["id"];
|
||||||
|
avatar: string;
|
||||||
|
}
|
||||||
|
|
|
@ -68,3 +68,10 @@ export type ThemeLikeProps = {
|
||||||
userIds: User["id"][];
|
userIds: User["id"][];
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface Contributor {
|
||||||
|
username: User["username"];
|
||||||
|
github_username: string;
|
||||||
|
id: User["id"];
|
||||||
|
avatar: string;
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { showNotification } from "@api/Notifications";
|
||||||
import { openModal } from "@utils/modal";
|
import { openModal } from "@utils/modal";
|
||||||
import { OAuth2AuthorizeModal, Toasts, UserStore } from "@webpack/common";
|
import { OAuth2AuthorizeModal, Toasts, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import { logger } from "../components/LikesComponent";
|
import { logger, themeRequest } from "../components/ThemeTab";
|
||||||
|
|
||||||
export async function authorizeUser(triggerModal: boolean = true) {
|
export async function authorizeUser(triggerModal: boolean = true) {
|
||||||
const isAuthorized = await getAuthorization();
|
const isAuthorized = await getAuthorization();
|
||||||
|
@ -76,7 +76,7 @@ export async function deauthorizeUser() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await fetch("https://themes-delta.vercel.app/api/user/revoke", {
|
const res = await themeRequest("/user/revoke", {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
|
@ -111,7 +111,7 @@ export async function getAuthorization() {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// check if valid
|
// check if valid
|
||||||
const res = await fetch("https://themes-delta.vercel.app/api/user/findUserByToken", {
|
const res = await themeRequest("/user/findUserByToken", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue