Browse Source

Fix deleting channels

Fix dropping chats when they are deleted
Confirm popups by Enter
master
morethanwords 3 years ago
parent
commit
3fc26e0262
  1. 1
      src/components/appSelectPeers.ts
  2. 8
      src/components/chat/bubbles.ts
  3. 2
      src/components/chat/contextMenu.ts
  4. 18
      src/components/chat/input.ts
  5. 5
      src/components/chat/topbar.ts
  6. 2
      src/components/dialogsContextMenu.ts
  7. 8
      src/components/emoticonsDropdown/index.ts
  8. 19
      src/components/popups/avatar.ts
  9. 22
      src/components/popups/createPoll.ts
  10. 13
      src/components/popups/datePicker.ts
  11. 52
      src/components/popups/deleteDialog.ts
  12. 67
      src/components/popups/index.ts
  13. 17
      src/components/popups/newMedia.ts
  14. 5
      src/components/popups/reportMessages.ts
  15. 3
      src/components/popups/schedule.ts
  16. 29
      src/components/popups/stickers.ts
  17. 4
      src/components/sidebarLeft/tabs/newChannel.ts
  18. 47
      src/components/sidebarRight/tabs/editChat.ts
  19. 28
      src/components/sidebarRight/tabs/sharedMedia.ts
  20. 6
      src/lang.ts
  21. 17
      src/lib/appManagers/appChatsManager.ts
  22. 2
      src/lib/appManagers/appImManager.ts
  23. 13
      src/lib/appManagers/appMessagesManager.ts
  24. 6
      src/lib/appManagers/appPeersManager.ts
  25. 12
      src/lib/storages/dialogs.ts
  26. 15
      src/scss/style.scss

1
src/components/appSelectPeers.ts

@ -7,7 +7,6 @@
import appChatsManager, { ChatRights } from "../lib/appManagers/appChatsManager"; import appChatsManager, { ChatRights } from "../lib/appManagers/appChatsManager";
import appDialogsManager from "../lib/appManagers/appDialogsManager"; import appDialogsManager from "../lib/appManagers/appDialogsManager";
import appMessagesManager, { Dialog } from "../lib/appManagers/appMessagesManager"; import appMessagesManager, { Dialog } from "../lib/appManagers/appMessagesManager";
import appPhotosManager from "../lib/appManagers/appPhotosManager";
import appUsersManager from "../lib/appManagers/appUsersManager"; import appUsersManager from "../lib/appManagers/appUsersManager";
import rootScope from "../lib/rootScope"; import rootScope from "../lib/rootScope";
import Scrollable from "./scrollable"; import Scrollable from "./scrollable";

8
src/components/chat/bubbles.ts

@ -456,7 +456,7 @@ export default class ChatBubbles {
if(!isMobile) { if(!isMobile) {
this.listenerSetter.add(this.bubblesContainer)('dblclick', (e) => { this.listenerSetter.add(this.bubblesContainer)('dblclick', (e) => {
if(this.chat.selection.isSelecting || if(this.chat.selection.isSelecting ||
!this.appMessagesManager.canWriteToPeer(this.peerId, this.chat.threadId)) { !this.appMessagesManager.canSendToPeer(this.peerId, this.chat.threadId)) {
return; return;
} }
@ -559,7 +559,7 @@ export default class ChatBubbles {
const chatId: number = e; const chatId: number = e;
if(this.peerId === -chatId) { if(this.peerId === -chatId) {
const hadRights = this.chatInner.classList.contains('has-rights'); const hadRights = this.chatInner.classList.contains('has-rights');
const hasRights = this.appMessagesManager.canWriteToPeer(this.peerId, this.chat.threadId); const hasRights = this.appMessagesManager.canSendToPeer(this.peerId, this.chat.threadId);
if(hadRights !== hasRights) { if(hadRights !== hasRights) {
this.finishPeerChange(); this.finishPeerChange();
@ -2007,7 +2007,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, this.chat.threadId); const canWrite = this.appMessagesManager.canSendToPeer(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);
@ -3495,7 +3495,7 @@ export default class ChatBubbles {
this.renderEmptyPlaceholder('group', bubble, message, elements); this.renderEmptyPlaceholder('group', bubble, message, elements);
} else if(rootScope.myId === this.peerId) { } else if(rootScope.myId === this.peerId) {
this.renderEmptyPlaceholder('saved', bubble, message, elements); this.renderEmptyPlaceholder('saved', bubble, message, elements);
} else if(this.peerId > 0 && !isBot && this.appMessagesManager.canWriteToPeer(this.peerId) && this.chat.type === 'chat') { } else if(this.peerId > 0 && !isBot && this.appMessagesManager.canSendToPeer(this.peerId) && this.chat.type === 'chat') {
this.renderEmptyPlaceholder('greeting', bubble, message, elements); this.renderEmptyPlaceholder('greeting', bubble, message, elements);
} else if(this.chat.type === 'scheduled') { } else if(this.chat.type === 'scheduled') {
this.renderEmptyPlaceholder('noScheduledMessages', bubble, message, elements); this.renderEmptyPlaceholder('noScheduledMessages', bubble, message, elements);

2
src/components/chat/contextMenu.ts

@ -188,7 +188,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, this.chat.threadId) && verify: () => this.appMessagesManager.canSendToPeer(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'/* ,

18
src/components/chat/input.ts

@ -107,7 +107,7 @@ export default class ChatInput {
private replyKeyboard: ReplyKeyboard; private replyKeyboard: ReplyKeyboard;
private attachMenu: HTMLButtonElement; private attachMenu: HTMLButtonElement;
private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerId: number) => boolean})[]; private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerId: number, threadId: number) => boolean})[];
private sendMenu: SendMenu; private sendMenu: SendMenu;
@ -371,7 +371,7 @@ export default class ChatInput {
this.willAttachType = 'media'; this.willAttachType = 'media';
this.fileInput.click(); this.fileInput.click();
}, },
verify: (peerId: number) => peerId > 0 || this.appChatsManager.hasRights(peerId, 'send_media') verify: (peerId, threadId) => this.appMessagesManager.canSendToPeer(peerId, threadId, 'send_media')
}, { }, {
icon: 'document', icon: 'document',
text: 'Chat.Input.Attach.Document', text: 'Chat.Input.Attach.Document',
@ -381,14 +381,14 @@ export default class ChatInput {
this.willAttachType = 'document'; this.willAttachType = 'document';
this.fileInput.click(); this.fileInput.click();
}, },
verify: (peerId: number) => peerId > 0 || this.appChatsManager.hasRights(peerId, 'send_media') verify: (peerId, threadId) => this.appMessagesManager.canSendToPeer(peerId, threadId, 'send_media')
}, { }, {
icon: 'poll', icon: 'poll',
text: 'Poll', text: 'Poll',
onClick: () => { onClick: () => {
new PopupCreatePoll(this.chat).show(); new PopupCreatePoll(this.chat).show();
}, },
verify: (peerId: number) => peerId < 0 && this.appChatsManager.hasRights(peerId, 'send_polls') verify: (peerId, threadId) => this.appMessagesManager.canSendToPeer(peerId, threadId, 'send_polls')
}]; }];
this.attachMenu = ButtonMenuToggle({noRipple: true, listenerSetter: this.listenerSetter}, 'top-left', this.attachMenuButtons); this.attachMenu = ButtonMenuToggle({noRipple: true, listenerSetter: this.listenerSetter}, 'top-left', this.attachMenuButtons);
@ -814,14 +814,14 @@ export default class ChatInput {
} }
public updateMessageInput() { public updateMessageInput() {
const canWrite = this.appMessagesManager.canWriteToPeer(this.chat.peerId, this.chat.threadId); const canWrite = this.appMessagesManager.canSendToPeer(this.chat.peerId, this.chat.threadId);
this.chatInput.classList.add('no-transition'); this.chatInput.classList.add('no-transition');
this.chatInput.classList.toggle('is-hidden', !canWrite); this.chatInput.classList.toggle('is-hidden', !canWrite);
void this.chatInput.offsetLeft; // reflow void this.chatInput.offsetLeft; // reflow
this.chatInput.classList.remove('no-transition'); this.chatInput.classList.remove('no-transition');
const visible = this.attachMenuButtons.filter(button => { const visible = this.attachMenuButtons.filter(button => {
const good = button.verify(this.chat.peerId); const good = button.verify(this.chat.peerId, this.chat.threadId);
button.element.classList.toggle('hide', !good); button.element.classList.toggle('hide', !good);
return good; return good;
}); });
@ -1328,7 +1328,7 @@ export default class ChatInput {
if(this.stickersHelper && if(this.stickersHelper &&
rootScope.settings.stickers.suggest && rootScope.settings.stickers.suggest &&
(this.chat.peerId > 0 || this.appChatsManager.hasRights(this.chat.peerId, 'send_stickers')) && this.appMessagesManager.canSendToPeer(this.chat.peerId, this.chat.threadId, 'send_stickers') &&
entity?._ === 'messageEntityEmoji' && entity.length === value.length && !entity.offset) { entity?._ === 'messageEntityEmoji' && entity.length === value.length && !entity.offset) {
foundHelper = this.stickersHelper; foundHelper = this.stickersHelper;
this.stickersHelper.checkEmoticon(value); this.stickersHelper.checkEmoticon(value);
@ -1414,7 +1414,7 @@ export default class ChatInput {
this.sendMessage(); this.sendMessage();
} }
} else { } else {
if(this.chat.peerId < 0 && !this.appChatsManager.hasRights(this.chat.peerId, 'send_media')) { if(this.chat.peerId < 0 && !this.appMessagesManager.canSendToPeer(this.chat.peerId, this.chat.threadId, 'send_media')) {
toast(POSTING_MEDIA_NOT_ALLOWED); toast(POSTING_MEDIA_NOT_ALLOWED);
return; return;
} }
@ -1694,7 +1694,7 @@ export default class ChatInput {
document = this.appDocsManager.getDoc(document); document = this.appDocsManager.getDoc(document);
const flag = document.type === 'sticker' ? 'send_stickers' : (document.type === 'gif' ? 'send_gifs' : 'send_media'); const flag = document.type === 'sticker' ? 'send_stickers' : (document.type === 'gif' ? 'send_gifs' : 'send_media');
if(this.chat.peerId < 0 && !this.appChatsManager.hasRights(this.chat.peerId, flag)) { if(this.chat.peerId < 0 && !this.appMessagesManager.canSendToPeer(this.chat.peerId, this.chat.threadId, flag)) {
toast(POSTING_MEDIA_NOT_ALLOWED); toast(POSTING_MEDIA_NOT_ALLOWED);
return false; return false;
} }

5
src/components/chat/topbar.ts

@ -314,7 +314,7 @@ export default class ChatTopbar {
icon: 'delete danger', icon: 'delete danger',
text: 'Delete', text: 'Delete',
onClick: () => { onClick: () => {
new PopupDeleteDialog(this.peerId); new PopupDeleteDialog(this.peerId/* , 'leave' */);
}, },
verify: () => this.chat.type === 'chat' && !!this.appMessagesManager.getDialogOnly(this.peerId) verify: () => this.chat.type === 'chat' && !!this.appMessagesManager.getDialogOnly(this.peerId)
}]; }];
@ -377,8 +377,7 @@ export default class ChatTopbar {
}); });
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});
this.listenerSetter.add(rootScope)('chat_update', (e) => { this.listenerSetter.add(rootScope)('chat_update', (chatId) => {
const chatId: number = e;
if(this.peerId === -chatId) { if(this.peerId === -chatId) {
const chat = this.appChatsManager.getChat(chatId) as Channel/* | Chat */; const chat = this.appChatsManager.getChat(chatId) as Channel/* | Chat */;

2
src/components/dialogsContextMenu.ts

@ -149,7 +149,7 @@ export default class DialogsContextMenu {
}; };
private onDeleteClick = () => { private onDeleteClick = () => {
new PopupDeleteDialog(this.selectedId); new PopupDeleteDialog(this.selectedId/* , 'delete' */);
}; };
onContextMenu = (e: MouseEvent | Touch) => { onContextMenu = (e: MouseEvent | Touch) => {

8
src/components/emoticonsDropdown/index.ts

@ -5,7 +5,6 @@
*/ */
import { isTouchSupported } from "../../helpers/touchSupport"; import { isTouchSupported } from "../../helpers/touchSupport";
import appChatsManager from "../../lib/appManagers/appChatsManager";
import appImManager from "../../lib/appManagers/appImManager"; import appImManager from "../../lib/appManagers/appImManager";
import rootScope from "../../lib/rootScope"; import rootScope from "../../lib/rootScope";
import animationIntersector from "../animationIntersector"; import animationIntersector from "../animationIntersector";
@ -27,6 +26,7 @@ import whichChild from "../../helpers/dom/whichChild";
import { cancelEvent } from "../../helpers/dom/cancelEvent"; import { cancelEvent } from "../../helpers/dom/cancelEvent";
import DropdownHover from "../../helpers/dropdownHover"; import DropdownHover from "../../helpers/dropdownHover";
import { pause } from "../../helpers/schedulers/pause"; import { pause } from "../../helpers/schedulers/pause";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown'; export const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
@ -188,14 +188,14 @@ export class EmoticonsDropdown extends DropdownHover {
}; };
private checkRights = () => { private checkRights = () => {
const peerId = appImManager.chat.peerId; const {peerId, threadId} = appImManager.chat;
const children = this.tabsEl.children; const children = this.tabsEl.children;
const tabsElements = Array.from(children) as HTMLElement[]; const tabsElements = Array.from(children) as HTMLElement[];
const canSendStickers = peerId > 0 || appChatsManager.hasRights(peerId, 'send_stickers'); const canSendStickers = appMessagesManager.canSendToPeer(peerId, threadId, 'send_stickers');
tabsElements[2].toggleAttribute('disabled', !canSendStickers); tabsElements[2].toggleAttribute('disabled', !canSendStickers);
const canSendGifs = peerId > 0 || appChatsManager.hasRights(peerId, 'send_gifs'); const canSendGifs = appMessagesManager.canSendToPeer(peerId, threadId, 'send_gifs');
tabsElements[3].toggleAttribute('disabled', !canSendGifs); tabsElements[3].toggleAttribute('disabled', !canSendGifs);
const active = this.tabsEl.querySelector('.active'); const active = this.tabsEl.querySelector('.active');

19
src/components/popups/avatar.ts

@ -7,14 +7,13 @@
import appDownloadManager from "../../lib/appManagers/appDownloadManager"; import appDownloadManager from "../../lib/appManagers/appDownloadManager";
import resizeableImage from "../../lib/cropper"; import resizeableImage from "../../lib/cropper";
import PopupElement from "."; import PopupElement from ".";
import { ripple } from "../ripple";
import { _i18n } from "../../lib/langPack"; import { _i18n } from "../../lib/langPack";
import { readBlobAsDataURL } from "../../helpers/blob"; import { readBlobAsDataURL } from "../../helpers/blob";
import { attachClickEvent } from "../../helpers/dom/clickEvent";
export default class PopupAvatar extends PopupElement { export default class PopupAvatar extends PopupElement {
private cropContainer: HTMLElement; private cropContainer: HTMLElement;
private input: HTMLInputElement; private input: HTMLInputElement;
private btnSubmit: HTMLElement;
private h6: HTMLElement; private h6: HTMLElement;
private image = new Image(); private image = new Image();
@ -29,7 +28,7 @@ export default class PopupAvatar extends PopupElement {
private onCrop: (upload: () => ReturnType<typeof appDownloadManager.upload>) => void; private onCrop: (upload: () => ReturnType<typeof appDownloadManager.upload>) => void;
constructor() { constructor() {
super('popup-avatar', null, {closable: true}); super('popup-avatar', null, {closable: true, withConfirm: true});
this.h6 = document.createElement('h6'); this.h6 = document.createElement('h6');
_i18n(this.h6, 'Popup.Avatar.Title'); _i18n(this.h6, 'Popup.Avatar.Title');
@ -45,7 +44,7 @@ export default class PopupAvatar extends PopupElement {
this.input = document.createElement('input'); this.input = document.createElement('input');
this.input.type = 'file'; this.input.type = 'file';
this.input.style.display = 'none'; this.input.style.display = 'none';
this.input.addEventListener('change', (e: any) => { this.listenerSetter.add(this.input)('change', (e: any) => {
const file = e.target.files[0]; const file = e.target.files[0];
if(!file) { if(!file) {
return; return;
@ -68,21 +67,19 @@ export default class PopupAvatar extends PopupElement {
}); });
}, false); }, false);
this.btnSubmit = document.createElement('button'); this.btnConfirm.className = 'btn-primary btn-color-primary btn-circle btn-crop btn-icon tgico-check z-depth-1';
this.btnSubmit.className = 'btn-primary btn-color-primary btn-circle btn-crop btn-icon tgico-check z-depth-1'; attachClickEvent(this.btnConfirm, () => {
ripple(this.btnSubmit);
this.btnSubmit.addEventListener('click', () => {
this.cropper.crop(); this.cropper.crop();
this.btnClose.click(); this.hide();
this.canvas.toBlob(blob => { this.canvas.toBlob(blob => {
this.blob = blob; // save blob to send after reg this.blob = blob; // save blob to send after reg
this.darkenCanvas(); this.darkenCanvas();
this.resolve(); this.resolve();
}, 'image/jpeg', 1); }, 'image/jpeg', 1);
}); }, {listenerSetter: this.listenerSetter});
this.container.append(this.cropContainer, this.btnSubmit, this.input); this.container.append(this.cropContainer, this.btnConfirm, this.input);
this.onCloseAfterTimeout = () => { this.onCloseAfterTimeout = () => {
this.cropper.removeHandlers(); this.cropper.removeHandlers();

22
src/components/popups/createPoll.ts

@ -18,6 +18,7 @@ import { cancelEvent } from "../../helpers/dom/cancelEvent";
import getRichValue from "../../helpers/dom/getRichValue"; import getRichValue from "../../helpers/dom/getRichValue";
import isInputEmpty from "../../helpers/dom/isInputEmpty"; import isInputEmpty from "../../helpers/dom/isInputEmpty";
import whichChild from "../../helpers/dom/whichChild"; import whichChild from "../../helpers/dom/whichChild";
import { attachClickEvent } from "../../helpers/dom/clickEvent";
const MAX_LENGTH_QUESTION = 255; const MAX_LENGTH_QUESTION = 255;
const MAX_LENGTH_OPTION = 100; const MAX_LENGTH_OPTION = 100;
@ -49,7 +50,7 @@ export default class PopupCreatePoll extends PopupElement {
maxLength: MAX_LENGTH_QUESTION maxLength: MAX_LENGTH_QUESTION
}); });
this.questionInputField.input.addEventListener('input', () => { this.listenerSetter.add(this.questionInputField.input)('input', () => {
this.handleChange(); this.handleChange();
}); });
@ -110,12 +111,12 @@ export default class PopupCreatePoll extends PopupElement {
name: 'quiz' name: 'quiz'
}); });
this.multipleCheckboxField.input.addEventListener('change', () => { this.listenerSetter.add(this.multipleCheckboxField.input)('change', () => {
const checked = this.multipleCheckboxField.input.checked; const checked = this.multipleCheckboxField.input.checked;
this.quizCheckboxField.input.toggleAttribute('disabled', checked); this.quizCheckboxField.input.toggleAttribute('disabled', checked);
}); });
this.quizCheckboxField.input.addEventListener('change', () => { this.listenerSetter.add(this.quizCheckboxField.input)('change', () => {
const checked = this.quizCheckboxField.input.checked; const checked = this.quizCheckboxField.input.checked;
(Array.from(this.questions.children) as HTMLElement[]).map(el => { (Array.from(this.questions.children) as HTMLElement[]).map(el => {
@ -153,7 +154,7 @@ export default class PopupCreatePoll extends PopupElement {
maxLength: MAX_LENGTH_SOLUTION maxLength: MAX_LENGTH_SOLUTION
}); });
this.questionInputField.input.addEventListener('input', () => { this.listenerSetter.add(this.questionInputField.input)('input', () => {
this.handleChange(); this.handleChange();
}); });
@ -169,7 +170,7 @@ export default class PopupCreatePoll extends PopupElement {
this.body.parentElement.insertBefore(hr, this.body); this.body.parentElement.insertBefore(hr, this.body);
this.body.append(d, this.questions, document.createElement('hr'), settingsCaption, dd, ...quizElements); this.body.append(d, this.questions, document.createElement('hr'), settingsCaption, dd, ...quizElements);
this.btnConfirm.addEventListener('click', this.onSubmitClick); attachClickEvent(this.btnConfirm, this.onSubmitClick, {listenerSetter: this.listenerSetter});
this.scrollable = new Scrollable(this.body); this.scrollable = new Scrollable(this.body);
this.appendMoreField(); this.appendMoreField();
@ -246,8 +247,7 @@ export default class PopupCreatePoll extends PopupElement {
return; return;
} }
this.btnClose.click(); this.hide();
this.btnConfirm.removeEventListener('click', this.onSubmitClick);
//const randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)]; //const randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)];
//const randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(); //const randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString();
@ -350,20 +350,20 @@ export default class PopupCreatePoll extends PopupElement {
name: 'question-' + tempId, name: 'question-' + tempId,
maxLength: MAX_LENGTH_OPTION maxLength: MAX_LENGTH_OPTION
}); });
questionField.input.addEventListener('input', this.onInput); this.listenerSetter.add(questionField.input)('input', this.onInput);
const radioField = new RadioField({ const radioField = new RadioField({
text: '', text: '',
name: 'question' name: 'question'
}); });
radioField.main.append(questionField.container); radioField.main.append(questionField.container);
questionField.input.addEventListener('click', cancelEvent); attachClickEvent(questionField.input, cancelEvent, {listenerSetter: this.listenerSetter});
radioField.label.classList.add('hidden-widget'); radioField.label.classList.add('hidden-widget');
radioField.input.disabled = true; radioField.input.disabled = true;
if(!this.quizCheckboxField.input.checked) { if(!this.quizCheckboxField.input.checked) {
radioField.label.classList.remove('radio-field'); radioField.label.classList.remove('radio-field');
} }
radioField.input.addEventListener('change', () => { this.listenerSetter.add(radioField.input)('change', () => {
const checked = radioField.input.checked; const checked = radioField.input.checked;
if(checked) { if(checked) {
const idx = whichChild(radioField.label); const idx = whichChild(radioField.label);
@ -376,7 +376,7 @@ export default class PopupCreatePoll extends PopupElement {
deleteBtn.classList.add('btn-icon', 'tgico-close'); deleteBtn.classList.add('btn-icon', 'tgico-close');
questionField.container.append(deleteBtn); questionField.container.append(deleteBtn);
deleteBtn.addEventListener('click', this.onDeleteClick, {once: true}); attachClickEvent(deleteBtn, this.onDeleteClick, {listenerSetter: this.listenerSetter, once: true});
this.questions.append(radioField.label); this.questions.append(radioField.label);

13
src/components/popups/datePicker.ts

@ -5,6 +5,7 @@
*/ */
import PopupElement, { PopupOptions } from "."; import PopupElement, { PopupOptions } from ".";
import { attachClickEvent } from "../../helpers/dom/clickEvent";
import mediaSizes from "../../helpers/mediaSizes"; import mediaSizes from "../../helpers/mediaSizes";
import I18n, { i18n, LangPackKey } from "../../lib/langPack"; import I18n, { i18n, LangPackKey } from "../../lib/langPack";
import InputField from "../inputField"; import InputField from "../inputField";
@ -62,11 +63,11 @@ export default class PopupDatePicker extends PopupElement {
this.prevBtn = document.createElement('button'); this.prevBtn = document.createElement('button');
this.prevBtn.classList.add('btn-icon', 'tgico-down', 'date-picker-prev'); this.prevBtn.classList.add('btn-icon', 'tgico-down', 'date-picker-prev');
this.prevBtn.addEventListener('click', this.onPrevClick); attachClickEvent(this.prevBtn, this.onPrevClick, {listenerSetter: this.listenerSetter});
this.nextBtn = document.createElement('button'); this.nextBtn = document.createElement('button');
this.nextBtn.classList.add('btn-icon', 'tgico-down', 'date-picker-next'); this.nextBtn.classList.add('btn-icon', 'tgico-down', 'date-picker-next');
this.nextBtn.addEventListener('click', this.onNextClick); attachClickEvent(this.nextBtn, this.onNextClick, {listenerSetter: this.listenerSetter});
this.monthTitle = document.createElement('div'); this.monthTitle = document.createElement('div');
this.monthTitle.classList.add('date-picker-month-title'); this.monthTitle.classList.add('date-picker-month-title');
@ -76,7 +77,7 @@ export default class PopupDatePicker extends PopupElement {
// Month // Month
this.monthsContainer = document.createElement('div'); this.monthsContainer = document.createElement('div');
this.monthsContainer.classList.add('date-picker-months'); this.monthsContainer.classList.add('date-picker-months');
this.monthsContainer.addEventListener('click', this.onDateClick); attachClickEvent(this.monthsContainer, this.onDateClick, {listenerSetter: this.listenerSetter});
this.body.append(this.controlsDiv, this.monthsContainer); this.body.append(this.controlsDiv, this.monthsContainer);
@ -91,7 +92,7 @@ export default class PopupDatePicker extends PopupElement {
const handleTimeInput = (max: number, inputField: InputField, onInput: (length: number) => void, onOverflow?: (number: number) => void) => { const handleTimeInput = (max: number, inputField: InputField, onInput: (length: number) => void, onOverflow?: (number: number) => void) => {
const maxString = '' + max; const maxString = '' + max;
inputField.input.addEventListener('input', (e) => { this.listenerSetter.add(inputField.input)('input', (e) => {
let value = inputField.value.replace(/\D/g, ''); let value = inputField.value.replace(/\D/g, '');
if(value.length > 2) { if(value.length > 2) {
value = value.slice(0, 2); value = value.slice(0, 2);
@ -141,14 +142,14 @@ export default class PopupDatePicker extends PopupElement {
this.timeDiv.append(this.hoursInputField.container, delimiter, this.minutesInputField.container); this.timeDiv.append(this.hoursInputField.container, delimiter, this.minutesInputField.container);
this.btnConfirm.addEventListener('click', () => { attachClickEvent(this.btnClose, () => {
if(this.onPick) { if(this.onPick) {
this.selectedDate.setHours(+this.hoursInputField.value || 0, +this.minutesInputField.value || 0, 0, 0); this.selectedDate.setHours(+this.hoursInputField.value || 0, +this.minutesInputField.value || 0, 0, 0);
this.onPick(this.selectedDate.getTime() / 1000 | 0); this.onPick(this.selectedDate.getTime() / 1000 | 0);
} }
this.hide(); this.hide();
}, {once: true}); }, {listenerSetter: this.listenerSetter});
this.body.append(this.timeDiv); this.body.append(this.timeDiv);

52
src/components/popups/deleteDialog.ts

@ -12,7 +12,12 @@ import PeerTitle from "../peerTitle";
import PopupPeer, { PopupPeerButtonCallbackCheckboxes, PopupPeerOptions } from "./peer"; import PopupPeer, { PopupPeerButtonCallbackCheckboxes, PopupPeerOptions } from "./peer";
export default class PopupDeleteDialog { export default class PopupDeleteDialog {
constructor(peerId: number, peerType: PeerType = appPeersManager.getDialogType(peerId), onSelect?: (promise: Promise<any>) => void) { constructor(
peerId: number,
// actionType: 'leave' | 'delete',
peerType: PeerType = appPeersManager.getDialogType(peerId),
onSelect?: (promise: Promise<any>) => void
) {
const peerTitleElement = new PeerTitle({peerId}).element; const peerTitleElement = new PeerTitle({peerId}).element;
/* const callbackFlush = (checked: PopupPeerButtonCallbackCheckboxes) => { /* const callbackFlush = (checked: PopupPeerButtonCallbackCheckboxes) => {
@ -51,14 +56,29 @@ export default class PopupDeleteDialog {
let title: LangPackKey, description: LangPackKey, descriptionArgs: any[], buttons: PopupPeerOptions['buttons'], checkboxes: PopupPeerOptions['checkboxes']; let title: LangPackKey, description: LangPackKey, descriptionArgs: any[], buttons: PopupPeerOptions['buttons'], checkboxes: PopupPeerOptions['checkboxes'];
switch(peerType) { switch(peerType) {
case 'channel': { case 'channel': {
title = 'LeaveChannelMenu'; if(/* actionType === 'delete' && */appChatsManager.hasRights(-peerId, 'delete_chat')) {
description = 'ChannelLeaveAlertWithName'; appChatsManager.deleteChannel
descriptionArgs = [peerTitleElement]; title = 'ChannelDeleteMenu';
buttons = [{ description = 'AreYouSureDeleteAndExitChannel';
langKey: 'LeaveChannel', buttons = [{
isDanger: true, langKey: 'ChannelDeleteMenu',
callback: callbackLeave isDanger: true,
}]; callback: callbackDelete
}];
checkboxes = [{
text: 'DeleteChannelForAll'
}];
} else {
title = 'LeaveChannelMenu';
description = 'ChannelLeaveAlertWithName';
descriptionArgs = [peerTitleElement];
buttons = [{
langKey: 'LeaveChannel',
isDanger: true,
callback: callbackLeave
}];
}
break; break;
} }
@ -80,6 +100,12 @@ export default class PopupDeleteDialog {
description = 'AreYouSureDeleteThisChatWithUser'; description = 'AreYouSureDeleteThisChatWithUser';
descriptionArgs = [peerTitleElement]; descriptionArgs = [peerTitleElement];
buttons = [{
langKey: 'DeleteChatUser',
isDanger: true,
callback: callbackDelete
}];
checkboxes = [{ checkboxes = [{
text: 'DeleteMessagesOptionAlso', text: 'DeleteMessagesOptionAlso',
textArgs: [ textArgs: [
@ -87,12 +113,6 @@ export default class PopupDeleteDialog {
] ]
}]; }];
buttons = [{
langKey: 'DeleteChatUser',
isDanger: true,
callback: callbackDelete
}];
break; break;
} }
@ -110,7 +130,7 @@ export default class PopupDeleteDialog {
case 'megagroup': case 'megagroup':
case 'group': { case 'group': {
if(appChatsManager.hasRights(-peerId, 'delete_chat')) { if(/* actionType === 'delete' && */appChatsManager.hasRights(-peerId, 'delete_chat')) {
title = 'DeleteMegaMenu'; title = 'DeleteMegaMenu';
description = 'AreYouSureDeleteAndExit'; description = 'AreYouSureDeleteAndExit';
buttons = [{ buttons = [{

67
src/components/popups/index.ts

@ -12,6 +12,9 @@ import { i18n, LangPackKey } from "../../lib/langPack";
import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpClassName from "../../helpers/dom/findUpClassName";
import blurActiveElement from "../../helpers/dom/blurActiveElement"; import blurActiveElement from "../../helpers/dom/blurActiveElement";
import ListenerSetter from "../../helpers/listenerSetter"; import ListenerSetter from "../../helpers/listenerSetter";
import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEvent";
import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed";
import { cancelEvent } from "../../helpers/dom/cancelEvent";
export type PopupButton = { export type PopupButton = {
text?: string, text?: string,
@ -19,14 +22,16 @@ export type PopupButton = {
langKey?: LangPackKey, langKey?: LangPackKey,
langArgs?: any[], langArgs?: any[],
isDanger?: true, isDanger?: true,
isCancel?: true isCancel?: true,
element?: HTMLButtonElement
}; };
export type PopupOptions = Partial<{ export type PopupOptions = Partial<{
closable: true, closable: true,
overlayClosable: true, overlayClosable: true,
withConfirm: LangPackKey | true, withConfirm: LangPackKey | true,
body: true body: true,
confirmShortcutIsSendShortcut: boolean
}>; }>;
export default class PopupElement { export default class PopupElement {
@ -47,6 +52,9 @@ export default class PopupElement {
protected listenerSetter: ListenerSetter; protected listenerSetter: ListenerSetter;
protected confirmShortcutIsSendShortcut: boolean;
protected btnConfirmOnEnter: HTMLButtonElement;
constructor(className: string, buttons?: Array<PopupButton>, options: PopupOptions = {}) { constructor(className: string, buttons?: Array<PopupButton>, options: PopupOptions = {}) {
this.element.classList.add('popup'); this.element.classList.add('popup');
this.element.className = 'popup' + (className ? ' ' + className : ''); this.element.className = 'popup' + (className ? ' ' + className : '');
@ -59,24 +67,23 @@ export default class PopupElement {
this.listenerSetter = new ListenerSetter(); this.listenerSetter = new ListenerSetter();
this.confirmShortcutIsSendShortcut = options.confirmShortcutIsSendShortcut;
if(options.closable) { if(options.closable) {
this.btnClose = document.createElement('span'); this.btnClose = document.createElement('span');
this.btnClose.classList.add('btn-icon', 'popup-close', 'tgico-close'); this.btnClose.classList.add('btn-icon', 'popup-close', 'tgico-close');
//ripple(this.closeBtn); //ripple(this.closeBtn);
this.header.prepend(this.btnClose); this.header.prepend(this.btnClose);
this.btnClose.addEventListener('click', this.hide, {once: true}); attachClickEvent(this.btnClose, this.hide, {listenerSetter: this.listenerSetter, once: true});
} }
if(options.overlayClosable) { if(options.overlayClosable) {
const onOverlayClick = (e: MouseEvent) => { attachClickEvent(this.element, (e: MouseEvent) => {
if(!findUpClassName(e.target, 'popup-container')) { if(!findUpClassName(e.target, 'popup-container')) {
this.hide(); this.hide();
this.element.removeEventListener('click', onOverlayClick);
} }
}; }, {listenerSetter: this.listenerSetter});
this.element.addEventListener('click', onOverlayClick);
} }
if(options.withConfirm) { if(options.withConfirm) {
@ -96,6 +103,7 @@ export default class PopupElement {
this.container.append(this.body); this.container.append(this.body);
} }
let btnConfirmOnEnter = this.btnConfirm;
if(buttons && buttons.length) { if(buttons && buttons.length) {
const buttonsDiv = this.buttons = document.createElement('div'); const buttonsDiv = this.buttons = document.createElement('div');
buttonsDiv.classList.add('popup-buttons'); buttonsDiv.classList.add('popup-buttons');
@ -103,11 +111,11 @@ export default class PopupElement {
if(buttons.length === 2) { if(buttons.length === 2) {
buttonsDiv.classList.add('popup-buttons-row'); buttonsDiv.classList.add('popup-buttons-row');
} }
const buttonsElements = buttons.map(b => { const buttonsElements = buttons.map(b => {
const button = document.createElement('button'); const button = document.createElement('button');
button.className = 'btn' + (b.isDanger ? ' danger' : ' primary'); button.className = 'btn' + (b.isDanger ? ' danger' : ' primary');
ripple(button); ripple(button);
if(b.text) { if(b.text) {
@ -115,25 +123,28 @@ export default class PopupElement {
} else { } else {
button.append(i18n(b.langKey, b.langArgs)); button.append(i18n(b.langKey, b.langArgs));
} }
if(b.callback) { attachClickEvent(button, () => {
button.addEventListener('click', () => { b.callback && b.callback();
b.callback(); this.destroy();
this.destroy(); }, {listenerSetter: this.listenerSetter, once: true});
}, {once: true});
} else if(b.isCancel) { return b.element = button;
button.addEventListener('click', () => {
this.destroy();
}, {once: true});
}
return button;
}); });
if(!btnConfirmOnEnter && buttons.length === 2) {
const button = buttons.find(button => !button.isCancel);
if(button) {
btnConfirmOnEnter = button.element;
}
}
buttonsDiv.append(...buttonsElements); buttonsDiv.append(...buttonsElements);
this.container.append(buttonsDiv); this.container.append(buttonsDiv);
} }
this.btnConfirmOnEnter = btnConfirmOnEnter;
this.element.append(this.container); this.element.append(this.container);
} }
@ -152,6 +163,13 @@ export default class PopupElement {
this.element.classList.add('active'); this.element.classList.add('active');
rootScope.isOverlayActive = true; rootScope.isOverlayActive = true;
animationIntersector.checkAnimations(true); animationIntersector.checkAnimations(true);
this.listenerSetter.add(document.body)('keydown', (e) => {
if(this.confirmShortcutIsSendShortcut ? isSendShortcutPressed(e) : e.key === 'Enter') {
simulateClickEvent(this.btnConfirmOnEnter);
cancelEvent(e);
}
});
} }
public hide = () => { public hide = () => {
@ -164,7 +182,6 @@ export default class PopupElement {
this.element.classList.remove('active'); this.element.classList.remove('active');
this.listenerSetter.removeAll(); this.listenerSetter.removeAll();
if(this.btnClose) this.btnClose.removeEventListener('click', this.hide);
rootScope.isOverlayActive = false; rootScope.isOverlayActive = false;
appNavigationController.removeItem(this.navigationItem); appNavigationController.removeItem(this.navigationItem);

17
src/components/popups/newMedia.ts

@ -17,11 +17,11 @@ import { MyDocument } from "../../lib/appManagers/appDocsManager";
import I18n, { i18n, LangPackKey } from "../../lib/langPack"; import I18n, { i18n, LangPackKey } from "../../lib/langPack";
import appDownloadManager from "../../lib/appManagers/appDownloadManager"; import appDownloadManager from "../../lib/appManagers/appDownloadManager";
import calcImageInBox from "../../helpers/calcImageInBox"; import calcImageInBox from "../../helpers/calcImageInBox";
import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed";
import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd"; import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd";
import rootScope from "../../lib/rootScope"; import rootScope from "../../lib/rootScope";
import RichTextProcessor from "../../lib/richtextprocessor"; import RichTextProcessor from "../../lib/richtextprocessor";
import { MediaSize } from "../../helpers/mediaSizes"; import { MediaSize } from "../../helpers/mediaSizes";
import { attachClickEvent } from "../../helpers/dom/clickEvent";
type SendFileParams = Partial<{ type SendFileParams = Partial<{
file: File, file: File,
@ -57,11 +57,11 @@ export default class PopupNewMedia extends PopupElement {
private inputField: InputField; private inputField: InputField;
constructor(private chat: Chat, files: File[], willAttachType: PopupNewMedia['willAttach']['type']) { constructor(private chat: Chat, files: File[], willAttachType: PopupNewMedia['willAttach']['type']) {
super('popup-send-photo popup-new-media', null, {closable: true, withConfirm: 'Modal.Send'}); super('popup-send-photo popup-new-media', null, {closable: true, withConfirm: 'Modal.Send', confirmShortcutIsSendShortcut: true});
this.willAttach.type = willAttachType; this.willAttach.type = willAttachType;
this.btnConfirm.addEventListener('click', () => this.send()); attachClickEvent(this.btnConfirm, () => this.send(), {listenerSetter: this.listenerSetter});
if(this.chat.type !== 'scheduled') { if(this.chat.type !== 'scheduled') {
const sendMenu = new SendContextMenu({ const sendMenu = new SendContextMenu({
@ -76,6 +76,7 @@ export default class PopupNewMedia extends PopupElement {
}, },
openSide: 'bottom-left', openSide: 'bottom-left',
onContextElement: this.btnConfirm, onContextElement: this.btnConfirm,
listenerSetter: this.listenerSetter
}); });
sendMenu.setPeerId(this.chat.peerId); sendMenu.setPeerId(this.chat.peerId);
@ -112,7 +113,7 @@ export default class PopupNewMedia extends PopupElement {
this.groupCheckboxField.input.checked = true; this.groupCheckboxField.input.checked = true;
this.willAttach.group = true; this.willAttach.group = true;
this.groupCheckboxField.input.addEventListener('change', () => { this.listenerSetter.add(this.groupCheckboxField.input)('change', () => {
const checked = this.groupCheckboxField.input.checked; const checked = this.groupCheckboxField.input.checked;
this.willAttach.group = checked; this.willAttach.group = checked;
@ -139,10 +140,6 @@ export default class PopupNewMedia extends PopupElement {
this.input.focus(); this.input.focus();
placeCaretAtEnd(this.input); placeCaretAtEnd(this.input);
} }
if(isSendShortcutPressed(e)) {
this.btnConfirm.click();
}
}; };
public send(force = false) { public send(force = false) {
@ -455,13 +452,11 @@ export default class PopupNewMedia extends PopupElement {
// show now // show now
if(!this.element.classList.contains('active')) { if(!this.element.classList.contains('active')) {
document.body.addEventListener('keydown', this.onKeyDown); this.listenerSetter.add(document.body)('keydown', this.onKeyDown);
this.onClose = () => { this.onClose = () => {
if(this.wasInputValue) { if(this.wasInputValue) {
this.chat.input.messageInputField.value = this.wasInputValue; this.chat.input.messageInputField.value = this.wasInputValue;
} }
document.body.removeEventListener('keydown', this.onKeyDown);
}; };
this.show(); this.show();
} }

5
src/components/popups/reportMessages.ts

@ -4,6 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import { attachClickEvent } from "../../helpers/dom/clickEvent";
import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpClassName from "../../helpers/dom/findUpClassName";
import whichChild from "../../helpers/dom/whichChild"; import whichChild from "../../helpers/dom/whichChild";
import { ReportReason } from "../../layer"; import { ReportReason } from "../../layer";
@ -35,7 +36,7 @@ export default class PopupReportMessages extends PopupPeer {
const preloadStickerPromise = appStickersManager.preloadAnimatedEmojiSticker(PopupReportMessagesConfirm.STICKER_EMOJI); const preloadStickerPromise = appStickersManager.preloadAnimatedEmojiSticker(PopupReportMessagesConfirm.STICKER_EMOJI);
this.body.addEventListener('click', (e) => { attachClickEvent(this.body, (e) => {
const target = findUpClassName(e.target, 'btn-primary'); const target = findUpClassName(e.target, 'btn-primary');
const reason = buttons[whichChild(target)][1]; const reason = buttons[whichChild(target)][1];
@ -44,7 +45,7 @@ export default class PopupReportMessages extends PopupPeer {
new PopupReportMessagesConfirm(peerId, mids, reason, onConfirm); new PopupReportMessagesConfirm(peerId, mids, reason, onConfirm);
}); });
}); }, {listenerSetter: this.listenerSetter});
this.body.style.margin = '0 -1rem'; this.body.style.margin = '0 -1rem';
this.buttons.style.marginTop = '.5rem'; this.buttons.style.marginTop = '.5rem';

3
src/components/popups/schedule.ts

@ -37,7 +37,8 @@ export default class PopupSchedule extends PopupDatePicker {
minDate: getMinDate(), minDate: getMinDate(),
maxDate: getMaxDate(), maxDate: getMaxDate(),
withTime: true, withTime: true,
showOverflowMonths: true showOverflowMonths: true,
confirmShortcutIsSendShortcut: true
}); });
this.element.classList.add('popup-schedule'); this.element.classList.add('popup-schedule');

29
src/components/popups/stickers.ts

@ -19,6 +19,7 @@ import { i18n } from "../../lib/langPack";
import Button from "../button"; import Button from "../button";
import findUpClassName from "../../helpers/dom/findUpClassName"; import findUpClassName from "../../helpers/dom/findUpClassName";
import toggleDisability from "../../helpers/dom/toggleDisability"; import toggleDisability from "../../helpers/dom/toggleDisability";
import { attachClickEvent } from "../../helpers/dom/clickEvent";
const ANIMATION_GROUP = 'STICKERS-POPUP'; const ANIMATION_GROUP = 'STICKERS-POPUP';
@ -39,8 +40,6 @@ export default class PopupStickers extends PopupElement {
this.onClose = () => { this.onClose = () => {
animationIntersector.setOnlyOnePlayableGroup(''); animationIntersector.setOnlyOnePlayableGroup('');
this.stickersFooter.removeEventListener('click', this.onFooterClick);
this.stickersDiv.removeEventListener('click', this.onStickersClick);
}; };
const div = document.createElement('div'); const div = document.createElement('div');
@ -49,6 +48,8 @@ export default class PopupStickers extends PopupElement {
this.stickersDiv = document.createElement('div'); this.stickersDiv = document.createElement('div');
this.stickersDiv.classList.add('sticker-set-stickers', 'is-loading'); this.stickersDiv.classList.add('sticker-set-stickers', 'is-loading');
attachClickEvent(this.stickersDiv, this.onStickersClick, {listenerSetter: this.listenerSetter});
putPreloader(this.stickersDiv, true); putPreloader(this.stickersDiv, true);
this.stickersFooter = document.createElement('div'); this.stickersFooter = document.createElement('div');
@ -71,17 +72,7 @@ export default class PopupStickers extends PopupElement {
this.loadStickerSet(); this.loadStickerSet();
} }
onFooterClick = () => { private onStickersClick = (e: MouseEvent) => {
const toggle = toggleDisability([this.stickersFooter], true);
appStickersManager.toggleStickerSet(this.set).then(() => {
this.hide();
}).catch(() => {
toggle();
});
};
onStickersClick = (e: MouseEvent) => {
const target = findUpClassName(e.target, 'sticker-set-sticker'); const target = findUpClassName(e.target, 'sticker-set-sticker');
if(!target) return; if(!target) return;
@ -116,11 +107,15 @@ export default class PopupStickers extends PopupElement {
this.stickersFooter.textContent = ''; this.stickersFooter.textContent = '';
this.stickersFooter.append(button); this.stickersFooter.append(button);
button.addEventListener('click', this.onFooterClick); attachClickEvent(button, () => {
const toggle = toggleDisability([button], true);
if(set.documents.length) { appStickersManager.toggleStickerSet(this.set).then(() => {
this.stickersDiv.addEventListener('click', this.onStickersClick); this.hide();
} }).catch(() => {
toggle();
});
});
const lazyLoadQueue = new LazyLoadQueue(); const lazyLoadQueue = new LazyLoadQueue();

4
src/components/sidebarLeft/tabs/newChannel.ts

@ -35,12 +35,12 @@ export default class AppNewChannelTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
this.channelNameInputField = new InputField({ this.channelNameInputField = new InputField({
label: 'Channel.ChannelNameHolder', label: 'EnterChannelName',
maxLength: 128 maxLength: 128
}); });
this.channelDescriptionInputField = new InputField({ this.channelDescriptionInputField = new InputField({
label: 'Channel.DescriptionPlaceholder', label: 'DescriptionOptionalPlaceholder',
maxLength: 255 maxLength: 255
}); });

47
src/components/sidebarRight/tabs/editChat.ts

@ -18,7 +18,6 @@ import rootScope from "../../../lib/rootScope";
import AppGroupPermissionsTab from "./groupPermissions"; import AppGroupPermissionsTab from "./groupPermissions";
import { i18n, LangPackKey } from "../../../lib/langPack"; import { i18n, LangPackKey } from "../../../lib/langPack";
import PopupDeleteDialog from "../../popups/deleteDialog"; import PopupDeleteDialog from "../../popups/deleteDialog";
import { addCancelButton } from "../../popups";
import PopupPeer from "../../popups/peer"; import PopupPeer from "../../popups/peer";
import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import toggleDisability from "../../../helpers/dom/toggleDisability"; import toggleDisability from "../../../helpers/dom/toggleDisability";
@ -63,7 +62,7 @@ export default class AppEditChatTab extends SliderSuperTab {
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
this.chatNameInputField = new InputField({ this.chatNameInputField = new InputField({
label: isBroadcast ? 'Channel.ChannelNameHolder' : 'CreateGroup.NameHolder', label: isBroadcast ? 'EnterChannelName' : 'CreateGroup.NameHolder',
name: 'chat-name', name: 'chat-name',
maxLength: 255, maxLength: 255,
required: true required: true
@ -281,45 +280,17 @@ export default class AppEditChatTab extends SliderSuperTab {
if(appChatsManager.hasRights(this.chatId, 'delete_chat')) { if(appChatsManager.hasRights(this.chatId, 'delete_chat')) {
const section = new SettingSection({}); const section = new SettingSection({});
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: isBroadcast ? 'PeerInfo.DeleteChannel' : 'DeleteMega'}); const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: isBroadcast ? 'PeerInfo.DeleteChannel' : 'DeleteAndExitButton'});
attachClickEvent(btnDelete, () => { attachClickEvent(btnDelete, () => {
if(isBroadcast) { new PopupDeleteDialog(-this.chatId/* , 'delete' */, undefined, (promise) => {
new PopupPeer('popup-delete-channel', { const toggle = toggleDisability([btnDelete], true);
peerId: -this.chatId, promise.then(() => {
titleLangKey: 'ChannelDeleteMenu', this.close();
descriptionLangKey: 'AreYouSureDeleteAndExitChannel', }, () => {
buttons: addCancelButton([{ toggle();
langKey: 'ChannelDeleteMenu',
callback: () => {
const toggle = toggleDisability([btnDelete], true);
},
isDanger: true
}, {
langKey: 'DeleteChannelForAll',
callback: () => {
const toggle = toggleDisability([btnDelete], true);
appChatsManager.deleteChannel(this.chatId).then(() => {
this.close();
}, () => {
toggle();
});
},
isDanger: true
}])
}).show();
} else {
new PopupDeleteDialog(-this.chatId, undefined, (promise) => {
const toggle = toggleDisability([btnDelete], true);
promise.then(() => {
this.close();
}, () => {
toggle();
});
}); });
} });
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});
section.content.append(btnDelete); section.content.append(btnDelete);

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

@ -810,7 +810,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
private searchSuper: AppSearchSuper; private searchSuper: AppSearchSuper;
private profile: PeerProfile; private profile: PeerProfile;
peerChanged: boolean; private peerChanged: boolean;
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
super(slider, false); super(slider, false);
@ -911,8 +911,14 @@ export default class AppSharedMediaTab extends SliderSuperTab {
}); });
rootScope.addEventListener('contacts_update', (userId) => { rootScope.addEventListener('contacts_update', (userId) => {
if(this.peerId === userId && rootScope.myId !== userId) { if(this.peerId === userId) {
this.editBtn.classList.toggle('hide', !appUsersManager.isContact(userId)); this.toggleEditBtn();
}
});
rootScope.addEventListener('chat_update', (chatId) => {
if(this.peerId === -chatId) {
this.toggleEditBtn();
} }
}); });
@ -1175,16 +1181,18 @@ export default class AppSharedMediaTab extends SliderSuperTab {
this.profile.fillProfileElements(); this.profile.fillProfileElements();
this.toggleEditBtn();
}
private toggleEditBtn() {
let show: boolean;
if(this.peerId > 0) { if(this.peerId > 0) {
if(this.peerId !== rootScope.myId && appUsersManager.isContact(this.peerId)) { show = this.peerId !== rootScope.myId && appUsersManager.isContact(this.peerId);
this.editBtn.classList.remove('hide');
}
} else { } else {
const chat: Chat = appChatsManager.getChat(-this.peerId); show = appChatsManager.hasRights(-this.peerId, 'change_info');
if((chat._ === 'chat' || (chat as Chat.channel).admin_rights) && !(chat as Chat.chat).pFlags.deactivated) {
this.editBtn.classList.remove('hide');
}
} }
this.editBtn.classList.toggle('hide', !show);
} }
public loadSidebarMedia(single: boolean, justLoad = false) { public loadSidebarMedia(single: boolean, justLoad = false) {

6
src/lang.ts

@ -175,7 +175,8 @@ const lang = {
"Bot": "bot", "Bot": "bot",
//"ChannelJoined": "You joined this channel", //"ChannelJoined": "You joined this channel",
"ChannelMegaJoined": "You joined this group", "ChannelMegaJoined": "You joined this group",
"Channel.DescriptionPlaceholder": "Description (optional)", "EnterChannelName": "Channel name",
"DescriptionOptionalPlaceholder": "Description (optional)",
"DescriptionPlaceholder": "Description", "DescriptionPlaceholder": "Description",
"DiscussionStarted": "Discussion started", "DiscussionStarted": "Discussion started",
"Draft": "Draft", "Draft": "Draft",
@ -356,6 +357,8 @@ const lang = {
"GroupMembers": "Members", "GroupMembers": "Members",
"DeleteMega": "Delete Group", "DeleteMega": "Delete Group",
"DeleteMegaMenu": "Delete group", "DeleteMegaMenu": "Delete group",
"DeleteAndExitButton": "Delete and Leave Group",
"ChannelDelete": "Delete Channel",
"ChannelDeleteMenu": "Delete channel", "ChannelDeleteMenu": "Delete channel",
"ChannelPermissions": "Permissions", "ChannelPermissions": "Permissions",
"ChannelPermissionsHeader": "What can members of this group do?", "ChannelPermissionsHeader": "What can members of this group do?",
@ -666,7 +669,6 @@ const lang = {
"ChatList.Filter.Archive": "Archived", "ChatList.Filter.Archive": "Archived",
"ChatList.Filter.Include.LimitReached": "Sorry, you can only add up to 100 individual chats. Try using chat types.", "ChatList.Filter.Include.LimitReached": "Sorry, you can only add up to 100 individual chats. Try using chat types.",
"ChatList.Filter.Exclude.LimitReached": "Sorry, you can only add up to 100 individual chats. Try using chat types.", "ChatList.Filter.Exclude.LimitReached": "Sorry, you can only add up to 100 individual chats. Try using chat types.",
"Channel.ChannelNameHolder": "Channel Name",
"Channel.DescriptionHolderDescrpiton": "You can provide an optional description for your channel.", "Channel.DescriptionHolderDescrpiton": "You can provide an optional description for your channel.",
"CreateGroup.NameHolder": "Group Name", "CreateGroup.NameHolder": "Group Name",
"Date.Today": "Today", "Date.Today": "Today",

17
src/lib/appManagers/appChatsManager.ts

@ -206,25 +206,26 @@ export class AppChatsManager {
return rights; return rights;
} }
// * creator can still send messages to left channel. so this function shows server rights. see canSendToPeer for local rights in messages manager.
public hasRights(id: number, action: ChatRights, rights?: ChatAdminRights | ChatBannedRights, isThread?: boolean) { 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;
if(chat._ === 'chatForbidden' ||
chat._ === 'channelForbidden' ||
(chat as Chat.chat).pFlags.kicked ||
(chat.pFlags.left && !(chat as Chat.channel).pFlags.megagroup)) {
return false;
}
if((chat as Chat.chat).pFlags.deactivated && action !== 'view_messages') { if((chat as Chat.chat).pFlags.deactivated && action !== 'view_messages') {
return false; return false;
} }
if(chat.pFlags.creator && rights === undefined) { if((chat as Chat.chat).pFlags.creator && rights === undefined) {
return true; return true;
} }
if(chat._ === 'chatForbidden' ||
chat._ === 'channelForbidden' ||
(chat as Chat.chat).pFlags.kicked ||
(chat.pFlags.left && !(chat as Chat.channel).pFlags.megagroup)) {
return false;
}
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;

2
src/lib/appManagers/appImManager.ts

@ -998,7 +998,7 @@ export class AppImManager {
private canDrag() { private canDrag() {
const peerId = this.chat?.peerId; const peerId = this.chat?.peerId;
return !(!peerId || rootScope.isOverlayActive || (peerId < 0 && !appChatsManager.hasRights(peerId, 'send_media'))); return !(!peerId || rootScope.isOverlayActive || !appMessagesManager.canSendToPeer(peerId, this.chat.threadId, 'send_media'));
} }
private onDocumentPaste = (e: ClipboardEvent | DragEvent, attachType?: 'media' | 'document') => { private onDocumentPaste = (e: ClipboardEvent | DragEvent, attachType?: 'media' | 'document') => {

13
src/lib/appManagers/appMessagesManager.ts

@ -32,7 +32,7 @@ import DialogsStorage from "../storages/dialogs";
import FiltersStorage from "../storages/filters"; import FiltersStorage from "../storages/filters";
//import { telegramMeWebService } from "../mtproto/mtproto"; //import { telegramMeWebService } from "../mtproto/mtproto";
import apiUpdatesManager from "./apiUpdatesManager"; import apiUpdatesManager from "./apiUpdatesManager";
import appChatsManager from "./appChatsManager"; import appChatsManager, { ChatRights } from "./appChatsManager";
import appDocsManager, { MyDocument } from "./appDocsManager"; import appDocsManager, { MyDocument } from "./appDocsManager";
import appDownloadManager from "./appDownloadManager"; import appDownloadManager from "./appDownloadManager";
import appPeersManager from "./appPeersManager"; import appPeersManager from "./appPeersManager";
@ -4673,11 +4673,12 @@ export class AppMessagesManager {
}, settings); }, settings);
} }
public canWriteToPeer(peerId: number, threadId?: number) { public canSendToPeer(peerId: number, threadId?: number, action: ChatRights = 'send_messages') {
if(peerId < 0) { if(peerId < 0) {
//const isChannel = appPeersManager.isChannel(peerId); //const isChannel = appPeersManager.isChannel(peerId);
const hasRights = /* isChannel && */appChatsManager.hasRights(-peerId, 'send_messages', undefined, !!threadId); const chat: Chat.chat = appChatsManager.getChat(-peerId);
return /* !isChannel || */hasRights; const hasRights = /* isChannel && */appChatsManager.hasRights(-peerId, action, undefined, !!threadId);
return /* !isChannel || */hasRights && (!chat.pFlags.left || !!threadId);
} else { } else {
return appUsersManager.canSendToUser(peerId); return appUsersManager.canSendToUser(peerId);
} }
@ -4875,7 +4876,7 @@ export class AppMessagesManager {
} }
public getScheduledMessages(peerId: number): Promise<number[]> { public getScheduledMessages(peerId: number): Promise<number[]> {
if(!this.canWriteToPeer(peerId)) return Promise.resolve([]); if(!this.canSendToPeer(peerId)) return Promise.resolve([]);
const storage = this.getScheduledMessagesStorage(peerId); const storage = this.getScheduledMessagesStorage(peerId);
if(Object.keys(storage).length) { if(Object.keys(storage).length) {
@ -5299,7 +5300,7 @@ export class AppMessagesManager {
let typing = this.typings[peerId]; let typing = this.typings[peerId];
if(!rootScope.myId || if(!rootScope.myId ||
!peerId || !peerId ||
!this.canWriteToPeer(peerId) || !this.canSendToPeer(peerId) ||
peerId === rootScope.myId || peerId === rootScope.myId ||
typing?.type === action._ typing?.type === action._
) { ) {

6
src/lib/appManagers/appPeersManager.ts

@ -288,13 +288,11 @@ export class AppPeersManager {
public getDeleteButtonText(peerId: number): LangPackKey { public getDeleteButtonText(peerId: number): LangPackKey {
switch(this.getDialogType(peerId)) { switch(this.getDialogType(peerId)) {
case 'channel': case 'channel':
return 'ChatList.Context.LeaveChannel'; return appChatsManager.hasRights(-peerId, 'delete_chat') ? 'ChannelDelete' : 'ChatList.Context.LeaveChannel';
case 'megagroup': case 'megagroup':
return 'ChatList.Context.LeaveGroup';
case 'group': case 'group':
return 'ChatList.Context.DeleteAndExit'; return appChatsManager.hasRights(-peerId, 'delete_chat') ? 'DeleteMega' : 'ChatList.Context.LeaveGroup';
default: default:
return 'ChatList.Context.DeleteChat'; return 'ChatList.Context.DeleteChat';

12
src/lib/storages/dialogs.ts

@ -93,6 +93,18 @@ export default class DialogsStorage {
} }
}); });
rootScope.addEventListener('chat_update', (chatId) => {
const chat: Chat.chat = this.appChatsManager.getChat(chatId);
const peerId = -chatId;
if(chat.pFlags.left && this.getDialogOnly(peerId)) {
const dropped = this.dropDialog(peerId);
if(dropped.length) {
rootScope.dispatchEvent('dialog_drop', {peerId, dialog: dropped[0]});
}
}
});
rootScope.addMultipleEventsListeners({ rootScope.addMultipleEventsListeners({
updateFolderPeers: this.onUpdateFolderPeers, updateFolderPeers: this.onUpdateFolderPeers,

15
src/scss/style.scss

@ -706,7 +706,20 @@ hr {
max-height: 100%; max-height: 100%;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: var(--primary-color); background: linear-gradient(var(--avatar-color-top), var(--avatar-color-bottom));
}
html.no-touch body.animation-level-2 & {
.tgico-cameraadd {
transform: translateY(-50%) translateX(-50%) scale(1);
transition: transform .2s ease-in-out;
}
&:hover {
.tgico-cameraadd {
transform: translateY(-50%) translateX(-50%) scale(1.2);
}
}
} }
.tgico-cameraadd { .tgico-cameraadd {

Loading…
Cancel
Save