import { Data } from '../data/data'; import { commitConversationAndRefreshWrapper } from '../models/conversation'; import { PubKey } from '../session/types'; import { Storage } from './storage'; const BLOCKED_NUMBERS_ID = 'blocked'; // tslint:disable-next-line: no-unnecessary-class export class BlockedNumberController { private static loaded: boolean = false; private static blockedNumbers: Set = new Set(); /** * Check if a device is blocked synchronously. * This will only check against the memory cache on if a device is blocked, it is recommended to pass in the primary device pub key. * * Make sure `load()` has been called before this function so that the correct blocked state is returned. * * @param number The device. */ public static isBlocked(device: string | PubKey): boolean { // This function is not `async` because the old `isBlocked` function in js was also not async. // To convert it means we'll have to re-wire all our UI components to work with async. const stringValue = device instanceof PubKey ? device.key : device.toLowerCase(); return this.blockedNumbers.has(stringValue); } /** * Block a user or group, by pubkey * * @param user The user to block. */ public static async block(user: string | PubKey): Promise { // The reason we add all linked device to block number set instead of checking if any device of a user is in the `isBlocked` function because // `isBlocked` is used synchronously in the code. To check if any device is blocked needs it to be async, which would mean all calls to `isBlocked` will also need to be async and so on // This is too much of a hassle at the moment as some UI code will have to be migrated to work with this async call. await this.load(); const toBlock = PubKey.cast(user); if (!this.blockedNumbers.has(toBlock.key)) { this.blockedNumbers.add(toBlock.key); await this.saveToDB(BLOCKED_NUMBERS_ID, this.blockedNumbers); await commitConversationAndRefreshWrapper(toBlock.key); } } /** * Unblock all these users. * This will only unblock the primary device of the user. * * @param user The user to unblock. */ public static async unblockAll(users: Array): Promise { await this.load(); let changes = false; users.forEach(user => { const toUnblock = PubKey.cast(user); if (this.blockedNumbers.has(toUnblock.key)) { this.blockedNumbers.delete(toUnblock.key); changes = true; } }); for (let index = 0; index < users.length; index++) { const user = users[index]; try { await commitConversationAndRefreshWrapper(user); } catch (e) { window.log.warn( 'failed to SessionUtilContact.insertContactFromDBIntoWrapperAndRefresh with: ', user ); } } if (changes) { await this.saveToDB(BLOCKED_NUMBERS_ID, this.blockedNumbers); } } public static async setBlocked(user: string | PubKey, blocked: boolean): Promise { if (blocked) { return BlockedNumberController.block(user); } return BlockedNumberController.unblockAll([PubKey.cast(user).key]); } public static getBlockedNumbers(): Array { return [...this.blockedNumbers]; } // ---- DB public static async load() { if (!this.loaded) { this.blockedNumbers = await this.getNumbersFromDB(BLOCKED_NUMBERS_ID); this.loaded = true; } } public static reset() { this.loaded = false; this.blockedNumbers = new Set(); } private static async getNumbersFromDB(id: string): Promise> { const data = await Data.getItemById(id); if (!data || !data.value) { return new Set(); } return new Set(data.value); } private static async saveToDB(id: string, numbers: Set): Promise { await Storage.put(id, [...numbers]); } }