mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-08 22:01:03 +01:00
Conpherence - paginate thread list
Summary: this is D5750 but just the conpherence part. fixes a few random conpherence bugs / quirks as well. Also messes with ApplicationTransactionEditor to expose the xactions so Conpherence doesn't over-update participation rows. Fixes T2429. Test Plan: set LIMIT to 3. verified I could scroll down all conpherences. next, picked a conpherence "in the middle" to load. verified I could page up and down. next, picked a conpherence in the middle then had another user update that conpherence. verified as I paged up the conpherence re-loaded properly selected Reviewers: epriestley Reviewed By: epriestley CC: chad, aran, Korvin, vrana Maniphest Tasks: T2429 Differential Revision: https://secure.phabricator.com/D5783
This commit is contained in:
parent
664fe7ef73
commit
11cb2f4f6c
14 changed files with 574 additions and 197 deletions
4
resources/sql/patches/20130423.conpherenceindices.sql
Normal file
4
resources/sql/patches/20130423.conpherenceindices.sql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
|
||||||
|
DROP KEY participantPHID,
|
||||||
|
ADD KEY unreadCount (participantPHID, participationStatus),
|
||||||
|
ADD KEY participationIndex (participantPHID, dateTouched, id);
|
|
@ -1289,7 +1289,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'javelin-behavior-conpherence-menu' =>
|
'javelin-behavior-conpherence-menu' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/ce7bfa44/rsrc/js/application/conpherence/behavior-menu.js',
|
'uri' => '/res/06bfc1a3/rsrc/js/application/conpherence/behavior-menu.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -1301,6 +1301,7 @@ celerity_register_resource_map(array(
|
||||||
5 => 'javelin-workflow',
|
5 => 'javelin-workflow',
|
||||||
6 => 'javelin-behavior-device',
|
6 => 'javelin-behavior-device',
|
||||||
7 => 'javelin-history',
|
7 => 'javelin-history',
|
||||||
|
8 => 'javelin-vector',
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/conpherence/behavior-menu.js',
|
'disk' => '/rsrc/js/application/conpherence/behavior-menu.js',
|
||||||
),
|
),
|
||||||
|
|
|
@ -238,6 +238,7 @@ phutil_register_library_map(array(
|
||||||
'ConpherenceMenuItemView' => 'applications/conpherence/view/ConpherenceMenuItemView.php',
|
'ConpherenceMenuItemView' => 'applications/conpherence/view/ConpherenceMenuItemView.php',
|
||||||
'ConpherenceNewController' => 'applications/conpherence/controller/ConpherenceNewController.php',
|
'ConpherenceNewController' => 'applications/conpherence/controller/ConpherenceNewController.php',
|
||||||
'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php',
|
'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php',
|
||||||
|
'ConpherenceParticipantCountQuery' => 'applications/conpherence/query/ConpherenceParticipantCountQuery.php',
|
||||||
'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php',
|
'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php',
|
||||||
'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php',
|
'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php',
|
||||||
'ConpherencePeopleMenuEventListener' => 'applications/conpherence/events/ConpherencePeopleMenuEventListener.php',
|
'ConpherencePeopleMenuEventListener' => 'applications/conpherence/events/ConpherencePeopleMenuEventListener.php',
|
||||||
|
@ -2001,6 +2002,7 @@ phutil_register_library_map(array(
|
||||||
'ConpherenceMenuItemView' => 'AphrontTagView',
|
'ConpherenceMenuItemView' => 'AphrontTagView',
|
||||||
'ConpherenceNewController' => 'ConpherenceController',
|
'ConpherenceNewController' => 'ConpherenceController',
|
||||||
'ConpherenceParticipant' => 'ConpherenceDAO',
|
'ConpherenceParticipant' => 'ConpherenceDAO',
|
||||||
|
'ConpherenceParticipantCountQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery',
|
'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
'ConpherenceParticipationStatus' => 'ConpherenceConstants',
|
'ConpherenceParticipationStatus' => 'ConpherenceConstants',
|
||||||
'ConpherencePeopleMenuEventListener' => 'PhutilEventListener',
|
'ConpherencePeopleMenuEventListener' => 'PhutilEventListener',
|
||||||
|
|
|
@ -6,62 +6,6 @@
|
||||||
abstract class ConpherenceController extends PhabricatorController {
|
abstract class ConpherenceController extends PhabricatorController {
|
||||||
private $conpherences;
|
private $conpherences;
|
||||||
|
|
||||||
/**
|
|
||||||
* Try for a full set of unread conpherences, and if we fail
|
|
||||||
* load read conpherences. Additional conpherences in either category
|
|
||||||
* are loaded asynchronously.
|
|
||||||
*/
|
|
||||||
public function loadStartingConpherences($current_selection_epoch = null) {
|
|
||||||
$user = $this->getRequest()->getUser();
|
|
||||||
|
|
||||||
$read_participant_query = id(new ConpherenceParticipantQuery())
|
|
||||||
->withParticipantPHIDs(array($user->getPHID()));
|
|
||||||
$read_status = ConpherenceParticipationStatus::UP_TO_DATE;
|
|
||||||
if ($current_selection_epoch) {
|
|
||||||
$read_one = $read_participant_query
|
|
||||||
->withParticipationStatus($read_status)
|
|
||||||
->withDateTouched($current_selection_epoch, '>')
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
$read_two = $read_participant_query
|
|
||||||
->withDateTouched($current_selection_epoch, '<=')
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
$read = array_merge($read_one, $read_two);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$read = $read_participant_query
|
|
||||||
->withParticipationStatus($read_status)
|
|
||||||
->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
$unread_status = ConpherenceParticipationStatus::BEHIND;
|
|
||||||
$unread = id(new ConpherenceParticipantQuery())
|
|
||||||
->withParticipantPHIDs(array($user->getPHID()))
|
|
||||||
->withParticipationStatus($unread_status)
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
$all_participation = $unread + $read;
|
|
||||||
$all_conpherence_phids = array_keys($all_participation);
|
|
||||||
$all_conpherences = array();
|
|
||||||
if ($all_conpherence_phids) {
|
|
||||||
$all_conpherences = id(new ConpherenceThreadQuery())
|
|
||||||
->setViewer($user)
|
|
||||||
->withPHIDs($all_conpherence_phids)
|
|
||||||
->needParticipantCache(true)
|
|
||||||
->execute();
|
|
||||||
}
|
|
||||||
$unread_conpherences = array_select_keys(
|
|
||||||
$all_conpherences,
|
|
||||||
array_keys($unread));
|
|
||||||
|
|
||||||
$read_conpherences = array_select_keys(
|
|
||||||
$all_conpherences,
|
|
||||||
array_keys($read));
|
|
||||||
|
|
||||||
return array($unread_conpherences, $read_conpherences);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildApplicationMenu() {
|
public function buildApplicationMenu() {
|
||||||
$nav = new PhabricatorMenuView();
|
$nav = new PhabricatorMenuView();
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
final class ConpherenceListController
|
final class ConpherenceListController
|
||||||
extends ConpherenceController {
|
extends ConpherenceController {
|
||||||
|
|
||||||
|
const SELECTED_MODE = 'selected';
|
||||||
|
const UNSELECTED_MODE = 'unselected';
|
||||||
|
const PAGING_MODE = 'paging';
|
||||||
|
|
||||||
private $conpherenceID;
|
private $conpherenceID;
|
||||||
|
|
||||||
public function setConpherenceID($conpherence_id) {
|
public function setConpherenceID($conpherence_id) {
|
||||||
|
@ -20,59 +24,227 @@ final class ConpherenceListController
|
||||||
$this->setConpherenceID(idx($data, 'id'));
|
$this->setConpherenceID(idx($data, 'id'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Three main modes of operation...
|
||||||
|
*
|
||||||
|
* 1 - /conpherence/ - UNSELECTED_MODE
|
||||||
|
* 2 - /conpherence/<id>/ - SELECTED_MODE
|
||||||
|
* 3 - /conpherence/?direction='up'&... - PAGING_MODE
|
||||||
|
*
|
||||||
|
* UNSELECTED_MODE is not an Ajax request while the other two are Ajax
|
||||||
|
* requests.
|
||||||
|
*/
|
||||||
|
private function determineMode() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
|
||||||
|
$mode = self::UNSELECTED_MODE;
|
||||||
|
if ($request->isAjax()) {
|
||||||
|
if ($request->getStr('direction')) {
|
||||||
|
$mode = self::PAGING_MODE;
|
||||||
|
} else {
|
||||||
|
$mode = self::SELECTED_MODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mode;
|
||||||
|
}
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$user = $request->getUser();
|
$user = $request->getUser();
|
||||||
$title = pht('Conpherence');
|
$title = pht('Conpherence');
|
||||||
|
|
||||||
$conpherence_id = $this->getConpherenceID();
|
|
||||||
$current_selection_epoch = null;
|
|
||||||
$conpherence = null;
|
$conpherence = null;
|
||||||
if ($conpherence_id) {
|
|
||||||
$conpherence = id(new ConpherenceThreadQuery())
|
|
||||||
->setViewer($user)
|
|
||||||
->withIDs(array($conpherence_id))
|
|
||||||
->executeOne();
|
|
||||||
if (!$conpherence) {
|
|
||||||
return new Aphront404Response();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($conpherence->getTitle()) {
|
$scroll_up_participant = $this->getEmptyParticipant();
|
||||||
$title = $conpherence->getTitle();
|
$scroll_down_participant = $this->getEmptyParticipant();
|
||||||
}
|
$too_many = ConpherenceParticipantQuery::LIMIT + 1;
|
||||||
|
$all_participation = array();
|
||||||
|
|
||||||
$participant = $conpherence->getParticipant($user->getPHID());
|
$mode = $this->determineMode();
|
||||||
$current_selection_epoch = $participant->getDateTouched();
|
switch ($mode) {
|
||||||
|
case self::SELECTED_MODE:
|
||||||
|
$conpherence_id = $this->getConpherenceID();
|
||||||
|
$conpherence = id(new ConpherenceThreadQuery())
|
||||||
|
->setViewer($user)
|
||||||
|
->withIDs(array($conpherence_id))
|
||||||
|
->executeOne();
|
||||||
|
if (!$conpherence) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
if ($conpherence->getTitle()) {
|
||||||
|
$title = $conpherence->getTitle();
|
||||||
|
}
|
||||||
|
$cursor = $conpherence->getParticipant($user->getPHID());
|
||||||
|
$data = $this->loadParticipationWithMidCursor($cursor);
|
||||||
|
$all_participation = $data['participation'];
|
||||||
|
$scroll_up_participant = $data['scroll_up_participant'];
|
||||||
|
$scroll_down_participant = $data['scroll_down_participant'];
|
||||||
|
break;
|
||||||
|
case self::PAGING_MODE:
|
||||||
|
$direction = $request->getStr('direction');
|
||||||
|
$id = $request->getInt('participant_id');
|
||||||
|
$date_touched = $request->getInt('date_touched');
|
||||||
|
$conpherence_phid = $request->getStr('conpherence_phid');
|
||||||
|
if ($direction == 'up') {
|
||||||
|
$order = ConpherenceParticipantQuery::ORDER_NEWER;
|
||||||
|
} else {
|
||||||
|
$order = ConpherenceParticipantQuery::ORDER_OLDER;
|
||||||
|
}
|
||||||
|
$scroller_participant = id(new ConpherenceParticipant())
|
||||||
|
->makeEphemeral()
|
||||||
|
->setID($id)
|
||||||
|
->setDateTouched($date_touched)
|
||||||
|
->setConpherencePHID($conpherence_phid);
|
||||||
|
$participation = id(new ConpherenceParticipantQuery())
|
||||||
|
->withParticipantPHIDs(array($user->getPHID()))
|
||||||
|
->withParticipantCursor($scroller_participant)
|
||||||
|
->setOrder($order)
|
||||||
|
->setLimit($too_many)
|
||||||
|
->execute();
|
||||||
|
if (count($participation) == $too_many) {
|
||||||
|
if ($direction == 'up') {
|
||||||
|
$node = $scroll_up_participant = reset($participation);
|
||||||
|
} else {
|
||||||
|
$node = $scroll_down_participant = end($participation);
|
||||||
|
}
|
||||||
|
unset($participation[$node->getConpherencePHID()]);
|
||||||
|
}
|
||||||
|
$all_participation = $participation;
|
||||||
|
break;
|
||||||
|
case self::UNSELECTED_MODE:
|
||||||
|
default:
|
||||||
|
$too_many = ConpherenceParticipantQuery::LIMIT + 1;
|
||||||
|
$all_participation = id(new ConpherenceParticipantQuery())
|
||||||
|
->withParticipantPHIDs(array($user->getPHID()))
|
||||||
|
->setLimit($too_many)
|
||||||
|
->execute();
|
||||||
|
if (count($all_participation) == $too_many) {
|
||||||
|
$node = end($participation);
|
||||||
|
unset($all_participation[$node->getConpherencePHID()]);
|
||||||
|
$scroll_down_participant = $node;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
list($unread, $read) = $this->loadStartingConpherences(
|
$threads = $this->loadConpherenceThreadData(
|
||||||
$current_selection_epoch);
|
$all_participation);
|
||||||
|
|
||||||
$thread_view = id(new ConpherenceThreadListView())
|
$thread_view = id(new ConpherenceThreadListView())
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
->setBaseURI($this->getApplicationURI())
|
->setBaseURI($this->getApplicationURI())
|
||||||
->setUnreadThreads($unread)
|
->setThreads($threads)
|
||||||
->setReadThreads($read);
|
->setScrollUpParticipant($scroll_up_participant)
|
||||||
|
->setScrollDownParticipant($scroll_down_participant);
|
||||||
|
|
||||||
if ($request->isAjax()) {
|
switch ($mode) {
|
||||||
return id(new AphrontAjaxResponse())->setContent($thread_view);
|
case self::SELECTED_MODE:
|
||||||
|
$response = id(new AphrontAjaxResponse())->setContent($thread_view);
|
||||||
|
break;
|
||||||
|
case self::PAGING_MODE:
|
||||||
|
$thread_html = $thread_view->renderThreadsHTML();
|
||||||
|
$phids = array_keys($participation);
|
||||||
|
$content = array(
|
||||||
|
'html' => $thread_html,
|
||||||
|
'phids' => $phids);
|
||||||
|
$response = id(new AphrontAjaxResponse())->setContent($content);
|
||||||
|
break;
|
||||||
|
case self::UNSELECTED_MODE:
|
||||||
|
default:
|
||||||
|
$layout = id(new ConpherenceLayoutView())
|
||||||
|
->setBaseURI($this->getApplicationURI())
|
||||||
|
->setThreadView($thread_view)
|
||||||
|
->setRole('list');
|
||||||
|
if ($conpherence) {
|
||||||
|
$layout->setThread($conpherence);
|
||||||
|
}
|
||||||
|
$response = $this->buildApplicationPage(
|
||||||
|
$layout,
|
||||||
|
array(
|
||||||
|
'title' => $title,
|
||||||
|
'device' => true,
|
||||||
|
));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$layout = id(new ConpherenceLayoutView())
|
return $response;
|
||||||
->setBaseURI($this->getApplicationURI())
|
|
||||||
->setThreadView($thread_view)
|
|
||||||
->setRole('list');
|
|
||||||
|
|
||||||
if ($conpherence) {
|
}
|
||||||
$layout->setThread($conpherence);
|
|
||||||
|
/**
|
||||||
|
* Handles the curious case when we are visiting a conpherence directly
|
||||||
|
* by issuing two separate queries. Otherwise, additional conpherences
|
||||||
|
* are fetched asynchronously. Note these can be earlier or later
|
||||||
|
* (up or down), depending on what conpherence was selected on initial
|
||||||
|
* load.
|
||||||
|
*/
|
||||||
|
private function loadParticipationWithMidCursor(
|
||||||
|
ConpherenceParticipant $cursor) {
|
||||||
|
|
||||||
|
$user = $this->getRequest()->getUser();
|
||||||
|
|
||||||
|
$scroll_up_participant = $this->getEmptyParticipant();
|
||||||
|
$scroll_down_participant = $this->getEmptyParticipant();
|
||||||
|
|
||||||
|
// Note this is a bit dodgy since there may be less than this
|
||||||
|
// amount in either the up or down direction, thus having us fail
|
||||||
|
// to fetch LIMIT in total. Whatevs for now and re-visit if we're
|
||||||
|
// fine-tuning this loading process.
|
||||||
|
$too_many = ceil(ConpherenceParticipantQuery::LIMIT / 2) + 1;
|
||||||
|
$participant_query = id(new ConpherenceParticipantQuery())
|
||||||
|
->withParticipantPHIDs(array($user->getPHID()))
|
||||||
|
->setLimit($too_many);
|
||||||
|
$current_selection_epoch = $cursor->getDateTouched();
|
||||||
|
$set_one = $participant_query
|
||||||
|
->withParticipantCursor($cursor)
|
||||||
|
->setOrder(ConpherenceParticipantQuery::ORDER_NEWER)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
if (count($set_one) == $too_many) {
|
||||||
|
$node = reset($set_one);
|
||||||
|
unset($set_one[$node->getConpherencePHID()]);
|
||||||
|
$scroll_up_participant = $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->buildApplicationPage(
|
$set_two = $participant_query
|
||||||
$layout,
|
->withParticipantCursor($cursor)
|
||||||
array(
|
->setOrder(ConpherenceParticipantQuery::ORDER_OLDER)
|
||||||
'title' => $title,
|
->execute();
|
||||||
'device' => true,
|
|
||||||
));
|
if (count($set_two) == $too_many) {
|
||||||
|
$node = end($set_two);
|
||||||
|
unset($set_two[$node->getConpherencePHID()]);
|
||||||
|
$scroll_down_participant = $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
$participation = array_merge(
|
||||||
|
$set_one,
|
||||||
|
$set_two);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'scroll_up_participant' => $scroll_up_participant,
|
||||||
|
'scroll_down_participant' => $scroll_down_participant,
|
||||||
|
'participation' => $participation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadConpherenceThreadData($participation) {
|
||||||
|
$user = $this->getRequest()->getUser();
|
||||||
|
$conpherence_phids = array_keys($participation);
|
||||||
|
if ($conpherence_phids) {
|
||||||
|
$conpherences = id(new ConpherenceThreadQuery())
|
||||||
|
->setViewer($user)
|
||||||
|
->withPHIDs($conpherence_phids)
|
||||||
|
->needParticipantCache(true)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this will re-sort by participation data
|
||||||
|
$conpherences = array_select_keys($conpherences, $conpherence_phids);
|
||||||
|
|
||||||
|
return $conpherences;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getEmptyParticipant() {
|
||||||
|
return id(new ConpherenceParticipant())
|
||||||
|
->makeEphemeral();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,9 @@ final class ConpherenceViewController extends
|
||||||
->setBeforeTransactionID($before_transaction_id);
|
->setBeforeTransactionID($before_transaction_id);
|
||||||
}
|
}
|
||||||
$conpherence = $query->executeOne();
|
$conpherence = $query->executeOne();
|
||||||
|
if (!$conpherence) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
$this->setConpherence($conpherence);
|
$this->setConpherence($conpherence);
|
||||||
|
|
||||||
$participant = $conpherence->getParticipant($user->getPHID());
|
$participant = $conpherence->getParticipant($user->getPHID());
|
||||||
|
|
|
@ -140,9 +140,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||||
$object->setRecentParticipantPHIDs($participants);
|
$object->setRecentParticipantPHIDs($participants);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* For now this only supports adding more files and participants.
|
|
||||||
*/
|
|
||||||
protected function applyCustomExternalTransaction(
|
protected function applyCustomExternalTransaction(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
@ -169,33 +166,8 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||||
$file_phid);
|
$file_phid);
|
||||||
}
|
}
|
||||||
$editor->save();
|
$editor->save();
|
||||||
// fallthrough
|
|
||||||
case PhabricatorTransactions::TYPE_COMMENT:
|
|
||||||
$xaction_phid = $xaction->getPHID();
|
|
||||||
$behind = ConpherenceParticipationStatus::BEHIND;
|
|
||||||
$up_to_date = ConpherenceParticipationStatus::UP_TO_DATE;
|
|
||||||
$participants = $object->getParticipants();
|
|
||||||
$user = $this->getActor();
|
|
||||||
$time = time();
|
|
||||||
foreach ($participants as $phid => $participant) {
|
|
||||||
if ($phid != $user->getPHID()) {
|
|
||||||
if ($participant->getParticipationStatus() != $behind) {
|
|
||||||
$participant->setBehindTransactionPHID($xaction_phid);
|
|
||||||
// decrement one as this is the message putting them behind!
|
|
||||||
$participant->setSeenMessageCount($object->getMessageCount() - 1);
|
|
||||||
}
|
|
||||||
$participant->setParticipationStatus($behind);
|
|
||||||
$participant->setDateTouched($time);
|
|
||||||
} else {
|
|
||||||
$participant->setSeenMessageCount($object->getMessageCount());
|
|
||||||
$participant->setParticipationStatus($up_to_date);
|
|
||||||
$participant->setDateTouched($time);
|
|
||||||
}
|
|
||||||
$participant->save();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||||
|
|
||||||
$participants = $object->getParticipants();
|
$participants = $object->getParticipants();
|
||||||
|
|
||||||
$old_map = array_fuse($xaction->getOldValue());
|
$old_map = array_fuse($xaction->getOldValue());
|
||||||
|
@ -229,7 +201,37 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||||
}
|
}
|
||||||
$object->attachParticipants($participants);
|
$object->attachParticipants($participants);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFinalEffects(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
array $xactions) {
|
||||||
|
|
||||||
|
// update everyone's participation status on the last xaction -only-
|
||||||
|
$xaction = end($xactions);
|
||||||
|
$xaction_phid = $xaction->getPHID();
|
||||||
|
$behind = ConpherenceParticipationStatus::BEHIND;
|
||||||
|
$up_to_date = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||||
|
$participants = $object->getParticipants();
|
||||||
|
$user = $this->getActor();
|
||||||
|
$time = time();
|
||||||
|
foreach ($participants as $phid => $participant) {
|
||||||
|
if ($phid != $user->getPHID()) {
|
||||||
|
if ($participant->getParticipationStatus() != $behind) {
|
||||||
|
$participant->setBehindTransactionPHID($xaction_phid);
|
||||||
|
// decrement one as this is the message putting them behind!
|
||||||
|
$participant->setSeenMessageCount($object->getMessageCount() - 1);
|
||||||
|
}
|
||||||
|
$participant->setParticipationStatus($behind);
|
||||||
|
$participant->setDateTouched($time);
|
||||||
|
} else {
|
||||||
|
$participant->setSeenMessageCount($object->getMessageCount());
|
||||||
|
$participant->setParticipationStatus($up_to_date);
|
||||||
|
$participant->setDateTouched($time);
|
||||||
|
}
|
||||||
|
$participant->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function mergeTransactions(
|
protected function mergeTransactions(
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query class that answers the question:
|
||||||
|
*
|
||||||
|
* - Q: How many unread conpherences am I participating in?
|
||||||
|
* - A:
|
||||||
|
* id(new ConpherenceParticipantCountQuery())
|
||||||
|
* ->withParticipantPHIDs(array($my_phid))
|
||||||
|
* ->withParticipationStatus(ConpherenceParticipationStatus::BEHIND)
|
||||||
|
* ->execute();
|
||||||
|
*
|
||||||
|
* @group conpherence
|
||||||
|
*/
|
||||||
|
final class ConpherenceParticipantCountQuery
|
||||||
|
extends PhabricatorOffsetPagedQuery {
|
||||||
|
|
||||||
|
private $participantPHIDs;
|
||||||
|
private $participationStatus;
|
||||||
|
|
||||||
|
public function withParticipantPHIDs(array $phids) {
|
||||||
|
$this->participantPHIDs = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withParticipationStatus($participation_status) {
|
||||||
|
$this->participationStatus = $participation_status;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() {
|
||||||
|
$table = new ConpherenceParticipant();
|
||||||
|
$conn_r = $table->establishConnection('r');
|
||||||
|
|
||||||
|
$rows = queryfx_all(
|
||||||
|
$conn_r,
|
||||||
|
'SELECT COUNT(*) as count, participantPHID '.
|
||||||
|
'FROM %T participant %Q %Q %Q',
|
||||||
|
$table->getTableName(),
|
||||||
|
$this->buildWhereClause($conn_r),
|
||||||
|
$this->buildGroupByClause($conn_r),
|
||||||
|
$this->buildLimitClause($conn_r));
|
||||||
|
|
||||||
|
return ipull($rows, 'count', 'participantPHID');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildWhereClause($conn_r) {
|
||||||
|
$where = array();
|
||||||
|
|
||||||
|
if ($this->participantPHIDs) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'participantPHID IN (%Ls)',
|
||||||
|
$this->participantPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->participationStatus !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'participationStatus = %d',
|
||||||
|
$this->participationStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->formatWhereClause($where);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildGroupByClause(AphrontDatabaseConnection $conn_r) {
|
||||||
|
|
||||||
|
$group_by = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'GROUP BY participantPHID');
|
||||||
|
|
||||||
|
return $group_by;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,35 +1,61 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Query class that answers these questions:
|
||||||
|
*
|
||||||
|
* - Q: What are the conpherences to show when I land on /conpherence/ ?
|
||||||
|
* - A:
|
||||||
|
*
|
||||||
|
* id(new ConpherenceParticipantQuery())
|
||||||
|
* ->withParticipantPHIDs(array($my_phid))
|
||||||
|
* ->execute();
|
||||||
|
*
|
||||||
|
* - Q: What are the next set of conpherences as I scroll up (more recent) or
|
||||||
|
* down (less recent) this list of conpherences?
|
||||||
|
* - A:
|
||||||
|
*
|
||||||
|
* id(new ConpherenceParticipantQuery())
|
||||||
|
* ->withParticipantPHIDs(array($my_phid))
|
||||||
|
* ->withParticipantCursor($top_participant)
|
||||||
|
* ->setOrder(ConpherenceParticipantQuery::ORDER_NEWER)
|
||||||
|
* ->execute();
|
||||||
|
*
|
||||||
|
* -or-
|
||||||
|
*
|
||||||
|
* id(new ConpherenceParticipantQuery())
|
||||||
|
* ->withParticipantPHIDs(array($my_phid))
|
||||||
|
* ->withParticipantCursor($bottom_participant)
|
||||||
|
* ->setOrder(ConpherenceParticipantQuery::ORDER_OLDER)
|
||||||
|
* ->execute();
|
||||||
|
*
|
||||||
|
* For counts of read, un-read, or all conpherences by participant, see
|
||||||
|
* @{class:ConpherenceParticipantCountQuery}.
|
||||||
|
*
|
||||||
* @group conpherence
|
* @group conpherence
|
||||||
*/
|
*/
|
||||||
final class ConpherenceParticipantQuery
|
final class ConpherenceParticipantQuery
|
||||||
extends PhabricatorOffsetPagedQuery {
|
extends PhabricatorOffsetPagedQuery {
|
||||||
|
|
||||||
private $conpherencePHIDs;
|
const LIMIT = 100;
|
||||||
private $participantPHIDs;
|
const ORDER_NEWER = 'newer';
|
||||||
private $dateTouched;
|
const ORDER_OLDER = 'older';
|
||||||
private $dateTouchedSort;
|
|
||||||
private $participationStatus;
|
|
||||||
|
|
||||||
public function withConpherencePHIDs(array $phids) {
|
private $participantPHIDs;
|
||||||
$this->conpherencePHIDs = $phids;
|
private $participantCursor;
|
||||||
return $this;
|
private $order = self::ORDER_OLDER;
|
||||||
}
|
|
||||||
|
|
||||||
public function withParticipantPHIDs(array $phids) {
|
public function withParticipantPHIDs(array $phids) {
|
||||||
$this->participantPHIDs = $phids;
|
$this->participantPHIDs = $phids;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withDateTouched($date, $sort = null) {
|
public function withParticipantCursor(ConpherenceParticipant $participant) {
|
||||||
$this->dateTouched = $date;
|
$this->participantCursor = $participant;
|
||||||
$this->dateTouchedSort = $sort ? $sort : '<';
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withParticipationStatus($participation_status) {
|
public function setOrder($order) {
|
||||||
$this->participationStatus = $participation_status;
|
$this->order = $order;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,19 +75,16 @@ final class ConpherenceParticipantQuery
|
||||||
|
|
||||||
$participants = mpull($participants, null, 'getConpherencePHID');
|
$participants = mpull($participants, null, 'getConpherencePHID');
|
||||||
|
|
||||||
|
if ($this->order == self::ORDER_NEWER) {
|
||||||
|
$participants = array_reverse($participants);
|
||||||
|
}
|
||||||
|
|
||||||
return $participants;
|
return $participants;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildWhereClause($conn_r) {
|
private function buildWhereClause($conn_r) {
|
||||||
$where = array();
|
$where = array();
|
||||||
|
|
||||||
if ($this->conpherencePHIDs) {
|
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'conpherencePHID IN (%Ls)',
|
|
||||||
$this->conpherencePHIDs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->participantPHIDs) {
|
if ($this->participantPHIDs) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
|
@ -69,28 +92,41 @@ final class ConpherenceParticipantQuery
|
||||||
$this->participantPHIDs);
|
$this->participantPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->participationStatus !== null) {
|
if ($this->participantCursor) {
|
||||||
|
$date_touched = $this->participantCursor->getDateTouched();
|
||||||
|
$id = $this->participantCursor->getID();
|
||||||
|
if ($this->order == self::ORDER_OLDER) {
|
||||||
|
$compare_date = '<';
|
||||||
|
$compare_id = '<=';
|
||||||
|
} else {
|
||||||
|
$compare_date = '>';
|
||||||
|
$compare_id = '>=';
|
||||||
|
}
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'participationStatus = %d',
|
'(dateTouched %Q %d OR (dateTouched = %d AND id %Q %d))',
|
||||||
$this->participationStatus);
|
$compare_date,
|
||||||
}
|
$date_touched,
|
||||||
|
$date_touched,
|
||||||
if ($this->dateTouched) {
|
$compare_id,
|
||||||
if ($this->dateTouchedSort) {
|
$id);
|
||||||
$where[] = qsprintf(
|
|
||||||
$conn_r,
|
|
||||||
'dateTouched %Q %d',
|
|
||||||
$this->dateTouchedSort,
|
|
||||||
$this->dateTouched);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->formatWhereClause($where);
|
return $this->formatWhereClause($where);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildOrderClause(AphrontDatabaseConnection $conn_r) {
|
private function buildOrderClause(AphrontDatabaseConnection $conn_r) {
|
||||||
return 'ORDER BY dateTouched DESC';
|
|
||||||
|
$order_word = ($this->order == self::ORDER_OLDER) ? 'DESC' : 'ASC';
|
||||||
|
// if these are different direction we won't get as efficient a query
|
||||||
|
// see http://dev.mysql.com/doc/refman/5.5/en/order-by-optimization.html
|
||||||
|
$order = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'ORDER BY dateTouched %Q, id %Q',
|
||||||
|
$order_word,
|
||||||
|
$order_word);
|
||||||
|
|
||||||
|
return $order;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,26 +3,33 @@
|
||||||
final class ConpherenceThreadListView extends AphrontView {
|
final class ConpherenceThreadListView extends AphrontView {
|
||||||
|
|
||||||
private $baseURI;
|
private $baseURI;
|
||||||
private $unreadThreads;
|
private $threads;
|
||||||
private $readThreads;
|
private $scrollUpParticipant;
|
||||||
|
private $scrollDownParticipant;
|
||||||
|
|
||||||
|
public function setThreads(array $threads) {
|
||||||
|
assert_instances_of($threads, 'ConpherenceThread');
|
||||||
|
$this->threads = $threads;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setScrollUpParticipant(
|
||||||
|
ConpherenceParticipant $participant) {
|
||||||
|
$this->scrollUpParticipant = $participant;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setScrollDownParticipant(
|
||||||
|
ConpherenceParticipant $participant) {
|
||||||
|
$this->scrollDownParticipant = $participant;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function setBaseURI($base_uri) {
|
public function setBaseURI($base_uri) {
|
||||||
$this->baseURI = $base_uri;
|
$this->baseURI = $base_uri;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUnreadThreads(array $unread_threads) {
|
|
||||||
assert_instances_of($unread_threads, 'ConpherenceThread');
|
|
||||||
$this->unreadThreads = $unread_threads;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setReadThreads(array $read_threads) {
|
|
||||||
assert_instances_of($read_threads, 'ConpherenceThread');
|
|
||||||
$this->readThreads = $read_threads;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
require_celerity_resource('conpherence-menu-css');
|
require_celerity_resource('conpherence-menu-css');
|
||||||
|
|
||||||
|
@ -39,10 +46,8 @@ final class ConpherenceThreadListView extends AphrontView {
|
||||||
->setHref($this->baseURI.'new/')
|
->setHref($this->baseURI.'new/')
|
||||||
->setType(PhabricatorMenuItemView::TYPE_BUTTON));
|
->setType(PhabricatorMenuItemView::TYPE_BUTTON));
|
||||||
|
|
||||||
$menu->newLabel(pht('Unread'));
|
$menu->newLabel('');
|
||||||
$this->addThreadsToMenu($menu, $this->unreadThreads, $read = false);
|
$this->addThreadsToMenu($menu, $this->threads);
|
||||||
$menu->newLabel(pht('Read'));
|
|
||||||
$this->addThreadsToMenu($menu, $this->readThreads, $read = true);
|
|
||||||
|
|
||||||
return $menu;
|
return $menu;
|
||||||
}
|
}
|
||||||
|
@ -51,6 +56,28 @@ final class ConpherenceThreadListView extends AphrontView {
|
||||||
return $this->renderThread($thread);
|
return $this->renderThread($thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function renderThreadsHTML() {
|
||||||
|
$thread_html = array();
|
||||||
|
|
||||||
|
if ($this->scrollUpParticipant->getID()) {
|
||||||
|
$thread_html[] = $this->getScrollMenuItem(
|
||||||
|
$this->scrollUpParticipant,
|
||||||
|
'up');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->threads as $thread) {
|
||||||
|
$thread_html[] = $this->renderSingleThread($thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->scrollDownParticipant->getID()) {
|
||||||
|
$thread_html[] = $this->getScrollMenuItem(
|
||||||
|
$this->scrollDownParticipant,
|
||||||
|
'down');
|
||||||
|
}
|
||||||
|
|
||||||
|
return phutil_implode_html('', $thread_html);
|
||||||
|
}
|
||||||
|
|
||||||
private function renderThreadItem(ConpherenceThread $thread) {
|
private function renderThreadItem(ConpherenceThread $thread) {
|
||||||
return id(new PhabricatorMenuItemView())
|
return id(new PhabricatorMenuItemView())
|
||||||
->setType(PhabricatorMenuItemView::TYPE_CUSTOM)
|
->setType(PhabricatorMenuItemView::TYPE_CUSTOM)
|
||||||
|
@ -87,28 +114,59 @@ final class ConpherenceThreadListView extends AphrontView {
|
||||||
|
|
||||||
private function addThreadsToMenu(
|
private function addThreadsToMenu(
|
||||||
PhabricatorMenuView $menu,
|
PhabricatorMenuView $menu,
|
||||||
array $conpherences,
|
array $conpherences) {
|
||||||
$read = false) {
|
|
||||||
|
if ($this->scrollUpParticipant->getID()) {
|
||||||
|
$item = $this->getScrollMenuItem($this->scrollUpParticipant, 'up');
|
||||||
|
$menu->addMenuItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($conpherences as $conpherence) {
|
foreach ($conpherences as $conpherence) {
|
||||||
$item = $this->renderThreadItem($conpherence);
|
$item = $this->renderThreadItem($conpherence);
|
||||||
$menu->addMenuItem($item);
|
$menu->addMenuItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($conpherences) || $read) {
|
if (empty($conpherences)) {
|
||||||
$menu->addMenuItem($this->getNoConpherencesBlock());
|
$menu->addMenuItem($this->getNoConpherencesMenuItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->scrollDownParticipant->getID()) {
|
||||||
|
$item = $this->getScrollMenuItem($this->scrollDownParticipant, 'down');
|
||||||
|
$menu->addMenuItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $menu;
|
return $menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getNoConpherencesBlock() {
|
public function getScrollMenuItem(
|
||||||
|
ConpherenceParticipant $participant,
|
||||||
|
$direction) {
|
||||||
|
|
||||||
|
if ($direction == 'up') {
|
||||||
|
$name = pht('Load Newer Threads');
|
||||||
|
} else {
|
||||||
|
$name = pht('Load Older Threads');
|
||||||
|
}
|
||||||
|
$item = id(new PhabricatorMenuItemView())
|
||||||
|
->addSigil('conpherence-menu-scroller')
|
||||||
|
->setName($name)
|
||||||
|
->setHref($this->baseURI)
|
||||||
|
->setType(PhabricatorMenuItemView::TYPE_BUTTON)
|
||||||
|
->setMetadata(array(
|
||||||
|
'participant_id' => $participant->getID(),
|
||||||
|
'conpherence_phid' => $participant->getConpherencePHID(),
|
||||||
|
'date_touched' => $participant->getDateTouched(),
|
||||||
|
'direction' => $direction));
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getNoConpherencesMenuItem() {
|
||||||
$message = phutil_tag(
|
$message = phutil_tag(
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'class' => 'no-conpherences-menu-item'
|
'class' => 'no-conpherences-menu-item'
|
||||||
),
|
),
|
||||||
pht('No more conpherences.'));
|
pht('No conpherences.'));
|
||||||
|
|
||||||
return id(new PhabricatorMenuItemView())
|
return id(new PhabricatorMenuItemView())
|
||||||
->setType(PhabricatorMenuItemView::TYPE_CUSTOM)
|
->setType(PhabricatorMenuItemView::TYPE_CUSTOM)
|
||||||
|
|
|
@ -257,6 +257,11 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
throw new Exception("Capability not supported!");
|
throw new Exception("Capability not supported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function applyFinalEffects(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
array $xactions) {
|
||||||
|
}
|
||||||
|
|
||||||
public function setContentSource(PhabricatorContentSource $content_source) {
|
public function setContentSource(PhabricatorContentSource $content_source) {
|
||||||
$this->contentSource = $content_source;
|
$this->contentSource = $content_source;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -386,6 +391,8 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
$this->applyExternalEffects($object, $xaction);
|
$this->applyExternalEffects($object, $xaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->applyFinalEffects($object, $xactions);
|
||||||
|
|
||||||
if ($read_locking) {
|
if ($read_locking) {
|
||||||
$object->endReadLocking();
|
$object->endReadLocking();
|
||||||
$read_locking = false;
|
$read_locking = false;
|
||||||
|
|
|
@ -1250,6 +1250,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
||||||
'type' => 'sql',
|
'type' => 'sql',
|
||||||
'name' => $this->getPatchPath('20130423.phortunepaymentrevised.sql'),
|
'name' => $this->getPatchPath('20130423.phortunepaymentrevised.sql'),
|
||||||
),
|
),
|
||||||
|
'20130423.conpherenceindices.sql' => array(
|
||||||
|
'type' => 'sql',
|
||||||
|
'name' => $this->getPatchPath('20130423.conpherenceindices.sql'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,11 +267,11 @@ final class PhabricatorMainMenuView extends AphrontView {
|
||||||
$message_count_id = celerity_generate_unique_node_id();
|
$message_count_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
$unread_status = ConpherenceParticipationStatus::BEHIND;
|
$unread_status = ConpherenceParticipationStatus::BEHIND;
|
||||||
$unread = id(new ConpherenceParticipantQuery())
|
$unread = id(new ConpherenceParticipantCountQuery())
|
||||||
->withParticipantPHIDs(array($user->getPHID()))
|
->withParticipantPHIDs(array($user->getPHID()))
|
||||||
->withParticipationStatus($unread_status)
|
->withParticipationStatus($unread_status)
|
||||||
->execute();
|
->execute();
|
||||||
$message_count_number = count($unread);
|
$message_count_number = $unread[$user->getPHID()];
|
||||||
if ($message_count_number > 999) {
|
if ($message_count_number > 999) {
|
||||||
$message_count_number = "\xE2\x88\x9E";
|
$message_count_number = "\xE2\x88\x9E";
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* javelin-workflow
|
* javelin-workflow
|
||||||
* javelin-behavior-device
|
* javelin-behavior-device
|
||||||
* javelin-history
|
* javelin-history
|
||||||
|
* javelin-vector
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JX.behavior('conpherence-menu', function(config) {
|
JX.behavior('conpherence-menu', function(config) {
|
||||||
|
@ -53,6 +54,15 @@ JX.behavior('conpherence-menu', function(config) {
|
||||||
redrawthread();
|
redrawthread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JX.Stratcom.listen(
|
||||||
|
'conpherence-selectthread',
|
||||||
|
null,
|
||||||
|
function (e) {
|
||||||
|
var node = JX.$(e.getData().id);
|
||||||
|
selectthread(node);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
function updatepagedata(data) {
|
function updatepagedata(data) {
|
||||||
var uri_suffix = thread.selected + '/';
|
var uri_suffix = thread.selected + '/';
|
||||||
if (data.use_base_uri) {
|
if (data.use_base_uri) {
|
||||||
|
@ -75,15 +85,6 @@ JX.behavior('conpherence-menu', function(config) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
JX.Stratcom.listen(
|
|
||||||
'conpherence-selectthread',
|
|
||||||
null,
|
|
||||||
function (e) {
|
|
||||||
var node = JX.$(e.getData().id);
|
|
||||||
selectthread(node);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
function redrawthread() {
|
function redrawthread() {
|
||||||
if (!thread.node) {
|
if (!thread.node) {
|
||||||
return;
|
return;
|
||||||
|
@ -96,9 +97,9 @@ JX.behavior('conpherence-menu', function(config) {
|
||||||
var data = JX.Stratcom.getData(thread.node);
|
var data = JX.Stratcom.getData(thread.node);
|
||||||
|
|
||||||
if (thread.visible !== null || !config.hasThread) {
|
if (thread.visible !== null || !config.hasThread) {
|
||||||
var uri = config.base_uri + data.id + '/';
|
var uri = config.base_uri + data.id + '/';
|
||||||
new JX.Workflow(uri, {})
|
new JX.Workflow(uri, {})
|
||||||
.setHandler(onresponse)
|
.setHandler(onloadthreadresponse)
|
||||||
.start();
|
.start();
|
||||||
} else {
|
} else {
|
||||||
didredrawthread();
|
didredrawthread();
|
||||||
|
@ -154,7 +155,7 @@ JX.behavior('conpherence-menu', function(config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onresponse(response) {
|
function onloadthreadresponse(response) {
|
||||||
var header = JX.$H(response.header);
|
var header = JX.$H(response.header);
|
||||||
var messages = JX.$H(response.messages);
|
var messages = JX.$H(response.messages);
|
||||||
var form = JX.$H(response.form);
|
var form = JX.$H(response.form);
|
||||||
|
@ -252,11 +253,9 @@ JX.behavior('conpherence-menu', function(config) {
|
||||||
}).setData({ oldest_transaction_id : oldest_transaction_id }).send();
|
}).setData({ oldest_transaction_id : oldest_transaction_id }).send();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// On mobile, we just show a thread list, so we don't want to automatically
|
// On mobile, we just show a thread list, so we don't want to automatically
|
||||||
// select or load any threads. On Desktop, we automatically select the first
|
// select or load any threads. On Desktop, we automatically select the first
|
||||||
// thread.
|
// thread.
|
||||||
|
|
||||||
var old_device = null;
|
var old_device = null;
|
||||||
function ondevicechange() {
|
function ondevicechange() {
|
||||||
var new_device = JX.Device.getDevice();
|
var new_device = JX.Device.getDevice();
|
||||||
|
@ -284,16 +283,18 @@ JX.behavior('conpherence-menu', function(config) {
|
||||||
function loadthreads() {
|
function loadthreads() {
|
||||||
var uri = config.base_uri + 'thread/' + config.selectedID + '/';
|
var uri = config.base_uri + 'thread/' + config.selectedID + '/';
|
||||||
new JX.Workflow(uri)
|
new JX.Workflow(uri)
|
||||||
.setHandler(onthreadresponse)
|
.setHandler(onloadthreadsresponse)
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onthreadresponse(r) {
|
function onloadthreadsresponse(r) {
|
||||||
var layout = JX.$(config.layoutID);
|
var layout = JX.$(config.layoutID);
|
||||||
var menu = JX.DOM.find(layout, 'div', 'conpherence-menu-pane');
|
var menu = JX.DOM.find(layout, 'div', 'conpherence-menu-pane');
|
||||||
JX.DOM.setContent(menu, JX.$H(r));
|
JX.DOM.setContent(menu, JX.$H(r));
|
||||||
|
|
||||||
config.selectedID && selectthreadid(config.selectedID);
|
config.selectedID && selectthreadid(config.selectedID);
|
||||||
|
|
||||||
|
thread.node.scrollIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
function didloadthreads() {
|
function didloadthreads() {
|
||||||
|
@ -316,4 +317,71 @@ JX.behavior('conpherence-menu', function(config) {
|
||||||
redrawthread();
|
redrawthread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var handlethreadscrollers = function (e) {
|
||||||
|
e.kill();
|
||||||
|
|
||||||
|
var data = e.getNodeData('conpherence-menu-scroller');
|
||||||
|
var scroller = e.getNode('conpherence-menu-scroller');
|
||||||
|
new JX.Workflow(scroller.href, data)
|
||||||
|
.setHandler(
|
||||||
|
JX.bind(null, threadscrollerresponse, scroller, data.direction))
|
||||||
|
.start();
|
||||||
|
};
|
||||||
|
|
||||||
|
var threadscrollerresponse = function (scroller, direction, r) {
|
||||||
|
var html = JX.$H(r.html);
|
||||||
|
|
||||||
|
var threadPhids = r.phids;
|
||||||
|
var reselectId = null;
|
||||||
|
// remove any threads that are in the list that we just got back
|
||||||
|
// in the result set; things have changed and they'll be in the
|
||||||
|
// right place soon
|
||||||
|
for (var ii = 0; ii < threadPhids.length; ii++) {
|
||||||
|
try {
|
||||||
|
var nodeId = threadPhids[ii] + '-nav-item';
|
||||||
|
var node = JX.$(nodeId);
|
||||||
|
var nodeData = JX.Stratcom.getData(node);
|
||||||
|
if (nodeData.id == thread.selected) {
|
||||||
|
reselectId = nodeId;
|
||||||
|
}
|
||||||
|
JX.DOM.remove(node);
|
||||||
|
} catch (ex) {
|
||||||
|
// ignore , just haven't seen this thread yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var root = JX.DOM.find(document, 'div', 'conpherence-layout');
|
||||||
|
var menuRoot = JX.DOM.find(root, 'div', 'conpherence-menu-pane');
|
||||||
|
var scrollY = 0;
|
||||||
|
// we have to do some hyjinx in the up case to make the menu scroll to
|
||||||
|
// where it should
|
||||||
|
if (direction == 'up') {
|
||||||
|
var style = {
|
||||||
|
position: 'absolute',
|
||||||
|
left: '-10000px'
|
||||||
|
};
|
||||||
|
var test_size = JX.$N('div', {style: style}, html);
|
||||||
|
document.body.appendChild(test_size);
|
||||||
|
var html_size = JX.Vector.getDim(test_size);
|
||||||
|
JX.DOM.remove(test_size);
|
||||||
|
scrollY = html_size.y;
|
||||||
|
}
|
||||||
|
JX.DOM.replace(scroller, html);
|
||||||
|
menuRoot.scrollTop += scrollY;
|
||||||
|
|
||||||
|
if (reselectId) {
|
||||||
|
JX.Stratcom.invoke(
|
||||||
|
'conpherence-selectthread',
|
||||||
|
null,
|
||||||
|
{ id : reselectId }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
JX.Stratcom.listen(
|
||||||
|
['click'],
|
||||||
|
'conpherence-menu-scroller',
|
||||||
|
handlethreadscrollers
|
||||||
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue