diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index 862c0356..d89d021e 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -310,7 +310,7 @@ export class AppSidebarLeft extends SidebarSlider { rootScope.addEventListener('folder_unread', (folder) => { if(folder.id === 1) { // const count = folder.unreadMessagesCount; - const count = folder.unreadDialogsCount; + const count = folder.unreadPeerIds.size; this.archivedCount.innerText = '' + formatNumber(count, 1); this.archivedCount.classList.toggle('hide', !count); } diff --git a/src/index.hbs b/src/index.hbs index 9f415faf..dbf6be10 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -3,26 +3,45 @@ - Telegram Web - + {{htmlWebpackPlugin.options.title}} + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{#each htmlWebpackPlugin.files.css}} {{/each}} diff --git a/src/index.ts b/src/index.ts index 770caf66..79150f8c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,9 @@ document.addEventListener('DOMContentLoaded', async() => { rootScope.managers = getProxiedManagers(); + const manifest = document.getElementById('manifest') as HTMLLinkElement; + manifest.href = `site${IS_APPLE && !IS_APPLE_MOBILE ? '_apple' : ''}.webmanifest?v=jw3mK7G9Aq`; + singleInstance.start(); // We listen to the resize event (https://css-tricks.com/the-trick-to-viewport-units-on-mobile/) diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 199b8528..c0957828 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -205,7 +205,6 @@ export class AppDialogsManager { } } = {}; private showFiltersPromise: Promise; - private allUnreadCount: HTMLElement; private sliceTimeout: number; @@ -236,8 +235,6 @@ export class AppDialogsManager { this.contextMenu = new DialogsContextMenu(managers); - this.allUnreadCount = this.folders.menu.querySelector('.badge'); - this.folders.menuScrollContainer = this.folders.menu.parentElement; this.onListLengthChange = debounce(this._onListLengthChange, 100, false, true); @@ -524,7 +521,6 @@ export class AppDialogsManager { rootScope.addEventListener('dialog_notify_settings', (dialog) => { this.validateDialogForFilter(dialog); this.setUnreadMessagesN({dialog}); // возможно это не нужно, но нужно менять is-muted - this.setFiltersUnreadCount(); }); rootScope.addEventListener('dialog_draft', ({dialog, drop, peerId}) => { @@ -667,20 +663,6 @@ export class AppDialogsManager { } } - if(state.notifySettings) { - const promises: Promise[] = []; - for(const key in state.notifySettings) { - assumeType>(key); - const promise = this.managers.appNotificationsManager.savePeerSettings({ - key, - settings: state.notifySettings[key] - }); - promises.push(promise); - } - - await Promise.all(promises); - } - this.managers.appNotificationsManager.getNotifyPeerTypeSettings(); await (await loadDialogsPromise).renderPromise; @@ -755,13 +737,17 @@ export class AppDialogsManager { }; private async setFilterUnreadCount(filterId: number) { - const unreadSpan = filterId === 0 ? this.allUnreadCount : this.filtersRendered[filterId]?.unread; + if(filterId === 0) { + return; + } + + const unreadSpan = this.filtersRendered[filterId]?.unread; if(!unreadSpan) { return; } - const {foundUnmuted, unreadCount} = await this.managers.dialogsStorage.getFolderUnreadCount(filterId); - unreadSpan.classList.toggle('badge-gray', !foundUnmuted); + const {unreadUnmutedCount, unreadCount} = await this.managers.dialogsStorage.getFolderUnreadCount(filterId); + unreadSpan.classList.toggle('badge-gray', !unreadUnmutedCount); unreadSpan.innerText = unreadCount ? '' + unreadCount : ''; } diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 3dba9d0b..47762baf 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -3113,18 +3113,14 @@ export class AppMessagesManager extends AppManager { if(!this.migratedFromTo[migrateFrom] && !this.migratedToFrom[migrateTo] && this.appChatsManager.hasChat(migrateTo.toChatId())) { - const fromChat = this.appChatsManager.getChat(migrateFrom.toChatId()); - if(fromChat && - fromChat.migrated_to && - fromChat.migrated_to.channel_id === migrateTo.toChatId()) { - this.migratedFromTo[migrateFrom] = migrateTo; - this.migratedToFrom[migrateTo] = migrateFrom; + const fromChat: Chat.chat = this.appChatsManager.getChat(migrateFrom.toChatId()); + if(fromChat?.migrated_to && (fromChat.migrated_to as InputChannel.inputChannel).channel_id === migrateTo.toChatId()) { + this.migratedFromTo[migrateFrom] = migrateTo; + this.migratedToFrom[migrateTo] = migrateFrom; - //setTimeout(() => { - this.rootScope.dispatchEvent('dialog_migrate', {migrateFrom, migrateTo}); + this.rootScope.dispatchEvent('dialog_migrate', {migrateFrom, migrateTo}); - this.dialogsStorage.dropDialogWithEvent(migrateFrom); - //}, 100); + this.dialogsStorage.dropDialogWithEvent(migrateFrom); } } } diff --git a/src/lib/appManagers/appNotificationsManager.ts b/src/lib/appManagers/appNotificationsManager.ts index 5d79717b..31df2959 100644 --- a/src/lib/appManagers/appNotificationsManager.ts +++ b/src/lib/appManagers/appNotificationsManager.ts @@ -17,6 +17,7 @@ import convertInputKeyToKey from "../../helpers/string/convertInputKeyToKey"; import { AppManager } from "./manager"; import getPeerId from "./utils/peers/getPeerId"; import ctx from "../../environment/ctx"; +import assumeType from "../../helpers/assumeType"; type ImSadAboutIt = Promise | PeerNotifySettings; export class AppNotificationsManager extends AppManager { @@ -50,6 +51,18 @@ export class AppNotificationsManager extends AppManager { this.rootScope.dispatchEvent('notify_settings', update); } }); + + return this.appStateManager.getState().then((state) => { + if(state.notifySettings) { + for(const key in state.notifySettings) { + assumeType>(key); + this.savePeerSettings({ + key, + settings: state.notifySettings[key] + }); + } + } + }); } public getNotifySettings(peer: InputNotifyPeer): ImSadAboutIt { diff --git a/src/lib/appManagers/createManagers.ts b/src/lib/appManagers/createManagers.ts index b485c610..79e7704b 100644 --- a/src/lib/appManagers/createManagers.ts +++ b/src/lib/appManagers/createManagers.ts @@ -108,7 +108,7 @@ export default function createManagers(appStoragesManager: AppStoragesManager, u const promises: Array void) | void> | void>[] = []; let names = Object.keys(managers) as (keyof T)[]; - names.unshift('appUsersManager', 'appChatsManager', 'appMessagesManager', 'dialogsStorage'); + names.unshift('appUsersManager', 'appChatsManager', 'appNotificationsManager', 'appMessagesManager', 'dialogsStorage'); names = filterUnique(names); for(const name of names) { const manager = managers[name]; diff --git a/src/lib/appManagers/uiNotificationsManager.ts b/src/lib/appManagers/uiNotificationsManager.ts index 4d71394e..1994ce34 100644 --- a/src/lib/appManagers/uiNotificationsManager.ts +++ b/src/lib/appManagers/uiNotificationsManager.ts @@ -82,11 +82,14 @@ export class UiNotificationsManager { private pushInited = false; private managers: AppManagers; + private setAppBadge: (contents?: any) => Promise; construct(managers: AppManagers) { this.managers = managers; navigator.vibrate = navigator.vibrate || (navigator as any).mozVibrate || (navigator as any).webkitVibrate; + this.setAppBadge = (navigator as any).setAppBadge && (navigator as any).setAppBadge.bind(navigator); + this.setAppBadge && this.setAppBadge(0); this.notificationsUiSupport = ('Notification' in window) || ('mozNotification' in navigator); @@ -125,6 +128,14 @@ export class UiNotificationsManager { rootScope.addEventListener('notification_cancel', (str) => { this.cancel(str); }); + + if(this.setAppBadge) { + rootScope.addEventListener('folder_unread', (folder) => { + if(folder.id === 0) { + this.setAppBadge(folder.unreadUnmutedPeerIds.size); + } + }); + } webPushApiManager.addEventListener('push_init', (tokenData) => { this.pushInited = true; @@ -280,7 +291,7 @@ export class UiNotificationsManager { private toggleToggler(enable = idleController.isIdle) { if(IS_MOBILE) return; - const resetTitle = () => { + const resetTitle = (isBlink?: boolean) => { this.titleChanged = false; document.title = this.titleBackup; this.setFavicon(); @@ -297,7 +308,7 @@ export class UiNotificationsManager { if(!count) { this.toggleToggler(false); } else if(this.titleChanged) { - resetTitle(); + resetTitle(true); } else { this.titleChanged = true; document.title = I18n.format('Notifications.Count', true, [count]); diff --git a/src/lib/storages/dialogs.ts b/src/lib/storages/dialogs.ts index 30dde67d..8e392916 100644 --- a/src/lib/storages/dialogs.ts +++ b/src/lib/storages/dialogs.ts @@ -45,7 +45,8 @@ export type Folder = { dialogs: Dialog[], id: number, unreadMessagesCount: number, - unreadDialogsCount: number, + unreadPeerIds: Set, + unreadUnmutedPeerIds: Set, dispatchUnreadTimeout?: number }; @@ -131,6 +132,7 @@ export default class DialogsStorage extends AppManager { this.rootScope.addEventListener('dialog_notify_settings', (dialog) => { this.processDialogForFilters(dialog); + this.prepareDialogUnreadCountModifying(dialog)(); }); this.rootScope.addEventListener('chat_update', (chatId) => { @@ -287,7 +289,14 @@ export default class DialogsStorage extends AppManager { public getFolder(id: number) { let folder = this.folders[id]; if(!folder) { - folder = this.folders[id] = {dialogs: [], id, unreadMessagesCount: 0, unreadDialogsCount: 0}; + folder = this.folders[id] = { + dialogs: [], + id, + unreadMessagesCount: 0, + unreadPeerIds: new Set(), + unreadUnmutedPeerIds: new Set() + }; + defineNotNumerableProperties(folder, ['dispatchUnreadTimeout']); } @@ -326,14 +335,14 @@ export default class DialogsStorage extends AppManager { const filter = this.filtersStorage.getFilter(filterId); return getDialogIndexKey(filter.orderIndex); } + + public isPeerUnmuted(peerId: PeerId) { + return !this.appNotificationsManager.isPeerLocalMuted(peerId, true); + } public getFolderUnreadCount(filterId: number) { const folder = this.getFolder(filterId); - const foundUnmuted = filterId === 0 || !!folder.dialogs.find((dialog) => { - return (dialog.unread_count || dialog.pFlags.unread_mark) && !this.appNotificationsManager.isPeerLocalMuted(dialog.peerId, true); - }); - - return {foundUnmuted, unreadCount: folder.unreadDialogsCount}; + return {unreadUnmutedCount: folder.unreadUnmutedPeerIds.size, unreadCount: folder.unreadPeerIds.size}; } public getCachedDialogs(skipMigrated?: boolean) { @@ -435,7 +444,7 @@ export default class DialogsStorage extends AppManager { const newDialogIndex = this.setDialogIndexInFilter(dialog, indexKey, filter); if(wasDialogIndex === newDialogIndex) { - return; + return false; } if((!wasDialogIndex && newDialogIndex) || (wasIndex && !newDialogIndex)) { @@ -449,6 +458,8 @@ export default class DialogsStorage extends AppManager { if(newDialogIndex) { insertInDescendSortedArray(dialogs, dialog, (dialog) => this.getDialogIndex(dialog, indexKey), -1); } + + return true; } public prepareDialogUnreadCountModifying(dialog: Dialog) { @@ -469,32 +480,46 @@ export default class DialogsStorage extends AppManager { public prepareFolderUnreadCountModifyingByDialog(folderId: number, dialog: Dialog, toggle?: boolean) { const wasUnreadCount = this.appMessagesManager.getDialogUnreadCount(dialog); + const wasUnmuted = this.isPeerUnmuted(dialog.peerId); if(toggle !== undefined) { - this.modifyFolderUnreadCount(folderId, toggle ? wasUnreadCount : -wasUnreadCount, wasUnreadCount ? (toggle ? 1 : -1) : 0); + const addMessagesCount = toggle ? wasUnreadCount : -wasUnreadCount; + this.modifyFolderUnreadCount(folderId, addMessagesCount, !!wasUnreadCount, wasUnreadCount && wasUnmuted, dialog); return; } return () => { const newUnreadCount = this.appMessagesManager.getDialogUnreadCount(dialog); + const newUnmuted = this.isPeerUnmuted(dialog.peerId); + const addMessagesCount = newUnreadCount - wasUnreadCount; - const addDialogsCount = (newUnreadCount && !wasUnreadCount) || (!newUnreadCount && wasUnreadCount) ? (wasUnreadCount ? -1 : 1) : 0; - this.modifyFolderUnreadCount(folderId, addMessagesCount, addDialogsCount); + this.modifyFolderUnreadCount(folderId, addMessagesCount, !!newUnreadCount, newUnreadCount && newUnmuted, dialog); }; } - public modifyFolderUnreadCount(folderId: number, addMessagesCount: number, addDialogsCount: number) { - if(!addMessagesCount && !addDialogsCount) { - return; - } - + public modifyFolderUnreadCount( + folderId: number, + addMessagesCount: number, + toggleDialog: boolean, + toggleUnmuted: boolean, + dialog: Dialog + ) { const folder = this.getFolder(folderId); if(addMessagesCount) { folder.unreadMessagesCount = Math.max(0, folder.unreadMessagesCount + addMessagesCount); } - if(addDialogsCount) { - folder.unreadDialogsCount = Math.max(0, folder.unreadDialogsCount + addDialogsCount); + const {peerId} = dialog; + if(toggleDialog) { + folder.unreadPeerIds.add(peerId); + } else { + folder.unreadPeerIds.delete(peerId); + } + + if(toggleUnmuted) { + folder.unreadUnmutedPeerIds.add(peerId); + } else { + folder.unreadUnmutedPeerIds.delete(peerId); } if(folder.dispatchUnreadTimeout === undefined) { diff --git a/webpack.common.js b/webpack.common.js index 07a6a442..5e617dc3 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -241,6 +241,9 @@ module.exports = { // }), new HtmlWebpackPlugin({ + title: 'Telegram Web', + description: 'Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.', + url: 'https://web.telegram.org/k/', filename: 'index.html', //template: 'public/index_template.html', template: 'src/index.hbs',