Browse Source

Reactions management

Config, stickers: fix possible race conditions
master
Eduard Kuzmenko 2 years ago
parent
commit
a0384f5daf
  1. 7
      src/components/radioField.ts
  2. 25
      src/components/row.ts
  3. 29
      src/components/sidebarLeft/tabs/generalSettings.ts
  4. 65
      src/components/sidebarLeft/tabs/quickReaction.ts
  5. 100
      src/components/sidebarRight/tabs/chatReactions.ts
  6. 39
      src/components/sidebarRight/tabs/editChat.ts
  7. 32
      src/components/wrappers.ts
  8. 9
      src/helpers/callbackify.ts
  9. 82
      src/lang.ts
  10. 9
      src/lib/appManagers/appChatsManager.ts
  11. 57
      src/lib/appManagers/appReactionsManager.ts
  12. 8
      src/lib/appManagers/appStickersManager.ts
  13. 8
      src/lib/mtproto/mtprotoworker.ts
  14. 5
      src/lib/rootScope.ts

7
src/components/radioField.ts

@ -18,10 +18,15 @@ export default class RadioField { @@ -18,10 +18,15 @@ export default class RadioField {
langKey?: LangPackKey,
name: string,
value?: string,
stateKey?: string
stateKey?: string,
alignRight?: boolean
}) {
const label = this.label = document.createElement('label');
label.classList.add('radio-field');
if(options.alignRight) {
label.classList.add('radio-field-right');
}
const input = this.input = document.createElement('input');
input.type = 'radio';

25
src/components/row.ts

@ -17,6 +17,7 @@ export default class Row { @@ -17,6 +17,7 @@ export default class Row {
public container: HTMLElement;
public title: HTMLDivElement;
public subtitle: HTMLElement;
public media: HTMLElement;
public checkboxField: CheckboxField;
public radioField: RadioField;
@ -31,7 +32,7 @@ export default class Row { @@ -31,7 +32,7 @@ export default class Row {
radioField: Row['radioField'],
checkboxField: Row['checkboxField'],
noCheckboxSubtitle: boolean,
title: string,
title: string | HTMLElement,
titleLangKey: LangPackKey,
titleRight: string | HTMLElement,
clickable: boolean | ((e: Event) => void),
@ -58,10 +59,10 @@ export default class Row { @@ -58,10 +59,10 @@ export default class Row {
let havePadding = !!options.havePadding;
if(options.radioField || options.checkboxField) {
havePadding = true;
if(options.radioField) {
this.radioField = options.radioField;
this.container.append(this.radioField.label);
havePadding = true;
}
if(options.checkboxField) {
@ -72,6 +73,7 @@ export default class Row { @@ -72,6 +73,7 @@ export default class Row {
this.container.classList.add('row-with-toggle');
options.titleRight = this.checkboxField.label;
} else {
havePadding = true;
this.container.append(this.checkboxField.label);
}
@ -100,7 +102,11 @@ export default class Row { @@ -100,7 +102,11 @@ export default class Row {
this.title.classList.add('row-title');
this.title.setAttribute('dir', 'auto');
if(options.title) {
this.title.innerHTML = options.title;
if(typeof(options.title) === 'string') {
this.title.innerHTML = options.title;
} else {
this.title.append(options.title);
}
} else {
this.title.append(i18n(options.titleLangKey));
}
@ -154,7 +160,20 @@ export default class Row { @@ -154,7 +160,20 @@ export default class Row {
}
}
public createMedia(size?: 'small') {
this.container.classList.add('row-with-padding');
const media = this.media = document.createElement('div');
media.classList.add('row-media');
if(size) {
media.classList.add('row-media-' + size);
}
this.container.append(media);
return media;
}
}
export const RadioFormFromRows = (rows: Row[], onChange: (value: string) => void) => {

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

@ -20,13 +20,14 @@ import appStickersManager from "../../../lib/appManagers/appStickersManager"; @@ -20,13 +20,14 @@ import appStickersManager from "../../../lib/appManagers/appStickersManager";
import assumeType from "../../../helpers/assumeType";
import { MessagesAllStickers, StickerSet } from "../../../layer";
import RichTextProcessor from "../../../lib/richtextprocessor";
import { wrapSticker, wrapStickerSetThumb } from "../../wrappers";
import { wrapStickerSetThumb, wrapStickerToRow } from "../../wrappers";
import LazyLoadQueue from "../../lazyLoadQueue";
import PopupStickers from "../../popups/stickers";
import eachMinute from "../../../helpers/eachMinute";
import { SliderSuperTabEventable } from "../../sliderTab";
import IS_GEOLOCATION_SUPPORTED from "../../../environment/geolocationSupport";
import appReactionsManager from "../../../lib/appManagers/appReactionsManager";
import AppQuickReactionTab from "./quickReaction";
export class RangeSettingSelector {
public container: HTMLDivElement;
@ -287,26 +288,26 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable { @@ -287,26 +288,26 @@ export default class AppGeneralSettingsTab extends SliderSuperTabEventable {
const container = section('Telegram.InstalledStickerPacksController');
const reactionsRow = new Row({
titleLangKey: 'Reactions',
titleLangKey: 'DoubleTapSetting',
havePadding: true,
clickable: () => {
new AppQuickReactionTab(this.slider).open();
}
});
const quickReactionMediaDiv = document.createElement('div');
quickReactionMediaDiv.classList.add('row-media', 'row-media-small');
appReactionsManager.getQuickReaction().then(reaction => {
wrapSticker({
div: quickReactionMediaDiv,
doc: reaction.static_icon,
width: 32,
height: 32
const renderQuickReaction = () => {
appReactionsManager.getQuickReaction().then(reaction => {
wrapStickerToRow({
row: reactionsRow,
doc: reaction.static_icon,
size: 'small'
});
});
});
};
renderQuickReaction();
reactionsRow.container.append(quickReactionMediaDiv);
this.listenerSetter.add(rootScope)('quick_reaction', renderQuickReaction);
const suggestCheckboxField = new CheckboxField({
text: 'Stickers.SuggestStickers',

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

@ -0,0 +1,65 @@ @@ -0,0 +1,65 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { SettingSection } from "..";
import appReactionsManager from "../../../lib/appManagers/appReactionsManager";
import RadioField from "../../radioField";
import Row, { RadioFormFromRows } from "../../row";
import SliderSuperTab from "../../sliderTab";
import { wrapStickerToRow } from "../../wrappers";
export default class AppQuickReactionTab extends SliderSuperTab {
protected init() {
this.header.classList.add('with-border');
this.setTitle('DoubleTapSetting');
this.container.classList.add('quick-reaction-container');
return Promise.all([
appReactionsManager.getQuickReaction(),
appReactionsManager.getAvailableReactions()
]).then(([quickReaction, availableReactions]) => {
availableReactions = availableReactions.filter(reaction => !reaction.pFlags.inactive);
const section = new SettingSection();
const name = 'quick-reaction';
const rows = availableReactions.map((availableReaction) => {
const radioField = new RadioField({
name,
text: availableReaction.title,
value: availableReaction.reaction,
alignRight: true
});
const row = new Row({
radioField,
havePadding: true
});
radioField.main.classList.add('quick-reaction-title');
wrapStickerToRow({
row,
doc: availableReaction.static_icon,
size: 'small'
});
if(availableReaction === quickReaction) {
radioField.setValueSilently(true);
}
return row;
});
const form = RadioFormFromRows(rows, (value) => {
appReactionsManager.setDefaultReaction(value);
});
section.content.append(form);
this.scrollable.append(section.container);
});
}
}

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

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import appChatsManager from "../../../lib/appManagers/appChatsManager";
import appProfileManager from "../../../lib/appManagers/appProfileManager";
import appReactionsManager from "../../../lib/appManagers/appReactionsManager";
import CheckboxField from "../../checkboxField";
import Row from "../../row";
import { SettingSection } from "../../sidebarLeft";
import { SliderSuperTabEventable } from "../../sliderTab";
import { wrapStickerToRow } from "../../wrappers";
export default class AppChatReactionsTab extends SliderSuperTabEventable {
public chatId: ChatId;
protected async init() {
this.setTitle('Reactions');
const availableReactions = await appReactionsManager.getActiveAvailableReactions();
const chatFull = await appProfileManager.getChatFull(this.chatId);
const originalReactions = chatFull.available_reactions ?? [];
const enabledReactions = new Set(originalReactions);
const toggleSection = new SettingSection({
caption: appChatsManager.isBroadcast(this.chatId) ? 'EnableReactionsChannelInfo' : 'EnableReactionsGroupInfo'
});
const toggleCheckboxField = new CheckboxField({toggle: true, checked: !!enabledReactions.size});
const toggleRow = new Row({
checkboxField: toggleCheckboxField,
titleLangKey: 'EnableReactions'
});
toggleSection.content.append(toggleRow.container);
const reactionsSection = new SettingSection({
name: 'AvailableReactions'
});
const checkboxFields = availableReactions.map(availableReaction => {
const checkboxField = new CheckboxField({
toggle: true,
checked: enabledReactions.has(availableReaction.reaction)
});
this.listenerSetter.add(checkboxField.input)('change', () => {
if(checkboxField.checked) {
enabledReactions.add(availableReaction.reaction);
if(!toggleCheckboxField.checked) {
toggleCheckboxField.setValueSilently(true);
}
} else enabledReactions.delete(availableReaction.reaction);
});
const row = new Row({
checkboxField,
title: availableReaction.title,
havePadding: true
});
wrapStickerToRow({
row,
doc: availableReaction.static_icon,
size: 'small'
});
reactionsSection.content.append(row.container);
return checkboxField;
});
this.listenerSetter.add(toggleRow.checkboxField.input)('change', () => {
if(!toggleCheckboxField.checked) {
checkboxFields.forEach(checkboxField => checkboxField.setValueSilently(false));
} else if(checkboxFields.every(checkboxField => !checkboxField.checked)) {
checkboxFields.forEach(checkboxField => checkboxField.setValueSilently(true));
}
});
this.eventListener.addEventListener('destroy', () => {
const newReactions = Array.from(enabledReactions);
if([...newReactions].sort().join() === [...originalReactions].sort().join()) {
return;
}
const chatFull = appProfileManager.getCachedFullChat(this.chatId);
if(chatFull) {
chatFull.available_reactions = newReactions;
}
appChatsManager.setChatAvailableReactions(this.chatId, newReactions);
}, {once: true});
this.scrollable.append(toggleSection.container, reactionsSection.container);
}
}

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

@ -21,22 +21,27 @@ import PopupDeleteDialog from "../../popups/deleteDialog"; @@ -21,22 +21,27 @@ import PopupDeleteDialog from "../../popups/deleteDialog";
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import toggleDisability from "../../../helpers/dom/toggleDisability";
import CheckboxField from "../../checkboxField";
import appReactionsManager from "../../../lib/appManagers/appReactionsManager";
import AppChatReactionsTab from "./chatReactions";
export default class AppEditChatTab extends SliderSuperTab {
private chatNameInputField: InputField;
private descriptionInputField: InputField;
private editPeer: EditPeer;
private tempId: number;
public chatId: ChatId;
protected async _init() {
// * cleanup prev
this.listenerSetter.removeAll();
this.scrollable.container.innerHTML = '';
this.tempId ??= 0;
const tempId = ++this.tempId;
this.container.classList.add('edit-peer-container', 'edit-group-container');
this.setTitle('Edit');
const chatFull = await appProfileManager.getChatFull(this.chatId, true);
let chatFull = await appProfileManager.getChatFull(this.chatId, true);
const chat: Chat.chat | Chat.channel = appChatsManager.getChat(this.chatId);
const isBroadcast = appChatsManager.isBroadcast(this.chatId);
@ -53,6 +58,12 @@ export default class AppEditChatTab extends SliderSuperTab { @@ -53,6 +58,12 @@ export default class AppEditChatTab extends SliderSuperTab {
}
});
this.listenerSetter.add(rootScope)('chat_full_update', (chatId) => {
if(this.chatId === chatId) {
chatFull = appProfileManager.getCachedFullChat(chatId) || chatFull;
}
});
const peerId = this.chatId.toPeerId(true);
{
@ -119,6 +130,32 @@ export default class AppEditChatTab extends SliderSuperTab { @@ -119,6 +130,32 @@ export default class AppEditChatTab extends SliderSuperTab {
setChatTypeSubtitle();
section.content.append(chatTypeRow.container);
const reactionsRow = new Row({
titleLangKey: 'Reactions',
icon: 'tip',
clickable: () => {
const tab = new AppChatReactionsTab(this.slider);
tab.chatId = this.chatId;
tab.open().then(() => {
if(this.tempId !== tempId) {
return;
}
this.listenerSetter.add(tab.eventListener)('destroy', setReactionsLength);
});
}
});
const availableReactions = await appReactionsManager.getAvailableReactions();
const availableReactionsLength = availableReactions.filter(availableReaction => !availableReaction.pFlags.inactive).length;
const setReactionsLength = () => {
reactionsRow.subtitle.innerHTML = chatFull.available_reactions.length + '/' + availableReactionsLength;
};
setReactionsLength();
section.content.append(reactionsRow.container);
}
if(appChatsManager.hasRights(this.chatId, 'change_permissions') && !isBroadcast) {

32
src/components/wrappers.ts

@ -56,6 +56,7 @@ import appMessagesIdsManager from '../lib/appManagers/appMessagesIdsManager'; @@ -56,6 +56,7 @@ import appMessagesIdsManager from '../lib/appManagers/appMessagesIdsManager';
import throttle from '../helpers/schedulers/throttle';
import { SendMessageEmojiInteractionData } from '../types';
import IS_VIBRATE_SUPPORTED from '../environment/vibrateSupport';
import Row from './row';
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
@ -1637,6 +1638,37 @@ export async function wrapStickerSetThumb({set, lazyLoadQueue, container, group, @@ -1637,6 +1638,37 @@ export async function wrapStickerSetThumb({set, lazyLoadQueue, container, group,
}
}
export function wrapStickerToRow({doc, row, size}: {
doc: MyDocument,
row: Row,
size?: 'small' | 'large',
}) {
const previousMedia = row.media;
const media = row.createMedia('small');
if(previousMedia) {
media.classList.add('hide');
}
const loadPromises: Promise<any>[] = previousMedia ? [] : undefined;
const _size = size === 'small' ? 32 : 48;
const result = wrapSticker({
div: media,
doc: doc,
width: _size,
height: _size,
loadPromises
});
loadPromises && Promise.all(loadPromises).then(() => {
media.classList.remove('hide');
previousMedia.remove();
});
return result;
}
export function wrapLocalSticker({emoji, width, height}: {
doc?: MyDocument,
url?: string,

9
src/helpers/callbackify.ts

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

82
src/lang.ts

@ -642,6 +642,10 @@ const lang = { @@ -642,6 +642,10 @@ const lang = {
"Update": "UPDATE",
"Reactions": "Reactions",
"DoubleTapSetting": "Quick Reaction",
"EnableReactions": "Enable Reactions",
"EnableReactionsChannelInfo": "Allow subscribers to react to channel posts.",
"EnableReactionsGroupInfo": "Allow members to react to group messages.",
"AvailableReactions": "Available reactions",
// * macos
"AccountSettings.Filters": "Chat Folders",
@ -833,22 +837,25 @@ const lang = { @@ -833,22 +837,25 @@ const lang = {
"Emoji.Objects": "Objects",
//"Emoji.Symbols": "Symbols",
"Emoji.Flags": "Flags",
"InstalledStickers.LoopAnimated": "Loop Animated Stickers",
"LastSeen.HoursAgo": {
"one_value": "last seen %d hour ago",
"other_value": "last seen %d hours ago"
},
"Login.Register.LastName.Placeholder": "Last Name",
"Message.Context.Select": "Select",
"Message.Context.Pin": "Pin",
"Message.Context.Unpin": "Unpin",
"Message.Context.Goto": "Show Message",
"MessageContext.CopyMessageLink1": "Copy Message Link",
"Modal.Send": "Send",
"Telegram.GeneralSettingsViewController": "General Settings",
"Telegram.InstalledStickerPacksController": "Stickers",
"Telegram.NotificationSettingsViewController": "Notifications",
"Telegram.LanguageViewController": "Language",
"Stickers.SearchAdd": "Add",
"Stickers.SearchAdded": "Added",
"Stickers.SuggestStickers": "Suggest Stickers by Emoji",
"ShareModal.Search.Placeholder": "Share to...",
"ShareModal.Search.ForwardPlaceholder": "Forward to...",
"InstalledStickers.LoopAnimated": "Loop Animated Stickers",
"NewPoll.Anonymous": "Anonymous Voting",
"NewPoll.Explanation.Placeholder": "Add a Comment (Optional)",
"NewPoll.OptionsAddOption": "Add an Option",
"NewPoll.MultipleChoice": "Multiple Answers",
"NewPoll.Quiz": "Quiz Mode",
"Notification.Contact.Reacted": "%1$@ to your \"%2$@\"",
// "Notification.Group.Reacted": "%1$@: %2$@ to your \"%3$@\"",
"Peer.Activity.User.PlayingGame": "playing a game",
"Peer.Activity.User.TypingText": "typing",
"Peer.Activity.User.SendingPhoto": "sending a photo",
@ -958,16 +965,15 @@ const lang = { @@ -958,16 +965,15 @@ const lang = {
},
"RecentSessions.Error.FreshReset": "For security reasons, you can't terminate older sessions from a device that you've just connected. Please use an earlier connection or wait for a few hours.",
"RequestJoin.Button": "Request to Join",
"Message.Context.Select": "Select",
"Message.Context.Pin": "Pin",
"Message.Context.Unpin": "Unpin",
"Message.Context.Goto": "Show Message",
"MessageContext.CopyMessageLink1": "Copy Message Link",
"NewPoll.Anonymous": "Anonymous Voting",
"NewPoll.Explanation.Placeholder": "Add a Comment (Optional)",
"NewPoll.OptionsAddOption": "Add an Option",
"NewPoll.MultipleChoice": "Multiple Answers",
"NewPoll.Quiz": "Quiz Mode",
"Stickers.SearchAdd": "Add",
"Stickers.SearchAdded": "Added",
"Stickers.SuggestStickers": "Suggest Stickers by Emoji",
"ShareModal.Search.Placeholder": "Share to...",
"ShareModal.Search.ForwardPlaceholder": "Forward to...",
"Telegram.GeneralSettingsViewController": "General Settings",
"Telegram.InstalledStickerPacksController": "Stickers",
"Telegram.NotificationSettingsViewController": "Notifications",
"Telegram.LanguageViewController": "Language",
"GeneralSettings.BigEmoji": "Large Emoji",
"GeneralSettings.EmojiPrediction": "Suggest Emoji",
"GroupPermission.Delete": "Delete Exception",
@ -981,6 +987,23 @@ const lang = { @@ -981,6 +987,23 @@ const lang = {
"Stickers.Recent": "Recent",
//"Stickers.Favorite": "Favorite",
"StickerSet.DontExist": "Sorry, this sticker set doesn't seem to exist.",
"Text.Context.Copy.Username": "Copy Username",
"Text.Context.Copy.Hashtag": "Copy Hashtag",
"Time.TomorrowAt": "tomorrow at %@",
"TwoStepAuth.SetPasswordHelp": "You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.",
"TwoStepAuth.GenericHelp": "You have enabled Two-Step verification.\nYou'll need the password you set up here to log in to your Telegram account.",
"TwoStepAuth.ChangePassword": "Change Password",
"TwoStepAuth.RemovePassword": "Turn Password Off",
"TwoStepAuth.SetupEmail": "Set Recovery Email",
"TwoStepAuth.ChangeEmail": "Change Recovery Email",
"TwoStepAuth.ConfirmEmailCodeDesc": "Please enter the code we've just emailed to %@.",
"TwoStepAuth.RecoveryTitle": "Email Code",
"TwoStepAuth.RecoveryCode": "Code",
"TwoStepAuth.RecoveryCodeInvalid": "Invalid code. Please try again.",
"TwoStepAuth.RecoveryCodeExpired": "Code Expired",
"TwoStepAuth.SetupHintTitle": "Password Hint",
"TwoStepAuth.SetupHintPlaceholder": "Hint",
"UsernameSettings.ChangeDescription": "You can choose a username on Telegram. If you do, people will be able to find you by this username and contact you without needing your phone number.\n\n\nYou can use a-z, 0-9 and underscores. Minimum length is 5 characters.",
"VoiceChat.Chat.StartNew": "Video chat ended. Start a new one?",
"VoiceChat.Chat.StartNew.OK": "Start",
"VoiceChat.Chat.Ended": "Video chat ended.",
@ -1012,24 +1035,7 @@ const lang = { @@ -1012,24 +1035,7 @@ const lang = {
"VoiceChat.UnmuteForMe": "Unmute For Me",
"VoiceChat.RemovePeer.Confirm.Channel": "Do you want to remove %1$@ from the channel?",
"VoiceChat.RemovePeer.Confirm": "Are you sure you want to remove %1$@ from the group?",
"VoiceChat.RemovePeer.Confirm.OK": "Remove",
"Text.Context.Copy.Username": "Copy Username",
"Text.Context.Copy.Hashtag": "Copy Hashtag",
"Time.TomorrowAt": "tomorrow at %@",
"TwoStepAuth.SetPasswordHelp": "You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.",
"TwoStepAuth.GenericHelp": "You have enabled Two-Step verification.\nYou'll need the password you set up here to log in to your Telegram account.",
"TwoStepAuth.ChangePassword": "Change Password",
"TwoStepAuth.RemovePassword": "Turn Password Off",
"TwoStepAuth.SetupEmail": "Set Recovery Email",
"TwoStepAuth.ChangeEmail": "Change Recovery Email",
"TwoStepAuth.ConfirmEmailCodeDesc": "Please enter the code we've just emailed to %@.",
"TwoStepAuth.RecoveryTitle": "Email Code",
"TwoStepAuth.RecoveryCode": "Code",
"TwoStepAuth.RecoveryCodeInvalid": "Invalid code. Please try again.",
"TwoStepAuth.RecoveryCodeExpired": "Code Expired",
"TwoStepAuth.SetupHintTitle": "Password Hint",
"TwoStepAuth.SetupHintPlaceholder": "Hint",
"UsernameSettings.ChangeDescription": "You can choose a username on Telegram. If you do, people will be able to find you by this username and contact you without needing your phone number.\n\n\nYou can use a-z, 0-9 and underscores. Minimum length is 5 characters."
"VoiceChat.RemovePeer.Confirm.OK": "Remove"
};
export default lang;

9
src/lib/appManagers/appChatsManager.ts

@ -770,6 +770,15 @@ export class AppChatsManager { @@ -770,6 +770,15 @@ export class AppChatsManager {
apiUpdatesManager.processUpdateMessage(updates);
});
}
public setChatAvailableReactions(id: ChatId, reactions: Array<string>) {
return apiManager.invokeApi('messages.setChatAvailableReactions', {
peer: this.getInputPeer(id),
available_reactions: reactions
}).then(updates => {
apiUpdatesManager.processUpdateMessage(updates);
});
}
}
const appChatsManager = new AppChatsManager();

57
src/lib/appManagers/appReactionsManager.ts

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
import { MOUNT_CLASS_TO } from "../../config/debug";
import assumeType from "../../helpers/assumeType";
import callbackify from "../../helpers/callbackify";
import { AvailableReaction, MessagesAvailableReactions } from "../../layer";
import apiManager from "../mtproto/mtprotoworker";
import { ReferenceContext } from "../mtproto/referenceDatabase";
@ -37,7 +38,7 @@ export class AppReactionsManager { @@ -37,7 +38,7 @@ export class AppReactionsManager {
}
public getAvailableReactions() {
if(this.availableReactions) return Promise.resolve(this.availableReactions);
if(this.availableReactions) return this.availableReactions;
return apiManager.invokeApiSingleProcess({
method: 'messages.getAvailableReactions',
processResult: (messagesAvailableReactions) => {
@ -62,6 +63,17 @@ export class AppReactionsManager { @@ -62,6 +63,17 @@ export class AppReactionsManager {
});
}
public getActiveAvailableReactions() {
return callbackify(this.getAvailableReactions(), (availableReactions) => {
return availableReactions.filter(availableReaction => !availableReaction.pFlags.inactive);
});
}
public isReactionActive(reaction: string) {
if(!this.availableReactions) return false;
return !!this.availableReactions.find(availableReaction => availableReaction.reaction === reaction);
}
public getQuickReaction() {
return Promise.all([
apiManager.getAppConfig(),
@ -70,6 +82,49 @@ export class AppReactionsManager { @@ -70,6 +82,49 @@ export class AppReactionsManager {
return availableReactions.find(reaction => reaction.reaction === appConfig.reactions_default);
});
}
public getReactionCached(reaction: string) {
return this.availableReactions.find(availableReaction => availableReaction.reaction === reaction);
}
public getReaction(reaction: string) {
return callbackify(this.getAvailableReactions(), () => {
return this.getReactionCached(reaction);
});
}
/* public getMessagesReactions(peerId: PeerId, mids: number[]) {
return apiManager.invokeApiSingleProcess({
method: 'messages.getMessagesReactions',
params: {
id: mids.map(mid => appMessagesIdsManager.getServerMessageId(mid)),
peer: appPeersManager.getInputPeerById(peerId)
},
processResult: (updates) => {
apiUpdatesManager.processUpdateMessage(updates);
// const update = (updates as Updates.updates).updates.find(update => update._ === 'updateMessageReactions') as Update.updateMessageReactions;
// return update.reactions;
}
});
} */
public setDefaultReaction(reaction: string) {
return apiManager.invokeApi('messages.setDefaultReaction', {reaction}).then(value => {
if(value) {
const appConfig = rootScope.appConfig;
if(appConfig) {
appConfig.reactions_default = reaction;
} else { // if no config or loading it - overwrite
apiManager.getAppConfig(true);
}
rootScope.dispatchEvent('quick_reaction', reaction);
}
return value;
});
}
}
const appReactionsManager = new AppReactionsManager();

8
src/lib/appManagers/appStickersManager.ts

@ -162,7 +162,11 @@ export class AppStickersManager { @@ -162,7 +162,11 @@ export class AppStickersManager {
public getAnimatedEmojiSounds(overwrite?: boolean) {
if(this.getAnimatedEmojiSoundsPromise && !overwrite) return this.getAnimatedEmojiSoundsPromise;
return this.getAnimatedEmojiSoundsPromise = apiManager.getAppConfig(overwrite).then(appConfig => {
const promise = this.getAnimatedEmojiSoundsPromise = apiManager.getAppConfig(overwrite).then(appConfig => {
if(this.getAnimatedEmojiSoundsPromise !== promise) {
return;
}
for(const emoji in appConfig.emojies_sounds) {
const sound = appConfig.emojies_sounds[emoji];
const bytesStr = atob(fixBase64String(sound.file_reference_base64, false));
@ -206,6 +210,8 @@ export class AppStickersManager { @@ -206,6 +210,8 @@ export class AppStickersManager {
// TEST_FILE_REFERENCE_REFRESH = false;
// }
});
return promise;
}
public async getRecentStickers(): Promise<Modify<MessagesRecentStickers.messagesRecentStickers, {

8
src/lib/mtproto/mtprotoworker.ts

@ -698,10 +698,16 @@ export class ApiManagerProxy extends CryptoWorkerMethods { @@ -698,10 +698,16 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
public getAppConfig(overwrite?: boolean): Promise<MTAppConfig> {
if(this.getAppConfigPromise && !overwrite) return this.getAppConfigPromise;
return this.getAppConfigPromise = this.invokeApi('help.getAppConfig').then(config => {
const promise = this.getAppConfigPromise = this.invokeApi('help.getAppConfig').then(config => {
if(this.getAppConfigPromise !== promise) {
return;
}
rootScope.appConfig = config;
return config;
});
return promise;
}
}

5
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, ConstructorDeclMap, Config, PollResults, Poll, WebPage, GroupCall, GroupCallParticipant, PhoneCall, MethodDeclMap } from "../layer";
import type { Message, StickerSet, Update, NotifyPeer, PeerNotifySettings, ConstructorDeclMap, Config, PollResults, Poll, WebPage, GroupCall, GroupCallParticipant, PhoneCall, MethodDeclMap, MessageReactions } from "../layer";
import type { MyDocument } from "./appManagers/appDocsManager";
import type { AppMessagesManager, Dialog, MessagesStorage, MyMessage } from "./appManagers/appMessagesManager";
import type { MyDialogFilter } from "./storages/filters";
@ -76,6 +76,7 @@ export type BroadcastEvents = { @@ -76,6 +76,7 @@ export type BroadcastEvents = {
'message_edit': {storage: MessagesStorage, peerId: PeerId, mid: number},
'message_views': {peerId: PeerId, mid: number, views: number},
'message_sent': {storage: MessagesStorage, tempId: number, tempMessage: any, mid: number, message: MyMessage},
'message_reactions': Message.message,
'messages_pending': void,
'messages_read': void,
'messages_downloaded': {peerId: PeerId, mids: number[]},
@ -156,6 +157,8 @@ export type BroadcastEvents = { @@ -156,6 +157,8 @@ export type BroadcastEvents = {
// 'group_call_video_track_added': {instance: GroupCallInstance}
'call_instance': {hasCurrent: boolean, instance: any/* CallInstance */},
'quick_reaction': string
};
export class RootScope extends EventListenerBase<{

Loading…
Cancel
Save