@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Rebuild Conpherence

Summary:
Minor rebuild / redesign of Conpherence. Most of this is new UX and tossing out things like widgets, device fallbacks. I expect some of the UI to get more polished after next pass, but most everything here is in place.

- Removed "Widgets", now just a single Participants pane
- Added "Topic"
- New header
- Settings, Edit are action icons
- Removed a lot of JS
- Simplified CSS as much as I could

Test Plan:
Desktop, Tablet, Mobile. Adding and removing people. Setting new topics, new rooms.

{F1828662}

{F1828669}

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Differential Revision: https://secure.phabricator.com/D16550

+582 -931
+53 -54
resources/celerity/map.php
··· 7 7 */ 8 8 return array( 9 9 'names' => array( 10 - 'conpherence.pkg.css' => 'fdc05791', 11 - 'core.pkg.css' => '1ca373de', 10 + 'conpherence.pkg.css' => '7bddd31a', 11 + 'core.pkg.css' => 'dc6d08e3', 12 12 'core.pkg.js' => '1d376fa9', 13 13 'darkconsole.pkg.js' => 'e7393ebb', 14 14 'differential.pkg.css' => '3fb7f532', ··· 19 19 'maniphest.pkg.js' => '949a7498', 20 20 'rsrc/css/aphront/aphront-bars.css' => '231ac33c', 21 21 'rsrc/css/aphront/dark-console.css' => 'f54bf286', 22 - 'rsrc/css/aphront/dialog-view.css' => '913c172e', 22 + 'rsrc/css/aphront/dialog-view.css' => '593d3f67', 23 23 'rsrc/css/aphront/lightbox-attachment.css' => '7acac05d', 24 24 'rsrc/css/aphront/list-filter-view.css' => '5d6f0526', 25 25 'rsrc/css/aphront/multi-column.css' => 'fd18389d', ··· 46 46 'rsrc/css/application/config/setup-issue.css' => 'f794cfc3', 47 47 'rsrc/css/application/config/unhandled-exception.css' => '4c96257a', 48 48 'rsrc/css/application/conpherence/durable-column.css' => '194ac487', 49 + 'rsrc/css/application/conpherence/header-pane.css' => 'bdba8a5b', 49 50 'rsrc/css/application/conpherence/menu.css' => '8344d122', 50 - 'rsrc/css/application/conpherence/message-pane.css' => 'ee0e27be', 51 + 'rsrc/css/application/conpherence/message-pane.css' => 'c075e8fe', 51 52 'rsrc/css/application/conpherence/notification.css' => '6cdcc253', 52 - 'rsrc/css/application/conpherence/transaction.css' => '2c71247c', 53 - 'rsrc/css/application/conpherence/update.css' => 'faf6be09', 54 - 'rsrc/css/application/conpherence/widget-pane.css' => 'a131d5b6', 53 + 'rsrc/css/application/conpherence/transaction.css' => '46253e19', 54 + 'rsrc/css/application/conpherence/update.css' => '53bc527a', 55 + 'rsrc/css/application/conpherence/widget-pane.css' => '827a21f1', 55 56 'rsrc/css/application/contentsource/content-source-view.css' => '4b8b05d4', 56 57 'rsrc/css/application/countdown/timer.css' => '16c52f5c', 57 58 'rsrc/css/application/daemon/bulk-job.css' => 'df9c1d4a', ··· 108 109 'rsrc/css/core/core.css' => 'd0801452', 109 110 'rsrc/css/core/remarkup.css' => 'cd912f2c', 110 111 'rsrc/css/core/syntax.css' => '769d3498', 111 - 'rsrc/css/core/z-index.css' => '2b01a823', 112 + 'rsrc/css/core/z-index.css' => 'a847e919', 112 113 'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa', 113 114 'rsrc/css/font/font-aleo.css' => '8bdb2835', 114 115 'rsrc/css/font/font-awesome.css' => '2b7ebbcc', ··· 437 438 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => '01774ab2', 438 439 'rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js' => 'cf86d16a', 439 440 'rsrc/js/application/conpherence/behavior-durable-column.js' => 'd3506890', 440 - 'rsrc/js/application/conpherence/behavior-menu.js' => '1d45c74d', 441 + 'rsrc/js/application/conpherence/behavior-menu.js' => '7a2f5952', 442 + 'rsrc/js/application/conpherence/behavior-participants-pane.js' => '08872fb7', 441 443 'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861', 442 444 'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3', 443 - 'rsrc/js/application/conpherence/behavior-toggle-widget.js' => 'b151bbbc', 444 - 'rsrc/js/application/conpherence/behavior-widget-pane.js' => '65845387', 445 + 'rsrc/js/application/conpherence/behavior-toggle-widget.js' => '9bdbbab0', 445 446 'rsrc/js/application/countdown/timer.js' => 'e4cc26b3', 446 447 'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => 'edf8a145', 447 448 'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e', ··· 601 602 'almanac-css' => 'dbb9b3af', 602 603 'aphront-bars' => '231ac33c', 603 604 'aphront-dark-console-css' => 'f54bf286', 604 - 'aphront-dialog-view-css' => '913c172e', 605 + 'aphront-dialog-view-css' => '593d3f67', 605 606 'aphront-list-filter-view-css' => '5d6f0526', 606 607 'aphront-multi-column-view-css' => 'fd18389d', 607 608 'aphront-panel-view-css' => '8427b78d', ··· 617 618 'config-options-css' => '0ede4c9b', 618 619 'config-page-css' => '8798e14f', 619 620 'conpherence-durable-column-view' => '194ac487', 621 + 'conpherence-header-pane-css' => 'bdba8a5b', 620 622 'conpherence-menu-css' => '8344d122', 621 - 'conpherence-message-pane-css' => 'ee0e27be', 623 + 'conpherence-message-pane-css' => 'c075e8fe', 622 624 'conpherence-notification-css' => '6cdcc253', 623 625 'conpherence-thread-manager' => '01774ab2', 624 - 'conpherence-transaction-css' => '2c71247c', 625 - 'conpherence-update-css' => 'faf6be09', 626 - 'conpherence-widget-pane-css' => 'a131d5b6', 626 + 'conpherence-transaction-css' => '46253e19', 627 + 'conpherence-update-css' => '53bc527a', 628 + 'conpherence-widget-pane-css' => '827a21f1', 627 629 'd3' => 'a11a5ff2', 628 630 'differential-changeset-view-css' => '9ef7d354', 629 631 'differential-core-view-css' => '5b7b8ff4', ··· 665 667 'javelin-behavior-comment-actions' => '0300eae6', 666 668 'javelin-behavior-config-reorder-fields' => 'b6993408', 667 669 'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a', 668 - 'javelin-behavior-conpherence-menu' => '1d45c74d', 670 + 'javelin-behavior-conpherence-menu' => '7a2f5952', 671 + 'javelin-behavior-conpherence-participants-pane' => '08872fb7', 669 672 'javelin-behavior-conpherence-pontificate' => '21ba5861', 670 - 'javelin-behavior-conpherence-widget-pane' => '65845387', 671 673 'javelin-behavior-countdown-timer' => 'e4cc26b3', 672 674 'javelin-behavior-dark-console' => 'f411b6ae', 673 675 'javelin-behavior-dashboard-async-panel' => '469c0d9e', ··· 773 775 'javelin-behavior-test-payment-form' => 'fc91ab6c', 774 776 'javelin-behavior-time-typeahead' => '522431f7', 775 777 'javelin-behavior-toggle-class' => '92b9ec77', 776 - 'javelin-behavior-toggle-widget' => 'b151bbbc', 778 + 'javelin-behavior-toggle-widget' => '9bdbbab0', 777 779 'javelin-behavior-typeahead-browse' => '635de1ec', 778 780 'javelin-behavior-typeahead-search' => '93d0c9e3', 779 781 'javelin-behavior-view-placeholder' => '47830651', ··· 881 883 'phabricator-uiexample-reactor-select' => 'a155550f', 882 884 'phabricator-uiexample-reactor-sendclass' => '1def2711', 883 885 'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee', 884 - 'phabricator-zindex-css' => '2b01a823', 886 + 'phabricator-zindex-css' => 'a847e919', 885 887 'phame-css' => '8efb0729', 886 888 'pholio-css' => 'ca89d380', 887 889 'pholio-edit-css' => '07676f51', ··· 1057 1059 'javelin-stratcom', 1058 1060 'javelin-vector', 1059 1061 ), 1062 + '08872fb7' => array( 1063 + 'javelin-behavior', 1064 + 'javelin-dom', 1065 + 'javelin-stratcom', 1066 + 'javelin-workflow', 1067 + 'javelin-util', 1068 + 'phabricator-notification', 1069 + 'conpherence-thread-manager', 1070 + ), 1060 1071 '0a0b10e9' => array( 1061 1072 'javelin-behavior', 1062 1073 'javelin-stratcom', ··· 1135 1146 'javelin-request', 1136 1147 'javelin-uri', 1137 1148 ), 1138 - '1d45c74d' => array( 1139 - 'javelin-behavior', 1140 - 'javelin-dom', 1141 - 'javelin-util', 1142 - 'javelin-stratcom', 1143 - 'javelin-workflow', 1144 - 'javelin-behavior-device', 1145 - 'javelin-history', 1146 - 'javelin-vector', 1147 - 'javelin-scrollbar', 1148 - 'phabricator-title', 1149 - 'phabricator-shaped-request', 1150 - 'conpherence-thread-manager', 1151 - ), 1152 1149 '1def2711' => array( 1153 1150 'javelin-install', 1154 1151 'javelin-dom', ··· 1484 1481 'javelin-request', 1485 1482 'javelin-workflow', 1486 1483 ), 1487 - 65845387 => array( 1488 - 'javelin-behavior', 1489 - 'javelin-dom', 1490 - 'javelin-stratcom', 1491 - 'javelin-workflow', 1492 - 'javelin-util', 1493 - 'phabricator-notification', 1494 - 'javelin-behavior-device', 1495 - 'phuix-dropdown-menu', 1496 - 'phuix-action-list-view', 1497 - 'phuix-action-view', 1498 - 'conpherence-thread-manager', 1499 - ), 1500 1484 '680ea2c8' => array( 1501 1485 'javelin-install', 1502 1486 'javelin-dom', ··· 1587 1571 'javelin-behavior', 1588 1572 'javelin-quicksand', 1589 1573 ), 1574 + '7a2f5952' => array( 1575 + 'javelin-behavior', 1576 + 'javelin-dom', 1577 + 'javelin-util', 1578 + 'javelin-stratcom', 1579 + 'javelin-workflow', 1580 + 'javelin-behavior-device', 1581 + 'javelin-history', 1582 + 'javelin-vector', 1583 + 'javelin-scrollbar', 1584 + 'phabricator-title', 1585 + 'phabricator-shaped-request', 1586 + 'conpherence-thread-manager', 1587 + ), 1590 1588 '7a68dda3' => array( 1591 1589 'owners-path-editor', 1592 1590 'javelin-behavior', ··· 1755 1753 'phabricator-phtize', 1756 1754 'changeset-view-manager', 1757 1755 ), 1756 + '9bdbbab0' => array( 1757 + 'javelin-behavior', 1758 + 'javelin-dom', 1759 + 'javelin-util', 1760 + 'javelin-workflow', 1761 + 'javelin-stratcom', 1762 + ), 1758 1763 '9ef7d354' => array( 1759 1764 'phui-inline-comment-view-css', 1760 1765 ), ··· 1849 1854 'javelin-request', 1850 1855 'javelin-util', 1851 1856 'phabricator-shaped-request', 1852 - ), 1853 - 'b151bbbc' => array( 1854 - 'javelin-behavior', 1855 - 'javelin-dom', 1856 - 'javelin-util', 1857 - 'javelin-workflow', 1858 - 'javelin-stratcom', 1859 1857 ), 1860 1858 'b1f0ccee' => array( 1861 1859 'javelin-install', ··· 2311 2309 'conpherence-transaction-css', 2312 2310 'conpherence-update-css', 2313 2311 'conpherence-widget-pane-css', 2312 + 'conpherence-header-pane-css', 2314 2313 ), 2315 2314 'core.pkg.css' => array( 2316 2315 'phabricator-core-css',
+1
resources/celerity/packages.php
··· 159 159 'conpherence-transaction-css', 160 160 'conpherence-update-css', 161 161 'conpherence-widget-pane-css', 162 + 'conpherence-header-pane-css', 162 163 ), 163 164 'differential.pkg.css' => array( 164 165 'differential-core-view-css',
+2
resources/sql/autopatches/20160913.conpherence.topic.1.sql
··· 1 + ALTER TABLE {$NAMESPACE}_conpherence.conpherence_thread 2 + ADD topic VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT};
+4 -8
src/__phutil_library_map__.php
··· 300 300 'ConpherenceNewRoomController' => 'applications/conpherence/controller/ConpherenceNewRoomController.php', 301 301 'ConpherenceNotificationPanelController' => 'applications/conpherence/controller/ConpherenceNotificationPanelController.php', 302 302 'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php', 303 + 'ConpherenceParticipantController' => 'applications/conpherence/controller/ConpherenceParticipantController.php', 303 304 'ConpherenceParticipantCountQuery' => 'applications/conpherence/query/ConpherenceParticipantCountQuery.php', 304 305 'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php', 306 + 'ConpherenceParticipantView' => 'applications/conpherence/view/ConpherenceParticipantView.php', 305 307 'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php', 306 - 'ConpherencePeopleWidgetView' => 'applications/conpherence/view/ConpherencePeopleWidgetView.php', 307 308 'ConpherencePicCropControl' => 'applications/conpherence/view/ConpherencePicCropControl.php', 308 309 'ConpherenceQueryThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php', 309 310 'ConpherenceQueryTransactionConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php', ··· 329 330 'ConpherenceUpdateController' => 'applications/conpherence/controller/ConpherenceUpdateController.php', 330 331 'ConpherenceUpdateThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceUpdateThreadConduitAPIMethod.php', 331 332 'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php', 332 - 'ConpherenceWidgetConfigConstants' => 'applications/conpherence/constants/ConpherenceWidgetConfigConstants.php', 333 - 'ConpherenceWidgetController' => 'applications/conpherence/controller/ConpherenceWidgetController.php', 334 - 'ConpherenceWidgetView' => 'applications/conpherence/view/ConpherenceWidgetView.php', 335 333 'DarkConsoleController' => 'applications/console/controller/DarkConsoleController.php', 336 334 'DarkConsoleCore' => 'applications/console/core/DarkConsoleCore.php', 337 335 'DarkConsoleDataController' => 'applications/console/controller/DarkConsoleDataController.php', ··· 4768 4766 'ConpherenceNewRoomController' => 'ConpherenceController', 4769 4767 'ConpherenceNotificationPanelController' => 'ConpherenceController', 4770 4768 'ConpherenceParticipant' => 'ConpherenceDAO', 4769 + 'ConpherenceParticipantController' => 'ConpherenceController', 4771 4770 'ConpherenceParticipantCountQuery' => 'PhabricatorOffsetPagedQuery', 4772 4771 'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery', 4772 + 'ConpherenceParticipantView' => 'AphrontView', 4773 4773 'ConpherenceParticipationStatus' => 'ConpherenceConstants', 4774 - 'ConpherencePeopleWidgetView' => 'ConpherenceWidgetView', 4775 4774 'ConpherencePicCropControl' => 'AphrontFormControl', 4776 4775 'ConpherenceQueryThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod', 4777 4776 'ConpherenceQueryTransactionConduitAPIMethod' => 'ConpherenceConduitAPIMethod', ··· 4803 4802 'ConpherenceUpdateController' => 'ConpherenceController', 4804 4803 'ConpherenceUpdateThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod', 4805 4804 'ConpherenceViewController' => 'ConpherenceController', 4806 - 'ConpherenceWidgetConfigConstants' => 'ConpherenceConstants', 4807 - 'ConpherenceWidgetController' => 'ConpherenceController', 4808 - 'ConpherenceWidgetView' => 'AphrontView', 4809 4805 'DarkConsoleController' => 'PhabricatorController', 4810 4806 'DarkConsoleCore' => 'Phobject', 4811 4807 'DarkConsoleDataController' => 'PhabricatorController',
+1 -1
src/applications/conpherence/application/PhabricatorConpherenceApplication.php
··· 42 42 'search/(?:query/(?P<queryKey>[^/]+)/)?' 43 43 => 'ConpherenceRoomListController', 44 44 'panel/' => 'ConpherenceNotificationPanelController', 45 - 'widget/(?P<id>[1-9]\d*)/' => 'ConpherenceWidgetController', 45 + 'participant/(?P<id>[1-9]\d*)/' => 'ConpherenceParticipantController', 46 46 'update/(?P<id>[1-9]\d*)/' => 'ConpherenceUpdateController', 47 47 ), 48 48 );
+10 -7
src/applications/conpherence/conduit/ConpherenceCreateThreadConduitAPIMethod.php
··· 13 13 14 14 protected function defineParamTypes() { 15 15 return array( 16 - 'title' => 'optional string', 17 - 'message' => 'required string', 16 + 'title' => 'required string', 17 + 'topic' => 'optional string', 18 + 'message' => 'optional string', 18 19 'participantPHIDs' => 'required list<phids>', 19 20 ); 20 21 } ··· 27 28 return array( 28 29 'ERR_EMPTY_PARTICIPANT_PHIDS' => pht( 29 30 'You must specify participant phids.'), 30 - 'ERR_EMPTY_MESSAGE' => pht( 31 - 'You must specify a message.'), 31 + 'ERR_EMPTY_TITLE' => pht( 32 + 'You must specify a title.'), 32 33 ); 33 34 } 34 35 ··· 36 37 $participant_phids = $request->getValue('participantPHIDs', array()); 37 38 $message = $request->getValue('message'); 38 39 $title = $request->getValue('title'); 40 + $topic = $request->getValue('topic'); 39 41 40 42 list($errors, $conpherence) = ConpherenceEditor::createThread( 41 43 $request->getUser(), 42 44 $participant_phids, 43 45 $title, 44 46 $message, 45 - $request->newContentSource()); 47 + $request->newContentSource(), 48 + $topic); 46 49 47 50 if ($errors) { 48 51 foreach ($errors as $error_code) { 49 52 switch ($error_code) { 50 - case ConpherenceEditor::ERROR_EMPTY_MESSAGE: 51 - throw new ConduitException('ERR_EMPTY_MESSAGE'); 53 + case ConpherenceEditor::ERROR_EMPTY_TITLE: 54 + throw new ConduitException('ERR_EMPTY_TITLE'); 52 55 break; 53 56 case ConpherenceEditor::ERROR_EMPTY_PARTICIPANTS: 54 57 throw new ConduitException('ERR_EMPTY_PARTICIPANT_PHIDS');
-42
src/applications/conpherence/constants/ConpherenceWidgetConfigConstants.php
··· 1 - <?php 2 - 3 - final class ConpherenceWidgetConfigConstants extends ConpherenceConstants { 4 - 5 - const UPDATE_URI = '/conpherence/update/'; 6 - 7 - public static function getWidgetPaneBehaviorConfig() { 8 - return array( 9 - 'widgetBaseUpdateURI' => self::UPDATE_URI, 10 - 'widgetRegistry' => self::getWidgetRegistry(), 11 - ); 12 - } 13 - 14 - public static function getWidgetRegistry() { 15 - return array( 16 - 'conpherence-message-pane' => array( 17 - 'name' => pht('Thread'), 18 - 'icon' => 'fa-comment', 19 - 'deviceOnly' => true, 20 - 'hasCreate' => false, 21 - ), 22 - 'widgets-people' => array( 23 - 'name' => pht('Participants'), 24 - 'icon' => 'fa-users', 25 - 'deviceOnly' => false, 26 - 'hasCreate' => true, 27 - 'createData' => array( 28 - 'refreshFromResponse' => true, 29 - 'action' => ConpherenceUpdateActions::ADD_PERSON, 30 - 'customHref' => null, 31 - ), 32 - ), 33 - 'widgets-settings' => array( 34 - 'name' => pht('Notifications'), 35 - 'icon' => 'fa-wrench', 36 - 'deviceOnly' => false, 37 - 'hasCreate' => false, 38 - ), 39 - ); 40 - } 41 - 42 - }
+51 -63
src/applications/conpherence/controller/ConpherenceController.php
··· 14 14 15 15 public function buildApplicationMenu() { 16 16 $nav = new PHUIListView(); 17 + $conpherence = $this->conpherence; 17 18 19 + // Local Links 20 + if ($conpherence) { 21 + $nav->addMenuItem( 22 + id(new PHUIListItemView()) 23 + ->setName(pht('Edit Room')) 24 + ->setType(PHUIListItemView::TYPE_LINK) 25 + ->setHref( 26 + $this->getApplicationURI('update/'.$conpherence->getID()).'/') 27 + ->setWorkflow(true)); 28 + 29 + $nav->addMenuItem( 30 + id(new PHUIListItemView()) 31 + ->setName(pht('Add Participants')) 32 + ->setType(PHUIListItemView::TYPE_LINK) 33 + ->setHref('#') 34 + ->addSigil('conpherence-widget-adder') 35 + ->setMetadata(array('widget' => 'widgets-people'))); 36 + } 37 + 38 + // Global Links 39 + $nav->newLabel(pht('Conpherence')); 18 40 $nav->newLink( 19 41 pht('New Room'), 20 42 $this->getApplicationURI('new/')); 21 - 22 - $nav->addMenuItem( 23 - id(new PHUIListItemView()) 24 - ->setName(pht('Add Participants')) 25 - ->setType(PHUIListItemView::TYPE_LINK) 26 - ->setHref('#') 27 - ->addSigil('conpherence-widget-adder') 28 - ->setMetadata(array('widget' => 'widgets-people'))); 43 + $nav->newLink( 44 + pht('Search Rooms'), 45 + $this->getApplicationURI('search/')); 29 46 30 47 return $nav; 31 48 } 32 49 33 - protected function buildApplicationCrumbs() { 34 - return $this->buildConpherenceApplicationCrumbs(); 35 - } 36 - 37 - protected function buildConpherenceApplicationCrumbs($is_rooms = false) { 38 - $crumbs = parent::buildApplicationCrumbs(); 39 - $crumbs->setBorder(true); 40 - 41 - if (!$is_rooms) { 42 - $crumbs 43 - ->addAction( 44 - id(new PHUIListItemView()) 45 - ->setName(pht('Room')) 46 - ->setHref('#') 47 - ->setIcon('fa-bars') 48 - ->setStyle('display: none;') 49 - ->addClass('device-widgets-selector') 50 - ->addSigil('device-widgets-selector')); 51 - } 52 - return $crumbs; 53 - } 54 - 55 50 protected function buildHeaderPaneContent( 56 51 ConpherenceThread $conpherence, 57 52 array $policy_objects) { 58 53 assert_instances_of($policy_objects, 'PhabricatorPolicy'); 59 54 $viewer = $this->getViewer(); 60 - 61 - $crumbs = $this->buildApplicationCrumbs(); 55 + $header = null; 62 56 63 57 if ($conpherence->getID()) { 64 58 $data = $conpherence->getDisplayData($this->getViewer()); 65 - $crumbs->addCrumb( 66 - id(new PHUICrumbView()) 67 - ->setName($data['title']) 68 - ->setHref('/'.$conpherence->getMonogram())); 69 - $can_edit = PhabricatorPolicyFilter::hasCapability( 59 + $header = id(new PHUIHeaderView()) 60 + ->setHeader($data['title']) 61 + ->setSubheader($data['topic']) 62 + ->addClass((!$data['topic']) ? 'conpherence-no-topic' : null); 63 + 64 + $can_edit = PhabricatorPolicyFilter::hasCapability( 70 65 $viewer, 71 66 $conpherence, 72 67 PhabricatorPolicyCapability::CAN_EDIT); 73 68 74 - $crumbs 75 - ->addAction( 76 - id(new PHUIListItemView()) 77 - ->setName(pht('Edit Room')) 69 + $header->addActionItem( 70 + id(new PHUIIconCircleView()) 78 71 ->setHref( 79 72 $this->getApplicationURI('update/'.$conpherence->getID()).'/') 80 73 ->setIcon('fa-pencil') 81 - ->setDisabled(!$can_edit) 74 + ->addClass('hide-on-device') 75 + ->setWorkflow(true)); 76 + 77 + $header->addActionItem( 78 + id(new PHUIIconCircleView()) 79 + ->setHref( 80 + $this->getApplicationURI('update/'.$conpherence->getID()).'/'. 81 + '?action='.ConpherenceUpdateActions::NOTIFICATIONS) 82 + ->setIcon('fa-gear') 83 + ->addClass('hide-on-device') 82 84 ->setWorkflow(true)); 83 85 84 86 $widget_key = PhabricatorConpherenceWidgetVisibleSetting::SETTINGKEY; 85 87 $widget_view = (bool)$viewer->getUserSetting($widget_key, false); 86 88 87 - $divider = id(new PHUIListItemView()) 88 - ->setType(PHUIListItemView::TYPE_DIVIDER) 89 - ->addClass('conpherence-header-desktop-item'); 90 - $crumbs->addAction($divider); 91 - 92 89 Javelin::initBehavior( 93 90 'toggle-widget', 94 91 array( ··· 96 93 'settingsURI' => '/settings/adjust/?key='.$widget_key, 97 94 )); 98 95 99 - $crumbs->addAction( 100 - id(new PHUIListItemView()) 101 - ->addSigil('conpherence-widget-toggle') 102 - ->setIcon('fa-columns') 103 - ->addClass('conpherence-header-desktop-item')); 96 + $header->addActionItem( 97 + id(new PHUIIconCircleView()) 98 + ->addSigil('conpherence-widget-toggle') 99 + ->setIcon('fa-group') 100 + ->setHref('#') 101 + ->addClass('conpherence-participant-toggle')); 104 102 } 105 103 106 - return hsprintf( 107 - '%s', 108 - array( 109 - phutil_tag( 110 - 'div', 111 - array( 112 - 'class' => 'header-loading-mask', 113 - ), 114 - ''), 115 - $crumbs, 116 - )); 104 + return $header; 117 105 } 118 106 119 107 }
+8
src/applications/conpherence/controller/ConpherenceNewRoomController.php
··· 27 27 ->setTransactionType(ConpherenceTransaction::TYPE_TITLE) 28 28 ->setNewValue($request->getStr('title')); 29 29 $xactions[] = id(new ConpherenceTransaction()) 30 + ->setTransactionType(ConpherenceTransaction::TYPE_TOPIC) 31 + ->setNewValue($request->getStr('topic')); 32 + $xactions[] = id(new ConpherenceTransaction()) 30 33 ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) 31 34 ->setNewValue($request->getStr('viewPolicy')); 32 35 $xactions[] = id(new ConpherenceTransaction()) ··· 93 96 ->setLabel(pht('Name')) 94 97 ->setName('title') 95 98 ->setValue($request->getStr('title'))) 99 + ->appendChild( 100 + id(new AphrontFormTextControl()) 101 + ->setLabel(pht('Topic')) 102 + ->setName('topic') 103 + ->setValue($request->getStr('topic'))) 96 104 ->appendChild( 97 105 id(new AphrontFormTokenizerControl()) 98 106 ->setName('participants')
+73
src/applications/conpherence/controller/ConpherenceParticipantController.php
··· 1 + <?php 2 + 3 + final class ConpherenceParticipantController extends ConpherenceController { 4 + 5 + public function shouldAllowPublic() { 6 + return true; 7 + } 8 + 9 + public function handleRequest(AphrontRequest $request) { 10 + $viewer = $request->getViewer(); 11 + 12 + $conpherence_id = $request->getURIData('id'); 13 + if (!$conpherence_id) { 14 + return new Aphront404Response(); 15 + } 16 + $conpherence = id(new ConpherenceThreadQuery()) 17 + ->setViewer($viewer) 18 + ->withIDs(array($conpherence_id)) 19 + ->needWidgetData(true) 20 + ->executeOne(); 21 + if (!$conpherence) { 22 + return new Aphront404Response(); 23 + } 24 + $this->setConpherence($conpherence); 25 + $content = $this->renderParticipantPaneContent(); 26 + 27 + return id(new AphrontAjaxResponse())->setContent($content); 28 + } 29 + 30 + private function renderParticipantPaneContent() { 31 + $conpherence = $this->getConpherence(); 32 + 33 + $widgets = array(); 34 + $new_icon = id(new PHUIIconView()) 35 + ->setIcon('fa-plus-square') 36 + ->setHref($this->getUpdateURI()) 37 + ->setMetadata(array('widget' => null)) 38 + ->addSigil('conpherence-widget-adder'); 39 + 40 + $content = id(new ConpherenceParticipantView()) 41 + ->setUser($this->getViewer()) 42 + ->setConpherence($this->getConpherence()) 43 + ->setUpdateURI($this->getUpdateURI()); 44 + 45 + $widgets[] = phutil_tag( 46 + 'div', 47 + array( 48 + 'class' => 'widgets-header', 49 + ), 50 + id(new PHUIHeaderView()) 51 + ->setHeader(pht('Participants')) 52 + ->addActionItem($new_icon)); 53 + 54 + $widgets[] = javelin_tag( 55 + 'div', 56 + array( 57 + 'class' => 'widgets-body', 58 + 'id' => 'widgets-people', 59 + 'sigil' => 'widgets-people', 60 + ), 61 + $content); 62 + 63 + // without this implosion we get "," between each element in our widgets 64 + // array 65 + return array('widgets' => phutil_implode_html('', $widgets)); 66 + } 67 + 68 + private function getUpdateURI() { 69 + $conpherence = $this->getConpherence(); 70 + return $this->getApplicationURI('update/'.$conpherence->getID().'/'); 71 + } 72 + 73 + }
-4
src/applications/conpherence/controller/ConpherenceRoomListController.php
··· 18 18 return $this->delegateToController($controller); 19 19 } 20 20 21 - protected function buildApplicationCrumbs() { 22 - return $this->buildConpherenceApplicationCrumbs($is_rooms = true); 23 - } 24 - 25 21 public function buildApplicationMenu() { 26 22 return $this->buildRoomsSideNavView(true)->getMenu(); 27 23 }
+94 -25
src/applications/conpherence/controller/ConpherenceUpdateController.php
··· 107 107 break; 108 108 case ConpherenceUpdateActions::REMOVE_PERSON: 109 109 if (!$request->isContinueRequest()) { 110 - // do nothing; we'll display a confirmation dialogue instead 110 + // do nothing; we'll display a confirmation dialog instead 111 111 break; 112 112 } 113 113 $person_phid = $request->getStr('remove_person'); ··· 127 127 } 128 128 $participant->setSettings(array('notifications' => $notifications)); 129 129 $participant->save(); 130 - 131 - $label = PhabricatorConpherenceNotificationsSetting::getSettingLabel( 132 - $notifications); 133 - 134 - $result = pht( 135 - 'Updated notification settings to "%s".', 136 - $label); 130 + return id(new AphrontRedirectResponse()) 131 + ->setURI('/'.$conpherence->getMonogram()); 137 132 138 - return id(new AphrontAjaxResponse()) 139 - ->setContent($result); 140 133 break; 141 134 case ConpherenceUpdateActions::METADATA: 142 135 $top = $request->getInt('image_y'); 143 136 $left = $request->getInt('image_x'); 144 137 $file_id = $request->getInt('file_id'); 145 138 $title = $request->getStr('title'); 139 + $topic = $request->getStr('topic'); 146 140 if ($file_id) { 147 141 $orig_file = id(new PhabricatorFileQuery()) 148 142 ->setViewer($user) ··· 190 184 ->setNewValue($image_phid); 191 185 } 192 186 $title = $request->getStr('title'); 187 + $topic = $request->getStr('topic'); 193 188 $xactions[] = id(new ConpherenceTransaction()) 194 189 ->setTransactionType(ConpherenceTransaction::TYPE_TITLE) 195 190 ->setNewValue($title); 196 191 $xactions[] = id(new ConpherenceTransaction()) 192 + ->setTransactionType(ConpherenceTransaction::TYPE_TOPIC) 193 + ->setNewValue($topic); 194 + $xactions[] = id(new ConpherenceTransaction()) 197 195 ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) 198 196 ->setNewValue($request->getStr('viewPolicy')); 199 197 $xactions[] = id(new ConpherenceTransaction()) ··· 269 267 } 270 268 271 269 switch ($action) { 270 + case ConpherenceUpdateActions::NOTIFICATIONS: 271 + $dialog = $this->renderPreferencesDialog($conpherence); 272 + break; 272 273 case ConpherenceUpdateActions::ADD_PERSON: 273 - $dialogue = $this->renderAddPersonDialogue($conpherence); 274 + $dialog = $this->renderAddPersonDialog($conpherence); 274 275 break; 275 276 case ConpherenceUpdateActions::REMOVE_PERSON: 276 - $dialogue = $this->renderRemovePersonDialogue($conpherence); 277 + $dialog = $this->renderRemovePersonDialog($conpherence); 277 278 break; 278 279 case ConpherenceUpdateActions::METADATA: 279 280 default: 280 - $dialogue = $this->renderMetadataDialogue($conpherence, $error_view); 281 + $dialog = $this->renderMetadataDialog($conpherence, $error_view); 281 282 break; 282 283 } 283 284 284 - return id(new AphrontDialogResponse()) 285 - ->setDialog($dialogue 285 + return 286 + $dialog 286 287 ->setUser($user) 287 288 ->setWidth(AphrontDialogView::WIDTH_FORM) 288 289 ->setSubmitURI($this->getApplicationURI('update/'.$conpherence_id.'/')) 289 290 ->addSubmitButton() 290 - ->addCancelButton($this->getApplicationURI($conpherence->getID().'/'))); 291 + ->addCancelButton($this->getApplicationURI($conpherence->getID().'/')); 292 + 293 + } 294 + 295 + private function renderPreferencesDialog( 296 + ConpherenceThread $conpherence) { 297 + 298 + $request = $this->getRequest(); 299 + $user = $request->getUser(); 300 + 301 + $participant = $conpherence->getParticipantIfExists($user->getPHID()); 302 + if (!$participant) { 303 + $can_join = PhabricatorPolicyFilter::hasCapability( 304 + $user, 305 + $conpherence, 306 + PhabricatorPolicyCapability::CAN_JOIN); 307 + if ($can_join) { 308 + $text = pht( 309 + 'Notification settings are available after joining the room.'); 310 + } else if ($user->isLoggedIn()) { 311 + $text = pht( 312 + 'Notification settings not applicable to rooms you can not join.'); 313 + } else { 314 + $text = pht( 315 + 'Notification settings are available after logging in and joining '. 316 + 'the room.'); 317 + } 318 + return id(new AphrontDialogView()) 319 + ->setTitle(pht('Room Preferences')) 320 + ->appendParagraph($text); 321 + } 322 + 323 + $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; 324 + $notification_default = $user->getUserSetting($notification_key); 325 + 326 + $settings = $participant->getSettings(); 327 + $notifications = idx( 328 + $settings, 329 + 'notifications', 330 + $notification_default); 331 + 332 + $form = id(new AphrontFormView()) 333 + ->setUser($user) 334 + ->setFullWidth(true) 335 + ->appendControl( 336 + id(new AphrontFormRadioButtonControl()) 337 + ->addButton( 338 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL, 339 + PhabricatorConpherenceNotificationsSetting::getSettingLabel( 340 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL), 341 + '') 342 + ->addButton( 343 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY, 344 + PhabricatorConpherenceNotificationsSetting::getSettingLabel( 345 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY), 346 + '') 347 + ->setName('notifications') 348 + ->setValue($notifications)); 349 + 350 + return id(new AphrontDialogView()) 351 + ->setTitle(pht('Room Preferences')) 352 + ->addHiddenInput('action', 'notifications') 353 + ->addHiddenInput( 354 + 'latest_transaction_id', 355 + $request->getInt('latest_transaction_id')) 356 + ->appendForm($form); 291 357 292 358 } 293 359 294 - private function renderAddPersonDialogue( 360 + private function renderAddPersonDialog( 295 361 ConpherenceThread $conpherence) { 296 362 297 363 $request = $this->getRequest(); ··· 322 388 return $view; 323 389 } 324 390 325 - private function renderRemovePersonDialogue( 391 + private function renderRemovePersonDialog( 326 392 ConpherenceThread $conpherence) { 327 393 328 394 $request = $this->getRequest(); ··· 405 471 return $dialog; 406 472 } 407 473 408 - private function renderMetadataDialogue( 474 + private function renderMetadataDialog( 409 475 ConpherenceThread $conpherence, 410 476 $error_view) { 411 477 ··· 419 485 id(new AphrontFormTextControl()) 420 486 ->setLabel(pht('Title')) 421 487 ->setName('title') 422 - ->setValue($conpherence->getTitle())); 488 + ->setValue($conpherence->getTitle())) 489 + ->appendChild( 490 + id(new AphrontFormTextControl()) 491 + ->setLabel(pht('Topic')) 492 + ->setName('topic') 493 + ->setValue($conpherence->getTopic())); 423 494 424 495 $nopic = $this->getRequest()->getExists('nopic'); 425 496 $image = $conpherence->getImage(ConpherenceImageData::SIZE_ORIG); ··· 548 619 $rendered_transactions = idx($data, 'transactions'); 549 620 $new_latest_transaction_id = idx($data, 'latest_transaction_id'); 550 621 551 - $widget_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); 622 + $update_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); 552 623 $nav_item = null; 553 624 $header = null; 554 625 $people_widget = null; 555 - $file_widget = null; 556 626 if (!$minimal_display) { 557 627 switch ($action) { 558 628 case ConpherenceUpdateActions::METADATA: ··· 571 641 $nav_item = hsprintf('%s', $nav_item); 572 642 break; 573 643 case ConpherenceUpdateActions::ADD_PERSON: 574 - $people_widget = id(new ConpherencePeopleWidgetView()) 644 + $people_widget = id(new ConpherenceParticipantView()) 575 645 ->setUser($user) 576 646 ->setConpherence($conpherence) 577 - ->setUpdateURI($widget_uri); 647 + ->setUpdateURI($update_uri); 578 648 $people_widget = hsprintf('%s', $people_widget->render()); 579 649 break; 580 650 case ConpherenceUpdateActions::REMOVE_PERSON: ··· 595 665 'nav_item' => $nav_item, 596 666 'conpherence_phid' => $conpherence->getPHID(), 597 667 'header' => $header, 598 - 'file_widget' => $file_widget, 599 668 'people_widget' => $people_widget, 600 669 'aphlictDropdownData' => array( 601 670 $dropdown_query->getNotificationData(),
-181
src/applications/conpherence/controller/ConpherenceWidgetController.php
··· 1 - <?php 2 - 3 - final class ConpherenceWidgetController extends ConpherenceController { 4 - 5 - public function shouldAllowPublic() { 6 - return true; 7 - } 8 - 9 - public function handleRequest(AphrontRequest $request) { 10 - $request = $this->getRequest(); 11 - $user = $request->getUser(); 12 - 13 - $conpherence_id = $request->getURIData('id'); 14 - if (!$conpherence_id) { 15 - return new Aphront404Response(); 16 - } 17 - $conpherence = id(new ConpherenceThreadQuery()) 18 - ->setViewer($user) 19 - ->withIDs(array($conpherence_id)) 20 - ->needWidgetData(true) 21 - ->executeOne(); 22 - if (!$conpherence) { 23 - return new Aphront404Response(); 24 - } 25 - $this->setConpherence($conpherence); 26 - 27 - switch ($request->getStr('widget')) { 28 - case 'widgets-people': 29 - $content = $this->renderPeopleWidgetPaneContent(); 30 - break; 31 - case 'widgets-settings': 32 - $content = $this->renderSettingsWidgetPaneContent(); 33 - break; 34 - default: 35 - $widgets = $this->renderWidgetPaneContent(); 36 - $content = $widgets; 37 - break; 38 - } 39 - return id(new AphrontAjaxResponse())->setContent($content); 40 - } 41 - 42 - private function renderWidgetPaneContent() { 43 - $conpherence = $this->getConpherence(); 44 - 45 - $widgets = array(); 46 - $new_icon = id(new PHUIIconView()) 47 - ->setIcon('fa-plus') 48 - ->setHref($this->getWidgetURI()) 49 - ->setMetadata(array('widget' => null)) 50 - ->addSigil('conpherence-widget-adder'); 51 - $header = javelin_tag( 52 - 'a', 53 - array( 54 - 'href' => '#', 55 - 'sigil' => 'widgets-selector', 56 - ), 57 - pht('Participants')); 58 - 59 - $widgets[] = phutil_tag( 60 - 'div', 61 - array( 62 - 'class' => 'widgets-header', 63 - ), 64 - id(new PHUIHeaderView()) 65 - ->setHeader($header) 66 - ->addActionItem($new_icon)); 67 - $user = $this->getRequest()->getUser(); 68 - // now the widget bodies 69 - $widgets[] = javelin_tag( 70 - 'div', 71 - array( 72 - 'class' => 'widgets-body', 73 - 'id' => 'widgets-people', 74 - 'sigil' => 'widgets-people', 75 - ), 76 - $this->renderPeopleWidgetPaneContent()); 77 - $widgets[] = phutil_tag( 78 - 'div', 79 - array( 80 - 'class' => 'widgets-body', 81 - 'id' => 'widgets-settings', 82 - 'style' => 'display: none', 83 - ), 84 - $this->renderSettingsWidgetPaneContent()); 85 - 86 - // without this implosion we get "," between each element in our widgets 87 - // array 88 - return array('widgets' => phutil_implode_html('', $widgets)); 89 - } 90 - 91 - private function renderPeopleWidgetPaneContent() { 92 - return id(new ConpherencePeopleWidgetView()) 93 - ->setUser($this->getViewer()) 94 - ->setConpherence($this->getConpherence()) 95 - ->setUpdateURI($this->getWidgetURI()); 96 - } 97 - 98 - 99 - private function renderSettingsWidgetPaneContent() { 100 - $viewer = $this->getViewer(); 101 - $conpherence = $this->getConpherence(); 102 - $participant = $conpherence->getParticipantIfExists($viewer->getPHID()); 103 - if (!$participant) { 104 - $can_join = PhabricatorPolicyFilter::hasCapability( 105 - $viewer, 106 - $conpherence, 107 - PhabricatorPolicyCapability::CAN_JOIN); 108 - if ($can_join) { 109 - $text = pht( 110 - 'Notification settings are available after joining the room.'); 111 - } else if ($viewer->isLoggedIn()) { 112 - $text = pht( 113 - 'Notification settings not applicable to rooms you can not join.'); 114 - } else { 115 - $text = pht( 116 - 'Notification settings are available after logging in and joining '. 117 - 'the room.'); 118 - } 119 - return phutil_tag( 120 - 'div', 121 - array( 122 - 'class' => 'no-settings', 123 - ), 124 - $text); 125 - } 126 - $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; 127 - $notification_default = $viewer->getUserSetting($notification_key); 128 - 129 - $settings = $participant->getSettings(); 130 - $notifications = idx( 131 - $settings, 132 - 'notifications', 133 - $notification_default); 134 - $options = id(new AphrontFormRadioButtonControl()) 135 - ->addButton( 136 - PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL, 137 - PhabricatorConpherenceNotificationsSetting::getSettingLabel( 138 - PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL), 139 - '') 140 - ->addButton( 141 - PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY, 142 - PhabricatorConpherenceNotificationsSetting::getSettingLabel( 143 - PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY), 144 - '') 145 - ->setName('notifications') 146 - ->setValue($notifications); 147 - 148 - $layout = array( 149 - $options, 150 - phutil_tag( 151 - 'input', 152 - array( 153 - 'type' => 'hidden', 154 - 'name' => 'action', 155 - 'value' => 'notifications', 156 - )), 157 - phutil_tag( 158 - 'button', 159 - array( 160 - 'type' => 'submit', 161 - 'class' => 'notifications-update', 162 - ), 163 - pht('Save')), 164 - ); 165 - 166 - return phabricator_form( 167 - $viewer, 168 - array( 169 - 'method' => 'POST', 170 - 'action' => $this->getWidgetURI(), 171 - 'sigil' => 'notifications-update', 172 - ), 173 - $layout); 174 - } 175 - 176 - private function getWidgetURI() { 177 - $conpherence = $this->getConpherence(); 178 - return $this->getApplicationURI('update/'.$conpherence->getID().'/'); 179 - } 180 - 181 - }
+15 -1
src/applications/conpherence/editor/ConpherenceEditor.php
··· 18 18 array $participant_phids, 19 19 $title, 20 20 $message, 21 - PhabricatorContentSource $source) { 21 + PhabricatorContentSource $source, 22 + $topic) { 22 23 23 24 $conpherence = ConpherenceThread::initializeNewRoom($creator); 24 25 $files = array(); ··· 58 59 $xactions[] = id(new ConpherenceTransaction()) 59 60 ->setTransactionType(ConpherenceTransaction::TYPE_TITLE) 60 61 ->setNewValue($title); 62 + } 63 + if (strlen($topic)) { 64 + $xactions[] = id(new ConpherenceTransaction()) 65 + ->setTransactionType(ConpherenceTransaction::TYPE_TOPIC) 66 + ->setNewValue($topic); 61 67 } 62 68 63 69 $xactions[] = id(new ConpherenceTransaction()) ··· 118 124 $types[] = PhabricatorTransactions::TYPE_COMMENT; 119 125 120 126 $types[] = ConpherenceTransaction::TYPE_TITLE; 127 + $types[] = ConpherenceTransaction::TYPE_TOPIC; 121 128 $types[] = ConpherenceTransaction::TYPE_PARTICIPANTS; 122 129 $types[] = ConpherenceTransaction::TYPE_FILES; 123 130 $types[] = ConpherenceTransaction::TYPE_PICTURE; ··· 136 143 switch ($xaction->getTransactionType()) { 137 144 case ConpherenceTransaction::TYPE_TITLE: 138 145 return $object->getTitle(); 146 + case ConpherenceTransaction::TYPE_TOPIC: 147 + return $object->getTopic(); 139 148 case ConpherenceTransaction::TYPE_PICTURE: 140 149 return $object->getImagePHID(ConpherenceImageData::SIZE_ORIG); 141 150 case ConpherenceTransaction::TYPE_PICTURE_CROP: ··· 156 165 157 166 switch ($xaction->getTransactionType()) { 158 167 case ConpherenceTransaction::TYPE_TITLE: 168 + case ConpherenceTransaction::TYPE_TOPIC: 159 169 case ConpherenceTransaction::TYPE_PICTURE_CROP: 160 170 return $xaction->getNewValue(); 161 171 case ConpherenceTransaction::TYPE_PICTURE: ··· 249 259 switch ($xaction->getTransactionType()) { 250 260 case ConpherenceTransaction::TYPE_TITLE: 251 261 $object->setTitle($xaction->getNewValue()); 262 + break; 263 + case ConpherenceTransaction::TYPE_TOPIC: 264 + $object->setTopic($xaction->getNewValue()); 252 265 break; 253 266 case ConpherenceTransaction::TYPE_PICTURE: 254 267 $object->setImagePHID( ··· 484 497 PhabricatorPolicyCapability::CAN_VIEW); 485 498 break; 486 499 case ConpherenceTransaction::TYPE_TITLE: 500 + case ConpherenceTransaction::TYPE_TOPIC: 487 501 PhabricatorPolicyFilter::requireCapability( 488 502 $this->requireActor(), 489 503 $object,
+5
src/applications/conpherence/storage/ConpherenceThread.php
··· 8 8 PhabricatorDestructibleInterface { 9 9 10 10 protected $title; 11 + protected $topic; 11 12 protected $imagePHIDs = array(); 12 13 protected $messageCount; 13 14 protected $recentParticipantPHIDs = array(); ··· 29 30 return id(new ConpherenceThread()) 30 31 ->setMessageCount(0) 31 32 ->setTitle('') 33 + ->setTopic('') 32 34 ->attachParticipants(array()) 33 35 ->attachFilePHIDs(array()) 34 36 ->attachImages(array()) ··· 46 48 ), 47 49 self::CONFIG_COLUMN_SCHEMA => array( 48 50 'title' => 'text255?', 51 + 'topic' => 'text255', 49 52 'messageCount' => 'uint64', 50 53 'mailKey' => 'text20', 51 54 'joinPolicy' => 'policy', ··· 342 345 $unread_count = $this->getMessageCount() - $user_seen_count; 343 346 344 347 $title = $this->getDisplayTitle($viewer); 348 + $topic = $this->getTopic(); 345 349 346 350 return array( 347 351 'title' => $title, 352 + 'topic' => $topic, 348 353 'subtitle' => $subtitle, 349 354 'unread_count' => $unread_count, 350 355 'epoch' => $this->getDateModified(),
+17
src/applications/conpherence/storage/ConpherenceTransaction.php
··· 4 4 5 5 const TYPE_FILES = 'files'; 6 6 const TYPE_TITLE = 'title'; 7 + const TYPE_TOPIC = 'topic'; 7 8 const TYPE_PARTICIPANTS = 'participants'; 8 9 const TYPE_DATE_MARKER = 'date-marker'; 9 10 const TYPE_PICTURE = 'picture'; ··· 39 40 case self::TYPE_PARTICIPANTS: 40 41 return ($old === null); 41 42 case self::TYPE_TITLE: 43 + case self::TYPE_TOPIC: 42 44 case self::TYPE_PICTURE: 43 45 case self::TYPE_DATE_MARKER: 44 46 return false; ··· 59 61 60 62 switch ($this->getTransactionType()) { 61 63 case self::TYPE_TITLE: 64 + case self::TYPE_TOPIC: 62 65 case PhabricatorTransactions::TYPE_VIEW_POLICY: 63 66 case PhabricatorTransactions::TYPE_EDIT_POLICY: 64 67 case PhabricatorTransactions::TYPE_JOIN_POLICY: ··· 144 147 '%s named this room "%s".', 145 148 $this->renderHandleLink($author_phid), 146 149 $new); 150 + } 151 + return $title; 152 + break; 153 + case self::TYPE_TOPIC: 154 + if ($new) { 155 + $title = pht( 156 + '%s set the topic of this room to "%s".', 157 + $this->renderHandleLink($author_phid), 158 + $new); 159 + } else if ($old) { 160 + $title = pht( 161 + '%s deleted the room topic "%s"', 162 + $this->renderHandleLink($author_phid), 163 + $old); 147 164 } 148 165 return $title; 149 166 break;
+1 -3
src/applications/conpherence/view/ConpherenceLayoutView.php
··· 105 105 $classes[] = 'hide-widgets'; 106 106 } 107 107 108 - $this->initBehavior( 109 - 'conpherence-widget-pane', 110 - ConpherenceWidgetConfigConstants::getWidgetPaneBehaviorConfig()); 108 + $this->initBehavior('conpherence-participants-pane'); 111 109 112 110 return javelin_tag( 113 111 'div',
+23 -2
src/applications/conpherence/view/ConpherencePeopleWidgetView.php src/applications/conpherence/view/ConpherenceParticipantView.php
··· 1 1 <?php 2 2 3 - final class ConpherencePeopleWidgetView extends ConpherenceWidgetView { 3 + final class ConpherenceParticipantView extends AphrontView { 4 + 5 + private $conpherence; 6 + private $updateURI; 7 + 8 + public function setUpdateURI($update_uri) { 9 + $this->updateURI = $update_uri; 10 + return $this; 11 + } 12 + public function getUpdateURI() { 13 + return $this->updateURI; 14 + } 15 + 16 + public function setConpherence(ConpherenceThread $conpherence) { 17 + $this->conpherence = $conpherence; 18 + return $this; 19 + } 20 + public function getConpherence() { 21 + return $this->conpherence; 22 + } 4 23 5 24 public function render() { 6 25 $conpherence = $this->getConpherence(); ··· 14 33 natcasesort($handle_list); 15 34 $handles = mpull($handles, null, 'getName'); 16 35 $handles = array_select_keys($handles, $handle_list); 36 + 17 37 $head_handles = mpull($head_handles, null, 'getName'); 18 38 $handles = $head_handles + $handles; 19 39 ··· 28 48 29 49 if (($user_phid == $viewer->getPHID()) || $can_edit) { 30 50 $icon = id(new PHUIIconView()) 31 - ->setIcon('fa-times lightbluetext'); 51 + ->setIcon('fa-times') 52 + ->addClass('lightbluetext'); 32 53 $remove_html = javelin_tag( 33 54 'a', 34 55 array(
+1
src/applications/conpherence/view/ConpherenceTransactionView.php
··· 231 231 $content = $transaction->getTitle(); 232 232 break; 233 233 case ConpherenceTransaction::TYPE_TITLE: 234 + case ConpherenceTransaction::TYPE_TOPIC: 234 235 case ConpherenceTransaction::TYPE_PICTURE: 235 236 case ConpherenceTransaction::TYPE_PICTURE_CROP: 236 237 case ConpherenceTransaction::TYPE_PARTICIPANTS:
-24
src/applications/conpherence/view/ConpherenceWidgetView.php
··· 1 - <?php 2 - 3 - abstract class ConpherenceWidgetView extends AphrontView { 4 - 5 - private $conpherence; 6 - private $updateURI; 7 - 8 - public function setUpdateURI($update_uri) { 9 - $this->updateURI = $update_uri; 10 - return $this; 11 - } 12 - public function getUpdateURI() { 13 - return $this->updateURI; 14 - } 15 - 16 - public function setConpherence(ConpherenceThread $conpherence) { 17 - $this->conpherence = $conpherence; 18 - return $this; 19 - } 20 - public function getConpherence() { 21 - return $this->conpherence; 22 - } 23 - 24 - }
+4
webroot/rsrc/css/aphront/dialog-view.css
··· 46 46 border: none; 47 47 } 48 48 49 + .device-phone .aphront-dialog-body { 50 + padding: 8px; 51 + } 52 + 49 53 .aphront-dialog-tail { 50 54 border: none; 51 55 position: relative;
+68
webroot/rsrc/css/application/conpherence/header-pane.css
··· 1 + /** 2 + * @provides conpherence-header-pane-css 3 + */ 4 + 5 + .conpherence-header-pane { 6 + } 7 + 8 + .conpherence-header-pane .phui-header-shell { 9 + padding: 8px 16px 10px; 10 + min-height: 38px; 11 + } 12 + 13 + .conpherence-header-pane .phui-header-header { 14 + font-size: 16px; 15 + font-family: 'Aleo', {$fontfamily}; 16 + color: #000; 17 + } 18 + 19 + .conpherence-header-pane .phui-header-subheader { 20 + color: {$lightgreyborder}; 21 + padding: 0; 22 + font-size: 12px; 23 + margin: 0; 24 + } 25 + 26 + .conpherence-header-pane .phui-header-shell.conpherence-no-topic { 27 + padding: 15px 16px 5px; 28 + } 29 + 30 + .conpherence-header-pane .phui-header-action-list .phui-header-action-item 31 + .phui-icon-view { 32 + height: 18px; 33 + width: 24px; 34 + font-size: 14px; 35 + line-height: 23px; 36 + display: block; 37 + } 38 + 39 + .device .hide-on-device { 40 + display: none; 41 + } 42 + 43 + .device-phone .conpherence-header-pane .phui-header-col3 { 44 + vertical-align: middle; 45 + } 46 + 47 + .conpherence-header-pane .conpherence-participant-toggle.phui-icon-circle { 48 + text-decoration: none; 49 + border-color: {$sky}; 50 + cursor: pointer; 51 + } 52 + 53 + .conpherence-header-pane .conpherence-participant-toggle.phui-icon-circle 54 + .phui-icon-view { 55 + color: {$sky}; 56 + } 57 + 58 + .hide-widgets .conpherence-header-pane 59 + .conpherence-participant-toggle.phui-icon-circle { 60 + text-decoration: none; 61 + border-color: {$lightblueborder}; 62 + cursor: pointer; 63 + } 64 + 65 + .hide-widgets .conpherence-header-pane 66 + .conpherence-participant-toggle.phui-icon-circle .phui-icon-view { 67 + color: {$lightblueborder}; 68 + }
+18 -20
webroot/rsrc/css/application/conpherence/message-pane.css
··· 9 9 position: fixed; 10 10 left: 240px; 11 11 right: 240px; 12 - top: 76px; 12 + top: 102px; 13 13 bottom: 0px; 14 14 min-width: 300px; 15 15 width: auto; ··· 32 32 margin: 16px 0px 16px 0px; 33 33 } 34 34 35 - .conpherence-layout .phui-crumbs-view { 36 - padding: 0 0 0 8px; 37 - background: #f7f7f7; 38 - } 39 - 40 35 .conpherence-show-more-messages { 41 36 display: block; 42 37 background: #e0e3ec; ··· 54 49 position: fixed; 55 50 left: 240px; 56 51 right: 240px; 57 - top: 78px; 52 + top: 103px; 58 53 bottom: 148px; 59 54 overflow-x: hidden; 60 55 overflow-y: auto; ··· 103 98 .conpherence-message-pane .phui-form-view { 104 99 border-width: 0; 105 100 height: 140px; 106 - padding: 0 8px 8px; 101 + padding: 0 20px 12px; 107 102 position: fixed; 108 103 bottom: 0; 109 104 left: 240px; 110 105 right: 241px; 106 + } 107 + 108 + .device .conpherence-message-pane .phui-form-view { 109 + padding: 8px 8px; 111 110 } 112 111 113 112 .conpherence-message-pane .phui-form-view.login-to-participate { ··· 123 122 margin-top: 6px; 124 123 } 125 124 125 + .device .conpherence-message-pane .aphront-form-control-submit button, 126 + .device .conpherence-message-pane .aphront-form-control-submit a.button { 127 + margin-top: 13px; 128 + } 129 + 126 130 /** 127 131 * When entering "Fullscreen Mode" in the remarkup control, we need to drop 128 132 * all of the "position: fixed" on parent elements or Chrome doesn't put the ··· 162 166 163 167 .conpherence-message-pane .conpherence-transaction-view { 164 168 padding: 2px 0px; 165 - margin: 4px 12px; 169 + margin: 4px 20px; 166 170 background-size: 100%; 167 171 min-height: auto; 168 172 } ··· 182 186 } 183 187 184 188 .device-phone .conpherence-message-pane .conpherence-transaction-image { 185 - display: none; 189 + height: 25px; 190 + width: 25px; 191 + background-size: 25px; 186 192 } 187 193 188 194 .conpherence-message-pane .conpherence-comment.anchor-target, ··· 206 212 } 207 213 208 214 .device-phone .conpherence-message-pane .conpherence-transaction-detail { 209 - margin: 0; 215 + margin-left: 32px; 210 216 } 211 217 212 218 .conpherence-message-pane .conpherence-transaction-view.date-marker { 213 219 padding: 0; 214 - margin: 20px 12px 4px; 220 + margin: 20px 20px 4px; 215 221 min-height: auto; 216 222 } 217 223 ··· 234 240 235 241 .device .conpherence-message-pane .conpherence-transaction-view.date-marker 236 242 .date { 237 - color: {$lightbluetext}; 238 243 left: 4px; 239 244 } 240 245 ··· 351 356 max-height: 200px; 352 357 } 353 358 354 - .device .conpherence-header-desktop-item { 355 - display: none; 356 - } 357 - 358 - .device .conpherence-header-pane .phui-crumb-action-divider { 359 - display: none; 360 - }
+2 -2
webroot/rsrc/css/application/conpherence/transaction.css
··· 17 17 } 18 18 19 19 .conpherence-transaction-view.date-marker { 20 - border-top: 1px solid {$thinblueborder}; 20 + border-top: 1px solid {$sh-violetborder}; 21 21 } 22 22 23 23 .conpherence-transaction-view.date-marker .date { 24 24 position: relative; 25 25 top: -11px; 26 26 background-color: #fff; 27 - color: #000; 27 + color: {$sh-violettext}; 28 28 font-weight: bold; 29 29 } 30 30
-4
webroot/rsrc/css/application/conpherence/update.css
··· 2 2 * @provides conpherence-update-css 3 3 */ 4 4 5 - .phabricator-standard-page-body .aphront-dialog-view { 6 - margin: 20px auto 0px auto; 7 - } 8 - 9 5 .aphront-dialog-view .conpherence-dialogue-drag-photo { 10 6 border: 1px dashed #bfbfbf; 11 7 padding: 10px 0px 10px 10px;
+13 -107
webroot/rsrc/css/application/conpherence/widget-pane.css
··· 2 2 * @provides conpherence-widget-pane-css 3 3 */ 4 4 5 - .conpherence-widget-pane, 6 - .loading .widgets-loading-mask { 5 + .conpherence-widget-pane { 7 6 position: fixed; 8 7 right: 0px; 9 - top: 79px; 8 + top: 103px; 10 9 bottom: 0; 11 10 width: 240px; 12 11 border-width: 0 0 0 1px; ··· 15 14 overflow-y: auto; 16 15 -webkit-overflow-scrolling: touch; 17 16 } 18 - .device .conpherence-widget-pane, 19 - .device .loading .widgets-loading-mask { 20 - top: 44px; 21 - width: 100%; 22 - } 23 17 24 - .conpherence-widget-pane .widgets-loading-mask { 25 - opacity: .6; 26 - background: #fff; 27 - display: none; 28 - } 29 - 30 - .loading .widgets-loading-mask { 31 - display: block; 18 + .device .conpherence-widget-pane { 19 + background-color: {$page.background}; 32 20 } 33 21 34 22 .conpherence-widget-pane .aphront-form-input { ··· 48 36 font-size: {$biggerfontsize}; 49 37 } 50 38 51 - .device .conpherence-widget-pane .widgets-header { 52 - display: none; 53 - } 54 - 55 - .conpherence-widget-pane .widgets-header .caret { 56 - float: none; 57 - height: 0px; 58 - width: 0px; 59 - margin: 10px 0 0 4px; 60 - border-top-color: #000; 61 - } 62 - 63 39 .conpherence-widget-pane .widgets-header .phui-icon-view.disabled { 64 40 color: {$lightgreytext}; 65 41 } 66 42 67 - .device-desktop .conpherence-layout .device-widgets-selector { 68 - display: none; 69 - } 70 - 71 43 .conpherence-widget-pane .widgets-body { 72 44 position: fixed; 73 45 overflow-y: auto; 74 46 bottom: 0; 75 - top: 76px; 76 - width: 100%; 77 - } 78 - 79 - #widgets-settings { 80 - padding: 3px 6px; 81 - } 82 - 83 - .device-desktop .conpherence-widget-pane .widgets-body { 84 - top: 115px; 47 + top: 144px; 85 48 width: 240px; 86 49 } 87 50 88 - .conpherence-widget-pane .widget-icon { 89 - display: block; 90 - height: 14px; 91 - width: 14px; 92 - } 93 - 94 - .conpherence-widget-pane .phabricator-remarkup-embed-layout-link { 95 - padding-bottom: 1px; 96 - } 97 - 98 - /* people widget */ 99 - .conpherence-widget-pane .people-widget-header .add-people-widget { 100 - padding: 10px 0 5px 0; 101 - overflow: hidden; 102 - } 103 - 104 - .conpherence-widget-pane .people-widget-header .add-people-widget 105 - .aphront-form-control-tokenizer { 106 - float: left; 107 - width: 150px; 108 - padding: 0px 0px 0px 10px 109 - } 110 - 111 - .device .conpherence-widget-pane .people-widget-header .add-people-widget 112 - .aphront-form-control-tokenizer { 113 - width: 70%; 114 - } 115 - 116 - .conpherence-widget-pane .people-widget-header .add-people-widget 117 - .people-add-button { 118 - float: right; 119 - margin: 2px 8px 0px 0px; 120 - padding: 3px 16px 4px 16px; 121 - } 122 - 123 - #widgets-people { 124 - margin-top: 4px; 125 - } 126 - 127 51 .conpherence-widget-pane .person-entry { 128 - padding: 4px 8px; 52 + padding: 4px 4px 4px 8px; 129 53 } 130 54 131 55 .conpherence-widget-pane .person-entry:hover { ··· 173 97 color: #000; 174 98 } 175 99 176 - /* settings widget */ 177 - .conpherence-widget-pane .title-update, 178 - .conpherence-widget-pane .notifications-update { 179 - margin: 3px 0px 0px 4px; 180 - } 181 - 182 - .conpherence-widget-pane .no-settings { 183 - width: 200px; 184 - padding: 20px; 185 - text-align: center; 186 - color: {$greytext}; 187 - } 188 - 189 - .device .conpherence-widget-pane .no-settings { 190 - width: 60px; 191 - margin: 0 auto 0 auto; 192 - } 193 - 194 100 /****** Hide Widgets **********************************************************/ 195 101 196 - .device-desktop .hide-widgets .conpherence-widget-pane { 102 + .hide-widgets .conpherence-widget-pane { 197 103 display: none; 198 104 } 199 105 200 - .device-desktop .hide-widgets .conpherence-message-pane, 201 - .device-desktop .hide-widgets .loading .messages-loading-mask, 202 - .device-desktop .hide-widgets .loading .messages-loading-icon, 203 - .device-desktop .hide-widgets .conpherence-no-threads, 204 - .device-desktop .hide-widgets .conpherence-message-pane .conpherence-messages, 205 - .device-desktop .hide-widgets .conpherence-message-pane .phui-form-view { 106 + .hide-widgets .conpherence-message-pane, 107 + .hide-widgets .loading .messages-loading-mask, 108 + .hide-widgets .loading .messages-loading-icon, 109 + .hide-widgets .conpherence-no-threads, 110 + .hide-widgets .conpherence-message-pane .conpherence-messages, 111 + .hide-widgets .conpherence-message-pane .phui-form-view { 206 112 right: 0; 207 113 }
+4
webroot/rsrc/css/core/z-index.css
··· 150 150 z-index: 21; 151 151 } 152 152 153 + .conpherence-widget-pane { 154 + z-index: 25; 155 + } 156 + 153 157 .phuix-dropdown-menu { 154 158 z-index: 32; 155 159 }
+1 -1
webroot/rsrc/js/application/conpherence/behavior-menu.js
··· 241 241 if (!data.widget) { 242 242 data.widget = getDefaultWidget(); 243 243 } 244 - var widget_uri = config.baseURI + 'widget/' + data.threadID + '/'; 244 + var widget_uri = config.baseURI + 'participant/' + data.threadID + '/'; 245 245 new JX.Workflow(widget_uri, {}) 246 246 .setHandler(JX.bind(null, onWidgetResponse, data.threadID, data.widget)) 247 247 .start();
+112
webroot/rsrc/js/application/conpherence/behavior-participants-pane.js
··· 1 + /** 2 + * @requires javelin-behavior 3 + * javelin-dom 4 + * javelin-stratcom 5 + * javelin-workflow 6 + * javelin-util 7 + * phabricator-notification 8 + * conpherence-thread-manager 9 + * @provides javelin-behavior-conpherence-participants-pane 10 + */ 11 + 12 + JX.behavior('conpherence-participants-pane', function() { 13 + 14 + /** 15 + * Generified adding new stuff to widgets technology! 16 + */ 17 + JX.Stratcom.listen( 18 + ['click'], 19 + 'conpherence-widget-adder', 20 + function (e) { 21 + e.kill(); 22 + 23 + var threadManager = JX.ConpherenceThreadManager.getInstance(); 24 + var href = threadManager._getUpdateURI(); 25 + var latest_transaction_id = threadManager.getLatestTransactionID(); 26 + var data = { 27 + latest_transaction_id : latest_transaction_id, 28 + action : 'add_person' 29 + }; 30 + 31 + var workflow = new JX.Workflow(href, data) 32 + .setHandler(function (r) { 33 + var threadManager = JX.ConpherenceThreadManager.getInstance(); 34 + threadManager.setLatestTransactionID(r.latest_transaction_id); 35 + var root = JX.DOM.find(document, 'div', 'conpherence-layout'); 36 + var messages = null; 37 + try { 38 + messages = JX.DOM.find(root, 'div', 'conpherence-messages'); 39 + } catch (ex) { 40 + } 41 + if (messages) { 42 + JX.DOM.appendContent(messages, JX.$H(r.transactions)); 43 + JX.Stratcom.invoke('conpherence-redraw-thread', null, {}); 44 + } 45 + 46 + try { 47 + var people_root = JX.DOM.find(root, 'div', 'widgets-people'); 48 + // update the people widget 49 + JX.DOM.setContent( 50 + people_root, 51 + JX.$H(r.people_widget)); 52 + } catch (ex) { 53 + } 54 + 55 + }); 56 + 57 + threadManager.syncWorkflow(workflow, 'submit'); 58 + } 59 + ); 60 + 61 + JX.Stratcom.listen( 62 + ['touchstart', 'mousedown'], 63 + 'remove-person', 64 + function (e) { 65 + var threadManager = JX.ConpherenceThreadManager.getInstance(); 66 + var href = threadManager._getUpdateURI(); 67 + var data = e.getNodeData('remove-person'); 68 + 69 + // While the user is removing themselves, disable the notification 70 + // update behavior. If we don't do this, the user can get an error 71 + // when they remove themselves about permissions as the notification 72 + // code tries to load what jist happened. 73 + var loadedPhid = threadManager.getLoadedThreadPHID(); 74 + threadManager.setLoadedThreadPHID(null); 75 + 76 + new JX.Workflow(href, data) 77 + .setCloseHandler(function() { 78 + threadManager.setLoadedThreadPHID(loadedPhid); 79 + }) 80 + // we re-direct to conpherence home so the thread manager will 81 + // fix itself there 82 + .setHandler(function(r) { 83 + JX.$U(r.href).go(); 84 + }) 85 + .start(); 86 + } 87 + ); 88 + 89 + /* settings widget */ 90 + var onsubmitSettings = function (e) { 91 + e.kill(); 92 + var form = e.getNode('tag:form'); 93 + var button = JX.DOM.find(form, 'button'); 94 + JX.Workflow.newFromForm(form) 95 + .setHandler(JX.bind(this, function (r) { 96 + new JX.Notification() 97 + .setDuration(6000) 98 + .setContent(r) 99 + .show(); 100 + button.disabled = ''; 101 + JX.DOM.alterClass(button, 'disabled', false); 102 + })) 103 + .start(); 104 + }; 105 + 106 + JX.Stratcom.listen( 107 + ['submit', 'didSyntheticSubmit'], 108 + 'notifications-update', 109 + onsubmitSettings 110 + ); 111 + 112 + });
+1
webroot/rsrc/js/application/conpherence/behavior-toggle-widget.js
··· 14 14 var node = JX.$('conpherence-main-layout'); 15 15 config.show = !config.show; 16 16 JX.DOM.alterClass(node, 'hide-widgets', !config.show); 17 + JX.Stratcom.invoke('resize'); 17 18 18 19 new JX.Request(config.settingsURI) 19 20 .setData({value: (config.show ? 1 : 0)})
-382
webroot/rsrc/js/application/conpherence/behavior-widget-pane.js
··· 1 - /** 2 - * @requires javelin-behavior 3 - * javelin-dom 4 - * javelin-stratcom 5 - * javelin-workflow 6 - * javelin-util 7 - * phabricator-notification 8 - * javelin-behavior-device 9 - * phuix-dropdown-menu 10 - * phuix-action-list-view 11 - * phuix-action-view 12 - * conpherence-thread-manager 13 - * @provides javelin-behavior-conpherence-widget-pane 14 - */ 15 - 16 - JX.behavior('conpherence-widget-pane', function(config) { 17 - 18 - /** 19 - * There can be race conditions around loading the messages or the widgets 20 - * first. Keep track of what widgets we've loaded with this variable. 21 - */ 22 - var _loadedWidgetsID = null; 23 - 24 - /** 25 - * At any given time there can be only one selected widget. Keep track of 26 - * which one it is by the user-facing name for ease of use with 27 - * PhabricatorDropdownMenuItems. 28 - */ 29 - var _selectedWidgetName = null; 30 - 31 - /** 32 - * This is potentially built each time the user switches conpherence threads 33 - * or when the result JX.Device.getDevice() changes from desktop to some 34 - * other value. 35 - */ 36 - var buildDeviceWidgetSelector = function (data) { 37 - var device_header = _getDeviceWidgetHeader(); 38 - if (!device_header) { 39 - return; 40 - } 41 - JX.DOM.show(device_header); 42 - var device_menu = new JX.PHUIXDropdownMenu(device_header); 43 - data.deviceMenu = true; 44 - _buildWidgetSelector(device_menu, data); 45 - }; 46 - 47 - /** 48 - * This is potentially built each time the user switches conpherence threads 49 - * or when the result JX.Device.getDevice() changes from mobile or tablet to 50 - * desktop. 51 - */ 52 - var buildDesktopWidgetSelector = function (data) { 53 - var root = JX.DOM.find(document, 'div', 'conpherence-layout'); 54 - var widget_pane = JX.DOM.find(root, 'div', 'conpherence-widget-pane'); 55 - var widget_header = JX.DOM.find(widget_pane, 'a', 'widgets-selector'); 56 - 57 - var menu = new JX.PHUIXDropdownMenu(widget_header); 58 - menu 59 - .setAlign('left') 60 - .setOffsetY(4); 61 - 62 - data.deviceMenu = false; 63 - _buildWidgetSelector(menu, data); 64 - }; 65 - 66 - /** 67 - * Workhorse that actually builds the widget selector. Note some fancy bits 68 - * where we listen for the "open" event and enable / disable widgets as 69 - * appropos. 70 - */ 71 - var _buildWidgetSelector = function (menu, data) { 72 - _loadedWidgetsID = data.threadID; 73 - 74 - var list = new JX.PHUIXActionListView(); 75 - var map = {}; 76 - 77 - var widgets = config.widgetRegistry; 78 - for (var widget in widgets) { 79 - var widget_data = widgets[widget]; 80 - if (widget_data.deviceOnly && data.deviceMenu === false) { 81 - continue; 82 - } 83 - 84 - var handler; 85 - var href; 86 - handler = JX.bind(null, function(widget, e) { 87 - toggleWidget({widget: widget}); 88 - e.prevent(); 89 - menu.close(); 90 - }, widget); 91 - var item = new JX.PHUIXActionView() 92 - .setIcon(widget_data.icon || 'none') 93 - .setName(widget_data.name) 94 - .setHref(href) 95 - .setHandler(handler); 96 - map[widget_data.name] = item; 97 - list.addItem(item); 98 - } 99 - 100 - menu 101 - .setWidth(200) 102 - .setContent(list.getNode()); 103 - 104 - menu.listen('open', function() { 105 - for (var k in map) { 106 - map[k].setDisabled((k == _selectedWidgetName)); 107 - } 108 - }); 109 - }; 110 - 111 - /** 112 - * Since this is not always on the page, avoid having a repeat 113 - * try / catch block and consolidate into this helper function. 114 - */ 115 - var _getDeviceWidgetHeader = function () { 116 - var root = JX.DOM.find(document, 'div', 'conpherence-layout'); 117 - var device_header = null; 118 - try { 119 - device_header = JX.DOM.find( 120 - root, 121 - 'a', 122 - 'device-widgets-selector'); 123 - } catch (ex) { 124 - // is okay - no deviceWidgetHeader yet... but bail time 125 - } 126 - return device_header; 127 - }; 128 - 129 - /** 130 - * Responder to the 'conpherence-did-redraw-thread' event, this bad boy 131 - * hides or shows the device widget selector as appropros. 132 - */ 133 - var _didRedrawThread = function (data) { 134 - if (_loadedWidgetsID === null || _loadedWidgetsID != data.threadID) { 135 - return; 136 - } 137 - var device = JX.Device.getDevice(); 138 - var device_selector = _getDeviceWidgetHeader(); 139 - if (device == 'desktop') { 140 - JX.DOM.hide(device_selector); 141 - } else { 142 - JX.DOM.show(device_selector); 143 - } 144 - if (data.buildDeviceWidgetSelector) { 145 - buildDeviceWidgetSelector(data); 146 - } 147 - toggleWidget(data); 148 - }; 149 - JX.Stratcom.listen( 150 - 'conpherence-did-redraw-thread', 151 - null, 152 - function (e) { 153 - _didRedrawThread(e.getData()); 154 - } 155 - ); 156 - 157 - /** 158 - * Toggling a widget involves showing / hiding the appropriate widget 159 - * bodies as well as updating the selectors to have the label on the 160 - * newly selected widget. 161 - */ 162 - var toggleWidget = function (data) { 163 - var widgets = config.widgetRegistry; 164 - var widget_data = widgets[data.widget]; 165 - var device = JX.Device.getDevice(); 166 - var is_desktop = device == 'desktop'; 167 - 168 - if (widget_data.deviceOnly && is_desktop) { 169 - return; 170 - } 171 - _selectedWidgetName = widget_data.name; 172 - 173 - var device_header = _getDeviceWidgetHeader(); 174 - if (device_header) { 175 - // this is fragile but adding a sigil to this element is awkward 176 - var device_header_spans = JX.DOM.scry(device_header, 'span'); 177 - var device_header_span = device_header_spans[1]; 178 - JX.DOM.setContent( 179 - device_header_span, 180 - widget_data.name); 181 - } 182 - 183 - // don't update the non-device selector with device only widget stuff 184 - if (!widget_data.deviceOnly) { 185 - var root = JX.DOM.find(document, 'div', 'conpherence-layout'); 186 - var widget_pane = JX.DOM.find(root, 'div', 'conpherence-widget-pane'); 187 - var widget_header = JX.DOM.find(widget_pane, 'a', 'widgets-selector'); 188 - var adder = JX.DOM.find(widget_pane, 'a', 'conpherence-widget-adder'); 189 - var threadManager = JX.ConpherenceThreadManager.getInstance(); 190 - var disabled = !threadManager.getCanEditLoadedThread(); 191 - JX.DOM.setContent( 192 - widget_header, 193 - widget_data.name); 194 - JX.DOM.appendContent( 195 - widget_header, 196 - JX.$N('span', { className : 'caret' })); 197 - if (widget_data.hasCreate) { 198 - JX.DOM.show(adder); 199 - JX.DOM.alterClass( 200 - adder, 201 - 'disabled', 202 - disabled); 203 - } else { 204 - JX.DOM.hide(adder); 205 - } 206 - } 207 - 208 - for (var widget in config.widgetRegistry) { 209 - widget_data = widgets[widget]; 210 - if (widget_data.deviceOnly && is_desktop) { 211 - // some one off code for conpherence messages which are device-only 212 - // as a widget, but shown always on the desktop 213 - if (widget == 'conpherence-message-pane') { 214 - JX.$(widget).style.display = 'block'; 215 - } 216 - continue; 217 - } 218 - if (widget == data.widget) { 219 - JX.$(widget).style.display = 'block'; 220 - // some one off code for conpherence messages - fancier refresh tech 221 - if (widget == 'conpherence-message-pane') { 222 - JX.Stratcom.invoke('conpherence-redraw-thread', null, {}); 223 - JX.Stratcom.invoke('conpherence-update-page-data', null, {}); 224 - } 225 - } else { 226 - JX.$(widget).style.display = 'none'; 227 - } 228 - } 229 - }; 230 - 231 - JX.Stratcom.listen( 232 - 'conpherence-update-widgets', 233 - null, 234 - function (e) { 235 - var data = e.getData(); 236 - if (data.buildSelectors) { 237 - buildDesktopWidgetSelector(data); 238 - buildDeviceWidgetSelector(data); 239 - } 240 - if (data.toggleWidget) { 241 - toggleWidget(data); 242 - } 243 - }); 244 - 245 - /** 246 - * Generified adding new stuff to widgets technology! 247 - */ 248 - JX.Stratcom.listen( 249 - ['click'], 250 - 'conpherence-widget-adder', 251 - function (e) { 252 - e.kill(); 253 - 254 - var widgets = config.widgetRegistry; 255 - // the widget key might be in node data, but otherwise use the 256 - // selected widget 257 - var event_data = e.getNodeData('conpherence-widget-adder'); 258 - var widget_key = _selectedWidgetName; 259 - if (event_data.widget) { 260 - widget_key = widgets[event_data.widget].name; 261 - } 262 - 263 - var widget_to_update = null; 264 - var create_data = null; 265 - for (var widget in widgets) { 266 - if (widgets[widget].name == widget_key) { 267 - create_data = widgets[widget].createData; 268 - widget_to_update = widget; 269 - break; 270 - } 271 - } 272 - // this should be impossible, but hey 273 - if (!widget_to_update) { 274 - return; 275 - } 276 - var href = config.widgetBaseUpdateURI + _loadedWidgetsID + '/'; 277 - if (create_data.customHref) { 278 - href = create_data.customHref; 279 - } 280 - 281 - var threadManager = JX.ConpherenceThreadManager.getInstance(); 282 - var latest_transaction_id = threadManager.getLatestTransactionID(); 283 - var data = { 284 - latest_transaction_id : latest_transaction_id, 285 - action : create_data.action 286 - }; 287 - 288 - var workflow = new JX.Workflow(href, data) 289 - .setHandler(function (r) { 290 - var threadManager = JX.ConpherenceThreadManager.getInstance(); 291 - threadManager.setLatestTransactionID(r.latest_transaction_id); 292 - var root = JX.DOM.find(document, 'div', 'conpherence-layout'); 293 - if (create_data.refreshFromResponse) { 294 - var messages = null; 295 - try { 296 - messages = JX.DOM.find(root, 'div', 'conpherence-messages'); 297 - } catch (ex) { 298 - } 299 - if (messages) { 300 - JX.DOM.appendContent(messages, JX.$H(r.transactions)); 301 - JX.Stratcom.invoke('conpherence-redraw-thread', null, {}); 302 - } 303 - 304 - if (r.people_widget) { 305 - try { 306 - var people_root = JX.DOM.find(root, 'div', 'widgets-people'); 307 - // update the people widget 308 - JX.DOM.setContent( 309 - people_root, 310 - JX.$H(r.people_widget)); 311 - } catch (ex) { 312 - } 313 - } 314 - 315 - // otherwise let's redraw the widget somewhat lazily 316 - } else { 317 - JX.Stratcom.invoke( 318 - 'conpherence-reload-widget', 319 - null, 320 - { 321 - threadID : _loadedWidgetsID, 322 - widget : widget_to_update 323 - }); 324 - } 325 - }); 326 - 327 - threadManager.syncWorkflow(workflow, 'submit'); 328 - } 329 - ); 330 - 331 - JX.Stratcom.listen( 332 - ['touchstart', 'mousedown'], 333 - 'remove-person', 334 - function (e) { 335 - var href = config.widgetBaseUpdateURI + _loadedWidgetsID + '/'; 336 - var data = e.getNodeData('remove-person'); 337 - 338 - // While the user is removing themselves, disable the notification 339 - // update behavior. If we don't do this, the user can get an error 340 - // when they remove themselves about permissions as the notification 341 - // code tries to load what jist happened. 342 - var threadManager = JX.ConpherenceThreadManager.getInstance(); 343 - var loadedPhid = threadManager.getLoadedThreadPHID(); 344 - threadManager.setLoadedThreadPHID(null); 345 - 346 - new JX.Workflow(href, data) 347 - .setCloseHandler(function() { 348 - threadManager.setLoadedThreadPHID(loadedPhid); 349 - }) 350 - // we re-direct to conpherence home so the thread manager will 351 - // fix itself there 352 - .setHandler(function(r) { 353 - JX.$U(r.href).go(); 354 - }) 355 - .start(); 356 - } 357 - ); 358 - 359 - /* settings widget */ 360 - var onsubmitSettings = function (e) { 361 - e.kill(); 362 - var form = e.getNode('tag:form'); 363 - var button = JX.DOM.find(form, 'button'); 364 - JX.Workflow.newFromForm(form) 365 - .setHandler(JX.bind(this, function (r) { 366 - new JX.Notification() 367 - .setDuration(6000) 368 - .setContent(r) 369 - .show(); 370 - button.disabled = ''; 371 - JX.DOM.alterClass(button, 'disabled', false); 372 - })) 373 - .start(); 374 - }; 375 - 376 - JX.Stratcom.listen( 377 - ['submit', 'didSyntheticSubmit'], 378 - 'notifications-update', 379 - onsubmitSettings 380 - ); 381 - 382 - });