Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15420742
D20639.id49225.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
23 KB
Referenced Files
None
Subscribers
None
D20639.id49225.diff
View Options
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -412,16 +412,16 @@
'rsrc/js/application/phortune/phortune-credit-card-form.js' => 'd12d214f',
'rsrc/js/application/policy/behavior-policy-control.js' => '0eaa33a9',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172',
- 'rsrc/js/application/projects/WorkboardBoard.js' => '44f71637',
+ 'rsrc/js/application/projects/WorkboardBoard.js' => 'bdb99902',
'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8',
'rsrc/js/application/projects/WorkboardCardTemplate.js' => '2a61f8d4',
'rsrc/js/application/projects/WorkboardColumn.js' => 'c3d24e63',
- 'rsrc/js/application/projects/WorkboardController.js' => '42c7a5a7',
+ 'rsrc/js/application/projects/WorkboardController.js' => 'b9d0c2f3',
'rsrc/js/application/projects/WorkboardDropEffect.js' => '8e0aa661',
'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d',
'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'ebe83a6b',
'rsrc/js/application/projects/WorkboardOrderTemplate.js' => '03e8891f',
- 'rsrc/js/application/projects/behavior-project-boards.js' => 'aad45445',
+ 'rsrc/js/application/projects/behavior-project-boards.js' => '58cb6a88',
'rsrc/js/application/projects/behavior-project-create.js' => '34c53422',
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
@@ -667,7 +667,7 @@
'javelin-behavior-phuix-example' => 'c2c500a7',
'javelin-behavior-policy-control' => '0eaa33a9',
'javelin-behavior-policy-rule-editor' => '9347f172',
- 'javelin-behavior-project-boards' => 'aad45445',
+ 'javelin-behavior-project-boards' => '58cb6a88',
'javelin-behavior-project-create' => '34c53422',
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
'javelin-behavior-read-only-warning' => 'b9109f8f',
@@ -743,11 +743,11 @@
'javelin-view-renderer' => '9aae2b66',
'javelin-view-visitor' => '308f9fe4',
'javelin-websocket' => 'fdc13e4e',
- 'javelin-workboard-board' => '44f71637',
+ 'javelin-workboard-board' => 'bdb99902',
'javelin-workboard-card' => '0392a5d8',
'javelin-workboard-card-template' => '2a61f8d4',
'javelin-workboard-column' => 'c3d24e63',
- 'javelin-workboard-controller' => '42c7a5a7',
+ 'javelin-workboard-controller' => 'b9d0c2f3',
'javelin-workboard-drop-effect' => '8e0aa661',
'javelin-workboard-header' => '111bfd2d',
'javelin-workboard-header-template' => 'ebe83a6b',
@@ -1264,16 +1264,6 @@
'4234f572' => array(
'syntax-default-css',
),
- '42c7a5a7' => array(
- 'javelin-install',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-vector',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'phabricator-drag-and-drop-file-upload',
- 'javelin-workboard-board',
- ),
'4370900d' => array(
'javelin-install',
'javelin-util',
@@ -1305,18 +1295,6 @@
'javelin-uri',
'javelin-routable',
),
- '44f71637' => array(
- 'javelin-install',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'phabricator-draggable-list',
- 'javelin-workboard-column',
- 'javelin-workboard-header-template',
- 'javelin-workboard-card-template',
- 'javelin-workboard-order-template',
- ),
'46116c01' => array(
'javelin-request',
'javelin-behavior',
@@ -1435,6 +1413,16 @@
'javelin-vector',
'javelin-typeahead-static-source',
),
+ '58cb6a88' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-vector',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'javelin-workboard-controller',
+ 'javelin-workboard-drop-effect',
+ ),
'5902260c' => array(
'javelin-util',
'javelin-magical-init',
@@ -1852,16 +1840,6 @@
'javelin-dom',
'javelin-util',
),
- 'aad45445' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-vector',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'javelin-workboard-controller',
- 'javelin-workboard-drop-effect',
- ),
'ab85e184' => array(
'javelin-install',
'javelin-dom',
@@ -1952,6 +1930,28 @@
'javelin-uri',
'phabricator-notification',
),
+ 'b9d0c2f3' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-vector',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'phabricator-drag-and-drop-file-upload',
+ 'javelin-workboard-board',
+ ),
+ 'bdb99902' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'phabricator-draggable-list',
+ 'javelin-workboard-column',
+ 'javelin-workboard-header-template',
+ 'javelin-workboard-card-template',
+ 'javelin-workboard-order-template',
+ ),
'bde53589' => array(
'phui-inline-comment-view-css',
),
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4163,6 +4163,7 @@
'PhabricatorProjectBoardFilterController' => 'applications/project/controller/PhabricatorProjectBoardFilterController.php',
'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php',
'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php',
+ 'PhabricatorProjectBoardReloadController' => 'applications/project/controller/PhabricatorProjectBoardReloadController.php',
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
'PhabricatorProjectBuiltinsExample' => 'applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php',
@@ -10431,6 +10432,7 @@
'PhabricatorProjectBoardFilterController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController',
+ 'PhabricatorProjectBoardReloadController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectBuiltinsExample' => 'PhabricatorUIExample',
diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php
--- a/src/applications/project/application/PhabricatorProjectApplication.php
+++ b/src/applications/project/application/PhabricatorProjectApplication.php
@@ -99,6 +99,8 @@
=> 'PhabricatorProjectBoardDefaultController',
'filter/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhabricatorProjectBoardFilterController',
+ 'reload/'
+ => 'PhabricatorProjectBoardReloadController',
),
'column/' => array(
'remove/(?P<id>\d+)/' =>
diff --git a/src/applications/project/controller/PhabricatorProjectBoardReloadController.php b/src/applications/project/controller/PhabricatorProjectBoardReloadController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectBoardReloadController.php
@@ -0,0 +1,38 @@
+<?php
+
+final class PhabricatorProjectBoardReloadController
+ extends PhabricatorProjectBoardController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $response = $this->loadProject();
+ if ($response) {
+ return $response;
+ }
+
+ $project = $this->getProject();
+ $state = $this->getViewState();
+ $board_uri = $state->newWorkboardURI();
+
+ $layout_engine = $state->getLayoutEngine();
+
+ $board_phid = $project->getPHID();
+
+ $objects = $state->getObjects();
+ $object_phids = mpull($objects, 'getPHID');
+
+ $engine = id(new PhabricatorBoardResponseEngine())
+ ->setViewer($viewer)
+ ->setBoardPHID($board_phid)
+ ->setUpdatePHIDs($object_phids);
+
+ // TODO: We don't currently process "order" properly. If a user is viewing
+ // a board grouped by "Owner", and another user changes a task to be owned
+ // by a user who currently owns nothing on the board, the new header won't
+ // generate correctly if the first user presses "R".
+
+ return $engine->buildResponse();
+ }
+
+}
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -286,6 +286,7 @@
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
'uploadURI' => '/file/dropupload/',
'coverURI' => $this->getApplicationURI('cover/'),
+ 'reloadURI' => phutil_string_cast($state->newWorkboardURI('reload/')),
'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
'pointsEnabled' => ManiphestTaskPoints::getIsEnabled(),
diff --git a/src/applications/project/engine/PhabricatorBoardResponseEngine.php b/src/applications/project/engine/PhabricatorBoardResponseEngine.php
--- a/src/applications/project/engine/PhabricatorBoardResponseEngine.php
+++ b/src/applications/project/engine/PhabricatorBoardResponseEngine.php
@@ -6,6 +6,7 @@
private $boardPHID;
private $objectPHID;
private $visiblePHIDs;
+ private $updatePHIDs = array();
private $ordering;
private $sounds;
@@ -45,6 +46,15 @@
return $this->visiblePHIDs;
}
+ public function setUpdatePHIDs(array $update_phids) {
+ $this->updatePHIDs = $update_phids;
+ return $this;
+ }
+
+ public function getUpdatePHIDs() {
+ return $this->updatePHIDs;
+ }
+
public function setOrdering(PhabricatorProjectColumnOrder $ordering) {
$this->ordering = $ordering;
return $this;
@@ -71,36 +81,41 @@
// Load all the other tasks that are visible in the affected columns and
// perform layout for them.
- $visible_phids = $this->getAllVisiblePHIDs();
+ $all_phids = $this->getAllVisiblePHIDs();
$layout_engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer)
->setBoardPHIDs(array($board_phid))
- ->setObjectPHIDs($visible_phids)
+ ->setObjectPHIDs($all_phids)
->executeLayout();
- $object_columns = $layout_engine->getObjectColumns(
- $board_phid,
- $object_phid);
-
$natural = array();
- foreach ($object_columns as $column_phid => $column) {
+
+ $update_phids = $this->getAllUpdatePHIDs();
+ $update_columns = array();
+ foreach ($update_phids as $update_phid) {
+ $update_columns += $layout_engine->getObjectColumns(
+ $board_phid,
+ $update_phid);
+ }
+
+ foreach ($update_columns as $column_phid => $column) {
$column_object_phids = $layout_engine->getColumnObjectPHIDs(
$board_phid,
$column_phid);
$natural[$column_phid] = array_values($column_object_phids);
}
- $all_visible = id(new ManiphestTaskQuery())
+ $all_objects = id(new ManiphestTaskQuery())
->setViewer($viewer)
- ->withPHIDs($visible_phids)
+ ->withPHIDs($all_phids)
->execute();
- $all_visible = mpull($all_visible, null, 'getPHID');
+ $all_objects = mpull($all_objects, null, 'getPHID');
if ($ordering) {
- $vectors = $ordering->getSortVectorsForObjects($all_visible);
- $header_keys = $ordering->getHeaderKeysForObjects($all_visible);
- $headers = $ordering->getHeadersForObjects($all_visible);
+ $vectors = $ordering->getSortVectorsForObjects($all_objects);
+ $header_keys = $ordering->getHeaderKeysForObjects($all_objects);
+ $headers = $ordering->getHeadersForObjects($all_objects);
$headers = mpull($headers, 'toDictionary');
} else {
$vectors = array();
@@ -108,19 +123,10 @@
$headers = array();
}
- $object = id(new ManiphestTaskQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($object_phid))
- ->needProjectPHIDs(true)
- ->executeOne();
- if (!$object) {
- return new Aphront404Response();
- }
-
- $template = $this->buildTemplate($object);
+ $templates = $this->newCardTemplates();
$cards = array();
- foreach ($all_visible as $card_phid => $object) {
+ foreach ($all_objects as $card_phid => $object) {
$card = array(
'vectors' => array(),
'headers' => array(),
@@ -144,8 +150,13 @@
$card['properties'] = self::newTaskProperties($object);
}
- if ($card_phid === $object_phid) {
- $card['nodeHTMLTemplate'] = hsprintf('%s', $template);
+ if (isset($templates[$card_phid])) {
+ phlog("full up: $card_phid");
+
+ $card['nodeHTMLTemplate'] = hsprintf('%s', $templates[$card_phid]);
+ $card['update'] = true;
+ } else {
+ $card['update'] = false;
}
$card['vectors'] = (object)$card['vectors'];
@@ -156,7 +167,6 @@
}
$payload = array(
- 'objectPHID' => $object_phid,
'columnMaps' => $natural,
'cards' => $cards,
'headers' => $headers,
@@ -176,22 +186,6 @@
);
}
- private function buildTemplate($object) {
- $viewer = $this->getViewer();
- $object_phid = $this->getObjectPHID();
-
- $excluded_phids = $this->loadExcludedProjectPHIDs();
-
- $rendering_engine = id(new PhabricatorBoardRenderingEngine())
- ->setViewer($viewer)
- ->setObjects(array($object))
- ->setExcludedProjectPHIDs($excluded_phids);
-
- $card = $rendering_engine->renderCard($object_phid);
-
- return hsprintf('%s', $card->getItem());
- }
-
private function loadExcludedProjectPHIDs() {
$viewer = $this->getViewer();
$board_phid = $this->getBoardPHID();
@@ -211,10 +205,67 @@
}
private function getAllVisiblePHIDs() {
- $visible_phids = $this->getVisiblePHIDs();
- $visible_phids[] = $this->getObjectPHID();
- $visible_phids = array_fuse($visible_phids);
- return $visible_phids;
+ $phids = $this->getAllUpdatePHIDs();
+
+ foreach ($this->getVisiblePHIDs() as $phid) {
+ $phids[] = $phid;
+ }
+
+ $phids = array_fuse($phids);
+
+ return $phids;
+ }
+
+ private function getAllUpdatePHIDs() {
+ $phids = $this->getUpdatePHIDs();
+
+ $object_phid = $this->getObjectPHID();
+ if ($object_phid) {
+ $phids[] = $object_phid;
+ }
+
+ $phids = array_fuse($phids);
+
+ return $phids;
+ }
+
+ private function newCardTemplates() {
+ $viewer = $this->getViewer();
+
+ $update_phids = $this->getAllUpdatePHIDs();
+ if (!$update_phids) {
+ return array();
+ }
+
+ $objects = id(new ManiphestTaskQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($update_phids)
+ ->needProjectPHIDs(true)
+ ->execute();
+
+ if (!$objects) {
+ return array();
+ }
+
+ $excluded_phids = $this->loadExcludedProjectPHIDs();
+
+ $rendering_engine = id(new PhabricatorBoardRenderingEngine())
+ ->setViewer($viewer)
+ ->setObjects($objects)
+ ->setExcludedProjectPHIDs($excluded_phids);
+
+ $templates = array();
+ foreach ($objects as $object) {
+ $object_phid = $object->getPHID();
+
+ $card = $rendering_engine->renderCard($object_phid);
+ $item = $card->getItem();
+ $template = hsprintf('%s', $item);
+
+ $templates[$object_phid] = $template;
+ }
+
+ return $templates;
}
}
diff --git a/webroot/rsrc/js/application/projects/WorkboardBoard.js b/webroot/rsrc/js/application/projects/WorkboardBoard.js
--- a/webroot/rsrc/js/application/projects/WorkboardBoard.js
+++ b/webroot/rsrc/js/application/projects/WorkboardBoard.js
@@ -129,6 +129,13 @@
start: function() {
this._setupDragHandlers();
+ // TODO: This is temporary code to make it easier to debug this workflow
+ // by pressing the "R" key.
+ var on_reload = JX.bind(this, this._reloadCards);
+ new JX.KeyboardShortcut('R', 'Reload Card State (Prototype)')
+ .setHandler(on_reload)
+ .register();
+
for (var k in this._columns) {
this._columns[k].redraw();
}
@@ -551,15 +558,6 @@
},
_oncardupdate: function(list, src_phid, dst_phid, after_phid, response) {
- var src_column = this.getColumn(src_phid);
- var dst_column = this.getColumn(dst_phid);
-
- var card = src_column.removeCard(response.objectPHID);
- dst_column.addCard(card, after_phid);
-
- src_column.markForRedraw();
- dst_column.markForRedraw();
-
this.updateCard(response);
var sounds = response.sounds || [];
@@ -572,37 +570,51 @@
updateCard: function(response) {
var columns = this.getColumns();
+ var column_phid;
+ var card_phid;
+ var card_data;
+
+ // The server may send us a full or partial update for a card. If we've
+ // received a full update, we're going to redraw the entire card and may
+ // need to change which columns it appears in.
- var phid = response.objectPHID;
+ // For a partial update, we've just received supplemental sorting or
+ // property information and do not need to perform a full redraw.
- for (var add_phid in response.columnMaps) {
- var target_column = this.getColumn(add_phid);
+ // When we reload card state, edit a card, or move a card, we get a full
+ // update for the card.
+
+ // We we move a card in a column, we may get a partial update for other
+ // visible cards in the column.
+
+
+ // Figure out which columns each card now appears in. For cards that
+ // have received a full update, we'll use this map to move them into
+ // the correct columns.
+ var update_map = {};
+ for (column_phid in response.columnMaps) {
+ var target_column = this.getColumn(column_phid);
if (!target_column) {
// If the column isn't visible, don't try to add a card to it.
continue;
}
- target_column.newCard(phid);
- }
+ var column_map = response.columnMaps[column_phid];
- var column_maps = response.columnMaps;
- var natural_column;
- for (var natural_phid in column_maps) {
- natural_column = this.getColumn(natural_phid);
- if (!natural_column) {
- // Our view of the board may be out of date, so we might get back
- // information about columns that aren't visible. Just ignore the
- // position information for any columns we aren't displaying on the
- // client.
- continue;
+ for (var ii = 0; ii < column_map.length; ii++) {
+ card_phid = column_map[ii];
+ if (!update_map[card_phid]) {
+ update_map[card_phid] = {};
+ }
+ update_map[card_phid][column_phid] = true;
}
-
- natural_column.setNaturalOrder(column_maps[natural_phid]);
}
- for (var card_phid in response.cards) {
- var card_data = response.cards[card_phid];
+ // Process partial updates for cards. This is supplemental data which
+ // we can just merge in without any special handling.
+ for (card_phid in response.cards) {
+ card_data = response.cards[card_phid];
var card_template = this.getCardTemplate(card_phid);
if (card_data.nodeHTMLTemplate) {
@@ -623,6 +635,57 @@
}
}
+
+ // Process full updates for cards which we have a full update for. This
+ // may involve moving them between columns.
+ for (card_phid in response.cards) {
+ card_data = response.cards[card_phid];
+
+ if (!card_data.update) {
+ continue;
+ }
+
+ for (column_phid in columns) {
+ var column = columns[column_phid];
+ var card = column.getCard(card_phid);
+
+ if (card) {
+ card.redraw();
+ column.markForRedraw();
+ }
+
+ // Compare the server state to the client state, and add or remove
+ // cards on the client as necessary to synchronize them.
+
+ if (update_map[card_phid][column_phid]) {
+ if (!card) {
+ column.newCard(card_phid);
+ column.markForRedraw();
+ }
+ } else {
+ if (card) {
+ column.removeCard(card_phid);
+ column.markForRedraw();
+ }
+ }
+ }
+ }
+
+ var column_maps = response.columnMaps;
+ var natural_column;
+ for (var natural_phid in column_maps) {
+ natural_column = this.getColumn(natural_phid);
+ if (!natural_column) {
+ // Our view of the board may be out of date, so we might get back
+ // information about columns that aren't visible. Just ignore the
+ // position information for any columns we aren't displaying on the
+ // client.
+ continue;
+ }
+
+ natural_column.setNaturalOrder(column_maps[natural_phid]);
+ }
+
var headers = response.headers;
for (var jj = 0; jj < headers.length; jj++) {
var header = headers[jj];
@@ -634,22 +697,6 @@
.setEditProperties(header.editProperties);
}
- for (var column_phid in columns) {
- var column = columns[column_phid];
-
- var cards = column.getCards();
- for (var object_phid in cards) {
- if (object_phid !== phid) {
- continue;
- }
-
- var card = cards[object_phid];
- card.redraw();
-
- column.markForRedraw();
- }
- }
-
this._redrawColumns();
},
@@ -660,6 +707,19 @@
columns[k].redraw();
}
}
+ },
+
+ _reloadCards: function() {
+ var data = {};
+ var on_reload = JX.bind(this, this._onReloadResponse);
+
+ new JX.Request(this.getController().getReloadURI(), on_reload)
+ .setData(data)
+ .send();
+ },
+
+ _onReloadResponse: function(response) {
+ this.updateCard(response);
}
}
diff --git a/webroot/rsrc/js/application/projects/WorkboardController.js b/webroot/rsrc/js/application/projects/WorkboardController.js
--- a/webroot/rsrc/js/application/projects/WorkboardController.js
+++ b/webroot/rsrc/js/application/projects/WorkboardController.js
@@ -21,6 +21,7 @@
uploadURI: null,
coverURI: null,
moveURI: null,
+ reloadURI: null,
chunkThreshold: null
},
diff --git a/webroot/rsrc/js/application/projects/behavior-project-boards.js b/webroot/rsrc/js/application/projects/behavior-project-boards.js
--- a/webroot/rsrc/js/application/projects/behavior-project-boards.js
+++ b/webroot/rsrc/js/application/projects/behavior-project-boards.js
@@ -71,6 +71,7 @@
.setUploadURI(config.uploadURI)
.setCoverURI(config.coverURI)
.setMoveURI(config.moveURI)
+ .setReloadURI(config.reloadURI)
.setChunkThreshold(config.chunkThreshold)
.start();
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 22, 4:42 PM (2 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7717223
Default Alt Text
D20639.id49225.diff (23 KB)
Attached To
Mode
D20639: Make pressing "R" on your keyboard reload the card state on workboards
Attached
Detach File
Event Timeline
Log In to Comment