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:
morethanwords 2021-11-26 13:58:12 +04:00
parent 24c1a4af23
commit bbb754196e
18 changed files with 622 additions and 499 deletions

View File

@ -2557,13 +2557,15 @@ export default class ChatBubbles {
} }
return new Promise<PeerId>((resolve, reject) => { return new Promise<PeerId>((resolve, reject) => {
new PopupForward({ const popup = new PopupForward({
[this.peerId]: [] [this.peerId]: []
}, (peerId) => { }, (peerId) => {
resolve(peerId); resolve(peerId);
}, () => {
reject();
}, true); }, true);
popup.addEventListener('close', () => {
reject();
});
}); });
}); });

View File

@ -5,7 +5,7 @@
*/ */
import generatePathData from "../../helpers/generatePathData"; import generatePathData from "../../helpers/generatePathData";
import { i18n, LangPackKey } from "../../lib/langPack"; import { FormatterArguments, i18n, LangPackKey } from "../../lib/langPack";
export default class ChatDragAndDrop { export default class ChatDragAndDrop {
container: HTMLDivElement; container: HTMLDivElement;
@ -14,9 +14,10 @@ export default class ChatDragAndDrop {
path: SVGPathElement; path: SVGPathElement;
constructor(appendTo: HTMLElement, private options: { constructor(appendTo: HTMLElement, private options: {
icon: string, icon?: string,
header: LangPackKey, header: LangPackKey,
subtitle: LangPackKey, headerArgs?: FormatterArguments,
subtitle?: LangPackKey,
onDrop: (e: DragEvent) => void onDrop: (e: DragEvent) => void
}) { }) {
this.container = document.createElement('div'); 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 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
this.path.classList.add('drop-outline-path'); this.path.classList.add('drop-outline-path');
const dropIcon = document.createElement('div'); let dropIcon: HTMLElement;
dropIcon.classList.add('drop-icon', 'tgico-' + options.icon); if(options.icon) {
dropIcon = document.createElement('div');
dropIcon.classList.add('drop-icon', 'tgico-' + options.icon);
}
const dropHeader = document.createElement('div'); const dropHeader = document.createElement('div');
dropHeader.classList.add('drop-header'); dropHeader.classList.add('drop-header');
dropHeader.append(i18n(options.header)); dropHeader.append(i18n(options.header, options.headerArgs));
const dropSubtitle = document.createElement('div'); let dropSubtitle: HTMLElement;
dropSubtitle.classList.add('drop-subtitle'); if(options.subtitle) {
dropSubtitle.append(i18n(options.subtitle)); dropSubtitle = document.createElement('div');
dropSubtitle.classList.add('drop-subtitle');
dropSubtitle.append(i18n(options.subtitle));
}
this.svg.append(this.path); this.svg.append(this.path);
this.outlineWrapper.append(this.svg); 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); appendTo.append(this.container);
this.container.addEventListener('dragover', this.onDragOver); this.container.addEventListener('dragover', this.onDragOver);

View File

@ -1677,9 +1677,11 @@ export default class ChatInput {
this.clearHelper(); this.clearHelper();
this.updateSendBtn(); this.updateSendBtn();
let selected = false; let selected = false;
new PopupForward(copy(this.forwarding), () => { const popup = new PopupForward(copy(this.forwarding), () => {
selected = true; selected = true;
}, () => { });
popup.addEventListener('close', () => {
this.helperWaitingForward = false; this.helperWaitingForward = false;
if(!selected) { if(!selected) {

View File

@ -104,7 +104,7 @@ class InputField {
this.required = options.required; this.required = options.required;
this.validate = options.validate; this.validate = options.validate;
if(options.maxLength) { if(options.maxLength !== undefined && options.showLengthOn === undefined) {
options.showLengthOn = Math.min(40, Math.round(options.maxLength / 3)); options.showLengthOn = Math.min(40, Math.round(options.maxLength / 3));
} }

View File

@ -81,12 +81,12 @@ export default class PopupAvatar extends PopupElement {
this.container.append(this.cropContainer, this.btnConfirm, this.input); this.container.append(this.cropContainer, this.btnConfirm, this.input);
this.onCloseAfterTimeout = () => { this.addEventListener('closeAfterTimeout', () => {
this.cropper.removeHandlers(); this.cropper.removeHandlers();
if(this.image) { if(this.image) {
this.image.remove(); this.image.remove();
} }
}; });
} }
private resolve() { private resolve() {

View File

@ -11,7 +11,6 @@ export default class PopupForward extends PopupPickUser {
constructor( constructor(
peerIdMids: {[fromPeerId: PeerId]: number[]}, peerIdMids: {[fromPeerId: PeerId]: number[]},
onSelect?: (peerId: PeerId) => Promise<void> | void, onSelect?: (peerId: PeerId) => Promise<void> | void,
onClose?: () => void,
overrideOnSelect = false overrideOnSelect = false
) { ) {
super({ super({
@ -27,7 +26,6 @@ export default class PopupForward extends PopupPickUser {
appImManager.setInnerPeer(peerId); appImManager.setInnerPeer(peerId);
appImManager.chat.input.initMessagesForward(peerIdMids); appImManager.chat.input.initMessagesForward(peerIdMids);
}, },
onClose,
placeholder: 'ShareModal.Search.ForwardPlaceholder', placeholder: 'ShareModal.Search.ForwardPlaceholder',
chatRightsAction: 'send_messages', chatRightsAction: 'send_messages',
selfPresence: 'ChatYourSelf' selfPresence: 'ChatYourSelf'

View File

@ -16,6 +16,7 @@ import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEve
import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed"; import isSendShortcutPressed from "../../helpers/dom/isSendShortcutPressed";
import { cancelEvent } from "../../helpers/dom/cancelEvent"; import { cancelEvent } from "../../helpers/dom/cancelEvent";
import getKeyFromEvent from "../../helpers/dom/getKeyFromEvent"; import getKeyFromEvent from "../../helpers/dom/getKeyFromEvent";
import EventListenerBase from "../../helpers/eventListenerBase";
export type PopupButton = { export type PopupButton = {
text?: string, text?: string,
@ -35,7 +36,10 @@ export type PopupOptions = Partial<{
confirmShortcutIsSendShortcut: boolean confirmShortcutIsSendShortcut: boolean
}>; }>;
export default class PopupElement { export default class PopupElement extends EventListenerBase<{
close: () => void,
closeAfterTimeout: () => void
}> {
protected element = document.createElement('div'); protected element = document.createElement('div');
protected container = document.createElement('div'); protected container = document.createElement('div');
protected header = document.createElement('div'); protected header = document.createElement('div');
@ -45,8 +49,6 @@ export default class PopupElement {
protected body: HTMLElement; protected body: HTMLElement;
protected buttonsEl: HTMLElement; protected buttonsEl: HTMLElement;
protected onClose: () => void;
protected onCloseAfterTimeout: () => void;
protected onEscape: () => boolean = () => true; protected onEscape: () => boolean = () => true;
protected navigationItem: NavigationItem; protected navigationItem: NavigationItem;
@ -57,6 +59,8 @@ export default class PopupElement {
protected btnConfirmOnEnter: HTMLButtonElement; protected btnConfirmOnEnter: HTMLButtonElement;
constructor(className: string, protected buttons?: Array<PopupButton>, options: PopupOptions = {}) { constructor(className: string, protected buttons?: Array<PopupButton>, options: PopupOptions = {}) {
super(false);
this.element.classList.add('popup'); this.element.classList.add('popup');
this.element.className = 'popup' + (className ? ' ' + className : ''); this.element.className = 'popup' + (className ? ' ' + className : '');
this.container.classList.add('popup-container', 'z-depth-1'); this.container.classList.add('popup-container', 'z-depth-1');
@ -181,7 +185,7 @@ export default class PopupElement {
}; };
private destroy = () => { private destroy = () => {
this.onClose && this.onClose(); this.dispatchEvent('close');
this.element.classList.add('hiding'); this.element.classList.add('hiding');
this.element.classList.remove('active'); this.element.classList.remove('active');
this.listenerSetter.removeAll(); this.listenerSetter.removeAll();
@ -193,7 +197,8 @@ export default class PopupElement {
setTimeout(() => { setTimeout(() => {
this.element.remove(); this.element.remove();
this.onCloseAfterTimeout && this.onCloseAfterTimeout(); this.dispatchEvent('closeAfterTimeout');
this.cleanup();
animationIntersector.checkAnimations(false); animationIntersector.checkAnimations(false);
}, 150); }, 150);
}; };

View File

@ -14,7 +14,7 @@ import CheckboxField from "../checkboxField";
import SendContextMenu from "../chat/sendContextMenu"; import SendContextMenu from "../chat/sendContextMenu";
import { createPosterFromMedia, createPosterFromVideo, onMediaLoad } from "../../helpers/files"; import { createPosterFromMedia, createPosterFromVideo, onMediaLoad } from "../../helpers/files";
import { MyDocument } from "../../lib/appManagers/appDocsManager"; 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 appDownloadManager from "../../lib/appManagers/appDownloadManager";
import calcImageInBox from "../../helpers/calcImageInBox"; import calcImageInBox from "../../helpers/calcImageInBox";
import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd"; import placeCaretAtEnd from "../../helpers/dom/placeCaretAtEnd";
@ -24,6 +24,7 @@ import { MediaSize } from "../../helpers/mediaSizes";
import { attachClickEvent } from "../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../helpers/dom/clickEvent";
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport'; import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
import getGifDuration from "../../helpers/getGifDuration"; import getGifDuration from "../../helpers/getGifDuration";
import replaceContent from "../../helpers/dom/replaceContent";
type SendFileParams = Partial<{ type SendFileParams = Partial<{
file: File, file: File,
@ -36,32 +37,39 @@ type SendFileParams = Partial<{
width: number, width: number,
height: number, height: number,
duration: 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 { export default class PopupNewMedia extends PopupElement {
private input: HTMLElement; private input: HTMLElement;
private mediaContainer: HTMLElement; private mediaContainer: HTMLElement;
private groupCheckboxField: CheckboxField; private groupCheckboxField: CheckboxField;
private wasInputValue = ''; private mediaCheckboxField: CheckboxField;
private wasInputValue: string;
private willAttach: Partial<{ private willAttach: Partial<{
type: 'media' | 'document', type: 'media' | 'document',
isMedia: true, isMedia: true,
group: boolean, group: boolean,
sendFileDetails: SendFileParams[] sendFileDetails: SendFileParams[]
}> = { }>;
sendFileDetails: [],
group: false
};
private inputField: InputField; private inputField: InputField;
constructor(private chat: Chat, files: File[], willAttachType: PopupNewMedia['willAttach']['type']) { 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}); 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}); attachClickEvent(this.btnConfirm, () => this.send(), {listenerSetter: this.listenerSetter});
@ -95,41 +103,98 @@ export default class PopupNewMedia extends PopupElement {
placeholder: 'PreviewSender.CaptionPlaceholder', placeholder: 'PreviewSender.CaptionPlaceholder',
label: 'Caption', label: 'Caption',
name: 'photo-caption', name: 'photo-caption',
maxLength: rootScope.config.caption_length_max, maxLength: rootScope.config.caption_length_max
showLengthOn: 80
}); });
this.input = this.inputField.input; this.input = this.inputField.input;
this.inputField.value = this.wasInputValue = this.chat.input.messageInputField.input.innerHTML; this.inputField.value = this.wasInputValue = this.chat.input.messageInputField.input.innerHTML;
this.chat.input.messageInputField.value = ''; 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({ this.groupCheckboxField = new CheckboxField({
text: 'PreviewSender.GroupItems', text: 'PreviewSender.GroupItems',
name: 'group-items' 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.willAttach.group = true;
this.groupCheckboxField.setValueSilently(this.willAttach.group);
this.listenerSetter.add(this.groupCheckboxField.input)('change', () => { this.listenerSetter.add(this.groupCheckboxField.input)('change', () => {
const checked = this.groupCheckboxField.input.checked; const checked = this.groupCheckboxField.checked;
this.willAttach.group = checked; this.willAttach.group = checked;
this.willAttach.sendFileDetails.length = 0;
//this.mediaContainer.innerHTML = ''; this.attachFiles();
//this.container.classList.remove('is-media', 'is-document', 'is-album');
this.attachFiles(files);
}); });
} 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) => { 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) { if(this.chat.type === 'scheduled' && !force) {
this.chat.input.scheduleSending(() => { this.chat.input.scheduleSending(() => {
this.send(true); this.send(true);
@ -162,324 +227,335 @@ export default class PopupNewMedia extends PopupElement {
this.hide(); this.hide();
const willAttach = this.willAttach; const willAttach = this.willAttach;
willAttach.isMedia = willAttach.type === 'media' ? true : undefined; willAttach.isMedia = willAttach.type === 'media' ? true : undefined;
const {sendFileDetails, isMedia} = willAttach;
//console.log('will send files with options:', willAttach); //console.log('will send files with options:', willAttach);
const peerId = this.chat.peerId; const {peerId, input} = this.chat;
const input = this.chat.input; const {sendSilent, scheduleDate} = input;
const silent = input.sendSilent;
const scheduleDate = input.scheduleDate;
if(willAttach.sendFileDetails.length > 1 && willAttach.group) { sendFileDetails.forEach(d => {
for(let i = 0; i < willAttach.sendFileDetails.length;) { d.itemDiv = undefined;
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;
}
}
const w = {...willAttach}; const {length} = sendFileDetails;
w.sendFileDetails = willAttach.sendFileDetails.slice(i - k, i); const replyToMsgId = input.replyToMsgId;
this.iterate((sendFileDetails) => {
this.chat.appMessagesManager.sendAlbum(peerId, w.sendFileDetails.map(d => d.file), Object.assign({ if(caption && sendFileDetails.length !== length) {
caption, this.chat.appMessagesManager.sendText(peerId, caption, {
replyToMsgId: input.replyToMsgId, replyToMsgId,
threadId: this.chat.threadId, threadId: this.chat.threadId,
isMedia: willAttach.isMedia, silent: sendSilent,
silent,
scheduleDate, scheduleDate,
clearDraft: true as true clearDraft: true
}, w)); });
caption = undefined; 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 = ''; const w = {
return promise; ...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); caption = undefined;
});
//appMessagesManager.sendFile(appImManager.peerId, willAttach.file, willAttach);
input.replyToMsgId = this.chat.threadId;
input.onMessageSent(); input.onMessageSent();
} }
public attachFile = (file: File) => { private attachMedia(file: File, params: SendFileParams, itemDiv: HTMLElement) {
const willAttach = this.willAttach; itemDiv.classList.add('popup-item-media');
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;
itemDiv.classList.add('popup-item-media'); const isVideo = file.type.startsWith('video/');
if(isVideo) { let promise: Promise<void>;
const video = document.createElement('video'); if(isVideo) {
const source = document.createElement('source'); const video = document.createElement('video');
source.src = params.objectURL = URL.createObjectURL(file); const source = document.createElement('source');
video.autoplay = true; source.src = params.objectURL = URL.createObjectURL(file);
video.controls = false; video.autoplay = true;
video.muted = true; video.controls = false;
video.setAttribute('playsinline', 'true'); video.muted = true;
video.setAttribute('playsinline', 'true');
video.addEventListener('timeupdate', () => { video.addEventListener('timeupdate', () => {
video.pause(); video.pause();
}, {once: true}); }, {once: true});
onMediaLoad(video).then(() => { promise = onMediaLoad(video).then(() => {
params.width = video.videoWidth; params.width = video.videoWidth;
params.height = video.videoHeight; params.height = video.videoHeight;
params.duration = Math.floor(video.duration); 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; createPosterFromMedia(img).then(thumb => {
if(audioDecodedByteCount !== undefined) {
params.noSound = !audioDecodedByteCount;
}
itemDiv.append(video);
createPosterFromVideo(video).then(thumb => {
params.thumb = { params.thumb = {
url: URL.createObjectURL(thumb.blob), url: URL.createObjectURL(thumb.blob),
...thumb ...thumb
}; };
resolve(itemDiv); })
}); ]).then(() => {
resolve();
}); });
video.append(source);
} else { } else {
const img = new Image(); resolve();
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;
} }
}; };
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();
});
} }
} }

View File

@ -15,7 +15,6 @@ export default class PopupPickUser extends PopupElement {
constructor(options: { constructor(options: {
peerTypes: AppSelectPeers['peerType'], peerTypes: AppSelectPeers['peerType'],
onSelect?: (peerId: PeerId) => Promise<void> | void, onSelect?: (peerId: PeerId) => Promise<void> | void,
onClose?: () => void,
placeholder: LangPackKey, placeholder: LangPackKey,
chatRightsAction?: AppSelectPeers['chatRightsAction'], chatRightsAction?: AppSelectPeers['chatRightsAction'],
peerId?: number, peerId?: number,
@ -23,8 +22,6 @@ export default class PopupPickUser extends PopupElement {
}) { }) {
super('popup-forward', null, {closable: true, overlayClosable: true, body: true}); super('popup-forward', null, {closable: true, overlayClosable: true, body: true});
if(options.onClose) this.onClose = options.onClose;
this.selector = new AppSelectPeers({ this.selector = new AppSelectPeers({
appendTo: this.body, appendTo: this.body,
onChange: async() => { onChange: async() => {

View File

@ -38,9 +38,9 @@ export default class PopupStickers extends PopupElement {
this.header.append(this.h6); this.header.append(this.h6);
this.onClose = () => { this.addEventListener('close', () => {
animationIntersector.setOnlyOnePlayableGroup(''); animationIntersector.setOnlyOnePlayableGroup('');
}; });
const div = document.createElement('div'); const div = document.createElement('div');
div.classList.add('sticker-set'); div.classList.add('sticker-set');

View File

@ -586,7 +586,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
docDiv.classList.add('document-with-thumb'); docDiv.classList.add('document-with-thumb');
let imgs: HTMLImageElement[] = []; let imgs: HTMLImageElement[] = [];
if(uploading) { if(message.pFlags.is_outgoing) {
icoDiv.innerHTML = `<img src="${cacheContext.url}">`; icoDiv.innerHTML = `<img src="${cacheContext.url}">`;
imgs.push(icoDiv.firstElementChild as HTMLImageElement); imgs.push(icoDiv.firstElementChild as HTMLImageElement);
} else { } else {
@ -647,9 +647,9 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
docDiv.prepend(icoDiv); docDiv.prepend(icoDiv);
/* if(!uploading && message.pFlags.is_outgoing) { if(!uploading && message.pFlags.is_outgoing) {
return docDiv; return docDiv;
} */ }
let downloadDiv: HTMLElement, preloader: ProgressivePreloader = null; let downloadDiv: HTMLElement, preloader: ProgressivePreloader = null;
const onLoad = () => { const onLoad = () => {
@ -700,7 +700,7 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
downloadDiv = docDiv.querySelector('.document-download'); downloadDiv = docDiv.querySelector('.document-download');
preloader = new ProgressivePreloader(); preloader = new ProgressivePreloader();
preloader.attach(downloadDiv, false, appDocsManager.downloading.get(doc.id)); preloader.attach(downloadDiv, false, appDocsManager.downloading.get(doc.id));
} else if(!(cacheContext.downloaded && !uploading)) { } else if(!cacheContext.downloaded || uploading) {
downloadDiv = docDiv.querySelector('.document-download'); downloadDiv = docDiv.querySelector('.document-download');
preloader = message.media.preloader as ProgressivePreloader; preloader = message.media.preloader as ProgressivePreloader;

View File

@ -791,7 +791,12 @@ const lang = {
"other_value": "Show More (%d)" "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?", //"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.CaptionPlaceholder": "Add a caption...",
"PreviewSender.CompressFile": "Send compressed",
"PreviewSender.SendFile": { "PreviewSender.SendFile": {
"one_value": "Send File", "one_value": "Send File",
"other_value": "Send %d Files" "other_value": "Send %d Files"

View File

@ -24,7 +24,7 @@ import appPhotosManager from './appPhotosManager';
import appProfileManager from './appProfileManager'; import appProfileManager from './appProfileManager';
import appStickersManager from './appStickersManager'; import appStickersManager from './appStickersManager';
import appWebPagesManager from './appWebPagesManager'; import appWebPagesManager from './appWebPagesManager';
import PopupNewMedia from '../../components/popups/newMedia'; import PopupNewMedia, { getCurrentNewMediaPopup } from '../../components/popups/newMedia';
import MarkupTooltip from '../../components/chat/markupTooltip'; import MarkupTooltip from '../../components/chat/markupTooltip';
import { IS_TOUCH_SUPPORTED } from '../../environment/touchSupport'; import { IS_TOUCH_SUPPORTED } from '../../environment/touchSupport';
import appPollsManager from './appPollsManager'; import appPollsManager from './appPollsManager';
@ -901,6 +901,7 @@ export class AppImManager {
private attachDragAndDropListeners() { private attachDragAndDropListeners() {
const drops: ChatDragAndDrop[] = []; const drops: ChatDragAndDrop[] = [];
const mediaDrops: ChatDragAndDrop[] = [];
let mounted = false; let mounted = false;
const toggle = async(e: DragEvent, mount: boolean) => { const toggle = async(e: DragEvent, mount: boolean) => {
if(mount === mounted) return; if(mount === mounted) return;
@ -909,63 +910,84 @@ export class AppImManager {
// @ts-ignore // @ts-ignore
const isFiles = _types.contains ? _types.contains('Files') : _types.indexOf('Files') >= 0; 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; counter = 0;
return; 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 types: string[] = await getFilesFromEvent(e, true);
const force = isFiles && !types.length; // * can't get file items not from 'drop' on Safari 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 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); this.log('drag files', types);
if(types.length || force) { if(newMediaPopup) {
drops.push(new ChatDragAndDrop(dropsContainer, { newMediaPopup.appendDrops(_dropsContainer);
icon: 'dragfiles',
header: 'Chat.DropTitle', if(types.length || force) {
subtitle: 'Chat.DropAsFilesDesc', _drops.push(new ChatDragAndDrop(_dropsContainer, {
onDrop: (e: DragEvent) => { header: 'Preview.Dragging.AddItems',
toggle(e, false); headerArgs: [types.length],
appImManager.log('drop', e); onDrop: (e: DragEvent) => {
appImManager.onDocumentPaste(e, 'document'); 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; //if(!mount) return;
SetTransition(dropsContainer, 'is-visible', mount, 200, () => { SetTransition(_dropsContainer, 'is-visible', mount, 200, () => {
if(!mount) { if(!mount) {
drops.forEach(drop => { _drops.forEach(drop => {
drop.destroy(); drop.destroy();
}); });
drops.length = 0; _drops.length = 0;
} }
}); });
if(mount) { if(mount) {
drops.forEach(drop => { _drops.forEach(drop => {
drop.setPath(); drop.setPath();
}); });
} else { } else {
@ -1003,6 +1025,8 @@ export class AppImManager {
const dropsContainer = document.createElement('div'); const dropsContainer = document.createElement('div');
dropsContainer.classList.add('drops-container'); dropsContainer.classList.add('drops-container');
const mediaDropsContainer = dropsContainer.cloneNode(true) as HTMLElement;
} }
private canDrag() { private canDrag() {
@ -1011,7 +1035,8 @@ export class AppImManager {
} }
private onDocumentPaste = (e: ClipboardEvent | DragEvent, attachType?: 'media' | 'document') => { 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('document paste');
//console.log('item', event.clipboardData.getData()); //console.log('item', event.clipboardData.getData());
@ -1027,12 +1052,13 @@ export class AppImManager {
getFilesFromEvent(e).then((files: File[]) => { getFilesFromEvent(e).then((files: File[]) => {
if(files.length) { if(files.length) {
if(/* attachType === 'media' && */files.find(file => !MEDIA_MIME_TYPES_SUPPORTED.has(file.type))) { if(newMediaPopup) {
attachType = 'document'; newMediaPopup.addFiles(files);
return;
} }
const chatInput = this.chat.input; 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); new PopupNewMedia(this.chat, files, chatInput.willAttachType);
} }
}); });

View File

@ -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 needSingleMessages: Map<PeerId, Map<number, CancellablePromise<Message>>> = new Map();
private fetchSingleMessagesPromise: Promise<void> = null; private fetchSingleMessagesPromise: Promise<void> = null;
@ -1115,7 +1115,7 @@ export class AppMessagesManager {
}; };
const inputPeer = appPeersManager.getInputPeerById(peerId); const inputPeer = appPeersManager.getInputPeerById(peerId);
const invoke = (multiMedia: any[]) => { const invoke = (multiMedia: InputSingleMedia[]) => {
this.setTyping(peerId, {_: 'sendMessageCancelAction'}); this.setTyping(peerId, {_: 'sendMessageCancelAction'});
this.sendSmthLazyLoadQueue.push({ this.sendSmthLazyLoadQueue.push({
@ -1136,15 +1136,15 @@ export class AppMessagesManager {
}); });
}; };
const promises: Promise<InputSingleMedia>[] = messages.map((message, idx) => { const promises: Promise<InputSingleMedia>[] = messages.map((message) => {
return (message.send() as Promise<InputMedia>).then((inputMedia: InputMedia) => { return (message.send() as Promise<InputMedia>).then((inputMedia) => {
return apiManager.invokeApi('messages.uploadMedia', { return apiManager.invokeApi('messages.uploadMedia', {
peer: inputPeer, peer: inputPeer,
media: inputMedia media: inputMedia
}); });
}) })
.then(messageMedia => { .then(messageMedia => {
let inputMedia: any; let inputMedia: InputMedia;
if(messageMedia._ === 'messageMediaPhoto') { if(messageMedia._ === 'messageMediaPhoto') {
const photo = appPhotosManager.savePhoto(messageMedia.photo); const photo = appPhotosManager.savePhoto(messageMedia.photo);
inputMedia = appPhotosManager.getMediaInput(photo); inputMedia = appPhotosManager.getMediaInput(photo);

View File

@ -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 { .chat-input-wrapper {

View File

@ -1039,17 +1039,13 @@ $bubble-beside-button-width: 38px;
&-content { &-content {
//max-width: 300px; //max-width: 300px;
position: absolute; position: absolute;
max-width: calc(100% - 1.875rem); max-width: calc(100% - 1.25rem);
height: auto; height: auto;
min-height: 32px; min-height: 32px;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
} }
&.is-media .reply-content {
max-width: calc(100% - 1.5rem);
}
} }
// &:not(.just-media):not(.is-message-empty) .reply { // &:not(.just-media):not(.is-message-empty) .reply {
@ -1068,7 +1064,7 @@ $bubble-beside-button-width: 38px;
margin-bottom: 0; margin-bottom: 0;
background-color: var(--message-highlightning-color); background-color: var(--message-highlightning-color);
white-space: nowrap; white-space: nowrap;
max-width: 300px; max-width: 15rem;
@include respond-to(handhelds) { @include respond-to(handhelds) {
max-width: calc(100vw - #{$chat-padding-handhelds * 2} - 10px - 100%); max-width: calc(100vw - #{$chat-padding-handhelds * 2} - 10px - 100%);
@ -1096,6 +1092,8 @@ $bubble-beside-button-width: 38px;
&-content { &-content {
margin-top: 0; margin-top: 0;
position: relative;
max-width: none !important;
} }
&-title, &-title,
@ -1620,7 +1618,6 @@ $bubble-beside-button-width: 38px;
color: white; color: white;
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer;
user-select: none; user-select: none;
height: 1.125rem; height: 1.125rem;
@ -1648,7 +1645,6 @@ $bubble-beside-button-width: 38px;
color: #fff; color: #fff;
text-align: center; text-align: center;
font-size: 2.125rem; font-size: 2.125rem;
cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@ -5,11 +5,10 @@
*/ */
.drops-container { .drops-container {
--padding: 20px; --padding: 0px;
--pinned-floating-height: 0px;
position: absolute !important; position: absolute !important;
z-index: 3; z-index: 3;
top: calc(56px + var(--pinned-floating-height) + var(--padding)); top: var(--padding);
right: var(--padding); right: var(--padding);
bottom: var(--padding); bottom: var(--padding);
left: var(--padding); left: var(--padding);
@ -20,19 +19,6 @@
user-select: none; user-select: none;
width: auto !important; 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) { &:not(.is-visible) {
display: none; display: none;
} }
@ -47,12 +33,13 @@
} }
.drop { .drop {
--wrapper-padding: -4px;
background-color: var(--surface-color); background-color: var(--surface-color);
position: relative; position: relative;
//height: 100%; //height: 100%;
border-radius: $border-radius-big; border-radius: $border-radius-big;
width: 100%; width: 100%;
max-width: 696px; max-width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -65,10 +52,10 @@
&-outline { &-outline {
&-wrapper { &-wrapper {
position: absolute; position: absolute;
top: 15px; top: var(--wrapper-padding);
right: 15px; right: var(--wrapper-padding);
bottom: 15px; bottom: var(--wrapper-padding);
left: 15px; left: var(--wrapper-padding);
pointer-events: none; pointer-events: none;
} }
@ -101,7 +88,10 @@
&-header { &-header {
font-weight: 500; font-weight: 500;
font-size: 1.25rem; font-size: 1.25rem;
margin-top: -10px;
&:not(:last-child) {
margin-top: -10px;
}
} }
@media only screen and (max-height: 670px) { @media only screen and (max-height: 670px) {

View File

@ -16,43 +16,8 @@
//max-height: unquote('min(640px, 100%)'); //max-height: unquote('min(640px, 100%)');
max-height: 100%; max-height: 100%;
&.is-media:not(.is-album) { img,
/* max-height: 425px; */ video {
#{$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 {
border-radius: inherit; border-radius: inherit;
} }
} }
@ -94,45 +59,12 @@
&-photo { &-photo {
max-width: 380px; max-width: 380px;
//display: flex;
overflow: hidden; overflow: hidden;
//justify-content: center; // width: fit-content;
width: fit-content; width: 100%;
border-radius: $border-radius-medium; border-radius: $border-radius-medium;
user-select: none; user-select: none;
/* align-items: center; */ position: relative;
.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;
}
} */
}
} }
} }
@ -175,22 +107,85 @@
padding: 0; padding: 0;
} }
.popup-body {
position: relative;
}
.checkbox-field { .checkbox-field {
margin-bottom: 0; margin-bottom: 0;
margin-left: 0; margin-left: 0;
} }
.popup-album, .popup-item-album {
.popup-container:not(.is-album) .popup-item-media { 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; position: relative;
border-radius: inherit; border-radius: inherit;
overflow: hidden; overflow: hidden;
} }
.popup-album + .popup-album, .popup-photo > .popup-item + .popup-item {
.popup-container:not(.is-album) .popup-item-media + .popup-item-media {
margin-top: .5rem; 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 { .popup-create-contact {