Add a "NEW" Badge for New Plugins (V2)! (#234)
Co-authored-by: Ven <vendicated@riseup.net> Co-authored-by: Justice Almanzar <superdash993@gmail.com> Co-authored-by: ArjixWasTaken <53124886+ArjixWasTaken@users.noreply.github.com>
This commit is contained in:
parent
074542f0b3
commit
aff1b68d6b
|
@ -34,6 +34,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/diff": "^5.0.2",
|
"@types/diff": "^5.0.2",
|
||||||
|
"@types/lodash": "^4.14.0",
|
||||||
"@types/node": "^18.11.9",
|
"@types/node": "^18.11.9",
|
||||||
"@types/react": "^18.0.25",
|
"@types/react": "^18.0.25",
|
||||||
"@types/react-dom": "^18.0.9",
|
"@types/react-dom": "^18.0.9",
|
||||||
|
|
|
@ -10,6 +10,7 @@ patchedDependencies:
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@types/diff': ^5.0.2
|
'@types/diff': ^5.0.2
|
||||||
|
'@types/lodash': ^4.14.0
|
||||||
'@types/node': ^18.11.9
|
'@types/node': ^18.11.9
|
||||||
'@types/react': ^18.0.25
|
'@types/react': ^18.0.25
|
||||||
'@types/react-dom': ^18.0.9
|
'@types/react-dom': ^18.0.9
|
||||||
|
@ -41,6 +42,7 @@ dependencies:
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/diff': 5.0.2
|
'@types/diff': 5.0.2
|
||||||
|
'@types/lodash': 4.14.189
|
||||||
'@types/node': 18.11.9
|
'@types/node': 18.11.9
|
||||||
'@types/react': 18.0.25
|
'@types/react': 18.0.25
|
||||||
'@types/react-dom': 18.0.9
|
'@types/react-dom': 18.0.9
|
||||||
|
@ -152,6 +154,10 @@ packages:
|
||||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/lodash/4.14.189:
|
||||||
|
resolution: {integrity: sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node/18.11.9:
|
/@types/node/18.11.9:
|
||||||
resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==}
|
resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
30
src/components/PluginSettings/components/BadgeComponent.tsx
Normal file
30
src/components/PluginSettings/components/BadgeComponent.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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 { BadgeStyle } from "@components/PluginSettings/styles";
|
||||||
|
|
||||||
|
export function Badge({ text, color }): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
backgroundColor: color,
|
||||||
|
justifySelf: "flex-end",
|
||||||
|
marginLeft: "auto",
|
||||||
|
...BadgeStyle
|
||||||
|
}}>{text}</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ export interface ISettingElementProps<T extends PluginOptionBase> {
|
||||||
onError(hasError: boolean): void;
|
onError(hasError: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export * from "./BadgeComponent";
|
||||||
export * from "./SettingBooleanComponent";
|
export * from "./SettingBooleanComponent";
|
||||||
export * from "./SettingCustomComponent";
|
export * from "./SettingCustomComponent";
|
||||||
export * from "./SettingNumericComponent";
|
export * from "./SettingNumericComponent";
|
||||||
|
|
|
@ -16,26 +16,27 @@
|
||||||
* 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 * as DataStore from "@api/DataStore";
|
||||||
import { showNotice } from "@api/Notices";
|
import { showNotice } from "@api/Notices";
|
||||||
import { Settings, useSettings } from "@api/settings";
|
import { Settings, useSettings } from "@api/settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { handleComponentFailed } from "@components/handleComponentFailed";
|
import { handleComponentFailed } from "@components/handleComponentFailed";
|
||||||
|
import { Badge } from "@components/PluginSettings/components";
|
||||||
|
import PluginModal from "@components/PluginSettings/PluginModal";
|
||||||
|
import * as styles from "@components/PluginSettings/styles";
|
||||||
import { ChangeList } from "@utils/ChangeList";
|
import { ChangeList } from "@utils/ChangeList";
|
||||||
import Logger from "@utils/Logger";
|
import Logger from "@utils/Logger";
|
||||||
import { classes, LazyComponent } from "@utils/misc";
|
import { classes, LazyComponent, useAwaiter } from "@utils/misc";
|
||||||
import { openModalLazy } from "@utils/modal";
|
import { openModalLazy } from "@utils/modal";
|
||||||
import { Plugin } from "@utils/types";
|
import { Plugin } from "@utils/types";
|
||||||
import { findByCode, findByPropsLazy } from "@webpack";
|
import { findByCode, findByPropsLazy } from "@webpack";
|
||||||
import { Alerts, Button, Forms, Margins, Parser, React, Select, Switch, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
|
import { Alerts, Button, Forms, Margins, Parser, React, Select, Switch, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
|
||||||
|
import { startDependenciesRecursive, startPlugin, stopPlugin } from "plugins";
|
||||||
|
|
||||||
import Plugins from "~plugins";
|
import Plugins from "~plugins";
|
||||||
|
|
||||||
import { startDependenciesRecursive, startPlugin, stopPlugin } from "../../plugins";
|
|
||||||
import PluginModal from "./PluginModal";
|
|
||||||
import * as styles from "./styles";
|
|
||||||
|
|
||||||
const logger = new Logger("PluginSettings", "#a6d189");
|
const logger = new Logger("PluginSettings", "#a6d189");
|
||||||
|
|
||||||
const InputStyles = findByPropsLazy("inputDefault", "inputWrapper");
|
const InputStyles = findByPropsLazy("inputDefault", "inputWrapper");
|
||||||
|
@ -78,9 +79,10 @@ interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
|
||||||
plugin: Plugin;
|
plugin: Plugin;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
onRestartNeeded(name: string): void;
|
onRestartNeeded(name: string): void;
|
||||||
|
isNew?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave }: PluginCardProps) {
|
function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const pluginSettings = settings.plugins[plugin.name];
|
const pluginSettings = settings.plugins[plugin.name];
|
||||||
|
|
||||||
|
@ -162,8 +164,15 @@ function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLe
|
||||||
</Text>}
|
</Text>}
|
||||||
hideBorder={true}
|
hideBorder={true}
|
||||||
>
|
>
|
||||||
<Flex style={{ marginTop: "auto", width: "100%", height: "100%", alignItems: "center" }}>
|
<Flex style={{ marginTop: "auto", width: "100%", height: "100%", alignItems: "center", gap: "8px" }}>
|
||||||
<Text variant="text-md/bold" style={{ flexGrow: "1" }}>{plugin.name}</Text>
|
<Text
|
||||||
|
variant="text-md/bold"
|
||||||
|
style={{
|
||||||
|
display: "flex", width: "100%", alignItems: "center", flexGrow: "1", gap: "8px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{plugin.name}{(isNew) && <Badge text="NEW" color="#ED4245" />}
|
||||||
|
</Text>
|
||||||
<button role="switch" onClick={() => openModal()} style={styles.SettingsIcon} className="button-12Fmur">
|
<button role="switch" onClick={() => openModal()} style={styles.SettingsIcon} className="button-12Fmur">
|
||||||
{plugin.options
|
{plugin.options
|
||||||
? <CogWheel
|
? <CogWheel
|
||||||
|
@ -243,6 +252,23 @@ export default ErrorBoundary.wrap(function Settings() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [newPlugins] = useAwaiter(() => DataStore.get("Vencord_existingPlugins").then((cachedPlugins: Record<string, number> | undefined) => {
|
||||||
|
const now = Date.now() / 1000;
|
||||||
|
const existingTimestamps: Record<string, number> = {};
|
||||||
|
const sortedPluginNames = Object.values(sortedPlugins).map(plugin => plugin.name);
|
||||||
|
|
||||||
|
const newPlugins: string[] = [];
|
||||||
|
for (const { name: p } of sortedPlugins) {
|
||||||
|
const time = existingTimestamps[p] = cachedPlugins?.[p] ?? now;
|
||||||
|
if ((time + 60 * 60 * 24 * 2) > now) {
|
||||||
|
newPlugins.push(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataStore.set("Vencord_existingPlugins", existingTimestamps);
|
||||||
|
|
||||||
|
return window._.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins;
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Forms.FormSection>
|
<Forms.FormSection>
|
||||||
<Forms.FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}>
|
<Forms.FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}>
|
||||||
|
@ -281,6 +307,7 @@ export default ErrorBoundary.wrap(function Settings() {
|
||||||
onRestartNeeded={name => changes.add(name)}
|
onRestartNeeded={name => changes.add(name)}
|
||||||
disabled={plugin.required || !!dependency}
|
disabled={plugin.required || !!dependency}
|
||||||
plugin={plugin}
|
plugin={plugin}
|
||||||
|
isNew={newPlugins?.includes(plugin.name)}
|
||||||
key={plugin.name}
|
key={plugin.name}
|
||||||
/>;
|
/>;
|
||||||
})
|
})
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const PluginsGridItem: React.CSSProperties = {
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
display: "block",
|
display: "block",
|
||||||
height: "min-content",
|
height: "100%",
|
||||||
padding: 10,
|
padding: 10,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
};
|
};
|
||||||
|
@ -48,3 +48,14 @@ export const SettingsIcon: React.CSSProperties = {
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
marginRight: 8
|
marginRight: 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const BadgeStyle: React.CSSProperties = {
|
||||||
|
padding: "0 6px",
|
||||||
|
fontFamily: "var(--font-display)",
|
||||||
|
fontWeight: "500",
|
||||||
|
borderRadius: "8px",
|
||||||
|
height: "16px",
|
||||||
|
fontSize: "12px",
|
||||||
|
lineHeight: "16px",
|
||||||
|
color: "var(--white-500)",
|
||||||
|
};
|
||||||
|
|
2
src/globals.d.ts
vendored
2
src/globals.d.ts
vendored
|
@ -16,6 +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 { LoDashStatic } from "lodash";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +55,7 @@ declare global {
|
||||||
push(chunk: any): any;
|
push(chunk: any): any;
|
||||||
pop(): any;
|
pop(): any;
|
||||||
};
|
};
|
||||||
|
_: LoDashStatic;
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue