mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-25 06:50:55 +01:00
b14ca38940
Summary: Fixes T7761. Fixes T7318. When we send an empty message to the server, pretend its just a request to load the page. Make load a bit smarter such that if we don't get back any transactions, rather than error like the fool, just send down to the client the notion of a 'non_update'. Instrument the client to just turn off the appropriate loading state, etc for a non update. T7318 is a tricky beast since we don't know exactly how to reproduce it but if / when it occurs again it would be some other bizarre application behavior maybe? We won't be getting the execption anymore, that's for sure. Test Plan: removed code in `ConpherenceThreadManager.sendMessage` that protects against sending empty messages. sent empty messages (non updates) like whoa and everything worked on both durable column and main column view. re-added the code in `ConpherenceThreadManager.sendMessage` and noted empty messages did not send while any text including a space sent up nicely Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T7318, T7761 Differential Revision: https://secure.phabricator.com/D12339
345 lines
9.5 KiB
JavaScript
345 lines
9.5 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 userVisible = config.visible;
|
|
var show = null;
|
|
var loadThreadID = null;
|
|
var scrollbar = null;
|
|
|
|
var columnWidth = 300;
|
|
// This is the smallest window size where we'll enable the column.
|
|
var minimumViewportWidth = 768;
|
|
|
|
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 _isViewportWideEnoughForColumn() {
|
|
var viewport = JX.Vector.getViewport();
|
|
if (viewport.x < minimumViewportWidth) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function _updateColumnVisibility() {
|
|
var new_value = (userVisible && _isViewportWideEnoughForColumn());
|
|
if (new_value !== show) {
|
|
show = new_value;
|
|
_drawColumn(show);
|
|
}
|
|
}
|
|
|
|
function _toggleColumn(explicit) {
|
|
userVisible = !userVisible;
|
|
_updateColumnVisibility();
|
|
|
|
new JX.Request(config.settingsURI)
|
|
.setData({value: (show ? 1 : 0)})
|
|
.send();
|
|
}
|
|
|
|
function _drawColumn(visible) {
|
|
JX.DOM.alterClass(document.body, 'with-durable-column', visible);
|
|
var column = _getColumnNode();
|
|
if (visible) {
|
|
JX.DOM.show(column);
|
|
threadManager.loadThreadByID(loadThreadID);
|
|
} else {
|
|
JX.DOM.hide(column);
|
|
}
|
|
JX.Quicksand.setFrame(visible ? quick : null);
|
|
|
|
// When we activate the column, adjust the tablet breakpoint so that we
|
|
// convert the left side of the screen to tablet mode on narrow displays.
|
|
var breakpoint;
|
|
if (visible) {
|
|
breakpoint = minimumViewportWidth + columnWidth;
|
|
} else {
|
|
breakpoint = minimumViewportWidth;
|
|
}
|
|
JX.Device.setTabletBreakpoint(breakpoint);
|
|
|
|
JX.Stratcom.invoke('resize');
|
|
}
|
|
|
|
new JX.KeyboardShortcut('\\', 'Toggle Conpherence Column')
|
|
.setHandler(_toggleColumn)
|
|
.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);
|
|
if (show) {
|
|
JX.DOM.show(_getColumnNode());
|
|
} else {
|
|
JX.DOM.hide(_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, non_update) {
|
|
if (non_update) {
|
|
return;
|
|
}
|
|
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();
|
|
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);
|
|
});
|
|
|
|
JX.Stratcom.listen('resize', null, _updateColumnVisibility);
|
|
|
|
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();
|
|
|
|
_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 = '';
|
|
}
|
|
}
|
|
});
|
|
|
|
_updateColumnVisibility();
|
|
|
|
});
|