Allow sending different media types
Added drag&drop in new media popup Fix rendering thumbnail for outgoing document Allow pasting media in current popup
This commit is contained in:
parent
24c1a4af23
commit
bbb754196e
@ -2557,13 +2557,15 @@ export default class ChatBubbles {
|
||||
}
|
||||
|
||||
return new Promise<PeerId>((resolve, reject) => {
|
||||
new PopupForward({
|
||||
const popup = new PopupForward({
|
||||
[this.peerId]: []
|
||||
}, (peerId) => {
|
||||
resolve(peerId);
|
||||
}, () => {
|
||||
reject();
|
||||
}, true);
|
||||
|
||||
popup.addEventListener('close', () => {
|
||||
reject();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import generatePathData from "../../helpers/generatePathData";
|
||||
import { i18n, LangPackKey } from "../../lib/langPack";
|
||||
import { FormatterArguments, i18n, LangPackKey } from "../../lib/langPack";
|
||||
|
||||
export default class ChatDragAndDrop {
|
||||
container: HTMLDivElement;
|
||||
@ -14,9 +14,10 @@ export default class ChatDragAndDrop {
|
||||
path: SVGPathElement;
|
||||
|
||||
constructor(appendTo: HTMLElement, private options: {
|
||||
icon: string,
|
||||
icon?: string,
|
||||
header: LangPackKey,
|
||||
subtitle: LangPackKey,
|
||||
headerArgs?: FormatterArguments,
|
||||
subtitle?: LangPackKey,
|
||||
onDrop: (e: DragEvent) => void
|
||||
}) {
|
||||
this.container = document.createElement('div');
|
||||
@ -31,21 +32,27 @@ export default class ChatDragAndDrop {
|
||||
this.path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
this.path.classList.add('drop-outline-path');
|
||||
|
||||
const dropIcon = document.createElement('div');
|
||||
dropIcon.classList.add('drop-icon', 'tgico-' + options.icon);
|
||||
let dropIcon: HTMLElement;
|
||||
if(options.icon) {
|
||||
dropIcon = document.createElement('div');
|
||||
dropIcon.classList.add('drop-icon', 'tgico-' + options.icon);
|
||||
}
|
||||
|
||||
const dropHeader = document.createElement('div');
|
||||
dropHeader.classList.add('drop-header');
|
||||
dropHeader.append(i18n(options.header));
|
||||
dropHeader.append(i18n(options.header, options.headerArgs));
|
||||
|
||||
const dropSubtitle = document.createElement('div');
|
||||
dropSubtitle.classList.add('drop-subtitle');
|
||||
dropSubtitle.append(i18n(options.subtitle));
|
||||
let dropSubtitle: HTMLElement;
|
||||
if(options.subtitle) {
|
||||
dropSubtitle = document.createElement('div');
|
||||
dropSubtitle.classList.add('drop-subtitle');
|
||||
dropSubtitle.append(i18n(options.subtitle));
|
||||
}
|
||||
|
||||
this.svg.append(this.path);
|
||||
this.outlineWrapper.append(this.svg);
|
||||
|
||||
this.container.append(this.outlineWrapper, dropIcon, dropHeader, dropSubtitle);
|
||||
this.container.append(...[this.outlineWrapper, dropIcon, dropHeader, dropSubtitle].filter(Boolean));
|
||||
appendTo.append(this.container);
|
||||
|
||||
this.container.addEventListener('dragover', this.onDragOver);
|
||||
|
@ -1677,9 +1677,11 @@ export default class ChatInput {
|
||||
this.clearHelper();
|
||||
this.updateSendBtn();
|
||||
let selected = false;
|
||||
new PopupForward(copy(this.forwarding), () => {
|
||||
const popup = new PopupForward(copy(this.forwarding), () => {
|
||||
selected = true;
|
||||
}, () => {
|
||||
});
|
||||
|
||||
popup.addEventListener('close', () => {
|
||||
this.helperWaitingForward = false;
|
||||
|
||||
if(!selected) {
|
||||
|
@ -104,7 +104,7 @@ class InputField {
|
||||
this.required = options.required;
|
||||
this.validate = options.validate;
|
||||
|
||||
if(options.maxLength) {
|
||||
if(options.maxLength !== undefined && options.showLengthOn === undefined) {
|
||||
options.showLengthOn = Math.min(40, Math.round(options.maxLength / 3));
|
||||
}
|
||||
|
||||
|
@ -81,12 +81,12 @@ export default class PopupAvatar extends PopupElement {
|
||||
|
||||
this.container.append(this.cropContainer, this.btnConfirm, this.input);
|
||||
|
||||
this.onCloseAfterTimeout = () => {
|
||||
this.addEventListener('closeAfterTimeout', () => {
|
||||
this.cropper.removeHandlers();
|
||||
if(this.image) {
|
||||
this.image.remove();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private resolve() {
|
||||
|
@ -11,7 +11,6 @@ export default class PopupForward extends PopupPickUser {
|
||||
constructor(
|
||||
peerIdMids: {[fromPeerId: PeerId]: number[]},
|
||||
onSelect?: (peerId: PeerId) => Promise<void> | void,
|
||||
onClose?: () => void,
|
||||
overrideOnSelect = false
|
||||
) {
|
||||
super({
|
||||
@ -27,7 +26,6 @@ export default class PopupForward extends PopupPickUser {
|
||||
appImManager.setInnerPeer(peerId);
|
||||
appImManager.chat.input.initMessagesForward(peerIdMids);
|
||||
},
|
||||
onClose,
|
||||
placeholder: 'ShareModal.Search.ForwardPlaceholder',
|
||||
chatRightsAction: 'send_messages',
|
||||
selfPresence: 'ChatYourSelf'
|
||||
|
@ -16,6 +16,7 @@ import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEve
|
||||
import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed";
|
||||
import { cancelEvent } from "../../helpers/dom/cancelEvent";
|
||||
import getKeyFromEvent from "../../helpers/dom/getKeyFromEvent";
|
||||
import EventListenerBase from "../../helpers/eventListenerBase";
|
||||
|
||||
export type PopupButton = {
|
||||
text?: string,
|
||||
@ -35,7 +36,10 @@ export type PopupOptions = Partial<{
|
||||
confirmShortcutIsSendShortcut: boolean
|
||||
}>;
|
||||
|
||||
export default class PopupElement {
|
||||
export default class PopupElement extends EventListenerBase<{
|
||||
close: () => void,
|
||||
closeAfterTimeout: () => void
|
||||
}> {
|
||||
protected element = document.createElement('div');
|
||||
protected container = document.createElement('div');
|
||||
protected header = document.createElement('div');
|
||||
@ -45,8 +49,6 @@ export default class PopupElement {
|
||||
protected body: HTMLElement;
|
||||
protected buttonsEl: HTMLElement;
|
||||
|
||||
protected onClose: () => void;
|
||||
protected onCloseAfterTimeout: () => void;
|
||||
protected onEscape: () => boolean = () => true;
|
||||
|
||||
protected navigationItem: NavigationItem;
|
||||
@ -57,6 +59,8 @@ export default class PopupElement {
|
||||
protected btnConfirmOnEnter: HTMLButtonElement;
|
||||
|
||||
constructor(className: string, protected buttons?: Array<PopupButton>, options: PopupOptions = {}) {
|
||||
super(false);
|
||||
|
||||
this.element.classList.add('popup');
|
||||
this.element.className = 'popup' + (className ? ' ' + className : '');
|
||||
this.container.classList.add('popup-container', 'z-depth-1');
|
||||
@ -181,7 +185,7 @@ export default class PopupElement {
|
||||
};
|
||||
|
||||
private destroy = () => {
|
||||
this.onClose && this.onClose();
|
||||
this.dispatchEvent('close');
|
||||
this.element.classList.add('hiding');
|
||||
this.element.classList.remove('active');
|
||||
this.listenerSetter.removeAll();
|
||||
@ -193,7 +197,8 @@ export default class PopupElement {
|
||||
|
||||
setTimeout(() => {
|
||||
this.element.remove();
|
||||
this.onCloseAfterTimeout && this.onCloseAfterTimeout();
|
||||
this.dispatchEvent('closeAfterTimeout');
|
||||
this.cleanup();
|
||||
animationIntersector.checkAnimations(false);
|
||||
}, 150);
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ import CheckboxField from "../checkboxField";
|
||||
import SendContextMenu from "../chat/sendContextMenu";
|
||||
import { createPosterFromMedia, createPosterFromVideo, onMediaLoad } from "../../helpers/files";
|
||||
import { MyDocument } from "../../lib/appManagers/appDocsManager";
|
||||
import I18n, { i18n, LangPackKey } from "../../lib/langPack";
|
||||
import I18n, { FormatterArguments, i18n, LangPackKey } from "../../lib/langPack";
|
||||
import appDownloadManager from "../../lib/appManagers/appDownloadManager";
|
||||
import calcImageInBox from "../../helpers/calcImageInBox";
|
||||
import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd";
|
||||
@ -24,6 +24,7 @@ import { MediaSize } from "../../helpers/mediaSizes";
|
||||
import { attachClickEvent } from "../../helpers/dom/clickEvent";
|
||||
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
|
||||
import getGifDuration from "../../helpers/getGifDuration";
|
||||
import replaceContent from "../../helpers/dom/replaceContent";
|
||||
|
||||
type SendFileParams = Partial<{
|
||||
file: File,
|
||||
@ -36,32 +37,39 @@ type SendFileParams = Partial<{
|
||||
width: number,
|
||||
height: number,
|
||||
duration: number,
|
||||
noSound: boolean
|
||||
noSound: boolean,
|
||||
itemDiv: HTMLElement
|
||||
}>;
|
||||
|
||||
// TODO: .gif upload as video
|
||||
let currentPopup: PopupNewMedia;
|
||||
|
||||
export function getCurrentNewMediaPopup() {
|
||||
return currentPopup;
|
||||
}
|
||||
|
||||
export default class PopupNewMedia extends PopupElement {
|
||||
private input: HTMLElement;
|
||||
private mediaContainer: HTMLElement;
|
||||
private groupCheckboxField: CheckboxField;
|
||||
private wasInputValue = '';
|
||||
private mediaCheckboxField: CheckboxField;
|
||||
private wasInputValue: string;
|
||||
|
||||
private willAttach: Partial<{
|
||||
type: 'media' | 'document',
|
||||
isMedia: true,
|
||||
group: boolean,
|
||||
sendFileDetails: SendFileParams[]
|
||||
}> = {
|
||||
sendFileDetails: [],
|
||||
group: false
|
||||
};
|
||||
}>;
|
||||
private inputField: InputField;
|
||||
|
||||
constructor(private chat: Chat, files: File[], willAttachType: PopupNewMedia['willAttach']['type']) {
|
||||
super('popup-send-photo popup-new-media', null, {closable: true, withConfirm: 'Modal.Send', confirmShortcutIsSendShortcut: true});
|
||||
constructor(private chat: Chat, private files: File[], willAttachType: PopupNewMedia['willAttach']['type']) {
|
||||
super('popup-send-photo popup-new-media', null, {closable: true, withConfirm: 'Modal.Send', confirmShortcutIsSendShortcut: true, body: true});
|
||||
|
||||
this.willAttach.type = willAttachType;
|
||||
this.willAttach = {
|
||||
type: willAttachType,
|
||||
sendFileDetails: [],
|
||||
group: false
|
||||
};
|
||||
|
||||
attachClickEvent(this.btnConfirm, () => this.send(), {listenerSetter: this.listenerSetter});
|
||||
|
||||
@ -95,41 +103,98 @@ export default class PopupNewMedia extends PopupElement {
|
||||
placeholder: 'PreviewSender.CaptionPlaceholder',
|
||||
label: 'Caption',
|
||||
name: 'photo-caption',
|
||||
maxLength: rootScope.config.caption_length_max,
|
||||
showLengthOn: 80
|
||||
maxLength: rootScope.config.caption_length_max
|
||||
});
|
||||
this.input = this.inputField.input;
|
||||
|
||||
this.inputField.value = this.wasInputValue = this.chat.input.messageInputField.input.innerHTML;
|
||||
this.chat.input.messageInputField.value = '';
|
||||
|
||||
this.container.append(scrollable.container);
|
||||
this.body.append(scrollable.container);
|
||||
this.container.append(this.inputField.container);
|
||||
|
||||
if(files.length > 1) {
|
||||
this.attachFiles();
|
||||
|
||||
this.addEventListener('close', () => {
|
||||
this.files = [];
|
||||
currentPopup = undefined;
|
||||
});
|
||||
|
||||
currentPopup = this;
|
||||
}
|
||||
|
||||
public appendDrops(element: HTMLElement) {
|
||||
this.body.append(element);
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this.willAttach.type;
|
||||
}
|
||||
|
||||
set type(type: PopupNewMedia['willAttach']['type']) {
|
||||
this.willAttach.type = type;
|
||||
}
|
||||
|
||||
private appendGroupCheckboxField() {
|
||||
const good = this.files.length > 1;
|
||||
if(good && !this.groupCheckboxField) {
|
||||
this.groupCheckboxField = new CheckboxField({
|
||||
text: 'PreviewSender.GroupItems',
|
||||
name: 'group-items'
|
||||
});
|
||||
this.container.append(this.groupCheckboxField.label, this.inputField.container);
|
||||
this.container.append(...[this.groupCheckboxField.label, this.mediaCheckboxField?.label, this.inputField.container].filter(Boolean));
|
||||
|
||||
this.groupCheckboxField.input.checked = true;
|
||||
this.willAttach.group = true;
|
||||
this.groupCheckboxField.setValueSilently(this.willAttach.group);
|
||||
|
||||
this.listenerSetter.add(this.groupCheckboxField.input)('change', () => {
|
||||
const checked = this.groupCheckboxField.input.checked;
|
||||
const checked = this.groupCheckboxField.checked;
|
||||
|
||||
this.willAttach.group = checked;
|
||||
this.willAttach.sendFileDetails.length = 0;
|
||||
|
||||
//this.mediaContainer.innerHTML = '';
|
||||
//this.container.classList.remove('is-media', 'is-document', 'is-album');
|
||||
this.attachFiles(files);
|
||||
this.attachFiles();
|
||||
});
|
||||
} else if(this.groupCheckboxField) {
|
||||
this.groupCheckboxField.label.classList.toggle('hide', !good);
|
||||
}
|
||||
|
||||
this.container.append(this.inputField.container);
|
||||
}
|
||||
|
||||
this.attachFiles(files);
|
||||
private appendMediaCheckboxField() {
|
||||
const good = !!this.files.find(file => MEDIA_MIME_TYPES_SUPPORTED.has(file.type));
|
||||
if(good && !this.mediaCheckboxField) {
|
||||
this.mediaCheckboxField = new CheckboxField({
|
||||
text: 'PreviewSender.CompressFile',
|
||||
name: 'compress-items'
|
||||
});
|
||||
this.container.append(...[this.groupCheckboxField?.label, this.mediaCheckboxField.label, this.inputField.container].filter(Boolean));
|
||||
|
||||
this.mediaCheckboxField.setValueSilently(this.willAttach.type === 'media');
|
||||
|
||||
this.listenerSetter.add(this.mediaCheckboxField.input)('change', () => {
|
||||
const checked = this.mediaCheckboxField.checked;
|
||||
|
||||
this.willAttach.type = checked ? 'media' : 'document';
|
||||
|
||||
this.attachFiles();
|
||||
});
|
||||
} else if(this.mediaCheckboxField) {
|
||||
this.mediaCheckboxField.label.classList.toggle('hide', !good);
|
||||
}
|
||||
}
|
||||
|
||||
public addFiles(files: File[]) {
|
||||
const toPush = files.filter(file => {
|
||||
const found = this.files.find(_file => {
|
||||
return _file.lastModified === file.lastModified && _file.name === file.name && _file.size === file.size;
|
||||
});
|
||||
|
||||
return !found;
|
||||
});
|
||||
|
||||
if(toPush.length) {
|
||||
this.files.push(...toPush);
|
||||
this.attachFiles();
|
||||
}
|
||||
}
|
||||
|
||||
private onKeyDown = (e: KeyboardEvent) => {
|
||||
@ -144,7 +209,7 @@ export default class PopupNewMedia extends PopupElement {
|
||||
}
|
||||
};
|
||||
|
||||
public send(force = false) {
|
||||
private send(force = false) {
|
||||
if(this.chat.type === 'scheduled' && !force) {
|
||||
this.chat.input.scheduleSending(() => {
|
||||
this.send(true);
|
||||
@ -162,324 +227,335 @@ export default class PopupNewMedia extends PopupElement {
|
||||
this.hide();
|
||||
const willAttach = this.willAttach;
|
||||
willAttach.isMedia = willAttach.type === 'media' ? true : undefined;
|
||||
const {sendFileDetails, isMedia} = willAttach;
|
||||
|
||||
//console.log('will send files with options:', willAttach);
|
||||
|
||||
const peerId = this.chat.peerId;
|
||||
const input = this.chat.input;
|
||||
const silent = input.sendSilent;
|
||||
const scheduleDate = input.scheduleDate;
|
||||
const {peerId, input} = this.chat;
|
||||
const {sendSilent, scheduleDate} = input;
|
||||
|
||||
if(willAttach.sendFileDetails.length > 1 && willAttach.group) {
|
||||
for(let i = 0; i < willAttach.sendFileDetails.length;) {
|
||||
let firstType = willAttach.sendFileDetails[i].file.type.split('/')[0];
|
||||
for(var k = 0; k < 10 && i < willAttach.sendFileDetails.length; ++i, ++k) {
|
||||
const type = willAttach.sendFileDetails[i].file.type.split('/')[0];
|
||||
if(firstType !== type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sendFileDetails.forEach(d => {
|
||||
d.itemDiv = undefined;
|
||||
});
|
||||
|
||||
const w = {...willAttach};
|
||||
w.sendFileDetails = willAttach.sendFileDetails.slice(i - k, i);
|
||||
|
||||
this.chat.appMessagesManager.sendAlbum(peerId, w.sendFileDetails.map(d => d.file), Object.assign({
|
||||
caption,
|
||||
replyToMsgId: input.replyToMsgId,
|
||||
const {length} = sendFileDetails;
|
||||
const replyToMsgId = input.replyToMsgId;
|
||||
this.iterate((sendFileDetails) => {
|
||||
if(caption && sendFileDetails.length !== length) {
|
||||
this.chat.appMessagesManager.sendText(peerId, caption, {
|
||||
replyToMsgId,
|
||||
threadId: this.chat.threadId,
|
||||
isMedia: willAttach.isMedia,
|
||||
silent,
|
||||
silent: sendSilent,
|
||||
scheduleDate,
|
||||
clearDraft: true as true
|
||||
}, w));
|
||||
clearDraft: true
|
||||
});
|
||||
|
||||
caption = undefined;
|
||||
input.replyToMsgId = this.chat.threadId;
|
||||
}
|
||||
} else {
|
||||
if(caption) {
|
||||
if(willAttach.sendFileDetails.length > 1) {
|
||||
this.chat.appMessagesManager.sendText(peerId, caption, {
|
||||
replyToMsgId: input.replyToMsgId,
|
||||
threadId: this.chat.threadId,
|
||||
silent,
|
||||
scheduleDate,
|
||||
clearDraft: true
|
||||
});
|
||||
caption = '';
|
||||
//input.replyToMsgId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const promises = willAttach.sendFileDetails.map(params => {
|
||||
const promise = this.chat.appMessagesManager.sendFile(peerId, params.file, Object.assign({
|
||||
//isMedia: willAttach.isMedia,
|
||||
isMedia: willAttach.isMedia,
|
||||
caption,
|
||||
replyToMsgId: input.replyToMsgId,
|
||||
threadId: this.chat.threadId,
|
||||
silent,
|
||||
scheduleDate,
|
||||
clearDraft: true as true
|
||||
}, params));
|
||||
|
||||
caption = '';
|
||||
return promise;
|
||||
});
|
||||
const w = {
|
||||
...willAttach,
|
||||
sendFileDetails
|
||||
};
|
||||
|
||||
input.replyToMsgId = this.chat.threadId;
|
||||
}
|
||||
this.chat.appMessagesManager.sendAlbum(peerId, w.sendFileDetails.map(d => d.file), Object.assign({
|
||||
caption,
|
||||
replyToMsgId,
|
||||
threadId: this.chat.threadId,
|
||||
isMedia: isMedia,
|
||||
silent: sendSilent,
|
||||
scheduleDate,
|
||||
clearDraft: true as true
|
||||
}, w));
|
||||
|
||||
//Promise.all(promises);
|
||||
|
||||
//appMessagesManager.sendFile(appImManager.peerId, willAttach.file, willAttach);
|
||||
caption = undefined;
|
||||
});
|
||||
|
||||
input.replyToMsgId = this.chat.threadId;
|
||||
input.onMessageSent();
|
||||
}
|
||||
|
||||
public attachFile = (file: File) => {
|
||||
const willAttach = this.willAttach;
|
||||
return new Promise<HTMLDivElement>((resolve) => {
|
||||
const params: SendFileParams = {};
|
||||
params.file = file;
|
||||
//console.log('selected file:', file, typeof(file), willAttach);
|
||||
const itemDiv = document.createElement('div');
|
||||
switch(willAttach.type) {
|
||||
case 'media': {
|
||||
const isVideo = file.type.indexOf('video/') === 0;
|
||||
private attachMedia(file: File, params: SendFileParams, itemDiv: HTMLElement) {
|
||||
itemDiv.classList.add('popup-item-media');
|
||||
|
||||
itemDiv.classList.add('popup-item-media');
|
||||
const isVideo = file.type.startsWith('video/');
|
||||
|
||||
if(isVideo) {
|
||||
const video = document.createElement('video');
|
||||
const source = document.createElement('source');
|
||||
source.src = params.objectURL = URL.createObjectURL(file);
|
||||
video.autoplay = true;
|
||||
video.controls = false;
|
||||
video.muted = true;
|
||||
video.setAttribute('playsinline', 'true');
|
||||
let promise: Promise<void>;
|
||||
if(isVideo) {
|
||||
const video = document.createElement('video');
|
||||
const source = document.createElement('source');
|
||||
source.src = params.objectURL = URL.createObjectURL(file);
|
||||
video.autoplay = true;
|
||||
video.controls = false;
|
||||
video.muted = true;
|
||||
video.setAttribute('playsinline', 'true');
|
||||
|
||||
video.addEventListener('timeupdate', () => {
|
||||
video.pause();
|
||||
}, {once: true});
|
||||
video.addEventListener('timeupdate', () => {
|
||||
video.pause();
|
||||
}, {once: true});
|
||||
|
||||
onMediaLoad(video).then(() => {
|
||||
params.width = video.videoWidth;
|
||||
params.height = video.videoHeight;
|
||||
params.duration = Math.floor(video.duration);
|
||||
promise = onMediaLoad(video).then(() => {
|
||||
params.width = video.videoWidth;
|
||||
params.height = video.videoHeight;
|
||||
params.duration = Math.floor(video.duration);
|
||||
|
||||
const audioDecodedByteCount = (video as any).webkitAudioDecodedByteCount;
|
||||
if(audioDecodedByteCount !== undefined) {
|
||||
params.noSound = !audioDecodedByteCount;
|
||||
}
|
||||
|
||||
itemDiv.append(video);
|
||||
return createPosterFromVideo(video).then(thumb => {
|
||||
params.thumb = {
|
||||
url: URL.createObjectURL(thumb.blob),
|
||||
...thumb
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
video.append(source);
|
||||
} else {
|
||||
const img = new Image();
|
||||
promise = new Promise<void>((resolve) => {
|
||||
img.onload = () => {
|
||||
params.width = img.naturalWidth;
|
||||
params.height = img.naturalHeight;
|
||||
|
||||
itemDiv.append(img);
|
||||
|
||||
if(file.type === 'image/gif') {
|
||||
params.noSound = true;
|
||||
|
||||
Promise.all([
|
||||
getGifDuration(img).then(duration => {
|
||||
params.duration = Math.ceil(duration);
|
||||
}),
|
||||
|
||||
const audioDecodedByteCount = (video as any).webkitAudioDecodedByteCount;
|
||||
if(audioDecodedByteCount !== undefined) {
|
||||
params.noSound = !audioDecodedByteCount;
|
||||
}
|
||||
|
||||
itemDiv.append(video);
|
||||
createPosterFromVideo(video).then(thumb => {
|
||||
createPosterFromMedia(img).then(thumb => {
|
||||
params.thumb = {
|
||||
url: URL.createObjectURL(thumb.blob),
|
||||
...thumb
|
||||
};
|
||||
resolve(itemDiv);
|
||||
});
|
||||
})
|
||||
]).then(() => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
video.append(source);
|
||||
} else {
|
||||
const img = new Image();
|
||||
img.src = params.objectURL = URL.createObjectURL(file);
|
||||
img.onload = () => {
|
||||
params.width = img.naturalWidth;
|
||||
params.height = img.naturalHeight;
|
||||
|
||||
itemDiv.append(img);
|
||||
|
||||
if(file.type === 'image/gif') {
|
||||
params.noSound = true;
|
||||
|
||||
Promise.all([
|
||||
getGifDuration(img).then(duration => {
|
||||
params.duration = Math.ceil(duration);
|
||||
}),
|
||||
|
||||
createPosterFromMedia(img).then(thumb => {
|
||||
params.thumb = {
|
||||
url: URL.createObjectURL(thumb.blob),
|
||||
...thumb
|
||||
};
|
||||
})
|
||||
]).then(() => {
|
||||
resolve(itemDiv);
|
||||
});
|
||||
} else {
|
||||
resolve(itemDiv);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'document': {
|
||||
const isPhoto = file.type.indexOf('image/') !== -1;
|
||||
const isAudio = file.type.indexOf('audio/') !== -1;
|
||||
if(isPhoto || isAudio) {
|
||||
params.objectURL = URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
const doc = {
|
||||
_: 'document',
|
||||
file: file,
|
||||
file_name: file.name || '',
|
||||
fileName: file.name ? RichTextProcessor.wrapEmojiText(file.name) : '',
|
||||
size: file.size,
|
||||
type: isPhoto ? 'photo' : 'doc'
|
||||
} as MyDocument;
|
||||
|
||||
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||
cacheContext.url = params.objectURL;
|
||||
cacheContext.downloaded = file.size;
|
||||
|
||||
const docDiv = wrapDocument({
|
||||
message: {
|
||||
_: 'message',
|
||||
pFlags: {
|
||||
is_outgoing: true
|
||||
},
|
||||
mid: 0,
|
||||
peerId: 0,
|
||||
media: {
|
||||
_: 'messageMediaDocument',
|
||||
document: doc
|
||||
}
|
||||
} as any
|
||||
});
|
||||
|
||||
const finish = () => {
|
||||
itemDiv.append(docDiv);
|
||||
resolve(itemDiv);
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
willAttach.sendFileDetails.push(params);
|
||||
});
|
||||
};
|
||||
|
||||
public attachFiles(files: File[]) {
|
||||
const container = this.container;
|
||||
const willAttach = this.willAttach;
|
||||
|
||||
/* if(files.length > 10 && willAttach.type === 'media') {
|
||||
willAttach.type = 'document';
|
||||
} */
|
||||
|
||||
if(willAttach.type === 'media') {
|
||||
files = files.filter(file => MEDIA_MIME_TYPES_SUPPORTED.has(file.type));
|
||||
} else {
|
||||
files = files.slice();
|
||||
}
|
||||
|
||||
Promise.all(files.map(this.attachFile)).then(results => {
|
||||
this.container.classList.remove('is-media', 'is-document', 'is-album');
|
||||
this.mediaContainer.innerHTML = '';
|
||||
|
||||
if(files.length) {
|
||||
let key: LangPackKey;
|
||||
const args: any[] = [];
|
||||
if(willAttach.type === 'document') {
|
||||
key = 'PreviewSender.SendFile';
|
||||
args.push(files.length);
|
||||
container.classList.add('is-document');
|
||||
} else {
|
||||
container.classList.add('is-media');
|
||||
|
||||
let foundPhotos = 0;
|
||||
let foundVideos = 0;
|
||||
files.forEach(file => {
|
||||
if(file.type.indexOf('image/') === 0) ++foundPhotos;
|
||||
else if(file.type.indexOf('video/') === 0) ++foundVideos;
|
||||
});
|
||||
|
||||
const sum = foundPhotos + foundVideos;
|
||||
if(sum > 1 && willAttach.group) {
|
||||
key = 'PreviewSender.SendAlbum';
|
||||
const albumsLength = Math.ceil(sum / 10);
|
||||
args.push(albumsLength);
|
||||
} else if(foundPhotos) {
|
||||
key = 'PreviewSender.SendPhoto';
|
||||
args.push(foundPhotos);
|
||||
} else if(foundVideos) {
|
||||
key = 'PreviewSender.SendVideo';
|
||||
args.push(foundVideos);
|
||||
}
|
||||
}
|
||||
|
||||
this.title.textContent = '';
|
||||
this.title.append(i18n(key, args));
|
||||
}
|
||||
|
||||
if(willAttach.type === 'media') {
|
||||
if(willAttach.sendFileDetails.length > 1 && willAttach.group) {
|
||||
container.classList.add('is-album');
|
||||
|
||||
for(let i = 0; i < results.length; i += 10) {
|
||||
const albumContainer = document.createElement('div');
|
||||
albumContainer.classList.add('popup-album');
|
||||
|
||||
albumContainer.append(...results.slice(i, i + 10));
|
||||
prepareAlbum({
|
||||
container: albumContainer,
|
||||
items: willAttach.sendFileDetails.slice(i, i + 10).map(o => ({w: o.width, h: o.height})),
|
||||
maxWidth: 380,
|
||||
minWidth: 100,
|
||||
spacing: 4
|
||||
});
|
||||
|
||||
this.mediaContainer.append(albumContainer);
|
||||
}
|
||||
|
||||
//console.log('chatInput album layout:', layout);
|
||||
} else {
|
||||
for(let i = 0; i < results.length; ++i) {
|
||||
const params = willAttach.sendFileDetails[i];
|
||||
const div = results[i];
|
||||
const size = calcImageInBox(params.width, params.height, 380, 320);
|
||||
div.style.width = size.width + 'px';
|
||||
div.style.height = size.height + 'px';
|
||||
this.mediaContainer.append(div);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.mediaContainer.append(...results);
|
||||
}
|
||||
|
||||
// show now
|
||||
if(!this.element.classList.contains('active')) {
|
||||
this.listenerSetter.add(document.body)('keydown', this.onKeyDown);
|
||||
this.onClose = () => {
|
||||
if(this.wasInputValue) {
|
||||
this.chat.input.messageInputField.value = this.wasInputValue;
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
this.show();
|
||||
});
|
||||
|
||||
img.src = params.objectURL = URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private attachDocument(file: File, params: SendFileParams, itemDiv: HTMLElement): ReturnType<PopupNewMedia['attachMedia']> {
|
||||
itemDiv.classList.add('popup-item-document');
|
||||
|
||||
const isPhoto = file.type.startsWith('image/');
|
||||
const isAudio = file.type.startsWith('audio/');
|
||||
if(isPhoto || isAudio) {
|
||||
params.objectURL = URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
const doc = {
|
||||
_: 'document',
|
||||
file: file,
|
||||
file_name: file.name || '',
|
||||
fileName: file.name ? RichTextProcessor.wrapEmojiText(file.name) : '',
|
||||
size: file.size,
|
||||
type: isPhoto ? 'photo' : 'doc'
|
||||
} as MyDocument;
|
||||
|
||||
const cacheContext = appDownloadManager.getCacheContext(doc);
|
||||
cacheContext.url = params.objectURL;
|
||||
cacheContext.downloaded = file.size;
|
||||
|
||||
const docDiv = wrapDocument({
|
||||
message: {
|
||||
_: 'message',
|
||||
pFlags: {
|
||||
is_outgoing: true
|
||||
},
|
||||
mid: 0,
|
||||
peerId: 0,
|
||||
media: {
|
||||
_: 'messageMediaDocument',
|
||||
document: doc
|
||||
}
|
||||
} as any
|
||||
});
|
||||
|
||||
const promise = new Promise<void>((resolve) => {
|
||||
const finish = () => {
|
||||
itemDiv.append(docDiv);
|
||||
resolve();
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
params.itemDiv = itemDiv;
|
||||
|
||||
const promise = shouldCompress ? this.attachMedia(file, params, itemDiv) : this.attachDocument(file, params, itemDiv);
|
||||
willAttach.sendFileDetails.push(params);
|
||||
return promise;
|
||||
};
|
||||
|
||||
private shouldCompress(mimeType: string) {
|
||||
return this.willAttach.type === 'media' && MEDIA_MIME_TYPES_SUPPORTED.has(mimeType);
|
||||
}
|
||||
|
||||
private onRender() {
|
||||
// show now
|
||||
if(!this.element.classList.contains('active')) {
|
||||
this.listenerSetter.add(document.body)('keydown', this.onKeyDown);
|
||||
this.addEventListener('close', () => {
|
||||
if(this.wasInputValue) {
|
||||
this.chat.input.messageInputField.value = this.wasInputValue;
|
||||
}
|
||||
});
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
|
||||
private setTitle() {
|
||||
const {willAttach, title, files} = this;
|
||||
let key: LangPackKey;
|
||||
const args: FormatterArguments = [];
|
||||
if(willAttach.type === 'document') {
|
||||
key = 'PreviewSender.SendFile';
|
||||
args.push(files.length);
|
||||
} else {
|
||||
let foundPhotos = 0, foundVideos = 0, foundFiles = 0;
|
||||
files.forEach(file => {
|
||||
if(file.type.startsWith('image/')) ++foundPhotos;
|
||||
else if(file.type.startsWith('video/')) ++foundVideos;
|
||||
else ++foundFiles;
|
||||
});
|
||||
|
||||
if([foundPhotos, foundVideos, foundFiles].filter(n => n > 0).length > 1) {
|
||||
key = 'PreviewSender.SendFile';
|
||||
args.push(files.length);
|
||||
} else
|
||||
|
||||
/* const sum = foundPhotos + foundVideos;
|
||||
if(sum > 1 && willAttach.group) {
|
||||
key = 'PreviewSender.SendAlbum';
|
||||
const albumsLength = Math.ceil(sum / 10);
|
||||
args.push(albumsLength);
|
||||
} else */if(foundPhotos) {
|
||||
key = 'PreviewSender.SendPhoto';
|
||||
args.push(foundPhotos);
|
||||
} else if(foundVideos) {
|
||||
key = 'PreviewSender.SendVideo';
|
||||
args.push(foundVideos);
|
||||
}
|
||||
}
|
||||
|
||||
replaceContent(title, i18n(key, args));
|
||||
}
|
||||
|
||||
private appendMediaToContainer(div: HTMLElement, 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';
|
||||
}
|
||||
|
||||
this.mediaContainer.append(div);
|
||||
}
|
||||
|
||||
private iterate(cb: (sendFileDetails: SendFileParams[]) => void) {
|
||||
const {sendFileDetails} = this.willAttach;
|
||||
if(!this.willAttach.group) {
|
||||
sendFileDetails.forEach(p => cb([p]));
|
||||
return;
|
||||
}
|
||||
|
||||
const length = sendFileDetails.length;
|
||||
for(let i = 0; i < length;) {
|
||||
const firstType = sendFileDetails[i].file.type;
|
||||
let k = 0;
|
||||
for(; k < 10 && i < length; ++i, ++k) {
|
||||
const type = sendFileDetails[i].file.type;
|
||||
if(this.shouldCompress(firstType) !== this.shouldCompress(type)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cb(sendFileDetails.slice(i - k, i));
|
||||
}
|
||||
}
|
||||
|
||||
private attachFiles() {
|
||||
const {files, willAttach, mediaContainer} = this;
|
||||
willAttach.sendFileDetails.length = 0;
|
||||
|
||||
this.appendGroupCheckboxField();
|
||||
this.appendMediaCheckboxField();
|
||||
|
||||
Promise.all(files.map(this.attachFile)).then(() => {
|
||||
mediaContainer.innerHTML = '';
|
||||
|
||||
if(!files.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setTitle();
|
||||
|
||||
this.iterate((sendFileDetails) => {
|
||||
if(this.shouldCompress(sendFileDetails[0].file.type) && sendFileDetails.length > 1) {
|
||||
const albumContainer = document.createElement('div');
|
||||
albumContainer.classList.add('popup-item-album', 'popup-item');
|
||||
albumContainer.append(...sendFileDetails.map(s => s.itemDiv));
|
||||
|
||||
prepareAlbum({
|
||||
container: albumContainer,
|
||||
items: sendFileDetails.map(o => ({w: o.width, h: o.height})),
|
||||
maxWidth: 380,
|
||||
minWidth: 100,
|
||||
spacing: 4
|
||||
});
|
||||
|
||||
mediaContainer.append(albumContainer);
|
||||
} else {
|
||||
sendFileDetails.forEach((params) => {
|
||||
this.appendMediaToContainer(params.itemDiv, params);
|
||||
});
|
||||
}
|
||||
});
|
||||
}).then(() => {
|
||||
this.onRender();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ export default class PopupPickUser extends PopupElement {
|
||||
constructor(options: {
|
||||
peerTypes: AppSelectPeers['peerType'],
|
||||
onSelect?: (peerId: PeerId) => Promise<void> | void,
|
||||
onClose?: () => void,
|
||||
placeholder: LangPackKey,
|
||||
chatRightsAction?: AppSelectPeers['chatRightsAction'],
|
||||
peerId?: number,
|
||||
@ -23,8 +22,6 @@ export default class PopupPickUser extends PopupElement {
|
||||
}) {
|
||||
super('popup-forward', null, {closable: true, overlayClosable: true, body: true});
|
||||
|
||||
if(options.onClose) this.onClose = options.onClose;
|
||||
|
||||
this.selector = new AppSelectPeers({
|
||||
appendTo: this.body,
|
||||
onChange: async() => {
|
||||
|
@ -38,9 +38,9 @@ export default class PopupStickers extends PopupElement {
|
||||
|
||||
this.header.append(this.h6);
|
||||
|
||||
this.onClose = () => {
|
||||
this.addEventListener('close', () => {
|
||||
animationIntersector.setOnlyOnePlayableGroup('');
|
||||
};
|
||||
});
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('sticker-set');
|
||||
|
@ -586,7 +586,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
||||
docDiv.classList.add('document-with-thumb');
|
||||
|
||||
let imgs: HTMLImageElement[] = [];
|
||||
if(uploading) {
|
||||
if(message.pFlags.is_outgoing) {
|
||||
icoDiv.innerHTML = `<img src="${cacheContext.url}">`;
|
||||
imgs.push(icoDiv.firstElementChild as HTMLImageElement);
|
||||
} else {
|
||||
@ -647,9 +647,9 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
||||
|
||||
docDiv.prepend(icoDiv);
|
||||
|
||||
/* if(!uploading && message.pFlags.is_outgoing) {
|
||||
if(!uploading && message.pFlags.is_outgoing) {
|
||||
return docDiv;
|
||||
} */
|
||||
}
|
||||
|
||||
let downloadDiv: HTMLElement, preloader: ProgressivePreloader = null;
|
||||
const onLoad = () => {
|
||||
@ -700,7 +700,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
||||
downloadDiv = docDiv.querySelector('.document-download');
|
||||
preloader = new ProgressivePreloader();
|
||||
preloader.attach(downloadDiv, false, appDocsManager.downloading.get(doc.id));
|
||||
} else if(!(cacheContext.downloaded && !uploading)) {
|
||||
} else if(!cacheContext.downloaded || uploading) {
|
||||
downloadDiv = docDiv.querySelector('.document-download');
|
||||
preloader = message.media.preloader as ProgressivePreloader;
|
||||
|
||||
|
@ -791,7 +791,12 @@ const lang = {
|
||||
"other_value": "Show More (%d)"
|
||||
},
|
||||
//"PeerInfo.Confirm.DeleteGroupConfirmation": "Wait! Deleting this group will remove all members and all messages will be lost. Delete the group anyway?",
|
||||
"Preview.Dragging.AddItems": {
|
||||
"one_value": "Add Item",
|
||||
"other_value": "Add Items"
|
||||
},
|
||||
"PreviewSender.CaptionPlaceholder": "Add a caption...",
|
||||
"PreviewSender.CompressFile": "Send compressed",
|
||||
"PreviewSender.SendFile": {
|
||||
"one_value": "Send File",
|
||||
"other_value": "Send %d Files"
|
||||
|
@ -24,7 +24,7 @@ import appPhotosManager from './appPhotosManager';
|
||||
import appProfileManager from './appProfileManager';
|
||||
import appStickersManager from './appStickersManager';
|
||||
import appWebPagesManager from './appWebPagesManager';
|
||||
import PopupNewMedia from '../../components/popups/newMedia';
|
||||
import PopupNewMedia, { getCurrentNewMediaPopup } from '../../components/popups/newMedia';
|
||||
import MarkupTooltip from '../../components/chat/markupTooltip';
|
||||
import { IS_TOUCH_SUPPORTED } from '../../environment/touchSupport';
|
||||
import appPollsManager from './appPollsManager';
|
||||
@ -901,6 +901,7 @@ export class AppImManager {
|
||||
|
||||
private attachDragAndDropListeners() {
|
||||
const drops: ChatDragAndDrop[] = [];
|
||||
const mediaDrops: ChatDragAndDrop[] = [];
|
||||
let mounted = false;
|
||||
const toggle = async(e: DragEvent, mount: boolean) => {
|
||||
if(mount === mounted) return;
|
||||
@ -909,63 +910,84 @@ export class AppImManager {
|
||||
// @ts-ignore
|
||||
const isFiles = _types.contains ? _types.contains('Files') : _types.indexOf('Files') >= 0;
|
||||
|
||||
if(!isFiles || !this.canDrag()) { // * skip dragging text case
|
||||
const newMediaPopup = getCurrentNewMediaPopup();
|
||||
if(!isFiles || (!this.canDrag() && !newMediaPopup)) { // * skip dragging text case
|
||||
counter = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(mount && !drops.length) {
|
||||
const _dropsContainer = newMediaPopup ? mediaDropsContainer : dropsContainer;
|
||||
const _drops = newMediaPopup ? mediaDrops : drops;
|
||||
|
||||
if(mount && !_drops.length) {
|
||||
const types: string[] = await getFilesFromEvent(e, true);
|
||||
const force = isFiles && !types.length; // * can't get file items not from 'drop' on Safari
|
||||
|
||||
const foundMedia = types.filter(t => MEDIA_MIME_TYPES_SUPPORTED.has(t)).length;
|
||||
const foundDocuments = types.length - foundMedia;
|
||||
// const foundDocuments = types.length - foundMedia;
|
||||
|
||||
this.log('drag files', types);
|
||||
|
||||
if(types.length || force) {
|
||||
drops.push(new ChatDragAndDrop(dropsContainer, {
|
||||
icon: 'dragfiles',
|
||||
header: 'Chat.DropTitle',
|
||||
subtitle: 'Chat.DropAsFilesDesc',
|
||||
onDrop: (e: DragEvent) => {
|
||||
toggle(e, false);
|
||||
appImManager.log('drop', e);
|
||||
appImManager.onDocumentPaste(e, 'document');
|
||||
}
|
||||
}));
|
||||
|
||||
if(newMediaPopup) {
|
||||
newMediaPopup.appendDrops(_dropsContainer);
|
||||
|
||||
if(types.length || force) {
|
||||
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
||||
header: 'Preview.Dragging.AddItems',
|
||||
headerArgs: [types.length],
|
||||
onDrop: (e: DragEvent) => {
|
||||
toggle(e, false);
|
||||
appImManager.log('drop', e);
|
||||
appImManager.onDocumentPaste(e, 'document');
|
||||
}
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
if(types.length || force) {
|
||||
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
||||
icon: 'dragfiles',
|
||||
header: 'Chat.DropTitle',
|
||||
subtitle: 'Chat.DropAsFilesDesc',
|
||||
onDrop: (e: DragEvent) => {
|
||||
toggle(e, false);
|
||||
appImManager.log('drop', e);
|
||||
appImManager.onDocumentPaste(e, 'document');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// if((foundMedia && !foundDocuments) || force) {
|
||||
if(foundMedia || force) {
|
||||
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
||||
icon: 'dragmedia',
|
||||
header: 'Chat.DropTitle',
|
||||
subtitle: 'Chat.DropQuickDesc',
|
||||
onDrop: (e: DragEvent) => {
|
||||
toggle(e, false);
|
||||
appImManager.log('drop', e);
|
||||
appImManager.onDocumentPaste(e, 'media');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this.chat.container.append(_dropsContainer);
|
||||
}
|
||||
|
||||
if((foundMedia && !foundDocuments) || force) {
|
||||
drops.push(new ChatDragAndDrop(dropsContainer, {
|
||||
icon: 'dragmedia',
|
||||
header: 'Chat.DropTitle',
|
||||
subtitle: 'Chat.DropQuickDesc',
|
||||
onDrop: (e: DragEvent) => {
|
||||
toggle(e, false);
|
||||
appImManager.log('drop', e);
|
||||
appImManager.onDocumentPaste(e, 'media');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this.chat.container.append(dropsContainer);
|
||||
}
|
||||
|
||||
//if(!mount) return;
|
||||
|
||||
SetTransition(dropsContainer, 'is-visible', mount, 200, () => {
|
||||
SetTransition(_dropsContainer, 'is-visible', mount, 200, () => {
|
||||
if(!mount) {
|
||||
drops.forEach(drop => {
|
||||
_drops.forEach(drop => {
|
||||
drop.destroy();
|
||||
});
|
||||
|
||||
drops.length = 0;
|
||||
_drops.length = 0;
|
||||
}
|
||||
});
|
||||
|
||||
if(mount) {
|
||||
drops.forEach(drop => {
|
||||
_drops.forEach(drop => {
|
||||
drop.setPath();
|
||||
});
|
||||
} else {
|
||||
@ -1003,6 +1025,8 @@ export class AppImManager {
|
||||
|
||||
const dropsContainer = document.createElement('div');
|
||||
dropsContainer.classList.add('drops-container');
|
||||
|
||||
const mediaDropsContainer = dropsContainer.cloneNode(true) as HTMLElement;
|
||||
}
|
||||
|
||||
private canDrag() {
|
||||
@ -1011,7 +1035,8 @@ export class AppImManager {
|
||||
}
|
||||
|
||||
private onDocumentPaste = (e: ClipboardEvent | DragEvent, attachType?: 'media' | 'document') => {
|
||||
if(!this.canDrag()) return;
|
||||
const newMediaPopup = getCurrentNewMediaPopup();
|
||||
if(!this.canDrag() && !newMediaPopup) return;
|
||||
|
||||
//console.log('document paste');
|
||||
//console.log('item', event.clipboardData.getData());
|
||||
@ -1027,12 +1052,13 @@ export class AppImManager {
|
||||
|
||||
getFilesFromEvent(e).then((files: File[]) => {
|
||||
if(files.length) {
|
||||
if(/* attachType === 'media' && */files.find(file => !MEDIA_MIME_TYPES_SUPPORTED.has(file.type))) {
|
||||
attachType = 'document';
|
||||
if(newMediaPopup) {
|
||||
newMediaPopup.addFiles(files);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const chatInput = this.chat.input;
|
||||
chatInput.willAttachType = attachType || (MEDIA_MIME_TYPES_SUPPORTED.has(files[0].type) ? 'media' : "document");
|
||||
chatInput.willAttachType = attachType || (MEDIA_MIME_TYPES_SUPPORTED.has(files[0].type) ? 'media' : 'document');
|
||||
new PopupNewMedia(this.chat, files, chatInput.willAttachType);
|
||||
}
|
||||
});
|
||||
|
@ -166,7 +166,7 @@ export class AppMessagesManager {
|
||||
}
|
||||
} = {};
|
||||
|
||||
private sendSmthLazyLoadQueue = new LazyLoadQueueBase(1);
|
||||
private sendSmthLazyLoadQueue = new LazyLoadQueueBase(10);
|
||||
|
||||
private needSingleMessages: Map<PeerId, Map<number, CancellablePromise<Message>>> = new Map();
|
||||
private fetchSingleMessagesPromise: Promise<void> = null;
|
||||
@ -1115,7 +1115,7 @@ export class AppMessagesManager {
|
||||
};
|
||||
|
||||
const inputPeer = appPeersManager.getInputPeerById(peerId);
|
||||
const invoke = (multiMedia: any[]) => {
|
||||
const invoke = (multiMedia: InputSingleMedia[]) => {
|
||||
this.setTyping(peerId, {_: 'sendMessageCancelAction'});
|
||||
|
||||
this.sendSmthLazyLoadQueue.push({
|
||||
@ -1136,15 +1136,15 @@ export class AppMessagesManager {
|
||||
});
|
||||
};
|
||||
|
||||
const promises: Promise<InputSingleMedia>[] = messages.map((message, idx) => {
|
||||
return (message.send() as Promise<InputMedia>).then((inputMedia: InputMedia) => {
|
||||
const promises: Promise<InputSingleMedia>[] = messages.map((message) => {
|
||||
return (message.send() as Promise<InputMedia>).then((inputMedia) => {
|
||||
return apiManager.invokeApi('messages.uploadMedia', {
|
||||
peer: inputPeer,
|
||||
media: inputMedia
|
||||
});
|
||||
})
|
||||
.then(messageMedia => {
|
||||
let inputMedia: any;
|
||||
let inputMedia: InputMedia;
|
||||
if(messageMedia._ === 'messageMediaPhoto') {
|
||||
const photo = appPhotosManager.savePhoto(messageMedia.photo);
|
||||
inputMedia = appPhotosManager.getMediaInput(photo);
|
||||
|
@ -663,6 +663,30 @@ $chat-helper-size: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.drops-container {
|
||||
--padding: 20px;
|
||||
--pinned-floating-height: 0px;
|
||||
top: calc(56px + var(--pinned-floating-height) + var(--padding));
|
||||
|
||||
@include respond-to(medium-screens) {
|
||||
//transition: transform var(--layer-transition);
|
||||
|
||||
body.is-right-column-shown & {
|
||||
//left: calc(var(--right-column-width) / -2);
|
||||
right: calc(var(--right-column-width));
|
||||
}
|
||||
}
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
--padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.drop {
|
||||
max-width: 696px;
|
||||
--wrapper-padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-input-wrapper {
|
||||
|
@ -1039,17 +1039,13 @@ $bubble-beside-button-width: 38px;
|
||||
&-content {
|
||||
//max-width: 300px;
|
||||
position: absolute;
|
||||
max-width: calc(100% - 1.875rem);
|
||||
max-width: calc(100% - 1.25rem);
|
||||
height: auto;
|
||||
min-height: 32px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.is-media .reply-content {
|
||||
max-width: calc(100% - 1.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
// &:not(.just-media):not(.is-message-empty) .reply {
|
||||
@ -1068,7 +1064,7 @@ $bubble-beside-button-width: 38px;
|
||||
margin-bottom: 0;
|
||||
background-color: var(--message-highlightning-color);
|
||||
white-space: nowrap;
|
||||
max-width: 300px;
|
||||
max-width: 15rem;
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
max-width: calc(100vw - #{$chat-padding-handhelds * 2} - 10px - 100%);
|
||||
@ -1096,6 +1092,8 @@ $bubble-beside-button-width: 38px;
|
||||
|
||||
&-content {
|
||||
margin-top: 0;
|
||||
position: relative;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
&-title,
|
||||
@ -1620,7 +1618,6 @@ $bubble-beside-button-width: 38px;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
height: 1.125rem;
|
||||
|
||||
@ -1648,7 +1645,6 @@ $bubble-beside-button-width: 38px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 2.125rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -5,11 +5,10 @@
|
||||
*/
|
||||
|
||||
.drops-container {
|
||||
--padding: 20px;
|
||||
--pinned-floating-height: 0px;
|
||||
--padding: 0px;
|
||||
position: absolute !important;
|
||||
z-index: 3;
|
||||
top: calc(56px + var(--pinned-floating-height) + var(--padding));
|
||||
top: var(--padding);
|
||||
right: var(--padding);
|
||||
bottom: var(--padding);
|
||||
left: var(--padding);
|
||||
@ -20,19 +19,6 @@
|
||||
user-select: none;
|
||||
width: auto !important;
|
||||
|
||||
@include respond-to(medium-screens) {
|
||||
//transition: transform var(--layer-transition);
|
||||
|
||||
body.is-right-column-shown & {
|
||||
//left: calc(var(--right-column-width) / -2);
|
||||
right: calc(var(--right-column-width));
|
||||
}
|
||||
}
|
||||
|
||||
@include respond-to(handhelds) {
|
||||
--padding: 10px;
|
||||
}
|
||||
|
||||
&:not(.is-visible) {
|
||||
display: none;
|
||||
}
|
||||
@ -47,12 +33,13 @@
|
||||
}
|
||||
|
||||
.drop {
|
||||
--wrapper-padding: -4px;
|
||||
background-color: var(--surface-color);
|
||||
position: relative;
|
||||
//height: 100%;
|
||||
border-radius: $border-radius-big;
|
||||
width: 100%;
|
||||
max-width: 696px;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@ -65,10 +52,10 @@
|
||||
&-outline {
|
||||
&-wrapper {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
left: 15px;
|
||||
top: var(--wrapper-padding);
|
||||
right: var(--wrapper-padding);
|
||||
bottom: var(--wrapper-padding);
|
||||
left: var(--wrapper-padding);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -101,7 +88,10 @@
|
||||
&-header {
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
margin-top: -10px;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-top: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-height: 670px) {
|
||||
|
@ -16,43 +16,8 @@
|
||||
//max-height: unquote('min(640px, 100%)');
|
||||
max-height: 100%;
|
||||
|
||||
&.is-media:not(.is-album) {
|
||||
/* max-height: 425px; */
|
||||
|
||||
#{$parent}-photo {
|
||||
//max-height: 320px;
|
||||
margin: 0 auto;
|
||||
|
||||
img {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.is-album {
|
||||
#{$parent}-photo {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
|
||||
.album-item {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
img, video {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img, video {
|
||||
img,
|
||||
video {
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
@ -94,45 +59,12 @@
|
||||
|
||||
&-photo {
|
||||
max-width: 380px;
|
||||
//display: flex;
|
||||
overflow: hidden;
|
||||
//justify-content: center;
|
||||
width: fit-content;
|
||||
// width: fit-content;
|
||||
width: 100%;
|
||||
border-radius: $border-radius-medium;
|
||||
user-select: none;
|
||||
/* align-items: center; */
|
||||
|
||||
.document {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
cursor: default;
|
||||
padding-left: 3.75rem;
|
||||
height: 4.5rem;
|
||||
|
||||
&-name {
|
||||
font-weight: normal;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
&-ico {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 11px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
/* &.photo {
|
||||
.document-ico {
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
} */
|
||||
}
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,22 +107,85 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.popup-body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.checkbox-field {
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.popup-album,
|
||||
.popup-container:not(.is-album) .popup-item-media {
|
||||
.popup-item-album {
|
||||
position: relative;
|
||||
|
||||
.album-item {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-photo > .popup-item-media {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0 auto;
|
||||
|
||||
img {
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-photo > .popup-item {
|
||||
position: relative;
|
||||
border-radius: inherit;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.popup-album + .popup-album,
|
||||
.popup-container:not(.is-album) .popup-item-media + .popup-item-media {
|
||||
.popup-photo > .popup-item + .popup-item {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.drop {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.document {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
cursor: default;
|
||||
padding-left: 3.75rem;
|
||||
height: 4.5rem;
|
||||
|
||||
&-name {
|
||||
font-weight: normal;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
&-ico {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
line-height: 11px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
/* &.photo {
|
||||
.document-ico {
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
.popup-create-contact {
|
||||
|
Loading…
Reference in New Issue
Block a user