perf: improve input responsiveness in compose input (#1413)

* perf: improve input responsiveness in compose input

* remove some unused code from autosize.js

* remove some more unused code
This commit is contained in:
Nolan Lawson 2019-08-19 21:37:11 -07:00 committed by GitHub
parent 4232da5e33
commit cccbfd70da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 35 deletions

View file

@ -102,6 +102,7 @@
import { postStatus, insertHandleForReply, setReplySpoiler, setReplyVisibility } from '../../_actions/compose' import { postStatus, insertHandleForReply, setReplySpoiler, setReplyVisibility } from '../../_actions/compose'
import { classname } from '../../_utils/classname' import { classname } from '../../_utils/classname'
import { POLL_EXPIRY_DEFAULT } from '../../_static/polls' import { POLL_EXPIRY_DEFAULT } from '../../_static/polls'
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
export default { export default {
oncreate () { oncreate () {
@ -179,6 +180,13 @@
}, },
methods: { methods: {
doPostStatus () { doPostStatus () {
// The reason we add a scheduleIdleTask delay here is because we also use scheduleIdleTask
// in ComposeInput.html to debounce the input events. If the user is very fast at typing
// at their keyboard and quickly presses Ctrl+Enter or the "Toot" button then there could
// be a race condition where not all of their status is posted.
scheduleIdleTask(() => this.doPostStatusAfterDelay())
},
doPostStatusAfterDelay () {
const { const {
text, text,
media, media,

View file

@ -59,7 +59,6 @@
import { store } from '../../_store/store' import { store } from '../../_store/store'
import { autosize } from '../../_thirdparty/autosize/autosize' import { autosize } from '../../_thirdparty/autosize/autosize'
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask' import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
import debounce from 'lodash-es/debounce'
import { mark, stop } from '../../_utils/marks' import { mark, stop } from '../../_utils/marks'
import { selectionChange } from '../../_utils/events' import { selectionChange } from '../../_utils/events'
import { import {
@ -70,6 +69,9 @@
import { get } from '../../_utils/lodash-lite' import { get } from '../../_utils/lodash-lite'
import { on } from '../../_utils/eventBus' import { on } from '../../_utils/eventBus'
import { requestPostAnimationFrame } from '../../_utils/requestPostAnimationFrame' import { requestPostAnimationFrame } from '../../_utils/requestPostAnimationFrame'
import { throttleTimer } from '../../_utils/throttleTimer'
const updateComposeTextInStore = throttleTimer(scheduleIdleTask)
export default { export default {
oncreate () { oncreate () {
@ -106,14 +108,14 @@
}) })
}, },
setupSyncToStore () { setupSyncToStore () {
const saveStore = debounce(() => scheduleIdleTask(() => this.store.save()), 1000) const { realm } = this.get()
this.observe('rawText', rawText => { this.observe('rawText', rawText => {
mark('observe rawText') updateComposeTextInStore(() => {
const { realm } = this.get() mark('updateComposeTextInStore')
this.store.setComposeData(realm, { text: rawText }) this.store.setComposeData(realm, { text: rawText })
saveStore() this.store.save()
stop('observe rawText') stop('updateComposeTextInStore')
})
}, { init: false }) }, { init: false })
}, },
setupAutosize () { setupAutosize () {

View file

@ -5,11 +5,12 @@
import { mark, stop } from '../../_utils/marks' import { mark, stop } from '../../_utils/marks'
import debounce from 'lodash-es/debounce' import debounce from 'lodash-es/debounce'
import throttle from 'lodash-es/throttle'
import { getScrollContainer } from '../../_utils/scrollContainer' import { getScrollContainer } from '../../_utils/scrollContainer'
import { throttleTimer } from '../../_utils/throttleTimer'
const doUpdate = process.browser && throttleTimer(requestAnimationFrame)
const map = new Map() const map = new Map()
const createEvent = (name) => new Event(name, { bubbles: true })
function assign (ta) { function assign (ta) {
if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) { if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) {
@ -19,7 +20,6 @@ function assign (ta) {
// TODO: hack - grab our scroll container so we can maintain the scrollTop // TODO: hack - grab our scroll container so we can maintain the scrollTop
const container = getScrollContainer() const container = getScrollContainer()
let heightOffset = null let heightOffset = null
let cachedHeight = null
function init () { function init () {
const style = window.getComputedStyle(ta, null) const style = window.getComputedStyle(ta, null)
@ -35,9 +35,8 @@ function assign (ta) {
function resize () { function resize () {
mark('autosize:resize()') mark('autosize:resize()')
const res = _resize() _resize()
stop('autosize:resize()') stop('autosize:resize()')
return res
} }
function _resize () { function _resize () {
@ -51,15 +50,13 @@ function assign (ta) {
if (ta.scrollHeight === 0) { if (ta.scrollHeight === 0) {
// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
ta.style.height = originalHeight ta.style.height = originalHeight
return } else {
ta.style.height = `${endHeight}px`
container.scrollTop = scrollTop // Firefox jiggles if we don't reset the scrollTop of the container
} }
ta.style.height = endHeight + 'px'
container.scrollTop = scrollTop // Firefox jiggles if we don't reset the scrollTop of the container
return endHeight
} }
const deferredUpdate = throttle(() => requestAnimationFrame(update), 100) const deferredUpdate = () => doUpdate(update)
function update () { function update () {
mark('autosize:update()') mark('autosize:update()')
@ -68,17 +65,7 @@ function assign (ta) {
} }
function _update () { function _update () {
const newHeight = resize() resize()
if (cachedHeight !== newHeight) {
cachedHeight = newHeight
const evt = createEvent('autosize:resized')
try {
ta.dispatchEvent(evt)
} catch (err) {
// Firefox will throw an error on dispatchEvent for a detached element
// https://bugzilla.mozilla.org/show_bug.cgi?id=889376
}
}
} }
const pageResize = debounce(() => requestAnimationFrame(update), 1000) const pageResize = debounce(() => requestAnimationFrame(update), 1000)
@ -86,17 +73,12 @@ function assign (ta) {
const destroy = () => { const destroy = () => {
window.removeEventListener('resize', pageResize, false) window.removeEventListener('resize', pageResize, false)
ta.removeEventListener('input', deferredUpdate, false) ta.removeEventListener('input', deferredUpdate, false)
ta.removeEventListener('autosize:destroy', destroy, false)
ta.removeEventListener('autosize:update', update, false)
map.delete(ta) map.delete(ta)
} }
ta.addEventListener('autosize:destroy', destroy, false)
window.addEventListener('resize', pageResize, false) window.addEventListener('resize', pageResize, false)
ta.addEventListener('input', deferredUpdate, false) ta.addEventListener('input', deferredUpdate, false)
ta.addEventListener('autosize:update', update, false)
map.set(ta, { map.set(ta, {
destroy, destroy,