perf: avoid uneccessary notes list item rerenders (#1904)
This commit is contained in:
@@ -7,7 +7,7 @@ import { Component } from 'react'
|
||||
export type PureComponentState = Partial<Record<string, any>>
|
||||
export type PureComponentProps = Partial<Record<string, any>>
|
||||
|
||||
export abstract class PureComponent<P = PureComponentProps, S = PureComponentState> extends Component<P, S> {
|
||||
export abstract class AbstractComponent<P = PureComponentProps, S = PureComponentState> extends Component<P, S> {
|
||||
private unsubApp!: () => void
|
||||
private reactionDisposers: IReactionDisposer[] = []
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ApplicationEvent, Challenge, removeFromArray, WebAppEvent } from '@stan
|
||||
import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants/Constants'
|
||||
import { alertDialog, RouteType } from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import Navigation from '@/Components/Navigation/Navigation'
|
||||
import Navigation from '@/Components/Tags/Navigation'
|
||||
import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView'
|
||||
import Footer from '@/Components/Footer/Footer'
|
||||
import SessionsModal from '@/Components/SessionsModal/SessionsModal'
|
||||
|
||||
@@ -40,6 +40,7 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
const { selectPreviousItem, selectNextItem } = selectionController
|
||||
const { hideTags, hideDate, hideNotePreview, hideEditorIcon } = itemListController.webDisplayOptions
|
||||
const { sortBy } = itemListController.displayOptions
|
||||
const selectedTag = navigationController.selected
|
||||
|
||||
const onScroll: UIEventHandler = useCallback(
|
||||
(e) => {
|
||||
@@ -72,25 +73,27 @@ const ContentList: FunctionComponent<Props> = ({
|
||||
[selectionController],
|
||||
)
|
||||
|
||||
const getTagsForItem = (item: ListableContentItem) => {
|
||||
if (hideTags) {
|
||||
return []
|
||||
}
|
||||
const getTagsForItem = useCallback(
|
||||
(item: ListableContentItem) => {
|
||||
if (hideTags) {
|
||||
return []
|
||||
}
|
||||
|
||||
const selectedTag = navigationController.selected
|
||||
if (!selectedTag) {
|
||||
return []
|
||||
}
|
||||
if (!selectedTag) {
|
||||
return []
|
||||
}
|
||||
|
||||
const tags = application.getItemTags(item)
|
||||
const tags = application.getItemTags(item)
|
||||
|
||||
const isNavigatingOnlyTag = selectedTag instanceof SNTag && tags.length === 1
|
||||
if (isNavigatingOnlyTag) {
|
||||
return []
|
||||
}
|
||||
const isNavigatingOnlyTag = selectedTag instanceof SNTag && tags.length === 1
|
||||
if (isNavigatingOnlyTag) {
|
||||
return []
|
||||
}
|
||||
|
||||
return tags
|
||||
}
|
||||
return tags
|
||||
},
|
||||
[hideTags, selectedTag, application],
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ContentType } from '@standardnotes/snjs'
|
||||
import { FunctionComponent } from 'react'
|
||||
import React, { FunctionComponent } from 'react'
|
||||
import FileListItem from './FileListItem'
|
||||
import NoteListItem from './NoteListItem'
|
||||
import { AbstractListItemProps } from './Types/AbstractListItemProps'
|
||||
import { AbstractListItemProps, doListItemPropsMeritRerender } from './Types/AbstractListItemProps'
|
||||
|
||||
const ContentListItem: FunctionComponent<AbstractListItemProps> = (props) => {
|
||||
switch (props.item.content_type) {
|
||||
@@ -15,4 +15,4 @@ const ContentListItem: FunctionComponent<AbstractListItemProps> = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
export default ContentListItem
|
||||
export default React.memo(ContentListItem, (a, b) => !doListItemPropsMeritRerender(a, b))
|
||||
|
||||
@@ -13,6 +13,7 @@ import { AppPaneId } from '../ResponsivePane/AppPaneMetadata'
|
||||
import { useContextMenuEvent } from '@/Hooks/useContextMenuEvent'
|
||||
import ListItemNotePreviewText from './ListItemNotePreviewText'
|
||||
import { ListItemTitle } from './ListItemTitle'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
|
||||
const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
application,
|
||||
@@ -70,6 +71,8 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
|
||||
useContextMenuEvent(listItemRef, openContextMenu)
|
||||
|
||||
log(LoggingDomain.ItemsList, 'Rendering note list item', item.title)
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={listItemRef}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { NotesController } from '@/Controllers/NotesController'
|
||||
import { SortableItem, SNTag } from '@standardnotes/snjs'
|
||||
import { SortableItem, SNTag, Uuids } from '@standardnotes/snjs'
|
||||
import { ListableContentItem } from './ListableContentItem'
|
||||
|
||||
export type AbstractListItemProps = {
|
||||
@@ -18,3 +18,89 @@ export type AbstractListItemProps = {
|
||||
sortBy: keyof SortableItem | undefined
|
||||
tags: SNTag[]
|
||||
}
|
||||
|
||||
export function doListItemPropsMeritRerender(previous: AbstractListItemProps, next: AbstractListItemProps): boolean {
|
||||
const simpleComparison: (keyof AbstractListItemProps)[] = [
|
||||
'onSelect',
|
||||
'hideDate',
|
||||
'hideIcon',
|
||||
'hideTags',
|
||||
'hidePreview',
|
||||
'selected',
|
||||
'sortBy',
|
||||
]
|
||||
|
||||
for (const key of simpleComparison) {
|
||||
if (previous[key] !== next[key]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (previous['item'] !== next['item']) {
|
||||
if (doesItemChangeMeritRerender(previous['item'], next['item'])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return doesTagsChangeMeritRerender(previous['tags'], next['tags'])
|
||||
}
|
||||
|
||||
function doesTagsChangeMeritRerender(previous: SNTag[], next: SNTag[]): boolean {
|
||||
if (previous === next) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (previous.length !== next.length) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (previous.length === 0 && next.length === 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (Uuids(previous).sort().join() !== Uuids(next).sort().join()) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
previous
|
||||
.map((t) => t.title)
|
||||
.sort()
|
||||
.join() !==
|
||||
next
|
||||
.map((t) => t.title)
|
||||
.sort()
|
||||
.join()
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function doesItemChangeMeritRerender(previous: ListableContentItem, next: ListableContentItem): boolean {
|
||||
if (previous.uuid !== next.uuid) {
|
||||
return true
|
||||
}
|
||||
|
||||
const propertiesMeritingRerender: (keyof ListableContentItem)[] = [
|
||||
'title',
|
||||
'protected',
|
||||
'updatedAtString',
|
||||
'createdAtString',
|
||||
'hidePreview',
|
||||
'preview_html',
|
||||
'preview_plain',
|
||||
'archived',
|
||||
'starred',
|
||||
'pinned',
|
||||
]
|
||||
|
||||
for (const key of propertiesMeritingRerender) {
|
||||
if (previous[key] !== next[key]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { PureComponent } from '@/Components/Abstract/PureComponent'
|
||||
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
|
||||
import { destroyAllObjectProperties, preventRefreshing } from '@/Utils'
|
||||
import { ApplicationEvent, ApplicationDescriptor, WebAppEvent } from '@standardnotes/snjs'
|
||||
import {
|
||||
@@ -41,7 +41,7 @@ type State = {
|
||||
arbitraryStatusMessage?: string
|
||||
}
|
||||
|
||||
class Footer extends PureComponent<Props, State> {
|
||||
class Footer extends AbstractComponent<Props, State> {
|
||||
public user?: unknown
|
||||
private didCheckForOffline = false
|
||||
private completedInitialSync = false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { FileItem, FileViewController, NoteViewController } from '@standardnotes/snjs'
|
||||
import { PureComponent } from '@/Components/Abstract/PureComponent'
|
||||
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import MultipleSelectedNotes from '@/Components/MultipleSelectedNotes/MultipleSelectedNotes'
|
||||
import MultipleSelectedFiles from '../MultipleSelectedFiles/MultipleSelectedFiles'
|
||||
@@ -22,7 +22,7 @@ type Props = {
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
class NoteGroupView extends PureComponent<Props, State> {
|
||||
class NoteGroupView extends AbstractComponent<Props, State> {
|
||||
private removeChangeObserver!: () => void
|
||||
|
||||
constructor(props: Props) {
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
import { ChangeEventHandler, createRef, KeyboardEventHandler, RefObject } from 'react'
|
||||
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
|
||||
import ChangeEditorButton from '@/Components/ChangeEditor/ChangeEditorButton'
|
||||
import ComponentView from '@/Components/ComponentView/ComponentView'
|
||||
import NotesOptionsPanel from '@/Components/NotesOptions/NotesOptionsPanel'
|
||||
import PanelResizer, { PanelResizeType, PanelSide } from '@/Components/PanelResizer/PanelResizer'
|
||||
import PinNoteButton from '@/Components/PinNoteButton/PinNoteButton'
|
||||
import ProtectedItemOverlay from '@/Components/ProtectedItemOverlay/ProtectedItemOverlay'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import { StringDeleteNote, STRING_DELETE_LOCKED_ATTEMPT, STRING_DELETE_PLACEHOLDER_ATTEMPT } from '@/Constants/Strings'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
import { debounce, isDesktopApplication, isMobileScreen } from '@/Utils'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
isPayloadSourceRetrieved,
|
||||
isPayloadSourceInternalChange,
|
||||
ContentType,
|
||||
SNComponent,
|
||||
SNNote,
|
||||
ComponentArea,
|
||||
PrefKey,
|
||||
ComponentViewerInterface,
|
||||
ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction,
|
||||
ContentType,
|
||||
EditorFontSize,
|
||||
EditorLineHeight,
|
||||
isPayloadSourceInternalChange,
|
||||
isPayloadSourceRetrieved,
|
||||
NoteType,
|
||||
NoteViewController,
|
||||
PayloadEmitSource,
|
||||
PrefKey,
|
||||
ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction,
|
||||
SNComponent,
|
||||
SNNote,
|
||||
WebAppEvent,
|
||||
EditorLineHeight,
|
||||
EditorFontSize,
|
||||
NoteType,
|
||||
} from '@standardnotes/snjs'
|
||||
import { debounce, isDesktopApplication, isMobileScreen } from '@/Utils'
|
||||
import { confirmDialog, KeyboardKey, KeyboardModifier } from '@standardnotes/ui-services'
|
||||
import { ChangeEventHandler, createRef, KeyboardEventHandler, RefObject } from 'react'
|
||||
import { EditorEventSource } from '../../Types/EditorEventSource'
|
||||
import { confirmDialog, KeyboardModifier, KeyboardKey } from '@standardnotes/ui-services'
|
||||
import { STRING_DELETE_PLACEHOLDER_ATTEMPT, STRING_DELETE_LOCKED_ATTEMPT, StringDeleteNote } from '@/Constants/Strings'
|
||||
import { PureComponent } from '@/Components/Abstract/PureComponent'
|
||||
import ProtectedItemOverlay from '@/Components/ProtectedItemOverlay/ProtectedItemOverlay'
|
||||
import PinNoteButton from '@/Components/PinNoteButton/PinNoteButton'
|
||||
import NotesOptionsPanel from '@/Components/NotesOptions/NotesOptionsPanel'
|
||||
import ComponentView from '@/Components/ComponentView/ComponentView'
|
||||
import PanelResizer, { PanelSide, PanelResizeType } from '@/Components/PanelResizer/PanelResizer'
|
||||
import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import ChangeEditorButton from '@/Components/ChangeEditor/ChangeEditorButton'
|
||||
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
|
||||
import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer'
|
||||
import LinkedItemsButton from '../LinkedItems/LinkedItemsButton'
|
||||
import MobileItemsListButton from '../NoteGroupView/MobileItemsListButton'
|
||||
import EditingDisabledBanner from './EditingDisabledBanner'
|
||||
import { reloadFont } from './FontFunctions'
|
||||
import NoteStatusIndicator, { NoteStatus } from './NoteStatusIndicator'
|
||||
import NoteViewFileDropTarget from './NoteViewFileDropTarget'
|
||||
import { NoteViewProps } from './NoteViewProps'
|
||||
import {
|
||||
transactionForAssociateComponentWithCurrentNote,
|
||||
transactionForDisassociateComponentWithCurrentNote,
|
||||
} from './TransactionFunctions'
|
||||
import { reloadFont } from './FontFunctions'
|
||||
import { NoteViewProps } from './NoteViewProps'
|
||||
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
|
||||
import { classNames } from '@/Utils/ConcatenateClassNames'
|
||||
import MobileItemsListButton from '../NoteGroupView/MobileItemsListButton'
|
||||
import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer'
|
||||
import NoteStatusIndicator, { NoteStatus } from './NoteStatusIndicator'
|
||||
import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
import LinkedItemsButton from '../LinkedItems/LinkedItemsButton'
|
||||
import NoteViewFileDropTarget from './NoteViewFileDropTarget'
|
||||
|
||||
const MinimumStatusDuration = 400
|
||||
const TextareaDebounce = 100
|
||||
@@ -98,7 +99,7 @@ const PlaintextFontSizeMapping: Record<EditorFontSize, string> = {
|
||||
Large: 'text-xl',
|
||||
}
|
||||
|
||||
class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
readonly controller!: NoteViewController
|
||||
|
||||
private statusTimeout?: NodeJS.Timeout
|
||||
@@ -193,7 +194,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
;(this.onPanelResizeFinish as unknown) = undefined
|
||||
;(this.stackComponentExpanded as unknown) = undefined
|
||||
;(this.toggleStackComponent as unknown) = undefined
|
||||
;(this.onSystemEditorLoad as unknown) = undefined
|
||||
;(this.onSystemEditorRef as unknown) = undefined
|
||||
;(this.debounceReloadEditorComponent as unknown) = undefined
|
||||
;(this.textAreaChangeDebounceSave as unknown) = undefined
|
||||
;(this.editorContentRef as unknown) = undefined
|
||||
@@ -207,6 +208,24 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
return this.controller.item
|
||||
}
|
||||
|
||||
override shouldComponentUpdate(_nextProps: Readonly<NoteViewProps>, nextState: Readonly<State>): boolean {
|
||||
const complexObjects: (keyof State)[] = ['availableStackComponents', 'stackComponentViewers']
|
||||
for (const key of Object.keys(nextState) as (keyof State)[]) {
|
||||
if (complexObjects.includes(key)) {
|
||||
continue
|
||||
}
|
||||
const prevValue = this.state[key]
|
||||
const nextValue = nextState[key]
|
||||
|
||||
if (prevValue !== nextValue) {
|
||||
log(LoggingDomain.NoteView, 'Rendering due to state change', key, prevValue, nextValue)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override componentDidMount(): void {
|
||||
super.componentDidMount()
|
||||
|
||||
@@ -253,6 +272,8 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
onNoteInnerChange(note: SNNote, source: PayloadEmitSource): void {
|
||||
log(LoggingDomain.NoteView, 'On inner note change', PayloadEmitSource[source])
|
||||
|
||||
if (note.uuid !== this.note.uuid) {
|
||||
throw Error('Editor received changes for non-current note')
|
||||
}
|
||||
@@ -431,12 +452,15 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
|
||||
streamItems() {
|
||||
this.removeComponentStreamObserver = this.application.streamItems(ContentType.Component, async ({ source }) => {
|
||||
log(LoggingDomain.NoteView, 'On component stream observer', PayloadEmitSource[source])
|
||||
if (isPayloadSourceInternalChange(source) || source === PayloadEmitSource.InitialObserverRegistrationPush) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.note) {
|
||||
return
|
||||
}
|
||||
|
||||
await this.reloadStackComponents()
|
||||
this.debounceReloadEditorComponent()
|
||||
})
|
||||
@@ -454,6 +478,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
if (this.state.editorComponentViewerDidAlreadyReload && !force) {
|
||||
return
|
||||
}
|
||||
|
||||
const component = viewer.component
|
||||
this.application.componentManager.destroyComponentViewer(viewer)
|
||||
this.setState(
|
||||
@@ -489,6 +514,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
async reloadEditorComponent() {
|
||||
log(LoggingDomain.NoteView, 'Reload editor component')
|
||||
if (this.state.showProtectedWarning) {
|
||||
this.destroyCurrentEditorComponent()
|
||||
return
|
||||
@@ -581,13 +607,16 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
|
||||
onTextAreaChange: ChangeEventHandler<HTMLTextAreaElement> = ({ currentTarget }) => {
|
||||
const text = currentTarget.value
|
||||
|
||||
this.setState({
|
||||
editorText: text,
|
||||
})
|
||||
|
||||
this.textAreaChangeDebounceSave()
|
||||
}
|
||||
|
||||
textAreaChangeDebounceSave = () => {
|
||||
log(LoggingDomain.NoteView, 'Performing save after debounce')
|
||||
this.controller
|
||||
.save({
|
||||
editorValues: {
|
||||
@@ -609,10 +638,14 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
onTitleChange: ChangeEventHandler<HTMLInputElement> = ({ currentTarget }) => {
|
||||
log(LoggingDomain.NoteView, 'Performing save after title change')
|
||||
|
||||
const title = currentTarget.value
|
||||
|
||||
this.setState({
|
||||
editorTitle: title,
|
||||
})
|
||||
|
||||
this.controller
|
||||
.save({
|
||||
editorValues: {
|
||||
@@ -662,10 +695,12 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
this.application.alertService.alert(STRING_DELETE_PLACEHOLDER_ATTEMPT).catch(console.error)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.note.locked) {
|
||||
this.application.alertService.alert(STRING_DELETE_LOCKED_ATTEMPT).catch(console.error)
|
||||
return
|
||||
}
|
||||
|
||||
const title = this.note.title.length ? `'${this.note.title}'` : 'this note'
|
||||
const text = StringDeleteNote(title, permanently)
|
||||
if (
|
||||
@@ -727,6 +762,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
async reloadPreferences() {
|
||||
log(LoggingDomain.NoteView, 'Reload preferences')
|
||||
const monospaceFont = this.application.getPreference(
|
||||
PrefKey.EditorMonospaceEnabled,
|
||||
PrefDefaults[PrefKey.EditorMonospaceEnabled],
|
||||
@@ -776,9 +812,8 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @components */
|
||||
|
||||
async reloadStackComponents() {
|
||||
log(LoggingDomain.NoteView, 'Reload stack components')
|
||||
const stackComponents = sortAlphabetically(
|
||||
this.application.componentManager
|
||||
.componentsForArea(ComponentArea.EditorStack)
|
||||
@@ -844,10 +879,13 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
})
|
||||
}
|
||||
|
||||
onSystemEditorLoad = (ref: HTMLTextAreaElement | null) => {
|
||||
onSystemEditorRef = (ref: HTMLTextAreaElement | null) => {
|
||||
if (this.removeTabObserver || !ref) {
|
||||
return
|
||||
}
|
||||
|
||||
log(LoggingDomain.NoteView, 'On system editor ref')
|
||||
|
||||
/**
|
||||
* Insert 4 spaces when a tab key is pressed,
|
||||
* only used when inside of the text editor.
|
||||
@@ -1070,7 +1108,7 @@ class NoteView extends PureComponent<NoteViewProps, State> {
|
||||
onFocus={this.onContentFocus}
|
||||
onBlur={this.onContentBlur}
|
||||
readOnly={this.state.noteLocked}
|
||||
ref={(ref) => ref && this.onSystemEditorLoad(ref)}
|
||||
ref={(ref) => ref && this.onSystemEditorRef(ref)}
|
||||
spellCheck={this.state.spellcheck}
|
||||
value={this.state.editorText}
|
||||
className={classNames(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { createRef } from 'react'
|
||||
import { PureComponent } from '@/Components/Abstract/PureComponent'
|
||||
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import DecoratedPasswordInput from '../Input/DecoratedPasswordInput'
|
||||
import ModalDialog from '../Shared/ModalDialog'
|
||||
@@ -38,7 +38,7 @@ type FormData = {
|
||||
status?: string
|
||||
}
|
||||
|
||||
class PasswordWizard extends PureComponent<Props, State> {
|
||||
class PasswordWizard extends AbstractComponent<Props, State> {
|
||||
private currentPasswordInput = createRef<HTMLInputElement>()
|
||||
|
||||
constructor(props: Props) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { PureComponent } from '@/Components/Abstract/PureComponent'
|
||||
import { AbstractComponent } from '@/Components/Abstract/PureComponent'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
close: () => void
|
||||
}
|
||||
|
||||
class SyncResolutionMenu extends PureComponent<Props> {
|
||||
class SyncResolutionMenu extends AbstractComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props, props.application)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { mergeRefs } from '@/Hooks/mergeRefs'
|
||||
import { useFileDragNDrop } from '../FileDragNDropProvider/FileDragNDropProvider'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { TagListSectionType } from './TagListSection'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
|
||||
type Props = {
|
||||
tag: SNTag
|
||||
@@ -237,6 +238,8 @@ export const TagsListItem: FunctionComponent<Props> = observer(
|
||||
}
|
||||
}, [addDragTarget, linkingController, removeDragTarget, tag])
|
||||
|
||||
log(LoggingDomain.NavigationList, 'Rendering TagsListItem')
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { log as utilsLog } from '@standardnotes/utils'
|
||||
import { isDev } from './Utils'
|
||||
|
||||
export enum LoggingDomain {
|
||||
DailyNotes,
|
||||
NoteView,
|
||||
ItemsList,
|
||||
NavigationList,
|
||||
}
|
||||
|
||||
const LoggingStatus: Record<LoggingDomain, boolean> = {
|
||||
[LoggingDomain.DailyNotes]: false,
|
||||
[LoggingDomain.NoteView]: false,
|
||||
[LoggingDomain.ItemsList]: false,
|
||||
[LoggingDomain.NavigationList]: false,
|
||||
}
|
||||
|
||||
export function log(domain: LoggingDomain, ...args: any[]): void {
|
||||
if (!LoggingStatus[domain]) {
|
||||
if (!isDev || !LoggingStatus[domain]) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user