Restrict sending messages to left megagroup

This commit is contained in:
Eduard Kuzmenko 2021-05-12 23:40:36 +04:00
parent 06d92091f2
commit 094ec4c858
15 changed files with 105 additions and 90 deletions

View File

@ -133,7 +133,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
buttonsDiv.classList.add(MEDIA_VIEWER_CLASSNAME + '-buttons'); buttonsDiv.classList.add(MEDIA_VIEWER_CLASSNAME + '-buttons');
topButtons.concat(['download', 'close']).forEach(name => { topButtons.concat(['download', 'close']).forEach(name => {
const button = ButtonIcon(name); const button = ButtonIcon(name, {noRipple: name === 'close' || undefined});
this.buttons[name] = button; this.buttons[name] = button;
buttonsDiv.append(button); buttonsDiv.append(button);
}); });

View File

@ -42,7 +42,7 @@ import { AppChatsManager } from "../../lib/appManagers/appChatsManager";
import ListenerSetter from "../../helpers/listenerSetter"; import ListenerSetter from "../../helpers/listenerSetter";
import PollElement from "../poll"; import PollElement from "../poll";
import AudioElement from "../audio"; import AudioElement from "../audio";
import { Message, MessageEntity, MessageReplyHeader } from "../../layer"; import { Message, MessageEntity, MessageReplyHeader, Update } from "../../layer";
import { REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config"; import { REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config";
import { FocusDirection } from "../../helpers/fastSmoothScroll"; import { FocusDirection } from "../../helpers/fastSmoothScroll";
import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent, interruptHeavyAnimation } from "../../hooks/useHeavyAnimationCheck"; import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent, interruptHeavyAnimation } from "../../hooks/useHeavyAnimationCheck";
@ -408,7 +408,7 @@ export default class ChatBubbles {
} }
this.listenerSetter.add(this.bubblesContainer, 'dblclick', (e) => { this.listenerSetter.add(this.bubblesContainer, 'dblclick', (e) => {
if(this.chat.selection.isSelecting || !this.appMessagesManager.canWriteToPeer(this.peerId)) { if(this.chat.selection.isSelecting || !this.appMessagesManager.canWriteToPeer(this.peerId, this.chat.threadId)) {
return; return;
} }
@ -502,6 +502,19 @@ export default class ChatBubbles {
} }
}); });
this.listenerSetter.add(rootScope, 'chat_update', (e) => {
const chatId: number = e;
if(this.peerId === -chatId) {
const hadRights = this.chatInner.classList.contains('has-rights');
const hasRights = this.appMessagesManager.canWriteToPeer(this.peerId, this.chat.threadId);
if(hadRights !== hasRights) {
this.finishPeerChange();
this.chat.input.updateMessageInput();
}
}
});
this.unreadedObserver = new IntersectionObserver((entries) => { this.unreadedObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => { entries.forEach(entry => {
if(entry.isIntersecting) { if(entry.isIntersecting) {
@ -1748,7 +1761,7 @@ export default class ChatBubbles {
public finishPeerChange() { public finishPeerChange() {
const peerId = this.peerId; const peerId = this.peerId;
const isChannel = this.appPeersManager.isChannel(peerId); const isChannel = this.appPeersManager.isChannel(peerId);
const canWrite = this.appMessagesManager.canWriteToPeer(peerId); const canWrite = this.appMessagesManager.canWriteToPeer(peerId, this.chat.threadId);
this.chatInner.classList.toggle('has-rights', canWrite); this.chatInner.classList.toggle('has-rights', canWrite);
this.bubblesContainer.classList.toggle('is-chat-input-hidden', !canWrite); this.bubblesContainer.classList.toggle('is-chat-input-hidden', !canWrite);

View File

@ -35,9 +35,9 @@ export default class ChatContextMenu {
private isTargetAGroupedItem: boolean; private isTargetAGroupedItem: boolean;
private isTextSelected: boolean; private isTextSelected: boolean;
private isAnchorTarget: boolean; private isAnchorTarget: boolean;
public peerId: number; private peerId: number;
public mid: number; private mid: number;
public message: any; private message: any;
constructor(private attachTo: HTMLElement, private chat: Chat, private appMessagesManager: AppMessagesManager, private appChatsManager: AppChatsManager, private appPeersManager: AppPeersManager, private appPollsManager: AppPollsManager) { constructor(private attachTo: HTMLElement, private chat: Chat, private appMessagesManager: AppMessagesManager, private appChatsManager: AppChatsManager, private appPeersManager: AppPeersManager, private appPollsManager: AppPollsManager) {
const onContextMenu = (e: MouseEvent | Touch | TouchEvent) => { const onContextMenu = (e: MouseEvent | Touch | TouchEvent) => {
@ -190,7 +190,7 @@ export default class ChatContextMenu {
icon: 'reply', icon: 'reply',
text: 'Reply', text: 'Reply',
onClick: this.onReplyClick, onClick: this.onReplyClick,
verify: () => this.appMessagesManager.canWriteToPeer(this.peerId) && verify: () => this.appMessagesManager.canWriteToPeer(this.peerId, this.chat.threadId) &&
!this.message.pFlags.is_outgoing && !this.message.pFlags.is_outgoing &&
!!this.chat.input.messageInput && !!this.chat.input.messageInput &&
this.chat.type !== 'scheduled'/* , this.chat.type !== 'scheduled'/* ,

View File

@ -692,31 +692,7 @@ export default class ChatInput {
} }
if(this.messageInput) { if(this.messageInput) {
const canWrite = this.appMessagesManager.canWriteToPeer(peerId); this.updateMessageInput();
this.chatInput.classList.add('no-transition');
this.chatInput.classList.toggle('is-hidden', !canWrite);
void this.chatInput.offsetLeft; // reflow
this.chatInput.classList.remove('no-transition');
const visible = this.attachMenuButtons.filter(button => {
const good = button.verify(peerId);
button.element.classList.toggle('hide', !good);
return good;
});
if(!canWrite) {
this.messageInput.removeAttribute('contenteditable');
} else {
this.messageInput.setAttribute('contenteditable', 'true');
this.setDraft(undefined, false);
if(!this.messageInput.innerHTML) {
this.messageInputField.onFakeInput();
}
}
this.attachMenu.toggleAttribute('disabled', !visible.length);
this.updateSendBtn();
} else if(this.pinnedControlBtn) { } else if(this.pinnedControlBtn) {
if(this.appPeersManager.canPinMessage(this.chat.peerId)) { if(this.appPeersManager.canPinMessage(this.chat.peerId)) {
this.pinnedControlBtn.append(i18n('Chat.Input.UnpinAll')); this.pinnedControlBtn.append(i18n('Chat.Input.UnpinAll'));
@ -728,6 +704,34 @@ export default class ChatInput {
} }
} }
public updateMessageInput() {
const canWrite = this.appMessagesManager.canWriteToPeer(this.chat.peerId, this.chat.threadId);
this.chatInput.classList.add('no-transition');
this.chatInput.classList.toggle('is-hidden', !canWrite);
void this.chatInput.offsetLeft; // reflow
this.chatInput.classList.remove('no-transition');
const visible = this.attachMenuButtons.filter(button => {
const good = button.verify(this.chat.peerId);
button.element.classList.toggle('hide', !good);
return good;
});
if(!canWrite) {
this.messageInput.removeAttribute('contenteditable');
} else {
this.messageInput.setAttribute('contenteditable', 'true');
this.setDraft(undefined, false);
if(!this.messageInput.innerHTML) {
this.messageInputField.onFakeInput();
}
}
this.attachMenu.toggleAttribute('disabled', !visible.length);
this.updateSendBtn();
}
private attachMessageInputField() { private attachMessageInputField() {
const oldInputField = this.messageInputField; const oldInputField = this.messageInputField;
this.messageInputField = new InputField({ this.messageInputField = new InputField({

View File

@ -289,9 +289,9 @@ export default class ChatTopbar {
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});
this.listenerSetter.add(rootScope, 'chat_update', (e) => { this.listenerSetter.add(rootScope, 'chat_update', (e) => {
const peerId: number = e; const chatId: number = e;
if(this.peerId === -peerId) { if(this.peerId === -chatId) {
const chat = this.appChatsManager.getChat(peerId) as Channel/* | Chat */; const chat = this.appChatsManager.getChat(chatId) as Channel/* | Chat */;
this.btnJoin.classList.toggle('hide', !(chat as Channel)?.pFlags?.left); this.btnJoin.classList.toggle('hide', !(chat as Channel)?.pFlags?.left);
this.setUtilsWidth(); this.setUtilsWidth();

View File

@ -24,9 +24,13 @@ export default class PopupDeleteDialog {
}; */ }; */
const callbackLeave = (checked: PopupPeerButtonCallbackCheckboxes) => { const callbackLeave = (checked: PopupPeerButtonCallbackCheckboxes) => {
const promise = appChatsManager.leave(-peerId).then(() => { let promise = appChatsManager.leave(-peerId);
return appMessagesManager.flushHistory(-peerId);
}); if(checkboxes && checked[checkboxes[0].text]) {
promise = promise.then(() => {
return appMessagesManager.flushHistory(peerId);
}) as any;
}
onSelect && onSelect(promise); onSelect && onSelect(promise);
}; };

View File

@ -13,7 +13,7 @@ const App = {
id: 1025907, id: 1025907,
hash: '452b0359b988148995f22ff0f4229750', hash: '452b0359b988148995f22ff0f4229750',
version: '0.5.3', version: '0.5.3',
langPackVersion: '0.1.6', langPackVersion: '0.1.7',
langPack: 'macos', langPack: 'macos',
langPackCode: 'en', langPackCode: 'en',
domains: [] as string[], domains: [] as string[],

View File

@ -417,6 +417,7 @@ const lang = {
"Online": "online", "Online": "online",
"MessageScheduleSend": "Send Now", "MessageScheduleSend": "Send Now",
"MessageScheduleEditTime": "Reschedule", "MessageScheduleEditTime": "Reschedule",
"YouLeft": "You left this group",
// * macos // * macos
"AccountSettings.Filters": "Chat Folders", "AccountSettings.Filters": "Chat Folders",

View File

@ -44,11 +44,11 @@ export class AppChatsManager {
constructor() { constructor() {
rootScope.addMultipleEventsListeners({ rootScope.addMultipleEventsListeners({
updateChannel: (update) => { /* updateChannel: (update) => {
const channelId = update.channel_id; const channelId = update.channel_id;
//console.log('updateChannel:', update); //console.log('updateChannel:', update);
rootScope.broadcast('channel_settings', {channelId}); rootScope.broadcast('channel_settings', {channelId});
}, }, */
updateChannelParticipant: (update) => { updateChannelParticipant: (update) => {
apiManagerProxy.clearCache('channels.getParticipants', (params) => { apiManagerProxy.clearCache('channels.getParticipants', (params) => {
@ -268,7 +268,7 @@ export class AppChatsManager {
return rights; return rights;
} }
public hasRights(id: number, action: ChatRights, rights?: ChatAdminRights | ChatBannedRights) { public hasRights(id: number, action: ChatRights, rights?: ChatAdminRights | ChatBannedRights, isThread?: boolean) {
const chat: Chat = this.getChat(id); const chat: Chat = this.getChat(id);
if(chat._ === 'chatEmpty') return false; if(chat._ === 'chatEmpty') return false;
@ -285,14 +285,16 @@ export class AppChatsManager {
if(!rights) { if(!rights) {
rights = chat.admin_rights || (chat as Chat.channel).banned_rights || chat.default_banned_rights; rights = chat.admin_rights || (chat as Chat.channel).banned_rights || chat.default_banned_rights;
}
if(!rights) {
if(!rights) { return false;
return false; }
} }
let myFlags: Partial<{[flag in keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags']]: true}> = {}; let myFlags: Partial<{[flag in keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags']]: true}> = {};
if(rights) myFlags = rights.pFlags as any; if(rights) {
myFlags = rights.pFlags as any;
}
switch(action) { switch(action) {
case 'embed_links': case 'embed_links':
@ -303,12 +305,16 @@ export class AppChatsManager {
case 'send_messages': case 'send_messages':
case 'send_polls': case 'send_polls':
case 'send_stickers': { case 'send_stickers': {
if(!isThread && chat.pFlags.left) {
return false;
}
if(rights._ === 'chatBannedRights' && myFlags[action]) { if(rights._ === 'chatBannedRights' && myFlags[action]) {
return false; return false;
} }
if(chat._ === 'channel') { if(chat._ === 'channel') {
if((!chat.pFlags.megagroup && !myFlags.post_messages)) { if(!chat.pFlags.megagroup && !myFlags.post_messages) {
return false; return false;
} }
} }
@ -375,7 +381,6 @@ export class AppChatsManager {
} */ } */
public isChannel(id: number) { public isChannel(id: number) {
if(id < 0) id = -id;
const chat = this.chats[id]; const chat = this.chats[id];
return chat && (chat._ === 'channel' || chat._ === 'channelForbidden')/* || this.channelAccess[id] */; return chat && (chat._ === 'channel' || chat._ === 'channelForbidden')/* || this.channelAccess[id] */;
} }
@ -409,7 +414,6 @@ export class AppChatsManager {
} }
public getChannelInput(id: number): InputChannel { public getChannelInput(id: number): InputChannel {
if(id < 0) id = -id;
const chat: Chat = this.getChat(id); const chat: Chat = this.getChat(id);
if(chat._ === 'chatEmpty' || !(chat as Chat.channel).access_hash) { if(chat._ === 'chatEmpty' || !(chat as Chat.channel).access_hash) {
return { return {

View File

@ -183,7 +183,7 @@ export class AppMessagesManager {
}} = {}; }} = {};
private reloadConversationsPromise: Promise<void>; private reloadConversationsPromise: Promise<void>;
private reloadConversationsPeers: number[] = []; private reloadConversationsPeers: Set<number> = new Set();
public log = logger('MESSAGES', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn); public log = logger('MESSAGES', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn);
@ -1883,8 +1883,8 @@ export class AppMessagesManager {
public reloadConversation(peerId: number | number[]) { public reloadConversation(peerId: number | number[]) {
[].concat(peerId).forEach(peerId => { [].concat(peerId).forEach(peerId => {
if(!this.reloadConversationsPeers.includes(peerId)) { if(!this.reloadConversationsPeers.has(peerId)) {
this.reloadConversationsPeers.push(peerId); this.reloadConversationsPeers.add(peerId);
//this.log('will reloadConversation', peerId); //this.log('will reloadConversation', peerId);
} }
}); });
@ -1892,8 +1892,8 @@ export class AppMessagesManager {
if(this.reloadConversationsPromise) return this.reloadConversationsPromise; if(this.reloadConversationsPromise) return this.reloadConversationsPromise;
return this.reloadConversationsPromise = new Promise((resolve, reject) => { return this.reloadConversationsPromise = new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
const peers = this.reloadConversationsPeers.map(peerId => appPeersManager.getInputDialogPeerById(peerId)); const peers = Array.from(this.reloadConversationsPeers).map(peerId => appPeersManager.getInputDialogPeerById(peerId));
this.reloadConversationsPeers.length = 0; this.reloadConversationsPeers.clear();
apiManager.invokeApi('messages.getPeerDialogs', {peers}).then((result) => { apiManager.invokeApi('messages.getPeerDialogs', {peers}).then((result) => {
this.dialogsStorage.applyDialogs(result); this.dialogsStorage.applyDialogs(result);
@ -1947,7 +1947,7 @@ export class AppMessagesManager {
_: 'updateChannelAvailableMessages', _: 'updateChannelAvailableMessages',
channel_id: channelId, channel_id: channelId,
available_min_id: maxId available_min_id: maxId
} } as Update.updateChannelAvailableMessages
}); });
return true; return true;
@ -2297,6 +2297,7 @@ export class AppMessagesManager {
if(message.action) { if(message.action) {
let migrateFrom: number; let migrateFrom: number;
let migrateTo: number; let migrateTo: number;
const suffix = message.fromId === appUsersManager.getSelf().id ? 'You' : '';
switch(message.action._) { switch(message.action._) {
//case 'messageActionChannelEditPhoto': //case 'messageActionChannelEditPhoto':
case 'messageActionChatEditPhoto': case 'messageActionChatEditPhoto':
@ -2332,7 +2333,6 @@ export class AppMessagesManager {
if(message.action.users.length === 1) { if(message.action.users.length === 1) {
message.action.user_id = message.action.users[0]; message.action.user_id = message.action.users[0];
if(message.fromId === message.action.user_id) { if(message.fromId === message.action.user_id) {
let suffix = message.fromId === appUsersManager.getSelf().id ? 'You' : '';
if(isChannel) { if(isChannel) {
message.action._ = 'messageActionChatJoined' + suffix; message.action._ = 'messageActionChatJoined' + suffix;
} else { } else {
@ -2346,7 +2346,7 @@ export class AppMessagesManager {
case 'messageActionChatDeleteUser': case 'messageActionChatDeleteUser':
if(message.fromId === message.action.user_id) { if(message.fromId === message.action.user_id) {
message.action._ = 'messageActionChatLeave'; message.action._ = 'messageActionChatLeave' + suffix;
} }
break; break;
@ -4029,19 +4029,19 @@ export class AppMessagesManager {
private onUpdateChannel = (update: Update.updateChannel) => { private onUpdateChannel = (update: Update.updateChannel) => {
const channelId: number = update.channel_id; const channelId: number = update.channel_id;
const peerId = -channelId; const peerId = -channelId;
const channel = appChatsManager.getChat(channelId); const channel: Chat.channel = appChatsManager.getChat(channelId);
const needDialog = channel._ === 'channel' && appChatsManager.isInChat(channelId); const needDialog = appChatsManager.isInChat(channelId);
const dialog = this.getDialogOnly(peerId);
const canViewHistory = !!channel.username || !channel.pFlags.left;
const canViewHistory = channel._ === 'channel' && (channel.username || !channel.pFlags.left && !channel.pFlags.kicked);
const hasHistory = this.historiesStorage[peerId] !== undefined; const hasHistory = this.historiesStorage[peerId] !== undefined;
if(canViewHistory !== hasHistory) { if(canViewHistory !== hasHistory) {
delete this.historiesStorage[peerId]; delete this.historiesStorage[peerId];
rootScope.broadcast('history_forbidden', peerId); rootScope.broadcast('history_forbidden', peerId);
} }
const dialog = this.getDialogOnly(peerId);
if(!!dialog !== needDialog) { if(!!dialog !== needDialog) {
if(needDialog) { if(needDialog) {
this.reloadConversation(-channelId); this.reloadConversation(-channelId);
@ -4299,10 +4299,10 @@ export class AppMessagesManager {
}, settings); }, settings);
} }
public canWriteToPeer(peerId: number) { public canWriteToPeer(peerId: number, threadId?: number) {
if(peerId < 0) { if(peerId < 0) {
const isChannel = appPeersManager.isChannel(peerId); const isChannel = appPeersManager.isChannel(peerId);
const hasRights = isChannel && appChatsManager.hasRights(-peerId, 'send_messages'); const hasRights = isChannel && appChatsManager.hasRights(-peerId, 'send_messages', undefined, !!threadId);
return !isChannel || hasRights; return !isChannel || hasRights;
} else { } else {
return appUsersManager.canSendToUser(peerId); return appUsersManager.canSendToUser(peerId);

View File

@ -369,7 +369,7 @@ export class AppStateManager extends EventListenerBase<{
public keepPeerSingle(peerId: number, type: string) { public keepPeerSingle(peerId: number, type: string) {
const existsPeerId = this.singlePeerMap.get(type); const existsPeerId = this.singlePeerMap.get(type);
if(existsPeerId && existsPeerId !== peerId) { if(existsPeerId && existsPeerId !== peerId && this.neededPeers.has(existsPeerId)) {
const set = this.neededPeers.get(existsPeerId); const set = this.neededPeers.get(existsPeerId);
set.delete(type); set.delete(type);

View File

@ -28,6 +28,7 @@ export const langPack: {[actionType: string]: LangPackKey} = {
"messageActionChatAddUser": "ActionAddUser", "messageActionChatAddUser": "ActionAddUser",
"messageActionChatAddUsers": "ActionAddUser", "messageActionChatAddUsers": "ActionAddUser",
"messageActionChatLeave": "ActionLeftUser", "messageActionChatLeave": "ActionLeftUser",
"messageActionChatLeaveYou": "YouLeft",
"messageActionChatDeleteUser": "ActionKickUser", "messageActionChatDeleteUser": "ActionKickUser",
"messageActionChatJoinedByLink": "ActionInviteUser", "messageActionChatJoinedByLink": "ActionInviteUser",
"messageActionPinMessage": "ActionPinnedNoText", "messageActionPinMessage": "ActionPinnedNoText",

View File

@ -83,7 +83,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,
'channel_settings': {channelId: number}, //'channel_settings': {channelId: number},
'webpage_updated': {id: string, msgs: number[]}, 'webpage_updated': {id: string, msgs: number[]},
'download_progress': any, 'download_progress': any,

View File

@ -11,13 +11,13 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0 1rem; padding: 0 1rem;
min-height: 56px; min-height: 3.5rem;
flex: 0 0 auto; flex: 0 0 auto;
user-select: none; user-select: none;
cursor: default; cursor: default;
@include respond-to(handhelds) { @include respond-to(handhelds) {
padding: 7.5px 8px; padding: 0 .5rem;
} }
/* //position: sticky !important; /* //position: sticky !important;
@ -28,8 +28,8 @@
&__title { &__title {
flex: 1; flex: 1;
font-weight: 500; font-weight: 500;
padding-left: 24px; padding-left: 1.5rem;
font-size: 20px; font-size: 1.25rem;
color: var(--primary-text-color); color: var(--primary-text-color);
} }
@ -40,8 +40,8 @@
&-close-button { &-close-button {
overflow: inherit !important; overflow: inherit !important;
width: 40px; width: 2.5rem;
height: 40px; height: 2.5rem;
} }
&-content { &-content {

View File

@ -14,7 +14,7 @@
z-index: 3; z-index: 3;
background-color: rgba(0, 0, 0, .3); background-color: rgba(0, 0, 0, .3);
margin: 0; margin: 0;
padding: 2.5rem; padding: 1.875rem;
box-shadow: none; box-shadow: none;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
@ -70,21 +70,9 @@
} }
&-close { &-close {
cursor: pointer;
color: var(--secondary-text-color);
z-index: 3; z-index: 3;
text-align: center;
justify-self: center; justify-self: center;
line-height: 1; flex: 0 0 auto;
//transition: color .2s;
body.animation-level-0 & {
transition: none;
}
/* @include hover() {
color: #000;
} */
} }
&-header { &-header {