move storage.js to ts

This commit is contained in:
Audric Ackermann 2022-03-23 15:57:11 +11:00
parent 747bcb766c
commit 6bd835dfc3
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
27 changed files with 232 additions and 403 deletions

View File

@ -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',
},

View File

@ -32,7 +32,6 @@
-->
<script type="text/javascript" src="js/components.js"></script>
<script type="text/javascript" src="js/storage.js"></script>
<script type="text/javascript" src="js/libtextsecure.js"></script>
<script type="text/javascript" src="js/views/react_wrapper_view.js"></script>

View File

@ -329,8 +329,7 @@
initialLoadComplete,
});
}
});
};
Whisper.events.on('openInbox', () => {
appView.openInbox({

View File

@ -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);
})();

View File

@ -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;
})();

View File

@ -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);
})();

View File

@ -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');
},
};
})();

View File

@ -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<Props, State> {
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, ''))) {

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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(() => {

View File

@ -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);
}
}

View File

@ -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(

View File

@ -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<ConversationAttributes> {
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<number>;
const receiptMessage = new ReadReceiptMessage({
timestamp: Date.now(),
@ -1658,7 +1659,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
// 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()
) {

View File

@ -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<MessageAttributes> {
}
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');

View File

@ -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';

View File

@ -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);

View File

@ -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;
}

View File

@ -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<HexKeyPair | undefined> {
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);
}

View File

@ -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(

View File

@ -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<Ui
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('decryptAttachmentBuffer', encryptingKey, bufferIn);
};

View File

@ -1,6 +1,5 @@
import { getConversationController } from '../session/conversations';
import { getSodium } from '../session/crypto';
import { UserUtils } from '../session/utils';
import { fromArrayBufferToBase64, fromHex, toHex } from '../session/utils/String';
import { getOurPubKeyStrFromCache } from '../session/utils/User';
import { trigger } from '../shims/events';
@ -9,6 +8,14 @@ import { actions as userActions } from '../state/ducks/user';
import { mn_decode, mn_encode } from '../session/crypto/mnemonic';
import { ConversationTypeEnum } from '../models/conversation';
import { SettingsKey } from '../data/settings-key';
import {
saveRecoveryPhrase,
setLastProfileUpdateTimestamp,
setLocalPubKey,
setSignInByLinking,
Storage,
} from './storage';
import { Registration } from './registration';
/**
* Might throw
@ -76,9 +83,9 @@ export async function signInByLinkingDevice(mnemonic: string, mnemonicLanguage:
}
const identityKeyPair = await generateKeypair(mnemonic, mnemonicLanguage);
UserUtils.setSignInByLinking(true);
await setSignInByLinking(true);
await createAccount(identityKeyPair);
UserUtils.saveRecoveryPhrase(mnemonic);
await saveRecoveryPhrase(mnemonic);
const pubKeyString = toHex(identityKeyPair.pubKey);
// await for the first configuration message to come in.
@ -109,8 +116,8 @@ export async function registerSingleDevice(
const identityKeyPair = await generateKeypair(generatedMnemonic, mnemonicLanguage);
await createAccount(identityKeyPair);
UserUtils.saveRecoveryPhrase(generatedMnemonic);
await UserUtils.setLastProfileUpdateTimestamp(Date.now());
await saveRecoveryPhrase(generatedMnemonic);
await setLastProfileUpdateTimestamp(Date.now());
const pubKeyString = toHex(identityKeyPair.pubKey);
await registrationDone(pubKeyString, profileName);
@ -131,39 +138,39 @@ async function createAccount(identityKeyPair: any) {
password = password.substring(0, password.length - 2);
await Promise.all([
window.textsecure.storage.remove('identityKey'),
window.textsecure.storage.remove('signaling_key'),
window.textsecure.storage.remove('password'),
window.textsecure.storage.remove('registrationId'),
window.textsecure.storage.remove('number_id'),
window.textsecure.storage.remove('device_name'),
window.textsecure.storage.remove('userAgent'),
window.textsecure.storage.remove(SettingsKey.settingsReadReceipt),
window.textsecure.storage.remove(SettingsKey.settingsTypingIndicator),
window.textsecure.storage.remove('regionCode'),
window.textsecure.storage.remove('local_attachment_encrypted_key'),
Storage.remove('identityKey'),
Storage.remove('signaling_key'),
Storage.remove('password'),
Storage.remove('registrationId'),
Storage.remove('number_id'),
Storage.remove('device_name'),
Storage.remove('userAgent'),
Storage.remove(SettingsKey.settingsReadReceipt),
Storage.remove(SettingsKey.settingsTypingIndicator),
Storage.remove('regionCode'),
Storage.remove('local_attachment_encrypted_key'),
]);
// update our own identity key, which may have changed
// if we're relinking after a reinstall on the master device
const pubKeyString = toHex(identityKeyPair.pubKey);
await window.textsecure.storage.put('identityKey', identityKeyPair);
await window.textsecure.storage.put('password', password);
await Storage.put('identityKey', identityKeyPair);
await Storage.put('password', password);
// disable read-receipt by default
await window.textsecure.storage.put(SettingsKey.settingsReadReceipt, false);
await Storage.put(SettingsKey.settingsReadReceipt, false);
// Enable typing indicators by default
await window.textsecure.storage.put(SettingsKey.settingsTypingIndicator, false);
await Storage.put(SettingsKey.settingsTypingIndicator, false);
await window.textsecure.storage.user.setNumberAndDeviceId(pubKeyString, 1);
await setLocalPubKey(pubKeyString);
}
async function registrationDone(ourPubkey: string, displayName: string) {
window?.log?.info('registration done');
window.textsecure.storage.put('primaryDevicePubKey', ourPubkey);
await Storage.put('primaryDevicePubKey', ourPubkey);
// Ensure that we always have a conversation for ourself
const conversation = await getConversationController().getOrCreateAndWait(
@ -174,10 +181,10 @@ async function registrationDone(ourPubkey: string, displayName: string) {
await conversation.setIsApproved(true);
const user = {
ourNumber: getOurPubKeyStrFromCache(),
ourPrimary: window.textsecure.storage.get('primaryDevicePubKey'),
ourPrimary: ourPubkey,
};
window.inboxStore?.dispatch(userActions.userChanged(user));
window.Whisper.Registration.markDone();
await Registration.markDone();
window?.log?.info('dispatching registration event');
trigger('registration_done');
}

View File

@ -1,8 +1,10 @@
import _ from 'lodash';
import { getStatus } from '../notifications';
import { UserSetting } from '../notifications/getStatus';
import { isMacOS } from '../OS';
import { isAudioNotificationSupported } from '../types/Settings';
import { isWindowFocused } from './focusListener';
import { Storage } from './storage';
const SettingNames = {
COUNT: 'count',
@ -106,7 +108,7 @@ function update() {
}
const isAppFocused = isWindowFocused();
const isAudioNotificationEnabled = storage.get('audio-notification') || false;
const isAudioNotificationEnabled = (Storage.get('audio-notification') as boolean) || false;
const audioNotificationSupported = isAudioNotificationSupported();
// const isNotificationGroupingSupported = Settings.isNotificationGroupingSupported();
const numNotifications = currentNotifications.length;
@ -221,7 +223,7 @@ function update() {
// 10 new messages) assuming that `Notification::close` does its job.
}
function getUserSetting() {
return storage.get('notification-setting') || SettingNames.MESSAGE;
return (Storage.get('notification-setting') as UserSetting) || SettingNames.MESSAGE;
}
function onRemove() {
// window.log.info('Remove notification');

View File

@ -1,21 +1,23 @@
function markEverDone() {
storage.put('chromiumRegistrationDoneEver', '');
import { Storage } from './storage';
async function markEverDone() {
await Storage.put('chromiumRegistrationDoneEver', '');
}
function markDone() {
this.markEverDone();
storage.put('chromiumRegistrationDone', '');
async function markDone() {
await markEverDone();
await Storage.put('chromiumRegistrationDone', '');
}
function isDone() {
return storage.get('chromiumRegistrationDone') === '';
return Storage.get('chromiumRegistrationDone') === '';
}
function everDone() {
return (
storage.get('chromiumRegistrationDoneEver') === '' ||
storage.get('chromiumRegistrationDone') === ''
Storage.get('chromiumRegistrationDoneEver') === '' ||
Storage.get('chromiumRegistrationDone') === ''
);
}
function remove() {
storage.remove('chromiumRegistrationDone');
async function remove() {
await Storage.remove('chromiumRegistrationDone');
}
export const Registration = { markEverDone, markDone, isDone, everDone, remove };

138
ts/util/storage.ts Normal file
View File

@ -0,0 +1,138 @@
import * as Data from '../data/data';
let ready = false;
type ValueType = string | number | boolean;
type InsertedValueType = { id: string; value: ValueType };
let items: Record<string, InsertedValueType>;
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 };