diff --git a/package.json b/package.json index 6033de408..9a8548a61 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": "true", "version": "1.1.3", "description": "The cutest Discord client mod", - "keywords": [ ], + "keywords": [], "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { "url": "https://github.com/Vendicated/Vencord/issues" @@ -34,11 +34,13 @@ "dependencies": { "@vap/core": "0.0.12", "@vap/shiki": "0.10.3", - "fflate": "^0.7.4" + "fflate": "^0.7.4", + "nanoid": "^4.0.2" }, "devDependencies": { "@types/diff": "^5.0.2", "@types/lodash": "^4.14.191", + "@types/nanoid": "^3.0.0", "@types/node": "^18.11.18", "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 30ce7bba0..6ac1fb7ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,7 @@ patchedDependencies: specifiers: '@types/diff': ^5.0.2 '@types/lodash': ^4.14.191 + '@types/nanoid': ^3.0.0 '@types/node': ^18.11.18 '@types/react': ^18.0.27 '@types/react-dom': ^18.0.10 @@ -31,6 +32,7 @@ specifiers: fflate: ^0.7.4 highlight.js: 10.6.0 moment: ^2.29.4 + nanoid: ^4.0.2 puppeteer-core: ^19.6.0 standalone-electron-types: ^1.0.0 stylelint: ^14.16.1 @@ -43,10 +45,12 @@ dependencies: '@vap/core': 0.0.12 '@vap/shiki': 0.10.3 fflate: 0.7.4 + nanoid: 4.0.2 devDependencies: '@types/diff': 5.0.2 '@types/lodash': 4.14.191 + '@types/nanoid': 3.0.0 '@types/node': 18.11.18 '@types/react': 18.0.27 '@types/react-dom': 18.0.10 @@ -417,6 +421,13 @@ packages: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true + /@types/nanoid/3.0.0: + resolution: {integrity: sha512-UXitWSmXCwhDmAKe7D3hNQtQaHeHt5L8LO1CB8GF8jlYVzOv5cBWDNqiJ+oPEWrWei3i3dkZtHY/bUtd0R/uOQ==} + deprecated: This is a stub types definition. nanoid provides its own type definitions, so you do not need this installed. + dependencies: + nanoid: 4.0.2 + dev: true + /@types/node/18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true @@ -2245,6 +2256,11 @@ packages: hasBin: true dev: true + /nanoid/4.0.2: + resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} + engines: {node: ^14 || ^16 || >=18} + hasBin: true + /nanomatch/1.2.13: resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} engines: {node: '>=0.10.0'} diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index dd7d32ebc..b9d361b6d 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -36,7 +36,7 @@ const commonOptions = { entryPoints: ["browser/Vencord.ts"], globalName: "Vencord", format: "iife", - external: ["plugins", "git-hash"], + external: ["plugins", "git-hash", "/assets/*"], plugins: [ globPlugins, ...commonOpts.plugins, diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 80c9ae1ad..53eb23f71 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -193,7 +193,7 @@ export const commonOpts = { legalComments: "linked", banner, plugins: [fileIncludePlugin, gitHashPlugin, gitRemotePlugin, stylePlugin], - external: ["~plugins", "~git-hash", "~git-remote"], + external: ["~plugins", "~git-hash", "~git-remote", "/assets/*"], inject: ["./scripts/build/inject/react.mjs"], jsxFactory: "VencordCreateElement", jsxFragment: "VencordFragment", diff --git a/src/Vencord.ts b/src/Vencord.ts index af6ca088a..00f8a58c0 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -54,6 +54,7 @@ async function init() { title: "Vencord has been updated!", body: "Click here to restart", permanent: true, + noPersist: true, onClick() { if (needsFullRestart) window.DiscordNative.app.relaunch(); @@ -69,6 +70,7 @@ async function init() { title: "A Vencord update is available!", body: "Click here to view the update", permanent: true, + noPersist: true, onClick() { SettingsRouter.open("VencordUpdater"); } diff --git a/src/api/Notifications/NotificationComponent.tsx b/src/api/Notifications/NotificationComponent.tsx index 53c1b8137..542c29bbf 100644 --- a/src/api/Notifications/NotificationComponent.tsx +++ b/src/api/Notifications/NotificationComponent.tsx @@ -20,6 +20,7 @@ import "./styles.css"; import { useSettings } from "@api/settings"; import ErrorBoundary from "@components/ErrorBoundary"; +import { classes } from "@utils/misc"; import { React, useEffect, useMemo, useState, useStateFromStores, WindowStore } from "@webpack/common"; import { NotificationData } from "./Notifications"; @@ -33,8 +34,10 @@ export default ErrorBoundary.wrap(function NotificationComponent({ onClick, onClose, image, - permanent -}: NotificationData) { + permanent, + className, + dismissOnClick +}: NotificationData & { className?: string; }) { const { timeout, position } = useSettings(["notifications.timeout", "notifications.position"]).notifications; const hasFocus = useStateFromStores([WindowStore], () => WindowStore.isFocused()); @@ -61,11 +64,12 @@ export default ErrorBoundary.wrap(function NotificationComponent({ return ( + + + ); +} + +export function openNotificationLogModal() { + const key = openModal(modalProps => ( + closeModal(key)} + /> + )); +} diff --git a/src/api/Notifications/styles.css b/src/api/Notifications/styles.css index cd3714282..98dff6df1 100644 --- a/src/api/Notifications/styles.css +++ b/src/api/Notifications/styles.css @@ -3,16 +3,20 @@ all: unset; display: flex; flex-direction: column; - width: 25vw; - min-height: 10vh; color: var(--text-normal); background-color: var(--background-secondary-alt); - position: absolute; - z-index: 2147483647; - right: 1rem; border-radius: 6px; overflow: hidden; cursor: pointer; + width: 100%; +} + +.vc-notification-root:not(.vc-notification-log-wrapper > .vc-notification-root) { + position: absolute; + z-index: 2147483647; + right: 1rem; + width: 25vw; + min-height: 10vh; } .vc-notification { @@ -72,3 +76,47 @@ .vc-notification-img { width: 100%; } + +.vc-notification-log-empty { + height: 218px; + background: url("/assets/b36de980b174d7b798c89f35c116e5c6.svg") center no-repeat; + margin-bottom: 40px; +} + +.vc-notification-log-container { + display: flex; + flex-direction: column; + padding: 1em; + overflow: hidden; +} + +.vc-notification-log-wrapper { + transition: 200ms ease; + transition-property: height, opacity; +} + +.vc-notification-log-wrapper:not(:last-child) { + margin-bottom: 1em; +} + +.vc-notification-log-removing { + height: 0 !important; + opacity: 0; + margin-bottom: 1em; +} + +.vc-notification-log-body { + display: flex; + flex-direction: column; +} + +.vc-notification-log-timestamp { + margin-left: auto; + font-size: 0.8em; + font-weight: lighter; +} + +.vc-notification-log-danger-btn { + color: var(--white-500); + background-color: var(--button-danger-background); +} diff --git a/src/api/settings.ts b/src/api/settings.ts index 0aaa4907e..321a4c423 100644 --- a/src/api/settings.ts +++ b/src/api/settings.ts @@ -47,6 +47,7 @@ export interface Settings { timeout: number; position: "top-right" | "bottom-right"; useNative: "always" | "never" | "not-focused"; + logLimit: number; }; } @@ -66,7 +67,8 @@ const DefaultSettings: Settings = { notifications: { timeout: 5000, position: "bottom-right", - useNative: "not-focused" + useNative: "not-focused", + logLimit: 50 } }; diff --git a/src/components/VencordSettings/VencordTab.tsx b/src/components/VencordSettings/VencordTab.tsx index 120f4c520..7113421a6 100644 --- a/src/components/VencordSettings/VencordTab.tsx +++ b/src/components/VencordSettings/VencordTab.tsx @@ -17,6 +17,7 @@ */ +import { openNotificationLogModal } from "@api/Notifications/notificationLog"; import { useSettings } from "@api/settings"; import { classNameFactory } from "@api/Styles"; import DonateButton from "@components/DonateButton"; @@ -165,7 +166,7 @@ function VencordSettings() { { label: "Only use Desktop notifications when Discord is not focused", value: "not-focused", default: true }, { label: "Always use Desktop notifications", value: "always" }, { label: "Always use Vencord notifications", value: "never" }, - ]satisfies Array<{ value: typeof settings["notifications"]["useNative"]; } & Record>} + ] satisfies Array<{ value: typeof settings["notifications"]["useNative"]; } & Record>} closeOnSelect={true} select={v => notifSettings.useNative = v} isSelected={v => v === notifSettings.useNative} @@ -179,7 +180,7 @@ function VencordSettings() { options={[ { label: "Bottom Right", value: "bottom-right", default: true }, { label: "Top Right", value: "top-right" }, - ]satisfies Array<{ value: typeof settings["notifications"]["position"]; } & Record>} + ] satisfies Array<{ value: typeof settings["notifications"]["position"]; } & Record>} select={v => notifSettings.position = v} isSelected={v => v === notifSettings.position} serialize={identity} @@ -198,6 +199,29 @@ function VencordSettings() { onMarkerRender={v => (v / 1000) + "s"} stickToMarkers={false} /> + + Notification Log Limit + + The amount of notifications to save in the log until old ones are removed. + Set to 0 to disable Notification log and to never automatically remove old Notifications + + notifSettings.logLimit = v} + onValueRender={v => v === 200 ? "∞" : v} + onMarkerRender={v => v === 200 ? "∞" : v} + /> + + ); } diff --git a/src/plugins/crashHandler.ts b/src/plugins/crashHandler.ts index 6457e09d2..61b06b353 100644 --- a/src/plugins/crashHandler.ts +++ b/src/plugins/crashHandler.ts @@ -78,6 +78,7 @@ export default definePlugin({ color: "#eed202", title: "Discord has crashed!", body: "Awn :( Discord has crashed more than five times, not attempting to recover.", + noPersist: true, }); } catch { } @@ -111,6 +112,7 @@ export default definePlugin({ color: "#eed202", title: "Discord has crashed!", body: "Attempting to recover...", + noPersist: true, }); } catch { } } diff --git a/src/plugins/devCompanion.dev.tsx b/src/plugins/devCompanion.dev.tsx index b9a7e4d82..c675fc237 100644 --- a/src/plugins/devCompanion.dev.tsx +++ b/src/plugins/devCompanion.dev.tsx @@ -116,7 +116,8 @@ function initWs(isManual = false) { showNotification({ title: "Dev Companion Error", body: (e as ErrorEvent).message || "No Error Message", - color: "var(--status-danger, red)" + color: "var(--status-danger, red)", + noPersist: true, }); }); @@ -128,7 +129,8 @@ function initWs(isManual = false) { showNotification({ title: "Dev Companion Disconnected", body: e.reason || "No Reason provided", - color: "var(--status-danger, red)" + color: "var(--status-danger, red)", + noPersist: true, }); }); diff --git a/src/webpack/common/react.ts b/src/webpack/common/react.ts index d73a3dfea..17196132d 100644 --- a/src/webpack/common/react.ts +++ b/src/webpack/common/react.ts @@ -24,10 +24,12 @@ export let useState: typeof React.useState; export let useEffect: typeof React.useEffect; export let useMemo: typeof React.useMemo; export let useRef: typeof React.useRef; +export let useReducer: typeof React.useReducer; +export let useCallback: typeof React.useCallback; export const ReactDOM: typeof import("react-dom") & typeof import("react-dom/client") = findByPropsLazy("createPortal", "render"); waitFor("useState", m => { React = m; - ({ useEffect, useState, useMemo, useRef } = React); + ({ useEffect, useState, useMemo, useRef, useReducer, useCallback } = React); });