Browse Source

Custom reactions

master
Eduard Kuzmenko 2 years ago committed by r4sas
parent
commit
1135fbfa64
  1. 4
      src/components/appSearchSuper..ts
  2. 2
      src/components/audio.ts
  3. 21
      src/components/chat/bubbleGroups.ts
  4. 84
      src/components/chat/bubbles.ts
  5. 29
      src/components/chat/contextMenu.ts
  6. 2
      src/components/chat/inlineHelper.ts
  7. 2
      src/components/chat/input.ts
  8. 36
      src/components/chat/messageRender.ts
  9. 86
      src/components/chat/reaction.ts
  10. 60
      src/components/chat/reactions.ts
  11. 8
      src/components/chat/reactionsMenu.ts
  12. 4
      src/components/chat/replyContainer.ts
  13. 3
      src/components/chat/sendAs.ts
  14. 7
      src/components/emoticonsDropdown/index.ts
  15. 117
      src/components/emoticonsDropdown/tabs/emoji.ts
  16. 227
      src/components/emoticonsDropdown/tabs/stickers.ts
  17. 8
      src/components/generateTitleIcons.ts
  18. 2
      src/components/gifsMasonry.ts
  19. 2
      src/components/peerProfileAvatars.ts
  20. 12
      src/components/peerTitle.ts
  21. 2
      src/components/popups/joinChatInvite.ts
  22. 47
      src/components/popups/mute.ts
  23. 2
      src/components/popups/newMedia.ts
  24. 2
      src/components/popups/payment.ts
  25. 52
      src/components/popups/reactedList.ts
  26. 31
      src/components/popups/stickers.ts
  27. 21
      src/components/row.ts
  28. 2
      src/components/sidebarLeft/tabs/background.ts
  29. 15
      src/components/sidebarLeft/tabs/generalSettings.ts
  30. 2
      src/components/sidebarLeft/tabs/peopleNearby.ts
  31. 7
      src/components/sidebarLeft/tabs/quickReaction.ts
  32. 178
      src/components/sidebarRight/tabs/chatReactions.ts
  33. 14
      src/components/sidebarRight/tabs/editChat.ts
  34. 2
      src/components/sidebarRight/tabs/stickers.ts
  35. 2
      src/components/stickerViewer.ts
  36. 30
      src/components/wrappers.ts
  37. 53
      src/components/wrappers/customEmoji.ts
  38. 2
      src/config/app.ts
  39. 2
      src/global.d.ts
  40. 2
      src/helpers/callbackify.ts
  41. 2
      src/helpers/callbackifyAll.ts
  42. 3
      src/helpers/schedulers/debounce.ts
  43. 24
      src/index.hbs
  44. 8
      src/lang.ts
  45. 454
      src/layer.d.ts
  46. 9
      src/lib/appManagers/appChatsManager.ts
  47. 2
      src/lib/appManagers/appDialogsManager.ts
  48. 4
      src/lib/appManagers/appEmojiManager.ts
  49. 13
      src/lib/appManagers/appMessagesManager.ts
  50. 141
      src/lib/appManagers/appReactionsManager.ts
  51. 36
      src/lib/appManagers/appStickersManager.ts
  52. 5
      src/lib/appManagers/uiNotificationsManager.ts
  53. 6
      src/lib/appManagers/utils/reactions/reactionsEqual.ts
  54. 4
      src/lib/mtproto/appConfig.d.ts
  55. 2
      src/lib/mtproto/schema.ts
  56. 14
      src/lib/richTextProcessor/wrapRichText.ts
  57. 4
      src/lib/rootScope.ts
  58. 4
      src/scripts/in/schema.json
  59. 2
      src/scripts/out/schema.json
  60. 24
      src/scss/partials/_avatar.scss
  61. 5
      src/scss/partials/_chatBubble.scss
  62. 10
      src/scss/partials/_customEmoji.scss
  63. 67
      src/scss/partials/_emojiDropdown.scss
  64. 16
      src/scss/partials/_leftSidebar.scss
  65. 16
      src/scss/partials/_reaction.scss
  66. 4
      src/scss/partials/_row.scss
  67. 11
      src/scss/partials/popups/_reactedList.scss

4
src/components/appSearchSuper..ts

@ -14,7 +14,6 @@ import LazyLoadQueue from './lazyLoadQueue';
import {putPreloader} from './putPreloader'; import {putPreloader} from './putPreloader';
import ripple from './ripple'; import ripple from './ripple';
import Scrollable, {ScrollableX} from './scrollable'; import Scrollable, {ScrollableX} from './scrollable';
import {wrapDocument, wrapPhoto, wrapVideo} from './wrappers';
import useHeavyAnimationCheck, {getHeavyAnimationPromise} from '../hooks/useHeavyAnimationCheck'; import useHeavyAnimationCheck, {getHeavyAnimationPromise} from '../hooks/useHeavyAnimationCheck';
import I18n, {LangPackKey, i18n} from '../lib/langPack'; import I18n, {LangPackKey, i18n} from '../lib/langPack';
import findUpClassName from '../helpers/dom/findUpClassName'; import findUpClassName from '../helpers/dom/findUpClassName';
@ -71,6 +70,9 @@ import positionMenu from '../helpers/positionMenu';
import apiManagerProxy from '../lib/mtproto/mtprotoworker'; import apiManagerProxy from '../lib/mtproto/mtprotoworker';
import ListenerSetter from '../helpers/listenerSetter'; import ListenerSetter from '../helpers/listenerSetter';
import SwipeHandler from './swipeHandler'; import SwipeHandler from './swipeHandler';
import wrapDocument from './wrappers/document';
import wrapPhoto from './wrappers/photo';
import wrapVideo from './wrappers/video';
// const testScroll = false; // const testScroll = false;

2
src/components/audio.ts

@ -5,7 +5,6 @@
*/ */
import type {MyDocument} from '../lib/appManagers/appDocsManager'; import type {MyDocument} from '../lib/appManagers/appDocsManager';
import {wrapPhoto} from './wrappers';
import ProgressivePreloader from './preloader'; import ProgressivePreloader from './preloader';
import appMediaPlaybackController, {MediaItem, MediaSearchContext} from './appMediaPlaybackController'; import appMediaPlaybackController, {MediaItem, MediaSearchContext} from './appMediaPlaybackController';
import {DocumentAttribute, Message} from '../layer'; import {DocumentAttribute, Message} from '../layer';
@ -36,6 +35,7 @@ import wrapSenderToPeer from './wrappers/senderToPeer';
import wrapSentTime from './wrappers/sentTime'; import wrapSentTime from './wrappers/sentTime';
import getMediaFromMessage from '../lib/appManagers/utils/messages/getMediaFromMessage'; import getMediaFromMessage from '../lib/appManagers/utils/messages/getMediaFromMessage';
import appDownloadManager from '../lib/appManagers/appDownloadManager'; import appDownloadManager from '../lib/appManagers/appDownloadManager';
import wrapPhoto from './wrappers/photo';
rootScope.addEventListener('messages_media_read', ({mids, peerId}) => { rootScope.addEventListener('messages_media_read', ({mids, peerId}) => {
mids.forEach((mid) => { mids.forEach((mid) => {

21
src/components/chat/bubbleGroups.ts

@ -77,16 +77,17 @@ class BubbleGroup {
peerTitle: !fwdFromId && fwdFrom && fwdFrom.from_name ? /* '🔥 FF 🔥' */fwdFrom.from_name : undefined peerTitle: !fwdFromId && fwdFrom && fwdFrom.from_name ? /* '🔥 FF 🔥' */fwdFrom.from_name : undefined
}); });
this.avatarLoadPromise = Promise.all([ // this.avatarLoadPromise = Promise.all([
avatarLoadPromise, // avatarLoadPromise,
peerId && peerId.isUser() ? this.chat.managers.appUsersManager.getUser(peerId.toUserId()) : undefined // peerId && peerId.isUser() ? this.chat.managers.appUsersManager.getUser(peerId.toUserId()) : undefined
]).then(([result, user]) => { // ]).then(([result, user]) => {
if(user?.pFlags?.premium) { // if(user?.pFlags?.premium) {
avatar.classList.add('is-premium', 'tgico-star'); // avatar.classList.add('is-premium', 'tgico-star');
} // }
return result; // return result;
}); // });
this.avatarLoadPromise = avatarLoadPromise;
this.avatarContainer.append(this.avatar); this.avatarContainer.append(this.avatar);
this.container.append(this.avatarContainer); this.container.append(this.avatarContainer);

84
src/components/chat/bubbles.ts

@ -25,7 +25,6 @@ import {IS_ANDROID, IS_APPLE, IS_MOBILE, IS_SAFARI} from '../../environment/user
import I18n, {FormatterArguments, i18n, langPack, LangPackKey, UNSUPPORTED_LANG_PACK_KEY, _i18n} from '../../lib/langPack'; import I18n, {FormatterArguments, i18n, langPack, LangPackKey, UNSUPPORTED_LANG_PACK_KEY, _i18n} from '../../lib/langPack';
import AvatarElement from '../avatar'; import AvatarElement from '../avatar';
import ripple from '../ripple'; import ripple from '../ripple';
import {wrapAlbum, wrapPhoto, wrapVideo, wrapDocument, wrapSticker, wrapPoll, wrapGroupedDocuments, wrapStickerAnimation} from '../wrappers';
import {MessageRender} from './messageRender'; import {MessageRender} from './messageRender';
import LazyLoadQueue from '../lazyLoadQueue'; import LazyLoadQueue from '../lazyLoadQueue';
import ListenerSetter from '../../helpers/listenerSetter'; import ListenerSetter from '../../helpers/listenerSetter';
@ -114,12 +113,15 @@ import isInDOM from '../../helpers/dom/isInDOM';
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb'; import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
import attachStickerViewerListeners from '../stickerViewer'; import attachStickerViewerListeners from '../stickerViewer';
import {makeMediaSize, MediaSize} from '../../helpers/mediaSize'; import {makeMediaSize, MediaSize} from '../../helpers/mediaSize';
import lottieLoader from '../../lib/rlottie/lottieLoader'; import wrapSticker, {onEmojiStickerClick} from '../wrappers/sticker';
import appDownloadManager from '../../lib/appManagers/appDownloadManager'; import wrapAlbum from '../wrappers/album';
import onMediaLoad from '../../helpers/onMediaLoad'; import wrapDocument from '../wrappers/document';
import throttle from '../../helpers/schedulers/throttle'; import wrapGroupedDocuments from '../wrappers/groupedDocuments';
import {onEmojiStickerClick} from '../wrappers/sticker'; import wrapPhoto from '../wrappers/photo';
import wrapPoll from '../wrappers/poll';
import wrapVideo from '../wrappers/video';
export const USER_REACTIONS_INLINE = false;
const USE_MEDIA_TAILS = false; const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([ const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
'messageActionHistoryClear', 'messageActionHistoryClear',
@ -151,6 +153,17 @@ const DO_NOT_UPDATE_MESSAGE_VIEWS = false;
const DO_NOT_UPDATE_MESSAGE_REACTIONS = false; const DO_NOT_UPDATE_MESSAGE_REACTIONS = false;
const DO_NOT_UPDATE_MESSAGE_REPLY = false; const DO_NOT_UPDATE_MESSAGE_REPLY = false;
const BIG_EMOJI_SIZES: {[size: number]: number} = {
1: 96,
2: 90,
3: 84,
4: 72,
5: 60,
6: 48,
7: 36
};
const BIG_EMOJI_SIZES_LENGTH = Object.keys(BIG_EMOJI_SIZES).length;
type Bubble = { type Bubble = {
bubble: HTMLElement, bubble: HTMLElement,
mids: Set<number>, mids: Set<number>,
@ -600,20 +613,20 @@ export default class ChatBubbles {
this.safeRenderMessage(message, true, bubble); this.safeRenderMessage(message, true, bubble);
}); });
this.listenerSetter.add(rootScope)('peer_title_edit', async(peerId) => { // this.listenerSetter.add(rootScope)('peer_title_edit', async(peerId) => {
if(peerId.isUser()) { // if(peerId.isUser()) {
const middleware = this.getMiddleware(); // const middleware = this.getMiddleware();
const user = await this.managers.appUsersManager.getUser(peerId.toUserId()); // const user = await this.managers.appUsersManager.getUser(peerId.toUserId());
if(!middleware()) return; // if(!middleware()) return;
const isPremium = user?.pFlags?.premium; // const isPremium = user?.pFlags?.premium;
const groups = this.bubbleGroups.groups.filter((group) => group.avatar?.peerId === peerId); // const groups = this.bubbleGroups.groups.filter((group) => group.avatar?.peerId === peerId);
groups.forEach((group) => { // groups.forEach((group) => {
group.avatar.classList.toggle('is-premium', isPremium); // group.avatar.classList.toggle('is-premium', isPremium);
group.avatar.classList.toggle('tgico-star', isPremium); // group.avatar.classList.toggle('tgico-star', isPremium);
}); // });
} // }
}); // });
if(this.chat.type !== 'scheduled' && !DO_NOT_UPDATE_MESSAGE_REACTIONS/* && false */) { if(this.chat.type !== 'scheduled' && !DO_NOT_UPDATE_MESSAGE_REACTIONS/* && false */) {
this.listenerSetter.add(rootScope)('messages_reactions', async(arr) => { this.listenerSetter.add(rootScope)('messages_reactions', async(arr) => {
@ -1268,7 +1281,7 @@ export default class ChatBubbles {
attachClickEvent(hoverReaction, (e) => { attachClickEvent(hoverReaction, (e) => {
cancelEvent(e); // cancel triggering selection cancelEvent(e); // cancel triggering selection
this.managers.appReactionsManager.sendReaction(message, availableReaction.reaction); this.managers.appReactionsManager.sendReaction(message, availableReaction);
this.unhoverPrevious(); this.unhoverPrevious();
}, {listenerSetter: this.listenerSetter}); }, {listenerSetter: this.listenerSetter});
}, noop); }, noop);
@ -1557,7 +1570,7 @@ export default class ChatBubbles {
} }
const stickerEmojiEl = findUpAttribute(target, 'data-sticker-emoji'); const stickerEmojiEl = findUpAttribute(target, 'data-sticker-emoji');
if(stickerEmojiEl) { if(stickerEmojiEl && stickerEmojiEl.parentElement.querySelectorAll('[data-sticker-emoji]').length === 1 && bubble.classList.contains('emoji-big')) {
onEmojiStickerClick({ onEmojiStickerClick({
event: e, event: e,
container: stickerEmojiEl, container: stickerEmojiEl,
@ -3576,17 +3589,11 @@ export default class ChatBubbles {
const emojiStrLength = emojiEntities.reduce((acc, curr) => acc + curr.length, 0); const emojiStrLength = emojiEntities.reduce((acc, curr) => acc + curr.length, 0);
if(emojiStrLength === strLength /* && emojiEntities.length <= 3 *//* && totalEntities.length === emojiEntities.length */) { if(emojiStrLength === strLength /* && emojiEntities.length <= 3 *//* && totalEntities.length === emojiEntities.length */) {
bigEmojis = Math.min(4, emojiEntities.length); bigEmojis = Math.min(BIG_EMOJI_SIZES_LENGTH, emojiEntities.length);
customEmojiSize = mediaSizes.active.customEmoji; customEmojiSize = mediaSizes.active.customEmoji;
const sizes: {[size: number]: number} = {
1: 96,
2: 64,
3: 52,
4: 36
};
const size = sizes[bigEmojis]; const size = BIG_EMOJI_SIZES[bigEmojis];
if(size) { if(size) {
customEmojiSize = makeMediaSize(size, size); customEmojiSize = makeMediaSize(size, size);
bubble.style.setProperty('--emoji-size', size + 'px'); bubble.style.setProperty('--emoji-size', size + 'px');
@ -4408,7 +4415,7 @@ export default class ChatBubbles {
// title = fwdFrom.from_name; // title = fwdFrom.from_name;
bubble.classList.add('hidden-profile'); bubble.classList.add('hidden-profile');
} else { } else {
title = new PeerTitle({peerId: fwdFromId || message.fromId}).element; title = new PeerTitle({peerId: fwdFromId || message.fromId, withPremiumIcon: true}).element;
} }
if(message.reply_to_mid && message.reply_to_mid !== this.chat.threadId && isMessage) { if(message.reply_to_mid && message.reply_to_mid !== this.chat.threadId && isMessage) {
@ -4438,6 +4445,7 @@ export default class ChatBubbles {
if((this.peerId === rootScope.myId || this.peerId === REPLIES_PEER_ID || isForwardFromChannel) && !isStandaloneMedia) { if((this.peerId === rootScope.myId || this.peerId === REPLIES_PEER_ID || isForwardFromChannel) && !isStandaloneMedia) {
nameDiv.style.color = getPeerColorById(fwdFromId, false); nameDiv.style.color = getPeerColorById(fwdFromId, false);
nameDiv.classList.add('colored-name');
nameDiv.append(title); nameDiv.append(title);
} else { } else {
/* const fromTitle = message.fromId === this.myID || appPeersManager.isBroadcast(fwdFromId || message.fromId) ? '' : `<div class="name" data-peer-id="${message.fromId}" style="color: ${appPeersManager.getPeerColorByID(message.fromId, false)};">${appPeersManager.getPeerTitle(message.fromId)}</div>`; /* const fromTitle = message.fromId === this.myID || appPeersManager.isBroadcast(fwdFromId || message.fromId) ? '' : `<div class="name" data-peer-id="${message.fromId}" style="color: ${appPeersManager.getPeerColorByID(message.fromId, false)};">${appPeersManager.getPeerTitle(message.fromId)}</div>`;
@ -4461,6 +4469,7 @@ export default class ChatBubbles {
if(!our) { if(!our) {
nameDiv.style.color = getPeerColorById(message.fromId, false); nameDiv.style.color = getPeerColorById(message.fromId, false);
nameDiv.classList.add('colored-name');
} }
nameDiv.dataset.peerId = '' + message.fromId; nameDiv.dataset.peerId = '' + message.fromId;
@ -4546,8 +4555,13 @@ export default class ChatBubbles {
return ret; return ret;
} }
private appendReactionsElementToBubble(bubble: HTMLElement, message: Message.message, reactionsMessage: Message.message, changedResults?: ReactionCount[]) { private appendReactionsElementToBubble(
if(this.peerId.isUser()/* || true */) { bubble: HTMLElement,
message: Message.message,
reactionsMessage: Message.message,
changedResults?: ReactionCount[]
) {
if(this.peerId.isUser() && USER_REACTIONS_INLINE/* || true */) {
return; return;
} }
@ -4558,7 +4572,7 @@ export default class ChatBubbles {
// message = this.appMessagesManager.getMessageWithReactions(message); // message = this.appMessagesManager.getMessageWithReactions(message);
const reactionsElement = new ReactionsElement(); const reactionsElement = new ReactionsElement();
reactionsElement.init(reactionsMessage, 'block'); reactionsElement.init(reactionsMessage, 'block', bubble.middlewareHelper.get());
reactionsElement.render(changedResults); reactionsElement.render(changedResults);
if(bubble.classList.contains('is-message-empty')) { if(bubble.classList.contains('is-message-empty')) {

29
src/components/chat/contextMenu.ts

@ -19,7 +19,7 @@ import findUpClassName from '../../helpers/dom/findUpClassName';
import cancelEvent from '../../helpers/dom/cancelEvent'; import cancelEvent from '../../helpers/dom/cancelEvent';
import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent'; import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
import isSelectionEmpty from '../../helpers/dom/isSelectionEmpty'; import isSelectionEmpty from '../../helpers/dom/isSelectionEmpty';
import {Message, Poll, Chat as MTChat, MessageMedia, AvailableReaction, MessageEntity, InputStickerSet, StickerSet, Document} from '../../layer'; import {Message, Poll, Chat as MTChat, MessageMedia, AvailableReaction, MessageEntity, InputStickerSet, StickerSet, Document, Reaction} from '../../layer';
import PopupReportMessages from '../popups/reportMessages'; import PopupReportMessages from '../popups/reportMessages';
import assumeType from '../../helpers/assumeType'; import assumeType from '../../helpers/assumeType';
import PopupSponsored from '../popups/sponsored'; import PopupSponsored from '../popups/sponsored';
@ -545,15 +545,30 @@ export default class ChatContextMenu {
new PopupStickers(inputs, true).show(); new PopupStickers(inputs, true).show();
}); });
}, },
verify: () => { verify: () => !!this.getUniqueCustomEmojisFromMessage(this.message).length,
const entities = (this.message as Message.message).entities;
return entities?.some((entity) => entity._ === 'messageEntityCustomEmoji');
},
notDirect: () => true, notDirect: () => true,
localName: 'emojis' localName: 'emojis'
}]; }];
} }
private getUniqueCustomEmojisFromMessage(message: Message) {
const docIds: DocId[] = [];
const entities = (message as Message.message).entities;
if(entities) {
const filtered = entities.filter((entity) => entity._ === 'messageEntityCustomEmoji') as MessageEntity.messageEntityCustomEmoji[];
docIds.push(...filtered.map((entity) => entity.document_id));
}
const reactions = (message as Message.message).reactions;
if(reactions) {
const results = reactions.results.filter((reactionCount) => reactionCount.reaction._ === 'reactionCustomEmoji');
docIds.push(...results.map((reactionCount) => (reactionCount.reaction as Reaction.reactionCustomEmoji).document_id));
}
return filterUnique(docIds);
}
private async init() { private async init() {
this.cleanup(); this.cleanup();
this.setButtons(); this.setButtons();
@ -711,9 +726,9 @@ export default class ChatContextMenu {
menuPadding.bottom = 24; menuPadding.bottom = 24;
}; };
const entities = (this.message as Message.message).entities.filter((entity) => entity._ === 'messageEntityCustomEmoji') as MessageEntity.messageEntityCustomEmoji[]; const docIds = this.getUniqueCustomEmojisFromMessage(this.message);
const docIds = filterUnique(entities.map((entity) => entity.document_id));
const inputsPromise = this.emojiInputsPromise = deferredPromise(); const inputsPromise = this.emojiInputsPromise = deferredPromise();
await this.managers.appEmojiManager.getCachedCustomEmojiDocuments(docIds).then(async(docs) => { await this.managers.appEmojiManager.getCachedCustomEmojiDocuments(docIds).then(async(docs) => {
const p = async(docs: Document.document[]) => { const p = async(docs: Document.document[]) => {
const s: Map<StickerSet['id'], InputStickerSet.inputStickerSetID> = new Map(); const s: Map<StickerSet['id'], InputStickerSet.inputStickerSetID> = new Map();

2
src/components/chat/inlineHelper.ts

@ -10,7 +10,6 @@ import {WebDocument} from '../../layer';
import {MyDocument} from '../../lib/appManagers/appDocsManager'; import {MyDocument} from '../../lib/appManagers/appDocsManager';
import LazyLoadQueue from '../lazyLoadQueue'; import LazyLoadQueue from '../lazyLoadQueue';
import Scrollable from '../scrollable'; import Scrollable from '../scrollable';
import {wrapPhoto} from '../wrappers';
import AutocompleteHelper from './autocompleteHelper'; import AutocompleteHelper from './autocompleteHelper';
import AutocompleteHelperController from './autocompleteHelperController'; import AutocompleteHelperController from './autocompleteHelperController';
import Button from '../button'; import Button from '../button';
@ -29,6 +28,7 @@ import wrapRichText from '../../lib/richTextProcessor/wrapRichText';
import generateQId from '../../lib/appManagers/utils/inlineBots/generateQId'; import generateQId from '../../lib/appManagers/utils/inlineBots/generateQId';
import appDownloadManager from '../../lib/appManagers/appDownloadManager'; import appDownloadManager from '../../lib/appManagers/appDownloadManager';
import {AnimationItemGroup} from '../animationIntersector'; import {AnimationItemGroup} from '../animationIntersector';
import wrapPhoto from '../wrappers/photo';
const ANIMATION_GROUP: AnimationItemGroup = 'INLINE-HELPER'; const ANIMATION_GROUP: AnimationItemGroup = 'INLINE-HELPER';
// const GRID_ITEMS = 5; // const GRID_ITEMS = 5;

2
src/components/chat/input.ts

@ -18,7 +18,6 @@ import PopupCreatePoll from '../popups/createPoll';
import PopupForward from '../popups/forward'; import PopupForward from '../popups/forward';
import PopupNewMedia from '../popups/newMedia'; import PopupNewMedia from '../popups/newMedia';
import {toast, toastNew} from '../toast'; import {toast, toastNew} from '../toast';
import {wrapReply} from '../wrappers';
import {MessageEntity, DraftMessage, WebPage, Message, UserFull} from '../../layer'; import {MessageEntity, DraftMessage, WebPage, Message, UserFull} from '../../layer';
import StickersHelper from './stickersHelper'; import StickersHelper from './stickersHelper';
import ButtonIcon from '../buttonIcon'; import ButtonIcon from '../buttonIcon';
@ -96,6 +95,7 @@ import InputFieldAnimated from '../inputFieldAnimated';
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb'; import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
import PopupStickers from '../popups/stickers'; import PopupStickers from '../popups/stickers';
import wrapPeerTitle from '../wrappers/peerTitle'; import wrapPeerTitle from '../wrappers/peerTitle';
import wrapReply from '../wrappers/reply';
const RECORD_MIN_TIME = 500; const RECORD_MIN_TIME = 500;
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.'; const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';

36
src/components/chat/messageRender.ts

@ -14,9 +14,8 @@ import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
import rootScope from '../../lib/rootScope'; import rootScope from '../../lib/rootScope';
import type LazyLoadQueue from '../lazyLoadQueue'; import type LazyLoadQueue from '../lazyLoadQueue';
import PeerTitle from '../peerTitle'; import PeerTitle from '../peerTitle';
import {wrapReply} from '../wrappers'; import wrapReply from '../wrappers/reply';
import Chat, {ChatType} from './chat'; import Chat, {ChatType} from './chat';
import ReactionsElement from './reactions';
import RepliesElement from './replies'; import RepliesElement from './replies';
const NBSP = '&nbsp;'; const NBSP = '&nbsp;';
@ -44,11 +43,14 @@ export namespace MessageRender {
const date = new Date(message.date * 1000); const date = new Date(message.date * 1000);
const args: (HTMLElement | string)[] = []; const args: (HTMLElement | string)[] = [];
let editedSpan: HTMLElement, sponsoredSpan: HTMLElement, reactionsElement: ReactionsElement, reactionsMessage: Message.message; let editedSpan: HTMLElement,
sponsoredSpan: HTMLElement;
// reactionsElement: ReactionsElement,
// reactionsMessage: Message.message;
const isSponsored = !!(message as Message.message).pFlags.sponsored; const isSponsored = !!(message as Message.message).pFlags.sponsored;
const isMessage = !('action' in message) && !isSponsored; const isMessage = !('action' in message) && !isSponsored;
let hasReactions: boolean; // let hasReactions: boolean;
const time: HTMLElement = isSponsored ? undefined : formatTime(date); const time: HTMLElement = isSponsored ? undefined : formatTime(date);
if(isMessage) { if(isMessage) {
@ -81,15 +83,15 @@ export namespace MessageRender {
args.unshift(i); args.unshift(i);
} }
if(message.peer_id._ === 'peerUser'/* && message.reactions?.results?.length */) { // if(USER_REACTIONS_INLINE && message.peer_id._ === 'peerUser'/* && message.reactions?.results?.length */) {
hasReactions = true; // hasReactions = true;
reactionsMessage = options.reactionsMessage; // reactionsMessage = options.reactionsMessage;
reactionsElement = new ReactionsElement(); // reactionsElement = new ReactionsElement();
reactionsElement.init(reactionsMessage, 'inline', true); // reactionsElement.init(reactionsMessage, 'inline', true);
reactionsElement.render(); // reactionsElement.render();
args.unshift(reactionsElement); // args.unshift(reactionsElement);
} // }
} else if(isSponsored) { } else if(isSponsored) {
args.push(sponsoredSpan = makeSponsored()); args.push(sponsoredSpan = makeSponsored());
} }
@ -120,11 +122,11 @@ export namespace MessageRender {
if(sponsoredSpan) { if(sponsoredSpan) {
clonedArgs[clonedArgs.indexOf(sponsoredSpan)] = makeSponsored(); clonedArgs[clonedArgs.indexOf(sponsoredSpan)] = makeSponsored();
} }
if(reactionsElement) { // if(reactionsElement) {
const _reactionsElement = clonedArgs[clonedArgs.indexOf(reactionsElement)] = new ReactionsElement(); // const _reactionsElement = clonedArgs[clonedArgs.indexOf(reactionsElement)] = new ReactionsElement();
_reactionsElement.init(reactionsMessage, 'inline'); // _reactionsElement.init(reactionsMessage, 'inline');
_reactionsElement.render(); // _reactionsElement.render();
} // }
clonedArgs = clonedArgs.map((a) => a instanceof HTMLElement && !a.classList.contains('i18n') && !a.classList.contains('reactions') ? a.cloneNode(true) as HTMLElement : a); clonedArgs = clonedArgs.map((a) => a instanceof HTMLElement && !a.classList.contains('i18n') && !a.classList.contains('reactions') ? a.cloneNode(true) as HTMLElement : a);
if(time) { if(time) {
clonedArgs[clonedArgs.length - 1] = formatTime(date); // clone time clonedArgs[clonedArgs.length - 1] = formatTime(date); // clone time

86
src/components/chat/reaction.ts

@ -6,17 +6,21 @@
import callbackify from '../../helpers/callbackify'; import callbackify from '../../helpers/callbackify';
import formatNumber from '../../helpers/number/formatNumber'; import formatNumber from '../../helpers/number/formatNumber';
import {fastRaf} from '../../helpers/schedulers'; import {Document, MessagePeerReaction, ReactionCount} from '../../layer';
import {MessagePeerReaction, ReactionCount} from '../../layer';
import {AppManagers} from '../../lib/appManagers/managers'; import {AppManagers} from '../../lib/appManagers/managers';
import getPeerId from '../../lib/appManagers/utils/peers/getPeerId'; import getPeerId from '../../lib/appManagers/utils/peers/getPeerId';
import RLottiePlayer from '../../lib/rlottie/rlottiePlayer';
import rootScope from '../../lib/rootScope'; import rootScope from '../../lib/rootScope';
import SetTransition from '../singleTransition'; import SetTransition from '../singleTransition';
import StackedAvatars from '../stackedAvatars'; import StackedAvatars from '../stackedAvatars';
import {wrapSticker, wrapStickerAnimation} from '../wrappers';
import {Awaited} from '../../types'; import {Awaited} from '../../types';
import wrapSticker from '../wrappers/sticker';
import wrapCustomEmoji from '../wrappers/customEmoji';
import wrapStickerAnimation from '../wrappers/stickerAnimation';
import {makeMediaSize} from '../../helpers/mediaSize';
import RLottiePlayer from '../../lib/rlottie/rlottiePlayer';
import {fastRaf} from '../../helpers/schedulers';
import noop from '../../helpers/noop'; import noop from '../../helpers/noop';
import {Middleware} from '../../helpers/middleware';
const CLASS_NAME = 'reaction'; const CLASS_NAME = 'reaction';
const TAG_NAME = CLASS_NAME + '-element'; const TAG_NAME = CLASS_NAME + '-element';
@ -37,6 +41,7 @@ export default class ReactionElement extends HTMLElement {
private _reactionCount: ReactionCount; private _reactionCount: ReactionCount;
private wrapStickerPromise: Awaited<ReturnType<typeof wrapSticker>>['render']; private wrapStickerPromise: Awaited<ReturnType<typeof wrapSticker>>['render'];
private managers: AppManagers; private managers: AppManagers;
private middleware: Middleware;
constructor() { constructor() {
super(); super();
@ -56,9 +61,10 @@ export default class ReactionElement extends HTMLElement {
return this.reactionCount.count; return this.reactionCount.count;
} }
public init(type: ReactionLayoutType) { public init(type: ReactionLayoutType, middleware: Middleware) {
this.type = type; this.type = type;
this.classList.add(CLASS_NAME + '-' + type); this.classList.add(CLASS_NAME + '-' + type);
this.middleware = middleware;
} }
public setCanRenderAvatars(canRenderAvatars: boolean) { public setCanRenderAvatars(canRenderAvatars: boolean) {
@ -75,33 +81,49 @@ export default class ReactionElement extends HTMLElement {
const reactionCount = this.reactionCount; const reactionCount = this.reactionCount;
if(!doNotRenderSticker && !hadStickerContainer) { if(!doNotRenderSticker && !hadStickerContainer) {
const availableReaction = this.managers.appReactionsManager.getReaction(reactionCount.reaction); const reaction = reactionCount.reaction;
callbackify(availableReaction, (availableReaction) => { if(reaction._ === 'reactionEmoji') {
if(!availableReaction.center_icon) { const availableReaction = this.managers.appReactionsManager.getReaction(reaction.emoticon);
this.stickerContainer.classList.add('is-static'); callbackify(availableReaction, (availableReaction) => {
} if(!availableReaction.center_icon) {
this.stickerContainer.classList.add('is-static');
if(availableReaction.pFlags.inactive) { }
this.classList.add('is-inactive');
}
const size = this.type === 'inline' ? REACTION_INLINE_SIZE : REACTION_BLOCK_SIZE; if(availableReaction.pFlags.inactive) {
const wrapPromise = this.wrapStickerPromise = wrapSticker({ this.classList.add('is-inactive');
div: this.stickerContainer,
doc: availableReaction.center_icon ?? availableReaction.static_icon,
width: size,
height: size,
static: true,
managers: this.managers
}).then(({render}) => render).finally(() => {
if(this.wrapStickerPromise === wrapPromise) {
this.wrapStickerPromise = undefined;
} }
this.renderDoc(availableReaction.center_icon ?? availableReaction.static_icon);
}); });
}); } else if(reaction._ === 'reactionCustomEmoji') {
this.stickerContainer.classList.add('is-custom');
const wrapped = wrapCustomEmoji({
docIds: [reaction.document_id],
size: makeMediaSize(REACTION_BLOCK_SIZE, REACTION_BLOCK_SIZE)
});
this.stickerContainer.append(wrapped);
}
} }
} }
private renderDoc(doc: Document.document) {
const size = this.type === 'inline' ? REACTION_INLINE_SIZE : REACTION_BLOCK_SIZE;
const wrapPromise = this.wrapStickerPromise = wrapSticker({
div: this.stickerContainer,
doc,
width: size,
height: size,
static: true,
managers: this.managers,
middleware: this.middleware
}).then(({render}) => render).finally(() => {
if(this.wrapStickerPromise === wrapPromise) {
this.wrapStickerPromise = undefined;
}
});
}
public renderCounter() { public renderCounter() {
const reactionCount = this.reactionCount; const reactionCount = this.reactionCount;
const displayOn = this.type === 'inline' ? REACTION_DISPLAY_INLINE_COUNTER_AT : REACTION_DISPLAY_BLOCK_COUNTER_AT; const displayOn = this.type === 'inline' ? REACTION_DISPLAY_INLINE_COUNTER_AT : REACTION_DISPLAY_BLOCK_COUNTER_AT;
@ -150,7 +172,7 @@ export default class ReactionElement extends HTMLElement {
this.stackedAvatars.render(recentReactions.map((reaction) => getPeerId(reaction.peer_id))); this.stackedAvatars.render(recentReactions.map((reaction) => getPeerId(reaction.peer_id)));
} }
public setIsChosen(isChosen = !!this.reactionCount.pFlags.chosen) { public setIsChosen(isChosen = this.reactionCount.chosen_order !== undefined) {
if(this.type === 'inline') return; if(this.type === 'inline') return;
const wasChosen = this.classList.contains('is-chosen') && !this.classList.contains('backwards'); const wasChosen = this.classList.contains('is-chosen') && !this.classList.contains('backwards');
if(wasChosen !== isChosen) { if(wasChosen !== isChosen) {
@ -159,7 +181,9 @@ export default class ReactionElement extends HTMLElement {
} }
public fireAroundAnimation() { public fireAroundAnimation() {
callbackify(this.managers.appReactionsManager.getReaction(this.reactionCount.reaction), (availableReaction) => { const reaction = this.reactionCount.reaction;
if(reaction._ !== 'reactionEmoji') return;
callbackify(this.managers.appReactionsManager.getReaction(reaction.emoticon), (availableReaction) => {
const size = this.type === 'inline' ? REACTION_INLINE_SIZE + 14 : REACTION_BLOCK_SIZE + 18; const size = this.type === 'inline' ? REACTION_INLINE_SIZE + 14 : REACTION_BLOCK_SIZE + 18;
const div = document.createElement('div'); const div = document.createElement('div');
div.classList.add(CLASS_NAME + '-sticker-activate'); div.classList.add(CLASS_NAME + '-sticker-activate');
@ -176,7 +200,8 @@ export default class ReactionElement extends HTMLElement {
skipRatio: 1, skipRatio: 1,
group: 'none', group: 'none',
needFadeIn: false, needFadeIn: false,
managers: this.managers managers: this.managers,
middleware: this.middleware
}).then(({render}) => render as Promise<RLottiePlayer>), }).then(({render}) => render as Promise<RLottiePlayer>),
wrapStickerAnimation({ wrapStickerAnimation({
@ -186,7 +211,8 @@ export default class ReactionElement extends HTMLElement {
side: 'center', side: 'center',
skipRatio: 1, skipRatio: 1,
play: false, play: false,
managers: this.managers managers: this.managers,
middleware: this.middleware
}).stickerPromise.catch(noop) }).stickerPromise.catch(noop)
]).then(([iconPlayer, aroundPlayer]) => { ]).then(([iconPlayer, aroundPlayer]) => {
const remove = () => { const remove = () => {

60
src/components/chat/reactions.ts

@ -6,9 +6,11 @@
import forEachReverse from '../../helpers/array/forEachReverse'; import forEachReverse from '../../helpers/array/forEachReverse';
import positionElementByIndex from '../../helpers/dom/positionElementByIndex'; import positionElementByIndex from '../../helpers/dom/positionElementByIndex';
import {Middleware, MiddlewareHelper} from '../../helpers/middleware';
import {Message, ReactionCount} from '../../layer'; import {Message, ReactionCount} from '../../layer';
import appImManager from '../../lib/appManagers/appImManager'; import appImManager from '../../lib/appManagers/appImManager';
import {AppManagers} from '../../lib/appManagers/managers'; import {AppManagers} from '../../lib/appManagers/managers';
import reactionsEqual from '../../lib/appManagers/utils/reactions/reactionsEqual';
import rootScope from '../../lib/rootScope'; import rootScope from '../../lib/rootScope';
import ReactionElement, {ReactionLayoutType, REACTION_DISPLAY_BLOCK_COUNTER_AT} from './reaction'; import ReactionElement, {ReactionLayoutType, REACTION_DISPLAY_BLOCK_COUNTER_AT} from './reaction';
@ -26,11 +28,14 @@ export default class ReactionsElement extends HTMLElement {
private sorted: ReactionElement[]; private sorted: ReactionElement[];
private onConnectCallback: () => void; private onConnectCallback: () => void;
private managers: AppManagers; private managers: AppManagers;
private middleware: Middleware;
private middlewareHelpers: Map<ReactionElement, MiddlewareHelper>;
constructor() { constructor() {
super(); super();
this.classList.add(CLASS_NAME); this.classList.add(CLASS_NAME);
this.sorted = []; this.sorted = [];
this.middlewareHelpers = new Map();
this.managers = rootScope.managers; this.managers = rootScope.managers;
} }
@ -64,13 +69,25 @@ export default class ReactionsElement extends HTMLElement {
return this.message; return this.message;
} }
public init(message: Message.message, type: ReactionLayoutType, isPlaceholder?: boolean) { public init(
message: Message.message,
type: ReactionLayoutType,
middleware: Middleware,
isPlaceholder = this.isPlaceholder
) {
if(this.key !== undefined) { if(this.key !== undefined) {
this.disconnectedCallback(); this.disconnectedCallback();
} }
if(this.middleware !== middleware) {
middleware.onDestroy(() => {
this.middlewareHelpers.clear();
});
}
this.message = message; this.message = message;
this.key = this.message.peerId + '_' + this.message.mid; this.key = this.message.peerId + '_' + this.message.mid;
this.middleware = middleware;
this.isPlaceholder = isPlaceholder; this.isPlaceholder = isPlaceholder;
if(this.type !== type) { if(this.type !== type) {
@ -82,7 +99,7 @@ export default class ReactionsElement extends HTMLElement {
} }
public changeMessage(message: Message.message) { public changeMessage(message: Message.message) {
return this.init(message, this.type, this.isPlaceholder); return this.init(message, this.type, this.middleware);
} }
public update(message: Message.message, changedResults?: ReactionCount[]) { public update(message: Message.message, changedResults?: ReactionCount[]) {
@ -99,35 +116,46 @@ export default class ReactionsElement extends HTMLElement {
const availableReactionsResult = this.managers.appReactionsManager.getAvailableReactions(); const availableReactionsResult = this.managers.appReactionsManager.getAvailableReactions();
// callbackify(availableReactionsResult, () => { // callbackify(availableReactionsResult, () => {
const counts = hasReactions ? ( const counts = hasReactions ? (
availableReactionsResult instanceof Promise ? reactions.results
reactions.results : // availableReactionsResult instanceof Promise ?
reactions.results.filter((reactionCount) => { // reactions.results :
return this.managers.appReactionsManager.isReactionActive(reactionCount.reaction); // reactions.results.filter((reactionCount) => {
}) // return this.managers.appReactionsManager.isReactionActive(reactionCount.reaction);
// })
) : []; ) : [];
if(this.message.peerId.isUser()) {
counts.sort((a, b) => (b.count - a.count) || ((b.chosen_order ?? 0) - (a.chosen_order ?? 0)));
} else {
counts.sort((a, b) => (b.count - a.count) || ((a.chosen_order ?? 0) - (b.chosen_order ?? 0)));
}
forEachReverse(this.sorted, (reactionElement, idx, arr) => { forEachReverse(this.sorted, (reactionElement, idx, arr) => {
const reaction = reactionElement.reactionCount.reaction; const reaction = reactionElement.reactionCount.reaction;
const found = counts.some((reactionCount) => reactionCount.reaction === reaction); const found = counts.some((reactionCount) => reactionsEqual(reactionCount.reaction, reaction));
if(!found) { if(!found) {
const middlewareHelper = this.middlewareHelpers.get(reactionElement);
middlewareHelper.destroy();
this.middlewareHelpers.delete(reactionElement);
arr.splice(idx, 1); arr.splice(idx, 1);
reactionElement.remove(); reactionElement.remove();
} }
}); });
const totalReactions = counts.reduce((acc, c) => acc + c.count, 0); const totalReactions = counts.reduce((acc, c) => acc + c.count, 0);
const canRenderAvatars = reactions && !!reactions.pFlags.can_see_list && totalReactions < REACTION_DISPLAY_BLOCK_COUNTER_AT; const canRenderAvatars = reactions && (!!reactions.pFlags.can_see_list || this.message.peerId.isUser()) && totalReactions < REACTION_DISPLAY_BLOCK_COUNTER_AT;
this.sorted = counts.map((reactionCount, idx) => { this.sorted = counts.map((reactionCount, idx) => {
const reactionElementIdx = this.sorted.findIndex((reactionElement) => reactionElement.reactionCount.reaction === reactionCount.reaction); let reactionElement = this.sorted.find((reactionElement) => reactionsEqual(reactionElement.reactionCount.reaction, reactionCount.reaction));
let reactionElement = reactionElementIdx !== -1 && this.sorted[reactionElementIdx];
if(!reactionElement) { if(!reactionElement) {
const middlewareHelper = this.middleware.create();
reactionElement = new ReactionElement(); reactionElement = new ReactionElement();
reactionElement.init(this.type); reactionElement.init(this.type, middlewareHelper.get());
this.middlewareHelpers.set(reactionElement, middlewareHelper);
} }
positionElementByIndex(reactionElement, this, idx); positionElementByIndex(reactionElement, this, idx);
const recentReactions = reactions.recent_reactions ? reactions.recent_reactions.filter((reaction) => reaction.reaction === reactionCount.reaction) : []; const recentReactions = reactions.recent_reactions ? reactions.recent_reactions.filter((reaction) => reactionsEqual(reaction.reaction, reactionCount.reaction)) : [];
reactionElement.reactionCount = {...reactionCount}; reactionElement.reactionCount = {...reactionCount};
reactionElement.setCanRenderAvatars(canRenderAvatars); reactionElement.setCanRenderAvatars(canRenderAvatars);
reactionElement.render(this.isPlaceholder); reactionElement.render(this.isPlaceholder);
@ -179,10 +207,8 @@ export default class ReactionsElement extends HTMLElement {
if(this.message.peerId !== appImManager.chat.peerId) return; if(this.message.peerId !== appImManager.chat.peerId) return;
changedResults.forEach((reactionCount) => { changedResults.forEach((reactionCount) => {
const reactionElement = this.sorted.find((reactionElement) => reactionElement.reactionCount.reaction === reactionCount.reaction); const reactionElement = this.sorted.find((reactionElement) => reactionsEqual(reactionElement.reactionCount.reaction, reactionCount.reaction));
if(reactionElement) { reactionElement?.fireAroundAnimation();
reactionElement.fireAroundAnimation();
}
}); });
} }
} }

8
src/components/chat/reactionsMenu.ts

@ -14,14 +14,14 @@ import getVisibleRect from '../../helpers/dom/getVisibleRect';
import {getMiddleware} from '../../helpers/middleware'; import {getMiddleware} from '../../helpers/middleware';
import noop from '../../helpers/noop'; import noop from '../../helpers/noop';
import {fastRaf} from '../../helpers/schedulers'; import {fastRaf} from '../../helpers/schedulers';
import {Message, AvailableReaction} from '../../layer'; import {Message, AvailableReaction, Reaction} from '../../layer';
import {AppManagers} from '../../lib/appManagers/managers'; import {AppManagers} from '../../lib/appManagers/managers';
import lottieLoader from '../../lib/rlottie/lottieLoader'; import lottieLoader from '../../lib/rlottie/lottieLoader';
import RLottiePlayer from '../../lib/rlottie/rlottiePlayer'; import RLottiePlayer from '../../lib/rlottie/rlottiePlayer';
import rootScope from '../../lib/rootScope'; import rootScope from '../../lib/rootScope';
import animationIntersector, {AnimationItemGroup} from '../animationIntersector'; import animationIntersector, {AnimationItemGroup} from '../animationIntersector';
import Scrollable, {ScrollableBase, ScrollableX} from '../scrollable'; import Scrollable, {ScrollableBase, ScrollableX} from '../scrollable';
import {wrapSticker} from '../wrappers'; import wrapSticker from '../wrappers/sticker';
const REACTIONS_CLASS_NAME = 'btn-menu-reactions'; const REACTIONS_CLASS_NAME = 'btn-menu-reactions';
const REACTION_CLASS_NAME = REACTIONS_CLASS_NAME + '-reaction'; const REACTION_CLASS_NAME = REACTIONS_CLASS_NAME + '-reaction';
@ -37,7 +37,7 @@ type ChatReactionsMenuPlayers = {
appear?: RLottiePlayer, appear?: RLottiePlayer,
selectWrapper: HTMLElement, selectWrapper: HTMLElement,
appearWrapper: HTMLElement, appearWrapper: HTMLElement,
reaction: string reaction: Reaction
}; };
export class ChatReactionsMenu { export class ChatReactionsMenu {
public widthContainer: HTMLElement; public widthContainer: HTMLElement;
@ -158,7 +158,7 @@ export class ChatReactionsMenu {
const players: ChatReactionsMenuPlayers = { const players: ChatReactionsMenuPlayers = {
selectWrapper, selectWrapper,
appearWrapper, appearWrapper,
reaction: reaction.reaction reaction: {_: 'reactionEmoji', emoticon: reaction.reaction}
}; };
this.reactionsMap.set(reactionDiv, players); this.reactionsMap.set(reactionDiv, players);

4
src/components/chat/replyContainer.ts

@ -12,8 +12,10 @@ import appImManager, {CHAT_ANIMATION_GROUP} from '../../lib/appManagers/appImMan
import choosePhotoSize from '../../lib/appManagers/utils/photos/choosePhotoSize'; import choosePhotoSize from '../../lib/appManagers/utils/photos/choosePhotoSize';
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText'; import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
import DivAndCaption from '../divAndCaption'; import DivAndCaption from '../divAndCaption';
import {wrapPhoto, wrapSticker, wrapVideo} from '../wrappers';
import wrapMessageForReply from '../wrappers/messageForReply'; import wrapMessageForReply from '../wrappers/messageForReply';
import wrapPhoto from '../wrappers/photo';
import wrapSticker from '../wrappers/sticker';
import wrapVideo from '../wrappers/video';
const MEDIA_SIZE = 32; const MEDIA_SIZE = 32;

3
src/components/chat/sendAs.ts

@ -232,9 +232,10 @@ export default class ChatSendAs {
await this.changeSendAsPeerId(sendAsPeerId, skipAnimation); await this.changeSendAsPeerId(sendAsPeerId, skipAnimation);
if(!middleware()) return; if(!middleware()) return;
this.managers.appChatsManager.getSendAs(chatId).then((peers) => { this.managers.appChatsManager.getSendAs(chatId).then((sendAsPeers) => {
if(!middleware()) return; if(!middleware()) return;
const peers = sendAsPeers.filter((sendAsPeer) => !sendAsPeer.pFlags.premium_required).map((sendAsPeer) => sendAsPeer.peer);
const peerIds = peers.map((peer) => getPeerId(peer)); const peerIds = peers.map((peer) => getPeerId(peer));
this.sendAsPeerIds = peerIds.slice(); this.sendAsPeerIds = peerIds.slice();

7
src/components/emoticonsDropdown/index.ts

@ -302,8 +302,11 @@ export class EmoticonsDropdown extends DropdownHover {
return; return;
} }
const element = (scroll.splitUp || scroll.container).children[which] as HTMLElement; let offsetTop = 0;
const offsetTop = element.offsetTop + 1; // * due to stickyIntersector if(which > 0) {
const element = (scroll.splitUp || scroll.container).children[which] as HTMLElement;
offsetTop = element.offsetTop + 1; // * due to stickyIntersector
}
scroll.container.scrollTop = jumpedTo = offsetTop; scroll.container.scrollTop = jumpedTo = offsetTop;

117
src/components/emoticonsDropdown/tabs/emoji.ts

@ -14,7 +14,7 @@ import {i18n, LangPackKey} from '../../../lib/langPack';
import rootScope from '../../../lib/rootScope'; import rootScope from '../../../lib/rootScope';
import {emojiFromCodePoints} from '../../../vendor/emoji'; import {emojiFromCodePoints} from '../../../vendor/emoji';
import {putPreloader} from '../../putPreloader'; import {putPreloader} from '../../putPreloader';
import Scrollable from '../../scrollable'; import Scrollable, {ScrollableX} from '../../scrollable';
import StickyIntersector from '../../stickyIntersector'; import StickyIntersector from '../../stickyIntersector';
import IS_EMOJI_SUPPORTED from '../../../environment/emojiSupport'; import IS_EMOJI_SUPPORTED from '../../../environment/emojiSupport';
import IS_TOUCH_SUPPORTED from '../../../environment/touchSupport'; import IS_TOUCH_SUPPORTED from '../../../environment/touchSupport';
@ -25,6 +25,8 @@ import fixEmoji from '../../../lib/richTextProcessor/fixEmoji';
import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText'; import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
import wrapSingleEmoji from '../../../lib/richTextProcessor/wrapSingleEmoji'; import wrapSingleEmoji from '../../../lib/richTextProcessor/wrapSingleEmoji';
import {attachClickEvent} from '../../../helpers/dom/clickEvent'; import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import {StickersTabCategory} from './stickers';
import positionElementByIndex from '../../../helpers/dom/positionElementByIndex';
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) {
@ -125,24 +127,25 @@ export default class EmojiTab implements EmoticonsTab {
init() { init() {
this.content = document.getElementById('content-emoji') as HTMLDivElement; this.content = document.getElementById('content-emoji') as HTMLDivElement;
const categories: LangPackKey[] = [ const EMOJI_RECENT_CATEGORY: (typeof EMOJI_CATEGORIES)[0] = ['Emoji.Recent', 'recent'];
'Emoji.SmilesAndPeople', const EMOJI_CATEGORIES: [LangPackKey, string][] = [
'Emoji.AnimalsAndNature', ['Emoji.SmilesAndPeople', 'smile'],
'Emoji.FoodAndDrink', ['Emoji.AnimalsAndNature', 'animals'],
'Emoji.TravelAndPlaces', ['Emoji.FoodAndDrink', 'eats'],
'Emoji.ActivityAndSport', ['Emoji.TravelAndPlaces', 'car'],
'Emoji.Objects', ['Emoji.ActivityAndSport', 'sport'],
/* 'Emoji.Symbols', */ ['Emoji.Objects', 'lamp'],
'Emoji.Flags', // ['Emoji.Symbols', 'info'],
'Skin Tones' as any ['Emoji.Flags', 'flag'],
['Skin Tones' as any, '']
]; ];
const divs: { const divs: {
[category in LangPackKey]?: HTMLDivElement [category in LangPackKey]?: StickersTabCategory
} = {}; } = {};
const sorted: Map<LangPackKey, string[]> = new Map([ const sorted: Map<(typeof EMOJI_CATEGORIES)[0], string[]> = new Map([
[ [
'Emoji.Recent', EMOJI_RECENT_CATEGORY,
[] []
] ]
]); ]);
@ -150,7 +153,7 @@ export default class EmojiTab implements EmoticonsTab {
for(const emoji in Emoji) { for(const emoji in Emoji) {
const details = Emoji[emoji]; const details = Emoji[emoji];
const i = '' + details; const i = '' + details;
const category = categories[+i[0] - 1]; const category = EMOJI_CATEGORIES[+i[0] - 1];
if(!category) continue; // maybe it's skin tones if(!category) continue; // maybe it's skin tones
let s = sorted.get(category); let s = sorted.get(category);
@ -162,25 +165,20 @@ export default class EmojiTab implements EmoticonsTab {
s[+i.slice(1) || 0] = emoji; s[+i.slice(1) || 0] = emoji;
} }
// console.log('emoticons sorted:', sorted); sorted.delete(EMOJI_CATEGORIES.pop());
// Object.keys(sorted).forEach((c) => sorted[c].sort((a, b) => a - b)); sorted.forEach((emojis, emojiCategory) => {
const titleLangPackKey = emojiCategory[0];
sorted.delete(categories.pop()); const category = new StickersTabCategory({
id: titleLangPackKey,
// console.time('emojiParse'); overflowElement: this.content,
sorted.forEach((emojis, category) => { title: i18n(titleLangPackKey),
const div = document.createElement('div'); getElementMediaSize: () => undefined
div.classList.add('emoji-category'); });
const titleDiv = document.createElement('div');
titleDiv.classList.add('category-title');
titleDiv.append(i18n(category));
const itemsDiv = document.createElement('div');
itemsDiv.classList.add('super-emojis');
div.append(titleDiv, itemsDiv); category.elements.menuTab.classList.add('tgico', 'tgico-' + emojiCategory[1]);
category.elements.menuTabPadding.remove();
category.elements.items.classList.add('super-emojis');
emojis.forEach((unified) => { emojis.forEach((unified) => {
/* if(emojiUnicode(emoji) === '1f481-200d-2642') { /* if(emojiUnicode(emoji) === '1f481-200d-2642') {
@ -204,59 +202,58 @@ export default class EmojiTab implements EmoticonsTab {
// debugger; // debugger;
// } // }
appendEmoji(emoji/* .replace(/[\ufe0f\u2640\u2642\u2695]/g, '') */, itemsDiv, false/* , false */); appendEmoji(emoji/* .replace(/[\ufe0f\u2640\u2642\u2695]/g, '') */, category.elements.items, false/* , false */);
/* if(category === 'Smileys & Emotion') { /* if(category === 'Smileys & Emotion') {
console.log('appended emoji', emoji, itemsDiv.children[itemsDiv.childElementCount - 1].innerHTML, emojiUnicode(emoji)); console.log('appended emoji', emoji, itemsDiv.children[itemsDiv.childElementCount - 1].innerHTML, emojiUnicode(emoji));
} */ } */
}); });
divs[category] = div; divs[titleLangPackKey] = category;
}); });
// console.timeEnd('emojiParse'); const menuWrapper = this.content.previousElementSibling as HTMLElement;
const menu = this.menu = menuWrapper.firstElementChild as HTMLElement;
const menuScroll = new ScrollableX(menuWrapper);
const menu = this.menu = this.content.previousElementSibling as HTMLElement;
const emojiScroll = this.scroll = new Scrollable(this.content, 'EMOJI'); const emojiScroll = this.scroll = new Scrollable(this.content, 'EMOJI');
// emojiScroll.setVirtualContainer(emojiScroll.container);
const preloader = putPreloader(this.content, true); const preloader = putPreloader(this.content, true);
Promise.all([ Promise.all([
pause(200), pause(200),
this.managers.appEmojiManager.getRecentEmojis().then((recent) => { this.managers.appEmojiManager.getRecentEmojis()
const hasRecent = !!recent.length;
const activeId = hasRecent ? 0 : 1;
this.menu.children[0].classList.toggle('hide', !hasRecent);
this.menu.children[activeId].classList.add('active');
const m = EmoticonsDropdown.menuOnClick(menu, emojiScroll, undefined, activeId);
this.stickyIntersector = m.stickyIntersector;
this.setMenuActive = m.setActive;
return recent;
})
]).then(([_, recent]) => { ]).then(([_, recent]) => {
preloader.remove(); preloader.remove();
this.recentItemsDiv = divs['Emoji.Recent'].querySelector('.super-emojis'); const m = EmoticonsDropdown.menuOnClick(menu, emojiScroll, menuScroll);
this.stickyIntersector = m.stickyIntersector;
this.setMenuActive = m.setActive;
const hasRecent = !!recent.length;
const activeId = hasRecent ? 0 : 1;
const recentCategory = divs[EMOJI_RECENT_CATEGORY[0]];
recentCategory.elements.menuTab.classList.toggle('hide', !hasRecent);
this.recentItemsDiv = recentCategory.elements.items;
for(const emoji of recent) { for(const emoji of recent) {
appendEmoji(emoji, this.recentItemsDiv); appendEmoji(emoji, this.recentItemsDiv);
} }
this.recentItemsDiv.parentElement.classList.toggle('hide', !this.recentItemsDiv.childElementCount); recentCategory.elements.container.classList.toggle('hide', !this.recentItemsDiv.childElementCount);
categories.unshift('Emoji.Recent'); EMOJI_CATEGORIES.unshift(EMOJI_RECENT_CATEGORY);
categories.map((category) => { EMOJI_CATEGORIES.map(([id], idx) => {
const div = divs[category]; const category = divs[id];
if(!div) { positionElementByIndex(category.elements.menuTab, menu, idx);
console.error('no div by category:', category); emojiScroll.container.append(category.elements.container);
} this.stickyIntersector.observeStickyHeaderChanges(category.elements.container);
return category;
emojiScroll.container.append(div);
this.stickyIntersector.observeStickyHeaderChanges(div);
return div;
}); });
this.menu.children[activeId].classList.add('active');
this.setMenuActive(activeId);
}); });
attachClickEvent(this.content, this.onContentClick); attachClickEvent(this.content, this.onContentClick);

227
src/components/emoticonsDropdown/tabs/stickers.ts

@ -20,7 +20,6 @@ import {putPreloader} from '../../putPreloader';
import PopupStickers from '../../popups/stickers'; import PopupStickers from '../../popups/stickers';
import Scrollable, {ScrollableX} from '../../scrollable'; import Scrollable, {ScrollableX} from '../../scrollable';
import StickyIntersector from '../../stickyIntersector'; import StickyIntersector from '../../stickyIntersector';
import {wrapSticker, wrapStickerSetThumb} from '../../wrappers';
import findAndSplice from '../../../helpers/array/findAndSplice'; import findAndSplice from '../../../helpers/array/findAndSplice';
import {attachClickEvent} from '../../../helpers/dom/clickEvent'; import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import positionElementByIndex from '../../../helpers/dom/positionElementByIndex'; import positionElementByIndex from '../../../helpers/dom/positionElementByIndex';
@ -34,6 +33,9 @@ import forEachReverse from '../../../helpers/array/forEachReverse';
import {MTAppConfig} from '../../../lib/mtproto/appConfig'; import {MTAppConfig} from '../../../lib/mtproto/appConfig';
import attachStickerViewerListeners from '../../stickerViewer'; import attachStickerViewerListeners from '../../stickerViewer';
import ListenerSetter from '../../../helpers/listenerSetter'; import ListenerSetter from '../../../helpers/listenerSetter';
import wrapSticker from '../../wrappers/sticker';
import wrapStickerSetThumb from '../../wrappers/stickerSetThumb';
import {MediaSize} from '../../../helpers/mediaSize';
export class SuperStickerRenderer { export class SuperStickerRenderer {
public lazyLoadQueue: LazyLoadQueueRepeat; public lazyLoadQueue: LazyLoadQueueRepeat;
@ -151,60 +153,40 @@ export class SuperStickerRenderer {
}; };
} }
type StickersTabCategory = { export class StickersTabCategory {
elements: { public elements: {
container: HTMLElement, container: HTMLElement,
title: HTMLElement, title: HTMLElement,
items: HTMLElement, items: HTMLElement,
menuTab: HTMLElement, menuTab: HTMLElement,
menuTabPadding: HTMLElement menuTabPadding: HTMLElement
}, };
set: StickerSet.stickerSet, public items: {
items: {
document: MyDocument, document: MyDocument,
element: HTMLElement element: HTMLElement
}[], }[];
mounted?: boolean, public mounted: boolean;
id: string, public id: string;
limit?: number public limit: number;
};
private overflowElement: HTMLElement;
export default class StickersTab implements EmoticonsTab { private getElementMediaSize: () => MediaSize;
private content: HTMLElement;
constructor(options: {
private categories: {[id: string]: StickersTabCategory}; id: string,
private categoriesMap: Map<HTMLElement, StickersTabCategory>; title: HTMLElement | DocumentFragment,
private categoriesIntersector: VisibilityIntersector; overflowElement: HTMLElement,
private localCategories: StickersTabCategory[]; getElementMediaSize: () => MediaSize
}) {
private scroll: Scrollable;
private menu: HTMLElement;
private mounted = false;
private stickyIntersector: StickyIntersector;
private superStickerRenderer: SuperStickerRenderer;
constructor(private managers: AppManagers) {
this.categories = {};
this.categoriesMap = new Map();
this.localCategories = [];
}
private setFavedLimit(appConfig: MTAppConfig) {
const limit = rootScope.premium ? appConfig.stickers_faved_limit_premium : appConfig.stickers_faved_limit_default;
const category = this.categories['faved'];
category.limit = limit;
}
private createCategory(stickerSet: StickerSet.stickerSet, _title: HTMLElement | DocumentFragment) {
const container = document.createElement('div'); const container = document.createElement('div');
container.classList.add('emoji-category', 'hide'); container.classList.add('emoji-category');
const items = document.createElement('div'); const items = document.createElement('div');
items.classList.add('category-items', 'super-stickers'); items.classList.add('category-items');
const title = document.createElement('div'); const title = document.createElement('div');
title.classList.add('category-title'); title.classList.add('category-title');
title.append(_title); title.append(options.title);
const menuTab = ButtonIcon(undefined, {noRipple: true}); const menuTab = ButtonIcon(undefined, {noRipple: true});
menuTab.classList.add('menu-horizontal-div-item'); menuTab.classList.add('menu-horizontal-div-item');
@ -214,21 +196,70 @@ export default class StickersTab implements EmoticonsTab {
menuTab.append(menuTabPadding); menuTab.append(menuTabPadding);
const category: StickersTabCategory = { container.append(title, items);
elements: {
container, this.elements = {
title, container,
items, title,
menuTab, items,
menuTabPadding menuTab,
}, menuTabPadding
set: stickerSet,
items: [],
id: '' + stickerSet.id
}; };
this.id = options.id;
this.items = [];
container.append(title, items); this.overflowElement = options.overflowElement;
this.getElementMediaSize = options.getElementMediaSize;
}
public setCategoryItemsHeight() {
const containerWidth = this.overflowElement.getBoundingClientRect().width - 10;
const elementSize = this.getElementMediaSize().width;
const itemsPerRow = Math.floor(containerWidth / elementSize);
const rows = Math.ceil(this.items.length / itemsPerRow);
const height = rows * elementSize;
this.elements.items.style.minHeight = height + 'px';
}
}
type S = StickersTabCategory & {set?: StickerSet};
class EmoticonsTabC {
protected content: HTMLElement;
protected categories: {[id: string]: S};
protected categoriesMap: Map<HTMLElement, S>;
protected categoriesIntersector: VisibilityIntersector;
protected localCategories: StickersTabCategory[];
protected scroll: Scrollable;
protected menu: HTMLElement;
protected mounted = false;
protected stickyIntersector: StickyIntersector;
constructor(
protected managers: AppManagers
) {
this.categories = {};
this.categoriesMap = new Map();
this.localCategories = [];
}
protected createCategory(stickerSet: StickerSet, title: HTMLElement | DocumentFragment) {
const category: S = new StickersTabCategory({
id: '' + stickerSet.id,
title,
overflowElement: this.content,
getElementMediaSize: () => mediaSizes.active.esgSticker
});
category.elements.items.classList.add('super-stickers');
const container = category.elements.container;
container.classList.add('hide');
category.set = stickerSet;
this.categories[stickerSet.id] = category; this.categories[stickerSet.id] = category;
this.categoriesMap.set(container, category); this.categoriesMap.set(container, category);
@ -238,6 +269,41 @@ export default class StickersTab implements EmoticonsTab {
return category; return category;
} }
protected isCategoryVisible(category: StickersTabCategory) {
return this.categoriesIntersector.getVisible().includes(category.elements.container);
}
protected toggleLocalCategory(category: StickersTabCategory, visible: boolean) {
if(!visible) {
category.elements.menuTab.remove();
category.elements.container.remove();
} else {
let idx = this.localCategories.indexOf(category);
const notMounted = this.localCategories.slice(0, idx).filter((category) => !category.mounted);
idx -= notMounted.length;
positionElementByIndex(category.elements.menuTab, this.menu, idx);
positionElementByIndex(category.elements.container, this.scroll.container, idx);
}
category.mounted = visible;
// category.elements.container.classList.toggle('hide', !visible);
}
protected onLocalCategoryUpdate(category: StickersTabCategory) {
category.setCategoryItemsHeight();
this.toggleLocalCategory(category, !!category.items.length);
}
}
export default class StickersTab extends EmoticonsTabC implements EmoticonsTab {
private superStickerRenderer: SuperStickerRenderer;
private setFavedLimit(appConfig: MTAppConfig) {
const limit = rootScope.premium ? appConfig.stickers_faved_limit_premium : appConfig.stickers_faved_limit_default;
const category = this.categories['faved'];
category.limit = limit;
}
private categoryAppendStickers( private categoryAppendStickers(
category: StickersTabCategory, category: StickersTabCategory,
promise: Promise<MyDocument[]> promise: Promise<MyDocument[]>
@ -256,26 +322,11 @@ export default class StickersTab implements EmoticonsTab {
} }
}); });
this.setCategoryItemsHeight(category); category.setCategoryItemsHeight();
container.classList.remove('hide'); container.classList.remove('hide');
}); });
} }
private isCategoryVisible(category: StickersTabCategory) {
return this.categoriesIntersector.getVisible().includes(category.elements.container);
}
private setCategoryItemsHeight(category: StickersTabCategory) {
const containerWidth = this.content.getBoundingClientRect().width - 10;
const stickerSize = mediaSizes.active.esgSticker.width;
const itemsPerRow = Math.floor(containerWidth / stickerSize);
const rows = Math.ceil(category.items.length / itemsPerRow);
const height = rows * stickerSize;
category.elements.items.style.minHeight = height + 'px';
}
private async renderStickerSet(set: StickerSet.stickerSet, prepend = false) { private async renderStickerSet(set: StickerSet.stickerSet, prepend = false) {
const category = this.createCategory(set, wrapEmojiText(set.title)); const category = this.createCategory(set, wrapEmojiText(set.title));
const {menuTab, menuTabPadding, container} = category.elements; const {menuTab, menuTabPadding, container} = category.elements;
@ -305,9 +356,8 @@ export default class StickersTab implements EmoticonsTab {
public init() { public init() {
this.content = document.getElementById('content-stickers'); this.content = document.getElementById('content-stickers');
const menuWrapper = this.content.previousElementSibling as HTMLDivElement; const menuWrapper = this.content.previousElementSibling as HTMLElement;
this.menu = menuWrapper.firstElementChild as HTMLUListElement; const menu = this.menu = menuWrapper.firstElementChild as HTMLElement;
const menuScroll = new ScrollableX(menuWrapper); const menuScroll = new ScrollableX(menuWrapper);
this.scroll = new Scrollable(this.content, 'STICKERS'); this.scroll = new Scrollable(this.content, 'STICKERS');
@ -334,7 +384,6 @@ export default class StickersTab implements EmoticonsTab {
const onCategoryVisibility: OnVisibilityChange = ({target, visible, entry}) => { const onCategoryVisibility: OnVisibilityChange = ({target, visible, entry}) => {
const category = this.categoriesMap.get(target); const category = this.categoriesMap.get(target);
// console.log('roll the windows up', category, target, visible, entry);
if(!visible) { if(!visible) {
category.elements.items.textContent = ''; category.elements.items.textContent = '';
} else { } else {
@ -356,7 +405,7 @@ export default class StickersTab implements EmoticonsTab {
if(findUpClassName(target, 'category-title')) { if(findUpClassName(target, 'category-title')) {
const container = findUpClassName(target, 'emoji-category'); const container = findUpClassName(target, 'emoji-category');
const category = this.categoriesMap.get(container); const category = this.categoriesMap.get(container);
if(category.set.id === 'recent') { if(category.id === 'recent') {
return; return;
} }
@ -379,7 +428,7 @@ export default class StickersTab implements EmoticonsTab {
setTyping(); setTyping();
}); });
const {stickyIntersector, setActive} = EmoticonsDropdown.menuOnClick(this.menu, this.scroll, menuScroll); const {stickyIntersector, setActive} = EmoticonsDropdown.menuOnClick(menu, this.scroll, menuScroll);
this.stickyIntersector = stickyIntersector; this.stickyIntersector = stickyIntersector;
const preloader = putPreloader(this.content, true); const preloader = putPreloader(this.content, true);
@ -388,7 +437,7 @@ export default class StickersTab implements EmoticonsTab {
const category = this.createCategory({id} as any, i18n(title)); const category = this.createCategory({id} as any, i18n(title));
this.localCategories.push(category); this.localCategories.push(category);
category.elements.title.classList.add('disable-hover'); category.elements.title.classList.add('disable-hover');
icon && category.elements.menuTab.classList.add('tgico-' + icon); icon && category.elements.menuTab.classList.add('tgico', 'tgico-' + icon);
category.elements.menuTabPadding.remove(); category.elements.menuTabPadding.remove();
this.toggleLocalCategory(category, false); this.toggleLocalCategory(category, false);
return category; return category;
@ -423,7 +472,6 @@ export default class StickersTab implements EmoticonsTab {
const recentCategory = createLocalCategory('recent', 'Stickers.Recent', 'recent'); const recentCategory = createLocalCategory('recent', 'Stickers.Recent', 'recent');
recentCategory.limit = 20; recentCategory.limit = 20;
// recentCategory.elements.menuTab.classList.add('active');
const clearButton = ButtonIcon('close', {noRipple: true}); const clearButton = ButtonIcon('close', {noRipple: true});
recentCategory.elements.title.append(clearButton); recentCategory.elements.title.append(clearButton);
@ -561,7 +609,7 @@ export default class StickersTab implements EmoticonsTab {
const resizeCategories = () => { const resizeCategories = () => {
for(const [container, category] of this.categoriesMap) { for(const [container, category] of this.categoriesMap) {
this.setCategoryItemsHeight(category); category.setCategoryItemsHeight();
} }
}; };
@ -585,27 +633,6 @@ export default class StickersTab implements EmoticonsTab {
this.init = null; this.init = null;
} }
private toggleLocalCategory(category: StickersTabCategory, visible: boolean) {
if(!visible) {
category.elements.menuTab.remove();
category.elements.container.remove();
} else {
let idx = this.localCategories.indexOf(category);
const notMounted = this.localCategories.slice(0, idx).filter((category) => !category.mounted);
idx -= notMounted.length;
positionElementByIndex(category.elements.menuTab, this.menu, idx);
positionElementByIndex(category.elements.container, this.scroll.container, idx);
}
category.mounted = visible;
// category.elements.container.classList.toggle('hide', !visible);
}
private onLocalCategoryUpdate(category: StickersTabCategory) {
this.setCategoryItemsHeight(category);
this.toggleLocalCategory(category, !!category.items.length);
}
public deleteSticker(category: StickersTabCategory, doc: MyDocument, batch?: boolean) { public deleteSticker(category: StickersTabCategory, doc: MyDocument, batch?: boolean) {
const item = findAndSplice(category.items, (item) => item.document.id === doc.id); const item = findAndSplice(category.items, (item) => item.document.id === doc.id);
if(item) { if(item) {

8
src/components/generateTitleIcons.ts

@ -10,18 +10,18 @@ import generateFakeIcon from './generateFakeIcon';
import generatePremiumIcon from './generatePremiumIcon'; import generatePremiumIcon from './generatePremiumIcon';
import generateVerifiedIcon from './generateVerifiedIcon'; import generateVerifiedIcon from './generateVerifiedIcon';
export default async function generateTitleIcons(peerId: PeerId) { export default async function generateTitleIcons(peerId: PeerId, noVerifiedIcon?: boolean, noFakeIcon?: boolean, noPremiumIcon?: boolean) {
const elements: Element[] = []; const elements: Element[] = [];
const peer: Chat | User = await rootScope.managers.appPeersManager.getPeer(peerId); const peer: Chat | User = await rootScope.managers.appPeersManager.getPeer(peerId);
if((peer as Chat.channel).pFlags.verified) { if((peer as Chat.channel).pFlags.verified && !noVerifiedIcon) {
elements.push(generateVerifiedIcon()); elements.push(generateVerifiedIcon());
} }
if((peer as Chat.channel).pFlags.fake || (peer as User.user).pFlags.scam) { if(((peer as Chat.channel).pFlags.fake || (peer as User.user).pFlags.scam) && !noFakeIcon) {
elements.push(generateFakeIcon((peer as User.user).pFlags.scam)); elements.push(generateFakeIcon((peer as User.user).pFlags.scam));
} }
if((peer as User.user).pFlags.premium) { if((peer as User.user).pFlags.premium && !noPremiumIcon) {
elements.push(generatePremiumIcon()); elements.push(generatePremiumIcon());
} }

2
src/components/gifsMasonry.ts

@ -5,7 +5,6 @@
*/ */
import type {MyDocument} from '../lib/appManagers/appDocsManager'; import type {MyDocument} from '../lib/appManagers/appDocsManager';
import {wrapVideo} from './wrappers';
import animationIntersector, {AnimationItemGroup} from './animationIntersector'; import animationIntersector, {AnimationItemGroup} from './animationIntersector';
import Scrollable from './scrollable'; import Scrollable from './scrollable';
import deferredPromise, {CancellablePromise} from '../helpers/cancellablePromise'; import deferredPromise, {CancellablePromise} from '../helpers/cancellablePromise';
@ -14,6 +13,7 @@ import {doubleRaf} from '../helpers/schedulers';
import {AppManagers} from '../lib/appManagers/managers'; import {AppManagers} from '../lib/appManagers/managers';
import rootScope from '../lib/rootScope'; import rootScope from '../lib/rootScope';
import LazyLoadQueueRepeat2 from './lazyLoadQueueRepeat2'; import LazyLoadQueueRepeat2 from './lazyLoadQueueRepeat2';
import wrapVideo from './wrappers/video';
const width = 400; const width = 400;
const maxSingleWidth = width - 100; const maxSingleWidth = width - 100;

2
src/components/peerProfileAvatars.ts

@ -21,7 +21,7 @@ import {openAvatarViewer} from './avatar';
import {putAvatar} from './putPhoto'; import {putAvatar} from './putPhoto';
import Scrollable from './scrollable'; import Scrollable from './scrollable';
import SwipeHandler from './swipeHandler'; import SwipeHandler from './swipeHandler';
import {wrapPhoto} from './wrappers'; import wrapPhoto from './wrappers/photo';
const LOAD_NEAREST = 3; const LOAD_NEAREST = 3;

12
src/components/peerTitle.ts

@ -23,7 +23,8 @@ export type PeerTitleOptions = {
dialog?: boolean, dialog?: boolean,
limitSymbols?: number, limitSymbols?: number,
managers?: AppManagers, managers?: AppManagers,
withIcons?: boolean withIcons?: boolean,
withPremiumIcon?: boolean
}; };
const weakMap: WeakMap<HTMLElement, PeerTitle> = new WeakMap(); const weakMap: WeakMap<HTMLElement, PeerTitle> = new WeakMap();
@ -32,11 +33,7 @@ rootScope.addEventListener('peer_title_edit', (peerId) => {
const elements = Array.from(document.querySelectorAll(`.peer-title[data-peer-id="${peerId}"]`)) as HTMLElement[]; const elements = Array.from(document.querySelectorAll(`.peer-title[data-peer-id="${peerId}"]`)) as HTMLElement[];
elements.forEach((element) => { elements.forEach((element) => {
const peerTitle = weakMap.get(element); const peerTitle = weakMap.get(element);
// console.log('in the summer silence i was doing nothing', peerTitle, peerId); peerTitle?.update();
if(peerTitle) {
peerTitle.update();
}
}); });
}); });
@ -51,6 +48,7 @@ export default class PeerTitle {
private managers: AppManagers; private managers: AppManagers;
private hasInner: boolean; private hasInner: boolean;
private withIcons: boolean; private withIcons: boolean;
private withPremiumIcon: boolean;
constructor(options?: PeerTitleOptions) { constructor(options?: PeerTitleOptions) {
this.element = document.createElement('span'); this.element = document.createElement('span');
@ -103,7 +101,7 @@ export default class PeerTitle {
const managers = this.managers ?? rootScope.managers; const managers = this.managers ?? rootScope.managers;
const [title, icons] = await Promise.all([ const [title, icons] = await Promise.all([
getPeerTitle(this.peerId, this.plainText, this.onlyFirstName, this.limitSymbols, managers), getPeerTitle(this.peerId, this.plainText, this.onlyFirstName, this.limitSymbols, managers),
this.withIcons && generateTitleIcons(this.peerId) (this.withIcons && generateTitleIcons(this.peerId)) || (this.withPremiumIcon && generateTitleIcons(this.peerId, true, true))
]); ]);
if(icons?.length) { if(icons?.length) {

2
src/components/popups/joinChatInvite.ts

@ -15,7 +15,7 @@ import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
import AvatarElement from '../avatar'; import AvatarElement from '../avatar';
import putPhoto from '../putPhoto'; import putPhoto from '../putPhoto';
import {toastNew} from '../toast'; import {toastNew} from '../toast';
import {wrapPhoto} from '../wrappers'; import wrapPhoto from '../wrappers/photo';
// const FAKE_CHAT_ID = Number.MAX_SAFE_INTEGER - 0x1000; // const FAKE_CHAT_ID = Number.MAX_SAFE_INTEGER - 0x1000;

47
src/components/popups/mute.ts

@ -7,29 +7,29 @@
import tsNow from '../../helpers/tsNow'; import tsNow from '../../helpers/tsNow';
import {LangPackKey} from '../../lib/langPack'; import {LangPackKey} from '../../lib/langPack';
import {MUTE_UNTIL} from '../../lib/mtproto/mtproto_config'; import {MUTE_UNTIL} from '../../lib/mtproto/mtproto_config';
import RadioField from '../radioField'; import {RadioFormFromValues} from '../row';
import Row, {RadioFormFromRows} from '../row';
import PopupPeer from './peer'; import PopupPeer from './peer';
const ONE_HOUR = 3600; const ONE_HOUR = 3600;
const times: {time: number, langKey: LangPackKey}[] = [{ const times: {value: number | string, langPackKey: LangPackKey, checked?: boolean}[] = [{
time: ONE_HOUR, value: ONE_HOUR,
langKey: 'ChatList.Mute.1Hour' langPackKey: 'ChatList.Mute.1Hour'
}, { }, {
time: ONE_HOUR * 4, value: ONE_HOUR * 4,
langKey: 'ChatList.Mute.4Hours' langPackKey: 'ChatList.Mute.4Hours'
}, { }, {
time: ONE_HOUR * 8, value: ONE_HOUR * 8,
langKey: 'ChatList.Mute.8Hours' langPackKey: 'ChatList.Mute.8Hours'
}, { }, {
time: ONE_HOUR * 24, value: ONE_HOUR * 24,
langKey: 'ChatList.Mute.1Day' langPackKey: 'ChatList.Mute.1Day'
}, { }, {
time: ONE_HOUR * 24 * 3, value: ONE_HOUR * 24 * 3,
langKey: 'ChatList.Mute.3Days' langPackKey: 'ChatList.Mute.3Days'
}, { }, {
time: -1, value: -1,
langKey: 'ChatList.Mute.Forever' langPackKey: 'ChatList.Mute.Forever',
checked: true
}]; }];
export default class PopupMute extends PopupPeer { export default class PopupMute extends PopupPeer {
@ -46,28 +46,13 @@ export default class PopupMute extends PopupPeer {
body: true body: true
}); });
const name = 'mute-time';
const rows = times.map((time) => {
const row = new Row({
radioField: new RadioField({
langKey: time.langKey,
name,
value: '' + time.time
})
});
return row;
});
let time: number; let time: number;
const radioForm = RadioFormFromRows(rows, (value) => { const radioForm = RadioFormFromValues(times, (value) => {
time = +value; time = +value;
}); });
this.body.append(radioForm); this.body.append(radioForm);
rows[rows.length - 1].radioField.checked = true;
this.show(); this.show();
} }
} }

2
src/components/popups/newMedia.ts

@ -9,7 +9,6 @@ import InputField from '../inputField';
import PopupElement from '.'; import PopupElement from '.';
import Scrollable from '../scrollable'; import Scrollable from '../scrollable';
import {toast} from '../toast'; import {toast} from '../toast';
import {wrapDocument} from '../wrappers';
import CheckboxField from '../checkboxField'; import CheckboxField from '../checkboxField';
import SendContextMenu from '../chat/sendContextMenu'; import SendContextMenu from '../chat/sendContextMenu';
import {createPosterFromMedia, createPosterFromVideo} from '../../helpers/createPoster'; import {createPosterFromMedia, createPosterFromVideo} from '../../helpers/createPoster';
@ -28,6 +27,7 @@ import {ThumbCache} from '../../lib/storages/thumbs';
import onMediaLoad from '../../helpers/onMediaLoad'; import onMediaLoad from '../../helpers/onMediaLoad';
import apiManagerProxy from '../../lib/mtproto/mtprotoworker'; import apiManagerProxy from '../../lib/mtproto/mtprotoworker';
import {THUMB_TYPE_FULL} from '../../lib/mtproto/mtproto_config'; import {THUMB_TYPE_FULL} from '../../lib/mtproto/mtproto_config';
import wrapDocument from '../wrappers/document';
type SendFileParams = Partial<{ type SendFileParams = Partial<{
file: File, file: File,

2
src/components/popups/payment.ts

@ -34,8 +34,8 @@ import PeerTitle from '../peerTitle';
import {putPreloader} from '../putPreloader'; import {putPreloader} from '../putPreloader';
import Row from '../row'; import Row from '../row';
import {toastNew} from '../toast'; import {toastNew} from '../toast';
import {wrapPhoto} from '../wrappers';
import wrapPeerTitle from '../wrappers/peerTitle'; import wrapPeerTitle from '../wrappers/peerTitle';
import wrapPhoto from '../wrappers/photo';
import PopupPaymentCard, {PaymentCardDetails, PaymentCardDetailsResult} from './paymentCard'; import PopupPaymentCard, {PaymentCardDetails, PaymentCardDetailsResult} from './paymentCard';
import PopupPaymentCardConfirmation from './paymentCardConfirmation'; import PopupPaymentCardConfirmation from './paymentCardConfirmation';
import PopupPaymentShipping, {PaymentShippingAddress} from './paymentShipping'; import PopupPaymentShipping, {PaymentShippingAddress} from './paymentShipping';

52
src/components/popups/reactedList.ts

@ -5,7 +5,7 @@
*/ */
import PopupElement from '.'; import PopupElement from '.';
import {Message} from '../../layer'; import {Message, Reaction} from '../../layer';
import {SettingSection} from '../sidebarLeft'; import {SettingSection} from '../sidebarLeft';
import ReactionsElement from '../chat/reactions'; import ReactionsElement from '../chat/reactions';
import {horizontalMenu} from '../horizontalMenu'; import {horizontalMenu} from '../horizontalMenu';
@ -13,9 +13,11 @@ import Scrollable from '../scrollable';
import ScrollableLoader from '../../helpers/scrollableLoader'; import ScrollableLoader from '../../helpers/scrollableLoader';
import appDialogsManager from '../../lib/appManagers/appDialogsManager'; import appDialogsManager from '../../lib/appManagers/appDialogsManager';
import replaceContent from '../../helpers/dom/replaceContent'; import replaceContent from '../../helpers/dom/replaceContent';
import {wrapSticker} from '../wrappers'; import wrapSticker from '../wrappers/sticker';
import ReactionElement from '../chat/reaction'; import ReactionElement from '../chat/reaction';
import getUserStatusString from '../wrappers/getUserStatusString'; import getUserStatusString from '../wrappers/getUserStatusString';
import {makeMediaSize} from '../../helpers/mediaSize';
import wrapCustomEmoji from '../wrappers/customEmoji';
export default class PopupReactedList extends PopupElement { export default class PopupReactedList extends PopupElement {
constructor( constructor(
@ -27,10 +29,11 @@ export default class PopupReactedList extends PopupElement {
} }
private async init() { private async init() {
const middleware = this.middlewareHelper.get();
const message = await this.managers.appMessagesManager.getGroupsFirstMessage(this.message); const message = await this.managers.appMessagesManager.getGroupsFirstMessage(this.message);
if(!middleware()) return;
const canViewReadParticipants = await this.managers.appMessagesManager.canViewMessageReadParticipants(message); const canViewReadParticipants = await this.managers.appMessagesManager.canViewMessageReadParticipants(message);
if(!middleware()) return;
// this.body.append(generateDelimiter()); // this.body.append(generateDelimiter());
const reactionsElement = new ReactionsElement(); const reactionsElement = new ReactionsElement();
@ -52,11 +55,12 @@ export default class PopupReactedList extends PopupElement {
newMessage.reactions.results = newMessage.reactions.results.map((reactionCount) => { newMessage.reactions.results = newMessage.reactions.results.map((reactionCount) => {
return { return {
...reactionCount, ...reactionCount,
chosen_order: undefined,
pFlags: {} pFlags: {}
}; };
}); });
reactionsElement.init(newMessage, 'block'); reactionsElement.init(newMessage, 'block', this.middlewareHelper.get());
reactionsElement.render(); reactionsElement.render();
reactionsElement.classList.add('no-stripe'); reactionsElement.classList.add('no-stripe');
reactionsElement.classList.remove('has-no-reactions'); reactionsElement.classList.remove('has-no-reactions');
@ -84,6 +88,7 @@ export default class PopupReactedList extends PopupElement {
if(canViewReadParticipants) { if(canViewReadParticipants) {
try { try {
const readUserIds = await this.managers.appMessagesManager.getMessageReadParticipants(message.peerId, message.mid); const readUserIds = await this.managers.appMessagesManager.getMessageReadParticipants(message.peerId, message.mid);
if(!middleware()) return;
if(!readUserIds.length) { if(!readUserIds.length) {
throw ''; throw '';
} }
@ -118,12 +123,15 @@ export default class PopupReactedList extends PopupElement {
section.content.append(chatlist); section.content.append(chatlist);
scrollable.container.append(section.container); scrollable.container.append(section.container);
const skipReadParticipants = reactionCount.reaction !== 'checks'; const skipReadParticipants = (reactionCount.reaction as any) !== 'checks';
const skipReactionsList = reactionCount.reaction === 'checks'; const skipReactionsList = (reactionCount.reaction as any) === 'checks';
if(['checks', 'reactions'].includes(reactionCount.reaction)) { if(['checks', 'reactions'].includes(reactionCount.reaction as any)) {
reactionCount.reaction = undefined; reactionCount.reaction = undefined;
} }
const size = 24;
const mediaSize = makeMediaSize(size, size);
let nextOffset: string; let nextOffset: string;
const loader = new ScrollableLoader({ const loader = new ScrollableLoader({
scrollable, scrollable,
@ -144,14 +152,24 @@ export default class PopupReactedList extends PopupElement {
if(reaction) { if(reaction) {
const stickerContainer = document.createElement('div'); const stickerContainer = document.createElement('div');
stickerContainer.classList.add('reacted-list-reaction-icon'); stickerContainer.classList.add('reacted-list-reaction-icon');
const availableReaction = await this.managers.appReactionsManager.getReactionCached(reaction);
wrapSticker({ if(reaction._ === 'reactionEmoji') {
doc: availableReaction.static_icon, const availableReaction = await this.managers.appReactionsManager.getReactionCached(reaction.emoticon);
div: stickerContainer,
width: 24, wrapSticker({
height: 24 doc: availableReaction.static_icon,
}); div: stickerContainer,
width: 24,
height: 24,
middleware
});
} else if(reaction._ === 'reactionCustomEmoji') {
stickerContainer.append(wrapCustomEmoji({
docIds: [reaction.document_id],
size: mediaSize,
middleware
}));
}
dom.listEl.append(stickerContainer); dom.listEl.append(stickerContainer);
} }
@ -195,11 +213,11 @@ export default class PopupReactedList extends PopupElement {
private createFakeReaction(icon: string, count: number) { private createFakeReaction(icon: string, count: number) {
const reaction = new ReactionElement(); const reaction = new ReactionElement();
reaction.init('block'); reaction.init('block', this.middlewareHelper.get());
reaction.reactionCount = { reaction.reactionCount = {
_: 'reactionCount', _: 'reactionCount',
count: count, count: count,
reaction: icon reaction: icon as any
}; };
reaction.setCanRenderAvatars(false); reaction.setCanRenderAvatars(false);
reaction.renderCounter(); reaction.renderCounter();

31
src/components/popups/stickers.ts

@ -4,9 +4,9 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import PopupElement from '.';
import type {AppStickersManager} from '../../lib/appManagers/appStickersManager'; import type {AppStickersManager} from '../../lib/appManagers/appStickersManager';
import {wrapSticker} from '../wrappers'; import PopupElement from '.';
import wrapSticker from '../wrappers/sticker';
import LazyLoadQueue from '../lazyLoadQueue'; import LazyLoadQueue from '../lazyLoadQueue';
import {putPreloader} from '../putPreloader'; import {putPreloader} from '../putPreloader';
import animationIntersector, {AnimationItemGroup} from '../animationIntersector'; import animationIntersector, {AnimationItemGroup} from '../animationIntersector';
@ -22,11 +22,11 @@ import setInnerHTML from '../../helpers/dom/setInnerHTML';
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText'; import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
import createStickersContextMenu from '../../helpers/dom/createStickersContextMenu'; import createStickersContextMenu from '../../helpers/dom/createStickersContextMenu';
import attachStickerViewerListeners from '../stickerViewer'; import attachStickerViewerListeners from '../stickerViewer';
import wrapRichText from '../../lib/richTextProcessor/wrapRichText'; import {Document, StickerSet} from '../../layer';
import {Document, MessageEntity, StickerSet} from '../../layer';
import Row from '../row'; import Row from '../row';
import replaceContent from '../../helpers/dom/replaceContent'; import replaceContent from '../../helpers/dom/replaceContent';
import rootScope from '../../lib/rootScope'; import rootScope from '../../lib/rootScope';
import wrapCustomEmoji from '../wrappers/customEmoji';
const ANIMATION_GROUP: AnimationItemGroup = 'STICKERS-POPUP'; const ANIMATION_GROUP: AnimationItemGroup = 'STICKERS-POPUP';
@ -175,29 +175,14 @@ export default class PopupStickers extends PopupElement {
const docs = set.documents.filter((doc) => doc?._ === 'document') as Document.document[]; const docs = set.documents.filter((doc) => doc?._ === 'document') as Document.document[];
if(isEmojis) { if(isEmojis) {
let text = ''; divs = [wrapCustomEmoji({
const entities: MessageEntity[] = []; docIds: docs.map((doc) => doc.id),
docs.forEach((doc) => {
entities.push({
_: 'messageEntityCustomEmoji',
offset: text.length,
length: doc.stickerEmojiRaw.length,
document_id: doc.id
});
text += doc.stickerEmojiRaw;
});
const wrapped = wrapRichText(text, {
entities,
loadPromises, loadPromises,
animationGroup: ANIMATION_GROUP, animationGroup: ANIMATION_GROUP,
customEmojiSize: mediaSizes.active.esgCustomEmoji, size: mediaSizes.active.esgCustomEmoji,
middleware middleware
// lazyLoadQueue // lazyLoadQueue
}); })];
divs = [wrapped];
itemsContainer.classList.add('is-emojis'); itemsContainer.classList.add('is-emojis');
} else { } else {

21
src/components/row.ts

@ -206,3 +206,24 @@ export default class Row {
export const RadioFormFromRows = (rows: Row[], onChange: (value: string) => void) => { export const RadioFormFromRows = (rows: Row[], onChange: (value: string) => void) => {
return RadioForm(rows.map((r) => ({container: r.container, input: r.radioField.input})), onChange); return RadioForm(rows.map((r) => ({container: r.container, input: r.radioField.input})), onChange);
}; };
export const RadioFormFromValues = (values: {langPackKey: LangPackKey, value: number | string, checked?: boolean}[], onChange: Parameters<typeof RadioFormFromRows>[1]) => {
const name = 'name-' + (Math.random() * 0x7FFFFF | 0);
const rows = values.map(({langPackKey, value, checked}) => {
const row = new Row({
radioField: new RadioField({
langKey: langPackKey,
name,
value: '' + value
})
});
if(checked) {
row.radioField.checked = checked;
}
return row;
});
return RadioFormFromRows(rows, onChange);
};

2
src/components/sidebarLeft/tabs/background.ts

@ -23,7 +23,6 @@ import Button from '../../button';
import CheckboxField from '../../checkboxField'; import CheckboxField from '../../checkboxField';
import ProgressivePreloader from '../../preloader'; import ProgressivePreloader from '../../preloader';
import {SliderSuperTab} from '../../slider'; import {SliderSuperTab} from '../../slider';
import {wrapPhoto} from '../../wrappers';
import AppBackgroundColorTab from './backgroundColor'; import AppBackgroundColorTab from './backgroundColor';
import choosePhotoSize from '../../../lib/appManagers/utils/photos/choosePhotoSize'; import choosePhotoSize from '../../../lib/appManagers/utils/photos/choosePhotoSize';
import {STATE_INIT, Theme} from '../../../config/state'; import {STATE_INIT, Theme} from '../../../config/state';
@ -32,6 +31,7 @@ import requestFile from '../../../helpers/files/requestFile';
import {renderImageFromUrlPromise} from '../../../helpers/dom/renderImageFromUrl'; import {renderImageFromUrlPromise} from '../../../helpers/dom/renderImageFromUrl';
import scaleMediaElement from '../../../helpers/canvas/scaleMediaElement'; import scaleMediaElement from '../../../helpers/canvas/scaleMediaElement';
import {MediaSize} from '../../../helpers/mediaSize'; import {MediaSize} from '../../../helpers/mediaSize';
import wrapPhoto from '../../wrappers/photo';
export default class AppBackgroundTab extends SliderSuperTab { export default class AppBackgroundTab extends SliderSuperTab {
private grid: HTMLElement; private grid: HTMLElement;

15
src/components/sidebarLeft/tabs/generalSettings.ts

@ -16,8 +16,7 @@ import AppBackgroundTab from './background';
import {LangPackKey, _i18n} from '../../../lib/langPack'; import {LangPackKey, _i18n} from '../../../lib/langPack';
import {attachClickEvent} from '../../../helpers/dom/clickEvent'; import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import assumeType from '../../../helpers/assumeType'; import assumeType from '../../../helpers/assumeType';
import {MessagesAllStickers, StickerSet} from '../../../layer'; import {AvailableReaction, MessagesAllStickers, StickerSet} from '../../../layer';
import {wrapStickerSetThumb, wrapStickerToRow} from '../../wrappers';
import LazyLoadQueue from '../../lazyLoadQueue'; import LazyLoadQueue from '../../lazyLoadQueue';
import PopupStickers from '../../popups/stickers'; import PopupStickers from '../../popups/stickers';
import eachMinute from '../../../helpers/eachMinute'; import eachMinute from '../../../helpers/eachMinute';
@ -26,6 +25,8 @@ import IS_GEOLOCATION_SUPPORTED from '../../../environment/geolocationSupport';
import AppQuickReactionTab from './quickReaction'; import AppQuickReactionTab from './quickReaction';
import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText'; import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
import {State} from '../../../config/state'; import {State} from '../../../config/state';
import wrapStickerSetThumb from '../../wrappers/stickerSetThumb';
import wrapStickerToRow from '../../wrappers/stickerToRow';
export class RangeSettingSelector { export class RangeSettingSelector {
public container: HTMLDivElement; public container: HTMLDivElement;
@ -259,10 +260,16 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
}); });
const renderQuickReaction = () => { const renderQuickReaction = () => {
Promise.resolve(this.managers.appReactionsManager.getQuickReaction()).then((reaction) => { this.managers.appReactionsManager.getQuickReaction().then((reaction) => {
if(reaction._ === 'availableReaction') {
return reaction.static_icon;
} else {
return this.managers.appEmojiManager.getCustomEmojiDocument(reaction.document_id);
}
}).then((doc) => {
wrapStickerToRow({ wrapStickerToRow({
row: reactionsRow, row: reactionsRow,
doc: reaction.static_icon, doc,
size: 'small' size: 'small'
}); });
}); });

2
src/components/sidebarLeft/tabs/peopleNearby.ts

@ -11,7 +11,7 @@ import {toast} from '../../toast';
import {ButtonMenuItemOptions} from '../../buttonMenu'; import {ButtonMenuItemOptions} from '../../buttonMenu';
import {i18n, join, _i18n} from '../../../lib/langPack'; import {i18n, join, _i18n} from '../../../lib/langPack';
import rootScope from '../../../lib/rootScope'; import rootScope from '../../../lib/rootScope';
import {wrapSticker} from '../../wrappers'; import wrapSticker from '../../wrappers/sticker';
import SortedUserList from '../../sortedUserList'; import SortedUserList from '../../sortedUserList';
import {PeerLocated, Update, Updates} from '../../../layer'; import {PeerLocated, Update, Updates} from '../../../layer';
import {SettingChatListSection} from '..'; import {SettingChatListSection} from '..';

7
src/components/sidebarLeft/tabs/quickReaction.ts

@ -5,10 +5,11 @@
*/ */
import {SettingSection} from '..'; import {SettingSection} from '..';
import {AvailableReaction} from '../../../layer';
import RadioField from '../../radioField'; import RadioField from '../../radioField';
import Row, {RadioFormFromRows} from '../../row'; import Row, {RadioFormFromRows} from '../../row';
import SliderSuperTab from '../../sliderTab'; import SliderSuperTab from '../../sliderTab';
import {wrapStickerToRow} from '../../wrappers'; import wrapStickerToRow from '../../wrappers/stickerToRow';
export default class AppQuickReactionTab extends SliderSuperTab { export default class AppQuickReactionTab extends SliderSuperTab {
protected init() { protected init() {
@ -46,7 +47,7 @@ export default class AppQuickReactionTab extends SliderSuperTab {
size: 'small' size: 'small'
}); });
if(availableReaction.reaction === quickReaction.reaction) { if(availableReaction.reaction === (quickReaction as AvailableReaction).reaction) {
radioField.setValueSilently(true); radioField.setValueSilently(true);
} }
@ -54,7 +55,7 @@ export default class AppQuickReactionTab extends SliderSuperTab {
}); });
const form = RadioFormFromRows(rows, (value) => { const form = RadioFormFromRows(rows, (value) => {
this.managers.appReactionsManager.setDefaultReaction(value); this.managers.appReactionsManager.setDefaultReaction({_: 'reactionEmoji', emoticon: value});
}); });
section.content.append(form); section.content.append(form);

178
src/components/sidebarRight/tabs/chatReactions.ts

@ -4,12 +4,15 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import replaceContent from '../../../helpers/dom/replaceContent';
import debounce from '../../../helpers/schedulers/debounce'; import debounce from '../../../helpers/schedulers/debounce';
import {ChatReactions, Reaction} from '../../../layer';
import {i18n, LangPackKey} from '../../../lib/langPack';
import CheckboxField from '../../checkboxField'; import CheckboxField from '../../checkboxField';
import Row from '../../row'; import Row, {RadioFormFromValues} from '../../row';
import {SettingSection} from '../../sidebarLeft'; import {SettingSection} from '../../sidebarLeft';
import {SliderSuperTabEventable} from '../../sliderTab'; import {SliderSuperTabEventable} from '../../sliderTab';
import {wrapStickerToRow} from '../../wrappers'; import wrapStickerToRow from '../../wrappers/stickerToRow';
export default class AppChatReactionsTab extends SliderSuperTabEventable { export default class AppChatReactionsTab extends SliderSuperTabEventable {
public chatId: ChatId; public chatId: ChatId;
@ -19,44 +22,138 @@ export default class AppChatReactionsTab extends SliderSuperTabEventable {
const availableReactions = await this.managers.appReactionsManager.getActiveAvailableReactions(); const availableReactions = await this.managers.appReactionsManager.getActiveAvailableReactions();
const chatFull = await this.managers.appProfileManager.getChatFull(this.chatId); const chatFull = await this.managers.appProfileManager.getChatFull(this.chatId);
let originalReactions = chatFull.available_reactions ?? []; const isBroadcast = await this.managers.appChatsManager.isBroadcast(this.chatId);
const enabledReactions = new Set(originalReactions);
let _chatReactions = chatFull.available_reactions ?? {_: 'chatReactionsNone'};
let chatReactions = _chatReactions;
let emoticons = new Set(_chatReactions._ === 'chatReactionsSome' ? _chatReactions.reactions.map((reaction) => (reaction as Reaction.reactionEmoji).emoticon) : []);
const makeReactionFromEmoticons = (emoticons: Array<string>): Reaction[] => emoticons.map((emoticon) => ({_: 'reactionEmoji', emoticon}));
const getCaptionLangPackKey = (): LangPackKey => {
if(isBroadcast) {
return 'EnableReactionsChannelInfo';
}
return chatReactions._ === 'chatReactionsAll' ? 'EnableAllReactionsInfo' : (chatReactions._ === 'chatReactionsNone' ? 'DisableReactionsInfo' : 'EnableSomeReactionsInfo');
};
const toggleSection = new SettingSection({ const toggleSection = new SettingSection({
caption: await this.managers.appChatsManager.isBroadcast(this.chatId) ? 'EnableReactionsChannelInfo' : 'EnableReactionsGroupInfo' name: isBroadcast ? undefined : 'AvailableReactions',
caption: getCaptionLangPackKey()
}); });
const toggleCheckboxField = new CheckboxField({toggle: true, checked: !!enabledReactions.size}); const reactionsSection = new SettingSection({
const toggleRow = new Row({ name: 'OnlyAllowThisReactions'
checkboxField: toggleCheckboxField,
titleLangKey: 'EnableReactions',
listenerSetter: this.listenerSetter
}); });
toggleSection.content.append(toggleRow.container); const toggleCheckboxFieldsByEmoticons = () => {
const r: Reaction.reactionEmoji[] = (chatReactions as ChatReactions.chatReactionsSome).reactions as any ?? [];
emoticons = new Set(r.map(({emoticon}) => emoticon));
checkboxFieldsByEmoticon.forEach((checkboxField, emoticon) => {
checkboxField.setValueSilently(emoticons.has(emoticon));
});
};
const reactionsSection = new SettingSection({ let toggleCheckboxField: CheckboxField;
name: 'AvailableReactions' if(isBroadcast) {
}); toggleCheckboxField = new CheckboxField({toggle: true, checked: _chatReactions._ === 'chatReactionsSome'});
const toggleRow = new Row({
checkboxField: toggleCheckboxField,
titleLangKey: 'EnableReactions',
listenerSetter: this.listenerSetter
});
toggleSection.content.append(toggleRow.container);
this.listenerSetter.add(toggleCheckboxField.input)('change', () => {
let save = true;
if(!toggleCheckboxField.checked) {
chatReactions = {_: 'chatReactionsNone'};
} else if(checkboxFields.every((checkboxField) => !checkboxField.checked)) {
chatReactions = {_: 'chatReactionsSome', reactions: makeReactionFromEmoticons(availableReactions.map(({reaction}) => reaction))};
} else if(chatReactions._ !== 'chatReactionsSome') {
chatReactions = {_: 'chatReactionsSome', reactions: makeReactionFromEmoticons(Array.from(emoticons))};
} else {
save = false;
}
if(save) {
toggleCheckboxFieldsByEmoticons();
saveReactionsDebounced();
}
});
} else {
const a: [ChatReactions['_'], LangPackKey][] = [
['chatReactionsAll', 'AllReactions'],
['chatReactionsSome', 'SomeReactions'],
['chatReactionsNone', 'NoReactions']
];
const onChange = () => {
reactionsSection.container.classList.toggle('hide', chatReactions._ !== 'chatReactionsSome');
};
let value = _chatReactions._;
const form = RadioFormFromValues(a.map(([value, langPackKey]) => {
return {
langPackKey,
value,
checked: _chatReactions._ === value
};
}), (_value) => {
value = _value as any;
if(value === 'chatReactionsAll') {
chatReactions = {
_: value,
pFlags: {
allow_custom: true
}
};
} else if(value === 'chatReactionsNone') {
chatReactions = {
_: value
};
} else {
chatReactions = {
_: value,
reactions: makeReactionFromEmoticons(['👍', '👎'])
};
}
replaceContent(toggleSection.caption, i18n(getCaptionLangPackKey()));
toggleCheckboxFieldsByEmoticons();
saveReactionsDebounced();
onChange();
});
toggleSection.content.append(form);
onChange();
}
const checkboxFieldsByEmoticon: Map<string, CheckboxField> = new Map();
const checkboxFields = availableReactions.map((availableReaction) => { const checkboxFields = availableReactions.map((availableReaction) => {
const emoticon = availableReaction.reaction;
const checkboxField = new CheckboxField({ const checkboxField = new CheckboxField({
toggle: true, toggle: true,
checked: enabledReactions.has(availableReaction.reaction) checked: emoticons.has(emoticon)
}); });
checkboxFieldsByEmoticon.set(emoticon, checkboxField);
this.listenerSetter.add(checkboxField.input)('change', () => { this.listenerSetter.add(checkboxField.input)('change', () => {
if(checkboxField.checked) { if(checkboxField.checked) {
enabledReactions.add(availableReaction.reaction); emoticons.add(emoticon);
if(!toggleCheckboxField.checked) { if(toggleCheckboxField && !toggleCheckboxField.checked) {
toggleCheckboxField.setValueSilently(true); toggleCheckboxField.checked = true;
} }
} else { } else {
enabledReactions.delete(availableReaction.reaction); emoticons.delete(emoticon);
if(!enabledReactions.size && toggleCheckboxField.checked) { if(toggleCheckboxField?.checked && !emoticons.size) {
toggleCheckboxField.setValueSilently(false); toggleCheckboxField.checked = false;
} }
} }
@ -81,34 +178,33 @@ export default class AppChatReactionsTab extends SliderSuperTabEventable {
return checkboxField; return checkboxField;
}); });
this.listenerSetter.add(toggleRow.checkboxField.input)('change', () => {
if(!toggleCheckboxField.checked) {
checkboxFields.forEach((checkboxField) => checkboxField.checked = false);
saveReactionsDebounced();
} else if(checkboxFields.every((checkboxField) => !checkboxField.checked)) {
checkboxFields.forEach((checkboxField) => checkboxField.checked = true);
saveReactionsDebounced();
}
});
const saveReactions = async() => { const saveReactions = async() => {
const newReactions = Array.from(enabledReactions); saveReactionsDebounced.clearTimeout();
if([...newReactions].sort().join() === [...originalReactions].sort().join()) { // const newReactions = Array.from(enabledReactions);
return; // if([...newReactions].sort().join() === [...originalReactions].sort().join()) {
// return;
// }
if(chatReactions._ === 'chatReactionsSome') {
chatReactions.reactions = makeReactionFromEmoticons(Array.from(emoticons));
} }
const chatFull = await this.managers.appProfileManager.getCachedFullChat(this.chatId); // const r = (chatReactions as ChatReactions.chatReactionsSome).reactions;
if(chatFull) { // if(r?.length === availableReactions.length) {
chatFull.available_reactions = newReactions; // chatReactions = {_: 'chatReactionsAll'};
} // }
this.managers.appChatsManager.setChatAvailableReactions(this.chatId, newReactions); this.managers.appChatsManager.setChatAvailableReactions(this.chatId, chatReactions);
originalReactions = newReactions; _chatReactions = chatReactions;
}; };
const saveReactionsDebounced = debounce(saveReactions, 3000, false, true); const saveReactionsDebounced = debounce(saveReactions, 3000, false, true);
this.eventListener.addEventListener('destroy', saveReactions, {once: true}); this.eventListener.addEventListener('destroy', () => {
if(saveReactionsDebounced.isDebounced()) {
saveReactions();
}
}, {once: true});
this.scrollable.append(toggleSection.container, reactionsSection.container); this.scrollable.append(toggleSection.container, reactionsSection.container);
} }

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

@ -22,6 +22,7 @@ import toggleDisability from '../../../helpers/dom/toggleDisability';
import CheckboxField from '../../checkboxField'; import CheckboxField from '../../checkboxField';
import AppChatReactionsTab from './chatReactions'; import AppChatReactionsTab from './chatReactions';
import hasRights from '../../../lib/appManagers/utils/chats/hasRights'; import hasRights from '../../../lib/appManagers/utils/chats/hasRights';
import replaceContent from '../../../helpers/dom/replaceContent';
export default class AppEditChatTab extends SliderSuperTab { export default class AppEditChatTab extends SliderSuperTab {
private chatNameInputField: InputField; private chatNameInputField: InputField;
@ -155,8 +156,17 @@ export default class AppEditChatTab extends SliderSuperTab {
const availableReactions = await this.managers.appReactionsManager.getAvailableReactions(); const availableReactions = await this.managers.appReactionsManager.getAvailableReactions();
const availableReactionsLength = availableReactions.filter((availableReaction) => !availableReaction.pFlags.inactive).length; const availableReactionsLength = availableReactions.filter((availableReaction) => !availableReaction.pFlags.inactive).length;
const setReactionsLength = () => { const setReactionsLength = () => {
const reactions = chatFull.available_reactions ?? []; const chatAvailableReactions = chatFull.available_reactions ?? {_: 'chatReactionsNone'};
reactionsRow.subtitle.innerHTML = reactions.length + '/' + availableReactionsLength; if(chatAvailableReactions._ === 'chatReactionsSome') {
const length = chatAvailableReactions.reactions.length;
if(length === availableReactionsLength) {
replaceContent(reactionsRow.subtitle, i18n('ReactionsAll'));
} else {
reactionsRow.subtitle.textContent = length + '/' + availableReactionsLength;
}
} else {
replaceContent(reactionsRow.subtitle, i18n(chatAvailableReactions._ === 'chatReactionsAll' ? 'ReactionsAll' : 'Checkbox.Disabled'));
}
}; };
setReactionsLength(); setReactionsLength();

2
src/components/sidebarRight/tabs/stickers.ts

@ -10,7 +10,6 @@ import LazyLoadQueue from '../../lazyLoadQueue';
import appImManager from '../../../lib/appManagers/appImManager'; import appImManager from '../../../lib/appManagers/appImManager';
import PopupStickers from '../../popups/stickers'; import PopupStickers from '../../popups/stickers';
import animationIntersector from '../../animationIntersector'; import animationIntersector from '../../animationIntersector';
import {wrapSticker} from '../../wrappers';
import appSidebarRight from '..'; import appSidebarRight from '..';
import {StickerSet, StickerSetCovered} from '../../../layer'; import {StickerSet, StickerSetCovered} from '../../../layer';
import {i18n} from '../../../lib/langPack'; import {i18n} from '../../../lib/langPack';
@ -20,6 +19,7 @@ import forEachReverse from '../../../helpers/array/forEachReverse';
import setInnerHTML from '../../../helpers/dom/setInnerHTML'; 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';
export default class AppStickersTab extends SliderSuperTab { export default class AppStickersTab extends SliderSuperTab {
private inputSearch: InputSearch; private inputSearch: InputSearch;

2
src/components/stickerViewer.ts

@ -24,7 +24,7 @@ import RLottiePlayer from '../lib/rlottie/rlottiePlayer';
import rootScope from '../lib/rootScope'; import rootScope from '../lib/rootScope';
import animationIntersector, {AnimationItemGroup} from './animationIntersector'; import animationIntersector, {AnimationItemGroup} from './animationIntersector';
import SetTransition from './singleTransition'; import SetTransition from './singleTransition';
import {wrapSticker} from './wrappers'; import wrapSticker from './wrappers/sticker';
import {STICKER_EFFECT_MULTIPLIER} from './wrappers/sticker'; import {STICKER_EFFECT_MULTIPLIER} from './wrappers/sticker';
let hasViewer = false; let hasViewer = false;

30
src/components/wrappers.ts

@ -4,33 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import wrapAlbum from './wrappers/album'; export {};
import wrapDocument from './wrappers/document';
import wrapGroupedDocuments from './wrappers/groupedDocuments';
import wrapLocalSticker from './wrappers/localSticker';
import wrapPhoto from './wrappers/photo';
import wrapPoll from './wrappers/poll';
import wrapReply from './wrappers/reply';
import wrapSticker from './wrappers/sticker';
import wrapStickerAnimation from './wrappers/stickerAnimation';
import wrapStickerSetThumb from './wrappers/stickerSetThumb';
import wrapStickerToRow from './wrappers/stickerToRow';
import wrapVideo from './wrappers/video';
export {
wrapAlbum,
wrapDocument,
wrapGroupedDocuments,
wrapLocalSticker,
wrapPhoto,
wrapPoll,
wrapReply,
wrapSticker,
wrapStickerAnimation,
wrapStickerSetThumb,
wrapStickerToRow,
wrapVideo
};
/* function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, message: string}, container: HTMLElement, boxWidth: number, boxHeight: number, isOut: boolean) { /* function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, message: string}, container: HTMLElement, boxWidth: number, boxHeight: number, isOut: boolean) {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
@ -144,4 +118,4 @@ export {
// }); // });
// } // }
export {}; export {};

53
src/components/wrappers/customEmoji.ts

@ -0,0 +1,53 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import {MediaSize} from '../../helpers/mediaSize';
import {Middleware} from '../../helpers/middleware';
import {MessageEntity} from '../../layer';
import wrapRichText from '../../lib/richTextProcessor/wrapRichText';
import {AnimationItemGroup} from '../animationIntersector';
import LazyLoadQueue from '../lazyLoadQueue';
export default function wrapCustomEmoji({
docIds,
loadPromises,
middleware,
lazyLoadQueue,
size,
animationGroup
}: {
docIds: DocId[],
loadPromises?: Promise<any>[],
middleware?: Middleware,
lazyLoadQueue?: LazyLoadQueue,
size?: MediaSize,
animationGroup?: AnimationItemGroup
}) {
let text = '';
const entities: MessageEntity[] = [];
docIds.forEach((docId) => {
entities.push({
_: 'messageEntityCustomEmoji',
offset: text.length,
length: 1,
document_id: docId
});
text += ' ';
});
const wrapped = wrapRichText(text, {
entities,
loadPromises,
animationGroup,
customEmojiSize: size,
middleware,
lazyLoadQueue
});
return wrapped;
}

2
src/config/app.ts

@ -19,7 +19,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: '0.4.6', langPackVersion: '0.4.8',
langPack: 'macos', langPack: 'macos',
langPackCode: 'en', langPackCode: 'en',
domains: [MAIN_DOMAIN] as string[], domains: [MAIN_DOMAIN] as string[],

2
src/global.d.ts vendored

@ -15,7 +15,7 @@ declare global {
interface HTMLElement { interface HTMLElement {
middlewareHelper?: MiddlewareHelper; middlewareHelper?: MiddlewareHelper;
middleware?: Middleware; // middleware?: Middleware;
} }
type UserId = User.user['id']; type UserId = User.user['id'];

2
src/helpers/callbackify.ts

@ -9,7 +9,7 @@ import {Awaited} from '../types';
export default function callbackify<T extends Awaited<any>, R>( export default function callbackify<T extends Awaited<any>, R>(
smth: T, smth: T,
callback: (result: Awaited<T>) => R callback: (result: Awaited<T>) => R
): R { ): Promise<Awaited<R>> | R {
if(smth instanceof Promise) { if(smth instanceof Promise) {
// @ts-ignore // @ts-ignore
return smth.then(callback); return smth.then(callback);

2
src/helpers/callbackifyAll.ts

@ -9,7 +9,7 @@ import {Awaited} from '../types';
export default function callbackifyAll<T extends readonly unknown[] | [], R extends any>( export default function callbackifyAll<T extends readonly unknown[] | [], R extends any>(
values: T, values: T,
callback: (result: { -readonly [P in keyof T]: Awaited<T[P]> }) => R callback: (result: { -readonly [P in keyof T]: Awaited<T[P]> }) => R
): PromiseLike<R> | R { ): Promise<Awaited<R>> | R {
if(values.some((value) => value instanceof Promise)) { if(values.some((value) => value instanceof Promise)) {
return Promise.all(values).then(callback as any); return Promise.all(values).then(callback as any);
} else { } else {

3
src/helpers/schedulers/debounce.ts

@ -7,6 +7,7 @@ import noop from '../noop';
export type DebounceReturnType<F extends AnyFunction> = { export type DebounceReturnType<F extends AnyFunction> = {
(...args: Parameters<F>): Promise<Awaited<ReturnType<F>>>; (...args: Parameters<F>): Promise<Awaited<ReturnType<F>>>;
clearTimeout(): void; clearTimeout(): void;
isDebounced(): boolean;
}; };
export default function debounce<F extends AnyFunction>( export default function debounce<F extends AnyFunction>(
@ -71,5 +72,7 @@ export default function debounce<F extends AnyFunction>(
} }
}; };
debounce.isDebounced = () => !!waitingTimeout;
return debounce; return debounce;
} }

24
src/index.hbs

@ -145,17 +145,9 @@
<div class="emoji-container"> <div class="emoji-container">
<div class="tabs-container"> <div class="tabs-container">
<div class="tabs-tab emoji-padding"> <div class="tabs-tab emoji-padding">
<nav class="menu-horizontal-div no-stripe"> <div class="menu-wrapper">
<button class="menu-horizontal-div-item btn-icon tgico-recent rp"></button> <nav class="menu-horizontal-div no-stripe"></nav>
<button class="menu-horizontal-div-item btn-icon tgico-smile rp"></button> </div>
<button class="menu-horizontal-div-item btn-icon tgico-animals rp"></button>
<button class="menu-horizontal-div-item btn-icon tgico-eats rp"></button>
<button class="menu-horizontal-div-item btn-icon tgico-car rp"></button>
<button class="menu-horizontal-div-item btn-icon tgico-sport rp"></button>
<button class="menu-horizontal-div-item btn-icon tgico-lamp rp"></button>
<!-- <button class="menu-horizontal-div-item btn-icon tgico-info rp"></button> -->
<button class="menu-horizontal-div-item btn-icon tgico-flag rp"></button>
</nav>
<div class="emoticons-content" id="content-emoji"></div> <div class="emoticons-content" id="content-emoji"></div>
</div> </div>
<div class="tabs-tab stickers-padding"> <div class="tabs-tab stickers-padding">
@ -172,11 +164,11 @@
</div> </div>
</div> </div>
<div class="emoji-tabs menu-horizontal-div no-stripe"> <div class="emoji-tabs menu-horizontal-div no-stripe">
<button class="menu-horizontal-div-item emoji-tabs-search justify-self-start btn-icon tgico-search rp" data-tab="-1"></button> <button class="menu-horizontal-div-item emoji-tabs-search justify-self-start btn-icon tgico-search" data-tab="-1"></button>
<button class="menu-horizontal-div-item emoji-tabs-emoji btn-icon tgico-smile rp" data-tab="0"></button> <button class="menu-horizontal-div-item emoji-tabs-emoji btn-icon tgico-smile" data-tab="0"></button>
<button class="menu-horizontal-div-item emoji-tabs-stickers btn-icon tgico-stickers rp" data-tab="1"></button> <button class="menu-horizontal-div-item emoji-tabs-stickers btn-icon tgico-stickers" data-tab="1"></button>
<button class="menu-horizontal-div-item emoji-tabs-gifs btn-icon tgico-gifs rp" data-tab="2"></button> <button class="menu-horizontal-div-item emoji-tabs-gifs btn-icon tgico-gifs" data-tab="2"></button>
<button class="menu-horizontal-div-item emoji-tabs-delete justify-self-end btn-icon tgico-deleteleft rp" data-tab="-1"></button> <button class="menu-horizontal-div-item emoji-tabs-delete justify-self-end btn-icon tgico-deleteleft" data-tab="-1"></button>
</div> </div>
</div> </div>
</div> </div>

8
src/lang.ts

@ -775,6 +775,14 @@ const lang = {
'one_value': '%1$d Emoji Pack', 'one_value': '%1$d Emoji Pack',
'other_value': '%1$d Emoji Packs' 'other_value': '%1$d Emoji Packs'
}, },
'ReactionsAll': 'All',
'EnableSomeReactionsInfo': 'Members of the group can use only certain approved emoji as reactions to messages.',
'EnableAllReactionsInfo': 'Members of the group can use any emoji as reactions to messages.',
'DisableReactionsInfo': 'Members of the group can’t add any reactions to messages.',
'OnlyAllowThisReactions': 'Only allow these reactions',
'AllReactions': 'All reactions',
'SomeReactions': 'Some reactions',
'NoReactions': 'No reactions',
// * macos // * macos
'AccountSettings.Filters': 'Chat Folders', 'AccountSettings.Filters': 'Chat Folders',

454
src/layer.d.ts vendored

@ -223,7 +223,8 @@ export namespace InputMedia {
payload: Uint8Array, payload: Uint8Array,
provider: string, provider: string,
provider_data: DataJSON, provider_data: DataJSON,
start_param?: string start_param?: string,
extended_media?: InputMedia
}; };
export type inputMediaGeoLive = { export type inputMediaGeoLive = {
@ -513,6 +514,7 @@ export namespace User {
restriction_reason?: Array<RestrictionReason>, restriction_reason?: Array<RestrictionReason>,
bot_inline_placeholder?: string, bot_inline_placeholder?: string,
lang_code?: string, lang_code?: string,
emoji_status?: EmojiStatus,
sortName?: string sortName?: string
}; };
} }
@ -690,7 +692,7 @@ export namespace ChatFull {
theme_emoticon?: string, theme_emoticon?: string,
requests_pending?: number, requests_pending?: number,
recent_requesters?: Array<string | number>, recent_requesters?: Array<string | number>,
available_reactions?: Array<string> available_reactions?: ChatReactions
}; };
export type channelFull = { export type channelFull = {
@ -742,7 +744,7 @@ export namespace ChatFull {
requests_pending?: number, requests_pending?: number,
recent_requesters?: Array<string | number>, recent_requesters?: Array<string | number>,
default_send_as?: Peer, default_send_as?: Peer,
available_reactions?: Array<string> available_reactions?: ChatReactions
}; };
} }
@ -1008,7 +1010,8 @@ export namespace MessageMedia {
receipt_msg_id?: number, receipt_msg_id?: number,
currency: string, currency: string,
total_amount: string | number, total_amount: string | number,
start_param: string start_param: string,
extended_media?: MessageExtendedMedia
}; };
export type messageMediaGeoLive = { export type messageMediaGeoLive = {
@ -2016,7 +2019,7 @@ export namespace MessagesFilter {
/** /**
* @link https://core.telegram.org/type/Update * @link https://core.telegram.org/type/Update
*/ */
export type Update = Update.updateNewMessage | Update.updateMessageID | Update.updateDeleteMessages | Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChatParticipants | Update.updateUserStatus | Update.updateUserName | Update.updateUserPhoto | Update.updateNewEncryptedMessage | Update.updateEncryptedChatTyping | Update.updateEncryption | Update.updateEncryptedMessagesRead | Update.updateChatParticipantAdd | Update.updateChatParticipantDelete | Update.updateDcOptions | Update.updateNotifySettings | Update.updateServiceNotification | Update.updatePrivacy | Update.updateUserPhone | Update.updateReadHistoryInbox | Update.updateReadHistoryOutbox | Update.updateWebPage | Update.updateReadMessagesContents | Update.updateChannelTooLong | Update.updateChannel | Update.updateNewChannelMessage | Update.updateReadChannelInbox | Update.updateDeleteChannelMessages | Update.updateChannelMessageViews | Update.updateChatParticipantAdmin | Update.updateNewStickerSet | Update.updateStickerSetsOrder | Update.updateStickerSets | Update.updateSavedGifs | Update.updateBotInlineQuery | Update.updateBotInlineSend | Update.updateEditChannelMessage | Update.updateBotCallbackQuery | Update.updateEditMessage | Update.updateInlineBotCallbackQuery | Update.updateReadChannelOutbox | Update.updateDraftMessage | Update.updateReadFeaturedStickers | Update.updateRecentStickers | Update.updateConfig | Update.updatePtsChanged | Update.updateChannelWebPage | Update.updateDialogPinned | Update.updatePinnedDialogs | Update.updateBotWebhookJSON | Update.updateBotWebhookJSONQuery | Update.updateBotShippingQuery | Update.updateBotPrecheckoutQuery | Update.updatePhoneCall | Update.updateLangPackTooLong | Update.updateLangPack | Update.updateFavedStickers | Update.updateChannelReadMessagesContents | Update.updateContactsReset | Update.updateChannelAvailableMessages | Update.updateDialogUnreadMark | Update.updateMessagePoll | Update.updateChatDefaultBannedRights | Update.updateFolderPeers | Update.updatePeerSettings | Update.updatePeerLocated | Update.updateNewScheduledMessage | Update.updateDeleteScheduledMessages | Update.updateTheme | Update.updateGeoLiveViewed | Update.updateLoginToken | Update.updateMessagePollVote | Update.updateDialogFilter | Update.updateDialogFilterOrder | Update.updateDialogFilters | Update.updatePhoneCallSignalingData | Update.updateChannelMessageForwards | Update.updateReadChannelDiscussionInbox | Update.updateReadChannelDiscussionOutbox | Update.updatePeerBlocked | Update.updateChannelUserTyping | Update.updatePinnedMessages | Update.updatePinnedChannelMessages | Update.updateChat | Update.updateGroupCallParticipants | Update.updateGroupCall | Update.updatePeerHistoryTTL | Update.updateChatParticipant | Update.updateChannelParticipant | Update.updateBotStopped | Update.updateGroupCallConnection | Update.updateBotCommands | Update.updatePendingJoinRequests | Update.updateBotChatInviteRequester | Update.updateMessageReactions | Update.updateAttachMenuBots | Update.updateWebViewResultSent | Update.updateBotMenuButton | Update.updateSavedRingtones | Update.updateTranscribedAudio | Update.updateReadFeaturedEmojiStickers | Update.updateNewDiscussionMessage | Update.updateDeleteDiscussionMessages | Update.updateChannelReload; export type Update = Update.updateNewMessage | Update.updateMessageID | Update.updateDeleteMessages | Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChatParticipants | Update.updateUserStatus | Update.updateUserName | Update.updateUserPhoto | Update.updateNewEncryptedMessage | Update.updateEncryptedChatTyping | Update.updateEncryption | Update.updateEncryptedMessagesRead | Update.updateChatParticipantAdd | Update.updateChatParticipantDelete | Update.updateDcOptions | Update.updateNotifySettings | Update.updateServiceNotification | Update.updatePrivacy | Update.updateUserPhone | Update.updateReadHistoryInbox | Update.updateReadHistoryOutbox | Update.updateWebPage | Update.updateReadMessagesContents | Update.updateChannelTooLong | Update.updateChannel | Update.updateNewChannelMessage | Update.updateReadChannelInbox | Update.updateDeleteChannelMessages | Update.updateChannelMessageViews | Update.updateChatParticipantAdmin | Update.updateNewStickerSet | Update.updateStickerSetsOrder | Update.updateStickerSets | Update.updateSavedGifs | Update.updateBotInlineQuery | Update.updateBotInlineSend | Update.updateEditChannelMessage | Update.updateBotCallbackQuery | Update.updateEditMessage | Update.updateInlineBotCallbackQuery | Update.updateReadChannelOutbox | Update.updateDraftMessage | Update.updateReadFeaturedStickers | Update.updateRecentStickers | Update.updateConfig | Update.updatePtsChanged | Update.updateChannelWebPage | Update.updateDialogPinned | Update.updatePinnedDialogs | Update.updateBotWebhookJSON | Update.updateBotWebhookJSONQuery | Update.updateBotShippingQuery | Update.updateBotPrecheckoutQuery | Update.updatePhoneCall | Update.updateLangPackTooLong | Update.updateLangPack | Update.updateFavedStickers | Update.updateChannelReadMessagesContents | Update.updateContactsReset | Update.updateChannelAvailableMessages | Update.updateDialogUnreadMark | Update.updateMessagePoll | Update.updateChatDefaultBannedRights | Update.updateFolderPeers | Update.updatePeerSettings | Update.updatePeerLocated | Update.updateNewScheduledMessage | Update.updateDeleteScheduledMessages | Update.updateTheme | Update.updateGeoLiveViewed | Update.updateLoginToken | Update.updateMessagePollVote | Update.updateDialogFilter | Update.updateDialogFilterOrder | Update.updateDialogFilters | Update.updatePhoneCallSignalingData | Update.updateChannelMessageForwards | Update.updateReadChannelDiscussionInbox | Update.updateReadChannelDiscussionOutbox | Update.updatePeerBlocked | Update.updateChannelUserTyping | Update.updatePinnedMessages | Update.updatePinnedChannelMessages | Update.updateChat | Update.updateGroupCallParticipants | Update.updateGroupCall | Update.updatePeerHistoryTTL | Update.updateChatParticipant | Update.updateChannelParticipant | Update.updateBotStopped | Update.updateGroupCallConnection | Update.updateBotCommands | Update.updatePendingJoinRequests | Update.updateBotChatInviteRequester | Update.updateMessageReactions | Update.updateAttachMenuBots | Update.updateWebViewResultSent | Update.updateBotMenuButton | Update.updateSavedRingtones | Update.updateTranscribedAudio | Update.updateReadFeaturedEmojiStickers | Update.updateUserEmojiStatus | Update.updateRecentEmojiStatuses | Update.updateRecentReactions | Update.updateMoveStickerSetToTop | Update.updateMessageExtendedMedia | Update.updateNewDiscussionMessage | Update.updateDeleteDiscussionMessages | Update.updateChannelReload;
export namespace Update { export namespace Update {
export type updateNewMessage = { export type updateNewMessage = {
@ -2256,7 +2259,12 @@ export namespace Update {
}; };
export type updateStickerSets = { export type updateStickerSets = {
_: 'updateStickerSets' _: 'updateStickerSets',
flags?: number,
pFlags?: Partial<{
masks?: true,
emojis?: true,
}>
}; };
export type updateSavedGifs = { export type updateSavedGifs = {
@ -2741,6 +2749,37 @@ export namespace Update {
_: 'updateReadFeaturedEmojiStickers' _: 'updateReadFeaturedEmojiStickers'
}; };
export type updateUserEmojiStatus = {
_: 'updateUserEmojiStatus',
user_id: string | number,
emoji_status: EmojiStatus
};
export type updateRecentEmojiStatuses = {
_: 'updateRecentEmojiStatuses'
};
export type updateRecentReactions = {
_: 'updateRecentReactions'
};
export type updateMoveStickerSetToTop = {
_: 'updateMoveStickerSetToTop',
flags?: number,
pFlags?: Partial<{
masks?: true,
emojis?: true,
}>,
stickerset: string | number
};
export type updateMessageExtendedMedia = {
_: 'updateMessageExtendedMedia',
peer: Peer,
msg_id: number,
extended_media: MessageExtendedMedia
};
export type updateNewDiscussionMessage = { export type updateNewDiscussionMessage = {
_: 'updateNewDiscussionMessage', _: 'updateNewDiscussionMessage',
message?: Message message?: Message
@ -3050,7 +3089,8 @@ export namespace Config {
webfile_dc_id: number, webfile_dc_id: number,
suggested_lang_code?: string, suggested_lang_code?: string,
lang_pack_version?: number, lang_pack_version?: number,
base_lang_pack_version?: number base_lang_pack_version?: number,
reactions_default?: Reaction
}; };
} }
@ -3928,7 +3968,8 @@ export namespace AccountPassword {
new_algo: PasswordKdfAlgo, new_algo: PasswordKdfAlgo,
new_secure_algo: SecurePasswordKdfAlgo, new_secure_algo: SecurePasswordKdfAlgo,
secure_random: Uint8Array, secure_random: Uint8Array,
pending_reset_date?: number pending_reset_date?: number,
login_email_pattern?: string
}; };
} }
@ -4056,7 +4097,7 @@ export namespace ChatInvite {
/** /**
* @link https://core.telegram.org/type/InputStickerSet * @link https://core.telegram.org/type/InputStickerSet
*/ */
export type InputStickerSet = InputStickerSet.inputStickerSetEmpty | InputStickerSet.inputStickerSetID | InputStickerSet.inputStickerSetShortName | InputStickerSet.inputStickerSetAnimatedEmoji | InputStickerSet.inputStickerSetDice | InputStickerSet.inputStickerSetAnimatedEmojiAnimations | InputStickerSet.inputStickerSetPremiumGifts; export type InputStickerSet = InputStickerSet.inputStickerSetEmpty | InputStickerSet.inputStickerSetID | InputStickerSet.inputStickerSetShortName | InputStickerSet.inputStickerSetAnimatedEmoji | InputStickerSet.inputStickerSetDice | InputStickerSet.inputStickerSetAnimatedEmojiAnimations | InputStickerSet.inputStickerSetPremiumGifts | InputStickerSet.inputStickerSetEmojiGenericAnimations | InputStickerSet.inputStickerSetEmojiDefaultStatuses;
export namespace InputStickerSet { export namespace InputStickerSet {
export type inputStickerSetEmpty = { export type inputStickerSetEmpty = {
@ -4090,6 +4131,14 @@ export namespace InputStickerSet {
export type inputStickerSetPremiumGifts = { export type inputStickerSetPremiumGifts = {
_: 'inputStickerSetPremiumGifts' _: 'inputStickerSetPremiumGifts'
}; };
export type inputStickerSetEmojiGenericAnimations = {
_: 'inputStickerSetEmojiGenericAnimations'
};
export type inputStickerSetEmojiDefaultStatuses = {
_: 'inputStickerSetEmojiDefaultStatuses'
};
} }
/** /**
@ -4133,6 +4182,7 @@ export namespace MessagesStickerSet {
_: 'messages.stickerSet', _: 'messages.stickerSet',
set: StickerSet, set: StickerSet,
packs: Array<StickerPack>, packs: Array<StickerPack>,
keywords: Array<StickerKeyword>,
documents: Array<Document>, documents: Array<Document>,
refreshTime?: number refreshTime?: number
}; };
@ -5114,7 +5164,7 @@ export namespace AuthCodeType {
/** /**
* @link https://core.telegram.org/type/auth.SentCodeType * @link https://core.telegram.org/type/auth.SentCodeType
*/ */
export type AuthSentCodeType = AuthSentCodeType.authSentCodeTypeApp | AuthSentCodeType.authSentCodeTypeSms | AuthSentCodeType.authSentCodeTypeCall | AuthSentCodeType.authSentCodeTypeFlashCall | AuthSentCodeType.authSentCodeTypeMissedCall; export type AuthSentCodeType = AuthSentCodeType.authSentCodeTypeApp | AuthSentCodeType.authSentCodeTypeSms | AuthSentCodeType.authSentCodeTypeCall | AuthSentCodeType.authSentCodeTypeFlashCall | AuthSentCodeType.authSentCodeTypeMissedCall | AuthSentCodeType.authSentCodeTypeEmailCode | AuthSentCodeType.authSentCodeTypeSetUpEmailRequired;
export namespace AuthSentCodeType { export namespace AuthSentCodeType {
export type authSentCodeTypeApp = { export type authSentCodeTypeApp = {
@ -5142,6 +5192,27 @@ export namespace AuthSentCodeType {
prefix: string, prefix: string,
length: number length: number
}; };
export type authSentCodeTypeEmailCode = {
_: 'auth.sentCodeTypeEmailCode',
flags?: number,
pFlags?: Partial<{
apple_signin_allowed?: true,
google_signin_allowed?: true,
}>,
email_pattern: string,
length: number,
next_phone_login_date?: number
};
export type authSentCodeTypeSetUpEmailRequired = {
_: 'auth.sentCodeTypeSetUpEmailRequired',
flags?: number,
pFlags?: Partial<{
apple_signin_allowed?: true,
google_signin_allowed?: true,
}>
};
} }
/** /**
@ -5437,6 +5508,7 @@ export namespace StickerSetCovered {
_: 'stickerSetFullCovered', _: 'stickerSetFullCovered',
set: StickerSet, set: StickerSet,
packs: Array<StickerPack>, packs: Array<StickerPack>,
keywords: Array<StickerKeyword>,
documents: Array<Document> documents: Array<Document>
}; };
} }
@ -6715,8 +6787,8 @@ export namespace ChannelAdminLogEventAction {
export type channelAdminLogEventActionChangeAvailableReactions = { export type channelAdminLogEventActionChangeAvailableReactions = {
_: 'channelAdminLogEventActionChangeAvailableReactions', _: 'channelAdminLogEventActionChangeAvailableReactions',
prev_value: Array<string>, prev_value: ChatReactions,
new_value: Array<string> new_value: ChatReactions
}; };
} }
@ -9422,7 +9494,7 @@ export type ChannelsSendAsPeers = ChannelsSendAsPeers.channelsSendAsPeers;
export namespace ChannelsSendAsPeers { export namespace ChannelsSendAsPeers {
export type channelsSendAsPeers = { export type channelsSendAsPeers = {
_: 'channels.sendAsPeers', _: 'channels.sendAsPeers',
peers: Array<Peer>, peers: Array<SendAsPeer>,
chats: Array<Chat>, chats: Array<Chat>,
users: Array<User> users: Array<User>
}; };
@ -9478,10 +9550,8 @@ export namespace ReactionCount {
export type reactionCount = { export type reactionCount = {
_: 'reactionCount', _: 'reactionCount',
flags?: number, flags?: number,
pFlags?: Partial<{ chosen_order?: number,
chosen?: true, reaction: Reaction,
}>,
reaction: string,
count: number count: number
}; };
} }
@ -9593,7 +9663,7 @@ export namespace MessagePeerReaction {
unread?: true, unread?: true,
}>, }>,
peer_id: Peer, peer_id: Peer,
reaction: string reaction: Reaction
}; };
} }
@ -9919,8 +9989,7 @@ export namespace HelpPremiumPromo {
status_entities: Array<MessageEntity>, status_entities: Array<MessageEntity>,
video_sections: Array<string>, video_sections: Array<string>,
videos: Array<Document>, videos: Array<Document>,
currency: string, period_options: Array<PremiumSubscriptionOption>,
monthly_amount: string | number,
users: Array<User> users: Array<User>
}; };
} }
@ -9977,6 +10046,239 @@ export namespace PaymentFormMethod {
}; };
} }
/**
* @link https://core.telegram.org/type/EmojiStatus
*/
export type EmojiStatus = EmojiStatus.emojiStatusEmpty | EmojiStatus.emojiStatus | EmojiStatus.emojiStatusUntil;
export namespace EmojiStatus {
export type emojiStatusEmpty = {
_: 'emojiStatusEmpty'
};
export type emojiStatus = {
_: 'emojiStatus',
document_id: string | number
};
export type emojiStatusUntil = {
_: 'emojiStatusUntil',
document_id: string | number,
until: number
};
}
/**
* @link https://core.telegram.org/type/account.EmojiStatuses
*/
export type AccountEmojiStatuses = AccountEmojiStatuses.accountEmojiStatusesNotModified | AccountEmojiStatuses.accountEmojiStatuses;
export namespace AccountEmojiStatuses {
export type accountEmojiStatusesNotModified = {
_: 'account.emojiStatusesNotModified'
};
export type accountEmojiStatuses = {
_: 'account.emojiStatuses',
hash: string | number,
statuses: Array<EmojiStatus>
};
}
/**
* @link https://core.telegram.org/type/Reaction
*/
export type Reaction = Reaction.reactionEmpty | Reaction.reactionEmoji | Reaction.reactionCustomEmoji;
export namespace Reaction {
export type reactionEmpty = {
_: 'reactionEmpty'
};
export type reactionEmoji = {
_: 'reactionEmoji',
emoticon: string
};
export type reactionCustomEmoji = {
_: 'reactionCustomEmoji',
document_id: string | number
};
}
/**
* @link https://core.telegram.org/type/ChatReactions
*/
export type ChatReactions = ChatReactions.chatReactionsNone | ChatReactions.chatReactionsAll | ChatReactions.chatReactionsSome;
export namespace ChatReactions {
export type chatReactionsNone = {
_: 'chatReactionsNone'
};
export type chatReactionsAll = {
_: 'chatReactionsAll',
flags?: number,
pFlags?: Partial<{
allow_custom?: true,
}>
};
export type chatReactionsSome = {
_: 'chatReactionsSome',
reactions: Array<Reaction>
};
}
/**
* @link https://core.telegram.org/type/messages.Reactions
*/
export type MessagesReactions = MessagesReactions.messagesReactionsNotModified | MessagesReactions.messagesReactions;
export namespace MessagesReactions {
export type messagesReactionsNotModified = {
_: 'messages.reactionsNotModified'
};
export type messagesReactions = {
_: 'messages.reactions',
hash: string | number,
reactions: Array<Reaction>
};
}
/**
* @link https://core.telegram.org/type/EmailVerifyPurpose
*/
export type EmailVerifyPurpose = EmailVerifyPurpose.emailVerifyPurposeLoginSetup | EmailVerifyPurpose.emailVerifyPurposeLoginChange | EmailVerifyPurpose.emailVerifyPurposePassport;
export namespace EmailVerifyPurpose {
export type emailVerifyPurposeLoginSetup = {
_: 'emailVerifyPurposeLoginSetup',
phone_number: string,
phone_code_hash: string
};
export type emailVerifyPurposeLoginChange = {
_: 'emailVerifyPurposeLoginChange'
};
export type emailVerifyPurposePassport = {
_: 'emailVerifyPurposePassport'
};
}
/**
* @link https://core.telegram.org/type/EmailVerification
*/
export type EmailVerification = EmailVerification.emailVerificationCode | EmailVerification.emailVerificationGoogle | EmailVerification.emailVerificationApple;
export namespace EmailVerification {
export type emailVerificationCode = {
_: 'emailVerificationCode',
code: string
};
export type emailVerificationGoogle = {
_: 'emailVerificationGoogle',
token: string
};
export type emailVerificationApple = {
_: 'emailVerificationApple',
token: string
};
}
/**
* @link https://core.telegram.org/type/account.EmailVerified
*/
export type AccountEmailVerified = AccountEmailVerified.accountEmailVerified | AccountEmailVerified.accountEmailVerifiedLogin;
export namespace AccountEmailVerified {
export type accountEmailVerified = {
_: 'account.emailVerified',
email: string
};
export type accountEmailVerifiedLogin = {
_: 'account.emailVerifiedLogin',
email: string,
sent_code: AuthSentCode
};
}
/**
* @link https://core.telegram.org/type/PremiumSubscriptionOption
*/
export type PremiumSubscriptionOption = PremiumSubscriptionOption.premiumSubscriptionOption;
export namespace PremiumSubscriptionOption {
export type premiumSubscriptionOption = {
_: 'premiumSubscriptionOption',
flags?: number,
pFlags?: Partial<{
current?: true,
can_purchase_upgrade?: true,
}>,
months: number,
currency: string,
amount: string | number,
bot_url: string,
store_product?: string
};
}
/**
* @link https://core.telegram.org/type/SendAsPeer
*/
export type SendAsPeer = SendAsPeer.sendAsPeer;
export namespace SendAsPeer {
export type sendAsPeer = {
_: 'sendAsPeer',
flags?: number,
pFlags?: Partial<{
premium_required?: true,
}>,
peer: Peer
};
}
/**
* @link https://core.telegram.org/type/MessageExtendedMedia
*/
export type MessageExtendedMedia = MessageExtendedMedia.messageExtendedMediaPreview | MessageExtendedMedia.messageExtendedMedia;
export namespace MessageExtendedMedia {
export type messageExtendedMediaPreview = {
_: 'messageExtendedMediaPreview',
flags?: number,
w?: number,
h?: number,
thumb?: PhotoSize,
video_duration?: number
};
export type messageExtendedMedia = {
_: 'messageExtendedMedia',
media: MessageMedia
};
}
/**
* @link https://core.telegram.org/type/StickerKeyword
*/
export type StickerKeyword = StickerKeyword.stickerKeyword;
export namespace StickerKeyword {
export type stickerKeyword = {
_: 'stickerKeyword',
document_id: string | number,
keyword: Array<string>
};
}
export interface ConstructorDeclMap { export interface ConstructorDeclMap {
'error': Error.error, 'error': Error.error,
'inputPeerEmpty': InputPeer.inputPeerEmpty, 'inputPeerEmpty': InputPeer.inputPeerEmpty,
@ -10986,6 +11288,41 @@ export interface ConstructorDeclMap {
'privacyKeyVoiceMessages': PrivacyKey.privacyKeyVoiceMessages, 'privacyKeyVoiceMessages': PrivacyKey.privacyKeyVoiceMessages,
'paymentFormMethod': PaymentFormMethod.paymentFormMethod, 'paymentFormMethod': PaymentFormMethod.paymentFormMethod,
'inputWebFileAudioAlbumThumbLocation': InputWebFileLocation.inputWebFileAudioAlbumThumbLocation, 'inputWebFileAudioAlbumThumbLocation': InputWebFileLocation.inputWebFileAudioAlbumThumbLocation,
'emojiStatusEmpty': EmojiStatus.emojiStatusEmpty,
'emojiStatus': EmojiStatus.emojiStatus,
'emojiStatusUntil': EmojiStatus.emojiStatusUntil,
'updateUserEmojiStatus': Update.updateUserEmojiStatus,
'updateRecentEmojiStatuses': Update.updateRecentEmojiStatuses,
'account.emojiStatusesNotModified': AccountEmojiStatuses.accountEmojiStatusesNotModified,
'account.emojiStatuses': AccountEmojiStatuses.accountEmojiStatuses,
'reactionEmpty': Reaction.reactionEmpty,
'reactionEmoji': Reaction.reactionEmoji,
'reactionCustomEmoji': Reaction.reactionCustomEmoji,
'chatReactionsNone': ChatReactions.chatReactionsNone,
'chatReactionsAll': ChatReactions.chatReactionsAll,
'chatReactionsSome': ChatReactions.chatReactionsSome,
'messages.reactionsNotModified': MessagesReactions.messagesReactionsNotModified,
'messages.reactions': MessagesReactions.messagesReactions,
'updateRecentReactions': Update.updateRecentReactions,
'updateMoveStickerSetToTop': Update.updateMoveStickerSetToTop,
'auth.sentCodeTypeEmailCode': AuthSentCodeType.authSentCodeTypeEmailCode,
'auth.sentCodeTypeSetUpEmailRequired': AuthSentCodeType.authSentCodeTypeSetUpEmailRequired,
'emailVerifyPurposeLoginSetup': EmailVerifyPurpose.emailVerifyPurposeLoginSetup,
'emailVerifyPurposeLoginChange': EmailVerifyPurpose.emailVerifyPurposeLoginChange,
'emailVerifyPurposePassport': EmailVerifyPurpose.emailVerifyPurposePassport,
'emailVerificationCode': EmailVerification.emailVerificationCode,
'emailVerificationGoogle': EmailVerification.emailVerificationGoogle,
'emailVerificationApple': EmailVerification.emailVerificationApple,
'account.emailVerified': AccountEmailVerified.accountEmailVerified,
'account.emailVerifiedLogin': AccountEmailVerified.accountEmailVerifiedLogin,
'premiumSubscriptionOption': PremiumSubscriptionOption.premiumSubscriptionOption,
'inputStickerSetEmojiGenericAnimations': InputStickerSet.inputStickerSetEmojiGenericAnimations,
'inputStickerSetEmojiDefaultStatuses': InputStickerSet.inputStickerSetEmojiDefaultStatuses,
'sendAsPeer': SendAsPeer.sendAsPeer,
'messageExtendedMediaPreview': MessageExtendedMedia.messageExtendedMediaPreview,
'messageExtendedMedia': MessageExtendedMedia.messageExtendedMedia,
'updateMessageExtendedMedia': Update.updateMessageExtendedMedia,
'stickerKeyword': StickerKeyword.stickerKeyword,
'messageEntityEmoji': MessageEntity.messageEntityEmoji, 'messageEntityEmoji': MessageEntity.messageEntityEmoji,
'messageEntityHighlight': MessageEntity.messageEntityHighlight, 'messageEntityHighlight': MessageEntity.messageEntityHighlight,
'messageEntityLinebreak': MessageEntity.messageEntityLinebreak, 'messageEntityLinebreak': MessageEntity.messageEntityLinebreak,
@ -11033,9 +11370,11 @@ export type AuthSignUp = {
}; };
export type AuthSignIn = { export type AuthSignIn = {
flags?: number,
phone_number: string, phone_number: string,
phone_code_hash: string, phone_code_hash: string,
phone_code: string phone_code?: string,
email_verification?: EmailVerification
}; };
export type AuthLogOut = { export type AuthLogOut = {
@ -11239,6 +11578,7 @@ export type MessagesSendMessage = {
background?: boolean, background?: boolean,
clear_draft?: boolean, clear_draft?: boolean,
noforwards?: boolean, noforwards?: boolean,
update_stickersets_order?: boolean,
peer: InputPeer, peer: InputPeer,
reply_to_msg_id?: number, reply_to_msg_id?: number,
message: string, message: string,
@ -11255,6 +11595,7 @@ export type MessagesSendMedia = {
background?: boolean, background?: boolean,
clear_draft?: boolean, clear_draft?: boolean,
noforwards?: boolean, noforwards?: boolean,
update_stickersets_order?: boolean,
peer: InputPeer, peer: InputPeer,
reply_to_msg_id?: number, reply_to_msg_id?: number,
media: InputMedia, media: InputMedia,
@ -12366,6 +12707,7 @@ export type MessagesSendMultiMedia = {
background?: boolean, background?: boolean,
clear_draft?: boolean, clear_draft?: boolean,
noforwards?: boolean, noforwards?: boolean,
update_stickersets_order?: boolean,
peer: InputPeer, peer: InputPeer,
reply_to_msg_id?: number, reply_to_msg_id?: number,
multi_media: Array<InputSingleMedia>, multi_media: Array<InputSingleMedia>,
@ -12458,12 +12800,13 @@ export type AccountVerifyPhone = {
}; };
export type AccountSendVerifyEmailCode = { export type AccountSendVerifyEmailCode = {
purpose: EmailVerifyPurpose,
email: string email: string
}; };
export type AccountVerifyEmail = { export type AccountVerifyEmail = {
email: string, purpose: EmailVerifyPurpose,
code: string verification: EmailVerification
}; };
export type HelpGetDeepLinkInfo = { export type HelpGetDeepLinkInfo = {
@ -13340,9 +13683,10 @@ export type ChannelsDeleteParticipantHistory = {
export type MessagesSendReaction = { export type MessagesSendReaction = {
flags?: number, flags?: number,
big?: boolean, big?: boolean,
add_to_recent?: boolean,
peer: InputPeer, peer: InputPeer,
msg_id: number, msg_id: number,
reaction?: string reaction?: Array<Reaction>
}; };
export type MessagesGetMessagesReactions = { export type MessagesGetMessagesReactions = {
@ -13354,14 +13698,14 @@ export type MessagesGetMessageReactionsList = {
flags?: number, flags?: number,
peer: InputPeer, peer: InputPeer,
id: number, id: number,
reaction?: string, reaction?: Reaction,
offset?: string, offset?: string,
limit: number limit: number
}; };
export type MessagesSetChatAvailableReactions = { export type MessagesSetChatAvailableReactions = {
peer: InputPeer, peer: InputPeer,
available_reactions: Array<string> available_reactions: ChatReactions
}; };
export type MessagesGetAvailableReactions = { export type MessagesGetAvailableReactions = {
@ -13369,7 +13713,7 @@ export type MessagesGetAvailableReactions = {
}; };
export type MessagesSetDefaultReaction = { export type MessagesSetDefaultReaction = {
reaction: string reaction: Reaction
}; };
export type MessagesTranslateText = { export type MessagesTranslateText = {
@ -13435,6 +13779,7 @@ export type MessagesRequestWebView = {
url?: string, url?: string,
start_param?: string, start_param?: string,
theme_params?: DataJSON, theme_params?: DataJSON,
platform: string,
reply_to_msg_id?: number, reply_to_msg_id?: number,
send_as?: InputPeer send_as?: InputPeer
}; };
@ -13453,7 +13798,8 @@ export type MessagesRequestSimpleWebView = {
flags?: number, flags?: number,
bot: InputUser, bot: InputUser,
url: string, url: string,
theme_params?: DataJSON theme_params?: DataJSON,
platform: string
}; };
export type MessagesSendWebViewResultMessage = { export type MessagesSendWebViewResultMessage = {
@ -13561,6 +13907,47 @@ export type MessagesGetFeaturedEmojiStickers = {
hash: string | number hash: string | number
}; };
export type AccountUpdateEmojiStatus = {
emoji_status: EmojiStatus
};
export type AccountGetDefaultEmojiStatuses = {
hash: string | number
};
export type AccountGetRecentEmojiStatuses = {
hash: string | number
};
export type AccountClearRecentEmojiStatuses = {
};
export type MessagesReportReaction = {
peer: InputPeer,
id: number,
reaction_peer: InputPeer
};
export type MessagesGetTopReactions = {
limit: number,
hash: string | number
};
export type MessagesGetRecentReactions = {
limit: number,
hash: string | number
};
export type MessagesClearRecentReactions = {
};
export type MessagesGetExtendedMedia = {
peer: InputPeer,
id: Array<number>
};
export interface MethodDeclMap { export interface MethodDeclMap {
'invokeAfterMsg': {req: InvokeAfterMsg, res: any}, 'invokeAfterMsg': {req: InvokeAfterMsg, res: any},
'invokeAfterMsgs': {req: InvokeAfterMsgs, res: any}, 'invokeAfterMsgs': {req: InvokeAfterMsgs, res: any},
@ -13808,7 +14195,7 @@ export interface MethodDeclMap {
'account.sendVerifyPhoneCode': {req: AccountSendVerifyPhoneCode, res: AuthSentCode}, 'account.sendVerifyPhoneCode': {req: AccountSendVerifyPhoneCode, res: AuthSentCode},
'account.verifyPhone': {req: AccountVerifyPhone, res: boolean}, 'account.verifyPhone': {req: AccountVerifyPhone, res: boolean},
'account.sendVerifyEmailCode': {req: AccountSendVerifyEmailCode, res: AccountSentEmailCode}, 'account.sendVerifyEmailCode': {req: AccountSendVerifyEmailCode, res: AccountSentEmailCode},
'account.verifyEmail': {req: AccountVerifyEmail, res: boolean}, 'account.verifyEmail': {req: AccountVerifyEmail, res: AccountEmailVerified},
'help.getDeepLinkInfo': {req: HelpGetDeepLinkInfo, res: HelpDeepLinkInfo}, 'help.getDeepLinkInfo': {req: HelpGetDeepLinkInfo, res: HelpDeepLinkInfo},
'contacts.getSaved': {req: ContactsGetSaved, res: Array<SavedContact>}, 'contacts.getSaved': {req: ContactsGetSaved, res: Array<SavedContact>},
'channels.getLeftChannels': {req: ChannelsGetLeftChannels, res: MessagesChats}, 'channels.getLeftChannels': {req: ChannelsGetLeftChannels, res: MessagesChats},
@ -14009,5 +14396,14 @@ export interface MethodDeclMap {
'messages.getCustomEmojiDocuments': {req: MessagesGetCustomEmojiDocuments, res: Array<Document>}, 'messages.getCustomEmojiDocuments': {req: MessagesGetCustomEmojiDocuments, res: Array<Document>},
'messages.getEmojiStickers': {req: MessagesGetEmojiStickers, res: MessagesAllStickers}, 'messages.getEmojiStickers': {req: MessagesGetEmojiStickers, res: MessagesAllStickers},
'messages.getFeaturedEmojiStickers': {req: MessagesGetFeaturedEmojiStickers, res: MessagesFeaturedStickers}, 'messages.getFeaturedEmojiStickers': {req: MessagesGetFeaturedEmojiStickers, res: MessagesFeaturedStickers},
'account.updateEmojiStatus': {req: AccountUpdateEmojiStatus, res: boolean},
'account.getDefaultEmojiStatuses': {req: AccountGetDefaultEmojiStatuses, res: AccountEmojiStatuses},
'account.getRecentEmojiStatuses': {req: AccountGetRecentEmojiStatuses, res: AccountEmojiStatuses},
'account.clearRecentEmojiStatuses': {req: AccountClearRecentEmojiStatuses, res: boolean},
'messages.reportReaction': {req: MessagesReportReaction, res: boolean},
'messages.getTopReactions': {req: MessagesGetTopReactions, res: MessagesReactions},
'messages.getRecentReactions': {req: MessagesGetRecentReactions, res: MessagesReactions},
'messages.clearRecentReactions': {req: MessagesClearRecentReactions, res: boolean},
'messages.getExtendedMedia': {req: MessagesGetExtendedMedia, res: Updates},
} }

9
src/lib/appManagers/appChatsManager.ts

@ -12,7 +12,7 @@
import deepEqual from '../../helpers/object/deepEqual'; import deepEqual from '../../helpers/object/deepEqual';
import isObject from '../../helpers/object/isObject'; import isObject from '../../helpers/object/isObject';
import safeReplaceObject from '../../helpers/object/safeReplaceObject'; import safeReplaceObject from '../../helpers/object/safeReplaceObject';
import {ChannelParticipant, ChannelsCreateChannel, Chat, ChatAdminRights, ChatBannedRights, ChatInvite, ChatPhoto, InputChannel, InputChatPhoto, InputFile, InputPeer, SponsoredMessage, Update, Updates} from '../../layer'; import {ChannelParticipant, ChannelsCreateChannel, Chat, ChatAdminRights, ChatBannedRights, ChatInvite, ChatPhoto, ChatReactions, InputChannel, InputChatPhoto, InputFile, InputPeer, SponsoredMessage, Update, Updates} from '../../layer';
import {isRestricted} from '../../helpers/restrictions'; import {isRestricted} from '../../helpers/restrictions';
import {AppManager} from './manager'; import {AppManager} from './manager';
import hasRights from './utils/chats/hasRights'; import hasRights from './utils/chats/hasRights';
@ -643,7 +643,12 @@ export class AppChatsManager extends AppManager {
}); });
} }
public setChatAvailableReactions(id: ChatId, reactions: Array<string>) { public setChatAvailableReactions(id: ChatId, reactions: ChatReactions) {
const chatFull = this.appProfileManager.getCachedFullChat(id);
if(chatFull) {
chatFull.available_reactions = reactions;
}
return this.apiManager.invokeApi('messages.setChatAvailableReactions', { return this.apiManager.invokeApi('messages.setChatAvailableReactions', {
peer: this.getInputPeer(id), peer: this.getInputPeer(id),
available_reactions: reactions available_reactions: reactions

2
src/lib/appManagers/appDialogsManager.ts

@ -28,7 +28,7 @@ import PeerTitle from '../../components/peerTitle';
import I18n, {FormatterArguments, i18n, LangPackKey, _i18n} from '../langPack'; import I18n, {FormatterArguments, i18n, LangPackKey, _i18n} from '../langPack';
import findUpTag from '../../helpers/dom/findUpTag'; import findUpTag from '../../helpers/dom/findUpTag';
import lottieLoader from '../rlottie/lottieLoader'; import lottieLoader from '../rlottie/lottieLoader';
import {wrapPhoto} from '../../components/wrappers'; import wrapPhoto from '../../components/wrappers/photo';
import AppEditFolderTab from '../../components/sidebarLeft/tabs/editFolder'; import AppEditFolderTab from '../../components/sidebarLeft/tabs/editFolder';
import appSidebarLeft, {SettingSection} from '../../components/sidebarLeft'; import appSidebarLeft, {SettingSection} from '../../components/sidebarLeft';
import {attachClickEvent} from '../../helpers/dom/clickEvent'; import {attachClickEvent} from '../../helpers/dom/clickEvent';

4
src/lib/appManagers/appEmojiManager.ts

@ -293,4 +293,8 @@ export class AppEmojiManager extends AppManager {
return promise; return promise;
} }
public getCustomEmojis() {
return this.appStickersManager.getEmojiStickers();
}
} }

13
src/lib/appManagers/appMessagesManager.ts

@ -13,7 +13,7 @@ import LazyLoadQueueBase from '../../components/lazyLoadQueueBase';
import deferredPromise, {CancellablePromise} from '../../helpers/cancellablePromise'; import deferredPromise, {CancellablePromise} from '../../helpers/cancellablePromise';
import tsNow from '../../helpers/tsNow'; import tsNow from '../../helpers/tsNow';
import {randomLong} from '../../helpers/random'; import {randomLong} from '../../helpers/random';
import {Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo, Updates, ReplyMarkup, InputPeer, InputPhoto, InputDocument, InputGeoPoint, WebPage, GeoPoint, ReportReason, MessagesGetDialogs, InputChannel, InputDialogPeer, ReactionCount, MessagePeerReaction, MessagesSearchCounter, Peer, MessageReactions, Document, InputFile} from '../../layer'; import {Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo, Updates, ReplyMarkup, InputPeer, InputPhoto, InputDocument, InputGeoPoint, WebPage, GeoPoint, ReportReason, MessagesGetDialogs, InputChannel, InputDialogPeer, ReactionCount, MessagePeerReaction, MessagesSearchCounter, Peer, MessageReactions, Document, InputFile, Reaction} from '../../layer';
import {ArgumentTypes, InvokeApiOptions} from '../../types'; import {ArgumentTypes, InvokeApiOptions} from '../../types';
import {logger, LogTypes} from '../logger'; import {logger, LogTypes} from '../logger';
import type {ApiFileManager} from '../mtproto/apiFileManager'; import type {ApiFileManager} from '../mtproto/apiFileManager';
@ -62,6 +62,7 @@ import pause from '../../helpers/schedulers/pause';
import makeError from '../../helpers/makeError'; import makeError from '../../helpers/makeError';
import getStickerEffectThumb from './utils/stickers/getStickerEffectThumb'; import getStickerEffectThumb from './utils/stickers/getStickerEffectThumb';
import getDocumentInput from './utils/docs/getDocumentInput'; import getDocumentInput from './utils/docs/getDocumentInput';
import reactionsEqual from './utils/reactions/reactionsEqual';
// console.trace('include'); // console.trace('include');
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках // TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
@ -5012,7 +5013,7 @@ export class AppMessagesManager extends AppManager {
public async getMessageReactionsListAndReadParticipants( public async getMessageReactionsListAndReadParticipants(
message: Message.message, message: Message.message,
limit?: number, limit?: number,
reaction?: string, reaction?: Reaction,
offset?: string, offset?: string,
skipReadParticipants?: boolean, skipReadParticipants?: boolean,
skipReactionsList?: boolean skipReactionsList?: boolean
@ -5044,7 +5045,7 @@ export class AppMessagesManager extends AppManager {
} }
}); });
let combined: {peerId: PeerId, reaction?: string}[] = messageReactionsList.reactions.map((reaction) => ({peerId: this.appPeersManager.getPeerId(reaction.peer_id), reaction: reaction.reaction})); let combined: {peerId: PeerId, reaction?: Reaction}[] = messageReactionsList.reactions.map((reaction) => ({peerId: this.appPeersManager.getPeerId(reaction.peer_id), reaction: reaction.reaction}));
combined = combined.concat(filteredReadParticipants.map((readPeerId) => ({peerId: readPeerId}))); combined = combined.concat(filteredReadParticipants.map((readPeerId) => ({peerId: readPeerId})));
return { return {
@ -5882,16 +5883,16 @@ export class AppMessagesManager extends AppManager {
const results = message.reactions?.results ?? []; const results = message.reactions?.results ?? [];
const previousResults = previousReactions?.results ?? []; const previousResults = previousReactions?.results ?? [];
const changedResults = results.filter((reactionCount) => { const changedResults = results.filter((reactionCount) => {
const previousReactionCount = previousResults.find((_reactionCount) => _reactionCount.reaction === reactionCount.reaction); const previousReactionCount = previousResults.find((_reactionCount) => reactionsEqual(_reactionCount.reaction, reactionCount.reaction));
return ( return (
message.pFlags.out && ( message.pFlags.out && (
!previousReactionCount || !previousReactionCount ||
reactionCount.count > previousReactionCount.count reactionCount.count > previousReactionCount.count
) )
) || ( ) || (
reactionCount.pFlags.chosen && ( reactionCount.chosen_order !== undefined && (
!previousReactionCount || !previousReactionCount ||
!previousReactionCount.pFlags.chosen previousReactionCount.chosen_order === undefined
) )
); );
}); });

141
src/lib/appManagers/appReactionsManager.ts

@ -5,14 +5,16 @@
*/ */
import findAndSplice from '../../helpers/array/findAndSplice'; import findAndSplice from '../../helpers/array/findAndSplice';
import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
import assumeType from '../../helpers/assumeType'; import assumeType from '../../helpers/assumeType';
import callbackify from '../../helpers/callbackify'; import callbackify from '../../helpers/callbackify';
import callbackifyAll from '../../helpers/callbackifyAll'; import callbackifyAll from '../../helpers/callbackifyAll';
import copy from '../../helpers/object/copy'; import copy from '../../helpers/object/copy';
import {AvailableReaction, Message, MessagePeerReaction, MessagesAvailableReactions, Update, Updates} from '../../layer'; import {AvailableReaction, Message, MessagePeerReaction, MessagesAvailableReactions, Reaction, ReactionCount, Update, Updates} from '../../layer';
import {ReferenceContext} from '../mtproto/referenceDatabase'; import {ReferenceContext} from '../mtproto/referenceDatabase';
import {AppManager} from './manager'; import {AppManager} from './manager';
import getServerMessageId from './utils/messageId/getServerMessageId'; import getServerMessageId from './utils/messageId/getServerMessageId';
import reactionsEqual from './utils/reactions/reactionsEqual';
const SAVE_DOC_KEYS = [ const SAVE_DOC_KEYS = [
'static_icon' as const, 'static_icon' as const,
@ -98,18 +100,25 @@ export class AppReactionsManager extends AppManager {
const chatFull = this.appProfileManager.getChatFull(peerId.toChatId()); const chatFull = this.appProfileManager.getChatFull(peerId.toChatId());
return callbackifyAll([activeAvailableReactions, chatFull, this.getQuickReaction()], ([activeAvailableReactions, chatFull, quickReaction]) => { return callbackifyAll([activeAvailableReactions, chatFull, this.getQuickReaction()], ([activeAvailableReactions, chatFull, quickReaction]) => {
const chatAvailableReactions = chatFull.available_reactions ?? []; const chatAvailableReactions = chatFull.available_reactions ?? {_: 'chatReactionsNone'};
const filteredChatAvailableReactions = chatAvailableReactions.map((reaction) => { let filteredChatAvailableReactions: AvailableReaction[] = [];
return activeAvailableReactions.find((availableReaction) => availableReaction.reaction === reaction); if(chatAvailableReactions._ === 'chatReactionsAll') {
}).filter(Boolean); filteredChatAvailableReactions = activeAvailableReactions;
} else if(chatAvailableReactions._ === 'chatReactionsSome') {
filteredChatAvailableReactions = chatAvailableReactions.reactions.map((reaction) => {
return activeAvailableReactions.find((availableReaction) => availableReaction.reaction === (reaction as Reaction.reactionEmoji).emoticon);
}).filter(Boolean);
}
return this.unshiftQuickReactionInner(filteredChatAvailableReactions, quickReaction); return this.unshiftQuickReactionInner(filteredChatAvailableReactions, quickReaction);
}); });
} }
private unshiftQuickReactionInner(availableReactions: AvailableReaction.availableReaction[], quickReaction: AvailableReaction.availableReaction) { private unshiftQuickReactionInner(availableReactions: AvailableReaction[], quickReaction: Reaction | AvailableReaction) {
const availableReaction = findAndSplice(availableReactions, availableReaction => availableReaction.reaction === quickReaction.reaction); if(quickReaction._ !== 'reactionEmoji' && quickReaction._ !== 'availableReaction') return availableReactions;
const emoticon = (quickReaction as Reaction.reactionEmoji).emoticon || (quickReaction as AvailableReaction).reaction;
const availableReaction = findAndSplice(availableReactions, (availableReaction) => availableReaction.reaction === emoticon);
if(availableReaction) { if(availableReaction) {
availableReactions.unshift(availableReaction); availableReactions.unshift(availableReaction);
} }
@ -118,7 +127,7 @@ export class AppReactionsManager extends AppManager {
} }
private unshiftQuickReaction( private unshiftQuickReaction(
availableReactions: AvailableReaction.availableReaction[] | PromiseLike<AvailableReaction.availableReaction[]>, availableReactions: AvailableReaction[] | PromiseLike<AvailableReaction.availableReaction[]>,
quickReaction: ReturnType<AppReactionsManager['getQuickReaction']> = this.getQuickReaction() quickReaction: ReturnType<AppReactionsManager['getQuickReaction']> = this.getQuickReaction()
) { ) {
return callbackifyAll([ return callbackifyAll([
@ -142,10 +151,15 @@ export class AppReactionsManager extends AppManager {
public getQuickReaction() { public getQuickReaction() {
return callbackifyAll([ return callbackifyAll([
this.apiManager.getAppConfig(), this.apiManager.getConfig(),
this.getAvailableReactions() this.getAvailableReactions()
], ([appConfig, availableReactions]) => { ], ([config, availableReactions]) => {
return availableReactions.find((reaction) => reaction.reaction === appConfig.reactions_default); const reaction = config.reactions_default;
if(reaction._ === 'reactionEmoji') {
return availableReactions.find((availableReaction) => availableReaction.reaction === reaction.emoticon);
}
return reaction as Reaction.reactionCustomEmoji;
}); });
} }
@ -175,7 +189,7 @@ export class AppReactionsManager extends AppManager {
}); });
} }
public getMessageReactionsList(peerId: PeerId, mid: number, limit: number, reaction?: string, offset?: string) { public getMessageReactionsList(peerId: PeerId, mid: number, limit: number, reaction?: Reaction, offset?: string) {
return this.apiManager.invokeApiSingleProcess({ return this.apiManager.invokeApiSingleProcess({
method: 'messages.getMessageReactionsList', method: 'messages.getMessageReactionsList',
params: { params: {
@ -192,10 +206,10 @@ export class AppReactionsManager extends AppManager {
}); });
} }
public setDefaultReaction(reaction: string) { public setDefaultReaction(reaction: Reaction) {
return this.apiManager.invokeApi('messages.setDefaultReaction', {reaction}).then(async(value) => { return this.apiManager.invokeApi('messages.setDefaultReaction', {reaction}).then(async(value) => {
if(value) { if(value) {
const appConfig = await this.apiManager.getAppConfig(); const appConfig = await this.apiManager.getConfig();
if(appConfig) { if(appConfig) {
appConfig.reactions_default = reaction; appConfig.reactions_default = reaction;
}/* else { // if no config or loading it - overwrite }/* else { // if no config or loading it - overwrite
@ -209,7 +223,17 @@ export class AppReactionsManager extends AppManager {
}); });
} }
public sendReaction(message: Message.message, reaction?: string, onlyLocal?: boolean) { public async sendReaction(message: Message.message, reaction?: Reaction | AvailableReaction, onlyLocal?: boolean) {
if(reaction._ === 'availableReaction') {
reaction = {
_: 'reactionEmoji',
emoticon: reaction.reaction
};
}
const appConfig = await this.apiManager.getAppConfig();
const limit = this.rootScope.premium ? appConfig.reactions_user_max_premium : appConfig.reactions_user_max_default;
const lastSendingTimeKey = message.peerId + '_' + message.mid; const lastSendingTimeKey = message.peerId + '_' + message.mid;
const lastSendingTime = this.lastSendingTimes.get(lastSendingTimeKey); const lastSendingTime = this.lastSendingTimes.get(lastSendingTimeKey);
if(lastSendingTime) { if(lastSendingTime) {
@ -224,32 +248,63 @@ export class AppReactionsManager extends AppManager {
const {peerId, mid} = message; const {peerId, mid} = message;
const myPeerId = this.appPeersManager.peerId; const myPeerId = this.appPeersManager.peerId;
let reactions = onlyLocal ? message.reactions : copy(message.reactions); const unsetReactionCount = (reactionCount: ReactionCount) => {
const chosenReactionIdx = reactions ? reactions.results.findIndex((reactionCount) => reactionCount.pFlags.chosen) : -1; --reactionCount.count;
const chosenReaction = chosenReactionIdx !== -1 && reactions.results[chosenReactionIdx]; delete reactionCount.chosen_order;
if(chosenReaction) { // clear current reaction
--chosenReaction.count; if(reactionsEqual(reaction as Reaction, reactionCount.reaction)) {
delete chosenReaction.pFlags.chosen; reaction = undefined as Reaction;
if(reaction === chosenReaction.reaction) {
reaction = undefined;
} }
if(!chosenReaction.count) { if(!reactionCount.count) {
reactions.results.splice(chosenReactionIdx, 1); indexOfAndSplice(reactions.results, reactionCount);
}/* else { }/* else {
insertInDescendSortedArray(reactions.results, chosenReaction, 'count', chosenReactionIdx); insertInDescendSortedArray(reactions.results, chosenReaction, 'count', chosenReactionIdx);
} */ } */
if(reactions.recent_reactions) { if(reactions.recent_reactions) {
findAndSplice(reactions.recent_reactions, (recentReaction) => this.appPeersManager.getPeerId(recentReaction.peer_id) === myPeerId); findAndSplice(reactions.recent_reactions, (recentReaction) => reactionsEqual(recentReaction.reaction, reactionCount.reaction) && this.appPeersManager.getPeerId(recentReaction.peer_id) === myPeerId);
} }
if(!reactions.results.length) { if(!reactions.results.length) {
reactions = undefined; reactions = undefined;
} }
};
const canSeeList = message.reactions?.pFlags?.can_see_list || !this.appPeersManager.isBroadcast(message.peerId) || message.peerId.isUser();
if(!message.reactions) {
message.reactions = {
_: 'messageReactions',
results: [],
recent_reactions: canSeeList ? [] : undefined,
pFlags: {
can_see_list: canSeeList || undefined
}
};
} }
let reactions = onlyLocal ? message.reactions : copy(message.reactions);
const chosenReactions = reactions.results.filter((reactionCount) => reactionCount.chosen_order !== undefined);
chosenReactions.sort((a, b) => b.chosen_order - a.chosen_order);
const unsetReactions: ReactionCount[] = [];
const chosenReactionIdx = chosenReactions.findIndex((reactionCount) => reactionsEqual(reactionCount.reaction, reaction as Reaction));
if(chosenReactionIdx !== -1) unsetReactions.push(...chosenReactions.splice(chosenReactionIdx, 1));
unsetReactions.push(...chosenReactions.splice(limit - +(chosenReactionIdx === -1)));
unsetReactions.forEach((reactionCount) => {
chosenReactions.forEach((chosenReactionCount) => {
if(chosenReactionCount.chosen_order > reactionCount.chosen_order) {
--chosenReactionCount.chosen_order;
}
});
unsetReactionCount(reactionCount);
});
const chosenReactionsLength = chosenReactions.length;
chosenReactions.forEach((reactionCount, idx) => {
reactionCount.chosen_order = chosenReactionsLength - 1 - idx;
});
if(reaction) { if(reaction) {
if(!reactions) { if(!reactions) {
reactions/* = message.reactions */ = { reactions/* = message.reactions */ = {
@ -258,43 +313,43 @@ export class AppReactionsManager extends AppManager {
pFlags: {} pFlags: {}
}; };
if(!this.appPeersManager.isBroadcast(message.peerId)) { if(canSeeList) {
reactions.pFlags.can_see_list = true; reactions.pFlags.can_see_list = true;
} }
} }
let reactionCountIdx = reactions.results.findIndex((reactionCount) => reactionCount.reaction === reaction); let reactionCountIdx = reactions.results.findIndex((reactionCount) => reactionsEqual(reactionCount.reaction, reaction as Reaction));
let reactionCount = reactionCountIdx !== -1 && reactions.results[reactionCountIdx]; let reactionCount = reactionCountIdx !== -1 && reactions.results[reactionCountIdx];
if(!reactionCount) { if(!reactionCount) {
reactionCount = { reactionCount = {
_: 'reactionCount', _: 'reactionCount',
count: 0, count: 0,
reaction, reaction
pFlags: {}
}; };
reactionCountIdx = reactions.results.push(reactionCount) - 1; reactionCountIdx = reactions.results.push(reactionCount) - 1;
} }
++reactionCount.count; ++reactionCount.count;
reactionCount.pFlags.chosen = true; reactionCount.chosen_order = chosenReactions.length ? chosenReactions[0].chosen_order + 1 : 0;
chosenReactions.unshift(reactionCount);
if(!reactions.recent_reactions && reactions.pFlags.can_see_list) { if(!reactions.recent_reactions && canSeeList) {
reactions.recent_reactions = []; reactions.recent_reactions = [];
} }
if(reactions.recent_reactions) { if(reactions.recent_reactions) {
const userReaction: MessagePeerReaction = { const peerReaction: MessagePeerReaction = {
_: 'messagePeerReaction', _: 'messagePeerReaction',
reaction, reaction,
peer_id: this.appPeersManager.getOutputPeer(myPeerId) peer_id: this.appPeersManager.getOutputPeer(myPeerId)
}; };
if(!this.appPeersManager.isMegagroup(peerId)) { if(!this.appPeersManager.isMegagroup(peerId) && false) {
reactions.recent_reactions.push(userReaction); reactions.recent_reactions.push(peerReaction);
reactions.recent_reactions = reactions.recent_reactions.slice(-3); reactions.recent_reactions = reactions.recent_reactions.slice(-3);
} else { } else {
reactions.recent_reactions.unshift(userReaction); reactions.recent_reactions.unshift(peerReaction);
reactions.recent_reactions = reactions.recent_reactions.slice(0, 3); reactions.recent_reactions = reactions.recent_reactions.slice(0, 3);
} }
} }
@ -304,13 +359,15 @@ export class AppReactionsManager extends AppManager {
const availableReactions = this.availableReactions; const availableReactions = this.availableReactions;
if(reactions && availableReactions?.length) { if(reactions && availableReactions?.length) {
const indexes: Map<string, number> = new Map(); const indexes: Map<DocId | string, number> = new Map();
availableReactions.forEach((availableReaction, idx) => { availableReactions.forEach((availableReaction, idx) => {
indexes.set(availableReaction.reaction, idx); indexes.set(availableReaction.reaction, idx);
}); });
reactions.results.sort((a, b) => { reactions.results.sort((a, b) => {
return (b.count - a.count) || (indexes.get(a.reaction) - indexes.get(b.reaction)); const id1 = (a.reaction as Reaction.reactionCustomEmoji).document_id || (a.reaction as Reaction.reactionEmoji).emoticon;
const id2 = (b.reaction as Reaction.reactionCustomEmoji).document_id || (b.reaction as Reaction.reactionEmoji).emoticon;
return (b.count - a.count) || ((indexes.get(id1) ?? 0) - (indexes.get(id2) ?? 0));
}); });
} }
@ -333,7 +390,7 @@ export class AppReactionsManager extends AppManager {
const promise = this.apiManager.invokeApi('messages.sendReaction', { const promise = this.apiManager.invokeApi('messages.sendReaction', {
peer: this.appPeersManager.getInputPeerById(peerId), peer: this.appPeersManager.getInputPeerById(peerId),
msg_id: msgId, msg_id: msgId,
reaction reaction: chosenReactions.map((reactionCount) => reactionCount.reaction)
}).then((updates) => { }).then((updates) => {
assumeType<Updates.updates>(updates); assumeType<Updates.updates>(updates);
@ -353,7 +410,7 @@ export class AppReactionsManager extends AppManager {
this.apiUpdatesManager.processUpdateMessage(updates); this.apiUpdatesManager.processUpdateMessage(updates);
}).catch((err) => { }).catch((err) => {
if(err.type === 'REACTION_INVALID' && this.sendReactionPromises.get(promiseKey) === promise) { if(err.type === 'REACTION_INVALID' && this.sendReactionPromises.get(promiseKey) === promise) {
this.sendReaction(message, chosenReaction?.reaction, true); this.sendReaction(message, chosenReactions[0]?.reaction, true);
} }
}).finally(() => { }).finally(() => {
if(this.sendReactionPromises.get(promiseKey) === promise) { if(this.sendReactionPromises.get(promiseKey) === promise) {

36
src/lib/appManagers/appStickersManager.ts

@ -392,7 +392,8 @@ export class AppStickersManager extends AppManager {
_: 'messages.stickerSet', _: 'messages.stickerSet',
set: res.set, set: res.set,
packs: res.packs, packs: res.packs,
documents: res.documents as Document[] documents: res.documents as Document[],
keywords: res.keywords
}; };
let stickerSet = this.storage.getFromCache(id); let stickerSet = this.storage.getFromCache(id);
@ -484,7 +485,7 @@ export class AppStickersManager extends AppManager {
}); });
res.sets.forEach((covered) => { res.sets.forEach((covered) => {
this.saveStickerSet({set: covered.set, documents: [], packs: []}, covered.set.id); this.saveStickerSet({set: covered.set, documents: [], packs: [], keywords: []}, covered.set.id);
}); });
return res; return res;
@ -605,7 +606,7 @@ export class AppStickersManager extends AppManager {
}); });
res.sets.forEach((covered) => { res.sets.forEach((covered) => {
this.saveStickerSet({set: covered.set, documents: [], packs: []}, covered.set.id); this.saveStickerSet({set: covered.set, documents: [], packs: [], keywords: []}, covered.set.id);
}); });
return res; return res;
@ -625,20 +626,29 @@ export class AppStickersManager extends AppManager {
return res.sets.concat(foundSaved); return res.sets.concat(foundSaved);
} }
private processAllStickersResult = (allStickers: MessagesAllStickers) => {
assumeType<MessagesAllStickers.messagesAllStickers>(allStickers);
forEachReverse(allStickers.sets, (stickerSet, idx, arr) => {
if(stickerSet.pFlags.videos && !getEnvironment().IS_WEBM_SUPPORTED) {
arr.splice(idx, 1);
}
});
return allStickers;
};
public getAllStickers() { public getAllStickers() {
return this.apiManager.invokeApiHashable({ return this.apiManager.invokeApiHashable({
method: 'messages.getAllStickers', method: 'messages.getAllStickers',
processResult: (allStickers) => { processResult: this.processAllStickersResult
assumeType<MessagesAllStickers.messagesAllStickers>(allStickers); });
}
forEachReverse(allStickers.sets, (stickerSet, idx, arr) => {
if(stickerSet.pFlags.videos && !getEnvironment().IS_WEBM_SUPPORTED) {
arr.splice(idx, 1);
}
});
return allStickers; public getEmojiStickers() {
} return this.apiManager.invokeApiHashable({
method: 'messages.getEmojiStickers',
processResult: this.processAllStickersResult
}); });
} }

5
src/lib/appManagers/uiNotificationsManager.ts

@ -232,10 +232,11 @@ export class UiNotificationsManager {
} else { } else {
notificationMessage = await wrapMessageForReply(message, undefined, undefined, true); notificationMessage = await wrapMessageForReply(message, undefined, undefined, true);
if(peerReaction) { const reaction = peerReaction?.reaction;
if(reaction?._ === 'reactionEmoji') {
const langPackKey: LangPackKey = /* isAnyChat ? 'Notification.Group.Reacted' : */'Notification.Contact.Reacted'; const langPackKey: LangPackKey = /* isAnyChat ? 'Notification.Group.Reacted' : */'Notification.Contact.Reacted';
const args: FormatterArguments = [ const args: FormatterArguments = [
fixEmoji(peerReaction.reaction), // can be plain heart fixEmoji(reaction.emoticon), // can be plain heart
notificationMessage notificationMessage
]; ];

6
src/lib/appManagers/utils/reactions/reactionsEqual.ts

@ -0,0 +1,6 @@
import deepEqual from '../../../../helpers/object/deepEqual';
import {Reaction} from '../../../../layer';
export default function reactionsEqual(r1: Reaction, r2: Reaction) {
return deepEqual(r1, r2);
}

4
src/lib/mtproto/appConfig.d.ts vendored

@ -12,6 +12,9 @@ export interface MTAppConfig {
youtube_pip?: string; youtube_pip?: string;
qr_login_camera?: boolean; qr_login_camera?: boolean;
qr_login_code?: string; qr_login_code?: string;
reactions_in_chat_max?: number;
reactions_user_max_default?: number;
reactions_user_max_premium?: number;
dialog_filters_enabled?: boolean; dialog_filters_enabled?: boolean;
dialog_filters_tooltip?: boolean; dialog_filters_tooltip?: boolean;
ignore_restriction_reasons?: string[]; ignore_restriction_reasons?: string[];
@ -23,7 +26,6 @@ export interface MTAppConfig {
round_video_encoding?: RoundVideoEncoding; round_video_encoding?: RoundVideoEncoding;
chat_read_mark_expire_period?: number; chat_read_mark_expire_period?: number;
chat_read_mark_size_threshold?: number; chat_read_mark_size_threshold?: number;
reactions_default?: string;
reactions_uniq_max?: number; reactions_uniq_max?: number;
ringtone_duration_max?: number; ringtone_duration_max?: number;
ringtone_size_max?: number; ringtone_size_max?: number;

2
src/lib/mtproto/schema.ts

File diff suppressed because one or more lines are too long

14
src/lib/richTextProcessor/wrapRichText.ts

@ -22,7 +22,7 @@ import {CLICK_EVENT_NAME} from '../../helpers/dom/clickEvent';
import IS_CUSTOM_EMOJI_SUPPORTED from '../../environment/customEmojiSupport'; import IS_CUSTOM_EMOJI_SUPPORTED from '../../environment/customEmojiSupport';
import rootScope from '../rootScope'; import rootScope from '../rootScope';
import mediaSizes from '../../helpers/mediaSizes'; import mediaSizes from '../../helpers/mediaSizes';
import {wrapSticker} from '../../components/wrappers'; import wrapSticker from '../../components/wrappers/sticker';
import RLottiePlayer from '../rlottie/rlottiePlayer'; import RLottiePlayer from '../rlottie/rlottiePlayer';
import animationIntersector, {AnimationItemGroup} from '../../components/animationIntersector'; import animationIntersector, {AnimationItemGroup} from '../../components/animationIntersector';
import type {MyDocument} from '../appManagers/appDocsManager'; import type {MyDocument} from '../appManagers/appDocsManager';
@ -65,6 +65,10 @@ class CustomEmojiElement extends HTMLElement {
} }
public disconnectedCallback() { public disconnectedCallback() {
if(this.isConnected) { // prepend on sibling can invoke disconnectedCallback
return;
}
if(this.syncedPlayer) { if(this.syncedPlayer) {
this.syncedPlayer.pausedElements.delete(this); this.syncedPlayer.pausedElements.delete(this);
} }
@ -169,6 +173,10 @@ export class CustomEmojiRendererElement extends HTMLElement {
} }
public disconnectedCallback() { public disconnectedCallback() {
if(this.isConnected) {
return;
}
for(const [syncedPlayer, elements] of this.syncedElements) { for(const [syncedPlayer, elements] of this.syncedElements) {
if(syncedPlayers.get(syncedPlayer.key) !== syncedPlayer) { if(syncedPlayers.get(syncedPlayer.key) !== syncedPlayer) {
continue; continue;
@ -218,6 +226,10 @@ export class CustomEmojiRendererElement extends HTMLElement {
} }
const overflowElement = findUpClassName(this, 'scrollable') || this.offsetParent as HTMLElement; const overflowElement = findUpClassName(this, 'scrollable') || this.offsetParent as HTMLElement;
if(!overflowElement) {
return offsetsMap;
}
const overflowRect = overflowElement.getBoundingClientRect(); const overflowRect = overflowElement.getBoundingClientRect();
const rect = this.getBoundingClientRect(); const rect = this.getBoundingClientRect();

4
src/lib/rootScope.ts

@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE * https://github.com/morethanwords/tweb/blob/master/LICENSE
*/ */
import type {Message, StickerSet, Update, NotifyPeer, PeerNotifySettings, PollResults, Poll, WebPage, GroupCall, GroupCallParticipant, ReactionCount, MessagePeerReaction, PhoneCall, Config} from '../layer'; import type {Message, StickerSet, Update, NotifyPeer, PeerNotifySettings, PollResults, Poll, WebPage, GroupCall, GroupCallParticipant, ReactionCount, MessagePeerReaction, PhoneCall, Config, Reaction} from '../layer';
import type {AppMessagesManager, Dialog, MessagesStorageKey, MyMessage} from './appManagers/appMessagesManager'; import type {AppMessagesManager, Dialog, MessagesStorageKey, MyMessage} from './appManagers/appMessagesManager';
import type {MyDialogFilter} from './storages/filters'; import type {MyDialogFilter} from './storages/filters';
import type {Folder} from './storages/dialogs'; import type {Folder} from './storages/dialogs';
@ -137,7 +137,7 @@ export type BroadcastEvents = {
'call_update': PhoneCall, 'call_update': PhoneCall,
'call_signaling': {callId: CallId, data: Uint8Array}, 'call_signaling': {callId: CallId, data: Uint8Array},
'quick_reaction': string, 'quick_reaction': Reaction,
'service_notification': Update.updateServiceNotification, 'service_notification': Update.updateServiceNotification,

4
src/scripts/in/schema.json

File diff suppressed because one or more lines are too long

2
src/scripts/out/schema.json

File diff suppressed because one or more lines are too long

24
src/scss/partials/_avatar.scss

@ -89,18 +89,18 @@ avatar-element {
position: relative; position: relative;
} }
&.is-premium { // &.is-premium {
&:before { // &:before {
font-size: .6875rem; // font-size: .6875rem;
color: #fff; // color: #fff;
position: absolute; // position: absolute;
right: 0; // right: 0;
bottom: -.0625rem; // bottom: -.0625rem;
line-height: 1 !important; // line-height: 1 !important;
z-index: 1; // z-index: 1;
pointer-events: none; // pointer-events: none;
} // }
} // }
img { img {
//width: 100% !important; //width: 100% !important;

5
src/scss/partials/_chatBubble.scss

@ -1790,6 +1790,11 @@ $bubble-beside-button-width: 38px;
&.with-beside-replies .bubble-content { &.with-beside-replies .bubble-content {
min-height: 5.5rem; min-height: 5.5rem;
} }
.colored-name .premium-icon {
opacity: .6;
color: inherit;
}
.time { .time {
visibility: hidden; // * can't use color transparent here, because in name can be emoji visibility: hidden; // * can't use color transparent here, because in name can be emoji

10
src/scss/partials/_customEmoji.scss

@ -6,10 +6,10 @@
.custom-emoji { .custom-emoji {
display: inline; display: inline;
width: var(--custom-emoji-size); width: var(--custom-emoji-size) !important;
height: var(--custom-emoji-size); height: var(--custom-emoji-size) !important;
min-height: var(--custom-emoji-size); min-width: var(--custom-emoji-size) !important;
min-width: var(--custom-emoji-size); min-height: var(--custom-emoji-size) !important;
position: relative; position: relative;
// pointer-events: none; // pointer-events: none;
@ -27,6 +27,8 @@
margin: 0; margin: 0;
width: inherit !important; width: inherit !important;
height: inherit !important; height: inherit !important;
max-width: inherit !important;
max-height: inherit !important;
} }
&-canvas { &-canvas {

67
src/scss/partials/_emojiDropdown.scss

@ -48,10 +48,37 @@
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
.menu-wrapper {
padding: 0;
height: var(--menu-height);
max-width: 100%;
position: relative;
border-bottom: 1px solid var(--border-color);
background-color: var(--surface-color);
}
.menu-horizontal-div { .menu-horizontal-div {
z-index: 4; z-index: 4;
background-color: var(--surface-color); background-color: var(--surface-color);
}
&-item {
flex: 0 0 auto;
padding: .25rem;
margin: 0 .3125rem;
&-padding {
width: 100%;
height: 100%;
position: relative;
}
&.active {
&:not(.tgico) {
background-color: var(--light-secondary-text-color);
}
}
}
}
} }
.emoji-tabs { .emoji-tabs {
@ -126,13 +153,6 @@
.super-emojis { .super-emojis {
padding: 0 .5rem; padding: 0 .5rem;
} }
@include respond-to(handhelds) {
.menu-horizontal-div-item {
flex: unset;
padding: 0;
}
}
} }
.emoji-category { .emoji-category {
@ -165,34 +185,5 @@
.category-items { .category-items {
padding: 0 .3125rem; padding: 0 .3125rem;
} }
.menu-wrapper {
padding: 0;
height: var(--menu-height);
max-width: 100%;
position: relative;
border-bottom: 1px solid var(--border-color);
background-color: var(--surface-color);
}
.menu-horizontal-div {
&-item {
flex: 0 0 auto;
padding: .25rem;
margin: 0 .3125rem;
&-padding {
width: 100%;
height: 100%;
position: relative;
}
&.active {
&:not(.tgico-recent):not(.tgico-saved) {
background-color: var(--light-secondary-text-color);
}
}
}
}
} }
} }

16
src/scss/partials/_leftSidebar.scss

@ -880,6 +880,14 @@
&.full-width { &.full-width {
margin: 0 !important; margin: 0 !important;
} }
> .media-sticker-wrapper {
width: 86px;
height: 86px;
margin: 1px auto 29px;
flex: 0 0 auto;
position: relative;
}
} }
&-name { &-name {
@ -925,14 +933,6 @@
opacity: .25; opacity: .25;
} }
.media-sticker-wrapper {
width: 86px;
height: 86px;
margin: 1px auto 29px;
flex: 0 0 auto;
position: relative;
}
.chatlist { .chatlist {
padding: 0; padding: 0;
} }

16
src/scss/partials/_reaction.scss

@ -25,6 +25,7 @@
align-items: center; align-items: center;
&-sticker { &-sticker {
--custom-emoji-size: var(--reaction-size);
position: relative; position: relative;
width: var(--reaction-size); width: var(--reaction-size);
height: var(--reaction-size); height: var(--reaction-size);
@ -48,7 +49,7 @@
} }
} }
&:not(.is-static) { &:not(.is-static):not(.is-custom) {
.media-sticker { .media-sticker {
--size: calc(var(--reaction-size) + var(--reaction-offset) * -2); --size: calc(var(--reaction-size) + var(--reaction-offset) * -2);
width: var(--size) !important; width: var(--size) !important;
@ -61,6 +62,19 @@
left: auto; left: auto;
} }
} }
&.is-custom {
border-radius: .3125rem;
.custom-emoji,
.media-sticker {
border-radius: inherit;
}
.custom-emoji-canvas {
z-index: auto;
}
}
} }
&-inline { &-inline {

4
src/scss/partials/_row.scss

@ -105,11 +105,11 @@ $row-border-radius: $border-radius-medium;
} }
.radio-field, .radio-field,
.radio-field-main, .radio-field:not(.radio-field-right) .radio-field-main,
.checkbox-field { .checkbox-field {
position: unset; position: unset;
} }
.radio-field, .radio-field,
.checkbox-field { .checkbox-field {
--offset-left: 1rem; --offset-left: 1rem;

11
src/scss/partials/popups/_reactedList.scss

@ -5,6 +5,8 @@
*/ */
.popup-reacted-list { .popup-reacted-list {
--size: 1.5rem;
--custom-emoji-size: var(--size);
$parent: ".popup"; $parent: ".popup";
#{$parent} { #{$parent} {
@ -31,8 +33,8 @@
} }
.reaction { .reaction {
--reaction-size: var(--size);
--additional-height: .75rem; --additional-height: .75rem;
--reaction-size: 1.5rem;
--margin: .5rem; --margin: .5rem;
// --background-color: var(--secondary-color); // --background-color: var(--secondary-color);
--background-color: var(--light-filled-primary-color); --background-color: var(--light-filled-primary-color);
@ -73,10 +75,11 @@
} }
.reacted-list-reaction-icon { .reacted-list-reaction-icon {
width: 1.5rem; width: var(--size);
height: 1.5rem; height: var(--size);
margin: 0;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
position: relative;
display: flex;
} }
} }

Loading…
Cancel
Save