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. 19
      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. 54
      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. 5
      src/components/emoticonsDropdown/index.ts
  15. 117
      src/components/emoticonsDropdown/tabs/emoji.ts
  16. 213
      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. 42
      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. 166
      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. 28
      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. 137
      src/lib/appManagers/appReactionsManager.ts
  51. 24
      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. 63
      src/scss/partials/_emojiDropdown.scss
  64. 16
      src/scss/partials/_leftSidebar.scss
  65. 16
      src/scss/partials/_reaction.scss
  66. 2
      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'; @@ -14,7 +14,6 @@ import LazyLoadQueue from './lazyLoadQueue';
import {putPreloader} from './putPreloader';
import ripple from './ripple';
import Scrollable, {ScrollableX} from './scrollable';
import {wrapDocument, wrapPhoto, wrapVideo} from './wrappers';
import useHeavyAnimationCheck, {getHeavyAnimationPromise} from '../hooks/useHeavyAnimationCheck';
import I18n, {LangPackKey, i18n} from '../lib/langPack';
import findUpClassName from '../helpers/dom/findUpClassName';
@ -71,6 +70,9 @@ import positionMenu from '../helpers/positionMenu'; @@ -71,6 +70,9 @@ import positionMenu from '../helpers/positionMenu';
import apiManagerProxy from '../lib/mtproto/mtprotoworker';
import ListenerSetter from '../helpers/listenerSetter';
import SwipeHandler from './swipeHandler';
import wrapDocument from './wrappers/document';
import wrapPhoto from './wrappers/photo';
import wrapVideo from './wrappers/video';
// const testScroll = false;

2
src/components/audio.ts

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

19
src/components/chat/bubbleGroups.ts

@ -77,16 +77,17 @@ class BubbleGroup { @@ -77,16 +77,17 @@ class BubbleGroup {
peerTitle: !fwdFromId && fwdFrom && fwdFrom.from_name ? /* '🔥 FF 🔥' */fwdFrom.from_name : undefined
});
this.avatarLoadPromise = Promise.all([
avatarLoadPromise,
peerId && peerId.isUser() ? this.chat.managers.appUsersManager.getUser(peerId.toUserId()) : undefined
]).then(([result, user]) => {
if(user?.pFlags?.premium) {
avatar.classList.add('is-premium', 'tgico-star');
}
// this.avatarLoadPromise = Promise.all([
// avatarLoadPromise,
// peerId && peerId.isUser() ? this.chat.managers.appUsersManager.getUser(peerId.toUserId()) : undefined
// ]).then(([result, user]) => {
// if(user?.pFlags?.premium) {
// avatar.classList.add('is-premium', 'tgico-star');
// }
return result;
});
// return result;
// });
this.avatarLoadPromise = avatarLoadPromise;
this.avatarContainer.append(this.avatar);
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 @@ -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 AvatarElement from '../avatar';
import ripple from '../ripple';
import {wrapAlbum, wrapPhoto, wrapVideo, wrapDocument, wrapSticker, wrapPoll, wrapGroupedDocuments, wrapStickerAnimation} from '../wrappers';
import {MessageRender} from './messageRender';
import LazyLoadQueue from '../lazyLoadQueue';
import ListenerSetter from '../../helpers/listenerSetter';
@ -114,12 +113,15 @@ import isInDOM from '../../helpers/dom/isInDOM'; @@ -114,12 +113,15 @@ import isInDOM from '../../helpers/dom/isInDOM';
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
import attachStickerViewerListeners from '../stickerViewer';
import {makeMediaSize, MediaSize} from '../../helpers/mediaSize';
import lottieLoader from '../../lib/rlottie/lottieLoader';
import appDownloadManager from '../../lib/appManagers/appDownloadManager';
import onMediaLoad from '../../helpers/onMediaLoad';
import throttle from '../../helpers/schedulers/throttle';
import {onEmojiStickerClick} from '../wrappers/sticker';
import wrapSticker, {onEmojiStickerClick} from '../wrappers/sticker';
import wrapAlbum from '../wrappers/album';
import wrapDocument from '../wrappers/document';
import wrapGroupedDocuments from '../wrappers/groupedDocuments';
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 IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
'messageActionHistoryClear',
@ -151,6 +153,17 @@ const DO_NOT_UPDATE_MESSAGE_VIEWS = false; @@ -151,6 +153,17 @@ const DO_NOT_UPDATE_MESSAGE_VIEWS = false;
const DO_NOT_UPDATE_MESSAGE_REACTIONS = 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 = {
bubble: HTMLElement,
mids: Set<number>,
@ -600,20 +613,20 @@ export default class ChatBubbles { @@ -600,20 +613,20 @@ export default class ChatBubbles {
this.safeRenderMessage(message, true, bubble);
});
this.listenerSetter.add(rootScope)('peer_title_edit', async(peerId) => {
if(peerId.isUser()) {
const middleware = this.getMiddleware();
const user = await this.managers.appUsersManager.getUser(peerId.toUserId());
if(!middleware()) return;
const isPremium = user?.pFlags?.premium;
const groups = this.bubbleGroups.groups.filter((group) => group.avatar?.peerId === peerId);
groups.forEach((group) => {
group.avatar.classList.toggle('is-premium', isPremium);
group.avatar.classList.toggle('tgico-star', isPremium);
});
}
});
// this.listenerSetter.add(rootScope)('peer_title_edit', async(peerId) => {
// if(peerId.isUser()) {
// const middleware = this.getMiddleware();
// const user = await this.managers.appUsersManager.getUser(peerId.toUserId());
// if(!middleware()) return;
// const isPremium = user?.pFlags?.premium;
// const groups = this.bubbleGroups.groups.filter((group) => group.avatar?.peerId === peerId);
// groups.forEach((group) => {
// group.avatar.classList.toggle('is-premium', isPremium);
// group.avatar.classList.toggle('tgico-star', isPremium);
// });
// }
// });
if(this.chat.type !== 'scheduled' && !DO_NOT_UPDATE_MESSAGE_REACTIONS/* && false */) {
this.listenerSetter.add(rootScope)('messages_reactions', async(arr) => {
@ -1268,7 +1281,7 @@ export default class ChatBubbles { @@ -1268,7 +1281,7 @@ export default class ChatBubbles {
attachClickEvent(hoverReaction, (e) => {
cancelEvent(e); // cancel triggering selection
this.managers.appReactionsManager.sendReaction(message, availableReaction.reaction);
this.managers.appReactionsManager.sendReaction(message, availableReaction);
this.unhoverPrevious();
}, {listenerSetter: this.listenerSetter});
}, noop);
@ -1557,7 +1570,7 @@ export default class ChatBubbles { @@ -1557,7 +1570,7 @@ export default class ChatBubbles {
}
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({
event: e,
container: stickerEmojiEl,
@ -3576,17 +3589,11 @@ export default class ChatBubbles { @@ -3576,17 +3589,11 @@ export default class ChatBubbles {
const emojiStrLength = emojiEntities.reduce((acc, curr) => acc + curr.length, 0);
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;
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) {
customEmojiSize = makeMediaSize(size, size);
bubble.style.setProperty('--emoji-size', size + 'px');
@ -4408,7 +4415,7 @@ export default class ChatBubbles { @@ -4408,7 +4415,7 @@ export default class ChatBubbles {
// title = fwdFrom.from_name;
bubble.classList.add('hidden-profile');
} 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) {
@ -4438,6 +4445,7 @@ export default class ChatBubbles { @@ -4438,6 +4445,7 @@ export default class ChatBubbles {
if((this.peerId === rootScope.myId || this.peerId === REPLIES_PEER_ID || isForwardFromChannel) && !isStandaloneMedia) {
nameDiv.style.color = getPeerColorById(fwdFromId, false);
nameDiv.classList.add('colored-name');
nameDiv.append(title);
} 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>`;
@ -4461,6 +4469,7 @@ export default class ChatBubbles { @@ -4461,6 +4469,7 @@ export default class ChatBubbles {
if(!our) {
nameDiv.style.color = getPeerColorById(message.fromId, false);
nameDiv.classList.add('colored-name');
}
nameDiv.dataset.peerId = '' + message.fromId;
@ -4546,8 +4555,13 @@ export default class ChatBubbles { @@ -4546,8 +4555,13 @@ export default class ChatBubbles {
return ret;
}
private appendReactionsElementToBubble(bubble: HTMLElement, message: Message.message, reactionsMessage: Message.message, changedResults?: ReactionCount[]) {
if(this.peerId.isUser()/* || true */) {
private appendReactionsElementToBubble(
bubble: HTMLElement,
message: Message.message,
reactionsMessage: Message.message,
changedResults?: ReactionCount[]
) {
if(this.peerId.isUser() && USER_REACTIONS_INLINE/* || true */) {
return;
}
@ -4558,7 +4572,7 @@ export default class ChatBubbles { @@ -4558,7 +4572,7 @@ export default class ChatBubbles {
// message = this.appMessagesManager.getMessageWithReactions(message);
const reactionsElement = new ReactionsElement();
reactionsElement.init(reactionsMessage, 'block');
reactionsElement.init(reactionsMessage, 'block', bubble.middlewareHelper.get());
reactionsElement.render(changedResults);
if(bubble.classList.contains('is-message-empty')) {

29
src/components/chat/contextMenu.ts

@ -19,7 +19,7 @@ import findUpClassName from '../../helpers/dom/findUpClassName'; @@ -19,7 +19,7 @@ import findUpClassName from '../../helpers/dom/findUpClassName';
import cancelEvent from '../../helpers/dom/cancelEvent';
import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
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 assumeType from '../../helpers/assumeType';
import PopupSponsored from '../popups/sponsored';
@ -545,15 +545,30 @@ export default class ChatContextMenu { @@ -545,15 +545,30 @@ export default class ChatContextMenu {
new PopupStickers(inputs, true).show();
});
},
verify: () => {
const entities = (this.message as Message.message).entities;
return entities?.some((entity) => entity._ === 'messageEntityCustomEmoji');
},
verify: () => !!this.getUniqueCustomEmojisFromMessage(this.message).length,
notDirect: () => true,
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() {
this.cleanup();
this.setButtons();
@ -711,9 +726,9 @@ export default class ChatContextMenu { @@ -711,9 +726,9 @@ export default class ChatContextMenu {
menuPadding.bottom = 24;
};
const entities = (this.message as Message.message).entities.filter((entity) => entity._ === 'messageEntityCustomEmoji') as MessageEntity.messageEntityCustomEmoji[];
const docIds = filterUnique(entities.map((entity) => entity.document_id));
const docIds = this.getUniqueCustomEmojisFromMessage(this.message);
const inputsPromise = this.emojiInputsPromise = deferredPromise();
await this.managers.appEmojiManager.getCachedCustomEmojiDocuments(docIds).then(async(docs) => {
const p = async(docs: Document.document[]) => {
const s: Map<StickerSet['id'], InputStickerSet.inputStickerSetID> = new Map();

2
src/components/chat/inlineHelper.ts

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

2
src/components/chat/input.ts

@ -18,7 +18,6 @@ import PopupCreatePoll from '../popups/createPoll'; @@ -18,7 +18,6 @@ import PopupCreatePoll from '../popups/createPoll';
import PopupForward from '../popups/forward';
import PopupNewMedia from '../popups/newMedia';
import {toast, toastNew} from '../toast';
import {wrapReply} from '../wrappers';
import {MessageEntity, DraftMessage, WebPage, Message, UserFull} from '../../layer';
import StickersHelper from './stickersHelper';
import ButtonIcon from '../buttonIcon';
@ -96,6 +95,7 @@ import InputFieldAnimated from '../inputFieldAnimated'; @@ -96,6 +95,7 @@ import InputFieldAnimated from '../inputFieldAnimated';
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
import PopupStickers from '../popups/stickers';
import wrapPeerTitle from '../wrappers/peerTitle';
import wrapReply from '../wrappers/reply';
const RECORD_MIN_TIME = 500;
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'; @@ -14,9 +14,8 @@ import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
import rootScope from '../../lib/rootScope';
import type LazyLoadQueue from '../lazyLoadQueue';
import PeerTitle from '../peerTitle';
import {wrapReply} from '../wrappers';
import wrapReply from '../wrappers/reply';
import Chat, {ChatType} from './chat';
import ReactionsElement from './reactions';
import RepliesElement from './replies';
const NBSP = '&nbsp;';
@ -44,11 +43,14 @@ export namespace MessageRender { @@ -44,11 +43,14 @@ export namespace MessageRender {
const date = new Date(message.date * 1000);
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 isMessage = !('action' in message) && !isSponsored;
let hasReactions: boolean;
// let hasReactions: boolean;
const time: HTMLElement = isSponsored ? undefined : formatTime(date);
if(isMessage) {
@ -81,15 +83,15 @@ export namespace MessageRender { @@ -81,15 +83,15 @@ export namespace MessageRender {
args.unshift(i);
}
if(message.peer_id._ === 'peerUser'/* && message.reactions?.results?.length */) {
hasReactions = true;
// if(USER_REACTIONS_INLINE && message.peer_id._ === 'peerUser'/* && message.reactions?.results?.length */) {
// hasReactions = true;
reactionsMessage = options.reactionsMessage;
reactionsElement = new ReactionsElement();
reactionsElement.init(reactionsMessage, 'inline', true);
reactionsElement.render();
args.unshift(reactionsElement);
}
// reactionsMessage = options.reactionsMessage;
// reactionsElement = new ReactionsElement();
// reactionsElement.init(reactionsMessage, 'inline', true);
// reactionsElement.render();
// args.unshift(reactionsElement);
// }
} else if(isSponsored) {
args.push(sponsoredSpan = makeSponsored());
}
@ -120,11 +122,11 @@ export namespace MessageRender { @@ -120,11 +122,11 @@ export namespace MessageRender {
if(sponsoredSpan) {
clonedArgs[clonedArgs.indexOf(sponsoredSpan)] = makeSponsored();
}
if(reactionsElement) {
const _reactionsElement = clonedArgs[clonedArgs.indexOf(reactionsElement)] = new ReactionsElement();
_reactionsElement.init(reactionsMessage, 'inline');
_reactionsElement.render();
}
// if(reactionsElement) {
// const _reactionsElement = clonedArgs[clonedArgs.indexOf(reactionsElement)] = new ReactionsElement();
// _reactionsElement.init(reactionsMessage, 'inline');
// _reactionsElement.render();
// }
clonedArgs = clonedArgs.map((a) => a instanceof HTMLElement && !a.classList.contains('i18n') && !a.classList.contains('reactions') ? a.cloneNode(true) as HTMLElement : a);
if(time) {
clonedArgs[clonedArgs.length - 1] = formatTime(date); // clone time

54
src/components/chat/reaction.ts

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

60
src/components/chat/reactions.ts

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

8
src/components/chat/reactionsMenu.ts

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

4
src/components/chat/replyContainer.ts

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

3
src/components/chat/sendAs.ts

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

5
src/components/emoticonsDropdown/index.ts

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

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

@ -14,7 +14,7 @@ import {i18n, LangPackKey} from '../../../lib/langPack'; @@ -14,7 +14,7 @@ import {i18n, LangPackKey} from '../../../lib/langPack';
import rootScope from '../../../lib/rootScope';
import {emojiFromCodePoints} from '../../../vendor/emoji';
import {putPreloader} from '../../putPreloader';
import Scrollable from '../../scrollable';
import Scrollable, {ScrollableX} from '../../scrollable';
import StickyIntersector from '../../stickyIntersector';
import IS_EMOJI_SUPPORTED from '../../../environment/emojiSupport';
import IS_TOUCH_SUPPORTED from '../../../environment/touchSupport';
@ -25,6 +25,8 @@ import fixEmoji from '../../../lib/richTextProcessor/fixEmoji'; @@ -25,6 +25,8 @@ import fixEmoji from '../../../lib/richTextProcessor/fixEmoji';
import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
import wrapSingleEmoji from '../../../lib/richTextProcessor/wrapSingleEmoji';
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import {StickersTabCategory} from './stickers';
import positionElementByIndex from '../../../helpers/dom/positionElementByIndex';
const loadedURLs: Set<string> = new Set();
export function appendEmoji(emoji: string, container: HTMLElement, prepend = false, unify = false) {
@ -125,24 +127,25 @@ export default class EmojiTab implements EmoticonsTab { @@ -125,24 +127,25 @@ export default class EmojiTab implements EmoticonsTab {
init() {
this.content = document.getElementById('content-emoji') as HTMLDivElement;
const categories: LangPackKey[] = [
'Emoji.SmilesAndPeople',
'Emoji.AnimalsAndNature',
'Emoji.FoodAndDrink',
'Emoji.TravelAndPlaces',
'Emoji.ActivityAndSport',
'Emoji.Objects',
/* 'Emoji.Symbols', */
'Emoji.Flags',
'Skin Tones' as any
const EMOJI_RECENT_CATEGORY: (typeof EMOJI_CATEGORIES)[0] = ['Emoji.Recent', 'recent'];
const EMOJI_CATEGORIES: [LangPackKey, string][] = [
['Emoji.SmilesAndPeople', 'smile'],
['Emoji.AnimalsAndNature', 'animals'],
['Emoji.FoodAndDrink', 'eats'],
['Emoji.TravelAndPlaces', 'car'],
['Emoji.ActivityAndSport', 'sport'],
['Emoji.Objects', 'lamp'],
// ['Emoji.Symbols', 'info'],
['Emoji.Flags', 'flag'],
['Skin Tones' as any, '']
];
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 { @@ -150,7 +153,7 @@ export default class EmojiTab implements EmoticonsTab {
for(const emoji in Emoji) {
const details = Emoji[emoji];
const i = '' + details;
const category = categories[+i[0] - 1];
const category = EMOJI_CATEGORIES[+i[0] - 1];
if(!category) continue; // maybe it's skin tones
let s = sorted.get(category);
@ -162,25 +165,20 @@ export default class EmojiTab implements EmoticonsTab { @@ -162,25 +165,20 @@ export default class EmojiTab implements EmoticonsTab {
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.delete(categories.pop());
// console.time('emojiParse');
sorted.forEach((emojis, category) => {
const div = document.createElement('div');
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');
sorted.forEach((emojis, emojiCategory) => {
const titleLangPackKey = emojiCategory[0];
const category = new StickersTabCategory({
id: titleLangPackKey,
overflowElement: this.content,
title: i18n(titleLangPackKey),
getElementMediaSize: () => undefined
});
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) => {
/* if(emojiUnicode(emoji) === '1f481-200d-2642') {
@ -204,59 +202,58 @@ export default class EmojiTab implements EmoticonsTab { @@ -204,59 +202,58 @@ export default class EmojiTab implements EmoticonsTab {
// 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') {
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');
// emojiScroll.setVirtualContainer(emojiScroll.container);
const preloader = putPreloader(this.content, true);
Promise.all([
pause(200),
this.managers.appEmojiManager.getRecentEmojis().then((recent) => {
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;
})
this.managers.appEmojiManager.getRecentEmojis()
]).then(([_, recent]) => {
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) {
appendEmoji(emoji, this.recentItemsDiv);
}
this.recentItemsDiv.parentElement.classList.toggle('hide', !this.recentItemsDiv.childElementCount);
categories.unshift('Emoji.Recent');
categories.map((category) => {
const div = divs[category];
recentCategory.elements.container.classList.toggle('hide', !this.recentItemsDiv.childElementCount);
if(!div) {
console.error('no div by category:', category);
}
EMOJI_CATEGORIES.unshift(EMOJI_RECENT_CATEGORY);
EMOJI_CATEGORIES.map(([id], idx) => {
const category = divs[id];
emojiScroll.container.append(div);
this.stickyIntersector.observeStickyHeaderChanges(div);
return div;
positionElementByIndex(category.elements.menuTab, menu, idx);
emojiScroll.container.append(category.elements.container);
this.stickyIntersector.observeStickyHeaderChanges(category.elements.container);
return category;
});
this.menu.children[activeId].classList.add('active');
this.setMenuActive(activeId);
});
attachClickEvent(this.content, this.onContentClick);

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

@ -20,7 +20,6 @@ import {putPreloader} from '../../putPreloader'; @@ -20,7 +20,6 @@ import {putPreloader} from '../../putPreloader';
import PopupStickers from '../../popups/stickers';
import Scrollable, {ScrollableX} from '../../scrollable';
import StickyIntersector from '../../stickyIntersector';
import {wrapSticker, wrapStickerSetThumb} from '../../wrappers';
import findAndSplice from '../../../helpers/array/findAndSplice';
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import positionElementByIndex from '../../../helpers/dom/positionElementByIndex';
@ -34,6 +33,9 @@ import forEachReverse from '../../../helpers/array/forEachReverse'; @@ -34,6 +33,9 @@ import forEachReverse from '../../../helpers/array/forEachReverse';
import {MTAppConfig} from '../../../lib/mtproto/appConfig';
import attachStickerViewerListeners from '../../stickerViewer';
import ListenerSetter from '../../../helpers/listenerSetter';
import wrapSticker from '../../wrappers/sticker';
import wrapStickerSetThumb from '../../wrappers/stickerSetThumb';
import {MediaSize} from '../../../helpers/mediaSize';
export class SuperStickerRenderer {
public lazyLoadQueue: LazyLoadQueueRepeat;
@ -151,60 +153,40 @@ export class SuperStickerRenderer { @@ -151,60 +153,40 @@ export class SuperStickerRenderer {
};
}
type StickersTabCategory = {
elements: {
export class StickersTabCategory {
public elements: {
container: HTMLElement,
title: HTMLElement,
items: HTMLElement,
menuTab: HTMLElement,
menuTabPadding: HTMLElement
},
set: StickerSet.stickerSet,
items: {
};
public items: {
document: MyDocument,
element: HTMLElement
}[],
mounted?: boolean,
id: string,
limit?: number
};
}[];
public mounted: boolean;
public id: string;
public limit: number;
export default class StickersTab implements EmoticonsTab {
private content: HTMLElement;
private categories: {[id: string]: StickersTabCategory};
private categoriesMap: Map<HTMLElement, StickersTabCategory>;
private categoriesIntersector: VisibilityIntersector;
private localCategories: StickersTabCategory[];
private scroll: Scrollable;
private menu: HTMLElement;
private mounted = false;
private stickyIntersector: StickyIntersector;
private superStickerRenderer: SuperStickerRenderer;
private overflowElement: HTMLElement;
private getElementMediaSize: () => MediaSize;
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) {
constructor(options: {
id: string,
title: HTMLElement | DocumentFragment,
overflowElement: HTMLElement,
getElementMediaSize: () => MediaSize
}) {
const container = document.createElement('div');
container.classList.add('emoji-category', 'hide');
container.classList.add('emoji-category');
const items = document.createElement('div');
items.classList.add('category-items', 'super-stickers');
items.classList.add('category-items');
const title = document.createElement('div');
title.classList.add('category-title');
title.append(_title);
title.append(options.title);
const menuTab = ButtonIcon(undefined, {noRipple: true});
menuTab.classList.add('menu-horizontal-div-item');
@ -214,21 +196,70 @@ export default class StickersTab implements EmoticonsTab { @@ -214,21 +196,70 @@ export default class StickersTab implements EmoticonsTab {
menuTab.append(menuTabPadding);
const category: StickersTabCategory = {
elements: {
container.append(title, items);
this.elements = {
container,
title,
items,
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.categoriesMap.set(container, category);
@ -238,6 +269,41 @@ export default class StickersTab implements EmoticonsTab { @@ -238,6 +269,41 @@ export default class StickersTab implements EmoticonsTab {
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(
category: StickersTabCategory,
promise: Promise<MyDocument[]>
@ -256,26 +322,11 @@ export default class StickersTab implements EmoticonsTab { @@ -256,26 +322,11 @@ export default class StickersTab implements EmoticonsTab {
}
});
this.setCategoryItemsHeight(category);
category.setCategoryItemsHeight();
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) {
const category = this.createCategory(set, wrapEmojiText(set.title));
const {menuTab, menuTabPadding, container} = category.elements;
@ -305,9 +356,8 @@ export default class StickersTab implements EmoticonsTab { @@ -305,9 +356,8 @@ export default class StickersTab implements EmoticonsTab {
public init() {
this.content = document.getElementById('content-stickers');
const menuWrapper = this.content.previousElementSibling as HTMLDivElement;
this.menu = menuWrapper.firstElementChild as HTMLUListElement;
const menuWrapper = this.content.previousElementSibling as HTMLElement;
const menu = this.menu = menuWrapper.firstElementChild as HTMLElement;
const menuScroll = new ScrollableX(menuWrapper);
this.scroll = new Scrollable(this.content, 'STICKERS');
@ -334,7 +384,6 @@ export default class StickersTab implements EmoticonsTab { @@ -334,7 +384,6 @@ export default class StickersTab implements EmoticonsTab {
const onCategoryVisibility: OnVisibilityChange = ({target, visible, entry}) => {
const category = this.categoriesMap.get(target);
// console.log('roll the windows up', category, target, visible, entry);
if(!visible) {
category.elements.items.textContent = '';
} else {
@ -356,7 +405,7 @@ export default class StickersTab implements EmoticonsTab { @@ -356,7 +405,7 @@ export default class StickersTab implements EmoticonsTab {
if(findUpClassName(target, 'category-title')) {
const container = findUpClassName(target, 'emoji-category');
const category = this.categoriesMap.get(container);
if(category.set.id === 'recent') {
if(category.id === 'recent') {
return;
}
@ -379,7 +428,7 @@ export default class StickersTab implements EmoticonsTab { @@ -379,7 +428,7 @@ export default class StickersTab implements EmoticonsTab {
setTyping();
});
const {stickyIntersector, setActive} = EmoticonsDropdown.menuOnClick(this.menu, this.scroll, menuScroll);
const {stickyIntersector, setActive} = EmoticonsDropdown.menuOnClick(menu, this.scroll, menuScroll);
this.stickyIntersector = stickyIntersector;
const preloader = putPreloader(this.content, true);
@ -388,7 +437,7 @@ export default class StickersTab implements EmoticonsTab { @@ -388,7 +437,7 @@ export default class StickersTab implements EmoticonsTab {
const category = this.createCategory({id} as any, i18n(title));
this.localCategories.push(category);
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();
this.toggleLocalCategory(category, false);
return category;
@ -423,7 +472,6 @@ export default class StickersTab implements EmoticonsTab { @@ -423,7 +472,6 @@ export default class StickersTab implements EmoticonsTab {
const recentCategory = createLocalCategory('recent', 'Stickers.Recent', 'recent');
recentCategory.limit = 20;
// recentCategory.elements.menuTab.classList.add('active');
const clearButton = ButtonIcon('close', {noRipple: true});
recentCategory.elements.title.append(clearButton);
@ -561,7 +609,7 @@ export default class StickersTab implements EmoticonsTab { @@ -561,7 +609,7 @@ export default class StickersTab implements EmoticonsTab {
const resizeCategories = () => {
for(const [container, category] of this.categoriesMap) {
this.setCategoryItemsHeight(category);
category.setCategoryItemsHeight();
}
};
@ -585,27 +633,6 @@ export default class StickersTab implements EmoticonsTab { @@ -585,27 +633,6 @@ export default class StickersTab implements EmoticonsTab {
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) {
const item = findAndSplice(category.items, (item) => item.document.id === doc.id);
if(item) {

8
src/components/generateTitleIcons.ts

@ -10,18 +10,18 @@ import generateFakeIcon from './generateFakeIcon'; @@ -10,18 +10,18 @@ import generateFakeIcon from './generateFakeIcon';
import generatePremiumIcon from './generatePremiumIcon';
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 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());
}
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));
}
if((peer as User.user).pFlags.premium) {
if((peer as User.user).pFlags.premium && !noPremiumIcon) {
elements.push(generatePremiumIcon());
}

2
src/components/gifsMasonry.ts

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

2
src/components/peerProfileAvatars.ts

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

12
src/components/peerTitle.ts

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

2
src/components/popups/joinChatInvite.ts

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

47
src/components/popups/mute.ts

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

2
src/components/popups/newMedia.ts

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

2
src/components/popups/payment.ts

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

42
src/components/popups/reactedList.ts

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

31
src/components/popups/stickers.ts

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

21
src/components/row.ts

@ -206,3 +206,24 @@ export default class Row { @@ -206,3 +206,24 @@ export default class Row {
export const RadioFormFromRows = (rows: Row[], onChange: (value: string) => void) => {
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'; @@ -23,7 +23,6 @@ import Button from '../../button';
import CheckboxField from '../../checkboxField';
import ProgressivePreloader from '../../preloader';
import {SliderSuperTab} from '../../slider';
import {wrapPhoto} from '../../wrappers';
import AppBackgroundColorTab from './backgroundColor';
import choosePhotoSize from '../../../lib/appManagers/utils/photos/choosePhotoSize';
import {STATE_INIT, Theme} from '../../../config/state';
@ -32,6 +31,7 @@ import requestFile from '../../../helpers/files/requestFile'; @@ -32,6 +31,7 @@ import requestFile from '../../../helpers/files/requestFile';
import {renderImageFromUrlPromise} from '../../../helpers/dom/renderImageFromUrl';
import scaleMediaElement from '../../../helpers/canvas/scaleMediaElement';
import {MediaSize} from '../../../helpers/mediaSize';
import wrapPhoto from '../../wrappers/photo';
export default class AppBackgroundTab extends SliderSuperTab {
private grid: HTMLElement;

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

@ -16,8 +16,7 @@ import AppBackgroundTab from './background'; @@ -16,8 +16,7 @@ import AppBackgroundTab from './background';
import {LangPackKey, _i18n} from '../../../lib/langPack';
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
import assumeType from '../../../helpers/assumeType';
import {MessagesAllStickers, StickerSet} from '../../../layer';
import {wrapStickerSetThumb, wrapStickerToRow} from '../../wrappers';
import {AvailableReaction, MessagesAllStickers, StickerSet} from '../../../layer';
import LazyLoadQueue from '../../lazyLoadQueue';
import PopupStickers from '../../popups/stickers';
import eachMinute from '../../../helpers/eachMinute';
@ -26,6 +25,8 @@ import IS_GEOLOCATION_SUPPORTED from '../../../environment/geolocationSupport'; @@ -26,6 +25,8 @@ import IS_GEOLOCATION_SUPPORTED from '../../../environment/geolocationSupport';
import AppQuickReactionTab from './quickReaction';
import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
import {State} from '../../../config/state';
import wrapStickerSetThumb from '../../wrappers/stickerSetThumb';
import wrapStickerToRow from '../../wrappers/stickerToRow';
export class RangeSettingSelector {
public container: HTMLDivElement;
@ -259,10 +260,16 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable { @@ -259,10 +260,16 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
});
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({
row: reactionsRow,
doc: reaction.static_icon,
doc,
size: 'small'
});
});

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

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

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

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

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

@ -4,12 +4,15 @@ @@ -4,12 +4,15 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import replaceContent from '../../../helpers/dom/replaceContent';
import debounce from '../../../helpers/schedulers/debounce';
import {ChatReactions, Reaction} from '../../../layer';
import {i18n, LangPackKey} from '../../../lib/langPack';
import CheckboxField from '../../checkboxField';
import Row from '../../row';
import Row, {RadioFormFromValues} from '../../row';
import {SettingSection} from '../../sidebarLeft';
import {SliderSuperTabEventable} from '../../sliderTab';
import {wrapStickerToRow} from '../../wrappers';
import wrapStickerToRow from '../../wrappers/stickerToRow';
export default class AppChatReactionsTab extends SliderSuperTabEventable {
public chatId: ChatId;
@ -19,14 +22,42 @@ export default class AppChatReactionsTab extends SliderSuperTabEventable { @@ -19,14 +22,42 @@ export default class AppChatReactionsTab extends SliderSuperTabEventable {
const availableReactions = await this.managers.appReactionsManager.getActiveAvailableReactions();
const chatFull = await this.managers.appProfileManager.getChatFull(this.chatId);
let originalReactions = chatFull.available_reactions ?? [];
const enabledReactions = new Set(originalReactions);
const isBroadcast = await this.managers.appChatsManager.isBroadcast(this.chatId);
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({
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({
name: 'OnlyAllowThisReactions'
});
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));
});
};
let toggleCheckboxField: CheckboxField;
if(isBroadcast) {
toggleCheckboxField = new CheckboxField({toggle: true, checked: _chatReactions._ === 'chatReactionsSome'});
const toggleRow = new Row({
checkboxField: toggleCheckboxField,
titleLangKey: 'EnableReactions',
@ -35,28 +66,94 @@ export default class AppChatReactionsTab extends SliderSuperTabEventable { @@ -35,28 +66,94 @@ export default class AppChatReactionsTab extends SliderSuperTabEventable {
toggleSection.content.append(toggleRow.container);
const reactionsSection = new SettingSection({
name: 'AvailableReactions'
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 emoticon = availableReaction.reaction;
const checkboxField = new CheckboxField({
toggle: true,
checked: enabledReactions.has(availableReaction.reaction)
checked: emoticons.has(emoticon)
});
checkboxFieldsByEmoticon.set(emoticon, checkboxField);
this.listenerSetter.add(checkboxField.input)('change', () => {
if(checkboxField.checked) {
enabledReactions.add(availableReaction.reaction);
emoticons.add(emoticon);
if(!toggleCheckboxField.checked) {
toggleCheckboxField.setValueSilently(true);
if(toggleCheckboxField && !toggleCheckboxField.checked) {
toggleCheckboxField.checked = true;
}
} else {
enabledReactions.delete(availableReaction.reaction);
emoticons.delete(emoticon);
if(!enabledReactions.size && toggleCheckboxField.checked) {
toggleCheckboxField.setValueSilently(false);
if(toggleCheckboxField?.checked && !emoticons.size) {
toggleCheckboxField.checked = false;
}
}
@ -81,34 +178,33 @@ export default class AppChatReactionsTab extends SliderSuperTabEventable { @@ -81,34 +178,33 @@ export default class AppChatReactionsTab extends SliderSuperTabEventable {
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 newReactions = Array.from(enabledReactions);
if([...newReactions].sort().join() === [...originalReactions].sort().join()) {
return;
saveReactionsDebounced.clearTimeout();
// const newReactions = Array.from(enabledReactions);
// 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);
if(chatFull) {
chatFull.available_reactions = newReactions;
}
// const r = (chatReactions as ChatReactions.chatReactionsSome).reactions;
// if(r?.length === availableReactions.length) {
// chatReactions = {_: 'chatReactionsAll'};
// }
this.managers.appChatsManager.setChatAvailableReactions(this.chatId, newReactions);
originalReactions = newReactions;
this.managers.appChatsManager.setChatAvailableReactions(this.chatId, chatReactions);
_chatReactions = chatReactions;
};
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);
}

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

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

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

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

2
src/components/stickerViewer.ts

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

28
src/components/wrappers.ts

@ -4,33 +4,7 @@ @@ -4,33 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import wrapAlbum from './wrappers/album';
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
};
export {};
/* 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");

53
src/components/wrappers/customEmoji.ts

@ -0,0 +1,53 @@ @@ -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 = { @@ -19,7 +19,7 @@ const App = {
version: process.env.VERSION,
versionFull: process.env.VERSION_FULL,
build: +process.env.BUILD,
langPackVersion: '0.4.6',
langPackVersion: '0.4.8',
langPack: 'macos',
langPackCode: 'en',
domains: [MAIN_DOMAIN] as string[],

2
src/global.d.ts vendored

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

2
src/helpers/callbackify.ts

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

2
src/helpers/callbackifyAll.ts

@ -9,7 +9,7 @@ import {Awaited} from '../types'; @@ -9,7 +9,7 @@ import {Awaited} from '../types';
export default function callbackifyAll<T extends readonly unknown[] | [], R extends any>(
values: T,
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)) {
return Promise.all(values).then(callback as any);
} else {

3
src/helpers/schedulers/debounce.ts

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

24
src/index.hbs

@ -145,17 +145,9 @@ @@ -145,17 +145,9 @@
<div class="emoji-container">
<div class="tabs-container">
<div class="tabs-tab emoji-padding">
<nav class="menu-horizontal-div no-stripe">
<button class="menu-horizontal-div-item btn-icon tgico-recent rp"></button>
<button class="menu-horizontal-div-item btn-icon tgico-smile rp"></button>
<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="menu-wrapper">
<nav class="menu-horizontal-div no-stripe"></nav>
</div>
<div class="emoticons-content" id="content-emoji"></div>
</div>
<div class="tabs-tab stickers-padding">
@ -172,11 +164,11 @@ @@ -172,11 +164,11 @@
</div>
</div>
<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-emoji btn-icon tgico-smile rp" 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-gifs btn-icon tgico-gifs rp" 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-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" data-tab="0"></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" data-tab="2"></button>
<button class="menu-horizontal-div-item emoji-tabs-delete justify-self-end btn-icon tgico-deleteleft" data-tab="-1"></button>
</div>
</div>
</div>

8
src/lang.ts

@ -775,6 +775,14 @@ const lang = { @@ -775,6 +775,14 @@ const lang = {
'one_value': '%1$d Emoji Pack',
'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
'AccountSettings.Filters': 'Chat Folders',

454
src/layer.d.ts vendored

@ -223,7 +223,8 @@ export namespace InputMedia { @@ -223,7 +223,8 @@ export namespace InputMedia {
payload: Uint8Array,
provider: string,
provider_data: DataJSON,
start_param?: string
start_param?: string,
extended_media?: InputMedia
};
export type inputMediaGeoLive = {
@ -513,6 +514,7 @@ export namespace User { @@ -513,6 +514,7 @@ export namespace User {
restriction_reason?: Array<RestrictionReason>,
bot_inline_placeholder?: string,
lang_code?: string,
emoji_status?: EmojiStatus,
sortName?: string
};
}
@ -690,7 +692,7 @@ export namespace ChatFull { @@ -690,7 +692,7 @@ export namespace ChatFull {
theme_emoticon?: string,
requests_pending?: number,
recent_requesters?: Array<string | number>,
available_reactions?: Array<string>
available_reactions?: ChatReactions
};
export type channelFull = {
@ -742,7 +744,7 @@ export namespace ChatFull { @@ -742,7 +744,7 @@ export namespace ChatFull {
requests_pending?: number,
recent_requesters?: Array<string | number>,
default_send_as?: Peer,
available_reactions?: Array<string>
available_reactions?: ChatReactions
};
}
@ -1008,7 +1010,8 @@ export namespace MessageMedia { @@ -1008,7 +1010,8 @@ export namespace MessageMedia {
receipt_msg_id?: number,
currency: string,
total_amount: string | number,
start_param: string
start_param: string,
extended_media?: MessageExtendedMedia
};
export type messageMediaGeoLive = {
@ -2016,7 +2019,7 @@ export namespace MessagesFilter { @@ -2016,7 +2019,7 @@ export namespace MessagesFilter {
/**
* @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 type updateNewMessage = {
@ -2256,7 +2259,12 @@ export namespace Update { @@ -2256,7 +2259,12 @@ export namespace Update {
};
export type updateStickerSets = {
_: 'updateStickerSets'
_: 'updateStickerSets',
flags?: number,
pFlags?: Partial<{
masks?: true,
emojis?: true,
}>
};
export type updateSavedGifs = {
@ -2741,6 +2749,37 @@ export namespace Update { @@ -2741,6 +2749,37 @@ export namespace Update {
_: '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 = {
_: 'updateNewDiscussionMessage',
message?: Message
@ -3050,7 +3089,8 @@ export namespace Config { @@ -3050,7 +3089,8 @@ export namespace Config {
webfile_dc_id: number,
suggested_lang_code?: string,
lang_pack_version?: number,
base_lang_pack_version?: number
base_lang_pack_version?: number,
reactions_default?: Reaction
};
}
@ -3928,7 +3968,8 @@ export namespace AccountPassword { @@ -3928,7 +3968,8 @@ export namespace AccountPassword {
new_algo: PasswordKdfAlgo,
new_secure_algo: SecurePasswordKdfAlgo,
secure_random: Uint8Array,
pending_reset_date?: number
pending_reset_date?: number,
login_email_pattern?: string
};
}
@ -4056,7 +4097,7 @@ export namespace ChatInvite { @@ -4056,7 +4097,7 @@ export namespace ChatInvite {
/**
* @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 type inputStickerSetEmpty = {
@ -4090,6 +4131,14 @@ export namespace InputStickerSet { @@ -4090,6 +4131,14 @@ export namespace InputStickerSet {
export type inputStickerSetPremiumGifts = {
_: 'inputStickerSetPremiumGifts'
};
export type inputStickerSetEmojiGenericAnimations = {
_: 'inputStickerSetEmojiGenericAnimations'
};
export type inputStickerSetEmojiDefaultStatuses = {
_: 'inputStickerSetEmojiDefaultStatuses'
};
}
/**
@ -4133,6 +4182,7 @@ export namespace MessagesStickerSet { @@ -4133,6 +4182,7 @@ export namespace MessagesStickerSet {
_: 'messages.stickerSet',
set: StickerSet,
packs: Array<StickerPack>,
keywords: Array<StickerKeyword>,
documents: Array<Document>,
refreshTime?: number
};
@ -5114,7 +5164,7 @@ export namespace AuthCodeType { @@ -5114,7 +5164,7 @@ export namespace AuthCodeType {
/**
* @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 type authSentCodeTypeApp = {
@ -5142,6 +5192,27 @@ export namespace AuthSentCodeType { @@ -5142,6 +5192,27 @@ export namespace AuthSentCodeType {
prefix: string,
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 { @@ -5437,6 +5508,7 @@ export namespace StickerSetCovered {
_: 'stickerSetFullCovered',
set: StickerSet,
packs: Array<StickerPack>,
keywords: Array<StickerKeyword>,
documents: Array<Document>
};
}
@ -6715,8 +6787,8 @@ export namespace ChannelAdminLogEventAction { @@ -6715,8 +6787,8 @@ export namespace ChannelAdminLogEventAction {
export type channelAdminLogEventActionChangeAvailableReactions = {
_: 'channelAdminLogEventActionChangeAvailableReactions',
prev_value: Array<string>,
new_value: Array<string>
prev_value: ChatReactions,
new_value: ChatReactions
};
}
@ -9422,7 +9494,7 @@ export type ChannelsSendAsPeers = ChannelsSendAsPeers.channelsSendAsPeers; @@ -9422,7 +9494,7 @@ export type ChannelsSendAsPeers = ChannelsSendAsPeers.channelsSendAsPeers;
export namespace ChannelsSendAsPeers {
export type channelsSendAsPeers = {
_: 'channels.sendAsPeers',
peers: Array<Peer>,
peers: Array<SendAsPeer>,
chats: Array<Chat>,
users: Array<User>
};
@ -9478,10 +9550,8 @@ export namespace ReactionCount { @@ -9478,10 +9550,8 @@ export namespace ReactionCount {
export type reactionCount = {
_: 'reactionCount',
flags?: number,
pFlags?: Partial<{
chosen?: true,
}>,
reaction: string,
chosen_order?: number,
reaction: Reaction,
count: number
};
}
@ -9593,7 +9663,7 @@ export namespace MessagePeerReaction { @@ -9593,7 +9663,7 @@ export namespace MessagePeerReaction {
unread?: true,
}>,
peer_id: Peer,
reaction: string
reaction: Reaction
};
}
@ -9919,8 +9989,7 @@ export namespace HelpPremiumPromo { @@ -9919,8 +9989,7 @@ export namespace HelpPremiumPromo {
status_entities: Array<MessageEntity>,
video_sections: Array<string>,
videos: Array<Document>,
currency: string,
monthly_amount: string | number,
period_options: Array<PremiumSubscriptionOption>,
users: Array<User>
};
}
@ -9977,6 +10046,239 @@ export namespace PaymentFormMethod { @@ -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 {
'error': Error.error,
'inputPeerEmpty': InputPeer.inputPeerEmpty,
@ -10986,6 +11288,41 @@ export interface ConstructorDeclMap { @@ -10986,6 +11288,41 @@ export interface ConstructorDeclMap {
'privacyKeyVoiceMessages': PrivacyKey.privacyKeyVoiceMessages,
'paymentFormMethod': PaymentFormMethod.paymentFormMethod,
'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,
'messageEntityHighlight': MessageEntity.messageEntityHighlight,
'messageEntityLinebreak': MessageEntity.messageEntityLinebreak,
@ -11033,9 +11370,11 @@ export type AuthSignUp = { @@ -11033,9 +11370,11 @@ export type AuthSignUp = {
};
export type AuthSignIn = {
flags?: number,
phone_number: string,
phone_code_hash: string,
phone_code: string
phone_code?: string,
email_verification?: EmailVerification
};
export type AuthLogOut = {
@ -11239,6 +11578,7 @@ export type MessagesSendMessage = { @@ -11239,6 +11578,7 @@ export type MessagesSendMessage = {
background?: boolean,
clear_draft?: boolean,
noforwards?: boolean,
update_stickersets_order?: boolean,
peer: InputPeer,
reply_to_msg_id?: number,
message: string,
@ -11255,6 +11595,7 @@ export type MessagesSendMedia = { @@ -11255,6 +11595,7 @@ export type MessagesSendMedia = {
background?: boolean,
clear_draft?: boolean,
noforwards?: boolean,
update_stickersets_order?: boolean,
peer: InputPeer,
reply_to_msg_id?: number,
media: InputMedia,
@ -12366,6 +12707,7 @@ export type MessagesSendMultiMedia = { @@ -12366,6 +12707,7 @@ export type MessagesSendMultiMedia = {
background?: boolean,
clear_draft?: boolean,
noforwards?: boolean,
update_stickersets_order?: boolean,
peer: InputPeer,
reply_to_msg_id?: number,
multi_media: Array<InputSingleMedia>,
@ -12458,12 +12800,13 @@ export type AccountVerifyPhone = { @@ -12458,12 +12800,13 @@ export type AccountVerifyPhone = {
};
export type AccountSendVerifyEmailCode = {
purpose: EmailVerifyPurpose,
email: string
};
export type AccountVerifyEmail = {
email: string,
code: string
purpose: EmailVerifyPurpose,
verification: EmailVerification
};
export type HelpGetDeepLinkInfo = {
@ -13340,9 +13683,10 @@ export type ChannelsDeleteParticipantHistory = { @@ -13340,9 +13683,10 @@ export type ChannelsDeleteParticipantHistory = {
export type MessagesSendReaction = {
flags?: number,
big?: boolean,
add_to_recent?: boolean,
peer: InputPeer,
msg_id: number,
reaction?: string
reaction?: Array<Reaction>
};
export type MessagesGetMessagesReactions = {
@ -13354,14 +13698,14 @@ export type MessagesGetMessageReactionsList = { @@ -13354,14 +13698,14 @@ export type MessagesGetMessageReactionsList = {
flags?: number,
peer: InputPeer,
id: number,
reaction?: string,
reaction?: Reaction,
offset?: string,
limit: number
};
export type MessagesSetChatAvailableReactions = {
peer: InputPeer,
available_reactions: Array<string>
available_reactions: ChatReactions
};
export type MessagesGetAvailableReactions = {
@ -13369,7 +13713,7 @@ export type MessagesGetAvailableReactions = { @@ -13369,7 +13713,7 @@ export type MessagesGetAvailableReactions = {
};
export type MessagesSetDefaultReaction = {
reaction: string
reaction: Reaction
};
export type MessagesTranslateText = {
@ -13435,6 +13779,7 @@ export type MessagesRequestWebView = { @@ -13435,6 +13779,7 @@ export type MessagesRequestWebView = {
url?: string,
start_param?: string,
theme_params?: DataJSON,
platform: string,
reply_to_msg_id?: number,
send_as?: InputPeer
};
@ -13453,7 +13798,8 @@ export type MessagesRequestSimpleWebView = { @@ -13453,7 +13798,8 @@ export type MessagesRequestSimpleWebView = {
flags?: number,
bot: InputUser,
url: string,
theme_params?: DataJSON
theme_params?: DataJSON,
platform: string
};
export type MessagesSendWebViewResultMessage = {
@ -13561,6 +13907,47 @@ export type MessagesGetFeaturedEmojiStickers = { @@ -13561,6 +13907,47 @@ export type MessagesGetFeaturedEmojiStickers = {
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 {
'invokeAfterMsg': {req: InvokeAfterMsg, res: any},
'invokeAfterMsgs': {req: InvokeAfterMsgs, res: any},
@ -13808,7 +14195,7 @@ export interface MethodDeclMap { @@ -13808,7 +14195,7 @@ export interface MethodDeclMap {
'account.sendVerifyPhoneCode': {req: AccountSendVerifyPhoneCode, res: AuthSentCode},
'account.verifyPhone': {req: AccountVerifyPhone, res: boolean},
'account.sendVerifyEmailCode': {req: AccountSendVerifyEmailCode, res: AccountSentEmailCode},
'account.verifyEmail': {req: AccountVerifyEmail, res: boolean},
'account.verifyEmail': {req: AccountVerifyEmail, res: AccountEmailVerified},
'help.getDeepLinkInfo': {req: HelpGetDeepLinkInfo, res: HelpDeepLinkInfo},
'contacts.getSaved': {req: ContactsGetSaved, res: Array<SavedContact>},
'channels.getLeftChannels': {req: ChannelsGetLeftChannels, res: MessagesChats},
@ -14009,5 +14396,14 @@ export interface MethodDeclMap { @@ -14009,5 +14396,14 @@ export interface MethodDeclMap {
'messages.getCustomEmojiDocuments': {req: MessagesGetCustomEmojiDocuments, res: Array<Document>},
'messages.getEmojiStickers': {req: MessagesGetEmojiStickers, res: MessagesAllStickers},
'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 @@ @@ -12,7 +12,7 @@
import deepEqual from '../../helpers/object/deepEqual';
import isObject from '../../helpers/object/isObject';
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 {AppManager} from './manager';
import hasRights from './utils/chats/hasRights';
@ -643,7 +643,12 @@ export class AppChatsManager extends AppManager { @@ -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', {
peer: this.getInputPeer(id),
available_reactions: reactions

2
src/lib/appManagers/appDialogsManager.ts

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

4
src/lib/appManagers/appEmojiManager.ts

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

13
src/lib/appManagers/appMessagesManager.ts

@ -13,7 +13,7 @@ import LazyLoadQueueBase from '../../components/lazyLoadQueueBase'; @@ -13,7 +13,7 @@ import LazyLoadQueueBase from '../../components/lazyLoadQueueBase';
import deferredPromise, {CancellablePromise} from '../../helpers/cancellablePromise';
import tsNow from '../../helpers/tsNow';
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 {logger, LogTypes} from '../logger';
import type {ApiFileManager} from '../mtproto/apiFileManager';
@ -62,6 +62,7 @@ import pause from '../../helpers/schedulers/pause'; @@ -62,6 +62,7 @@ import pause from '../../helpers/schedulers/pause';
import makeError from '../../helpers/makeError';
import getStickerEffectThumb from './utils/stickers/getStickerEffectThumb';
import getDocumentInput from './utils/docs/getDocumentInput';
import reactionsEqual from './utils/reactions/reactionsEqual';
// console.trace('include');
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
@ -5012,7 +5013,7 @@ export class AppMessagesManager extends AppManager { @@ -5012,7 +5013,7 @@ export class AppMessagesManager extends AppManager {
public async getMessageReactionsListAndReadParticipants(
message: Message.message,
limit?: number,
reaction?: string,
reaction?: Reaction,
offset?: string,
skipReadParticipants?: boolean,
skipReactionsList?: boolean
@ -5044,7 +5045,7 @@ export class AppMessagesManager extends AppManager { @@ -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})));
return {
@ -5882,16 +5883,16 @@ export class AppMessagesManager extends AppManager { @@ -5882,16 +5883,16 @@ export class AppMessagesManager extends AppManager {
const results = message.reactions?.results ?? [];
const previousResults = previousReactions?.results ?? [];
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 (
message.pFlags.out && (
!previousReactionCount ||
reactionCount.count > previousReactionCount.count
)
) || (
reactionCount.pFlags.chosen && (
reactionCount.chosen_order !== undefined && (
!previousReactionCount ||
!previousReactionCount.pFlags.chosen
previousReactionCount.chosen_order === undefined
)
);
});

137
src/lib/appManagers/appReactionsManager.ts

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

24
src/lib/appManagers/appStickersManager.ts

@ -392,7 +392,8 @@ export class AppStickersManager extends AppManager { @@ -392,7 +392,8 @@ export class AppStickersManager extends AppManager {
_: 'messages.stickerSet',
set: res.set,
packs: res.packs,
documents: res.documents as Document[]
documents: res.documents as Document[],
keywords: res.keywords
};
let stickerSet = this.storage.getFromCache(id);
@ -484,7 +485,7 @@ export class AppStickersManager extends AppManager { @@ -484,7 +485,7 @@ export class AppStickersManager extends AppManager {
});
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;
@ -605,7 +606,7 @@ export class AppStickersManager extends AppManager { @@ -605,7 +606,7 @@ export class AppStickersManager extends AppManager {
});
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;
@ -625,10 +626,7 @@ export class AppStickersManager extends AppManager { @@ -625,10 +626,7 @@ export class AppStickersManager extends AppManager {
return res.sets.concat(foundSaved);
}
public getAllStickers() {
return this.apiManager.invokeApiHashable({
method: 'messages.getAllStickers',
processResult: (allStickers) => {
private processAllStickersResult = (allStickers: MessagesAllStickers) => {
assumeType<MessagesAllStickers.messagesAllStickers>(allStickers);
forEachReverse(allStickers.sets, (stickerSet, idx, arr) => {
@ -638,7 +636,19 @@ export class AppStickersManager extends AppManager { @@ -638,7 +636,19 @@ export class AppStickersManager extends AppManager {
});
return allStickers;
};
public getAllStickers() {
return this.apiManager.invokeApiHashable({
method: 'messages.getAllStickers',
processResult: this.processAllStickersResult
});
}
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 { @@ -232,10 +232,11 @@ export class UiNotificationsManager {
} else {
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 args: FormatterArguments = [
fixEmoji(peerReaction.reaction), // can be plain heart
fixEmoji(reaction.emoticon), // can be plain heart
notificationMessage
];

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

@ -0,0 +1,6 @@ @@ -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 { @@ -12,6 +12,9 @@ export interface MTAppConfig {
youtube_pip?: string;
qr_login_camera?: boolean;
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_tooltip?: boolean;
ignore_restriction_reasons?: string[];
@ -23,7 +26,6 @@ export interface MTAppConfig { @@ -23,7 +26,6 @@ export interface MTAppConfig {
round_video_encoding?: RoundVideoEncoding;
chat_read_mark_expire_period?: number;
chat_read_mark_size_threshold?: number;
reactions_default?: string;
reactions_uniq_max?: number;
ringtone_duration_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'; @@ -22,7 +22,7 @@ import {CLICK_EVENT_NAME} from '../../helpers/dom/clickEvent';
import IS_CUSTOM_EMOJI_SUPPORTED from '../../environment/customEmojiSupport';
import rootScope from '../rootScope';
import mediaSizes from '../../helpers/mediaSizes';
import {wrapSticker} from '../../components/wrappers';
import wrapSticker from '../../components/wrappers/sticker';
import RLottiePlayer from '../rlottie/rlottiePlayer';
import animationIntersector, {AnimationItemGroup} from '../../components/animationIntersector';
import type {MyDocument} from '../appManagers/appDocsManager';
@ -65,6 +65,10 @@ class CustomEmojiElement extends HTMLElement { @@ -65,6 +65,10 @@ class CustomEmojiElement extends HTMLElement {
}
public disconnectedCallback() {
if(this.isConnected) { // prepend on sibling can invoke disconnectedCallback
return;
}
if(this.syncedPlayer) {
this.syncedPlayer.pausedElements.delete(this);
}
@ -169,6 +173,10 @@ export class CustomEmojiRendererElement extends HTMLElement { @@ -169,6 +173,10 @@ export class CustomEmojiRendererElement extends HTMLElement {
}
public disconnectedCallback() {
if(this.isConnected) {
return;
}
for(const [syncedPlayer, elements] of this.syncedElements) {
if(syncedPlayers.get(syncedPlayer.key) !== syncedPlayer) {
continue;
@ -218,6 +226,10 @@ export class CustomEmojiRendererElement extends HTMLElement { @@ -218,6 +226,10 @@ export class CustomEmojiRendererElement extends HTMLElement {
}
const overflowElement = findUpClassName(this, 'scrollable') || this.offsetParent as HTMLElement;
if(!overflowElement) {
return offsetsMap;
}
const overflowRect = overflowElement.getBoundingClientRect();
const rect = this.getBoundingClientRect();

4
src/lib/rootScope.ts

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
* 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 {MyDialogFilter} from './storages/filters';
import type {Folder} from './storages/dialogs';
@ -137,7 +137,7 @@ export type BroadcastEvents = { @@ -137,7 +137,7 @@ export type BroadcastEvents = {
'call_update': PhoneCall,
'call_signaling': {callId: CallId, data: Uint8Array},
'quick_reaction': string,
'quick_reaction': Reaction,
'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 { @@ -89,18 +89,18 @@ avatar-element {
position: relative;
}
&.is-premium {
&:before {
font-size: .6875rem;
color: #fff;
position: absolute;
right: 0;
bottom: -.0625rem;
line-height: 1 !important;
z-index: 1;
pointer-events: none;
}
}
// &.is-premium {
// &:before {
// font-size: .6875rem;
// color: #fff;
// position: absolute;
// right: 0;
// bottom: -.0625rem;
// line-height: 1 !important;
// z-index: 1;
// pointer-events: none;
// }
// }
img {
//width: 100% !important;

5
src/scss/partials/_chatBubble.scss

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

10
src/scss/partials/_customEmoji.scss

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

63
src/scss/partials/_emojiDropdown.scss

@ -48,9 +48,36 @@ @@ -48,9 +48,36 @@
overflow: hidden;
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 {
z-index: 4;
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);
}
}
}
}
}
@ -125,13 +152,6 @@ @@ -125,13 +152,6 @@
.emoji-padding {
.super-emojis {
padding: 0 .5rem;
}
@include respond-to(handhelds) {
.menu-horizontal-div-item {
flex: unset;
padding: 0;
}
}
}
@ -164,35 +184,6 @@ @@ -164,35 +184,6 @@
.category-items {
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 @@ @@ -880,6 +880,14 @@
&.full-width {
margin: 0 !important;
}
> .media-sticker-wrapper {
width: 86px;
height: 86px;
margin: 1px auto 29px;
flex: 0 0 auto;
position: relative;
}
}
&-name {
@ -925,14 +933,6 @@ @@ -925,14 +933,6 @@
opacity: .25;
}
.media-sticker-wrapper {
width: 86px;
height: 86px;
margin: 1px auto 29px;
flex: 0 0 auto;
position: relative;
}
.chatlist {
padding: 0;
}

16
src/scss/partials/_reaction.scss

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

2
src/scss/partials/_row.scss

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

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

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

Loading…
Cancel
Save