Premium gifts

Reply timecodes
Fix stuck chat history
This commit is contained in:
Eduard Kuzmenko 2023-03-08 01:25:07 +04:00
parent 5b1097d7f9
commit c1f87d1b3e
65 changed files with 872 additions and 384 deletions

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Binary file not shown.

View File

@ -29,6 +29,7 @@ import PopupForward from './popups/forward';
import Scrollable from './scrollable'; import Scrollable from './scrollable';
import appSidebarRight from './sidebarRight'; import appSidebarRight from './sidebarRight';
import AppSharedMediaTab from './sidebarRight/tabs/sharedMedia'; import AppSharedMediaTab from './sidebarRight/tabs/sharedMedia';
import PopupElement from './popups';
type AppMediaViewerTargetType = { type AppMediaViewerTargetType = {
element: HTMLElement, element: HTMLElement,
@ -193,7 +194,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
onDeleteClick = () => { onDeleteClick = () => {
const target = this.target; const target = this.target;
new PopupDeleteMessages(target.peerId, [target.mid], 'chat', () => { PopupElement.createPopup(PopupDeleteMessages, target.peerId, [target.mid], 'chat', () => {
this.target = {element: this.content.media} as any; this.target = {element: this.content.media} as any;
this.close(); this.close();
}); });
@ -203,7 +204,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
const target = this.target; const target = this.target;
if(target.mid) { if(target.mid) {
// appSidebarRight.forwardTab.open([target.mid]); // appSidebarRight.forwardTab.open([target.mid]);
new PopupForward({ PopupElement.createPopup(PopupForward, {
[target.peerId]: [target.mid] [target.peerId]: [target.mid]
}, () => { }, () => {
return this.close(); return this.close();

View File

@ -1151,7 +1151,7 @@
// onForwardClick = () => { // onForwardClick = () => {
// if(this.currentMessageId) { // if(this.currentMessageId) {
// //appSidebarRight.forwardTab.open([this.currentMessageId]); // //appSidebarRight.forwardTab.open([this.currentMessageId]);
// new PopupForward(this.currentPeerId, [this.currentMessageId], () => { // PopupElement.createPopup(PopupForward(this.currentPeerId, [this.currentMessageId], , ) => {
// return this.close(); // return this.close();
// }); // });
// } // }

View File

@ -77,6 +77,7 @@ import noop from '../helpers/noop';
import wrapMediaSpoiler, {onMediaSpoilerClick} from './wrappers/mediaSpoiler'; import wrapMediaSpoiler, {onMediaSpoilerClick} from './wrappers/mediaSpoiler';
import filterAsync from '../helpers/array/filterAsync'; import filterAsync from '../helpers/array/filterAsync';
import ChatContextMenu from './chat/contextMenu'; import ChatContextMenu from './chat/contextMenu';
import PopupElement from './popups';
// const testScroll = false; // const testScroll = false;
@ -257,7 +258,7 @@ class SearchContextMenu {
if(this.searchSuper.selection.isSelecting) { if(this.searchSuper.selection.isSelecting) {
simulateClickEvent(this.searchSuper.selection.selectionForwardBtn); simulateClickEvent(this.searchSuper.selection.selectionForwardBtn);
} else { } else {
new PopupForward({ PopupElement.createPopup(PopupForward, {
[this.peerId]: [this.mid] [this.peerId]: [this.mid]
}); });
} }
@ -275,7 +276,7 @@ class SearchContextMenu {
if(this.searchSuper.selection.isSelecting) { if(this.searchSuper.selection.isSelecting) {
simulateClickEvent(this.searchSuper.selection.selectionDeleteBtn); simulateClickEvent(this.searchSuper.selection.selectionDeleteBtn);
} else { } else {
new PopupDeleteMessages(this.peerId, [this.mid], 'chat'); PopupElement.createPopup(PopupDeleteMessages, this.peerId, [this.mid], 'chat');
} }
}; };
} }

View File

@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import {i18n, LangPackKey} from '../lib/langPack'; import {FormatterArguments, i18n, LangPackKey} from '../lib/langPack';
import ripple from './ripple'; import ripple from './ripple';
export type ButtonOptions = Partial<{ export type ButtonOptions = Partial<{
@ -13,6 +13,7 @@ export type ButtonOptions = Partial<{
icon: string, icon: string,
rippleSquare: true, rippleSquare: true,
text: LangPackKey, text: LangPackKey,
textArgs?: FormatterArguments,
disabled: boolean, disabled: boolean,
asDiv: boolean, asDiv: boolean,
asLink: boolean asLink: boolean
@ -39,7 +40,7 @@ export default function Button<T extends ButtonOptions>(className: string, optio
} }
if(options.text) { if(options.text) {
button.append(i18n(options.text)); button.append(i18n(options.text, options.textArgs));
} }
return button as any; return button as any;

View File

@ -8,6 +8,7 @@ import Button from './button';
const ButtonCorner = (options: Partial<{className: string, icon: string, noRipple: true, onlyMobile: true, asDiv: boolean}> = {}) => { const ButtonCorner = (options: Partial<{className: string, icon: string, noRipple: true, onlyMobile: true, asDiv: boolean}> = {}) => {
const button = Button('btn-circle btn-corner z-depth-1' + (options.className ? ' ' + options.className : ''), options); const button = Button('btn-circle btn-corner z-depth-1' + (options.className ? ' ' + options.className : ''), options);
button.tabIndex = -1;
return button; return button;
}; };

View File

@ -785,13 +785,30 @@ export default class ChatBubbles {
const bubble = this.bubbles[mid]; const bubble = this.bubbles[mid];
if(!bubble) return; if(!bubble) return;
const message = (await this.chat.getMessage(mid)) as Message.message; const [message, originalMessage] = await Promise.all([
(await this.chat.getMessage(mid)) as Message.message,
(await this.managers.appMessagesManager.getMessageByPeer(replyToPeerId, replyMid)) as Message.message
]);
if(!middleware()) return;
MessageRender.setReply({ MessageRender.setReply({
chat: this.chat, chat: this.chat,
bubble, bubble,
message message
}); });
let maxMediaTimestamp: number;
const timestamps = bubble.querySelectorAll<HTMLAnchorElement>('.timestamp');
if(originalMessage && (maxMediaTimestamp = getMediaDurationFromMessage(originalMessage))) {
timestamps.forEach((timestamp) => {
const value = +timestamp.dataset.timestamp;
if(value < maxMediaTimestamp) {
timestamp.classList.remove('is-disabled');
} else {
timestamp.removeAttribute('href');
}
});
}
}); });
}); });
}); });
@ -1787,7 +1804,8 @@ export default class ChatBubbles {
return; return;
} }
new PopupPayment( PopupElement.createPopup(
PopupPayment,
message as Message.message, message as Message.message,
await this.managers.appPaymentsManager.getInputInvoiceByPeerId(message.peerId, message.mid) await this.managers.appPaymentsManager.getInputInvoiceByPeerId(message.peerId, message.mid)
); );
@ -1878,7 +1896,7 @@ export default class ChatBubbles {
const message = await this.managers.appMessagesManager.getMessageByPeer(peerId.toPeerId(), +mid); const message = await this.managers.appMessagesManager.getMessageByPeer(peerId.toPeerId(), +mid);
if(message) { if(message) {
const inputInvoice = await this.managers.appPaymentsManager.getInputInvoiceByPeerId(this.peerId, +bubble.dataset.mid); const inputInvoice = await this.managers.appPaymentsManager.getInputInvoiceByPeerId(this.peerId, +bubble.dataset.mid);
new PopupPayment(message as Message.message, inputInvoice, undefined, true); PopupElement.createPopup(PopupPayment, message as Message.message, inputInvoice, undefined, true);
} }
} else { } else {
this.chat.appImManager.setInnerPeer({ this.chat.appImManager.setInnerPeer({
@ -1916,7 +1934,7 @@ export default class ChatBubbles {
const doc = ((message as Message.message).media as MessageMedia.messageMediaDocument)?.document as Document.document; const doc = ((message as Message.message).media as MessageMedia.messageMediaDocument)?.document as Document.document;
if(doc?.stickerSetInput) { if(doc?.stickerSetInput) {
new PopupStickers(doc.stickerSetInput).show(); PopupElement.createPopup(PopupStickers, doc.stickerSetInput).show();
} }
return; return;
@ -1941,7 +1959,7 @@ export default class ChatBubbles {
} else if(target.classList.contains('forward')) { } else if(target.classList.contains('forward')) {
const mid = +bubble.dataset.mid; const mid = +bubble.dataset.mid;
const message = await this.managers.appMessagesManager.getMessageByPeer(this.peerId, mid); const message = await this.managers.appMessagesManager.getMessageByPeer(this.peerId, mid);
new PopupForward({ PopupElement.createPopup(PopupForward, {
[this.peerId]: await this.managers.appMessagesManager.getMidsByMessage(message) [this.peerId]: await this.managers.appMessagesManager.getMidsByMessage(message)
}); });
// appSidebarRight.forwardTab.open([mid]); // appSidebarRight.forwardTab.open([mid]);
@ -3024,6 +3042,16 @@ export default class ChatBubbles {
this.chat.input.setStartParam(startParam); this.chat.input.setStartParam(startParam);
} }
if(options.mediaTimestamp) {
getHeavyAnimationPromise().then(() => {
this.playMediaWithTimestampAndMid({
lastMsgId,
middleware,
mediaTimestamp: options.mediaTimestamp
});
});
}
return null; return null;
} }
} else { } else {
@ -3251,13 +3279,11 @@ export default class ChatBubbles {
pause(400) : pause(400) :
Promise.resolve(); Promise.resolve();
p.then(() => { p.then(() => {
return this.getMountedBubble(lastMsgId); return this.playMediaWithTimestampAndMid({
}).then((mounted) => { lastMsgId,
if(!middleware() || !mounted) { middleware,
return; mediaTimestamp: options.mediaTimestamp
} });
this.playMediaWithTimestamp(mounted.bubble, options.mediaTimestamp);
}); });
} }
@ -3305,6 +3331,24 @@ export default class ChatBubbles {
return {cached, promise: setPeerPromise}; return {cached, promise: setPeerPromise};
} }
public playMediaWithTimestampAndMid({
middleware,
lastMsgId,
mediaTimestamp
}: {
middleware: () => boolean,
lastMsgId: number,
mediaTimestamp: number
}) {
this.getMountedBubble(lastMsgId).then((mounted) => {
if(!middleware() || !mounted) {
return;
}
this.playMediaWithTimestamp(mounted.bubble, mediaTimestamp);
});
}
public playMediaWithTimestamp(element: HTMLElement, timestamp: number) { public playMediaWithTimestamp(element: HTMLElement, timestamp: number) {
const bubble = findUpClassName(element, 'bubble'); const bubble = findUpClassName(element, 'bubble');
const groupedItem = findUpClassName(element, 'grouped-item'); const groupedItem = findUpClassName(element, 'grouped-item');
@ -3325,6 +3369,19 @@ export default class ChatBubbles {
audio.playWithTimestamp(timestamp); audio.playWithTimestamp(timestamp);
return; return;
} }
const replyToPeerId = bubble.dataset.replyToPeerId.toPeerId();
const replyToMid = +bubble.dataset.replyToMid;
if(replyToPeerId && replyToMid) {
if(replyToPeerId === this.peerId) {
this.chat.setMessageId(replyToMid, timestamp);
} else {
this.chat.appImManager.setInnerPeer({
peerId: replyToPeerId,
mediaTimestamp: timestamp
});
}
}
} }
private async setFetchReactionsInterval(afterSetPromise: Promise<any>) { private async setFetchReactionsInterval(afterSetPromise: Promise<any>) {
@ -3937,11 +3994,28 @@ export default class ChatBubbles {
customEmojiSize ??= this.chat.appImManager.customEmojiSize; customEmojiSize ??= this.chat.appImManager.customEmojiSize;
const maxMediaTimestamp = getMediaDurationFromMessage(albumTextMessage || message as Message.message); let maxMediaTimestamp = getMediaDurationFromMessage(albumTextMessage || message as Message.message);
if(albumTextMessage && needToSetHTML) { if(albumTextMessage && needToSetHTML) {
bubble.dataset.textMid = '' + albumTextMessage.mid; bubble.dataset.textMid = '' + albumTextMessage.mid;
} }
if(message.reply_to) {
const replyToPeerId = message.reply_to.reply_to_peer_id ? getPeerId(message.reply_to.reply_to_peer_id) : this.peerId;
bubble.dataset.replyToPeerId = '' + replyToPeerId;
bubble.dataset.replyToMid = '' + message.reply_to_mid;
if(maxMediaTimestamp === undefined) {
const originalMessage = await rootScope.managers.appMessagesManager.getMessageByPeer(replyToPeerId, message.reply_to_mid);
if(originalMessage) {
maxMediaTimestamp = getMediaDurationFromMessage(originalMessage as Message.message);
} else {
// this.managers.appMessagesManager.fetchMessageReplyTo(message);
// this.needUpdate.push({replyToPeerId, replyMid: message.reply_to_mid, mid: message.mid});
maxMediaTimestamp = Infinity;
}
}
}
const richTextOptions: Parameters<typeof wrapRichText>[1] = { const richTextOptions: Parameters<typeof wrapRichText>[1] = {
entities: totalEntities, entities: totalEntities,
passEntities: this.passEntities, passEntities: this.passEntities,
@ -4095,7 +4169,7 @@ export default class ChatBubbles {
} }
return new Promise<PeerId>((resolve, reject) => { return new Promise<PeerId>((resolve, reject) => {
const popup = new PopupForward(undefined, (peerId) => { const popup = PopupElement.createPopup(PopupForward, undefined, (peerId) => {
resolve(peerId); resolve(peerId);
}); });
@ -5726,7 +5800,7 @@ export default class ChatBubbles {
if(sponsoredMessage.chat_invite) { if(sponsoredMessage.chat_invite) {
callback = () => { callback = () => {
new PopupJoinChatInvite(sponsoredMessage.chat_invite_hash, sponsoredMessage.chat_invite as ChatInvite.chatInvite); PopupElement.createPopup(PopupJoinChatInvite, sponsoredMessage.chat_invite_hash, sponsoredMessage.chat_invite as ChatInvite.chatInvite);
}; };
} else if(sponsoredMessage.chat_invite_hash) { } else if(sponsoredMessage.chat_invite_hash) {
callback = () => { callback = () => {
@ -6147,6 +6221,11 @@ export default class ChatBubbles {
return result; return result;
} }
// private async getDiscussionMessages() {
// const mids = await this.chat.getMidsByMid(this.chat.threadId);
// return Promise.all(mids.map((mid) => this.chat.getMessage(mid)));
// }
/** /**
* Load and render history * Load and render history
* @param maxId max message id * @param maxId max message id

View File

@ -571,11 +571,12 @@ export default class Chat extends EventListenerBase<{
this.autoDownload = await getAutoDownloadSettingsByPeerId(this.peerId); this.autoDownload = await getAutoDownloadSettingsByPeerId(this.peerId);
} }
public setMessageId(messageId?: number) { public setMessageId(messageId?: number, mediaTimestamp?: number) {
return this.setPeer({ return this.setPeer({
peerId: this.peerId, peerId: this.peerId,
threadId: this.threadId, threadId: this.threadId,
lastMsgId: messageId lastMsgId: messageId,
mediaTimestamp
}); });
} }
@ -711,4 +712,9 @@ export default class Chat extends EventListenerBase<{
public isPinnedMessagesNeeded() { public isPinnedMessagesNeeded() {
return this.type === 'chat' || this.isForum; return this.type === 'chat' || this.isForum;
} }
public canGiftPremium() {
const peerId = this.peerId;
return peerId.isUser() && this.managers.appProfileManager.canGiftPremium(this.peerId.toUserId());
}
} }

View File

@ -48,6 +48,7 @@ import PopupStickers from '../popups/stickers';
import getMediaFromMessage from '../../lib/appManagers/utils/messages/getMediaFromMessage'; import getMediaFromMessage from '../../lib/appManagers/utils/messages/getMediaFromMessage';
import canSaveMessageMedia from '../../lib/appManagers/utils/messages/canSaveMessageMedia'; import canSaveMessageMedia from '../../lib/appManagers/utils/messages/canSaveMessageMedia';
import getAlbumText from '../../lib/appManagers/utils/messages/getAlbumText'; import getAlbumText from '../../lib/appManagers/utils/messages/getAlbumText';
import PopupElement from '../popups';
export default class ChatContextMenu { export default class ChatContextMenu {
private buttons: (ButtonMenuItemOptions & {verify: () => boolean | Promise<boolean>, notDirect?: () => boolean, withSelection?: true, isSponsored?: true, localName?: 'views' | 'emojis'})[]; private buttons: (ButtonMenuItemOptions & {verify: () => boolean | Promise<boolean>, notDirect?: () => boolean, withSelection?: true, isSponsored?: true, localName?: 'views' | 'emojis'})[];
@ -490,7 +491,7 @@ export default class ChatContextMenu {
icon: 'flag', icon: 'flag',
text: 'ReportChat', text: 'ReportChat',
onClick: () => { onClick: () => {
new PopupReportMessages(this.peerId, [this.mid]); PopupElement.createPopup(PopupReportMessages, this.peerId, [this.mid]);
}, },
verify: async() => !this.message.pFlags.out && this.message._ === 'message' && !this.message.pFlags.is_outgoing && await this.managers.appPeersManager.isChannel(this.peerId), verify: async() => !this.message.pFlags.out && this.message._ === 'message' && !this.message.pFlags.is_outgoing && await this.managers.appPeersManager.isChannel(this.peerId),
notDirect: () => true, notDirect: () => true,
@ -516,7 +517,7 @@ export default class ChatContextMenu {
peerId: this.viewerPeerId peerId: this.viewerPeerId
}); });
} else if(this.canOpenReactedList) { } else if(this.canOpenReactedList) {
new PopupReactedList(this.message as Message.message); PopupElement.createPopup(PopupReactedList, this.message as Message.message);
} else { } else {
return false; return false;
} }
@ -540,7 +541,7 @@ export default class ChatContextMenu {
icon: 'info', icon: 'info',
text: 'Chat.Message.Sponsored.What', text: 'Chat.Message.Sponsored.What',
onClick: () => { onClick: () => {
new PopupSponsored(); PopupElement.createPopup(PopupSponsored);
}, },
verify: () => false, verify: () => false,
isSponsored: true isSponsored: true
@ -549,7 +550,7 @@ export default class ChatContextMenu {
text: 'Loading', text: 'Loading',
onClick: () => { onClick: () => {
this.emojiInputsPromise.then((inputs) => { this.emojiInputsPromise.then((inputs) => {
new PopupStickers(inputs, true).show(); PopupElement.createPopup(PopupStickers, inputs, true).show();
}); });
}, },
verify: () => !!this.getUniqueCustomEmojisFromMessage().length, verify: () => !!this.getUniqueCustomEmojisFromMessage().length,
@ -890,7 +891,7 @@ export default class ChatContextMenu {
if(this.chat.selection.isSelecting) { if(this.chat.selection.isSelecting) {
simulateClickEvent(this.chat.selection.selectionSendNowBtn); simulateClickEvent(this.chat.selection.selectionSendNowBtn);
} else { } else {
new PopupSendNow(this.peerId, await this.chat.getMidsByMid(this.mid)); PopupElement.createPopup(PopupSendNow, this.peerId, await this.chat.getMidsByMid(this.mid));
} }
}; };
@ -929,11 +930,11 @@ export default class ChatContextMenu {
}; };
private onPinClick = () => { private onPinClick = () => {
new PopupPinMessage(this.peerId, this.mid); PopupElement.createPopup(PopupPinMessage, this.peerId, this.mid);
}; };
private onUnpinClick = () => { private onUnpinClick = () => {
new PopupPinMessage(this.peerId, this.mid, true); PopupElement.createPopup(PopupPinMessage, this.peerId, this.mid, true);
}; };
private onRetractVote = () => { private onRetractVote = () => {
@ -968,7 +969,7 @@ export default class ChatContextMenu {
if(this.chat.selection.isSelecting) { if(this.chat.selection.isSelecting) {
simulateClickEvent(this.chat.selection.selectionDeleteBtn); simulateClickEvent(this.chat.selection.selectionDeleteBtn);
} else { } else {
new PopupDeleteMessages(this.peerId, this.isTargetAGroupedItem ? [this.mid] : await this.chat.getMidsByMid(this.mid), this.chat.type); PopupElement.createPopup(PopupDeleteMessages, this.peerId, this.isTargetAGroupedItem ? [this.mid] : await this.chat.getMidsByMid(this.mid), this.chat.type);
} }
}; };

View File

@ -111,6 +111,7 @@ import {MARKDOWN_ENTITIES} from '../../lib/richTextProcessor';
import IMAGE_MIME_TYPES_SUPPORTED from '../../environment/imageMimeTypesSupport'; import IMAGE_MIME_TYPES_SUPPORTED from '../../environment/imageMimeTypesSupport';
import VIDEO_MIME_TYPES_SUPPORTED from '../../environment/videoMimeTypesSupport'; import VIDEO_MIME_TYPES_SUPPORTED from '../../environment/videoMimeTypesSupport';
import {ChatRights} from '../../lib/appManagers/appChatsManager'; import {ChatRights} from '../../lib/appManagers/appChatsManager';
import PopupGiftPremium from '../popups/giftPremium';
const RECORD_MIN_TIME = 500; const RECORD_MIN_TIME = 500;
@ -644,6 +645,11 @@ export default class ChatInput {
this.fileInput.click(); this.fileInput.click();
} }
// verify: () => this.chat.canSend('send_docs') // verify: () => this.chat.canSend('send_docs')
}, {
icon: 'gift',
text: 'GiftPremium',
onClick: () => this.chat.appImManager.giftPremium(this.chat.peerId),
verify: () => this.chat.canGiftPremium()
}, { }, {
icon: 'poll', icon: 'poll',
text: 'Poll', text: 'Poll',
@ -1049,7 +1055,7 @@ export default class ChatInput {
this.listenerSetter.add(this.pinnedControlBtn)('click', () => { this.listenerSetter.add(this.pinnedControlBtn)('click', () => {
const peerId = this.chat.peerId; const peerId = this.chat.peerId;
new PopupPinMessage(peerId, 0, true, () => { PopupElement.createPopup(PopupPinMessage, peerId, 0, true, () => {
this.chat.appImManager.setPeer(); // * close tab this.chat.appImManager.setPeer(); // * close tab
// ! костыль, это скроет закреплённые сообщения сразу, вместо того, чтобы ждать пока анимация перехода закончится // ! костыль, это скроет закреплённые сообщения сразу, вместо того, чтобы ждать пока анимация перехода закончится
@ -1219,7 +1225,7 @@ export default class ChatInput {
const middleware = this.chat.bubbles.getMiddleware(); const middleware = this.chat.bubbles.getMiddleware();
const canSendWhenOnline = rootScope.myId !== peerId && peerId.isUser() && await this.managers.appUsersManager.isUserOnlineVisible(peerId); const canSendWhenOnline = rootScope.myId !== peerId && peerId.isUser() && await this.managers.appUsersManager.isUserOnlineVisible(peerId);
new PopupSchedule(initDate, (timestamp) => { PopupElement.createPopup(PopupSchedule, initDate, (timestamp) => {
if(!middleware()) { if(!middleware()) {
return; return;
} }
@ -2470,7 +2476,7 @@ export default class ChatInput {
opusDecodeController.setKeepAlive(true); opusDecodeController.setKeepAlive(true);
const showDiscardPopup = () => { const showDiscardPopup = () => {
new PopupPeer('popup-cancel-record', { PopupElement.createPopup(PopupPeer, 'popup-cancel-record', {
titleLangKey: 'DiscardVoiceMessageTitle', titleLangKey: 'DiscardVoiceMessageTitle',
descriptionLangKey: 'DiscardVoiceMessageDescription', descriptionLangKey: 'DiscardVoiceMessageDescription',
buttons: [{ buttons: [{
@ -2614,7 +2620,7 @@ export default class ChatInput {
} }
if(!draftsAreEqual(draft, originalDraft)) { if(!draftsAreEqual(draft, originalDraft)) {
new PopupPeer('discard-editing', { PopupElement.createPopup(PopupPeer, 'discard-editing', {
buttons: [{ buttons: [{
langKey: 'Alert.Confirm.Discard', langKey: 'Alert.Confirm.Discard',
callback: () => { callback: () => {
@ -2657,7 +2663,7 @@ export default class ChatInput {
this.clearHelper(); this.clearHelper();
this.updateSendBtn(); this.updateSendBtn();
let selected = false; let selected = false;
const popup = new PopupForward(forwarding, () => { const popup = PopupElement.createPopup(PopupForward, forwarding, () => {
selected = true; selected = true;
}); });
@ -2800,7 +2806,7 @@ export default class ChatInput {
this.onMessageSent(); this.onMessageSent();
} else { } else {
new PopupDeleteMessages(peerId, [editMsgId], chat.type); PopupElement.createPopup(PopupDeleteMessages, peerId, [editMsgId], chat.type);
return; return;
} }

View File

@ -23,6 +23,7 @@ import throttle from '../../helpers/schedulers/throttle';
import {AppManagers} from '../../lib/appManagers/managers'; import {AppManagers} from '../../lib/appManagers/managers';
import {Message} from '../../layer'; import {Message} from '../../layer';
import {logger} from '../../lib/logger'; import {logger} from '../../lib/logger';
import PopupElement from '../popups';
class AnimatedSuper { class AnimatedSuper {
static DURATION = 200; static DURATION = 200;
@ -275,9 +276,9 @@ export default class ChatPinnedMessage {
divAndCaption: dAC, divAndCaption: dAC,
onClose: async() => { onClose: async() => {
if(await managers.appPeersManager.canPinMessage(this.chat.peerId)) { if(await managers.appPeersManager.canPinMessage(this.chat.peerId)) {
new PopupPinMessage(this.chat.peerId, this.pinnedMid, true); PopupElement.createPopup(PopupPinMessage, this.chat.peerId, this.pinnedMid, true);
} else { } else {
new PopupPinMessage(this.chat.peerId, 0, true); PopupElement.createPopup(PopupPinMessage, this.chat.peerId, 0, true);
} }
return false; return false;

View File

@ -38,6 +38,7 @@ import {attachContextMenuListener} from '../../helpers/dom/attachContextMenuList
import filterUnique from '../../helpers/array/filterUnique'; import filterUnique from '../../helpers/array/filterUnique';
import appImManager from '../../lib/appManagers/appImManager'; import appImManager from '../../lib/appManagers/appImManager';
import {Message} from '../../layer'; import {Message} from '../../layer';
import PopupElement from '../popups';
const accumulateMapSet = (map: Map<any, Set<number>>) => { const accumulateMapSet = (map: Map<any, Set<number>>) => {
return [...map.values()].reduce((acc, v) => acc + v.size, 0); return [...map.values()].reduce((acc, v) => acc + v.size, 0);
@ -676,7 +677,7 @@ export class SearchSelection extends AppSelection {
obj[fromPeerId] = Array.from(mids).sort((a, b) => a - b); obj[fromPeerId] = Array.from(mids).sort((a, b) => a - b);
} }
new PopupForward(obj, () => { PopupElement.createPopup(PopupForward, obj, () => {
this.cancelSelection(); this.cancelSelection();
}); });
}, attachClickOptions); }, attachClickOptions);
@ -685,7 +686,7 @@ export class SearchSelection extends AppSelection {
this.selectionDeleteBtn = ButtonIcon(`delete danger ${BASE_CLASS}-delete`); this.selectionDeleteBtn = ButtonIcon(`delete danger ${BASE_CLASS}-delete`);
attachClickEvent(this.selectionDeleteBtn, () => { attachClickEvent(this.selectionDeleteBtn, () => {
const peerId = [...this.selectedMids.keys()][0]; const peerId = [...this.selectedMids.keys()][0];
new PopupDeleteMessages(peerId, [...this.selectedMids.get(peerId)], 'chat', () => { PopupElement.createPopup(PopupDeleteMessages, peerId, [...this.selectedMids.get(peerId)], 'chat', () => {
this.cancelSelection(); this.cancelSelection();
}); });
}, attachClickOptions); }, attachClickOptions);
@ -949,7 +950,7 @@ export default class ChatSelection extends AppSelection {
this.selectionSendNowBtn = Button('btn-primary btn-transparent btn-short text-bold selection-container-send', {icon: 'send2'}); this.selectionSendNowBtn = Button('btn-primary btn-transparent btn-short text-bold selection-container-send', {icon: 'send2'});
this.selectionSendNowBtn.append(i18n('MessageScheduleSend')); this.selectionSendNowBtn.append(i18n('MessageScheduleSend'));
attachClickEvent(this.selectionSendNowBtn, () => { attachClickEvent(this.selectionSendNowBtn, () => {
new PopupSendNow(this.chat.peerId, [...this.selectedMids.get(this.chat.peerId)], () => { PopupElement.createPopup(PopupSendNow, this.chat.peerId, [...this.selectedMids.get(this.chat.peerId)], () => {
this.cancelSelection(); this.cancelSelection();
}); });
}, attachClickOptions); }, attachClickOptions);
@ -962,7 +963,7 @@ export default class ChatSelection extends AppSelection {
obj[fromPeerId] = Array.from(mids).sort((a, b) => a - b); obj[fromPeerId] = Array.from(mids).sort((a, b) => a - b);
} }
new PopupForward(obj, () => { PopupElement.createPopup(PopupForward, obj, () => {
this.cancelSelection(); this.cancelSelection();
}); });
}, attachClickOptions); }, attachClickOptions);
@ -971,7 +972,7 @@ export default class ChatSelection extends AppSelection {
this.selectionDeleteBtn = Button('btn-primary btn-transparent danger text-bold selection-container-delete', {icon: 'delete'}); this.selectionDeleteBtn = Button('btn-primary btn-transparent danger text-bold selection-container-delete', {icon: 'delete'});
this.selectionDeleteBtn.append(i18n('Delete')); this.selectionDeleteBtn.append(i18n('Delete'));
attachClickEvent(this.selectionDeleteBtn, () => { attachClickEvent(this.selectionDeleteBtn, () => {
new PopupDeleteMessages(this.chat.peerId, [...this.selectedMids.get(this.chat.peerId)], this.chat.type, () => { PopupElement.createPopup(PopupDeleteMessages, this.chat.peerId, [...this.selectedMids.get(this.chat.peerId)], this.chat.type, () => {
this.cancelSelection(); this.cancelSelection();
}); });
}, attachClickOptions); }, attachClickOptions);

View File

@ -47,6 +47,7 @@ import apiManagerProxy from '../../lib/mtproto/mtprotoworker';
import {makeMediaSize} from '../../helpers/mediaSize'; import {makeMediaSize} from '../../helpers/mediaSize';
import {FOLDER_ID_ALL} from '../../lib/mtproto/mtproto_config'; import {FOLDER_ID_ALL} from '../../lib/mtproto/mtproto_config';
import formatNumber from '../../helpers/number/formatNumber'; import formatNumber from '../../helpers/number/formatNumber';
import PopupElement from '../popups';
type ButtonToVerify = {element?: HTMLElement, verify: () => boolean | Promise<boolean>}; type ButtonToVerify = {element?: HTMLElement, verify: () => boolean | Promise<boolean>};
@ -419,11 +420,11 @@ export default class ChatTopbar {
text: 'ShareContact', text: 'ShareContact',
onClick: () => { onClick: () => {
const contactPeerId = this.peerId; const contactPeerId = this.peerId;
new PopupPickUser({ PopupElement.createPopup(PopupPickUser, {
peerTypes: ['dialogs', 'contacts'], peerTypes: ['dialogs', 'contacts'],
onSelect: (peerId) => { onSelect: (peerId) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
new PopupPeer('', { PopupElement.createPopup(PopupPeer, '', {
titleLangKey: 'SendMessageTitle', titleLangKey: 'SendMessageTitle',
descriptionLangKey: 'SendContactToGroupText', descriptionLangKey: 'SendContactToGroupText',
descriptionLangArgs: [new PeerTitle({peerId, dialog: true}).element], descriptionLangArgs: [new PeerTitle({peerId, dialog: true}).element],
@ -453,6 +454,11 @@ export default class ChatTopbar {
}); });
}, },
verify: async() => rootScope.myId !== this.peerId && this.peerId.isUser() && (await this.managers.appPeersManager.isContact(this.peerId)) && !!(await this.managers.appUsersManager.getUser(this.peerId.toUserId())).phone verify: async() => rootScope.myId !== this.peerId && this.peerId.isUser() && (await this.managers.appPeersManager.isContact(this.peerId)) && !!(await this.managers.appUsersManager.getUser(this.peerId.toUserId())).phone
}, {
icon: 'gift',
text: 'GiftPremium',
onClick: () => this.chat.appImManager.giftPremium(this.peerId),
verify: () => this.chat.canGiftPremium()
}, { }, {
icon: 'bots', icon: 'bots',
text: 'Settings', text: 'Settings',
@ -471,7 +477,7 @@ export default class ChatTopbar {
icon: 'lock', icon: 'lock',
text: 'BlockUser', text: 'BlockUser',
onClick: () => { onClick: () => {
new PopupPeer('', { PopupElement.createPopup(PopupPeer, '', {
peerId: this.peerId, peerId: this.peerId,
titleLangKey: 'BlockUser', titleLangKey: 'BlockUser',
descriptionLangKey: 'AreYouSureBlockContact2', descriptionLangKey: 'AreYouSureBlockContact2',
@ -512,7 +518,7 @@ export default class ChatTopbar {
icon: 'delete danger', icon: 'delete danger',
text: 'Delete', text: 'Delete',
onClick: () => { onClick: () => {
new PopupDeleteDialog(this.peerId/* , 'leave' */); PopupElement.createPopup(PopupDeleteDialog, this.peerId/* , 'leave' */);
}, },
verify: async() => this.chat.type === 'chat' && !!(await this.managers.appMessagesManager.getDialogOnly(this.peerId)) verify: async() => this.chat.type === 'chat' && !!(await this.managers.appMessagesManager.getDialogOnly(this.peerId))
}]; }];
@ -680,7 +686,7 @@ export default class ChatTopbar {
} }
private onMuteClick = () => { private onMuteClick = () => {
new PopupMute(this.peerId); PopupElement.createPopup(PopupMute, this.peerId);
}; };
private onResize = () => { private onResize = () => {

View File

@ -26,7 +26,8 @@ export type CheckboxFieldOptions = {
restriction?: boolean, restriction?: boolean,
withRipple?: boolean, withRipple?: boolean,
withHover?: boolean, withHover?: boolean,
listenerSetter?: ListenerSetter listenerSetter?: ListenerSetter,
asRadio?: boolean
}; };
export default class CheckboxField { export default class CheckboxField {
public input: HTMLInputElement; public input: HTMLInputElement;
@ -54,9 +55,9 @@ export default class CheckboxField {
const input = this.input = document.createElement('input'); const input = this.input = document.createElement('input');
input.classList.add('checkbox-field-input'); input.classList.add('checkbox-field-input');
input.type = 'checkbox'; input.type = options.asRadio ? 'radio' : 'checkbox';
if(options.name) { if(options.name) {
input.id = 'input-' + options.name; input[options.asRadio ? 'name' : 'id'] = 'input-' + options.name;
} }
if(options.checked) { if(options.checked) {

View File

@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import {addCancelButton} from './popups'; import PopupElement, {addCancelButton} from './popups';
import PopupPeer, {PopupPeerCheckboxOptions, PopupPeerOptions} from './popups/peer'; import PopupPeer, {PopupPeerCheckboxOptions, PopupPeerOptions} from './popups/peer';
// type PopupConfirmationOptions = Pick<PopupPeerOptions, 'titleLangKey'>; // type PopupConfirmationOptions = Pick<PopupPeerOptions, 'titleLangKey'>;
@ -35,6 +35,6 @@ export default function confirmationPopup<T extends PopupConfirmationOptions>(
options.buttons = buttons; options.buttons = buttons;
options.checkboxes ??= checkbox && [checkbox]; options.checkboxes ??= checkbox && [checkbox];
new PopupPeer('popup-confirmation', options).show(); PopupElement.createPopup(PopupPeer, 'popup-confirmation', options).show();
}); });
} }

View File

@ -18,6 +18,7 @@ import {AppManagers} from '../lib/appManagers/managers';
import {GENERAL_TOPIC_ID} from '../lib/mtproto/mtproto_config'; import {GENERAL_TOPIC_ID} from '../lib/mtproto/mtproto_config';
import showLimitPopup from './popups/limit'; import showLimitPopup from './popups/limit';
import createContextMenu from '../helpers/dom/createContextMenu'; import createContextMenu from '../helpers/dom/createContextMenu';
import PopupElement from './popups';
export default class DialogsContextMenu { export default class DialogsContextMenu {
private buttons: ButtonMenuItemOptionsVerifiable[]; private buttons: ButtonMenuItemOptionsVerifiable[];
@ -216,7 +217,7 @@ export default class DialogsContextMenu {
}; };
private onMuteClick = () => { private onMuteClick = () => {
new PopupMute(this.peerId, this.threadId); PopupElement.createPopup(PopupMute, this.peerId, this.threadId);
}; };
private onUnreadClick = async() => { private onUnreadClick = async() => {
@ -233,6 +234,6 @@ export default class DialogsContextMenu {
}; };
private onDeleteClick = () => { private onDeleteClick = () => {
new PopupDeleteDialog(this.peerId, undefined, undefined, this.threadId); PopupElement.createPopup(PopupDeleteDialog, this.peerId, undefined, undefined, this.threadId);
}; };
} }

View File

@ -40,6 +40,7 @@ import {hideToast, toastNew} from '../../toast';
import safeAssign from '../../../helpers/object/safeAssign'; import safeAssign from '../../../helpers/object/safeAssign';
import type {AppStickersManager} from '../../../lib/appManagers/appStickersManager'; import type {AppStickersManager} from '../../../lib/appManagers/appStickersManager';
import liteMode from '../../../helpers/liteMode'; import liteMode from '../../../helpers/liteMode';
import PopupElement from '../../popups';
const loadedURLs: Set<string> = new Set(); const loadedURLs: Set<string> = new Set();
export function appendEmoji(emoji: string, container?: HTMLElement, prepend = false, unify = false) { export function appendEmoji(emoji: string, container?: HTMLElement, prepend = false, unify = false) {
@ -703,7 +704,7 @@ export default class EmojiTab extends EmoticonsTabC<EmojiTabCategory> {
return; return;
} }
new PopupStickers({id: category.set.id, access_hash: category.set.access_hash}, true).show(); PopupElement.createPopup(PopupStickers, {id: category.set.id, access_hash: category.set.access_hash}, true).show();
return; return;
} }

View File

@ -38,6 +38,7 @@ import {AnyFunction} from '../../../types';
import {IgnoreMouseOutType} from '../../../helpers/dropdownHover'; import {IgnoreMouseOutType} from '../../../helpers/dropdownHover';
import customProperties from '../../../helpers/dom/customProperties'; import customProperties from '../../../helpers/dom/customProperties';
import windowSize from '../../../helpers/windowSize'; import windowSize from '../../../helpers/windowSize';
import PopupElement from '../../popups';
export class SuperStickerRenderer { export class SuperStickerRenderer {
public lazyLoadQueue: LazyLoadQueueRepeat; public lazyLoadQueue: LazyLoadQueueRepeat;
@ -617,7 +618,7 @@ export default class StickersTab extends EmoticonsTabC<StickersTabCategory<Stick
return; return;
} }
new PopupStickers({id: category.set.id, access_hash: category.set.access_hash}).show(); PopupElement.createPopup(PopupStickers, {id: category.set.id, access_hash: category.set.access_hash}).show();
return; return;
} }

View File

@ -373,7 +373,7 @@ export default class PopupGroupCall extends PopupElement {
}; };
if(await this.managers.appChatsManager.hasRights(this.instance.chatId, 'manage_call')) { if(await this.managers.appChatsManager.hasRights(this.instance.chatId, 'manage_call')) {
new PopupPeer('popup-end-video-chat', { PopupElement.createPopup(PopupPeer, 'popup-end-video-chat', {
titleLangKey: 'VoiceChat.End.Title', titleLangKey: 'VoiceChat.End.Title',
descriptionLangKey: 'VoiceChat.End.Text', descriptionLangKey: 'VoiceChat.End.Text',
checkboxes: [{ checkboxes: [{

View File

@ -183,7 +183,7 @@ export default class PopupDeleteDialog {
} }
} }
new PopupPeer('popup-delete-chat', { PopupElement.createPopup(PopupPeer, 'popup-delete-chat', {
peerId, peerId,
threadId, threadId,
titleLangKey: title, titleLangKey: title,

View File

@ -102,7 +102,7 @@ export default class PopupDeleteMessages {
addCancelButton(buttons); addCancelButton(buttons);
const popup = new PopupPeer('popup-delete-chat', { const popup = PopupElement.createPopup(PopupPeer, 'popup-delete-chat', {
peerId, peerId,
threadId, threadId,
titleLangKey: title, titleLangKey: title,

View File

@ -11,6 +11,7 @@ import rootScope from '../../lib/rootScope';
import {toastNew} from '../toast'; import {toastNew} from '../toast';
import PopupPickUser from './pickUser'; import PopupPickUser from './pickUser';
import getMediaFromMessage from '../../lib/appManagers/utils/messages/getMediaFromMessage'; import getMediaFromMessage from '../../lib/appManagers/utils/messages/getMediaFromMessage';
import PopupElement from '.';
export default class PopupForward extends PopupPickUser { export default class PopupForward extends PopupPickUser {
constructor( constructor(
@ -115,6 +116,6 @@ export default class PopupForward extends PopupPickUser {
} }
}); });
new PopupForward(args[0], args[1], Array.from(actions)); PopupElement.createPopup(PopupForward, args[0], args[1], Array.from(actions));
} }
} }

View File

@ -0,0 +1,121 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import PopupElement from '.';
import {attachClickEvent} from '../../helpers/dom/clickEvent';
import paymentsWrapCurrencyAmount from '../../helpers/paymentsWrapCurrencyAmount';
import {PremiumGiftOption} from '../../layer';
import appImManager from '../../lib/appManagers/appImManager';
import I18n, {i18n, _i18n} from '../../lib/langPack';
import AvatarElement from '../avatar';
import Button from '../button';
import CheckboxField from '../checkboxField';
import Row from '../row';
import wrapPeerTitle from '../wrappers/peerTitle';
const className = 'popup-gift-premium';
export default class PopupGiftPremium extends PopupElement {
constructor(
private peerId: PeerId,
private giftOptions: PremiumGiftOption[]
) {
super(className, {closable: true, overlayClosable: true, body: true, scrollable: true});
this.construct();
}
public async construct() {
const {peerId, giftOptions} = this;
const avatar = new AvatarElement();
avatar.classList.add('avatar-100', className + '-avatar');
await avatar.updateWithOptions({
peerId
});
const title = document.createElement('span');
_i18n(title, 'GiftTelegramPremiumTitle');
title.classList.add(className + '-title');
const subtitle = i18n('GiftTelegramPremiumDescription', [await wrapPeerTitle({peerId})]);
subtitle.classList.add(className + '-subtitle');
const shortestOption = this.giftOptions.slice().sort((a, b) => a.months - b.months)[0];
const wrapCurrency = (amount: number | string) => paymentsWrapCurrencyAmount(amount, shortestOption.currency, false, true);
const rows = this.giftOptions.map((giftOption, idx) => {
let subtitle = i18n('PricePerMonth', [wrapCurrency(+giftOption.amount / giftOption.months)]);
if(giftOption !== shortestOption) {
const span = document.createElement('span');
const badge = document.createElement('span');
badge.classList.add(className + '-discount');
const shortestAmount = +shortestOption.amount * giftOption.months / shortestOption.months;
const discount = Math.round((1 - +giftOption.amount / shortestAmount) * 100);
badge.textContent = '-' + discount + '%';
span.append(badge, subtitle);
subtitle = span;
}
const isYears = !(giftOption.months % 12);
const checkboxField = new CheckboxField({
// text: 'Months',
// textArgs: [giftOption.months],
checked: idx === 0,
round: true,
name: 'gift-months',
asRadio: true
});
const row = new Row({
title: i18n(isYears ? 'Years' : 'Months', [isYears ? giftOption.months / 12 : giftOption.months]),
checkboxField,
clickable: true,
subtitle,
titleRightSecondary: wrapCurrency(giftOption.amount)
});
row.container.classList.add(className + '-option');
return row;
});
const form = document.createElement('form');
form.classList.add(className + '-options');
form.append(...rows.map((row) => row.container));
const buttonText = new I18n.IntlElement({key: 'GiftSubscriptionFor', args: [wrapCurrency(giftOptions[0].amount)]});
const getSelectedOption = () => giftOptions[rows.findIndex((row) => row.checkboxField.checked)];
this.listenerSetter.add(form)('change', () => {
buttonText.compareAndUpdate({
args: [
wrapCurrency(getSelectedOption().amount)
]
});
});
const giftButton = Button(`btn-primary ${className}-confirm shimmer`);
giftButton.append(buttonText.element);
attachClickEvent(giftButton, () => {
const giftOption = getSelectedOption();
appImManager.openUrl(giftOption.bot_url);
this.hide();
}, {listenerSetter: this.listenerSetter});
this.scrollable.append(
avatar,
title,
subtitle,
form,
giftButton
);
this.show();
}
}

View File

@ -318,7 +318,7 @@ export default class PopupElement<T extends EventListenerListeners = {}> extends
return this.POPUPS.filter((element) => element instanceof popupConstructor) as T[]; return this.POPUPS.filter((element) => element instanceof popupConstructor) as T[];
} }
public static createPopup<T extends PopupElement, A extends Array<any>>(ctor: {new(...args: A): T}, ...args: A) { public static createPopup<T extends /* PopupElement */any, A extends Array<any>>(ctor: {new(...args: A): T}, ...args: A) {
const popup = new ctor(...args); const popup = new ctor(...args);
return popup; return popup;
} }

View File

@ -531,7 +531,7 @@ export default class PopupPayment extends PopupElement {
}; };
const onMethodClick = () => { const onMethodClick = () => {
new PopupPaymentCard(paymentForm as PaymentsPaymentForm, previousCardDetails as PaymentCardDetails).addEventListener('finish', ({token, card}) => { PopupElement.createPopup(PopupPaymentCard, paymentForm as PaymentsPaymentForm, previousCardDetails as PaymentCardDetails).addEventListener('finish', ({token, card}) => {
previousToken = token, previousCardDetails = card; previousToken = token, previousCardDetails = card;
setCardSubtitle(card); setCardSubtitle(card);
@ -587,7 +587,7 @@ export default class PopupPayment extends PopupElement {
if(!isReceipt) { if(!isReceipt) {
onShippingAddressClick = (focus) => { onShippingAddressClick = (focus) => {
new PopupPaymentShipping(paymentForm as PaymentsPaymentForm, inputInvoice, focus).addEventListener('finish', ({shippingAddress, requestedInfo}) => { PopupElement.createPopup(PopupPaymentShipping, paymentForm as PaymentsPaymentForm, inputInvoice, focus).addEventListener('finish', ({shippingAddress, requestedInfo}) => {
lastRequestedInfo = requestedInfo; lastRequestedInfo = requestedInfo;
savedInfo = (paymentForm as PaymentsPaymentForm).saved_info = shippingAddress; savedInfo = (paymentForm as PaymentsPaymentForm).saved_info = shippingAddress;
setShippingInfo(shippingAddress); setShippingInfo(shippingAddress);
@ -643,7 +643,7 @@ export default class PopupPayment extends PopupElement {
icon: 'shipping', icon: 'shipping',
titleLangKey: 'PaymentCheckoutShippingMethod', titleLangKey: 'PaymentCheckoutShippingMethod',
clickable: !isReceipt && (onShippingMethodClick = () => { clickable: !isReceipt && (onShippingMethodClick = () => {
new PopupPaymentShippingMethods(paymentForm as PaymentsPaymentForm, lastRequestedInfo, lastShippingOption).addEventListener('finish', (shippingOption) => { PopupElement.createPopup(PopupPaymentShippingMethods, paymentForm as PaymentsPaymentForm, lastRequestedInfo, lastShippingOption).addEventListener('finish', (shippingOption) => {
setShippingOption(shippingOption); setShippingOption(shippingOption);
}); });
}) })
@ -736,7 +736,7 @@ export default class PopupPayment extends PopupElement {
} }
Promise.resolve(passwordState ?? this.managers.passwordManager.getState()).then((_passwordState) => { Promise.resolve(passwordState ?? this.managers.passwordManager.getState()).then((_passwordState) => {
new PopupPaymentCardConfirmation(savedCredentials.title, _passwordState).addEventListener('finish', (tmpPassword) => { PopupElement.createPopup(PopupPaymentCardConfirmation, savedCredentials.title, _passwordState).addEventListener('finish', (tmpPassword) => {
passwordState = undefined; passwordState = undefined;
lastTmpPasword = tmpPassword; lastTmpPasword = tmpPassword;
simulateClickEvent(payButton); simulateClickEvent(payButton);
@ -783,7 +783,7 @@ export default class PopupPayment extends PopupElement {
if(paymentResult._ === 'payments.paymentResult') { if(paymentResult._ === 'payments.paymentResult') {
onConfirmed(); onConfirmed();
} else { } else {
popupPaymentVerification = new PopupPaymentVerification(paymentResult.url, !mediaInvoice.extended_media); popupPaymentVerification = PopupElement.createPopup(PopupPaymentVerification, paymentResult.url, !mediaInvoice.extended_media);
popupPaymentVerification.addEventListener('finish', () => { popupPaymentVerification.addEventListener('finish', () => {
popupPaymentVerification = undefined; popupPaymentVerification = undefined;

View File

@ -4,6 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import PopupElement from '.';
import {attachClickEvent} from '../../helpers/dom/clickEvent'; 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';
@ -45,7 +46,7 @@ export default class PopupReportMessages extends PopupPeer {
preloadStickerPromise.then(() => { preloadStickerPromise.then(() => {
this.hide(); this.hide();
new PopupReportMessagesConfirm(peerId, mids, reason, onConfirm); PopupElement.createPopup(PopupReportMessagesConfirm, peerId, mids, reason, onConfirm);
}); });
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});

View File

@ -110,7 +110,7 @@ export default class PopupPinMessage {
addCancelButton(buttons); addCancelButton(buttons);
const popup = new PopupPeer('popup-delete-chat', { const popup = PopupElement.createPopup(PopupPeer, 'popup-delete-chat', {
peerId, peerId,
titleLangKey: title, titleLangKey: title,
descriptionLangKey: description, descriptionLangKey: description,

View File

@ -33,7 +33,6 @@ export type RowMediaSizeType = 'small' | 'medium' | 'big' | 'abitbigger' | 'bigg
export default class Row { export default class Row {
public container: HTMLElement; public container: HTMLElement;
public title: HTMLElement;
public titleRow: HTMLElement; public titleRow: HTMLElement;
public titleRight: HTMLElement; public titleRight: HTMLElement;
public media: HTMLElement; public media: HTMLElement;
@ -48,6 +47,7 @@ export default class Row {
public buttonRight: HTMLElement; public buttonRight: HTMLElement;
private _title: HTMLElement;
private _subtitle: HTMLElement; private _subtitle: HTMLElement;
private _midtitle: HTMLElement; private _midtitle: HTMLElement;
@ -127,6 +127,9 @@ export default class Row {
options.titleRight = this.checkboxField.label; options.titleRight = this.checkboxField.label;
} else { } else {
havePadding = true; havePadding = true;
if(!this.checkboxField.span) {
this.checkboxField.label.classList.add('checkbox-field-absolute');
}
this.container.append(this.checkboxField.label); this.container.append(this.checkboxField.label);
} }
@ -144,7 +147,7 @@ export default class Row {
i.label.classList.add('disable-hover'); i.label.classList.add('disable-hover');
} }
if(options.title || options.titleLangKey) { if(options.title || options.titleLangKey || options.titleRight || options.titleRightSecondary) {
let c: HTMLElement; let c: HTMLElement;
const titleRightContent = options.titleRight || options.titleRightSecondary; const titleRightContent = options.titleRight || options.titleRightSecondary;
if(titleRightContent) { if(titleRightContent) {
@ -154,7 +157,7 @@ export default class Row {
c = this.container; c = this.container;
} }
this.title = this.createTitle(); this._title = this.createTitle();
if(options.noWrap) this.title.classList.add('no-wrap'); if(options.noWrap) this.title.classList.add('no-wrap');
if(options.title) { if(options.title) {
setContent(this.title, options.title); setContent(this.title, options.title);
@ -227,6 +230,10 @@ export default class Row {
} }
} }
public get title() {
return this._title;
}
public get subtitle() { public get subtitle() {
return this._subtitle ??= this.createSubtitle(); return this._subtitle ??= this.createSubtitle();
} }

View File

@ -288,6 +288,7 @@ export class AppSidebarLeft extends SidebarSlider {
}] }]
}); });
this.newBtnMenu.className = 'btn-circle rp btn-corner z-depth-1 btn-menu-toggle animated-button-icon'; this.newBtnMenu.className = 'btn-circle rp btn-corner z-depth-1 btn-menu-toggle animated-button-icon';
this.newBtnMenu.tabIndex = -1;
this.newBtnMenu.insertAdjacentHTML('afterbegin', ` this.newBtnMenu.insertAdjacentHTML('afterbegin', `
<span class="tgico tgico-newchat_filled"></span> <span class="tgico tgico-newchat_filled"></span>
<span class="tgico tgico-close"></span> <span class="tgico tgico-close"></span>
@ -297,6 +298,7 @@ export class AppSidebarLeft extends SidebarSlider {
this.updateBtn = document.createElement('div'); this.updateBtn = document.createElement('div');
this.updateBtn.className = 'btn-circle rp btn-corner z-depth-1 btn-update is-hidden'; this.updateBtn.className = 'btn-circle rp btn-corner z-depth-1 btn-update is-hidden';
this.updateBtn.tabIndex = -1;
ripple(this.updateBtn); ripple(this.updateBtn);
this.updateBtn.append(i18n('Update')); this.updateBtn.append(i18n('Update'));

View File

@ -18,6 +18,7 @@ import {attachClickEvent} from '../../../../helpers/dom/clickEvent';
import matchEmail from '../../../../lib/richTextProcessor/matchEmail'; import matchEmail from '../../../../lib/richTextProcessor/matchEmail';
import wrapStickerEmoji from '../../../wrappers/stickerEmoji'; import wrapStickerEmoji from '../../../wrappers/stickerEmoji';
import SettingSection from '../../../settingSection'; import SettingSection from '../../../settingSection';
import PopupElement from '../../../popups';
export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
public inputField: InputField; public inputField: InputField;
@ -125,7 +126,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
}; };
attachClickEvent(btnSkip, (e) => { attachClickEvent(btnSkip, (e) => {
const popup = new PopupPeer('popup-skip-email', { const popup = PopupElement.createPopup(PopupPeer, 'popup-skip-email', {
buttons: [{ buttons: [{
langKey: 'Cancel', langKey: 'Cancel',
isCancel: true isCancel: true

View File

@ -8,6 +8,7 @@ import {attachClickEvent} from '../../../../helpers/dom/clickEvent';
import {AccountPassword} from '../../../../layer'; import {AccountPassword} from '../../../../layer';
import {_i18n} from '../../../../lib/langPack'; import {_i18n} from '../../../../lib/langPack';
import Button from '../../../button'; import Button from '../../../button';
import PopupElement from '../../../popups';
import PopupPeer from '../../../popups/peer'; import PopupPeer from '../../../popups/peer';
import SettingSection from '../../../settingSection'; import SettingSection from '../../../settingSection';
import {SliderSuperTab} from '../../../slider'; import {SliderSuperTab} from '../../../slider';
@ -57,7 +58,7 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
}); });
attachClickEvent(btnDisablePassword, () => { attachClickEvent(btnDisablePassword, () => {
const popup = new PopupPeer('popup-disable-password', { const popup = PopupElement.createPopup(PopupPeer, 'popup-disable-password', {
buttons: [{ buttons: [{
langKey: 'Disable', langKey: 'Disable',
callback: () => { callback: () => {

View File

@ -21,6 +21,7 @@ import {attachContextMenuListener} from '../../../helpers/dom/attachContextMenuL
import positionMenu from '../../../helpers/positionMenu'; import positionMenu from '../../../helpers/positionMenu';
import contextMenuController from '../../../helpers/contextMenuController'; import contextMenuController from '../../../helpers/contextMenuController';
import SettingSection from '../../settingSection'; import SettingSection from '../../settingSection';
import PopupElement from '../../popups';
export default class AppActiveSessionsTab extends SliderSuperTabEventable { export default class AppActiveSessionsTab extends SliderSuperTabEventable {
public authorizations: Authorization.authorization[]; public authorizations: Authorization.authorization[];
@ -61,7 +62,7 @@ export default class AppActiveSessionsTab extends SliderSuperTabEventable {
if(authorizations.length) { if(authorizations.length) {
const btnTerminate = Button('btn-primary btn-transparent danger', {icon: 'stop', text: 'TerminateAllSessions'}); const btnTerminate = Button('btn-primary btn-transparent danger', {icon: 'stop', text: 'TerminateAllSessions'});
attachClickEvent(btnTerminate, (e) => { attachClickEvent(btnTerminate, (e) => {
new PopupPeer('revoke-session', { PopupElement.createPopup(PopupPeer, 'revoke-session', {
buttons: [{ buttons: [{
langKey: 'Terminate', langKey: 'Terminate',
isDanger: true, isDanger: true,
@ -112,7 +113,7 @@ export default class AppActiveSessionsTab extends SliderSuperTabEventable {
const onTerminateClick = () => { const onTerminateClick = () => {
const hash = target.dataset.hash; const hash = target.dataset.hash;
new PopupPeer('revoke-session', { PopupElement.createPopup(PopupPeer, 'revoke-session', {
buttons: [{ buttons: [{
langKey: 'Terminate', langKey: 'Terminate',
isDanger: true, isDanger: true,

View File

@ -19,6 +19,7 @@ import positionMenu from '../../../helpers/positionMenu';
import contextMenuController from '../../../helpers/contextMenuController'; import contextMenuController from '../../../helpers/contextMenuController';
import getPeerActiveUsernames from '../../../lib/appManagers/utils/peers/getPeerActiveUsernames'; import getPeerActiveUsernames from '../../../lib/appManagers/utils/peers/getPeerActiveUsernames';
import SettingSection from '../../settingSection'; import SettingSection from '../../settingSection';
import PopupElement from '../../popups';
export default class AppBlockedUsersTab extends SliderSuperTab { export default class AppBlockedUsersTab extends SliderSuperTab {
public peerIds: PeerId[]; public peerIds: PeerId[];
@ -40,7 +41,7 @@ export default class AppBlockedUsersTab extends SliderSuperTab {
this.content.append(btnAdd); this.content.append(btnAdd);
attachClickEvent(btnAdd, (e) => { attachClickEvent(btnAdd, (e) => {
new PopupPickUser({ PopupElement.createPopup(PopupPickUser, {
peerTypes: ['contacts'], peerTypes: ['contacts'],
placeholder: 'BlockModal.Search.Placeholder', placeholder: 'BlockModal.Search.Placeholder',
onSelect: (peerId) => { onSelect: (peerId) => {

View File

@ -24,6 +24,7 @@ import wrapDraftText from '../../../lib/richTextProcessor/wrapDraftText';
import filterAsync from '../../../helpers/array/filterAsync'; import filterAsync from '../../../helpers/array/filterAsync';
import {attachClickEvent} from '../../../helpers/dom/clickEvent'; import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import SettingSection from '../../settingSection'; import SettingSection from '../../settingSection';
import PopupElement from '../../popups';
const MAX_FOLDER_NAME_LENGTH = 12; const MAX_FOLDER_NAME_LENGTH = 12;
@ -66,7 +67,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
icon: 'delete danger', icon: 'delete danger',
text: 'FilterMenuDelete', text: 'FilterMenuDelete',
onClick: () => { onClick: () => {
new PopupPeer('filter-delete', { PopupElement.createPopup(PopupPeer, 'filter-delete', {
titleLangKey: 'ChatList.Filter.Confirm.Remove.Header', titleLangKey: 'ChatList.Filter.Confirm.Remove.Header',
descriptionLangKey: 'ChatList.Filter.Confirm.Remove.Text', descriptionLangKey: 'ChatList.Filter.Confirm.Remove.Text',
buttons: [{ buttons: [{

View File

@ -34,6 +34,7 @@ import {toastNew} from '../../toast';
import AppPrivacyVoicesTab from './privacy/voices'; import AppPrivacyVoicesTab from './privacy/voices';
import SettingSection from '../../settingSection'; import SettingSection from '../../settingSection';
import AppActiveWebSessionsTab from './activeWebSessions'; import AppActiveWebSessionsTab from './activeWebSessions';
import PopupElement from '../../popups';
export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable { export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
private activeSessionsRow: Row; private activeSessionsRow: Row;
@ -424,7 +425,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
const section = new SettingSection({name: 'FilterChats'}); const section = new SettingSection({name: 'FilterChats'});
const onDeleteClick = () => { const onDeleteClick = () => {
const popup = new PopupPeer('popup-delete-drafts', { const popup = PopupElement.createPopup(PopupPeer, 'popup-delete-drafts', {
buttons: [{ buttons: [{
langKey: 'Delete', langKey: 'Delete',
callback: () => { callback: () => {

View File

@ -29,6 +29,7 @@ import PopupElement from '../../popups';
import {attachClickEvent} from '../../../helpers/dom/clickEvent'; import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import SettingSection from '../../settingSection'; import SettingSection from '../../settingSection';
import AppStickersAndEmojiTab from './stickersAndEmoji'; import AppStickersAndEmojiTab from './stickersAndEmoji';
import ButtonCorner from '../../buttonCorner';
export default class AppSettingsTab extends SliderSuperTab { export default class AppSettingsTab extends SliderSuperTab {
private buttons: { private buttons: {
@ -58,7 +59,7 @@ export default class AppSettingsTab extends SliderSuperTab {
icon: 'logout', icon: 'logout',
text: 'EditAccount.Logout', text: 'EditAccount.Logout',
onClick: () => { onClick: () => {
new PopupPeer('logout', { PopupElement.createPopup(PopupPeer, 'logout', {
titleLangKey: 'LogOut', titleLangKey: 'LogOut',
descriptionLangKey: 'LogOut.Description', descriptionLangKey: 'LogOut.Description',
buttons: [{ buttons: [{
@ -82,7 +83,7 @@ export default class AppSettingsTab extends SliderSuperTab {
this.profile.setPeer(rootScope.myId); this.profile.setPeer(rootScope.myId);
const fillPromise = this.profile.fillProfileElements(); const fillPromise = this.profile.fillProfileElements();
const changeAvatarBtn = Button('btn-circle btn-corner z-depth-1 profile-change-avatar', {icon: 'cameraadd'}); const changeAvatarBtn = ButtonCorner({icon: 'cameraadd', className: 'profile-change-avatar'});
attachClickEvent(changeAvatarBtn, () => { attachClickEvent(changeAvatarBtn, () => {
const canvas = document.createElement('canvas'); const canvas = document.createElement('canvas');
PopupElement.createPopup(PopupAvatar).open(canvas, (upload) => { PopupElement.createPopup(PopupAvatar).open(canvas, (upload) => {

View File

@ -15,6 +15,7 @@ import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
import rootScope from '../../../lib/rootScope'; import rootScope from '../../../lib/rootScope';
import CheckboxField from '../../checkboxField'; import CheckboxField from '../../checkboxField';
import LazyLoadQueue from '../../lazyLoadQueue'; import LazyLoadQueue from '../../lazyLoadQueue';
import PopupElement from '../../popups';
import PopupStickers from '../../popups/stickers'; import PopupStickers from '../../popups/stickers';
import Row, {CreateRowFromCheckboxField} from '../../row'; import Row, {CreateRowFromCheckboxField} from '../../row';
import SettingSection from '../../settingSection'; import SettingSection from '../../settingSection';
@ -209,7 +210,7 @@ export default class AppStickersAndEmojiTab extends SliderSuperTab {
subtitleLangArgs: [stickerSet.count], subtitleLangArgs: [stickerSet.count],
havePadding: true, havePadding: true,
clickable: () => { clickable: () => {
new PopupStickers({id: stickerSet.id, access_hash: stickerSet.access_hash}).show(); PopupElement.createPopup(PopupStickers, {id: stickerSet.id, access_hash: stickerSet.access_hash}).show();
}, },
listenerSetter: this.listenerSetter listenerSetter: this.listenerSetter
}); });

View File

@ -27,6 +27,7 @@ import getPeerEditableUsername from '../../../lib/appManagers/utils/peers/getPee
import getPeerActiveUsernames from '../../../lib/appManagers/utils/peers/getPeerActiveUsernames'; import getPeerActiveUsernames from '../../../lib/appManagers/utils/peers/getPeerActiveUsernames';
import {purchaseUsernameCaption} from '../../sidebarLeft/tabs/editProfile'; import {purchaseUsernameCaption} from '../../sidebarLeft/tabs/editProfile';
import confirmationPopup from '../../confirmationPopup'; import confirmationPopup from '../../confirmationPopup';
import PopupElement from '../../popups';
export default class AppChatTypeTab extends SliderSuperTabEventable { export default class AppChatTypeTab extends SliderSuperTabEventable {
public chatId: ChatId; public chatId: ChatId;
@ -102,7 +103,7 @@ export default class AppChatTypeTab extends SliderSuperTabEventable {
const btnRevoke = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'RevokeLink'}); const btnRevoke = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'RevokeLink'});
attachClickEvent(btnRevoke, () => { attachClickEvent(btnRevoke, () => {
new PopupPeer('revoke-link', { PopupElement.createPopup(PopupPeer, 'revoke-link', {
buttons: [{ buttons: [{
langKey: 'RevokeButton', langKey: 'RevokeButton',
callback: () => { callback: () => {

View File

@ -24,6 +24,7 @@ import hasRights from '../../../lib/appManagers/utils/chats/hasRights';
import replaceContent from '../../../helpers/dom/replaceContent'; import replaceContent from '../../../helpers/dom/replaceContent';
import SettingSection from '../../settingSection'; import SettingSection from '../../settingSection';
import getPeerActiveUsernames from '../../../lib/appManagers/utils/peers/getPeerActiveUsernames'; import getPeerActiveUsernames from '../../../lib/appManagers/utils/peers/getPeerActiveUsernames';
import PopupElement from '../../popups';
export default class AppEditChatTab extends SliderSuperTab { export default class AppEditChatTab extends SliderSuperTab {
private chatNameInputField: InputField; private chatNameInputField: InputField;
@ -377,7 +378,7 @@ export default class AppEditChatTab extends SliderSuperTab {
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: isBroadcast ? 'PeerInfo.DeleteChannel' : 'DeleteAndExitButton'}); const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: isBroadcast ? 'PeerInfo.DeleteChannel' : 'DeleteAndExitButton'});
attachClickEvent(btnDelete, () => { attachClickEvent(btnDelete, () => {
new PopupDeleteDialog(peerId/* , 'delete' */, undefined, (promise) => { PopupElement.createPopup(PopupDeleteDialog, peerId/* , 'delete' */, undefined, (promise) => {
const toggle = toggleDisability([btnDelete], true); const toggle = toggleDisability([btnDelete], true);
promise.then(() => { promise.then(() => {
this.close(); this.close();

View File

@ -13,7 +13,7 @@ import Button from '../../button';
import PeerTitle from '../../peerTitle'; import PeerTitle from '../../peerTitle';
import rootScope from '../../../lib/rootScope'; import rootScope from '../../../lib/rootScope';
import PopupPeer from '../../popups/peer'; import PopupPeer from '../../popups/peer';
import {addCancelButton} from '../../popups'; import PopupElement, {addCancelButton} from '../../popups';
import {i18n} from '../../../lib/langPack'; import {i18n} from '../../../lib/langPack';
import {attachClickEvent} from '../../../helpers/dom/clickEvent'; import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import toggleDisability from '../../../helpers/dom/toggleDisability'; import toggleDisability from '../../../helpers/dom/toggleDisability';
@ -160,7 +160,7 @@ export default class AppEditContactTab extends SliderSuperTab {
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'PeerInfo.DeleteContact'}); const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'PeerInfo.DeleteContact'});
attachClickEvent(btnDelete, () => { attachClickEvent(btnDelete, () => {
new PopupPeer('popup-delete-contact', { PopupElement.createPopup(PopupPeer, 'popup-delete-contact', {
peerId: peerId, peerId: peerId,
titleLangKey: 'DeleteContact', titleLangKey: 'DeleteContact',
descriptionLangKey: 'AreYouSureDeleteContact', descriptionLangKey: 'AreYouSureDeleteContact',

View File

@ -26,6 +26,7 @@ import {SliderSuperTabEventable} from '../../sliderTab';
import {toast} from '../../toast'; import {toast} from '../../toast';
import AppUserPermissionsTab from './userPermissions'; import AppUserPermissionsTab from './userPermissions';
import CheckboxFields, {CheckboxFieldsField} from '../../checkboxFields'; import CheckboxFields, {CheckboxFieldsField} from '../../checkboxFields';
import PopupElement from '../../popups';
type PermissionsCheckboxFieldsField = CheckboxFieldsField & { type PermissionsCheckboxFieldsField = CheckboxFieldsField & {
flags: ChatRights[], flags: ChatRights[],
@ -186,7 +187,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
subtitleLangKey: 'Loading', subtitleLangKey: 'Loading',
icon: 'adduser', icon: 'adduser',
clickable: () => { clickable: () => {
new PopupPickUser({ PopupElement.createPopup(PopupPickUser, {
peerTypes: ['channelParticipants'], peerTypes: ['channelParticipants'],
onSelect: (peerId) => { onSelect: (peerId) => {
setTimeout(() => { setTimeout(() => {

View File

@ -25,6 +25,7 @@ import {Message} from '../../../layer';
import getMessageThreadId from '../../../lib/appManagers/utils/messages/getMessageThreadId'; import getMessageThreadId from '../../../lib/appManagers/utils/messages/getMessageThreadId';
import AppEditTopicTab from './editTopic'; import AppEditTopicTab from './editTopic';
import liteMode from '../../../helpers/liteMode'; import liteMode from '../../../helpers/liteMode';
import PopupElement from '../../popups';
type SharedMediaHistoryStorage = Partial<{ type SharedMediaHistoryStorage = Partial<{
[type in SearchSuperType]: {mid: number, peerId: PeerId}[] [type in SearchSuperType]: {mid: number, peerId: PeerId}[]
@ -296,7 +297,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
peerId peerId
}).element); }).element);
new PopupPeer('popup-add-members', { PopupElement.createPopup(PopupPeer, 'popup-add-members', {
peerId, peerId,
titleLangKey, titleLangKey,
descriptionLangKey, descriptionLangKey,
@ -333,7 +334,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
placeholder: 'SendMessageTo' placeholder: 'SendMessageTo'
}); });
} else { } else {
new PopupPickUser({ PopupElement.createPopup(PopupPickUser, {
peerTypes: ['contacts'], peerTypes: ['contacts'],
placeholder: 'Search', placeholder: 'Search',
onSelect: (peerId) => { onSelect: (peerId) => {

View File

@ -20,6 +20,7 @@ import setInnerHTML from '../../../helpers/dom/setInnerHTML';
import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText'; import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
import attachStickerViewerListeners from '../../stickerViewer'; import attachStickerViewerListeners from '../../stickerViewer';
import wrapSticker from '../../wrappers/sticker'; import wrapSticker from '../../wrappers/sticker';
import PopupElement from '../../popups';
export default class AppStickersTab extends SliderSuperTab { export default class AppStickersTab extends SliderSuperTab {
private inputSearch: InputSearch; private inputSearch: InputSearch;
@ -79,7 +80,7 @@ export default class AppStickersTab extends SliderSuperTab {
}); });
} else { } else {
this.managers.appStickersManager.getStickerSet({id, access_hash}).then((full) => { this.managers.appStickersManager.getStickerSet({id, access_hash}).then((full) => {
new PopupStickers(full.set).show(); PopupElement.createPopup(PopupStickers, full.set).show();
}); });
} }
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});

View File

@ -263,14 +263,14 @@ export default class TopbarCall {
return; return;
} }
new PopupGroupCall().show(); PopupElement.createPopup(PopupGroupCall).show();
} else if(this.instance instanceof CallInstance) { } else if(this.instance instanceof CallInstance) {
const popups = PopupElement.getPopups(PopupCall); const popups = PopupElement.getPopups(PopupCall);
if(popups.find((popup) => popup.getCallInstance() === this.instance)) { if(popups.find((popup) => popup.getCallInstance() === this.instance)) {
return; return;
} }
new PopupCall(this.instance).show(); PopupElement.createPopup(PopupCall, this.instance).show();
} }
}, {listenerSetter}); }, {listenerSetter});

View File

@ -47,6 +47,7 @@ import wrapStickerAnimation from './stickerAnimation';
import framesCache from '../../helpers/framesCache'; import framesCache from '../../helpers/framesCache';
import {IS_MOBILE} from '../../environment/userAgent'; import {IS_MOBILE} from '../../environment/userAgent';
import liteMode, {LiteModeKey} from '../../helpers/liteMode'; import liteMode, {LiteModeKey} from '../../helpers/liteMode';
import PopupElement from '../popups';
// https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp#L40 // https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp#L40
export const STICKER_EFFECT_MULTIPLIER = 1 + 0.245 * 2; export const STICKER_EFFECT_MULTIPLIER = 1 + 0.245 * 2;
@ -699,7 +700,7 @@ function attachStickerEffectHandler({container, doc, managers, middleware, isOut
const a = document.createElement('a'); const a = document.createElement('a');
a.onclick = () => { a.onclick = () => {
hideToast(); hideToast();
new PopupStickers(doc.stickerSetInput).show(); PopupElement.createPopup(PopupStickers, doc.stickerSetInput).show();
}; };
toastNew({ toastNew({
@ -782,7 +783,7 @@ export async function onEmojiStickerClick({event, container, managers, peerId, m
} }
const activeAnimations: Set<{}> = (container as any).activeAnimations ??= new Set(); const activeAnimations: Set<{}> = (container as any).activeAnimations ??= new Set();
if(activeAnimations.size >= (IS_MOBILE ? 3 : 5)) { if(activeAnimations.size >= 3) {
return; return;
} }

View File

@ -21,7 +21,7 @@ const App = {
version: process.env.VERSION, version: process.env.VERSION,
versionFull: process.env.VERSION_FULL, versionFull: process.env.VERSION_FULL,
build: +process.env.BUILD, build: +process.env.BUILD,
langPackVersion: '1.0.1', langPackVersion: '1.0.3',
langPack: 'webk', langPack: 'webk',
langPackCode: 'en', langPackCode: 'en',
domains: MAIN_DOMAINS, domains: MAIN_DOMAINS,

View File

@ -22,6 +22,10 @@ export default function addAnchorListener<Params extends {pathnameParams?: any,
!options.noCancelEvent && cancelEvent(null); !options.noCancelEvent && cancelEvent(null);
let href = element.href; let href = element.href;
if(!href) {
return;
}
let pathnameParams: any[]; let pathnameParams: any[];
let uriParams: any; let uriParams: any;

View File

@ -10,7 +10,8 @@ import appImManager from '../../lib/appManagers/appImManager';
import rootScope from '../../lib/rootScope'; import rootScope from '../../lib/rootScope';
import createContextMenu from './createContextMenu'; import createContextMenu from './createContextMenu';
import findUpClassName from './findUpClassName'; import findUpClassName from './findUpClassName';
import emoticonsDropdown, {EmoticonsDropdown} from '../../components/emoticonsDropdown'; import {EmoticonsDropdown} from '../../components/emoticonsDropdown';
import PopupElement from '../../components/popups';
export default function createStickersContextMenu(options: { export default function createStickersContextMenu(options: {
listenTo: HTMLElement, listenTo: HTMLElement,
@ -45,7 +46,7 @@ export default function createStickersContextMenu(options: {
buttons: [{ buttons: [{
icon: 'stickers', icon: 'stickers',
text: 'Context.ViewStickerSet', text: 'Context.ViewStickerSet',
onClick: () => new PopupStickers(doc.stickerSetInput).show(), onClick: () => PopupElement.createPopup(PopupStickers, doc.stickerSetInput).show(),
verify: () => !isStickerPack verify: () => !isStickerPack
}, { }, {
icon: 'favourites', icon: 'favourites',

View File

@ -25,7 +25,12 @@ function number_format(number: any, decimals: any, dec_point: any, thousands_sep
return s.join(dec); return s.join(dec);
} }
export default function paymentsWrapCurrencyAmount(amount: number | string, currency: string, skipSymbol?: boolean) { export default function paymentsWrapCurrencyAmount(
amount: number | string,
currency: string,
skipSymbol?: boolean,
useNative?: boolean
) {
amount = +amount; amount = +amount;
const isNegative = amount < 0; const isNegative = amount < 0;
@ -47,7 +52,7 @@ export default function paymentsWrapCurrencyAmount(amount: number | string, curr
return formatted; return formatted;
} }
let symbol = currencyData.symbol; let symbol = useNative ? currencyData.native || currencyData.symbol : currencyData.symbol;
if(isNegative && !currencyData.space_between && currencyData.symbol_left) { if(isNegative && !currencyData.space_between && currencyData.symbol_left) {
symbol = '-' + symbol; symbol = '-' + symbol;
formatted = formatted.replace('-', ''); formatted = formatted.replace('-', '');

View File

@ -940,6 +940,11 @@ const lang = {
'SuggestStickersNone': 'None', 'SuggestStickersNone': 'None',
'DynamicPackOrder': 'Dynamic Pack Order', 'DynamicPackOrder': 'Dynamic Pack Order',
'DynamicPackOrderInfo': 'Automatically place recently used sticker packs at the front of the panel.', 'DynamicPackOrderInfo': 'Automatically place recently used sticker packs at the front of the panel.',
'GiftPremium': 'Gift Premium',
'GiftTelegramPremiumTitle': 'Gift Telegram Premium',
'GiftTelegramPremiumDescription': 'Give **%1$s** access to exclusive features with **Telegram Premium**.',
'PricePerMonth': '%1$s / month',
'GiftSubscriptionFor': 'Gift Subscription for %1$s',
// * macos // * macos
'AccountSettings.Filters': 'Chat Folders', 'AccountSettings.Filters': 'Chat Folders',

View File

@ -107,6 +107,7 @@ import partition from '../../helpers/array/partition';
import indexOfAndSplice from '../../helpers/array/indexOfAndSplice'; import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
import liteMode, {LiteModeKey} from '../../helpers/liteMode'; import liteMode, {LiteModeKey} from '../../helpers/liteMode';
import RLottiePlayer from '../rlottie/rlottiePlayer'; import RLottiePlayer from '../rlottie/rlottiePlayer';
import PopupGiftPremium from '../../components/popups/giftPremium';
export type ChatSavedPosition = { export type ChatSavedPosition = {
mids: number[], mids: number[],
@ -345,7 +346,7 @@ export class AppImManager extends EventListenerBase<{
const onInstanceDeactivated = (reason: InstanceDeactivateReason) => { const onInstanceDeactivated = (reason: InstanceDeactivateReason) => {
const isUpdated = reason === 'version'; const isUpdated = reason === 'version';
const popup = new PopupElement('popup-instance-deactivated', {overlayClosable: true}); const popup = PopupElement.createPopup(PopupElement, 'popup-instance-deactivated', {overlayClosable: true});
const c = document.createElement('div'); const c = document.createElement('div');
c.classList.add('instance-deactivated-container'); c.classList.add('instance-deactivated-container');
(popup as any).container.replaceWith(c); (popup as any).container.replaceWith(c);
@ -530,7 +531,7 @@ export class AppImManager extends EventListenerBase<{
// return; // return;
// } // }
const popup = new PopupCall(instance); const popup = PopupElement.createPopup(PopupCall, instance);
instance.addEventListener('acceptCallOverride', () => { instance.addEventListener('acceptCallOverride', () => {
return this.discardCurrentCall(instance.interlocutorUserId.toPeerId(), undefined, instance) return this.discardCurrentCall(instance.interlocutorUserId.toPeerId(), undefined, instance)
@ -582,7 +583,7 @@ export class AppImManager extends EventListenerBase<{
a.innerText = href; a.innerText = href;
a.removeAttribute('onclick'); a.removeAttribute('onclick');
new PopupPeer('popup-masked-url', { PopupElement.createPopup(PopupPeer, 'popup-masked-url', {
titleLangKey: 'OpenUrlTitle', titleLangKey: 'OpenUrlTitle',
descriptionLangKey: 'OpenUrlAlert2', descriptionLangKey: 'OpenUrlAlert2',
descriptionLangArgs: [a], descriptionLangArgs: [a],
@ -862,11 +863,11 @@ export class AppImManager extends EventListenerBase<{
const share = apiManagerProxy.share; const share = apiManagerProxy.share;
if(share) { if(share) {
apiManagerProxy.share = undefined; apiManagerProxy.share = undefined;
new PopupForward(undefined, async(peerId) => { PopupElement.createPopup(PopupForward, undefined, async(peerId) => {
await this.setPeer({peerId}); await this.setPeer({peerId});
if(share.files?.length) { if(share.files?.length) {
const foundMedia = share.files.some((file) => MEDIA_MIME_TYPES_SUPPORTED.has(file.type)); const foundMedia = share.files.some((file) => MEDIA_MIME_TYPES_SUPPORTED.has(file.type));
new PopupNewMedia(this.chat, share.files, foundMedia ? 'media' : 'document'); PopupElement.createPopup(PopupNewMedia, this.chat, share.files, foundMedia ? 'media' : 'document');
} else { } else {
this.managers.appMessagesManager.sendText(peerId, share.text); this.managers.appMessagesManager.sendText(peerId, share.text);
} }
@ -1184,7 +1185,7 @@ export class AppImManager extends EventListenerBase<{
case INTERNAL_LINK_TYPE.EMOJI_SET: case INTERNAL_LINK_TYPE.EMOJI_SET:
case INTERNAL_LINK_TYPE.STICKER_SET: { case INTERNAL_LINK_TYPE.STICKER_SET: {
new PopupStickers({id: link.set}, link._ === INTERNAL_LINK_TYPE.EMOJI_SET).show(); PopupElement.createPopup(PopupStickers, {id: link.set}, link._ === INTERNAL_LINK_TYPE.EMOJI_SET).show();
break; break;
} }
@ -1204,7 +1205,7 @@ export class AppImManager extends EventListenerBase<{
return; return;
} }
new PopupJoinChatInvite(link.invite, chatInvite); PopupElement.createPopup(PopupJoinChatInvite, link.invite, chatInvite);
}, (err) => { }, (err) => {
if(err.type === 'INVITE_HASH_EXPIRED') { if(err.type === 'INVITE_HASH_EXPIRED') {
toast(i18n('InviteExpired')); toast(i18n('InviteExpired'));
@ -1252,7 +1253,7 @@ export class AppImManager extends EventListenerBase<{
// } // }
// }; // };
new PopupPayment(undefined, inputInvoice, paymentForm); PopupElement.createPopup(PopupPayment, undefined, inputInvoice, paymentForm);
}); });
}); });
break; break;
@ -2546,6 +2547,12 @@ export class AppImManager extends EventListenerBase<{
options1.threadId === options2.threadId && options1.threadId === options2.threadId &&
(typeof(options1.type) !== typeof(options2.type) || options1.type === options2.type); (typeof(options1.type) !== typeof(options2.type) || options1.type === options2.type);
} }
public giftPremium(peerId: PeerId) {
this.managers.appProfileManager.getProfile(peerId.toUserId()).then((profile) => {
PopupElement.createPopup(PopupGiftPremium, peerId, profile.premium_gifts);
});
}
} }
const appImManager = new AppImManager(); const appImManager = new AppImManager();

View File

@ -5904,18 +5904,54 @@ export class AppMessagesManager extends AppManager {
}); });
} }
public isHistoryResultEnd(historyResult: Exclude<MessagesMessages, MessagesMessages.messagesMessagesNotModified>, limit: number, add_offset: number) { public isHistoryResultEnd(
historyResult: Exclude<MessagesMessages, MessagesMessages.messagesMessagesNotModified>,
limit: number,
add_offset: number,
offset_id: number
) {
const {offset_id_offset, messages} = historyResult as MessagesMessages.messagesMessagesSlice; const {offset_id_offset, messages} = historyResult as MessagesMessages.messagesMessagesSlice;
const mids = messages.map((message) => {
return (message as MyMessage).mid;
});
const count = (historyResult as MessagesMessages.messagesMessagesSlice).count || messages.length; const count = (historyResult as MessagesMessages.messagesMessagesSlice).count || messages.length;
const offsetIdOffset = offset_id_offset ?? count - 1;
const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit; const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit;
const bottomWasMeantToLoad = Math.abs(add_offset);
const isTopEnd = offsetIdOffset >= (count - topWasMeantToLoad) || count < topWasMeantToLoad; let offsetIdOffset = offset_id_offset;
const isBottomEnd = !offsetIdOffset || (add_offset < 0 && (offsetIdOffset + add_offset) <= 0); let isTopEnd = false, isBottomEnd = false;
return {count, offsetIdOffset, isTopEnd, isBottomEnd}; // if(offsetIdOffset === undefined && !bottomWasMeantToLoad) {
// offsetIdOffset = 0;
// }
if(offsetIdOffset !== undefined) {
isTopEnd = offsetIdOffset >= (count - topWasMeantToLoad) || count < topWasMeantToLoad;
isBottomEnd = !offsetIdOffset || (add_offset < 0 && (offsetIdOffset + add_offset) <= 0);
} else if(offset_id && getServerMessageId(offset_id)) {
let i = 0;
for(const length = mids.length; i < length; ++i) {
if(offset_id > mids[i]) {
break;
}
}
const topLoaded = messages.length - i;
const bottomLoaded = mids.includes(offset_id) ? i - 1 : i;
if(topWasMeantToLoad) isTopEnd = topLoaded < topWasMeantToLoad;
if(bottomWasMeantToLoad) isBottomEnd = bottomLoaded < bottomWasMeantToLoad;
if(isTopEnd || isBottomEnd) {
offsetIdOffset = isTopEnd ? count - topLoaded : bottomLoaded;
}
}
offsetIdOffset ??= 0;
return {count, offsetIdOffset, isTopEnd, isBottomEnd, mids};
} }
public mergeHistoryResult( public mergeHistoryResult(
@ -5926,11 +5962,8 @@ export class AppMessagesManager extends AppManager {
add_offset: number add_offset: number
) { ) {
const {messages} = historyResult as MessagesMessages.messagesMessagesSlice; const {messages} = historyResult as MessagesMessages.messagesMessagesSlice;
const isEnd = this.isHistoryResultEnd(historyResult, limit, add_offset); const isEnd = this.isHistoryResultEnd(historyResult, limit, add_offset, offset_id);
const {count, offsetIdOffset, isTopEnd, isBottomEnd} = isEnd; const {count, offsetIdOffset, isTopEnd, isBottomEnd, mids} = isEnd;
const mids = messages.map((message) => {
return (message as MyMessage).mid;
});
// * add bound manually. // * add bound manually.
// * offset_id will be inclusive only if there is 'add_offset' <= -1 (-1 - will only include the 'offset_id') // * offset_id will be inclusive only if there is 'add_offset' <= -1 (-1 - will only include the 'offset_id')

View File

@ -22,6 +22,7 @@ import {ReferenceContext} from '../mtproto/referenceDatabase';
import generateMessageId from './utils/messageId/generateMessageId'; import generateMessageId from './utils/messageId/generateMessageId';
import assumeType from '../../helpers/assumeType'; import assumeType from '../../helpers/assumeType';
import makeError from '../../helpers/makeError'; import makeError from '../../helpers/makeError';
import callbackify from '../../helpers/callbackify';
export type UserTyping = Partial<{userId: UserId, action: SendMessageAction, timeout: number}>; export type UserTyping = Partial<{userId: UserId, action: SendMessageAction, timeout: number}>;
@ -669,6 +670,18 @@ export class AppProfileManager extends AppManager {
return this.typingsInPeer[this.getTypingsKey(peerId, threadId)]; return this.typingsInPeer[this.getTypingsKey(peerId, threadId)];
} }
public canGiftPremium(userId: UserId) {
const user = this.appUsersManager.getUser(userId);
if(user?.pFlags?.premium || true) {
return false;
}
return callbackify(this.getProfile(userId), (userFull) => {
const user = this.appUsersManager.getUser(userId);
return !!userFull.premium_gifts && !user?.pFlags?.premium;
});
}
private onUpdateChatParticipants = (update: Update.updateChatParticipants) => { private onUpdateChatParticipants = (update: Update.updateChatParticipants) => {
const participants = update.participants; const participants = update.participants;
if(participants._ !== 'chatParticipants') { if(participants._ !== 'chatParticipants') {

View File

@ -1,6 +1,7 @@
import {Document, Message, MessageMedia} from '../../../../layer'; import {Document, Message, MessageMedia} from '../../../../layer';
export default function getMediaDurationFromMessage(message: Message.message) { export default function getMediaDurationFromMessage(message: Message.message) {
if(!message) return undefined;
const doc = (message.media as MessageMedia.messageMediaDocument)?.document as Document.document; const doc = (message.media as MessageMedia.messageMediaDocument)?.document as Document.document;
const duration = ((['voice', 'audio', 'video'] as Document.document['type'][]).includes(doc?.type) && doc.duration) || undefined; const duration = ((['voice', 'audio', 'video'] as Document.document['type'][]).includes(doc?.type) && doc.duration) || undefined;
return duration; return duration;

View File

@ -1475,6 +1475,10 @@ export default function wrapRichText(text: string, options: Partial<{
(element as HTMLAnchorElement).href = '#'; (element as HTMLAnchorElement).href = '#';
element.setAttribute('onclick', 'setMediaTimestamp(this)'); element.setAttribute('onclick', 'setMediaTimestamp(this)');
if(options.maxMediaTimestamp === Infinity) {
element.classList.add('is-disabled');
}
break; break;
} }
} }

View File

@ -686,6 +686,31 @@ $btn-menu-z-index: 4;
pointer-events: none !important; pointer-events: none !important;
opacity: var(--disabled-opacity); opacity: var(--disabled-opacity);
} }
&.shimmer {
&:before {
content: "";
position: absolute;
top: 0;
display: block;
width: 100%;
height: 100%;
background: linear-gradient(to right, transparent 0%, rgba(var(--surface-color-rgb), .2) 50%, transparent 100%);
animation: wave 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
@keyframes wave {
0% {
transform: translateX(-100%);
}
50% {
transform: translateX(100%);
}
100% {
transform: translateX(100%);
}
}
}
}
} }
.btn-control { .btn-control {

View File

@ -2475,6 +2475,12 @@ $bubble-border-radius-big: 12px;
// } // }
} }
.timestamp.is-disabled {
color: inherit;
text-decoration: none !important;
cursor: inherit;
}
@keyframes audio-dots { @keyframes audio-dots {
0% { 0% {
content: ""; content: "";

View File

@ -254,6 +254,19 @@ $row-border-radius: $border-radius-medium;
margin-inline: .125rem .125rem; margin-inline: .125rem .125rem;
padding: 0; padding: 0;
} }
&-absolute {
position: absolute;
margin: 0 !important;
padding: 0 !important;
left: 0;
}
&-round {
.checkbox-box-border {
z-index: unset;
}
}
} }
&-subtitle { &-subtitle {

View File

@ -0,0 +1,103 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
.popup-gift-premium {
$parent: ".popup";
#{$parent} {
&-container {
padding: 0;
width: 26.25rem;
max-width: 26.25rem;
max-height: unquote('min(100%, 43.5rem)');
border-radius: $border-radius-huge;
}
&-header {
height: 3.5rem;
margin: 0;
padding: 0 1rem;
margin-bottom: -2rem;
}
}
.scrollable-y {
flex: 1 1 auto;
padding: 0 1rem 1rem;
}
&-avatar {
display: block;
margin: 0 auto;
}
&-title,
&-subtitle {
text-align: center;
display: block;
}
&-title {
font-size: var(--font-size-20);
font-weight: var(--font-weight-bold);
margin: .75rem 0;
}
&-options {
display: flex;
flex-direction: column;
margin: .5rem 0 1rem;
}
&-option {
margin-top: .5rem;
&:nth-child(1) {
--primary-color: #C564F3;
}
&:nth-child(2) {
--primary-color: #AC64F3;
}
&:nth-child(3) {
--primary-color: #9377FF;
}
}
&-discount {
background-color: var(--primary-color);
border-radius: 6px;
color: #fff;
margin-right: .375rem;
padding: 0 0.3125rem;
height: 20px;
display: inline-block;
line-height: 20px;
}
&-confirm {
--ripple-color: rgba(255, 255, 255, #{$hover-alpha});
background: linear-gradient(88.39deg, #6C93FF -2.56%, #976FFF 51.27%, #DF69D1 107.39%) !important;
font-weight: var(--font-weight-bold);
color: #fff;
text-transform: uppercase;
@include hover() {
&:after {
content: " ";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #fff;
opacity: $hover-alpha;
}
}
}
}

View File

@ -119,6 +119,12 @@
.scrollable { .scrollable {
position: relative; position: relative;
} }
.scrollable-y-bordered {
&:last-child {
border-bottom: none;
}
}
} }
&-buttons { &-buttons {

View File

@ -411,6 +411,7 @@ $chat-input-inner-padding-handhelds: .25rem;
@import "partials/popups/paymentVerification"; @import "partials/popups/paymentVerification";
@import "partials/popups/paymentCardConfirmation"; @import "partials/popups/paymentCardConfirmation";
@import "partials/popups/limit"; @import "partials/popups/limit";
@import "partials/popups/giftPremium";
@import "partials/pages/pages"; @import "partials/pages/pages";
@import "partials/pages/authCode"; @import "partials/pages/authCode";

View File

@ -3,9 +3,9 @@
@font-face { @font-face {
font-family: '#{$tgico-font-family}'; font-family: '#{$tgico-font-family}';
src: src:
url('#{$tgico-font-path}/#{$tgico-font-family}.ttf?2fcrrv') format('truetype'), url('#{$tgico-font-path}/#{$tgico-font-family}.ttf?bv435t') format('truetype'),
url('#{$tgico-font-path}/#{$tgico-font-family}.woff?2fcrrv') format('woff'), url('#{$tgico-font-path}/#{$tgico-font-family}.woff?bv435t') format('woff'),
url('#{$tgico-font-path}/#{$tgico-font-family}.svg?2fcrrv##{$tgico-font-family}') format('svg'); url('#{$tgico-font-path}/#{$tgico-font-family}.svg?bv435t##{$tgico-font-family}') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: block; font-display: block;
@ -432,6 +432,11 @@
content: $tgico-gifs; content: $tgico-gifs;
} }
} }
.tgico-gift {
&:before {
content: $tgico-gift;
}
}
.tgico-group { .tgico-group {
&:before { &:before {
content: $tgico-group; content: $tgico-group;

View File

@ -78,135 +78,136 @@ $tgico-fullscreen: "\e94c";
$tgico-gc_microphone: "\e94d"; $tgico-gc_microphone: "\e94d";
$tgico-gc_microphoneoff: "\e94e"; $tgico-gc_microphoneoff: "\e94e";
$tgico-gifs: "\e94f"; $tgico-gifs: "\e94f";
$tgico-group: "\e950"; $tgico-gift: "\e950";
$tgico-groupmedia: "\e951"; $tgico-group: "\e951";
$tgico-groupmediaoff: "\e952"; $tgico-groupmedia: "\e952";
$tgico-help: "\e953"; $tgico-groupmediaoff: "\e953";
$tgico-hide: "\e954"; $tgico-help: "\e954";
$tgico-image: "\e955"; $tgico-hide: "\e955";
$tgico-info: "\e956"; $tgico-image: "\e956";
$tgico-info2: "\e957"; $tgico-info: "\e957";
$tgico-italic: "\e958"; $tgico-info2: "\e958";
$tgico-keyboard: "\e959"; $tgico-italic: "\e959";
$tgico-lamp: "\e95a"; $tgico-keyboard: "\e95a";
$tgico-language: "\e95b"; $tgico-lamp: "\e95b";
$tgico-largepause: "\e95c"; $tgico-language: "\e95c";
$tgico-largeplay: "\e95d"; $tgico-largepause: "\e95d";
$tgico-left: "\e95e"; $tgico-largeplay: "\e95e";
$tgico-limit_chat: "\e95f"; $tgico-left: "\e95f";
$tgico-limit_chats: "\e960"; $tgico-limit_chat: "\e960";
$tgico-limit_file: "\e961"; $tgico-limit_chats: "\e961";
$tgico-limit_folders: "\e962"; $tgico-limit_file: "\e962";
$tgico-limit_link: "\e963"; $tgico-limit_folders: "\e963";
$tgico-limit_pin: "\e964"; $tgico-limit_link: "\e964";
$tgico-link: "\e965"; $tgico-limit_pin: "\e965";
$tgico-listscreenshare: "\e966"; $tgico-link: "\e966";
$tgico-livelocation: "\e967"; $tgico-listscreenshare: "\e967";
$tgico-location: "\e968"; $tgico-livelocation: "\e968";
$tgico-lock: "\e969"; $tgico-location: "\e969";
$tgico-lockoff: "\e96a"; $tgico-lock: "\e96a";
$tgico-loginlogodesktop: "\e96b"; $tgico-lockoff: "\e96b";
$tgico-loginlogomobile: "\e96c"; $tgico-loginlogodesktop: "\e96c";
$tgico-logout: "\e96d"; $tgico-loginlogomobile: "\e96d";
$tgico-mediaspoiler: "\e96e"; $tgico-logout: "\e96e";
$tgico-mediaspoileroff: "\e96f"; $tgico-mediaspoiler: "\e96f";
$tgico-mention: "\e970"; $tgico-mediaspoileroff: "\e970";
$tgico-menu: "\e971"; $tgico-mention: "\e971";
$tgico-message: "\e972"; $tgico-menu: "\e972";
$tgico-messageunread: "\e973"; $tgico-message: "\e973";
$tgico-microphone: "\e974"; $tgico-messageunread: "\e974";
$tgico-microphone_crossed: "\e975"; $tgico-microphone: "\e975";
$tgico-microphone_crossed_filled: "\e976"; $tgico-microphone_crossed: "\e976";
$tgico-microphone_filled: "\e977"; $tgico-microphone_crossed_filled: "\e977";
$tgico-minus: "\e978"; $tgico-microphone_filled: "\e978";
$tgico-monospace: "\e979"; $tgico-minus: "\e979";
$tgico-more: "\e97a"; $tgico-monospace: "\e97a";
$tgico-mute: "\e97b"; $tgico-more: "\e97b";
$tgico-muted: "\e97c"; $tgico-mute: "\e97c";
$tgico-newchannel: "\e97d"; $tgico-muted: "\e97d";
$tgico-newchat_filled: "\e97e"; $tgico-newchannel: "\e97e";
$tgico-newgroup: "\e97f"; $tgico-newchat_filled: "\e97f";
$tgico-newprivate: "\e980"; $tgico-newgroup: "\e980";
$tgico-next: "\e981"; $tgico-newprivate: "\e981";
$tgico-noncontacts: "\e982"; $tgico-next: "\e982";
$tgico-nosound: "\e983"; $tgico-noncontacts: "\e983";
$tgico-passwordoff: "\e984"; $tgico-nosound: "\e984";
$tgico-pause: "\e985"; $tgico-passwordoff: "\e985";
$tgico-permissions: "\e986"; $tgico-pause: "\e986";
$tgico-phone: "\e987"; $tgico-permissions: "\e987";
$tgico-pin: "\e988"; $tgico-phone: "\e988";
$tgico-pinlist: "\e989"; $tgico-pin: "\e989";
$tgico-pinned_filled: "\e98a"; $tgico-pinlist: "\e98a";
$tgico-pinnedchat: "\e98b"; $tgico-pinned_filled: "\e98b";
$tgico-pip: "\e98c"; $tgico-pinnedchat: "\e98c";
$tgico-play: "\e98d"; $tgico-pip: "\e98d";
$tgico-playback_05: "\e98e"; $tgico-play: "\e98e";
$tgico-playback_15: "\e98f"; $tgico-playback_05: "\e98f";
$tgico-playback_1x: "\e990"; $tgico-playback_15: "\e990";
$tgico-playback_2x: "\e991"; $tgico-playback_1x: "\e991";
$tgico-plus: "\e992"; $tgico-playback_2x: "\e992";
$tgico-poll: "\e993"; $tgico-plus: "\e993";
$tgico-premium_addone: "\e994"; $tgico-poll: "\e994";
$tgico-premium_double: "\e995"; $tgico-premium_addone: "\e995";
$tgico-premium_lock: "\e996"; $tgico-premium_double: "\e996";
$tgico-premium_unlock: "\e997"; $tgico-premium_lock: "\e997";
$tgico-previous: "\e998"; $tgico-premium_unlock: "\e998";
$tgico-radiooff: "\e999"; $tgico-previous: "\e999";
$tgico-radioon: "\e99a"; $tgico-radiooff: "\e99a";
$tgico-reactions: "\e99b"; $tgico-radioon: "\e99b";
$tgico-readchats: "\e99c"; $tgico-reactions: "\e99c";
$tgico-recent: "\e99d"; $tgico-readchats: "\e99d";
$tgico-replace: "\e99e"; $tgico-recent: "\e99e";
$tgico-reply: "\e99f"; $tgico-replace: "\e99f";
$tgico-reply_filled: "\e9a0"; $tgico-reply: "\e9a0";
$tgico-rightpanel: "\e9a1"; $tgico-reply_filled: "\e9a1";
$tgico-rotate_left: "\e9a2"; $tgico-rightpanel: "\e9a2";
$tgico-rotate_right: "\e9a3"; $tgico-rotate_left: "\e9a3";
$tgico-saved: "\e9a4"; $tgico-rotate_right: "\e9a4";
$tgico-savedmessages: "\e9a5"; $tgico-saved: "\e9a5";
$tgico-schedule: "\e9a6"; $tgico-savedmessages: "\e9a6";
$tgico-scheduled: "\e9a7"; $tgico-schedule: "\e9a7";
$tgico-search: "\e9a8"; $tgico-scheduled: "\e9a8";
$tgico-select: "\e9a9"; $tgico-search: "\e9a9";
$tgico-send: "\e9aa"; $tgico-select: "\e9aa";
$tgico-send2: "\e9ab"; $tgico-send: "\e9ab";
$tgico-sending: "\e9ac"; $tgico-send2: "\e9ac";
$tgico-sendingerror: "\e9ad"; $tgico-sending: "\e9ad";
$tgico-settings: "\e9ae"; $tgico-sendingerror: "\e9ae";
$tgico-settings_filled: "\e9af"; $tgico-settings: "\e9af";
$tgico-sharescreen_filled: "\e9b0"; $tgico-settings_filled: "\e9b0";
$tgico-shipping: "\e9b1"; $tgico-sharescreen_filled: "\e9b1";
$tgico-shuffle: "\e9b2"; $tgico-shipping: "\e9b2";
$tgico-smallscreen: "\e9b3"; $tgico-shuffle: "\e9b3";
$tgico-smile: "\e9b4"; $tgico-smallscreen: "\e9b4";
$tgico-spoiler: "\e9b5"; $tgico-smile: "\e9b5";
$tgico-sport: "\e9b6"; $tgico-spoiler: "\e9b6";
$tgico-star: "\e9b7"; $tgico-sport: "\e9b7";
$tgico-stickers: "\e9b8"; $tgico-star: "\e9b8";
$tgico-stickers_face: "\e9b9"; $tgico-stickers: "\e9b9";
$tgico-stop: "\e9ba"; $tgico-stickers_face: "\e9ba";
$tgico-strikethrough: "\e9bb"; $tgico-stop: "\e9bb";
$tgico-textedit: "\e9bc"; $tgico-strikethrough: "\e9bc";
$tgico-tip: "\e9bd"; $tgico-textedit: "\e9bd";
$tgico-tools: "\e9be"; $tgico-tip: "\e9be";
$tgico-topics: "\e9bf"; $tgico-tools: "\e9bf";
$tgico-transcribe: "\e9c0"; $tgico-topics: "\e9c0";
$tgico-unarchive: "\e9c1"; $tgico-transcribe: "\e9c1";
$tgico-underline: "\e9c2"; $tgico-unarchive: "\e9c2";
$tgico-unmute: "\e9c3"; $tgico-underline: "\e9c3";
$tgico-unpin: "\e9c4"; $tgico-unmute: "\e9c4";
$tgico-unread: "\e9c5"; $tgico-unpin: "\e9c5";
$tgico-up: "\e9c6"; $tgico-unread: "\e9c6";
$tgico-user: "\e9c7"; $tgico-up: "\e9c7";
$tgico-username: "\e9c8"; $tgico-user: "\e9c8";
$tgico-videocamera: "\e9c9"; $tgico-username: "\e9c9";
$tgico-videocamera_crossed_filled: "\e9ca"; $tgico-videocamera: "\e9ca";
$tgico-videocamera_filled: "\e9cb"; $tgico-videocamera_crossed_filled: "\e9cb";
$tgico-videochat: "\e9cc"; $tgico-videocamera_filled: "\e9cc";
$tgico-volume_down: "\e9cd"; $tgico-videochat: "\e9cd";
$tgico-volume_mute: "\e9ce"; $tgico-volume_down: "\e9ce";
$tgico-volume_off: "\e9cf"; $tgico-volume_mute: "\e9cf";
$tgico-volume_up: "\e9d0"; $tgico-volume_off: "\e9d0";
$tgico-zoomin: "\e9d1"; $tgico-volume_up: "\e9d1";
$tgico-zoomout: "\e9d2"; $tgico-zoomin: "\e9d2";
$tgico-zoomout: "\e9d3";