Browse Source

[breaking change] separate session and state db

master
morethanwords 3 years ago
parent
commit
54ace14d23
  1. 11
      src/components/chat/bubbles.ts
  2. 6
      src/components/chat/chat.ts
  3. 2
      src/components/chat/contextMenu.ts
  4. 14
      src/components/sidebarLeft/tabs/addContact.ts
  5. 30
      src/config/database.ts
  6. 14
      src/config/databases/index.ts
  7. 17
      src/config/databases/session.ts
  8. 27
      src/config/databases/state.ts
  9. 2
      src/lang.ts
  10. 6
      src/lib/appManagers/appDraftsManager.ts
  11. 10
      src/lib/appManagers/appEmojiManager.ts
  12. 14
      src/lib/appManagers/appImManager.ts
  13. 4
      src/lib/appManagers/appNotificationsManager.ts
  14. 59
      src/lib/appManagers/appStateManager.ts
  15. 5
      src/lib/appManagers/appStickersManager.ts
  16. 49
      src/lib/idb.ts
  17. 6
      src/lib/langPack.ts
  18. 34
      src/lib/mtproto/apiManager.ts
  19. 5
      src/lib/mtproto/mtproto_config.ts
  20. 27
      src/lib/mtproto/mtprotoworker.ts
  21. 2
      src/lib/mtproto/timeManager.ts
  22. 2
      src/lib/rootScope.ts
  23. 36
      src/lib/sessionStorage.ts
  24. 23
      src/lib/stateStorage.ts
  25. 79
      src/lib/storage.ts
  26. 2
      src/lib/storages/filters.ts

11
src/components/chat/bubbles.ts

@ -12,7 +12,7 @@ import type { AppInlineBotsManager } from "../../lib/appManagers/appInlineBotsMa @@ -12,7 +12,7 @@ import type { AppInlineBotsManager } from "../../lib/appManagers/appInlineBotsMa
import type { AppPhotosManager } from "../../lib/appManagers/appPhotosManager";
import type { AppDocsManager, MyDocument } from "../../lib/appManagers/appDocsManager";
import type { AppPeersManager } from "../../lib/appManagers/appPeersManager";
import type sessionStorage from '../../lib/sessionStorage';
import type stateStorage from '../../lib/stateStorage';
import type Chat from "./chat";
import { CHAT_ANIMATION_GROUP } from "../../lib/appManagers/appImManager";
import { getObjectKeysAndSort } from "../../helpers/object";
@ -147,7 +147,14 @@ export default class ChatBubbles { @@ -147,7 +147,14 @@ export default class ChatBubbles {
[_ in MessageEntity['_']]: boolean
}> = {};
constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager, private storage: typeof sessionStorage) {
constructor(private chat: Chat,
private appMessagesManager: AppMessagesManager,
private appStickersManager: AppStickersManager,
private appUsersManager: AppUsersManager,
private appInlineBotsManager: AppInlineBotsManager,
private appPhotosManager: AppPhotosManager,
private appPeersManager: AppPeersManager
) {
//this.chat.log.error('Bubbles construction');
this.listenerSetter = new ListenerSetter();

6
src/components/chat/chat.ts

@ -21,7 +21,7 @@ import type { ApiManagerProxy } from "../../lib/mtproto/mtprotoworker"; @@ -21,7 +21,7 @@ import type { ApiManagerProxy } from "../../lib/mtproto/mtprotoworker";
import type { AppDraftsManager } from "../../lib/appManagers/appDraftsManager";
import type { AppEmojiManager } from "../../lib/appManagers/appEmojiManager";
import type { ServerTimeManager } from "../../lib/mtproto/serverTimeManager";
import type sessionStorage from '../../lib/sessionStorage';
import type stateStorage from '../../lib/stateStorage';
import EventListenerBase from "../../helpers/eventListenerBase";
import { logger, LogTypes } from "../../lib/logger";
import rootScope from "../../lib/rootScope";
@ -82,7 +82,7 @@ export default class Chat extends EventListenerBase<{ @@ -82,7 +82,7 @@ export default class Chat extends EventListenerBase<{
public apiManager: ApiManagerProxy,
public appDraftsManager: AppDraftsManager,
public serverTimeManager: ServerTimeManager,
public storage: typeof sessionStorage,
public storage: typeof stateStorage,
public appNotificationsManager: AppNotificationsManager,
public appEmojiManager: AppEmojiManager
) {
@ -170,7 +170,7 @@ export default class Chat extends EventListenerBase<{ @@ -170,7 +170,7 @@ export default class Chat extends EventListenerBase<{
this.initPeerId = peerId;
this.topbar = new ChatTopbar(this, appSidebarRight, this.appMessagesManager, this.appPeersManager, this.appChatsManager, this.appNotificationsManager);
this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appDocsManager, this.appPeersManager, this.appChatsManager, this.storage);
this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appPeersManager);
this.input = new ChatInput(this, this.appMessagesManager, this.appDocsManager, this.appChatsManager, this.appPeersManager, this.appWebPagesManager, this.appImManager, this.appDraftsManager, this.serverTimeManager, this.appNotificationsManager, this.appEmojiManager);
this.selection = new ChatSelection(this, this.bubbles, this.input, this.appMessagesManager);
this.contextMenu = new ChatContextMenu(this.bubbles.bubblesContainer, this, this.appMessagesManager, this.appChatsManager, this.appPeersManager, this.appPollsManager);

2
src/components/chat/contextMenu.ts

@ -225,7 +225,7 @@ export default class ChatContextMenu { @@ -225,7 +225,7 @@ export default class ChatContextMenu {
withSelection: true
}, {
icon: 'link',
text: 'CopyLink',
text: 'MessageContext.CopyMessageLink1',
onClick: this.onCopyLinkClick,
verify: () => this.appPeersManager.isChannel(this.peerId) && !this.message.pFlags.is_outgoing
}, {

14
src/components/sidebarLeft/tabs/addContact.ts

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { SliderSuperTab } from "../../slider";
export default class AppAddContactTab extends SliderSuperTab {
protected init() {
this.container.classList.add('add-contact-container');
this.setTitle('AddContactTitle');
}
}

30
src/config/database.ts

@ -1,30 +0,0 @@ @@ -1,30 +0,0 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { IDBStore } from "../lib/idb";
import Modes from "./modes";
export type DatabaseStoreName = 'session' | 'stickerSets' | 'users' | 'chats' | 'messages' | 'dialogs';
export type DatabaseStore = Omit<IDBStore, 'name'> & {name: DatabaseStoreName};
const Database = {
name: 'tweb' + (Modes.test ? '_test' : ''),
version: 7,
stores: [{
name: 'session'
}, {
name: 'stickerSets'
}, {
name: 'users'
}, {
name: 'chats'
}, {
name: 'dialogs'
}, {
name: 'messages'
}] as DatabaseStore[],
};
export default Database;

14
src/config/databases/index.ts

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { IDBStore } from "../../lib/idb";
export type DatabaseStore<StoreName extends string> = Omit<IDBStore, 'name'> & {name: StoreName};
export type Database<StoreName extends string> = {
name: string,
version: number,
stores: DatabaseStore<StoreName>[]
};

17
src/config/databases/session.ts

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { Database } from '.';
const DATABASE_SESSION: Database<'session'> = {
name: 'telegram',
version: 1,
stores: [{
name: 'session'
}]
};
export default DATABASE_SESSION;

27
src/config/databases/state.ts

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { Database } from '.';
const DATABASE_STATE: Database<'session' | 'stickerSets' | 'users' | 'chats' | 'messages' | 'dialogs'> = {
name: 'tweb',
version: 7,
stores: [{
name: 'session'
}, {
name: 'stickerSets'
}, {
name: 'users'
}, {
name: 'chats'
}, {
name: 'dialogs'
}, {
name: 'messages'
}]
};
export default DATABASE_STATE;

2
src/lang.ts

@ -437,6 +437,7 @@ const lang = { @@ -437,6 +437,7 @@ const lang = {
"NoResult": "No results",
"Updating": "Updating...",
"Emoji": "Emoji",
"AddContactTitle": "Add Contact",
// * macos
"AccountSettings.Filters": "Chat Folders",
@ -664,6 +665,7 @@ const lang = { @@ -664,6 +665,7 @@ const lang = {
"Message.Context.Select": "Select",
"Message.Context.Pin": "Pin",
"Message.Context.Unpin": "Unpin",
"MessageContext.CopyMessageLink1": "Copy Message Link",
"NewPoll.Anonymous": "Anonymous Voting",
"NewPoll.Explanation.Placeholder": "Add a Comment (Optional)",
"NewPoll.OptionsAddOption": "Add an Option",

6
src/lib/appManagers/appDraftsManager.ts

@ -21,7 +21,7 @@ import { tsNow } from "../../helpers/date"; @@ -21,7 +21,7 @@ import { tsNow } from "../../helpers/date";
import { deepEqual } from "../../helpers/object";
import { isObject } from "../mtproto/bin_utils";
import { MOUNT_CLASS_TO } from "../../config/debug";
import sessionStorage from "../sessionStorage";
import stateStorage from "../stateStorage";
export type MyDraftMessage = DraftMessage.draftMessage;
@ -30,7 +30,7 @@ export class AppDraftsManager { @@ -30,7 +30,7 @@ export class AppDraftsManager {
private getAllDraftPromise: Promise<void> = null;
constructor() {
sessionStorage.get('drafts').then(drafts => {
stateStorage.get('drafts').then(drafts => {
this.drafts = drafts || {};
});
@ -96,7 +96,7 @@ export class AppDraftsManager { @@ -96,7 +96,7 @@ export class AppDraftsManager {
delete this.drafts[key];
}
sessionStorage.set({
stateStorage.set({
drafts: this.drafts
});

10
src/lib/appManagers/appEmojiManager.ts

@ -11,7 +11,7 @@ import I18n from "../langPack"; @@ -11,7 +11,7 @@ import I18n from "../langPack";
import { isObject } from "../mtproto/bin_utils";
import apiManager from "../mtproto/mtprotoworker";
import SearchIndex from "../searchIndex";
import sessionStorage from "../sessionStorage";
import stateStorage from "../stateStorage";
import appStateManager from "./appStateManager";
type EmojiLangPack = {
@ -45,7 +45,7 @@ export class AppEmojiManager { @@ -45,7 +45,7 @@ export class AppEmojiManager {
private getRecentEmojisPromise: Promise<AppEmojiManager['recent']>;
/* public getPopularEmoji() {
return sessionStorage.get('emojis_popular').then(popEmojis => {
return stateStorage.get('emojis_popular').then(popEmojis => {
var result = []
if (popEmojis && popEmojis.length) {
for (var i = 0, len = popEmojis.length; i < len; i++) {
@ -55,7 +55,7 @@ export class AppEmojiManager { @@ -55,7 +55,7 @@ export class AppEmojiManager {
return
}
return sessionStorage.get('emojis_recent').then(recentEmojis => {
return stateStorage.get('emojis_recent').then(recentEmojis => {
recentEmojis = recentEmojis || popular || []
var shortcut
var code
@ -111,7 +111,7 @@ export class AppEmojiManager { @@ -111,7 +111,7 @@ export class AppEmojiManager {
}
const storageKey: any = 'emojiKeywords_' + langCode;
return this.getKeywordsPromises[langCode] = sessionStorage.get(storageKey).then((pack: EmojiLangPack) => {
return this.getKeywordsPromises[langCode] = stateStorage.get(storageKey).then((pack: EmojiLangPack) => {
if(!isObject(pack)) {
pack = {} as any;
}
@ -135,7 +135,7 @@ export class AppEmojiManager { @@ -135,7 +135,7 @@ export class AppEmojiManager {
packKeywords[keyword] = emoticons;
}
sessionStorage.set({
stateStorage.set({
[storageKey]: pack
});

14
src/lib/appManagers/appImManager.ts

@ -35,7 +35,7 @@ import lottieLoader from '../lottieLoader'; @@ -35,7 +35,7 @@ import lottieLoader from '../lottieLoader';
import useHeavyAnimationCheck, { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
import appDraftsManager from './appDraftsManager';
import serverTimeManager from '../mtproto/serverTimeManager';
import sessionStorage from '../sessionStorage';
import stateStorage from '../stateStorage';
import appDownloadManager from './appDownloadManager';
import { AppStateManager } from './appStateManager';
import { MOUNT_CLASS_TO } from '../../config/debug';
@ -215,8 +215,8 @@ export class AppImManager { @@ -215,8 +215,8 @@ export class AppImManager {
popup.show();
});
sessionStorage.get('chatPositions').then((c) => {
sessionStorage.setToCache('chatPositions', c || {});
stateStorage.get('chatPositions').then((c) => {
stateStorage.setToCache('chatPositions', c || {});
});
(window as any).showMaskedAlert = (element: HTMLAnchorElement, e: Event) => {
@ -385,7 +385,7 @@ export class AppImManager { @@ -385,7 +385,7 @@ export class AppImManager {
const key = chat.peerId + (chat.threadId ? '_' + chat.threadId : '');
const chatPositions = sessionStorage.getFromCache('chatPositions');
const chatPositions = stateStorage.getFromCache('chatPositions');
if(!(chat.bubbles.scrollable.getDistanceToEnd() <= 16 && chat.bubbles.scrollable.loadedAll.bottom) && Object.keys(chat.bubbles.bubbles).length) {
const position = {
mids: getObjectKeysAndSort(chat.bubbles.bubbles, 'desc'),
@ -401,7 +401,7 @@ export class AppImManager { @@ -401,7 +401,7 @@ export class AppImManager {
this.log('deleted chat position');
}
sessionStorage.set({chatPositions}, true);
stateStorage.set({chatPositions}, true);
//}
}
@ -411,7 +411,7 @@ export class AppImManager { @@ -411,7 +411,7 @@ export class AppImManager {
}
const key = chat.peerId + (chat.threadId ? '_' + chat.threadId : '');
const cache = sessionStorage.getFromCache('chatPositions');
const cache = stateStorage.getFromCache('chatPositions');
return cache && cache[key];
}
@ -820,7 +820,7 @@ export class AppImManager { @@ -820,7 +820,7 @@ export class AppImManager {
apiManager,
appDraftsManager,
serverTimeManager,
sessionStorage,
stateStorage,
appNotificationsManager,
appEmojiManager
);

4
src/lib/appManagers/appNotificationsManager.ts

@ -20,7 +20,7 @@ import { InputNotifyPeer, InputPeerNotifySettings, NotifyPeer, PeerNotifySetting @@ -20,7 +20,7 @@ import { InputNotifyPeer, InputPeerNotifySettings, NotifyPeer, PeerNotifySetting
import I18n from "../langPack";
import apiManager from "../mtproto/mtprotoworker";
import rootScope from "../rootScope";
import sessionStorage from "../sessionStorage";
import stateStorage from "../stateStorage";
import apiUpdatesManager from "./apiUpdatesManager";
import appPeersManager from "./appPeersManager";
import appStateManager from "./appStateManager";
@ -268,7 +268,7 @@ export class AppNotificationsManager { @@ -268,7 +268,7 @@ export class AppNotificationsManager {
}
public updateLocalSettings = () => {
Promise.all(['notify_nodesktop', 'notify_volume', 'notify_novibrate', 'notify_nopreview', 'notify_nopush'].map(k => sessionStorage.get(k as any)))
Promise.all(['notify_nodesktop', 'notify_volume', 'notify_novibrate', 'notify_nopreview', 'notify_nopush'].map(k => stateStorage.get(k as any)))
.then((updSettings) => {
this.settings.nodesktop = updSettings[0];
this.settings.volume = updSettings[1] === undefined ? 0.5 : updSettings[1];

59
src/lib/appManagers/appStateManager.ts

@ -12,7 +12,7 @@ import type FiltersStorage from '../storages/filters'; @@ -12,7 +12,7 @@ import type FiltersStorage from '../storages/filters';
import type DialogsStorage from '../storages/dialogs';
import EventListenerBase from '../../helpers/eventListenerBase';
import rootScope from '../rootScope';
import sessionStorage from '../sessionStorage';
import stateStorage from '../stateStorage';
import { logger } from '../logger';
import { copy, setDeepProperty, validateInitObject } from '../../helpers/object';
import App from '../../config/app';
@ -20,6 +20,8 @@ import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug'; @@ -20,6 +20,8 @@ import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug';
import AppStorage from '../storage';
import { Chat } from '../../layer';
import { isMobile } from '../../helpers/userAgent';
import DATABASE_STATE from '../../config/databases/state';
import sessionStorage from '../sessionStorage';
const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day
const REFRESH_EVERY_WEEK = 24 * 60 * 60 * 1000 * 7; // 7 days
@ -174,22 +176,14 @@ export class AppStateManager extends EventListenerBase<{ @@ -174,22 +176,14 @@ export class AppStateManager extends EventListenerBase<{
private singlePeerMap: Map<string, number> = new Map();
public storages = {
users: new AppStorage<Record<number, User>>({
storeName: 'users'
}),
chats: new AppStorage<Record<number, Chat>>({
storeName: 'chats'
}),
dialogs: new AppStorage<Record<number, Dialog>>({
storeName: 'dialogs'
})
users: new AppStorage<Record<number, User>, typeof DATABASE_STATE>(DATABASE_STATE, 'users'),
chats: new AppStorage<Record<number, Chat>, typeof DATABASE_STATE>(DATABASE_STATE, 'chats'),
dialogs: new AppStorage<Record<number, Dialog>, typeof DATABASE_STATE>(DATABASE_STATE, 'dialogs')
};
public storagesResults: {[key in keyof AppStateManager['storages']]: any[]} = {} as any;
public storage = sessionStorage;
public storage = stateStorage;
constructor() {
super();
@ -201,11 +195,10 @@ export class AppStateManager extends EventListenerBase<{ @@ -201,11 +195,10 @@ export class AppStateManager extends EventListenerBase<{
console.time('load state');
this.loaded = new Promise((resolve) => {
const storagesKeys = Object.keys(this.storages) as Array<keyof AppStateManager['storages']>;
const storagesPromises = storagesKeys.map(key => this.storages[key].getAll());
const storagesPromises: Promise<any>[] = storagesKeys.map(key => this.storages[key].getAll());
const promises = ALL_KEYS
.concat('user_auth' as any)
.map(key => sessionStorage.get(key))
const promises: Promise<any>[] = ALL_KEYS.map(key => stateStorage.get(key))
.concat(sessionStorage.get('user_auth'))
.concat(storagesPromises);
Promise.all(promises).then((arr) => {
@ -257,16 +250,40 @@ export class AppStateManager extends EventListenerBase<{ @@ -257,16 +250,40 @@ export class AppStateManager extends EventListenerBase<{
arr.splice(0, ALL_KEYS.length);
// * Read auth
const auth: UserAuth = arr.shift() as any;
let auth = arr.shift() as UserAuth | number;
if(!auth) { // try to read Webogram's session from localStorage
try {
const keys = Object.keys(localStorage);
for(let i = 0; i < keys.length; ++i) {
const key = keys[i];
let value: any;
try {
value = localStorage.getItem(key);
value = JSON.parse(value);
} catch(err) {
//console.error(err);
}
sessionStorage.set({
[key as any]: value
});
}
auth = sessionStorage.getFromCache('user_auth');
} catch(err) {
this.log.error('localStorage import error', err);
}
}
if(auth) {
// ! Warning ! DON'T delete this
state.authState = {_: 'authStateSignedIn'};
rootScope.broadcast('user_auth', typeof(auth) !== 'number' ? (auth as any).id : auth); // * support old version
rootScope.broadcast('user_auth', typeof(auth) === 'number' ? {dcID: 0, id: auth} : auth); // * support old version
}
// * Read storages
for(let i = 0, length = storagesKeys.length; i < length; ++i) {
this.storagesResults[storagesKeys[i]] = arr[i];
this.storagesResults[storagesKeys[i]] = arr[i] as any;
}
arr.splice(0, storagesKeys.length);
@ -362,7 +379,7 @@ export class AppStateManager extends EventListenerBase<{ @@ -362,7 +379,7 @@ export class AppStateManager extends EventListenerBase<{
this.state[key] = value;
}
sessionStorage.set({
this.storage.set({
[key]: value
});
}

5
src/lib/appManagers/appStickersManager.ts

@ -12,13 +12,12 @@ import appDocsManager from './appDocsManager'; @@ -12,13 +12,12 @@ import appDocsManager from './appDocsManager';
import AppStorage from '../storage';
import { MOUNT_CLASS_TO } from '../../config/debug';
import { forEachReverse } from '../../helpers/array';
import DATABASE_STATE from '../../config/databases/state';
const CACHE_TIME = 3600e3;
export class AppStickersManager {
private storage = new AppStorage<Record<string, MessagesStickerSet>>({
storeName: 'stickerSets'
});
private storage = new AppStorage<Record<string, MessagesStickerSet>, typeof DATABASE_STATE>(DATABASE_STATE, 'stickerSets');
private getStickerSetPromises: {[setId: string]: Promise<MessagesStickerSet>} = {};
private getStickersByEmoticonsPromises: {[emoticon: string]: Promise<Document[]>} = {};

49
src/lib/idb.ts

@ -9,7 +9,8 @@ @@ -9,7 +9,8 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
import Database from '../config/database';
import { Database } from '../config/databases';
import Modes from '../config/modes';
import { blobConstruct } from '../helpers/blob';
import { safeAssign } from '../helpers/object';
import { logger } from './logger';
@ -37,27 +38,31 @@ export type IDBOptions = { @@ -37,27 +38,31 @@ export type IDBOptions = {
const DEBUG = false;
export default class IDBStorage {
private static STORAGES: IDBStorage[] = [];
export default class IDBStorage<T extends Database<any>> {
private static STORAGES: IDBStorage<Database<any>>[] = [];
private openDbPromise: Promise<IDBDatabase>;
private db: IDBDatabase;
private storageIsAvailable = true;
private log: ReturnType<typeof logger>;
private name: string = Database.name;
private version: number = Database.version;
private stores: IDBStore[] = Database.stores;
private name: string;
private version: number;
private stores: IDBStore[];
private storeName: string;
constructor(options: IDBOptions) {
safeAssign(this, options);
constructor(db: T, storeName: typeof db['stores'][0]['name']) {
safeAssign(this, db);
this.storeName = storeName;
this.log = logger('IDB-' + this.storeName);
this.openDatabase(true);
if(Modes.test) {
this.name += '_test';
}
IDBStorage.STORAGES.push(this);
}
@ -74,17 +79,23 @@ export default class IDBStorage { @@ -74,17 +79,23 @@ export default class IDBStorage {
public static deleteDatabase() {
this.closeDatabases();
return new Promise<void>((resolve, reject) => {
const deleteRequest = indexedDB.deleteDatabase(Database.name);
deleteRequest.onerror = () => {
reject();
};
deleteRequest.onsuccess = () => {
resolve();
};
const storages = this.STORAGES;
const dbNames = Array.from(new Set(storages.map(storage => storage.name)));
const promises = dbNames.map(dbName => {
return new Promise<void>((resolve, reject) => {
const deleteRequest = indexedDB.deleteDatabase(dbName);
deleteRequest.onerror = () => {
reject();
};
deleteRequest.onsuccess = () => {
resolve();
};
});
});
return Promise.all(promises);
}
public isAvailable() {

6
src/lib/langPack.ts

@ -11,7 +11,7 @@ import type lang from "../lang"; @@ -11,7 +11,7 @@ import type lang from "../lang";
import type langSign from "../langSign";
import { LangPackDifference, LangPackString } from "../layer";
import apiManager from "./mtproto/mtprotoworker";
import sessionStorage from "./sessionStorage";
import stateStorage from "./stateStorage";
import App from "../config/app";
import rootScope from "./rootScope";
@ -62,7 +62,7 @@ namespace I18n { @@ -62,7 +62,7 @@ namespace I18n {
export function getCacheLangPack(): Promise<LangPackDifference> {
if(cacheLangPackPromise) return cacheLangPackPromise;
return cacheLangPackPromise = Promise.all([
sessionStorage.get('langPack') as Promise<LangPackDifference>,
stateStorage.get('langPack') as Promise<LangPackDifference>,
polyfillPromise
]).then(([langPack]) => {
if(!langPack/* || true */) {
@ -177,7 +177,7 @@ namespace I18n { @@ -177,7 +177,7 @@ namespace I18n {
export function saveLangPack(langPack: LangPackDifference) {
langPack.appVersion = App.langPackVersion;
return sessionStorage.set({langPack}).then(() => {
return stateStorage.set({langPack}).then(() => {
applyLangPack(langPack);
return langPack;
});

34
src/lib/mtproto/apiManager.ts

@ -9,8 +9,8 @@ @@ -9,8 +9,8 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
import type { UserAuth } from './mtproto_config';
import sessionStorage from '../sessionStorage';
import MTPNetworker, { MTMessage } from './networker';
import { isObject } from './bin_utils';
import networkerFactory from './networkerFactory';
@ -104,17 +104,39 @@ export class ApiManager { @@ -104,17 +104,39 @@ export class ApiManager {
//telegramMeWebService.setAuthorized(this.telegramMeNotified);
}
} */
public async getBaseDcId() {
if(this.baseDcId) {
return this.baseDcId;
}
const baseDcId = await sessionStorage.get('dc');
if(!this.baseDcId) {
if(!baseDcId) {
this.setBaseDcId(App.baseDcId);
} else {
this.baseDcId = baseDcId;
}
}
return this.baseDcId;
}
// mtpSetUserAuth
public setUserAuth(userId: number) {
public async setUserAuth(userAuth: UserAuth) {
if(!userAuth.dcID) {
const baseDcId = await this.getBaseDcId();
userAuth.dcID = baseDcId;
}
sessionStorage.set({
user_auth: userId
user_auth: userAuth
});
//this.telegramMeNotify(true);
/// #if !MTPROTO_WORKER
rootScope.broadcast('user_auth', userId);
rootScope.broadcast('user_auth', userAuth);
/// #endif
}
@ -429,8 +451,8 @@ export class ApiManager { @@ -429,8 +451,8 @@ export class ApiManager {
if(dcId = (options.dcId || this.baseDcId)) {
this.getNetworker(dcId, options).then(performRequest, rejectPromise);
} else {
sessionStorage.get('dc').then((baseDcId) => {
this.getNetworker(this.baseDcId = dcId = baseDcId || App.baseDcId, options).then(performRequest, rejectPromise);
this.getBaseDcId().then(baseDcId => {
this.getNetworker(dcId = baseDcId, options).then(performRequest, rejectPromise);
});
}

5
src/lib/mtproto/mtproto_config.ts

@ -4,6 +4,9 @@ @@ -4,6 +4,9 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
export type UserAuth = number;
/**
* Legacy Webogram's format, don't change dcID to camelCase.
*/
export type UserAuth = {dcID: number, id: number};
export const REPLIES_PEER_ID = 1271266957;

27
src/lib/mtproto/mtprotoworker.ts

@ -4,11 +4,12 @@ @@ -4,11 +4,12 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type { LocalStorageProxyDeleteTask, LocalStorageProxySetTask } from '../storage';
import type { InvokeApiOptions } from '../../types';
import type { MethodDeclMap } from '../../layer';
import MTProtoWorker from 'worker-loader!./mtproto.worker';
//import './mtproto.worker';
import { isObject } from '../../helpers/object';
import type { MethodDeclMap } from '../../layer';
import type { InvokeApiOptions } from '../../types';
import CryptoWorkerMethods from '../crypto/crypto_methods';
import { logger } from '../logger';
import rootScope from '../rootScope';
@ -93,6 +94,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods { @@ -93,6 +94,7 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
this.addTaskListener('clear', () => {
const promise = IDBStorage.deleteDatabase();
localStorage.clear(); // * clear legacy Webogram's localStorage
promise.finally(() => {
location.reload();
});
@ -162,6 +164,21 @@ export class ApiManagerProxy extends CryptoWorkerMethods { @@ -162,6 +164,21 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
}
});
this.addTaskListener('localStorageProxy', (task: LocalStorageProxySetTask | LocalStorageProxyDeleteTask) => {
const storageTask = task.payload;
if(storageTask.type === 'set') {
for(let i = 0, length = storageTask.keys.length; i < length; ++i) {
if(storageTask.values[i] !== undefined) {
localStorage.setItem(storageTask.keys[i], JSON.stringify(storageTask.values[i]));
}
}
} else if(storageTask.type === 'delete') {
for(let i = 0, length = storageTask.keys.length; i < length; ++i) {
localStorage.removeItem(storageTask.keys[i]);
}
}
});
/// #if !MTPROTO_SW
this.registerWorker();
/// #endif
@ -457,7 +474,11 @@ export class ApiManagerProxy extends CryptoWorkerMethods { @@ -457,7 +474,11 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
return this.performTaskWorker('setQueueId', queueId);
}
public setUserAuth(userAuth: UserAuth) {
public setUserAuth(userAuth: UserAuth | number) {
if(typeof(userAuth) === 'number') {
userAuth = {dcID: 0, id: userAuth};
}
rootScope.broadcast('user_auth', userAuth);
return this.performTaskWorker('setUserAuth', userAuth);
}

2
src/lib/mtproto/timeManager.ts

@ -26,7 +26,7 @@ export class TimeManager { @@ -26,7 +26,7 @@ export class TimeManager {
private timeOffset = 0;
constructor() {
sessionStorage.get('server_time_offset').then((to: any) => {
sessionStorage.get('server_time_offset').then((to) => {
if(to) {
this.timeOffset = to;
}

2
src/lib/rootScope.ts

@ -141,7 +141,7 @@ export class RootScope extends EventListenerBase<{ @@ -141,7 +141,7 @@ export class RootScope extends EventListenerBase<{
});
this.on('user_auth', (e) => {
this.myId = e;
this.myId = e.id;
});
this.on('connection_status_change', (e) => {

36
src/lib/sessionStorage.ts

@ -4,33 +4,27 @@ @@ -4,33 +4,27 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type { ChatSavedPosition } from './appManagers/appImManager';
import type { State } from './appManagers/appStateManager';
import type { AppDraftsManager } from './appManagers/appDraftsManager';
import type { AppInstance } from './mtproto/singleInstance';
import type { UserAuth } from './mtproto/mtproto_config';
import { MOUNT_CLASS_TO } from '../config/debug';
import { LangPackDifference } from '../layer';
import AppStorage from './storage';
import DATABASE_SESSION from '../config/databases/session';
const sessionStorage = new AppStorage<{
dc: number,
user_auth: number,
dc1_auth_key: any,
dc2_auth_key: any,
dc3_auth_key: any,
dc4_auth_key: any,
dc5_auth_key: any,
max_seen_msg: number,
user_auth: UserAuth,
dc1_auth_key: string,
dc2_auth_key: string,
dc3_auth_key: string,
dc4_auth_key: string,
dc5_auth_key: string,
dc1_server_salt: string,
dc2_server_salt: string,
dc3_server_salt: string,
dc4_server_salt: string,
dc5_server_salt: string,
server_time_offset: number,
xt_instance: AppInstance,
chatPositions: {
[peerId_threadId: string]: ChatSavedPosition
},
langPack: LangPackDifference,
drafts: AppDraftsManager['drafts']
} & State>({
storeName: 'session'
});
xt_instance: AppInstance
}, typeof DATABASE_SESSION>(DATABASE_SESSION, 'session');
MOUNT_CLASS_TO.appStorage = sessionStorage;
export default sessionStorage;

23
src/lib/stateStorage.ts

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type { ChatSavedPosition } from './appManagers/appImManager';
import type { State } from './appManagers/appStateManager';
import type { AppDraftsManager } from './appManagers/appDraftsManager';
import { MOUNT_CLASS_TO } from '../config/debug';
import { LangPackDifference } from '../layer';
import AppStorage from './storage';
import DATABASE_STATE from '../config/databases/state';
const stateStorage = new AppStorage<{
chatPositions: {
[peerId_threadId: string]: ChatSavedPosition
},
langPack: LangPackDifference,
drafts: AppDraftsManager['drafts']
} & State, typeof DATABASE_STATE>(DATABASE_STATE, 'session');
MOUNT_CLASS_TO.stateStorage = stateStorage;
export default stateStorage;

79
src/lib/storage.ts

@ -9,16 +9,35 @@ @@ -9,16 +9,35 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
import { DatabaseStore, DatabaseStoreName } from "../config/database";
import { Database } from "../config/databases";
import DATABASE_SESSION from "../config/databases/session";
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
import { throttle } from "../helpers/schedulers";
import IDBStorage, { IDBOptions } from "./idb";
import { WorkerTaskTemplate } from "../types";
import IDBStorage from "./idb";
function noop() {}
export default class AppStorage<Storage extends Record<string, any>/* Storage extends {[name: string]: any} *//* Storage extends Record<string, any> */> {
private static STORAGES: AppStorage<any>[] = [];
private storage: IDBStorage;//new CacheStorageController('session');
export interface LocalStorageProxySetTask extends WorkerTaskTemplate {
type: 'localStorageProxy',
payload: {
type: 'set',
keys: string[],
values: any[]
}
};
export interface LocalStorageProxyDeleteTask extends WorkerTaskTemplate {
type: 'localStorageProxy',
payload: {
type: 'delete',
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> */> {
private static STORAGES: AppStorage<any, Database<any>>[] = [];
private storage: IDBStorage<T>;//new CacheStorageController('session');
//private cache: Partial<{[key: string]: Storage[typeof key]}> = {};
private cache: Partial<Storage> = {};
@ -35,8 +54,8 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex @@ -35,8 +54,8 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
private deleteThrottled: () => void;
private deleteDeferred = deferredPromise<void>();
constructor(storageOptions: Omit<IDBOptions, 'storeName' | 'stores'> & {stores?: DatabaseStore[], storeName: DatabaseStoreName}) {
this.storage = new IDBStorage(storageOptions);
constructor(private db: T, storeName: typeof db['stores'][number]['name']) {
this.storage = new IDBStorage<T>(db, storeName);
AppStorage.STORAGES.push(this);
@ -53,7 +72,20 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex @@ -53,7 +72,20 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
//console.log('setItem: will set', key/* , value */);
//await this.cacheStorage.delete(key); // * try to prevent memory leak in Chrome leading to 'Unexpected internal error.'
//await this.storage.save(key, new Response(value, {headers: {'Content-Type': 'application/json'}}));
await this.storage.save(keys, 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
self.postMessage({
type: 'localStorageProxy',
payload: {
type: 'set',
keys,
values
}
} as LocalStorageProxySetTask);
}
await this.storage.save(keys, values);
//console.log('setItem: have set', key/* , value */);
} catch(e) {
//this.useCS = false;
@ -78,6 +110,16 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex @@ -78,6 +110,16 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
set.clear();
try {
if(db === DATABASE_SESSION && !('localStorage' in self)) { // * support legacy Webogram's localStorage
self.postMessage({
type: 'localStorageProxy',
payload: {
type: 'delete',
keys
}
} as LocalStorageProxyDeleteTask);
}
await this.storage.delete(keys);
} catch(e) {
console.error('[AS]: delete error:', e, keys);
@ -107,7 +149,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex @@ -107,7 +149,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
}, (error) => {
if(!['NO_ENTRY_FOUND', 'STORAGE_OFFLINE'].includes(error)) {
this.useStorage = false;
console.error('[AS]: get error:', error, keys, storageOptions.storeName);
console.error('[AS]: get error:', error, keys, storeName);
}
for(let i = 0, length = keys.length; i < length; ++i) {
@ -135,7 +177,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex @@ -135,7 +177,7 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
return this.cache;
}
public getFromCache(key: keyof Storage) {
public getFromCache<T extends keyof Storage>(key: T) {
return this.cache[key];
}
@ -143,12 +185,12 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex @@ -143,12 +185,12 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
return this.cache[key] = value;
}
public async get(key: keyof Storage, useCache = true): Promise<Storage[typeof key]> {
public async get<T extends keyof Storage>(key: T, useCache = true): Promise<Storage[T]> {
if(this.cache.hasOwnProperty(key) && useCache) {
return this.getFromCache(key);
} else if(this.useStorage) {
const r = this.getPromises.get(key);
if(r) return r;
if(r) return r as any;
const p = deferredPromise<Storage[typeof key]>();
this.getPromises.set(key, p);
@ -232,8 +274,21 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex @@ -232,8 +274,21 @@ export default class AppStorage<Storage extends Record<string, any>/* Storage ex
storage.keysToDelete.clear();
storage.getPromises.forEach((deferred) => deferred.resolve());
storage.getPromises.clear();
if(storage.db === DATABASE_SESSION && 'localStorage' in self) { // * support legacy Webogram's localStorage
localStorage.clear();
}
return storage.clear();
} else {
if(storage.db === DATABASE_SESSION && 'localStorage' in self) { // * support legacy Webogram's localStorage
for(const i in storage.cache) {
if(storage.cache[i] !== undefined) {
localStorage.setItem(i, JSON.stringify(storage.cache[i]));
}
}
}
return storage.set(storage.cache);
}
})).catch(noop);

2
src/lib/storages/filters.ts

@ -126,7 +126,7 @@ export default class FiltersStorage { @@ -126,7 +126,7 @@ export default class FiltersStorage {
}
// exclude_read
if(pFlags.exclude_read && !dialog.unread_count) {
if(pFlags.exclude_read && !dialog.unread_count && !dialog.pFlags.unread_mark) {
return false;
}

Loading…
Cancel
Save