Fix import order
This commit is contained in:
parent
4d2523d810
commit
6b3b2c343d
@ -718,7 +718,7 @@ export default class AppSearchSuper {
|
||||
if(showMembersCount && (peer.participants_count || peer.participants)) {
|
||||
const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(cleanSearchText(query))})`, 'gi');
|
||||
dom.titleSpan.innerHTML = dom.titleSpan.innerHTML.replace(regExp, '<i>$1</i>');
|
||||
dom.lastMessageSpan.append(appChatsManager.getChatMembersString(-peerId));
|
||||
dom.lastMessageSpan.append(appProfileManager.getChatMembersString(-peerId));
|
||||
} else if(peerId === rootScope.myId) {
|
||||
dom.lastMessageSpan.append(i18n('Presence.YourChat'));
|
||||
} else {
|
||||
@ -811,7 +811,7 @@ export default class AppSearchSuper {
|
||||
autonomous: true
|
||||
});
|
||||
|
||||
dom.lastMessageSpan.append(peerId > 0 ? appUsersManager.getUserStatusString(peerId) : appChatsManager.getChatMembersString(peerId));
|
||||
dom.lastMessageSpan.append(peerId > 0 ? appUsersManager.getUserStatusString(peerId) : appProfileManager.getChatMembersString(-peerId));
|
||||
});
|
||||
|
||||
if(!state.recentSearch.length) {
|
||||
|
@ -447,7 +447,7 @@ export default class AppSelectPeers {
|
||||
|
||||
let subtitleEl: HTMLElement;
|
||||
if(peerId < 0) {
|
||||
subtitleEl = appChatsManager.getChatMembersString(-peerId);
|
||||
subtitleEl = appProfileManager.getChatMembersString(-peerId);
|
||||
} else if(peerId === rootScope.myId) {
|
||||
subtitleEl = i18n('Presence.YourChat');
|
||||
} else {
|
||||
|
@ -14,9 +14,10 @@ import appPhotosManager from "../lib/appManagers/appPhotosManager";
|
||||
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
|
||||
import { attachClickEvent } from "../helpers/dom/clickEvent";
|
||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||
import appAvatarsManager from "../lib/appManagers/appAvatarsManager";
|
||||
|
||||
const onAvatarUpdate = (peerId: number) => {
|
||||
appProfileManager.removeFromAvatarsCache(peerId);
|
||||
appAvatarsManager.removeFromAvatarsCache(peerId);
|
||||
(Array.from(document.querySelectorAll('avatar-element[peer="' + peerId + '"]')) as AvatarElement[]).forEach(elem => {
|
||||
//console.log('updating avatar:', elem);
|
||||
elem.update();
|
||||
@ -180,7 +181,7 @@ export default class AvatarElement extends HTMLElement {
|
||||
}
|
||||
|
||||
private r(onlyThumb = false) {
|
||||
const res = appProfileManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle, onlyThumb);
|
||||
const res = appAvatarsManager.putPhoto(this, this.peerId, this.isDialog, this.peerTitle, onlyThumb);
|
||||
const promise = res ? res.loadPromise : Promise.resolve();
|
||||
if(this.loadPromises) {
|
||||
if(res && res.cached) {
|
||||
|
@ -48,6 +48,7 @@ import ButtonCorner from "../../buttonCorner";
|
||||
import { cancelEvent } from "../../../helpers/dom/cancelEvent";
|
||||
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
|
||||
import replaceContent from "../../../helpers/dom/replaceContent";
|
||||
import appAvatarsManager from "../../../lib/appManagers/appAvatarsManager";
|
||||
|
||||
let setText = (text: string, row: Row) => {
|
||||
//fastRaf(() => {
|
||||
@ -437,7 +438,7 @@ class PeerProfileAvatars {
|
||||
});
|
||||
} else {
|
||||
const photo = appPeersManager.getPeerPhoto(this.peerId);
|
||||
appProfileManager.putAvatar(avatar, this.peerId, photo, 'photo_big', img);
|
||||
appAvatarsManager.putAvatar(avatar, this.peerId, photo, 'photo_big', img);
|
||||
}
|
||||
|
||||
this.avatars.append(avatar);
|
||||
|
202
src/lib/appManagers/appAvatarsManager.ts
Normal file
202
src/lib/appManagers/appAvatarsManager.ts
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* https://github.com/morethanwords/tweb
|
||||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
|
||||
import replaceContent from "../../helpers/dom/replaceContent";
|
||||
import sequentialDom from "../../helpers/sequentialDom";
|
||||
import { UserProfilePhoto, ChatPhoto, InputFileLocation } from "../../layer";
|
||||
import RichTextProcessor from "../richtextprocessor";
|
||||
import rootScope from "../rootScope";
|
||||
import appDownloadManager from "./appDownloadManager";
|
||||
import appPeersManager from "./appPeersManager";
|
||||
import appPhotosManager from "./appPhotosManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
|
||||
type PeerPhotoSize = 'photo_small' | 'photo_big';
|
||||
|
||||
export class AppAvatarsManager {
|
||||
private savedAvatarURLs: {
|
||||
[peerId: number]: {
|
||||
[size in PeerPhotoSize]?: string | Promise<string>
|
||||
}
|
||||
} = {};
|
||||
|
||||
public removeFromAvatarsCache(peerId: number) {
|
||||
if(this.savedAvatarURLs[peerId]) {
|
||||
delete this.savedAvatarURLs[peerId];
|
||||
}
|
||||
}
|
||||
|
||||
public loadAvatar(peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize) {
|
||||
const inputPeer = appPeersManager.getInputPeerById(peerId);
|
||||
|
||||
let cached = false;
|
||||
let getAvatarPromise: Promise<string>;
|
||||
let saved = this.savedAvatarURLs[peerId];
|
||||
if(!saved || !saved[size]) {
|
||||
if(!saved) {
|
||||
saved = this.savedAvatarURLs[peerId] = {};
|
||||
}
|
||||
|
||||
//console.warn('will invoke downloadSmallFile:', peerId);
|
||||
const peerPhotoFileLocation: InputFileLocation.inputPeerPhotoFileLocation = {
|
||||
_: 'inputPeerPhotoFileLocation',
|
||||
pFlags: {},
|
||||
peer: inputPeer,
|
||||
photo_id: photo.photo_id
|
||||
};
|
||||
|
||||
if(size === 'photo_big') {
|
||||
peerPhotoFileLocation.pFlags.big = true;
|
||||
}
|
||||
|
||||
const downloadOptions = {dcId: photo.dc_id, location: peerPhotoFileLocation};
|
||||
|
||||
/* let str: string;
|
||||
const time = Date.now();
|
||||
if(peerId === 0) {
|
||||
str = `download avatar ${peerId}`;
|
||||
} */
|
||||
|
||||
const promise = appDownloadManager.download(downloadOptions);
|
||||
getAvatarPromise = saved[size] = promise.then(blob => {
|
||||
return saved[size] = URL.createObjectURL(blob);
|
||||
|
||||
/* if(str) {
|
||||
console.log(str, Date.now() / 1000, Date.now() - time);
|
||||
} */
|
||||
});
|
||||
} else if(typeof(saved[size]) !== 'string') {
|
||||
getAvatarPromise = saved[size] as Promise<any>;
|
||||
} else {
|
||||
getAvatarPromise = Promise.resolve(saved[size]);
|
||||
cached = true;
|
||||
}
|
||||
|
||||
return {cached, loadPromise: getAvatarPromise};
|
||||
}
|
||||
|
||||
public putAvatar(div: HTMLElement, peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize, img = new Image(), onlyThumb = false) {
|
||||
let {cached, loadPromise} = this.loadAvatar(peerId, photo, size);
|
||||
|
||||
let renderThumbPromise: Promise<void>;
|
||||
let callback: () => void;
|
||||
if(cached) {
|
||||
// смотри в misc.ts: renderImageFromUrl
|
||||
callback = () => {
|
||||
replaceContent(div, img);
|
||||
div.dataset.color = '';
|
||||
};
|
||||
} else {
|
||||
const animate = rootScope.settings.animationsEnabled;
|
||||
if(animate) {
|
||||
img.classList.add('fade-in');
|
||||
}
|
||||
|
||||
let thumbImage: HTMLImageElement;
|
||||
if(photo.stripped_thumb) {
|
||||
thumbImage = new Image();
|
||||
div.classList.add('avatar-relative');
|
||||
thumbImage.classList.add('avatar-photo', 'avatar-photo-thumbnail');
|
||||
img.classList.add('avatar-photo');
|
||||
const url = appPhotosManager.getPreviewURLFromBytes(photo.stripped_thumb);
|
||||
renderThumbPromise = renderImageFromUrl(thumbImage, url).then(() => {
|
||||
replaceContent(div, thumbImage);
|
||||
});
|
||||
}
|
||||
|
||||
callback = () => {
|
||||
if(photo.stripped_thumb) {
|
||||
div.append(img);
|
||||
} else {
|
||||
replaceContent(div, img);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if(div.childElementCount) {
|
||||
sequentialDom.mutateElement(img, () => {
|
||||
div.dataset.color = '';
|
||||
|
||||
if(animate) {
|
||||
img.classList.remove('fade-in');
|
||||
}
|
||||
|
||||
if(thumbImage) {
|
||||
thumbImage.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, animate ? 200 : 0);
|
||||
};
|
||||
}
|
||||
|
||||
const renderPromise = loadPromise
|
||||
.then((url) => renderImageFromUrl(img, url/* , false */))
|
||||
.then(() => callback());
|
||||
|
||||
return {cached, loadPromise: renderThumbPromise || renderPromise};
|
||||
}
|
||||
|
||||
// peerId === peerId || title
|
||||
public putPhoto(div: HTMLElement, peerId: number, isDialog = false, title = '', onlyThumb = false) {
|
||||
const photo = appPeersManager.getPeerPhoto(peerId);
|
||||
|
||||
const size: PeerPhotoSize = 'photo_small';
|
||||
const avatarAvailable = !!photo;
|
||||
const avatarRendered = div.firstElementChild && !(div.firstElementChild as HTMLElement).classList.contains('emoji');
|
||||
|
||||
const myId = rootScope.myId;
|
||||
|
||||
//console.log('loadDialogPhoto location:', location, inputPeer);
|
||||
if(peerId === myId && isDialog) {
|
||||
div.innerText = '';
|
||||
div.dataset.color = '';
|
||||
div.classList.add('tgico-saved');
|
||||
div.classList.remove('tgico-deletedaccount');
|
||||
return;
|
||||
}
|
||||
|
||||
if(peerId > 0) {
|
||||
const user = appUsersManager.getUser(peerId);
|
||||
if(user && user.pFlags && user.pFlags.deleted) {
|
||||
div.innerText = '';
|
||||
div.dataset.color = appPeersManager.getPeerColorById(peerId);
|
||||
div.classList.add('tgico-deletedaccount');
|
||||
div.classList.remove('tgico-saved');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!avatarAvailable || !avatarRendered || !this.savedAvatarURLs[peerId]) {
|
||||
let color = '';
|
||||
if(peerId && (peerId !== myId || !isDialog)) {
|
||||
color = appPeersManager.getPeerColorById(peerId);
|
||||
}
|
||||
|
||||
div.innerText = '';
|
||||
div.classList.remove('tgico-saved', 'tgico-deletedaccount');
|
||||
div.dataset.color = color;
|
||||
|
||||
let abbr: string;
|
||||
if(!title) {
|
||||
const peer = appPeersManager.getPeer(peerId);
|
||||
abbr = peer.initials ?? '';
|
||||
} else {
|
||||
abbr = RichTextProcessor.getAbbreviation(title);
|
||||
}
|
||||
|
||||
div.innerHTML = abbr;
|
||||
//return Promise.resolve(true);
|
||||
}
|
||||
|
||||
if(avatarAvailable/* && false */) {
|
||||
return this.putAvatar(div, peerId, photo, size, undefined, onlyThumb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const appAvatarsManager = new AppAvatarsManager();
|
||||
export default appAvatarsManager;
|
@ -10,17 +10,14 @@
|
||||
*/
|
||||
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import { numberThousandSplitter } from "../../helpers/number";
|
||||
import { isObject, safeReplaceObject, copy, deepEqual } from "../../helpers/object";
|
||||
import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatFull, ChatParticipant, ChatParticipants, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, SendMessageAction, Update, Updates } from "../../layer";
|
||||
import { i18n, LangPackKey } from "../langPack";
|
||||
import { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatParticipant, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, Update, Updates } from "../../layer";
|
||||
import apiManagerProxy from "../mtproto/mtprotoworker";
|
||||
import apiManager from '../mtproto/mtprotoworker';
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
import rootScope from "../rootScope";
|
||||
import apiUpdatesManager from "./apiUpdatesManager";
|
||||
import appPeersManager from "./appPeersManager";
|
||||
import appProfileManager from "./appProfileManager";
|
||||
import appStateManager from "./appStateManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
|
||||
@ -28,8 +25,6 @@ export type Channel = Chat.channel;
|
||||
|
||||
export type ChatRights = keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags'] | 'change_type' | 'change_permissions' | 'delete_chat' | 'view_participants';
|
||||
|
||||
export type UserTyping = Partial<{userId: number, action: SendMessageAction, timeout: number}>;
|
||||
|
||||
export class AppChatsManager {
|
||||
private storage = appStateManager.storages.chats;
|
||||
|
||||
@ -38,10 +33,6 @@ export class AppChatsManager {
|
||||
//private channelAccess: any;
|
||||
//private megagroups: {[id: number]: true};
|
||||
|
||||
private megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}};
|
||||
|
||||
private typingsInPeer: {[peerId: number]: UserTyping[]};
|
||||
|
||||
constructor() {
|
||||
this.clear();
|
||||
|
||||
@ -65,11 +56,7 @@ export class AppChatsManager {
|
||||
chat.default_banned_rights = update.default_banned_rights;
|
||||
rootScope.dispatchEvent('chat_update', chatId);
|
||||
}
|
||||
},
|
||||
|
||||
updateUserTyping: this.onUpdateUserTyping,
|
||||
updateChatUserTyping: this.onUpdateUserTyping,
|
||||
updateChannelUserTyping: this.onUpdateUserTyping
|
||||
}
|
||||
});
|
||||
|
||||
appStateManager.getState().then((state) => {
|
||||
@ -119,81 +106,6 @@ export class AppChatsManager {
|
||||
} else {
|
||||
this.chats = {};
|
||||
}
|
||||
|
||||
this.megagroupOnlines = {};
|
||||
this.typingsInPeer = {};
|
||||
}
|
||||
|
||||
private onUpdateUserTyping = (update: Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChannelUserTyping) => {
|
||||
const fromId = (update as Update.updateUserTyping).user_id || appPeersManager.getPeerId((update as Update.updateChatUserTyping).from_id);
|
||||
if(rootScope.myId === fromId || update.action._ === 'speakingInGroupCallAction') {
|
||||
return;
|
||||
}
|
||||
|
||||
const peerId = update._ === 'updateUserTyping' ?
|
||||
fromId :
|
||||
-((update as Update.updateChatUserTyping).chat_id || (update as Update.updateChannelUserTyping).channel_id);
|
||||
const typings = this.typingsInPeer[peerId] ?? (this.typingsInPeer[peerId] = []);
|
||||
let typing = typings.find(t => t.userId === fromId);
|
||||
|
||||
const cancelAction = () => {
|
||||
delete typing.timeout;
|
||||
//typings.findAndSplice(t => t === typing);
|
||||
const idx = typings.indexOf(typing);
|
||||
if(idx !== -1) {
|
||||
typings.splice(idx, 1);
|
||||
}
|
||||
|
||||
rootScope.dispatchEvent('peer_typings', {peerId, typings});
|
||||
|
||||
if(!typings.length) {
|
||||
delete this.typingsInPeer[peerId];
|
||||
}
|
||||
};
|
||||
|
||||
if(typing && typing.timeout !== undefined) {
|
||||
clearTimeout(typing.timeout);
|
||||
}
|
||||
|
||||
if(update.action._ === 'sendMessageCancelAction') {
|
||||
if(!typing) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancelAction();
|
||||
return;
|
||||
} else {
|
||||
if(!typing) {
|
||||
typing = {
|
||||
userId: fromId
|
||||
};
|
||||
|
||||
typings.push(typing);
|
||||
}
|
||||
|
||||
//console.log('updateChatUserTyping', update, typings);
|
||||
|
||||
typing.action = update.action;
|
||||
|
||||
if(!appUsersManager.hasUser(fromId)) {
|
||||
if(update._ === 'updateChatUserTyping') {
|
||||
if(update.chat_id && appChatsManager.hasChat(update.chat_id) && !appChatsManager.isChannel(update.chat_id)) {
|
||||
appProfileManager.getChatFull(update.chat_id);
|
||||
}
|
||||
}
|
||||
|
||||
//return;
|
||||
}
|
||||
|
||||
appUsersManager.forceUserOnline(fromId);
|
||||
|
||||
typing.timeout = window.setTimeout(cancelAction, 6000);
|
||||
rootScope.dispatchEvent('peer_typings', {peerId, typings});
|
||||
}
|
||||
};
|
||||
|
||||
public getPeerTypings(peerId: number) {
|
||||
return this.typingsInPeer[peerId];
|
||||
}
|
||||
|
||||
public saveApiChats(apiChats: any[], override?: boolean) {
|
||||
@ -486,27 +398,6 @@ export class AppChatsManager {
|
||||
return 'g' + id;
|
||||
}
|
||||
|
||||
public getChatMembersString(id: number) {
|
||||
const chat = this.getChat(id);
|
||||
const chatFull = appProfileManager.chatsFull[id];
|
||||
let count: number;
|
||||
if(chatFull) {
|
||||
if(chatFull._ === 'channelFull') {
|
||||
count = chatFull.participants_count;
|
||||
} else {
|
||||
count = (chatFull.participants as ChatParticipants.chatParticipants).participants?.length;
|
||||
}
|
||||
} else {
|
||||
count = chat.participants_count || chat.participants?.participants.length;
|
||||
}
|
||||
|
||||
const isChannel = this.isBroadcast(id);
|
||||
count = count || 1;
|
||||
|
||||
let key: LangPackKey = isChannel ? 'Peer.Status.Subscribers' : 'Peer.Status.Member';
|
||||
return i18n(key, [numberThousandSplitter(count)]);
|
||||
}
|
||||
|
||||
/* public wrapForFull(id: number, fullChat: any) {
|
||||
const chatFull = copy(fullChat);
|
||||
const chat = this.getChat(id);
|
||||
@ -600,45 +491,6 @@ export class AppChatsManager {
|
||||
});
|
||||
}
|
||||
|
||||
public async getOnlines(id: number): Promise<number> {
|
||||
if(this.isMegagroup(id)) {
|
||||
const timestamp = Date.now() / 1000 | 0;
|
||||
const cached = this.megagroupOnlines[id] ?? (this.megagroupOnlines[id] = {timestamp: 0, onlines: 1});
|
||||
if((timestamp - cached.timestamp) < 60) {
|
||||
return cached.onlines;
|
||||
}
|
||||
|
||||
const res = await apiManager.invokeApi('messages.getOnlines', {
|
||||
peer: this.getChannelInputPeer(id)
|
||||
});
|
||||
|
||||
const onlines = res.onlines ?? 1;
|
||||
cached.timestamp = timestamp;
|
||||
cached.onlines = onlines;
|
||||
|
||||
return onlines;
|
||||
} else if(this.isBroadcast(id)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const chatInfo = await appProfileManager.getChatFull(id);
|
||||
const _participants = (chatInfo as ChatFull.chatFull).participants as ChatParticipants.chatParticipants;
|
||||
if(_participants && _participants.participants) {
|
||||
const participants = _participants.participants;
|
||||
|
||||
return participants.reduce((acc: number, participant) => {
|
||||
const user = appUsersManager.getUser(participant.user_id);
|
||||
if(user && user.status && user.status._ === 'userStatusOnline') {
|
||||
return acc + 1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, 0);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private onChatUpdated = (chatId: number, updates: any) => {
|
||||
//console.log('onChatUpdated', chatId, updates);
|
||||
|
||||
@ -647,7 +499,7 @@ export class AppChatsManager {
|
||||
/* updates.updates &&
|
||||
updates.updates.length && */
|
||||
this.isChannel(chatId)) {
|
||||
appProfileManager.invalidateChannelParticipants(chatId);
|
||||
rootScope.dispatchEvent('invalidate_participants', chatId);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -103,6 +103,7 @@ export class AppImManager {
|
||||
|
||||
constructor() {
|
||||
apiUpdatesManager.attach();
|
||||
appNotificationsManager.start();
|
||||
|
||||
this.log = logger('IM', LogTypes.Log | LogTypes.Warn | LogTypes.Debug | LogTypes.Error);
|
||||
|
||||
@ -1035,7 +1036,7 @@ export class AppImManager {
|
||||
|
||||
public getPeerTyping(peerId: number, container?: HTMLElement) {
|
||||
if(!appUsersManager.isBot(peerId)) {
|
||||
const typings = appChatsManager.getPeerTypings(peerId);
|
||||
const typings = appProfileManager.getPeerTypings(peerId);
|
||||
if(!typings || !typings.length) {
|
||||
return;
|
||||
}
|
||||
@ -1152,7 +1153,7 @@ export class AppImManager {
|
||||
|
||||
const participants_count = chatInfo.participants_count || (chatInfo.participants && chatInfo.participants.participants && chatInfo.participants.participants.length) || 1;
|
||||
//if(participants_count) {
|
||||
subtitle = appChatsManager.getChatMembersString(-peerId);
|
||||
subtitle = appProfileManager.getChatMembersString(-peerId);
|
||||
|
||||
if(participants_count < 2) return subtitle;
|
||||
/* const onlines = await appChatsManager.getOnlines(chat.id);
|
||||
|
@ -53,6 +53,7 @@ import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment";
|
||||
import htmlToSpan from "../../helpers/dom/htmlToSpan";
|
||||
import { REPLIES_PEER_ID } from "../mtproto/mtproto_config";
|
||||
import formatCallDuration from "../../helpers/formatCallDuration";
|
||||
import appAvatarsManager from "./appAvatarsManager";
|
||||
|
||||
//console.trace('include');
|
||||
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
|
||||
@ -304,8 +305,6 @@ export class AppMessagesManager {
|
||||
this.maxSeenId = state.maxSeenMsgId;
|
||||
}
|
||||
});
|
||||
|
||||
appNotificationsManager.start();
|
||||
}
|
||||
|
||||
public construct() {
|
||||
@ -4546,7 +4545,7 @@ export class AppMessagesManager {
|
||||
|
||||
const peerPhoto = appPeersManager.getPeerPhoto(peerId);
|
||||
if(peerPhoto) {
|
||||
appProfileManager.loadAvatar(peerId, peerPhoto, 'photo_small').loadPromise.then(url => {
|
||||
appAvatarsManager.loadAvatar(peerId, peerPhoto, 'photo_small').loadPromise.then(url => {
|
||||
if(message.pFlags.unread) {
|
||||
notification.image = url;
|
||||
appNotificationsManager.notify(notification);
|
||||
|
@ -706,7 +706,6 @@ export class AppNotificationsManager {
|
||||
}
|
||||
|
||||
private registerDevice(tokenData: PushSubscriptionNotify) {
|
||||
return;
|
||||
if(this.registeredDevice && deepEqual(this.registeredDevice, tokenData)) {
|
||||
return false;
|
||||
}
|
||||
@ -725,7 +724,6 @@ export class AppNotificationsManager {
|
||||
}
|
||||
|
||||
private unregisterDevice(tokenData: PushSubscriptionNotify) {
|
||||
return;
|
||||
if(!this.registeredDevice) {
|
||||
return false;
|
||||
}
|
||||
|
@ -11,10 +11,9 @@
|
||||
|
||||
import { MOUNT_CLASS_TO } from "../../config/debug";
|
||||
import { tsNow } from "../../helpers/date";
|
||||
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl";
|
||||
import replaceContent from "../../helpers/dom/replaceContent";
|
||||
import sequentialDom from "../../helpers/sequentialDom";
|
||||
import { ChannelParticipantsFilter, ChannelsChannelParticipants, Chat, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, Update, UserFull, UserProfilePhoto } from "../../layer";
|
||||
import { numberThousandSplitter } from "../../helpers/number";
|
||||
import { ChannelParticipantsFilter, ChannelsChannelParticipants, Chat, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, SendMessageAction, Update, UserFull, UserProfilePhoto } from "../../layer";
|
||||
import { LangPackKey, i18n } from "../langPack";
|
||||
//import apiManager from '../mtproto/apiManager';
|
||||
import apiManager from '../mtproto/mtprotoworker';
|
||||
import { RichTextProcessor } from "../richtextprocessor";
|
||||
@ -22,13 +21,12 @@ import rootScope from "../rootScope";
|
||||
import SearchIndex from "../searchIndex";
|
||||
import apiUpdatesManager from "./apiUpdatesManager";
|
||||
import appChatsManager from "./appChatsManager";
|
||||
import appDownloadManager from "./appDownloadManager";
|
||||
import appNotificationsManager from "./appNotificationsManager";
|
||||
import appPeersManager from "./appPeersManager";
|
||||
import appPhotosManager, { MyPhoto } from "./appPhotosManager";
|
||||
import appPhotosManager from "./appPhotosManager";
|
||||
import appUsersManager, { User } from "./appUsersManager";
|
||||
|
||||
type PeerPhotoSize = 'photo_small' | 'photo_big';
|
||||
export type UserTyping = Partial<{userId: number, action: SendMessageAction, timeout: number}>;
|
||||
|
||||
export class AppProfileManager {
|
||||
//private botInfos: any = {};
|
||||
@ -36,11 +34,9 @@ export class AppProfileManager {
|
||||
public chatsFull: {[id: string]: ChatFull} = {};
|
||||
private fullPromises: {[peerId: string]: Promise<ChatFull.chatFull | ChatFull.channelFull | UserFull>} = {};
|
||||
|
||||
private savedAvatarURLs: {
|
||||
[peerId: number]: {
|
||||
[size in PeerPhotoSize]?: string | Promise<string>
|
||||
}
|
||||
} = {};
|
||||
private megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}};
|
||||
|
||||
private typingsInPeer: {[peerId: number]: UserTyping[]};
|
||||
|
||||
constructor() {
|
||||
rootScope.addMultipleEventsListeners({
|
||||
@ -93,7 +89,11 @@ export class AppProfileManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateUserTyping: this.onUpdateUserTyping,
|
||||
updateChatUserTyping: this.onUpdateUserTyping,
|
||||
updateChannelUserTyping: this.onUpdateUserTyping
|
||||
});
|
||||
|
||||
rootScope.addEventListener('chat_update', (chatId) => {
|
||||
@ -121,6 +121,13 @@ export class AppProfileManager {
|
||||
rootScope.dispatchEvent('chat_full_update', chatId);
|
||||
}
|
||||
});
|
||||
|
||||
rootScope.addEventListener('invalidate_participants', chatId => {
|
||||
this.invalidateChannelParticipants(chatId);
|
||||
});
|
||||
|
||||
this.megagroupOnlines = {};
|
||||
this.typingsInPeer = {};
|
||||
}
|
||||
|
||||
/* public saveBotInfo(botInfo: any) {
|
||||
@ -457,177 +464,144 @@ export class AppProfileManager {
|
||||
});
|
||||
}
|
||||
|
||||
public removeFromAvatarsCache(peerId: number) {
|
||||
if(this.savedAvatarURLs[peerId]) {
|
||||
delete this.savedAvatarURLs[peerId];
|
||||
public getChatMembersString(id: number) {
|
||||
const chat = appChatsManager.getChat(id);
|
||||
const chatFull = this.chatsFull[id];
|
||||
let count: number;
|
||||
if(chatFull) {
|
||||
if(chatFull._ === 'channelFull') {
|
||||
count = chatFull.participants_count;
|
||||
} else {
|
||||
count = (chatFull.participants as ChatParticipants.chatParticipants).participants?.length;
|
||||
}
|
||||
} else {
|
||||
count = chat.participants_count || chat.participants?.participants.length;
|
||||
}
|
||||
|
||||
const isChannel = appChatsManager.isBroadcast(id);
|
||||
count = count || 1;
|
||||
|
||||
let key: LangPackKey = isChannel ? 'Peer.Status.Subscribers' : 'Peer.Status.Member';
|
||||
return i18n(key, [numberThousandSplitter(count)]);
|
||||
}
|
||||
|
||||
public loadAvatar(peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize) {
|
||||
const inputPeer = appPeersManager.getInputPeerById(peerId);
|
||||
|
||||
let cached = false;
|
||||
let getAvatarPromise: Promise<string>;
|
||||
let saved = this.savedAvatarURLs[peerId];
|
||||
if(!saved || !saved[size]) {
|
||||
if(!saved) {
|
||||
saved = this.savedAvatarURLs[peerId] = {};
|
||||
public async getOnlines(id: number): Promise<number> {
|
||||
if(appChatsManager.isMegagroup(id)) {
|
||||
const timestamp = Date.now() / 1000 | 0;
|
||||
const cached = this.megagroupOnlines[id] ?? (this.megagroupOnlines[id] = {timestamp: 0, onlines: 1});
|
||||
if((timestamp - cached.timestamp) < 60) {
|
||||
return cached.onlines;
|
||||
}
|
||||
|
||||
//console.warn('will invoke downloadSmallFile:', peerId);
|
||||
const peerPhotoFileLocation: InputFileLocation.inputPeerPhotoFileLocation = {
|
||||
_: 'inputPeerPhotoFileLocation',
|
||||
pFlags: {},
|
||||
peer: inputPeer,
|
||||
photo_id: photo.photo_id
|
||||
};
|
||||
|
||||
if(size === 'photo_big') {
|
||||
peerPhotoFileLocation.pFlags.big = true;
|
||||
}
|
||||
|
||||
const downloadOptions = {dcId: photo.dc_id, location: peerPhotoFileLocation};
|
||||
|
||||
/* let str: string;
|
||||
const time = Date.now();
|
||||
if(peerId === 0) {
|
||||
str = `download avatar ${peerId}`;
|
||||
} */
|
||||
|
||||
const promise = appDownloadManager.download(downloadOptions);
|
||||
getAvatarPromise = saved[size] = promise.then(blob => {
|
||||
return saved[size] = URL.createObjectURL(blob);
|
||||
|
||||
/* if(str) {
|
||||
console.log(str, Date.now() / 1000, Date.now() - time);
|
||||
} */
|
||||
const res = await apiManager.invokeApi('messages.getOnlines', {
|
||||
peer: appChatsManager.getChannelInputPeer(id)
|
||||
});
|
||||
} else if(typeof(saved[size]) !== 'string') {
|
||||
getAvatarPromise = saved[size] as Promise<any>;
|
||||
} else {
|
||||
getAvatarPromise = Promise.resolve(saved[size]);
|
||||
cached = true;
|
||||
|
||||
const onlines = res.onlines ?? 1;
|
||||
cached.timestamp = timestamp;
|
||||
cached.onlines = onlines;
|
||||
|
||||
return onlines;
|
||||
} else if(appChatsManager.isBroadcast(id)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return {cached, loadPromise: getAvatarPromise};
|
||||
}
|
||||
const chatInfo = await this.getChatFull(id);
|
||||
const _participants = (chatInfo as ChatFull.chatFull).participants as ChatParticipants.chatParticipants;
|
||||
if(_participants && _participants.participants) {
|
||||
const participants = _participants.participants;
|
||||
|
||||
public putAvatar(div: HTMLElement, peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize, img = new Image(), onlyThumb = false) {
|
||||
let {cached, loadPromise} = this.loadAvatar(peerId, photo, size);
|
||||
|
||||
let renderThumbPromise: Promise<void>;
|
||||
let callback: () => void;
|
||||
if(cached) {
|
||||
// смотри в misc.ts: renderImageFromUrl
|
||||
callback = () => {
|
||||
replaceContent(div, img);
|
||||
div.dataset.color = '';
|
||||
};
|
||||
} else {
|
||||
const animate = rootScope.settings.animationsEnabled;
|
||||
if(animate) {
|
||||
img.classList.add('fade-in');
|
||||
}
|
||||
|
||||
let thumbImage: HTMLImageElement;
|
||||
if(photo.stripped_thumb) {
|
||||
thumbImage = new Image();
|
||||
div.classList.add('avatar-relative');
|
||||
thumbImage.classList.add('avatar-photo', 'avatar-photo-thumbnail');
|
||||
img.classList.add('avatar-photo');
|
||||
const url = appPhotosManager.getPreviewURLFromBytes(photo.stripped_thumb);
|
||||
renderThumbPromise = renderImageFromUrl(thumbImage, url).then(() => {
|
||||
replaceContent(div, thumbImage);
|
||||
});
|
||||
}
|
||||
|
||||
callback = () => {
|
||||
if(photo.stripped_thumb) {
|
||||
div.append(img);
|
||||
} else {
|
||||
replaceContent(div, img);
|
||||
return participants.reduce((acc: number, participant) => {
|
||||
const user = appUsersManager.getUser(participant.user_id);
|
||||
if(user && user.status && user.status._ === 'userStatusOnline') {
|
||||
return acc + 1;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if(div.childElementCount) {
|
||||
sequentialDom.mutateElement(img, () => {
|
||||
div.dataset.color = '';
|
||||
|
||||
if(animate) {
|
||||
img.classList.remove('fade-in');
|
||||
}
|
||||
|
||||
if(thumbImage) {
|
||||
thumbImage.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, animate ? 200 : 0);
|
||||
};
|
||||
return acc;
|
||||
}, 0);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const renderPromise = loadPromise
|
||||
.then((url) => renderImageFromUrl(img, url/* , false */))
|
||||
.then(() => callback());
|
||||
|
||||
return {cached, loadPromise: renderThumbPromise || renderPromise};
|
||||
}
|
||||
|
||||
// peerId === peerId || title
|
||||
public putPhoto(div: HTMLElement, peerId: number, isDialog = false, title = '', onlyThumb = false) {
|
||||
const photo = appPeersManager.getPeerPhoto(peerId);
|
||||
|
||||
const size: PeerPhotoSize = 'photo_small';
|
||||
const avatarAvailable = !!photo;
|
||||
const avatarRendered = div.firstElementChild && !(div.firstElementChild as HTMLElement).classList.contains('emoji');
|
||||
private onUpdateUserTyping = (update: Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChannelUserTyping) => {
|
||||
const fromId = (update as Update.updateUserTyping).user_id || appPeersManager.getPeerId((update as Update.updateChatUserTyping).from_id);
|
||||
if(rootScope.myId === fromId || update.action._ === 'speakingInGroupCallAction') {
|
||||
return;
|
||||
}
|
||||
|
||||
const myId = rootScope.myId;
|
||||
const peerId = update._ === 'updateUserTyping' ?
|
||||
fromId :
|
||||
-((update as Update.updateChatUserTyping).chat_id || (update as Update.updateChannelUserTyping).channel_id);
|
||||
const typings = this.typingsInPeer[peerId] ?? (this.typingsInPeer[peerId] = []);
|
||||
let typing = typings.find(t => t.userId === fromId);
|
||||
|
||||
//console.log('loadDialogPhoto location:', location, inputPeer);
|
||||
if(peerId === myId && isDialog) {
|
||||
div.innerText = '';
|
||||
div.dataset.color = '';
|
||||
div.classList.add('tgico-saved');
|
||||
div.classList.remove('tgico-deletedaccount');
|
||||
const cancelAction = () => {
|
||||
delete typing.timeout;
|
||||
//typings.findAndSplice(t => t === typing);
|
||||
const idx = typings.indexOf(typing);
|
||||
if(idx !== -1) {
|
||||
typings.splice(idx, 1);
|
||||
}
|
||||
|
||||
rootScope.dispatchEvent('peer_typings', {peerId, typings});
|
||||
|
||||
if(!typings.length) {
|
||||
delete this.typingsInPeer[peerId];
|
||||
}
|
||||
};
|
||||
|
||||
if(typing && typing.timeout !== undefined) {
|
||||
clearTimeout(typing.timeout);
|
||||
}
|
||||
|
||||
if(update.action._ === 'sendMessageCancelAction') {
|
||||
if(!typing) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancelAction();
|
||||
return;
|
||||
}
|
||||
|
||||
if(peerId > 0) {
|
||||
const user = appUsersManager.getUser(peerId);
|
||||
if(user && user.pFlags && user.pFlags.deleted) {
|
||||
div.innerText = '';
|
||||
div.dataset.color = appPeersManager.getPeerColorById(peerId);
|
||||
div.classList.add('tgico-deletedaccount');
|
||||
div.classList.remove('tgico-saved');
|
||||
return;
|
||||
}
|
||||
if(!typing) {
|
||||
typing = {
|
||||
userId: fromId
|
||||
};
|
||||
|
||||
typings.push(typing);
|
||||
}
|
||||
|
||||
if(!avatarAvailable || !avatarRendered || !this.savedAvatarURLs[peerId]) {
|
||||
let color = '';
|
||||
if(peerId && (peerId !== myId || !isDialog)) {
|
||||
color = appPeersManager.getPeerColorById(peerId);
|
||||
//console.log('updateChatUserTyping', update, typings);
|
||||
|
||||
typing.action = update.action;
|
||||
|
||||
const hasUser = appUsersManager.hasUser(fromId);
|
||||
if(!hasUser) {
|
||||
// let's load user here
|
||||
if(update._ === 'updateChatUserTyping') {
|
||||
if(update.chat_id && appChatsManager.hasChat(update.chat_id) && !appChatsManager.isChannel(update.chat_id)) {
|
||||
appProfileManager.getChatFull(update.chat_id).then(() => {
|
||||
if(typing.timeout !== undefined && appUsersManager.hasUser(fromId)) {
|
||||
rootScope.dispatchEvent('peer_typings', {peerId, typings});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
div.innerText = '';
|
||||
div.classList.remove('tgico-saved', 'tgico-deletedaccount');
|
||||
div.dataset.color = color;
|
||||
|
||||
let abbr: string;
|
||||
if(!title) {
|
||||
const peer = appPeersManager.getPeer(peerId);
|
||||
abbr = peer.initials ?? '';
|
||||
} else {
|
||||
abbr = RichTextProcessor.getAbbreviation(title);
|
||||
}
|
||||
|
||||
div.innerHTML = abbr;
|
||||
//return Promise.resolve(true);
|
||||
//return;
|
||||
} else {
|
||||
appUsersManager.forceUserOnline(fromId);
|
||||
}
|
||||
|
||||
if(avatarAvailable/* && false */) {
|
||||
return this.putAvatar(div, peerId, photo, size, undefined, onlyThumb);
|
||||
typing.timeout = window.setTimeout(cancelAction, 6000);
|
||||
if(hasUser) {
|
||||
rootScope.dispatchEvent('peer_typings', {peerId, typings});
|
||||
}
|
||||
};
|
||||
|
||||
public getPeerTypings(peerId: number) {
|
||||
return this.typingsInPeer[peerId];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import type { AppMessagesManager, Dialog, MessagesStorage } from "./appManagers/
|
||||
import type { Poll, PollResults } from "./appManagers/appPollsManager";
|
||||
import type { MyDialogFilter } from "./storages/filters";
|
||||
import type { ConnectionStatusChange } from "../types";
|
||||
import type { UserTyping } from "./appManagers/appChatsManager";
|
||||
import type { UserTyping } from "./appManagers/appProfileManager";
|
||||
import type Chat from "../components/chat/chat";
|
||||
import type { UserAuth } from "./mtproto/mtproto_config";
|
||||
import type { State, Theme } from "./appManagers/appStateManager";
|
||||
@ -86,6 +86,7 @@ export type BroadcastEvents = {
|
||||
'chat_full_update': number,
|
||||
'poll_update': {poll: Poll, results: PollResults},
|
||||
'chat_update': number,
|
||||
'invalidate_participants': number,
|
||||
//'channel_settings': {channelId: number},
|
||||
'webpage_updated': {id: string, msgs: number[]},
|
||||
|
||||
|
@ -9,10 +9,7 @@ import { AuthSentCode, AuthSentCodeType, AuthSignIn } from '../layer';
|
||||
import appStateManager from '../lib/appManagers/appStateManager';
|
||||
import apiManager from '../lib/mtproto/mtprotoworker';
|
||||
import Page from './page';
|
||||
import pageIm from './pageIm';
|
||||
import pagePassword from './pagePassword';
|
||||
import pageSignIn from './pageSignIn';
|
||||
import pageSignUp from './pageSignUp';
|
||||
import TrackingMonkey from '../components/monkeys/tracking';
|
||||
import CodeInputField from '../components/codeInputField';
|
||||
import { i18n, LangPackKey } from '../lib/langPack';
|
||||
@ -72,15 +69,19 @@ let onFirstMount = (): Promise<any> => {
|
||||
case 'auth.authorization':
|
||||
apiManager.setUserAuth(response.user.id);
|
||||
|
||||
pageIm.mount();
|
||||
import('./pageIm').then(m => {
|
||||
m.default.mount();
|
||||
});
|
||||
cleanup();
|
||||
break;
|
||||
case 'auth.authorizationSignUpRequired':
|
||||
//console.log('Registration needed!');
|
||||
|
||||
pageSignUp.mount({
|
||||
'phone_number': authCode.phone_number,
|
||||
'phone_code_hash': authCode.phone_code_hash
|
||||
import('./pageSignUp').then(m => {
|
||||
m.default.mount({
|
||||
'phone_number': authCode.phone_number,
|
||||
'phone_code_hash': authCode.phone_code_hash
|
||||
});
|
||||
});
|
||||
|
||||
cleanup();
|
||||
@ -96,7 +97,7 @@ let onFirstMount = (): Promise<any> => {
|
||||
//console.warn('pageAuthCode: SESSION_PASSWORD_NEEDED');
|
||||
good = true;
|
||||
err.handled = true;
|
||||
await pagePassword.mount();
|
||||
await (await import('./pagePassword')).default.mount(); // lol
|
||||
setTimeout(() => {
|
||||
codeInput.value = '';
|
||||
}, 300);
|
||||
|
@ -10,7 +10,6 @@ import { AccountPassword } from '../layer';
|
||||
import appStateManager from '../lib/appManagers/appStateManager';
|
||||
import passwordManager from '../lib/mtproto/passwordManager';
|
||||
import Page from './page';
|
||||
import pageIm from './pageIm';
|
||||
import Button from '../components/button';
|
||||
import PasswordInputField from '../components/passwordInputField';
|
||||
import PasswordMonkey from '../components/monkeys/password';
|
||||
@ -91,7 +90,9 @@ let onFirstMount = (): Promise<any> => {
|
||||
switch(response._) {
|
||||
case 'auth.authorization':
|
||||
clearInterval(getStateInterval);
|
||||
pageIm.mount();
|
||||
import('./pageIm').then(m => {
|
||||
m.default.mount();
|
||||
});
|
||||
if(monkey) monkey.remove();
|
||||
break;
|
||||
default:
|
||||
|
@ -18,7 +18,6 @@ import apiManager from '../lib/mtproto/mtprotoworker';
|
||||
import RichTextProcessor from '../lib/richtextprocessor';
|
||||
import LoginPage from './loginPage';
|
||||
import Page from './page';
|
||||
import pageIm from './pageIm';
|
||||
import blurActiveElement from '../helpers/dom/blurActiveElement';
|
||||
import replaceContent from '../helpers/dom/replaceContent';
|
||||
|
||||
@ -135,7 +134,9 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i
|
||||
apiManager.setUserAuth(response.user.id);
|
||||
|
||||
sendAvatar().finally(() => {
|
||||
pageIm.mount();
|
||||
import('./pageIm').then(m => {
|
||||
m.default.mount();
|
||||
});
|
||||
});
|
||||
|
||||
break;
|
||||
|
Loading…
x
Reference in New Issue
Block a user