tweb/src/lib/appManagers/appChatsManager.ts
morethanwords d1e3827c12 fix mediaviewer
mediaviewer forward
chat contextmenu with rights
support manually attached stickers (not upload)
right sidebar fix blink & preloader
minor fixes

Signed-off-by: morethanwords <thanwords24@gmail.com>
2020-05-27 09:21:16 +03:00

460 lines
12 KiB
TypeScript

import { $rootScope, isObject, SearchIndexManager, safeReplaceObject, copy, numberWithCommas } from "../utils";
import { RichTextProcessor } from "../richtextprocessor";
import appUsersManager from "./appUsersManager";
import apiManager from '../mtproto/mtprotoworker';
import apiUpdatesManager from "./apiUpdatesManager";
import appProfileManager from "./appProfileManager";
type Channel = {
_: 'channel',
flags: number,
pFlags: Partial<{
creator: true,
left: true,
broadcast: true,
verified: true,
megagroup: true,
restricted: true,
signatures: true,
min: true,
scam: true,
has_link: true,
has_geo: true,
slowmode_enabled: true
}>,
id: number,
access_hash?: string,
title: string,
username?: string,
photo: any,
date: number,
version: number,
restriction_reason?: any,
admin_rights?: any,
banned_rights?: any,
default_banned_rights?: any,
participants_count: number
};
export class AppChatsManager {
public chats: {[id: number]: Channel | any} = {};
public usernames: any = {};
public channelAccess: any = {};
public megagroups: any = {};
public cachedPhotoLocations: any = {};
public megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}} = {};
constructor() {
$rootScope.$on('apiUpdate', (e: CustomEvent) => {
// console.log('on apiUpdate', update)
let update = e.detail;
switch(update._) {
case 'updateChannel':
var channelID = update.channel_id;
$rootScope.$broadcast('channel_settings', {channelID: channelID});
break;
}
});
}
public saveApiChats(apiChats: any[]) {
apiChats.forEach(chat => this.saveApiChat(chat));
}
public saveApiChat(apiChat: any) {
if(!isObject(apiChat)) {
return;
}
apiChat.rTitle = apiChat.title || 'chat_title_deleted';
apiChat.rTitle = RichTextProcessor.wrapRichText(apiChat.title, {noLinks: true, noLinebreaks: true}) || 'chat_title_deleted';
let oldChat = this.chats[apiChat.id];
let titleWords = SearchIndexManager.cleanSearchText(apiChat.title || '', false).split(' ');
let firstWord = titleWords.shift();
let lastWord = titleWords.pop();
apiChat.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : '');
if(apiChat.pFlags === undefined) {
apiChat.pFlags = {};
}
if(apiChat.pFlags.min) {
if(oldChat !== undefined) {
return;
}
}
if(apiChat._ == 'channel' &&
apiChat.participants_count === undefined &&
oldChat !== undefined &&
oldChat.participants_count) {
apiChat.participants_count = oldChat.participants_count;
}
if(apiChat.username) {
let searchUsername = SearchIndexManager.cleanUsername(apiChat.username);
this.usernames[searchUsername] = apiChat.id;
}
let changedPhoto = false;
if(oldChat === undefined) {
oldChat = this.chats[apiChat.id] = apiChat;
} else {
let oldPhoto = oldChat.photo && oldChat.photo.photo_small;
let newPhoto = apiChat.photo && apiChat.photo.photo_small;
if(JSON.stringify(oldPhoto) !== JSON.stringify(newPhoto)) {
changedPhoto = true;
}
safeReplaceObject(oldChat, apiChat);
$rootScope.$broadcast('chat_update', apiChat.id);
}
if(this.cachedPhotoLocations[apiChat.id] !== undefined) {
safeReplaceObject(this.cachedPhotoLocations[apiChat.id], apiChat &&
apiChat.photo ? apiChat.photo : {empty: true});
}
if(changedPhoto) {
$rootScope.$broadcast('avatar_update', -apiChat.id);
}
}
public getChat(id: number) {
if(id < 0) id = -id;
return this.chats[id] || {id: id, deleted: true, access_hash: this.channelAccess[id]};
}
public hasRights(id: number, action: 'send' | 'edit_title' | 'edit_photo' | 'invite' | 'pin' | 'deleteRevoke') {
const chat = this.getChat(id);
if(!chat) return false;
if(chat._ == 'chatForbidden' ||
chat._ == 'channelForbidden' ||
chat.pFlags.kicked ||
chat.pFlags.left) {
return false;
}
if(chat.pFlags.creator) {
return true;
}
let myFlags = (chat.admin_rights || chat.banned_rights || chat.default_banned_rights)?.pFlags ?? {};
switch(action) {
// good
case 'send': {
if(chat._ == 'channel' &&
!chat.pFlags.megagroup &&
!myFlags.post_messages) {
return false;
}
break;
}
// good
case 'deleteRevoke': {
if(chat._ == 'channel') {
return !!myFlags.delete_messages;
} else if(!chat.pFlags.admin) {
return false;
}
break;
}
// good
case 'pin': {
if(chat._ == 'channel') {
return chat.admin_rights ? !!myFlags.pin_messages || !!myFlags.post_messages : !myFlags.pin_messages;
} else {
if(myFlags.pin_messages && !chat.pFlags.admin) {
return false;
}
}
break;
}
case 'edit_title':
case 'edit_photo':
case 'invite': {
if(chat._ == 'channel') {
if(chat.pFlags.megagroup) {
if(!(action == 'invite' && chat.pFlags.democracy)) {
return false;
}
} else {
return false;
}
} else {
if(chat.pFlags.admins_enabled &&
!chat.pFlags.admin) {
return false;
}
}
break;
}
}
return true;
}
public resolveUsername(username: string) {
return this.usernames[username] || 0;
}
public saveChannelAccess(id: number, accessHash: string) {
this.channelAccess[id] = accessHash;
}
public saveIsMegagroup(id: number) {
this.megagroups[id] = true;
}
public isChannel(id: number) {
let chat = this.chats[id];
if(chat && (chat._ == 'channel' || chat._ == 'channelForbidden') || this.channelAccess[id]) {
return true;
}
return false;
}
public isMegagroup(id: number) {
if(this.megagroups[id]) {
return true;
}
let chat = this.chats[id];
if(chat && chat._ == 'channel' && chat.pFlags.megagroup) {
return true;
}
return false;
}
public isBroadcast(id: number) {
return this.isChannel(id) && !this.isMegagroup(id);
}
public getChannelInput(id: number) {
if(!id) {
return {_: 'inputChannelEmpty'};
}
return {
_: 'inputChannel',
channel_id: id,
access_hash: this.getChat(id).access_hash || this.channelAccess[id] || 0
};
}
public getChatInputPeer(id: number) {
return {
_: 'inputPeerChat',
chat_id: id
};
}
public getChannelInputPeer(id: number) {
if(!id) {
return {_: 'inputPeerEmpty'};
}
return {
_: 'inputPeerChannel',
channel_id: id,
access_hash: this.getChat(id).access_hash || this.channelAccess[id] || 0
};
}
public hasChat(id: number, allowMin?: any) {
let chat = this.chats[id]
return isObject(chat) && (allowMin || !chat.pFlags.min);
}
public getChatPhoto(id: number) {
let chat = this.getChat(id);
if(this.cachedPhotoLocations[id] === undefined) {
this.cachedPhotoLocations[id] = chat && chat.photo ? chat.photo : {empty: true};
}
return this.cachedPhotoLocations[id];
}
public getChatString(id: number) {
let chat = this.getChat(id);
if(this.isChannel(id)) {
return (this.isMegagroup(id) ? 's' : 'c') + id + '_' + chat.access_hash;
}
return 'g' + id;
}
public getChatMembersString(id: number) {
let chat = this.getChat(id);
let isChannel = this.isChannel(id) && !this.isMegagroup(id);
let participants_count = chat.participants_count || chat.participants.participants.length;
return numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members');
}
public wrapForFull(id: number, fullChat: any) {
let chatFull = copy(fullChat);
let chat = this.getChat(id);
if(!chatFull.participants_count) {
chatFull.participants_count = chat.participants_count;
}
if(chatFull.participants &&
chatFull.participants._ == 'chatParticipants') {
chatFull.participants.participants = this.wrapParticipants(id, chatFull.participants.participants);
}
if(chatFull.about) {
chatFull.rAbout = RichTextProcessor.wrapRichText(chatFull.about, {noLinebreaks: true});
}
chatFull.peerString = this.getChatString(id);
chatFull.chat = chat;
return chatFull;
}
public wrapParticipants(id: number, participants: any[]) {
let chat = this.getChat(id);
let myID = appUsersManager.getSelf().id;
if(this.isChannel(id)) {
let isAdmin = chat.pFlags.creator;
participants.forEach((participant) => {
participant.canLeave = myID == participant.user_id;
participant.canKick = isAdmin && participant._ == 'channelParticipant';
// just for order by last seen
participant.user = appUsersManager.getUser(participant.user_id);
});
} else {
let isAdmin = chat.pFlags.creator || chat.pFlags.admins_enabled && chat.pFlags.admin;
participants.forEach((participant) => {
participant.canLeave = myID == participant.user_id;
participant.canKick = !participant.canLeave && (
chat.pFlags.creator ||
participant._ == 'chatParticipant' && (isAdmin || myID == participant.inviter_id)
);
// just for order by last seen
participant.user = appUsersManager.getUser(participant.user_id);
});
}
return participants;
}
public createChannel(title: string, about: String): Promise<number> {
return apiManager.invokeApi('channels.createChannel', {
flags: 1,
broadcast: true,
title: title,
about: about
}).then((updates: any) => {
apiUpdatesManager.processUpdateMessage(updates);
return updates.chats[0].id;
});
}
public inviteToChannel(id: number, userIDs: number[]) {
let input = this.getChannelInput(id);
let usersInputs = userIDs.map(u => appUsersManager.getUserInput(u));
return apiManager.invokeApi('channels.inviteToChannel', {
channel: input,
users: usersInputs
}).then(updates => {
apiUpdatesManager.processUpdateMessage(updates);
});
}
public createChat(title: string, userIDs: number[]): Promise<number> {
return apiManager.invokeApi('messages.createChat', {
users: userIDs.map(u => appUsersManager.getUserInput(u)),
title: title
}).then(updates => {
apiUpdatesManager.processUpdateMessage(updates);
return updates.chats[0].id;
});
}
public editPhoto(id: number, inputFile: any) {
let isChannel = this.isChannel(id);
let inputChatPhoto = {
_: 'inputChatUploadedPhoto',
file: inputFile
};
if(isChannel) {
return apiManager.invokeApi('channels.editPhoto', {
channel: this.getChannelInputPeer(id),
photo: inputChatPhoto
}).then(updates => {
apiUpdatesManager.processUpdateMessage(updates);
});
} else {
return apiManager.invokeApi('messages.editChatPhoto', {
chat_id: id,
photo: inputChatPhoto
}).then(updates => {
apiUpdatesManager.processUpdateMessage(updates);
});
}
}
public async getOnlines(id: number): Promise<number> {
if(this.isMegagroup(id)) {
let timestamp = Date.now() / 1000 | 0;
let cached = this.megagroupOnlines[id] ?? (this.megagroupOnlines[id] = {timestamp: 0, onlines: 1});
if((timestamp - cached.timestamp) < 60) {
return cached.onlines;
}
let res = await apiManager.invokeApi('messages.getOnlines', {
peer: this.getChannelInputPeer(id)
});
let onlines = res.onlines ?? 1;
cached.timestamp = timestamp;
cached.onlines = onlines;
return onlines;
} else if(this.isBroadcast(id)) {
return 1;
}
let chatInfo = appProfileManager.getChatFull(id);
if(chatInfo._ == 'chatFull' && chatInfo.participants && chatInfo.participants.participants) {
let participants = chatInfo.participants.participants;
return participants.reduce((acc: number, participant: any) => {
let user = appUsersManager.getUser(participant.user_id);
if(user && user.status && user.status._ == 'userStatusOnline') {
return acc + 1;
}
return acc;
}, 0);
} else {
return 1;
}
}
}
export default new AppChatsManager();