mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
Make pressing "R" on your keyboard reload the card state on workboards
Summary: Depends on D20638. Ref T4900. This is an incremental step toward proper workboard updates. Currently, the client can mostly update its view because we do updates when you edit or move a card, and the client and server know how to send lists of card updates, so a lot of the work is already done. However, the code assumes we're only updating/redrawing one card at a time. Make the client accept and process multiple card updates. In future changes, I'll add versioning (so we only update cards that have actually changed), fix the "TODO" around ordering, and move toward actual Aphlict-based real-time updates. Test Plan: - Opened the same workboard in two windows. - Edited cards in one window, pressed "R" (capital letter, with no modifier keys) to reload the second window. - Saw edits and moves reflected accurately after sync, except for some special cases of header/order interaction (see "TODO"). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T4900 Differential Revision: https://secure.phabricator.com/D20639
This commit is contained in:
parent
97c1699756
commit
db69686927
9 changed files with 283 additions and 129 deletions
|
@ -412,16 +412,16 @@ 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' => '44f71637',
|
'rsrc/js/application/projects/WorkboardBoard.js' => '34c2f539',
|
||||||
'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',
|
||||||
'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/WorkboardDropEffect.js' => '8e0aa661',
|
||||||
'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d',
|
'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d',
|
||||||
'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'ebe83a6b',
|
'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'ebe83a6b',
|
||||||
'rsrc/js/application/projects/WorkboardOrderTemplate.js' => '03e8891f',
|
'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-project-create.js' => '34c53422',
|
||||||
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
|
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
|
||||||
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
|
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
|
||||||
|
@ -667,7 +667,7 @@ return array(
|
||||||
'javelin-behavior-phuix-example' => 'c2c500a7',
|
'javelin-behavior-phuix-example' => 'c2c500a7',
|
||||||
'javelin-behavior-policy-control' => '0eaa33a9',
|
'javelin-behavior-policy-control' => '0eaa33a9',
|
||||||
'javelin-behavior-policy-rule-editor' => '9347f172',
|
'javelin-behavior-policy-rule-editor' => '9347f172',
|
||||||
'javelin-behavior-project-boards' => 'aad45445',
|
'javelin-behavior-project-boards' => '58cb6a88',
|
||||||
'javelin-behavior-project-create' => '34c53422',
|
'javelin-behavior-project-create' => '34c53422',
|
||||||
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
|
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
|
||||||
'javelin-behavior-read-only-warning' => 'b9109f8f',
|
'javelin-behavior-read-only-warning' => 'b9109f8f',
|
||||||
|
@ -743,11 +743,11 @@ 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' => '44f71637',
|
'javelin-workboard-board' => '34c2f539',
|
||||||
'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',
|
||||||
'javelin-workboard-controller' => '42c7a5a7',
|
'javelin-workboard-controller' => 'b9d0c2f3',
|
||||||
'javelin-workboard-drop-effect' => '8e0aa661',
|
'javelin-workboard-drop-effect' => '8e0aa661',
|
||||||
'javelin-workboard-header' => '111bfd2d',
|
'javelin-workboard-header' => '111bfd2d',
|
||||||
'javelin-workboard-header-template' => 'ebe83a6b',
|
'javelin-workboard-header-template' => 'ebe83a6b',
|
||||||
|
@ -1202,6 +1202,18 @@ 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',
|
||||||
|
@ -1264,16 +1276,6 @@ return array(
|
||||||
'4234f572' => array(
|
'4234f572' => array(
|
||||||
'syntax-default-css',
|
'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(
|
'4370900d' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -1294,18 +1296,6 @@ return array(
|
||||||
'43bc9360' => array(
|
'43bc9360' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
'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(
|
'46116c01' => array(
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
|
@ -1424,6 +1414,16 @@ return array(
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
'javelin-typeahead-static-source',
|
'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(
|
'5902260c' => array(
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
'javelin-magical-init',
|
'javelin-magical-init',
|
||||||
|
@ -1852,16 +1852,6 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-util',
|
'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(
|
'ab85e184' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1952,6 +1942,16 @@ return array(
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
'phabricator-notification',
|
'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',
|
||||||
|
),
|
||||||
'bde53589' => array(
|
'bde53589' => array(
|
||||||
'phui-inline-comment-view-css',
|
'phui-inline-comment-view-css',
|
||||||
),
|
),
|
||||||
|
|
|
@ -4163,6 +4163,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectBoardFilterController' => 'applications/project/controller/PhabricatorProjectBoardFilterController.php',
|
'PhabricatorProjectBoardFilterController' => 'applications/project/controller/PhabricatorProjectBoardFilterController.php',
|
||||||
'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php',
|
'PhabricatorProjectBoardImportController' => 'applications/project/controller/PhabricatorProjectBoardImportController.php',
|
||||||
'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php',
|
'PhabricatorProjectBoardManageController' => 'applications/project/controller/PhabricatorProjectBoardManageController.php',
|
||||||
|
'PhabricatorProjectBoardReloadController' => 'applications/project/controller/PhabricatorProjectBoardReloadController.php',
|
||||||
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
|
'PhabricatorProjectBoardReorderController' => 'applications/project/controller/PhabricatorProjectBoardReorderController.php',
|
||||||
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
|
'PhabricatorProjectBoardViewController' => 'applications/project/controller/PhabricatorProjectBoardViewController.php',
|
||||||
'PhabricatorProjectBuiltinsExample' => 'applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php',
|
'PhabricatorProjectBuiltinsExample' => 'applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php',
|
||||||
|
@ -10431,6 +10432,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectBoardFilterController' => 'PhabricatorProjectBoardController',
|
'PhabricatorProjectBoardFilterController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController',
|
'PhabricatorProjectBoardImportController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController',
|
'PhabricatorProjectBoardManageController' => 'PhabricatorProjectBoardController',
|
||||||
|
'PhabricatorProjectBoardReloadController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
|
'PhabricatorProjectBoardReorderController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
|
'PhabricatorProjectBoardViewController' => 'PhabricatorProjectBoardController',
|
||||||
'PhabricatorProjectBuiltinsExample' => 'PhabricatorUIExample',
|
'PhabricatorProjectBuiltinsExample' => 'PhabricatorUIExample',
|
||||||
|
|
|
@ -99,6 +99,8 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
|
||||||
=> 'PhabricatorProjectBoardDefaultController',
|
=> 'PhabricatorProjectBoardDefaultController',
|
||||||
'filter/(?:query/(?P<queryKey>[^/]+)/)?'
|
'filter/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||||
=> 'PhabricatorProjectBoardFilterController',
|
=> 'PhabricatorProjectBoardFilterController',
|
||||||
|
'reload/'
|
||||||
|
=> 'PhabricatorProjectBoardReloadController',
|
||||||
),
|
),
|
||||||
'column/' => array(
|
'column/' => array(
|
||||||
'remove/(?P<id>\d+)/' =>
|
'remove/(?P<id>\d+)/' =>
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -286,6 +286,7 @@ final class PhabricatorProjectBoardViewController
|
||||||
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
|
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
|
||||||
'uploadURI' => '/file/dropupload/',
|
'uploadURI' => '/file/dropupload/',
|
||||||
'coverURI' => $this->getApplicationURI('cover/'),
|
'coverURI' => $this->getApplicationURI('cover/'),
|
||||||
|
'reloadURI' => phutil_string_cast($state->newWorkboardURI('reload/')),
|
||||||
'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
|
'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
|
||||||
'pointsEnabled' => ManiphestTaskPoints::getIsEnabled(),
|
'pointsEnabled' => ManiphestTaskPoints::getIsEnabled(),
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
private $boardPHID;
|
private $boardPHID;
|
||||||
private $objectPHID;
|
private $objectPHID;
|
||||||
private $visiblePHIDs;
|
private $visiblePHIDs;
|
||||||
|
private $updatePHIDs = array();
|
||||||
private $ordering;
|
private $ordering;
|
||||||
private $sounds;
|
private $sounds;
|
||||||
|
|
||||||
|
@ -45,6 +46,15 @@ final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
return $this->visiblePHIDs;
|
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) {
|
public function setOrdering(PhabricatorProjectColumnOrder $ordering) {
|
||||||
$this->ordering = $ordering;
|
$this->ordering = $ordering;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -71,36 +81,41 @@ final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
|
|
||||||
// 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.
|
||||||
$visible_phids = $this->getAllVisiblePHIDs();
|
$all_phids = $this->getAllVisiblePHIDs();
|
||||||
|
|
||||||
$layout_engine = id(new PhabricatorBoardLayoutEngine())
|
$layout_engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->setBoardPHIDs(array($board_phid))
|
->setBoardPHIDs(array($board_phid))
|
||||||
->setObjectPHIDs($visible_phids)
|
->setObjectPHIDs($all_phids)
|
||||||
->executeLayout();
|
->executeLayout();
|
||||||
|
|
||||||
$object_columns = $layout_engine->getObjectColumns(
|
|
||||||
$board_phid,
|
|
||||||
$object_phid);
|
|
||||||
|
|
||||||
$natural = array();
|
$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(
|
$column_object_phids = $layout_engine->getColumnObjectPHIDs(
|
||||||
$board_phid,
|
$board_phid,
|
||||||
$column_phid);
|
$column_phid);
|
||||||
$natural[$column_phid] = array_values($column_object_phids);
|
$natural[$column_phid] = array_values($column_object_phids);
|
||||||
}
|
}
|
||||||
|
|
||||||
$all_visible = id(new ManiphestTaskQuery())
|
$all_objects = id(new ManiphestTaskQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs($visible_phids)
|
->withPHIDs($all_phids)
|
||||||
->execute();
|
->execute();
|
||||||
$all_visible = mpull($all_visible, null, 'getPHID');
|
$all_objects = mpull($all_objects, null, 'getPHID');
|
||||||
|
|
||||||
if ($ordering) {
|
if ($ordering) {
|
||||||
$vectors = $ordering->getSortVectorsForObjects($all_visible);
|
$vectors = $ordering->getSortVectorsForObjects($all_objects);
|
||||||
$header_keys = $ordering->getHeaderKeysForObjects($all_visible);
|
$header_keys = $ordering->getHeaderKeysForObjects($all_objects);
|
||||||
$headers = $ordering->getHeadersForObjects($all_visible);
|
$headers = $ordering->getHeadersForObjects($all_objects);
|
||||||
$headers = mpull($headers, 'toDictionary');
|
$headers = mpull($headers, 'toDictionary');
|
||||||
} else {
|
} else {
|
||||||
$vectors = array();
|
$vectors = array();
|
||||||
|
@ -108,19 +123,10 @@ final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
$headers = array();
|
$headers = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$object = id(new ManiphestTaskQuery())
|
$templates = $this->newCardTemplates();
|
||||||
->setViewer($viewer)
|
|
||||||
->withPHIDs(array($object_phid))
|
|
||||||
->needProjectPHIDs(true)
|
|
||||||
->executeOne();
|
|
||||||
if (!$object) {
|
|
||||||
return new Aphront404Response();
|
|
||||||
}
|
|
||||||
|
|
||||||
$template = $this->buildTemplate($object);
|
|
||||||
|
|
||||||
$cards = array();
|
$cards = array();
|
||||||
foreach ($all_visible as $card_phid => $object) {
|
foreach ($all_objects as $card_phid => $object) {
|
||||||
$card = array(
|
$card = array(
|
||||||
'vectors' => array(),
|
'vectors' => array(),
|
||||||
'headers' => array(),
|
'headers' => array(),
|
||||||
|
@ -144,8 +150,11 @@ final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
$card['properties'] = self::newTaskProperties($object);
|
$card['properties'] = self::newTaskProperties($object);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($card_phid === $object_phid) {
|
if (isset($templates[$card_phid])) {
|
||||||
$card['nodeHTMLTemplate'] = hsprintf('%s', $template);
|
$card['nodeHTMLTemplate'] = hsprintf('%s', $templates[$card_phid]);
|
||||||
|
$card['update'] = true;
|
||||||
|
} else {
|
||||||
|
$card['update'] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$card['vectors'] = (object)$card['vectors'];
|
$card['vectors'] = (object)$card['vectors'];
|
||||||
|
@ -156,7 +165,6 @@ final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
$payload = array(
|
$payload = array(
|
||||||
'objectPHID' => $object_phid,
|
|
||||||
'columnMaps' => $natural,
|
'columnMaps' => $natural,
|
||||||
'cards' => $cards,
|
'cards' => $cards,
|
||||||
'headers' => $headers,
|
'headers' => $headers,
|
||||||
|
@ -176,22 +184,6 @@ final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
private function loadExcludedProjectPHIDs() {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
$board_phid = $this->getBoardPHID();
|
$board_phid = $this->getBoardPHID();
|
||||||
|
@ -211,10 +203,67 @@ final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getAllVisiblePHIDs() {
|
private function getAllVisiblePHIDs() {
|
||||||
$visible_phids = $this->getVisiblePHIDs();
|
$phids = $this->getAllUpdatePHIDs();
|
||||||
$visible_phids[] = $this->getObjectPHID();
|
|
||||||
$visible_phids = array_fuse($visible_phids);
|
foreach ($this->getVisiblePHIDs() as $phid) {
|
||||||
return $visible_phids;
|
$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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,13 @@ JX.install('WorkboardBoard', {
|
||||||
start: function() {
|
start: function() {
|
||||||
this._setupDragHandlers();
|
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) {
|
for (var k in this._columns) {
|
||||||
this._columns[k].redraw();
|
this._columns[k].redraw();
|
||||||
}
|
}
|
||||||
|
@ -551,15 +558,6 @@ JX.install('WorkboardBoard', {
|
||||||
},
|
},
|
||||||
|
|
||||||
_oncardupdate: function(list, src_phid, dst_phid, after_phid, response) {
|
_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);
|
this.updateCard(response);
|
||||||
|
|
||||||
var sounds = response.sounds || [];
|
var sounds = response.sounds || [];
|
||||||
|
@ -572,37 +570,51 @@ JX.install('WorkboardBoard', {
|
||||||
|
|
||||||
updateCard: function(response) {
|
updateCard: function(response) {
|
||||||
var columns = this.getColumns();
|
var columns = this.getColumns();
|
||||||
|
var column_phid;
|
||||||
|
var card_phid;
|
||||||
|
var card_data;
|
||||||
|
|
||||||
var phid = response.objectPHID;
|
// 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.
|
||||||
|
|
||||||
for (var add_phid in response.columnMaps) {
|
// For a partial update, we've just received supplemental sorting or
|
||||||
var target_column = this.getColumn(add_phid);
|
// property information and do not need to perform a full redraw.
|
||||||
|
|
||||||
|
// When we reload card state, edit a card, or move a card, we get a full
|
||||||
|
// update for the card.
|
||||||
|
|
||||||
|
// Ween 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 (!target_column) {
|
||||||
// If the column isn't visible, don't try to add a card to it.
|
// If the column isn't visible, don't try to add a card to it.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_column.newCard(phid);
|
var column_map = response.columnMaps[column_phid];
|
||||||
}
|
|
||||||
|
|
||||||
var column_maps = response.columnMaps;
|
for (var ii = 0; ii < column_map.length; ii++) {
|
||||||
var natural_column;
|
card_phid = column_map[ii];
|
||||||
for (var natural_phid in column_maps) {
|
if (!update_map[card_phid]) {
|
||||||
natural_column = this.getColumn(natural_phid);
|
update_map[card_phid] = {};
|
||||||
if (!natural_column) {
|
}
|
||||||
// Our view of the board may be out of date, so we might get back
|
update_map[card_phid][column_phid] = true;
|
||||||
// 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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var card_phid in response.cards) {
|
// Process partial updates for cards. This is supplemental data which
|
||||||
var card_data = response.cards[card_phid];
|
// 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);
|
var card_template = this.getCardTemplate(card_phid);
|
||||||
|
|
||||||
if (card_data.nodeHTMLTemplate) {
|
if (card_data.nodeHTMLTemplate) {
|
||||||
|
@ -623,6 +635,57 @@ JX.install('WorkboardBoard', {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
var headers = response.headers;
|
||||||
for (var jj = 0; jj < headers.length; jj++) {
|
for (var jj = 0; jj < headers.length; jj++) {
|
||||||
var header = headers[jj];
|
var header = headers[jj];
|
||||||
|
@ -634,22 +697,6 @@ JX.install('WorkboardBoard', {
|
||||||
.setEditProperties(header.editProperties);
|
.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();
|
this._redrawColumns();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -660,6 +707,19 @@ JX.install('WorkboardBoard', {
|
||||||
columns[k].redraw();
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ JX.install('WorkboardController', {
|
||||||
uploadURI: null,
|
uploadURI: null,
|
||||||
coverURI: null,
|
coverURI: null,
|
||||||
moveURI: null,
|
moveURI: null,
|
||||||
|
reloadURI: null,
|
||||||
chunkThreshold: null
|
chunkThreshold: null
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ JX.behavior('project-boards', function(config, statics) {
|
||||||
.setUploadURI(config.uploadURI)
|
.setUploadURI(config.uploadURI)
|
||||||
.setCoverURI(config.coverURI)
|
.setCoverURI(config.coverURI)
|
||||||
.setMoveURI(config.moveURI)
|
.setMoveURI(config.moveURI)
|
||||||
|
.setReloadURI(config.reloadURI)
|
||||||
.setChunkThreshold(config.chunkThreshold)
|
.setChunkThreshold(config.chunkThreshold)
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue