1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-24 14:30:56 +01:00
phorge-phorge/webroot/rsrc/js/application/differential/behavior-populate.js
epriestley 2b4c551b0e Provide a global router for Ajax requests
Summary:
Fixes T430. Fixes T4834. Obsoletes D7641. Currently, we do some things less-well than we could:

  - We just let the browser queue and prioritize requests, so if you load a revision with 50 changes and then click "Award Token", the action blocks until the changes load in most/all browsers. It would be better to prioritize this action and queue it immediately.
  - Similarly, changes tend to load in order, even if the user has clicked to a specific file. When the user expresses a preference for a specific file, we should prioritize it.
  - We show a spinning GIF when waiting on requests. This is appropriate for some types of reuqests, but distracting for others.

To fix this:

  - Queue all (or, at least, most) requests into a new queue in JX.Router.
  - JX.Router handles prioritizing the requests. Principally:
    - You can submit a request with a specific priority (500 = general content loading, 1000 = default, 2000 = explicit user action) and JX.Router will get the higher stuff fired off sooner.
    - You can name requests and then adjust their prorities later, if the user expresses an interest in specific results.
  - Only use the spinner gif for "workflow" requests, which is bascially when the user clicked something and we're waiting on the server. I think it's useful and not-annoying in this case.
  - Don't show any status for draft requests.
  - For content requests, show a subtle hipster-style top loading bar.

Test Plan:
  - Viewed a diff with 93 changes, and clicked award token.
    - Prior to this patch, the action took many many seconds to resolve.
    - After this patch, it resolves quickly.
  - Viewed a diff with 93 changes and saw a pleasant subtle hipster-style loading bar.
  - Viewed a diff with 93 changes and typed some draft text. Previews populated fairly quickly and there was no spinner.
  - Viewed a diff with 93 changes and clicked something with workflow, saw a spinner after a moment.
  - Viewed a diff with 93 changes and clicked a file in the table of contents near the end of the list.
    - Prior to this patch, it took a long time to show up.
    - After this patch, it loads directly.

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T430, T4834

Differential Revision: https://secure.phabricator.com/D8979
2014-05-05 10:57:42 -07:00

206 lines
5.6 KiB
JavaScript

/**
* @provides javelin-behavior-differential-populate
* @requires javelin-behavior
* javelin-workflow
* javelin-util
* javelin-dom
* javelin-stratcom
* javelin-behavior-device
* javelin-vector
* javelin-router
* phabricator-tooltip
*/
JX.behavior('differential-populate', function(config) {
function onresponse(target_id, response) {
// As we populate the diff, we try to hold the document scroll position
// steady, so that, e.g., users who want to leave a comment on a diff with a
// large number of changes don't constantly have the text area scrolled off
// the bottom of the screen until the entire diff loads.
//
// There are two three major cases here:
//
// - If we're near the top of the document, never scroll.
// - If we're near the bottom of the document, always scroll.
// - Otherwise, scroll if the changes were above the midline of the
// viewport.
var target = JX.$(target_id);
var old_pos = JX.Vector.getScroll();
var old_view = JX.Vector.getViewport();
var old_dim = JX.Vector.getDocument();
// Number of pixels away from the top or bottom of the document which
// count as "nearby".
var sticky = 480;
var near_top = (old_pos.y <= sticky);
var near_bot = ((old_pos.y + old_view.y) >= (old_dim.y - sticky));
var target_pos = JX.Vector.getPos(target);
var target_dim = JX.Vector.getDim(target);
var target_mid = (target_pos.y + (target_dim.y / 2));
var view_mid = (old_pos.y + (old_view.y / 2));
var above_mid = (target_mid < view_mid);
JX.DOM.replace(target, JX.$H(response.changeset));
if (!near_top) {
if (near_bot || above_mid) {
// Figure out how much taller the document got.
var delta = (JX.Vector.getDocument().y - old_dim.y);
window.scrollTo(old_pos.x, old_pos.y + delta);
}
}
if (response.coverage) {
for (var k in response.coverage) {
try {
JX.DOM.replace(JX.$(k), JX.$H(response.coverage[k]));
} catch (ignored) {
// Not terribly important.
}
}
}
}
// NOTE: If you load the page at one device resolution and then resize to
// a different one we don't re-render the diffs, because it's a complicated
// mess and you could lose inline comments, cursor positions, etc.
var renderer = (JX.Device.getDevice() == 'desktop') ? '2up' : '1up';
// TODO: Once 1up works better, figure out when to show it.
renderer = '2up';
var get_key = function(id) {
return 'differential-populate.' + id;
};
var load = function(id, data) {
var routable = new JX.Workflow(config.uri, data)
.setHandler(JX.bind(null, onresponse, id))
.getRoutable();
routable
.setPriority(500)
.setType('content')
.setKey(get_key(id));
JX.Router.getInstance().queue(routable);
return routable;
};
for (var k in config.registry) {
var data = {
ref : config.registry[k],
whitespace: config.whitespace,
renderer: renderer
};
load(k, data);
}
var highlighted = null;
var highlight_class = null;
JX.Stratcom.listen(
'click',
'differential-load',
function(e) {
var meta = e.getNodeData('differential-load');
var diff;
try {
diff = JX.$(meta.id);
} catch (ex) {
// Already loaded.
}
if (diff) {
JX.DOM.setContent(
diff,
JX.$H('<div class="differential-loading">Loading...</div>'));
// When a user explicitly clicks "Load" (or clicks a link in the table
// of contents) prioritize this request if it already exists. If it
// doesn't, make a new high-priority request.
var key = get_key(meta.id);
var routable = JX.Router.getInstance().getRoutableByKey(key);
if (!routable) {
var data = {
ref : meta.ref,
whitespace : config.whitespace
};
routable = load(meta.id, data);
}
routable.setPriority(2000);
}
if (meta.kill) {
e.kill();
}
});
JX.Stratcom.listen(
['mouseover', 'mouseout'],
['differential-changeset', 'tag:td'],
function(e) {
var t = e.getTarget();
// NOTE: Using className is not best practice, but the diff UI is perf
// sensitive.
if (!t.className.match(/cov|copy/)) {
return;
}
if (e.getType() == 'mouseout') {
JX.Tooltip.hide();
if (highlighted) {
JX.DOM.alterClass(highlighted, highlight_class, false);
highlighted = null;
}
} else {
highlight_class = null;
var msg;
var align = 'W';
var sibling = 'previousSibling';
var width = 120;
if (t.className.match(/cov-C/)) {
msg = 'Covered';
highlight_class = 'source-cov-C';
} else if (t.className.match(/cov-U/)) {
msg = 'Not Covered';
highlight_class = 'source-cov-U';
} else if (t.className.match(/cov-N/)) {
msg = 'Not Executable';
highlight_class = 'source-cov-N';
} else {
var match = /new-copy|new-move/.exec(t.className);
if (match) {
sibling = 'nextSibling';
width = 500;
msg = JX.Stratcom.getData(t).msg;
highlight_class = match[0];
}
}
if (msg) {
JX.Tooltip.show(t, width, align, msg);
}
if (highlight_class) {
highlighted = t[sibling];
JX.DOM.alterClass(highlighted, highlight_class, true);
}
}
});
});