Notification Log: fix lag if there are too many entries (#3584)
Use Discord's lazy list implementation for only rendering what's on screen
This commit is contained in:
parent
1ebd412392
commit
6380111f32
5 changed files with 106 additions and 40 deletions
|
|
@ -104,9 +104,7 @@ export default ErrorBoundary.wrap(function NotificationComponent({
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
{richBody ?? <p className="vc-notification-p">{body}</p>}
|
||||
</div>
|
||||
{richBody ?? <p className="vc-notification-p">{body}</p>}
|
||||
</div>
|
||||
</div>
|
||||
{image && <img className="vc-notification-img" src={image} alt="" />}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import { Settings } from "@api/Settings";
|
|||
import { classNameFactory } from "@api/Styles";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { openNotificationSettingsModal } from "@components/settings/tabs/vencord/NotificationSettings";
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { closeModal, ModalCloseButton, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { Alerts, Button, Forms, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
||||
import { Alerts, Button, Forms, ListScrollerThin, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
||||
import { nanoid } from "nanoid";
|
||||
import type { DispatchWithoutAction } from "react";
|
||||
|
||||
|
|
@ -103,21 +103,9 @@ export function useLogs() {
|
|||
|
||||
function NotificationEntry({ data }: { data: PersistentNotificationData; }) {
|
||||
const [removing, setRemoving] = useState(false);
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const div = ref.current!;
|
||||
|
||||
const setHeight = () => {
|
||||
if (div.clientHeight === 0) return requestAnimationFrame(setHeight);
|
||||
div.style.height = `${div.clientHeight}px`;
|
||||
};
|
||||
|
||||
setHeight();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={cl("wrapper", { removing })} ref={ref}>
|
||||
<div className={cl("wrapper", { removing })}>
|
||||
<NotificationComponent
|
||||
{...data}
|
||||
permanent={true}
|
||||
|
|
@ -129,13 +117,13 @@ function NotificationEntry({ data }: { data: PersistentNotificationData; }) {
|
|||
setTimeout(() => deleteNotification(data.timestamp), 200);
|
||||
}}
|
||||
richBody={
|
||||
<div className={cl("body")}>
|
||||
{data.body}
|
||||
<div className={cl("body-wrapper")}>
|
||||
<div className={cl("body")}>{data.body}</div>
|
||||
<Timestamp timestamp={new Date(data.timestamp)} className={cl("timestamp")} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -151,9 +139,14 @@ export function NotificationLog({ log, pending }: { log: PersistentNotificationD
|
|||
);
|
||||
|
||||
return (
|
||||
<div className={cl("container")}>
|
||||
{log.map(n => <NotificationEntry data={n} key={n.id} />)}
|
||||
</div>
|
||||
<ListScrollerThin
|
||||
className={cl("container")}
|
||||
sections={[log.length]}
|
||||
sectionHeight={0}
|
||||
rowHeight={120}
|
||||
renderSection={() => null}
|
||||
renderRow={item => <NotificationEntry data={log[item.row]} key={log[item.row].id} />}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -161,15 +154,15 @@ function LogModal({ modalProps, close }: { modalProps: ModalProps; close(): void
|
|||
const [log, pending] = useLogs();
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE} className={cl("modal")}>
|
||||
<ModalHeader>
|
||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>Notification Log</Text>
|
||||
<ModalCloseButton onClick={close} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent>
|
||||
<div style={{ width: "100%" }}>
|
||||
<NotificationLog log={log} pending={pending} />
|
||||
</ModalContent>
|
||||
</div>
|
||||
|
||||
<ModalFooter>
|
||||
<Flex>
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
.vc-notification-content {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vc-notification-header {
|
||||
|
|
@ -81,6 +82,11 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.vc-notification-log-modal {
|
||||
max-width: 962px;
|
||||
width: clamp(var(--modal-width-large, 800px), 962px, 85vw);
|
||||
}
|
||||
|
||||
.vc-notification-log-empty {
|
||||
height: 218px;
|
||||
background: url("/assets/b36de980b174d7b798c89f35c116e5c6.svg") center no-repeat;
|
||||
|
|
@ -88,19 +94,23 @@
|
|||
}
|
||||
|
||||
.vc-notification-log-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
overflow: hidden;
|
||||
max-height: min(750px, 75vh);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vc-notification-log-wrapper {
|
||||
height: 120px;
|
||||
width: 100%;
|
||||
padding-bottom: 16px;
|
||||
box-sizing: border-box;
|
||||
transition: 200ms ease;
|
||||
transition-property: height, opacity;
|
||||
}
|
||||
|
||||
.vc-notification-log-wrapper:not(:last-child) {
|
||||
margin-bottom: 1em;
|
||||
/* stylelint-disable-next-line no-descending-specificity */
|
||||
.vc-notification-root {
|
||||
height: 104px;
|
||||
}
|
||||
}
|
||||
|
||||
.vc-notification-log-removing {
|
||||
|
|
@ -109,9 +119,18 @@
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.vc-notification-log-body {
|
||||
.vc-notification-log-body-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vc-notification-log-body {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.vc-notification-log-timestamp {
|
||||
|
|
@ -123,4 +142,4 @@
|
|||
.vc-notification-log-danger-btn {
|
||||
color: var(--white-500);
|
||||
background-color: var(--button-danger-background);
|
||||
}
|
||||
}
|
||||
|
|
@ -70,14 +70,23 @@ export const ColorPicker = waitForComponent<t.ColorPicker>("ColorPicker", filter
|
|||
export const UserSummaryItem = waitForComponent("UserSummaryItem", filters.componentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
|
||||
|
||||
export let createScroller: (scrollbarClassName: string, fadeClassName: string, customThemeClassName: string) => t.ScrollerThin;
|
||||
export let createListScroller: (scrollBarClassName: string, fadeClassName: string, someOtherClassIdkMan: string, resizeObserverClass: typeof ResizeObserver) => t.ListScrollerThin;
|
||||
export let scrollerClasses: Record<string, string>;
|
||||
export let listScrollerClasses: Record<string, string>;
|
||||
|
||||
waitFor(filters.byCode('="ltr",orientation:', "customTheme:", "forwardRef"), m => createScroller = m);
|
||||
waitFor(filters.byCode("getScrollerNode:", "resizeObserver:", "sectionHeight:"), m => createListScroller = m);
|
||||
waitFor(["thin", "auto", "customTheme"], m => scrollerClasses = m);
|
||||
waitFor(m => m.thin && m.auto && !m.customTheme, m => listScrollerClasses = m);
|
||||
|
||||
export const ScrollerNone = LazyComponent(() => createScroller(scrollerClasses.none, scrollerClasses.fade, scrollerClasses.customTheme));
|
||||
export const ScrollerThin = LazyComponent(() => createScroller(scrollerClasses.thin, scrollerClasses.fade, scrollerClasses.customTheme));
|
||||
export const ScrollerAuto = LazyComponent(() => createScroller(scrollerClasses.auto, scrollerClasses.fade, scrollerClasses.customTheme));
|
||||
|
||||
export const ListScrollerNone = LazyComponent(() => createListScroller(listScrollerClasses.none, listScrollerClasses.fade, "", ResizeObserver));
|
||||
export const ListScrollerThin = LazyComponent(() => createListScroller(listScrollerClasses.thin, listScrollerClasses.fade, "", ResizeObserver));
|
||||
export const ListScrollerAuto = LazyComponent(() => createListScroller(listScrollerClasses.auto, listScrollerClasses.fade, "", ResizeObserver));
|
||||
|
||||
const { FocusLock_ } = mapMangledModuleLazy('document.getElementById("app-mount"))', {
|
||||
FocusLock_: filters.componentByCode(".containerRef")
|
||||
}) as {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue