Reactions management
Config, stickers: fix possible race conditions
This commit is contained in:
parent
77d48d764d
commit
a0384f5daf
@ -18,11 +18,16 @@ 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';
|
||||
/* input.id = */input.name = 'input-radio-' + options.name;
|
||||
|
@ -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 {
|
||||
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 {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
this.title.classList.add('row-title');
|
||||
this.title.setAttribute('dir', 'auto');
|
||||
if(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 {
|
||||
}
|
||||
}
|
||||
|
||||
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) => {
|
||||
|
@ -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 {
|
||||
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');
|
||||
|
||||
const renderQuickReaction = () => {
|
||||
appReactionsManager.getQuickReaction().then(reaction => {
|
||||
wrapSticker({
|
||||
div: quickReactionMediaDiv,
|
||||
wrapStickerToRow({
|
||||
row: reactionsRow,
|
||||
doc: reaction.static_icon,
|
||||
width: 32,
|
||||
height: 32
|
||||
size: 'small'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
reactionsRow.container.append(quickReactionMediaDiv);
|
||||
renderQuickReaction();
|
||||
|
||||
this.listenerSetter.add(rootScope)('quick_reaction', renderQuickReaction);
|
||||
|
||||
const suggestCheckboxField = new CheckboxField({
|
||||
text: 'Stickers.SuggestStickers',
|
||||
|
65
src/components/sidebarLeft/tabs/quickReaction.ts
Normal file
65
src/components/sidebarLeft/tabs/quickReaction.ts
Normal file
@ -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
Normal file
100
src/components/sidebarRight/tabs/chatReactions.ts
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
|
||||
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) {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
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
Normal file
9
src/helpers/callbackify.ts
Normal file
@ -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
82
src/lang.ts
@ -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 = {
|
||||
"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 = {
|
||||
},
|
||||
"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 = {
|
||||
"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 = {
|
||||
"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;
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
}
|
||||
|
||||
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 {
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
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();
|
||||
|
@ -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 {
|
||||
// TEST_FILE_REFERENCE_REFRESH = false;
|
||||
// }
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
public async getRecentStickers(): Promise<Modify<MessagesRecentStickers.messagesRecentStickers, {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 = {
|
||||
'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 = {
|
||||
// 'group_call_video_track_added': {instance: GroupCallInstance}
|
||||
|
||||
'call_instance': {hasCurrent: boolean, instance: any/* CallInstance */},
|
||||
|
||||
'quick_reaction': string
|
||||
};
|
||||
|
||||
export class RootScope extends EventListenerBase<{
|
||||
|
Loading…
Reference in New Issue
Block a user