Browse Source

Migrate session from IndexedDB to LocalStorage

master
Eduard Kuzmenko 4 years ago
parent
commit
f8069d3e85
  1. 3
      src/components/sidebarLeft/index.ts
  2. 8
      src/lib/appManagers/appStateManager.ts
  3. 6
      src/lib/cacheStorage.ts
  4. 206
      src/lib/localStorage.ts
  5. 47
      src/lib/mtproto/mtproto.worker.ts
  6. 4
      src/lib/mtproto/mtproto_config.ts
  7. 34
      src/lib/mtproto/mtprotoworker.ts
  8. 10
      src/lib/sessionStorage.ts
  9. 28
      src/lib/storage.ts
  10. 2
      src/pages/pageSignIn.ts

3
src/components/sidebarLeft/index.ts

@ -36,6 +36,7 @@ import PeerTitle from "../peerTitle";
import App from "../../config/app"; import App from "../../config/app";
import ButtonMenuToggle from "../buttonMenuToggle"; import ButtonMenuToggle from "../buttonMenuToggle";
import replaceContent from "../../helpers/dom/replaceContent"; import replaceContent from "../../helpers/dom/replaceContent";
import sessionStorage from "../../lib/sessionStorage";
export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown'; export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
@ -171,7 +172,9 @@ export class AppSidebarLeft extends SidebarSlider {
icon: 'char z', icon: 'char z',
text: 'ChatList.Menu.SwitchTo.Z', text: 'ChatList.Menu.SwitchTo.Z',
onClick: () => { onClick: () => {
sessionStorage.set({kz_version: 'z'}).then(() => {
location.href = 'https://web.telegram.org/z/'; location.href = 'https://web.telegram.org/z/';
});
}, },
verify: () => App.isMainDomain verify: () => App.isMainDomain
}, { }, {

8
src/lib/appManagers/appStateManager.ts

@ -263,7 +263,7 @@ export class AppStateManager extends EventListenerBase<{
const values = await Promise.all(keys.map(key => stateStorage.get(key as any))); const values = await Promise.all(keys.map(key => stateStorage.get(key as any)));
keys.push('user_auth'); keys.push('user_auth');
values.push(typeof(auth) === 'number' ? {dcID: values[0] || App.baseDcId, id: auth} : auth); values.push(typeof(auth) === 'number' ? {dcID: values[0] || App.baseDcId, date: Date.now() / 1000 | 0, id: auth} as UserAuth : auth);
let obj: any = {}; let obj: any = {};
keys.forEach((key, idx) => { keys.forEach((key, idx) => {
@ -273,7 +273,7 @@ export class AppStateManager extends EventListenerBase<{
await sessionStorage.set(obj); await sessionStorage.set(obj);
} }
if(!auth) { // try to read Webogram's session from localStorage /* if(!auth) { // try to read Webogram's session from localStorage
try { try {
const keys = Object.keys(localStorage); const keys = Object.keys(localStorage);
for(let i = 0; i < keys.length; ++i) { for(let i = 0; i < keys.length; ++i) {
@ -295,12 +295,12 @@ export class AppStateManager extends EventListenerBase<{
} catch(err) { } catch(err) {
this.log.error('localStorage import error', err); this.log.error('localStorage import error', err);
} }
} } */
if(auth) { if(auth) {
// ! Warning ! DON'T delete this // ! Warning ! DON'T delete this
state.authState = {_: 'authStateSignedIn'}; state.authState = {_: 'authStateSignedIn'};
rootScope.dispatchEvent('user_auth', typeof(auth) === 'number' ? {dcID: 0, id: auth} : auth); // * support old version rootScope.dispatchEvent('user_auth', typeof(auth) === 'number' ? {dcID: 0, date: Date.now() / 1000 | 0, id: auth} : auth); // * support old version
} }
// * Read storages // * Read storages

6
src/lib/cacheStorage.ts

@ -24,6 +24,10 @@ export default class CacheStorageController {
this.dbName += '_test'; this.dbName += '_test';
} }
if(CacheStorageController.STORAGES.length) {
this.useStorage = CacheStorageController.STORAGES[0].useStorage;
}
this.openDatabase(); this.openDatabase();
CacheStorageController.STORAGES.push(this); CacheStorageController.STORAGES.push(this);
} }
@ -131,7 +135,7 @@ export default class CacheStorageController {
public getFileWriter(fileName: string, mimeType: string) { public getFileWriter(fileName: string, mimeType: string) {
const fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob) => { const fakeWriter = FileManager.getFakeFileWriter(mimeType, (blob) => {
return this.saveFile(fileName, blob); return this.saveFile(fileName, blob).catch(() => blob);
}); });
return Promise.resolve(fakeWriter); return Promise.resolve(fakeWriter);

206
src/lib/localStorage.ts

@ -0,0 +1,206 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*
* Originally from:
* https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
import Modes from '../config/modes';
import { notifySomeone, isWorker } from '../helpers/context';
import { WorkerTaskTemplate } from '../types';
//import { stringify } from '../helpers/json';
class LocalStorage<Storage extends Record<string, any>> {
private prefix = '';
private cache: Partial<Storage> = {};
private useStorage = true;
constructor(private preserveKeys: (keyof Storage)[]) {
if(Modes.test) {
this.prefix = 't_';
}
}
public get<T extends keyof Storage>(key: T, useCache = true): Storage[T] {
if(this.cache.hasOwnProperty(key) && useCache) {
return this.cache[key];
} else if(this.useStorage) {
let value: Storage[T];
try {
value = localStorage.getItem(this.prefix + key as string) as any;
} catch(err) {
this.useStorage = false;
}
if(value !== null) {
try {
value = JSON.parse(value);
} catch(err) {
//console.error(err);
}
}
return value;
}/* else {
throw 'something went wrong';
} */
}
public set(obj: Partial<Storage>, onlyLocal = false) {
for(const key in obj) {
if(obj.hasOwnProperty(key)) {
const value = obj[key];
this.cache[key] = value;
if(this.useStorage && !onlyLocal) {
try {
const stringified = JSON.stringify(value);
localStorage.setItem(this.prefix + key, stringified);
} catch(err) {
this.useStorage = false;
}
}
}
}
}
public delete(key: keyof Storage, saveLocal = false) {
// ! it is needed here
key = '' + key;
if(!saveLocal) {
delete this.cache[key];
}
if(this.useStorage) {
localStorage.removeItem(this.prefix + key);
}
}
public clear(preserveKeys: (keyof Storage)[] = this.preserveKeys) {
// if(this.useStorage) {
try {
let obj: Partial<Storage> = {};
if(preserveKeys) {
preserveKeys.forEach(key => {
const value = this.get(key);
if(value !== undefined) {
obj[key] = value;
}
});
}
localStorage.clear();
if(preserveKeys) {
this.set(obj);
}
} catch(err) {
}
// }
}
public toggleStorage(enabled: boolean) {
this.useStorage = enabled;
if(!enabled) {
this.clear();
} else {
return this.set(this.cache);
}
}
}
export interface LocalStorageProxyTask extends WorkerTaskTemplate {
type: 'localStorageProxy',
payload: {
type: 'set' | 'get' | 'delete' | 'clear' | 'toggleStorage',
args: any[]
}
};
export interface LocalStorageProxyTaskResponse extends WorkerTaskTemplate {
type: 'localStorageProxy',
payload: any
};
export default class LocalStorageController<Storage extends Record<string, any>> {
private static STORAGES: LocalStorageController<any>[] = [];
private taskId = 0;
private tasks: {[taskID: number]: (result: any) => void} = {};
//private log = (...args: any[]) => console.log('[SW LS]', ...args);
//private log = (...args: any[]) => {};
private storage: LocalStorage<Storage>;
constructor(private preserveKeys: (keyof Storage)[] = []) {
LocalStorageController.STORAGES.push(this);
if(!isWorker) {
this.storage = new LocalStorage(preserveKeys);
}
}
public finishTask(taskId: number, result: any) {
//this.log('finishTask:', taskID, result, Object.keys(this.tasks));
if(!this.tasks.hasOwnProperty(taskId)) {
//this.log('no such task:', taskID, result);
return;
}
this.tasks[taskId](result);
delete this.tasks[taskId];
}
private proxy<T>(type: LocalStorageProxyTask['payload']['type'], ...args: LocalStorageProxyTask['payload']['args']) {
return new Promise<T>((resolve, reject) => {
if(isWorker) {
const taskId = this.taskId++;
this.tasks[taskId] = resolve;
const task: LocalStorageProxyTask = {
type: 'localStorageProxy',
id: taskId,
payload: {
type,
args
}
};
notifySomeone(task);
} else {
args = Array.prototype.slice.call(args);
// @ts-ignore
const result: any = this.storage[type].apply(this.storage, args as any);
resolve(result);
}
});
}
public get<T extends keyof Storage>(key: T, useCache?: boolean) {
return this.proxy<Storage[T]>('get', key, useCache);
}
public set(obj: Partial<Storage>, onlyLocal?: boolean) {
return this.proxy<void>('set', obj, onlyLocal);
}
public delete(key: keyof Storage, saveLocal?: boolean) {
return this.proxy<void>('delete', key, saveLocal);
}
public clear(preserveKeys?: (keyof Storage)[]) {
return this.proxy<void>('clear', preserveKeys);
}
public toggleStorage(enabled: boolean) {
return this.proxy<void>('toggleStorage', enabled);
}
}

47
src/lib/mtproto/mtproto.worker.ts

@ -15,8 +15,11 @@ import type { ServiceWorkerTask, ServiceWorkerTaskResponse } from './mtproto.ser
import { ctx } from '../../helpers/userAgent'; import { ctx } from '../../helpers/userAgent';
import { socketsProxied } from './dcConfigurator'; import { socketsProxied } from './dcConfigurator';
import { notifyAll } from '../../helpers/context'; import { notifyAll } from '../../helpers/context';
import AppStorage from '../storage'; // import AppStorage from '../storage';
import CacheStorageController from '../cacheStorage'; import CacheStorageController from '../cacheStorage';
import sessionStorage from '../sessionStorage';
import { LocalStorageProxyTask } from '../localStorage';
import { WebpConvertTask } from '../webp/webpWorkerController';
let webpSupported = false; let webpSupported = false;
export const isWebpSupported = () => { export const isWebpSupported = () => {
@ -31,22 +34,17 @@ networkerFactory.onConnectionStatusChange = (status) => {
notifyAll({type: 'connectionStatusChange', payload: status}); notifyAll({type: 'connectionStatusChange', payload: status});
}; };
const onMessage = async(e: any) => { const taskListeners = {
try { convertWebp: (task: WebpConvertTask) => {
const task = e.data;
const taskId = task.taskId;
if(task.type === 'convertWebp') {
const {fileName, bytes} = task.payload; const {fileName, bytes} = task.payload;
const deferred = apiFileManager.webpConvertPromises[fileName]; const deferred = apiFileManager.webpConvertPromises[fileName];
if(deferred) { if(deferred) {
deferred.resolve(bytes); deferred.resolve(bytes);
delete apiFileManager.webpConvertPromises[fileName]; delete apiFileManager.webpConvertPromises[fileName];
} }
},
return; requestFilePart: async(task: ServiceWorkerTask) => {
} else if((task as ServiceWorkerTask).type === 'requestFilePart') {
const task = e.data as ServiceWorkerTask;
const responseTask: ServiceWorkerTaskResponse = { const responseTask: ServiceWorkerTaskResponse = {
type: task.type, type: task.type,
id: task.id id: task.id
@ -61,11 +59,13 @@ const onMessage = async(e: any) => {
} }
notifyAll(responseTask); notifyAll(responseTask);
return; },
} else if(task.type === 'webpSupport') {
webpSupport: (task: any) => {
webpSupported = task.payload; webpSupported = task.payload;
return; },
} else if(task.type === 'socketProxy') {
socketProxy: (task: any) => {
const socketTask = task.payload; const socketTask = task.payload;
const id = socketTask.id; const id = socketTask.id;
@ -78,6 +78,23 @@ const onMessage = async(e: any) => {
socketProxied.dispatchEvent('close'); socketProxied.dispatchEvent('close');
socketsProxied.delete(id); socketsProxied.delete(id);
} }
},
localStorageProxy: (task: LocalStorageProxyTask) => {
sessionStorage.finishTask(task.id, task.payload);
}
};
const onMessage = async(e: any) => {
try {
const task = e.data;
const taskId = task.taskId;
// @ts-ignore
const f = taskListeners[task.type];
if(f) {
f(task);
return;
} }
if(!task.task) { if(!task.task) {
@ -126,7 +143,7 @@ const onMessage = async(e: any) => {
case 'toggleStorage': { case 'toggleStorage': {
const enabled = task.args[0]; const enabled = task.args[0];
AppStorage.toggleStorage(enabled); // AppStorage.toggleStorage(enabled);
CacheStorageController.toggleStorage(enabled); CacheStorageController.toggleStorage(enabled);
break; break;
} }

4
src/lib/mtproto/mtproto_config.ts

@ -5,8 +5,8 @@
*/ */
/** /**
* Legacy Webogram's format, don't change dcID to camelCase. * Legacy Webogram's format, don't change dcID to camelCase. date is timestamp
*/ */
export type UserAuth = {dcID: number, id: number}; export type UserAuth = {dcID: number | string, date: number, id: number};
export const REPLIES_PEER_ID = 1271266957; export const REPLIES_PEER_ID = 1271266957;

34
src/lib/mtproto/mtprotoworker.ts

@ -4,7 +4,8 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import type { LocalStorageProxyDeleteTask, LocalStorageProxySetTask } from '../storage'; import type { LocalStorageProxyTask, LocalStorageProxyTaskResponse } from '../localStorage';
//import type { LocalStorageProxyDeleteTask, LocalStorageProxySetTask } from '../storage';
import type { InvokeApiOptions } from '../../types'; import type { InvokeApiOptions } from '../../types';
import type { MethodDeclMap } from '../../layer'; import type { MethodDeclMap } from '../../layer';
import MTProtoWorker from 'worker-loader!./mtproto.worker'; import MTProtoWorker from 'worker-loader!./mtproto.worker';
@ -22,6 +23,7 @@ import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug';
import Socket from './transports/websocket'; import Socket from './transports/websocket';
import IDBStorage from '../idb'; import IDBStorage from '../idb';
import singleInstance from './singleInstance'; import singleInstance from './singleInstance';
import sessionStorage from '../sessionStorage';
type Task = { type Task = {
taskId: number, taskId: number,
@ -93,9 +95,10 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
this.registerServiceWorker(); this.registerServiceWorker();
this.addTaskListener('clear', () => { this.addTaskListener('clear', () => {
const promise = IDBStorage.deleteDatabase(); Promise.all([
localStorage.clear(); // * clear legacy Webogram's localStorage IDBStorage.deleteDatabase(),
promise.finally(() => { sessionStorage.clear()
]).finally(() => {
location.reload(); location.reload();
}); });
}); });
@ -164,19 +167,16 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
} }
}); });
this.addTaskListener('localStorageProxy', (task: LocalStorageProxySetTask | LocalStorageProxyDeleteTask) => { this.addTaskListener('localStorageProxy', (task: LocalStorageProxyTask) => {
const storageTask = task.payload; const storageTask = task.payload;
if(storageTask.type === 'set') { // @ts-ignore
for(let i = 0, length = storageTask.keys.length; i < length; ++i) { sessionStorage[storageTask.type](...storageTask.args).then(res => {
if(storageTask.values[i] !== undefined) { this.postMessage({
localStorage.setItem(storageTask.keys[i], JSON.stringify(storageTask.values[i])); type: 'localStorageProxy',
} id: task.id,
} payload: res
} else if(storageTask.type === 'delete') { } as LocalStorageProxyTaskResponse);
for(let i = 0, length = storageTask.keys.length; i < length; ++i) { });
localStorage.removeItem(storageTask.keys[i]);
}
}
}); });
rootScope.addEventListener('language_change', (language) => { rootScope.addEventListener('language_change', (language) => {
@ -483,7 +483,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
public setUserAuth(userAuth: UserAuth | number) { public setUserAuth(userAuth: UserAuth | number) {
if(typeof(userAuth) === 'number') { if(typeof(userAuth) === 'number') {
userAuth = {dcID: 0, id: userAuth}; userAuth = {dcID: 0, date: Date.now() / 1000 | 0, id: userAuth};
} }
rootScope.dispatchEvent('user_auth', userAuth); rootScope.dispatchEvent('user_auth', userAuth);

10
src/lib/sessionStorage.ts

@ -7,10 +7,9 @@
import type { AppInstance } from './mtproto/singleInstance'; import type { AppInstance } from './mtproto/singleInstance';
import type { UserAuth } from './mtproto/mtproto_config'; import type { UserAuth } from './mtproto/mtproto_config';
import { MOUNT_CLASS_TO } from '../config/debug'; import { MOUNT_CLASS_TO } from '../config/debug';
import AppStorage from './storage'; import LocalStorageController from './localStorage';
import DATABASE_SESSION from '../config/databases/session';
const sessionStorage = new AppStorage<{ const sessionStorage = new LocalStorageController<{
dc: number, dc: number,
user_auth: UserAuth, user_auth: UserAuth,
dc1_auth_key: string, dc1_auth_key: string,
@ -24,7 +23,8 @@ const sessionStorage = new AppStorage<{
dc4_server_salt: string, dc4_server_salt: string,
dc5_server_salt: string, dc5_server_salt: string,
server_time_offset: number, server_time_offset: number,
xt_instance: AppInstance xt_instance: AppInstance,
}, typeof DATABASE_SESSION>(DATABASE_SESSION, 'session'); kz_version: 'k' | 'z'
}>(['kz_version']);
MOUNT_CLASS_TO.appStorage = sessionStorage; MOUNT_CLASS_TO.appStorage = sessionStorage;
export default sessionStorage; export default sessionStorage;

28
src/lib/storage.ts

@ -10,15 +10,15 @@
*/ */
import { Database } from "../config/databases"; import { Database } from "../config/databases";
import DATABASE_SESSION from "../config/databases/session"; //import DATABASE_SESSION from "../config/databases/session";
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise"; import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
import { throttle } from "../helpers/schedulers"; import { throttle } from "../helpers/schedulers";
import { WorkerTaskTemplate } from "../types"; //import { WorkerTaskTemplate } from "../types";
import IDBStorage from "./idb"; import IDBStorage from "./idb";
function noop() {} function noop() {}
export interface LocalStorageProxySetTask extends WorkerTaskTemplate { /* export interface LocalStorageProxySetTask extends WorkerTaskTemplate {
type: 'localStorageProxy', type: 'localStorageProxy',
payload: { payload: {
type: 'set', type: 'set',
@ -33,7 +33,7 @@ export interface LocalStorageProxyDeleteTask extends WorkerTaskTemplate {
type: 'delete', type: 'delete',
keys: string[] keys: string[]
} }
}; }; */
export default class AppStorage<Storage extends Record<string, any>, T extends Database<any>/* Storage extends {[name: string]: any} *//* Storage extends Record<string, any> */> { export default class AppStorage<Storage extends Record<string, any>, T extends Database<any>/* Storage extends {[name: string]: any} *//* Storage extends Record<string, any> */> {
private static STORAGES: AppStorage<any, Database<any>>[] = []; private static STORAGES: AppStorage<any, Database<any>>[] = [];
@ -57,6 +57,10 @@ export default class AppStorage<Storage extends Record<string, any>, T extends D
constructor(private db: T, storeName: typeof db['stores'][number]['name']) { constructor(private db: T, storeName: typeof db['stores'][number]['name']) {
this.storage = new IDBStorage<T>(db, storeName); this.storage = new IDBStorage<T>(db, storeName);
if(AppStorage.STORAGES.length) {
this.useStorage = AppStorage.STORAGES[0].useStorage;
}
AppStorage.STORAGES.push(this); AppStorage.STORAGES.push(this);
this.saveThrottled = throttle(async() => { this.saveThrottled = throttle(async() => {
@ -74,7 +78,7 @@ export default class AppStorage<Storage extends Record<string, any>, T extends D
//await this.storage.save(key, new Response(value, {headers: {'Content-Type': 'application/json'}})); //await this.storage.save(key, new Response(value, {headers: {'Content-Type': 'application/json'}}));
const values = keys.map(key => this.cache[key]); const values = keys.map(key => this.cache[key]);
if(db === DATABASE_SESSION && !('localStorage' in self)) { // * support legacy Webogram's localStorage /* if(db === DATABASE_SESSION && !('localStorage' in self)) { // * support legacy Webogram's localStorage
self.postMessage({ self.postMessage({
type: 'localStorageProxy', type: 'localStorageProxy',
payload: { payload: {
@ -83,7 +87,7 @@ export default class AppStorage<Storage extends Record<string, any>, T extends D
values values
} }
} as LocalStorageProxySetTask); } as LocalStorageProxySetTask);
} } */
await this.storage.save(keys, values); await this.storage.save(keys, values);
//console.log('setItem: have set', key/* , value */); //console.log('setItem: have set', key/* , value */);
@ -110,7 +114,7 @@ export default class AppStorage<Storage extends Record<string, any>, T extends D
set.clear(); set.clear();
try { try {
if(db === DATABASE_SESSION && !('localStorage' in self)) { // * support legacy Webogram's localStorage /* if(db === DATABASE_SESSION && !('localStorage' in self)) { // * support legacy Webogram's localStorage
self.postMessage({ self.postMessage({
type: 'localStorageProxy', type: 'localStorageProxy',
payload: { payload: {
@ -118,7 +122,7 @@ export default class AppStorage<Storage extends Record<string, any>, T extends D
keys keys
} }
} as LocalStorageProxyDeleteTask); } as LocalStorageProxyDeleteTask);
} } */
await this.storage.delete(keys); await this.storage.delete(keys);
} catch(e) { } catch(e) {
@ -275,19 +279,19 @@ export default class AppStorage<Storage extends Record<string, any>, T extends D
storage.getPromises.forEach((deferred) => deferred.resolve()); storage.getPromises.forEach((deferred) => deferred.resolve());
storage.getPromises.clear(); storage.getPromises.clear();
if(storage.db === DATABASE_SESSION && 'localStorage' in self) { // * support legacy Webogram's localStorage /* if(storage.db === DATABASE_SESSION && 'localStorage' in self) { // * support legacy Webogram's localStorage
localStorage.clear(); localStorage.clear();
} } */
return storage.clear(); return storage.clear();
} else { } else {
if(storage.db === DATABASE_SESSION && 'localStorage' in self) { // * support legacy Webogram's localStorage /* if(storage.db === DATABASE_SESSION && 'localStorage' in self) { // * support legacy Webogram's localStorage
for(const i in storage.cache) { for(const i in storage.cache) {
if(storage.cache[i] !== undefined) { if(storage.cache[i] !== undefined) {
localStorage.setItem(i, JSON.stringify(storage.cache[i])); localStorage.setItem(i, JSON.stringify(storage.cache[i]));
} }
} }
} } */
return storage.set(storage.cache); return storage.set(storage.cache);
} }

2
src/pages/pageSignIn.ts

@ -33,6 +33,7 @@ import { cancelEvent } from "../helpers/dom/cancelEvent";
import { attachClickEvent } from "../helpers/dom/clickEvent"; import { attachClickEvent } from "../helpers/dom/clickEvent";
import replaceContent from "../helpers/dom/replaceContent"; import replaceContent from "../helpers/dom/replaceContent";
import toggleDisability from "../helpers/dom/toggleDisability"; import toggleDisability from "../helpers/dom/toggleDisability";
import sessionStorage from "../lib/sessionStorage";
type Country = _Country & { type Country = _Country & {
li?: HTMLLIElement[] li?: HTMLLIElement[]
@ -332,6 +333,7 @@ let onFirstMount = () => {
AppStorage.toggleStorage(keepSigned); AppStorage.toggleStorage(keepSigned);
CacheStorageController.toggleStorage(keepSigned); CacheStorageController.toggleStorage(keepSigned);
apiManager.toggleStorage(keepSigned); apiManager.toggleStorage(keepSigned);
sessionStorage.toggleStorage(keepSigned);
}); });
appStateManager.getState().then(state => { appStateManager.getState().then(state => {

Loading…
Cancel
Save