Popup changes:
Closing by escape Avatar & new media are now popups Fix deleting message Edit single item from album Fix message text in album Fix overflow in pinned subtitle
This commit is contained in:
parent
1dd4697c8c
commit
1b6fc2622e
@ -45,9 +45,16 @@ export class ChatContextMenu {
|
|||||||
if(!msgID) return;
|
if(!msgID) return;
|
||||||
|
|
||||||
this.peerID = $rootScope.selectedPeerID;
|
this.peerID = $rootScope.selectedPeerID;
|
||||||
this.msgID = msgID;
|
//this.msgID = msgID;
|
||||||
this.target = e.target as HTMLElement;
|
this.target = e.target as HTMLElement;
|
||||||
|
|
||||||
|
const albumItem = findUpClassName(this.target, 'album-item');
|
||||||
|
if(albumItem) {
|
||||||
|
this.msgID = +albumItem.dataset.mid;
|
||||||
|
} else {
|
||||||
|
this.msgID = msgID;
|
||||||
|
}
|
||||||
|
|
||||||
this.buttons.forEach(button => {
|
this.buttons.forEach(button => {
|
||||||
const good = button.verify(this.peerID, this.msgID, this.target);
|
const good = button.verify(this.peerID, this.msgID, this.target);
|
||||||
button.element.classList.toggle('hide', !good);
|
button.element.classList.toggle('hide', !good);
|
||||||
@ -145,13 +152,13 @@ export class ChatContextMenu {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private onCopyClick = () => {
|
private onCopyClick = () => {
|
||||||
let message = appMessagesManager.getMessage(this.msgID);
|
const message = appMessagesManager.getMessage(this.msgID);
|
||||||
|
|
||||||
let str = message ? message.message : '';
|
const str = message ? message.message : '';
|
||||||
|
|
||||||
var textArea = document.createElement("textarea");
|
const textArea = document.createElement('textarea');
|
||||||
textArea.value = str;
|
textArea.value = str;
|
||||||
textArea.style.position = "fixed"; //avoid scrolling to bottom
|
textArea.style.position = 'fixed'; //avoid scrolling to bottom
|
||||||
document.body.appendChild(textArea);
|
document.body.appendChild(textArea);
|
||||||
textArea.focus();
|
textArea.focus();
|
||||||
textArea.select();
|
textArea.select();
|
||||||
@ -182,22 +189,16 @@ export class ChatContextMenu {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private onForwardClick = () => {
|
private onForwardClick = () => {
|
||||||
let msgID: number;
|
appSidebarRight.forwardTab.open([this.msgID]);
|
||||||
|
|
||||||
const albumItem = findUpClassName(this.target, 'album-item');
|
|
||||||
if(albumItem) {
|
|
||||||
msgID = +albumItem.dataset.mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
appSidebarRight.forwardTab.open([msgID]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDeleteClick = () => {
|
private onDeleteClick = () => {
|
||||||
let peerID = $rootScope.selectedPeerID;
|
const peerID = $rootScope.selectedPeerID;
|
||||||
let firstName = appPeersManager.getPeerTitle(peerID, false, true);
|
const firstName = appPeersManager.getPeerTitle(peerID, false, true);
|
||||||
|
|
||||||
let callback = (revoke: boolean) => {
|
const msgID = this.msgID;
|
||||||
appMessagesManager.deleteMessages([this.msgID], revoke);
|
const callback = (revoke: boolean) => {
|
||||||
|
appMessagesManager.deleteMessages([msgID], revoke);
|
||||||
};
|
};
|
||||||
|
|
||||||
let title: string, description: string, buttons: PopupButton[];
|
let title: string, description: string, buttons: PopupButton[];
|
||||||
@ -237,7 +238,7 @@ export class ChatContextMenu {
|
|||||||
isCancel: true
|
isCancel: true
|
||||||
});
|
});
|
||||||
|
|
||||||
let popup = new PopupPeer('popup-delete-chat', {
|
const popup = new PopupPeer('popup-delete-chat', {
|
||||||
peerID: peerID,
|
peerID: peerID,
|
||||||
title: title,
|
title: title,
|
||||||
description: description,
|
description: description,
|
||||||
|
@ -10,15 +10,15 @@ import apiManager from "../../lib/mtproto/mtprotoworker";
|
|||||||
import opusDecodeController from "../../lib/opusDecodeController";
|
import opusDecodeController from "../../lib/opusDecodeController";
|
||||||
import { RichTextProcessor } from "../../lib/richtextprocessor";
|
import { RichTextProcessor } from "../../lib/richtextprocessor";
|
||||||
import $rootScope from '../../lib/rootScope';
|
import $rootScope from '../../lib/rootScope';
|
||||||
import { calcImageInBox, cancelEvent, getRichValue } from "../../lib/utils";
|
import { cancelEvent, getRichValue } from "../../lib/utils";
|
||||||
import ButtonMenu, { ButtonMenuItemOptions } from '../buttonMenu';
|
import ButtonMenu, { ButtonMenuItemOptions } from '../buttonMenu';
|
||||||
import emoticonsDropdown from "../emoticonsDropdown";
|
import emoticonsDropdown from "../emoticonsDropdown";
|
||||||
import { Layouter, RectPart } from "../groupedLayout";
|
|
||||||
import PopupCreatePoll from "../popupCreatePoll";
|
import PopupCreatePoll from "../popupCreatePoll";
|
||||||
|
import PopupNewMedia from '../popupNewMedia';
|
||||||
import { ripple } from '../ripple';
|
import { ripple } from '../ripple';
|
||||||
import Scrollable from "../scrollable";
|
import Scrollable from "../scrollable";
|
||||||
import { toast } from "../toast";
|
import { toast } from "../toast";
|
||||||
import { wrapDocument, wrapReply } from "../wrappers";
|
import { wrapReply } from "../wrappers";
|
||||||
|
|
||||||
const RECORD_MIN_TIME = 500;
|
const RECORD_MIN_TIME = 500;
|
||||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
||||||
@ -40,14 +40,6 @@ export class ChatInput {
|
|||||||
public attachMenu: HTMLButtonElement;
|
public attachMenu: HTMLButtonElement;
|
||||||
private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerID: number) => boolean})[];
|
private attachMenuButtons: (ButtonMenuItemOptions & {verify: (peerID: number) => boolean})[];
|
||||||
|
|
||||||
public attachMediaPopUp: {
|
|
||||||
container?: HTMLDivElement,
|
|
||||||
titleEl?: HTMLDivElement,
|
|
||||||
sendBtn?: HTMLButtonElement,
|
|
||||||
mediaContainer?: HTMLDivElement,
|
|
||||||
captionInput?: HTMLInputElement
|
|
||||||
} = {};
|
|
||||||
|
|
||||||
public replyElements: {
|
public replyElements: {
|
||||||
container?: HTMLDivElement,
|
container?: HTMLDivElement,
|
||||||
cancelBtn?: HTMLButtonElement,
|
cancelBtn?: HTMLButtonElement,
|
||||||
@ -74,12 +66,14 @@ export class ChatInput {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.attachMenu = document.getElementById('attach-file') as HTMLButtonElement;
|
this.attachMenu = document.getElementById('attach-file') as HTMLButtonElement;
|
||||||
|
|
||||||
|
let willAttachType: 'document' | 'media';
|
||||||
this.attachMenuButtons = [{
|
this.attachMenuButtons = [{
|
||||||
icon: 'photo',
|
icon: 'photo',
|
||||||
text: 'Photo or Video',
|
text: 'Photo or Video',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
this.fileInput.value = '';
|
||||||
this.fileInput.setAttribute('accept', 'image/*, video/*');
|
this.fileInput.setAttribute('accept', 'image/*, video/*');
|
||||||
willAttach.type = 'media';
|
willAttachType = 'media';
|
||||||
this.fileInput.click();
|
this.fileInput.click();
|
||||||
},
|
},
|
||||||
verify: (peerID: number) => peerID > 0 || appChatsManager.hasRights(peerID, 'send', 'send_media')
|
verify: (peerID: number) => peerID > 0 || appChatsManager.hasRights(peerID, 'send', 'send_media')
|
||||||
@ -87,8 +81,9 @@ export class ChatInput {
|
|||||||
icon: 'document',
|
icon: 'document',
|
||||||
text: 'Document',
|
text: 'Document',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
this.fileInput.value = '';
|
||||||
this.fileInput.removeAttribute('accept');
|
this.fileInput.removeAttribute('accept');
|
||||||
willAttach.type = 'document';
|
willAttachType = 'document';
|
||||||
this.fileInput.click();
|
this.fileInput.click();
|
||||||
},
|
},
|
||||||
verify: (peerID: number) => peerID > 0 || appChatsManager.hasRights(peerID, 'send', 'send_media')
|
verify: (peerID: number) => peerID > 0 || appChatsManager.hasRights(peerID, 'send', 'send_media')
|
||||||
@ -116,12 +111,6 @@ export class ChatInput {
|
|||||||
|
|
||||||
ripple(this.attachMenu);
|
ripple(this.attachMenu);
|
||||||
|
|
||||||
this.attachMediaPopUp.container = this.pageEl.querySelector('.popup-send-photo') as HTMLDivElement;
|
|
||||||
this.attachMediaPopUp.titleEl = this.attachMediaPopUp.container.querySelector('.popup-title') as HTMLDivElement;
|
|
||||||
this.attachMediaPopUp.sendBtn = this.attachMediaPopUp.container.querySelector('.btn-primary') as HTMLButtonElement;
|
|
||||||
this.attachMediaPopUp.mediaContainer = this.attachMediaPopUp.container.querySelector('.popup-photo') as HTMLDivElement;
|
|
||||||
this.attachMediaPopUp.captionInput = this.attachMediaPopUp.container.querySelector('input') as HTMLInputElement;
|
|
||||||
|
|
||||||
this.replyElements.container = this.pageEl.querySelector('.reply-wrapper') as HTMLDivElement;
|
this.replyElements.container = this.pageEl.querySelector('.reply-wrapper') as HTMLDivElement;
|
||||||
this.replyElements.cancelBtn = this.replyElements.container.querySelector('.reply-cancel') as HTMLButtonElement;
|
this.replyElements.cancelBtn = this.replyElements.container.querySelector('.reply-cancel') as HTMLButtonElement;
|
||||||
this.replyElements.titleEl = this.replyElements.container.querySelector('.reply-title') as HTMLDivElement;
|
this.replyElements.titleEl = this.replyElements.container.querySelector('.reply-title') as HTMLDivElement;
|
||||||
@ -281,210 +270,19 @@ export class ChatInput {
|
|||||||
window.document.execCommand('insertHTML', false, text);
|
window.document.execCommand('insertHTML', false, text);
|
||||||
});
|
});
|
||||||
|
|
||||||
let attachFile = (file: File) => {
|
|
||||||
return new Promise<HTMLDivElement>((resolve, reject) => {
|
|
||||||
let params: SendFileParams = {};
|
|
||||||
params.file = file;
|
|
||||||
//console.log('selected file:', file, typeof(file), willAttach);
|
|
||||||
let itemDiv = document.createElement('div');
|
|
||||||
switch(willAttach.type) {
|
|
||||||
case 'media': {
|
|
||||||
let isVideo = file.type.indexOf('video/') === 0;
|
|
||||||
|
|
||||||
itemDiv.classList.add('popup-item-media');
|
|
||||||
|
|
||||||
if(isVideo) {
|
|
||||||
let video = document.createElement('video');
|
|
||||||
let source = document.createElement('source');
|
|
||||||
source.src = params.objectURL = URL.createObjectURL(file);
|
|
||||||
video.autoplay = false;
|
|
||||||
video.controls = false;
|
|
||||||
video.muted = true;
|
|
||||||
video.setAttribute('playsinline', '');
|
|
||||||
|
|
||||||
video.onloadeddata = () => {
|
|
||||||
params.width = video.videoWidth;
|
|
||||||
params.height = video.videoHeight;
|
|
||||||
params.duration = Math.floor(video.duration);
|
|
||||||
|
|
||||||
itemDiv.append(video);
|
|
||||||
resolve(itemDiv);
|
|
||||||
};
|
|
||||||
|
|
||||||
video.append(source);
|
|
||||||
} else {
|
|
||||||
let img = new Image();
|
|
||||||
img.src = params.objectURL = URL.createObjectURL(file);
|
|
||||||
img.onload = () => {
|
|
||||||
params.width = img.naturalWidth;
|
|
||||||
params.height = img.naturalHeight;
|
|
||||||
|
|
||||||
itemDiv.append(img);
|
|
||||||
resolve(itemDiv);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'document': {
|
|
||||||
const isPhoto = file.type.indexOf('image/') !== -1;
|
|
||||||
if(isPhoto) {
|
|
||||||
params.objectURL = URL.createObjectURL(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
let docDiv = wrapDocument({
|
|
||||||
file: file,
|
|
||||||
file_name: file.name || '',
|
|
||||||
size: file.size,
|
|
||||||
type: isPhoto ? 'photo' : 'doc',
|
|
||||||
url: params.objectURL
|
|
||||||
} as any, false, true);
|
|
||||||
|
|
||||||
const finish = () => {
|
|
||||||
itemDiv.append(docDiv);
|
|
||||||
resolve(itemDiv);
|
|
||||||
};
|
|
||||||
|
|
||||||
if(isPhoto) {
|
|
||||||
let 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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let attachFiles = (files: File[]) => {
|
|
||||||
this.fileInput.value = '';
|
|
||||||
|
|
||||||
let container = this.attachMediaPopUp.container.firstElementChild as HTMLElement;
|
|
||||||
container.classList.remove('is-media', 'is-document', 'is-album');
|
|
||||||
|
|
||||||
this.attachMediaPopUp.captionInput.value = '';
|
|
||||||
this.attachMediaPopUp.mediaContainer.innerHTML = '';
|
|
||||||
this.attachMediaPopUp.mediaContainer.style.width = this.attachMediaPopUp.mediaContainer.style.height = '';
|
|
||||||
//willAttach.sendFileDetails.length = 0;
|
|
||||||
willAttach.sendFileDetails = []; // need new array
|
|
||||||
|
|
||||||
files = files.filter(file => {
|
|
||||||
if(willAttach.type == 'media') {
|
|
||||||
return ['image/', 'video/'].find(s => file.type.indexOf(s) === 0);
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(files.length) {
|
|
||||||
if(willAttach.type == 'document') {
|
|
||||||
this.attachMediaPopUp.titleEl.innerText = 'Send ' + (files.length > 1 ? files.length + ' Files' : 'File');
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
if(foundPhotos && foundVideos) {
|
|
||||||
this.attachMediaPopUp.titleEl.innerText = 'Send Album';
|
|
||||||
} else if(foundPhotos) {
|
|
||||||
this.attachMediaPopUp.titleEl.innerText = 'Send ' + (foundPhotos > 1 ? foundPhotos + ' Photos' : 'Photo');
|
|
||||||
} else if(foundVideos) {
|
|
||||||
this.attachMediaPopUp.titleEl.innerText = 'Send ' + (foundVideos > 1 ? foundVideos + ' Videos' : 'Video');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all(files.map(attachFile)).then(results => {
|
|
||||||
if(willAttach.type == 'media') {
|
|
||||||
if(willAttach.sendFileDetails.length > 1) {
|
|
||||||
container.classList.add('is-album');
|
|
||||||
|
|
||||||
let layouter = new Layouter(willAttach.sendFileDetails.map(o => ({w: o.width, h: o.height})), 380, 100, 4);
|
|
||||||
let layout = layouter.layout();
|
|
||||||
|
|
||||||
for(let {geometry, sides} of layout) {
|
|
||||||
let div = results.shift();
|
|
||||||
|
|
||||||
div.style.width = geometry.width + 'px';
|
|
||||||
div.style.height = geometry.height + 'px';
|
|
||||||
div.style.top = geometry.y + 'px';
|
|
||||||
div.style.left = geometry.x + 'px';
|
|
||||||
|
|
||||||
if(sides & RectPart.Right) {
|
|
||||||
this.attachMediaPopUp.mediaContainer.style.width = geometry.width + geometry.x + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sides & RectPart.Bottom) {
|
|
||||||
this.attachMediaPopUp.mediaContainer.style.height = geometry.height + geometry.y + 'px';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.attachMediaPopUp.mediaContainer.append(div);
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log('chatInput album layout:', layout);
|
|
||||||
} else {
|
|
||||||
let params = willAttach.sendFileDetails[0];
|
|
||||||
let div = results[0];
|
|
||||||
let {w, h} = calcImageInBox(params.width, params.height, 380, 320);
|
|
||||||
div.style.width = w + 'px';
|
|
||||||
div.style.height = h + 'px';
|
|
||||||
this.attachMediaPopUp.mediaContainer.append(div);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.attachMediaPopUp.mediaContainer.append(...results);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.attachMediaPopUp.container.classList.add('active');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
type SendFileParams = Partial<{
|
|
||||||
file: File,
|
|
||||||
objectURL: string,
|
|
||||||
width: number,
|
|
||||||
height: number,
|
|
||||||
duration: number
|
|
||||||
}>;
|
|
||||||
|
|
||||||
let willAttach: Partial<{
|
|
||||||
type: 'media' | 'document',
|
|
||||||
isMedia: boolean,
|
|
||||||
sendFileDetails: SendFileParams[]
|
|
||||||
}> = {
|
|
||||||
sendFileDetails: []
|
|
||||||
};
|
|
||||||
|
|
||||||
this.fileInput.addEventListener('change', (e) => {
|
this.fileInput.addEventListener('change', (e) => {
|
||||||
let files = (e.target as HTMLInputElement & EventTarget).files;
|
let files = (e.target as HTMLInputElement & EventTarget).files;
|
||||||
if(!files.length) {
|
if(!files.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
attachFiles(Array.from(files));
|
new PopupNewMedia(Array.from(files).slice(), willAttachType);
|
||||||
|
this.fileInput.value = '';
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
document.addEventListener('paste', (event) => {
|
document.addEventListener('paste', (event) => {
|
||||||
const peerID = $rootScope.selectedPeerID;
|
const peerID = $rootScope.selectedPeerID;
|
||||||
if(!peerID || this.attachMediaPopUp.container.classList.contains('active') || (peerID < 0 && !appChatsManager.hasRights(peerID, 'send', 'send_media'))) {
|
if(!peerID || $rootScope.overlayIsActive || (peerID < 0 && !appChatsManager.hasRights(peerID, 'send', 'send_media'))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,56 +301,12 @@ export class ChatInput {
|
|||||||
//console.log(items[i], file);
|
//console.log(items[i], file);
|
||||||
if(!file) continue;
|
if(!file) continue;
|
||||||
|
|
||||||
willAttach.type = file.type.indexOf('image/') === 0 ? 'media' : "document";
|
willAttachType = file.type.indexOf('image/') === 0 ? 'media' : "document";
|
||||||
attachFiles([file]);
|
new PopupNewMedia([file], willAttachType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
this.attachMediaPopUp.sendBtn.addEventListener('click', () => {
|
|
||||||
this.attachMediaPopUp.container.classList.remove('active');
|
|
||||||
let caption = this.attachMediaPopUp.captionInput.value;
|
|
||||||
willAttach.isMedia = willAttach.type == 'media';
|
|
||||||
|
|
||||||
//console.log('will send files with options:', willAttach);
|
|
||||||
|
|
||||||
let peerID = appImManager.peerID;
|
|
||||||
|
|
||||||
if(willAttach.sendFileDetails.length > 1 && willAttach.isMedia) {
|
|
||||||
appMessagesManager.sendAlbum(peerID, willAttach.sendFileDetails.map(d => d.file), Object.assign({
|
|
||||||
caption,
|
|
||||||
replyToMsgID: this.replyToMsgID
|
|
||||||
}, willAttach));
|
|
||||||
} else {
|
|
||||||
if(caption) {
|
|
||||||
if(willAttach.sendFileDetails.length > 1) {
|
|
||||||
appMessagesManager.sendText(peerID, caption, {replyToMsgID: this.replyToMsgID});
|
|
||||||
caption = '';
|
|
||||||
this.replyToMsgID = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let promises = willAttach.sendFileDetails.map(params => {
|
|
||||||
let promise = appMessagesManager.sendFile(peerID, params.file, Object.assign({
|
|
||||||
//isMedia: willAttach.isMedia,
|
|
||||||
isMedia: params.file.type.includes('audio/') || willAttach.isMedia,
|
|
||||||
caption,
|
|
||||||
replyToMsgID: this.replyToMsgID
|
|
||||||
}, params));
|
|
||||||
|
|
||||||
caption = '';
|
|
||||||
this.replyToMsgID = 0;
|
|
||||||
return promise;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Promise.all(promises);
|
|
||||||
|
|
||||||
//appMessagesManager.sendFile(appImManager.peerID, willAttach.file, willAttach);
|
|
||||||
|
|
||||||
this.onMessageSent();
|
|
||||||
});
|
|
||||||
|
|
||||||
const onBtnSendClick = (e: Event) => {
|
const onBtnSendClick = (e: Event) => {
|
||||||
cancelEvent(e);
|
cancelEvent(e);
|
||||||
|
|
||||||
|
50
src/components/chat/messageRender.ts
Normal file
50
src/components/chat/messageRender.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { formatNumber } from "../../lib/utils";
|
||||||
|
|
||||||
|
type Message = any;
|
||||||
|
|
||||||
|
export namespace MessageRender {
|
||||||
|
/* export const setText = () => {
|
||||||
|
|
||||||
|
}; */
|
||||||
|
|
||||||
|
export const setTime = (message: Message, bubble: HTMLElement, bubbleContainer: HTMLElement, messageDiv: HTMLElement) => {
|
||||||
|
let date = new Date(message.date * 1000);
|
||||||
|
let time = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2);
|
||||||
|
|
||||||
|
if(message.views) {
|
||||||
|
bubble.classList.add('channel-post');
|
||||||
|
time = formatNumber(message.views, 1) + ' <i class="tgico-channelviews"></i> ' + time;
|
||||||
|
|
||||||
|
if(!message.savedFrom) {
|
||||||
|
let forward = document.createElement('div');
|
||||||
|
forward.classList.add('bubble-beside-button', 'forward');
|
||||||
|
forward.innerHTML = `
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
|
||||||
|
<defs>
|
||||||
|
<path d="M13.55 3.24L13.64 3.25L13.73 3.27L13.81 3.29L13.9 3.32L13.98 3.35L14.06 3.39L14.14 3.43L14.22 3.48L14.29 3.53L14.36 3.59L14.43 3.64L22.23 10.85L22.36 10.99L22.48 11.15L22.57 11.31L22.64 11.48L22.69 11.66L22.72 11.85L22.73 12.04L22.71 12.22L22.67 12.41L22.61 12.59L22.53 12.76L22.42 12.93L22.29 13.09L22.23 13.15L14.43 20.36L14.28 20.48L14.12 20.58L13.95 20.66L13.77 20.72L13.58 20.76L13.4 20.77L13.22 20.76L13.03 20.73L12.85 20.68L12.68 20.61L12.52 20.52L12.36 20.4L12.22 20.27L12.16 20.2L12.1 20.13L12.05 20.05L12.01 19.98L11.96 19.9L11.93 19.82L11.89 19.73L11.87 19.65L11.84 19.56L11.83 19.47L11.81 19.39L11.81 19.3L11.8 19.2L11.8 16.42L11 16.49L10.23 16.58L9.51 16.71L8.82 16.88L8.18 17.09L7.57 17.33L7.01 17.6L6.48 17.91L5.99 18.26L5.55 18.64L5.14 19.05L4.77 19.51L4.43 19.99L4.29 20.23L4.21 20.35L4.11 20.47L4 20.57L3.88 20.65L3.75 20.72L3.62 20.78L3.48 20.82L3.33 20.84L3.19 20.84L3.04 20.83L2.9 20.79L2.75 20.74L2.62 20.68L2.53 20.62L2.45 20.56L2.38 20.5L2.31 20.43L2.25 20.36L2.2 20.28L2.15 20.19L2.11 20.11L2.07 20.02L2.04 19.92L2.02 19.83L2.01 19.73L2 19.63L2.04 17.99L2.19 16.46L2.46 15.05L2.85 13.75L3.35 12.58L3.97 11.53L4.7 10.6L5.55 9.8L6.51 9.12L7.59 8.56L8.77 8.13L10.07 7.83L11.48 7.65L11.8 7.63L11.8 4.8L11.91 4.56L12.02 4.35L12.14 4.16L12.25 3.98L12.37 3.82L12.48 3.68L12.61 3.56L12.73 3.46L12.85 3.38L12.98 3.31L13.11 3.27L13.24 3.24L13.37 3.23L13.46 3.23L13.55 3.24Z" id="b13RmHDQtl"></path>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use>
|
||||||
|
</svg>`;
|
||||||
|
bubbleContainer.append(forward);
|
||||||
|
bubble.classList.add('with-beside-button');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message.edit_date) {
|
||||||
|
bubble.classList.add('is-edited');
|
||||||
|
time = '<i class="edited">edited</i> ' + time;
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeSpan = document.createElement('span');
|
||||||
|
timeSpan.classList.add('time');
|
||||||
|
|
||||||
|
let timeInner = document.createElement('div');
|
||||||
|
timeInner.classList.add('inner', 'tgico');
|
||||||
|
timeInner.innerHTML = time;
|
||||||
|
|
||||||
|
timeSpan.appendChild(timeInner);
|
||||||
|
messageDiv.append(timeSpan);
|
||||||
|
|
||||||
|
return timeSpan;
|
||||||
|
};
|
||||||
|
}
|
13
src/components/inputField.ts
Normal file
13
src/components/inputField.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const InputField = (placeholder: string, label: string, name: string) => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.classList.add('input-field');
|
||||||
|
|
||||||
|
div.innerHTML = `
|
||||||
|
<input type="text" name="${name}" id="input-${name}" placeholder="${placeholder}" autocomplete="off" required="">
|
||||||
|
<label for="input-${name}">${label}</label>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return div;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputField;
|
@ -1,3 +1,5 @@
|
|||||||
|
import $rootScope from "../lib/rootScope";
|
||||||
|
import { cancelEvent } from "../lib/utils";
|
||||||
import AvatarElement from "./avatar";
|
import AvatarElement from "./avatar";
|
||||||
import { ripple } from "./ripple";
|
import { ripple } from "./ripple";
|
||||||
|
|
||||||
@ -12,6 +14,7 @@ export class PopupElement {
|
|||||||
|
|
||||||
protected onClose: () => void;
|
protected onClose: () => void;
|
||||||
protected onCloseAfterTimeout: () => void;
|
protected onCloseAfterTimeout: () => void;
|
||||||
|
protected onEscape: () => boolean = () => true;
|
||||||
|
|
||||||
constructor(className: string, buttons?: Array<PopupButton>, options: Partial<{closable: boolean, withConfirm: string, body: boolean}> = {}) {
|
constructor(className: string, buttons?: Array<PopupButton>, options: Partial<{closable: boolean, withConfirm: string, body: boolean}> = {}) {
|
||||||
this.element.classList.add('popup');
|
this.element.classList.add('popup');
|
||||||
@ -26,14 +29,14 @@ export class PopupElement {
|
|||||||
if(options.closable) {
|
if(options.closable) {
|
||||||
this.closeBtn = document.createElement('span');
|
this.closeBtn = document.createElement('span');
|
||||||
this.closeBtn.classList.add('btn-icon', 'popup-close', 'tgico-close');
|
this.closeBtn.classList.add('btn-icon', 'popup-close', 'tgico-close');
|
||||||
ripple(this.closeBtn);
|
//ripple(this.closeBtn);
|
||||||
this.header.prepend(this.closeBtn);
|
this.header.prepend(this.closeBtn);
|
||||||
|
|
||||||
this.closeBtn.addEventListener('click', () => {
|
this.closeBtn.addEventListener('click', this.destroy, {once: true});
|
||||||
this.destroy();
|
|
||||||
}, {once: true});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.addEventListener('keydown', this._onKeyDown, {capture: true});
|
||||||
|
|
||||||
if(options.withConfirm) {
|
if(options.withConfirm) {
|
||||||
this.confirmBtn = document.createElement('button');
|
this.confirmBtn = document.createElement('button');
|
||||||
this.confirmBtn.classList.add('btn-primary');
|
this.confirmBtn.classList.add('btn-primary');
|
||||||
@ -80,20 +83,33 @@ export class PopupElement {
|
|||||||
this.element.append(this.container);
|
this.element.append(this.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _onKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if(e.key == 'Escape' && this.onEscape()) {
|
||||||
|
cancelEvent(e);
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public show() {
|
public show() {
|
||||||
document.body.append(this.element);
|
document.body.append(this.element);
|
||||||
void this.element.offsetWidth; // reflow
|
void this.element.offsetWidth; // reflow
|
||||||
this.element.classList.add('active');
|
this.element.classList.add('active');
|
||||||
|
$rootScope.overlayIsActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy = () => {
|
||||||
this.onClose && this.onClose();
|
this.onClose && this.onClose();
|
||||||
this.element.classList.remove('active');
|
this.element.classList.remove('active');
|
||||||
|
|
||||||
|
window.removeEventListener('keydown', this._onKeyDown, {capture: true});
|
||||||
|
if(this.closeBtn) this.closeBtn.removeEventListener('click', this.destroy);
|
||||||
|
$rootScope.overlayIsActive = false;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.element.remove();
|
this.element.remove();
|
||||||
this.onCloseAfterTimeout && this.onCloseAfterTimeout();
|
this.onCloseAfterTimeout && this.onCloseAfterTimeout();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PopupButton = {
|
export type PopupButton = {
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import resizeableImage from "../lib/cropper";
|
|
||||||
import appDownloadManager from "../lib/appManagers/appDownloadManager";
|
import appDownloadManager from "../lib/appManagers/appDownloadManager";
|
||||||
|
import resizeableImage from "../lib/cropper";
|
||||||
|
import { PopupElement } from "./popup";
|
||||||
|
import { ripple } from "./ripple";
|
||||||
|
|
||||||
|
export default class PopupAvatar extends PopupElement {
|
||||||
|
private cropContainer: HTMLElement;
|
||||||
|
private input: HTMLInputElement;
|
||||||
|
private btnSubmit: HTMLElement;
|
||||||
|
private h6: HTMLElement;
|
||||||
|
|
||||||
export class PopupAvatar {
|
|
||||||
private container = document.getElementById('popup-avatar');
|
|
||||||
private input = this.container.querySelector('input') as HTMLInputElement;
|
|
||||||
private cropContainer = this.container.querySelector('.crop') as HTMLDivElement;
|
|
||||||
private closeBtn = this.container.querySelector('.popup-close') as HTMLButtonElement;
|
|
||||||
private image = new Image();
|
private image = new Image();
|
||||||
|
|
||||||
private canvas: HTMLCanvasElement;
|
private canvas: HTMLCanvasElement;
|
||||||
@ -18,18 +21,31 @@ export class PopupAvatar {
|
|||||||
private onCrop: (upload: () => ReturnType<typeof appDownloadManager.upload>) => void;
|
private onCrop: (upload: () => ReturnType<typeof appDownloadManager.upload>) => void;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.container.style.display = ''; // need for no blink
|
super('popup-avatar', null, {closable: true});
|
||||||
|
|
||||||
|
this.h6 = document.createElement('h6');
|
||||||
|
this.h6.innerText = 'Drag to Reposition';
|
||||||
|
|
||||||
|
this.closeBtn.classList.remove('btn-icon');
|
||||||
|
|
||||||
|
this.header.append(this.h6);
|
||||||
|
|
||||||
|
this.cropContainer = document.createElement('div');
|
||||||
|
this.cropContainer.classList.add('crop');
|
||||||
this.cropContainer.append(this.image);
|
this.cropContainer.append(this.image);
|
||||||
|
|
||||||
|
this.input = document.createElement('input');
|
||||||
|
this.input.type = 'file';
|
||||||
|
this.input.style.display = 'none';
|
||||||
this.input.addEventListener('change', (e: any) => {
|
this.input.addEventListener('change', (e: any) => {
|
||||||
var file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if(!file) {
|
if(!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
var contents = e.target.result as string;
|
const contents = e.target.result as string;
|
||||||
|
|
||||||
this.image = new Image();
|
this.image = new Image();
|
||||||
this.cropContainer.append(this.image);
|
this.cropContainer.append(this.image);
|
||||||
@ -39,9 +55,7 @@ export class PopupAvatar {
|
|||||||
/* let {w, h} = calcImageInBox(this.image.naturalWidth, this.image.naturalHeight, 460, 554);
|
/* let {w, h} = calcImageInBox(this.image.naturalWidth, this.image.naturalHeight, 460, 554);
|
||||||
cropContainer.style.width = w + 'px';
|
cropContainer.style.width = w + 'px';
|
||||||
cropContainer.style.height = h + 'px'; */
|
cropContainer.style.height = h + 'px'; */
|
||||||
this.container.classList.remove('hide');
|
this.show();
|
||||||
void this.container.offsetWidth; // reflow
|
|
||||||
this.container.classList.add('active');
|
|
||||||
|
|
||||||
this.cropper = resizeableImage(this.image, this.canvas);
|
this.cropper = resizeableImage(this.image, this.canvas);
|
||||||
this.input.value = '';
|
this.input.value = '';
|
||||||
@ -51,8 +65,10 @@ export class PopupAvatar {
|
|||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
// apply
|
this.btnSubmit = document.createElement('button');
|
||||||
this.container.querySelector('.btn-crop').addEventListener('click', () => {
|
this.btnSubmit.className = 'btn-primary btn-circle btn-crop btn-icon tgico-check z-depth-1';
|
||||||
|
ripple(this.btnSubmit);
|
||||||
|
this.btnSubmit.addEventListener('click', () => {
|
||||||
this.cropper.crop();
|
this.cropper.crop();
|
||||||
this.closeBtn.click();
|
this.closeBtn.click();
|
||||||
|
|
||||||
@ -63,16 +79,14 @@ export class PopupAvatar {
|
|||||||
}, 'image/jpeg', 1);
|
}, 'image/jpeg', 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.closeBtn.addEventListener('click', () => {
|
this.container.append(this.cropContainer, this.btnSubmit, this.input);
|
||||||
setTimeout(() => {
|
|
||||||
this.cropper.removeHandlers();
|
|
||||||
if(this.image) {
|
|
||||||
this.image.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.container.classList.add('hide');
|
this.onCloseAfterTimeout = () => {
|
||||||
}, 200);
|
this.cropper.removeHandlers();
|
||||||
});
|
if(this.image) {
|
||||||
|
this.image.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolve() {
|
private resolve() {
|
||||||
@ -94,5 +108,3 @@ export class PopupAvatar {
|
|||||||
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new PopupAvatar();
|
|
||||||
|
@ -4,23 +4,12 @@ import appPollsManager, { Poll } from "../lib/appManagers/appPollsManager";
|
|||||||
import $rootScope from "../lib/rootScope";
|
import $rootScope from "../lib/rootScope";
|
||||||
import { findUpTag, whichChild } from "../lib/utils";
|
import { findUpTag, whichChild } from "../lib/utils";
|
||||||
import CheckboxField from "./checkbox";
|
import CheckboxField from "./checkbox";
|
||||||
|
import InputField from "./inputField";
|
||||||
import { PopupElement } from "./popup";
|
import { PopupElement } from "./popup";
|
||||||
import RadioField from "./radioField";
|
import RadioField from "./radioField";
|
||||||
import Scrollable from "./scrollable";
|
import Scrollable from "./scrollable";
|
||||||
import { toast } from "./toast";
|
import { toast } from "./toast";
|
||||||
|
|
||||||
const InputField = (placeholder: string, label: string, name: string) => {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.classList.add('input-field');
|
|
||||||
|
|
||||||
div.innerHTML = `
|
|
||||||
<input type="text" name="${name}" id="input-${name}" placeholder="${placeholder}" autocomplete="off" required="">
|
|
||||||
<label for="input-${name}">${label}</label>
|
|
||||||
`;
|
|
||||||
|
|
||||||
return div;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class PopupCreatePoll extends PopupElement {
|
export default class PopupCreatePoll extends PopupElement {
|
||||||
private questionInput: HTMLInputElement;
|
private questionInput: HTMLInputElement;
|
||||||
private questions: HTMLElement;
|
private questions: HTMLElement;
|
||||||
@ -119,6 +108,19 @@ export default class PopupCreatePoll extends PopupElement {
|
|||||||
|
|
||||||
this.scrollable = new Scrollable(this.body);
|
this.scrollable = new Scrollable(this.body);
|
||||||
this.appendMoreField();
|
this.appendMoreField();
|
||||||
|
|
||||||
|
this.onEscape = () => {
|
||||||
|
return !this.getFilledAnswers().length;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFilledAnswers() {
|
||||||
|
const answers = Array.from(this.questions.children).map((el, idx) => {
|
||||||
|
const input = el.querySelector('input[type="text"]') as HTMLInputElement;
|
||||||
|
return input.value;
|
||||||
|
}).filter(v => !!v.trim());
|
||||||
|
|
||||||
|
return answers;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmitClick = (e: MouseEvent) => {
|
onSubmitClick = (e: MouseEvent) => {
|
||||||
@ -134,10 +136,7 @@ export default class PopupCreatePoll extends PopupElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const answers = Array.from(this.questions.children).map((el, idx) => {
|
const answers = this.getFilledAnswers();
|
||||||
const input = el.querySelector('input[type="text"]') as HTMLInputElement;
|
|
||||||
return input.value;
|
|
||||||
}).filter(v => !!v.trim());
|
|
||||||
|
|
||||||
if(answers.length < 2) {
|
if(answers.length < 2) {
|
||||||
toast('Please enter at least two options.');
|
toast('Please enter at least two options.');
|
||||||
|
285
src/components/popupNewMedia.ts
Normal file
285
src/components/popupNewMedia.ts
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
import { isTouchSupported } from "../helpers/touchSupport";
|
||||||
|
import appImManager from "../lib/appManagers/appImManager";
|
||||||
|
import appMessagesManager from "../lib/appManagers/appMessagesManager";
|
||||||
|
import { calcImageInBox } from "../lib/utils";
|
||||||
|
import { Layouter, RectPart } from "./groupedLayout";
|
||||||
|
import InputField from "./inputField";
|
||||||
|
import { PopupElement } from "./popup";
|
||||||
|
import { ripple } from "./ripple";
|
||||||
|
import { wrapDocument } from "./wrappers";
|
||||||
|
|
||||||
|
type SendFileParams = Partial<{
|
||||||
|
file: File,
|
||||||
|
objectURL: string,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
duration: number
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export default class PopupNewMedia extends PopupElement {
|
||||||
|
private btnSend: HTMLElement;
|
||||||
|
private input: HTMLInputElement;
|
||||||
|
private mediaContainer: HTMLElement;
|
||||||
|
|
||||||
|
private willAttach: Partial<{
|
||||||
|
type: 'media' | 'document',
|
||||||
|
isMedia: boolean,
|
||||||
|
sendFileDetails: SendFileParams[]
|
||||||
|
}> = {
|
||||||
|
sendFileDetails: []
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(files: File[], willAttachType: PopupNewMedia['willAttach']['type']) {
|
||||||
|
super('popup-send-photo popup-new-media', null, {closable: true});
|
||||||
|
|
||||||
|
this.willAttach.type = willAttachType;
|
||||||
|
|
||||||
|
this.btnSend = document.createElement('button');
|
||||||
|
this.btnSend.className = 'btn-primary';
|
||||||
|
this.btnSend.innerText = 'SEND';
|
||||||
|
ripple(this.btnSend);
|
||||||
|
this.btnSend.addEventListener('click', this.send, {once: true});
|
||||||
|
|
||||||
|
this.header.append(this.btnSend);
|
||||||
|
|
||||||
|
this.mediaContainer = document.createElement('div');
|
||||||
|
this.mediaContainer.classList.add('popup-photo');
|
||||||
|
|
||||||
|
const inputField = InputField('Add a caption...', 'Caption', 'photo-caption');
|
||||||
|
this.input = inputField.firstElementChild as HTMLInputElement;
|
||||||
|
this.container.append(this.mediaContainer, inputField);
|
||||||
|
|
||||||
|
this.attachFiles(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onKeyDown = (e: KeyboardEvent) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if(target.tagName != 'INPUT') {
|
||||||
|
this.input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.key == 'Enter' && !isTouchSupported) {
|
||||||
|
this.btnSend.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public send = () => {
|
||||||
|
this.destroy();
|
||||||
|
let caption = this.input.value;
|
||||||
|
const willAttach = this.willAttach;
|
||||||
|
willAttach.isMedia = willAttach.type == 'media';
|
||||||
|
|
||||||
|
//console.log('will send files with options:', willAttach);
|
||||||
|
|
||||||
|
const peerID = appImManager.peerID;
|
||||||
|
const chatInputC = appImManager.chatInputC;
|
||||||
|
|
||||||
|
if(willAttach.sendFileDetails.length > 1 && willAttach.isMedia) {
|
||||||
|
appMessagesManager.sendAlbum(peerID, willAttach.sendFileDetails.map(d => d.file), Object.assign({
|
||||||
|
caption,
|
||||||
|
replyToMsgID: chatInputC.replyToMsgID
|
||||||
|
}, willAttach));
|
||||||
|
} else {
|
||||||
|
if(caption) {
|
||||||
|
if(willAttach.sendFileDetails.length > 1) {
|
||||||
|
appMessagesManager.sendText(peerID, caption, {replyToMsgID: chatInputC.replyToMsgID});
|
||||||
|
caption = '';
|
||||||
|
chatInputC.replyToMsgID = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises = willAttach.sendFileDetails.map(params => {
|
||||||
|
const promise = appMessagesManager.sendFile(peerID, params.file, Object.assign({
|
||||||
|
//isMedia: willAttach.isMedia,
|
||||||
|
isMedia: params.file.type.includes('audio/') || willAttach.isMedia,
|
||||||
|
caption,
|
||||||
|
replyToMsgID: chatInputC.replyToMsgID
|
||||||
|
}, params));
|
||||||
|
|
||||||
|
caption = '';
|
||||||
|
chatInputC.replyToMsgID = 0;
|
||||||
|
return promise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Promise.all(promises);
|
||||||
|
|
||||||
|
//appMessagesManager.sendFile(appImManager.peerID, willAttach.file, willAttach);
|
||||||
|
|
||||||
|
chatInputC.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;
|
||||||
|
|
||||||
|
itemDiv.classList.add('popup-item-media');
|
||||||
|
|
||||||
|
if(isVideo) {
|
||||||
|
const video = document.createElement('video');
|
||||||
|
const source = document.createElement('source');
|
||||||
|
source.src = params.objectURL = URL.createObjectURL(file);
|
||||||
|
video.autoplay = false;
|
||||||
|
video.controls = false;
|
||||||
|
video.muted = true;
|
||||||
|
video.setAttribute('playsinline', '');
|
||||||
|
|
||||||
|
video.onloadeddata = () => {
|
||||||
|
params.width = video.videoWidth;
|
||||||
|
params.height = video.videoHeight;
|
||||||
|
params.duration = Math.floor(video.duration);
|
||||||
|
|
||||||
|
itemDiv.append(video);
|
||||||
|
resolve(itemDiv);
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
resolve(itemDiv);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'document': {
|
||||||
|
const isPhoto = file.type.indexOf('image/') !== -1;
|
||||||
|
if(isPhoto) {
|
||||||
|
params.objectURL = URL.createObjectURL(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
const docDiv = wrapDocument({
|
||||||
|
file: file,
|
||||||
|
file_name: file.name || '',
|
||||||
|
size: file.size,
|
||||||
|
type: isPhoto ? 'photo' : 'doc',
|
||||||
|
url: params.objectURL
|
||||||
|
} as any, false, true);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
files = files.filter(file => {
|
||||||
|
if(willAttach.type == 'media') {
|
||||||
|
return ['image/', 'video/'].find(s => file.type.indexOf(s) === 0);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(files.length) {
|
||||||
|
if(willAttach.type == 'document') {
|
||||||
|
this.title.innerText = 'Send ' + (files.length > 1 ? files.length + ' Files' : 'File');
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(foundPhotos && foundVideos) {
|
||||||
|
this.title.innerText = 'Send Album';
|
||||||
|
} else if(foundPhotos) {
|
||||||
|
this.title.innerText = 'Send ' + (foundPhotos > 1 ? foundPhotos + ' Photos' : 'Photo');
|
||||||
|
} else if(foundVideos) {
|
||||||
|
this.title.innerText = 'Send ' + (foundVideos > 1 ? foundVideos + ' Videos' : 'Video');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(files.map(this.attachFile)).then(results => {
|
||||||
|
if(willAttach.type == 'media') {
|
||||||
|
if(willAttach.sendFileDetails.length > 1) {
|
||||||
|
container.classList.add('is-album');
|
||||||
|
|
||||||
|
const layouter = new Layouter(willAttach.sendFileDetails.map(o => ({w: o.width, h: o.height})), 380, 100, 4);
|
||||||
|
const layout = layouter.layout();
|
||||||
|
|
||||||
|
for(const {geometry, sides} of layout) {
|
||||||
|
const div = results.shift();
|
||||||
|
|
||||||
|
div.style.width = geometry.width + 'px';
|
||||||
|
div.style.height = geometry.height + 'px';
|
||||||
|
div.style.top = geometry.y + 'px';
|
||||||
|
div.style.left = geometry.x + 'px';
|
||||||
|
|
||||||
|
if(sides & RectPart.Right) {
|
||||||
|
this.mediaContainer.style.width = geometry.width + geometry.x + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sides & RectPart.Bottom) {
|
||||||
|
this.mediaContainer.style.height = geometry.height + geometry.y + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mediaContainer.append(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('chatInput album layout:', layout);
|
||||||
|
} else {
|
||||||
|
const params = willAttach.sendFileDetails[0];
|
||||||
|
const div = results[0];
|
||||||
|
const {w, h} = calcImageInBox(params.width, params.height, 380, 320);
|
||||||
|
div.style.width = w + 'px';
|
||||||
|
div.style.height = h + 'px';
|
||||||
|
this.mediaContainer.append(div);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.mediaContainer.append(...results);
|
||||||
|
}
|
||||||
|
|
||||||
|
// show now
|
||||||
|
document.body.addEventListener('keydown', this.onKeyDown);
|
||||||
|
this.onClose = () => {
|
||||||
|
document.body.removeEventListener('keydown', this.onKeyDown);
|
||||||
|
};
|
||||||
|
this.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
import { SliderTab } from "../../slider";
|
|
||||||
import popupAvatar from "../../popupAvatar";
|
|
||||||
import apiManager from "../../../lib/mtproto/mtprotoworker";
|
|
||||||
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
|
||||||
import appSidebarLeft from "..";
|
import appSidebarLeft from "..";
|
||||||
import Scrollable from "../../scrollable";
|
|
||||||
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
|
||||||
import $rootScope from "../../../lib/rootScope";
|
|
||||||
import { InputFile } from "../../../layer";
|
import { InputFile } from "../../../layer";
|
||||||
|
import appProfileManager from "../../../lib/appManagers/appProfileManager";
|
||||||
|
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
||||||
|
import apiManager from "../../../lib/mtproto/mtprotoworker";
|
||||||
|
import $rootScope from "../../../lib/rootScope";
|
||||||
|
import PopupAvatar from "../../popupAvatar";
|
||||||
|
import Scrollable from "../../scrollable";
|
||||||
|
import { SliderTab } from "../../slider";
|
||||||
|
|
||||||
// TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист)
|
// TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист)
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ export default class AppEditProfileTab implements SliderTab {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.container.querySelector('.avatar-edit').addEventListener('click', () => {
|
this.container.querySelector('.avatar-edit').addEventListener('click', () => {
|
||||||
popupAvatar.open(this.canvas, (_upload) => {
|
new PopupAvatar().open(this.canvas, (_upload) => {
|
||||||
this.uploadAvatar = _upload;
|
this.uploadAvatar = _upload;
|
||||||
this.handleChange();
|
this.handleChange();
|
||||||
this.avatarElem.remove();
|
this.avatarElem.remove();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { SliderTab } from "../../slider";
|
|
||||||
import popupAvatar from "../../popupAvatar";
|
|
||||||
import appChatsManager from "../../../lib/appManagers/appChatsManager";
|
|
||||||
import appSidebarLeft, { AppSidebarLeft } from "..";
|
import appSidebarLeft, { AppSidebarLeft } from "..";
|
||||||
|
import appChatsManager from "../../../lib/appManagers/appChatsManager";
|
||||||
|
import PopupAvatar from "../../popupAvatar";
|
||||||
|
import { SliderTab } from "../../slider";
|
||||||
|
|
||||||
export default class AppNewChannelTab implements SliderTab {
|
export default class AppNewChannelTab implements SliderTab {
|
||||||
private container = document.querySelector('.new-channel-container') as HTMLDivElement;
|
private container = document.querySelector('.new-channel-container') as HTMLDivElement;
|
||||||
@ -14,7 +14,7 @@ export default class AppNewChannelTab implements SliderTab {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.container.querySelector('.avatar-edit').addEventListener('click', () => {
|
this.container.querySelector('.avatar-edit').addEventListener('click', () => {
|
||||||
popupAvatar.open(this.canvas, (_upload) => {
|
new PopupAvatar().open(this.canvas, (_upload) => {
|
||||||
this.uploadAvatar = _upload;
|
this.uploadAvatar = _upload;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { SliderTab } from "../../slider";
|
|
||||||
import { SearchGroup } from "../../appSearch";
|
|
||||||
import popupAvatar from "../../popupAvatar";
|
|
||||||
import appChatsManager from "../../../lib/appManagers/appChatsManager";
|
|
||||||
import appSidebarLeft, { AppSidebarLeft } from "..";
|
import appSidebarLeft, { AppSidebarLeft } from "..";
|
||||||
import Scrollable from "../../scrollable";
|
import appChatsManager from "../../../lib/appManagers/appChatsManager";
|
||||||
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
|
||||||
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
import appUsersManager from "../../../lib/appManagers/appUsersManager";
|
||||||
|
import { SearchGroup } from "../../appSearch";
|
||||||
|
import PopupAvatar from "../../popupAvatar";
|
||||||
|
import Scrollable from "../../scrollable";
|
||||||
|
import { SliderTab } from "../../slider";
|
||||||
|
|
||||||
export default class AppNewGroupTab implements SliderTab {
|
export default class AppNewGroupTab implements SliderTab {
|
||||||
private container = document.querySelector('.new-group-container') as HTMLDivElement;
|
private container = document.querySelector('.new-group-container') as HTMLDivElement;
|
||||||
@ -19,7 +19,7 @@ export default class AppNewGroupTab implements SliderTab {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.container.querySelector('.avatar-edit').addEventListener('click', () => {
|
this.container.querySelector('.avatar-edit').addEventListener('click', () => {
|
||||||
popupAvatar.open(this.canvas, (_upload) => {
|
new PopupAvatar().open(this.canvas, (_upload) => {
|
||||||
this.uploadAvatar = _upload;
|
this.uploadAvatar = _upload;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
<div class="container center-align">
|
<div class="container center-align">
|
||||||
<div class="scrollable scrollable-y">
|
<div class="scrollable scrollable-y">
|
||||||
<div class="auth-image"></div>
|
<div class="auth-image"></div>
|
||||||
<h4 class="phone">Enter a password</h4>
|
<h4 class="phone">Enter Your Password</h4>
|
||||||
<p class="subtitle">Your account is protected with<br>an additional password</p>
|
<p class="subtitle">Your account is protected with<br>an additional password</p>
|
||||||
<div class="input-wrapper">
|
<div class="input-wrapper">
|
||||||
<div class="input-field">
|
<div class="input-field">
|
||||||
@ -131,17 +131,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="popup popup-avatar hide" id="popup-avatar" style="display: none;">
|
|
||||||
<div class="popup-container z-depth-1">
|
|
||||||
<div class="popup-header">
|
|
||||||
<span class="popup-close tgico-close"></span>
|
|
||||||
<h6>Drag to Reposition</h6>
|
|
||||||
</div>
|
|
||||||
<div class="crop"></div>
|
|
||||||
<button class="btn-primary rp btn-circle btn-crop btn-icon tgico-check z-depth-1"></button>
|
|
||||||
<input type="file" style="display: none;" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="whole page-chats" style="display: none;" id="page-chats">
|
<div class="whole page-chats" style="display: none;" id="page-chats">
|
||||||
<svg style="position: absolute; top: -10000px; left: -10000px;">
|
<svg style="position: absolute; top: -10000px; left: -10000px;">
|
||||||
<defs id="svg-defs">
|
<defs id="svg-defs">
|
||||||
@ -186,20 +175,6 @@
|
|||||||
<div class="media-viewer-switcher media-viewer-switcher-right menu-next"><span class="tgico-down media-viewer-next-button"></span></div>
|
<div class="media-viewer-switcher media-viewer-switcher-right menu-next"><span class="tgico-down media-viewer-next-button"></span></div>
|
||||||
{{!-- </div> --}}
|
{{!-- </div> --}}
|
||||||
</div>
|
</div>
|
||||||
<div class="popup popup-send-photo popup-new-media">
|
|
||||||
<div class="popup-container z-depth-1">
|
|
||||||
<div class="popup-header">
|
|
||||||
<span class="btn-icon popup-close tgico-close"></span>
|
|
||||||
<div class="popup-title">Send Photo</div>
|
|
||||||
<button class="btn-primary rp">SEND</button>
|
|
||||||
</div>
|
|
||||||
<div class="popup-photo"></div>
|
|
||||||
<div class="input-field">
|
|
||||||
<input type="text" name="photo-caption" id="photo-caption" placeholder="Add a caption..." autocomplete="off" required />
|
|
||||||
<label for="photo-caption">Caption</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="main-columns" class="tabs-container">
|
<div id="main-columns" class="tabs-container">
|
||||||
<div class="chats-container sidebar sidebar-left main-column" id="column-left">
|
<div class="chats-container sidebar sidebar-left main-column" id="column-left">
|
||||||
<div class="sidebar-slider tabs-container">
|
<div class="sidebar-slider tabs-container">
|
||||||
|
@ -6,6 +6,7 @@ import BubbleGroups from '../../components/bubbleGroups';
|
|||||||
import { ChatAudio } from '../../components/chat/audio';
|
import { ChatAudio } from '../../components/chat/audio';
|
||||||
import { ChatContextMenu } from '../../components/chat/contextMenu';
|
import { ChatContextMenu } from '../../components/chat/contextMenu';
|
||||||
import { ChatInput } from '../../components/chat/input';
|
import { ChatInput } from '../../components/chat/input';
|
||||||
|
import { MessageRender } from '../../components/chat/messageRender';
|
||||||
import PinnedContainer from '../../components/chat/pinnedContainer';
|
import PinnedContainer from '../../components/chat/pinnedContainer';
|
||||||
import ReplyContainer from '../../components/chat/replyContainer';
|
import ReplyContainer from '../../components/chat/replyContainer';
|
||||||
import { ChatSearch } from '../../components/chat/search';
|
import { ChatSearch } from '../../components/chat/search';
|
||||||
@ -31,7 +32,7 @@ import apiManager from '../mtproto/mtprotoworker';
|
|||||||
import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config';
|
import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config';
|
||||||
import { RichTextProcessor } from "../richtextprocessor";
|
import { RichTextProcessor } from "../richtextprocessor";
|
||||||
import $rootScope from '../rootScope';
|
import $rootScope from '../rootScope';
|
||||||
import { cancelEvent, findUpClassName, findUpTag, formatNumber, getObjectKeysAndSort, langPack, numberWithCommas, placeCaretAtEnd, whichChild } from "../utils";
|
import { cancelEvent, findUpClassName, findUpTag, getObjectKeysAndSort, langPack, numberWithCommas, placeCaretAtEnd, whichChild } from "../utils";
|
||||||
import apiUpdatesManager from './apiUpdatesManager';
|
import apiUpdatesManager from './apiUpdatesManager';
|
||||||
import appChatsManager, { Channel, Chat } from "./appChatsManager";
|
import appChatsManager, { Channel, Chat } from "./appChatsManager";
|
||||||
import appDialogsManager from "./appDialogsManager";
|
import appDialogsManager from "./appDialogsManager";
|
||||||
@ -618,26 +619,14 @@ export class AppImManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let onKeyDown = (e: KeyboardEvent) => {
|
let onKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if($rootScope.overlayIsActive) return;
|
||||||
|
|
||||||
let target = e.target as HTMLElement;
|
let target = e.target as HTMLElement;
|
||||||
|
|
||||||
//if(target.tagName == 'INPUT') return;
|
//if(target.tagName == 'INPUT') return;
|
||||||
|
|
||||||
//this.log('onkeydown', e);
|
//this.log('onkeydown', e);
|
||||||
|
|
||||||
if(this.chatInputC.attachMediaPopUp.container.classList.contains('active')) {
|
|
||||||
if(target.tagName != 'INPUT') {
|
|
||||||
this.chatInputC.attachMediaPopUp.captionInput.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(e.key == 'Enter' && !isTouchSupported) {
|
|
||||||
this.chatInputC.attachMediaPopUp.sendBtn.click();
|
|
||||||
} else if(e.key == 'Escape') {
|
|
||||||
this.chatInputC.attachMediaPopUp.container.classList.remove('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(e.key == 'Escape') {
|
if(e.key == 'Escape') {
|
||||||
if(appMediaViewer.wholeDiv.classList.contains('active')) {
|
if(appMediaViewer.wholeDiv.classList.contains('active')) {
|
||||||
appMediaViewer.buttons.close.click();
|
appMediaViewer.buttons.close.click();
|
||||||
@ -777,13 +766,14 @@ export class AppImManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getAlbumBubble(groupID: string) {
|
public getAlbumBubble(groupID: string) {
|
||||||
let group = appMessagesManager.groupedMessagesStorage[groupID];
|
const group = appMessagesManager.groupedMessagesStorage[groupID];
|
||||||
for(let i in group) {
|
for(const mid in group) {
|
||||||
let mid = +i;
|
if(this.bubbles[mid]) {
|
||||||
if(this.bubbles[mid]) return {
|
return {
|
||||||
bubble: this.bubbles[mid],
|
bubble: this.bubbles[mid],
|
||||||
message: appMessagesManager.getMessage(mid)
|
message: appMessagesManager.getMessage(+mid)
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -1622,42 +1612,6 @@ export class AppImManager {
|
|||||||
|
|
||||||
return bubble;
|
return bubble;
|
||||||
}
|
}
|
||||||
|
|
||||||
// time section
|
|
||||||
|
|
||||||
let date = new Date(message.date * 1000);
|
|
||||||
let time = ('0' + date.getHours()).slice(-2) + ':' + ('0' + date.getMinutes()).slice(-2);
|
|
||||||
|
|
||||||
if(message.views) {
|
|
||||||
bubble.classList.add('channel-post');
|
|
||||||
time = formatNumber(message.views, 1) + ' <i class="tgico-channelviews"></i> ' + time;
|
|
||||||
|
|
||||||
if(!message.savedFrom) {
|
|
||||||
let forward = document.createElement('div');
|
|
||||||
forward.classList.add('bubble-beside-button', 'forward');
|
|
||||||
forward.innerHTML = `
|
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
|
|
||||||
<defs>
|
|
||||||
<path d="M13.55 3.24L13.64 3.25L13.73 3.27L13.81 3.29L13.9 3.32L13.98 3.35L14.06 3.39L14.14 3.43L14.22 3.48L14.29 3.53L14.36 3.59L14.43 3.64L22.23 10.85L22.36 10.99L22.48 11.15L22.57 11.31L22.64 11.48L22.69 11.66L22.72 11.85L22.73 12.04L22.71 12.22L22.67 12.41L22.61 12.59L22.53 12.76L22.42 12.93L22.29 13.09L22.23 13.15L14.43 20.36L14.28 20.48L14.12 20.58L13.95 20.66L13.77 20.72L13.58 20.76L13.4 20.77L13.22 20.76L13.03 20.73L12.85 20.68L12.68 20.61L12.52 20.52L12.36 20.4L12.22 20.27L12.16 20.2L12.1 20.13L12.05 20.05L12.01 19.98L11.96 19.9L11.93 19.82L11.89 19.73L11.87 19.65L11.84 19.56L11.83 19.47L11.81 19.39L11.81 19.3L11.8 19.2L11.8 16.42L11 16.49L10.23 16.58L9.51 16.71L8.82 16.88L8.18 17.09L7.57 17.33L7.01 17.6L6.48 17.91L5.99 18.26L5.55 18.64L5.14 19.05L4.77 19.51L4.43 19.99L4.29 20.23L4.21 20.35L4.11 20.47L4 20.57L3.88 20.65L3.75 20.72L3.62 20.78L3.48 20.82L3.33 20.84L3.19 20.84L3.04 20.83L2.9 20.79L2.75 20.74L2.62 20.68L2.53 20.62L2.45 20.56L2.38 20.5L2.31 20.43L2.25 20.36L2.2 20.28L2.15 20.19L2.11 20.11L2.07 20.02L2.04 19.92L2.02 19.83L2.01 19.73L2 19.63L2.04 17.99L2.19 16.46L2.46 15.05L2.85 13.75L3.35 12.58L3.97 11.53L4.7 10.6L5.55 9.8L6.51 9.12L7.59 8.56L8.77 8.13L10.07 7.83L11.48 7.65L11.8 7.63L11.8 4.8L11.91 4.56L12.02 4.35L12.14 4.16L12.25 3.98L12.37 3.82L12.48 3.68L12.61 3.56L12.73 3.46L12.85 3.38L12.98 3.31L13.11 3.27L13.24 3.24L13.37 3.23L13.46 3.23L13.55 3.24Z" id="b13RmHDQtl"></path>
|
|
||||||
</defs>
|
|
||||||
<use xlink:href="#b13RmHDQtl" opacity="1" fill="#fff" fill-opacity="1"></use>
|
|
||||||
</svg>`;
|
|
||||||
bubbleContainer.append(forward);
|
|
||||||
bubble.classList.add('with-beside-button');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(message.edit_date) {
|
|
||||||
bubble.classList.add('is-edited');
|
|
||||||
time = '<i class="edited">edited</i> ' + time;
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeSpan = document.createElement('span');
|
|
||||||
timeSpan.classList.add('time');
|
|
||||||
|
|
||||||
let timeInner = document.createElement('div');
|
|
||||||
timeInner.classList.add('inner', 'tgico');
|
|
||||||
timeInner.innerHTML = time;
|
|
||||||
|
|
||||||
let messageMessage: string, totalEntities: any[];
|
let messageMessage: string, totalEntities: any[];
|
||||||
if(message.grouped_id) {
|
if(message.grouped_id) {
|
||||||
@ -1676,9 +1630,7 @@ export class AppImManager {
|
|||||||
messageMessage = undefined;
|
messageMessage = undefined;
|
||||||
totalEntities = undefined;
|
totalEntities = undefined;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if(!messageMessage && !totalEntities) {
|
|
||||||
messageMessage = message.message;
|
messageMessage = message.message;
|
||||||
totalEntities = message.totalEntities;
|
totalEntities = message.totalEntities;
|
||||||
}
|
}
|
||||||
@ -1725,8 +1677,7 @@ export class AppImManager {
|
|||||||
messageDiv.innerHTML = richText;
|
messageDiv.innerHTML = richText;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeSpan.appendChild(timeInner);
|
const timeSpan = MessageRender.setTime(message, bubble, bubbleContainer, messageDiv);
|
||||||
messageDiv.append(timeSpan);
|
|
||||||
bubbleContainer.prepend(messageDiv);
|
bubbleContainer.prepend(messageDiv);
|
||||||
//bubble.prepend(timeSpan, messageDiv); // that's bad
|
//bubble.prepend(timeSpan, messageDiv); // that's bad
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ const $rootScope = {
|
|||||||
document.removeEventListener(name, callback);
|
document.removeEventListener(name, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
overlayIsActive: false,
|
||||||
selectedPeerID: 0,
|
selectedPeerID: 0,
|
||||||
myID: 0,
|
myID: 0,
|
||||||
idle: {
|
idle: {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import {putPreloader} from '../components/misc';
|
import { putPreloader } from '../components/misc';
|
||||||
import pageIm from './pageIm';
|
import PopupAvatar from '../components/popupAvatar';
|
||||||
//import apiManager from '../lib/mtproto/apiManager';
|
//import apiManager from '../lib/mtproto/apiManager';
|
||||||
import apiManager from '../lib/mtproto/mtprotoworker';
|
import apiManager from '../lib/mtproto/mtprotoworker';
|
||||||
import Page from './page';
|
import Page from './page';
|
||||||
import popupAvatar from '../components/popupAvatar';
|
import pageIm from './pageIm';
|
||||||
|
|
||||||
let authCode: {
|
let authCode: {
|
||||||
'phone_number': string,
|
'phone_number': string,
|
||||||
@ -17,7 +17,7 @@ let onFirstMount = () => import('../lib/appManagers/appProfileManager').then(imp
|
|||||||
|
|
||||||
let uploadAvatar: () => Promise<any>;
|
let uploadAvatar: () => Promise<any>;
|
||||||
pageElement.querySelector('.auth-image').addEventListener('click', () => {
|
pageElement.querySelector('.auth-image').addEventListener('click', () => {
|
||||||
popupAvatar.open(avatarPreview, (_uploadAvatar) => {
|
new PopupAvatar().open(avatarPreview, (_uploadAvatar) => {
|
||||||
uploadAvatar = _uploadAvatar;
|
uploadAvatar = _uploadAvatar;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -681,10 +681,7 @@
|
|||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
||||||
&-subtitle {
|
&-subtitle {
|
||||||
line-height: 13px !important;
|
line-height: 1 !important;
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -714,7 +711,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-border {
|
&-border {
|
||||||
height: 32px;
|
height: 2rem;
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
min-width: 2px;
|
min-width: 2px;
|
||||||
background: $color-blue;
|
background: $color-blue;
|
||||||
@ -727,7 +724,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 32px;
|
height: 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -752,14 +749,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-subtitle {
|
&-subtitle {
|
||||||
white-space: nowrap;
|
|
||||||
color: #111;
|
color: #111;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-media {
|
&-media {
|
||||||
height: 32px;
|
height: 2rem;
|
||||||
width: 32px;
|
width: 2rem;
|
||||||
border-radius: 8px;
|
border-radius: .5rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -775,13 +771,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
img.emoji {
|
img.emoji {
|
||||||
height: 16px;
|
height: 1rem;
|
||||||
width: 16px;
|
width: 1rem;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.emoji {
|
span.emoji {
|
||||||
font-size: 16px;
|
font-size: 1rem;
|
||||||
vertical-align: unset;
|
vertical-align: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user