Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
99
gaia/apps/sms/services/js/conversation/conversation_client.js
Executable file
99
gaia/apps/sms/services/js/conversation/conversation_client.js
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
/* global bridge,
|
||||
streamClient,
|
||||
Thread
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
const debug = 0 ?
|
||||
(arg1, ...args) => console.log(`[ConversationClient] ${arg1}`, ...args):
|
||||
() => {};
|
||||
|
||||
const mark = 0 ?
|
||||
(...args) => exports.performance.mark(`[ConversationClient] ${args}`):
|
||||
() => {};
|
||||
|
||||
const priv = {
|
||||
client: Symbol('client'),
|
||||
appInstanceId: Symbol('appInstanceId')
|
||||
};
|
||||
|
||||
/**
|
||||
* Name of the service that is responsible for managing conversation.
|
||||
* @type {string}
|
||||
*/
|
||||
const SERVICE_NAME = 'conversation-service';
|
||||
|
||||
/**
|
||||
* ConversationClient is a wrapper around bridge client connected to the
|
||||
* bridge service hosted in a Shared Worker.
|
||||
* @type {Object}
|
||||
*/
|
||||
var ConversationClient = {
|
||||
/**
|
||||
* Reference to active bridge client instance.
|
||||
* @type {Client}
|
||||
*/
|
||||
[priv.client]: null,
|
||||
|
||||
/**
|
||||
* Unique identifier of app instance where client resides in.
|
||||
* @type {string}
|
||||
*/
|
||||
[priv.appInstanceId]: null,
|
||||
|
||||
/**
|
||||
* Initialized conversation service client bridge.
|
||||
* @param {string} appInstanceId Unique identifier of app instance where
|
||||
* client resides in.
|
||||
*/
|
||||
init(appInstanceId) {
|
||||
if (!appInstanceId) {
|
||||
throw new Error('AppInstanceId is required!');
|
||||
}
|
||||
|
||||
this[priv.client] = bridge.client({
|
||||
service: SERVICE_NAME,
|
||||
endpoint: new SharedWorker(
|
||||
'/services/js/conversation/conversation_service.js'
|
||||
),
|
||||
timeout: false
|
||||
}).plugin(streamClient);
|
||||
|
||||
this[priv.appInstanceId] = appInstanceId;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves all conversations and drafts.
|
||||
* @param {Function} onConversationRetrieved Callback that is called for
|
||||
* every retrieved conversation.
|
||||
* @returns {Promise} Promise is resolved once all conversations are
|
||||
* retrieved and rejected if conversation stream is closed unexpectedly.
|
||||
*/
|
||||
getAllConversations(onConversationRetrieved) {
|
||||
mark('start retrieving conversations');
|
||||
|
||||
var getAllConversationsStream = this[priv.client].stream(
|
||||
'getAllConversations', this[priv.appInstanceId]
|
||||
);
|
||||
|
||||
var index = 0;
|
||||
getAllConversationsStream.listen((thread) => {
|
||||
mark(++index, ' conversation retrieved');
|
||||
debug('Conversation with id %s is retrieved from stream', thread.id);
|
||||
|
||||
onConversationRetrieved(new Thread(thread));
|
||||
});
|
||||
|
||||
return getAllConversationsStream.closed.then(() => {
|
||||
mark('all conversations retrieved');
|
||||
}).catch((e) => {
|
||||
console.error('Conversation stream is closed unexpectedly', e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.ConversationClient = Object.seal(ConversationClient);
|
||||
})(window);
|
||||
234
gaia/apps/sms/services/js/conversation/conversation_service.js
Executable file
234
gaia/apps/sms/services/js/conversation/conversation_service.js
Executable file
|
|
@ -0,0 +1,234 @@
|
|||
/*global BridgeServiceMixin,
|
||||
Draft,
|
||||
Drafts,
|
||||
MozMobileMessageClient
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Type description for the ConversationSummary object that is return from the
|
||||
* Conversation service.
|
||||
* @typedef {Object} ConversationSummary
|
||||
* @property {number} id Unique identifier of the the conversation.
|
||||
* @property {string} body Short content of the last message in the
|
||||
* conversation.
|
||||
* @property {Array.<string>} participants List of the conversation
|
||||
* participants.
|
||||
* @property {Date} timestamp Conversation timestamp.
|
||||
* @property {{ hasUnread: boolean, hasNewError: boolean }} status Conversation
|
||||
* status that indicates whether conversation has unread messages or/and failed
|
||||
* messages that user hasn't seen yet.
|
||||
* @property {string} lastMessageType Type of the last message in the
|
||||
* conversation. Can be either 'sms' or 'mms'.
|
||||
* @property {Draft} draft If conversation is draft or has draft, then this
|
||||
* field contains appropriate draft information, otherwise it's null.
|
||||
*/
|
||||
|
||||
[
|
||||
['BridgeServiceMixin', '/services/js/bridge_service_mixin.js'],
|
||||
[
|
||||
'MozMobileMessageClient',
|
||||
'/services/js/moz_mobile_message/moz_mobile_message_client.js'
|
||||
],
|
||||
['Drafts', '/services/js/drafts.js']
|
||||
].forEach(([dependencyName, dependencyPath]) => {
|
||||
if (!(dependencyName in self)) {
|
||||
importScripts(dependencyPath);
|
||||
}
|
||||
});
|
||||
|
||||
(function(exports) {
|
||||
const debug = 0 ?
|
||||
(arg1, ...args) => console.log(`[ConversationService] ${arg1}`, ...args):
|
||||
() => {};
|
||||
|
||||
// We use @-directive to override marker context that is scoped by
|
||||
// SharedWorker lifetime by default, but we need it to be aligned with the
|
||||
// main app context.
|
||||
const mark = 0 ?
|
||||
(...args) => exports.performance.mark(
|
||||
`[ConversationService] ${args}@sms.gaiamobile.org`
|
||||
):
|
||||
() => {};
|
||||
|
||||
const SERVICE_CONTRACT = Object.freeze({
|
||||
name: 'conversation-service',
|
||||
|
||||
streams: Object.freeze(
|
||||
['getAllConversations', 'getMessagesForConversation']
|
||||
),
|
||||
|
||||
methods: Object.freeze([
|
||||
'deleteConversations', 'deleteMessages', 'markConversationsAs',
|
||||
'getConversationSummary', 'getMessage', 'findConversationFromAddress'
|
||||
]),
|
||||
|
||||
events: Object.freeze(['message-change'])
|
||||
});
|
||||
|
||||
function draftToConversationSummary(draft) {
|
||||
var body = Array.isArray(draft.content) ?
|
||||
draft.content.find((content) => typeof content === 'string') : '';
|
||||
|
||||
return {
|
||||
id: +draft.id,
|
||||
participants: Array.isArray(draft.recipients) ? draft.recipients : [],
|
||||
body: body,
|
||||
timestamp: new Date(draft.timestamp),
|
||||
status: { hasUnread: false, hasNewError: false },
|
||||
lastMessageType: draft.type || 'sms',
|
||||
draft: new Draft(draft)
|
||||
};
|
||||
}
|
||||
|
||||
function threadToConversationSummary(thread) {
|
||||
return {
|
||||
id: thread.id,
|
||||
participants: thread.participants,
|
||||
body: thread.body,
|
||||
timestamp: thread.timestamp,
|
||||
status: { hasUnread: thread.unreadCount > 0, hasNewError: false },
|
||||
lastMessageType: thread.lastMessageType,
|
||||
draft: Drafts.byThreadId(thread.id)
|
||||
};
|
||||
}
|
||||
|
||||
var ConversationService = {
|
||||
/**
|
||||
* Initializes our service using the Service mixin's initService.
|
||||
*/
|
||||
init() {
|
||||
this.initService();
|
||||
},
|
||||
|
||||
/**
|
||||
* Stream that returns everything needed to display conversations in the
|
||||
* inbox view.
|
||||
* @param {ServiceStream.<ConversationSummary>} serviceStream The stream to
|
||||
* use in the implementation: it will be passed automatically by the Bridge
|
||||
* library.
|
||||
* @param {appInstanceId} appInstanceId Unique id of the app instance
|
||||
* requesting service.
|
||||
*/
|
||||
getAllConversations(serviceStream, appInstanceId) {
|
||||
mark('start retrieving conversations');
|
||||
|
||||
var getThreadsStream = MozMobileMessageClient.forApp(
|
||||
appInstanceId
|
||||
).getThreads();
|
||||
|
||||
// If conversation stream is cancelled we should cancel internal threads
|
||||
// stream as well.
|
||||
serviceStream.cancel = (reason) => {
|
||||
debug('getAllConversations stream is cancelled: %s', reason);
|
||||
return getThreadsStream.cancel(reason);
|
||||
};
|
||||
|
||||
var draftsPromise = Drafts.request().then(() => {
|
||||
// Return draft list that is sorted by timestamp in inverse order.
|
||||
return Drafts.getAllThreadless().sort(
|
||||
(draftA, draftB) => draftB.timestamp - draftA.timestamp
|
||||
);
|
||||
});
|
||||
|
||||
// Assume that we get the data in an inverse sort order.
|
||||
var index = 0;
|
||||
getThreadsStream.listen((thread) => {
|
||||
mark(++index, ' conversation retrieved');
|
||||
debug('Retrieved conversation with id %s', thread.id);
|
||||
|
||||
draftsPromise.then((drafts) => {
|
||||
// Flush drafts that are created earlier than current thread.
|
||||
while(drafts.length && drafts[0].timestamp >= thread.timestamp) {
|
||||
serviceStream.write(draftToConversationSummary(drafts.shift()));
|
||||
}
|
||||
|
||||
serviceStream.write(threadToConversationSummary(thread));
|
||||
});
|
||||
});
|
||||
|
||||
return getThreadsStream.closed.then(() => draftsPromise).then(
|
||||
(drafts) => {
|
||||
for(var draft of drafts) {
|
||||
serviceStream.write(draftToConversationSummary(draft));
|
||||
}
|
||||
|
||||
mark('All conversations retrieved');
|
||||
|
||||
serviceStream.close();
|
||||
}).catch((e) => {
|
||||
// Pass error object once the following issue is fixed:
|
||||
// https://github.com/gaia-components/threads/issues/74
|
||||
serviceStream.abort(`[${e.name}] ${e.message || ''}`);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Stream that returns messages in a conversation, with all suitable
|
||||
* information to display in the conversation view.
|
||||
* @param {ServiceStream.<MessageSummary>} The stream to use in the
|
||||
* implementation: it will be passed automatically by the Bridge library.
|
||||
* @param {Number} conversationId Conversation to retrieve.
|
||||
*/
|
||||
getMessagesForConversation(stream, conversationId) {},
|
||||
|
||||
/**
|
||||
* Returns all information related to a single conversation, to display in
|
||||
* the conversation view.
|
||||
* @param {Number} conversationId Conversation to retrieve.
|
||||
* @returns {ConversationSummary}
|
||||
*/
|
||||
getConversationSummary(conversationId) {},
|
||||
|
||||
/**
|
||||
* Deletes one or several conversations, including their messages.
|
||||
* @param {Array.<Number>} conversationIds ID of the conversations to be
|
||||
* deleted.
|
||||
*/
|
||||
deleteConversations(conversationIds) {},
|
||||
|
||||
/**
|
||||
* Deletes one or several messages.
|
||||
* @param {Array.<Number>} messageIds ID of the messages to be deleted.
|
||||
*/
|
||||
deleteMessages(messageIds) {},
|
||||
|
||||
/**
|
||||
* Marks a conversation as read or unread.
|
||||
* @param {Object} param
|
||||
* @param {Array.<Number>} param.conversations Conversation IDs to be
|
||||
* marked.
|
||||
* @param {Boolean} param.asRead Specifies whether we mark as read of
|
||||
* unread.
|
||||
*/
|
||||
markConversationsAs({conversations, asRead}) {},
|
||||
|
||||
/**
|
||||
* Fetches a message information.
|
||||
* @param {Number} messageId The message ID to retrieve.
|
||||
* @returns {Message} All message information, including contact, delivery,
|
||||
* read reports, size, timestamps, etc.
|
||||
*/
|
||||
getMessage(messageId) {},
|
||||
|
||||
/**
|
||||
* Finds a conversation from an address (that is: a phone number or an
|
||||
* email).
|
||||
* @param {String} address This is either a phone number or an email, to
|
||||
* match an existing conversation from.
|
||||
* @returns {Number?} The id of the conversation, or null if not found.
|
||||
*/
|
||||
findConversationFromAddress(address) {}
|
||||
};
|
||||
|
||||
exports.ConversationService = Object.seal(
|
||||
BridgeServiceMixin.mixin(
|
||||
ConversationService, SERVICE_CONTRACT.name, SERVICE_CONTRACT
|
||||
)
|
||||
);
|
||||
|
||||
// Automatically init service if it's run inside worker.
|
||||
if (!self.document) {
|
||||
ConversationService.init();
|
||||
}
|
||||
})(self);
|
||||
Loading…
Reference in a new issue