Browse Source
Handle reordering Handle updateDialogFilters Ordering in folder settings Fix contacts list loading after state if it is emptymaster
Eduard Kuzmenko
4 years ago
15 changed files with 565 additions and 517 deletions
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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…
Reference in new issue