From 7e92fd94a32028486a22aa9ab521b101448c32c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Z=20=E2=9D=A4=27s=20Life?= Date: Wed, 28 Jan 2026 09:00:02 -0500 Subject: [PATCH] holyNotes: fix - Modal Fixes, Don't Update Global UserStore (#693) * Fix: holyNotes - Make Modal Actually Bearable * Fix: holyNotes - Fix Double Declaration * Fix: holyNotes - Don't Change Global UserStore Co-Authored-By: prism <69634294+imjustprism@users.noreply.github.com> * QOL: holyNotes - Change Bot Tag to "NOTE" * Fix: holyNotes - Fuck Your Comments GitHub Copilot, I Thought I Turned You Off * Feat: holyNotes - UI Overhaul, Fixed Spaghetti CSS * Fix: holyNotes - Fix CSS Classes * Fix: holyNotes - Fix Fetching User Profiles for Notes --------- Co-authored-by: prism <69634294+imjustprism@users.noreply.github.com> --- .../holyNotes/components/modals/HelpModal.tsx | 11 +- .../holyNotes/components/modals/Notebook.tsx | 230 ++++++++++++------ .../components/modals/RenderMessage.tsx | 18 +- src/equicordplugins/holyNotes/index.tsx | 1 - src/equicordplugins/holyNotes/style.css | 161 +++++++++--- 5 files changed, 299 insertions(+), 122 deletions(-) diff --git a/src/equicordplugins/holyNotes/components/modals/HelpModal.tsx b/src/equicordplugins/holyNotes/components/modals/HelpModal.tsx index e7f745fe5..af1fae928 100644 --- a/src/equicordplugins/holyNotes/components/modals/HelpModal.tsx +++ b/src/equicordplugins/holyNotes/components/modals/HelpModal.tsx @@ -6,7 +6,6 @@ import { BaseText } from "@components/BaseText"; import { Paragraph } from "@components/Paragraph"; -import { statusTagGreen } from "@equicordplugins/holyNotes"; import { noteHandler } from "@equicordplugins/holyNotes/NoteHandler"; import { downloadNotes, uploadNotes } from "@equicordplugins/holyNotes/utils"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal"; @@ -21,30 +20,30 @@ export default ({ onClose, ...modalProps }: ModalProps & { onClose: () => void;
- Adding Notes + Adding Notes To add a note right click on a message then hover over the "Note Message" item and click the button with the notebook name you would like to note the message to.
- Protip: + Protip:
Clicking the "Note Message" button by itself will note to Main by default!
- Deleting Notes + Deleting Notes Note you can either right click the note and hit "Delete Note" or you can hold the 'DELETE' key on your keyboard and click on a note; it's like magic!
- Moving Notes + Moving Notes To move a note right click on a note and hover over the "Move Note" item and click on the button corresponding to the notebook you would like to move the note to.
- Jump To Message + Jump To Message To jump to the location that the note was originally located at just right click on the note and hit "Jump to Message". diff --git a/src/equicordplugins/holyNotes/components/modals/Notebook.tsx b/src/equicordplugins/holyNotes/components/modals/Notebook.tsx index 14e32c847..9b9dd4d15 100644 --- a/src/equicordplugins/holyNotes/components/modals/Notebook.tsx +++ b/src/equicordplugins/holyNotes/components/modals/Notebook.tsx @@ -9,6 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { quickSelectClasses } from "@equicordplugins/holyNotes"; import HelpIcon from "@equicordplugins/holyNotes/components/icons/HelpIcon"; +import NoteButton from "@equicordplugins/holyNotes/components/icons/NoteButton"; import { noteHandler } from "@equicordplugins/holyNotes/NoteHandler"; import { HolyNotes } from "@equicordplugins/holyNotes/types"; import { classes } from "@utils/misc"; @@ -74,38 +75,88 @@ export const NoteModal = (props: ModalProps & { onClose: () => void; }) => { const notes = noteHandler.getNotes(currentNotebook); if (!notes) return <>; - const { TabBar, selectedTab } = CreateTabBar({ tabs: noteHandler.getAllNotes(), firstSelectedTab: currentNotebook, onChangeTab: setCurrentNotebook }); + const notesArray = Object.values(notes); + const noteCount = notesArray.length; + + // Filter notes for display count + let filteredCount = noteCount; + if (searchInput) { + const searchLower = searchInput.toLowerCase(); + filteredCount = notesArray.filter(note => + note.content?.toLowerCase().includes(searchLower) + ).length; + } + + const { TabBar, selectedTab } = CreateTabBar({ + tabs: noteHandler.getAllNotes(), + firstSelectedTab: currentNotebook, + onChangeTab: setCurrentNotebook + }); return ( - +
- - NOTEBOOK - -
openModal(HelpModal)}> - -
-
- setSearch(e)} - /> -
- + + + + + Notebook + + + {searchInput + ? `${filteredCount} of ${noteCount} ${noteCount === 1 ? "note" : "notes"}` + : `${noteCount} ${noteCount === 1 ? "note" : "notes"}` + } + + + + +
openModal(HelpModal)} + role="button" + tabIndex={0} + aria-label="Help" + onKeyDown={e => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + openModal(HelpModal); + } + }}> + +
+ +
+
+ setSearch(e)} + className={classes("vc-notebook-search-input")} + /> +
{TabBar}
- + {renderNotebook({ notes, @@ -119,59 +170,94 @@ export const NoteModal = (props: ModalProps & { onClose: () => void; }) => {
- - -
- ) => { - ContextMenuApi.openContextMenu(event, () => ( - FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })} - aria-label="Sort Menu" - > - { - setSortDirection(true); - setSortType(true); - }} /> { - setSortDirection(true); - setSortType(false); - }} /> { - setSortDirection(false); - setSortType(true); - }} /> { - setSortDirection(false); - setSortType(false); - }} /> - + + + +
+ ) => { + ContextMenuApi.openContextMenu(event, () => ( + FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })} + aria-label="Sort options" + > + { + setSortDirection(true); + setSortType(true); + }} + /> + { + setSortDirection(true); + setSortType(false); + }} + /> + { + setSortDirection(false); + setSortType(true); + }} + /> + { + setSortDirection(false); + setSortType(false); + }} + /> + + )); + }} + onKeyDown={e => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + const el = e.currentTarget as HTMLElement; + const rect = el.getBoundingClientRect(); + const evt = { + clientX: rect.left + rect.width / 2, + clientY: rect.top + rect.height / 2 + } as any; - )); - }} - > - Change Sorting: - - - {sortDirection ? "Ascending" : "Descending"} /{" "} - {sortType ? "Date Added" : "Message Date"} - -
+ ContextMenuApi.openContextMenu(evt, () => ( + FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })} + aria-label="Sort options" + > + { setSortDirection(true); setSortType(true); }} /> + { setSortDirection(true); setSortType(false); }} /> + { setSortDirection(false); setSortType(true); }} /> + { setSortDirection(false); setSortType(false); }} /> + + )); + } + }} + > + Sort: + + + {sortDirection ? "Asc" : "Desc"} /{" "} + {sortType ? "Date Added" : "Msg Date"} + + +
+
diff --git a/src/equicordplugins/holyNotes/components/modals/RenderMessage.tsx b/src/equicordplugins/holyNotes/components/modals/RenderMessage.tsx index a20c35a19..c7d2db182 100644 --- a/src/equicordplugins/holyNotes/components/modals/RenderMessage.tsx +++ b/src/equicordplugins/holyNotes/components/modals/RenderMessage.tsx @@ -5,17 +5,20 @@ */ import { CopyIcon, DeleteIcon, IDIcon, LinkIcon, OpenExternalIcon } from "@components/Icons"; -import { makeDummyUser } from "@components/settings/tabs/plugins/PluginModal"; import { ChannelMessage, messageClasses, MessageType } from "@equicordplugins/holyNotes"; import { noteHandler } from "@equicordplugins/holyNotes/NoteHandler"; import { HolyNotes } from "@equicordplugins/holyNotes/types"; import { copyToClipboard } from "@utils/clipboard"; +import { fetchUserProfile } from "@utils/discord"; +import { proxyLazy } from "@utils/lazy"; import { classes } from "@utils/misc"; import { ModalProps } from "@utils/modal"; -import { ContextMenuApi, FluxDispatcher, Menu, NavigationRouter, React } from "@webpack/common"; +import { ContextMenuApi, FluxDispatcher, Menu, NavigationRouter, React, UserStore } from "@webpack/common"; import { Channel } from "../.."; +const UserRecord = proxyLazy(() => UserStore.getCurrentUser().constructor) as any; + export const RenderMessage = ({ note, notebook, @@ -32,6 +35,15 @@ export const RenderMessage = ({ const isHoldingDeleteRef = React.useRef(false); const [, forceUpdate] = React.useReducer(x => x + 1, 0); + React.useEffect(() => { + const user = UserStore.getUser(note.author.id); + if (!user) { + fetchUserProfile(note.author.id); + } + }, [note.author.id]); + + const author = UserStore.getUser(note.author.id) ?? note.author; + React.useEffect(() => { const deleteHandler = (e: KeyboardEvent) => { if (e.key.toLowerCase() !== "delete") return; @@ -97,7 +109,7 @@ export const RenderMessage = ({ Object.assign( { ...note }, { - author: makeDummyUser(note?.author), + author: new UserRecord({ ...author, bot: true }), timestamp: new Date(note?.timestamp), // @ts-ignore embeds: note?.embeds?.map((embed: { timestamp: string | number | Date; }) => diff --git a/src/equicordplugins/holyNotes/index.tsx b/src/equicordplugins/holyNotes/index.tsx index 6ce9c23ac..a09ca34a3 100644 --- a/src/equicordplugins/holyNotes/index.tsx +++ b/src/equicordplugins/holyNotes/index.tsx @@ -35,7 +35,6 @@ import { noteHandler, noteHandlerCache } from "./NoteHandler"; import { DataStoreToCache, HolyNoteStore } from "./utils"; export const MessageType = findByCodeLazy("isEdited(){"); -export const { statusTagGreen } = findCssClassesLazy("statusTagGreen"); export const iconClasses = findCssClassesLazy("iconWrapper", "clickable"); export const resultsClasses = findCssClassesLazy("emptyResultsWrap", "emptyResultsContent", "errorImage", "emptyResultsText", "noResultsImage", "alt"); export const quickSelectClasses = findCssClassesLazy("quickSelect", "quickSelectLabel", "quickSelectClick", "quickSelectValue", "quickSelectArrow"); diff --git a/src/equicordplugins/holyNotes/style.css b/src/equicordplugins/holyNotes/style.css index fa69ea465..3b7da72ed 100644 --- a/src/equicordplugins/holyNotes/style.css +++ b/src/equicordplugins/holyNotes/style.css @@ -1,16 +1,11 @@ /* stylelint-disable shorthand-property-no-redundant-values */ /* stylelint-disable length-zero-no-unit */ -/* -$modifier: var(--input-border-default, var(--input-border)); -*/ - .vc-notebook-search { flex: auto; margin-right: 15px; - margin-bottom: 6px; - background-color: var(--background-base-low); - border: solid 2px var(--background-secondary-alt); + margin-bottom: 10px; + background-color: var(--background-surface-higher); } .vc-notebook-header { @@ -19,53 +14,102 @@ $modifier: var(--input-border-default, var(--input-border)); } .vc-notebook-header-main { - padding-top: 15px; - padding-left: 10px; - padding-bottom: 0px; - background-color: var(--background-base-low); - box-shadow: 0 1px 0 0 var(--background-base-low), 0 1px 2px 0 var(--background-base-low) !important; + padding: 16px 16px 12px 16px; + background-color: var(--background-surface-higher); + box-shadow: none !important; } .vc-notebook-heading { - max-width: 110px; - margin-right: 0px; - padding-bottom: 6px; - transform: scale(1); + max-width: none; + margin: 0; + padding: 0; + font-size: 20px; + font-weight: 600; + line-height: 24px; + color: var(--text-default); } +.vc-notebook-count { + font-size: 12px; + font-weight: 500; + line-height: 16px; + color: var(--text-muted); +} .vc-notebook-flex { overflow: hidden; } .vc-notebook-flex .vc-help-icon { - width: 23px; - opacity: 0.5; + width: 20px; + height: 20px; + opacity: 0.6; cursor: pointer; - padding-left: 0px; - margin: 0px 14px 6px 0px; - color: var(--interactive-icon-default); + padding: 0; + margin: 0; + color: var(--icon-default); + transition: opacity 0.2s ease-in-out, color 0.2s ease-in-out; +} + +.vc-notebook-icon { + width: 24px; + height: 24px; + cursor: default; + margin: 0; + color: var(--text-default); + flex-shrink: 0; +} + +.vc-notebook-help-button { + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + opacity: 0.6; transition: opacity 0.2s ease-in-out; } -.vc-notebook-flex .help-icon:hover { +.vc-notebook-help-button:hover { opacity: 1; } +.vc-notebook-help-button svg { + width: 20px; + height: 20px; + color: var(--icon-default); +} + .vc-notebook-display-left { flex: auto; display: flex; } +.vc-notebook-search-container { + padding: 0 16px 12px 16px; + background-color: var(--background-surface-higher); +} + +.vc-notebook-search-input { + width: 100%; + background-color: var(--input-background-default); +} + .vc-help-markdown { margin-top: 6px; } .vc-help-markdown hr { border: 0; - height: 2px; + height: 1px; margin: 12px 0px 16px 0px; - background-color: var(--input-border-default, var(--input-border)); + background-color: var(--border-normal); +} + +.vc-help-markdown .vc-help-protip { + font-weight: 600; + color: var(--text-feedback-positive); } .vc-holy-note [class*="buttonContainer"] { @@ -77,20 +121,14 @@ $modifier: var(--input-border-default, var(--input-border)); } .vc-notebook-tabbar { - border-bottom: 1px solid var(--background-base-low); - background-color: var(--background-base-low); + border-bottom: 1px solid var(--border-normal); + background-color: var(--background-surface-higher); white-space: nowrap; overflow: hidden; height: 36px; flex-shrink: 0; } - -/* .vc-notebook-tabbar-bar { - margin: 0; - padding: 10px 20px 0; -} */ - .vc-notebook-overflow-chevron { display: inline-block; padding: 0 6px 2px; @@ -100,39 +138,61 @@ $modifier: var(--input-border-default, var(--input-border)); } .vc-notebook-tabbar-container { - border-bottom: 1px solid var(--profile-body-divider-color); - margin: 20px 12px 0; + border-bottom: 1px solid var(--border-normal); + margin: 0 16px; padding: 0; + background-color: var(--background-surface-higher); } .vc-notebook-tabbar-item { display: inline-block; - padding: 6px 12px; + padding: 8px 12px; + margin-bottom: -1px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; - line-height: 22px; - font-size: 16px; + line-height: 20px; + font-size: 14px; font-weight: 500; cursor: pointer; - color: var(--interactive-icon-default); + color: var(--interactive-text-default); border-bottom-width: 2px; border-bottom-style: solid; /* stylelint-disable-next-line declaration-block-no-redundant-longhand-properties */ border-bottom-color: transparent; + transition: color 0.2s ease-in-out, border-bottom-color 0.2s ease-in-out, font-weight 0.2s ease-in-out; } .vc-notebook-selected { + border-bottom-width: 3px; border-bottom-color: var(--control-brand-foreground); + color: var(--text-default); + font-weight: 600; } .vc-notebook-tabbar-item:hover { - border-bottom-color: var(--brand-500); + color: var(--interactive-text-hover); +} + +.vc-notebook-tabbar-item:hover:not(.vc-notebook-selected) { + border-bottom-color: var(--interactive-text-hover); } .vc-notebook-top-section { - margin-bottom: calc(-8px + .15*(var(--custom-user-profile-modal-header-avatar-size) + var(--custom-user-profile-modal-header-total-avatar-border-size))); + margin-bottom: 0; z-index: 1; + background-color: var(--background-surface-higher); +} + +.vc-notebook-content { + padding: 16px; + background-color: var(--background-surface-high); +} + +.vc-notebook-footer { + padding: 16px; + background-color: var(--background-surface-higher); + border-top: 1px solid var(--border-normal); } .vc-help-modal-header { @@ -152,4 +212,25 @@ $modifier: var(--input-border-default, var(--input-border)); .vc-help-markdown span[class*="statusTag"] { display: inline-block; width: auto; +} + +.vc-holy-note [class*="botText"] { + font-size: 0 !important; + line-height: 0; +} + +.vc-holy-note [class*="botText"]::after { + content: "NOTE"; + font-size: 0.675rem; + line-height: 1.3; + font-weight: 800; + font-family: var(--font-display); + color: inherit; + letter-spacing: 0.02em; + text-transform: uppercase; +} + +.sort-button-container { + display: flex; + align-items: center; } \ No newline at end of file