@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Conpherence - make pontificate be all ajaxy

Summary: serving up for some feedback -- does anything fancy need to happen when new messages load (say highlight em and fade the highlight out) or just scrolling down okay? in JS I have a TODO about how come it doesn't work; that code works okay in my console if I print out various debugging values and enter them in manually.

Test Plan: pontificate with myself; note new message and message entry updates properly. pontificate as different user then as myself; note two messages load and message entry updates properly. pontificate as a user who isn't the last to reply; note new message and message entry updates properly

Reviewers: epriestley, chad

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T2522

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

+327 -114
+15
src/__celerity_resource_map__.php
··· 1162 1162 ), 1163 1163 'disk' => '/rsrc/js/application/conpherence/behavior-menu.js', 1164 1164 ), 1165 + 'javelin-behavior-conpherence-pontificate' => 1166 + array( 1167 + 'uri' => '/res/7fb8cf6e/rsrc/js/application/conpherence/behavior-pontificate.js', 1168 + 'type' => 'js', 1169 + 'requires' => 1170 + array( 1171 + 0 => 'javelin-behavior', 1172 + 1 => 'javelin-dom', 1173 + 2 => 'javelin-workflow', 1174 + 3 => 'javelin-util', 1175 + 4 => 'javelin-stratcom', 1176 + 5 => 'javelin-uri', 1177 + ), 1178 + 'disk' => '/rsrc/js/application/conpherence/behavior-pontificate.js', 1179 + ), 1165 1180 'javelin-behavior-conpherence-widget-pane' => 1166 1181 array( 1167 1182 'uri' => '/res/4fb51b46/rsrc/js/application/conpherence/behavior-widget-pane.js',
+2
src/__phutil_library_map__.php
··· 216 216 'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php', 217 217 'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php', 218 218 'ConpherencePeopleMenuEventListener' => 'applications/conpherence/events/ConpherencePeopleMenuEventListener.php', 219 + 'ConpherencePontificateControl' => 'applications/conpherence/view/ConpherencePontificateControl.php', 219 220 'ConpherenceReplyHandler' => 'applications/conpherence/mail/ConpherenceReplyHandler.php', 220 221 'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php', 221 222 'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php', ··· 1759 1760 'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery', 1760 1761 'ConpherenceParticipationStatus' => 'ConpherenceConstants', 1761 1762 'ConpherencePeopleMenuEventListener' => 'PhutilEventListener', 1763 + 'ConpherencePontificateControl' => 'AphrontFormControl', 1762 1764 'ConpherenceReplyHandler' => 'PhabricatorMailReplyHandler', 1763 1765 'ConpherenceThread' => 1764 1766 array(
+155 -32
src/applications/conpherence/controller/ConpherenceController.php
··· 93 93 return $this; 94 94 } 95 95 96 - public function buildSideNavView($filter = null) { 96 + public function buildSideNavView($filter = null, $for_application = false) { 97 97 require_celerity_resource('conpherence-menu-css'); 98 98 $unread_conpherences = $this->getUnreadConpherences(); 99 99 $read_conpherences = $this->getReadConpherences(); ··· 109 109 pht('New Conversation'), 110 110 $this->getApplicationURI('new/')); 111 111 $nav->addLabel(pht('Unread')); 112 - $nav = $this->addConpherencesToNav($unread_conpherences, $nav); 112 + $nav = $this->addConpherencesToNav( 113 + $unread_conpherences, 114 + $nav, 115 + false, 116 + $for_application); 113 117 $nav->addLabel(pht('Read')); 114 - $nav = $this->addConpherencesToNav($read_conpherences, $nav, true); 118 + $nav = $this->addConpherencesToNav( 119 + $read_conpherences, 120 + $nav, 121 + true, 122 + $for_application); 115 123 $nav->selectFilter($filter); 116 124 return $nav; 117 125 } ··· 119 127 private function addConpherencesToNav( 120 128 array $conpherences, 121 129 AphrontSideNavFilterView $nav, 122 - $read = false) { 130 + $read = false, 131 + $for_application = false) { 123 132 124 133 $user = $this->getRequest()->getUser(); 134 + $id_suffix = $for_application ? '-menu-item' : '-nav-item'; 125 135 foreach ($conpherences as $conpherence) { 126 - $uri = $this->getApplicationURI('view/'.$conpherence->getID().'/'); 127 - $data = $conpherence->getDisplayData( 128 - $user, 129 - null); 130 - $title = $data['title']; 131 - $subtitle = $data['subtitle']; 132 - $unread_count = $data['unread_count']; 133 - $epoch = $data['epoch']; 134 - $image = $data['image']; 135 - $snippet = $data['snippet']; 136 - 137 - $item = id(new ConpherenceMenuItemView()) 138 - ->setUser($user) 139 - ->setTitle($title) 140 - ->setSubtitle($subtitle) 141 - ->setHref($uri) 142 - ->setEpoch($epoch) 143 - ->setImageURI($image) 144 - ->setMessageText($snippet) 145 - ->setUnreadCount($unread_count) 146 - ->setID($conpherence->getPHID()) 147 - ->addSigil('conpherence-menu-click') 148 - ->setMetadata(array('id' => $conpherence->getID())); 136 + $selected = false; 149 137 if ($this->getSelectedConpherencePHID() == $conpherence->getPHID()) { 150 - $item->addClass('conpherence-selected'); 151 - $item->addClass('hide-unread-count'); 152 - } 138 + $selected = true; 139 + } 140 + $item = $this->buildConpherenceMenuItem( 141 + $conpherence, 142 + $id_suffix, 143 + $selected); 153 144 154 145 $nav->addCustomBlock($item->render()); 155 146 } ··· 170 161 } 171 162 172 163 public function buildApplicationMenu() { 173 - return $this->buildSideNavView()->getMenu(); 164 + return $this->buildSideNavView( 165 + $filter = null, 166 + $for_application = true)->getMenu(); 174 167 } 175 168 176 169 public function buildApplicationCrumbs() { ··· 189 182 return $crumbs; 190 183 } 191 184 185 + protected function buildConpherenceMenuItem( 186 + $conpherence, 187 + $id_suffix, 188 + $selected) { 189 + 190 + $user = $this->getRequest()->getUser(); 191 + $uri = $this->getApplicationURI('view/'.$conpherence->getID().'/'); 192 + $data = $conpherence->getDisplayData( 193 + $user, 194 + null); 195 + $title = $data['title']; 196 + $subtitle = $data['subtitle']; 197 + $unread_count = $data['unread_count']; 198 + $epoch = $data['epoch']; 199 + $image = $data['image']; 200 + $snippet = $data['snippet']; 201 + 202 + $item = id(new ConpherenceMenuItemView()) 203 + ->setUser($user) 204 + ->setTitle($title) 205 + ->setSubtitle($subtitle) 206 + ->setHref($uri) 207 + ->setEpoch($epoch) 208 + ->setImageURI($image) 209 + ->setMessageText($snippet) 210 + ->setUnreadCount($unread_count) 211 + ->setID($conpherence->getPHID().$id_suffix) 212 + ->addSigil('conpherence-menu-click') 213 + ->setMetadata(array('id' => $conpherence->getID())); 214 + 215 + if ($selected) { 216 + $item 217 + ->addClass('conpherence-selected') 218 + ->addClass('hide-unread-count'); 219 + } 220 + 221 + return $item; 222 + } 223 + 224 + protected function buildHeaderPaneContent(ConpherenceThread $conpherence) { 225 + $user = $this->getRequest()->getUser(); 226 + $display_data = $conpherence->getDisplayData( 227 + $user, 228 + ConpherenceImageData::SIZE_HEAD); 229 + $edit_href = $this->getApplicationURI('update/'.$conpherence->getID().'/'); 230 + $class_mod = $display_data['image_class']; 231 + 232 + $header = 233 + phutil_tag( 234 + 'div', 235 + array( 236 + 'class' => 'upload-photo' 237 + ), 238 + pht('Drop photo here to change this Conpherence photo.')). 239 + javelin_tag( 240 + 'a', 241 + array( 242 + 'class' => 'edit', 243 + 'href' => $edit_href, 244 + 'sigil' => 'workflow edit-action', 245 + ), 246 + ''). 247 + phutil_tag( 248 + 'div', 249 + array( 250 + 'class' => $class_mod.'header-image', 251 + 'style' => 'background-image: url('.$display_data['image'].');' 252 + ), 253 + ''). 254 + phutil_tag( 255 + 'div', 256 + array( 257 + 'class' => $class_mod.'title', 258 + ), 259 + $display_data['title']). 260 + phutil_tag( 261 + 'div', 262 + array( 263 + 'class' => $class_mod.'subtitle', 264 + ), 265 + $display_data['subtitle']); 266 + 267 + return $header; 268 + } 269 + 270 + protected function renderConpherenceTransactions( 271 + ConpherenceThread $conpherence) { 272 + 273 + $user = $this->getRequest()->getUser(); 274 + $transactions = $conpherence->getTransactions(); 275 + $handles = $conpherence->getHandles(); 276 + $rendered_transactions = array(); 277 + $engine = id(new PhabricatorMarkupEngine()) 278 + ->setViewer($user); 279 + foreach ($transactions as $key => $transaction) { 280 + if ($transaction->shouldHide()) { 281 + unset($transactions[$key]); 282 + continue; 283 + } 284 + if ($transaction->getComment()) { 285 + $engine->addObject( 286 + $transaction->getComment(), 287 + PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT); 288 + } 289 + } 290 + $engine->process(); 291 + foreach ($transactions as $transaction) { 292 + $rendered_transactions[] = id(new ConpherenceTransactionView()) 293 + ->setUser($user) 294 + ->setConpherenceTransaction($transaction) 295 + ->setHandles($handles) 296 + ->setMarkupEngine($engine) 297 + ->render(); 298 + } 299 + $latest_transaction_id = $transaction->getID(); 300 + $rendered_transactions = phutil_implode_html(' ', $rendered_transactions); 301 + 302 + return array( 303 + 'transactions' => $rendered_transactions, 304 + 'latest_transaction_id' => $latest_transaction_id 305 + ); 306 + 307 + } 308 + 192 309 protected function initJavelinBehaviors() { 193 310 194 311 Javelin::initBehavior('conpherence-menu', ··· 217 334 'upload_uri' => '/file/dropupload/', 218 335 'activated_class' => 'conpherence-header-upload-photo', 219 336 )); 337 + Javelin::initBehavior('conpherence-pontificate', 338 + array( 339 + 'messages' => 'conpherence-messages', 340 + 'header' => 'conpherence-header-pane', 341 + 'menu_pane' => 'conpherence-menu', 342 + 'form_pane' => 'conpherence-form', 343 + )); 220 344 } 221 - 222 345 }
+52 -9
src/applications/conpherence/controller/ConpherenceUpdateController.php
··· 35 35 ->executeOne(); 36 36 $supported_formats = PhabricatorFile::getTransformableImageFormats(); 37 37 38 - $updated = false; 38 + $latest_transaction_id = null; 39 39 $error_view = null; 40 40 $e_file = array(); 41 41 $errors = array(); ··· 54 54 switch ($action) { 55 55 case 'message': 56 56 $message = $request->getStr('text'); 57 + $latest_transaction_id = $request->getInt('latest_transaction_id'); 57 58 $xactions = $editor->generateTransactionsFromText( 58 59 $conpherence, 59 60 $message); ··· 129 130 if ($xactions) { 130 131 try { 131 132 $xactions = $editor->applyTransactions($conpherence, $xactions); 132 - $updated = true; 133 + if ($latest_transaction_id) { 134 + $content = $this->loadAndRenderNewTransactions( 135 + $conpherence_id, 136 + $latest_transaction_id); 137 + return id(new AphrontAjaxResponse()) 138 + ->setContent($content); 139 + } else { 140 + return id(new AphrontRedirectResponse())->setURI( 141 + $this->getApplicationURI($conpherence_id.'/')); 142 + } 133 143 } catch (PhabricatorApplicationTransactionNoEffectException $ex) { 134 144 return id(new PhabricatorApplicationTransactionNoEffectResponse()) 135 145 ->setCancelURI($this->getApplicationURI($conpherence_id.'/')) ··· 141 151 } 142 152 } 143 153 144 - if ($updated) { 145 - return id(new AphrontRedirectResponse())->setURI( 146 - $this->getApplicationURI($conpherence_id.'/')); 147 - } 148 - 149 154 if ($errors) { 150 155 $error_view = id(new AphrontErrorView()) 151 156 ->setTitle(pht('Errors editing conpherence.')) ··· 181 186 ->appendChild( 182 187 id(new ConpherenceFormDragAndDropUploadControl()) 183 188 ->setLabel(pht('Change Image'))); 184 - 185 189 } else { 186 - 187 190 $form 188 191 ->appendChild( 189 192 id(new ConpherenceFormDragAndDropUploadControl()) ··· 204 207 ->addSubmitButton() 205 208 ->addCancelButton($this->getApplicationURI($conpherence->getID().'/'))); 206 209 } 210 + 211 + private function loadAndRenderNewTransactions( 212 + $conpherence_id, 213 + $latest_transaction_id) { 214 + 215 + $user = $this->getRequest()->getUser(); 216 + $conpherence = id(new ConpherenceThreadQuery()) 217 + ->setViewer($user) 218 + ->setAfterID($latest_transaction_id) 219 + ->needHeaderPics(true) 220 + ->withIDs(array($conpherence_id)) 221 + ->executeOne(); 222 + 223 + $data = $this->renderConpherenceTransactions($conpherence); 224 + $rendered_transactions = $data['transactions']; 225 + $new_latest_transaction_id = $data['latest_transaction_id']; 226 + 227 + $selected = true; 228 + $nav_item = $this->buildConpherenceMenuItem( 229 + $conpherence, 230 + '-nav-item', 231 + $selected); 232 + $menu_item = $this->buildConpherenceMenuItem( 233 + $conpherence, 234 + '-menu-item', 235 + $selected); 236 + 237 + $header = $this->buildHeaderPaneContent($conpherence); 238 + 239 + $content = array( 240 + 'transactions' => $rendered_transactions, 241 + 'latest_transaction_id' => $new_latest_transaction_id, 242 + 'menu_item' => $menu_item->render(), 243 + 'nav_item' => $nav_item->render(), 244 + 'conpherence_phid' => $conpherence->getPHID(), 245 + 'header' => $header 246 + ); 247 + return $content; 248 + } 249 + 207 250 }
+13 -72
src/applications/conpherence/controller/ConpherenceViewController.php
··· 59 59 60 60 private function renderHeaderPaneContent() { 61 61 require_celerity_resource('conpherence-header-pane-css'); 62 - $user = $this->getRequest()->getUser(); 63 62 $conpherence = $this->getConpherence(); 64 - $display_data = $conpherence->getDisplayData( 65 - $user, 66 - ConpherenceImageData::SIZE_HEAD); 67 - $edit_href = $this->getApplicationURI('update/'.$conpherence->getID().'/'); 68 - $class_mod = $display_data['image_class']; 69 - 70 - $header = 71 - phutil_tag( 72 - 'div', 73 - array( 74 - 'class' => 'upload-photo' 75 - ), 76 - pht('Drop photo here to change this Conpherence photo.')). 77 - javelin_tag( 78 - 'a', 79 - array( 80 - 'class' => 'edit', 81 - 'href' => $edit_href, 82 - 'sigil' => 'workflow edit-action', 83 - ), 84 - ''). 85 - phutil_tag( 86 - 'div', 87 - array( 88 - 'class' => $class_mod.'header-image', 89 - 'style' => 'background-image: url('.$display_data['image'].');' 90 - ), 91 - ''). 92 - phutil_tag( 93 - 'div', 94 - array( 95 - 'class' => $class_mod.'title', 96 - ), 97 - $display_data['title']). 98 - phutil_tag( 99 - 'div', 100 - array( 101 - 'class' => $class_mod.'subtitle', 102 - ), 103 - $display_data['subtitle']); 104 - 63 + $header = $this->buildHeaderPaneContent($conpherence); 105 64 return array('header' => $header); 106 65 } 107 66 67 + 108 68 private function renderMessagePaneContent() { 109 69 require_celerity_resource('conpherence-message-pane-css'); 110 70 $user = $this->getRequest()->getUser(); 111 71 $conpherence = $this->getConpherence(); 112 - $handles = $conpherence->getHandles(); 113 - $rendered_transactions = array(); 114 72 73 + $data = $this->renderConpherenceTransactions($conpherence); 74 + $latest_transaction_id = $data['latest_transaction_id']; 75 + $transactions = $data['transactions']; 115 76 116 - $transactions = $conpherence->getTransactionsFrom(0, 100); 117 - 118 - $engine = id(new PhabricatorMarkupEngine()) 119 - ->setViewer($user); 120 - foreach ($transactions as $transaction) { 121 - if ($transaction->getComment()) { 122 - $engine->addObject( 123 - $transaction->getComment(), 124 - PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT); 125 - } 126 - } 127 - $engine->process(); 128 - foreach ($transactions as $transaction) { 129 - if ($transaction->shouldHide()) { 130 - continue; 131 - } 132 - $rendered_transactions[] = id(new ConpherenceTransactionView()) 133 - ->setUser($user) 134 - ->setConpherenceTransaction($transaction) 135 - ->setHandles($handles) 136 - ->setMarkupEngine($engine) 137 - ->render(); 138 - } 139 - $transactions = phutil_implode_html(' ', $rendered_transactions); 77 + $update_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); 78 + $form_id = celerity_generate_unique_node_id(); 140 79 141 80 $form = 142 81 id(new AphrontFormView()) 143 - ->setWorkflow(true) 144 - ->setAction($this->getApplicationURI('update/'.$conpherence->getID().'/')) 82 + ->setID($form_id) 83 + ->setAction($update_uri) 145 84 ->setFlexible(true) 146 85 ->setUser($user) 147 86 ->addHiddenInput('action', 'message') 87 + ->addHiddenInput('latest_transaction_id', $latest_transaction_id) 148 88 ->appendChild( 149 89 id(new PhabricatorRemarkupControl()) 150 90 ->setUser($user) 151 91 ->setName('text')) 152 92 ->appendChild( 153 - id(new AphrontFormSubmitControl()) 154 - ->setValue(pht('Pontificate')))->render(); 93 + id(new ConpherencePontificateControl()) 94 + ->setFormID($form_id)) 95 + ->render(); 155 96 156 97 $scrollbutton = javelin_tag( 157 98 'a',
+29
src/applications/conpherence/view/ConpherencePontificateControl.php
··· 1 + <?php 2 + 3 + final class ConpherencePontificateControl extends AphrontFormControl { 4 + 5 + private $formID; 6 + 7 + public function setFormID($form_id) { 8 + $this->formID = $form_id; 9 + return $this; 10 + } 11 + public function getFormID() { 12 + return $this->formID; 13 + } 14 + 15 + protected function getCustomControlClass() { 16 + return 'aphront-form-control-submit'; 17 + } 18 + 19 + protected function renderInput() { 20 + 21 + return javelin_tag( 22 + 'button', 23 + array ( 24 + 'sigil' => 'conpherence-pontificate', 25 + ), 26 + pht('Pontificate')); 27 + } 28 + 29 + }
+1 -1
webroot/rsrc/js/application/conpherence/behavior-init.js
··· 8 8 // select the current message 9 9 var selectedConpherence = false; 10 10 if (config.selected_conpherence_id) { 11 - var selected = JX.$(config.selected_conpherence_id); 11 + var selected = JX.$(config.selected_conpherence_id + '-nav-item'); 12 12 JX.Stratcom.invoke( 13 13 'conpherence-initial-selected', 14 14 null,
+60
webroot/rsrc/js/application/conpherence/behavior-pontificate.js
··· 1 + /** 2 + * @provides javelin-behavior-conpherence-pontificate 3 + * @requires javelin-behavior 4 + * javelin-dom 5 + * javelin-request 6 + */ 7 + 8 + JX.behavior('conpherence-pontificate', function(config) { 9 + 10 + var root = JX.$(config.form_pane); 11 + 12 + var onsubmit = function(e) { 13 + e.kill(); 14 + var form = JX.DOM.find(root, 'form'); 15 + JX.Workflow.newFromForm(form) 16 + .setHandler(JX.bind(this, function(r) { 17 + // add the new transactions, probably just our post but who knows 18 + var messages = JX.$(config.messages); 19 + JX.DOM.appendContent(messages, JX.$H(r.transactions)); 20 + messages.scrollTop = messages.scrollHeight; 21 + 22 + // update the menu entry as well 23 + JX.DOM.replace( 24 + JX.$(r.conpherence_phid + '-nav-item'), 25 + JX.$H(r.nav_item) 26 + ); 27 + JX.DOM.replace( 28 + JX.$(r.conpherence_phid + '-menu-item'), 29 + JX.$H(r.menu_item) 30 + ); 31 + 32 + // update the header 33 + JX.DOM.setContent( 34 + JX.$(config.header), 35 + JX.$H(r.header) 36 + ); 37 + 38 + // clear the textarea 39 + var textarea = JX.DOM.find(form, 'textarea'); 40 + textarea.value = ''; 41 + 42 + })) 43 + .start(); 44 + }; 45 + 46 + JX.DOM.listen( 47 + root, 48 + ['submit', 'didSyntheticSubmit'], 49 + null, 50 + onsubmit 51 + ); 52 + 53 + JX.DOM.listen( 54 + root, 55 + ['click'], 56 + 'conpherence-pontificate', 57 + onsubmit 58 + ); 59 + 60 + });