diff --git a/Gruntfile.js b/Gruntfile.js index 4a20457c7..e769102ca 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -67,9 +67,6 @@ module.exports = grunt => { 'libtextsecure/errors.js', 'libtextsecure/libsignal-protocol.js', 'libtextsecure/crypto.js', - 'libtextsecure/storage.js', - 'libtextsecure/storage/user.js', - 'libtextsecure/helpers.js', ], dest: 'js/libtextsecure.js', }, diff --git a/background.html b/background.html index 5004ac77f..6a3ee28aa 100644 --- a/background.html +++ b/background.html @@ -32,7 +32,6 @@ --> - diff --git a/js/background.js b/js/background.js index 548649428..aa2f349bc 100644 --- a/js/background.js +++ b/js/background.js @@ -329,8 +329,7 @@ initialLoadComplete, }); } - }); - + }; Whisper.events.on('openInbox', () => { appView.openInbox({ diff --git a/js/storage.js b/js/storage.js deleted file mode 100644 index 017eee1b3..000000000 --- a/js/storage.js +++ /dev/null @@ -1,107 +0,0 @@ -/* eslint-disable more/no-then */ - -// eslint-disable-next-line func-names -(function() { - 'use strict'; - - window.Whisper = window.Whisper || {}; - - let ready = false; - let items; - let callbacks = []; - - reset(); - - async function put(key, value) { - if (value === undefined) { - throw new Error('Tried to store undefined'); - } - if (!ready) { - window.log.warn('Called storage.put before storage is ready. key:', key); - } - - const data = { id: key, value }; - - items[key] = data; - await window.Signal.Data.createOrUpdateItem(data); - } - - function get(key, defaultValue) { - if (!ready) { - window.log.warn('Called storage.get before storage is ready. key:', key); - } - - const item = items[key]; - if (!item) { - return defaultValue; - } - - return item.value; - } - - async function remove(key) { - if (!ready) { - window.log.warn('Called storage.get before storage is ready. key:', key); - } - - delete items[key]; - await window.Signal.Data.removeItemById(key); - } - - function onready(callback) { - if (ready) { - callback(); - } else { - callbacks.push(callback); - } - } - - function callListeners() { - if (ready) { - callbacks.forEach(callback => callback()); - callbacks = []; - } - } - - async function fetch() { - this.reset(); - const array = await window.Signal.Data.getAllItems(); - - for (let i = 0, max = array.length; i < max; i += 1) { - const item = array[i]; - const { id } = item; - items[id] = item; - } - - ready = true; - callListeners(); - } - - function reset() { - ready = false; - items = Object.create(null); - } - - const storage = { - fetch, - put, - get, - remove, - onready, - reset, - }; - - // Keep a reference to this storage system, since there are scenarios where - // we need to replace it with the legacy storage system for a while. - window.newStorage = storage; - - window.textsecure = window.textsecure || {}; - window.textsecure.storage = window.textsecure.storage || {}; - - window.installStorage = newStorage => { - window.storage = newStorage; - window.textsecure.storage.impl = newStorage; - }; - - window.installStorage(storage); -})(); diff --git a/libtextsecure/helpers.js b/libtextsecure/helpers.js deleted file mode 100644 index 87574d975..000000000 --- a/libtextsecure/helpers.js +++ /dev/null @@ -1,76 +0,0 @@ -/* global window, dcodeIO */ - -/* eslint-disable no-proto, no-restricted-syntax, guard-for-in */ - -window.textsecure = window.textsecure || {}; - -/** ******************************* - *** Type conversion utilities *** - ******************************** */ -// Strings/arrays -// TODO: Throw all this shit in favor of consistent types -// TODO: Namespace -const StaticByteBufferProto = new dcodeIO.ByteBuffer().__proto__; -const StaticArrayBufferProto = new ArrayBuffer().__proto__; -const StaticUint8ArrayProto = new Uint8Array().__proto__; -function getString(thing) { - if (thing === Object(thing)) { - if (thing.__proto__ === StaticUint8ArrayProto) { - return String.fromCharCode.apply(null, thing); - } - if (thing.__proto__ === StaticArrayBufferProto) { - return getString(new Uint8Array(thing)); - } - if (thing.__proto__ === StaticByteBufferProto) { - return thing.toString('binary'); - } - } - return thing; -} - -function getStringable(thing) { - return ( - typeof thing === 'string' || - typeof thing === 'number' || - typeof thing === 'boolean' || - (thing === Object(thing) && - (thing.__proto__ === StaticArrayBufferProto || - thing.__proto__ === StaticUint8ArrayProto || - thing.__proto__ === StaticByteBufferProto)) - ); -} - -// Number formatting utils -window.textsecure.utils = (() => { - const self = {}; - self.unencodeNumber = number => number.split('.'); - self.isNumberSane = number => number[0] === '+' && /^[0-9]+$/.test(number.substring(1)); - - /** ************************ - *** JSON'ing Utilities *** - ************************* */ - function ensureStringed(thing) { - if (getStringable(thing)) { - return getString(thing); - } else if (thing instanceof Array) { - const res = []; - for (let i = 0; i < thing.length; i += 1) { - res[i] = ensureStringed(thing[i]); - } - return res; - } else if (thing === Object(thing)) { - const res = {}; - for (const key in thing) { - res[key] = ensureStringed(thing[key]); - } - return res; - } else if (thing === null) { - return null; - } - throw new Error(`unsure of how to jsonify object of type ${typeof thing}`); - } - - self.jsonThing = thing => JSON.stringify(ensureStringed(thing)); - - return self; -})(); diff --git a/libtextsecure/storage.js b/libtextsecure/storage.js deleted file mode 100644 index 300f7e913..000000000 --- a/libtextsecure/storage.js +++ /dev/null @@ -1,40 +0,0 @@ -/* global window, textsecure, localStorage */ - -// eslint-disable-next-line func-names -(function() { - /** ********************************************** - *** Utilities to store data in local storage *** - *********************************************** */ - window.textsecure = window.textsecure || {}; - window.textsecure.storage = window.textsecure.storage || {}; - - // Overrideable storage implementation - window.textsecure.storage.impl = window.textsecure.storage.impl || { - /** *************************** - *** Base Storage Routines *** - **************************** */ - put(key, value) { - if (value === undefined) { - throw new Error('Tried to store undefined'); - } - localStorage.setItem(`${key}`, textsecure.utils.jsonThing(value)); - }, - - get(key, defaultValue) { - const value = localStorage.getItem(`${key}`); - if (value === null) { - return defaultValue; - } - return JSON.parse(value); - }, - - remove(key) { - localStorage.removeItem(`${key}`); - }, - }; - - window.textsecure.storage.put = (key, value) => textsecure.storage.impl.put(key, value); - window.textsecure.storage.get = (key, defaultValue) => - textsecure.storage.impl.get(key, defaultValue); - window.textsecure.storage.remove = key => textsecure.storage.impl.remove(key); -})(); diff --git a/libtextsecure/storage/user.js b/libtextsecure/storage/user.js deleted file mode 100644 index b4b771e88..000000000 --- a/libtextsecure/storage/user.js +++ /dev/null @@ -1,70 +0,0 @@ -/* global textsecure, window */ - -// eslint-disable-next-line func-names -(function() { - /** ******************************************* - *** Utilities to store data about the user *** - ********************************************* */ - window.textsecure = window.textsecure || {}; - window.textsecure.storage = window.textsecure.storage || {}; - - window.textsecure.storage.user = { - setNumberAndDeviceId(number, deviceId, deviceName) { - textsecure.storage.put('number_id', `${number}.${deviceId}`); - if (deviceName) { - textsecure.storage.put('device_name', deviceName); - } - }, - - getNumber() { - const numberId = textsecure.storage.get('number_id'); - if (numberId === undefined) { - return undefined; - } - return textsecure.utils.unencodeNumber(numberId)[0]; - }, - - isSignInByLinking() { - const isSignInByLinking = textsecure.storage.get('is_sign_in_by_linking'); - if (isSignInByLinking === undefined) { - return false; - } - return isSignInByLinking; - }, - - setSignInByLinking(isLinking) { - textsecure.storage.put('is_sign_in_by_linking', isLinking); - }, - - isSignWithRecoveryPhrase() { - const isRecoveryPhraseUsed = textsecure.storage.get('is_sign_in_recovery_phrase'); - if (isRecoveryPhraseUsed === undefined) { - return false; - } - return isRecoveryPhraseUsed; - }, - setSignWithRecoveryPhrase(isRecoveryPhraseUsed) { - textsecure.storage.put('is_sign_in_recovery_phrase', isRecoveryPhraseUsed); - }, - - getLastProfileUpdateTimestamp() { - return textsecure.storage.get('last_profile_update_timestamp'); - }, - - setLastProfileUpdateTimestamp(lastUpdateTimestamp) { - textsecure.storage.put('last_profile_update_timestamp', lastUpdateTimestamp); - }, - - getDeviceId() { - const numberId = textsecure.storage.get('number_id'); - if (numberId === undefined) { - return undefined; - } - return textsecure.utils.unencodeNumber(numberId)[1]; - }, - - getDeviceName() { - return textsecure.storage.get('device_name'); - }, - }; -})(); diff --git a/ts/components/conversation/SessionConversation.tsx b/ts/components/conversation/SessionConversation.tsx index cc02b2862..b3e993e73 100644 --- a/ts/components/conversation/SessionConversation.tsx +++ b/ts/components/conversation/SessionConversation.tsx @@ -20,7 +20,7 @@ import { SplitViewContainer } from '../SplitViewContainer'; import { LightboxGallery, MediaItemType } from '../lightbox/LightboxGallery'; import { getLastMessageInConversation, getPubkeysInPublicConversation } from '../../data/data'; import { getConversationController } from '../../session/conversations'; -import { ToastUtils, UserUtils } from '../../session/utils'; +import { ToastUtils } from '../../session/utils'; import { openConversationToSpecificMessage, quoteMessage, @@ -50,6 +50,7 @@ import { blobToArrayBuffer } from 'blob-util'; import { MAX_ATTACHMENT_FILESIZE_BYTES } from '../../session/constants'; import { ConversationMessageRequestButtons } from './ConversationRequestButtons'; import { ConversationRequestinfo } from './ConversationRequestInfo'; +import { getCurrentRecoveryPhrase } from '../../util/storage'; // tslint:disable: jsx-curly-spacing interface State { @@ -176,8 +177,7 @@ export class SessionConversation extends React.Component { await this.scrollToNow(); }; - // const recoveryPhrase = window.textsecure.storage.get('mnemonic'); - const recoveryPhrase = UserUtils.getCurrentRecoveryPhrase(); + const recoveryPhrase = getCurrentRecoveryPhrase() as string; // string replace to fix case where pasted text contains invis characters causing false negatives if (msg.body.replace(/\s/g, '').includes(recoveryPhrase.replace(/\s/g, ''))) { diff --git a/ts/components/dialog/EditProfileDialog.tsx b/ts/components/dialog/EditProfileDialog.tsx index b115a44db..4447e1020 100644 --- a/ts/components/dialog/EditProfileDialog.tsx +++ b/ts/components/dialog/EditProfileDialog.tsx @@ -21,6 +21,7 @@ import { MAX_USERNAME_LENGTH } from '../registration/RegistrationStages'; import { SessionWrapperModal } from '../SessionWrapperModal'; import { pickFileForAvatar } from '../../types/attachments/VisualAttachment'; import { sanitizeSessionUsername } from '../../session/utils/String'; +import { setLastProfileUpdateTimestamp } from '../../util/storage'; interface State { profileName: string; @@ -319,7 +320,7 @@ async function commitProfileEdits(newName: string, scaledAvatarUrl: string | nul }); // might be good to not trigger a sync if the name did not change await conversation.commit(); - UserUtils.setLastProfileUpdateTimestamp(Date.now()); + await setLastProfileUpdateTimestamp(Date.now()); await SyncUtils.forceSyncConfigurationNowIfNeeded(true); } diff --git a/ts/components/dialog/SessionSeedModal.tsx b/ts/components/dialog/SessionSeedModal.tsx index 08b8f4f2a..c24c66373 100644 --- a/ts/components/dialog/SessionSeedModal.tsx +++ b/ts/components/dialog/SessionSeedModal.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; -import { ToastUtils, UserUtils } from '../../session/utils'; +import { ToastUtils } from '../../session/utils'; import { PasswordUtil } from '../../util'; import { getPasswordHash } from '../../data/data'; import { QRCode } from 'react-qr-svg'; @@ -10,6 +10,7 @@ import { recoveryPhraseModal } from '../../state/ducks/modalDialog'; import { useDispatch } from 'react-redux'; import { SessionButton, SessionButtonColor } from '../basic/SessionButton'; import { SessionWrapperModal } from '../SessionWrapperModal'; +import { getCurrentRecoveryPhrase } from '../../util/storage'; interface PasswordProps { setPasswordValid: (val: boolean) => any; @@ -168,7 +169,7 @@ const SessionSeedModalInner = (props: ModalInnerProps) => { if (recoveryPhrase) { return false; } - const newRecoveryPhrase = UserUtils.getCurrentRecoveryPhrase(); + const newRecoveryPhrase = getCurrentRecoveryPhrase(); setRecoveryPhrase(newRecoveryPhrase); setLoadingSeed(false); diff --git a/ts/components/leftpane/LeftPaneSectionHeader.tsx b/ts/components/leftpane/LeftPaneSectionHeader.tsx index a2610e7f4..f75f2a227 100644 --- a/ts/components/leftpane/LeftPaneSectionHeader.tsx +++ b/ts/components/leftpane/LeftPaneSectionHeader.tsx @@ -7,9 +7,9 @@ import { recoveryPhraseModal } from '../../state/ducks/modalDialog'; import { Flex } from '../basic/Flex'; import { getFocusedSection, getOverlayMode } from '../../state/selectors/section'; import { SectionType, setOverlayMode } from '../../state/ducks/section'; -import { UserUtils } from '../../session/utils'; import { SessionButton, SessionButtonType } from '../basic/SessionButton'; import { SessionIcon, SessionIconButton } from '../icon'; +import { isSignWithRecoveryPhrase } from '../../util/storage'; const SectionTitle = styled.h1` padding: 0 var(--margins-sm); @@ -94,7 +94,7 @@ const BannerInner = () => { export const LeftPaneBanner = () => { const section = useSelector(getFocusedSection); - const isSignInWithRecoveryPhrase = UserUtils.isSignWithRecoveryPhrase(); + const isSignInWithRecoveryPhrase = isSignWithRecoveryPhrase(); if (section !== SectionType.Message || isSignInWithRecoveryPhrase) { return null; diff --git a/ts/components/registration/RegistrationStages.tsx b/ts/components/registration/RegistrationStages.tsx index 80b0f1b0f..4f1f8a63f 100644 --- a/ts/components/registration/RegistrationStages.tsx +++ b/ts/components/registration/RegistrationStages.tsx @@ -5,7 +5,7 @@ import { createOrUpdateItem, removeAll } from '../../data/data'; import { getSwarmPollingInstance } from '../../session/apis/snode_api'; import { getConversationController } from '../../session/conversations'; import { mn_decode } from '../../session/crypto/mnemonic'; -import { PromiseUtils, StringUtils, ToastUtils, UserUtils } from '../../session/utils'; +import { PromiseUtils, StringUtils, ToastUtils } from '../../session/utils'; import { TaskTimedOutError } from '../../session/utils/Promise'; import { trigger } from '../../shims/events'; import { @@ -15,14 +15,15 @@ import { signInByLinkingDevice, } from '../../util/accountManager'; import { fromHex } from '../../session/utils/String'; +import { setSignInByLinking, setSignWithRecoveryPhrase, Storage } from '../../util/storage'; export const MAX_USERNAME_LENGTH = 26; // tslint:disable: use-simple-attributes export async function resetRegistration() { await removeAll(); - await window.storage.reset(); - await window.storage.fetch(); + Storage.reset(); + await Storage.fetch(); getConversationController().reset(); await getConversationController().load(); } @@ -64,7 +65,7 @@ export async function signUp(signUpDetails: { value: true, timestamp: Date.now(), }); - UserUtils.setSignWithRecoveryPhrase(false); + setSignWithRecoveryPhrase(false); trigger('openInbox'); } catch (e) { await resetRegistration(); @@ -95,7 +96,7 @@ export async function signInWithRecovery(signInDetails: { await resetRegistration(); await registerSingleDevice(userRecoveryPhrase, 'english', trimName); - UserUtils.setSignWithRecoveryPhrase(true); + setSignWithRecoveryPhrase(true); trigger('openInbox'); } catch (e) { @@ -120,10 +121,10 @@ export async function signInWithLinking(signInDetails: { userRecoveryPhrase: str await getSwarmPollingInstance().start(); await PromiseUtils.waitForTask(done => { - window.Whisper.events.on('configurationMessageReceived', (displayName: string) => { + window.Whisper.events.on('configurationMessageReceived', async (displayName: string) => { window.Whisper.events.off('configurationMessageReceived'); - UserUtils.setSignInByLinking(false); - UserUtils.setSignWithRecoveryPhrase(true); + await setSignInByLinking(false); + await setSignWithRecoveryPhrase(true); done(displayName); displayNameFromNetwork = displayName; diff --git a/ts/components/registration/SessionRegistrationView.tsx b/ts/components/registration/SessionRegistrationView.tsx index a830c5244..41919d3ef 100644 --- a/ts/components/registration/SessionRegistrationView.tsx +++ b/ts/components/registration/SessionRegistrationView.tsx @@ -4,9 +4,9 @@ import { AccentText } from './AccentText'; import { RegistrationStages } from './RegistrationStages'; import { SessionIcon } from '../icon'; import { SessionToastContainer } from '../SessionToastContainer'; -import { setSignInByLinking } from '../../session/utils/User'; import { SessionTheme } from '../../state/ducks/SessionTheme'; import { Flex } from '../basic/Flex'; +import { setSignInByLinking } from '../../util/storage'; export const SessionRegistrationView = () => { useEffect(() => { diff --git a/ts/data/data.ts b/ts/data/data.ts index 733b1c64a..f8ded917c 100644 --- a/ts/data/data.ts +++ b/ts/data/data.ts @@ -19,6 +19,7 @@ import { PubKey } from '../session/types'; import { fromArrayBufferToBase64, fromBase64ToArrayBuffer } from '../session/utils/String'; import { ReduxConversationType } from '../state/ducks/conversations'; import { ExpirationTimerOptions } from '../util/expiringMessages'; +import { Storage } from '../util/storage'; import { channels } from './channels'; import { channelsToMake as channelstoMakeOpenGroupV2 } from './opengroups'; @@ -469,7 +470,7 @@ export async function generateAttachmentKeyIfEmpty() { value: encryptingKey, }); // be sure to write the new key to the cache. so we can access it straight away - window.textsecure.storage.put('local_attachment_encrypted_key', encryptingKey); + await Storage.put('local_attachment_encrypted_key', encryptingKey); } } diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index 3f3c49cf8..2e035b4df 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -45,6 +45,7 @@ import { perfEnd, perfStart } from '../session/utils/Performance'; import { processNewAttachment } from '../types/MessageAttachment'; import { urlToBlob } from '../types/attachments/VisualAttachment'; import { MIME } from '../types'; +import { setLastProfileUpdateTimestamp } from '../util/storage'; export const getCompleteUrlForV2ConvoId = async (convoId: string) => { if (convoId.match(openGroupV2ConversationIdRegex)) { @@ -462,7 +463,7 @@ export async function uploadOurAvatar(newAvatarDecrypted?: ArrayBuffer) { await createOrUpdateItem({ id: lastAvatarUploadTimestamp, value: newTimestampReupload }); if (newAvatarDecrypted) { - UserUtils.setLastProfileUpdateTimestamp(Date.now()); + await setLastProfileUpdateTimestamp(Date.now()); await SyncUtils.forceSyncConfigurationNowIfNeeded(true); } else { window.log.info( diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index fd94a6776..5e2b58bca 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -64,6 +64,7 @@ import { import { getOurPubKeyStrFromCache } from '../session/utils/User'; import { MessageRequestResponse } from '../session/messages/outgoing/controlMessage/MessageRequestResponse'; import { Notifications } from '../util/notifications'; +import { Storage } from '../util/storage'; export enum ConversationTypeEnum { GROUP = 'group', @@ -1146,10 +1147,10 @@ export class ConversationModel extends Backbone.Model { if (this.isPrivate() && read.length && options.sendReadReceipts) { window?.log?.info( `Sending ${read.length} read receipts?`, - window.storage.get(SettingsKey.settingsReadReceipt) || false + Storage.get(SettingsKey.settingsReadReceipt) || false ); const dontSendReceipt = this.isBlocked() || this.isIncomingRequest(); - if (window.storage.get(SettingsKey.settingsReadReceipt) && !dontSendReceipt) { + if (Storage.get(SettingsKey.settingsReadReceipt) && !dontSendReceipt) { const timestamps = _.map(read, 'timestamp').filter(t => !!t) as Array; const receiptMessage = new ReadReceiptMessage({ timestamp: Date.now(), @@ -1658,7 +1659,7 @@ export class ConversationModel extends Backbone.Model { // for typing to happen, this must be a private unblocked active convo, and the settings to be on if ( !this.isActive() || - !window.storage.get(SettingsKey.settingsTypingIndicator) || + !Storage.get(SettingsKey.settingsTypingIndicator) || this.isBlocked() || !this.isPrivate() ) { diff --git a/ts/models/message.ts b/ts/models/message.ts index 9148c15eb..4f066ef2a 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -62,6 +62,7 @@ import { } from '../types/MessageAttachment'; import { ExpirationTimerOptions } from '../util/expiringMessages'; import { Notifications } from '../util/notifications'; +import { Storage } from '../util/storage'; // tslint:disable: cyclomatic-complexity /** @@ -428,7 +429,7 @@ export class MessageModel extends Backbone.Model { } const readBy = this.get('read_by') || []; - if (window.storage.get(SettingsKey.settingsReadReceipt) && readBy.length > 0) { + if (Storage.get(SettingsKey.settingsReadReceipt) && readBy.length > 0) { return 'read'; } const sent = this.get('sent'); diff --git a/ts/notifications/getStatus.ts b/ts/notifications/getStatus.ts index c837b132a..379e95e59 100644 --- a/ts/notifications/getStatus.ts +++ b/ts/notifications/getStatus.ts @@ -14,7 +14,7 @@ interface Status { type: Type; } -type UserSetting = 'off' | 'count' | 'name' | 'message'; +export type UserSetting = 'off' | 'count' | 'name' | 'message'; type Type = 'ok' | 'disabled' | 'appIsFocused' | 'noNotifications' | 'userSetting'; diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 8c3801673..e76ccf46d 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -17,13 +17,14 @@ import { handleNewClosedGroup } from './closedGroups'; import { updateProfileOneAtATime } from './dataMessage'; import { EnvelopePlus } from './types'; import { ConversationInteraction } from '../interactions'; +import { getLastProfileUpdateTimestamp, setLastProfileUpdateTimestamp } from '../util/storage'; async function handleOurProfileUpdate( sentAt: number | Long, configMessage: SignalService.ConfigurationMessage, ourPubkey: string ) { - const latestProfileUpdateTimestamp = UserUtils.getLastProfileUpdateTimestamp(); + const latestProfileUpdateTimestamp = getLastProfileUpdateTimestamp(); if (!latestProfileUpdateTimestamp || sentAt > latestProfileUpdateTimestamp) { window?.log?.info( `Handling our profileUdpate ourLastUpdate:${latestProfileUpdateTimestamp}, envelope sent at: ${sentAt}` @@ -41,7 +42,7 @@ async function handleOurProfileUpdate( profilePicture, }; await updateProfileOneAtATime(ourConversation, lokiProfile, profileKey); - UserUtils.setLastProfileUpdateTimestamp(_.toNumber(sentAt)); + await setLastProfileUpdateTimestamp(_.toNumber(sentAt)); // do not trigger a signin by linking if the display name is empty if (displayName) { trigger(configurationMessageReceived, displayName); diff --git a/ts/receiver/contentMessage.ts b/ts/receiver/contentMessage.ts index 90a8adb71..7a678ba86 100644 --- a/ts/receiver/contentMessage.ts +++ b/ts/receiver/contentMessage.ts @@ -20,6 +20,7 @@ import { handleCallMessage } from './callMessage'; import { SettingsKey } from '../data/settings-key'; import { ConversationTypeEnum } from '../models/conversation'; import { ReadReceipts } from '../util/readReceipts'; +import { Storage } from '../util/storage'; export async function handleSwarmContentMessage(envelope: EnvelopePlus, messageHash: string) { try { @@ -491,7 +492,7 @@ async function handleTypingMessage( await removeFromCache(envelope); // We don't do anything with incoming typing messages if the setting is disabled - if (!window.storage.get(SettingsKey.settingsTypingIndicator)) { + if (!Storage.get(SettingsKey.settingsTypingIndicator)) { return; } diff --git a/ts/session/utils/User.ts b/ts/session/utils/User.ts index 02bfcd26c..f3c3266e7 100644 --- a/ts/session/utils/User.ts +++ b/ts/session/utils/User.ts @@ -6,6 +6,7 @@ import { PubKey } from '../types'; import { fromHexToArray, toHex } from './String'; import { getConversationController } from '../conversations'; import { LokiProfile } from '../../types/Message'; +import { getNumber, Storage } from '../../util/storage'; export type HexKeyPair = { pubKey: string; @@ -29,7 +30,7 @@ export function isUsFromCache(pubKey: string | PubKey | undefined): boolean { * Returns the public key of this current device as a STRING, or throws an error */ export function getOurPubKeyStrFromCache(): string { - const ourNumber = window.textsecure.storage.user.getNumber(); + const ourNumber = getNumber(); if (!ourNumber) { throw new Error('ourNumber is not set'); } @@ -78,27 +79,11 @@ export async function getUserED25519KeyPair(): Promise { return undefined; } -export function isSignInByLinking(): boolean { - return window.textsecure.storage.user.isSignInByLinking(); -} - -export function setSignInByLinking(isLinking: boolean) { - window.textsecure.storage.user.setSignInByLinking(isLinking); -} - -export function isSignWithRecoveryPhrase(): boolean { - return window.textsecure.storage.user.isSignWithRecoveryPhrase(); -} - -export function setSignWithRecoveryPhrase(isLinking: boolean) { - window.textsecure.storage.user.setSignWithRecoveryPhrase(isLinking); -} - export function getOurProfile(): LokiProfile | undefined { try { // Secondary devices have their profile stored // in their primary device's conversation - const ourNumber = window.storage.get('primaryDevicePubKey'); + const ourNumber = Storage.get('primaryDevicePubKey') as string; const ourConversation = getConversationController().get(ourNumber); const ourProfileKeyHex = ourConversation.get('profileKey'); const profileKeyAsBytes = ourProfileKeyHex ? fromHexToArray(ourProfileKeyHex) : null; @@ -115,19 +100,3 @@ export function getOurProfile(): LokiProfile | undefined { return undefined; } } - -export function getLastProfileUpdateTimestamp(): number | undefined { - return window.textsecure.storage.user.getLastProfileUpdateTimestamp(); -} - -export function setLastProfileUpdateTimestamp(lastUpdateTimestamp: number) { - return window.textsecure.storage.user.setLastProfileUpdateTimestamp(lastUpdateTimestamp); -} - -export function getCurrentRecoveryPhrase() { - return window.textsecure.storage.get('mnemonic'); -} - -export function saveRecoveryPhrase(mnemonic: string) { - return window.textsecure.storage.put('mnemonic', mnemonic); -} diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 4fbed7e74..f040e5b82 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -33,6 +33,7 @@ import { GenericReadableMessageSelectorProps } from '../../components/conversati import { LightBoxOptions } from '../../components/conversation/SessionConversation'; import { getConversationController } from '../../session/conversations'; import { UserUtils } from '../../session/utils'; +import { Storage } from '../../util/storage'; export const getConversations = (state: StateType): ConversationsStateType => state.conversations; @@ -129,7 +130,7 @@ export const isPublicGroupConversation = createSelector( export const getOurPrimaryConversation = createSelector( getConversations, (state: ConversationsStateType): ReduxConversationType => - state.conversationLookup[window.storage.get('primaryDevicePubKey')] + state.conversationLookup[Storage.get('primaryDevicePubKey') as string] ); const getMessagesOfSelectedConversation = createSelector( diff --git a/ts/types/Attachment.ts b/ts/types/Attachment.ts index a8263a623..59fe9f3d6 100644 --- a/ts/types/Attachment.ts +++ b/ts/types/Attachment.ts @@ -7,6 +7,7 @@ import { SignalService } from '../protobuf'; import { isImageTypeSupported, isVideoTypeSupported } from '../util/GoogleChrome'; import { fromHexToArray } from '../session/utils/String'; import { ATTACHMENT_DEFAULT_MAX_SIDE } from '../util/attachmentsUtil'; +import { Storage } from '../util/storage'; const MAX_WIDTH = 200; const MAX_HEIGHT = MAX_WIDTH; @@ -396,9 +397,8 @@ export const encryptAttachmentBuffer = async (bufferIn: ArrayBuffer) => { if (!isArrayBuffer(bufferIn)) { throw new TypeError("'bufferIn' must be an array buffer"); } - const encryptingKey = fromHexToArray( - window.textsecure.storage.get('local_attachment_encrypted_key') - ); + const key = Storage.get('local_attachment_encrypted_key') as string; + const encryptingKey = fromHexToArray(key); return window.callWorker('encryptAttachmentBuffer', encryptingKey, bufferIn); }; @@ -406,8 +406,7 @@ export const decryptAttachmentBuffer = async (bufferIn: ArrayBuffer): Promise; +let callbacks: Array<() => void> = []; + +reset(); + +async function put(key: string, value: ValueType) { + if (value === undefined) { + throw new Error('Tried to store undefined'); + } + if (!ready) { + window.log.warn('Called storage.put before storage is ready. key:', key); + } + + const data: InsertedValueType = { id: key, value }; + + items[key] = data; + await Data.createOrUpdateItem(data); +} + +function get(key: string, defaultValue?: ValueType) { + if (!ready) { + window.log.warn('Called storage.get before storage is ready. key:', key); + } + + const item = items[key]; + if (!item) { + return defaultValue; + } + + return item.value; +} + +async function remove(key: string) { + if (!ready) { + window.log.warn('Called storage.get before storage is ready. key:', key); + } + + // tslint:disable-next-line: no-dynamic-delete + delete items[key]; + await Data.removeItemById(key); +} + +function onready(callback: () => void) { + if (ready) { + callback(); + } else { + callbacks.push(callback); + } +} + +function callListeners() { + if (ready) { + callbacks.forEach(callback => { + callback(); + }); + callbacks = []; + } +} + +async function fetch() { + reset(); + const array = await Data.getAllItems(); + + // tslint:disable-next-line: one-variable-per-declaration + for (let i = 0, max = array.length; i < max; i += 1) { + const item = array[i]; + const { id } = item; + items[id] = item; + } + + ready = true; + callListeners(); +} + +function reset() { + ready = false; + items = Object.create(null); +} + +export async function setLocalPubKey(pubkey: string) { + await put('number_id', `${pubkey}.1`); +} + +export function getNumber() { + const numberId = get('number_id') as string | undefined; + if (numberId === undefined) { + return undefined; + } + return numberId.split('.')[0]; +} + +export function isSignInByLinking() { + const isByLinking = get('is_sign_in_by_linking'); + if (isByLinking === undefined) { + return false; + } + return isByLinking; +} + +export async function setSignInByLinking(isLinking: boolean) { + await put('is_sign_in_by_linking', isLinking); +} + +export function isSignWithRecoveryPhrase() { + const isRecoveryPhraseUsed = get('is_sign_in_recovery_phrase'); + if (isRecoveryPhraseUsed === undefined) { + return false; + } + return isRecoveryPhraseUsed; +} + +export async function setSignWithRecoveryPhrase(isRecoveryPhraseUsed: boolean) { + await put('is_sign_in_recovery_phrase', isRecoveryPhraseUsed); +} + +export function getLastProfileUpdateTimestamp() { + return get('last_profile_update_timestamp'); +} + +export async function setLastProfileUpdateTimestamp(lastUpdateTimestamp: number) { + await put('last_profile_update_timestamp', lastUpdateTimestamp); +} + +export function getCurrentRecoveryPhrase() { + return Storage.get('mnemonic') as string; +} + +export async function saveRecoveryPhrase(mnemonic: string) { + return Storage.put('mnemonic', mnemonic); +} + +export const Storage = { fetch, put, get, remove, onready, reset };