refactor: component viewer use state for timeout so timeout isnt set if no render eventually occurs

This commit is contained in:
Mo
2022-05-17 22:58:55 -05:00
parent aeae2b644f
commit a77bf5b525
4 changed files with 84 additions and 46 deletions

View File

@@ -54,10 +54,6 @@ export abstract class PureComponent<P = PureComponentProps, S = PureComponentSta
this.deinit() this.deinit()
} }
render() {
return <div>Must override</div>
}
public get appState(): AppState { public get appState(): AppState {
return this.application.getAppState() return this.application.getAppState()
} }

View File

@@ -9,7 +9,7 @@ import {
} from '@standardnotes/snjs' } from '@standardnotes/snjs'
import { WebApplication } from '@/UIModels/Application' import { WebApplication } from '@/UIModels/Application'
import { FunctionalComponent } from 'preact' import { FunctionalComponent } from 'preact'
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks' import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
import { observer } from 'mobx-react-lite' import { observer } from 'mobx-react-lite'
import { OfflineRestricted } from '@/Components/ComponentView/OfflineRestricted' import { OfflineRestricted } from '@/Components/ComponentView/OfflineRestricted'
import { UrlMissing } from '@/Components/ComponentView/UrlMissing' import { UrlMissing } from '@/Components/ComponentView/UrlMissing'
@@ -25,7 +25,6 @@ interface IProps {
componentViewer: ComponentViewer componentViewer: ComponentViewer
requestReload?: (viewer: ComponentViewer, force?: boolean) => void requestReload?: (viewer: ComponentViewer, force?: boolean) => void
onLoad?: (component: SNComponent) => void onLoad?: (component: SNComponent) => void
manualDealloc?: boolean
} }
/** /**
@@ -39,7 +38,7 @@ const MSToWaitAfterIframeLoadToAvoidFlicker = 35
export const ComponentView: FunctionalComponent<IProps> = observer( export const ComponentView: FunctionalComponent<IProps> = observer(
({ application, onLoad, componentViewer, requestReload }) => { ({ application, onLoad, componentViewer, requestReload }) => {
const iframeRef = useRef<HTMLIFrameElement>(null) const iframeRef = useRef<HTMLIFrameElement>(null)
const excessiveLoadingTimeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined) const [loadTimeout, setLoadTimeout] = useState<ReturnType<typeof setTimeout> | undefined>(undefined)
const [hasIssueLoading, setHasIssueLoading] = useState(false) const [hasIssueLoading, setHasIssueLoading] = useState(false)
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
@@ -88,7 +87,8 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
} }
}, [hasIssueLoading, componentViewer, requestReload]) }, [hasIssueLoading, componentViewer, requestReload])
const handleIframeTakingTooLongToLoad = useCallback(async () => { useEffect(() => {
const loadTimeout = setTimeout(() => {
setIsLoading(false) setIsLoading(false)
setHasIssueLoading(true) setHasIssueLoading(true)
@@ -98,24 +98,25 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
} else { } else {
document.addEventListener(VisibilityChangeKey, onVisibilityChange) document.addEventListener(VisibilityChangeKey, onVisibilityChange)
} }
}, [didAttemptReload, onVisibilityChange, componentViewer, requestReload])
useMemo(() => {
const loadTimeout = setTimeout(() => {
handleIframeTakingTooLongToLoad().catch(console.error)
}, MaxLoadThreshold) }, MaxLoadThreshold)
excessiveLoadingTimeout.current = loadTimeout setLoadTimeout(loadTimeout)
return () => { return () => {
excessiveLoadingTimeout.current && clearTimeout(excessiveLoadingTimeout.current) if (loadTimeout) {
clearTimeout(loadTimeout)
} }
}, [handleIframeTakingTooLongToLoad]) }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [componentViewer])
const onIframeLoad = useCallback(() => { const onIframeLoad = useCallback(() => {
const iframe = iframeRef.current as HTMLIFrameElement const iframe = iframeRef.current as HTMLIFrameElement
const contentWindow = iframe.contentWindow as Window const contentWindow = iframe.contentWindow as Window
excessiveLoadingTimeout.current && clearTimeout(excessiveLoadingTimeout.current)
if (loadTimeout) {
clearTimeout(loadTimeout)
}
try { try {
componentViewer.setWindow(contentWindow) componentViewer.setWindow(contentWindow)
@@ -128,7 +129,7 @@ export const ComponentView: FunctionalComponent<IProps> = observer(
setHasIssueLoading(false) setHasIssueLoading(false)
onLoad?.(component) onLoad?.(component)
}, MSToWaitAfterIframeLoadToAvoidFlicker) }, MSToWaitAfterIframeLoadToAvoidFlicker)
}, [componentViewer, onLoad, component, excessiveLoadingTimeout]) }, [componentViewer, onLoad, component, loadTimeout])
useEffect(() => { useEffect(() => {
const removeFeaturesChangedObserver = componentViewer.addEventObserver((event) => { const removeFeaturesChangedObserver = componentViewer.addEventObserver((event) => {

View File

@@ -264,21 +264,26 @@ export class NoteView extends PureComponent<Props, State> {
let title = this.state.editorTitle, let title = this.state.editorTitle,
text = this.state.editorText text = this.state.editorText
if (isPayloadSourceRetrieved(source)) { if (isPayloadSourceRetrieved(source)) {
title = note.title title = note.title
text = note.text text = note.text
} }
if (!this.state.editorTitle) { if (!this.state.editorTitle) {
title = note.title title = note.title
} }
if (!this.state.editorText) { if (!this.state.editorText) {
text = note.text text = note.text
} }
if (title !== this.state.editorTitle) { if (title !== this.state.editorTitle) {
this.setState({ this.setState({
editorTitle: title, editorTitle: title,
}) })
} }
if (text !== this.state.editorText) { if (text !== this.state.editorText) {
this.setState({ this.setState({
editorText: text, editorText: text,
@@ -317,6 +322,7 @@ export class NoteView extends PureComponent<Props, State> {
if (this.state.editorComponentViewer) { if (this.state.editorComponentViewer) {
this.application.componentManager?.destroyComponentViewer(this.state.editorComponentViewer) this.application.componentManager?.destroyComponentViewer(this.state.editorComponentViewer)
} }
super.componentWillUnmount() super.componentWillUnmount()
} }
@@ -401,12 +407,15 @@ export class NoteView extends PureComponent<Props, State> {
dismissProtectedWarning = async () => { dismissProtectedWarning = async () => {
let showNoteContents = true let showNoteContents = true
if (this.application.hasProtectionSources()) { if (this.application.hasProtectionSources()) {
showNoteContents = await this.application.authorizeNoteAccess(this.note) showNoteContents = await this.application.authorizeNoteAccess(this.note)
} }
if (!showNoteContents) { if (!showNoteContents) {
return return
} }
this.setShowProtectedOverlay(false) this.setShowProtectedOverlay(false)
this.focusTitle() this.focusTitle()
} }
@@ -1021,6 +1030,7 @@ export class NoteView extends PureComponent<Props, State> {
{this.state.editorComponentViewer && ( {this.state.editorComponentViewer && (
<div className="component-view"> <div className="component-view">
<ComponentView <ComponentView
key={this.state.editorComponentViewer.identifier}
componentViewer={this.state.editorComponentViewer} componentViewer={this.state.editorComponentViewer}
onLoad={this.onEditorComponentLoad} onLoad={this.onEditorComponentLoad}
requestReload={this.editorComponentViewerRequestsReload} requestReload={this.editorComponentViewerRequestsReload}
@@ -1099,7 +1109,6 @@ export class NoteView extends PureComponent<Props, State> {
<ComponentView <ComponentView
key={viewer.identifier} key={viewer.identifier}
componentViewer={viewer} componentViewer={viewer}
manualDealloc={true}
application={this.application} application={this.application}
appState={this.appState} appState={this.appState}
/> />

View File

@@ -14,7 +14,7 @@ import {
SNTag, SNTag,
SystemViewId, SystemViewId,
} from '@standardnotes/snjs' } from '@standardnotes/snjs'
import { action, computed, makeObservable, observable, reaction } from 'mobx' import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'
import { AppState, AppStateEvent } from '.' import { AppState, AppStateEvent } from '.'
import { WebApplication } from '../Application' import { WebApplication } from '../Application'
import { AbstractState } from './AbstractState' import { AbstractState } from './AbstractState'
@@ -228,7 +228,9 @@ export class NotesViewState extends AbstractState {
this.notes = notes this.notes = notes
runInAction(() => {
this.renderedNotes = renderedNotes this.renderedNotes = renderedNotes
})
await this.recomputeSelectionAfterNotesReload() await this.recomputeSelectionAfterNotesReload()
@@ -258,7 +260,7 @@ export class NotesViewState extends AbstractState {
const noteExistsInUpdatedResults = this.notes.find((note) => note.uuid === activeNote.uuid) const noteExistsInUpdatedResults = this.notes.find((note) => note.uuid === activeNote.uuid)
if (!noteExistsInUpdatedResults && !isSearching) { if (!noteExistsInUpdatedResults && !isSearching) {
this.application.noteControllerGroup.closeNoteController(activeController) this.closeNoteController(activeController)
this.selectNextNote() this.selectNextNote()
@@ -318,11 +320,12 @@ export class NotesViewState extends AbstractState {
reloadPreferences = async () => { reloadPreferences = async () => {
const freshDisplayOptions = {} as DisplayOptions const freshDisplayOptions = {} as DisplayOptions
const currentSortBy = this.displayOptions.sortBy const currentSortBy = this.displayOptions.sortBy
let sortBy = this.application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt) let sortBy = this.application.getPreference(PrefKey.SortNotesBy, CollectionSort.CreatedAt)
if (sortBy === CollectionSort.UpdatedAt || (sortBy as string) === 'client_updated_at') { if (sortBy === CollectionSort.UpdatedAt || (sortBy as string) === 'client_updated_at') {
/** Use UserUpdatedAt instead */
sortBy = CollectionSort.UpdatedAt sortBy = CollectionSort.UpdatedAt
} }
freshDisplayOptions.sortBy = sortBy freshDisplayOptions.sortBy = sortBy
freshDisplayOptions.sortReverse = this.application.getPreference(PrefKey.SortNotesReverse, false) freshDisplayOptions.sortReverse = this.application.getPreference(PrefKey.SortNotesReverse, false)
freshDisplayOptions.showArchived = this.application.getPreference(PrefKey.NotesShowArchived, false) freshDisplayOptions.showArchived = this.application.getPreference(PrefKey.NotesShowArchived, false)
@@ -468,17 +471,29 @@ export class NotesViewState extends AbstractState {
selectNextNote = () => { selectNextNote = () => {
const displayableNotes = this.notes const displayableNotes = this.notes
const currentIndex = displayableNotes.findIndex((candidate) => { const currentIndex = displayableNotes.findIndex((candidate) => {
return candidate.uuid === this.activeControllerNote?.uuid return candidate.uuid === this.activeControllerNote?.uuid
}) })
if (currentIndex + 1 < displayableNotes.length) { let nextIndex = currentIndex + 1
const nextNote = displayableNotes[currentIndex + 1]
while (nextIndex < displayableNotes.length) {
const nextNote = displayableNotes[nextIndex]
nextIndex++
if (nextNote.protected) {
continue
}
this.selectNoteWithScrollHandling(nextNote).catch(console.error) this.selectNoteWithScrollHandling(nextNote).catch(console.error)
const nextNoteElement = document.getElementById(`note-${nextNote.uuid}`) const nextNoteElement = document.getElementById(`note-${nextNote.uuid}`)
nextNoteElement?.focus() nextNoteElement?.focus()
return
} }
} }
@@ -495,20 +510,31 @@ export class NotesViewState extends AbstractState {
selectPreviousNote = () => { selectPreviousNote = () => {
const displayableNotes = this.notes const displayableNotes = this.notes
if (this.activeControllerNote) { if (!this.activeControllerNote) {
const currentIndex = displayableNotes.indexOf(this.activeControllerNote) return
if (currentIndex - 1 >= 0) {
const previousNote = displayableNotes[currentIndex - 1]
this.selectNoteWithScrollHandling(previousNote).catch(console.error)
const previousNoteElement = document.getElementById(`note-${previousNote.uuid}`)
previousNoteElement?.focus()
return true
} else {
return false
}
} }
return undefined const currentIndex = displayableNotes.indexOf(this.activeControllerNote)
let previousIndex = currentIndex - 1
while (previousIndex >= 0) {
const previousNote = displayableNotes[previousIndex]
previousIndex--
if (previousNote.protected) {
continue
}
this.selectNoteWithScrollHandling(previousNote).catch(console.error)
const previousNoteElement = document.getElementById(`note-${previousNote.uuid}`)
previousNoteElement?.focus()
return
}
} }
setNoteFilterText = (text: string) => { setNoteFilterText = (text: string) => {
@@ -543,10 +569,14 @@ export class NotesViewState extends AbstractState {
} }
} }
private closeNoteController(controller: NoteViewController): void {
this.application.noteControllerGroup.closeNoteController(controller)
}
handleTagChange = () => { handleTagChange = () => {
const activeNoteController = this.getActiveNoteController() const activeNoteController = this.getActiveNoteController()
if (activeNoteController?.isTemplateNote) { if (activeNoteController?.isTemplateNote) {
this.application.noteControllerGroup.closeNoteController(activeNoteController) this.closeNoteController(activeNoteController)
} }
this.resetScrollPosition() this.resetScrollPosition()
@@ -591,7 +621,9 @@ export class NotesViewState extends AbstractState {
if (this.searchSubmitted) { if (this.searchSubmitted) {
this.searchSubmitted = false this.searchSubmitted = false
} }
this.reloadNotesDisplayOptions() this.reloadNotesDisplayOptions()
void this.reloadNotes() void this.reloadNotes()
} }