|
@@ -17,6 +17,15 @@
|
|
|
|
|
|
import {ReceiverService} from '../services/receiver';
|
|
|
|
|
|
+type ContactMap = Map<string, threema.ContactReceiver>;
|
|
|
+type GroupMap = Map<string, threema.GroupReceiver>;
|
|
|
+type DistributionListMap = Map<string, threema.DistributionListReceiver>;
|
|
|
+type MessageMap = Map<threema.ReceiverType, Map<string, ReceiverMessages>>;
|
|
|
+
|
|
|
+type ConversationFilter = (data: threema.Conversation[]) => threema.Conversation[];
|
|
|
+type ConversationConverter = (data: threema.Conversation) => threema.Conversation;
|
|
|
+type MessageConverter = (data: threema.Message) => threema.Message;
|
|
|
+
|
|
|
/**
|
|
|
* A simple HashSet implementation based on a JavaScript object.
|
|
|
*
|
|
@@ -49,754 +58,758 @@ class StringHashSet {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-angular.module('3ema.container', [])
|
|
|
-.factory('Container', ['$filter', '$log', 'ReceiverService',
|
|
|
- function($filter, $log, receiverService: ReceiverService) {
|
|
|
- type ContactMap = Map<string, threema.ContactReceiver>;
|
|
|
- type GroupMap = Map<string, threema.GroupReceiver>;
|
|
|
- type DistributionListMap = Map<string, threema.DistributionListReceiver>;
|
|
|
- type MessageMap = Map<threema.ReceiverType, Map<string, ReceiverMessages>>;
|
|
|
-
|
|
|
- type ConversationFilter = (data: threema.Conversation[]) => threema.Conversation[];
|
|
|
- type ConversationConverter = (data: threema.Conversation) => threema.Conversation;
|
|
|
- type MessageConverter = (data: threema.Message) => threema.Message;
|
|
|
+/**
|
|
|
+ * Collection that manages receivers like contacts, groups or distribution lists.
|
|
|
+ *
|
|
|
+ * Think of it like the "address book".
|
|
|
+ */
|
|
|
+class Receivers implements threema.Container.Receivers {
|
|
|
+ public me: threema.MeReceiver = null;
|
|
|
+ public contacts: ContactMap = new Map();
|
|
|
+ public groups: GroupMap = new Map();
|
|
|
+ public distributionLists: DistributionListMap = new Map();
|
|
|
|
|
|
/**
|
|
|
- * Collection that manages receivers like contacts, groups or distribution lists.
|
|
|
- *
|
|
|
- * Think of it like the "address book".
|
|
|
- */
|
|
|
- class Receivers implements threema.Container.Receivers {
|
|
|
- public me: threema.MeReceiver = null;
|
|
|
- public contacts: ContactMap = new Map();
|
|
|
- public groups: GroupMap = new Map();
|
|
|
- public distributionLists: DistributionListMap = new Map();
|
|
|
-
|
|
|
- /**
|
|
|
- * Get the receiver map for the specified type.
|
|
|
- */
|
|
|
- public get(receiverType: threema.ReceiverType): threema.Receiver | Map<string, threema.Receiver> {
|
|
|
- switch (receiverType) {
|
|
|
- case 'me':
|
|
|
- return this.me;
|
|
|
- case 'contact':
|
|
|
- return this.contacts;
|
|
|
- case 'group':
|
|
|
- return this.groups;
|
|
|
- case 'distributionList':
|
|
|
- return this.distributionLists;
|
|
|
- default:
|
|
|
- throw new Error('Unknown or invalid receiver type: ' + receiverType);
|
|
|
- }
|
|
|
+ * Get the receiver map for the specified type.
|
|
|
+ */
|
|
|
+ public get(receiverType: threema.ReceiverType): threema.Receiver | Map<string, threema.Receiver> {
|
|
|
+ switch (receiverType) {
|
|
|
+ case 'me':
|
|
|
+ return this.me;
|
|
|
+ case 'contact':
|
|
|
+ return this.contacts;
|
|
|
+ case 'group':
|
|
|
+ return this.groups;
|
|
|
+ case 'distributionList':
|
|
|
+ return this.distributionLists;
|
|
|
+ default:
|
|
|
+ throw new Error('Unknown or invalid receiver type: ' + receiverType);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Get the receiver matching a certain template.
|
|
|
- */
|
|
|
- public getData(receiver: threema.BaseReceiver): threema.Receiver | null {
|
|
|
- if (receiver.type === 'me') {
|
|
|
- return this.me.id === receiver.id ? this.me : undefined;
|
|
|
- } else {
|
|
|
- const receivers = this.get(receiver.type) as Map<string, threema.Receiver>;
|
|
|
- return receivers.get(receiver.id);
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Get the receiver matching a certain template.
|
|
|
+ */
|
|
|
+ public getData(receiver: threema.BaseReceiver): threema.Receiver | null {
|
|
|
+ if (receiver.type === 'me') {
|
|
|
+ return this.me.id === receiver.id ? this.me : undefined;
|
|
|
+ } else {
|
|
|
+ const receivers = this.get(receiver.type) as Map<string, threema.Receiver>;
|
|
|
+ return receivers.get(receiver.id);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Set receiver data.
|
|
|
- */
|
|
|
- public set(data: threema.Container.ReceiverData) {
|
|
|
- this.setContacts(data['contact' as threema.ReceiverType]);
|
|
|
- this.setGroups(data['group' as threema.ReceiverType]);
|
|
|
- this.setDistributionLists(data['distributionList' as threema.ReceiverType]);
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Set receiver data.
|
|
|
+ */
|
|
|
+ public set(data: threema.Container.ReceiverData) {
|
|
|
+ this.setContacts(data['contact' as threema.ReceiverType]);
|
|
|
+ this.setGroups(data['group' as threema.ReceiverType]);
|
|
|
+ this.setDistributionLists(data['distributionList' as threema.ReceiverType]);
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Set own contact.
|
|
|
- */
|
|
|
- public setMe(data: threema.MeReceiver): void {
|
|
|
- data.type = 'me';
|
|
|
- this.me = data;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Set own contact.
|
|
|
+ */
|
|
|
+ public setMe(data: threema.MeReceiver): void {
|
|
|
+ data.type = 'me';
|
|
|
+ this.me = data;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Set contacts.
|
|
|
- */
|
|
|
- public setContacts(data: threema.ContactReceiver[]): void {
|
|
|
- this.contacts = new Map(data.map((c) => {
|
|
|
- c.type = 'contact';
|
|
|
- return [c.id, c];
|
|
|
- }) as any) as ContactMap;
|
|
|
- if (this.me !== undefined) {
|
|
|
- this.contacts.set(this.me.id, this.me);
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Set contacts.
|
|
|
+ */
|
|
|
+ public setContacts(data: threema.ContactReceiver[]): void {
|
|
|
+ this.contacts = new Map(data.map((c) => {
|
|
|
+ c.type = 'contact';
|
|
|
+ return [c.id, c];
|
|
|
+ }) as any) as ContactMap;
|
|
|
+ if (this.me !== undefined) {
|
|
|
+ this.contacts.set(this.me.id, this.me);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Set groups.
|
|
|
- */
|
|
|
- public setGroups(data: threema.GroupReceiver[]): void {
|
|
|
- this.groups = new Map(data.map((g) => {
|
|
|
- g.type = 'group';
|
|
|
- return [g.id, g];
|
|
|
- }) as any) as GroupMap;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Set groups.
|
|
|
+ */
|
|
|
+ public setGroups(data: threema.GroupReceiver[]): void {
|
|
|
+ this.groups = new Map(data.map((g) => {
|
|
|
+ g.type = 'group';
|
|
|
+ return [g.id, g];
|
|
|
+ }) as any) as GroupMap;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Set distribution lists.
|
|
|
- */
|
|
|
- public setDistributionLists(data: threema.DistributionListReceiver[]): void {
|
|
|
- this.distributionLists = new Map(data.map((d) => {
|
|
|
- d.type = 'distributionList';
|
|
|
- return [d.id, d];
|
|
|
- }) as any) as DistributionListMap;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Set distribution lists.
|
|
|
+ */
|
|
|
+ public setDistributionLists(data: threema.DistributionListReceiver[]): void {
|
|
|
+ this.distributionLists = new Map(data.map((d) => {
|
|
|
+ d.type = 'distributionList';
|
|
|
+ return [d.id, d];
|
|
|
+ }) as any) as DistributionListMap;
|
|
|
+ }
|
|
|
|
|
|
- public extend(receiverType: threema.ReceiverType, data: threema.Receiver): threema.Receiver {
|
|
|
- switch (receiverType) {
|
|
|
- case 'me':
|
|
|
- return this.extendMe(data as threema.MeReceiver);
|
|
|
- case 'contact':
|
|
|
- return this.extendContact(data as threema.ContactReceiver);
|
|
|
- case 'group':
|
|
|
- return this.extendGroup(data as threema.GroupReceiver);
|
|
|
- case 'distributionList':
|
|
|
- return this.extendDistributionList(data as threema.DistributionListReceiver);
|
|
|
- default:
|
|
|
- throw new Error('Unknown or invalid receiver type: ' + receiverType);
|
|
|
- }
|
|
|
+ public extend(receiverType: threema.ReceiverType, data: threema.Receiver): threema.Receiver {
|
|
|
+ switch (receiverType) {
|
|
|
+ case 'me':
|
|
|
+ return this.extendMe(data as threema.MeReceiver);
|
|
|
+ case 'contact':
|
|
|
+ return this.extendContact(data as threema.ContactReceiver);
|
|
|
+ case 'group':
|
|
|
+ return this.extendGroup(data as threema.GroupReceiver);
|
|
|
+ case 'distributionList':
|
|
|
+ return this.extendDistributionList(data as threema.DistributionListReceiver);
|
|
|
+ default:
|
|
|
+ throw new Error('Unknown or invalid receiver type: ' + receiverType);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- public extendDistributionList(data: threema.DistributionListReceiver): threema.DistributionListReceiver {
|
|
|
- let distributionListReceiver = this.distributionLists.get(data.id);
|
|
|
- if (distributionListReceiver === undefined) {
|
|
|
- data.type = 'distributionList';
|
|
|
- this.distributionLists.set(data.id, data);
|
|
|
- return data;
|
|
|
- }
|
|
|
-
|
|
|
- // update existing object
|
|
|
- distributionListReceiver = angular.extend(distributionListReceiver, data);
|
|
|
- return distributionListReceiver;
|
|
|
+ public extendDistributionList(data: threema.DistributionListReceiver): threema.DistributionListReceiver {
|
|
|
+ let distributionListReceiver = this.distributionLists.get(data.id);
|
|
|
+ if (distributionListReceiver === undefined) {
|
|
|
+ data.type = 'distributionList';
|
|
|
+ this.distributionLists.set(data.id, data);
|
|
|
+ return data;
|
|
|
}
|
|
|
|
|
|
- public extendGroup(data: threema.GroupReceiver): threema.GroupReceiver {
|
|
|
- let groupReceiver = this.groups.get(data.id);
|
|
|
- if (groupReceiver === undefined) {
|
|
|
- data.type = 'group';
|
|
|
- this.groups.set(data.id, data);
|
|
|
- return data;
|
|
|
- }
|
|
|
+ // update existing object
|
|
|
+ distributionListReceiver = angular.extend(distributionListReceiver, data);
|
|
|
+ return distributionListReceiver;
|
|
|
+ }
|
|
|
|
|
|
- // update existing object
|
|
|
- groupReceiver = angular.extend(groupReceiver, data);
|
|
|
- return groupReceiver;
|
|
|
+ public extendGroup(data: threema.GroupReceiver): threema.GroupReceiver {
|
|
|
+ let groupReceiver = this.groups.get(data.id);
|
|
|
+ if (groupReceiver === undefined) {
|
|
|
+ data.type = 'group';
|
|
|
+ this.groups.set(data.id, data);
|
|
|
+ return data;
|
|
|
}
|
|
|
|
|
|
- public extendMe(data: threema.MeReceiver): threema.MeReceiver {
|
|
|
- if (this.me === undefined) {
|
|
|
- data.type = 'me';
|
|
|
- this.me = data;
|
|
|
- return data;
|
|
|
- }
|
|
|
+ // update existing object
|
|
|
+ groupReceiver = angular.extend(groupReceiver, data);
|
|
|
+ return groupReceiver;
|
|
|
+ }
|
|
|
|
|
|
- // update existing object
|
|
|
- this.me = angular.extend(this.me, data);
|
|
|
- return this.me;
|
|
|
+ public extendMe(data: threema.MeReceiver): threema.MeReceiver {
|
|
|
+ if (this.me === undefined) {
|
|
|
+ data.type = 'me';
|
|
|
+ this.me = data;
|
|
|
+ return data;
|
|
|
}
|
|
|
|
|
|
- public extendContact(data: threema.ContactReceiver): threema.ContactReceiver {
|
|
|
- let contactReceiver = this.contacts.get(data.id);
|
|
|
- if (contactReceiver === undefined) {
|
|
|
- data.type = 'contact';
|
|
|
- this.contacts.set(data.id, data);
|
|
|
- return data;
|
|
|
- }
|
|
|
+ // update existing object
|
|
|
+ this.me = angular.extend(this.me, data);
|
|
|
+ return this.me;
|
|
|
+ }
|
|
|
|
|
|
- // update existing object
|
|
|
- contactReceiver = angular.extend(contactReceiver, data);
|
|
|
- return contactReceiver;
|
|
|
+ public extendContact(data: threema.ContactReceiver): threema.ContactReceiver {
|
|
|
+ let contactReceiver = this.contacts.get(data.id);
|
|
|
+ if (contactReceiver === undefined) {
|
|
|
+ data.type = 'contact';
|
|
|
+ this.contacts.set(data.id, data);
|
|
|
+ return data;
|
|
|
}
|
|
|
+
|
|
|
+ // update existing object
|
|
|
+ contactReceiver = angular.extend(contactReceiver, data);
|
|
|
+ return contactReceiver;
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- class Conversations implements threema.Container.Conversations {
|
|
|
+class Conversations implements threema.Container.Conversations {
|
|
|
|
|
|
- private conversations: threema.Conversation[] = [];
|
|
|
+ private conversations: threema.Conversation[] = [];
|
|
|
|
|
|
- public filter: ConversationFilter = null;
|
|
|
- private converter: ConversationConverter = null;
|
|
|
+ public filter: ConversationFilter = null;
|
|
|
+ private converter: ConversationConverter = null;
|
|
|
|
|
|
- /**
|
|
|
- * Get conversations.
|
|
|
- */
|
|
|
- public get(): threema.Conversation[] {
|
|
|
- let conversations = this.conversations;
|
|
|
- if (this.filter != null) {
|
|
|
- conversations = this.filter(conversations);
|
|
|
- }
|
|
|
- if (this.converter != null) {
|
|
|
- conversations = conversations.map(this.converter);
|
|
|
- }
|
|
|
- return conversations;
|
|
|
- }
|
|
|
+ private receiverService: ReceiverService;
|
|
|
|
|
|
- /**
|
|
|
- * Set conversations.
|
|
|
- */
|
|
|
- public set(data: threema.Conversation[]): void {
|
|
|
- data.forEach((existingConversation: threema.Conversation) => {
|
|
|
- this.updateOrAdd(existingConversation);
|
|
|
- });
|
|
|
- }
|
|
|
+ constructor(receiverService: ReceiverService) {
|
|
|
+ this.receiverService = receiverService;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Find a stored conversation matching the given conversation or receiver.
|
|
|
- *
|
|
|
- * Comparison is done by type and id.
|
|
|
- */
|
|
|
- public find(pattern: threema.Conversation | threema.Receiver): threema.Conversation | null {
|
|
|
- for (const conversation of this.get()) {
|
|
|
- const a = pattern;
|
|
|
- const b = conversation;
|
|
|
- if (a !== undefined && b !== undefined && a.type === b.type && a.id === b.id) {
|
|
|
- return conversation;
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
+ /**
|
|
|
+ * Get conversations.
|
|
|
+ */
|
|
|
+ public get(): threema.Conversation[] {
|
|
|
+ let conversations = this.conversations;
|
|
|
+ if (this.filter != null) {
|
|
|
+ conversations = this.filter(conversations);
|
|
|
}
|
|
|
-
|
|
|
- public add(conversation: threema.Conversation): void {
|
|
|
- this.conversations.splice(conversation.position, 0, conversation);
|
|
|
+ if (this.converter != null) {
|
|
|
+ conversations = conversations.map(this.converter);
|
|
|
}
|
|
|
+ return conversations;
|
|
|
+ }
|
|
|
|
|
|
- public updateOrAdd(conversation: threema.Conversation): void {
|
|
|
- let moveDirection = 0;
|
|
|
- let updated = false;
|
|
|
- for (const p of this.conversations.keys()) {
|
|
|
- if (receiverService.compare(this.conversations[p], conversation)) {
|
|
|
- // ok, replace me and break
|
|
|
- const old = this.conversations[p];
|
|
|
- if (old.position !== conversation.position) {
|
|
|
- // position also changed...
|
|
|
- moveDirection = old.position > conversation.position ? -1 : 1;
|
|
|
- }
|
|
|
- this.conversations[p] = conversation;
|
|
|
- updated = true;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Set conversations.
|
|
|
+ */
|
|
|
+ public set(data: threema.Conversation[]): void {
|
|
|
+ data.forEach((existingConversation: threema.Conversation) => {
|
|
|
+ this.updateOrAdd(existingConversation);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Find a stored conversation matching the given conversation or receiver.
|
|
|
+ *
|
|
|
+ * Comparison is done by type and id.
|
|
|
+ */
|
|
|
+ public find(pattern: threema.Conversation | threema.Receiver): threema.Conversation | null {
|
|
|
+ for (const conversation of this.get()) {
|
|
|
+ const a = pattern;
|
|
|
+ const b = conversation;
|
|
|
+ if (a !== undefined && b !== undefined && a.type === b.type && a.id === b.id) {
|
|
|
+ return conversation;
|
|
|
}
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
- // reset the position field to correct the sorting
|
|
|
- if (moveDirection !== 0) {
|
|
|
- // reindex
|
|
|
- let before = true;
|
|
|
- for (const p in this.conversations) {
|
|
|
- if (receiverService.compare(this.conversations[p], conversation)) {
|
|
|
- before = false;
|
|
|
- } else if (before && moveDirection < 0) {
|
|
|
- this.conversations[p].position++;
|
|
|
- } else if (!before && moveDirection > 0) {
|
|
|
- this.conversations[p].position++;
|
|
|
- }
|
|
|
- }
|
|
|
+ public add(conversation: threema.Conversation): void {
|
|
|
+ this.conversations.splice(conversation.position, 0, conversation);
|
|
|
+ }
|
|
|
|
|
|
- // sort by position field
|
|
|
- this.conversations.sort(function(convA, convB) {
|
|
|
- return convA.position - convB.position;
|
|
|
- });
|
|
|
- } else if (!updated) {
|
|
|
- this.add(conversation);
|
|
|
+ public updateOrAdd(conversation: threema.Conversation): void {
|
|
|
+ let moveDirection = 0;
|
|
|
+ let updated = false;
|
|
|
+ for (const p of this.conversations.keys()) {
|
|
|
+ if (this.receiverService.compare(this.conversations[p], conversation)) {
|
|
|
+ // ok, replace me and break
|
|
|
+ const old = this.conversations[p];
|
|
|
+ if (old.position !== conversation.position) {
|
|
|
+ // position also changed...
|
|
|
+ moveDirection = old.position > conversation.position ? -1 : 1;
|
|
|
+ }
|
|
|
+ this.conversations[p] = conversation;
|
|
|
+ updated = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public remove(conversation: threema.Conversation): void {
|
|
|
- for (const p of this.conversations.keys()) {
|
|
|
- if (receiverService.compare(this.conversations[p], conversation)) {
|
|
|
- // remove conversation from array
|
|
|
- this.conversations.splice(p, 1);
|
|
|
- return;
|
|
|
+ // reset the position field to correct the sorting
|
|
|
+ if (moveDirection !== 0) {
|
|
|
+ // reindex
|
|
|
+ let before = true;
|
|
|
+ for (const p in this.conversations) {
|
|
|
+ if (this.receiverService.compare(this.conversations[p], conversation)) {
|
|
|
+ before = false;
|
|
|
+ } else if (before && moveDirection < 0) {
|
|
|
+ this.conversations[p].position++;
|
|
|
+ } else if (!before && moveDirection > 0) {
|
|
|
+ this.conversations[p].position++;
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- /**
|
|
|
- * Set a filter.
|
|
|
- */
|
|
|
- public setFilter(filter: ConversationFilter): void {
|
|
|
- this.filter = filter;
|
|
|
+ // sort by position field
|
|
|
+ this.conversations.sort(function(convA, convB) {
|
|
|
+ return convA.position - convB.position;
|
|
|
+ });
|
|
|
+ } else if (!updated) {
|
|
|
+ this.add(conversation);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Set a converter.
|
|
|
- */
|
|
|
- public setConverter(converter: ConversationConverter): void {
|
|
|
- this.converter = converter;
|
|
|
+ public remove(conversation: threema.Conversation): void {
|
|
|
+ for (const p of this.conversations.keys()) {
|
|
|
+ if (this.receiverService.compare(this.conversations[p], conversation)) {
|
|
|
+ // remove conversation from array
|
|
|
+ this.conversations.splice(p, 1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Messages between local user and a receiver.
|
|
|
+ * Set a filter.
|
|
|
*/
|
|
|
- class ReceiverMessages {
|
|
|
+ public setFilter(filter: ConversationFilter): void {
|
|
|
+ this.filter = filter;
|
|
|
+ }
|
|
|
|
|
|
- // The message id used as reference when paging.
|
|
|
- public referenceMsgId: string = null;
|
|
|
+ /**
|
|
|
+ * Set a converter.
|
|
|
+ */
|
|
|
+ public setConverter(converter: ConversationConverter): void {
|
|
|
+ this.converter = converter;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- // Whether a message request has been sent yet.
|
|
|
- public requested = false;
|
|
|
+/**
|
|
|
+ * Messages between local user and a receiver.
|
|
|
+ */
|
|
|
+class ReceiverMessages {
|
|
|
|
|
|
- // This flag indicates that more (older) messages are available.
|
|
|
- public more = true;
|
|
|
+ // The message id used as reference when paging.
|
|
|
+ public referenceMsgId: string = null;
|
|
|
|
|
|
- // List of messages.
|
|
|
- public list: threema.Message[] = [];
|
|
|
+ // Whether a message request has been sent yet.
|
|
|
+ public requested = false;
|
|
|
|
|
|
- }
|
|
|
+ // This flag indicates that more (older) messages are available.
|
|
|
+ public more = true;
|
|
|
|
|
|
- /**
|
|
|
- * This class manages all messages for the current user.
|
|
|
- */
|
|
|
- class Messages implements threema.Container.Messages {
|
|
|
- // The messages are stored in date-ascending order,
|
|
|
- // newest messages are appended, older messages are prepended.
|
|
|
- private messages: MessageMap = new Map();
|
|
|
+ // List of messages.
|
|
|
+ public list: threema.Message[] = [];
|
|
|
|
|
|
- // Message converter
|
|
|
- public converter: MessageConverter = null;
|
|
|
+}
|
|
|
|
|
|
- /**
|
|
|
- * Ensure that the receiver exists in the receiver map.
|
|
|
- */
|
|
|
- private lazyCreate(receiver: threema.Receiver): void {
|
|
|
- // If the type is not yet known, create a new type map.
|
|
|
- if (!this.messages.has(receiver.type)) {
|
|
|
- this.messages.set(receiver.type, new Map());
|
|
|
- }
|
|
|
+/**
|
|
|
+ * This class manages all messages for the current user.
|
|
|
+ */
|
|
|
+class Messages implements threema.Container.Messages {
|
|
|
+ // The messages are stored in date-ascending order,
|
|
|
+ // newest messages are appended, older messages are prepended.
|
|
|
+ private messages: MessageMap = new Map();
|
|
|
|
|
|
- // If the receiver is not yet known, initialize it.
|
|
|
- const typeMap = this.messages.get(receiver.type);
|
|
|
- if (!typeMap.has(receiver.id)) {
|
|
|
- typeMap.set(receiver.id, new ReceiverMessages());
|
|
|
- }
|
|
|
- }
|
|
|
+ // Message converter
|
|
|
+ public converter: MessageConverter = null;
|
|
|
|
|
|
- /**
|
|
|
- * Return the `ReceiverMessages` instance for the specified receiver.
|
|
|
- *
|
|
|
- * If the receiver is not known yet, it is initialized.
|
|
|
- */
|
|
|
- private getReceiverMessages(receiver: threema.Receiver): ReceiverMessages {
|
|
|
- this.lazyCreate(receiver);
|
|
|
- return this.messages.get(receiver.type).get(receiver.id);
|
|
|
- }
|
|
|
+ private $log: ng.ILogService;
|
|
|
|
|
|
- /**
|
|
|
- * Return the list of messages for the specified receiver.
|
|
|
- *
|
|
|
- * If the receiver is not known yet, it is initialized with an empty
|
|
|
- * message list.
|
|
|
- */
|
|
|
- public getList(receiver: threema.Receiver): threema.Message[] {
|
|
|
- return this.getReceiverMessages(receiver).list;
|
|
|
+ constructor($log: ng.ILogService) {
|
|
|
+ this.$log = $log;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Ensure that the receiver exists in the receiver map.
|
|
|
+ */
|
|
|
+ private lazyCreate(receiver: threema.Receiver): void {
|
|
|
+ // If the type is not yet known, create a new type map.
|
|
|
+ if (!this.messages.has(receiver.type)) {
|
|
|
+ this.messages.set(receiver.type, new Map());
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Clear and reset all loaded messages but do not remove objects
|
|
|
- * @param $scope
|
|
|
- */
|
|
|
- public clear($scope: ng.IScope): void {
|
|
|
- this.messages.forEach((messageMap: Map<string, ReceiverMessages>, receiverType: threema.ReceiverType) => {
|
|
|
- messageMap.forEach((messages: ReceiverMessages, id: string) => {
|
|
|
- messages.requested = false;
|
|
|
- messages.referenceMsgId = null;
|
|
|
- messages.more = true;
|
|
|
- messages.list = [];
|
|
|
-
|
|
|
- this.notify({
|
|
|
- id: id,
|
|
|
- type: receiverType,
|
|
|
- } as threema.Receiver, $scope);
|
|
|
- });
|
|
|
- });
|
|
|
+ // If the receiver is not yet known, initialize it.
|
|
|
+ const typeMap = this.messages.get(receiver.type);
|
|
|
+ if (!typeMap.has(receiver.id)) {
|
|
|
+ typeMap.set(receiver.id, new ReceiverMessages());
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Reset the cached messages of a receiver (e.g. the receiver was locked by the mobile)
|
|
|
- */
|
|
|
- public clearReceiverMessages(receiver: threema.Receiver): number {
|
|
|
- let cachedMessageCount = 0;
|
|
|
- if (this.messages.has(receiver.type)) {
|
|
|
- const typeMessages = this.messages.get(receiver.type);
|
|
|
- if (typeMessages.has(receiver.id)) {
|
|
|
- cachedMessageCount = typeMessages.get(receiver.id).list.length;
|
|
|
- typeMessages.delete(receiver.id);
|
|
|
- }
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Return the `ReceiverMessages` instance for the specified receiver.
|
|
|
+ *
|
|
|
+ * If the receiver is not known yet, it is initialized.
|
|
|
+ */
|
|
|
+ private getReceiverMessages(receiver: threema.Receiver): ReceiverMessages {
|
|
|
+ this.lazyCreate(receiver);
|
|
|
+ return this.messages.get(receiver.type).get(receiver.id);
|
|
|
+ }
|
|
|
|
|
|
- return cachedMessageCount;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Return the list of messages for the specified receiver.
|
|
|
+ *
|
|
|
+ * If the receiver is not known yet, it is initialized with an empty
|
|
|
+ * message list.
|
|
|
+ */
|
|
|
+ public getList(receiver: threema.Receiver): threema.Message[] {
|
|
|
+ return this.getReceiverMessages(receiver).list;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Return whether messages from/for the specified receiver are available.
|
|
|
- */
|
|
|
- public contains(receiver: threema.Receiver): boolean {
|
|
|
- return this.messages.has(receiver.type) &&
|
|
|
- this.messages.get(receiver.type).has(receiver.id);
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Clear and reset all loaded messages but do not remove objects
|
|
|
+ * @param $scope
|
|
|
+ */
|
|
|
+ public clear($scope: ng.IScope): void {
|
|
|
+ this.messages.forEach((messageMap: Map<string, ReceiverMessages>, receiverType: threema.ReceiverType) => {
|
|
|
+ messageMap.forEach((messages: ReceiverMessages, id: string) => {
|
|
|
+ messages.requested = false;
|
|
|
+ messages.referenceMsgId = null;
|
|
|
+ messages.more = true;
|
|
|
+ messages.list = [];
|
|
|
+
|
|
|
+ this.notify({
|
|
|
+ id: id,
|
|
|
+ type: receiverType,
|
|
|
+ } as threema.Receiver, $scope);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Return whether there are more (older) messages available to fetch
|
|
|
- * for the specified receiver.
|
|
|
- */
|
|
|
- public hasMore(receiver: threema.Receiver): boolean {
|
|
|
- return this.getReceiverMessages(receiver).more;
|
|
|
+ /**
|
|
|
+ * Reset the cached messages of a receiver (e.g. the receiver was locked by the mobile)
|
|
|
+ */
|
|
|
+ public clearReceiverMessages(receiver: threema.Receiver): number {
|
|
|
+ let cachedMessageCount = 0;
|
|
|
+ if (this.messages.has(receiver.type)) {
|
|
|
+ const typeMessages = this.messages.get(receiver.type);
|
|
|
+ if (typeMessages.has(receiver.id)) {
|
|
|
+ cachedMessageCount = typeMessages.get(receiver.id).list.length;
|
|
|
+ typeMessages.delete(receiver.id);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Set the "more" flag for the specified receiver.
|
|
|
- *
|
|
|
- * The flag indicates that more (older) messages are available.
|
|
|
- */
|
|
|
- public setMore(receiver: threema.Receiver, more: boolean): void {
|
|
|
- this.getReceiverMessages(receiver).more = more;
|
|
|
- }
|
|
|
+ return cachedMessageCount;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Return the reference msg id for the specified receiver.
|
|
|
- */
|
|
|
- public getReferenceMsgId(receiver: threema.Receiver): string {
|
|
|
- return this.getReceiverMessages(receiver).referenceMsgId;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Return whether messages from/for the specified receiver are available.
|
|
|
+ */
|
|
|
+ public contains(receiver: threema.Receiver): boolean {
|
|
|
+ return this.messages.has(receiver.type) &&
|
|
|
+ this.messages.get(receiver.type).has(receiver.id);
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Return whether the messages for the specified receiver are already
|
|
|
- * requested.
|
|
|
- */
|
|
|
- public isRequested(receiver: threema.Receiver): boolean {
|
|
|
- return this.getReceiverMessages(receiver).requested;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Return whether there are more (older) messages available to fetch
|
|
|
+ * for the specified receiver.
|
|
|
+ */
|
|
|
+ public hasMore(receiver: threema.Receiver): boolean {
|
|
|
+ return this.getReceiverMessages(receiver).more;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Set the requested flag for the specified receiver.
|
|
|
- */
|
|
|
- public setRequested(receiver: threema.Receiver): void {
|
|
|
- const messages = this.getReceiverMessages(receiver);
|
|
|
+ /**
|
|
|
+ * Set the "more" flag for the specified receiver.
|
|
|
+ *
|
|
|
+ * The flag indicates that more (older) messages are available.
|
|
|
+ */
|
|
|
+ public setMore(receiver: threema.Receiver, more: boolean): void {
|
|
|
+ this.getReceiverMessages(receiver).more = more;
|
|
|
+ }
|
|
|
|
|
|
- // If a request was already pending, this must be a bug.
|
|
|
- if (messages.requested) {
|
|
|
- throw new Error('Message request for receiver ' + receiver.id + ' still pending');
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Return the reference msg id for the specified receiver.
|
|
|
+ */
|
|
|
+ public getReferenceMsgId(receiver: threema.Receiver): string {
|
|
|
+ return this.getReceiverMessages(receiver).referenceMsgId;
|
|
|
+ }
|
|
|
|
|
|
- // Set requested
|
|
|
- messages.requested = true;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Return whether the messages for the specified receiver are already
|
|
|
+ * requested.
|
|
|
+ */
|
|
|
+ public isRequested(receiver: threema.Receiver): boolean {
|
|
|
+ return this.getReceiverMessages(receiver).requested;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Clear the "requested" flag for the specified receiver messages.
|
|
|
- */
|
|
|
- public clearRequested(receiver): void {
|
|
|
- const messages = this.getReceiverMessages(receiver);
|
|
|
- messages.requested = false;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Set the requested flag for the specified receiver.
|
|
|
+ */
|
|
|
+ public setRequested(receiver: threema.Receiver): void {
|
|
|
+ const messages = this.getReceiverMessages(receiver);
|
|
|
|
|
|
- /**
|
|
|
- * Append newer messages.
|
|
|
- *
|
|
|
- * Messages must be sorted ascending by date.
|
|
|
- */
|
|
|
- public addNewer(receiver: threema.Receiver, messages: threema.Message[]): void {
|
|
|
- if (messages.length === 0) {
|
|
|
- // do nothing
|
|
|
- return;
|
|
|
- }
|
|
|
- const receiverMessages = this.getReceiverMessages(receiver);
|
|
|
- // if the list is empty, add the current message as ref
|
|
|
- if (receiverMessages.list.length === 0) {
|
|
|
- receiverMessages.referenceMsgId = messages[0].id;
|
|
|
- }
|
|
|
- receiverMessages.list.push.apply(receiverMessages.list, messages);
|
|
|
+ // If a request was already pending, this must be a bug.
|
|
|
+ if (messages.requested) {
|
|
|
+ throw new Error('Message request for receiver ' + receiver.id + ' still pending');
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Prepend older messages.
|
|
|
- *
|
|
|
- * Messages must be sorted ascending by date (oldest first).
|
|
|
- */
|
|
|
- public addOlder(receiver: threema.Receiver, messages: threema.Message[]): void {
|
|
|
- if (messages.length === 0) {
|
|
|
- // do nothing
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Get reference to message list for the specified receiver
|
|
|
- const receiverMessages = this.getReceiverMessages(receiver);
|
|
|
+ // Set requested
|
|
|
+ messages.requested = true;
|
|
|
+ }
|
|
|
|
|
|
- // If the first or last message is already contained in the list,
|
|
|
- // do nothing.
|
|
|
- const firstId = messages[0].id;
|
|
|
- const lastId = messages[messages.length - 1].id;
|
|
|
- const predicate = (msg: threema.Message) => msg.id === firstId || msg.id === lastId;
|
|
|
- if (receiverMessages.list.findIndex(predicate, receiverMessages.list) !== -1) {
|
|
|
- $log.warn('Messages to be prepended intersect with existing messages:', messages);
|
|
|
- return;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Clear the "requested" flag for the specified receiver messages.
|
|
|
+ */
|
|
|
+ public clearRequested(receiver): void {
|
|
|
+ const messages = this.getReceiverMessages(receiver);
|
|
|
+ messages.requested = false;
|
|
|
+ }
|
|
|
|
|
|
- // Add the oldest message as ref
|
|
|
+ /**
|
|
|
+ * Append newer messages.
|
|
|
+ *
|
|
|
+ * Messages must be sorted ascending by date.
|
|
|
+ */
|
|
|
+ public addNewer(receiver: threema.Receiver, messages: threema.Message[]): void {
|
|
|
+ if (messages.length === 0) {
|
|
|
+ // do nothing
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const receiverMessages = this.getReceiverMessages(receiver);
|
|
|
+ // if the list is empty, add the current message as ref
|
|
|
+ if (receiverMessages.list.length === 0) {
|
|
|
receiverMessages.referenceMsgId = messages[0].id;
|
|
|
- receiverMessages.list.unshift.apply(receiverMessages.list, messages);
|
|
|
}
|
|
|
+ receiverMessages.list.push.apply(receiverMessages.list, messages);
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Update/replace a message with a newer version.
|
|
|
- *
|
|
|
- * Return a boolean indicating whether the message was found and
|
|
|
- * replaced, or not.
|
|
|
- */
|
|
|
- public update(receiver: threema.Receiver, message: threema.Message): boolean {
|
|
|
- const list = this.getList(receiver);
|
|
|
- for (let i = 0; i < list.length; i++) {
|
|
|
- if (list[i].id === message.id) {
|
|
|
- if (message.thumbnail === undefined) {
|
|
|
- // do not reset the thumbnail
|
|
|
- message.thumbnail = list[i].thumbnail;
|
|
|
- }
|
|
|
- list[i] = message;
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
+ /**
|
|
|
+ * Prepend older messages.
|
|
|
+ *
|
|
|
+ * Messages must be sorted ascending by date (oldest first).
|
|
|
+ */
|
|
|
+ public addOlder(receiver: threema.Receiver, messages: threema.Message[]): void {
|
|
|
+ if (messages.length === 0) {
|
|
|
+ // do nothing
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Update a thumbnail of a message, if a message was found the method will return true
|
|
|
- */
|
|
|
- public setThumbnail(receiver: threema.Receiver, messageId: string, thumbnailImage: string): boolean {
|
|
|
- const list = this.getList(receiver);
|
|
|
- for (const message of list) {
|
|
|
- if (message.id === messageId) {
|
|
|
- if (message.thumbnail === undefined) {
|
|
|
- message.thumbnail = {img: thumbnailImage} as threema.Thumbnail;
|
|
|
- } else {
|
|
|
- message.thumbnail.img = thumbnailImage;
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
+ // Get reference to message list for the specified receiver
|
|
|
+ const receiverMessages = this.getReceiverMessages(receiver);
|
|
|
|
|
|
- return false;
|
|
|
+ // If the first or last message is already contained in the list,
|
|
|
+ // do nothing.
|
|
|
+ const firstId = messages[0].id;
|
|
|
+ const lastId = messages[messages.length - 1].id;
|
|
|
+ const predicate = (msg: threema.Message) => msg.id === firstId || msg.id === lastId;
|
|
|
+ if (receiverMessages.list.findIndex(predicate, receiverMessages.list) !== -1) {
|
|
|
+ this.$log.warn('Messages to be prepended intersect with existing messages:', messages);
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Remove a message.
|
|
|
- *
|
|
|
- * Return a boolean indicating whether the message was found and
|
|
|
- * removed, or not.
|
|
|
- */
|
|
|
- public remove(receiver: threema.Receiver, messageId: string): boolean {
|
|
|
- const list = this.getList(receiver);
|
|
|
- for (let i = 0; i < list.length; i++) {
|
|
|
- if (list[i].id === messageId) {
|
|
|
- list.splice(i, 1);
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
- /**
|
|
|
- * Remove a message.
|
|
|
- *
|
|
|
- * Return a boolean indicating whether the message was found and
|
|
|
- * removed, or not.
|
|
|
- */
|
|
|
- public removeTemporary(receiver: threema.Receiver, temporaryMessageId: string): boolean {
|
|
|
- const list = this.getList(receiver);
|
|
|
- for (let i = 0; i < list.length; i++) {
|
|
|
- if (list[i].temporaryId === temporaryMessageId) {
|
|
|
- list.splice(i, 1);
|
|
|
- return true;
|
|
|
+ // Add the oldest message as ref
|
|
|
+ receiverMessages.referenceMsgId = messages[0].id;
|
|
|
+ receiverMessages.list.unshift.apply(receiverMessages.list, messages);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Update/replace a message with a newer version.
|
|
|
+ *
|
|
|
+ * Return a boolean indicating whether the message was found and
|
|
|
+ * replaced, or not.
|
|
|
+ */
|
|
|
+ public update(receiver: threema.Receiver, message: threema.Message): boolean {
|
|
|
+ const list = this.getList(receiver);
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
+ if (list[i].id === message.id) {
|
|
|
+ if (message.thumbnail === undefined) {
|
|
|
+ // do not reset the thumbnail
|
|
|
+ message.thumbnail = list[i].thumbnail;
|
|
|
}
|
|
|
+ list[i] = message;
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
- return false;
|
|
|
}
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- public bindTemporaryToMessageId(receiver: threema.Receiver, temporaryId: string, messageId: string): boolean {
|
|
|
- const list = this.getList(receiver);
|
|
|
- for (const item of list) {
|
|
|
- if (item.temporaryId === temporaryId) {
|
|
|
- if (item.id !== undefined) {
|
|
|
- // do not bind to a new message id
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // reset temporary id
|
|
|
- item.temporaryId = null;
|
|
|
-
|
|
|
- // assign to "real" message id
|
|
|
- item.id = messageId;
|
|
|
- return true;
|
|
|
+ /**
|
|
|
+ * Update a thumbnail of a message, if a message was found the method will return true
|
|
|
+ */
|
|
|
+ public setThumbnail(receiver: threema.Receiver, messageId: string, thumbnailImage: string): boolean {
|
|
|
+ const list = this.getList(receiver);
|
|
|
+ for (const message of list) {
|
|
|
+ if (message.id === messageId) {
|
|
|
+ if (message.thumbnail === undefined) {
|
|
|
+ message.thumbnail = {img: thumbnailImage} as threema.Thumbnail;
|
|
|
+ } else {
|
|
|
+ message.thumbnail.img = thumbnailImage;
|
|
|
}
|
|
|
+ return true;
|
|
|
}
|
|
|
- return false;
|
|
|
}
|
|
|
|
|
|
- public notify(receiver: threema.Receiver, $scope: ng.IScope) {
|
|
|
- $scope.$broadcast('threema.receiver.' + receiver.type + '.' + receiver.id + '.messages',
|
|
|
- this.getList(receiver), this.hasMore(receiver));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * register a message change notify on the given scope
|
|
|
- * return the CURRENT list of loaded messages
|
|
|
- */
|
|
|
- public register(receiver: threema.Receiver, $scope: ng.IScope, callback: any): threema.Message[] {
|
|
|
- $scope.$on('threema.receiver.' + receiver.type + '.' + receiver.id + '.messages', callback);
|
|
|
- return this.getList(receiver);
|
|
|
- }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- public updateFirstUnreadMessage(receiver: threema.Receiver): void {
|
|
|
- const receiverMessages = this.getReceiverMessages(receiver);
|
|
|
- if (receiverMessages !== undefined && receiverMessages.list.length > 0) {
|
|
|
- // remove unread
|
|
|
- let removedElements = 0;
|
|
|
- let firstUnreadMessageIndex;
|
|
|
- receiverMessages.list = receiverMessages.list.filter((message: threema.Message, index: number) => {
|
|
|
- if (message.type === 'status'
|
|
|
- && message.statusType === 'firstUnreadMessage') {
|
|
|
- removedElements++;
|
|
|
- return false;
|
|
|
- } else if (firstUnreadMessageIndex === undefined
|
|
|
- && !message.isOutbox
|
|
|
- && message.unread) {
|
|
|
- firstUnreadMessageIndex = index;
|
|
|
- }
|
|
|
- return true;
|
|
|
- });
|
|
|
-
|
|
|
- if (firstUnreadMessageIndex !== undefined) {
|
|
|
- firstUnreadMessageIndex -= removedElements;
|
|
|
- receiverMessages.list.splice(firstUnreadMessageIndex, 0 , {
|
|
|
- type: 'status',
|
|
|
- isStatus: true,
|
|
|
- statusType: 'firstUnreadMessage',
|
|
|
- } as threema.Message);
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Remove a message.
|
|
|
+ *
|
|
|
+ * Return a boolean indicating whether the message was found and
|
|
|
+ * removed, or not.
|
|
|
+ */
|
|
|
+ public remove(receiver: threema.Receiver, messageId: string): boolean {
|
|
|
+ const list = this.getList(receiver);
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
+ if (list[i].id === messageId) {
|
|
|
+ list.splice(i, 1);
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
+ return false;
|
|
|
}
|
|
|
-
|
|
|
/**
|
|
|
- * Converters transform a message or conversation.
|
|
|
+ * Remove a message.
|
|
|
+ *
|
|
|
+ * Return a boolean indicating whether the message was found and
|
|
|
+ * removed, or not.
|
|
|
*/
|
|
|
- class Converter {
|
|
|
- public static unicodeToEmoji(message: threema.Message) {
|
|
|
- if (message.type === 'text') {
|
|
|
- message.body = emojione.toShort(message.body);
|
|
|
+ public removeTemporary(receiver: threema.Receiver, temporaryMessageId: string): boolean {
|
|
|
+ const list = this.getList(receiver);
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
+ if (list[i].temporaryId === temporaryMessageId) {
|
|
|
+ list.splice(i, 1);
|
|
|
+ return true;
|
|
|
}
|
|
|
- return message;
|
|
|
}
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public bindTemporaryToMessageId(receiver: threema.Receiver, temporaryId: string, messageId: string): boolean {
|
|
|
+ const list = this.getList(receiver);
|
|
|
+ for (const item of list) {
|
|
|
+ if (item.temporaryId === temporaryId) {
|
|
|
+ if (item.id !== undefined) {
|
|
|
+ // do not bind to a new message id
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- * Retrieve the receiver corresponding to this conversation and set the
|
|
|
- * `receiver` attribute.
|
|
|
- */
|
|
|
- public static addReceiverToConversation(receivers: Receivers) {
|
|
|
- return (conversation: threema.Conversation): threema.Conversation => {
|
|
|
- conversation.receiver = receivers.getData({
|
|
|
- type: conversation.type,
|
|
|
- id: conversation.id,
|
|
|
- } as threema.Receiver);
|
|
|
+ // reset temporary id
|
|
|
+ item.temporaryId = null;
|
|
|
|
|
|
- return conversation;
|
|
|
- };
|
|
|
+ // assign to "real" message id
|
|
|
+ item.id = messageId;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
}
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- class Filters {
|
|
|
- public static hasData(receivers) {
|
|
|
- return (obj) => $filter('hasData')(obj, receivers);
|
|
|
- }
|
|
|
+ public notify(receiver: threema.Receiver, $scope: ng.IScope) {
|
|
|
+ $scope.$broadcast('threema.receiver.' + receiver.type + '.' + receiver.id + '.messages',
|
|
|
+ this.getList(receiver), this.hasMore(receiver));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * register a message change notify on the given scope
|
|
|
+ * return the CURRENT list of loaded messages
|
|
|
+ */
|
|
|
+ public register(receiver: threema.Receiver, $scope: ng.IScope, callback: any): threema.Message[] {
|
|
|
+ $scope.$on('threema.receiver.' + receiver.type + '.' + receiver.id + '.messages', callback);
|
|
|
+ return this.getList(receiver);
|
|
|
+ }
|
|
|
+
|
|
|
+ public updateFirstUnreadMessage(receiver: threema.Receiver): void {
|
|
|
+ const receiverMessages = this.getReceiverMessages(receiver);
|
|
|
+ if (receiverMessages !== undefined && receiverMessages.list.length > 0) {
|
|
|
+ // remove unread
|
|
|
+ let removedElements = 0;
|
|
|
+ let firstUnreadMessageIndex;
|
|
|
+ receiverMessages.list = receiverMessages.list.filter((message: threema.Message, index: number) => {
|
|
|
+ if (message.type === 'status'
|
|
|
+ && message.statusType === 'firstUnreadMessage') {
|
|
|
+ removedElements++;
|
|
|
+ return false;
|
|
|
+ } else if (firstUnreadMessageIndex === undefined
|
|
|
+ && !message.isOutbox
|
|
|
+ && message.unread) {
|
|
|
+ firstUnreadMessageIndex = index;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ });
|
|
|
|
|
|
- public static hasContact(contacts) {
|
|
|
- return (obj) => $filter('hasContact')(obj, contacts);
|
|
|
+ if (firstUnreadMessageIndex !== undefined) {
|
|
|
+ firstUnreadMessageIndex -= removedElements;
|
|
|
+ receiverMessages.list.splice(firstUnreadMessageIndex, 0 , {
|
|
|
+ type: 'status',
|
|
|
+ isStatus: true,
|
|
|
+ statusType: 'firstUnreadMessage',
|
|
|
+ } as threema.Message);
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- public static isValidMessage(contacts) {
|
|
|
- return (obj) => $filter('isValidMessage')(obj, contacts);
|
|
|
+/**
|
|
|
+ * Converters transform a message or conversation.
|
|
|
+ */
|
|
|
+class Converter {
|
|
|
+ public static unicodeToEmoji(message: threema.Message) {
|
|
|
+ if (message.type === 'text') {
|
|
|
+ message.body = emojione.toShort(message.body);
|
|
|
}
|
|
|
+ return message;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * This class manages the typing flags for receivers.
|
|
|
- *
|
|
|
- * Internally values are stored in a hash set for efficient lookup.
|
|
|
+ * Retrieve the receiver corresponding to this conversation and set the
|
|
|
+ * `receiver` attribute.
|
|
|
*/
|
|
|
- class Typing implements threema.Container.Typing {
|
|
|
- private set = new StringHashSet();
|
|
|
+ public static addReceiverToConversation(receivers: Receivers) {
|
|
|
+ return (conversation: threema.Conversation): threema.Conversation => {
|
|
|
+ conversation.receiver = receivers.getData({
|
|
|
+ type: conversation.type,
|
|
|
+ id: conversation.id,
|
|
|
+ } as threema.Receiver);
|
|
|
+
|
|
|
+ return conversation;
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- private getReceiverUid(receiver: threema.ContactReceiver): string {
|
|
|
- return receiver.type + '-' + receiver.id;
|
|
|
- }
|
|
|
+/**
|
|
|
+ * This class manages the typing flags for receivers.
|
|
|
+ *
|
|
|
+ * Internally values are stored in a hash set for efficient lookup.
|
|
|
+ */
|
|
|
+class Typing implements threema.Container.Typing {
|
|
|
+ private set = new StringHashSet();
|
|
|
|
|
|
- public setTyping(receiver: threema.ContactReceiver): void {
|
|
|
- this.set.add(this.getReceiverUid(receiver));
|
|
|
- }
|
|
|
+ private getReceiverUid(receiver: threema.ContactReceiver): string {
|
|
|
+ return receiver.type + '-' + receiver.id;
|
|
|
+ }
|
|
|
|
|
|
- public unsetTyping(receiver: threema.ContactReceiver): void {
|
|
|
- this.set.remove(this.getReceiverUid(receiver));
|
|
|
- }
|
|
|
+ public setTyping(receiver: threema.ContactReceiver): void {
|
|
|
+ this.set.add(this.getReceiverUid(receiver));
|
|
|
+ }
|
|
|
|
|
|
- public isTyping(receiver: threema.ContactReceiver): boolean {
|
|
|
- return this.set.contains(this.getReceiverUid(receiver));
|
|
|
- }
|
|
|
+ public unsetTyping(receiver: threema.ContactReceiver): void {
|
|
|
+ this.set.remove(this.getReceiverUid(receiver));
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Holds message drafts and quotes
|
|
|
- */
|
|
|
- class Drafts implements threema.Container.Drafts {
|
|
|
+ public isTyping(receiver: threema.ContactReceiver): boolean {
|
|
|
+ return this.set.contains(this.getReceiverUid(receiver));
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- private quotes = new Map<string, threema.Quote>();
|
|
|
+/**
|
|
|
+ * Holds message drafts and quotes
|
|
|
+ */
|
|
|
+class Drafts implements threema.Container.Drafts {
|
|
|
|
|
|
- // Use to implement draft texts!
|
|
|
- private texts = new Map<string, string>();
|
|
|
+ private quotes = new Map<string, threema.Quote>();
|
|
|
|
|
|
- private getReceiverUid(receiver: threema.Receiver): string {
|
|
|
- // do not use receiver.type => can be null
|
|
|
- return receiver.id;
|
|
|
- }
|
|
|
+ // Use to implement draft texts!
|
|
|
+ private texts = new Map<string, string>();
|
|
|
|
|
|
- public setQuote(receiver: threema.Receiver, quote: threema.Quote): void {
|
|
|
- this.quotes.set(this.getReceiverUid(receiver), quote);
|
|
|
- }
|
|
|
+ private getReceiverUid(receiver: threema.Receiver): string {
|
|
|
+ // do not use receiver.type => can be null
|
|
|
+ return receiver.id;
|
|
|
+ }
|
|
|
|
|
|
- public removeQuote(receiver: threema.Receiver): void {
|
|
|
- this.quotes.delete(this.getReceiverUid(receiver));
|
|
|
- }
|
|
|
+ public setQuote(receiver: threema.Receiver, quote: threema.Quote): void {
|
|
|
+ this.quotes.set(this.getReceiverUid(receiver), quote);
|
|
|
+ }
|
|
|
|
|
|
- public getQuote(receiver: threema.Receiver): threema.Quote {
|
|
|
- return this.quotes.get(this.getReceiverUid(receiver));
|
|
|
- }
|
|
|
+ public removeQuote(receiver: threema.Receiver): void {
|
|
|
+ this.quotes.delete(this.getReceiverUid(receiver));
|
|
|
+ }
|
|
|
|
|
|
- public setText(receiver: threema.Receiver, draftMessage: string): void {
|
|
|
- this.texts.set(this.getReceiverUid(receiver), draftMessage);
|
|
|
- }
|
|
|
+ public getQuote(receiver: threema.Receiver): threema.Quote {
|
|
|
+ return this.quotes.get(this.getReceiverUid(receiver));
|
|
|
+ }
|
|
|
|
|
|
- public removeText(receiver: threema.Receiver): void {
|
|
|
- this.texts.delete(this.getReceiverUid(receiver));
|
|
|
- }
|
|
|
+ public setText(receiver: threema.Receiver, draftMessage: string): void {
|
|
|
+ this.texts.set(this.getReceiverUid(receiver), draftMessage);
|
|
|
+ }
|
|
|
|
|
|
- public getText(receiver: threema.Receiver): string {
|
|
|
- return this.texts.get(this.getReceiverUid(receiver));
|
|
|
- }
|
|
|
+ public removeText(receiver: threema.Receiver): void {
|
|
|
+ this.texts.delete(this.getReceiverUid(receiver));
|
|
|
+ }
|
|
|
+
|
|
|
+ public getText(receiver: threema.Receiver): string {
|
|
|
+ return this.texts.get(this.getReceiverUid(receiver));
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+angular.module('3ema.container', [])
|
|
|
+.factory('Container', ['$filter', '$log', 'ReceiverService',
|
|
|
+ function($filter, $log, receiverService: ReceiverService) {
|
|
|
+ class Filters {
|
|
|
+ public static hasData(receivers) {
|
|
|
+ return (obj) => $filter('hasData')(obj, receivers);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static hasContact(contacts) {
|
|
|
+ return (obj) => $filter('hasContact')(obj, contacts);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static isValidMessage(contacts) {
|
|
|
+ return (obj) => $filter('isValidMessage')(obj, contacts);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- return {
|
|
|
- Converter: Converter as threema.Container.Converter,
|
|
|
- Filters: Filters as threema.Container.Filters,
|
|
|
- createReceivers: () => new Receivers(),
|
|
|
- createConversations: () => new Conversations(),
|
|
|
- createMessages: () => new Messages(),
|
|
|
- createTyping: () => new Typing(),
|
|
|
- createDrafts: () => new Drafts(),
|
|
|
- } as threema.Container.Factory;
|
|
|
-}]);
|
|
|
+ return {
|
|
|
+ Converter: Converter as threema.Container.Converter,
|
|
|
+ Filters: Filters as threema.Container.Filters,
|
|
|
+ createReceivers: () => new Receivers(),
|
|
|
+ createConversations: () => new Conversations(receiverService),
|
|
|
+ createMessages: () => new Messages($log),
|
|
|
+ createTyping: () => new Typing(),
|
|
|
+ createDrafts: () => new Drafts(),
|
|
|
+ } as threema.Container.Factory;
|
|
|
+ },
|
|
|
+]);
|