@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Consolidate changeset rendering logic

Summary:
Ref T5179. Currently, all the changeset rendering logic is in the "populate" behavior, and a lot of it comes in via configuration and is hard to get at.

Instead, surface an object which can control it, and which other behaviors can access more easily.

In particular, this allows us to add a "Load/Reload" item to the view options menu, which would previously have been very challenging.

Load/Reload isn't useful on its own, but is a step away from "Show whitespace as...", "Highlight as...", "Show tabtops as...", "View Unified", "View Side-By-Side", etc.

Test Plan:
- Viewed Differential.
- Viewed Diffusion.
- Viewed large changesets, clicked "Load".
- Used "Load" and "Reload" from view options menu.
- Loaded all changes in a large diff, verified "Load" and TOC clicks take precedence over other content loads.
- Played with content stability stuff.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T5179

Differential Revision: https://secure.phabricator.com/D9286

+445 -175
+45 -35
resources/celerity/map.php
··· 11 11 'core.pkg.js' => '639b2433', 12 12 'darkconsole.pkg.js' => 'ca8671ce', 13 13 'differential.pkg.css' => 'fbf57382', 14 - 'differential.pkg.js' => '74cb0d29', 14 + 'differential.pkg.js' => 'eca39a2c', 15 15 'diffusion.pkg.css' => '3783278d', 16 16 'diffusion.pkg.js' => '077e3ad0', 17 17 'maniphest.pkg.css' => 'f88a8402', ··· 352 352 'rsrc/js/application/countdown/timer.js' => '889c96f3', 353 353 'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e', 354 354 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => 'fa187a68', 355 + 'rsrc/js/application/differential/ChangesetViewManager.js' => 'db09a523', 355 356 'rsrc/js/application/differential/DifferentialInlineCommentEditor.js' => 'f2441746', 356 357 'rsrc/js/application/differential/behavior-add-reviewers-and-ccs.js' => '533a187b', 357 358 'rsrc/js/application/differential/behavior-comment-jump.js' => '71755c79', 358 359 'rsrc/js/application/differential/behavior-comment-preview.js' => '127f2018', 359 360 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 360 - 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '9f0dfafa', 361 + 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '64a79839', 361 362 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '00861799', 362 363 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '173ce7e7', 363 - 'rsrc/js/application/differential/behavior-populate.js' => 'dfdf9f34', 364 + 'rsrc/js/application/differential/behavior-populate.js' => 'bdb3e4d0', 364 365 'rsrc/js/application/differential/behavior-show-all-comments.js' => '7c273581', 365 366 'rsrc/js/application/differential/behavior-show-field-details.js' => '441f2137', 366 367 'rsrc/js/application/differential/behavior-show-more.js' => 'dd7e8ef5', ··· 502 503 'aphront-two-column-view-css' => '16ab3ad2', 503 504 'aphront-typeahead-control-css' => 'a989b5b3', 504 505 'auth-css' => '1e655982', 506 + 'changeset-view-manager' => 'db09a523', 505 507 'config-options-css' => '7fedf08b', 506 508 'conpherence-menu-css' => 'e1e0fdf1', 507 509 'conpherence-message-pane-css' => '7703a9a9', ··· 554 556 'javelin-behavior-differential-add-reviewers-and-ccs' => '533a187b', 555 557 'javelin-behavior-differential-comment-jump' => '71755c79', 556 558 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 557 - 'javelin-behavior-differential-dropdown-menus' => '9f0dfafa', 559 + 'javelin-behavior-differential-dropdown-menus' => '64a79839', 558 560 'javelin-behavior-differential-edit-inline-comments' => '00861799', 559 561 'javelin-behavior-differential-feedback-preview' => '127f2018', 560 562 'javelin-behavior-differential-keyboard-navigation' => '173ce7e7', 561 - 'javelin-behavior-differential-populate' => 'dfdf9f34', 563 + 'javelin-behavior-differential-populate' => 'bdb3e4d0', 562 564 'javelin-behavior-differential-show-field-details' => '441f2137', 563 565 'javelin-behavior-differential-show-more' => 'dd7e8ef5', 564 566 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', ··· 1251 1253 2 => 'javelin-util', 1252 1254 3 => 'phabricator-shaped-request', 1253 1255 ), 1256 + '62e18640' => 1257 + array( 1258 + 0 => 'javelin-install', 1259 + 1 => 'javelin-util', 1260 + 2 => 'javelin-dom', 1261 + 3 => 'javelin-typeahead-normalizer', 1262 + ), 1254 1263 '6453c869' => 1255 1264 array( 1256 1265 0 => 'javelin-install', 1257 1266 1 => 'javelin-dom', 1258 1267 2 => 'javelin-fx', 1259 1268 ), 1269 + '64a79839' => 1270 + array( 1271 + 0 => 'javelin-behavior', 1272 + 1 => 'javelin-dom', 1273 + 2 => 'javelin-util', 1274 + 3 => 'javelin-stratcom', 1275 + 4 => 'phuix-dropdown-menu', 1276 + 5 => 'phuix-action-list-view', 1277 + 6 => 'phuix-action-view', 1278 + 7 => 'phabricator-phtize', 1279 + 8 => 'changeset-view-manager', 1280 + ), 1260 1281 '64ef2fd2' => 1261 1282 array( 1262 1283 0 => 'javelin-behavior', ··· 1308 1329 array( 1309 1330 0 => 'javelin-behavior', 1310 1331 1 => 'javelin-dom', 1311 - ), 1312 - '62e18640' => 1313 - array( 1314 - 0 => 'javelin-install', 1315 - 1 => 'javelin-util', 1316 - 2 => 'javelin-dom', 1317 - 3 => 'javelin-typeahead-normalizer', 1318 1332 ), 1319 1333 '76f4ebed' => 1320 1334 array( ··· 1552 1566 2 => 'javelin-uri', 1553 1567 3 => 'javelin-request', 1554 1568 ), 1555 - '9f0dfafa' => 1556 - array( 1557 - 0 => 'javelin-behavior', 1558 - 1 => 'javelin-dom', 1559 - 2 => 'javelin-util', 1560 - 3 => 'javelin-stratcom', 1561 - 4 => 'phuix-dropdown-menu', 1562 - 5 => 'phuix-action-list-view', 1563 - 6 => 'phuix-action-view', 1564 - 7 => 'phabricator-phtize', 1565 - ), 1566 1569 'a3e2244e' => 1567 1570 array( 1568 1571 0 => 'javelin-behavior', ··· 1725 1728 2 => 'javelin-util', 1726 1729 3 => 'javelin-request', 1727 1730 ), 1731 + 'bdb3e4d0' => 1732 + array( 1733 + 0 => 'javelin-behavior', 1734 + 1 => 'javelin-dom', 1735 + 2 => 'javelin-stratcom', 1736 + 3 => 'phabricator-tooltip', 1737 + 4 => 'changeset-view-manager', 1738 + ), 1728 1739 'be81801d' => 1729 1740 array( 1730 1741 0 => 'javelin-behavior', ··· 1883 1894 1 => 'javelin-util', 1884 1895 2 => 'javelin-stratcom', 1885 1896 ), 1897 + 'db09a523' => 1898 + array( 1899 + 0 => 'javelin-dom', 1900 + 1 => 'javelin-util', 1901 + 2 => 'javelin-stratcom', 1902 + 3 => 'javelin-install', 1903 + 4 => 'javelin-workflow', 1904 + 5 => 'javelin-router', 1905 + 6 => 'javelin-behavior-device', 1906 + 7 => 'javelin-vector', 1907 + ), 1886 1908 'dd7e8ef5' => 1887 1909 array( 1888 1910 0 => 'javelin-behavior', ··· 1896 1918 0 => 'javelin-behavior', 1897 1919 1 => 'javelin-dom', 1898 1920 2 => 'phabricator-prefab', 1899 - ), 1900 - 'dfdf9f34' => 1901 - array( 1902 - 0 => 'javelin-behavior', 1903 - 1 => 'javelin-workflow', 1904 - 2 => 'javelin-util', 1905 - 3 => 'javelin-dom', 1906 - 4 => 'javelin-stratcom', 1907 - 5 => 'javelin-behavior-device', 1908 - 6 => 'javelin-vector', 1909 - 7 => 'javelin-router', 1910 - 8 => 'phabricator-tooltip', 1911 1921 ), 1912 1922 'e1ff79b1' => 1913 1923 array(
+60 -3
src/applications/differential/view/DifferentialChangesetDetailView.php
··· 8 8 private $symbolIndex; 9 9 private $id; 10 10 private $vsChangesetID; 11 + private $renderURI; 12 + private $whitespace; 13 + private $renderingRef; 14 + private $autoload; 15 + 16 + public function setAutoload($autoload) { 17 + $this->autoload = $autoload; 18 + return $this; 19 + } 20 + 21 + public function getAutoload() { 22 + return $this->autoload; 23 + } 24 + 25 + public function setRenderingRef($rendering_ref) { 26 + $this->renderingRef = $rendering_ref; 27 + return $this; 28 + } 29 + 30 + public function getRenderingRef() { 31 + return $this->renderingRef; 32 + } 33 + 34 + public function setWhitespace($whitespace) { 35 + $this->whitespace = $whitespace; 36 + return $this; 37 + } 38 + 39 + public function getWhitespace() { 40 + return $this->whitespace; 41 + } 42 + 43 + public function setRenderURI($render_uri) { 44 + $this->renderURI = $render_uri; 45 + return $this; 46 + } 47 + 48 + public function getRenderURI() { 49 + return $this->renderURI; 50 + } 11 51 12 52 public function setChangeset($changeset) { 13 53 $this->changeset = $changeset; ··· 34 74 $this->id = celerity_generate_unique_node_id(); 35 75 } 36 76 return $this->id; 77 + } 78 + 79 + public function setID($id) { 80 + $this->id = $id; 81 + return $this; 37 82 } 38 83 39 84 public function setVsChangesetID($vs_changeset_id) { ··· 139 184 $this->getVsChangesetID(), 140 185 $this->changeset->getID()), 141 186 'right' => $this->changeset->getID(), 187 + 'renderURI' => $this->getRenderURI(), 188 + 'whitespace' => $this->getWhitespace(), 189 + 'highlight' => null, 190 + 'renderer' => null, 191 + 'ref' => $this->getRenderingRef(), 192 + 'autoload' => $this->getAutoload(), 142 193 ), 143 194 'class' => $class, 144 195 'id' => $id, ··· 154 205 'class' => 'differential-file-icon-header'), 155 206 array( 156 207 $icon, 157 - $display_filename)), 158 - phutil_tag('div', array('style' => 'clear: both'), ''), 159 - $this->renderChildren(), 208 + $display_filename, 209 + )), 210 + javelin_tag( 211 + 'div', 212 + array( 213 + 'class' => 'changeset-view-content', 214 + 'sigil' => 'changeset-view-content', 215 + ), 216 + $this->renderChildren()), 160 217 )); 161 218 } 162 219
+15 -9
src/applications/differential/view/DifferentialChangesetListView.php
··· 131 131 )); 132 132 133 133 $output = array(); 134 - $mapping = array(); 134 + $ids = array(); 135 135 foreach ($changesets as $key => $changeset) { 136 + 136 137 $file = $changeset->getFilename(); 137 138 $class = 'differential-changeset'; 138 139 if (!$this->inlineURI) { ··· 142 143 $ref = $this->references[$key]; 143 144 144 145 $detail = new DifferentialChangesetDetailView(); 146 + 147 + $uniq_id = 'diff-'.$changeset->getAnchorName(); 148 + $detail->setID($uniq_id); 145 149 146 150 $view_options = $this->renderViewOptionsDropdown( 147 151 $detail, ··· 153 157 $detail->setSymbolIndex(idx($this->symbolIndexes, $key)); 154 158 $detail->setVsChangesetID(idx($this->vsMap, $changeset->getID())); 155 159 $detail->setEditable(true); 160 + $detail->setRenderingRef($ref); 161 + $detail->setAutoload(isset($this->visibleChangesets[$key])); 156 162 157 - $uniq_id = 'diff-'.$changeset->getAnchorName(); 163 + $detail->setRenderURI($this->renderURI); 164 + $detail->setWhitespace($this->whitespace); 165 + 158 166 if (isset($this->visibleChangesets[$key])) { 159 167 $load = 'Loading...'; 160 - $mapping[$uniq_id] = $ref; 161 168 } else { 162 169 $load = javelin_tag( 163 170 'a', 164 171 array( 165 172 'href' => '#'.$uniq_id, 173 + 'sigil' => 'differential-load', 166 174 'meta' => array( 167 - 'id' => $uniq_id, 168 - 'ref' => $ref, 175 + 'id' => $detail->getID(), 169 176 'kill' => true, 170 177 ), 171 - 'sigil' => 'differential-load', 172 178 'mustcapture' => true, 173 179 ), 174 180 pht('Load')); ··· 181 187 ), 182 188 phutil_tag('div', array('class' => 'differential-loading'), $load))); 183 189 $output[] = $detail->render(); 190 + 191 + $ids[] = $detail->getID(); 184 192 } 185 193 186 194 $this->requireResource('aphront-tooltip-css'); 187 195 188 196 $this->initBehavior('differential-populate', array( 189 - 'registry' => $mapping, 190 - 'whitespace' => $this->whitespace, 191 - 'uri' => $this->renderURI, 197 + 'changesetViewIDs' => $ids, 192 198 )); 193 199 194 200 $this->initBehavior('differential-show-more', array(
+1 -2
src/applications/differential/view/DifferentialDiffTableOfContentsView.php
··· 312 312 'a', 313 313 array( 314 314 'href' => '#'.$changeset->getAnchorName(), 315 + 'sigil' => 'differential-load', 315 316 'meta' => array( 316 317 'id' => 'diff-'.$changeset->getAnchorName(), 317 - 'ref' => $ref, 318 318 ), 319 - 'sigil' => 'differential-load', 320 319 ), 321 320 $display_file); 322 321 }
+269
webroot/rsrc/js/application/differential/ChangesetViewManager.js
··· 1 + /** 2 + * @provides changeset-view-manager 3 + * @requires javelin-dom 4 + * javelin-util 5 + * javelin-stratcom 6 + * javelin-install 7 + * javelin-workflow 8 + * javelin-router 9 + * javelin-behavior-device 10 + * javelin-vector 11 + */ 12 + 13 + 14 + JX.install('ChangesetViewManager', { 15 + 16 + construct : function(node) { 17 + this._node = node; 18 + 19 + var data = this._getNodeData(); 20 + this._renderURI = data.renderURI; 21 + this._ref = data.ref; 22 + this._whitespace = data.whitespace; 23 + this._renderer = data.renderer; 24 + this._highlight = data.highlight; 25 + }, 26 + 27 + members: { 28 + _node: null, 29 + _loaded: false, 30 + _sequence: 0, 31 + _stabilize: false, 32 + 33 + _renderURI: null, 34 + _ref: null, 35 + _whitespace: null, 36 + _renderer: null, 37 + _highlight: null, 38 + 39 + 40 + /** 41 + * Has the content of this changeset been loaded? 42 + * 43 + * This method returns `true` if a request has been fired, even if the 44 + * response has not returned yet. 45 + * 46 + * @return bool True if the content has been loaded. 47 + */ 48 + isLoaded: function() { 49 + return this._loaded; 50 + }, 51 + 52 + 53 + /** 54 + * Configure stabilization of the document position on content load. 55 + * 56 + * When we dump the changeset into the document, we can try to stabilize 57 + * the document scroll position so that the user doesn't feel like they 58 + * are jumping around as things load in. This is generally useful when 59 + * populating initial changes. 60 + * 61 + * However, if a user explicitly requests a content load by clicking a 62 + * "Load" link or using the dropdown menu, this stabilization generally 63 + * feels unnatural, so we don't use it in response to explicit user action. 64 + * 65 + * @param bool True to stabilize the next content fill. 66 + * @return this 67 + */ 68 + setStabilize: function(stabilize) { 69 + this._stabilize = stabilize; 70 + return this; 71 + }, 72 + 73 + 74 + /** 75 + * Should this changeset load immediately when the page loads? 76 + * 77 + * Normally, changes load immediately, but if a diff or commit is very 78 + * large we stop doing this and have the user load files explicitly, or 79 + * choose to load everything. 80 + * 81 + * @return bool True if the changeset should load automatically when the 82 + * page loads. 83 + */ 84 + shouldAutoload: function() { 85 + return this._getNodeData().autoload; 86 + }, 87 + 88 + 89 + /** 90 + * Load this changeset, if it isn't already loading. 91 + * 92 + * This fires a request to fill the content of this changeset, provided 93 + * there isn't already a request in flight. To force a reload, use 94 + * @{method:reload}. 95 + * 96 + * @return this 97 + */ 98 + load: function() { 99 + if (this._loaded) { 100 + return this; 101 + } 102 + 103 + return this.reload(); 104 + }, 105 + 106 + 107 + /** 108 + * Reload the changeset content. 109 + * 110 + * This method always issues a request, even if the content is already 111 + * loading. To load conditionally, use @{method:load}. 112 + * 113 + * @return this 114 + */ 115 + reload: function() { 116 + this._loaded = true; 117 + this._sequence++; 118 + 119 + var data = this._getNodeData(); 120 + 121 + var params = { 122 + ref: this._ref, 123 + whitespace: this._whitespace, 124 + renderer: this._getRenderer() 125 + }; 126 + 127 + var workflow = new JX.Workflow(this._renderURI, params) 128 + .setHandler(JX.bind(this, this._onresponse, this._sequence)); 129 + 130 + var routable = workflow.getRoutable(); 131 + 132 + routable 133 + .setPriority(500) 134 + .setType('content') 135 + .setKey(this._getRoutableKey()); 136 + 137 + JX.Router.getInstance().queue(routable); 138 + 139 + JX.DOM.setContent( 140 + this._getContentFrame(), 141 + JX.$N( 142 + 'div', 143 + {className: 'differential-loading'}, 144 + 'Loading...')); 145 + 146 + return this; 147 + }, 148 + 149 + 150 + /** 151 + * Get the active @{class:JX.Routable} for this changeset. 152 + * 153 + * After issuing a request with @{method:load} or @{method:reload}, you 154 + * can adjust routable settings (like priority) by querying the routable 155 + * with this method. Note that there may not be a current routable. 156 + * 157 + * @return JX.Routable|null Active routable, if one exists. 158 + */ 159 + getRoutable: function() { 160 + return JX.Router.getInstance().getRoutableByKey(this._getRoutableKey()); 161 + }, 162 + 163 + 164 + _getRenderer: function() { 165 + // TODO: This is a big pile of TODOs. 166 + 167 + // NOTE: If you load the page at one device resolution and then resize to 168 + // a different one we don't re-render the diffs, because it's a 169 + // complicated mess and you could lose inline comments, cursor positions, 170 + // etc. 171 + var renderer = (JX.Device.getDevice() == 'desktop') ? '2up' : '1up'; 172 + 173 + // TODO: Once 1up works better, figure out when to show it. 174 + renderer = '2up'; 175 + 176 + return renderer; 177 + }, 178 + 179 + 180 + _getNodeData: function() { 181 + return JX.Stratcom.getData(this._node); 182 + }, 183 + 184 + 185 + _onresponse: function(sequence, response) { 186 + if (sequence != this._sequence) { 187 + // If this isn't the most recent request, ignore it. This normally 188 + // means the user changed view settings between the time the page loaded 189 + // and the content filled. 190 + return; 191 + } 192 + 193 + // As we populate the changeset list, we try to hold the document scroll 194 + // position steady, so that, e.g., users who want to leave a comment on a 195 + // diff with a large number of changes don't constantly have the text 196 + // area scrolled off the bottom of the screen until the entire diff loads. 197 + // 198 + // There are two three major cases here: 199 + // 200 + // - If we're near the top of the document, never scroll. 201 + // - If we're near the bottom of the document, always scroll. 202 + // - Otherwise, scroll if the changes were above the midline of the 203 + // viewport. 204 + 205 + var target = this._node; 206 + 207 + var old_pos = JX.Vector.getScroll(); 208 + var old_view = JX.Vector.getViewport(); 209 + var old_dim = JX.Vector.getDocument(); 210 + 211 + // Number of pixels away from the top or bottom of the document which 212 + // count as "nearby". 213 + var sticky = 480; 214 + 215 + var near_top = (old_pos.y <= sticky); 216 + var near_bot = ((old_pos.y + old_view.y) >= (old_dim.y - sticky)); 217 + 218 + var target_pos = JX.Vector.getPos(target); 219 + var target_dim = JX.Vector.getDim(target); 220 + var target_mid = (target_pos.y + (target_dim.y / 2)); 221 + 222 + var view_mid = (old_pos.y + (old_view.y / 2)); 223 + var above_mid = (target_mid < view_mid); 224 + 225 + var frame = this._getContentFrame(); 226 + JX.DOM.setContent(frame, JX.$H(response.changeset)); 227 + 228 + if (this._stabilize) { 229 + if (!near_top) { 230 + if (near_bot || above_mid) { 231 + // Figure out how much taller the document got. 232 + var delta = (JX.Vector.getDocument().y - old_dim.y); 233 + window.scrollTo(old_pos.x, old_pos.y + delta); 234 + } 235 + } 236 + this._stabilize = false; 237 + } 238 + 239 + if (response.coverage) { 240 + for (var k in response.coverage) { 241 + try { 242 + JX.DOM.replace(JX.$(k), JX.$H(response.coverage[k])); 243 + } catch (ignored) { 244 + // Not terribly important. 245 + } 246 + } 247 + } 248 + }, 249 + 250 + _getContentFrame: function() { 251 + return JX.DOM.find(this._node, 'div', 'changeset-view-content'); 252 + }, 253 + 254 + _getRoutableKey: function() { 255 + return 'changeset-view.' + this._ref + '.' + this._sequence; 256 + } 257 + 258 + }, 259 + 260 + statics: { 261 + getForNode: function(node) { 262 + var data = JX.Stratcom.getData(node); 263 + if (!data.changesetViewManager) { 264 + data.changesetViewManager = new JX.ChangesetViewManager(node); 265 + } 266 + return data.changesetViewManager; 267 + } 268 + } 269 + });
+40 -3
webroot/rsrc/js/application/differential/behavior-dropdown-menus.js
··· 8 8 * phuix-action-list-view 9 9 * phuix-action-view 10 10 * phabricator-phtize 11 + * changeset-view-manager 11 12 */ 12 13 13 14 JX.behavior('differential-dropdown-menus', function(config) { ··· 97 98 }); 98 99 list.addItem(visible_item); 99 100 100 - add_link('fa-files-o', pht('Browse in Diffusion'), data.diffusionURI); 101 + add_link('fa-file-text', pht('Browse in Diffusion'), data.diffusionURI); 101 102 add_link('fa-file-o', pht('View Standalone'), data.standaloneURI); 103 + 104 + var up_item = new JX.PHUIXActionView() 105 + .setHandler(function(e) { 106 + var changeset = JX.DOM.findAbove( 107 + button, 108 + 'div', 109 + 'differential-changeset'); 110 + 111 + var view = JX.ChangesetViewManager.getForNode(changeset); 112 + view.reload(); 113 + 114 + e.prevent(); 115 + menu.close(); 116 + }); 117 + list.addItem(up_item); 118 + 102 119 add_link('fa-arrow-left', pht('Show Raw File (Left)'), data.leftURI); 103 120 add_link('fa-arrow-right', pht('Show Raw File (Right)'), data.rightURI); 104 121 add_link('fa-pencil', pht('Open in Editor'), data.editor, true); ··· 108 125 menu.setContent(list.getNode()); 109 126 110 127 menu.listen('open', function() { 128 + var changeset = JX.DOM.findAbove( 129 + button, 130 + 'div', 131 + 'differential-changeset'); 132 + 133 + var view = JX.ChangesetViewManager.getForNode(changeset); 111 134 112 135 // When the user opens the menu, check if there are any "Show More" 113 136 // links in the changeset body. If there aren't, disable the "Show ··· 132 155 .setHandler(function(e) { e.prevent(); }); 133 156 } 134 157 135 - visible_item.setDisabled(true); 136 - visible_item.setName(pht("Can't Toggle Unloaded File")); 158 + // TODO: This is temporary and just makes testing easier. It will do 159 + // some mojo soon. 160 + if (view.isLoaded()) { 161 + up_item 162 + .setIcon('fa-refresh') 163 + .setName('Reload'); 164 + } else { 165 + up_item 166 + .setIcon('fa-refresh') 167 + .setName('Load'); 168 + } 169 + 170 + visible_item 171 + .setDisabled(true) 172 + .setIcon('fa-expand') 173 + .setName(pht("Can't Toggle Unloaded File")); 137 174 var diffs = JX.DOM.scry( 138 175 JX.$(data.containerID), 139 176 'table',
+15 -123
webroot/rsrc/js/application/differential/behavior-populate.js
··· 1 1 /** 2 2 * @provides javelin-behavior-differential-populate 3 3 * @requires javelin-behavior 4 - * javelin-workflow 5 - * javelin-util 6 4 * javelin-dom 7 5 * javelin-stratcom 8 - * javelin-behavior-device 9 - * javelin-vector 10 - * javelin-router 11 6 * phabricator-tooltip 7 + * changeset-view-manager 12 8 */ 13 9 14 10 JX.behavior('differential-populate', function(config) { 15 11 16 - function onresponse(target_id, response) { 17 - // As we populate the diff, we try to hold the document scroll position 18 - // steady, so that, e.g., users who want to leave a comment on a diff with a 19 - // large number of changes don't constantly have the text area scrolled off 20 - // the bottom of the screen until the entire diff loads. 21 - // 22 - // There are two three major cases here: 23 - // 24 - // - If we're near the top of the document, never scroll. 25 - // - If we're near the bottom of the document, always scroll. 26 - // - Otherwise, scroll if the changes were above the midline of the 27 - // viewport. 28 - var target = JX.$(target_id); 29 - 30 - var old_pos = JX.Vector.getScroll(); 31 - var old_view = JX.Vector.getViewport(); 32 - var old_dim = JX.Vector.getDocument(); 33 - 34 - // Number of pixels away from the top or bottom of the document which 35 - // count as "nearby". 36 - var sticky = 480; 37 - 38 - var near_top = (old_pos.y <= sticky); 39 - var near_bot = ((old_pos.y + old_view.y) >= (old_dim.y - sticky)); 40 - 41 - var target_pos = JX.Vector.getPos(target); 42 - var target_dim = JX.Vector.getDim(target); 43 - var target_mid = (target_pos.y + (target_dim.y / 2)); 44 - 45 - var view_mid = (old_pos.y + (old_view.y / 2)); 46 - var above_mid = (target_mid < view_mid); 47 - 48 - JX.DOM.replace(target, JX.$H(response.changeset)); 49 - 50 - if (!near_top) { 51 - if (near_bot || above_mid) { 52 - // Figure out how much taller the document got. 53 - var delta = (JX.Vector.getDocument().y - old_dim.y); 54 - 55 - window.scrollTo(old_pos.x, old_pos.y + delta); 56 - } 57 - } 58 - 59 - if (response.coverage) { 60 - for (var k in response.coverage) { 61 - try { 62 - JX.DOM.replace(JX.$(k), JX.$H(response.coverage[k])); 63 - } catch (ignored) { 64 - // Not terribly important. 65 - } 66 - } 12 + for (var ii = 0; ii < config.changesetViewIDs.length; ii++) { 13 + var id = config.changesetViewIDs[ii]; 14 + var view = JX.ChangesetViewManager.getForNode(JX.$(id)); 15 + if (view.shouldAutoload()) { 16 + view.setStabilize(true).load(); 67 17 } 68 18 } 69 19 70 - // NOTE: If you load the page at one device resolution and then resize to 71 - // a different one we don't re-render the diffs, because it's a complicated 72 - // mess and you could lose inline comments, cursor positions, etc. 73 - var renderer = (JX.Device.getDevice() == 'desktop') ? '2up' : '1up'; 74 - 75 - // TODO: Once 1up works better, figure out when to show it. 76 - renderer = '2up'; 77 - 78 - var get_key = function(id) { 79 - return 'differential-populate.' + id; 80 - }; 81 - 82 - var load = function(id, data) { 83 - var routable = new JX.Workflow(config.uri, data) 84 - .setHandler(JX.bind(null, onresponse, id)) 85 - .getRoutable(); 86 - 87 - routable 88 - .setPriority(500) 89 - .setType('content') 90 - .setKey(get_key(id)); 91 - 92 - JX.Router.getInstance().queue(routable); 93 - 94 - return routable; 95 - }; 96 - 97 - for (var k in config.registry) { 98 - var data = { 99 - ref : config.registry[k], 100 - whitespace: config.whitespace, 101 - renderer: renderer 102 - }; 103 - 104 - load(k, data); 105 - } 106 - 107 - var highlighted = null; 108 - var highlight_class = null; 109 - 110 20 JX.Stratcom.listen( 111 21 'click', 112 22 'differential-load', 113 23 function(e) { 114 24 var meta = e.getNodeData('differential-load'); 115 - var diff; 116 - try { 117 - diff = JX.$(meta.id); 118 - } catch (ex) { 119 - // Already loaded. 120 - } 121 - if (diff) { 122 - JX.DOM.setContent( 123 - diff, 124 - JX.$H('<div class="differential-loading">Loading...</div>')); 25 + var changeset = JX.$(meta.id); 26 + var view = JX.ChangesetViewManager.getForNode(changeset); 125 27 126 - // When a user explicitly clicks "Load" (or clicks a link in the table 127 - // of contents) prioritize this request if it already exists. If it 128 - // doesn't, make a new high-priority request. 129 - 130 - var key = get_key(meta.id); 131 - var routable = JX.Router.getInstance().getRoutableByKey(key); 132 - 133 - if (!routable) { 134 - var data = { 135 - ref : meta.ref, 136 - whitespace : config.whitespace 137 - }; 138 - 139 - routable = load(meta.id, data); 140 - } 141 - 28 + view.load(); 29 + var routable = view.getRoutable(); 30 + if (routable) { 142 31 routable.setPriority(2000); 143 32 } 33 + 144 34 if (meta.kill) { 145 35 e.kill(); 146 36 } 147 37 }); 38 + 39 + var highlighted = null; 40 + var highlight_class = null; 148 41 149 42 JX.Stratcom.listen( 150 43 ['mouseover', 'mouseout'], ··· 200 93 } 201 94 202 95 }); 203 - 204 96 205 97 206 98 });