You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/session/sending/PendingMessageCache.ts

143 lines
3.9 KiB
TypeScript

5 years ago
import { createOrUpdateItem, getItemById } from '../../../js/modules/data';
import { RawMessage } from '../types/RawMessage';
import { ContentMessage } from '../messages/outgoing';
5 years ago
import { PubKey } from '../types';
5 years ago
import * as MessageUtils from '../utils';
5 years ago
// This is an abstraction for storing pending messages.
// Ideally we want to store pending messages in the database so that
// on next launch we can re-send the pending messages, but we don't want
// to constantly fetch pending messages from the database.
// Thus we have an intermediary cache which will store pending messagesin
// memory and sync its state with the database on modification (add or remove).
export class PendingMessageCache {
5 years ago
public readonly isReady: Promise<boolean>;
private cache: Array<any>;
constructor() {
5 years ago
// Load pending messages from the database
5 years ago
// You should await isReady on making a new PendingMessageCache
// if you'd like to have instant access to the cache
5 years ago
this.cache = ['bleep'];
this.isReady = new Promise(async resolve => {
await this.loadFromDB();
resolve(true);
});
5 years ago
}
5 years ago
public getAllPending(): Array<RawMessage> {
5 years ago
// Get all pending from cache, sorted with oldest first
5 years ago
return [...this.cache].sort((a, b) => a.timestamp - b.timestamp);
}
5 years ago
public getForDevice(device: PubKey): Array<RawMessage> {
const pending = this.cache.filter(m => m.device === device.key);
return pending.sort((a, b) => a.timestamp - b.timestamp);
}
public getDevices(): Array<PubKey> {
// Gets all unique devices with pending messages
const pubkeyStrings = [...new Set(this.cache.map(m => m.device))];
const pubkeys: Array<PubKey> = [];
pubkeyStrings.forEach(pubkey => {
if (PubKey.validate(pubkey)) {
pubkeys.push(new PubKey(pubkey));
}
});
return pubkeys;
5 years ago
}
5 years ago
public async add(
device: PubKey,
message: ContentMessage
): Promise<RawMessage> {
5 years ago
const rawMessage = MessageUtils.toRawMessage(device, message);
// Does it exist in cache already?
5 years ago
if (this.find(rawMessage)) {
5 years ago
return rawMessage;
}
this.cache.push(rawMessage);
5 years ago
await this.saveToDB();
5 years ago
return rawMessage;
}
5 years ago
public async remove(
message: RawMessage
): Promise<Array<RawMessage> | undefined> {
5 years ago
// Should only be called after message is processed
// Return if message doesn't exist in cache
if (!this.find(message)) {
return;
}
// Remove item from cache and sync with database
const updatedCache = this.cache.filter(
m => m.identifier !== message.identifier
);
this.cache = updatedCache;
5 years ago
await this.saveToDB();
5 years ago
return updatedCache;
}
5 years ago
public find(message: RawMessage): RawMessage | undefined {
// Find a message in the cache
return this.cache.find(
m => m.device === message.device && m.timestamp === message.timestamp
);
}
5 years ago
public async clear() {
// Clears the cache and syncs to DB
this.cache = [];
5 years ago
await this.saveToDB();
}
5 years ago
5 years ago
public async loadFromDB() {
5 years ago
const messages = await this.getFromStorage();
this.cache = messages;
}
5 years ago
private async getFromStorage(): Promise<Array<RawMessage>> {
5 years ago
const data = await getItemById('pendingMessages');
if (!data || !data.value) {
return [];
}
5 years ago
const barePending = JSON.parse(String(data.value));
5 years ago
const pending = barePending.map((message: any) => {
5 years ago
const { identifier, plainTextBuffer, timestamp, device, ttl, encryption } = message;
return {
identifier,
plainTextBuffer,
timestamp,
device,
ttl,
encryption,
} as RawMessage;
});
5 years ago
return pending as Array<RawMessage>;
5 years ago
}
5 years ago
private async saveToDB() {
5 years ago
// Only call when adding / removing from cache.
5 years ago
const encodedPendingMessages = JSON.stringify(this.cache) || '[]';
5 years ago
await createOrUpdateItem({
5 years ago
id: 'pendingMessages',
value: encodedPendingMessages,
});
5 years ago
}
5 years ago
}