FIX: Dirty keys should become to a widget, not global

This commit is contained in:
Robin Ward 2017-06-21 14:07:49 -04:00
parent 287cb4bfc5
commit 1a02f5154f
6 changed files with 63 additions and 32 deletions

View File

@ -1,8 +1,8 @@
import { keyDirty } from 'discourse/widgets/widget';
import { diff, patch } from 'virtual-dom'; import { diff, patch } from 'virtual-dom';
import { WidgetClickHook } from 'discourse/widgets/hooks'; import { WidgetClickHook } from 'discourse/widgets/hooks';
import { renderedKey, queryRegistry } from 'discourse/widgets/widget'; import { queryRegistry } from 'discourse/widgets/widget';
import { getRegister } from 'discourse-common/lib/get-owner'; import { getRegister } from 'discourse-common/lib/get-owner';
import DirtyKeys from 'discourse/lib/dirty-keys';
const _cleanCallbacks = {}; const _cleanCallbacks = {};
export function addWidgetCleanCallback(widgetName, fn) { export function addWidgetCleanCallback(widgetName, fn) {
@ -18,6 +18,7 @@ export default Ember.Component.extend({
_renderCallback: null, _renderCallback: null,
_childEvents: null, _childEvents: null,
_dispatched: null, _dispatched: null,
dirtyKeys: null,
init() { init() {
this._super(); this._super();
@ -34,6 +35,7 @@ export default Ember.Component.extend({
this._childEvents = []; this._childEvents = [];
this._connected = []; this._connected = [];
this._dispatched = []; this._dispatched = [];
this.dirtyKeys = new DirtyKeys(name);
}, },
didInsertElement() { didInsertElement() {
@ -73,7 +75,7 @@ export default Ember.Component.extend({
eventDispatched(eventName, key, refreshArg) { eventDispatched(eventName, key, refreshArg) {
const onRefresh = Ember.String.camelize(eventName.replace(/:/, '-')); const onRefresh = Ember.String.camelize(eventName.replace(/:/, '-'));
keyDirty(key, { onRefresh, refreshArg }); this.dirtyKeys.keyDirty(key, { onRefresh, refreshArg });
this.queueRerender(); this.queueRerender();
}, },
@ -104,7 +106,10 @@ export default Ember.Component.extend({
const t0 = new Date().getTime(); const t0 = new Date().getTime();
const args = this.get('args') || this.buildArgs(); const args = this.get('args') || this.buildArgs();
const opts = { model: this.get('model') }; const opts = {
model: this.get('model'),
dirtyKeys: this.dirtyKeys,
};
const newTree = new this._widgetClass(args, this.register, opts); const newTree = new this._widgetClass(args, this.register, opts);
newTree._rerenderable = this; newTree._rerenderable = this;
@ -122,8 +127,8 @@ export default Ember.Component.extend({
this._renderCallback = null; this._renderCallback = null;
} }
this.afterRender(); this.afterRender();
this.dirtyKeys.renderedKey('*');
Ember.run.scheduleOnce('afterRender', () => renderedKey('*'));
if (this.profileWidget) { if (this.profileWidget) {
console.log(new Date().getTime() - t0); console.log(new Date().getTime() - t0);
} }

View File

@ -1,5 +1,4 @@
import DiscourseURL from 'discourse/lib/url'; import DiscourseURL from 'discourse/lib/url';
import { keyDirty } from 'discourse/widgets/widget';
import MountWidget from 'discourse/components/mount-widget'; import MountWidget from 'discourse/components/mount-widget';
import { cloak, uncloak } from 'discourse/widgets/post-stream'; import { cloak, uncloak } from 'discourse/widgets/post-stream';
import { isWorkaroundActive } from 'discourse/lib/safari-hacks'; import { isWorkaroundActive } from 'discourse/lib/safari-hacks';
@ -245,13 +244,13 @@ export default MountWidget.extend({
this.appEvents.on('post-stream:refresh', args => { this.appEvents.on('post-stream:refresh', args => {
if (args) { if (args) {
if (args.id) { if (args.id) {
keyDirty(`post-${args.id}`); this.dirtyKeys.keyDirty(`post-${args.id}`);
if (args.refreshLikes) { if (args.refreshLikes) {
keyDirty(`post-menu-${args.id}`, { onRefresh: 'refreshLikes' }); this.dirtyKeys.keyDirty(`post-menu-${args.id}`, { onRefresh: 'refreshLikes' });
} }
} else if (args.force) { } else if (args.force) {
keyDirty(`*`); this.dirtyKeys.forceAll();
} }
} }
this.queueRerender(); this.queueRerender();

View File

@ -0,0 +1,32 @@
export default class DirtyKeys {
constructor(name) {
this.name = name;
this._keys = {};
}
keyDirty(key, options) {
options = options || {};
options.dirty = true;
this._keys[key] = options;
}
forceAll() {
this.keyDirty('*');
}
allDirty() {
return !!this._keys['*'];
}
optionsFor(key) {
return this._keys[key] || { dirty: false };
}
renderedKey(key) {
if (key === '*') {
this._keys = {};
} else {
delete this._keys[key];
}
}
}

View File

@ -1,5 +1,6 @@
import { diff, patch } from 'virtual-dom'; import { diff, patch } from 'virtual-dom';
import { queryRegistry } from 'discourse/widgets/widget'; import { queryRegistry } from 'discourse/widgets/widget';
import DirtyKeys from 'discourse/lib/dirty-keys';
export default class WidgetGlue { export default class WidgetGlue {
@ -9,6 +10,7 @@ export default class WidgetGlue {
this.register = register; this.register = register;
this.attrs = attrs; this.attrs = attrs;
this._timeout = null; this._timeout = null;
this.dirtyKeys = new DirtyKeys(name);
this._widgetClass = queryRegistry(name) || this.register.lookupFactory(`widget:${name}`); this._widgetClass = queryRegistry(name) || this.register.lookupFactory(`widget:${name}`);
if (!this._widgetClass) { if (!this._widgetClass) {
@ -27,7 +29,11 @@ export default class WidgetGlue {
rerenderWidget() { rerenderWidget() {
Ember.run.cancel(this._timeout); Ember.run.cancel(this._timeout);
const newTree = new this._widgetClass(this.attrs, this.register); const newTree = new this._widgetClass(
this.attrs,
this.register,
{ dirtyKeys: this.dirtyKeys }
);
const patches = diff(this._tree || this._rootNode, newTree); const patches = diff(this._tree || this._rootNode, newTree);
newTree._rerenderable = this; newTree._rerenderable = this;

View File

@ -2,7 +2,6 @@ import { createWidget } from 'discourse/widgets/widget';
import transformPost from 'discourse/lib/transform-post'; import transformPost from 'discourse/lib/transform-post';
import { Placeholder } from 'discourse/lib/posts-with-placeholders'; import { Placeholder } from 'discourse/lib/posts-with-placeholders';
import { addWidgetCleanCallback } from 'discourse/components/mount-widget'; import { addWidgetCleanCallback } from 'discourse/components/mount-widget';
import { keyDirty } from 'discourse/widgets/widget';
let transformCallbacks = null; let transformCallbacks = null;
function postTransformCallbacks(transformed) { function postTransformCallbacks(transformed) {
@ -36,14 +35,15 @@ export function cloak(post, component) {
const $post = $(`#post_${post.post_number}`); const $post = $(`#post_${post.post_number}`);
_cloaked[post.id] = true; _cloaked[post.id] = true;
_heights[post.id] = $post.outerHeight(); _heights[post.id] = $post.outerHeight();
keyDirty(`post-${post.id}`);
component.dirtyKeys.keyDirty(`post-${post.id}`);
Ember.run.debounce(component, 'queueRerender', 1000); Ember.run.debounce(component, 'queueRerender', 1000);
} }
export function uncloak(post, component) { export function uncloak(post, component) {
if (!CLOAKING_ENABLED || !_cloaked[post.id]) { return; } if (!CLOAKING_ENABLED || !_cloaked[post.id]) { return; }
_cloaked[post.id] = null; _cloaked[post.id] = null;
keyDirty(`post-${post.id}`); component.dirtyKeys.keyDirty(`post-${post.id}`);
component.queueRerender(); component.queueRerender();
} }

View File

@ -9,21 +9,6 @@ import DecoratorHelper from 'discourse/widgets/decorator-helper';
function emptyContent() { } function emptyContent() { }
const _registry = {}; const _registry = {};
let _dirty = {};
export function keyDirty(key, options) {
options = options || {};
options.dirty = true;
_dirty[key] = options;
}
export function renderedKey(key) {
if (key === '*') {
_dirty = {};
} else {
delete _dirty[key];
}
}
export function queryRegistry(name) { export function queryRegistry(name) {
return _registry[name]; return _registry[name];
@ -149,6 +134,7 @@ export default class Widget {
this.mergeState = opts.state; this.mergeState = opts.state;
this.model = opts.model; this.model = opts.model;
this.register = register; this.register = register;
this.dirtyKeys = opts.dirtyKeys;
register.deprecateContainer(this); register.deprecateContainer(this);
@ -188,6 +174,8 @@ export default class Widget {
} }
render(prev) { render(prev) {
const { dirtyKeys } = this;
if (prev && prev.key && prev.key === this.key) { if (prev && prev.key && prev.key === this.key) {
this.state = prev.state; this.state = prev.state;
} else { } else {
@ -200,16 +188,16 @@ export default class Widget {
} }
if (prev) { if (prev) {
const dirtyOpts = _dirty[prev.key] || { dirty: false }; const dirtyOpts = dirtyKeys.optionsFor(prev.key);
if (prev.shadowTree) { if (prev.shadowTree) {
this.shadowTree = true; this.shadowTree = true;
if (!dirtyOpts.dirty && !_dirty['*']) { if (!dirtyOpts.dirty && !dirtyKeys.allDirty()) {
return prev.vnode; return prev.vnode;
} }
} }
if (prev.key) { if (prev.key) {
renderedKey(prev.key); dirtyKeys.renderedKey(prev.key);
} }
const refreshAction = dirtyOpts.onRefresh; const refreshAction = dirtyOpts.onRefresh;
@ -256,6 +244,7 @@ export default class Widget {
if (WidgetClass) { if (WidgetClass) {
const result = new WidgetClass(attrs, this.register, opts); const result = new WidgetClass(attrs, this.register, opts);
result.parentWidget = this; result.parentWidget = this;
result.dirtyKeys = this.dirtyKeys;
return result; return result;
} else { } else {
throw `Couldn't find ${widgetName} factory`; throw `Couldn't find ${widgetName} factory`;
@ -266,7 +255,7 @@ export default class Widget {
let widget = this; let widget = this;
while (widget) { while (widget) {
if (widget.shadowTree) { if (widget.shadowTree) {
keyDirty(widget.key); this.dirtyKeys.keyDirty(widget.key);
} }
const rerenderable = widget._rerenderable; const rerenderable = widget._rerenderable;