1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 00:42:41 +01:00

Conpherence - make the durable column kind of work and stuff

Summary:
Ref T7014. This hooks up the durable column such that when you open it up it loads your most recent Conpherence. You can then switch amongst the various widgets and stuff and everything works nicely.

Except...

 - scroll bar does not work
   - also doesn't work at HEAD when I add a ton of text to the UI with no changes? (wrapped $copy in array_fill(0, 1000, $copy))
 - "widget selector" does not collapse when you select something else
   - this part wasn't really specified so I used the aphlict dropdown stuff. didn't want to keep working on that if this was the wrong UI choice
 - can not edit title
   - do we still want that to be done by clicking on the title, which pops a dialogue?
 - can not add participants or calendar events
   - what should this UI be? maybe just a button on the top for "participants" and a button on the bottom for calendar? both on top?
 - this is not pixel perfect to the mock or two I've seen around. Aside from generally being bad at that, I definitely didn't get the name + timestamps formatting correctly, because the standard DOM of that has timestamp FIRST which appears second due to a "float right". Seemed like a lot of special-casing for what might not even be that important in the UI so I punted. (And again, there's likely many unknown ways in which this isn't pixel perfect)

There's also code quality issues

 - `ConpherenceWidgetConfigConstants` is hopefully temporary or at least gets more sleek as we keep progressing here
 - copied some CSS from main Conpherence app
   - DOM structure is pretty different
   - there's some minor CSS tweaks too given the different width (not to mention the DOM structure being different)
 - copied some JS from behavior-pontificate.js to sync threads relative to aphlict updates
 - JS in general is like a better version of existing JS; these should collapse I'd hope?
 - maybe the aphlict-behavior-dropdown change was badsauce?

...but all that said, this definitely feels really nice and I feel like adding stuff is going to be really easy compared to how normal Conpherence is.

Also includes a bonus bug fix - we now correctly update participation. The user would encounter this issue if they were in a conpherence that got some updates and then they went to a different page; they would have unread status for the messages that were ajax'd in. This patch fixes that by making sure we mark participation up to date with the proper transaction in all cases.

Test Plan: hit "\" to invoke the column and saw nice loading UI and my latest conpherence load. sent messages and verified they received A-OK by looking in DOM console. toggled various widges and verified they rendered correctly. opened up a second browser with a second user on the thread, sent a message, and it was received in a nice asynchronous fashion

Reviewers: chad, epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T7014

Differential Revision: https://secure.phabricator.com/D11968
This commit is contained in:
Bob Trahan 2015-03-05 10:33:39 -08:00
parent 386a517c14
commit 27a60bdb3c
17 changed files with 1113 additions and 294 deletions

View file

@ -7,8 +7,8 @@
*/
return array(
'names' => array(
'core.pkg.css' => 'd9fa6161',
'core.pkg.js' => '23d653bb',
'core.pkg.css' => '1a530a25',
'core.pkg.js' => 'a77025a1',
'darkconsole.pkg.js' => '8ab24e01',
'differential.pkg.css' => '4c3242f8',
'differential.pkg.js' => '7b5a4aa4',
@ -44,7 +44,7 @@ return array(
'rsrc/css/application/config/config-welcome.css' => '6abd79be',
'rsrc/css/application/config/setup-issue.css' => '22270af2',
'rsrc/css/application/config/unhandled-exception.css' => '37d4f9a2',
'rsrc/css/application/conpherence/durable-column.css' => '12846d25',
'rsrc/css/application/conpherence/durable-column.css' => '3b836442',
'rsrc/css/application/conpherence/menu.css' => '73774137',
'rsrc/css/application/conpherence/message-pane.css' => '17a9517f',
'rsrc/css/application/conpherence/notification.css' => '04a6e10a',
@ -107,7 +107,7 @@ return array(
'rsrc/css/core/core.css' => 'c8c5ecd2',
'rsrc/css/core/remarkup.css' => '2dbff225',
'rsrc/css/core/syntax.css' => '56c1ba38',
'rsrc/css/core/z-index.css' => '8239495e',
'rsrc/css/core/z-index.css' => '9ec70c03',
'rsrc/css/diviner/diviner-shared.css' => '38813222',
'rsrc/css/font/font-awesome.css' => 'ae9a7b4d',
'rsrc/css/font/font-source-sans-pro.css' => '0d859f60',
@ -346,12 +346,12 @@ return array(
'rsrc/image/texture/table_header_hover.png' => '038ec3b9',
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
'rsrc/js/application/aphlict/Aphlict.js' => '2be71d56',
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '335470d7',
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'cc2d9c80',
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '851f167c',
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761',
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de',
'rsrc/js/application/conpherence/behavior-durable-column.js' => '3d86ed6e',
'rsrc/js/application/conpherence/behavior-durable-column.js' => 'dc9fb293',
'rsrc/js/application/conpherence/behavior-menu.js' => '869e3445',
'rsrc/js/application/conpherence/behavior-pontificate.js' => '86df5915',
'rsrc/js/application/conpherence/behavior-widget-pane.js' => '40b1ff90',
@ -513,7 +513,7 @@ return array(
'changeset-view-manager' => '5eb5b98c',
'config-options-css' => '7fedf08b',
'config-welcome-css' => '6abd79be',
'conpherence-durable-column-view' => '12846d25',
'conpherence-durable-column-view' => '3b836442',
'conpherence-menu-css' => '73774137',
'conpherence-message-pane-css' => '17a9517f',
'conpherence-notification-css' => '04a6e10a',
@ -542,7 +542,7 @@ return array(
'inline-comment-summary-css' => 'eb5f8e8c',
'javelin-aphlict' => '2be71d56',
'javelin-behavior' => '61cbc29a',
'javelin-behavior-aphlict-dropdown' => '335470d7',
'javelin-behavior-aphlict-dropdown' => 'cc2d9c80',
'javelin-behavior-aphlict-listen' => '851f167c',
'javelin-behavior-aphlict-status' => 'ea681761',
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
@ -584,7 +584,7 @@ return array(
'javelin-behavior-diffusion-locate-file' => '6d3e1947',
'javelin-behavior-diffusion-pull-lastmodified' => '2b228192',
'javelin-behavior-doorkeeper-tag' => 'e5822781',
'javelin-behavior-durable-column' => '3d86ed6e',
'javelin-behavior-durable-column' => 'dc9fb293',
'javelin-behavior-error-log' => '6882e80a',
'javelin-behavior-fancy-datepicker' => 'c51ae228',
'javelin-behavior-global-drag-and-drop' => '07f199d8',
@ -759,7 +759,7 @@ return array(
'phabricator-uiexample-reactor-select' => 'a155550f',
'phabricator-uiexample-reactor-sendclass' => '1def2711',
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
'phabricator-zindex-css' => '8239495e',
'phabricator-zindex-css' => '9ec70c03',
'phame-css' => '88bd4705',
'pholio-css' => '95174bdd',
'pholio-edit-css' => '3ad9d1ee',
@ -1011,16 +1011,6 @@ return array(
'331b1611' => array(
'javelin-install',
),
'335470d7' => array(
'javelin-behavior',
'javelin-request',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-uri',
'javelin-behavior-device',
'phabricator-title',
),
'3ab51e2c' => array(
'javelin-behavior',
'javelin-behavior-device',
@ -1037,14 +1027,6 @@ return array(
'javelin-util',
'javelin-uri',
),
'3d86ed6e' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-scrollbar',
'javelin-quicksand',
'phabricator-keyboard-shortcut',
),
'3ee3408b' => array(
'javelin-behavior',
'javelin-behavior-device',
@ -1751,6 +1733,16 @@ return array(
'javelin-stratcom',
'phabricator-phtize',
),
'cc2d9c80' => array(
'javelin-behavior',
'javelin-request',
'javelin-stratcom',
'javelin-vector',
'javelin-dom',
'javelin-uri',
'javelin-behavior-device',
'phabricator-title',
),
'd19198c8' => array(
'javelin-install',
'javelin-dom',
@ -1790,6 +1782,15 @@ return array(
'javelin-dom',
'phabricator-busy',
),
'dc9fb293' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-scrollbar',
'javelin-quicksand',
'phabricator-keyboard-shortcut',
'javelin-behavior-conpherence-widget-pane',
),
'de2e896f' => array(
'javelin-behavior',
'javelin-dom',

View file

@ -221,6 +221,7 @@ phutil_register_library_map(array(
'ConduitQueryConduitAPIMethod' => 'applications/conduit/method/ConduitQueryConduitAPIMethod.php',
'ConduitSSHWorkflow' => 'applications/conduit/ssh/ConduitSSHWorkflow.php',
'ConduitTokenGarbageCollector' => 'applications/conduit/garbagecollector/ConduitTokenGarbageCollector.php',
'ConpherenceColumnViewController' => 'applications/conpherence/controller/ConpherenceColumnViewController.php',
'ConpherenceConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceConduitAPIMethod.php',
'ConpherenceConfigOptions' => 'applications/conpherence/config/ConpherenceConfigOptions.php',
'ConpherenceConstants' => 'applications/conpherence/constants/ConpherenceConstants.php',
@ -263,6 +264,7 @@ phutil_register_library_map(array(
'ConpherenceUpdateController' => 'applications/conpherence/controller/ConpherenceUpdateController.php',
'ConpherenceUpdateThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceUpdateThreadConduitAPIMethod.php',
'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php',
'ConpherenceWidgetConfigConstants' => 'applications/conpherence/constants/ConpherenceWidgetConfigConstants.php',
'ConpherenceWidgetController' => 'applications/conpherence/controller/ConpherenceWidgetController.php',
'ConpherenceWidgetView' => 'applications/conpherence/view/ConpherenceWidgetView.php',
'DarkConsoleController' => 'applications/console/controller/DarkConsoleController.php',
@ -3361,6 +3363,7 @@ phutil_register_library_map(array(
'ConduitQueryConduitAPIMethod' => 'ConduitAPIMethod',
'ConduitSSHWorkflow' => 'PhabricatorSSHWorkflow',
'ConduitTokenGarbageCollector' => 'PhabricatorGarbageCollector',
'ConpherenceColumnViewController' => 'ConpherenceController',
'ConpherenceConduitAPIMethod' => 'ConduitAPIMethod',
'ConpherenceConfigOptions' => 'PhabricatorApplicationConfigOptions',
'ConpherenceController' => 'PhabricatorController',
@ -3405,6 +3408,7 @@ phutil_register_library_map(array(
'ConpherenceUpdateController' => 'ConpherenceController',
'ConpherenceUpdateThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
'ConpherenceViewController' => 'ConpherenceController',
'ConpherenceWidgetConfigConstants' => 'ConpherenceConstants',
'ConpherenceWidgetController' => 'ConpherenceController',
'ConpherenceWidgetView' => 'AphrontView',
'DarkConsoleController' => 'PhabricatorController',

View file

@ -34,6 +34,7 @@ final class PhabricatorConpherenceApplication extends PhabricatorApplication {
'' => 'ConpherenceListController',
'thread/(?P<id>[1-9]\d*)/' => 'ConpherenceListController',
'(?P<id>[1-9]\d*)/' => 'ConpherenceViewController',
'columnview/' => 'ConpherenceColumnViewController',
'new/' => 'ConpherenceNewController',
'panel/' => 'ConpherenceNotificationPanelController',
'widget/(?P<id>[1-9]\d*)/' => 'ConpherenceWidgetController',

View file

@ -0,0 +1,59 @@
<?php
final class ConpherenceWidgetConfigConstants extends ConpherenceConstants {
const UPDATE_URI = '/conpherence/update/';
public static function getWidgetPaneBehaviorConfig() {
return array(
'widgetBaseUpdateURI' => self::UPDATE_URI,
'widgetRegistry' => self::getWidgetRegistry(),
);
}
public static function getWidgetRegistry() {
return array(
'conpherence-message-pane' => array(
'name' => pht('Thread'),
'icon' => 'fa-comment',
'deviceOnly' => true,
'hasCreate' => false,
),
'widgets-people' => array(
'name' => pht('Participants'),
'icon' => 'fa-users',
'deviceOnly' => false,
'hasCreate' => true,
'createData' => array(
'refreshFromResponse' => true,
'action' => ConpherenceUpdateActions::ADD_PERSON,
'customHref' => null,
),
),
'widgets-files' => array(
'name' => pht('Files'),
'icon' => 'fa-files-o',
'deviceOnly' => false,
'hasCreate' => false,
),
'widgets-calendar' => array(
'name' => pht('Calendar'),
'icon' => 'fa-calendar',
'deviceOnly' => false,
'hasCreate' => true,
'createData' => array(
'refreshFromResponse' => false,
'action' => ConpherenceUpdateActions::ADD_STATUS,
'customHref' => '/calendar/event/create/',
),
),
'widgets-settings' => array(
'name' => pht('Settings'),
'icon' => 'fa-wrench',
'deviceOnly' => false,
'hasCreate' => false,
),
);
}
}

View file

@ -0,0 +1,59 @@
<?php
final class ConpherenceColumnViewController extends
ConpherenceController {
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$conpherence = null;
if ($request->getInt('id')) {
$conpherence = id(new ConpherenceThreadQuery())
->setViewer($user)
->withIDs(array($request->getInt('id')))
->needTransactions(true)
->setTransactionLimit(ConpherenceThreadQuery::TRANSACTION_LIMIT)
->executeOne();
} else {
// TODO - should be pulling more data than this to build the
// icon bar, etc, kind of always
$latest_participant = id(new ConpherenceParticipantQuery())
->withParticipantPHIDs(array($user->getPHID()))
->setLimit(1)
->execute();
$participant = head($latest_participant);
$conpherence = id(new ConpherenceThreadQuery())
->setViewer($user)
->withPHIDs(array($participant->getConpherencePHID()))
->needTransactions(true)
->setTransactionLimit(ConpherenceThreadQuery::TRANSACTION_LIMIT)
->executeOne();
}
if (!$conpherence) {
return new Aphront404Response();
}
$this->setConpherence($conpherence);
$participant = $conpherence->getParticipant($user->getPHID());
$transactions = $conpherence->getTransactions();
$latest_transaction = head($transactions);
$write_guard = AphrontWriteGuard::beginScopedUnguardedWrites();
$participant->markUpToDate($conpherence, $latest_transaction);
unset($write_guard);
$durable_column = id(new ConpherenceDurableColumnView())
->setUser($user)
->setSelectedConpherence($conpherence)
->setStyle(null);
$response = array(
'content' => hsprintf('%s', $durable_column),
'threadID' => $conpherence->getID(),
'threadPHID' => $conpherence->getPHID(),
'latestTransactionID' => $latest_transaction->getID(),);
return id(new AphrontAjaxResponse())->setContent($response);
}
}

View file

@ -2,7 +2,15 @@
abstract class ConpherenceController extends PhabricatorController {
private $conpherences;
private $conpherence;
public function setConpherence(ConpherenceThread $conpherence) {
$this->conpherence = $conpherence;
return $this;
}
public function getConpherence() {
return $this->conpherence;
}
public function buildApplicationMenu() {
$nav = new PHUIListView();
@ -77,80 +85,4 @@ abstract class ConpherenceController extends PhabricatorController {
$crumbs,
));
}
protected function renderConpherenceTransactions(
ConpherenceThread $conpherence) {
$user = $this->getRequest()->getUser();
$transactions = $conpherence->getTransactions();
$oldest_transaction_id = 0;
$too_many = ConpherenceThreadQuery::TRANSACTION_LIMIT + 1;
if (count($transactions) == $too_many) {
$last_transaction = end($transactions);
unset($transactions[$last_transaction->getID()]);
$oldest_transaction = end($transactions);
$oldest_transaction_id = $oldest_transaction->getID();
}
$transactions = array_reverse($transactions);
$handles = $conpherence->getHandles();
$rendered_transactions = array();
$engine = id(new PhabricatorMarkupEngine())
->setViewer($user);
foreach ($transactions as $key => $transaction) {
if ($transaction->shouldHide()) {
unset($transactions[$key]);
continue;
}
if ($transaction->getComment()) {
$engine->addObject(
$transaction->getComment(),
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
}
}
$engine->process();
// we're going to insert a dummy date marker transaction for breaks
// between days. some setup required!
$previous_transaction = null;
$date_marker_transaction = id(new ConpherenceTransaction())
->setTransactionType(ConpherenceTransactionType::TYPE_DATE_MARKER)
->makeEphemeral();
$date_marker_transaction_view = id(new ConpherenceTransactionView())
->setUser($user)
->setConpherenceTransaction($date_marker_transaction)
->setHandles($handles)
->setMarkupEngine($engine);
foreach ($transactions as $transaction) {
if ($previous_transaction) {
$previous_day = phabricator_format_local_time(
$previous_transaction->getDateCreated(),
$user,
'Ymd');
$current_day = phabricator_format_local_time(
$transaction->getDateCreated(),
$user,
'Ymd');
// date marker transaction time!
if ($previous_day != $current_day) {
$date_marker_transaction->setDateCreated(
$transaction->getDateCreated());
$rendered_transactions[] = $date_marker_transaction_view->render();
}
}
$rendered_transactions[] = id(new ConpherenceTransactionView())
->setUser($user)
->setConpherenceTransaction($transaction)
->setHandles($handles)
->setMarkupEngine($engine)
->render();
$previous_transaction = $transaction;
}
$latest_transaction_id = $transaction->getID();
return array(
'transactions' => $rendered_transactions,
'latest_transaction_id' => $latest_transaction_id,
'oldest_transaction_id' => $oldest_transaction_id,
);
}
}

View file

@ -309,7 +309,12 @@ final class ConpherenceUpdateController
->executeOne();
if ($need_transactions) {
$data = $this->renderConpherenceTransactions($conpherence);
$data = ConpherenceTransactionView::renderTransactions(
$user,
$conpherence,
!$this->getRequest()->getExists('minimal_display'));
$participant_obj = $conpherence->getParticipant($user->getPHID());
$participant_obj->markUpToDate($conpherence, $data['latest_transaction']);
} else {
$data = array();
}

View file

@ -3,34 +3,10 @@
final class ConpherenceViewController extends
ConpherenceController {
private $conpherenceID;
private $conpherence;
public function setConpherence(ConpherenceThread $conpherence) {
$this->conpherence = $conpherence;
return $this;
}
public function getConpherence() {
return $this->conpherence;
}
public function setConpherenceID($conpherence_id) {
$this->conpherenceID = $conpherence_id;
return $this;
}
public function getConpherenceID() {
return $this->conpherenceID;
}
public function willProcessRequest(array $data) {
$this->setConpherenceID(idx($data, 'id'));
}
public function processRequest() {
$request = $this->getRequest();
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$conpherence_id = $this->getConpherenceID();
$conpherence_id = $request->getURIData('id');
if (!$conpherence_id) {
return new Aphront404Response();
}
@ -53,13 +29,15 @@ final class ConpherenceViewController extends
$participant = $conpherence->getParticipant($user->getPHID());
$transactions = $conpherence->getTransactions();
$latest_transaction = end($transactions);
$latest_transaction = head($transactions);
$write_guard = AphrontWriteGuard::beginScopedUnguardedWrites();
$participant->markUpToDate($conpherence, $latest_transaction);
unset($write_guard);
$data = $this->renderConpherenceTransactions($conpherence);
$messages = $this->renderMessagePaneContent(
$data = ConpherenceTransactionView::renderTransactions(
$user,
$conpherence);
$messages = ConpherenceTransactionView::renderMessagePaneContent(
$data['transactions'],
$data['oldest_transaction_id']);
if ($before_transaction_id) {
@ -76,6 +54,12 @@ final class ConpherenceViewController extends
);
}
$title = $conpherence->getTitle();
if (!$title) {
$title = pht('[No Title]');
}
$content['title'] = $title;
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())->setContent($content);
}
@ -88,11 +72,7 @@ final class ConpherenceViewController extends
->setReplyForm($form)
->setRole('thread');
$title = $conpherence->getTitle();
if (!$title) {
$title = pht('[No Title]');
}
return $this->buildApplicationPage(
return $this->buildApplicationPage(
$layout,
array(
'title' => $title,
@ -100,29 +80,6 @@ final class ConpherenceViewController extends
));
}
private function renderMessagePaneContent(
array $transactions,
$oldest_transaction_id) {
$scrollbutton = '';
if ($oldest_transaction_id) {
$scrollbutton = javelin_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-older-messages',
'class' => 'conpherence-show-older-messages',
'meta' => array(
'oldest_transaction_id' => $oldest_transaction_id,
),
),
pht('Show Older Messages'));
}
return hsprintf('%s%s', $scrollbutton, $transactions);
}
private function renderFormContent($latest_transaction_id) {
$conpherence = $this->getConpherence();
@ -168,4 +125,5 @@ final class ConpherenceViewController extends
return $form;
}
}

View file

@ -2,8 +2,6 @@
final class ConpherenceWidgetController extends ConpherenceController {
private $conpherenceID;
private $conpherence;
private $userPreferences;
public function setUserPreferences(PhabricatorUserPreferences $pref) {
@ -15,33 +13,11 @@ final class ConpherenceWidgetController extends ConpherenceController {
return $this->userPreferences;
}
public function setConpherence(ConpherenceThread $conpherence) {
$this->conpherence = $conpherence;
return $this;
}
public function getConpherence() {
return $this->conpherence;
}
public function setConpherenceID($conpherence_id) {
$this->conpherenceID = $conpherence_id;
return $this;
}
public function getConpherenceID() {
return $this->conpherenceID;
}
public function willProcessRequest(array $data) {
$this->setConpherenceID(idx($data, 'id'));
}
public function processRequest() {
public function handleRequest(AphrontRequest $request) {
$request = $this->getRequest();
$user = $request->getUser();
$conpherence_id = $this->getConpherenceID();
$conpherence_id = $request->getURIData('id');
if (!$conpherence_id) {
return new Aphront404Response();
}
@ -54,8 +30,25 @@ final class ConpherenceWidgetController extends ConpherenceController {
$this->setUserPreferences($user->loadPreferences());
$widgets = $this->renderWidgetPaneContent();
$content = $widgets;
switch ($request->getStr('widget')) {
case 'widgets-people':
$content = $this->renderPeopleWidgetPaneContent();
break;
case 'widgets-files':
$content = $this->renderFileWidgetPaneContent();
break;
case 'widgets-calendar':
$widget = $this->renderCalendarWidgetPaneContent();
$content = phutil_implode_html('', $widget);
break;
case 'widgets-settings':
$content = $this->renderSettingsWidgetPaneContent();
break;
default:
$widgets = $this->renderWidgetPaneContent();
$content = $widgets;
break;
}
return id(new AphrontAjaxResponse())->setContent($content);
}
@ -89,11 +82,8 @@ final class ConpherenceWidgetController extends ConpherenceController {
'id' => 'widgets-people',
'sigil' => 'widgets-people',
),
id(new ConpherencePeopleWidgetView())
->setUser($user)
->setConpherence($conpherence)
->setUpdateURI($this->getWidgetURI()));
$widgets[] = javelin_tag(
$this->renderPeopleWidgetPaneContent());
$widgets[] = javelin_tag(
'div',
array(
'class' => 'widgets-body',
@ -101,11 +91,8 @@ final class ConpherenceWidgetController extends ConpherenceController {
'sigil' => 'widgets-files',
'style' => 'display: none;',
),
id(new ConpherenceFileWidgetView())
->setUser($user)
->setConpherence($conpherence)
->setUpdateURI($this->getWidgetURI()));
$widgets[] = phutil_tag(
$this->renderFileWidgetPaneContent());
$widgets[] = phutil_tag(
'div',
array(
'class' => 'widgets-body',
@ -127,11 +114,25 @@ final class ConpherenceWidgetController extends ConpherenceController {
return array('widgets' => phutil_implode_html('', $widgets));
}
private function renderPeopleWidgetPaneContent() {
return id(new ConpherencePeopleWidgetView())
->setUser($this->getViewer())
->setConpherence($this->getConpherence())
->setUpdateURI($this->getWidgetURI());
}
private function renderFileWidgetPaneContent() {
return id(new ConpherenceFileWidgetView())
->setUser($this->getViewer())
->setConpherence($this->getConpherence())
->setUpdateURI($this->getWidgetURI());
}
private function renderSettingsWidgetPaneContent() {
$user = $this->getRequest()->getUser();
$viewer = $this->getViewer();
$conpherence = $this->getConpherence();
$participants = $conpherence->getParticipants();
$participant = $participants[$user->getPHID()];
$participant = $participants[$viewer->getPHID()];
$default = ConpherenceSettings::EMAIL_ALWAYS;
$preference = $this->getUserPreferences();
if ($preference) {
@ -177,7 +178,7 @@ final class ConpherenceWidgetController extends ConpherenceController {
);
return phabricator_form(
$user,
$viewer,
array(
'method' => 'POST',
'action' => $this->getWidgetURI(),

View file

@ -2,45 +2,76 @@
final class ConpherenceDurableColumnView extends AphrontTagView {
private $conpherences;
private $selectedConpherence;
private $transactions;
public function setConpherences(array $conpherences) {
assert_instances_of($conpherences, 'ConpherenceThread');
$this->conpherences = $conpherences;
return $this;
}
public function getConpherences() {
return $this->conpherences;
}
public function setSelectedConpherence(
ConpherenceThread $conpherence = null) {
$this->selectedConpherence = $conpherence;
return $this;
}
public function getSelectedConpherence() {
return $this->selectedConpherence;
}
public function setTransactions(array $transactions) {
assert_instances_of($transactions, 'ConpherenceTransaction');
$this->transactions = $transactions;
return $this;
}
public function getTransactions() {
return $this->transactions;
}
protected function getTagAttributes() {
return array(
'id' => 'durable-column',
'id' => 'conpherence-durable-column',
'class' => 'conpherence-durable-column',
'style' => 'display: none;',
'sigil' => 'conpherence-durable-column',
);
}
protected function getTagContent() {
Javelin::initBehavior('durable-column');
$classes = array();
$classes[] = 'conpherence-durable-column-header';
$classes[] = 'sprite-main-header';
$classes[] = 'main-header-'.PhabricatorEnv::getEnvConfig('ui.header-color');
$loading_mask = phutil_tag(
'div',
array(
'class' => 'loading-mask',
),
'');
$header = phutil_tag(
'div',
array(
'class' => implode(' ', $classes),
),
phutil_tag(
'div',
array(
'class' => 'conpherence-durable-column-header-text',
),
pht('Column Prototype')));
$this->buildHeader());
$icon_bar = phutil_tag(
'div',
array(
'class' => 'conpherence-durable-column-icon-bar',
),
null); // <-- TODO: Icon buttons go here.
$this->buildIconBar());
$copy = pht(
'This is a very early prototype of a persistent column. It is not '.
'expected to work yet, and leaving it open will activate other new '.
'features which will break things. Press "\\" (backslash) on your '.
'keyboard to close it now.');
$transactions = $this->buildTransactions();
$content = phutil_tag(
'div',
@ -53,19 +84,15 @@ final class ConpherenceDurableColumnView extends AphrontTagView {
'id' => 'conpherence-durable-column-content',
'class' => 'conpherence-durable-column-frame',
),
phutil_tag(
javelin_tag(
'div',
array(
'class' => 'conpherence-durable-column-content',
'class' => 'conpherence-durable-column-transactions',
'sigil' => 'conpherence-durable-column-transactions',
),
$copy)));
$transactions)));
$input = phutil_tag(
'textarea',
array(
'class' => 'conpherence-durable-column-textarea',
'placeholder' => pht('Box for text...'),
));
$input = $this->buildTextInput();
$footer = phutil_tag(
'div',
@ -73,26 +100,23 @@ final class ConpherenceDurableColumnView extends AphrontTagView {
'class' => 'conpherence-durable-column-footer',
),
array(
phutil_tag(
'button',
array(
'class' => 'grey',
),
pht('Send')),
$this->buildSendButton(),
phutil_tag(
'div',
array(
'class' => 'conpherence-durable-column-status',
),
pht('Status Text')),
$this->buildStatusText()),
));
return array(
$loading_mask,
$header,
phutil_tag(
javelin_tag(
'div',
array(
'class' => 'conpherence-durable-column-body',
'sigil' => 'conpherence-durable-column-body',
),
array(
$icon_bar,
@ -103,4 +127,161 @@ final class ConpherenceDurableColumnView extends AphrontTagView {
);
}
private function buildIconBar() {
return null;
}
private function buildHeader() {
$conpherence = $this->getSelectedConpherence();
if (!$conpherence) {
$title = pht('Loading...');
$settings_button = null;
$settings_menu = null;
} else {
$bubble_id = celerity_generate_unique_node_id();
$dropdown_id = celerity_generate_unique_node_id();
$settings_list = new PHUIListView();
$cw_registry =
ConpherenceWidgetConfigConstants::getWidgetRegistry();
$first = true;
foreach ($cw_registry as $widget => $config) {
$settings_list->addMenuItem(
id(new PHUIListItemView())
->setHref('#')
->setDisabled($first)
->setName($config['name'])
->setIcon($config['icon'])
->addSigil('conpherence-durable-column-widget-selected')
->setMetadata(array(
'widget' => $widget,
)));
$first = false;
}
$settings_menu = phutil_tag(
'div',
array(
'id' => $dropdown_id,
'class' => 'phabricator-main-menu-dropdown phui-list-sidenav '.
'conpherence-settings-dropdown',
'sigil' => 'phabricator-notification-menu',
'style' => 'display: none',
),
$settings_list);
Javelin::initBehavior(
'aphlict-dropdown',
array(
'bubbleID' => $bubble_id,
'dropdownID' => $dropdown_id,
'local' => true,
'containerDivID' => 'conpherence-durable-column',
));
$item = id(new PHUIListItemView())
->setName(pht('Settings'))
->setIcon('fa-bars')
->addClass('core-menu-item')
->addSigil('conpherence-settings-menu')
->setID($bubble_id)
->setAural(pht('Settings'))
->setOrder(300);
$settings_button = id(new PHUIListView())
->addMenuItem($item)
->addClass('phabricator-dark-menu')
->addClass('phabricator-application-menu');
$title = $conpherence->getTitle();
if (!$title) {
$title = pht('[No Title]');
}
}
return
phutil_tag(
'div',
array(
'class' => 'conpherence-durable-column-header',
),
array(
phutil_tag(
'div',
array(
'class' => 'conpherence-durable-column-header-text',
),
$title),
$settings_button,
$settings_menu,));
}
private function buildTransactions() {
$conpherence = $this->getSelectedConpherence();
if (!$conpherence) {
return pht('Loading...');
}
$data = ConpherenceTransactionView::renderTransactions(
$this->getUser(),
$conpherence,
$full_display = false);
$messages = ConpherenceTransactionView::renderMessagePaneContent(
$data['transactions'],
$data['oldest_transaction_id']);
return $messages;
}
private function buildTextInput() {
$conpherence = $this->getSelectedConpherence();
$textarea = javelin_tag(
'textarea',
array(
'name' => 'text',
'class' => 'conpherence-durable-column-textarea',
'sigil' => 'conpherence-durable-column-textarea',
'placeholder' => pht('Send a message...'),
));
if (!$conpherence) {
return $textarea;
}
$id = $conpherence->getID();
return phabricator_form(
$this->getUser(),
array(
'method' => 'POST',
'action' => '/conpherence/update/'.$id.'/',
'sigil' => 'conpherence-message-form',
),
array(
$textarea,
phutil_tag(
'input',
array(
'type' => 'hidden',
'name' => 'action',
'value' => ConpherenceUpdateActions::MESSAGE,
)),));
}
private function buildStatusText() {
return null;
}
private function buildSendButton() {
return javelin_tag(
'button',
array(
'class' => 'grey',
'sigil' => 'conpherence-send-message',
),
pht('Send'));
}
}

View file

@ -79,52 +79,7 @@ final class ConpherenceLayoutView extends AphrontView {
$this->initBehavior(
'conpherence-widget-pane',
array(
'widgetBaseUpdateURI' => $this->baseURI.'update/',
'widgetRegistry' => array(
'conpherence-message-pane' => array(
'name' => pht('Thread'),
'icon' => 'fa-comment',
'deviceOnly' => true,
'hasCreate' => false,
),
'widgets-people' => array(
'name' => pht('Participants'),
'icon' => 'fa-users',
'deviceOnly' => false,
'hasCreate' => true,
'createData' => array(
'refreshFromResponse' => true,
'action' => ConpherenceUpdateActions::ADD_PERSON,
'customHref' => null,
),
),
'widgets-files' => array(
'name' => pht('Files'),
'icon' => 'fa-files-o',
'deviceOnly' => false,
'hasCreate' => false,
),
'widgets-calendar' => array(
'name' => pht('Calendar'),
'icon' => 'fa-calendar',
'deviceOnly' => false,
'hasCreate' => true,
'createData' => array(
'refreshFromResponse' => false,
'action' => ConpherenceUpdateActions::ADD_STATUS,
'customHref' => '/calendar/event/create/',
),
),
'widgets-settings' => array(
'name' => pht('Settings'),
'icon' => 'fa-wrench',
'deviceOnly' => false,
'hasCreate' => false,
),
),
));
ConpherenceWidgetConfigConstants::getWidgetPaneBehaviorConfig());
return javelin_tag(
'div',

View file

@ -5,6 +5,8 @@ final class ConpherenceTransactionView extends AphrontView {
private $conpherenceTransaction;
private $handles;
private $markupEngine;
private $showImages = true;
private $showContentSource = true;
public function setMarkupEngine(PhabricatorMarkupEngine $markup_engine) {
$this->markupEngine = $markup_engine;
@ -30,6 +32,24 @@ final class ConpherenceTransactionView extends AphrontView {
return $this->conpherenceTransaction;
}
public function setShowImages($bool) {
$this->showImages = $bool;
return $this;
}
private function getShowImages() {
return $this->showImages;
}
public function setShowContentSource($bool) {
$this->showContentSource = $bool;
return $this;
}
private function getShowContentSource() {
return $this->showContentSource;
}
public function render() {
$user = $this->getUser();
$transaction = $this->getConpherenceTransaction();
@ -59,8 +79,10 @@ final class ConpherenceTransactionView extends AphrontView {
$author = $handles[$transaction->getAuthorPHID()];
$transaction_view = id(new PhabricatorTransactionView())
->setUser($user)
->setEpoch($transaction->getDateCreated())
->setContentSource($transaction->getContentSource());
->setEpoch($transaction->getDateCreated());
if ($this->getShowContentSource()) {
$transaction_view->setContentSource($transaction->getContentSource());
}
$content = null;
$content_class = null;
@ -83,9 +105,10 @@ final class ConpherenceTransactionView extends AphrontView {
$comment,
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
$content_class = 'conpherence-message phabricator-remarkup';
$transaction_view
->setImageURI($author->getImageURI())
->setActions(array($author->renderLink()));
if ($this->getShowImages()) {
$transaction_view->setImageURI($author->getImageURI());
}
$transaction_view->setActions(array($author->renderLink()));
break;
}
@ -100,4 +123,108 @@ final class ConpherenceTransactionView extends AphrontView {
return $transaction_view->render();
}
public static function renderTransactions(
PhabricatorUser $user,
ConpherenceThread $conpherence,
$full_display = true) {
$transactions = $conpherence->getTransactions();
$oldest_transaction_id = 0;
$too_many = ConpherenceThreadQuery::TRANSACTION_LIMIT + 1;
if (count($transactions) == $too_many) {
$last_transaction = end($transactions);
unset($transactions[$last_transaction->getID()]);
$oldest_transaction = end($transactions);
$oldest_transaction_id = $oldest_transaction->getID();
}
$transactions = array_reverse($transactions);
$handles = $conpherence->getHandles();
$rendered_transactions = array();
$engine = id(new PhabricatorMarkupEngine())
->setViewer($user);
foreach ($transactions as $key => $transaction) {
if ($transaction->shouldHide()) {
unset($transactions[$key]);
continue;
}
if ($transaction->getComment()) {
$engine->addObject(
$transaction->getComment(),
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
}
}
$engine->process();
// we're going to insert a dummy date marker transaction for breaks
// between days. some setup required!
$previous_transaction = null;
$date_marker_transaction = id(new ConpherenceTransaction())
->setTransactionType(ConpherenceTransactionType::TYPE_DATE_MARKER)
->makeEphemeral();
$date_marker_transaction_view = id(new ConpherenceTransactionView())
->setUser($user)
->setConpherenceTransaction($date_marker_transaction)
->setHandles($handles)
->setShowImages($full_display)
->setShowContentSource($full_display)
->setMarkupEngine($engine);
foreach ($transactions as $transaction) {
if ($previous_transaction) {
$previous_day = phabricator_format_local_time(
$previous_transaction->getDateCreated(),
$user,
'Ymd');
$current_day = phabricator_format_local_time(
$transaction->getDateCreated(),
$user,
'Ymd');
// date marker transaction time!
if ($previous_day != $current_day) {
$date_marker_transaction->setDateCreated(
$transaction->getDateCreated());
$rendered_transactions[] = $date_marker_transaction_view->render();
}
}
$rendered_transactions[] = id(new ConpherenceTransactionView())
->setUser($user)
->setConpherenceTransaction($transaction)
->setHandles($handles)
->setMarkupEngine($engine)
->setShowImages($full_display)
->setShowContentSource($full_display)
->render();
$previous_transaction = $transaction;
}
$latest_transaction_id = $transaction->getID();
return array(
'transactions' => $rendered_transactions,
'latest_transaction' => $transaction,
'latest_transaction_id' => $latest_transaction_id,
'oldest_transaction_id' => $oldest_transaction_id,
);
}
public static function renderMessagePaneContent(
array $transactions,
$oldest_transaction_id) {
$scrollbutton = '';
if ($oldest_transaction_id) {
$scrollbutton = javelin_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'show-older-messages',
'class' => 'conpherence-show-older-messages',
'meta' => array(
'oldest_transaction_id' => $oldest_transaction_id,
),
),
pht('Show Older Messages'));
}
return hsprintf('%s%s', $scrollbutton, $transactions);
}
}

View file

@ -80,7 +80,14 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
}
public function getShowDurableColumn() {
return $this->showDurableColumn;
$request = $this->getRequest();
if ($request) {
$viewer = $request->getUser();
return PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorConpherenceApplication',
$viewer);
}
return false;
}
public function getTitle() {
@ -391,7 +398,12 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
$durable_column = null;
if ($this->getShowDurableColumn()) {
$durable_column = new ConpherenceDurableColumnView();
$durable_column = id(new ConpherenceDurableColumnView())
->setSelectedConpherence(null)
->setUser($user);
Javelin::initBehavior(
'durable-column',
array());
}
return phutil_tag(

View file

@ -13,7 +13,22 @@
right: 0;
width: 300px;
background: #fff;
}
.conpherence-durable-column .loading-mask {
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 300px;
background: #fff;
display: none;
opacity: .6;
z-index: 2;
}
.conpherence-durable-column.loading .loading-mask {
display: block;
}
.conpherence-durable-column-header {
@ -21,10 +36,16 @@
border-left: 1px solid #000;
}
.conpherence-durable-column-header .conpherence-settings-dropdown {
z-index: 1;
}
.conpherence-durable-column-header-text {
float: left;
padding: 12px 16px;
font-size: 15px;
color: rgba(255,255,255,.8);
width: 230px;
}
.conpherence-durable-column-header-text:hover {
@ -55,10 +76,36 @@
overflow: hidden;
}
.conpherence-durable-column-content {
.conpherence-durable-column-transactions {
padding: 8px 12px;
}
.conpherence-durable-column-transactions .phabricator-transaction-view {
background: none;
margin: 0 10px 0 0;
padding: 0;
}
.conpherence-durable-column-transactions .phabricator-transaction-detail {
border: 0;
margin: 0;
}
.conpherence-durable-column-transactions .phabricator-transaction-detail
.phabricator-transaction-header {
background: none;
padding: 0;
}
.conpherence-durable-column-transactions .phabricator-transaction-detail
.phabricator-transaction-header .phabricator-transaction-info {
margin: 3px 0px 0px 0px;
}
.conpherence-durable-column-transactions .phabricator-transaction-detail
.phabricator-transaction-content {
padding: 0;
}
.conpherence-durable-column-textarea {
position: absolute;
left: 0;
@ -82,6 +129,239 @@
box-shadow: none;
}
/* participants widget */
.conpherence-durable-column-body .person-entry {
padding: 8px 0 0 8px;
}
.conpherence-durable-column-body .person-entry a {
float: left;
font-weight: bold;
line-height: 20px;
}
.conpherence-durable-column-body .person-entry a img {
height: 35px;
width: 35px;
}
.conpherence-durable-column-body .person-entry .pic {
float: left;
margin: 0 8px 0 0;
width: 35px;
padding: 0;
}
.conpherence-durable-column-body .person-entry .remove {
float: right;
width: 20px;
font-size: 18px;
padding: 5px 0 8px 0;
}
.conpherence-durable-column-body .person-entry .remove:hover {
text-decoration: none;
}
.conpherence-durable-column-body .person-entry .remove .close-icon {
color: #bfbfbf;
}
.conpherence-durable-column-body .person-entry .remove:hover .close-icon {
color: #000;
}
/* files widget */
.conpherence-durable-column-body .no-files {
width: 100%;
padding: 20px 0px;
text-align: center;
color: #555;
}
.conpherence-durable-column-body .file-entry {
padding: 10px 0;
margin: 0 5px 0 10px;
border-bottom: 1px solid #e7e7e7;
}
.conpherence-durable-column-body .file-icon {
width: 32px;
height: 32px;
float: left;
font-size: 30px;
}
.conpherence-durable-column-body .file-title {
display: block;
position: relative;
top: -4px;
left: 2px;
overflow-x: hidden;
width: 180px;
font-weight: bold;
text-overflow: ellipsis;
white-space: nowrap;
}
.conpherence-durable-column-body .file-uploaded-by {
color: #a1a5a9;
position: relative;
top: 0px;
left: 2px;
width: 170px;
font-size: 11px;
}
/* calendar widget */
.conpherence-durable-column-body .aphront-multi-column-view {
width: 100%;
}
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column {
background: white;
border-right: 1px solid #bfbfbf;
text-align: center;
}
.conpherence-durable-column-body .no-events {
color: {$lightgreytext};
}
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column-last {
border-right: 0;
}
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .day-column,
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .day-column-active {
color: #bfbfbf;
background-color: white;
font-weight: bold;
padding: 0px 0px 10px 0px;
}
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .day-column-active {
color: black;
}
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .present ,
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .sporadic ,
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .away {
height: 10px;
margin: 5px 0px 5px 0px;
width: 100%;
}
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .present {
background-color: white;
}
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .sporadic {
background-color: rgb(222, 226, 232);
}
.conpherence-durable-column-body .aphront-multi-column-view
.aphront-multi-column-column .away {
background-color: rgb(102, 204, 255);
}
.conpherence-durable-column-body .aphront-multi-column-view
.day-name {
padding: 5px 0px 0px 0px;
font-size: 12px;
}
.conpherence-durable-column-body .aphront-multi-column-view
.day-number {
font-size: 16px;
padding: 0 0 5px 0;
}
.conpherence-durable-column-body .day-header {
overflow: hidden;
background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
border-top: 1px solid #e7e7e7;
border-bottom: 1px solid #d7d7d7;
padding: 5px 10px 5px 10px;
}
.conpherence-durable-column-body .day-header.today {
background-image: linear-gradient(to bottom, #3b86c4, #2b628f);
background-image: -webkit-linear-gradient(top, #3b86c4, #2b628f);
border-top: none;
border-bottom: none;
}
.conpherence-durable-column-body .day-header.today .day-name,
.conpherence-durable-column-body .day-header.today .day-date {
color: #fff;
}
.conpherence-durable-column-body .day-header .day-name {
float: left;
color: #555759;
font-weight: bold;
text-transform: uppercase;
font-size: 11px;
}
.conpherence-durable-column-body .day-header .day-date {
float: right;
color: #555759;
font-size: 11px;
}
.conpherence-durable-column-body .top-border {
border-top: 1px solid #E7E7E7;
}
.conpherence-durable-column-body .user-status {
padding: 10px 0px 10px 0px;
margin: 0px 0px 0px 10px;
}
.conpherence-durable-column-body .user-status .icon {
border-radius: 8px;
height: 14px;
width: 14px;
margin-top: 7px;
float: left;
}
.conpherence-durable-column-body .sporadic .icon {
background-color: rgb(222, 226, 232);
}
.conpherence-durable-column-body .away .icon {
background-color: rgb(102, 204, 255);
}
.conpherence-durable-column-body .user-status .description {
width: 195px;
text-overflow: ellipsis;
margin: 0 0 0px 20px;
}
.conpherence-durable-column-body .user-status .participant {
font-size: 11px;
color: {$lightgreytext};
padding-top: 2px;
}
/* settings widget */
.conpherence-durable-column-body .notifications-update {
margin: 0px 12px;
}
.conpherence-durable-column-body .aphront-form-input {
margin: 8px 12px;
width: 100%;
}
.conpherence-durable-column-footer {
position: absolute;
height: 26px;

View file

@ -75,6 +75,7 @@
z-index: 5;
}
.conpherence-durable-column-header,
.phabricator-main-menu {
z-index: 6;
}

View file

@ -148,7 +148,11 @@ JX.behavior('aphlict-dropdown', function(config, statics) {
JX.DOM.show(dropdown);
p.y = null;
if (config.right) {
if (config.containerDivID) {
var pc = JX.$V(JX.$(config.containerDivID));
p.x -= (JX.Vector.getDim(dropdown).x - JX.Vector.getDim(bubble).x +
pc.x);
} else if (config.right) {
p.x -= (JX.Vector.getDim(dropdown).x - JX.Vector.getDim(bubble).x);
} else {
p.x -= 6;

View file

@ -6,19 +6,113 @@
* javelin-scrollbar
* javelin-quicksand
* phabricator-keyboard-shortcut
* javelin-behavior-conpherence-widget-pane
*/
JX.behavior('durable-column', function() {
var shouldInit = true;
var loadThreadID = null;
var loadedThreadID = null;
var loadedThreadPHID = null;
var latestTransactionID = null;
var frame = JX.$('phabricator-standard-page');
var quick = JX.$('phabricator-standard-page-body');
var show = false;
new JX.KeyboardShortcut('\\', 'Toggle Column (Prototype)')
// TODO - this "upating" stuff is a copy from behavior-pontificate
// TODO: This isn't very clean. When you submit a message, you may get a
// notification about it back before you get the rendered message back. To
// prevent this, we keep track of whether we're currently updating the
// thread. If we are, we hold further updates until the response comes
// back.
// After the response returns, we'll do another update if we know about
// a transaction newer than the one we got back from the server.
var updating = null;
// Copy continues with slight modifications for how we store data now
JX.Stratcom.listen('aphlict-server-message', null, function(e) {
var message = e.getData();
if (message.type != 'message') {
// Not a message event.
return;
}
if (message.threadPHID != loadedThreadPHID) {
// Message event for some thread other than the visible one.
return;
}
if (message.messageID <= latestTransactionID) {
// Message event for something we already know about.
return;
}
// If we're currently updating, wait for the update to complete.
// If this notification tells us about a message which is newer than the
// newest one we know to exist, keep track of it so we can update once
// the in-flight update finishes.
if (updating && updating.threadPHID == loadedThreadPHID) {
if (message.messageID > updating.knownID) {
updating.knownID = message.messageID;
return;
}
}
update_thread();
});
function update_thread() {
var params = {
action: 'load',
latest_transaction_id: latestTransactionID,
minimal_display: true
};
var uri = '/conpherence/update/' + loadedThreadID + '/';
var workflow = new JX.Workflow(uri)
.setData(params)
.setHandler(function(r) {
var messages = _getColumnMessagesNode();
JX.DOM.appendContent(messages, JX.$H(r.transactions));
messages.scrollTop = messages.scrollHeight;
latestTransactionID = r.latest_transaction_id;
});
sync_workflow(workflow);
}
function sync_workflow(workflow) {
updating = {
threadPHID: loadedThreadPHID,
knownID: latestTransactionID
};
workflow.listen('finally', function() {
var need_sync = (updating.knownID > latestTransactionID);
updating = null;
if (need_sync) {
update_thread();
}
});
workflow.start();
}
// end copy / hack of stuff with big ole TODO on it
new JX.KeyboardShortcut('\\', 'Toggle Conpherence Column')
.setHandler(function() {
show = !show;
JX.DOM.alterClass(frame, 'with-durable-column', show);
JX.$('durable-column').style.display = (show ? 'block' : 'none');
var column = JX.$('conpherence-durable-column');
if (show) {
JX.DOM.show(column);
loadThreadContent(loadThreadID);
} else {
JX.DOM.hide(column);
}
JX.Stratcom.invoke('resize');
JX.Quicksand.setFrame(show ? quick : null);
})
@ -28,4 +122,149 @@ JX.behavior('durable-column', function() {
JX.Quicksand.start();
JX.Stratcom.listen(
'click',
'conpherence-durable-column-widget-selected',
function (e) {
e.kill();
var data = e.getNodeData('conpherence-durable-column-widget-selected');
var widget = data.widget;
if (widget == 'conpherence-message-pane') {
return loadThreadContent(loadThreadID);
}
_markLoading(true);
var uri = '/conpherence/widget/' + loadThreadID + '/';
loadedThreadID = null;
var params = { widget : widget };
new JX.Workflow(uri)
.setData(params)
.setHandler(function(r) {
var body = _getColumnBodyNode();
JX.DOM.setContent(body, JX.$H(r));
new JX.Scrollbar(JX.$('conpherence-durable-column-content'));
_markLoading(false);
})
.start();
});
function _getColumnNode() {
return JX.$('conpherence-durable-column');
}
function _getColumnBodyNode() {
var column = JX.$('conpherence-durable-column');
return JX.DOM.find(
column,
'div',
'conpherence-durable-column-body');
}
function _getColumnMessagesNode() {
var column = JX.$('conpherence-durable-column');
return JX.DOM.find(
column,
'div',
'conpherence-durable-column-transactions');
}
function _getColumnFormNode() {
var column = JX.$('conpherence-durable-column');
return JX.DOM.find(
column,
'form',
'conpherence-message-form');
}
function _getColumnTextareaNode() {
var column = JX.$('conpherence-durable-column');
return JX.DOM.find(
column,
'textarea',
'conpherence-durable-column-textarea');
}
function _focusColumnTextareaNode() {
var textarea = _getColumnTextareaNode();
setTimeout(function() { JX.DOM.focus(textarea); }, 1);
}
function _markLoading(loading) {
var column = _getColumnNode();
JX.DOM.alterClass(column, 'loading', loading);
}
function loadThreadContent(thread_id) {
// loaded this thread already
if (loadedThreadID !== null && loadedThreadID == thread_id) {
return;
}
_markLoading(true);
var uri = '/conpherence/columnview/';
var params = null;
// We can pick a thread from the server the first time
if (shouldInit) {
shouldInit = false;
params = { shouldInit : true };
} else {
params = { id : thread_id };
}
var handler = function(r) {
var column = _getColumnNode();
var new_column = JX.$H(r.content);
loadedThreadID = r.threadID;
loadedThreadPHID = r.threadPHID;
loadThreadID = r.threadID;
latestTransactionID = r.latestTransactionID;
JX.DOM.replace(column, new_column);
JX.DOM.show(_getColumnNode());
new JX.Scrollbar(JX.$('conpherence-durable-column-content'));
_markLoading(false);
};
new JX.Workflow(uri)
.setData(params)
.setHandler(handler)
.start();
}
function _sendMessage(e) {
e.kill();
_markLoading(true);
var form = _getColumnFormNode();
var params = {
latest_transaction_id : latestTransactionID,
minimal_display : true
};
var workflow = JX.Workflow.newFromForm(form, params)
.setHandler(function(r) {
var messages = _getColumnMessagesNode();
JX.DOM.appendContent(messages, JX.$H(r.transactions));
messages.scrollTop = messages.scrollHeight;
var textarea = _getColumnTextareaNode();
textarea.value = '';
latestTransactionID = r.latest_transaction_id;
_markLoading(false);
_focusColumnTextareaNode();
});
sync_workflow(workflow);
}
JX.Stratcom.listen(
'click',
'conpherence-send-message',
_sendMessage);
JX.Stratcom.listen(
['submit', 'didSyntheticSubmit'],
'conpherence-message-form',
_sendMessage);
});