mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-30 18:52:42 +01:00
e4b7263bf8
Summary: Fixes T7629 plus an un filed bug that's breaking creating new threads since we need to add participants EVEN EARLIER than we were doing it now that policy is actually enforced. Back to the main thrust of this, there is one UI corner case - in the main view if you go from 1:1 to 1:1:1 (i.e. add a 3rd recipient, or Nth in a row) the icon only updates on page reload. I figure this will get sorted out at a later refactor as we make the client better / share more code with durable column. One other small behavioral oddity is in the main view sometime we start loading with no conpherence. in that case, rather than show some incorrect icon, we show no icon (and "no title") and then things change at load. Seems okay-ish. Finally, @chad - the CSS is a very work-man-like "use the built in stuff you can specify from PHP" so I'm sure it needs some love. Test Plan: made all sorts of rooms and threads and liked the icons. noted smooth loading action as i switched around Reviewers: chad, epriestley Reviewed By: epriestley Subscribers: Korvin, chad, epriestley Maniphest Tasks: T7629 Differential Revision: https://secure.phabricator.com/D12163
353 lines
9.6 KiB
JavaScript
353 lines
9.6 KiB
JavaScript
/**
|
|
* @provides javelin-behavior-durable-column
|
|
* @requires javelin-behavior
|
|
* javelin-dom
|
|
* javelin-stratcom
|
|
* javelin-behavior-device
|
|
* javelin-scrollbar
|
|
* javelin-quicksand
|
|
* phabricator-keyboard-shortcut
|
|
* conpherence-thread-manager
|
|
*/
|
|
|
|
JX.behavior('durable-column', function(config, statics) {
|
|
// TODO: Currently, updating the column sends the entire column back. This
|
|
// includes the `durable-column` behavior itself, which tries to re-initialize
|
|
// the column. Detect this and bail.
|
|
//
|
|
// If ThreadManager gets separated into a UI part and a thread part (which
|
|
// seems likely), responses may no longer ship back the entire column. This
|
|
// might let us remove this check.
|
|
if (statics.initialized) {
|
|
return;
|
|
} else {
|
|
statics.initialized = true;
|
|
}
|
|
|
|
var show = false;
|
|
var loadThreadID = null;
|
|
var scrollbar = null;
|
|
|
|
var quick = JX.$('phabricator-standard-page-body');
|
|
|
|
function _getColumnNode() {
|
|
return JX.$('conpherence-durable-column');
|
|
}
|
|
|
|
function _getColumnScrollNode() {
|
|
var column = _getColumnNode();
|
|
return JX.DOM.find(column, 'div', 'conpherence-durable-column-main');
|
|
}
|
|
|
|
function _toggleColumn(explicit) {
|
|
if (explicit) {
|
|
var device = JX.Device.getDevice();
|
|
// don't allow users to invoke the column from devices
|
|
if (device != 'desktop') {
|
|
return;
|
|
}
|
|
}
|
|
show = !show;
|
|
JX.DOM.alterClass(document.body, 'with-durable-column', show);
|
|
var column = _getColumnNode();
|
|
if (show) {
|
|
JX.DOM.show(column);
|
|
threadManager.loadThreadByID(loadThreadID);
|
|
} else {
|
|
JX.DOM.hide(column);
|
|
}
|
|
JX.Stratcom.invoke('resize');
|
|
JX.Quicksand.setFrame(show ? quick : null);
|
|
|
|
// If this was an explicit toggle action from the user, save their
|
|
// preference.
|
|
if (explicit) {
|
|
new JX.Request(config.settingsURI)
|
|
.setData({value: (show ? 1 : 0)})
|
|
.send();
|
|
}
|
|
}
|
|
|
|
new JX.KeyboardShortcut('\\', 'Toggle Conpherence Column')
|
|
.setHandler(JX.bind(null, _toggleColumn, true))
|
|
.register();
|
|
|
|
scrollbar = new JX.Scrollbar(_getColumnScrollNode());
|
|
|
|
JX.Quicksand.start();
|
|
|
|
/* Conpherence Thread Manager configuration - lots of display
|
|
* callbacks.
|
|
*/
|
|
|
|
var threadManager = new JX.ConpherenceThreadManager();
|
|
threadManager.setMinimalDisplay(true);
|
|
threadManager.setLoadThreadURI('/conpherence/columnview/');
|
|
threadManager.setWillLoadThreadCallback(function() {
|
|
_markLoading(true);
|
|
});
|
|
threadManager.setDidLoadThreadCallback(function(r) {
|
|
var column = _getColumnNode();
|
|
var new_column = JX.$H(r.content);
|
|
JX.DOM.replace(column, new_column);
|
|
JX.DOM.show(_getColumnNode());
|
|
var messages = _getColumnMessagesNode();
|
|
scrollbar = new JX.Scrollbar(_getColumnScrollNode());
|
|
scrollbar.scrollTo(messages.scrollHeight);
|
|
_markLoading(false);
|
|
loadThreadID = threadManager.getLoadedThreadID();
|
|
});
|
|
threadManager.setDidUpdateThreadCallback(function(r) {
|
|
var messages = _getColumnMessagesNode();
|
|
JX.DOM.appendContent(messages, JX.$H(r.transactions));
|
|
scrollbar.scrollTo(messages.scrollHeight);
|
|
});
|
|
|
|
threadManager.setWillSendMessageCallback(function() {
|
|
// Wipe the textarea immediately so the user can start typing more text.
|
|
var textarea = _getColumnTextareaNode();
|
|
textarea.value = '';
|
|
_focusColumnTextareaNode();
|
|
});
|
|
|
|
threadManager.setDidSendMessageCallback(function(r) {
|
|
var messages = _getColumnMessagesNode();
|
|
JX.DOM.appendContent(messages, JX.$H(r.transactions));
|
|
scrollbar.scrollTo(messages.scrollHeight);
|
|
});
|
|
|
|
threadManager.setWillUpdateWorkflowCallback(function() {
|
|
JX.Stratcom.invoke('notification-panel-close');
|
|
});
|
|
threadManager.setDidUpdateWorkflowCallback(function(r) {
|
|
var messages = _getColumnMessagesNode();
|
|
JX.DOM.appendContent(messages, JX.$H(r.transactions));
|
|
scrollbar.scrollTo(messages.scrollHeight);
|
|
JX.DOM.setContent(_getColumnTitleNode(), r.conpherence_title);
|
|
});
|
|
threadManager.start();
|
|
|
|
JX.Stratcom.listen(
|
|
'click',
|
|
'conpherence-durable-column-header-action',
|
|
function (e) {
|
|
e.kill();
|
|
var data = e.getNodeData('conpherence-durable-column-header-action');
|
|
var action = data.action;
|
|
var link = e.getNode('tag:a');
|
|
var params = null;
|
|
|
|
switch (action) {
|
|
case 'metadata':
|
|
threadManager.runUpdateWorkflowFromLink(
|
|
link,
|
|
{
|
|
action: action,
|
|
force_ajax: true,
|
|
stage: 'submit'
|
|
});
|
|
break;
|
|
case 'add_person':
|
|
threadManager.runUpdateWorkflowFromLink(
|
|
link,
|
|
{
|
|
action: action,
|
|
stage: 'submit'
|
|
});
|
|
break;
|
|
case 'go_conpherence':
|
|
JX.$U(link.href).go();
|
|
break;
|
|
case 'hide_column':
|
|
JX.Stratcom.invoke('notification-panel-close');
|
|
_toggleColumn(true);
|
|
break;
|
|
}
|
|
});
|
|
|
|
JX.Stratcom.listen(
|
|
'click',
|
|
'conpherence-durable-column-thread-icon',
|
|
function (e) {
|
|
e.kill();
|
|
var icons = JX.DOM.scry(
|
|
JX.$('conpherence-durable-column'),
|
|
'a',
|
|
'conpherence-durable-column-thread-icon');
|
|
var data = e.getNodeData('conpherence-durable-column-thread-icon');
|
|
var cdata = null;
|
|
for (var i = 0; i < icons.length; i++) {
|
|
cdata = JX.Stratcom.getData(icons[i]);
|
|
JX.DOM.alterClass(
|
|
icons[i],
|
|
'selected',
|
|
cdata.threadID == data.threadID);
|
|
}
|
|
JX.DOM.setContent(_getColumnTitleNode(), JX.$H(data.threadTitle));
|
|
threadManager.loadThreadByID(data.threadID);
|
|
});
|
|
|
|
var resizeClose = false;
|
|
JX.Stratcom.listen(
|
|
'phabricator-device-change',
|
|
null,
|
|
function() {
|
|
var device = JX.Device.getDevice();
|
|
switch (device) {
|
|
case 'phone':
|
|
case 'tablet':
|
|
if (show === true) {
|
|
_toggleColumn(false);
|
|
resizeClose = true;
|
|
}
|
|
break;
|
|
case 'desktop':
|
|
if (resizeClose) {
|
|
resizeClose = false;
|
|
if (show === false) {
|
|
_toggleColumn(false);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
function _getColumnBodyNode() {
|
|
var column = JX.$('conpherence-durable-column');
|
|
return JX.DOM.find(
|
|
column,
|
|
'div',
|
|
'conpherence-durable-column-body');
|
|
}
|
|
|
|
function _getColumnMessagesNode() {
|
|
var column = JX.$('conpherence-durable-column');
|
|
return JX.DOM.find(
|
|
column,
|
|
'div',
|
|
'conpherence-durable-column-transactions');
|
|
}
|
|
|
|
function _getColumnTitleNode() {
|
|
var column = JX.$('conpherence-durable-column');
|
|
return JX.DOM.find(
|
|
column,
|
|
'div',
|
|
'conpherence-durable-column-header-text');
|
|
}
|
|
|
|
function _getColumnFormNode() {
|
|
var column = JX.$('conpherence-durable-column');
|
|
return JX.DOM.find(
|
|
column,
|
|
'form',
|
|
'conpherence-message-form');
|
|
}
|
|
|
|
function _getColumnTextareaNode() {
|
|
var column = JX.$('conpherence-durable-column');
|
|
return JX.DOM.find(
|
|
column,
|
|
'textarea',
|
|
'conpherence-durable-column-textarea');
|
|
}
|
|
|
|
function _focusColumnTextareaNode() {
|
|
var textarea = _getColumnTextareaNode();
|
|
setTimeout(function() { JX.DOM.focus(textarea); }, 1);
|
|
}
|
|
|
|
function _markLoading(loading) {
|
|
var column = _getColumnNode();
|
|
JX.DOM.alterClass(column, 'loading', loading);
|
|
}
|
|
|
|
function _sendMessage(e) {
|
|
e.kill();
|
|
var form = _getColumnFormNode();
|
|
threadManager.sendMessage(form, { minimal_display: true });
|
|
}
|
|
|
|
JX.Stratcom.listen(
|
|
'click',
|
|
'conpherence-send-message',
|
|
_sendMessage);
|
|
|
|
JX.Stratcom.listen(
|
|
['submit', 'didSyntheticSubmit'],
|
|
'conpherence-message-form',
|
|
_sendMessage);
|
|
|
|
// Send on enter if the shift key is not held.
|
|
JX.Stratcom.listen(
|
|
'keydown',
|
|
'conpherence-message-form',
|
|
function(e) {
|
|
if (e.getSpecialKey() != 'return') {
|
|
return;
|
|
}
|
|
|
|
var raw = e.getRawEvent();
|
|
if (raw.shiftKey) {
|
|
// If the shift key is pressed, let the browser write a newline into
|
|
// the textarea.
|
|
return;
|
|
}
|
|
|
|
// From here on, interpret this as a "send" action, not a literal
|
|
// newline.
|
|
e.kill();
|
|
|
|
var textarea = _getColumnTextareaNode();
|
|
if (!textarea.value.length) {
|
|
// If there's no text, don't try to submit the form.
|
|
return;
|
|
}
|
|
|
|
_sendMessage(e);
|
|
});
|
|
|
|
JX.Stratcom.listen(
|
|
['keydown'],
|
|
'conpherence-durable-column-textarea',
|
|
function (e) {
|
|
threadManager.handleDraftKeydown(e);
|
|
});
|
|
|
|
// HTML5 placeholders are rendered as long as the input is empty, even if the
|
|
// input is currently focused. This is undesirable for the chat input,
|
|
// especially immediately after sending a message. Hide the placeholder while
|
|
// the input is focused.
|
|
JX.Stratcom.listen(
|
|
['focus', 'blur'],
|
|
'conpherence-durable-column-textarea',
|
|
function (e) {
|
|
var node = e.getTarget();
|
|
if (e.getType() == 'focus') {
|
|
if (node.placeholder) {
|
|
node.placeholderStorage = node.placeholder;
|
|
node.placeholder = '';
|
|
}
|
|
} else {
|
|
if (node.placeholderStorage) {
|
|
node.placeholder = node.placeholderStorage;
|
|
node.placeholderStorage = '';
|
|
}
|
|
}
|
|
});
|
|
|
|
if (config.visible) {
|
|
var device = JX.Device.getDevice();
|
|
if (device == 'desktop') {
|
|
_toggleColumn(false);
|
|
} else {
|
|
// pretend we closed due to resize so if we do resize later things work
|
|
// correctly
|
|
resizeClose = true;
|
|
JX.DOM.hide(_getColumnNode());
|
|
}
|
|
}
|
|
|
|
});
|