1
0
mirror of https://github.com/moparisthebest/kaiwa synced 2024-11-30 05:02:16 -05:00
kaiwa/clientapp/models/contact.js

400 lines
14 KiB
JavaScript
Raw Normal View History

/*global app, me, client, URL*/
"use strict";
2013-08-29 23:38:28 -04:00
var _ = require('underscore');
2013-10-15 03:02:45 -04:00
var crypto = require('crypto');
2013-08-29 23:38:28 -04:00
var async = require('async');
var uuid = require('node-uuid');
var HumanModel = require('human-model');
2013-08-29 23:38:28 -04:00
var Resources = require('./resources');
var Messages = require('./messages');
var Message = require('./message');
var logger = require('andlog');
2015-03-20 15:36:40 -04:00
var avatarHandler = require('../helpers/avatarHandler');
2013-08-29 23:38:28 -04:00
2013-09-05 19:53:23 -04:00
module.exports = HumanModel.define({
2013-08-29 23:38:28 -04:00
initialize: function (attrs) {
if (attrs.jid) {
this.id = attrs.jid;
2013-08-29 23:38:28 -04:00
}
this.setAvatar(attrs.avatarID);
this.resources.bind('add remove reset', this.onResourceListChange, this);
this.resources.bind('change', this.onResourceChange, this);
this.bind('change:topResource change:lockedResource change:_forceUpdate', this.summarizeResources, this);
2015-02-15 12:06:39 -05:00
2015-02-22 17:43:49 -05:00
this.fetchHistory(true);
var self = this;
client.on('session:started', function () {
if (self.messages.length)
self.fetchHistory(true, true);
});
2013-08-29 23:38:28 -04:00
},
type: 'contact',
props: {
id: ['string', true, false],
avatarID: ['string', false, ''],
groups: ['array', false, []],
inRoster: ['bool', true, false],
2013-08-29 23:38:28 -04:00
jid: ['string', true],
name: ['string', false, ''],
owner: ['string', true, ''],
storageId: ['string', true, ''],
subscription: ['string', false, 'none']
2013-08-29 23:38:28 -04:00
},
session: {
activeContact: ['bool', false, false],
avatar: 'string',
avatarSource: 'string',
lastInteraction: 'date',
2014-01-07 12:08:21 -05:00
lastHistoryFetch: 'date',
lastSentMessage: 'object',
2013-09-13 16:55:46 -04:00
lockedResource: 'string',
offlineStatus: ['string', false, ''],
2013-09-13 16:55:46 -04:00
topResource: 'string',
unreadCount: ['number', false, 0],
_forceUpdate: ['number', false, 0],
onCall: ['boolean', false, false],
persistent: ['bool', false, false],
2013-10-15 03:02:45 -04:00
stream: 'object'
},
2013-08-29 23:38:28 -04:00
derived: {
streamUrl: {
deps: ['stream'],
cache: true,
fn: function () {
if (!this.stream) return '';
return URL.createObjectURL(this.stream);
}
},
2013-08-29 23:38:28 -04:00
displayName: {
deps: ['name', 'jid'],
fn: function () {
return this.name || this.jid;
2013-08-29 23:38:28 -04:00
}
},
displayUnreadCount: {
deps: ['unreadCount'],
fn: function () {
if (this.unreadCount > 0) {
return this.unreadCount.toString();
}
return '';
}
},
2013-09-03 18:25:14 -04:00
formattedTZO: {
2013-12-20 02:49:23 -05:00
deps: ['timezoneOffset'],
2013-08-29 23:38:28 -04:00
fn: function () {
2013-12-20 02:49:23 -05:00
if (!this.timezoneOffset) return '';
var localTime = new Date();
var localTZO = localTime.getTimezoneOffset();
var diff = Math.abs(localTZO % (24 * 60) - this.timezoneOffset % (24 * 60));
var remoteTime = new Date(Date.now() + diff * 60000);
var day = remoteTime.getDate();
var hour = remoteTime.getHours();
var minutes = remoteTime.getMinutes();
var days = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
var dow = days[remoteTime.getDay()];
var localDow = days[localTime.getDay()];
var m = (hour >= 12) ? ' PM' : ' AM';
hour = hour % 12;
if (hour === 0) {
hour = 12;
}
var strDay = (day < 10) ? '0' + day : day;
var strHour = (hour < 10) ? '0' + hour : hour;
var strMin = (minutes < 10) ? '0' + minutes: minutes;
if (localDow == dow) {
return strHour + ':' + strMin + m;
2013-09-03 18:25:14 -04:00
} else {
2013-12-20 02:49:23 -05:00
return dow + ' ' + strHour + ':' + strMin + m;
2013-08-29 23:38:28 -04:00
}
}
},
status: {
deps: ['topResource', 'lockedResource', '_forceUpdate'],
fn: function () {
var resource = this.resources.get(this.lockedResource) || this.resources.get(this.topResource) || {};
return resource.status || '';
}
},
show: {
deps: ['topResource', 'lockedResource', '_forceUpdate'],
fn: function () {
if (this.resources.length === 0) {
return 'offline';
}
var resource = this.resources.get(this.lockedResource) || this.resources.get(this.topResource) || {};
return resource.show || 'online';
}
},
timezoneOffset: {
deps: ['topResource', 'lockedResource', '_forceUpdate'],
fn: function () {
var resource = this.resources.get(this.lockedResource) || this.resources.get(this.topResource) || {};
return resource.timezoneOffset || undefined;
}
},
idleSince: {
deps: ['topResource', 'lockedResource', '_forceUpdate'],
fn: function () {
var resource = this.resources.get(this.lockedResource) || this.resources.get(this.topResource) || {};
return resource.idleSince || undefined;
}
},
2013-09-27 00:29:45 -04:00
idle: {
deps: ['idleSince'],
fn: function () {
return this.idleSince && !isNaN(this.idleSince.valueOf());
2013-09-27 00:29:45 -04:00
}
},
chatState: {
deps: ['topResource', 'lockedResource', '_forceUpdate'],
fn: function () {
var states = {};
this.resources.models.forEach(function (resource) {
states[resource.chatState] = true;
});
if (states.composing) return 'composing';
if (states.paused) return 'paused';
if (states.active) return 'active';
if (states.inactive) return 'inactive';
return 'gone';
}
},
chatStateText: {
deps: ['topResource', 'lockedResource', '_forceUpdate'],
fn: function () {
var chatState = this.chatState;
if (chatState == 'composing')
return this.displayName + ' is composing';
else if (chatState == 'paused')
2015-04-07 04:36:01 -04:00
return this.displayName + ' stopped writing';
else if (chatState == 'gone')
return this.displayName + ' is gone';
return '';
}
},
supportsReceipts: {
deps: ['lockedResource', '_forceUpdate'],
fn: function () {
if (!this.lockedResource) return false;
var res = this.resources.get(this.lockedResource);
return res.supportsReceipts;
}
},
supportsChatStates: {
deps: ['lockedResource', '_forceUpdate'],
fn: function () {
if (!this.lockedResource) return false;
var res = this.resources.get(this.lockedResource);
return res && res.supportsChatStates;
}
},
hasUnread: {
deps: ['unreadCount'],
fn: function () {
return this.unreadCount > 0;
}
2013-10-14 17:11:32 -04:00
},
jingleResources: {
2013-10-15 03:02:45 -04:00
deps: ['_forceUpdate'],
2013-10-14 17:11:32 -04:00
fn: function () {
return this.resources.filter(function (res) {
return res.supportsJingleMedia;
});
}
},
callable: {
deps: ['jingleResources'],
fn: function () {
return !!this.jingleResources.length;
}
},
callObject: {
fn: function () {
return app.calls.where('contact', this);
}
2013-08-29 23:38:28 -04:00
}
},
collections: {
resources: Resources,
messages: Messages
},
2013-10-15 03:02:45 -04:00
call: function () {
if (this.jingleResources.length) {
2013-10-15 03:02:45 -04:00
var peer = this.jingleResources[0];
this.callState = 'starting';
app.api.call(peer.id);
} else {
logger.error('no jingle resources for this user');
2013-10-15 03:02:45 -04:00
}
},
setAvatar: function (id, type, source) {
2015-03-20 15:36:40 -04:00
if (!this.avatar) this.avatar = avatarHandler.getGravatar(this.jid).uri;
2013-08-29 23:38:28 -04:00
var self = this;
2015-03-20 15:36:40 -04:00
avatarHandler.fetch(this.jid, id, type, source, function (avatar) {
if (source == 'vcard' && self.avatarSource == 'pubsub') return;
self.avatarID = avatar.id;
self.avatar = avatar.uri;
2014-04-21 12:29:02 -04:00
self.avatarSource = source;
self.save();
2013-08-29 23:38:28 -04:00
});
},
onResourceChange: function () {
this.resources.sort();
this.topResource = (this.resources.first() || {}).id;
this._forceUpdate++;
},
onResourceListChange: function () {
2013-08-29 23:38:28 -04:00
// Manually propagate change events for properties that
// depend on the resources collection.
this.resources.sort();
var res = this.resources.first();
if (res) {
this.offlineStatus = '';
2013-09-13 16:55:46 -04:00
this.topResource = res.id;
2013-08-29 23:38:28 -04:00
} else {
this.topResource = undefined;
2013-08-29 23:38:28 -04:00
}
this.lockedResource = undefined;
},
addMessage: function (message, notify) {
message.owner = me.jid.bare;
2013-09-27 14:54:51 -04:00
if (notify && (!this.activeContact || (this.activeContact && !app.state.focused)) && message.from.bare === this.jid) {
this.unreadCount++;
2013-10-11 18:40:42 -04:00
app.notifications.create(this.displayName, {
body: message.body,
icon: this.avatar,
2013-10-11 18:40:42 -04:00
tag: this.jid,
2014-12-10 05:25:19 -05:00
onclick: _.bind(app.navigate, app, '/chat/' + encodeURIComponent(this.jid))
});
2015-01-25 14:21:57 -05:00
if (me.soundEnabled)
app.soundManager.play('ding');
}
2013-12-18 16:31:22 -05:00
var existing = Message.idLookup(message.from[message.type == 'groupchat' ? 'full' : 'bare'], message.mid);
if (existing) {
existing.set(message);
existing.save();
} else {
this.messages.add(message);
message.save();
}
var newInteraction = new Date(message.created);
if (!this.lastInteraction || this.lastInteraction < newInteraction) {
this.lastInteraction = newInteraction;
}
2013-08-29 23:38:28 -04:00
},
2015-02-22 17:43:49 -05:00
fetchHistory: function (onlyLastMessages, allInterval) {
2013-08-29 23:38:28 -04:00
var self = this;
app.whenConnected(function () {
var filter = {
2014-01-07 12:08:21 -05:00
'with': self.jid,
rsm: {
2015-02-22 17:43:49 -05:00
max: !!onlyLastMessages && !allInterval ? 50 : 40
2014-01-07 12:08:21 -05:00
}
};
2015-02-22 17:43:49 -05:00
if (!!onlyLastMessages) {
var lastMessage = self.messages.last();
if (lastMessage && lastMessage.archivedId) {
filter.rsm.after = lastMessage.archivedId;
}
if (!allInterval) {
filter.rsm.before = true;
if (self.lastHistoryFetch && !isNaN(self.lastHistoryFetch.valueOf())) {
if (self.lastInteraction > self.lastHistoryFetch) {
filter.start = self.lastInteraction;
} else {
filter.start = self.lastHistoryFetch;
}
} else {
filter.end = new Date(Date.now() + app.timeInterval);
}
}
2014-01-07 12:08:21 -05:00
} else {
2015-02-22 17:43:49 -05:00
var firstMessage = self.messages.first();
if (firstMessage && firstMessage.archivedId) {
filter.rsm.before = firstMessage.archivedId;
}
}
2014-01-07 12:08:21 -05:00
client.getHistory(filter, function (err, res) {
2013-08-29 23:38:28 -04:00
if (err) return;
2015-02-15 12:06:39 -05:00
self.lastHistoryFetch = new Date(Date.now() + app.timeInterval);
2014-01-07 12:08:21 -05:00
2013-08-29 23:38:28 -04:00
var results = res.mamQuery.results || [];
2015-02-22 17:43:49 -05:00
if (!!onlyLastMessages && !allInterval) results.reverse();
2013-08-29 23:38:28 -04:00
results.forEach(function (result) {
var msg = result.mam.forwarded.message;
2013-12-18 16:31:22 -05:00
msg.mid = msg.id;
delete msg.id;
2013-08-29 23:38:28 -04:00
if (!msg.delay) {
msg.delay = result.mam.forwarded.delay;
}
if (msg.replace) {
2013-12-18 16:31:22 -05:00
var original = Message.idLookup(msg.from[msg.type == 'groupchat' ? 'full' : 'bare'], msg.replace);
// Drop the message if editing a previous, but
// keep it if it didn't actually change an
// existing message.
if (original && original.correct(msg)) return;
2013-08-29 23:38:28 -04:00
}
var message = new Message(msg);
message.archivedId = result.mam.id;
message.acked = true;
self.addMessage(message, false);
2013-08-29 23:38:28 -04:00
});
2015-02-22 17:43:49 -05:00
if (allInterval) {
if (results.length == 40) {
self.fetchHistory(true, true);
} else {
self.trigger('refresh');
}
}
2013-08-29 23:38:28 -04:00
});
});
},
save: function () {
if (!this.inRoster) return;
2013-10-15 03:02:45 -04:00
var storageId = crypto.createHash('sha1').update(this.owner + '/' + this.id).digest('hex');
2013-08-29 23:38:28 -04:00
var data = {
2013-10-15 03:02:45 -04:00
storageId: storageId,
2013-09-11 17:58:39 -04:00
owner: this.owner,
2013-08-29 23:38:28 -04:00
jid: this.jid,
name: this.name,
groups: this.groups,
subscription: this.subscription,
avatarID: this.avatarID
};
app.storage.roster.add(data);
}
});