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 @@
@@ -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 @@
@@ -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