diff --git a/src/components/sidebarRight/sharedMedia.ts b/src/components/sidebarRight/sharedMedia.ts index 52ad4aee..b6d38227 100644 --- a/src/components/sidebarRight/sharedMedia.ts +++ b/src/components/sidebarRight/sharedMedia.ts @@ -8,6 +8,7 @@ import appUsersManager from "../../lib/appManagers/appUsersManager"; import { logger, LogLevels } from "../../lib/logger"; import { RichTextProcessor } from "../../lib/richtextprocessor"; import $rootScope from "../../lib/rootScope"; +import { getAbbreviation, limitSymbols } from "../../lib/utils"; import AvatarElement from "../avatar"; import { horizontalMenu } from "../horizontalMenu"; import LazyLoadQueue from "../lazyLoadQueue"; @@ -108,7 +109,7 @@ export default class AppSharedMediaTab implements SliderTab { private loadMutex: Promise = Promise.resolve(); - private log = logger('SM', LogLevels.error); + private log = logger('SM'/* , LogLevels.error */); public init() { this.container = document.getElementById('shared-media-container'); @@ -226,11 +227,16 @@ export default class AppSharedMediaTab implements SliderTab { container.style.paddingRight = '0'; }; - public filterMessagesByType(ids: number[], type: string) { + public filterMessagesByType(ids: number[], type: SharedMediaType) { let messages: any[] = []; - for(let mid of ids) { - let message = appMessagesManager.getMessage(mid); - if(message.media) messages.push(message); + + if(type != 'inputMessagesFilterUrl') { + for(let mid of ids) { + let message = appMessagesManager.getMessage(mid); + if(message.media) messages.push(message); + } + } else { + messages = ids.slice().map(mid => appMessagesManager.getMessage(mid)); } let filtered: any[] = []; @@ -257,7 +263,7 @@ export default class AppSharedMediaTab implements SliderTab { case 'inputMessagesFilterDocument': { for(let message of messages) { - if(!message.media.document || message.media.document.type == 'voice' || message.media.document.type == 'audio') { + if(!message.media.document || ['voice', 'audio', 'gif'].includes(message.media.document.type)) { continue; } @@ -274,12 +280,11 @@ export default class AppSharedMediaTab implements SliderTab { } case 'inputMessagesFilterUrl': { + this.log('inputMessagesFilterUrl', messages); for(let message of messages) { - if(!message.media.webpage || message.media.webpage._ == 'webPageEmpty') { - continue; - } - - filtered.push(message); + //if((message.media.webpage && message.media.webpage._ != 'webPageEmpty')) { + filtered.push(message); + //} } break; @@ -443,15 +448,63 @@ export default class AppSharedMediaTab implements SliderTab { sharedMediaDiv = this.sharedMedia.contentLinks; for(let message of messages) { - let webpage = message.media.webpage; + let webpage: any; + + if(message.media?.webpage && message.media.webpage._ != 'webPageEmpty') { + webpage = message.media.webpage; + } else { + const entity = message.totalEntities.find((e: any) => e._ == 'messageEntityUrl' || e._ == 'messageEntityTextUrl'); + let url: string, display_url: string, sliced: string; + + if(!entity) { + this.log.error('NO ENTITY:', message); + const match = RichTextProcessor.matchUrl(message.message); + if(!match) { + this.log.error('NO ENTITY AND NO MATCH:', message); + } + + url = match[0]; + } else { + sliced = message.message.slice(entity.offset, entity.offset + entity.length); + } + + if(entity?._ == 'messageEntityTextUrl') { + url = entity.url; + //display_url = sliced; + } else { + url = url || sliced; + } + + display_url = url; + + const same = message.message == url; + if(!url.match(/^(ftp|http|https):\/\//)) { + display_url = 'https://' + url; + url = url.includes('@') ? url : 'https://' + url; + } + + display_url = new URL(display_url).hostname; + + webpage = { + url, + display_url + }; + + if(!same) { + webpage.description = message.message; + webpage.rDescription = RichTextProcessor.wrapRichText(limitSymbols(message.message, 150, 180)); + } + } + let div = document.createElement('div'); + div.dataset.mid = '' + message.mid; let previewDiv = document.createElement('div'); previewDiv.classList.add('preview'); //this.log('wrapping webpage', webpage); - previewDiv.innerText = (webpage.title || webpage.description || webpage.url || webpage.display_url).slice(0, 1); + previewDiv.innerHTML = getAbbreviation(webpage.title || webpage.display_url || webpage.description || webpage.url, true); previewDiv.classList.add('empty'); if(webpage.photo) { let load = () => appPhotosManager.preloadPhoto(webpage.photo.id, appPhotosManager.choosePhotoSize(webpage.photo, 60, 60)) @@ -476,7 +529,11 @@ export default class AppSharedMediaTab implements SliderTab { if(!title) { //title = new URL(webpage.url).hostname; - title = webpage.display_url.split('/', 1)[0]; + title = RichTextProcessor.wrapPlainText(webpage.display_url.split('/', 1)[0]); + } + + if(webpage.description?.includes('Еще в начале')) { + this.log.error('FROM THE START', webpage); } div.append(previewDiv); @@ -566,6 +623,8 @@ export default class AppSharedMediaTab implements SliderTab { const history = historyStorage[type] ?? (historyStorage[type] = []); + const logStr = `loadSidebarMedia [${type}]: `; + // render from cache if(history.length && this.usedFromHistory[type] < history.length) { let messages: any[] = []; @@ -573,7 +632,7 @@ export default class AppSharedMediaTab implements SliderTab { do { let ids = history.slice(used, used + loadCount); - this.log('loadSidebarMedia: will render from cache', used, history, ids, loadCount); + this.log(logStr + 'will render from cache', used, history, ids, loadCount); used += ids.length; messages.push(...this.filterMessagesByType(ids, type)); @@ -597,19 +656,14 @@ export default class AppSharedMediaTab implements SliderTab { // заливать новую картинку сюда только после полной отправки! let maxID = history[history.length - 1] || 0; - let ids = !maxID && appMessagesManager.historiesStorage[peerID] - ? appMessagesManager.historiesStorage[peerID].history.slice() : []; - - maxID = !maxID && ids.length ? ids[ids.length - 1] : maxID; - this.log('loadSidebarMedia: search house of glass pre', type, ids, maxID); + this.log(logStr + 'search house of glass pre', type, maxID); //let loadCount = history.length ? 50 : 15; return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerID, '', {_: type}, maxID, loadCount) .then(value => { - ids = ids.concat(value.history); - history.push(...ids); + history.push(...value.history); - this.log('loadSidebarMedia: search house of glass', type, value, ids); + this.log(logStr + 'search house of glass', type, value); if($rootScope.selectedPeerID != peerID) { this.log.warn('peer changed'); @@ -617,22 +671,25 @@ export default class AppSharedMediaTab implements SliderTab { } if(value.history.length < loadCount) { + this.log(logStr + 'loaded all media', value, loadCount); this.loadedAllMedia[type] = true; } this.usedFromHistory[type] = history.length; - //if(ids.length) { - return this.performSearchResult(this.filterMessagesByType(ids, type), type); + //if(value.history.length) { + return this.performSearchResult(this.filterMessagesByType(value.history, type), type); //} - }, (err) => { + }).catch(err => { this.log.error('load error:', err); - }).then(() => { + }).finally(() => { this.loadSidebarMediaPromises[type] = null; }); }); - return Promise.all(promises); + return Promise.all(promises).catch(err => { + this.log.error('Load error all promises:', err); + }); } public cleanup() { diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 63df357b..0964a393 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -308,7 +308,7 @@ export function wrapDocument(doc: MyDocument, withTime = false, uploading = fals const icoDiv = document.createElement('div'); icoDiv.classList.add('document-ico'); - if(doc.type == 'photo') { + if(doc.thumbs?.length || uploading) { docDiv.classList.add('photo'); if(uploading) { diff --git a/src/lib/appManagers/apiUpdatesManager.ts b/src/lib/appManagers/apiUpdatesManager.ts index 7b564095..8769b004 100644 --- a/src/lib/appManagers/apiUpdatesManager.ts +++ b/src/lib/appManagers/apiUpdatesManager.ts @@ -168,7 +168,7 @@ export class ApiUpdatesManager { date: updateMessage.date, message: updateMessage.message, fwd_from: updateMessage.fwd_from, - reply_to_msg_id: updateMessage.reply_to_msg_id, + reply_to: updateMessage.reply_to, entities: updateMessage.entities }, pts: updateMessage.pts, diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 8fd30a98..1252ac69 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -969,7 +969,7 @@ export class AppDialogsManager { } let messageWrapped = RichTextProcessor.wrapRichText(messageText, { - noLinebreakers: true, + noLinebreaks: true, entities: entities, noTextFormat: true }); @@ -995,7 +995,7 @@ export class AppDialogsManager { } //senderBold.innerText = str + ': '; - senderBold.innerHTML = RichTextProcessor.wrapRichText(str, {noLinebreakers: true}) + ': '; + senderBold.innerHTML = RichTextProcessor.wrapRichText(str, {noLinebreaks: true}) + ': '; //console.log(sender, senderBold.innerText); dom.lastMessageSpan.prepend(senderBold); } //////// else console.log('no sender', lastMessage, peerID); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 64db7b45..0b74d035 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -1,4 +1,4 @@ -import { copy, tsNow, safeReplaceObject, listMergeSorted, deepEqual, langPack, getObjectKeysAndSort } from "../utils"; +import { copy, tsNow, safeReplaceObject, listMergeSorted, deepEqual, langPack, getObjectKeysAndSort, limitSymbols } from "../utils"; import appMessagesIDsManager from "./appMessagesIDsManager"; import appChatsManager from "./appChatsManager"; import appUsersManager from "./appUsersManager"; @@ -822,7 +822,7 @@ export class AppMessagesManager { date: tsNow(true) + serverTimeManager.serverTimeOffset, message: text, random_id: randomIDS, - reply_to_msg_id: replyToMsgID, + reply_to: {reply_to_msg_id: replyToMsgID}, via_bot_id: options.viaBotID, reply_markup: options.reply_markup, entities: entities, @@ -1184,7 +1184,7 @@ export class AppMessagesManager { document: file } : media, random_id: randomIDS, - reply_to_msg_id: replyToMsgID, + reply_to: {reply_to_msg_id: replyToMsgID}, views: asChannel && 1, pending: true }; @@ -1459,7 +1459,7 @@ export class AppMessagesManager { media: media, random_id: randomIDS, randomID: randomID, - reply_to_msg_id: replyToMsgID, + reply_to: {reply_to_msg_id: replyToMsgID}, views: asChannel && 1, pending: true, error: false @@ -1752,7 +1752,7 @@ export class AppMessagesManager { message: '', media: media, random_id: randomIDS, - reply_to_msg_id: replyToMsgID, + reply_to: {reply_to_msg_id: replyToMsgID}, via_bot_id: options.viaBotID, reply_markup: options.reply_markup, views: asChannel && 1, @@ -2132,7 +2132,8 @@ export class AppMessagesManager { return this.messagesStorage[messageID] || { _: 'messageEmpty', id: messageID, - deleted: true + deleted: true, + pFlags: {} }; } @@ -2308,8 +2309,8 @@ export class AppMessagesManager { } // this.log(dT(), 'msg unread', mid, apiMessage.pFlags.out, dialog && dialog[apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) - if(apiMessage.reply_to_msg_id) { - apiMessage.reply_to_mid = appMessagesIDsManager.getFullMessageID(apiMessage.reply_to_msg_id, channelID); + if(apiMessage.reply_to && apiMessage.reply_to.reply_to_msg_id) { + apiMessage.reply_to_mid = appMessagesIDsManager.getFullMessageID(apiMessage.reply_to.reply_to_msg_id, channelID); } apiMessage.date -= serverTimeManager.serverTimeOffset; @@ -2575,14 +2576,12 @@ export class AppMessagesManager { let messageWrapped = ''; if(text) { // * 80 for chatlist in landscape orientation - if(text.length > 80) { - text = text.substr(0, 75) + '...'; - } + text = limitSymbols(text, 75, 80); let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '), {noLinebreakers: true}); messageWrapped = RichTextProcessor.wrapRichText(text, { - noLinebreakers: true, + noLinebreaks: true, entities: entities, noTextFormat: true }); @@ -2827,6 +2826,10 @@ export class AppMessagesManager { this.saveMessages([message]); } + if(!message?.pFlags) { + this.log.error('saveConversation no message:', dialog, message); + } + if(!channelID && peerID < 0) { const chat = appChatsManager.getChat(-peerID); if(chat && chat.migrated_to && chat.pFlags.deactivated) { @@ -2886,7 +2889,7 @@ export class AppMessagesManager { public mergeReplyKeyboard(historyStorage: HistoryStorage, message: any) { // this.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup) if(!message.reply_markup && - !message.pFlags.out && + !message.pFlags?.out && !message.action) { return false; } @@ -2966,10 +2969,10 @@ export class AppMessagesManager { history: number[] }> { //peerID = peerID ? parseInt(peerID) : 0; - var foundMsgs: number[] = []; - var useSearchCache = !query; - var newSearchFilter = {peer: peerID, filter: inputFilter}; - var sameSearchCache = useSearchCache && deepEqual(this.lastSearchFilter, newSearchFilter); + const foundMsgs: number[] = []; + const useSearchCache = !query; + const newSearchFilter = {peer: peerID, filter: inputFilter}; + const sameSearchCache = useSearchCache && deepEqual(this.lastSearchFilter, newSearchFilter); if(useSearchCache && !sameSearchCache) { // this.log.warn(dT(), 'new search filter', lastSearchFilter, newSearchFilter) @@ -2987,7 +2990,6 @@ export class AppMessagesManager { [messageMediaType: string]: boolean } = {}, neededDocTypes: string[] = []; - var message; switch(inputFilter._) { case 'inputMessagesFilterPhotos': @@ -3033,9 +3035,9 @@ export class AppMessagesManager { neededContents['url'] = true; break; - case 'inputMessagesFilterMyMentions': + /* case 'inputMessagesFilterMyMentions': neededContents['mentioned'] = true; - break; + break; */ default: return Promise.resolve({ @@ -3045,8 +3047,12 @@ export class AppMessagesManager { }); } - for(let i = 0; i < historyStorage.history.length; i++) { - message = this.messagesStorage[historyStorage.history[i]]; + for(let i = 0, length = historyStorage.history.length; i < length; i++) { + const message = this.messagesStorage[historyStorage.history[i]]; + + //|| (neededContents['mentioned'] && message.totalEntities.find((e: any) => e._ == 'messageEntityMention')); + + let found = false; if(message.media && neededContents[message.media._]) { if(neededDocTypes.length && message.media._ == 'messageMediaDocument' && @@ -3054,6 +3060,12 @@ export class AppMessagesManager { continue; } + found = true; + } else if(neededContents['url'] && message.message && RichTextProcessor.matchUrl(message.message)) { + found = true; + } + + if(found) { foundMsgs.push(message.mid); if(foundMsgs.length >= limit) { break; @@ -3078,15 +3090,20 @@ export class AppMessagesManager { } if(foundMsgs.length) { - if(useSearchCache) { - this.lastSearchResults = listMergeSorted(this.lastSearchResults, foundMsgs) + if(foundMsgs.length < limit) { + maxID = foundMsgs[foundMsgs.length - 1]; + limit = limit - foundMsgs.length; + } else { + if(useSearchCache) { + this.lastSearchResults = listMergeSorted(this.lastSearchResults, foundMsgs) + } + + return Promise.resolve({ + count: 0, + next_rate: 0, + history: foundMsgs + }); } - - return Promise.resolve({ - count: 0, - next_rate: 0, - history: foundMsgs - }); } let apiPromise: Promise; @@ -3141,15 +3158,14 @@ export class AppMessagesManager { appChatsManager.saveApiChats(searchResult.chats); this.saveMessages(searchResult.messages); - ///////////this.log('messages.search result:', searchResult); + this.log('messages.search result:', inputFilter, searchResult); - var foundCount: number = searchResult.count || searchResult.messages.length; + const foundCount: number = searchResult.count || (foundMsgs.length + searchResult.messages.length); - foundMsgs = []; searchResult.messages.forEach((message: any) => { - var peerID = this.getMessagePeer(message); + const peerID = this.getMessagePeer(message); if(peerID < 0) { - var chat = appChatsManager.getChat(-peerID); + const chat = appChatsManager.getChat(-peerID); if(chat.migrated_to) { this.migrateChecks(peerID, -chat.migrated_to.channel_id); } @@ -3413,7 +3429,7 @@ export class AppMessagesManager { } public handleUpdate(update: any) { - this.log('AMM: handleUpdate:', update._); + this.log.debug('AMM: handleUpdate:', update._); switch(update._) { case 'updateMessageID': { var randomID = update.random_id; diff --git a/src/lib/appManagers/appStateManager.ts b/src/lib/appManagers/appStateManager.ts index 02e9fe2f..f1615adb 100644 --- a/src/lib/appManagers/appStateManager.ts +++ b/src/lib/appManagers/appStateManager.ts @@ -1,7 +1,7 @@ import type { Dialog, DialogsStorage, FiltersStorage } from './appMessagesManager'; import type { AppStickersManager } from './appStickersManager'; import type { AppPeersManager } from './appPeersManager'; -import { App, MOUNT_CLASS_TO } from '../mtproto/mtproto_config'; +import { App, MOUNT_CLASS_TO, UserAuth } from '../mtproto/mtproto_config'; import EventListenerBase from '../../helpers/eventListenerBase'; import $rootScope from '../rootScope'; import AppStorage from '../storage'; @@ -45,14 +45,14 @@ export class AppStateManager extends EventListenerBase<{ public loadSavedState() { if(this.loaded) return this.loaded; - console.time('load state'); + //console.time('load state'); return this.loaded = new Promise((resolve) => { - AppStorage.get<[State, {id: number}]>('state', 'user_auth').then(([state, auth]) => { + AppStorage.get<[State, UserAuth]>('state', 'user_auth').then(([state, auth]) => { const time = Date.now(); if(state) { - if(state?.version != STATE_VERSION) { + if(state.version != STATE_VERSION) { state = {}; - } else if((state?.stateCreatedTime ?? 0) + REFRESH_EVERY < time) { + } else if((state.stateCreatedTime || 0) + REFRESH_EVERY < time) { this.log('will refresh state', state.stateCreatedTime, time); REFRESH_KEYS.forEach(key => { delete state[key]; @@ -62,7 +62,7 @@ export class AppStateManager extends EventListenerBase<{ } // will not throw error because state can be `FALSE` - const {peers, updates} = state; + const {peers} = state; this.state = state || {}; this.state.peers = peers || {}; @@ -81,7 +81,7 @@ export class AppStateManager extends EventListenerBase<{ $rootScope.$broadcast('user_auth', {id: auth.id}); } - console.timeEnd('load state'); + //console.timeEnd('load state'); resolve(state); }).catch(resolve).finally(() => { setInterval(() => this.saveState(), 10000); diff --git a/src/lib/appManagers/appWebPagesManager.ts b/src/lib/appManagers/appWebPagesManager.ts index 3a36b935..62b09059 100644 --- a/src/lib/appManagers/appWebPagesManager.ts +++ b/src/lib/appManagers/appWebPagesManager.ts @@ -1,4 +1,4 @@ -import { safeReplaceObject } from "../utils"; +import { limitSymbols, safeReplaceObject } from "../utils"; import appPhotosManager from "./appPhotosManager"; import appDocsManager from "./appDocsManager"; import { RichTextProcessor } from "../richtextprocessor"; @@ -59,10 +59,7 @@ class AppWebPagesManager { } // delete apiWebPage.description - var shortDescriptionText = (apiWebPage.description || ''); - if(shortDescriptionText.length > 180) { - shortDescriptionText = shortDescriptionText.substr(0, 150).replace(/(\n|\s)+$/, '') + '...'; - } + var shortDescriptionText = limitSymbols(apiWebPage.description || '', 150, 180); apiWebPage.rDescription = RichTextProcessor.wrapRichText(shortDescriptionText, { contextSite: siteName || 'external', contextHashtag: contextHashtag diff --git a/src/lib/mtproto/tl_utils.ts b/src/lib/mtproto/tl_utils.ts index ad781863..278b98be 100644 --- a/src/lib/mtproto/tl_utils.ts +++ b/src/lib/mtproto/tl_utils.ts @@ -204,7 +204,9 @@ class TLSerialization { var len = bytes.length; if((bits % 32) || (len * 8) != bits) { - throw new Error('Invalid bits: ' + bits + ', ' + bytes.length); + const error = new Error('Invalid bits: ' + bits + ', ' + bytes.length); + console.error(error, bytes, field); + throw error; } this.debug && console.log('>>>', bytesToHex(bytes), (field || '') + ':int' + bits); diff --git a/src/lib/richtextprocessor.ts b/src/lib/richtextprocessor.ts index ffdc3d19..b82335d3 100644 --- a/src/lib/richtextprocessor.ts +++ b/src/lib/richtextprocessor.ts @@ -271,7 +271,7 @@ function parseMarkdown(text: string, entities: any[], noTrim?: any) { } return newText } -function mergeEntities (currentEntities: any[], newEntities: any[], fromApi: any) { +function mergeEntities(currentEntities: any[], newEntities: any[], fromApi: any) { var totalEntities = newEntities.slice(); var i; var len = currentEntities.length; @@ -337,14 +337,26 @@ function mergeEntities (currentEntities: any[], newEntities: any[], fromApi: any // console.log('merge', currentEntities, newEntities, totalEntities) return totalEntities; } -function wrapRichNestedText (text: string, nested: any, options: any) { - if (nested === undefined) { - return encodeEntities(text) +function wrapRichNestedText(text: string, nested: any, options: any) { + if(nested === undefined) { + return encodeEntities(text); } - options.hasNested = true - return wrapRichText(text, {entities: nested, nested: true}) + + options.hasNested = true; + return wrapRichText(text, {entities: nested, nested: true}); } -function wrapRichText (text: string, options: any = {}) { +function wrapRichText(text: string, options: Partial<{ + entities: any, + contextSite: string, + highlightUsername: string, + noLinks: boolean, + noLinebreaks: boolean, + noCommands: boolean, + fromBot: boolean, + noTextFormat: boolean, + nested?: boolean, + contextHashtag?: string +}> = {}) { if(!text || !text.length) { return '' } @@ -713,7 +725,7 @@ function wrapEmojiText(text: string) { let entities = parseEntities(text).filter(e => e._ == 'messageEntityEmoji'); return wrapRichText(text, {entities}); } -function wrapUrl(url: string, unsafe: any): string { +function wrapUrl(url: string, unsafe: number | boolean): string { if(!url.match(/^https?:\/\//i)) { url = 'http://' + url; } @@ -765,18 +777,23 @@ function wrapUrl(url: string, unsafe: any): string { return url; } +function matchUrl(text: string) { + return text.match(urlRegExp); +} + let RichTextProcessor = { - wrapRichText: wrapRichText, - wrapPlainText: wrapPlainText, - wrapDraftText: wrapDraftText, - wrapUrl: wrapUrl, - wrapEmojiText: wrapEmojiText, - parseEntities: parseEntities, - parseMarkdown: parseMarkdown, - parseEmojis: parseEmojis, - mergeEntities: mergeEntities, - getEmojiSpritesheetCoords: getEmojiSpritesheetCoords, - emojiSupported: emojiSupported + wrapRichText, + wrapPlainText, + wrapDraftText, + wrapUrl, + wrapEmojiText, + parseEntities, + parseMarkdown, + parseEmojis, + mergeEntities, + getEmojiSpritesheetCoords, + emojiSupported, + matchUrl }; MOUNT_CLASS_TO && (MOUNT_CLASS_TO.RichTextProcessor = RichTextProcessor); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 5bc00a7c..f818d81a 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -235,7 +235,7 @@ export function tsNow(seconds?: boolean) { } const el = document.createElement('span'); -export function getAbbreviation(str: string) { +export function getAbbreviation(str: string, onlyFirst = false) { const wrapped = RichTextProcessor.wrapEmojiText(str); el.innerHTML = wrapped; @@ -246,6 +246,8 @@ export function getAbbreviation(str: string) { if('length' in firstNode) first = (firstNode as any).textContent.charAt(0).toUpperCase(); else first = (firstNode as HTMLElement).outerHTML; + if(onlyFirst) return first; + if(str.indexOf(' ') !== -1) { const lastNode = childNodes[childNodes.length - 1]; if(lastNode == firstNode) last = lastNode.textContent.split(' ').pop().charAt(0).toUpperCase(); @@ -292,6 +294,14 @@ export function safeReplaceArrayInObject(key: K, wasObject: any, newObject: a } } +export function limitSymbols(str: string, length: number, limitFrom = length + 10) { + if(str.length > limitFrom) { + str = str.slice(0, length).replace(/(\n|\s)+$/, '') + '...'; + } + + return str; +} + export function numberWithCommas(x: number) { var parts = x.toString().split("."); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");