diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts
index 92a577bf..3c730376 100644
--- a/src/components/chat/input.ts
+++ b/src/components/chat/input.ts
@@ -11,7 +11,7 @@ import apiManager from "../../lib/mtproto/mtprotoworker";
import opusDecodeController from "../../lib/opusDecodeController";
import { RichTextProcessor } from "../../lib/richtextprocessor";
import rootScope from '../../lib/rootScope';
-import { cancelEvent, findUpClassName, getRichValue, isInputEmpty, serializeNodes } from "../../helpers/dom";
+import { cancelEvent, findUpClassName, getRichValue, isInputEmpty, placeCaretAtEnd, serializeNodes } from "../../helpers/dom";
import ButtonMenu, { ButtonMenuItemOptions } from '../buttonMenu';
import emoticonsDropdown from "../emoticonsDropdown";
import PopupCreatePoll from "../popupCreatePoll";
@@ -719,6 +719,10 @@ export class ChatInput {
if(input !== undefined) {
this.messageInput.innerHTML = input || '';
+ window.requestAnimationFrame(() => {
+ placeCaretAtEnd(this.messageInput);
+ this.inputScroll.scrollTop = this.inputScroll.scrollHeight;
+ });
}
setTimeout(() => {
diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts
index a69c6f07..f60ae6d3 100644
--- a/src/components/wrappers.ts
+++ b/src/components/wrappers.ts
@@ -581,7 +581,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
} else if('bytes' in thumb) {
img = new Image();
- if((!isSafari || doc.pFlags.stickerThumbConverted || thumb.url)/* && false */) {
+ if((webpWorkerController.isWebpSupported() || doc.pFlags.stickerThumbConverted || thumb.url)/* && false */) {
renderImageFromUrl(img, appPhotosManager.getPreviewURLFromThumb(thumb, true), afterRender);
} else {
webpWorkerController.convert(doc.id, thumb.bytes as Uint8Array).then(bytes => {
@@ -692,6 +692,10 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
img.classList.add('fade-in-transition');
img.style.opacity = '0';
+ if(!div.firstElementChild) {
+ div.append(img);
+ }
+
img.addEventListener('load', () => {
doc.downloaded = true;
diff --git a/src/lib/appManagers/appDocsManager.ts b/src/lib/appManagers/appDocsManager.ts
index 3530f7df..99c47bfa 100644
--- a/src/lib/appManagers/appDocsManager.ts
+++ b/src/lib/appManagers/appDocsManager.ts
@@ -1,10 +1,12 @@
import { FileURLType, getFileNameByLocation, getFileURL } from '../../helpers/fileName';
import { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from '../../helpers/object';
+import { isSafari } from '../../helpers/userAgent';
import { Document, InputFileLocation, PhotoSize } from '../../layer';
import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config';
import referenceDatabase, { ReferenceContext } from '../mtproto/referenceDatabase';
import opusDecodeController from '../opusDecodeController';
import { RichTextProcessor } from '../richtextprocessor';
+import webpWorkerController from '../webp/webpWorkerController';
import appDownloadManager, { DownloadBlob } from './appDownloadManager';
import appPhotosManager from './appPhotosManager';
@@ -99,7 +101,8 @@ class AppDocsManager {
}
}
- if(/* apiDoc.thumbs && */doc.mime_type == 'image/webp') {
+ // * there can be no thumbs, then it is a document
+ if(/* apiDoc.thumbs && */doc.mime_type == 'image/webp' && (doc.thumbs || webpWorkerController.isWebpSupported())) {
doc.type = 'sticker';
doc.sticker = 1;
}
diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts
index 33b4c2ee..a4bfab5d 100644
--- a/src/lib/appManagers/appImManager.ts
+++ b/src/lib/appManagers/appImManager.ts
@@ -732,7 +732,7 @@ export class AppImManager {
//if(target.tagName == 'INPUT') return;
- //this.log('onkeydown', e);
+ this.log('onkeydown', e);
if(e.key == 'Escape') {
/* if(AppMediaViewer.wholeDiv.classList.contains('active')) {
@@ -750,6 +750,31 @@ export class AppImManager {
return;
} else if(e.code == "KeyC" && (e.ctrlKey || e.metaKey) && target.tagName != 'INPUT') {
return;
+ } else if(e.code == 'ArrowUp') {
+ if(!this.chatInputC.editMsgID) {
+ const history = appMessagesManager.historiesStorage[this.peerID];
+ if(history?.history) {
+ let goodMid: number;
+ for(const mid of history.history) {
+ const message = appMessagesManager.getMessage(mid);
+ const good = this.myID == this.peerID ? message.fromID == this.myID : message.pFlags.out;
+
+ if(good) {
+ if(appMessagesManager.canEditMessage(mid, 'text')) {
+ goodMid = mid;
+ }
+
+ break;
+ }
+ }
+
+ if(goodMid) {
+ this.chatInputC.initMessageEditing(goodMid);
+ }
+ }
+ }/* else {
+ this.scrollable.scrollTop -= 20;
+ } */
}
if(e.target != this.chatInputC.messageInput && target.tagName != 'INPUT' && !target.hasAttribute('contenteditable')) {
@@ -1394,7 +1419,11 @@ export class AppImManager {
this.chatInput.style.display = '';
this.chatInput.classList.toggle('is-hidden', !canWrite);
this.bubblesContainer.classList.toggle('is-chat-input-hidden', !canWrite);
- this.chatInputC.messageInput.toggleAttribute('contenteditable', canWrite);
+ if(!canWrite) {
+ this.chatInputC.messageInput.removeAttribute('contenteditable');
+ } else {
+ this.chatInputC.messageInput.setAttribute('contenteditable', 'true');
+ }
// const noTransition = [this.columnEl/* appSidebarRight.sidebarEl, this.backgroundEl,
// this.bubblesContainer, this.chatInput, this.chatInner,
@@ -1765,12 +1794,14 @@ export class AppImManager {
return bubble;
}
+ let messageMedia = message.media;
+
let messageMessage: string, totalEntities: any[];
if(message.grouped_id) {
const t = appMessagesManager.getAlbumText(message.grouped_id);
messageMessage = t.message;
totalEntities = t.totalEntities;
- } else {
+ } else if(messageMedia?.document?.type != 'sticker') {
messageMessage = message.message;
totalEntities = message.totalEntities;
}
@@ -1779,8 +1810,6 @@ export class AppImManager {
entities: totalEntities
});
- let messageMedia = message.media;
-
if(totalEntities && !messageMedia) {
let emojiEntities = totalEntities.filter((e: any) => e._ == 'messageEntityEmoji');
let strLength = messageMessage.length;
diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts
index 092c1b86..966ff3a0 100644
--- a/src/lib/appManagers/appMessagesManager.ts
+++ b/src/lib/appManagers/appMessagesManager.ts
@@ -2142,6 +2142,7 @@ export class AppMessagesManager {
messageText = 'Video message' + (message.message ? ', ' : '') + '';
} else if(document.type == 'sticker') {
messageText = (document.stickerEmoji || '') + 'Sticker';
+ text = '';
} else {
messageText = '' + document.file_name + (message.message ? ', ' : '') + '';
}
@@ -2379,7 +2380,8 @@ export class AppMessagesManager {
return false;
}
- if(this.getMessagePeer(message) == appUsersManager.getSelf().id) {
+ // * second rule for saved messages, because there is no 'out' flag
+ if(message.pFlags.out || this.getMessagePeer(message) == appUsersManager.getSelf().id) {
return true;
}
diff --git a/src/lib/mtproto/apiFileManager.ts b/src/lib/mtproto/apiFileManager.ts
index 03525d63..f5425ea8 100644
--- a/src/lib/mtproto/apiFileManager.ts
+++ b/src/lib/mtproto/apiFileManager.ts
@@ -2,13 +2,13 @@ import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePr
import { notifyAll, notifySomeone } from "../../helpers/context";
import { getFileNameByLocation } from "../../helpers/fileName";
import { nextRandomInt } from "../../helpers/random";
-import { isSafari } from "../../helpers/userAgent";
import { FileLocation, InputFile, InputFileLocation, UploadFile } from "../../layer";
import cacheStorage from "../cacheStorage";
import cryptoWorker from "../crypto/cryptoworker";
import FileManager from "../filemanager";
import { logger, LogLevels } from "../logger";
import apiManager from "./apiManager";
+import { isWebpSupported } from "./mtproto.worker";
import { MOUNT_CLASS_TO } from "./mtproto_config";
@@ -182,7 +182,7 @@ export class ApiFileManager {
let process: ApiFileManager['uncompressTGS'] | ApiFileManager['convertWebp'];
- if(options.mimeType == 'image/webp' && isSafari) {
+ if(options.mimeType == 'image/webp' && !isWebpSupported()) {
process = this.convertWebp;
options.mimeType = 'image/png';
} else if(options.mimeType == 'application/x-tgsticker') {
diff --git a/src/lib/mtproto/mtproto.worker.ts b/src/lib/mtproto/mtproto.worker.ts
index 2bed4f5e..36d37132 100644
--- a/src/lib/mtproto/mtproto.worker.ts
+++ b/src/lib/mtproto/mtproto.worker.ts
@@ -52,6 +52,11 @@ function respond(...args: any[]) {
} */
}
+let webpSupported = false;
+export const isWebpSupported = () => {
+ return webpSupported;
+};
+
networkerFactory.setUpdatesProcessor((obj, bool) => {
respond({update: {obj, bool}});
});
@@ -98,6 +103,9 @@ ctx.addEventListener('message', async(e) => {
respond(responseTask);
return;
+ } else if(task.type == 'webpSupport') {
+ webpSupported = task.payload;
+ return;
}
switch(task.task) {
diff --git a/src/lib/mtproto/mtprotoworker.ts b/src/lib/mtproto/mtprotoworker.ts
index 55cdd048..0ec014f5 100644
--- a/src/lib/mtproto/mtprotoworker.ts
+++ b/src/lib/mtproto/mtprotoworker.ts
@@ -106,6 +106,10 @@ export class ApiManagerProxy extends CryptoWorkerMethods {
this.postMessage = this.worker.postMessage.bind(this.worker);
}
+ const isWebpSupported = webpWorkerController.isWebpSupported();
+ this.log('WebP supported:', isWebpSupported);
+ this.postMessage({type: 'webpSupport', payload: isWebpSupported});
+
this.releasePending();
}
diff --git a/src/lib/richtextprocessor.ts b/src/lib/richtextprocessor.ts
index 242ad1d1..54f9870f 100644
--- a/src/lib/richtextprocessor.ts
+++ b/src/lib/richtextprocessor.ts
@@ -870,7 +870,7 @@ namespace RichTextProcessor {
return text.match(urlRegExp);
}
- const el = document.createElement('span');
+ /* const el = document.createElement('span');
export function getAbbreviation(str: string, onlyFirst = false) {
const wrapped = wrapEmojiText(str);
el.innerHTML = wrapped;
@@ -892,6 +892,18 @@ namespace RichTextProcessor {
}
return first + last;
+ } */
+ export function getAbbreviation(str: string, onlyFirst = false) {
+ const splitted = str.trim().split(' ');
+ if(!splitted[0]) return '';
+
+ const first = [...splitted[0]][0];
+
+ if(onlyFirst || splitted.length == 1) return wrapEmojiText(first);
+
+ const last = [...splitted[splitted.length - 1]][0];
+
+ return wrapEmojiText(first + last);
}
}
diff --git a/src/lib/webp/webpWorkerController.ts b/src/lib/webp/webpWorkerController.ts
index b23e86a3..db7cedf6 100644
--- a/src/lib/webp/webpWorkerController.ts
+++ b/src/lib/webp/webpWorkerController.ts
@@ -14,6 +14,7 @@ export type WebpConvertTask = {
export class WebpWorkerController {
private worker: Worker;
private convertPromises: {[fileName: string]: CancellablePromise} = {};
+ private isWebpSupportedCache: boolean;
init() {
this.worker = new WebpWorker();
@@ -41,6 +42,14 @@ export class WebpWorkerController {
this.worker.postMessage(data);
}
+ isWebpSupported() {
+ if(this.isWebpSupportedCache === undefined) {
+ this.isWebpSupportedCache = document.createElement('canvas').toDataURL('image/webp').startsWith('data:image/webp');
+ }
+
+ return this.isWebpSupportedCache;
+ }
+
convert(fileName: string, bytes: Uint8Array) {
fileName = 'main-' + fileName;
diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss
index ac42943d..adb49656 100644
--- a/src/scss/partials/_chatBubble.scss
+++ b/src/scss/partials/_chatBubble.scss
@@ -1242,6 +1242,10 @@ $bubble-margin: .25rem;
&__media-container {
cursor: pointer;
}
+
+ audio-element, poll-element {
+ white-space: nowrap; // * fix due to .message white-space prewrap
+ }
}
.bubble.service {