1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-19 16:58:48 +02:00

Move "BoardResponseEngine" toward a more comprehensive update model

Summary:
Depends on D20639. Ref T4900. Currently, "BoardResponseEngine" has a `setObjectPHID()` method. This is called after edit operations to mean "we just edited object X, so we know it needs to be updated".

Move toward `setUpdatePHIDs(...)` in all cases, with `setUpdatePHIDs(array(the-object-we-just-edited))` as a special case of that. After this change, callers pass:

  - An optional list of PHIDs they know need to be updated on the client. Today, this is always be a card we just edited (on edit/move flows), or a sort of made-up list of PHIDs for the moment (when you press "R"). In the future, the "R" endpoint will do a better job of figuring out a more realistic update set.
  - An optional list of PHIDs currently visible on the client. This is used to update ordering details and mark cards for removal. This is currently passed by edit/move, but not by pressing "R" (it will be in the future).
  - An optional list of objects. The "R" workflow has to load these anyway, so we can save a couple queries by letting callers pass them. For now, the edit/move flows still rely on the engine to figure out what it needs to load.

This does very little to actually change client behavior, it mostly just paves the way for the next update to the "R" workflow to make it handle add/remove cases properly.

Test Plan:
  - Edited and moved cards on a workboard.
  - Pressed "R" to reload a workboard.

Neither of these operations seem any worse off than they were before. They still don't fully work:

  - When you edit a card and delete the current workboard project from it, it remains visible. This is also the behavior on `master`. This is sort of intentional since we don't necessarily want to make these cards suddenly disappear? Ideally, we would probably have some kind of "tombstone" state where the card can still be edited but can't be dragged, and the next explicit user interaction would clean up old tombstones. This interaction is very rare and I don't think it's particularly important to specialize.
  - When a card is removed from the board, "R" can't currently figure out that it should be removed from the client. This is because the client does not yet pass a "visiblePHIDs" state. It will in an upcoming change.
  - The "R" flow always sends a full set of card updates, and can not yet detect that some cards have not changed.
  - There's a TODO, but some ordering stuff isn't handled yet.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T4900

Differential Revision: https://secure.phabricator.com/D20652
This commit is contained in:
epriestley 2019-07-17 10:12:39 -07:00
parent db69686927
commit 1ee6ecf397
6 changed files with 91 additions and 63 deletions

View file

@ -412,7 +412,7 @@ return array(
'rsrc/js/application/phortune/phortune-credit-card-form.js' => 'd12d214f', '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-control.js' => '0eaa33a9',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172', 'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172',
'rsrc/js/application/projects/WorkboardBoard.js' => '34c2f539', 'rsrc/js/application/projects/WorkboardBoard.js' => '46573d65',
'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8', 'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8',
'rsrc/js/application/projects/WorkboardCardTemplate.js' => '2a61f8d4', 'rsrc/js/application/projects/WorkboardCardTemplate.js' => '2a61f8d4',
'rsrc/js/application/projects/WorkboardColumn.js' => 'c3d24e63', 'rsrc/js/application/projects/WorkboardColumn.js' => 'c3d24e63',
@ -743,7 +743,7 @@ return array(
'javelin-view-renderer' => '9aae2b66', 'javelin-view-renderer' => '9aae2b66',
'javelin-view-visitor' => '308f9fe4', 'javelin-view-visitor' => '308f9fe4',
'javelin-websocket' => 'fdc13e4e', 'javelin-websocket' => 'fdc13e4e',
'javelin-workboard-board' => '34c2f539', 'javelin-workboard-board' => '46573d65',
'javelin-workboard-card' => '0392a5d8', 'javelin-workboard-card' => '0392a5d8',
'javelin-workboard-card-template' => '2a61f8d4', 'javelin-workboard-card-template' => '2a61f8d4',
'javelin-workboard-column' => 'c3d24e63', 'javelin-workboard-column' => 'c3d24e63',
@ -1202,18 +1202,6 @@ return array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',
), ),
'34c2f539' => 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',
),
'34c53422' => array( '34c53422' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1304,6 +1292,18 @@ return array(
'javelin-util', 'javelin-util',
'phabricator-busy', 'phabricator-busy',
), ),
'46573d65' => 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',
),
'47a0728b' => array( '47a0728b' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',

View file

@ -434,7 +434,7 @@ EODOCS
$engine = id(new PhabricatorBoardResponseEngine()) $engine = id(new PhabricatorBoardResponseEngine())
->setViewer($viewer) ->setViewer($viewer)
->setBoardPHID($board_phid) ->setBoardPHID($board_phid)
->setObjectPHID($object_phid) ->setUpdatePHIDs(array($object_phid))
->setVisiblePHIDs($visible_phids); ->setVisiblePHIDs($visible_phids);
if ($ordering) { if ($ordering) {

View file

@ -25,6 +25,7 @@ final class PhabricatorProjectBoardReloadController
$engine = id(new PhabricatorBoardResponseEngine()) $engine = id(new PhabricatorBoardResponseEngine())
->setViewer($viewer) ->setViewer($viewer)
->setBoardPHID($board_phid) ->setBoardPHID($board_phid)
->setObjects($objects)
->setUpdatePHIDs($object_phids); ->setUpdatePHIDs($object_phids);
// TODO: We don't currently process "order" properly. If a user is viewing // TODO: We don't currently process "order" properly. If a user is viewing

View file

@ -184,7 +184,7 @@ abstract class PhabricatorProjectController extends PhabricatorController {
$engine = id(new PhabricatorBoardResponseEngine()) $engine = id(new PhabricatorBoardResponseEngine())
->setViewer($viewer) ->setViewer($viewer)
->setBoardPHID($board_phid) ->setBoardPHID($board_phid)
->setObjectPHID($object_phid) ->setUpdatePHIDs(array($object_phid))
->setVisiblePHIDs($visible_phids) ->setVisiblePHIDs($visible_phids)
->setSounds($sounds); ->setSounds($sounds);

View file

@ -3,9 +3,9 @@
final class PhabricatorBoardResponseEngine extends Phobject { final class PhabricatorBoardResponseEngine extends Phobject {
private $viewer; private $viewer;
private $objects;
private $boardPHID; private $boardPHID;
private $objectPHID; private $visiblePHIDs = array();
private $visiblePHIDs;
private $updatePHIDs = array(); private $updatePHIDs = array();
private $ordering; private $ordering;
private $sounds; private $sounds;
@ -28,13 +28,13 @@ final class PhabricatorBoardResponseEngine extends Phobject {
return $this->boardPHID; return $this->boardPHID;
} }
public function setObjectPHID($object_phid) { public function setObjects(array $objects) {
$this->objectPHID = $object_phid; $this->objects = $objects;
return $this; return $this;
} }
public function getObjectPHID() { public function getObjects() {
return $this->objectPHID; return $this->objects;
} }
public function setVisiblePHIDs(array $visible_phids) { public function setVisiblePHIDs(array $visible_phids) {
@ -75,13 +75,30 @@ final class PhabricatorBoardResponseEngine extends Phobject {
public function buildResponse() { public function buildResponse() {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$object_phid = $this->getObjectPHID();
$board_phid = $this->getBoardPHID(); $board_phid = $this->getBoardPHID();
$ordering = $this->getOrdering(); $ordering = $this->getOrdering();
$update_phids = $this->getUpdatePHIDs();
$update_phids = array_fuse($update_phids);
$visible_phids = $this->getVisiblePHIDs();
$visible_phids = array_fuse($visible_phids);
$all_phids = $update_phids + $visible_phids;
// Load all the other tasks that are visible in the affected columns and // Load all the other tasks that are visible in the affected columns and
// perform layout for them. // perform layout for them.
$all_phids = $this->getAllVisiblePHIDs();
if ($this->objects !== null) {
$all_objects = $this->getObjects();
$all_objects = mpull($all_objects, null, 'getPHID');
} else {
$all_objects = id(new ManiphestTaskQuery())
->setViewer($viewer)
->withPHIDs($all_phids)
->execute();
$all_objects = mpull($all_objects, null, 'getPHID');
}
$layout_engine = id(new PhabricatorBoardLayoutEngine()) $layout_engine = id(new PhabricatorBoardLayoutEngine())
->setViewer($viewer) ->setViewer($viewer)
@ -91,7 +108,6 @@ final class PhabricatorBoardResponseEngine extends Phobject {
$natural = array(); $natural = array();
$update_phids = $this->getAllUpdatePHIDs();
$update_columns = array(); $update_columns = array();
foreach ($update_phids as $update_phid) { foreach ($update_phids as $update_phid) {
$update_columns += $layout_engine->getObjectColumns( $update_columns += $layout_engine->getObjectColumns(
@ -106,12 +122,6 @@ final class PhabricatorBoardResponseEngine extends Phobject {
$natural[$column_phid] = array_values($column_object_phids); $natural[$column_phid] = array_values($column_object_phids);
} }
$all_objects = id(new ManiphestTaskQuery())
->setViewer($viewer)
->withPHIDs($all_phids)
->execute();
$all_objects = mpull($all_objects, null, 'getPHID');
if ($ordering) { if ($ordering) {
$vectors = $ordering->getSortVectorsForObjects($all_objects); $vectors = $ordering->getSortVectorsForObjects($all_objects);
$header_keys = $ordering->getHeaderKeysForObjects($all_objects); $header_keys = $ordering->getHeaderKeysForObjects($all_objects);
@ -164,6 +174,17 @@ final class PhabricatorBoardResponseEngine extends Phobject {
$cards[$card_phid] = $card; $cards[$card_phid] = $card;
} }
// Mark cards which are currently visible on the client but not visible
// on the board on the server for removal from the client view of the
// board state.
foreach ($visible_phids as $card_phid) {
if (!isset($cards[$card_phid])) {
$cards[$card_phid] = array(
'remove' => true,
);
}
}
$payload = array( $payload = array(
'columnMaps' => $natural, 'columnMaps' => $natural,
'cards' => $cards, 'cards' => $cards,
@ -202,44 +223,26 @@ final class PhabricatorBoardResponseEngine extends Phobject {
return array_fuse($exclude_phids); return array_fuse($exclude_phids);
} }
private function getAllVisiblePHIDs() {
$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() { private function newCardTemplates() {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$update_phids = $this->getAllUpdatePHIDs(); $update_phids = $this->getUpdatePHIDs();
if (!$update_phids) { if (!$update_phids) {
return array(); return array();
} }
$update_phids = array_fuse($update_phids);
$objects = id(new ManiphestTaskQuery()) if ($this->objects === null) {
->setViewer($viewer) $objects = id(new ManiphestTaskQuery())
->withPHIDs($update_phids) ->setViewer($viewer)
->needProjectPHIDs(true) ->withPHIDs($update_phids)
->execute(); ->needProjectPHIDs(true)
->execute();
} else {
$objects = $this->getObjects();
$objects = mpull($objects, null, 'getPHID');
$objects = array_select_keys($objects, $update_phids);
}
if (!$objects) { if (!$objects) {
return array(); return array();

View file

@ -611,10 +611,35 @@ JX.install('WorkboardBoard', {
} }
} }
// Process card removals. These are cases where the client still sees
// a particular card on a board but it has been removed on the server.
for (card_phid in response.cards) {
card_data = response.cards[card_phid];
if (!card_data.remove) {
continue;
}
for (column_phid in columns) {
var column = columns[column_phid];
var card = column.getCard(card_phid);
if (card) {
column.removeCard(card_phid);
column.markForRedraw();
}
}
}
// Process partial updates for cards. This is supplemental data which // Process partial updates for cards. This is supplemental data which
// we can just merge in without any special handling. // we can just merge in without any special handling.
for (card_phid in response.cards) { for (card_phid in response.cards) {
card_data = response.cards[card_phid]; card_data = response.cards[card_phid];
if (card_data.remove) {
continue;
}
var card_template = this.getCardTemplate(card_phid); var card_template = this.getCardTemplate(card_phid);
if (card_data.nodeHTMLTemplate) { if (card_data.nodeHTMLTemplate) {
@ -635,7 +660,6 @@ JX.install('WorkboardBoard', {
} }
} }
// Process full updates for cards which we have a full update for. This // Process full updates for cards which we have a full update for. This
// may involve moving them between columns. // may involve moving them between columns.
for (card_phid in response.cards) { for (card_phid in response.cards) {