diff --git a/packages/discord-types/src/components.d.ts b/packages/discord-types/src/components.d.ts index eff9d73a..1df03735 100644 --- a/packages/discord-types/src/components.d.ts +++ b/packages/discord-types/src/components.d.ts @@ -474,19 +474,66 @@ export type MaskedLink = ComponentType>; -export type ScrollerThin = ComponentType>; +interface BaseListItem { + anchorId: any; + listIndex: number; + offsetTop: number; + section: number; +} +interface ListSection extends BaseListItem { + type: "section"; +} +interface ListRow extends BaseListItem { + type: "row"; + row: number; + rowIndex: number; +} + +export type ListScrollerThin = ComponentType React.ReactNode; + renderRow: (item: ListRow) => React.ReactNode; + renderFooter?: (item: any) => React.ReactNode; + renderSidebar?: (listVisible: boolean, sidebarVisible: boolean) => React.ReactNode; + wrapSection?: (section: number, children: React.ReactNode) => React.ReactNode; + + sectionHeight: number; + rowHeight: number; + footerHeight?: number; + sidebarHeight?: number; + + chunkSize?: number; + + paddingTop?: number; + paddingBottom?: number; + fade?: boolean; + onResize?: Function; + getAnchorId?: any; + + innerTag?: string; + innerId?: string; + innerClassName?: string; + innerRole?: string; + innerAriaLabel?: string; + // Yes, Discord uses this casing + innerAriaMultiselectable?: boolean; + innerAriaOrientation?: "vertical" | "horizontal"; +}>; + export type Clickable = (props: PropsWithChildren> & { tag?: T; }) => ReactNode; diff --git a/src/api/Notifications/NotificationComponent.tsx b/src/api/Notifications/NotificationComponent.tsx index d07143c4..52e56d5d 100644 --- a/src/api/Notifications/NotificationComponent.tsx +++ b/src/api/Notifications/NotificationComponent.tsx @@ -104,9 +104,7 @@ export default ErrorBoundary.wrap(function NotificationComponent({ -
- {richBody ??

{body}

} -
+ {richBody ??

{body}

} {image && } diff --git a/src/api/Notifications/notificationLog.tsx b/src/api/Notifications/notificationLog.tsx index a943db07..dca7679e 100644 --- a/src/api/Notifications/notificationLog.tsx +++ b/src/api/Notifications/notificationLog.tsx @@ -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(null); - - useEffect(() => { - const div = ref.current!; - - const setHeight = () => { - if (div.clientHeight === 0) return requestAnimationFrame(setHeight); - div.style.height = `${div.clientHeight}px`; - }; - - setHeight(); - }, []); return ( -
+
deleteNotification(data.timestamp), 200); }} richBody={ -
- {data.body} +
+
{data.body}
} /> -
+
); } @@ -151,9 +139,14 @@ export function NotificationLog({ log, pending }: { log: PersistentNotificationD ); return ( -
- {log.map(n => )} -
+ null} + renderRow={item => } + /> ); } @@ -161,15 +154,15 @@ function LogModal({ modalProps, close }: { modalProps: ModalProps; close(): void const [log, pending] = useLogs(); return ( - + Notification Log - +
- +
diff --git a/src/api/Notifications/styles.css b/src/api/Notifications/styles.css index 10d3c0cf..52cbc805 100644 --- a/src/api/Notifications/styles.css +++ b/src/api/Notifications/styles.css @@ -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); -} +} \ No newline at end of file diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index bdf0b2c4..bf7c9dd1 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -70,14 +70,23 @@ export const ColorPicker = waitForComponent("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; +export let listScrollerClasses: Record; + 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 {