Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1415 lines
46 KiB
1415 lines
46 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import AvatarElement from "../../components/avatar"; |
|
import DialogsContextMenu from "../../components/dialogsContextMenu"; |
|
import { horizontalMenu } from "../../components/horizontalMenu"; |
|
import { attachContextMenuListener, putPreloader } from "../../components/misc"; |
|
import { ripple } from "../../components/ripple"; |
|
//import Scrollable from "../../components/scrollable"; |
|
import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable"; |
|
import { formatDateAccordingToTodayNew } from "../../helpers/date"; |
|
import { isSafari } from "../../helpers/userAgent"; |
|
import { logger, LogLevels } from "../logger"; |
|
import { RichTextProcessor } from "../richtextprocessor"; |
|
import rootScope from "../rootScope"; |
|
import { positionElementByIndex } from "../../helpers/dom"; |
|
import appImManager from "./appImManager"; |
|
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"; |
|
import Button from "../../components/button"; |
|
import SetTransition from "../../components/singleTransition"; |
|
import sessionStorage from '../sessionStorage'; |
|
import apiUpdatesManager from "./apiUpdatesManager"; |
|
import appDraftsManager, { MyDraftMessage } from "./appDraftsManager"; |
|
import ProgressivePreloader from "../../components/preloader"; |
|
import App from "../../config/app"; |
|
import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug"; |
|
import appNotificationsManager from "./appNotificationsManager"; |
|
import { InputNotifyPeer } from "../../layer"; |
|
import PeerTitle from "../../components/peerTitle"; |
|
import { i18n } from "../langPack"; |
|
import findUpTag from "../../helpers/dom/findUpTag"; |
|
|
|
export type DialogDom = { |
|
avatarEl: AvatarElement, |
|
captionDiv: HTMLDivElement, |
|
titleSpan: HTMLSpanElement, |
|
titleSpanContainer: HTMLSpanElement, |
|
statusSpan: HTMLSpanElement, |
|
lastTimeSpan: HTMLSpanElement, |
|
unreadMessagesSpan: HTMLSpanElement, |
|
lastMessageSpan: HTMLSpanElement, |
|
containerEl: HTMLElement, |
|
listEl: HTMLLIElement, |
|
muteAnimationTimeout?: number |
|
}; |
|
|
|
//const testScroll = false; |
|
//let testTopSlice = 1; |
|
|
|
class ConnectionStatusComponent { |
|
public static CHANGE_STATE_DELAY = 1000; |
|
|
|
private statusContainer: HTMLElement; |
|
private statusEl: HTMLElement; |
|
private statusPreloader: ProgressivePreloader; |
|
|
|
private currentText = ''; |
|
|
|
private connecting = false; |
|
private updating = false; |
|
|
|
private log: ReturnType<typeof logger>; |
|
|
|
private setFirstConnectionTimeout: number; |
|
private setStateTimeout: number; |
|
|
|
constructor(chatsContainer: HTMLElement) { |
|
this.log = logger('CS'); |
|
|
|
this.statusContainer = document.createElement('div'); |
|
this.statusContainer.classList.add('connection-status'); |
|
|
|
this.statusEl = Button('btn-primary bg-warning connection-status-button', {noRipple: true}); |
|
this.statusPreloader = new ProgressivePreloader({cancelable: false}); |
|
this.statusPreloader.constructContainer({color: 'transparent', bold: true}); |
|
this.statusContainer.append(this.statusEl); |
|
|
|
chatsContainer.prepend(this.statusContainer); |
|
|
|
rootScope.on('connection_status_change', (e) => { |
|
const status = e; |
|
console.log(status); |
|
|
|
this.setConnectionStatus(); |
|
}); |
|
|
|
rootScope.on('state_synchronizing', (e) => { |
|
const channelId = e; |
|
if(!channelId) { |
|
this.updating = true; |
|
DEBUG && this.log('updating', this.updating); |
|
this.setState(); |
|
} |
|
}); |
|
|
|
rootScope.on('state_synchronized', (e) => { |
|
const channelId = e; |
|
DEBUG && this.log('state_synchronized', channelId); |
|
if(!channelId) { |
|
this.updating = false; |
|
DEBUG && this.log('updating', this.updating); |
|
this.setState(); |
|
} |
|
}); |
|
|
|
this.setFirstConnectionTimeout = window.setTimeout(this.setConnectionStatus, ConnectionStatusComponent.CHANGE_STATE_DELAY + 1e3); |
|
|
|
/* let bool = true; |
|
document.addEventListener('dblclick', () => { |
|
rootScope.broadcast('connection_status_change', { |
|
dcId: 2, |
|
isFileDownload: false, |
|
isFileNetworker: false, |
|
isFileUpload: false, |
|
name: "NET-2", |
|
online: bool = !bool, |
|
_: "networkerStatus" |
|
}); |
|
}); */ |
|
} |
|
|
|
private setConnectionStatus = () => { |
|
sessionStorage.get('dc').then(baseDcId => { |
|
if(!baseDcId) { |
|
baseDcId = App.baseDcId; |
|
} |
|
|
|
if(this.setFirstConnectionTimeout) { |
|
clearTimeout(this.setFirstConnectionTimeout); |
|
this.setFirstConnectionTimeout = 0; |
|
} |
|
|
|
const status = rootScope.connectionStatus['NET-' + baseDcId]; |
|
const online = status && status.online; |
|
|
|
if(this.connecting && online) { |
|
apiUpdatesManager.forceGetDifference(); |
|
} |
|
|
|
this.connecting = !online; |
|
DEBUG && this.log('connecting', this.connecting); |
|
this.setState(); |
|
}); |
|
}; |
|
|
|
private setStatusText = (text: string) => { |
|
if(this.currentText === text) return; |
|
this.statusEl.innerText = this.currentText = text; |
|
this.statusPreloader.attach(this.statusEl); |
|
}; |
|
|
|
private setState = () => { |
|
const timeout = ConnectionStatusComponent.CHANGE_STATE_DELAY; |
|
if(this.connecting) { |
|
this.setStatusText('Waiting for network...'); |
|
} else if(this.updating) { |
|
this.setStatusText('Updating...'); |
|
} |
|
|
|
DEBUG && this.log('setState', this.connecting || this.updating); |
|
window.requestAnimationFrame(() => { |
|
if(this.setStateTimeout) clearTimeout(this.setStateTimeout); |
|
|
|
const cb = () => { |
|
SetTransition(this.statusContainer, 'is-shown', this.connecting || this.updating, 200); |
|
this.setStateTimeout = 0; |
|
DEBUG && this.log('setState: isShown:', this.connecting || this.updating); |
|
}; |
|
|
|
this.setStateTimeout = window.setTimeout(cb, timeout); |
|
//cb(); |
|
/* if(timeout) this.setStateTimeout = window.setTimeout(cb, timeout); |
|
else cb(); */ |
|
}); |
|
}; |
|
} |
|
|
|
export class AppDialogsManager { |
|
public chatList: HTMLUListElement; |
|
|
|
public doms: {[peerId: number]: DialogDom} = {}; |
|
|
|
public chatsContainer = document.getElementById('chatlist-container') as HTMLDivElement; |
|
private chatsPreloader: HTMLElement; |
|
|
|
public loadDialogsPromise: Promise<any>; |
|
|
|
public scroll: Scrollable = null; |
|
public _scroll: Scrollable = null; |
|
|
|
private log = logger('DIALOGS', LogLevels.log | LogLevels.error | LogLevels.warn | LogLevels.debug); |
|
|
|
public contextMenu = new DialogsContextMenu(); |
|
|
|
public chatLists: {[filterId: number]: HTMLUListElement}; |
|
public filterId: number; |
|
private folders: {[k in 'menu' | 'container' | 'menuScrollContainer']: HTMLElement} = { |
|
menu: document.getElementById('folders-tabs'), |
|
menuScrollContainer: null, |
|
container: document.getElementById('folders-container') |
|
}; |
|
private filtersRendered: { |
|
[filterId: string]: { |
|
menu: HTMLElement, |
|
container: HTMLElement, |
|
unread: HTMLElement, |
|
title: HTMLElement |
|
} |
|
} = {}; |
|
private showFiltersTimeout: number; |
|
private allUnreadCount: HTMLElement; |
|
|
|
private accumulateArchivedTimeout: number; |
|
|
|
//private topOffsetIndex = 0; |
|
|
|
private sliceTimeout: number; |
|
private reorderDialogsTimeout: number; |
|
|
|
private lastActiveElements: Set<HTMLElement> = new Set(); |
|
|
|
constructor() { |
|
this.chatLists = { |
|
1: this.createChatList() |
|
}; |
|
|
|
this.chatsPreloader = putPreloader(null, true); |
|
|
|
this.allUnreadCount = this.folders.menu.querySelector('.badge'); |
|
|
|
this.folders.menuScrollContainer = this.folders.menu.parentElement; |
|
|
|
const bottomPart = document.createElement('div'); |
|
bottomPart.classList.add('connection-status-bottom'); |
|
bottomPart.append(this.folders.container); |
|
|
|
this.scroll = this._scroll = new Scrollable(bottomPart, 'CL', 500); |
|
this.scroll.container.addEventListener('scroll', this.onChatsRegularScroll); |
|
this.scroll.onScrolledTop = this.onChatsScrollTop; |
|
this.scroll.onScrolledBottom = this.onChatsScroll; |
|
//this.scroll.attachSentinels(); |
|
|
|
/* if(isTouchSupported && isSafari) { |
|
let allowUp: boolean, allowDown: boolean, slideBeginY: number; |
|
const container = this.scroll.container; |
|
container.addEventListener('touchstart', (event) => { |
|
allowUp = container.scrollTop > 0; |
|
allowDown = (container.scrollTop < container.scrollHeight - container.clientHeight); |
|
// @ts-ignore |
|
slideBeginY = event.pageY; |
|
}); |
|
|
|
container.addEventListener('touchmove', (event: any) => { |
|
var up = (event.pageY > slideBeginY); |
|
var down = (event.pageY < slideBeginY); |
|
slideBeginY = event.pageY; |
|
if((up && allowUp) || (down && allowDown)) { |
|
event.stopPropagation(); |
|
} else if(up || down) { |
|
event.preventDefault(); |
|
} |
|
}); |
|
} */ |
|
|
|
this.filterId = 0; |
|
this.addFilter({ |
|
id: this.filterId, |
|
title: '', |
|
titleEl: i18n('ChatList.Filter.All'), |
|
orderIndex: 0 |
|
}); |
|
|
|
this.chatList = this.chatLists[this.filterId]; |
|
this.scroll.setVirtualContainer(this.chatList); |
|
|
|
/* if(testScroll) { |
|
let i = 0; |
|
let add = () => { |
|
let li = document.createElement('li'); |
|
li.dataset.id = '' + i; |
|
li.id = '' + i; |
|
li.innerHTML = `<div class="rp"><avatar-element style="background-color: rgb(166, 149, 231); font-size: 0px;"><img src="assets/img/pepe.jpg"></avatar-element><div class="user-caption"><p><span class="user-title">${i}</span><span><span class="message-status"></span><span class="message-time">18:33</span></span></p><p><span class="user-last-message"><b>-_-_-_-: </b>qweasd</span><span></span></p></div></div>`; |
|
i++; |
|
this.scroll.append(li); |
|
}; |
|
for(let i = 0; i < 500; ++i) { |
|
add(); |
|
} |
|
(window as any).addElement = add; |
|
} */ |
|
|
|
rootScope.on('user_update', (e) => { |
|
const userId = e; |
|
const user = appUsersManager.getUser(userId); |
|
const dialog = appMessagesManager.getDialogByPeerId(user.id)[0]; |
|
//console.log('updating user:', user, dialog); |
|
|
|
if(dialog && !appUsersManager.isBot(dialog.peerId) && dialog.peerId !== rootScope.myId) { |
|
const online = user.status?._ === 'userStatusOnline'; |
|
const dom = this.getDialogDom(dialog.peerId); |
|
|
|
if(dom) { |
|
dom.avatarEl.classList.toggle('is-online', online); |
|
} |
|
} |
|
}); |
|
|
|
/* rootScope.$on('dialog_top', (e) => { |
|
const dialog = e; |
|
|
|
this.setLastMessage(dialog); |
|
this.setDialogPosition(dialog); |
|
|
|
this.setFiltersUnreadCount(); |
|
}); */ |
|
|
|
rootScope.on('dialog_flush', (e) => { |
|
const peerId: number = e.peerId; |
|
const dialog = appMessagesManager.getDialogByPeerId(peerId)[0]; |
|
if(dialog) { |
|
this.setLastMessage(dialog); |
|
this.validateForFilter(); |
|
this.setFiltersUnreadCount(); |
|
} |
|
}); |
|
|
|
rootScope.on('dialogs_multiupdate', (e) => { |
|
const dialogs = e; |
|
|
|
for(const id in dialogs) { |
|
const dialog = dialogs[id]; |
|
this.updateDialog(dialog); |
|
} |
|
|
|
this.validateForFilter(); |
|
this.setFiltersUnreadCount(); |
|
}); |
|
|
|
rootScope.on('dialog_drop', (e) => { |
|
const {peerId, dialog} = e; |
|
|
|
const dom = this.getDialogDom(peerId); |
|
if(dom) { |
|
dom.listEl.remove(); |
|
delete this.doms[peerId]; |
|
} |
|
|
|
this.setFiltersUnreadCount(); |
|
}); |
|
|
|
rootScope.on('dialog_unread', (e) => { |
|
const info = e; |
|
|
|
const dialog = appMessagesManager.getDialogByPeerId(info.peerId)[0]; |
|
if(dialog) { |
|
this.setUnreadMessages(dialog); |
|
this.validateForFilter(); |
|
this.setFiltersUnreadCount(); |
|
} |
|
}); |
|
|
|
rootScope.on('dialog_notify_settings', (dialog) => { |
|
this.setUnreadMessages(dialog); // возможно это не нужно, но нужно менять is-muted |
|
}); |
|
|
|
rootScope.on('dialog_draft', (e) => { |
|
const dialog = appMessagesManager.getDialogByPeerId(e.peerId)[0]; |
|
if(dialog) { |
|
this.updateDialog(dialog); |
|
} |
|
}); |
|
|
|
rootScope.on('peer_changed', (e) => { |
|
const peerId = e; |
|
|
|
//const perf = performance.now(); |
|
for(const element of this.lastActiveElements) { |
|
if(+element.dataset.peerId !== peerId) { |
|
element.classList.remove('active'); |
|
this.lastActiveElements.delete(element); |
|
} |
|
} |
|
|
|
const elements = Array.from(document.querySelectorAll(`[data-autonomous="0"] li[data-peer-id="${peerId}"]`)) as HTMLElement[]; |
|
elements.forEach(element => { |
|
element.classList.add('active'); |
|
this.lastActiveElements.add(element); |
|
}); |
|
//this.log('peer_changed total time:', performance.now() - perf); |
|
}); |
|
|
|
rootScope.on('filter_update', (e) => { |
|
const filter: DialogFilter = e; |
|
if(!this.filtersRendered[filter.id]) { |
|
this.addFilter(filter); |
|
return; |
|
} else if(filter.id === this.filterId) { // это нет тут смысла вызывать, так как будет dialogs_multiupdate |
|
//this.validateForFilter(); |
|
const folder = appMessagesManager.dialogsStorage.getFolder(filter.id); |
|
this.validateForFilter(); |
|
for(let i = 0, length = folder.length; i < length; ++i) { |
|
const dialog = folder[i]; |
|
this.updateDialog(dialog); |
|
} |
|
this.setFiltersUnreadCount(); |
|
} |
|
|
|
const elements = this.filtersRendered[filter.id]; |
|
elements.title.innerHTML = RichTextProcessor.wrapEmojiText(filter.title); |
|
}); |
|
|
|
rootScope.on('filter_delete', (e) => { |
|
const filter: DialogFilter = e; |
|
const elements = this.filtersRendered[filter.id]; |
|
if(!elements) return; |
|
|
|
// set tab |
|
//(this.folders.menu.firstElementChild.children[Math.max(0, filter.id - 2)] as HTMLElement).click(); |
|
(this.folders.menu.firstElementChild as HTMLElement).click(); |
|
|
|
elements.container.remove(); |
|
elements.menu.remove(); |
|
|
|
delete this.chatLists[filter.id]; |
|
delete this.filtersRendered[filter.id]; |
|
|
|
if(Object.keys(this.filtersRendered).length <= 1) { |
|
this.folders.menuScrollContainer.classList.add('hide'); |
|
} |
|
}); |
|
|
|
rootScope.on('filter_order', (e) => { |
|
const order = e; |
|
|
|
const containerToAppend = this.folders.menu as HTMLElement; |
|
order.forEach((filterId) => { |
|
const filter = appMessagesManager.filtersStorage.filters[filterId]; |
|
const renderedFilter = this.filtersRendered[filterId]; |
|
|
|
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; |
|
} */ |
|
}); |
|
|
|
rootScope.on('peer_typings', (e) => { |
|
const {peerId, typings} = e; |
|
|
|
const dialog = appMessagesManager.getDialogByPeerId(peerId)[0]; |
|
if(!dialog) return; |
|
|
|
if(typings.length) { |
|
this.setTyping(dialog, appUsersManager.getUser(typings[0])); |
|
} else { |
|
this.unsetTyping(dialog); |
|
} |
|
}); |
|
|
|
const foldersScrollable = new ScrollableX(this.folders.menuScrollContainer); |
|
bottomPart.prepend(this.folders.menuScrollContainer); |
|
const selectTab = horizontalMenu(this.folders.menu, this.folders.container, (id, tabContent) => { |
|
/* if(id !== 0) { |
|
id += 1; |
|
} */ |
|
|
|
id = +tabContent.dataset.filterId || 0; |
|
|
|
if(this.filterId === id) return; |
|
|
|
this.chatLists[id].innerHTML = ''; |
|
this.scroll.setVirtualContainer(this.chatLists[id]); |
|
this.filterId = id; |
|
this.onTabChange(); |
|
}, () => { |
|
for(const folderId in this.chatLists) { |
|
if(+folderId !== this.filterId) { |
|
this.chatLists[folderId].innerHTML = ''; |
|
} |
|
} |
|
}, undefined, foldersScrollable); |
|
|
|
//selectTab(0); |
|
(this.folders.menu.firstElementChild as HTMLElement).click(); |
|
appStateManager.getState().then((state) => { |
|
(['inputNotifyBroadcasts', 'inputNotifyUsers', 'inputNotifyChats'] as Exclude<InputNotifyPeer['_'], 'inputNotifyPeer'>[]) |
|
.forEach((inputKey) => { |
|
appNotificationsManager.getNotifySettings({_: inputKey}); |
|
}); |
|
|
|
const getFiltersPromise = appMessagesManager.filtersStorage.getDialogFilters(); |
|
getFiltersPromise.then((filters) => { |
|
for(const filter of filters) { |
|
this.addFilter(filter); |
|
} |
|
}); |
|
|
|
if(state.dialogs?.length) { |
|
appDraftsManager.getAllDrafts(); |
|
appDraftsManager.addMissedDialogs(); |
|
} |
|
|
|
return this.loadDialogs(); |
|
}).then(() => { |
|
const allDialogsLoaded = appMessagesManager.dialogsStorage.allDialogsLoaded; |
|
const wasLoaded = allDialogsLoaded[0] || allDialogsLoaded[1]; |
|
const a: Promise<any> = allDialogsLoaded[0] ? Promise.resolve() : appMessagesManager.getConversationsAll('', 0); |
|
const b: Promise<any> = allDialogsLoaded[1] ? Promise.resolve() : appMessagesManager.getConversationsAll('', 1); |
|
a.finally(() => { |
|
b.then(() => { |
|
this.accumulateArchivedUnread(); |
|
|
|
if(wasLoaded) { |
|
(apiUpdatesManager.updatesState.syncLoading || Promise.resolve()).then(() => { |
|
appMessagesManager.refreshConversations(); |
|
}); |
|
} |
|
}); |
|
}); |
|
}); |
|
|
|
new ConnectionStatusComponent(this.chatsContainer); |
|
this.chatsContainer.append(bottomPart); |
|
} |
|
|
|
private getOffset(side: 'top' | 'bottom'): {index: number, pos: number} { |
|
if(!this.scroll.loadedAll[side]) { |
|
const element = (side === 'top' ? this.chatList.firstElementChild : this.chatList.lastElementChild) as HTMLElement; |
|
if(element) { |
|
const peerId = +element.dataset.peerId; |
|
const dialog = appMessagesManager.getDialogByPeerId(peerId); |
|
return {index: dialog[0].index, pos: dialog[1]}; |
|
} |
|
} |
|
|
|
return {index: 0, pos: -1}; |
|
} |
|
|
|
private isDialogMustBeInViewport(dialog: Dialog) { |
|
//return true; |
|
const topOffset = this.getOffset('top'); |
|
const bottomOffset = this.getOffset('bottom'); |
|
|
|
if(!topOffset.index && !bottomOffset.index) { |
|
return true; |
|
} |
|
|
|
const index = dialog.index; |
|
return (!topOffset.index || index <= topOffset.index) && (!bottomOffset.index || index >= bottomOffset.index); |
|
} |
|
|
|
private updateDialog(dialog: Dialog) { |
|
if(!dialog) { |
|
return; |
|
} |
|
|
|
if(this.isDialogMustBeInViewport(dialog)) { |
|
if(!this.doms.hasOwnProperty(dialog.peerId)) { |
|
const ret = this.addDialogNew({dialog}); |
|
if(ret) { |
|
const idx = appMessagesManager.getDialogByPeerId(dialog.peerId)[1]; |
|
positionElementByIndex(ret.dom.listEl, this.chatList, idx); |
|
} else { |
|
return; |
|
} |
|
} |
|
} else { |
|
const dom = this.getDialogDom(dialog.peerId); |
|
if(dom) { |
|
dom.listEl.remove(); |
|
delete this.doms[dialog.peerId]; |
|
} |
|
|
|
return; |
|
} |
|
|
|
/* const topOffset = this.getOffset('top'); |
|
if(topOffset.index && dialog.index > topOffset.index) { |
|
const dom = this.getDialogDom(dialog.peerId); |
|
if(dom) { |
|
dom.listEl.remove(); |
|
delete this.doms[dialog.peerId]; |
|
} |
|
|
|
return; |
|
} |
|
|
|
if(!this.doms.hasOwnProperty(dialog.peerId)) { |
|
this.addDialogNew({dialog}); |
|
} */ |
|
|
|
if(this.getDialogDom(dialog.peerId)) { |
|
this.setLastMessage(dialog); |
|
this.reorderDialogs(); |
|
} |
|
} |
|
|
|
onTabChange = () => { |
|
this.doms = {}; |
|
this.scroll.loadedAll.top = true; |
|
this.scroll.loadedAll.bottom = false; |
|
this.loadDialogsPromise = undefined; |
|
this.chatList = this.chatLists[this.filterId]; |
|
this.loadDialogs(); |
|
}; |
|
|
|
private setFilterUnreadCount(filterId: number, folder?: Dialog[]) { |
|
const unreadSpan = filterId === 0 ? this.allUnreadCount : this.filtersRendered[filterId]?.unread; |
|
if(!unreadSpan) { |
|
return; |
|
} |
|
|
|
folder = folder || appMessagesManager.dialogsStorage.getFolder(filterId); |
|
let mutedCount = 0; |
|
let notMutedCount = 0; |
|
folder.forEach(dialog => { |
|
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true); |
|
|
|
if(isMuted && filterId === 0) { |
|
return; |
|
} |
|
|
|
const value = +!!dialog.unread_count || +dialog.pFlags.unread_mark || 0; // * unread_mark can be undefined |
|
if(isMuted) mutedCount += value; |
|
else notMutedCount += value; |
|
}); |
|
|
|
unreadSpan.classList.toggle('badge-gray', mutedCount && !notMutedCount); |
|
|
|
const sum = mutedCount + notMutedCount; |
|
unreadSpan.innerText = sum ? '' + sum : ''; |
|
} |
|
|
|
private setFiltersUnreadCount() { |
|
for(const filterId in this.filtersRendered) { |
|
this.setFilterUnreadCount(+filterId); |
|
} |
|
|
|
this.setFilterUnreadCount(0); |
|
} |
|
|
|
/** |
|
* Удалит неподходящие чаты из списка, но не добавит их(!) |
|
*/ |
|
private validateForFilter() { |
|
// !WARNING, возможно это было зачем-то, но комментарий исправил архивирование |
|
//if(this.filterId === 0) return; |
|
|
|
const folder = appMessagesManager.dialogsStorage.getFolder(this.filterId); |
|
for(const _peerId in this.doms) { |
|
const peerId = +_peerId; |
|
|
|
// если больше не подходит по фильтру, удаляем |
|
if(folder.findIndex((dialog) => dialog.peerId === peerId) === -1) { |
|
const listEl = this.doms[peerId].listEl; |
|
listEl.remove(); |
|
delete this.doms[peerId]; |
|
} |
|
} |
|
} |
|
|
|
private addFilter(filter: Pick<DialogFilter, 'title' | 'id' | 'orderIndex'> & Partial<{titleEl: HTMLElement}>) { |
|
if(this.filtersRendered[filter.id]) return; |
|
|
|
const menuTab = document.createElement('div'); |
|
menuTab.classList.add('menu-horizontal-div-item'); |
|
const span = document.createElement('span'); |
|
const titleSpan = document.createElement('span'); |
|
titleSpan.classList.add('text-super'); |
|
if(filter.titleEl) titleSpan.append(filter.titleEl); |
|
else titleSpan.innerHTML = RichTextProcessor.wrapEmojiText(filter.title); |
|
const unreadSpan = document.createElement('div'); |
|
unreadSpan.classList.add('badge', 'badge-20', 'badge-primary'); |
|
const i = document.createElement('i'); |
|
span.append(titleSpan, unreadSpan, i); |
|
menuTab.append(span); |
|
ripple(menuTab); |
|
|
|
const containerToAppend = this.folders.menu as HTMLElement; |
|
positionElementByIndex(menuTab, containerToAppend, filter.orderIndex); |
|
//containerToAppend.append(li); |
|
|
|
const ul = this.createChatList(); |
|
const div = document.createElement('div'); |
|
div.append(ul); |
|
div.dataset.filterId = '' + filter.id; |
|
//this.folders.container.append(div); |
|
positionElementByIndex(div, this.folders.container, filter.orderIndex); |
|
|
|
this.chatLists[filter.id] = ul; |
|
this.setListClickListener(ul, null, true); |
|
|
|
this.filtersRendered[filter.id] = { |
|
menu: menuTab, |
|
container: div, |
|
unread: unreadSpan, |
|
title: titleSpan |
|
}; |
|
|
|
if(!this.showFiltersTimeout && Object.keys(this.filtersRendered).length > 1) { |
|
this.showFiltersTimeout = window.setTimeout(() => { |
|
this.showFiltersTimeout = 0; |
|
this.folders.menuScrollContainer.classList.remove('hide'); |
|
this.setFiltersUnreadCount(); |
|
}, 0); |
|
} |
|
} |
|
|
|
private loadDialogs(side: SliceSides = 'bottom') { |
|
/* if(testScroll) { |
|
return; |
|
} */ |
|
|
|
if(this.loadDialogsPromise/* || 1 === 1 */) return this.loadDialogsPromise; |
|
|
|
const promise = new Promise<void>(async(resolve, reject) => { |
|
if(!this.chatList.childElementCount) { |
|
const container = this.chatList.parentElement; |
|
container.append(this.chatsPreloader); |
|
} |
|
|
|
//return; |
|
|
|
const filterId = this.filterId; |
|
let loadCount = 30/*this.chatsLoadCount */; |
|
|
|
const storage = appMessagesManager.dialogsStorage.getFolder(filterId); |
|
let offsetIndex = 0; |
|
|
|
if(side === 'top') { |
|
const element = this.chatList.firstElementChild as HTMLElement; |
|
if(element) { |
|
const peerId = +element.dataset.peerId; |
|
const index = storage.findIndex(dialog => dialog.peerId === peerId); |
|
const needIndex = Math.max(0, index - loadCount); |
|
loadCount = index - needIndex; |
|
offsetIndex = storage[needIndex].index + 1; |
|
} |
|
} else { |
|
const element = this.chatList.lastElementChild as HTMLElement; |
|
if(element) { |
|
const peerId = +element.dataset.peerId; |
|
const dialog = storage.find(dialog => dialog.peerId === peerId); |
|
offsetIndex = dialog.index; |
|
} |
|
} |
|
|
|
//let offset = storage[storage.length - 1]?.index || 0; |
|
|
|
try { |
|
//console.time('getDialogs time'); |
|
|
|
const getConversationPromise = (this.filterId > 1 ? appUsersManager.getContacts() as Promise<any> : Promise.resolve()).then(() => { |
|
return appMessagesManager.getConversations('', offsetIndex, loadCount, filterId); |
|
}); |
|
|
|
const result = await getConversationPromise; |
|
|
|
if(this.filterId !== filterId) { |
|
return; |
|
} |
|
|
|
//console.timeEnd('getDialogs time'); |
|
|
|
// * loaded all |
|
//if(!result.dialogs.length || this.chatList.childElementCount === result.count) { |
|
// !result.dialogs.length не подходит, так как при супердревном диалоге getConversations его не выдаст. |
|
//if(this.chatList.childElementCount === result.count) { |
|
if(side === 'bottom') { |
|
if(result.isEnd) { |
|
this.scroll.loadedAll[side] = true; |
|
} |
|
} else { |
|
const storage = appMessagesManager.dialogsStorage.getFolder(filterId); |
|
if(!result.dialogs.length || (storage.length && storage[0].index < offsetIndex)) { |
|
this.scroll.loadedAll[side] = true; |
|
} |
|
} |
|
|
|
if(result.dialogs.length) { |
|
const dialogs = side === 'top' ? result.dialogs.slice().reverse() : result.dialogs; |
|
|
|
dialogs.forEach((dialog) => { |
|
this.addDialogNew({ |
|
dialog, |
|
append: side === 'bottom' |
|
}); |
|
}); |
|
} |
|
|
|
this.log.debug('getDialogs ' + loadCount + ' dialogs by offset:', offsetIndex, result, this.chatList.childElementCount); |
|
|
|
setTimeout(() => { |
|
this.scroll.onScroll(); |
|
}, 0); |
|
} catch(err) { |
|
this.log.error(err); |
|
} |
|
|
|
this.chatsPreloader.remove(); |
|
resolve(); |
|
}); |
|
|
|
return this.loadDialogsPromise = promise.finally(() => { |
|
this.loadDialogsPromise = undefined; |
|
}); |
|
} |
|
|
|
public onChatsRegularScroll = () => { |
|
if(this.sliceTimeout) clearTimeout(this.sliceTimeout); |
|
this.sliceTimeout = window.setTimeout(() => { |
|
this.sliceTimeout = undefined; |
|
|
|
if(!this.chatList.childElementCount) { |
|
return; |
|
} |
|
|
|
/* const observer = new IntersectionObserver((entries) => { |
|
const |
|
}); |
|
|
|
Array.from(this.chatList.children).forEach(el => { |
|
observer.observe(el); |
|
}); */ |
|
|
|
//const scrollTopWas = this.scroll.scrollTop; |
|
|
|
const firstElementChild = this.chatList.firstElementChild; |
|
const rectContainer = this.scroll.container.getBoundingClientRect(); |
|
const rectTarget = firstElementChild.getBoundingClientRect(); |
|
const children = Array.from(this.scroll.splitUp.children) as HTMLElement[]; |
|
|
|
const offsetTop = this.folders.container.offsetTop; |
|
const firstY = rectContainer.y + offsetTop; |
|
const lastY = rectContainer.y; |
|
|
|
const firstElement = findUpTag(document.elementFromPoint(Math.ceil(rectTarget.x), Math.ceil(firstY + 1)), firstElementChild.tagName) as HTMLElement; |
|
const lastElement = findUpTag(document.elementFromPoint(Math.ceil(rectTarget.x), Math.floor(lastY + rectContainer.height - 1)), firstElementChild.tagName) as HTMLElement; |
|
|
|
//alert('got element:' + rect.y); |
|
|
|
if(!firstElement || !lastElement) { |
|
return; |
|
} |
|
|
|
//alert('got element:' + !!firstElement); |
|
|
|
const firstElementRect = firstElement.getBoundingClientRect(); |
|
const elementOverflow = firstElementRect.y - firstY; |
|
|
|
const sliced: HTMLElement[] = []; |
|
const firstIndex = children.indexOf(firstElement); |
|
const lastIndex = children.indexOf(lastElement); |
|
|
|
const saveLength = 10; |
|
|
|
const sliceFromStart = isSafari ? [] : children.slice(0, Math.max(0, firstIndex - saveLength)); |
|
const sliceFromEnd = children.slice(lastIndex + saveLength); |
|
|
|
/* if(sliceFromStart.length !== sliceFromEnd.length) { |
|
console.log('not equal', sliceFromStart.length, sliceFromEnd.length); |
|
} |
|
|
|
if(sliceFromStart.length > sliceFromEnd.length) { |
|
const diff = sliceFromStart.length - sliceFromEnd.length; |
|
sliceFromStart.splice(0, diff); |
|
} else if(sliceFromEnd.length > sliceFromStart.length) { |
|
const diff = sliceFromEnd.length - sliceFromStart.length; |
|
sliceFromEnd.splice(sliceFromEnd.length - diff, diff); |
|
} */ |
|
|
|
if(sliceFromStart.length) { |
|
this.scroll.loadedAll['top'] = false; |
|
} |
|
|
|
if(sliceFromEnd.length) { |
|
this.scroll.loadedAll['bottom'] = false; |
|
} |
|
|
|
sliced.push(...sliceFromStart); |
|
sliced.push(...sliceFromEnd); |
|
|
|
sliced.forEach(el => { |
|
el.remove(); |
|
const peerId = +el.dataset.peerId; |
|
delete this.doms[peerId]; |
|
}); |
|
|
|
//this.log('[slicer] elements', firstElement, lastElement, rect, sliced, sliceFromStart.length, sliceFromEnd.length); |
|
|
|
//this.log('[slicer] reset scrollTop', this.scroll.scrollTop, firstElement.offsetTop, firstElementRect.y, rect.y, elementOverflow); |
|
|
|
//alert('left length:' + children.length); |
|
|
|
this.scroll.scrollTop = firstElement.offsetTop - elementOverflow; |
|
/* const firstElementRect = firstElement.getBoundingClientRect(); |
|
const scrollTop = */ |
|
|
|
//this.scroll.scrollIntoView(firstElement, false); |
|
}, 200); |
|
}; |
|
|
|
public onChatsScrollTop = () => { |
|
this.onChatsScroll('top'); |
|
}; |
|
|
|
public onChatsScroll = (side: SliceSides = 'bottom') => { |
|
if(this.scroll.loadedAll[side] || this.loadDialogsPromise) return; |
|
this.log('onChatsScroll', side); |
|
this.loadDialogs(side); |
|
}; |
|
|
|
public setListClickListener(list: HTMLUListElement, onFound?: () => void, withContext = false, autonomous = false, openInner = false) { |
|
let lastActiveListElement: HTMLElement; |
|
|
|
const setPeerFunc = (openInner ? appImManager.setInnerPeer : appImManager.setPeer).bind(appImManager); |
|
|
|
list.dataset.autonomous = '' + +autonomous; |
|
list.addEventListener('mousedown', (e) => { |
|
if(e.button !== 0) return; |
|
//cancelEvent(e); |
|
|
|
this.log('dialogs click list'); |
|
const target = e.target as HTMLElement; |
|
const elem = findUpTag(target, 'LI'); |
|
|
|
if(!elem) { |
|
return; |
|
} |
|
|
|
if(autonomous) { |
|
const sameElement = lastActiveListElement === elem; |
|
if(lastActiveListElement && !sameElement) { |
|
lastActiveListElement.classList.remove('active'); |
|
} |
|
|
|
if(elem) { |
|
elem.classList.add('active'); |
|
lastActiveListElement = elem; |
|
this.lastActiveElements.add(elem); |
|
} |
|
} |
|
|
|
if(elem) { |
|
if(onFound) onFound(); |
|
|
|
const peerId = +elem.dataset.peerId; |
|
const lastMsgId = +elem.dataset.mid || undefined; |
|
|
|
setPeerFunc(peerId, lastMsgId); |
|
} else { |
|
setPeerFunc(0); |
|
} |
|
}, {capture: true}); |
|
|
|
if(DEBUG) { |
|
list.addEventListener('dblclick', (e) => { |
|
const li = findUpTag(e.target, 'LI'); |
|
if(li) { |
|
const peerId = +li.dataset.peerId; |
|
this.log('debug dialog:', appMessagesManager.getDialogByPeerId(peerId)); |
|
} |
|
}); |
|
} |
|
|
|
if(withContext) { |
|
attachContextMenuListener(list, this.contextMenu.onContextMenu); |
|
} |
|
} |
|
|
|
public createChatList(/* options: { |
|
avatarSize?: number, |
|
handheldsSize?: number, |
|
//size?: number, |
|
} = {} */) { |
|
const list = document.createElement('ul'); |
|
list.classList.add('chatlist'/* , |
|
'chatlist-avatar-' + (options.avatarSize || 54) *//* , 'chatlist-' + (options.size || 72) */); |
|
|
|
/* if(options.handheldsSize) { |
|
list.classList.add('chatlist-handhelds-' + options.handheldsSize); |
|
} */ |
|
|
|
return list; |
|
} |
|
|
|
private reorderDialogs() { |
|
//const perf = performance.now(); |
|
if(this.reorderDialogsTimeout) { |
|
window.cancelAnimationFrame(this.reorderDialogsTimeout); |
|
} |
|
|
|
this.reorderDialogsTimeout = window.requestAnimationFrame(() => { |
|
this.reorderDialogsTimeout = 0; |
|
const offset = Math.max(0, this.getOffset('top').pos); |
|
|
|
const dialogs = appMessagesManager.dialogsStorage.getFolder(this.filterId); |
|
const currentOrder = (Array.from(this.chatList.children) as HTMLElement[]).map(el => +el.dataset.peerId); |
|
|
|
dialogs.forEach((dialog, index) => { |
|
const dom = this.getDialogDom(dialog.peerId); |
|
if(!dom) { |
|
return; |
|
} |
|
|
|
const needIndex = index - offset; |
|
if(needIndex > currentOrder.length) { |
|
dom.listEl.remove(); |
|
delete this.doms[dialog.peerId]; |
|
return; |
|
} |
|
|
|
const peerIdByIndex = currentOrder[needIndex]; |
|
|
|
if(peerIdByIndex !== dialog.peerId) { |
|
if(positionElementByIndex(dom.listEl, this.chatList, needIndex)) { |
|
this.log.debug('setDialogPosition:', dialog, dom, peerIdByIndex, needIndex); |
|
} |
|
} |
|
}); |
|
|
|
//this.log('Reorder time:', performance.now() - perf); |
|
}); |
|
} |
|
|
|
public setLastMessage(dialog: Dialog, lastMessage?: any, dom?: DialogDom, highlightWord?: string) { |
|
///////console.log('setlastMessage:', lastMessage); |
|
if(!dom) { |
|
dom = this.getDialogDom(dialog.peerId); |
|
|
|
if(!dom) { |
|
//this.log.error('no dom for dialog:', dialog, lastMessage, dom, highlightWord); |
|
return; |
|
} |
|
} |
|
|
|
let draftMessage: MyDraftMessage; |
|
if(!lastMessage) { |
|
if(dialog.draft && dialog.draft._ === 'draftMessage') { |
|
draftMessage = dialog.draft; |
|
} |
|
|
|
lastMessage = appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message); |
|
} |
|
|
|
if(lastMessage._ === 'messageEmpty'/* || (lastMessage._ === 'messageService' && !lastMessage.rReply) */) { |
|
dom.lastMessageSpan.innerHTML = ''; |
|
dom.lastTimeSpan.innerHTML = ''; |
|
delete dom.listEl.dataset.mid; |
|
return; |
|
} |
|
|
|
let peer = dialog.peer; |
|
let peerId = dialog.peerId; |
|
//let peerId = appMessagesManager.getMessagePeer(lastMessage); |
|
|
|
//console.log('setting last message:', lastMessage); |
|
|
|
/* if(!dom.lastMessageSpan.classList.contains('user-typing')) */ { |
|
|
|
dom.lastMessageSpan.textContent = ''; |
|
if(highlightWord && lastMessage.message) { |
|
dom.lastMessageSpan.append(appMessagesManager.wrapMessageForReply(lastMessage, undefined, undefined, false, highlightWord)); |
|
} else if(draftMessage) { |
|
dom.lastMessageSpan.append(appMessagesManager.wrapMessageForReply(draftMessage)); |
|
} else if(!lastMessage.deleted) { |
|
dom.lastMessageSpan.append(appMessagesManager.wrapMessageForReply(lastMessage)); |
|
} |
|
|
|
/* if(lastMessage.from_id === auth.id) { // You: */ |
|
if(draftMessage) { |
|
const bold = document.createElement('b'); |
|
bold.classList.add('danger'); |
|
bold.append(i18n('Draft')); |
|
bold.append(': '); |
|
dom.lastMessageSpan.prepend(bold); |
|
} else if(peer._ !== 'peerUser' && peerId !== lastMessage.fromId && !lastMessage.action) { |
|
const sender = appPeersManager.getPeer(lastMessage.fromId); |
|
if(sender && sender.id) { |
|
const senderBold = document.createElement('b'); |
|
|
|
if(sender.id === rootScope.myId) { |
|
senderBold.append(i18n('FromYou')); |
|
} else { |
|
//str = sender.first_name || sender.last_name || sender.username; |
|
senderBold.append(new PeerTitle({ |
|
peerId: lastMessage.fromId, |
|
onlyFirstName: true, |
|
}).element); |
|
} |
|
|
|
senderBold.append(': '); |
|
//console.log(sender, senderBold.innerText); |
|
dom.lastMessageSpan.prepend(senderBold); |
|
} //////// else console.log('no sender', lastMessage, peerId); |
|
} |
|
} |
|
|
|
if(!lastMessage.deleted || draftMessage/* && lastMessage._ !== 'draftMessage' */) { |
|
const date = draftMessage ? Math.max(draftMessage.date, lastMessage.date || 0) : lastMessage.date; |
|
dom.lastTimeSpan.textContent = ''; |
|
dom.lastTimeSpan.append(formatDateAccordingToTodayNew(new Date(date * 1000))); |
|
} else dom.lastTimeSpan.textContent = ''; |
|
|
|
if(this.doms[peerId] === dom) { |
|
this.setUnreadMessages(dialog); |
|
} else { // means search |
|
dom.listEl.dataset.mid = lastMessage.mid; |
|
} |
|
} |
|
|
|
private setUnreadMessages(dialog: Dialog) { |
|
const dom = this.getDialogDom(dialog.peerId); |
|
|
|
if(dialog.folder_id === 1) { |
|
this.accumulateArchivedUnread(); |
|
} |
|
|
|
if(!dom) { |
|
//this.log.error('setUnreadMessages no dom!', dialog); |
|
return; |
|
} |
|
|
|
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true); |
|
const wasMuted = dom.listEl.classList.contains('is-muted'); |
|
if(isMuted !== wasMuted) { |
|
SetTransition(dom.listEl, 'is-muted', isMuted, 200); |
|
} |
|
|
|
const lastMessage = dialog.draft && dialog.draft._ === 'draftMessage' ? |
|
dialog.draft : |
|
appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message); |
|
if(lastMessage._ !== 'messageEmpty' && !lastMessage.deleted && |
|
lastMessage.pFlags.out && lastMessage.peerId !== rootScope.myId/* && |
|
dialog.read_outbox_max_id */) { // maybe comment, 06.20.2020 |
|
const outgoing = (lastMessage.pFlags && lastMessage.pFlags.unread) |
|
/* && dialog.read_outbox_max_id !== 0 */; // maybe uncomment, 31.01.2020 |
|
|
|
//console.log('outgoing', outgoing, lastMessage); |
|
|
|
if(outgoing) { |
|
dom.statusSpan.classList.remove('tgico-checks'); |
|
dom.statusSpan.classList.add('tgico-check'); |
|
} else { |
|
dom.statusSpan.classList.remove('tgico-check'); |
|
dom.statusSpan.classList.add('tgico-checks'); |
|
} |
|
} else dom.statusSpan.classList.remove('tgico-check', 'tgico-checks'); |
|
|
|
dom.unreadMessagesSpan.innerText = ''; |
|
|
|
const filter = appMessagesManager.filtersStorage.filters[this.filterId]; |
|
let isPinned: boolean; |
|
if(filter) { |
|
isPinned = filter.pinned_peers.findIndex(peerId => peerId === dialog.peerId) !== -1; |
|
} else { |
|
isPinned = !!dialog.pFlags.pinned; |
|
} |
|
|
|
if(isPinned) { |
|
dom.unreadMessagesSpan.classList.add('tgico-chatspinned', 'tgico'); |
|
} else { |
|
dom.unreadMessagesSpan.classList.remove('tgico-chatspinned', 'tgico'); |
|
} |
|
|
|
if(dialog.unread_count || dialog.pFlags.unread_mark) { |
|
//dom.unreadMessagesSpan.innerText = '' + (dialog.unread_count ? formatNumber(dialog.unread_count, 1) : ' '); |
|
dom.unreadMessagesSpan.innerText = '' + (dialog.unread_count || ' '); |
|
dom.unreadMessagesSpan.classList.add('unread'); |
|
} else { |
|
dom.unreadMessagesSpan.classList.remove('unread'); |
|
} |
|
} |
|
|
|
private accumulateArchivedUnread() { |
|
if(this.accumulateArchivedTimeout) return; |
|
this.accumulateArchivedTimeout = window.setTimeout(() => { |
|
this.accumulateArchivedTimeout = 0; |
|
const dialogs = appMessagesManager.dialogsStorage.getFolder(1); |
|
const sum = dialogs.reduce((acc, dialog) => acc + dialog.unread_count, 0); |
|
rootScope.broadcast('dialogs_archived_unread', {count: sum}); |
|
}, 0); |
|
} |
|
|
|
private getDialogDom(peerId: number) { |
|
return this.doms[peerId]; |
|
} |
|
|
|
public addDialogNew(options: { |
|
dialog: Dialog | number, |
|
container?: HTMLUListElement | Scrollable | false, |
|
drawStatus?: boolean, |
|
rippleEnabled?: boolean, |
|
onlyFirstName?: boolean, |
|
meAsSaved?: boolean, |
|
append?: boolean, |
|
avatarSize?: number, |
|
autonomous?: boolean |
|
}) { |
|
return this.addDialog(options.dialog, options.container, options.drawStatus, options.rippleEnabled, options.onlyFirstName, options.meAsSaved, options.append, options.avatarSize, options.autonomous); |
|
} |
|
|
|
public addDialog(_dialog: Dialog | number, container?: HTMLUListElement | Scrollable | false, drawStatus = true, rippleEnabled = true, onlyFirstName = false, meAsSaved = true, append = true, avatarSize = 54, autonomous = !!container) { |
|
let dialog: Dialog; |
|
|
|
if(typeof(_dialog) === 'number') { |
|
let originalDialog = appMessagesManager.getDialogByPeerId(_dialog)[0]; |
|
if(!originalDialog) { |
|
originalDialog = { |
|
peerId: _dialog, |
|
peer: appPeersManager.getOutputPeer(_dialog), |
|
pFlags: {} |
|
} as any; |
|
} |
|
|
|
dialog = originalDialog; |
|
} else { |
|
dialog = _dialog; |
|
} |
|
|
|
const peerId: number = dialog.peerId; |
|
|
|
if(container === undefined) { |
|
if(this.doms[peerId]) return; |
|
|
|
const filter = appMessagesManager.filtersStorage.filters[this.filterId]; |
|
if((filter && !appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter)) || (!filter && this.filterId !== dialog.folder_id)) { |
|
return; |
|
} |
|
} |
|
|
|
const avatarEl = new AvatarElement(); |
|
avatarEl.setAttribute('dialog', meAsSaved ? '1' : '0'); |
|
avatarEl.setAttribute('peer', '' + peerId); |
|
avatarEl.classList.add('dialog-avatar', 'avatar-' + avatarSize); |
|
|
|
if(drawStatus && peerId !== rootScope.myId && dialog.peer) { |
|
const peer = dialog.peer; |
|
|
|
switch(peer._) { |
|
case 'peerUser': |
|
const user = appUsersManager.getUser(peerId); |
|
//console.log('found user', user); |
|
|
|
if(user.status && user.status._ === 'userStatusOnline') { |
|
avatarEl.classList.add('is-online'); |
|
} |
|
|
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
const captionDiv = document.createElement('div'); |
|
captionDiv.classList.add('user-caption'); |
|
|
|
const titleSpanContainer = document.createElement('span'); |
|
titleSpanContainer.classList.add('user-title'); |
|
|
|
const peerTitle = new PeerTitle({ |
|
peerId, |
|
dialog: meAsSaved, |
|
onlyFirstName, |
|
plainText: false |
|
}); |
|
|
|
titleSpanContainer.append(peerTitle.element); |
|
//p.classList.add('') |
|
|
|
// в других случаях иконка верификации не нужна (а первый - это главные чатлисты) |
|
//if(!container) { |
|
const peer = appPeersManager.getPeer(peerId); |
|
|
|
// for muted icon |
|
titleSpanContainer.classList.add('tgico'); // * эта строка будет актуальна только для !container, но ладно |
|
|
|
if(peer?.pFlags?.verified) { |
|
titleSpanContainer.classList.add('is-verified'); |
|
const i = document.createElement('i'); |
|
i.classList.add('verified-icon'); |
|
titleSpanContainer.append(i); |
|
} |
|
//} |
|
|
|
const span = document.createElement('span'); |
|
span.classList.add('user-last-message'); |
|
|
|
//captionDiv.append(titleSpan); |
|
//captionDiv.append(span); |
|
|
|
const li = document.createElement('li'); |
|
if(rippleEnabled) { |
|
ripple(li); |
|
} |
|
|
|
li.append(avatarEl, captionDiv); |
|
li.dataset.peerId = '' + peerId; |
|
|
|
const statusSpan = document.createElement('span'); |
|
statusSpan.classList.add('message-status'); |
|
|
|
const lastTimeSpan = document.createElement('span'); |
|
lastTimeSpan.classList.add('message-time'); |
|
|
|
const unreadMessagesSpan = document.createElement('div'); |
|
unreadMessagesSpan.className = 'dialog-subtitle-badge badge badge-24'; |
|
|
|
const titleP = document.createElement('p'); |
|
titleP.classList.add('dialog-title'); |
|
|
|
const rightSpan = document.createElement('span'); |
|
rightSpan.classList.add('dialog-title-details'); |
|
rightSpan.append(statusSpan, lastTimeSpan); |
|
titleP.append(titleSpanContainer, rightSpan); |
|
|
|
const messageP = document.createElement('p'); |
|
messageP.classList.add('dialog-subtitle'); |
|
messageP.append(span, unreadMessagesSpan); |
|
|
|
captionDiv.append(titleP, messageP); |
|
|
|
const dom: DialogDom = { |
|
avatarEl, |
|
captionDiv, |
|
titleSpan: peerTitle.element, |
|
titleSpanContainer, |
|
statusSpan, |
|
lastTimeSpan, |
|
unreadMessagesSpan, |
|
lastMessageSpan: span, |
|
containerEl: li, |
|
listEl: li |
|
}; |
|
|
|
/* let good = false; |
|
for(const folderId in this.chatLists) { |
|
if(this.chatLists[folderId] === container) { |
|
good = true; |
|
} |
|
} */ |
|
const method: 'append' | 'prepend' = append ? 'append' : 'prepend'; |
|
if(container === undefined/* || good */) { |
|
this.scroll[method](li); |
|
|
|
this.doms[dialog.peerId] = dom; |
|
|
|
/* if(container) { |
|
container.append(li); |
|
} */ |
|
|
|
const isMuted = appNotificationsManager.isPeerLocalMuted(dialog.peerId, true); |
|
if(isMuted) { |
|
li.classList.add('is-muted'); |
|
} |
|
|
|
this.setLastMessage(dialog); |
|
} else if(container) { |
|
container[method](li); |
|
} |
|
|
|
if(!autonomous && appImManager.chat?.peerId === peerId) { |
|
li.classList.add('active'); |
|
this.lastActiveElements.add(li); |
|
} |
|
|
|
return {dom, dialog}; |
|
} |
|
|
|
public setTyping(dialog: Dialog, user: User) { |
|
const dom = this.getDialogDom(dialog.peerId); |
|
if(!dom) { |
|
return; |
|
} |
|
|
|
let str = ''; |
|
if(dialog.peerId < 0) { |
|
let s = user.rFirstName || user.username; |
|
if(!s) return; |
|
str = s + ' '; |
|
} |
|
|
|
const senderBold = document.createElement('i'); |
|
str += 'typing...'; |
|
senderBold.innerHTML = str; |
|
|
|
dom.lastMessageSpan.innerHTML = ''; |
|
dom.lastMessageSpan.append(senderBold); |
|
dom.lastMessageSpan.classList.add('user-typing'); |
|
} |
|
|
|
public unsetTyping(dialog: Dialog) { |
|
const dom = this.getDialogDom(dialog.peerId); |
|
if(!dom) { |
|
return; |
|
} |
|
|
|
dom.lastMessageSpan.classList.remove('user-typing'); |
|
this.setLastMessage(dialog, null, dom); |
|
} |
|
} |
|
|
|
const appDialogsManager = new AppDialogsManager(); |
|
MOUNT_CLASS_TO.appDialogsManager = appDialogsManager; |
|
export default appDialogsManager;
|
|
|