FIlters changes:
Handle reordering Handle updateDialogFilters Ordering in folder settings Fix contacts list loading after state if it is empty
This commit is contained in:
parent
8f841344eb
commit
6c36941b41
@ -2,8 +2,6 @@ import { findUpTag, whichChild } from "../helpers/dom";
|
||||
import Transition from "./transition";
|
||||
|
||||
export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?: (id: number, tabContent: HTMLDivElement) => void, onTransitionEnd?: () => void, transitionTime = 250) {
|
||||
let prevId = -1;
|
||||
|
||||
const selectTab = Transition(content, tabs || content.dataset.slider == 'tabs' ? 'tabs' : 'navigation', transitionTime, onTransitionEnd);
|
||||
|
||||
if(tabs) {
|
||||
@ -34,7 +32,7 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
|
||||
const tabContent = content.children[id] as HTMLDivElement;
|
||||
|
||||
if(onClick) onClick(id, tabContent);
|
||||
if(target.classList.contains('active') || id == prevId) {
|
||||
if(target.classList.contains('active') || id == selectTab.prevId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -42,9 +40,9 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
|
||||
prev && prev.classList.remove('active');
|
||||
|
||||
// stripe from ZINCHUK
|
||||
if(useStripe && prevId != -1) {
|
||||
if(useStripe && selectTab.prevId != -1) {
|
||||
const indicator = target.querySelector('i')!;
|
||||
const currentIndicator = target.parentElement.children[prevId].querySelector('i')!;
|
||||
const currentIndicator = target.parentElement.children[selectTab.prevId].querySelector('i')!;
|
||||
|
||||
currentIndicator.classList.remove('animate');
|
||||
indicator.classList.remove('animate');
|
||||
@ -66,8 +64,6 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
|
||||
|
||||
target.classList.add('active');
|
||||
selectTab(id);
|
||||
|
||||
prevId = id;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import { MOUNT_CLASS_TO } from "../../lib/mtproto/mtproto_config";
|
||||
import $rootScope from "../../lib/rootScope";
|
||||
import { findUpClassName, findUpTag } from "../../helpers/dom";
|
||||
import AppSearch, { SearchGroup } from "../appSearch";
|
||||
import AvatarElement from "../avatar";
|
||||
import "../avatar";
|
||||
import { parseMenuButtonsTo } from "../misc";
|
||||
import { ScrollableX } from "../scrollable";
|
||||
import SearchInput from "../searchInput";
|
||||
@ -26,8 +26,8 @@ import AppIncludedChatsTab from "./tabs/includedChats";
|
||||
import AppNewChannelTab from "./tabs/newChannel";
|
||||
import AppNewGroupTab from "./tabs/newGroup";
|
||||
import AppSettingsTab from "./tabs/settings";
|
||||
|
||||
AvatarElement;
|
||||
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
|
||||
import apiManagerProxy from "../../lib/mtproto/mtprotoworker";
|
||||
|
||||
const newChannelTab = new AppNewChannelTab();
|
||||
const addMembersTab = new AppAddMembersTab();
|
||||
@ -35,7 +35,6 @@ const contactsTab = new AppContactsTab();
|
||||
const newGroupTab = new AppNewGroupTab();
|
||||
const settingsTab = new AppSettingsTab();
|
||||
const editProfileTab = new AppEditProfileTab();
|
||||
const chatFoldersTab = new AppChatFoldersTab();
|
||||
const editFolderTab = new AppEditFolderTab();
|
||||
const includedChatsTab = new AppIncludedChatsTab();
|
||||
const archivedTab = new AppArchivedTab();
|
||||
@ -124,7 +123,9 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
private recentSearchClearBtn: HTMLElement;
|
||||
|
||||
constructor() {
|
||||
super(document.getElementById('column-left') as HTMLDivElement, {
|
||||
super(document.getElementById('column-left') as HTMLDivElement);
|
||||
|
||||
Object.assign(this.tabs, {
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.archived]: archivedTab,
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.newChannel]: newChannelTab,
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.contacts]: contactsTab,
|
||||
@ -132,7 +133,7 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.newGroup]: newGroupTab,
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.settings]: settingsTab,
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.editProfile]: editProfileTab,
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.chatFolders]: chatFoldersTab,
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.chatFolders]: this.chatFoldersTab = new AppChatFoldersTab(appMessagesManager, appPeersManager, this, apiManagerProxy, $rootScope),
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.editFolder]: editFolderTab,
|
||||
[AppSidebarLeft.SLIDERITEMSIDS.includedChats]: includedChatsTab,
|
||||
});
|
||||
@ -153,7 +154,6 @@ export class AppSidebarLeft extends SidebarSlider {
|
||||
this.newGroupTab = newGroupTab;
|
||||
this.settingsTab = settingsTab;
|
||||
this.editProfileTab = editProfileTab;
|
||||
this.chatFoldersTab = chatFoldersTab;
|
||||
this.editFolderTab = editFolderTab;
|
||||
this.includedChatsTab = includedChatsTab;
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { SliderTab } from "../../slider";
|
||||
import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader";
|
||||
import apiManager from "../../../lib/mtproto/mtprotoworker";
|
||||
import appMessagesManager, { MyDialogFilter } from "../../../lib/appManagers/appMessagesManager";
|
||||
import { RichTextProcessor } from "../../../lib/richtextprocessor";
|
||||
import appPeersManager from "../../../lib/appManagers/appPeersManager";
|
||||
import { cancelEvent } from "../../../helpers/dom";
|
||||
import appSidebarLeft from "..";
|
||||
import { cancelEvent, positionElementByIndex } from "../../../helpers/dom";
|
||||
import { ripple } from "../../ripple";
|
||||
import { toast } from "../../toast";
|
||||
import { DialogFilterSuggested, DialogFilter } from "../../../layer";
|
||||
import $rootScope from "../../../lib/rootScope";
|
||||
import type { ApiManagerProxy } from "../../../lib/mtproto/mtprotoworker";
|
||||
import type { AppMessagesManager } from "../../../lib/appManagers/appMessagesManager";
|
||||
import type { MyDialogFilter } from "../../../lib/storages/filters";
|
||||
import type { AppPeersManager } from "../../../lib/appManagers/appPeersManager";
|
||||
import type { AppSidebarLeft } from "..";
|
||||
import type { DialogFilterSuggested, DialogFilter } from "../../../layer";
|
||||
import type _$rootScope from "../../../lib/rootScope";
|
||||
|
||||
export default class AppChatFoldersTab implements SliderTab {
|
||||
public container: HTMLElement;
|
||||
@ -21,6 +22,10 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
|
||||
private filtersRendered: {[filterID: number]: HTMLElement} = {};
|
||||
|
||||
constructor(private appMessagesManager: AppMessagesManager, private appPeersManager: AppPeersManager, private appSidebarLeft: AppSidebarLeft, private apiManager: ApiManagerProxy, private $rootScope: typeof _$rootScope) {
|
||||
|
||||
}
|
||||
|
||||
private renderFolder(dialogFilter: DialogFilterSuggested | DialogFilter | MyDialogFilter, container?: HTMLElement, div: HTMLElement = document.createElement('div')) {
|
||||
let filter: DialogFilter | MyDialogFilter;
|
||||
let description = '';
|
||||
@ -35,7 +40,7 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
const filterID = filter.id;
|
||||
if(!this.filtersRendered.hasOwnProperty(filter.id)) {
|
||||
div.addEventListener('click', () => {
|
||||
appSidebarLeft.editFolderTab.open(appMessagesManager.filtersStorage.filters[filterID]);
|
||||
this.appSidebarLeft.editFolderTab.open(this.appMessagesManager.filtersStorage.filters[filterID]);
|
||||
});
|
||||
}
|
||||
|
||||
@ -60,11 +65,11 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
else if(pFlags.exclude_archived) description += 'Unarchived';
|
||||
d.push(description);
|
||||
} else {
|
||||
const folder = appMessagesManager.dialogsStorage.getFolder(filter.id);
|
||||
const folder = this.appMessagesManager.dialogsStorage.getFolder(filter.id);
|
||||
let chats = 0, channels = 0, groups = 0;
|
||||
for(const dialog of folder) {
|
||||
if(appPeersManager.isAnyGroup(dialog.peerID)) groups++;
|
||||
else if(appPeersManager.isBroadcast(dialog.peerID)) channels++;
|
||||
if(this.appPeersManager.isAnyGroup(dialog.peerID)) groups++;
|
||||
else if(this.appPeersManager.isBroadcast(dialog.peerID)) channels++;
|
||||
else chats++;
|
||||
}
|
||||
|
||||
@ -83,7 +88,11 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
`;
|
||||
ripple(div);
|
||||
|
||||
if(container) container.append(div);
|
||||
if((filter as MyDialogFilter).hasOwnProperty('orderIndex')) {
|
||||
// ! header will be at 0 index
|
||||
positionElementByIndex(div, div.parentElement || container, (filter as MyDialogFilter).orderIndex);
|
||||
} else if(container) container.append(div);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
@ -98,7 +107,7 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
if(Object.keys(this.filtersRendered).length >= 10) {
|
||||
toast('Sorry, you can\'t create more folders.');
|
||||
} else {
|
||||
appSidebarLeft.editFolderTab.open();
|
||||
this.appSidebarLeft.editFolderTab.open();
|
||||
}
|
||||
});
|
||||
|
||||
@ -112,14 +121,13 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
this.animation = player;
|
||||
});
|
||||
|
||||
appMessagesManager.filtersStorage.getDialogFilters().then(filters => {
|
||||
for(const filterID in filters) {
|
||||
const filter = filters[filterID];
|
||||
this.appMessagesManager.filtersStorage.getDialogFilters().then(filters => {
|
||||
for(const filter of filters) {
|
||||
this.renderFolder(filter, this.foldersContainer);
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('filter_update', (e) => {
|
||||
this.$rootScope.$on('filter_update', (e) => {
|
||||
const filter = e.detail;
|
||||
if(this.filtersRendered.hasOwnProperty(filter.id)) {
|
||||
this.renderFolder(filter, null, this.filtersRendered[filter.id]);
|
||||
@ -130,7 +138,7 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
this.getSuggestedFilters();
|
||||
});
|
||||
|
||||
$rootScope.$on('filter_delete', (e) => {
|
||||
this.$rootScope.$on('filter_delete', (e) => {
|
||||
const filter = e.detail;
|
||||
if(this.filtersRendered.hasOwnProperty(filter.id)) {
|
||||
/* for(const suggested of this.suggestedFilters) {
|
||||
@ -145,11 +153,19 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
}
|
||||
});
|
||||
|
||||
this.$rootScope.$on('filter_order', (e) => {
|
||||
const order = e.detail;
|
||||
order.forEach((filterID, idx) => {
|
||||
const div = this.filtersRendered[filterID];
|
||||
positionElementByIndex(div, div.parentElement, idx + 1); // ! + 1 due to header
|
||||
});
|
||||
});
|
||||
|
||||
this.getSuggestedFilters();
|
||||
}
|
||||
|
||||
private getSuggestedFilters() {
|
||||
apiManager.invokeApi('messages.getSuggestedDialogFilters').then(suggestedFilters => {
|
||||
this.apiManager.invokeApi('messages.getSuggestedDialogFilters').then(suggestedFilters => {
|
||||
this.suggestedContainer.style.display = suggestedFilters.length ? '' : 'none';
|
||||
Array.from(this.suggestedContainer.children).slice(1).forEach(el => el.remove());
|
||||
|
||||
@ -171,7 +187,7 @@ export default class AppChatFoldersTab implements SliderTab {
|
||||
|
||||
button.setAttribute('disabled', 'true');
|
||||
|
||||
appMessagesManager.filtersStorage.createDialogFilter(filter.filter as any).then(bool => {
|
||||
this.appMessagesManager.filtersStorage.createDialogFilter(filter.filter as any).then(bool => {
|
||||
if(bool) {
|
||||
div.remove();
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import appSidebarLeft, { AppSidebarLeft } from "..";
|
||||
import { deepEqual, copy } from "../../../helpers/object";
|
||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||
import appMessagesManager, { MyDialogFilter as DialogFilter } from "../../../lib/appManagers/appMessagesManager";
|
||||
import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters";
|
||||
import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader";
|
||||
import { parseMenuButtonsTo } from "../../misc";
|
||||
import { ripple } from "../../ripple";
|
||||
import { SliderTab } from "../../slider";
|
||||
import { toast } from "../../toast";
|
||||
import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
|
||||
|
||||
const MAX_FOLDER_NAME_LENGTH = 12;
|
||||
|
||||
|
@ -4,7 +4,7 @@ import appSidebarLeft, { AppSidebarLeft } from "..";
|
||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||
import appPeersManager from "../../../lib/appManagers/appPeersManager";
|
||||
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
||||
import { MyDialogFilter as DialogFilter } from "../../../lib/appManagers/appMessagesManager";
|
||||
import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters";
|
||||
import $rootScope from "../../../lib/rootScope";
|
||||
import { copy } from "../../../helpers/object";
|
||||
|
||||
|
@ -13,7 +13,7 @@ export default class SidebarSlider {
|
||||
protected _selectTab: (id: number) => void;
|
||||
public historyTabIDs: number[] = [];
|
||||
|
||||
constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab}, canHideFirst = false) {
|
||||
constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab} = {}, canHideFirst = false) {
|
||||
this._selectTab = horizontalMenu(null, this.sidebarEl.querySelector('.sidebar-slider') as HTMLDivElement, null, null, TRANSITION_TIME);
|
||||
if(!canHideFirst) {
|
||||
this._selectTab(0);
|
||||
|
@ -31,12 +31,12 @@ const Transition = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fa
|
||||
//const deferred: (() => void)[] = [];
|
||||
let transitionEndTimeout: number;
|
||||
let prevTabContent: HTMLElement = null;
|
||||
let prevId = -1;
|
||||
|
||||
const animationFunction = type == 'zoom-fade' ? null : (type == 'tabs' ? slideTabs : slideNavigation);
|
||||
|
||||
const selectTab = (id: number, animate = true) => {
|
||||
if(id == prevId) return false;
|
||||
function selectTab(id: number, animate = true) {
|
||||
const self = selectTab;
|
||||
if(id == self.prevId) return false;
|
||||
|
||||
//console.log('selectTab id:', id);
|
||||
|
||||
@ -51,10 +51,10 @@ const Transition = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fa
|
||||
|
||||
tabContent.classList.add('active');
|
||||
|
||||
prevId = id;
|
||||
self.prevId = id;
|
||||
prevTabContent = tabContent;
|
||||
|
||||
if(onTransitionEnd) onTransitionEnd(prevId);
|
||||
if(onTransitionEnd) onTransitionEnd(self.prevId);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -64,12 +64,12 @@ const Transition = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fa
|
||||
}
|
||||
|
||||
content.classList.add('animating');
|
||||
const toRight = prevId < id;
|
||||
const toRight = self.prevId < id;
|
||||
content.classList.toggle('backwards', !toRight);
|
||||
|
||||
if(!tabContent) {
|
||||
//prevTabContent.classList.remove('active');
|
||||
} else if(prevId != -1) {
|
||||
} else if(self.prevId != -1) {
|
||||
if(animationFunction) {
|
||||
animationFunction(tabContent, prevTabContent, toRight);
|
||||
}
|
||||
@ -79,7 +79,7 @@ const Transition = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fa
|
||||
tabContent.classList.add('active');
|
||||
}
|
||||
|
||||
const _prevId = prevId;
|
||||
const _prevId = self.prevId;
|
||||
if(hideTimeouts.hasOwnProperty(id)) clearTimeout(hideTimeouts[id]);
|
||||
if(p/* && false */) {
|
||||
hideTimeouts[_prevId] = window.setTimeout(() => {
|
||||
@ -99,13 +99,13 @@ const Transition = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fa
|
||||
if(onTransitionEnd) {
|
||||
if(transitionEndTimeout) clearTimeout(transitionEndTimeout);
|
||||
transitionEndTimeout = window.setTimeout(() => {
|
||||
onTransitionEnd(prevId);
|
||||
onTransitionEnd(self.prevId);
|
||||
transitionEndTimeout = 0;
|
||||
}, transitionTime);
|
||||
}
|
||||
}
|
||||
|
||||
prevId = id;
|
||||
self.prevId = id;
|
||||
prevTabContent = tabContent;
|
||||
|
||||
/* if(p) {
|
||||
@ -115,7 +115,9 @@ const Transition = (content: HTMLElement, type: 'tabs' | 'navigation' | 'zoom-fa
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
} */
|
||||
};
|
||||
}
|
||||
|
||||
selectTab.prevId = -1;
|
||||
|
||||
return selectTab;
|
||||
};
|
||||
|
@ -15,7 +15,8 @@ import { RichTextProcessor } from "../richtextprocessor";
|
||||
import $rootScope from "../rootScope";
|
||||
import { findUpClassName, positionElementByIndex } from "../../helpers/dom";
|
||||
import appImManager, { AppImManager } from "./appImManager";
|
||||
import appMessagesManager, { Dialog, MyDialogFilter as DialogFilter } from "./appMessagesManager";
|
||||
import appMessagesManager, { Dialog } from "./appMessagesManager";
|
||||
import {MyDialogFilter as DialogFilter} from "../storages/filters";
|
||||
import appPeersManager from './appPeersManager';
|
||||
import appStateManager from "./appStateManager";
|
||||
import appUsersManager, { User } from "./appUsersManager";
|
||||
@ -35,14 +36,11 @@ type DialogDom = {
|
||||
};
|
||||
|
||||
const testScroll = false;
|
||||
//const USEPINNEDDELIMITER = false;
|
||||
|
||||
export class AppDialogsManager {
|
||||
public _chatList = document.getElementById('dialogs') as HTMLUListElement;
|
||||
public chatList = this._chatList;
|
||||
|
||||
//public pinnedDelimiter: HTMLDivElement;
|
||||
|
||||
|
||||
public doms: {[peerID: number]: DialogDom} = {};
|
||||
public lastActiveListElement: HTMLElement = null;
|
||||
|
||||
@ -91,12 +89,6 @@ export class AppDialogsManager {
|
||||
|
||||
this.allUnreadCount = this.folders.menu.querySelector('.unread-count');
|
||||
|
||||
/* if(USEPINNEDDELIMITER) {
|
||||
this.pinnedDelimiter = document.createElement('div');
|
||||
this.pinnedDelimiter.classList.add('pinned-delimiter');
|
||||
this.pinnedDelimiter.appendChild(document.createElement('span'));
|
||||
} */
|
||||
|
||||
this.folders.menuScrollContainer = this.folders.menu.parentElement;
|
||||
|
||||
this.scroll = this._scroll = new Scrollable(this.chatsContainer, 'CL', this.chatList, 500);
|
||||
@ -165,18 +157,17 @@ export class AppDialogsManager {
|
||||
});
|
||||
|
||||
$rootScope.$on('dialog_top', (e) => {
|
||||
let dialog: any = e.detail;
|
||||
const dialog = e.detail;
|
||||
|
||||
this.setLastMessage(dialog);
|
||||
this.setDialogPosition(dialog);
|
||||
|
||||
//this.setPinnedDelimiter();
|
||||
this.setFiltersUnreadCount();
|
||||
});
|
||||
|
||||
$rootScope.$on('dialog_flush', (e) => {
|
||||
let peerID: number = e.detail.peerID;
|
||||
let dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
|
||||
const peerID: number = e.detail.peerID;
|
||||
const dialog = appMessagesManager.getDialogByPeerID(peerID)[0];
|
||||
if(dialog) {
|
||||
this.setLastMessage(dialog);
|
||||
this.validateForFilter();
|
||||
@ -192,7 +183,6 @@ export class AppDialogsManager {
|
||||
this.updateDialog(dialog);
|
||||
}
|
||||
|
||||
//this.setPinnedDelimiter();
|
||||
this.validateForFilter();
|
||||
this.setFiltersUnreadCount();
|
||||
});
|
||||
@ -291,16 +281,23 @@ export class AppDialogsManager {
|
||||
}
|
||||
});
|
||||
|
||||
/* $rootScope.$on('filter_pinned_order', (e) => {
|
||||
const {order, id} = e.detail as {order: number[], id: number};
|
||||
if(this.prevTabID != id) {
|
||||
return;
|
||||
}
|
||||
$rootScope.$on('filter_order', (e) => {
|
||||
const order = e.detail;
|
||||
|
||||
const containerToAppend = this.folders.menu.firstElementChild as HTMLUListElement;
|
||||
order.forEach((filterID) => {
|
||||
const filter = appMessagesManager.filtersStorage.filters[filterID];
|
||||
const renderedFilter = this.filtersRendered[filterID];
|
||||
|
||||
for(const peerID of order) {
|
||||
this.updateDialog(appMessagesManager.getDialogByPeerID(peerID)[0]);
|
||||
positionElementByIndex(renderedFilter.menu, containerToAppend, filter.orderIndex);
|
||||
positionElementByIndex(renderedFilter.container, this.folders.container, filter.orderIndex);
|
||||
});
|
||||
|
||||
if(this.filterID) {
|
||||
const tabIndex = order.indexOf(this.filterID) + 1;
|
||||
selectTab.prevId = tabIndex;
|
||||
}
|
||||
}); */
|
||||
});
|
||||
|
||||
const foldersScrollable = new ScrollableX(this.folders.menuScrollContainer);
|
||||
this.chatsContainer.prepend(this.folders.menuScrollContainer);
|
||||
@ -330,10 +327,10 @@ export class AppDialogsManager {
|
||||
//selectTab(0);
|
||||
(this.folders.menu.firstElementChild.firstElementChild as HTMLElement).click();
|
||||
appStateManager.getState().then((state) => {
|
||||
const getFiltersPromise = !state.filters || Object.keys(state.filters).length ? appMessagesManager.filtersStorage.getDialogFilters() : Promise.resolve({});
|
||||
const getFiltersPromise = !state.filters || Object.keys(state.filters).length ? appMessagesManager.filtersStorage.getDialogFilters() : Promise.resolve([]);
|
||||
getFiltersPromise.then((filters) => {
|
||||
for(const filterID in filters) {
|
||||
this.addFilter(filters[filterID]);
|
||||
for(const filter of filters) {
|
||||
this.addFilter(filter);
|
||||
}
|
||||
});
|
||||
|
||||
@ -433,7 +430,7 @@ export class AppDialogsManager {
|
||||
ripple(li);
|
||||
|
||||
const containerToAppend = this.folders.menu.firstElementChild as HTMLUListElement;
|
||||
positionElementByIndex(li, containerToAppend, filter.orderIndex + 1); // because 0 is All
|
||||
positionElementByIndex(li, containerToAppend, filter.orderIndex);
|
||||
//containerToAppend.append(li);
|
||||
|
||||
const ul = document.createElement('ul');
|
||||
@ -441,7 +438,7 @@ export class AppDialogsManager {
|
||||
div.append(ul);
|
||||
div.dataset.filterID = '' + filter.id;
|
||||
//this.folders.container.append(div);
|
||||
positionElementByIndex(div, this.folders.container, filter.orderIndex + 1); // because 0 is All
|
||||
positionElementByIndex(div, this.folders.container, filter.orderIndex);
|
||||
|
||||
this.chatLists[filter.id] = ul;
|
||||
this.setListClickListener(ul, null, true);
|
||||
@ -608,42 +605,6 @@ export class AppDialogsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/* public setPinnedDelimiter() {
|
||||
if(!USEPINNEDDELIMITER) return;
|
||||
|
||||
let index = -1;
|
||||
let dialogs = appMessagesManager.dialogsStorage.getFolder(0);
|
||||
for(let dialog of dialogs) {
|
||||
if(dialog.pFlags?.pinned) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
let currentIndex = (this.pinnedDelimiter.parentElement && whichChild(this.pinnedDelimiter.parentElement)) ?? -1;
|
||||
|
||||
if(index == currentIndex) return;
|
||||
|
||||
let children = this.chatList.children;
|
||||
|
||||
let modifying: HTMLElement[] = [];
|
||||
if(currentIndex != -1 && children.length > currentIndex) {
|
||||
let li = children[currentIndex] as HTMLElement;
|
||||
modifying.push(li);
|
||||
}
|
||||
|
||||
if(index != -1 && children.length > index) {
|
||||
let li = children[index] as HTMLElement;
|
||||
modifying.push(li);
|
||||
li.append(this.pinnedDelimiter);
|
||||
} else {
|
||||
this.pinnedDelimiter.remove();
|
||||
}
|
||||
|
||||
modifying.forEach(elem => {
|
||||
this.scroll.updateElement(elem);
|
||||
});
|
||||
} */
|
||||
|
||||
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom, highlightWord?: string) {
|
||||
if(!lastMessage) {
|
||||
lastMessage = appMessagesManager.getMessage(dialog.top_message);
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { MessageRender } from "../../components/chat/messageRender";
|
||||
import ProgressivePreloader from "../../components/preloader";
|
||||
import { listMergeSorted } from "../../helpers/array";
|
||||
import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise";
|
||||
@ -6,19 +5,21 @@ import { tsNow } from "../../helpers/date";
|
||||
import { copy, defineNotNumerableProperties, deepEqual, safeReplaceObject, getObjectKeysAndSort } from "../../helpers/object";
|
||||
import { randomLong } from "../../helpers/random";
|
||||
import { splitStringByLength, limitSymbols } from "../../helpers/string";
|
||||
import { Dialog as MTDialog, DialogFilter, DialogPeer, DocumentAttribute, InputMessage, Message, MessageAction, MessageEntity, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, PhotoSize, SendMessageAction, Update } from "../../layer";
|
||||
import { InvokeApiOptions, Modify } from "../../types";
|
||||
import { Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMessage, Message, MessageAction, MessageEntity, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, PhotoSize, SendMessageAction, Update } from "../../layer";
|
||||
import { InvokeApiOptions } from "../../types";
|
||||
import { langPack } from "../langPack";
|
||||
import { logger, LogLevels } from "../logger";
|
||||
import type { ApiFileManager } from '../mtproto/apiFileManager';
|
||||
//import apiManager from '../mtproto/apiManager';
|
||||
import apiManager from '../mtproto/mtprotoworker';
|
||||
import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabase";
|
||||
import { default as ServerTimeManager, default as serverTimeManager } from "../mtproto/serverTimeManager";
|
||||
import serverTimeManager from "../mtproto/serverTimeManager";
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
import $rootScope from "../rootScope";
|
||||
import searchIndexManager from '../searchIndexManager';
|
||||
import AppStorage from '../storage';
|
||||
import DialogsStorage from "../storages/dialogs";
|
||||
import FiltersStorage from "../storages/filters";
|
||||
//import { telegramMeWebService } from "../mtproto/mtproto";
|
||||
import apiUpdatesManager from "./apiUpdatesManager";
|
||||
import appChatsManager from "./appChatsManager";
|
||||
@ -32,7 +33,6 @@ import appStateManager from "./appStateManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
import appWebPagesManager from "./appWebPagesManager";
|
||||
|
||||
|
||||
//console.trace('include');
|
||||
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
|
||||
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
|
||||
@ -59,390 +59,7 @@ export type HistoryResult = {
|
||||
|
||||
export type Dialog = MTDialog.dialog;
|
||||
|
||||
export class DialogsStorage {
|
||||
public dialogs: {[peerID: string]: Dialog} = {};
|
||||
public byFolders: {[folderID: number]: Dialog[]} = {};
|
||||
|
||||
public allDialogsLoaded: {[folder_id: number]: boolean} = {};
|
||||
public dialogsOffsetDate: {[folder_id: number]: number} = {};
|
||||
public pinnedOrders: {[folder_id: number]: number[]} = {
|
||||
0: [],
|
||||
1: []
|
||||
};
|
||||
public dialogsNum = 0;
|
||||
|
||||
public getFolder(id: number) {
|
||||
if(id <= 1) {
|
||||
return this.byFolders[id] ?? (this.byFolders[id] = []);
|
||||
}
|
||||
|
||||
const dialogs: {dialog: Dialog, index: number}[] = [];
|
||||
const filter = appMessagesManager.filtersStorage.filters[id];
|
||||
|
||||
for(const peerID in this.dialogs) {
|
||||
const dialog = this.dialogs[peerID];
|
||||
if(appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) {
|
||||
let index: number;
|
||||
|
||||
const pinnedIndex = filter.pinned_peers.indexOf(dialog.peerID);
|
||||
if(pinnedIndex !== -1) {
|
||||
index = this.generateDialogIndex(this.generateDialogPinnedDateByIndex(filter.pinned_peers.length - 1 - pinnedIndex));
|
||||
} else if(dialog.pFlags?.pinned) {
|
||||
index = this.generateIndexForDialog(dialog, true);
|
||||
} else {
|
||||
index = dialog.index;
|
||||
}
|
||||
|
||||
dialogs.push({dialog, index});
|
||||
}
|
||||
}
|
||||
|
||||
dialogs.sort((a, b) => b.index - a.index);
|
||||
return dialogs.map(d => d.dialog);
|
||||
}
|
||||
|
||||
public getDialog(peerID: number, folderID?: number): [Dialog, number] | [] {
|
||||
const folders: Dialog[][] = [];
|
||||
|
||||
if(folderID === undefined) {
|
||||
const dialogs = this.byFolders;
|
||||
for(const folderID in dialogs) {
|
||||
folders.push(dialogs[folderID]);
|
||||
}
|
||||
} else {
|
||||
folders.push(this.getFolder(folderID));
|
||||
}
|
||||
|
||||
for(let folder of folders) {
|
||||
const index = folder.findIndex(dialog => dialog.peerID == peerID);
|
||||
if(index !== -1) {
|
||||
return [folder[index], index];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/*
|
||||
var date = Date.now() / 1000 | 0;
|
||||
var m = date * 0x10000;
|
||||
|
||||
var k = (date + 1) * 0x10000;
|
||||
k - m;
|
||||
65536
|
||||
*/
|
||||
public generateDialogIndex(date?: number) {
|
||||
if(date === undefined) {
|
||||
date = tsNow(true) + serverTimeManager.serverTimeOffset;
|
||||
}
|
||||
|
||||
return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF);
|
||||
}
|
||||
|
||||
public generateIndexForDialog(dialog: Dialog, justReturn = false) {
|
||||
const channelID = appPeersManager.isChannel(dialog.peerID) ? -dialog.peerID : 0;
|
||||
const mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
|
||||
const message = appMessagesManager.getMessage(mid);
|
||||
|
||||
let topDate = (message as Message.message).date || Date.now() / 1000;
|
||||
if(channelID) {
|
||||
const channel = appChatsManager.getChat(channelID);
|
||||
if(!topDate || channel.date && channel.date > topDate) {
|
||||
topDate = channel.date;
|
||||
}
|
||||
}
|
||||
|
||||
const savedDraft: any = {};// DraftsManager.saveDraft(peerID, dialog.draft); // warning
|
||||
if(savedDraft && savedDraft.date > topDate) {
|
||||
topDate = savedDraft.date;
|
||||
}
|
||||
|
||||
if(dialog.pFlags.pinned && !justReturn) {
|
||||
topDate = this.generateDialogPinnedDate(dialog);
|
||||
//this.log('topDate', peerID, topDate);
|
||||
}
|
||||
|
||||
const index = this.generateDialogIndex(topDate);
|
||||
if(justReturn) return index;
|
||||
dialog.index = index;
|
||||
}
|
||||
|
||||
public generateDialogPinnedDateByIndex(pinnedIndex: number) {
|
||||
return 0x7fff0000 + (pinnedIndex & 0xFFFF); // 0xFFFF - потому что в папках может быть бесконечное число пиннедов
|
||||
}
|
||||
|
||||
public generateDialogPinnedDate(dialog: Dialog) {
|
||||
const order = this.pinnedOrders[dialog.folder_id];
|
||||
|
||||
const foundIndex = order.indexOf(dialog.peerID);
|
||||
const pinnedIndex = foundIndex === -1 ? order.push(dialog.peerID) - 1 : foundIndex;
|
||||
|
||||
return this.generateDialogPinnedDateByIndex(pinnedIndex);
|
||||
}
|
||||
|
||||
public pushDialog(dialog: Dialog, offsetDate?: number) {
|
||||
const dialogs = this.getFolder(dialog.folder_id);
|
||||
const pos = dialogs.findIndex(d => d.peerID == dialog.peerID);
|
||||
if(pos !== -1) {
|
||||
dialogs.splice(pos, 1);
|
||||
}
|
||||
|
||||
//if(!this.dialogs[dialog.peerID]) {
|
||||
this.dialogs[dialog.peerID] = dialog;
|
||||
//}
|
||||
|
||||
if(offsetDate &&
|
||||
!dialog.pFlags.pinned &&
|
||||
(!this.dialogsOffsetDate[dialog.folder_id] || offsetDate < this.dialogsOffsetDate[dialog.folder_id])) {
|
||||
if(pos !== -1) {
|
||||
// So the dialog jumped to the last position
|
||||
return false;
|
||||
}
|
||||
this.dialogsOffsetDate[dialog.folder_id] = offsetDate;
|
||||
}
|
||||
|
||||
const index = dialog.index;
|
||||
const len = dialogs.length;
|
||||
if(!len || index < dialogs[len - 1].index) {
|
||||
dialogs.push(dialog);
|
||||
} else if(index >= dialogs[0].index) {
|
||||
dialogs.unshift(dialog);
|
||||
} else {
|
||||
for(let i = 0; i < len; i++) {
|
||||
if(index > dialogs[i].index) {
|
||||
dialogs.splice(i, 0, dialog);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public dropDialog(peerID: number): [Dialog, number] | [] {
|
||||
const foundDialog = this.getDialog(peerID);
|
||||
if(foundDialog[0]) {
|
||||
this.byFolders[foundDialog[0].folder_id].splice(foundDialog[1], 1);
|
||||
delete this.dialogs[peerID];
|
||||
}
|
||||
|
||||
return foundDialog;
|
||||
}
|
||||
}
|
||||
|
||||
export type MyDialogFilter = Modify<DialogFilter, {
|
||||
pinned_peers: number[],
|
||||
include_peers: number[],
|
||||
exclude_peers: number[],
|
||||
orderIndex?: number
|
||||
}>;
|
||||
|
||||
export class FiltersStorage {
|
||||
public filters: {[filterID: string]: MyDialogFilter} = {};
|
||||
public orderIndex = 0;
|
||||
|
||||
constructor() {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
this.handleUpdate(e.detail);
|
||||
});
|
||||
}
|
||||
|
||||
public handleUpdate(update: Update) {
|
||||
switch(update._) {
|
||||
case 'updateDialogFilter': {
|
||||
//console.log('updateDialogFilter', update);
|
||||
if(update.filter) {
|
||||
this.saveDialogFilter(update.filter as any);
|
||||
} else if(this.filters[update.id]) { // Папка удалена
|
||||
//this.getDialogFilters(true);
|
||||
$rootScope.$broadcast('filter_delete', this.filters[update.id]);
|
||||
delete this.filters[update.id];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public testDialogForFilter(dialog: Dialog, filter: MyDialogFilter) {
|
||||
// exclude_peers
|
||||
for(const peerID of filter.exclude_peers) {
|
||||
if(peerID == dialog.peerID) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// include_peers
|
||||
for(const peerID of filter.include_peers) {
|
||||
if(peerID == dialog.peerID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const pFlags = filter.pFlags;
|
||||
|
||||
// exclude_archived
|
||||
if(pFlags.exclude_archived && dialog.folder_id == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// exclude_read
|
||||
if(pFlags.exclude_read && !dialog.unread_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// exclude_muted
|
||||
if(pFlags.exclude_muted) {
|
||||
const isMuted = (dialog.notify_settings?.mute_until * 1000) > Date.now();
|
||||
if(isMuted) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const peerID = dialog.peerID;
|
||||
if(peerID < 0) {
|
||||
// broadcasts
|
||||
if(pFlags.broadcasts && appPeersManager.isBroadcast(peerID)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// groups
|
||||
if(pFlags.groups && appPeersManager.isAnyGroup(peerID)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// bots
|
||||
if(appPeersManager.isBot(peerID)) {
|
||||
return !!pFlags.bots;
|
||||
}
|
||||
|
||||
// non_contacts
|
||||
if(pFlags.non_contacts && !appUsersManager.contactsList.has(peerID)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// contacts
|
||||
if(pFlags.contacts && appUsersManager.contactsList.has(peerID)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public toggleDialogPin(peerID: number, filterID: number) {
|
||||
const filter = this.filters[filterID];
|
||||
|
||||
const wasPinned = filter.pinned_peers.findAndSplice(p => p == peerID);
|
||||
if(!wasPinned) {
|
||||
filter.pinned_peers.unshift(peerID);
|
||||
}
|
||||
|
||||
return this.updateDialogFilter(filter);
|
||||
}
|
||||
|
||||
public createDialogFilter(filter: MyDialogFilter) {
|
||||
let maxID = Math.max(1, ...Object.keys(this.filters).map(i => +i));
|
||||
filter = copy(filter);
|
||||
filter.id = maxID + 1;
|
||||
return this.updateDialogFilter(filter);
|
||||
}
|
||||
|
||||
public updateDialogFilter(filter: MyDialogFilter, remove = false) {
|
||||
const flags = remove ? 0 : 1;
|
||||
|
||||
return apiManager.invokeApi('messages.updateDialogFilter', {
|
||||
flags,
|
||||
id: filter.id,
|
||||
filter: remove ? undefined : this.getOutputDialogFilter(filter)
|
||||
}).then((bool: boolean) => { // возможно нужна проверка и откат, если результат не ТРУ
|
||||
//console.log('updateDialogFilter bool:', bool);
|
||||
|
||||
if(bool) {
|
||||
/* if(!this.filters[filter.id]) {
|
||||
this.saveDialogFilter(filter);
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('filter_update', filter); */
|
||||
|
||||
this.handleUpdate({
|
||||
_: 'updateDialogFilter',
|
||||
id: filter.id,
|
||||
filter: remove ? undefined : filter as any
|
||||
});
|
||||
}
|
||||
|
||||
return bool;
|
||||
});
|
||||
}
|
||||
|
||||
public getOutputDialogFilter(filter: MyDialogFilter) {
|
||||
const c: MyDialogFilter = copy(filter);
|
||||
['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {
|
||||
// @ts-ignore
|
||||
c[key] = c[key].map((peerID: number) => appPeersManager.getInputPeerByID(peerID));
|
||||
});
|
||||
|
||||
c.include_peers.forEachReverse((peerID, idx) => {
|
||||
if(c.pinned_peers.includes(peerID)) {
|
||||
c.include_peers.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
|
||||
return c as any as DialogFilter;
|
||||
}
|
||||
|
||||
public async getDialogFilters(overwrite = false) {
|
||||
if(Object.keys(this.filters).length && !overwrite) {
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
const filters = await apiManager.invokeApi('messages.getDialogFilters');
|
||||
for(const filter of filters) {
|
||||
this.saveDialogFilter(filter as any as MyDialogFilter, false);
|
||||
}
|
||||
|
||||
//console.log(this.filters);
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
public saveDialogFilter(filter: MyDialogFilter, update = true) {
|
||||
['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {
|
||||
// @ts-ignore
|
||||
filter[key] = filter[key].map((peer: any) => appPeersManager.getPeerID(peer));
|
||||
});
|
||||
|
||||
filter.include_peers.forEachReverse((peerID, idx) => {
|
||||
if(filter.pinned_peers.includes(peerID)) {
|
||||
filter.include_peers.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
|
||||
filter.include_peers = filter.pinned_peers.concat(filter.include_peers);
|
||||
|
||||
if(this.filters[filter.id]) {
|
||||
Object.assign(this.filters[filter.id], filter);
|
||||
} else {
|
||||
this.filters[filter.id] = filter;
|
||||
}
|
||||
|
||||
this.setOrderIndex(filter);
|
||||
|
||||
if(update) {
|
||||
$rootScope.$broadcast('filter_update', filter);
|
||||
}
|
||||
}
|
||||
|
||||
public setOrderIndex(filter: MyDialogFilter) {
|
||||
if(filter.hasOwnProperty('orderIndex')) {
|
||||
if(filter.orderIndex > this.orderIndex) {
|
||||
this.orderIndex = filter.orderIndex;
|
||||
}
|
||||
} else {
|
||||
filter.orderIndex = this.orderIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MyMessage = Message.message | Message.messageService;
|
||||
export type MyMessage = Message.message | Message.messageService;
|
||||
type MyInputMessagesFilter = 'inputMessagesFilterEmpty'
|
||||
| 'inputMessagesFilterPhotos'
|
||||
| 'inputMessagesFilterPhotoVideo'
|
||||
@ -514,10 +131,13 @@ export class AppMessagesManager {
|
||||
|
||||
private log = logger('MESSAGES'/* , LogLevels.error | LogLevels.debug | LogLevels.log | LogLevels.warn */);
|
||||
|
||||
public dialogsStorage = new DialogsStorage();
|
||||
public filtersStorage = new FiltersStorage();
|
||||
public dialogsStorage: DialogsStorage;
|
||||
public filtersStorage: FiltersStorage;
|
||||
|
||||
constructor() {
|
||||
this.dialogsStorage = new DialogsStorage(this, appMessagesIDsManager, appChatsManager, appPeersManager, serverTimeManager);
|
||||
this.filtersStorage = new FiltersStorage(appPeersManager, appUsersManager, /* apiManager, */ $rootScope);
|
||||
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
this.handleUpdate(e.detail);
|
||||
});
|
||||
@ -970,7 +590,7 @@ export class AppMessagesManager {
|
||||
const isDocument = !(file instanceof File) && !(file instanceof Blob);
|
||||
let caption = options.caption || '';
|
||||
|
||||
const date = tsNow(true) + ServerTimeManager.serverTimeOffset;
|
||||
const date = tsNow(true) + serverTimeManager.serverTimeOffset;
|
||||
|
||||
this.log('sendFile', file, fileType);
|
||||
|
||||
@ -1321,7 +941,7 @@ export class AppMessagesManager {
|
||||
|
||||
let caption = options.caption || '';
|
||||
|
||||
let date = tsNow(true) + ServerTimeManager.serverTimeOffset;
|
||||
let date = tsNow(true) + serverTimeManager.serverTimeOffset;
|
||||
|
||||
if(caption) {
|
||||
let entities = options.entities || [];
|
||||
@ -1707,7 +1327,7 @@ export class AppMessagesManager {
|
||||
from_id: appPeersManager.getOutputPeer(fromID),
|
||||
peer_id: appPeersManager.getOutputPeer(peerID),
|
||||
pFlags: pFlags,
|
||||
date: tsNow(true) + ServerTimeManager.serverTimeOffset,
|
||||
date: tsNow(true) + serverTimeManager.serverTimeOffset,
|
||||
message: '',
|
||||
media: media,
|
||||
random_id: randomIDS,
|
||||
@ -3193,7 +2813,7 @@ export class AppMessagesManager {
|
||||
var offsetMessage = maxID && this.getMessage(maxID);
|
||||
|
||||
if(offsetMessage && offsetMessage.date) {
|
||||
offsetDate = offsetMessage.date + ServerTimeManager.serverTimeOffset;
|
||||
offsetDate = offsetMessage.date + serverTimeManager.serverTimeOffset;
|
||||
offsetID = offsetMessage.id;
|
||||
offsetPeerID = this.getMessagePeer(offsetMessage);
|
||||
}
|
||||
@ -3484,7 +3104,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
|
||||
public handleUpdate(update: Update) {
|
||||
this.log.debug('AMM: handleUpdate:', update._);
|
||||
this.log.debug('handleUpdate', update._);
|
||||
switch(update._) {
|
||||
case 'updateMessageID': {
|
||||
const randomID = update.random_id;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { Dialog, DialogsStorage, FiltersStorage } from './appMessagesManager';
|
||||
import type { Dialog } from './appMessagesManager';
|
||||
import type { AppStickersManager } from './appStickersManager';
|
||||
import { App, MOUNT_CLASS_TO, UserAuth } from '../mtproto/mtproto_config';
|
||||
import EventListenerBase from '../../helpers/eventListenerBase';
|
||||
@ -9,6 +9,8 @@ import type { AppUsersManager } from './appUsersManager';
|
||||
import type { AppChatsManager } from './appChatsManager';
|
||||
import type { AuthState } from '../../types';
|
||||
import type { AppMessagesIDsManager } from './appMessagesIDsManager';
|
||||
import type FiltersStorage from '../storages/filters';
|
||||
import type DialogsStorage from '../storages/dialogs';
|
||||
|
||||
const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day
|
||||
const STATE_VERSION = App.version;
|
||||
|
@ -24,6 +24,7 @@ export class AppUsersManager {
|
||||
public contactsIndex = searchIndexManager.createIndex();
|
||||
public contactsFillPromise: Promise<Set<number>>;
|
||||
public contactsList: Set<number> = new Set();
|
||||
private updatedContactsList = false;
|
||||
|
||||
public getTopPeersPromise: Promise<number[]>;
|
||||
|
||||
@ -107,10 +108,11 @@ export class AppUsersManager {
|
||||
|
||||
appStateManager.getState().then((state) => {
|
||||
const contactsList = state.contactsList;
|
||||
if(contactsList && Array.isArray(contactsList) && contactsList.length) {
|
||||
if(contactsList && Array.isArray(contactsList)) {
|
||||
contactsList.forEach(userID => {
|
||||
this.pushContact(userID);
|
||||
});
|
||||
|
||||
this.contactsFillPromise = Promise.resolve(this.contactsList);
|
||||
}
|
||||
|
||||
@ -119,13 +121,13 @@ export class AppUsersManager {
|
||||
}
|
||||
|
||||
public fillContacts() {
|
||||
if(this.contactsFillPromise) {
|
||||
if(this.contactsFillPromise && this.updatedContactsList) {
|
||||
return this.contactsFillPromise;
|
||||
}
|
||||
|
||||
return this.contactsFillPromise = apiManager.invokeApi('contacts.getContacts', {
|
||||
hash: 0
|
||||
}).then((result) => {
|
||||
this.updatedContactsList = true;
|
||||
|
||||
const promise = apiManager.invokeApi('contacts.getContacts').then((result) => {
|
||||
if(result._ == 'contacts.contacts') {
|
||||
this.saveApiUsers(result.users);
|
||||
|
||||
@ -134,8 +136,12 @@ export class AppUsersManager {
|
||||
});
|
||||
}
|
||||
|
||||
this.contactsFillPromise = promise;
|
||||
|
||||
return this.contactsList;
|
||||
});
|
||||
|
||||
return this.contactsFillPromise || (this.contactsFillPromise = promise);
|
||||
}
|
||||
|
||||
public async resolveUsername(username: string) {
|
||||
|
@ -22,7 +22,7 @@ type Task = {
|
||||
|
||||
const USEWORKERASWORKER = true;
|
||||
|
||||
class ApiManagerProxy extends CryptoWorkerMethods {
|
||||
export class ApiManagerProxy extends CryptoWorkerMethods {
|
||||
public worker: Worker;
|
||||
public postMessage: (...args: any[]) => void;
|
||||
private afterMessageIDTemp = 0;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import type { StickerSet, Update } from "../layer";
|
||||
import type { MyDocument } from "./appManagers/appDocsManager";
|
||||
import type { AppMessagesManager, Dialog, MyDialogFilter } from "./appManagers/appMessagesManager";
|
||||
import type { AppMessagesManager, Dialog } from "./appManagers/appMessagesManager";
|
||||
import type { Poll, PollResults } from "./appManagers/appPollsManager";
|
||||
import type { MyDialogFilter } from "./storages/filters";
|
||||
import { MOUNT_CLASS_TO } from "./mtproto/mtproto_config";
|
||||
|
||||
type BroadcastEvents = {
|
||||
@ -12,6 +13,7 @@ type BroadcastEvents = {
|
||||
|
||||
'filter_delete': MyDialogFilter,
|
||||
'filter_update': MyDialogFilter,
|
||||
'filter_order': number[],
|
||||
|
||||
'dialog_draft': {peerID: number, draft: any, index: number},
|
||||
'dialog_unread': {peerID: number, count?: number},
|
||||
|
180
src/lib/storages/dialogs.ts
Normal file
180
src/lib/storages/dialogs.ts
Normal file
@ -0,0 +1,180 @@
|
||||
import { tsNow } from "../../helpers/date";
|
||||
import type { Message } from "../../layer";
|
||||
import type { AppChatsManager } from "../appManagers/appChatsManager";
|
||||
import type { AppMessagesIDsManager } from "../appManagers/appMessagesIDsManager";
|
||||
import type { AppMessagesManager, Dialog } from "../appManagers/appMessagesManager";
|
||||
import type { AppPeersManager } from "../appManagers/appPeersManager";
|
||||
import type { ServerTimeManager } from "../mtproto/serverTimeManager";
|
||||
|
||||
export default class DialogsStorage {
|
||||
public dialogs: {[peerID: string]: Dialog} = {};
|
||||
public byFolders: {[folderID: number]: Dialog[]} = {};
|
||||
|
||||
public allDialogsLoaded: {[folder_id: number]: boolean} = {};
|
||||
public dialogsOffsetDate: {[folder_id: number]: number} = {};
|
||||
public pinnedOrders: {[folder_id: number]: number[]} = {
|
||||
0: [],
|
||||
1: []
|
||||
};
|
||||
public dialogsNum = 0;
|
||||
|
||||
constructor(private appMessagesManager: AppMessagesManager, private appMessagesIDsManager: AppMessagesIDsManager, private appChatsManager: AppChatsManager, private appPeersManager: AppPeersManager, private serverTimeManager: ServerTimeManager) {
|
||||
|
||||
}
|
||||
|
||||
public getFolder(id: number) {
|
||||
if(id <= 1) {
|
||||
return this.byFolders[id] ?? (this.byFolders[id] = []);
|
||||
}
|
||||
|
||||
const dialogs: {dialog: Dialog, index: number}[] = [];
|
||||
const filter = this.appMessagesManager.filtersStorage.filters[id];
|
||||
|
||||
for(const peerID in this.dialogs) {
|
||||
const dialog = this.dialogs[peerID];
|
||||
if(this.appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) {
|
||||
let index: number;
|
||||
|
||||
const pinnedIndex = filter.pinned_peers.indexOf(dialog.peerID);
|
||||
if(pinnedIndex !== -1) {
|
||||
index = this.generateDialogIndex(this.generateDialogPinnedDateByIndex(filter.pinned_peers.length - 1 - pinnedIndex));
|
||||
} else if(dialog.pFlags?.pinned) {
|
||||
index = this.generateIndexForDialog(dialog, true);
|
||||
} else {
|
||||
index = dialog.index;
|
||||
}
|
||||
|
||||
dialogs.push({dialog, index});
|
||||
}
|
||||
}
|
||||
|
||||
dialogs.sort((a, b) => b.index - a.index);
|
||||
return dialogs.map(d => d.dialog);
|
||||
}
|
||||
|
||||
public getDialog(peerID: number, folderID?: number): [Dialog, number] | [] {
|
||||
const folders: Dialog[][] = [];
|
||||
|
||||
if(folderID === undefined) {
|
||||
const dialogs = this.byFolders;
|
||||
for(const folderID in dialogs) {
|
||||
folders.push(dialogs[folderID]);
|
||||
}
|
||||
} else {
|
||||
folders.push(this.getFolder(folderID));
|
||||
}
|
||||
|
||||
for(let folder of folders) {
|
||||
const index = folder.findIndex(dialog => dialog.peerID == peerID);
|
||||
if(index !== -1) {
|
||||
return [folder[index], index];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/*
|
||||
var date = Date.now() / 1000 | 0;
|
||||
var m = date * 0x10000;
|
||||
|
||||
var k = (date + 1) * 0x10000;
|
||||
k - m;
|
||||
65536
|
||||
*/
|
||||
public generateDialogIndex(date?: number) {
|
||||
if(date === undefined) {
|
||||
date = tsNow(true) + this.serverTimeManager.serverTimeOffset;
|
||||
}
|
||||
|
||||
return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF);
|
||||
}
|
||||
|
||||
public generateIndexForDialog(dialog: Dialog, justReturn = false) {
|
||||
const channelID = this.appPeersManager.isChannel(dialog.peerID) ? -dialog.peerID : 0;
|
||||
const mid = this.appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID);
|
||||
const message = this.appMessagesManager.getMessage(mid);
|
||||
|
||||
let topDate = (message as Message.message).date || Date.now() / 1000;
|
||||
if(channelID) {
|
||||
const channel = this.appChatsManager.getChat(channelID);
|
||||
if(!topDate || channel.date && channel.date > topDate) {
|
||||
topDate = channel.date;
|
||||
}
|
||||
}
|
||||
|
||||
const savedDraft: any = {};// DraftsManager.saveDraft(peerID, dialog.draft); // warning
|
||||
if(savedDraft && savedDraft.date > topDate) {
|
||||
topDate = savedDraft.date;
|
||||
}
|
||||
|
||||
if(dialog.pFlags.pinned && !justReturn) {
|
||||
topDate = this.generateDialogPinnedDate(dialog);
|
||||
//this.log('topDate', peerID, topDate);
|
||||
}
|
||||
|
||||
const index = this.generateDialogIndex(topDate);
|
||||
if(justReturn) return index;
|
||||
dialog.index = index;
|
||||
}
|
||||
|
||||
public generateDialogPinnedDateByIndex(pinnedIndex: number) {
|
||||
return 0x7fff0000 + (pinnedIndex & 0xFFFF); // 0xFFFF - потому что в папках может быть бесконечное число пиннедов
|
||||
}
|
||||
|
||||
public generateDialogPinnedDate(dialog: Dialog) {
|
||||
const order = this.pinnedOrders[dialog.folder_id];
|
||||
|
||||
const foundIndex = order.indexOf(dialog.peerID);
|
||||
const pinnedIndex = foundIndex === -1 ? order.push(dialog.peerID) - 1 : foundIndex;
|
||||
|
||||
return this.generateDialogPinnedDateByIndex(pinnedIndex);
|
||||
}
|
||||
|
||||
public pushDialog(dialog: Dialog, offsetDate?: number) {
|
||||
const dialogs = this.getFolder(dialog.folder_id);
|
||||
const pos = dialogs.findIndex(d => d.peerID == dialog.peerID);
|
||||
if(pos !== -1) {
|
||||
dialogs.splice(pos, 1);
|
||||
}
|
||||
|
||||
//if(!this.dialogs[dialog.peerID]) {
|
||||
this.dialogs[dialog.peerID] = dialog;
|
||||
//}
|
||||
|
||||
if(offsetDate &&
|
||||
!dialog.pFlags.pinned &&
|
||||
(!this.dialogsOffsetDate[dialog.folder_id] || offsetDate < this.dialogsOffsetDate[dialog.folder_id])) {
|
||||
if(pos !== -1) {
|
||||
// So the dialog jumped to the last position
|
||||
return false;
|
||||
}
|
||||
this.dialogsOffsetDate[dialog.folder_id] = offsetDate;
|
||||
}
|
||||
|
||||
const index = dialog.index;
|
||||
const len = dialogs.length;
|
||||
if(!len || index < dialogs[len - 1].index) {
|
||||
dialogs.push(dialog);
|
||||
} else if(index >= dialogs[0].index) {
|
||||
dialogs.unshift(dialog);
|
||||
} else {
|
||||
for(let i = 0; i < len; i++) {
|
||||
if(index > dialogs[i].index) {
|
||||
dialogs.splice(i, 0, dialog);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public dropDialog(peerID: number): [Dialog, number] | [] {
|
||||
const foundDialog = this.getDialog(peerID);
|
||||
if(foundDialog[0]) {
|
||||
this.byFolders[foundDialog[0].folder_id].splice(foundDialog[1], 1);
|
||||
delete this.dialogs[peerID];
|
||||
}
|
||||
|
||||
return foundDialog;
|
||||
}
|
||||
}
|
262
src/lib/storages/filters.ts
Normal file
262
src/lib/storages/filters.ts
Normal file
@ -0,0 +1,262 @@
|
||||
import { copy } from "../../helpers/object";
|
||||
import type { DialogFilter, Update } from "../../layer";
|
||||
import type { Modify } from "../../types";
|
||||
import type { AppPeersManager } from "../appManagers/appPeersManager";
|
||||
import type { AppUsersManager } from "../appManagers/appUsersManager";
|
||||
//import type { ApiManagerProxy } from "../mtproto/mtprotoworker";
|
||||
import type _$rootScope from "../rootScope";
|
||||
import type {Dialog} from '../appManagers/appMessagesManager';
|
||||
import apiManager from "../mtproto/mtprotoworker";
|
||||
|
||||
export type MyDialogFilter = Modify<DialogFilter, {
|
||||
pinned_peers: number[],
|
||||
include_peers: number[],
|
||||
exclude_peers: number[],
|
||||
orderIndex?: number
|
||||
}>;
|
||||
|
||||
// ! because 0 index is 'All Chats'
|
||||
const START_ORDER_INDEX = 1;
|
||||
|
||||
export default class FiltersStorage {
|
||||
public filters: {[filterID: string]: MyDialogFilter} = {};
|
||||
public orderIndex = START_ORDER_INDEX;
|
||||
|
||||
constructor(private appPeersManager: AppPeersManager, private appUsersManager: AppUsersManager, /* private apiManager: ApiManagerProxy, */ private $rootScope: typeof _$rootScope) {
|
||||
$rootScope.$on('apiUpdate', (e) => {
|
||||
this.handleUpdate(e.detail);
|
||||
});
|
||||
}
|
||||
|
||||
public handleUpdate(update: Update) {
|
||||
switch(update._) {
|
||||
case 'updateDialogFilter': {
|
||||
//console.log('updateDialogFilter', update);
|
||||
|
||||
if(update.filter) {
|
||||
this.saveDialogFilter(update.filter as any);
|
||||
} else if(this.filters[update.id]) { // Папка удалена
|
||||
//this.getDialogFilters(true);
|
||||
this.$rootScope.$broadcast('filter_delete', this.filters[update.id]);
|
||||
delete this.filters[update.id];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'updateDialogFilters': {
|
||||
//console.warn('updateDialogFilters', update);
|
||||
|
||||
const oldFilters = copy(this.filters);
|
||||
|
||||
this.getDialogFilters(true).then(filters => {
|
||||
for(const _filterID in oldFilters) {
|
||||
const filterID = +_filterID;
|
||||
if(!filters.find(filter => filter.id == filterID)) { // * deleted
|
||||
this.handleUpdate({_: 'updateDialogFilter', id: filterID});
|
||||
}
|
||||
}
|
||||
|
||||
this.handleUpdate({_: 'updateDialogFilterOrder', order: filters.map(filter => filter.id)});
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'updateDialogFilterOrder': {
|
||||
//console.log('updateDialogFilterOrder', update);
|
||||
|
||||
this.orderIndex = START_ORDER_INDEX;
|
||||
update.order.forEach((filterID, idx) => {
|
||||
const filter = this.filters[filterID];
|
||||
delete filter.orderIndex;
|
||||
this.setOrderIndex(filter);
|
||||
});
|
||||
|
||||
this.$rootScope.$broadcast('filter_order', update.order);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public testDialogForFilter(dialog: Dialog, filter: MyDialogFilter) {
|
||||
// exclude_peers
|
||||
for(const peerID of filter.exclude_peers) {
|
||||
if(peerID == dialog.peerID) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// include_peers
|
||||
for(const peerID of filter.include_peers) {
|
||||
if(peerID == dialog.peerID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const pFlags = filter.pFlags;
|
||||
|
||||
// exclude_archived
|
||||
if(pFlags.exclude_archived && dialog.folder_id == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// exclude_read
|
||||
if(pFlags.exclude_read && !dialog.unread_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// exclude_muted
|
||||
if(pFlags.exclude_muted) {
|
||||
const isMuted = (dialog.notify_settings?.mute_until * 1000) > Date.now();
|
||||
if(isMuted) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const peerID = dialog.peerID;
|
||||
if(peerID < 0) {
|
||||
// broadcasts
|
||||
if(pFlags.broadcasts && this.appPeersManager.isBroadcast(peerID)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// groups
|
||||
if(pFlags.groups && this.appPeersManager.isAnyGroup(peerID)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// bots
|
||||
if(this.appPeersManager.isBot(peerID)) {
|
||||
return !!pFlags.bots;
|
||||
}
|
||||
|
||||
// non_contacts
|
||||
if(pFlags.non_contacts && !this.appUsersManager.contactsList.has(peerID)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// contacts
|
||||
if(pFlags.contacts && this.appUsersManager.contactsList.has(peerID)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public toggleDialogPin(peerID: number, filterID: number) {
|
||||
const filter = this.filters[filterID];
|
||||
|
||||
const wasPinned = filter.pinned_peers.findAndSplice(p => p == peerID);
|
||||
if(!wasPinned) {
|
||||
filter.pinned_peers.unshift(peerID);
|
||||
}
|
||||
|
||||
return this.updateDialogFilter(filter);
|
||||
}
|
||||
|
||||
public createDialogFilter(filter: MyDialogFilter) {
|
||||
let maxID = Math.max(1, ...Object.keys(this.filters).map(i => +i));
|
||||
filter = copy(filter);
|
||||
filter.id = maxID + 1;
|
||||
return this.updateDialogFilter(filter);
|
||||
}
|
||||
|
||||
public updateDialogFilter(filter: MyDialogFilter, remove = false) {
|
||||
const flags = remove ? 0 : 1;
|
||||
|
||||
return apiManager.invokeApi('messages.updateDialogFilter', {
|
||||
flags,
|
||||
id: filter.id,
|
||||
filter: remove ? undefined : this.getOutputDialogFilter(filter)
|
||||
}).then((bool: boolean) => { // возможно нужна проверка и откат, если результат не ТРУ
|
||||
//console.log('updateDialogFilter bool:', bool);
|
||||
|
||||
if(bool) {
|
||||
/* if(!this.filters[filter.id]) {
|
||||
this.saveDialogFilter(filter);
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('filter_update', filter); */
|
||||
|
||||
this.handleUpdate({
|
||||
_: 'updateDialogFilter',
|
||||
id: filter.id,
|
||||
filter: remove ? undefined : filter as any
|
||||
});
|
||||
}
|
||||
|
||||
return bool;
|
||||
});
|
||||
}
|
||||
|
||||
public getOutputDialogFilter(filter: MyDialogFilter) {
|
||||
const c: MyDialogFilter = copy(filter);
|
||||
['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {
|
||||
// @ts-ignore
|
||||
c[key] = c[key].map((peerID: number) => this.appPeersManager.getInputPeerByID(peerID));
|
||||
});
|
||||
|
||||
c.include_peers.forEachReverse((peerID, idx) => {
|
||||
if(c.pinned_peers.includes(peerID)) {
|
||||
c.include_peers.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
|
||||
return c as any as DialogFilter;
|
||||
}
|
||||
|
||||
public async getDialogFilters(overwrite = false): Promise<MyDialogFilter[]> {
|
||||
const keys = Object.keys(this.filters);
|
||||
if(keys.length && !overwrite) {
|
||||
return keys.map(filterID => this.filters[filterID]).sort((a, b) => a.orderIndex - b.orderIndex);
|
||||
}
|
||||
|
||||
const filters: MyDialogFilter[] = await apiManager.invokeApi('messages.getDialogFilters') as any;
|
||||
for(const filter of filters) {
|
||||
this.saveDialogFilter(filter, overwrite);
|
||||
}
|
||||
|
||||
//console.log(this.filters);
|
||||
return filters;
|
||||
}
|
||||
|
||||
public saveDialogFilter(filter: MyDialogFilter, update = true) {
|
||||
['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {
|
||||
// @ts-ignore
|
||||
filter[key] = filter[key].map((peer: any) => this.appPeersManager.getPeerID(peer));
|
||||
});
|
||||
|
||||
filter.include_peers.forEachReverse((peerID, idx) => {
|
||||
if(filter.pinned_peers.includes(peerID)) {
|
||||
filter.include_peers.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
|
||||
filter.include_peers = filter.pinned_peers.concat(filter.include_peers);
|
||||
|
||||
if(this.filters[filter.id]) {
|
||||
Object.assign(this.filters[filter.id], filter);
|
||||
} else {
|
||||
this.filters[filter.id] = filter;
|
||||
}
|
||||
|
||||
this.setOrderIndex(filter);
|
||||
|
||||
if(update) {
|
||||
this.$rootScope.$broadcast('filter_update', filter);
|
||||
}
|
||||
}
|
||||
|
||||
public setOrderIndex(filter: MyDialogFilter) {
|
||||
if(filter.hasOwnProperty('orderIndex')) {
|
||||
if(filter.orderIndex >= this.orderIndex) {
|
||||
this.orderIndex = filter.orderIndex + 1;
|
||||
}
|
||||
} else {
|
||||
filter.orderIndex = this.orderIndex++;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user