VulpesOS/gaia/apps/sms/services/js/drafts.js
OverSu b38dea0597 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.
2026-09-30 23:13:03 +02:00

266 lines
7.2 KiB
JavaScript
Executable file

/*global asyncStorage,
InterInstanceEventDispatcher,
Utils,
EventDispatcher
*/
(function(exports) {
'use strict';
[
['asyncStorage', '/shared/js/async_storage.js'],
['EventDispatcher', '/shared/js/event_dispatcher.js'],
['Utils', '/views/shared/js/utils.js']
].forEach(([dependencyName, dependencyPath]) => {
if (!(dependencyName in self)) {
importScripts(dependencyPath);
}
});
var draftIndex = new Map();
var deferredDraftRequest = null;
/**
* Drafts
*
* A collection of active Draft objects, indexed by thread id.
*/
var Drafts = {
/**
* size
*
* Returns the number of Draft Lists.
*
* There is one Draft.List per Thread and
* one Draft.List for all threadless Drafts.
*
* @return {Number} Maps to draftIndex size property.
*/
get size() {
return draftIndex.size;
},
/**
* Initialize the InterInstanceEventDispatcher and relay the draft
* saved/deleted events from another instance.
*/
init() {
InterInstanceEventDispatcher.on('draft-saved', (draft) =>
this.request(true).then(() => this.emit('saved', draft))
);
InterInstanceEventDispatcher.on('draft-deleted', (draft) =>
this.request(true).then(() => this.emit('deleted', draft))
);
},
/**
* Pushes a Draft instance to the Drafts in-memory collection and commits
* it to the persistent storage.
* @param {Draft} draft Draft instance.
* @returns {Drafts} Returns the Drafts object.
*/
add: function(draft) {
if (!draft || !(draft instanceof Draft)) {
throw new Error('Draft should be valid Draft instance.');
}
// Remove existing draft.
this.delete(draft);
var drafts = draftIndex.get(draft.threadId) || [];
drafts.push(draft);
draftIndex.set(draft.threadId, drafts);
this.emit('saved', draft);
InterInstanceEventDispatcher.emit('draft-saved', draft);
return this;
},
/**
* Deletes a draft record from Drafts in-memory collection.
* @param {Draft} draft Draft instance to delete.
* @returns {Drafts} Returns the Drafts object.
*/
delete: function(draft) {
if (!draft || !(draft instanceof Draft)) {
throw new Error('Draft should be valid Draft instance.');
}
var drafts = draftIndex.get(draft.threadId);
if (!drafts || !drafts.length) {
return this;
}
// Flag to track that draft was really removed.
var isDeleted = false;
// For drafts bound to thread we should remove all drafts we have, but for
// thread-less drafts we should remove draft with the same id only.
if (draft.threadId) {
drafts.length = 0;
isDeleted = true;
} else {
var indexToRemove = drafts.findIndex(
(storedDraft) => storedDraft.id === draft.id
);
if (indexToRemove > -1) {
isDeleted = true;
drafts.splice(indexToRemove, 1);
}
}
if (isDeleted) {
this.emit('deleted', draft);
InterInstanceEventDispatcher.emit('draft-deleted', draft);
}
return this;
},
/**
* Returns draft for the specified thread id or null if it doesn't exist.
* @param {Number} threadId Thread id to recall draft for.
* @returns {Draft}
*/
byThreadId: function(threadId) {
if (!threadId) {
throw new Error('Thread Id is not defined');
}
var threadDrafts = draftIndex.get(threadId);
return threadDrafts && threadDrafts.length ?
threadDrafts[threadDrafts.length - 1] : null;
},
/**
* Returns the draft object with the specified id.
* @param {Number|String} id Id of the draft to return.
* @returns {Draft} Found draft instance or null if nothing is found.
*/
byDraftId: function(id) {
// Ensure a number is used for the comparison, as this value may come from
// a dataset property.
id = +id;
for (var draft of this.getAllThreadless()) {
if (draft.id === id) {
return draft;
}
}
return null;
},
/**
* Returns iterator for all available drafts. For the drafts associated with
* the thread, only the latest draft is returned.
*/
getAll: function* () {
for (var [threadId, drafts] of draftIndex) {
// For drafts with associated thread we consider only the latest draft.
yield* threadId ? drafts.slice(-1) : drafts;
}
},
/**
* Returns list of all thread less drafts.
* @returns {Array.<Draft>}
*/
getAllThreadless: function () {
return draftIndex.get(null) || [];
},
/**
* clear
*
* Delete drafts from the map.
*
* @return {Drafts} return the Drafts object.
*/
clear: function() {
draftIndex = new Map();
deferredDraftRequest = null;
return this;
},
/**
* Stores drafts that are held in memory to local storage.
*/
store: function() {
var defer = Utils.Promise.defer();
asyncStorage.setItem('draft index', [...draftIndex], () => {
defer.resolve();
});
return defer.promise;
},
/**
* Requests drafts from asyncStorage or in-memory cache. Result is cached.
* @param {boolean?} force Indicates whether we should respect already
* cached result or _force_ asyncStorage request once again.
* @returns {Promise} Promise that resolves when all drafts are retrieved
* from persistent storage.
*/
request: function(force) {
// Loading from storage only happens once or when specifically requested
// with force parameter.
if (deferredDraftRequest && !force) {
return deferredDraftRequest.promise;
}
deferredDraftRequest = Utils.Promise.defer();
asyncStorage.getItem('draft index', function (records) {
// Convert every plain JS draft object into Draft "class" instance.
// record[0] is the threadId or null key, record[1] is the array of
// draft objects associated with that threadId or null key.
records && records.forEach((record) => {
record[1] = record[1].map((draft) => new Draft(draft));
});
draftIndex = new Map(records || []);
deferredDraftRequest.resolve();
});
return deferredDraftRequest.promise;
}
};
exports.Drafts = EventDispatcher.mixin(Drafts, ['saved', 'deleted']);
/**
* Draft
*
* A message-like object containing unsent
* message content to be stored temporarily
* in a Drafts collection.
*
* @param {Object} draft Draft or empty object.
*/
function Draft(opts) {
var draft = opts || {};
if (draft.id && typeof draft.id !== 'number') {
throw new Error('Draft id must be a number');
}
this.id = draft.id || guid();
this.recipients = draft.recipients || [];
this.content = draft.content || [];
this.subject = draft.subject || '';
this.timestamp = +draft.timestamp || Date.now();
this.threadId = draft.threadId || null;
this.type = draft.type;
this.isEdited = false;
}
function guid() {
return +(Date.now() + '' + (Math.random() * 1000 | 0));
}
exports.Draft = Draft;
}(this));