[breaking change] separate session and state db
This commit is contained in:
parent
705e9a3f10
commit
54ace14d23
@ -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 {
|
||||
[_ 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();
|
||||
|
@ -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<{
|
||||
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<{
|
||||
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);
|
||||
|
@ -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
Normal file
14
src/components/sidebarLeft/tabs/addContact.ts
Normal file
@ -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');
|
||||
}
|
||||
}
|
@ -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
Normal file
14
src/config/databases/index.ts
Normal file
@ -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
Normal file
17
src/config/databases/session.ts
Normal file
@ -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
Normal file
27
src/config/databases/state.ts
Normal file
@ -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;
|
@ -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 = {
|
||||
"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",
|
||||
|
@ -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 {
|
||||
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 {
|
||||
delete this.drafts[key];
|
||||
}
|
||||
|
||||
sessionStorage.set({
|
||||
stateStorage.set({
|
||||
drafts: this.drafts
|
||||
});
|
||||
|
||||
|
@ -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 {
|
||||
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 {
|
||||
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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
packKeywords[keyword] = emoticons;
|
||||
}
|
||||
|
||||
sessionStorage.set({
|
||||
stateStorage.set({
|
||||
[storageKey]: pack
|
||||
});
|
||||
|
||||
|
@ -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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
this.log('deleted chat position');
|
||||
}
|
||||
|
||||
sessionStorage.set({chatPositions}, true);
|
||||
stateStorage.set({chatPositions}, true);
|
||||
//}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
apiManager,
|
||||
appDraftsManager,
|
||||
serverTimeManager,
|
||||
sessionStorage,
|
||||
stateStorage,
|
||||
appNotificationsManager,
|
||||
appEmojiManager
|
||||
);
|
||||
|
@ -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 {
|
||||
}
|
||||
|
||||
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];
|
||||
|
@ -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';
|
||||
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<{
|
||||
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<{
|
||||
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<{
|
||||
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<{
|
||||
this.state[key] = value;
|
||||
}
|
||||
|
||||
sessionStorage.set({
|
||||
this.storage.set({
|
||||
[key]: value
|
||||
});
|
||||
}
|
||||
|
@ -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[]>} = {};
|
||||
|
@ -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 = {
|
||||
|
||||
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,8 +79,11 @@ export default class IDBStorage {
|
||||
public static deleteDatabase() {
|
||||
this.closeDatabases();
|
||||
|
||||
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(Database.name);
|
||||
const deleteRequest = indexedDB.deleteDatabase(dbName);
|
||||
|
||||
deleteRequest.onerror = () => {
|
||||
reject();
|
||||
@ -85,6 +93,9 @@ export default class IDBStorage {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
public isAvailable() {
|
||||
|
@ -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 {
|
||||
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 {
|
||||
export function saveLangPack(langPack: LangPackDifference) {
|
||||
langPack.appVersion = App.langPackVersion;
|
||||
|
||||
return sessionStorage.set({langPack}).then(() => {
|
||||
return stateStorage.set({langPack}).then(() => {
|
||||
applyLangPack(langPack);
|
||||
return langPack;
|
||||
});
|
||||
|
@ -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';
|
||||
@ -105,16 +105,38 @@ export class ApiManager {
|
||||
}
|
||||
} */
|
||||
|
||||
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 {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
||||
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 {
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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
Normal file
23
src/lib/stateStorage.ts
Normal file
@ -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;
|
@ -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
|
||||
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
|
||||
//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
|
||||
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
|
||||
}, (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
|
||||
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
|
||||
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
|
||||
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);
|
||||
|
@ -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…
Reference in New Issue
Block a user