diff --git a/src/applications/conpherence/controller/ConpherenceListController.php b/src/applications/conpherence/controller/ConpherenceListController.php index d60f6053e..8b8b3414c 100644 --- a/src/applications/conpherence/controller/ConpherenceListController.php +++ b/src/applications/conpherence/controller/ConpherenceListController.php @@ -1,73 +1,74 @@ <?php /** * @group conpherence */ final class ConpherenceListController extends ConpherenceController { private $conpherenceID; 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(); $user = $request->getUser(); $title = pht('Conpherence'); $conpherence_id = $this->getConpherenceID(); $current_selection_epoch = null; $conpherence = null; if ($conpherence_id) { $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withIDs(array($conpherence_id)) ->executeOne(); if (!$conpherence) { return new Aphront404Response(); } if ($conpherence->getTitle()) { $title = $conpherence->getTitle(); } $participant = $conpherence->getParticipant($user->getPHID()); $current_selection_epoch = $participant->getDateTouched(); } list($unread, $read) = $this->loadStartingConpherences( $current_selection_epoch); $thread_view = id(new ConpherenceThreadListView()) ->setUser($user) ->setBaseURI($this->getApplicationURI()) ->setUnreadThreads($unread) ->setReadThreads($read); - $main_pane = id(new ConpherenceLayoutView()) + $layout = id(new ConpherenceLayoutView()) ->setBaseURI($this->getApplicationURI()) - ->setThreadView($thread_view); + ->setThreadView($thread_view) + ->setRole('list'); if ($conpherence) { - $main_pane->setThread($conpherence); + $layout->setThread($conpherence); } return $this->buildApplicationPage( - $main_pane, + $layout, array( 'title' => $title, 'device' => true, )); } } diff --git a/src/applications/conpherence/view/ConpherenceLayoutView.php b/src/applications/conpherence/view/ConpherenceLayoutView.php index 34f9aaa2e..56aa32100 100644 --- a/src/applications/conpherence/view/ConpherenceLayoutView.php +++ b/src/applications/conpherence/view/ConpherenceLayoutView.php @@ -1,117 +1,124 @@ <?php final class ConpherenceLayoutView extends AphrontView { private $thread; private $baseURI; private $threadView; + private $role; + + public function setRole($role) { + $this->role = $role; + return $this; + } public function getThreadView() { return $this->threadView; } public function setBaseURI($base_uri) { $this->baseURI = $base_uri; return $this; } public function setThread(ConpherenceThread $thread) { $this->thread = $thread; return $this; } public function setThreadView(ConpherenceThreadListView $thead_view) { $this->threadView = $thead_view; return $this; } public function render() { Javelin::initBehavior('conpherence-menu', array( 'base_uri' => $this->baseURI, 'header' => 'conpherence-header-pane', 'messages' => 'conpherence-messages', 'messages_pane' => 'conpherence-message-pane', 'widgets_pane' => 'conpherence-widget-pane', 'form_pane' => 'conpherence-form', 'menu_pane' => 'conpherence-menu', 'selectedID' => ($this->thread ? $this->thread->getID() : null), + 'role' => $this->role, )); Javelin::initBehavior('conpherence-drag-and-drop-photo', array( 'target' => 'conpherence-header-pane', 'form_pane' => 'conpherence-form', 'upload_uri' => '/file/dropupload/', 'activated_class' => 'conpherence-header-upload-photo', )); return javelin_tag( 'div', array( 'sigil' => 'conpherence-layout', - 'class' => 'conpherence-layout', + 'class' => 'conpherence-layout conpherence-role-'.$this->role, ), array( javelin_tag( 'div', array( 'class' => 'phabricator-nav-column-background', ), ''), javelin_tag( 'div', array( 'class' => 'conpherence-menu-pane phabricator-side-menu', 'sigil' => 'conpherence-menu-pane', ), nonempty($this->threadView, '')), javelin_tag( 'div', array( 'class' => 'conpherence-content-pane', ), array( javelin_tag( 'div', array( 'class' => 'conpherence-header-pane', 'id' => 'conpherence-header-pane', 'sigil' => 'conpherence-header', ), ''), phutil_tag( 'div', array( 'class' => 'conpherence-widget-pane', 'id' => 'conpherence-widget-pane' ), ''), javelin_tag( 'div', array( 'class' => 'conpherence-message-pane', 'id' => 'conpherence-message-pane' ), array( javelin_tag( 'div', array( 'class' => 'conpherence-messages', 'id' => 'conpherence-messages', 'sigil' => 'conpherence-messages', ), ''), phutil_tag( 'div', array( 'id' => 'conpherence-form' ), '') )), )), )); } } diff --git a/webroot/rsrc/css/application/conpherence/menu.css b/webroot/rsrc/css/application/conpherence/menu.css index 9340124e8..9b87c65a5 100644 --- a/webroot/rsrc/css/application/conpherence/menu.css +++ b/webroot/rsrc/css/application/conpherence/menu.css @@ -1,128 +1,138 @@ /** * @provides conpherence-menu-css */ .conpherence-layout { position: fixed; bottom: 0; left: 0; right: 0; top: 44px; } .conpherence-menu-pane { width: 300px; position: absolute; overflow-x: hidden; overflow-y: auto; top: 0; bottom: 0; } .conpherence-content-pane { margin-left: 300px; position: relative; } div.conpherence-layout .phabricator-nav-column-background { display: block; width: 300px; } +.device .conpherence-role-list .conpherence-menu-pane, +.device .conpherence-role-list .phabricator-nav-column-background { + width: 100%; +} + +.device .conpherence-role-list .conpherence-content-pane { + display: none; +} + + .conpherence-menu .conpherence-menu-item-view { display: block; height: 70px; width: 100%; overflow: hidden; position: relative; text-decoration: none; border-top: solid 1px #3B3D3E; border-bottom: solid 1px #1C1F21; border-right: 0; border-left: 2px solid transparent; } .conpherence-menu .conpherence-selected { background: rgba(0, 0, 0, .6); border-left: 2px solid #66CCFF; } -.conpherence-menu .conpherence-menu-item-view:hover { +.device-desktop .conpherence-menu .conpherence-menu-item-view:hover { background-image: url('/rsrc/image/texture/dark-menu-hover.png'); } .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-image { top: 6px; left: 6px; display: block; position: absolute; width: 50px; height: 50px; border: 4px solid rgb(29, 32, 34); border-radius: 2px; } .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-title { display: block; margin-top: 12px; margin-left: 70px; text-align: left; font-weight: bold; font-size: 12px; color: #ffffff; text-shadow: 0px 1px 1px #000000; } .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-subtitle { display: block; color: #bfbfbf; font-size: 11px; margin-top: 2px; margin-left: 70px; font-style: italic; } .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-message-text { display: block; color: #66CCFF; font-size: 12px; margin-top: 4px; margin-left: 70px; } .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-unread-count { position: absolute; left: 48px; top: 3px; background: #f00; border-radius: 10px; color: white; font-weight: bold; padding: 1px 6px 2px; border: 1px solid #a00; font-size: 12px; } .conpherence-menu .hide-unread-count .conpherence-menu-item-unread-count, .conpherence-menu .conpherence-selected .conpherence-menu-item-unread-count { display: none; } .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-date { position: absolute; top: 10px; right: 12px; color: white; font-size: 12px; } .no-conpherences-menu-item { color: #a1a5a9; border-top: solid 1px #3B3D3E; padding: 20px 0; margin: 0px auto; width: 300px; text-align: center; } diff --git a/webroot/rsrc/css/application/conpherence/message-pane.css b/webroot/rsrc/css/application/conpherence/message-pane.css index 280709413..8fac609ad 100644 --- a/webroot/rsrc/css/application/conpherence/message-pane.css +++ b/webroot/rsrc/css/application/conpherence/message-pane.css @@ -1,123 +1,106 @@ /** * @provides conpherence-message-pane-css */ .conpherence-message-pane { position: fixed; left: 300px; right: 281px; top: 125px; min-width: 300px; width: auto; height: 100%; } -/* js should control this */ -.device-phone .conpherence-message-pane { - display: none; -} - .conpherence-show-older-messages { display: block; background: #e0e3ec; margin: 10px; text-align: center; padding: 10px; color: #18559D; } .conpherence-message-pane .conpherence-messages { position: fixed; left: 300px; right: 281px; top: 125px; bottom: 142px; overflow-y: auto; box-shadow: inset 1px 4px 5px rgba(0,0,0,0.1); -webkit-overflow-scrolling: touch; } .conpherence-message-pane .phabricator-form-view { border-width: 0; background: none; height: 143px; padding: 0; position: fixed; bottom: 0; left: 300px; right: 281px; } -.device-tablet -.conpherence-message-pane .phabricator-form-view, -.device-tablet -.conpherence-message-pane .conpherence-messages, -.device-tablet -.conpherence-message-pane { - left: 0px; -} .conpherence-message-pane .aphront-form-input { margin: 0; width: 100%; } .conpherence-message-pane .phabricator-transaction-view { margin: 10px 0 10px 15px; } .conpherence-message-pane .phabricator-transaction-detail { border-width: 0; margin-left: 45px; } .conpherence-message-pane .phabricator-transaction-info, .conpherence-message-pane .phabricator-content-source-view { color: #b7b7b7; } .conpherence-message-pane .phabricator-transaction-header, .conpherence-message-pane .phabricator-transaction-info, .conpherence-message-pane .phabricator-transaction-content { background: none; } .conpherence-message-pane .phabricator-transaction-content { padding-top: 0; } .conpherence-message-pane .conpherence-edited .phabricator-transaction-content { padding: 0 1em 0 1em; margin-top: -4px; } .conpherence-message-pane .aphront-form-control { padding: 0; } .conpherence-message-pane .aphront-form-control-submit { margin-right: 8px; } .conpherence-message-pane .remarkup-assist-bar { border-width: 1px 0 0; border-color: #CCC; border-bottom: transparent; } .conpherence-message-pane .remarkup-assist-textarea { border-width: 0 0 1px 0; border-color: #e7e7e7; } .conpherence-message-pane .remarkup-assist-textarea:focus { outline: none; } .conpherence-message-pane .aphront-form-input textarea { padding: 6px; height: 6em; } -.device .conpherence-message-pane .phabricator-form-view .aphront-form-control .aphront-form-input { - margin: 0; - width: 100%; -} diff --git a/webroot/rsrc/js/application/conpherence/behavior-menu.js b/webroot/rsrc/js/application/conpherence/behavior-menu.js index a0c59c656..b249e907b 100644 --- a/webroot/rsrc/js/application/conpherence/behavior-menu.js +++ b/webroot/rsrc/js/application/conpherence/behavior-menu.js @@ -1,177 +1,182 @@ /** * @provides javelin-behavior-conpherence-menu * @requires javelin-behavior * javelin-dom * javelin-request * javelin-stratcom * javelin-workflow * javelin-behavior-device * javelin-history */ JX.behavior('conpherence-menu', function(config) { var thread = { selected: null, node: null, visible: null }; function selectthreadid(id) { var threads = JX.DOM.scry(document.body, 'a', 'conpherence-menu-click'); for (var ii = 0; ii < threads.length; ii++) { var data = JX.Stratcom.getData(threads[ii]); if (data.id == id) { selectthread(threads[ii]); return; } } } function selectthread(node) { if (node === thread.node) { return; } if (thread.node) { JX.DOM.alterClass(thread.node, 'conpherence-selected', false); JX.DOM.alterClass(thread.node, 'hide-unread-count', false); } JX.DOM.alterClass(node, 'conpherence-selected', true); JX.DOM.alterClass(node, 'hide-unread-count', true); thread.node = node; var data = JX.Stratcom.getData(node); thread.selected = data.id; JX.History.replace(config.base_uri + data.id + '/'); redrawthread(); } function redrawthread() { if (!thread.node) { return; } if (thread.visible == thread.selected) { return; } var data = JX.Stratcom.getData(thread.node); var uri = config.base_uri + 'view/' + data.id + '/'; var widget_uri = config.base_uri + 'widget/' + data.id + '/'; new JX.Workflow(uri, {}) .setHandler(onresponse) .start(); new JX.Workflow(widget_uri, {}) .setHandler(onwidgetresponse) .start(); thread.visible = thread.selected; } function onwidgetresponse(response) { var widgets = JX.$H(response.widgets); var widgetsRoot = JX.$(config.widgets_pane); JX.DOM.setContent(widgetsRoot, widgets); } function onresponse(response) { var header = JX.$H(response.header); var messages = JX.$H(response.messages); var form = JX.$H(response.form); var headerRoot = JX.$(config.header); var messagesRoot = JX.$(config.messages); var formRoot = JX.$(config.form_pane); var widgetsRoot = JX.$(config.widgets_pane); var menuRoot = JX.$(config.menu_pane); JX.DOM.setContent(headerRoot, header); JX.DOM.setContent(messagesRoot, messages); messagesRoot.scrollTop = messagesRoot.scrollHeight; JX.DOM.setContent(formRoot, form); } JX.Stratcom.listen( 'click', 'conpherence-menu-click', function(e) { if (!e.isNormalClick()) { return; } + // On devices, just follow the link normally. + if (JX.Device.getDevice() != 'desktop') { + return; + } + e.kill(); selectthread(e.getNode('conpherence-menu-click')); }); JX.Stratcom.listen('click', 'conpherence-edit-metadata', function (e) { e.kill(); var root = JX.$(config.form_pane); var form = JX.DOM.find(root, 'form'); var data = e.getNodeData('conpherence-edit-metadata'); new JX.Workflow.newFromForm(form, data) .setHandler(function (r) { // update the header JX.DOM.setContent( JX.$(config.header), JX.$H(r.header) ); // update the menu entry as well JX.DOM.replace( JX.$(r.conpherence_phid + '-nav-item'), JX.$H(r.nav_item) ); }) .start(); }); JX.Stratcom.listen('click', 'show-older-messages', function(e) { e.kill(); var last_offset = e.getNodeData('show-older-messages').offset; var conf_id = e.getNodeData('show-older-messages').ID; JX.DOM.remove(e.getNode('show-older-messages')); var messages_root = JX.$(config.messages); new JX.Request('/conpherence/view/'+conf_id+'/', function(r) { var messages = JX.$H(r.messages); JX.DOM.prependContent(messages_root, JX.$H(messages)); }).setData({ offset: last_offset+1 }).send(); }); // On mobile, we just show a thread list, so we don't want to automatically // select or load any threads. On Desktop, we automatically select the first // thread. function ondevicechange() { if (JX.Device.getDevice() != 'desktop') { return; } // If there's no thread selected yet, select the first thread. if (!thread.selected) { var threads = JX.DOM.scry(document.body, 'a', 'conpherence-menu-click'); if (threads.length) { selectthread(threads[0]); } } // We might have a selected but undrawn thread for redrawthread(); } JX.Stratcom.listen('phabricator-device-change', null, ondevicechange); ondevicechange(); // If there's a currently visible thread, select it. if (config.selectedID) { selectthreadid(config.selectedID); } });