Browse Source

Fix import order

master
morethanwords 4 years ago
parent
commit
6b3b2c343d
  1. 4
      src/components/appSearchSuper..ts
  2. 2
      src/components/appSelectPeers.ts
  3. 5
      src/components/avatar.ts
  4. 3
      src/components/sidebarRight/tabs/sharedMedia.ts
  5. 202
      src/lib/appManagers/appAvatarsManager.ts
  6. 154
      src/lib/appManagers/appChatsManager.ts
  7. 5
      src/lib/appManagers/appImManager.ts
  8. 5
      src/lib/appManagers/appMessagesManager.ts
  9. 2
      src/lib/appManagers/appNotificationsManager.ts
  10. 258
      src/lib/appManagers/appProfileManager.ts
  11. 3
      src/lib/rootScope.ts
  12. 13
      src/pages/pageAuthCode.ts
  13. 5
      src/pages/pagePassword.ts
  14. 5
      src/pages/pageSignUp.ts

4
src/components/appSearchSuper..ts

@ -718,7 +718,7 @@ export default class AppSearchSuper {
if(showMembersCount && (peer.participants_count || peer.participants)) { if(showMembersCount && (peer.participants_count || peer.participants)) {
const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(cleanSearchText(query))})`, 'gi'); const regExp = new RegExp(`(${escapeRegExp(query)}|${escapeRegExp(cleanSearchText(query))})`, 'gi');
dom.titleSpan.innerHTML = dom.titleSpan.innerHTML.replace(regExp, '<i>$1</i>'); 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) { } else if(peerId === rootScope.myId) {
dom.lastMessageSpan.append(i18n('Presence.YourChat')); dom.lastMessageSpan.append(i18n('Presence.YourChat'));
} else { } else {
@ -811,7 +811,7 @@ export default class AppSearchSuper {
autonomous: true 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) { if(!state.recentSearch.length) {

2
src/components/appSelectPeers.ts

@ -447,7 +447,7 @@ export default class AppSelectPeers {
let subtitleEl: HTMLElement; let subtitleEl: HTMLElement;
if(peerId < 0) { if(peerId < 0) {
subtitleEl = appChatsManager.getChatMembersString(-peerId); subtitleEl = appProfileManager.getChatMembersString(-peerId);
} else if(peerId === rootScope.myId) { } else if(peerId === rootScope.myId) {
subtitleEl = i18n('Presence.YourChat'); subtitleEl = i18n('Presence.YourChat');
} else { } else {

5
src/components/avatar.ts

@ -14,9 +14,10 @@ import appPhotosManager from "../lib/appManagers/appPhotosManager";
import type { LazyLoadQueueIntersector } from "./lazyLoadQueue"; import type { LazyLoadQueueIntersector } from "./lazyLoadQueue";
import { attachClickEvent } from "../helpers/dom/clickEvent"; import { attachClickEvent } from "../helpers/dom/clickEvent";
import { cancelEvent } from "../helpers/dom/cancelEvent"; import { cancelEvent } from "../helpers/dom/cancelEvent";
import appAvatarsManager from "../lib/appManagers/appAvatarsManager";
const onAvatarUpdate = (peerId: number) => { const onAvatarUpdate = (peerId: number) => {
appProfileManager.removeFromAvatarsCache(peerId); appAvatarsManager.removeFromAvatarsCache(peerId);
(Array.from(document.querySelectorAll('avatar-element[peer="' + peerId + '"]')) as AvatarElement[]).forEach(elem => { (Array.from(document.querySelectorAll('avatar-element[peer="' + peerId + '"]')) as AvatarElement[]).forEach(elem => {
//console.log('updating avatar:', elem); //console.log('updating avatar:', elem);
elem.update(); elem.update();
@ -180,7 +181,7 @@ export default class AvatarElement extends HTMLElement {
} }
private r(onlyThumb = false) { 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(); const promise = res ? res.loadPromise : Promise.resolve();
if(this.loadPromises) { if(this.loadPromises) {
if(res && res.cached) { if(res && res.cached) {

3
src/components/sidebarRight/tabs/sharedMedia.ts

@ -48,6 +48,7 @@ import ButtonCorner from "../../buttonCorner";
import { cancelEvent } from "../../../helpers/dom/cancelEvent"; import { cancelEvent } from "../../../helpers/dom/cancelEvent";
import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import replaceContent from "../../../helpers/dom/replaceContent"; import replaceContent from "../../../helpers/dom/replaceContent";
import appAvatarsManager from "../../../lib/appManagers/appAvatarsManager";
let setText = (text: string, row: Row) => { let setText = (text: string, row: Row) => {
//fastRaf(() => { //fastRaf(() => {
@ -437,7 +438,7 @@ class PeerProfileAvatars {
}); });
} else { } else {
const photo = appPeersManager.getPeerPhoto(this.peerId); 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); this.avatars.append(avatar);

202
src/lib/appManagers/appAvatarsManager.ts

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

154
src/lib/appManagers/appChatsManager.ts

@ -10,17 +10,14 @@
*/ */
import { MOUNT_CLASS_TO } from "../../config/debug"; import { MOUNT_CLASS_TO } from "../../config/debug";
import { numberThousandSplitter } from "../../helpers/number";
import { isObject, safeReplaceObject, copy, deepEqual } from "../../helpers/object"; 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 { ChannelParticipant, Chat, ChatAdminRights, ChatBannedRights, ChatParticipant, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, Update, Updates } from "../../layer";
import { i18n, LangPackKey } from "../langPack";
import apiManagerProxy from "../mtproto/mtprotoworker"; import apiManagerProxy from "../mtproto/mtprotoworker";
import apiManager from '../mtproto/mtprotoworker'; import apiManager from '../mtproto/mtprotoworker';
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
import rootScope from "../rootScope"; import rootScope from "../rootScope";
import apiUpdatesManager from "./apiUpdatesManager"; import apiUpdatesManager from "./apiUpdatesManager";
import appPeersManager from "./appPeersManager"; import appPeersManager from "./appPeersManager";
import appProfileManager from "./appProfileManager";
import appStateManager from "./appStateManager"; import appStateManager from "./appStateManager";
import appUsersManager from "./appUsersManager"; 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 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 { export class AppChatsManager {
private storage = appStateManager.storages.chats; private storage = appStateManager.storages.chats;
@ -38,10 +33,6 @@ export class AppChatsManager {
//private channelAccess: any; //private channelAccess: any;
//private megagroups: {[id: number]: true}; //private megagroups: {[id: number]: true};
private megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}};
private typingsInPeer: {[peerId: number]: UserTyping[]};
constructor() { constructor() {
this.clear(); this.clear();
@ -65,11 +56,7 @@ export class AppChatsManager {
chat.default_banned_rights = update.default_banned_rights; chat.default_banned_rights = update.default_banned_rights;
rootScope.dispatchEvent('chat_update', chatId); rootScope.dispatchEvent('chat_update', chatId);
} }
}, }
updateUserTyping: this.onUpdateUserTyping,
updateChatUserTyping: this.onUpdateUserTyping,
updateChannelUserTyping: this.onUpdateUserTyping
}); });
appStateManager.getState().then((state) => { appStateManager.getState().then((state) => {
@ -119,81 +106,6 @@ export class AppChatsManager {
} else { } else {
this.chats = {}; 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) { public saveApiChats(apiChats: any[], override?: boolean) {
@ -486,27 +398,6 @@ export class AppChatsManager {
return 'g' + id; 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) { /* public wrapForFull(id: number, fullChat: any) {
const chatFull = copy(fullChat); const chatFull = copy(fullChat);
const chat = this.getChat(id); 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) => { private onChatUpdated = (chatId: number, updates: any) => {
//console.log('onChatUpdated', chatId, updates); //console.log('onChatUpdated', chatId, updates);
@ -647,7 +499,7 @@ export class AppChatsManager {
/* updates.updates && /* updates.updates &&
updates.updates.length && */ updates.updates.length && */
this.isChannel(chatId)) { this.isChannel(chatId)) {
appProfileManager.invalidateChannelParticipants(chatId); rootScope.dispatchEvent('invalidate_participants', chatId);
} }
}; };

5
src/lib/appManagers/appImManager.ts

@ -103,6 +103,7 @@ export class AppImManager {
constructor() { constructor() {
apiUpdatesManager.attach(); apiUpdatesManager.attach();
appNotificationsManager.start();
this.log = logger('IM', LogTypes.Log | LogTypes.Warn | LogTypes.Debug | LogTypes.Error); 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) { public getPeerTyping(peerId: number, container?: HTMLElement) {
if(!appUsersManager.isBot(peerId)) { if(!appUsersManager.isBot(peerId)) {
const typings = appChatsManager.getPeerTypings(peerId); const typings = appProfileManager.getPeerTypings(peerId);
if(!typings || !typings.length) { if(!typings || !typings.length) {
return; return;
} }
@ -1152,7 +1153,7 @@ export class AppImManager {
const participants_count = chatInfo.participants_count || (chatInfo.participants && chatInfo.participants.participants && chatInfo.participants.participants.length) || 1; const participants_count = chatInfo.participants_count || (chatInfo.participants && chatInfo.participants.participants && chatInfo.participants.participants.length) || 1;
//if(participants_count) { //if(participants_count) {
subtitle = appChatsManager.getChatMembersString(-peerId); subtitle = appProfileManager.getChatMembersString(-peerId);
if(participants_count < 2) return subtitle; if(participants_count < 2) return subtitle;
/* const onlines = await appChatsManager.getOnlines(chat.id); /* const onlines = await appChatsManager.getOnlines(chat.id);

5
src/lib/appManagers/appMessagesManager.ts

@ -53,6 +53,7 @@ import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment";
import htmlToSpan from "../../helpers/dom/htmlToSpan"; import htmlToSpan from "../../helpers/dom/htmlToSpan";
import { REPLIES_PEER_ID } from "../mtproto/mtproto_config"; import { REPLIES_PEER_ID } from "../mtproto/mtproto_config";
import formatCallDuration from "../../helpers/formatCallDuration"; import formatCallDuration from "../../helpers/formatCallDuration";
import appAvatarsManager from "./appAvatarsManager";
//console.trace('include'); //console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет // TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
@ -304,8 +305,6 @@ export class AppMessagesManager {
this.maxSeenId = state.maxSeenMsgId; this.maxSeenId = state.maxSeenMsgId;
} }
}); });
appNotificationsManager.start();
} }
public construct() { public construct() {
@ -4546,7 +4545,7 @@ export class AppMessagesManager {
const peerPhoto = appPeersManager.getPeerPhoto(peerId); const peerPhoto = appPeersManager.getPeerPhoto(peerId);
if(peerPhoto) { if(peerPhoto) {
appProfileManager.loadAvatar(peerId, peerPhoto, 'photo_small').loadPromise.then(url => { appAvatarsManager.loadAvatar(peerId, peerPhoto, 'photo_small').loadPromise.then(url => {
if(message.pFlags.unread) { if(message.pFlags.unread) {
notification.image = url; notification.image = url;
appNotificationsManager.notify(notification); appNotificationsManager.notify(notification);

2
src/lib/appManagers/appNotificationsManager.ts

@ -706,7 +706,6 @@ export class AppNotificationsManager {
} }
private registerDevice(tokenData: PushSubscriptionNotify) { private registerDevice(tokenData: PushSubscriptionNotify) {
return;
if(this.registeredDevice && deepEqual(this.registeredDevice, tokenData)) { if(this.registeredDevice && deepEqual(this.registeredDevice, tokenData)) {
return false; return false;
} }
@ -725,7 +724,6 @@ export class AppNotificationsManager {
} }
private unregisterDevice(tokenData: PushSubscriptionNotify) { private unregisterDevice(tokenData: PushSubscriptionNotify) {
return;
if(!this.registeredDevice) { if(!this.registeredDevice) {
return false; return false;
} }

258
src/lib/appManagers/appProfileManager.ts

@ -11,10 +11,9 @@
import { MOUNT_CLASS_TO } from "../../config/debug"; import { MOUNT_CLASS_TO } from "../../config/debug";
import { tsNow } from "../../helpers/date"; import { tsNow } from "../../helpers/date";
import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; import { numberThousandSplitter } from "../../helpers/number";
import replaceContent from "../../helpers/dom/replaceContent"; import { ChannelParticipantsFilter, ChannelsChannelParticipants, Chat, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, SendMessageAction, Update, UserFull, UserProfilePhoto } from "../../layer";
import sequentialDom from "../../helpers/sequentialDom"; import { LangPackKey, i18n } from "../langPack";
import { ChannelParticipantsFilter, ChannelsChannelParticipants, Chat, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, Update, UserFull, UserProfilePhoto } from "../../layer";
//import apiManager from '../mtproto/apiManager'; //import apiManager from '../mtproto/apiManager';
import apiManager from '../mtproto/mtprotoworker'; import apiManager from '../mtproto/mtprotoworker';
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
@ -22,13 +21,12 @@ import rootScope from "../rootScope";
import SearchIndex from "../searchIndex"; import SearchIndex from "../searchIndex";
import apiUpdatesManager from "./apiUpdatesManager"; import apiUpdatesManager from "./apiUpdatesManager";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import appDownloadManager from "./appDownloadManager";
import appNotificationsManager from "./appNotificationsManager"; import appNotificationsManager from "./appNotificationsManager";
import appPeersManager from "./appPeersManager"; import appPeersManager from "./appPeersManager";
import appPhotosManager, { MyPhoto } from "./appPhotosManager"; import appPhotosManager from "./appPhotosManager";
import appUsersManager, { User } from "./appUsersManager"; import appUsersManager, { User } from "./appUsersManager";
type PeerPhotoSize = 'photo_small' | 'photo_big'; export type UserTyping = Partial<{userId: number, action: SendMessageAction, timeout: number}>;
export class AppProfileManager { export class AppProfileManager {
//private botInfos: any = {}; //private botInfos: any = {};
@ -36,11 +34,9 @@ export class AppProfileManager {
public chatsFull: {[id: string]: ChatFull} = {}; public chatsFull: {[id: string]: ChatFull} = {};
private fullPromises: {[peerId: string]: Promise<ChatFull.chatFull | ChatFull.channelFull | UserFull>} = {}; private fullPromises: {[peerId: string]: Promise<ChatFull.chatFull | ChatFull.channelFull | UserFull>} = {};
private savedAvatarURLs: { private megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}};
[peerId: number]: {
[size in PeerPhotoSize]?: string | Promise<string> private typingsInPeer: {[peerId: number]: UserTyping[]};
}
} = {};
constructor() { constructor() {
rootScope.addMultipleEventsListeners({ rootScope.addMultipleEventsListeners({
@ -93,7 +89,11 @@ export class AppProfileManager {
} }
} }
} }
} },
updateUserTyping: this.onUpdateUserTyping,
updateChatUserTyping: this.onUpdateUserTyping,
updateChannelUserTyping: this.onUpdateUserTyping
}); });
rootScope.addEventListener('chat_update', (chatId) => { rootScope.addEventListener('chat_update', (chatId) => {
@ -121,6 +121,13 @@ export class AppProfileManager {
rootScope.dispatchEvent('chat_full_update', chatId); rootScope.dispatchEvent('chat_full_update', chatId);
} }
}); });
rootScope.addEventListener('invalidate_participants', chatId => {
this.invalidateChannelParticipants(chatId);
});
this.megagroupOnlines = {};
this.typingsInPeer = {};
} }
/* public saveBotInfo(botInfo: any) { /* public saveBotInfo(botInfo: any) {
@ -457,177 +464,144 @@ export class AppProfileManager {
}); });
} }
public removeFromAvatarsCache(peerId: number) { public getChatMembersString(id: number) {
if(this.savedAvatarURLs[peerId]) { const chat = appChatsManager.getChat(id);
delete this.savedAvatarURLs[peerId]; 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;
} }
public loadAvatar(peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize) { const isChannel = appChatsManager.isBroadcast(id);
const inputPeer = appPeersManager.getInputPeerById(peerId); count = count || 1;
let cached = false; let key: LangPackKey = isChannel ? 'Peer.Status.Subscribers' : 'Peer.Status.Member';
let getAvatarPromise: Promise<string>; return i18n(key, [numberThousandSplitter(count)]);
let saved = this.savedAvatarURLs[peerId];
if(!saved || !saved[size]) {
if(!saved) {
saved = this.savedAvatarURLs[peerId] = {};
} }
//console.warn('will invoke downloadSmallFile:', peerId); public async getOnlines(id: number): Promise<number> {
const peerPhotoFileLocation: InputFileLocation.inputPeerPhotoFileLocation = { if(appChatsManager.isMegagroup(id)) {
_: 'inputPeerPhotoFileLocation', const timestamp = Date.now() / 1000 | 0;
pFlags: {}, const cached = this.megagroupOnlines[id] ?? (this.megagroupOnlines[id] = {timestamp: 0, onlines: 1});
peer: inputPeer, if((timestamp - cached.timestamp) < 60) {
photo_id: photo.photo_id return cached.onlines;
};
if(size === 'photo_big') {
peerPhotoFileLocation.pFlags.big = true;
} }
const downloadOptions = {dcId: photo.dc_id, location: peerPhotoFileLocation}; const res = await apiManager.invokeApi('messages.getOnlines', {
peer: appChatsManager.getChannelInputPeer(id)
/* let str: string; });
const time = Date.now();
if(peerId === 0) {
str = `download avatar ${peerId}`;
} */
const promise = appDownloadManager.download(downloadOptions); const onlines = res.onlines ?? 1;
getAvatarPromise = saved[size] = promise.then(blob => { cached.timestamp = timestamp;
return saved[size] = URL.createObjectURL(blob); cached.onlines = onlines;
/* if(str) { return onlines;
console.log(str, Date.now() / 1000, Date.now() - time); } else if(appChatsManager.isBroadcast(id)) {
} */ return 1;
});
} else if(typeof(saved[size]) !== 'string') {
getAvatarPromise = saved[size] as Promise<any>;
} else {
getAvatarPromise = Promise.resolve(saved[size]);
cached = true;
} }
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) { return participants.reduce((acc: number, participant) => {
let {cached, loadPromise} = this.loadAvatar(peerId, photo, size); const user = appUsersManager.getUser(participant.user_id);
if(user && user.status && user.status._ === 'userStatusOnline') {
return acc + 1;
}
let renderThumbPromise: Promise<void>; return acc;
let callback: () => void; }, 0);
if(cached) {
// смотри в misc.ts: renderImageFromUrl
callback = () => {
replaceContent(div, img);
div.dataset.color = '';
};
} else { } else {
const animate = rootScope.settings.animationsEnabled; return 1;
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 = () => { private onUpdateUserTyping = (update: Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChannelUserTyping) => {
if(photo.stripped_thumb) { const fromId = (update as Update.updateUserTyping).user_id || appPeersManager.getPeerId((update as Update.updateChatUserTyping).from_id);
div.append(img); if(rootScope.myId === fromId || update.action._ === 'speakingInGroupCallAction') {
} else { return;
replaceContent(div, img);
} }
setTimeout(() => { const peerId = update._ === 'updateUserTyping' ?
if(div.childElementCount) { fromId :
sequentialDom.mutateElement(img, () => { -((update as Update.updateChatUserTyping).chat_id || (update as Update.updateChannelUserTyping).channel_id);
div.dataset.color = ''; const typings = this.typingsInPeer[peerId] ?? (this.typingsInPeer[peerId] = []);
let typing = typings.find(t => t.userId === fromId);
if(animate) { const cancelAction = () => {
img.classList.remove('fade-in'); delete typing.timeout;
//typings.findAndSplice(t => t === typing);
const idx = typings.indexOf(typing);
if(idx !== -1) {
typings.splice(idx, 1);
} }
if(thumbImage) { rootScope.dispatchEvent('peer_typings', {peerId, typings});
thumbImage.remove();
} if(!typings.length) {
}); delete this.typingsInPeer[peerId];
} }
}, animate ? 200 : 0);
}; };
if(typing && typing.timeout !== undefined) {
clearTimeout(typing.timeout);
} }
const renderPromise = loadPromise if(update.action._ === 'sendMessageCancelAction') {
.then((url) => renderImageFromUrl(img, url/* , false */)) if(!typing) {
.then(() => callback()); return;
}
return {cached, loadPromise: renderThumbPromise || renderPromise}; cancelAction();
return;
} }
// peerId === peerId || title if(!typing) {
public putPhoto(div: HTMLElement, peerId: number, isDialog = false, title = '', onlyThumb = false) { typing = {
const photo = appPeersManager.getPeerPhoto(peerId); userId: fromId
};
const size: PeerPhotoSize = 'photo_small'; typings.push(typing);
const avatarAvailable = !!photo; }
const avatarRendered = div.firstElementChild && !(div.firstElementChild as HTMLElement).classList.contains('emoji');
const myId = rootScope.myId; //console.log('updateChatUserTyping', update, typings);
//console.log('loadDialogPhoto location:', location, inputPeer); typing.action = update.action;
if(peerId === myId && isDialog) {
div.innerText = '';
div.dataset.color = '';
div.classList.add('tgico-saved');
div.classList.remove('tgico-deletedaccount');
return;
}
if(peerId > 0) { const hasUser = appUsersManager.hasUser(fromId);
const user = appUsersManager.getUser(peerId); if(!hasUser) {
if(user && user.pFlags && user.pFlags.deleted) { // let's load user here
div.innerText = ''; if(update._ === 'updateChatUserTyping') {
div.dataset.color = appPeersManager.getPeerColorById(peerId); if(update.chat_id && appChatsManager.hasChat(update.chat_id) && !appChatsManager.isChannel(update.chat_id)) {
div.classList.add('tgico-deletedaccount'); appProfileManager.getChatFull(update.chat_id).then(() => {
div.classList.remove('tgico-saved'); if(typing.timeout !== undefined && appUsersManager.hasUser(fromId)) {
return; rootScope.dispatchEvent('peer_typings', {peerId, typings});
} }
});
} }
if(!avatarAvailable || !avatarRendered || !this.savedAvatarURLs[peerId]) {
let color = '';
if(peerId && (peerId !== myId || !isDialog)) {
color = appPeersManager.getPeerColorById(peerId);
} }
div.innerText = ''; //return;
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 { } else {
abbr = RichTextProcessor.getAbbreviation(title); appUsersManager.forceUserOnline(fromId);
} }
div.innerHTML = abbr; typing.timeout = window.setTimeout(cancelAction, 6000);
//return Promise.resolve(true); if(hasUser) {
rootScope.dispatchEvent('peer_typings', {peerId, typings});
} }
};
if(avatarAvailable/* && false */) { public getPeerTypings(peerId: number) {
return this.putAvatar(div, peerId, photo, size, undefined, onlyThumb); return this.typingsInPeer[peerId];
}
} }
} }

3
src/lib/rootScope.ts

@ -10,7 +10,7 @@ import type { AppMessagesManager, Dialog, MessagesStorage } from "./appManagers/
import type { Poll, PollResults } from "./appManagers/appPollsManager"; import type { Poll, PollResults } from "./appManagers/appPollsManager";
import type { MyDialogFilter } from "./storages/filters"; import type { MyDialogFilter } from "./storages/filters";
import type { ConnectionStatusChange } from "../types"; 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 Chat from "../components/chat/chat";
import type { UserAuth } from "./mtproto/mtproto_config"; import type { UserAuth } from "./mtproto/mtproto_config";
import type { State, Theme } from "./appManagers/appStateManager"; import type { State, Theme } from "./appManagers/appStateManager";
@ -86,6 +86,7 @@ export type BroadcastEvents = {
'chat_full_update': number, 'chat_full_update': number,
'poll_update': {poll: Poll, results: PollResults}, 'poll_update': {poll: Poll, results: PollResults},
'chat_update': number, 'chat_update': number,
'invalidate_participants': number,
//'channel_settings': {channelId: number}, //'channel_settings': {channelId: number},
'webpage_updated': {id: string, msgs: number[]}, 'webpage_updated': {id: string, msgs: number[]},

13
src/pages/pageAuthCode.ts

@ -9,10 +9,7 @@ import { AuthSentCode, AuthSentCodeType, AuthSignIn } from '../layer';
import appStateManager from '../lib/appManagers/appStateManager'; import appStateManager from '../lib/appManagers/appStateManager';
import apiManager from '../lib/mtproto/mtprotoworker'; import apiManager from '../lib/mtproto/mtprotoworker';
import Page from './page'; import Page from './page';
import pageIm from './pageIm';
import pagePassword from './pagePassword';
import pageSignIn from './pageSignIn'; import pageSignIn from './pageSignIn';
import pageSignUp from './pageSignUp';
import TrackingMonkey from '../components/monkeys/tracking'; import TrackingMonkey from '../components/monkeys/tracking';
import CodeInputField from '../components/codeInputField'; import CodeInputField from '../components/codeInputField';
import { i18n, LangPackKey } from '../lib/langPack'; import { i18n, LangPackKey } from '../lib/langPack';
@ -72,16 +69,20 @@ let onFirstMount = (): Promise<any> => {
case 'auth.authorization': case 'auth.authorization':
apiManager.setUserAuth(response.user.id); apiManager.setUserAuth(response.user.id);
pageIm.mount(); import('./pageIm').then(m => {
m.default.mount();
});
cleanup(); cleanup();
break; break;
case 'auth.authorizationSignUpRequired': case 'auth.authorizationSignUpRequired':
//console.log('Registration needed!'); //console.log('Registration needed!');
pageSignUp.mount({ import('./pageSignUp').then(m => {
m.default.mount({
'phone_number': authCode.phone_number, 'phone_number': authCode.phone_number,
'phone_code_hash': authCode.phone_code_hash 'phone_code_hash': authCode.phone_code_hash
}); });
});
cleanup(); cleanup();
break; break;
@ -96,7 +97,7 @@ let onFirstMount = (): Promise<any> => {
//console.warn('pageAuthCode: SESSION_PASSWORD_NEEDED'); //console.warn('pageAuthCode: SESSION_PASSWORD_NEEDED');
good = true; good = true;
err.handled = true; err.handled = true;
await pagePassword.mount(); await (await import('./pagePassword')).default.mount(); // lol
setTimeout(() => { setTimeout(() => {
codeInput.value = ''; codeInput.value = '';
}, 300); }, 300);

5
src/pages/pagePassword.ts

@ -10,7 +10,6 @@ import { AccountPassword } from '../layer';
import appStateManager from '../lib/appManagers/appStateManager'; import appStateManager from '../lib/appManagers/appStateManager';
import passwordManager from '../lib/mtproto/passwordManager'; import passwordManager from '../lib/mtproto/passwordManager';
import Page from './page'; import Page from './page';
import pageIm from './pageIm';
import Button from '../components/button'; import Button from '../components/button';
import PasswordInputField from '../components/passwordInputField'; import PasswordInputField from '../components/passwordInputField';
import PasswordMonkey from '../components/monkeys/password'; import PasswordMonkey from '../components/monkeys/password';
@ -91,7 +90,9 @@ let onFirstMount = (): Promise<any> => {
switch(response._) { switch(response._) {
case 'auth.authorization': case 'auth.authorization':
clearInterval(getStateInterval); clearInterval(getStateInterval);
pageIm.mount(); import('./pageIm').then(m => {
m.default.mount();
});
if(monkey) monkey.remove(); if(monkey) monkey.remove();
break; break;
default: default:

5
src/pages/pageSignUp.ts

@ -18,7 +18,6 @@ import apiManager from '../lib/mtproto/mtprotoworker';
import RichTextProcessor from '../lib/richtextprocessor'; import RichTextProcessor from '../lib/richtextprocessor';
import LoginPage from './loginPage'; import LoginPage from './loginPage';
import Page from './page'; import Page from './page';
import pageIm from './pageIm';
import blurActiveElement from '../helpers/dom/blurActiveElement'; import blurActiveElement from '../helpers/dom/blurActiveElement';
import replaceContent from '../helpers/dom/replaceContent'; import replaceContent from '../helpers/dom/replaceContent';
@ -135,7 +134,9 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i
apiManager.setUserAuth(response.user.id); apiManager.setUserAuth(response.user.id);
sendAvatar().finally(() => { sendAvatar().finally(() => {
pageIm.mount(); import('./pageIm').then(m => {
m.default.mount();
});
}); });
break; break;

Loading…
Cancel
Save