parent
3a04f3b2c9
commit
68355c71b9
|
@ -127,7 +127,7 @@ import {BatchProcessor} from '../../helpers/sortedList';
|
|||
import wrapUrl from '../../lib/richTextProcessor/wrapUrl';
|
||||
import getMessageThreadId from '../../lib/appManagers/utils/messages/getMessageThreadId';
|
||||
import wrapTopicNameButton from '../wrappers/topicNameButton';
|
||||
import wrapMediaSpoiler from '../wrappers/mediaSpoiler';
|
||||
import wrapMediaSpoiler, {toggleMediaSpoiler} from '../wrappers/mediaSpoiler';
|
||||
import confirmationPopup from '../confirmationPopup';
|
||||
import wrapPeerTitle from '../wrappers/peerTitle';
|
||||
import {PopupPeerCheckboxOptions} from '../popups/peer';
|
||||
|
@ -1654,16 +1654,12 @@ export default class ChatBubbles {
|
|||
video.play().catch(noop);
|
||||
}
|
||||
|
||||
SetTransition({
|
||||
element: mediaSpoiler,
|
||||
forwards: true,
|
||||
className: 'is-revealing',
|
||||
duration: 250,
|
||||
onTransitionEnd: () => {
|
||||
mediaSpoiler.remove();
|
||||
mediaSpoiler.middlewareHelper.destroy();
|
||||
}
|
||||
toggleMediaSpoiler({
|
||||
mediaSpoiler,
|
||||
reveal: true,
|
||||
destroyAfter: true
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -944,7 +944,8 @@ export default class ChatInput {
|
|||
opusDecodeController.setKeepAlive(false);
|
||||
|
||||
// тут objectURL ставится уже с audio/wav
|
||||
this.managers.appMessagesManager.sendFile(peerId, dataBlob, {
|
||||
this.managers.appMessagesManager.sendFile(peerId, {
|
||||
file: dataBlob,
|
||||
isVoiceMessage: true,
|
||||
isMedia: true,
|
||||
duration,
|
||||
|
@ -2769,10 +2770,11 @@ export default class ChatInput {
|
|||
return false;
|
||||
}
|
||||
|
||||
this.managers.appMessagesManager.sendFile(this.chat.peerId, document, {
|
||||
this.managers.appMessagesManager.sendFile(this.chat.peerId, {
|
||||
...this.chat.getMessageSendingParams(),
|
||||
file: document,
|
||||
isMedia: true,
|
||||
clearDraft: clearDraft || undefined,
|
||||
clearDraft,
|
||||
silent
|
||||
});
|
||||
this.onMessageSent(clearDraft, true);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import type Chat from '../chat/chat';
|
||||
import type {SendFileDetails} from '../../lib/appManagers/appMessagesManager';
|
||||
import InputField from '../inputField';
|
||||
import PopupElement from '.';
|
||||
import Scrollable from '../scrollable';
|
||||
|
@ -22,27 +23,33 @@ import getGifDuration from '../../helpers/getGifDuration';
|
|||
import replaceContent from '../../helpers/dom/replaceContent';
|
||||
import createVideo from '../../helpers/dom/createVideo';
|
||||
import prepareAlbum from '../prepareAlbum';
|
||||
import {MediaSize} from '../../helpers/mediaSize';
|
||||
import {makeMediaSize, MediaSize} from '../../helpers/mediaSize';
|
||||
import {ThumbCache} from '../../lib/storages/thumbs';
|
||||
import onMediaLoad from '../../helpers/onMediaLoad';
|
||||
import apiManagerProxy from '../../lib/mtproto/mtprotoworker';
|
||||
import {THUMB_TYPE_FULL} from '../../lib/mtproto/mtproto_config';
|
||||
import wrapDocument from '../wrappers/document';
|
||||
import createContextMenu from '../../helpers/dom/createContextMenu';
|
||||
import findUpClassName from '../../helpers/dom/findUpClassName';
|
||||
import wrapMediaSpoiler, {toggleMediaSpoiler} from '../wrappers/mediaSpoiler';
|
||||
import {MiddlewareHelper} from '../../helpers/middleware';
|
||||
import {AnimationItemGroup} from '../animationIntersector';
|
||||
import scaleMediaElement from '../../helpers/canvas/scaleMediaElement';
|
||||
import {doubleRaf} from '../../helpers/schedulers';
|
||||
import defineNotNumerableProperties from '../../helpers/object/defineNotNumerableProperties';
|
||||
import {Photo, PhotoSize} from '../../layer';
|
||||
import {getPreviewBytesFromURL} from '../../helpers/bytes/getPreviewURLFromBytes';
|
||||
import {renderImageFromUrlPromise} from '../../helpers/dom/renderImageFromUrl';
|
||||
|
||||
type SendFileParams = Partial<{
|
||||
file: File,
|
||||
objectURL: string,
|
||||
thumb: {
|
||||
blob: Blob,
|
||||
url: string,
|
||||
size: MediaSize
|
||||
},
|
||||
width: number,
|
||||
height: number,
|
||||
duration: number,
|
||||
noSound: boolean,
|
||||
itemDiv: HTMLElement
|
||||
}>;
|
||||
type SendFileParams = SendFileDetails & {
|
||||
file?: File,
|
||||
scaledBlob?: Blob,
|
||||
noSound?: boolean,
|
||||
itemDiv: HTMLElement,
|
||||
mediaSpoiler?: HTMLElement,
|
||||
middlewareHelper: MiddlewareHelper
|
||||
// strippedBytes?: PhotoSize.photoStrippedSize['bytes']
|
||||
};
|
||||
|
||||
let currentPopup: PopupNewMedia;
|
||||
|
||||
|
@ -66,8 +73,22 @@ export default class PopupNewMedia extends PopupElement {
|
|||
private inputField: InputField;
|
||||
private captionLengthMax: number;
|
||||
|
||||
constructor(private chat: Chat, private files: File[], willAttachType: PopupNewMedia['willAttach']['type']) {
|
||||
super('popup-send-photo popup-new-media', {closable: true, withConfirm: 'Modal.Send', confirmShortcutIsSendShortcut: true, body: true, title: true});
|
||||
private animationGroup: AnimationItemGroup;
|
||||
|
||||
constructor(
|
||||
private chat: Chat,
|
||||
private files: File[],
|
||||
willAttachType: PopupNewMedia['willAttach']['type']
|
||||
) {
|
||||
super('popup-send-photo popup-new-media', {
|
||||
closable: true,
|
||||
withConfirm: 'Modal.Send',
|
||||
confirmShortcutIsSendShortcut: true,
|
||||
body: true,
|
||||
title: true
|
||||
});
|
||||
|
||||
this.animationGroup = '';
|
||||
this.construct(willAttachType);
|
||||
}
|
||||
|
||||
|
@ -78,8 +99,8 @@ export default class PopupNewMedia extends PopupElement {
|
|||
group: false
|
||||
};
|
||||
|
||||
const config = await this.managers.apiManager.getConfig();
|
||||
this.captionLengthMax = config.caption_length_max;
|
||||
const captionMaxLength = await this.managers.apiManager.getLimit('caption');
|
||||
this.captionLengthMax = captionMaxLength;
|
||||
|
||||
attachClickEvent(this.btnConfirm, () => this.send(), {listenerSetter: this.listenerSetter});
|
||||
|
||||
|
@ -127,13 +148,126 @@ export default class PopupNewMedia extends PopupElement {
|
|||
this.attachFiles();
|
||||
|
||||
this.addEventListener('close', () => {
|
||||
this.files = [];
|
||||
currentPopup = undefined;
|
||||
this.files.length = 0;
|
||||
this.willAttach.sendFileDetails.length = 0;
|
||||
|
||||
if(currentPopup === this) {
|
||||
currentPopup = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
let target: HTMLElement, isMedia: boolean, item: SendFileParams;
|
||||
createContextMenu({
|
||||
buttons: [{
|
||||
icon: 'spoiler',
|
||||
text: 'EnablePhotoSpoiler',
|
||||
onClick: () => {
|
||||
this.applyMediaSpoiler(item);
|
||||
},
|
||||
verify: () => isMedia && !item.mediaSpoiler
|
||||
}, {
|
||||
icon: 'spoiler',
|
||||
text: 'DisablePhotoSpoiler',
|
||||
onClick: () => {
|
||||
toggleMediaSpoiler({
|
||||
mediaSpoiler: item.mediaSpoiler,
|
||||
reveal: true,
|
||||
destroyAfter: true
|
||||
});
|
||||
|
||||
item.mediaSpoiler = undefined;
|
||||
},
|
||||
verify: () => !!(isMedia && item.mediaSpoiler)
|
||||
}],
|
||||
listenTo: this.mediaContainer,
|
||||
listenerSetter: this.listenerSetter,
|
||||
findElement: (e) => {
|
||||
target = findUpClassName(e.target, 'popup-item');
|
||||
isMedia = target.classList.contains('popup-item-media');
|
||||
item = this.willAttach.sendFileDetails.find((i) => i.itemDiv === target);
|
||||
return target;
|
||||
}
|
||||
});
|
||||
|
||||
currentPopup = this;
|
||||
}
|
||||
|
||||
private async applyMediaSpoiler(item: SendFileParams, noAnimation?: boolean) {
|
||||
const middleware = item.middlewareHelper.get();
|
||||
const {width: widthStr, height: heightStr} = item.itemDiv.style;
|
||||
|
||||
let width: number, height: number;
|
||||
if(item.itemDiv.classList.contains('album-item')) {
|
||||
const {width: containerWidthStr, height: containerHeightStr} = item.itemDiv.parentElement.style;
|
||||
const containerWidth = parseInt(containerWidthStr);
|
||||
const containerHeight = parseInt(containerHeightStr);
|
||||
|
||||
width = +widthStr.slice(0, -1) / 100 * containerWidth;
|
||||
height = +heightStr.slice(0, -1) / 100 * containerHeight;
|
||||
} else {
|
||||
width = parseInt(widthStr);
|
||||
height = parseInt(heightStr);
|
||||
}
|
||||
|
||||
const {url} = await scaleMediaElement({
|
||||
media: item.itemDiv.firstElementChild as HTMLImageElement,
|
||||
boxSize: makeMediaSize(40, 40),
|
||||
mediaSize: makeMediaSize(width, height),
|
||||
toDataURL: true,
|
||||
quality: 0.2
|
||||
});
|
||||
|
||||
const strippedBytes = getPreviewBytesFromURL(url);
|
||||
const photoSize: PhotoSize.photoStrippedSize = {
|
||||
_: 'photoStrippedSize',
|
||||
bytes: strippedBytes,
|
||||
type: 'i'
|
||||
};
|
||||
|
||||
item.strippedBytes = strippedBytes;
|
||||
|
||||
const photo: Photo.photo = {
|
||||
_: 'photo',
|
||||
sizes: [
|
||||
photoSize
|
||||
],
|
||||
id: 0,
|
||||
access_hash: 0,
|
||||
date: 0,
|
||||
dc_id: 0,
|
||||
file_reference: []
|
||||
};
|
||||
|
||||
const mediaSpoiler = await wrapMediaSpoiler({
|
||||
middleware,
|
||||
width,
|
||||
height,
|
||||
animationGroup: this.animationGroup,
|
||||
media: photo
|
||||
});
|
||||
|
||||
if(!middleware()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!noAnimation) {
|
||||
mediaSpoiler.classList.add('is-revealing');
|
||||
}
|
||||
|
||||
item.mediaSpoiler = mediaSpoiler;
|
||||
item.itemDiv.append(mediaSpoiler);
|
||||
|
||||
await doubleRaf();
|
||||
if(!middleware()) {
|
||||
return;
|
||||
}
|
||||
|
||||
toggleMediaSpoiler({
|
||||
mediaSpoiler,
|
||||
reveal: false
|
||||
});
|
||||
}
|
||||
|
||||
public appendDrops(element: HTMLElement) {
|
||||
this.body.append(element);
|
||||
}
|
||||
|
@ -153,7 +287,11 @@ export default class PopupNewMedia extends PopupElement {
|
|||
text: 'PreviewSender.GroupItems',
|
||||
name: 'group-items'
|
||||
});
|
||||
this.container.append(...[this.groupCheckboxField.label, this.mediaCheckboxField?.label, this.inputField.container].filter(Boolean));
|
||||
this.container.append(...[
|
||||
this.groupCheckboxField.label,
|
||||
this.mediaCheckboxField?.label,
|
||||
this.inputField.container
|
||||
].filter(Boolean));
|
||||
|
||||
this.willAttach.group = true;
|
||||
this.groupCheckboxField.setValueSilently(this.willAttach.group);
|
||||
|
@ -177,7 +315,11 @@ export default class PopupNewMedia extends PopupElement {
|
|||
text: 'PreviewSender.CompressFile',
|
||||
name: 'compress-items'
|
||||
});
|
||||
this.container.append(...[this.groupCheckboxField?.label, this.mediaCheckboxField.label, this.inputField.container].filter(Boolean));
|
||||
this.container.append(...[
|
||||
this.groupCheckboxField?.label,
|
||||
this.mediaCheckboxField.label,
|
||||
this.inputField.container
|
||||
].filter(Boolean));
|
||||
|
||||
this.mediaCheckboxField.setValueSilently(this.willAttach.type === 'media');
|
||||
|
||||
|
@ -235,23 +377,16 @@ export default class PopupNewMedia extends PopupElement {
|
|||
return;
|
||||
}
|
||||
|
||||
this.hide();
|
||||
const willAttach = this.willAttach;
|
||||
willAttach.isMedia = willAttach.type === 'media' ? true : undefined;
|
||||
willAttach.isMedia = willAttach.type === 'media' || undefined;
|
||||
const {sendFileDetails, isMedia} = willAttach;
|
||||
|
||||
// console.log('will send files with options:', willAttach);
|
||||
|
||||
const {peerId, input} = this.chat;
|
||||
|
||||
sendFileDetails.forEach((d) => {
|
||||
d.itemDiv = undefined;
|
||||
});
|
||||
|
||||
const {length} = sendFileDetails;
|
||||
const sendingParams = this.chat.getMessageSendingParams();
|
||||
this.iterate((sendFileDetails) => {
|
||||
if(caption && sendFileDetails.length !== length) {
|
||||
this.iterate((sendFileParams) => {
|
||||
if(caption && sendFileParams.length !== length) {
|
||||
this.managers.appMessagesManager.sendText(peerId, caption, {
|
||||
...sendingParams,
|
||||
clearDraft: true
|
||||
|
@ -260,16 +395,24 @@ export default class PopupNewMedia extends PopupElement {
|
|||
caption = undefined;
|
||||
}
|
||||
|
||||
const d: SendFileDetails[] = sendFileParams.map((params) => {
|
||||
return {
|
||||
...params,
|
||||
file: params.scaledBlob || params.file,
|
||||
spoiler: !!params.mediaSpoiler
|
||||
};
|
||||
});
|
||||
|
||||
const w = {
|
||||
...willAttach,
|
||||
sendFileDetails
|
||||
sendFileDetails: d
|
||||
};
|
||||
|
||||
this.managers.appMessagesManager.sendAlbum(peerId, w.sendFileDetails.map((d) => d.file), Object.assign({
|
||||
this.managers.appMessagesManager.sendAlbum(peerId, Object.assign({
|
||||
...sendingParams,
|
||||
caption,
|
||||
isMedia: isMedia,
|
||||
clearDraft: true as true
|
||||
isMedia,
|
||||
clearDraft: true
|
||||
}, w));
|
||||
|
||||
caption = undefined;
|
||||
|
@ -277,9 +420,34 @@ export default class PopupNewMedia extends PopupElement {
|
|||
|
||||
input.replyToMsgId = this.chat.threadId;
|
||||
input.onMessageSent();
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
private async attachMedia(params: SendFileParams, itemDiv: HTMLElement) {
|
||||
private async scaleImageForTelegram(image: HTMLImageElement, params: SendFileParams) {
|
||||
const PHOTO_SIDE_LIMIT = 2560;
|
||||
const mimeType = params.file.type;
|
||||
let url = image.src, scaledBlob: Blob;
|
||||
if(mimeType !== 'image/gif' && Math.max(image.naturalWidth, image.naturalHeight) > PHOTO_SIDE_LIMIT) {
|
||||
const {blob} = await scaleMediaElement({
|
||||
media: image,
|
||||
boxSize: makeMediaSize(PHOTO_SIDE_LIMIT, PHOTO_SIDE_LIMIT),
|
||||
mediaSize: makeMediaSize(image.naturalWidth, image.naturalHeight),
|
||||
mimeType: mimeType as any
|
||||
});
|
||||
|
||||
scaledBlob = blob;
|
||||
URL.revokeObjectURL(url);
|
||||
url = await apiManagerProxy.invoke('createObjectURL', blob);
|
||||
await renderImageFromUrlPromise(image, url);
|
||||
}
|
||||
|
||||
params.objectURL = url;
|
||||
params.scaledBlob = scaledBlob;
|
||||
}
|
||||
|
||||
private async attachMedia(params: SendFileParams) {
|
||||
const {itemDiv} = params;
|
||||
itemDiv.classList.add('popup-item-media');
|
||||
|
||||
const file = params.file;
|
||||
|
@ -319,50 +487,53 @@ export default class PopupNewMedia extends PopupElement {
|
|||
video.append(source);
|
||||
} else {
|
||||
const img = new Image();
|
||||
promise = new Promise<void>((resolve) => {
|
||||
img.onload = () => {
|
||||
params.width = img.naturalWidth;
|
||||
params.height = img.naturalHeight;
|
||||
const url = await apiManagerProxy.invoke('createObjectURL', file);
|
||||
await renderImageFromUrlPromise(img, url);
|
||||
|
||||
itemDiv.append(img);
|
||||
await this.scaleImageForTelegram(img, params);
|
||||
params.width = img.naturalWidth;
|
||||
params.height = img.naturalHeight;
|
||||
|
||||
if(file.type === 'image/gif') {
|
||||
params.noSound = true;
|
||||
itemDiv.append(img);
|
||||
|
||||
Promise.all([
|
||||
getGifDuration(img).then((duration) => {
|
||||
params.duration = Math.ceil(duration);
|
||||
}),
|
||||
if(file.type === 'image/gif') {
|
||||
params.noSound = true;
|
||||
|
||||
createPosterFromMedia(img).then(async(thumb) => {
|
||||
params.thumb = {
|
||||
url: await apiManagerProxy.invoke('createObjectURL', thumb.blob),
|
||||
...thumb
|
||||
};
|
||||
})
|
||||
]).then(() => {
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
});
|
||||
return Promise.all([
|
||||
getGifDuration(img).then((duration) => {
|
||||
params.duration = Math.ceil(duration);
|
||||
}),
|
||||
|
||||
img.src = params.objectURL = await apiManagerProxy.invoke('createObjectURL', file);
|
||||
createPosterFromMedia(img).then(async(thumb) => {
|
||||
params.thumb = {
|
||||
url: await apiManagerProxy.invoke('createObjectURL', thumb.blob),
|
||||
...thumb
|
||||
};
|
||||
})
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private async attachDocument(params: SendFileParams, itemDiv: HTMLElement): ReturnType<PopupNewMedia['attachMedia']> {
|
||||
private async attachDocument(params: SendFileParams): ReturnType<PopupNewMedia['attachMedia']> {
|
||||
const {itemDiv} = params;
|
||||
itemDiv.classList.add('popup-item-document');
|
||||
const file = params.file;
|
||||
|
||||
const isPhoto = file.type.startsWith('image/');
|
||||
const isAudio = file.type.startsWith('audio/');
|
||||
if(isPhoto || isAudio || file.size < 20e6) {
|
||||
params.objectURL = await apiManagerProxy.invoke('createObjectURL', file);
|
||||
params.objectURL ||= await apiManagerProxy.invoke('createObjectURL', file);
|
||||
}
|
||||
|
||||
let img: HTMLImageElement;
|
||||
if(isPhoto) {
|
||||
img = new Image();
|
||||
await renderImageFromUrlPromise(img, params.objectURL);
|
||||
await this.scaleImageForTelegram(img, params);
|
||||
params.scaledBlob = undefined;
|
||||
}
|
||||
|
||||
const doc = {
|
||||
|
@ -398,44 +569,32 @@ export default class PopupNewMedia extends PopupElement {
|
|||
cacheContext
|
||||
});
|
||||
|
||||
const promise = new Promise<void>((resolve) => {
|
||||
const finish = () => {
|
||||
itemDiv.append(docDiv);
|
||||
resolve();
|
||||
};
|
||||
if(isPhoto) {
|
||||
params.width = img.naturalWidth;
|
||||
params.height = img.naturalHeight;
|
||||
}
|
||||
|
||||
if(isPhoto) {
|
||||
const img = new Image();
|
||||
img.src = params.objectURL;
|
||||
img.onload = () => {
|
||||
params.width = img.naturalWidth;
|
||||
params.height = img.naturalHeight;
|
||||
|
||||
finish();
|
||||
};
|
||||
|
||||
img.onerror = finish;
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
itemDiv.append(docDiv);
|
||||
}
|
||||
|
||||
private attachFile = (file: File) => {
|
||||
const willAttach = this.willAttach;
|
||||
const shouldCompress = this.shouldCompress(file.type);
|
||||
|
||||
const params: SendFileParams = {};
|
||||
params.file = file;
|
||||
|
||||
const itemDiv = document.createElement('div');
|
||||
itemDiv.classList.add('popup-item');
|
||||
|
||||
const params: SendFileParams = {
|
||||
file
|
||||
} as any;
|
||||
|
||||
// do not pass these properties to worker
|
||||
defineNotNumerableProperties(params, ['scaledBlob', 'middlewareHelper', 'itemDiv', 'mediaSpoiler']);
|
||||
|
||||
params.middlewareHelper = this.middlewareHelper.get().create();
|
||||
params.itemDiv = itemDiv;
|
||||
|
||||
const promise = shouldCompress ? this.attachMedia(params, itemDiv) : this.attachDocument(params, itemDiv);
|
||||
const promise = shouldCompress ? this.attachMedia(params) : this.attachDocument(params);
|
||||
willAttach.sendFileDetails.push(params);
|
||||
return promise;
|
||||
};
|
||||
|
@ -494,14 +653,14 @@ export default class PopupNewMedia extends PopupElement {
|
|||
replaceContent(title, i18n(key, args));
|
||||
}
|
||||
|
||||
private appendMediaToContainer(div: HTMLElement, params: SendFileParams) {
|
||||
private appendMediaToContainer(params: SendFileParams) {
|
||||
if(this.shouldCompress(params.file.type)) {
|
||||
const size = calcImageInBox(params.width, params.height, 380, 320);
|
||||
div.style.width = size.width + 'px';
|
||||
div.style.height = size.height + 'px';
|
||||
params.itemDiv.style.width = size.width + 'px';
|
||||
params.itemDiv.style.height = size.height + 'px';
|
||||
}
|
||||
|
||||
this.mediaContainer.append(div);
|
||||
this.mediaContainer.append(params.itemDiv);
|
||||
}
|
||||
|
||||
private iterate(cb: (sendFileDetails: SendFileParams[]) => void) {
|
||||
|
@ -528,13 +687,19 @@ export default class PopupNewMedia extends PopupElement {
|
|||
|
||||
private attachFiles() {
|
||||
const {files, willAttach, mediaContainer} = this;
|
||||
willAttach.sendFileDetails.length = 0;
|
||||
|
||||
const oldSendFileDetails = willAttach.sendFileDetails.splice(0, willAttach.sendFileDetails.length);
|
||||
oldSendFileDetails.forEach((params) => {
|
||||
params.middlewareHelper.destroy();
|
||||
});
|
||||
|
||||
this.appendGroupCheckboxField();
|
||||
this.appendMediaCheckboxField();
|
||||
|
||||
Promise.all(files.map(this.attachFile)).then(() => {
|
||||
mediaContainer.innerHTML = '';
|
||||
const promises = files.map((file) => this.attachFile(file));
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
mediaContainer.replaceChildren();
|
||||
|
||||
if(!files.length) {
|
||||
return;
|
||||
|
@ -543,7 +708,8 @@ export default class PopupNewMedia extends PopupElement {
|
|||
this.setTitle();
|
||||
|
||||
this.iterate((sendFileDetails) => {
|
||||
if(this.shouldCompress(sendFileDetails[0].file.type) && sendFileDetails.length > 1) {
|
||||
const shouldCompress = this.shouldCompress(sendFileDetails[0].file.type);
|
||||
if(shouldCompress && sendFileDetails.length > 1) {
|
||||
const albumContainer = document.createElement('div');
|
||||
albumContainer.classList.add('popup-item-album', 'popup-item');
|
||||
albumContainer.append(...sendFileDetails.map((s) => s.itemDiv));
|
||||
|
@ -559,9 +725,24 @@ export default class PopupNewMedia extends PopupElement {
|
|||
mediaContainer.append(albumContainer);
|
||||
} else {
|
||||
sendFileDetails.forEach((params) => {
|
||||
this.appendMediaToContainer(params.itemDiv, params);
|
||||
this.appendMediaToContainer(params);
|
||||
});
|
||||
}
|
||||
|
||||
if(!shouldCompress) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendFileDetails.forEach((params) => {
|
||||
const oldParams = oldSendFileDetails.find((o) => o.file === params.file);
|
||||
if(!oldParams) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(oldParams.mediaSpoiler) {
|
||||
this.applyMediaSpoiler(params, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}).then(() => {
|
||||
this.onRender();
|
||||
|
|
|
@ -9,29 +9,41 @@ import {Middleware} from '../../helpers/middleware';
|
|||
import {Document, Photo, PhotoSize} from '../../layer';
|
||||
import {AnimationItemGroup} from '../animationIntersector';
|
||||
import DotRenderer from '../dotRenderer';
|
||||
import SetTransition from '../singleTransition';
|
||||
|
||||
export default async function wrapMediaSpoiler({
|
||||
media,
|
||||
export function toggleMediaSpoiler(options: {
|
||||
mediaSpoiler: HTMLElement,
|
||||
reveal: boolean,
|
||||
destroyAfter?: boolean
|
||||
}) {
|
||||
const {mediaSpoiler, reveal, destroyAfter} = options;
|
||||
SetTransition({
|
||||
element: mediaSpoiler,
|
||||
forwards: reveal,
|
||||
className: 'is-revealing',
|
||||
duration: 250,
|
||||
onTransitionEnd: () => {
|
||||
if(reveal && destroyAfter) {
|
||||
mediaSpoiler.remove();
|
||||
mediaSpoiler.middlewareHelper.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function wrapMediaSpoilerWithImage({
|
||||
middleware,
|
||||
width,
|
||||
height,
|
||||
animationGroup
|
||||
animationGroup,
|
||||
image
|
||||
}: {
|
||||
media: Document.document | Photo.photo,
|
||||
middleware: Middleware,
|
||||
width: number,
|
||||
height: number,
|
||||
animationGroup: AnimationItemGroup
|
||||
animationGroup: AnimationItemGroup,
|
||||
image: Awaited<ReturnType<typeof getImageFromStrippedThumb>>['image']
|
||||
}) {
|
||||
const sizes = (media as Photo.photo).sizes || (media as Document.document).thumbs;
|
||||
const thumb = sizes.find((size) => size._ === 'photoStrippedSize') as PhotoSize.photoStrippedSize;
|
||||
if(!thumb) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {image, loadPromise} = getImageFromStrippedThumb(media, thumb, true);
|
||||
await loadPromise;
|
||||
|
||||
if(!middleware()) {
|
||||
return;
|
||||
}
|
||||
|
@ -53,3 +65,24 @@ export default async function wrapMediaSpoiler({
|
|||
|
||||
return container;
|
||||
}
|
||||
|
||||
export default async function wrapMediaSpoiler(
|
||||
options: Omit<Parameters<typeof wrapMediaSpoilerWithImage>[0], 'image'> & {
|
||||
media: Document.document | Photo.photo
|
||||
}
|
||||
) {
|
||||
const {media} = options;
|
||||
const sizes = (media as Photo.photo).sizes || (media as Document.document).thumbs;
|
||||
const thumb = sizes.find((size) => size._ === 'photoStrippedSize') as PhotoSize.photoStrippedSize;
|
||||
if(!thumb) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {image, loadPromise} = getImageFromStrippedThumb(media, thumb, true);
|
||||
await loadPromise;
|
||||
|
||||
return wrapMediaSpoilerWithImage({
|
||||
...options,
|
||||
image
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ const App = {
|
|||
version: process.env.VERSION,
|
||||
versionFull: process.env.VERSION_FULL,
|
||||
build: +process.env.BUILD,
|
||||
langPackVersion: '0.7.6',
|
||||
langPackVersion: '0.7.7',
|
||||
langPack: 'webk',
|
||||
langPackCode: 'en',
|
||||
domains: MAIN_DOMAINS,
|
||||
|
|
|
@ -7,18 +7,46 @@
|
|||
import {IS_SAFARI} from '../../environment/userAgent';
|
||||
import bytesFromHex from './bytesFromHex';
|
||||
import bytesToDataURL from './bytesToDataURL';
|
||||
import convertToUint8Array from './convertToUint8Array';
|
||||
|
||||
const JPEG_HEADER = bytesFromHex('ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e19282321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c353c76414176f8a58ca5f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8ffc00011080000000003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00');
|
||||
const JPEG_HEADER_HEX = 'ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e19282321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c353c76414176f8a58ca5f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8ffc00011080000000003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00';
|
||||
|
||||
const JPEG_HEADER = bytesFromHex(JPEG_HEADER_HEX);
|
||||
const JPEG_TAIL = bytesFromHex('ffd9');
|
||||
|
||||
export function getPreviewBytesFromURL(url: string) {
|
||||
const needle = 'base64,';
|
||||
const sliced = url.slice(url.indexOf(needle) + needle.length);
|
||||
const jpegBytes = [...atob(sliced)].map((char) => char.charCodeAt(0));
|
||||
|
||||
return new Uint8Array(jpegBytes);
|
||||
// console.log('getPreviewBytesFromURL', bytesToHex(jpegBytes));
|
||||
|
||||
// const n = JPEG_HEADER_HEX.slice(-10);
|
||||
// const one = jpegBytes[164];
|
||||
// const two = jpegBytes[166];
|
||||
|
||||
// const body = jpegBytes.slice(bytesToHex(jpegBytes).indexOf(n) / 2 + n.length / 2/* JPEG_HEADER.length */, jpegBytes.length - JPEG_TAIL.length);
|
||||
// const original = new Uint8Array([
|
||||
// 0xFF,
|
||||
// one,
|
||||
// two,
|
||||
// ...body
|
||||
// ]);
|
||||
|
||||
// console.log(bytesToHex(body));
|
||||
|
||||
// return original;
|
||||
}
|
||||
|
||||
export default function getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {
|
||||
let arr: Uint8Array;
|
||||
if(!isSticker) {
|
||||
if(!isSticker && bytes[0] === 0x1) {
|
||||
arr = new Uint8Array(JPEG_HEADER.concat(Array.from(bytes.slice(3)), JPEG_TAIL));
|
||||
arr[164] = bytes[1];
|
||||
arr[166] = bytes[2];
|
||||
} else {
|
||||
arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
||||
arr = convertToUint8Array(bytes);
|
||||
}
|
||||
|
||||
let mimeType: string;
|
||||
|
@ -28,5 +56,6 @@ export default function getPreviewURLFromBytes(bytes: Uint8Array | number[], isS
|
|||
mimeType = 'image/jpeg';
|
||||
}
|
||||
|
||||
return bytesToDataURL(arr, mimeType);
|
||||
const dataURL = bytesToDataURL(arr, mimeType);
|
||||
return dataURL;
|
||||
}
|
||||
|
|
|
@ -1,22 +1,45 @@
|
|||
import type {MediaSize} from '../mediaSize';
|
||||
import IS_IMAGE_BITMAP_SUPPORTED from '../../environment/imageBitmapSupport';
|
||||
|
||||
export default function scaleMediaElement(options: {
|
||||
export default function scaleMediaElement<T extends {
|
||||
media: CanvasImageSource,
|
||||
mediaSize?: MediaSize,
|
||||
boxSize?: MediaSize,
|
||||
quality?: number,
|
||||
mimeType?: 'image/jpeg' | 'image/png',
|
||||
size?: MediaSize
|
||||
}): Promise<{blob: Blob, size: MediaSize}> {
|
||||
return new Promise((resolve) => {
|
||||
size?: MediaSize,
|
||||
toDataURL?: boolean
|
||||
}>(options: T): Promise<T['toDataURL'] extends true ? {url: string, size: MediaSize} : {blob: Blob, size: MediaSize}> {
|
||||
return new Promise(async(resolve) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const size = options.size ?? options.mediaSize.aspectFitted(options.boxSize);
|
||||
canvas.width = size.width * window.devicePixelRatio;
|
||||
canvas.height = size.height * window.devicePixelRatio;
|
||||
const dpr = window.devicePixelRatio && 1;
|
||||
canvas.width = size.width * dpr;
|
||||
canvas.height = size.height * dpr;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(options.media, 0, 0, canvas.width, canvas.height);
|
||||
canvas.toBlob((blob) => {
|
||||
resolve({blob, size});
|
||||
}, options.mimeType ?? 'image/jpeg', options.quality ?? 1);
|
||||
|
||||
let source: CanvasImageSource;
|
||||
if(IS_IMAGE_BITMAP_SUPPORTED) {
|
||||
source = await createImageBitmap(options.media, {resizeWidth: size.width, resizeHeight: size.height});
|
||||
} else {
|
||||
source = options.media;
|
||||
}
|
||||
|
||||
ctx.drawImage(source, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
if(IS_IMAGE_BITMAP_SUPPORTED) {
|
||||
(source as ImageBitmap)?.close();
|
||||
}
|
||||
|
||||
const mimeType = options.mimeType ?? 'image/jpeg';
|
||||
const quality = options.quality ?? 1;
|
||||
if(options.toDataURL) {
|
||||
const url = canvas.toDataURL(mimeType, quality);
|
||||
resolve({url, size} as any);
|
||||
} else {
|
||||
canvas.toBlob((blob) => {
|
||||
resolve({blob, size} as any);
|
||||
}, mimeType, quality);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export function createPosterFromMedia(media: HTMLVideoElement | HTMLImageElement
|
|||
});
|
||||
}
|
||||
|
||||
export function createPosterFromVideo(video: HTMLVideoElement): ReturnType<typeof scaleMediaElement> {
|
||||
export function createPosterFromVideo(video: HTMLVideoElement): ReturnType<typeof createPosterFromMedia> {
|
||||
return new Promise((resolve, reject) => {
|
||||
video.onseeked = () => {
|
||||
video.onseeked = () => {
|
||||
|
|
|
@ -867,6 +867,8 @@ const lang = {
|
|||
'AreYouSureWebSessions': 'Are you sure you want to disconnect all websites where you logged in with Telegram?',
|
||||
'ClearOtherWebSessionsHelp': 'You can log in on websites that support signing in with Telegram.',
|
||||
'TerminateWebSessionInfo': 'Tap to disconnect from your Telegram account.',
|
||||
'EnablePhotoSpoiler': 'Hide with spoiler',
|
||||
'DisablePhotoSpoiler': 'Remove spoiler',
|
||||
|
||||
// * macos
|
||||
'AccountSettings.Filters': 'Chat Folders',
|
||||
|
|
|
@ -74,6 +74,22 @@ import getMessageThreadId from './utils/messages/getMessageThreadId';
|
|||
const APITIMEOUT = 0;
|
||||
const DO_NOT_READ_HISTORY = false;
|
||||
|
||||
export type SendFileDetails = {
|
||||
file: File | Blob | MyDocument,
|
||||
} & Partial<{
|
||||
duration: number,
|
||||
width: number,
|
||||
height: number,
|
||||
objectURL: string,
|
||||
thumb: {
|
||||
blob: Blob,
|
||||
url: string,
|
||||
size: MediaSize
|
||||
},
|
||||
strippedBytes: PhotoSize.photoStrippedSize['bytes'],
|
||||
spoiler: boolean
|
||||
}>;
|
||||
|
||||
export type HistoryStorage = {
|
||||
count: number | null,
|
||||
history: SlicedArray<number>,
|
||||
|
@ -676,24 +692,15 @@ export class AppMessagesManager extends AppManager {
|
|||
return Promise.all(promises).then(noop);
|
||||
}
|
||||
|
||||
public sendFile(peerId: PeerId, file: File | Blob | MyDocument, options: MessageSendingParams & Partial<{
|
||||
isRoundMessage: true,
|
||||
isVoiceMessage: true,
|
||||
isGroupedItem: true,
|
||||
isMedia: true,
|
||||
public sendFile(peerId: PeerId, options: MessageSendingParams & SendFileDetails & Partial<{
|
||||
isRoundMessage: boolean,
|
||||
isVoiceMessage: boolean,
|
||||
isGroupedItem: boolean,
|
||||
isMedia: boolean,
|
||||
|
||||
groupId: string,
|
||||
caption: string,
|
||||
entities: MessageEntity[],
|
||||
width: number,
|
||||
height: number,
|
||||
objectURL: string,
|
||||
thumb: {
|
||||
blob: Blob,
|
||||
url: string,
|
||||
size: MediaSize
|
||||
},
|
||||
duration: number,
|
||||
background: boolean,
|
||||
clearDraft: boolean,
|
||||
noSound: boolean,
|
||||
|
@ -702,7 +709,8 @@ export class AppMessagesManager extends AppManager {
|
|||
|
||||
// ! only for internal use
|
||||
processAfter?: typeof processAfter
|
||||
}> = {}) {
|
||||
}>) {
|
||||
const file = options.file;
|
||||
peerId = this.appPeersManager.getPeerMigratedTo(peerId) || peerId;
|
||||
|
||||
// this.checkSendOptions(options);
|
||||
|
@ -728,6 +736,12 @@ export class AppMessagesManager extends AppManager {
|
|||
|
||||
const isPhoto = getEnvironment().IMAGE_MIME_TYPES_SUPPORTED.has(fileType);
|
||||
|
||||
const strippedPhotoSize: PhotoSize.photoStrippedSize = options.strippedBytes && {
|
||||
_: 'photoStrippedSize',
|
||||
bytes: options.strippedBytes,
|
||||
type: 'i'
|
||||
};
|
||||
|
||||
let photo: MyPhoto, document: MyDocument;
|
||||
|
||||
let actionName: Extract<SendMessageAction['_'], 'sendMessageUploadAudioAction' | 'sendMessageUploadDocumentAction' | 'sendMessageUploadPhotoAction' | 'sendMessageUploadVideoAction'>;
|
||||
|
@ -747,7 +761,7 @@ export class AppMessagesManager extends AppManager {
|
|||
const attribute: DocumentAttribute.documentAttributeAudio = {
|
||||
_: 'documentAttributeAudio',
|
||||
pFlags: {
|
||||
voice: options.isVoiceMessage
|
||||
voice: options.isVoiceMessage || undefined
|
||||
},
|
||||
waveform: options.waveform,
|
||||
duration: options.duration || 0
|
||||
|
@ -780,6 +794,10 @@ export class AppMessagesManager extends AppManager {
|
|||
h: options.height
|
||||
} as any;
|
||||
|
||||
if(strippedPhotoSize) {
|
||||
photo.sizes.unshift(strippedPhotoSize);
|
||||
}
|
||||
|
||||
const cacheContext = this.thumbsStorage.getCacheContext(photo, photoSize.type);
|
||||
cacheContext.downloaded = file.size;
|
||||
cacheContext.url = options.objectURL || '';
|
||||
|
@ -793,7 +811,7 @@ export class AppMessagesManager extends AppManager {
|
|||
const videoAttribute: DocumentAttribute.documentAttributeVideo = {
|
||||
_: 'documentAttributeVideo',
|
||||
pFlags: {
|
||||
round_message: options.isRoundMessage,
|
||||
round_message: options.isRoundMessage || undefined,
|
||||
supports_streaming: true
|
||||
},
|
||||
duration: options.duration,
|
||||
|
@ -874,6 +892,10 @@ export class AppMessagesManager extends AppManager {
|
|||
thumbs.push(thumb);
|
||||
}
|
||||
|
||||
if(strippedPhotoSize) {
|
||||
thumbs.unshift(strippedPhotoSize);
|
||||
}
|
||||
|
||||
/* if(thumbs.length) {
|
||||
const thumb = thumbs[0] as PhotoSize.photoSize;
|
||||
const docThumb = appPhotosManager.getDocumentCachedThumb(document.id);
|
||||
|
@ -908,6 +930,10 @@ export class AppMessagesManager extends AppManager {
|
|||
if(media) {
|
||||
defineNotNumerableProperties(media as any, ['promise']);
|
||||
(media as any).promise = sentDeferred;
|
||||
|
||||
if(options.spoiler) {
|
||||
(media as MessageMedia.messageMediaPhoto).pFlags.spoiler = true;
|
||||
}
|
||||
}
|
||||
|
||||
message.entities = entities;
|
||||
|
@ -989,7 +1015,10 @@ export class AppMessagesManager extends AppManager {
|
|||
case 'photo':
|
||||
inputMedia = {
|
||||
_: 'inputMediaUploadedPhoto',
|
||||
file: inputFile
|
||||
file: inputFile,
|
||||
pFlags: {
|
||||
spoiler: options.spoiler || undefined
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
|
@ -999,7 +1028,8 @@ export class AppMessagesManager extends AppManager {
|
|||
file: inputFile,
|
||||
mime_type: fileType,
|
||||
pFlags: {
|
||||
force_file: actionName === 'sendMessageUploadDocumentAction' ? true : undefined
|
||||
force_file: actionName === 'sendMessageUploadDocumentAction' || undefined,
|
||||
spoiler: options.spoiler || undefined
|
||||
// nosound_video: options.noSound ? true : undefined
|
||||
},
|
||||
attributes
|
||||
|
@ -1094,28 +1124,21 @@ export class AppMessagesManager extends AppManager {
|
|||
return ret;
|
||||
}
|
||||
|
||||
public async sendAlbum(peerId: PeerId, files: File[], options: MessageSendingParams & Partial<{
|
||||
isMedia: true,
|
||||
entities: MessageEntity[],
|
||||
caption: string,
|
||||
sendFileDetails: Partial<{
|
||||
duration: number,
|
||||
width: number,
|
||||
height: number,
|
||||
objectURL: string,
|
||||
thumbBlob: Blob,
|
||||
thumbURL: string
|
||||
}>[],
|
||||
clearDraft: true
|
||||
}> = {}) {
|
||||
public async sendAlbum(peerId: PeerId, options: MessageSendingParams & {
|
||||
isMedia?: boolean,
|
||||
entities?: MessageEntity[],
|
||||
caption?: string,
|
||||
sendFileDetails: SendFileDetails[],
|
||||
clearDraft?: boolean
|
||||
}) {
|
||||
// this.checkSendOptions(options);
|
||||
|
||||
if(options.threadId && !options.replyToMsgId) {
|
||||
options.replyToMsgId = options.threadId;
|
||||
}
|
||||
|
||||
if(files.length === 1) {
|
||||
return this.sendFile(peerId, files[0], {...options, ...options.sendFileDetails[0]});
|
||||
if(options.sendFileDetails.length === 1) {
|
||||
return this.sendFile(peerId, {...options, ...options.sendFileDetails[0]});
|
||||
}
|
||||
|
||||
peerId = this.appPeersManager.getPeerMigratedTo(peerId) || peerId;
|
||||
|
@ -1127,7 +1150,7 @@ export class AppMessagesManager extends AppManager {
|
|||
caption = parseMarkdown(caption, entities);
|
||||
}
|
||||
|
||||
this.log('sendAlbum', files, options);
|
||||
this.log('sendAlbum', options);
|
||||
|
||||
const groupId = '' + ++this.groupedTempId;
|
||||
|
||||
|
@ -1136,9 +1159,8 @@ export class AppMessagesManager extends AppManager {
|
|||
callbacks.push(cb);
|
||||
};
|
||||
|
||||
const messages = files.map((file, idx) => {
|
||||
const details = options.sendFileDetails[idx];
|
||||
const o: Parameters<AppMessagesManager['sendFile']>[2] = {
|
||||
const messages = options.sendFileDetails.map((details, idx) => {
|
||||
const o: Parameters<AppMessagesManager['sendFile']>[1] = {
|
||||
isGroupedItem: true,
|
||||
isMedia: options.isMedia,
|
||||
scheduleDate: options.scheduleDate,
|
||||
|
@ -1157,7 +1179,7 @@ export class AppMessagesManager extends AppManager {
|
|||
// o.replyToMsgId = replyToMsgId;
|
||||
}
|
||||
|
||||
return this.sendFile(peerId, file, o).message;
|
||||
return this.sendFile(peerId, o).message;
|
||||
});
|
||||
|
||||
if(options.clearDraft) {
|
||||
|
@ -1209,12 +1231,15 @@ export class AppMessagesManager extends AppManager {
|
|||
|
||||
const promises: Promise<InputSingleMedia>[] = messages.map((message) => {
|
||||
return (message.send() as Promise<InputMedia>).then((inputMedia) => {
|
||||
return this.apiManager.invokeApi('messages.uploadMedia', {
|
||||
peer: inputPeer,
|
||||
media: inputMedia
|
||||
});
|
||||
return Promise.all([
|
||||
inputMedia,
|
||||
this.apiManager.invokeApi('messages.uploadMedia', {
|
||||
peer: inputPeer,
|
||||
media: inputMedia
|
||||
})
|
||||
]);
|
||||
})
|
||||
.then((messageMedia) => {
|
||||
.then(([originalInputMedia, messageMedia]) => {
|
||||
let inputMedia: InputMedia;
|
||||
if(messageMedia._ === 'messageMediaPhoto') {
|
||||
const photo = this.appPhotosManager.savePhoto(messageMedia.photo);
|
||||
|
@ -1224,6 +1249,17 @@ export class AppMessagesManager extends AppManager {
|
|||
inputMedia = getDocumentMediaInput(doc);
|
||||
}
|
||||
|
||||
// copy original flags
|
||||
const copyProperties: (keyof InputMedia.inputMediaPhoto)[] = [
|
||||
'pFlags',
|
||||
'ttl_seconds'
|
||||
];
|
||||
|
||||
copyProperties.forEach((property) => {
|
||||
// @ts-ignore
|
||||
inputMedia[property] = originalInputMedia[property] ?? inputMedia[property];
|
||||
});
|
||||
|
||||
const inputSingleMedia: InputSingleMedia = {
|
||||
_: 'inputSingleMedia',
|
||||
media: inputMedia,
|
||||
|
|
|
@ -24,7 +24,7 @@ type HashOptions = {
|
|||
};
|
||||
|
||||
export type ApiLimitType = 'pin' | 'folderPin' | 'folders' |
|
||||
'favedStickers' | 'reactions' | 'bio' | 'topicPin';
|
||||
'favedStickers' | 'reactions' | 'bio' | 'topicPin' | 'caption';
|
||||
|
||||
export default abstract class ApiManagerMethods extends AppManager {
|
||||
private afterMessageIdTemp: number;
|
||||
|
@ -303,7 +303,8 @@ export default abstract class ApiManagerMethods extends AppManager {
|
|||
favedStickers: ['stickers_faved_limit_default', 'stickers_faved_limit_premium'],
|
||||
reactions: ['reactions_user_max_default', 'reactions_user_max_premium'],
|
||||
bio: ['about_length_limit_default', 'about_length_limit_premium'],
|
||||
topicPin: 'topics_pinned_limit'
|
||||
topicPin: 'topics_pinned_limit',
|
||||
caption: ['caption_length_limit_default', 'caption_length_limit_premium']
|
||||
};
|
||||
|
||||
isPremium ??= this.rootScope.premium;
|
||||
|
|
|
@ -2385,30 +2385,6 @@ $bubble-border-radius-big: 12px;
|
|||
margin-top: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.media-spoiler {
|
||||
&-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
border-radius: inherit;
|
||||
|
||||
&.is-revealing {
|
||||
opacity: 1;
|
||||
|
||||
&:not(.backwards) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.animating {
|
||||
transition: opacity var(--transition-standard-out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @keyframes arrow-shake {
|
||||
|
|
|
@ -2076,3 +2076,39 @@ hr {
|
|||
color: var(--danger-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.media-spoiler {
|
||||
&-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
border-radius: inherit;
|
||||
|
||||
&.is-revealing {
|
||||
opacity: 1;
|
||||
|
||||
&:not(.backwards) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.animating {
|
||||
transition: opacity var(--transition-standard-out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-thumbnail,
|
||||
.canvas-thumbnail {
|
||||
position: absolute;
|
||||
// top: 0;
|
||||
// right: 0;
|
||||
// bottom: 0;
|
||||
// left: 0;
|
||||
object-fit: unset !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue