Add attach bot to menu

Fix extra newlines with Ctrl+Enter
Fix newlines in Firefox
This commit is contained in:
Eduard Kuzmenko 2023-03-13 18:46:26 +04:00
parent e617a5f547
commit 7b6a714bf8
19 changed files with 224 additions and 55 deletions

View File

@ -1141,7 +1141,7 @@ export default class ChatBubbles {
if(hadRights !== hasRights || hadPlainRights !== hasPlainRights) {
const callbacks = await Promise.all([
this.finishPeerChange(),
this.chat.input.finishPeerChange()
this.chat.input.finishPeerChange({middleware: this.getMiddleware()})
]);
callbacks.forEach((callback) => callback());
@ -3184,10 +3184,17 @@ export default class ChatBubbles {
log.warn('got history');// warning
const {promise, cached} = result;
const finishPeerChangeOptions: Parameters<Chat['finishPeerChange']>[0] = {
isTarget,
isJump,
lastMsgId,
startParam,
middleware
};
if(!cached && !samePeer) {
await m(this.chat.finishPeerChange(isTarget, isJump, lastMsgId, startParam));
this.scrollable.container.textContent = '';
await m(this.chat.finishPeerChange(finishPeerChangeOptions));
this.scrollable.container.replaceChildren();
// oldContainer.textContent = '';
// oldChatInner.remove();
this.preloader.attach(this.container);
@ -3203,7 +3210,7 @@ export default class ChatBubbles {
const mountedByLastMsgId = haveToScrollToBubble ? await m(lastMsgId ? this.getMountedBubble(lastMsgId) : {bubble: this.getLastBubble()}) : undefined;
if(cached && !samePeer) {
log.warn('finishing peer change');
await m(this.chat.finishPeerChange(isTarget, isJump, lastMsgId, startParam)); // * костыль
await m(this.chat.finishPeerChange(finishPeerChangeOptions)); // * костыль
log.warn('finished peer change');
}

View File

@ -580,23 +580,29 @@ export default class Chat extends EventListenerBase<{
});
}
public async finishPeerChange(isTarget: boolean, isJump: boolean, lastMsgId: number, startParam?: string) {
public async finishPeerChange(options: {
isTarget?: boolean,
isJump?: boolean,
lastMsgId?: number,
startParam?: string,
middleware: () => boolean
}) {
if(this.peerChanged) return;
const peerId = this.peerId;
this.peerChanged = true;
this.wasAlreadyUsed = true;
const middleware = this.bubbles.getMiddleware();
const {middleware} = options;
this.cleanup(false);
const sharedMediaTab = this.sharedMediaTab;
const callbacksPromise = Promise.all([
this.topbar.finishPeerChange(isTarget),
this.topbar.finishPeerChange(options),
this.bubbles.finishPeerChange(),
this.input.finishPeerChange(startParam),
this.input.finishPeerChange(options),
sharedMediaTab.fillProfileElements()
]);

View File

@ -648,7 +648,7 @@ export default class ChatInput {
icon: 'gift',
text: 'GiftPremium',
onClick: () => this.chat.appImManager.giftPremium(this.chat.peerId),
verify: async() => await this.chat.canGiftPremium() && (await this.managers.apiManager.getAppConfig()).premium_gift_attach_menu_icon
verify: () => Promise.all([this.chat.canGiftPremium(), this.managers.apiManager.getAppConfig()]).then(([canGift, {premium_gift_attach_menu_icon}]) => canGift && premium_gift_attach_menu_icon)
}, {
icon: 'poll',
text: 'Poll',
@ -676,6 +676,7 @@ export default class ChatInput {
onOpenBefore: async() => {
const attachMenuBots = await this.managers.appAttachMenuBotsManager.getAttachMenuBots();
const buttons = attachMenuButtons.slice();
console.log(attachMenuBots);
const attachMenuBotsButtons = attachMenuBots.filter((attachMenuBot) => {
return !attachMenuBot.pFlags.inactive;
}).map((attachMenuBot) => {
@ -693,8 +694,8 @@ export default class ChatInput {
'allow-same-origin',
'allow-popups',
'allow-forms',
'allow-modals',
'allow-storage-access-by-user-activation'
'allow-modals'
// 'allow-storage-access-by-user-activation'
].join(' ');
class P extends PopupElement<{
@ -1407,13 +1408,24 @@ export default class ChatInput {
return this.sendAs;
}
public async finishPeerChange(startParam?: string) {
public async finishPeerChange(options: Parameters<Chat['finishPeerChange']>[0]) {
const peerId = this.chat.peerId;
const {startParam, middleware} = options;
const {forwardElements, btnScheduled, replyKeyboard, sendMenu, goDownBtn, chatInput, botCommandsToggle} = this;
const {
forwardElements,
btnScheduled,
replyKeyboard,
sendMenu,
goDownBtn,
chatInput,
botCommandsToggle,
attachMenu
} = this;
const previousSendAs = this.sendAs;
const sendAs = this.createSendAs();
const filteredAttachMenuButtons = this.filterAttachMenuButtons();
const [
isBroadcast,
@ -1424,8 +1436,7 @@ export default class ChatInput {
neededFakeContainer,
ackedPeerFull,
ackedScheduledMids,
setSendAsCallback,
filteredAttachMenuButtons
setSendAsCallback
] = await Promise.all([
this.managers.appPeersManager.isBroadcast(peerId),
this.managers.appPeersManager.canPinMessage(peerId),
@ -1435,8 +1446,7 @@ export default class ChatInput {
this.getNeededFakeContainer(startParam),
modifyAckedPromise(this.managers.acknowledged.appProfileManager.getProfileByPeerId(peerId)),
btnScheduled ? modifyAckedPromise(this.managers.acknowledged.appMessagesManager.getScheduledMessages(peerId)) : undefined,
sendAs ? (sendAs.setPeerId(this.chat.peerId), sendAs.updateManual(true)) : undefined,
this.filterAttachMenuButtons()
sendAs ? (sendAs.setPeerId(this.chat.peerId), sendAs.updateManual(true)) : undefined
]);
const placeholderKey = this.messageInput ? await this.getPlaceholderKey(canSendPlain) : undefined;
@ -1466,7 +1476,6 @@ export default class ChatInput {
if(btnScheduled && ackedScheduledMids) {
btnScheduled.classList.add('hide');
const middleware = this.chat.bubbles.getMiddleware();
callbackify(ackedScheduledMids.result, (mids) => {
if(!middleware() || !mids) return;
btnScheduled.classList.toggle('hide', !mids.length);
@ -1483,7 +1492,6 @@ export default class ChatInput {
this.updateBotCommandsToggle(true);
botCommandsToggle.remove();
if(isBot) {
const middleware = this.chat.bubbles.getMiddleware();
const result = ackedPeerFull.result;
callbackify(result, (userFull) => {
if(!middleware()) return;
@ -1498,8 +1506,19 @@ export default class ChatInput {
sendMenu?.setPeerId(peerId);
if(this.messageInput) {
this.updateMessageInput(canSend, canSendPlain, placeholderKey, filteredAttachMenuButtons);
this.updateMessageInput(canSend, canSendPlain, placeholderKey);
this.messageInput.dataset.peerId = '' + peerId;
if(filteredAttachMenuButtons && attachMenu) {
filteredAttachMenuButtons.then((visible) => {
if(!middleware()) {
return;
}
attachMenu.toggleAttribute('disabled', !visible.length);
attachMenu.classList.toggle('btn-disabled', !visible.length);
});
}
}
this.messageInputField?.onFakeInput(undefined, true);
@ -1619,10 +1638,9 @@ export default class ChatInput {
public updateMessageInput(
canSend: boolean,
canSendPlain: boolean,
placeholderKey: LangPackKey,
visible: ChatInput['attachMenuButtons']
placeholderKey: LangPackKey
) {
const {chatInput, attachMenu, messageInput} = this;
const {chatInput, messageInput} = this;
const isHidden = chatInput.classList.contains('is-hidden');
const willBeHidden = !canSend;
if(isHidden !== willBeHidden) {
@ -1657,11 +1675,6 @@ export default class ChatInput {
}
}
if(attachMenu) {
attachMenu.toggleAttribute('disabled', !visible.length);
attachMenu.classList.toggle('btn-disabled', !visible.length);
}
this.updateSendBtn();
}

View File

@ -719,7 +719,7 @@ export default class ChatTopbar {
}
}
public async finishPeerChange(isTarget: boolean) {
public async finishPeerChange(options: Parameters<Chat['finishPeerChange']>[0]) {
const {peerId, threadId} = this.chat;
let newAvatar: AvatarElement;
@ -731,7 +731,15 @@ export default class ChatTopbar {
}
}
const [isBroadcast, isAnyChat, chat, _, setTitleCallback, setStatusCallback, state] = await Promise.all([
const [
isBroadcast,
isAnyChat,
chat,
_,
setTitleCallback,
setStatusCallback,
state
] = await Promise.all([
this.managers.appPeersManager.isBroadcast(peerId),
this.managers.appPeersManager.isAnyChat(peerId),
peerId.isAnyChat() ? this.managers.appChatsManager.getChat(peerId.toChatId()) : undefined,

View File

@ -166,7 +166,7 @@ let init = () => {
let usePlainText = true;
// @ts-ignore
let html: string = (e.originalEvent || e).clipboardData.getData('text/html');
let html: string = (e.originalEvent || e).clipboardData.getData('text/html') || plainText;
const filterEntity = (e: MessageEntity) => e._ === 'messageEntityEmoji' || (e._ === 'messageEntityLinebreak' && !noLinebreaks);
if(noLinebreaks) {

View File

@ -640,7 +640,7 @@ export default class PeerProfile {
return;
}
return await m(this._setMoreDetails(peerId, peerFull));
return m(this._setMoreDetails(peerId, peerFull));
});
if(result.cached && manual) {

View File

@ -313,6 +313,13 @@ export default async function wrapMessageActionTextNewUnsafe(options: WrapMessag
}
case 'messageActionBotAllowed': {
if(action.pFlags?.attach_menu) {
langPackKey = 'ActionAttachMenuBotAllowed';
break;
} else if(!action.domain) {
break;
}
const anchorHTML = wrapRichText(action.domain, {
entities: [{
_: 'messageEntityUrl',

View File

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

View File

@ -212,7 +212,7 @@ export default function getRichElementValue(
const isSelected = selNode === node;
const isBlock = BLOCK_TAGS.has(node.tagName);
if(isBlock && (line.length || node.tagName === 'BR'/* || (BLOCK_TAGS.has(node.tagName) && lines.length) */)) {
if(isBlock && ((line.length && line[line.length - 1].slice(-1) !== '\n') || node.tagName === 'BR'/* || (BLOCK_TAGS.has(node.tagName) && lines.length) */)) {
pushLine();
} else {
const alt = node.dataset.stickerEmoji || (node as HTMLImageElement).alt;

View File

@ -8,7 +8,9 @@
export default function setInnerHTML(elem: Element, html: string | DocumentFragment | Element) {
setDirection(elem);
if(typeof(html) === 'string') {
if(html === undefined) {
elem.replaceChildren();
} else if(typeof(html) === 'string') {
if(!html) elem.replaceChildren();
else elem.textContent = html;
} else {

View File

@ -15,7 +15,8 @@ export function parseUriParamsLine(line: string) {
}
line.split('&').forEach((item) => {
params[item.split('=')[0]] = decodeURIComponent(item.split('=')[1]);
const [key, value = ''] = item.split('=');
params[key] = decodeURIComponent(value);
});
return params;

View File

@ -950,6 +950,11 @@ const lang = {
'ActionGiftPremiumTitle': 'Telegram Premium',
'ActionGiftPremiumSubtitle': 'for %1$s',
'ChatAdmin': 'admin',
'BotRequestAttachPermission': '**%1$s** requests to be added as an option to your attachment menu so you can access it from any chat.',
'BotCantAddToAttachMenu': 'This bot can\'t be added to the attachment menu.',
'BotAlreadyAddedToAttachMenu': 'This bot is already added to your attachment menu.',
'AddBot': 'Add Bot',
'ActionAttachMenuBotAllowed': 'You allowed this bot to message you when you added it to your attachment menu.',
// * macos
'AccountSettings.Filters': 'Chat Folders',

View File

@ -12,6 +12,8 @@ import makeError from '../../helpers/makeError';
import getAttachMenuBotIcon from './utils/attachMenuBots/getAttachMenuBotIcon';
import getServerMessageId from './utils/messageId/getServerMessageId';
const BOTS_SUPPORTED = false;
export default class AppAttachMenuBotsManager extends AppManager {
private attachMenuBots: Map<BotId, AttachMenuBot>;
private attachMenuBotsArr: AttachMenuBot[];
@ -58,8 +60,7 @@ export default class AppAttachMenuBotsManager extends AppManager {
assumeType<AttachMenuBots.attachMenuBots>(attachMenuBots);
this.appUsersManager.saveApiUsers(attachMenuBots.users);
this.saveAttachMenuBots(attachMenuBots.bots);
// ! temporary
return this.attachMenuBotsArr = attachMenuBots.bots.slice(0, 0);
return this.attachMenuBotsArr = attachMenuBots.bots.slice(0, BOTS_SUPPORTED ? undefined : 0);
}
});
}
@ -69,7 +70,7 @@ export default class AppAttachMenuBotsManager extends AppManager {
}
public getAttachMenuBot(botId: BotId, overwrite?: boolean) {
if(!this.appUsersManager.isAttachMenuBot(botId) || true) {
if(!this.appUsersManager.isAttachMenuBot(botId) || !BOTS_SUPPORTED) {
throw makeError('BOT_INVALID');
}
@ -154,4 +155,18 @@ export default class AppAttachMenuBotsManager extends AppManager {
}
});
}
public toggleBotInAttachMenu(botId: BotId, enabled: boolean, writeAllowed?: boolean) {
return this.apiManager.invokeApiSingleProcess({
method: 'messages.toggleBotInAttachMenu',
params: {
bot: this.appUsersManager.getUserInput(botId),
enabled,
write_allowed: writeAllowed
},
processResult: () => {
this.apiUpdatesManager.processLocalUpdate({_: 'updateAttachMenuBots'});
}
});
}
}

View File

@ -108,6 +108,8 @@ import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
import liteMode, {LiteModeKey} from '../../helpers/liteMode';
import RLottiePlayer from '../rlottie/rlottiePlayer';
import PopupGiftPremium from '../../components/popups/giftPremium';
import assumeType from '../../helpers/assumeType';
import noop from '../../helpers/noop';
export type ChatSavedPosition = {
mids: number[],
@ -719,6 +721,10 @@ export class AppImManager extends EventListenerBase<{
});
}
type K1 = {thread?: string, comment?: string, t?: string};
type K2 = {thread?: string, comment?: string, start?: string, t?: string};
type K3 = {startattach?: string, attach?: string, choose?: string};
addAnchorListener<{
// pathnameParams: ['c', string, string],
// uriParams: {thread?: number}
@ -726,7 +732,7 @@ export class AppImManager extends EventListenerBase<{
// pathnameParams: [string, string?],
// uriParams: {comment?: number}
pathnameParams: ['c', string, string] | [string, string?],
uriParams: {thread?: string, comment?: string, t?: string} | {comment?: string, start?: string, t?: string}
uriParams: K1 | K2 | K3
}>({
name: 'im',
callback: async({pathnameParams, uriParams}, element) => {
@ -737,6 +743,7 @@ export class AppImManager extends EventListenerBase<{
phone: pathnameParams[0].slice(1)
};
} else if(pathnameParams[0] === 'c') {
assumeType<K1>(uriParams);
pathnameParams.shift();
const thread = 'thread' in uriParams ? uriParams.thread : pathnameParams[2] && pathnameParams[1];
link = {
@ -749,6 +756,7 @@ export class AppImManager extends EventListenerBase<{
t: uriParams.t
};
} else {
assumeType<K2>(uriParams);
const thread = 'thread' in uriParams ? uriParams.thread : pathnameParams[2] && pathnameParams[1];
link = {
_: INTERNAL_LINK_TYPE.MESSAGE,
@ -762,6 +770,15 @@ export class AppImManager extends EventListenerBase<{
};
}
if('startattach' in uriParams || 'attach' in uriParams) {
assumeType<K3>(uriParams);
link = {
_: INTERNAL_LINK_TYPE.ATTACH_MENU_BOT,
nestedLink: link,
...uriParams
};
}
this.processInternalLink(link);
}
});
@ -787,7 +804,10 @@ export class AppImManager extends EventListenerBase<{
thread?: string,
comment?: string,
phone?: string,
t?: string
t?: string,
attach?: string,
startattach?: string,
choose?: string
}
}>({
name: 'resolve',
@ -805,6 +825,12 @@ export class AppImManager extends EventListenerBase<{
});
}
if(uriParams.attach !== undefined || uriParams.startattach !== undefined) {
const nestedLink = link;
link = this.makeLink(INTERNAL_LINK_TYPE.ATTACH_MENU_BOT, uriParams as Required<typeof uriParams>);
link.nestedLink = nestedLink;
}
this.processInternalLink(link);
}
});
@ -1265,6 +1291,56 @@ export class AppImManager extends EventListenerBase<{
break;
}
case INTERNAL_LINK_TYPE.ATTACH_MENU_BOT: {
console.log(link);
const botUsername = link.attach || link.domain;
const user = await this.managers.appUsersManager.resolveUserByUsername(botUsername).catch(() => undefined as User.user);
if(link.attach !== undefined) {
this.processInternalLink(link.nestedLink);
}
let errorLangPackKey: LangPackKey;
if(!user) {
errorLangPackKey = 'Alert.UserDoesntExists';
} else if(!user.pFlags.bot_attach_menu) {
errorLangPackKey = 'BotCantAddToAttachMenu';
}/* else if(user.pFlags.attach_menu_enabled) {
errorLangPackKey = 'BotAlreadyAddedToAttachMenu';
} */
if(errorLangPackKey) {
toastNew({langPackKey: errorLangPackKey});
break;
}
const peerId = user.id.toPeerId(false);
const attachMenuBot = await this.managers.appAttachMenuBotsManager.getAttachMenuBot(user.id);
console.log(attachMenuBot);
if(attachMenuBot.pFlags.inactive) {
const haveWriteAccess = await confirmationPopup({
button: {
text: 'Add'
},
descriptionLangKey: 'BotRequestAttachPermission',
descriptionLangArgs: [await wrapPeerTitle({peerId})],
checkbox: attachMenuBot.pFlags.request_write_access ? {
text: 'OpenUrlOption2',
textArgs: [await wrapPeerTitle({peerId})],
checked: true
} : undefined,
peerId,
titleLangKey: 'AddBot'
});
await this.managers.appAttachMenuBotsManager.toggleBotInAttachMenu(user.id, true, haveWriteAccess);
}
break;
}
default: {
this.log.warn('Not supported internal link:', link);
break;

View File

@ -49,7 +49,8 @@ export class AppReactionsManager extends AppManager {
this.rootScope.addEventListener('user_auth', () => {
setTimeout(() => {
Promise.resolve(this.getAvailableReactions()).then(async(availableReactions) => {
for(const availableReaction of availableReactions) {
for(let i = 0, length = availableReactions.length; i < length; ++i) {
const availableReaction = availableReactions[i];
await Promise.all([
availableReaction.around_animation && this.apiFileManager.downloadMedia({media: availableReaction.around_animation}),
availableReaction.static_icon && this.apiFileManager.downloadMedia({media: availableReaction.static_icon}),
@ -57,6 +58,10 @@ export class AppReactionsManager extends AppManager {
availableReaction.center_icon && this.apiFileManager.downloadMedia({media: availableReaction.center_icon})
]);
if(i > 15) {
break;
}
await pause(1000);
}
});

View File

@ -28,6 +28,7 @@ import canSendToUser from './utils/users/canSendToUser';
import {AppStoragesManager} from './appStoragesManager';
import deepEqual from '../../helpers/object/deepEqual';
import getPeerActiveUsernames from './utils/peers/getPeerActiveUsernames';
import callbackify from '../../helpers/callbackify';
export type User = MTUser.user;
export type TopPeerType = 'correspondents' | 'bots_inline';
@ -313,6 +314,12 @@ export class AppUsersManager extends AppManager {
});
}
public resolveUserByUsername(username: string) {
return callbackify(this.resolveUsername(username), (peer) => {
return peer?._ === 'user' ? peer : undefined;
});
}
private processResolvedPeer(resolvedPeer: ContactsResolvedPeer.contactsResolvedPeer) {
this.saveApiUsers(resolvedPeer.users);
this.appChatsManager.saveApiChats(resolvedPeer.chats);

View File

@ -60,7 +60,9 @@ import DEBUG from '../../config/debug';
// sentMethods2 = {};
// }, 2000);
const DEBUG_MANAGER_REQUESTS: {[managerName: string]: Set<string>} = {};
const DEBUG_MANAGER_REQUESTS: {[managerName: string]: Set<string>} = {
// appProfileManager: new Set(['getProfile', 'getProfileByPeerId'])
};
if(DEBUG) {
(window as any).DEBUG_MANAGER_REQUESTS = DEBUG_MANAGER_REQUESTS;
}
@ -84,7 +86,7 @@ function createProxy(/* source: T, */name: string, ack?: boolean) {
if(DEBUG) {
if(DEBUG_MANAGER_REQUESTS[name]?.has(p as any)) {
console.warn('manager request', name, p, args);
console.warn('manager request', name, p, args, ack);
}
}

View File

@ -12,10 +12,11 @@ export enum INTERNAL_LINK_TYPE {
VOICE_CHAT,
USER_PHONE_NUMBER,
INVOICE,
EMOJI_SET
EMOJI_SET,
ATTACH_MENU_BOT
};
export type InternalLink = InternalLink.InternalLinkMessage | InternalLink.InternalLinkPrivatePost | InternalLink.InternalLinkStickerSet | InternalLink.InternalLinkJoinChat | InternalLink.InternalLinkVoiceChat | InternalLink.InternalLinkUserPhoneNumber | InternalLink.InternalLinkInvoice | InternalLink.InternalLinkEmojiSet;
export type InternalLink = InternalLink.InternalLinkMessage | InternalLink.InternalLinkPrivatePost | InternalLink.InternalLinkStickerSet | InternalLink.InternalLinkJoinChat | InternalLink.InternalLinkVoiceChat | InternalLink.InternalLinkUserPhoneNumber | InternalLink.InternalLinkInvoice | InternalLink.InternalLinkEmojiSet | InternalLink.InternalLinkAttachMenuBot;
export namespace InternalLink {
export interface InternalLinkMessage {
@ -73,6 +74,15 @@ export namespace InternalLink {
_: INTERNAL_LINK_TYPE.EMOJI_SET,
set: string
}
export interface InternalLinkAttachMenuBot {
_: INTERNAL_LINK_TYPE.ATTACH_MENU_BOT,
startattach?: string,
choose?: string,
attach?: string,
domain?: string,
nestedLink?: InternalLink
}
}
export type InternalLinkTypeMap = {
@ -83,5 +93,6 @@ export type InternalLinkTypeMap = {
[INTERNAL_LINK_TYPE.VOICE_CHAT]: InternalLink.InternalLinkVoiceChat,
[INTERNAL_LINK_TYPE.USER_PHONE_NUMBER]: InternalLink.InternalLinkUserPhoneNumber,
[INTERNAL_LINK_TYPE.INVOICE]: InternalLink.InternalLinkInvoice,
[INTERNAL_LINK_TYPE.EMOJI_SET]: InternalLink.InternalLinkEmojiSet
[INTERNAL_LINK_TYPE.EMOJI_SET]: InternalLink.InternalLinkEmojiSet,
[INTERNAL_LINK_TYPE.ATTACH_MENU_BOT]: InternalLink.InternalLinkAttachMenuBot
};

View File

@ -38,6 +38,7 @@ import replaceContent from '../../helpers/dom/replaceContent';
import BOM from '../../helpers/string/bom';
import framesCache from '../../helpers/framesCache';
import wrapTelegramUrlToAnchor from './wrapTelegramUrlToAnchor';
import {IS_FIREFOX} from '../../environment/userAgent';
const resizeObserver = new ResizeObserver((entries) => {
for(const entry of entries) {
@ -1311,15 +1312,18 @@ export default function wrapRichText(text: string, options: Partial<{
break;
}
// case 'messageEntityLinebreak': {
// if(options.noLinebreaks) {
// insertPart(entity, ' ');
// } else {
// insertPart(entity, '<br/>');
// }
case 'messageEntityLinebreak': {
if(options.wrappingDraft && IS_FIREFOX) {
element = document.createElement('br');
}
// if(options.noLinebreaks) {
// insertPart(entity, ' ');
// } else {
// insertPart(entity, '<br/>');
// }
// break;
// }
break;
}
case 'messageEntityUrl':
case 'messageEntityTextUrl': {