jam-cloud/web/app/assets/javascripts/textMessageDialog.js

328 lines
9.9 KiB
JavaScript

(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.TextMessageDialog = function(app) {
var logger = context.JK.logger;
var rest = context.JK.Rest();
var $dialog = null;
var $previousMessages = null;
var $previousMessagesScroller = null;
var $sendTextMessage = null;
var $form = null;
var $interactionBlocker = null;
var $disconnectedMsg = null;
var $textBox = null;
var userLookup = null;
var otherId = null;
var offset = 0;
var sendingMessage = false;
var LIMIT = 20;
var showing = false;
var fullyInitialized = false;
var user = null;
var renderQueue = []; // to handle race condition between dialog showing and populated using REST, and messages coming in
var remainingCap = 400;
function reset() {
fullyInitialized = false;
renderQueue = [];
sendingMessage = false;
offset = 0;
userLookup = {};
$previousMessages.empty();
$textBox.val('');
$sendTextMessage.unbind('click');
}
function buildParams() {
return { type: 'TEXT_MESSAGE', receiver: otherId, offset: offset, limit: LIMIT};
}
function buildMessage() {
var message = {};
message['message'] = $textBox.val();
message['receiver'] = otherId;
return message;
}
function sendMessage() {
if(!context.JK.JamServer.connected) {
return false;
}
var msg = $textBox.val();
if(!msg || msg == '') {
// don't bother the server with empty messages
return false;
}
if(!sendingMessage) {
sendingMessage = true;
$sendTextMessage.text('SENDING...')
rest.createTextMessage(buildMessage())
.done(function() {
$textBox.val('');
renderMessage(msg, user.id, user.name, new Date().toISOString(), true);
})
.fail(function(jqXHR) {
app.notifyServerError(jqXHR, 'Unable to Send Message');
})
.always(function() {
sendingMessage = false;
$sendTextMessage.text('SEND');
})
}
return false;
}
function scrollToBottom(instant) {
$previousMessagesScroller.animate({scrollTop: $previousMessagesScroller[0].scrollHeight}, instant ? 0 : 'slow');
}
function renderMessage(msg, senderId, senderName, sent, append) {
var options = {
msg: msg,
sender: senderId == user.id ? 'me' : senderName,
sent: sent
};
var txt = $(context._.template($('#template-previous-message').html(), options, { variable: 'data' }));
txt.find('.timeago').timeago();
if(append) {
$previousMessages.append(txt);
scrollToBottom();
}
else {
$previousMessages.prepend(txt);
}
}
function drainQueue() {
context._.each(renderQueue, function(msg) {
renderMessage(msg.msg, msg.senderId, msg.senderName, msg.sent, true);
});
renderQueue = [];
}
function formatTextMessage(msg, sender_id, sender_name, clipped_msg) {
var markedUpMsg = $('<span><span class="sender-name"></span> says: <span class="text-message"></span><a href="#" class="more-text-available"></a></span>');
markedUpMsg.find('.sender-name').text(sender_name)
markedUpMsg.find('.text-message').text(clipped_msg ? msg + "... " : msg).attr('data-is-clipped', clipped_msg)
var moreTextLink = markedUpMsg.find('.more-text-available').attr('data-sender-id', sender_id);
if(clipped_msg) {
moreTextLink.text('more').show();
moreTextLink.click(function(e) {
app.layout.showDialog('text-message', {d1: $(this).attr('data-sender-id')});
return false;
});
}
else {
moreTextLink.hide();
}
return markedUpMsg;
}
// we handled the notification, meaning the dialog showed this message as a chat message
function handledNotification(payload) {
return showing && payload.description == "TEXT_MESSAGE" && payload.sender_id == otherId;
}
function isNoisyNotification(payload) {
return handledNotification(payload);
}
function afterShow(args) {
$textBox.focus();
}
function renderDialog() {
app.user()
.done(function (userDetail) {
user = userDetail;
showing = true;
userLookup[user.id] = user;
rest.getUserDetail({id: otherId})
.done(function (otherUser) {
userLookup[otherUser.id] = otherUser;
$dialog.find('.receiver-name').text(otherUser.name);
$dialog.find('textarea').attr('placeholder', 'enter a message to ' + otherUser.name + '...');
$dialog.find('.offline-tip').text('An email will be sent if ' + otherUser.name + ' is offline');
if (!context.JK.JamServer.connected) {
renderNotConnected();
}
$sendTextMessage.click(sendMessage);
rest.getNotifications(buildParams())
.done(function (response) {
context._.each(response, function (textMessage) {
renderMessage(textMessage.message, textMessage.source_user_id, userLookup[textMessage.source_user_id].name, textMessage.created_at);
})
scrollToBottom(true);
fullyInitialized = true;
drainQueue();
})
.fail(function (jqXHR) {
app.notifyServerError(jqXHR, 'Unable to Load Conversation')
})
})
.fail(function (jqXHR) {
app.notifyServerError(jqXHR, 'Unable to Load Other User')
})
})
}
function beforeShow(args) {
app.layout.closeDialog('text-message') // ensure no others are showing. this is a singleton dialog
var other = args.d1;
if (!other) throw "other must be specified in TextMessageDialog"
otherId = other;
renderDialog();
}
function afterHide() {
showing = false;
reset();
}
function renderNotConnected() {
$interactionBlocker.addClass('active');
$disconnectedMsg.addClass('active');
}
function renderConnected() {
$interactionBlocker.removeClass('active');
$disconnectedMsg.removeClass('active');
}
function beforeDisconnect() {
renderNotConnected();
}
function afterConnect() {
renderConnected();
reset();
renderDialog();
}
function pasteIntoInput(el, text) {
el.focus();
if (typeof el.selectionStart == "number"
&& typeof el.selectionEnd == "number") {
var val = el.value;
var selStart = el.selectionStart;
el.value = val.slice(0, selStart) + text + val.slice(el.selectionEnd);
el.selectionEnd = el.selectionStart = selStart + text.length;
} else if (typeof document.selection != "undefined") {
var textRange = document.selection.createRange();
textRange.text = text;
textRange.collapse(false);
textRange.select();
}
}
function handleEnter(evt) {
if (evt.keyCode == 13 && evt.shiftKey) {
pasteIntoInput(this, "\n");
evt.preventDefault();
}
else if(evt.keyCode == 13 && !evt.shiftKey){
sendMessage();
return false;
}
}
function events() {
$form.submit(sendMessage)
// http://stackoverflow.com/questions/6014702/how-do-i-detect-shiftenter-and-generate-a-new-line-in-textarea
$textBox.keydown(handleEnter);
}
function respondTextInvitation(args) {
app.layout.showDialog('text-message', {d1: args.sender_id}) ;
}
// called from sidebar when messages come in
function messageReceived(payload) {
if(showing && otherId == payload.sender_id) {
if(fullyInitialized) {
renderMessage(payload.msg, payload.sender_id, payload.sender_name, payload.created_at, true);
}
else {
// the dialog caught a message as it was initializing... queue it for later once dialog is showing
renderQueue.push({msg: payload.msg, senderId: payload.sender_id, senderName: payload.sender_name, sent: msg.created_at});
}
}
else {
payload.msg = formatTextMessage(payload.msg, payload.sender_id, payload.sender_name, payload.clipped_msg);
app.notify({
"title": "Message from " + payload.sender_name,
"text": payload.msg,
"icon_url": context.JK.resolveAvatarUrl(payload.photo_url)
}, [{
id: "btn-reply",
text: "REPLY",
"layout-action": "close",
href: "#",
css: "button-orange",
callback: respondTextInvitation,
callback_args: {
"sender_id": payload.sender_id,
"notification_id": payload.notification_id
}
}]
);
}
}
function initialize() {
var dialogBindings = {
'beforeShow' : beforeShow,
'afterShow' : afterShow,
'afterHide': afterHide,
'beforeDisconnect': beforeDisconnect,
'afterConnect': afterConnect
};
app.bindDialog('text-message', dialogBindings);
$dialog = $('#text-message-dialog');
$previousMessagesScroller = $dialog.find('.previous-messages-scroller');
$previousMessages = $dialog.find('.previous-messages');
$sendTextMessage = $dialog.find('.btn-send-text-message');
$form = $dialog.find('form');
$textBox = $form.find('textarea');
$interactionBlocker = $dialog.find('.interaction-blocker');
$disconnectedMsg = $dialog.find('.disconnected-msg');
events();
}
this.initialize = initialize;
this.messageReceived = messageReceived;
this.formatTextMessage = formatTextMessage;
this.handledNotification = handledNotification;
}
return this;
})(window,jQuery);