Browse Source

send media popup & menu & from clipboard & autofocus input

master
Eduard Kuzmenko 4 years ago
parent
commit
04123d0ba6
  1. 201
      src/lib/appManagers/appImManager.ts
  2. 6
      src/lib/appManagers/appMessagesManager.ts
  3. 2
      src/lib/appManagers/appSidebarLeft.ts
  4. 2
      src/lib/richtextprocessor.js
  5. 18
      src/lib/utils.js
  6. 11
      src/scss/partials/_chat.scss
  7. 119
      src/scss/style.scss

201
src/lib/appManagers/appImManager.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import apiManager from '../mtproto/apiManager';
import { $rootScope, isElementInViewport, numberWithCommas, findUpClassName, formatNumber } from "../utils";
import { $rootScope, isElementInViewport, numberWithCommas, findUpClassName, formatNumber, placeCaretAtEnd, calcImageInBox } from "../utils";
import appUsersManager from "./appUsersManager";
import appMessagesManager from "./appMessagesManager";
import appPeersManager from "./appPeersManager";
@ -102,9 +102,35 @@ class ChatInput { @@ -102,9 +102,35 @@ class ChatInput {
public lastUrl = '';
public lastTimeType = 0;
public attachMenu: {
container?: HTMLButtonElement,
media?: HTMLDivElement,
document?: HTMLDivElement,
poll?: HTMLDivElement
} = {};
public attachMediaPopUp: {
container?: HTMLDivElement,
titleEl?: HTMLDivElement,
sendBtn?: HTMLButtonElement,
mediaContainer?: HTMLDivElement,
captionInput?: HTMLInputElement
} = {};
constructor() {
this.toggleEmoticons = this.pageEl.querySelector('.toggle-emoticons') as HTMLButtonElement;
this.attachMenu.container = document.getElementById('attach-file') as HTMLButtonElement;
this.attachMenu.media = this.attachMenu.container.querySelector('.menu-media') as HTMLDivElement;
this.attachMenu.document = this.attachMenu.container.querySelector('.menu-document') as HTMLDivElement;
this.attachMenu.poll = this.attachMenu.container.querySelector('.menu-poll') as HTMLDivElement;
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.messageInput.addEventListener('keydown', (e: KeyboardEvent) => {
if(e.key == 'Enter') {
if(e.shiftKey) {
@ -116,18 +142,18 @@ class ChatInput { @@ -116,18 +142,18 @@ class ChatInput {
});
this.messageInput.addEventListener('input', (e) => {
console.log('messageInput input', this.messageInput.innerText, this.serializeNodes(Array.from(this.messageInput.childNodes)));
//console.log('messageInput input', this.messageInput.innerText, this.serializeNodes(Array.from(this.messageInput.childNodes)));
let value = this.messageInput.innerText;
let entities = RichTextProcessor.parseEntities(value);
console.log('messageInput entities', entities);
//console.log('messageInput entities', entities);
let entityUrl = entities.find(e => e._ == 'messageEntityUrl');
if(entityUrl) { // need to get webpage
let url = value.slice(entityUrl.offset, entityUrl.offset + entityUrl.length);
console.log('messageInput url:', url);
//console.log('messageInput url:', url);
if(this.lastUrl != url) {
this.lastUrl = url;
@ -136,7 +162,7 @@ class ChatInput { @@ -136,7 +162,7 @@ class ChatInput {
hash: 0
}).then((webpage: any) => {
if(this.lastUrl != url) return;
console.log(webpage);
//console.log(webpage);
appImManager.replyElements.titleEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.site_name || webpage.title || '');
appImManager.replyElements.subtitleEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.description || webpage.url || '');
@ -187,14 +213,12 @@ class ChatInput { @@ -187,14 +213,12 @@ class ChatInput {
event.preventDefault();
});
this.messageInput.addEventListener('paste', (e) => {
/* this.messageInput.addEventListener('paste', (e) => {
e.preventDefault();
// @ts-ignore
let text = (e.originalEvent || e).clipboardData.getData('text/plain');
// console.log('messageInput paste', text);
let entities = RichTextProcessor.parseEntities(text);
text = RichTextProcessor.wrapEmojiText(text);
// console.log('messageInput paste after', text);
@ -205,30 +229,114 @@ class ChatInput { @@ -205,30 +229,114 @@ class ChatInput {
// @ts-ignore
//console.log('paste text', text, );
window.document.execCommand('insertHTML', false, text);
});
}); */
let attachFile = (file: File) => {
console.log('selected file:', file, typeof(file));
willAttachFile = file;
this.fileInput.value = '';
this.attachMediaPopUp.captionInput.value = '';
this.attachMediaPopUp.mediaContainer.innerHTML = '';
switch(willAttach) {
case 'media': {
let img = new Image();
img.src = URL.createObjectURL(file);
img.onload = () => {
willAttachWidth = img.naturalWidth;
willAttachHeight = img.naturalHeight;
};
this.attachMediaPopUp.titleEl.innerText = 'Send Photo';
this.attachMediaPopUp.mediaContainer.append(img);
this.attachMediaPopUp.container.classList.add('active');
break;
}
case 'document': {
let docDiv = wrapDocument({
file: file,
file_name: file.name || '',
size: file.size,
type: ['image/jpeg',
'image/png',
'image/gif',
'image/webp',
'image/bmp'].indexOf(file.type) !== -1 ? 'photo' : 'doc'
} as any, false);
this.attachMediaPopUp.titleEl.innerText = 'Send File';
this.attachMediaPopUp.mediaContainer.append(docDiv);
this.attachMediaPopUp.container.classList.add('active');
break;
}
}
};
let willAttach = '';
let willAttachFile: File = null;
let willAttachWidth = 0, willAttachHeight = 0;
this.fileInput.addEventListener('change', (e) => {
var file = (e.target as HTMLInputElement & EventTarget).files[0];
if(!file) {
return;
}
console.log('selected file:', file, typeof(file));
this.fileInput.value = '';
appMessagesManager.sendFile(appImManager.peerID, file, {isMedia: true});
appImManager.scroll.scrollTop = appImManager.scroll.scrollHeight;
/* MTProto.apiFileManager.uploadFile(file).then((inputFile) => {
console.log('uploaded smthn', inputFile);
}); */
attachFile(file);
}, false);
this.pageEl.querySelector('#attach-file').addEventListener('click', () => {
this.attachMenu.media.addEventListener('click', () => {
willAttach = 'media';
this.fileInput.click();
});
this.attachMenu.document.addEventListener('click', () => {
willAttach = 'document';
this.fileInput.click();
});
document.addEventListener('paste', (event) => {
if(!appImManager.peerID || this.attachMediaPopUp.container.classList.contains('active')) {
return;
}
// @ts-ignore
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
//console.log(items); // will give you the mime types
for(let i = 0; i < items.length; ++i) {
if(items[i].kind == 'file') {
event.cancelBubble = true;
event.stopPropagation();
let file = items[i].getAsFile();
//console.log(items[i], file);
if(!file) continue;
willAttach = file.type.indexOf('image/') === 0 ? 'media' : "document";
attachFile(file);
}
}
}, true);
this.attachMediaPopUp.sendBtn.addEventListener('click', () => {
this.attachMediaPopUp.container.classList.remove('active');
let caption = this.attachMediaPopUp.captionInput.value;
appMessagesManager.sendFile(appImManager.peerID, willAttachFile, {
isMedia: true,
caption,
width: willAttachWidth,
height: willAttachHeight
});
appImManager.scroll.scrollTop = appImManager.scroll.scrollHeight;
});
this.btnSend.addEventListener('click', () => {
if(this.btnSend.classList.contains('tgico-send')) {
this.sendMessage();
@ -497,7 +605,7 @@ export class AppImManager { @@ -497,7 +605,7 @@ export class AppImManager {
return;
}
if(target.tagName == 'IMG' || target.tagName == 'VIDEO') {
if((target.tagName == 'IMG' && !target.classList.contains('emoji')) || target.tagName == 'VIDEO') {
let messageID = +target.getAttribute('message-id');
let message = appMessagesManager.getMessage(messageID);
@ -540,6 +648,45 @@ export class AppImManager { @@ -540,6 +648,45 @@ export class AppImManager {
this.btnMenuMute.addEventListener('click', () => this.mutePeer());
this.btnMute.addEventListener('click', () => this.mutePeer());
let onKeyDown = (e: KeyboardEvent) => {
let target = e.target as HTMLElement;
//if(target.tagName == 'INPUT') return;
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') {
this.chatInputC.attachMediaPopUp.sendBtn.click();
} else if(e.key == 'Escape') {
this.chatInputC.attachMediaPopUp.container.classList.remove('active');
}
return;
}
if(e.target != this.chatInputC.messageInput && target.tagName != 'INPUT') {
this.chatInputC.messageInput.focus();
placeCaretAtEnd(this.chatInputC.messageInput);
}
};
document.body.addEventListener('keydown', onKeyDown);
/* this.chatInner.addEventListener('mouseover', () => {
document.body.addEventListener('keydown', onKeyDown);
this.log('mouseover');
this.chatInner.addEventListener('mouseout', () => {
document.body.removeEventListener('keydown', onKeyDown);
}, {once: true});
}); */
this.chatInner.addEventListener('contextmenu', e => {
let bubble: HTMLDivElement = null;
@ -1272,9 +1419,15 @@ export class AppImManager { @@ -1272,9 +1419,15 @@ export class AppImManager {
if(pending.size < 1e6) {
let img = new Image();
img.src = URL.createObjectURL(pending.file);
let {w, h} = calcImageInBox(pending.w, pending.h, 380, 380);
attachmentDiv.style.width = w + 'px';
attachmentDiv.style.height = h + 'px';
attachmentDiv.append(img);
preloader.attach(attachmentDiv, false);
bubble.classList.add('hide-name', 'photo');
break;
}
@ -1478,7 +1631,7 @@ export class AppImManager { @@ -1478,7 +1631,7 @@ export class AppImManager {
let nameDiv = document.createElement('div');
nameDiv.classList.add('name');
nameDiv.innerHTML = 'Forwarded from ' + title;
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID);
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
bubble.append(nameDiv);
}
} else {
@ -1549,7 +1702,7 @@ export class AppImManager { @@ -1549,7 +1702,7 @@ export class AppImManager {
let nameDiv = document.createElement('div');
nameDiv.classList.add('name');
nameDiv.innerHTML = title;
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID);
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
bubble.append(nameDiv);
} else if(!message.reply_to_mid) {
bubble.classList.add('hide-name');

6
src/lib/appManagers/appMessagesManager.ts

@ -371,7 +371,9 @@ export class AppMessagesManager { @@ -371,7 +371,9 @@ export class AppMessagesManager {
isMedia?: boolean,
replyToMsgID?: number,
caption?: string,
entities?: any[]
entities?: any[],
width?: number,
height?: number
} = {}) {
peerID = AppPeersManager.getPeerMigratedTo(peerID) || peerID;
var messageID = this.tempID--;
@ -459,6 +461,8 @@ export class AppMessagesManager { @@ -459,6 +461,8 @@ export class AppMessagesManager {
size: file.size,
file: file,
preloader: preloader,
w: options.width,
h: options.height,
progress: {
percent: 1,
total: file.size,

2
src/lib/appManagers/appSidebarLeft.ts

@ -188,7 +188,7 @@ class AppSidebarLeft { @@ -188,7 +188,7 @@ class AppSidebarLeft {
}
public onChatsScroll() {
this.log(this.scroll);
//this.log(this.scroll);
if(this.scroll.hiddenElements.down.length > 0/* || 1 == 1 */) return;
if(!this.loadDialogsPromise) {

2
src/lib/richtextprocessor.js

@ -8,7 +8,7 @@ var EmojiHelper = { @@ -8,7 +8,7 @@ var EmojiHelper = {
var emojiData = Config.Emoji;
var emojiIconSize = emojiData.img_size;
var emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS|Android/i) != -1 /* && false */,
var emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS|Android/i) != -1 && false,
emojiCode;
//var emojiRegExp = '\\u0023\\u20E3|\\u00a9|\\u00ae|\\u203c|\\u2049|\\u2139|[\\u2194-\\u2199]|\\u21a9|\\u21aa|\\u231a|\\u231b|\\u23e9|[\\u23ea-\\u23ec]|\\u23f0|\\u24c2|\\u25aa|\\u25ab|\\u25b6|\\u2611|\\u2614|\\u26fd|\\u2705|\\u2709|[\\u2795-\\u2797]|\\u27a1|\\u27b0|\\u27bf|\\u2934|\\u2935|[\\u2b05-\\u2b07]|\\u2b1b|\\u2b1c|\\u2b50|\\u2b55|\\u3030|\\u303d|\\u3297|\\u3299|[\\uE000-\\uF8FF\\u270A-\\u2764\\u2122\\u25C0\\u25FB-\\u25FE\\u2615\\u263a\\u2648-\\u2653\\u2660-\\u2668\\u267B\\u267F\\u2693\\u261d\\u26A0-\\u26FA\\u2708\\u2702\\u2601\\u260E]|[\\u2600\\u26C4\\u26BE\\u23F3\\u2764]|\\uD83D[\\uDC00-\\uDFFF]|\\uD83C[\\uDDE8-\\uDDFA\uDDEC]\\uD83C[\\uDDEA-\\uDDFA\uDDE7]|[0-9]\\u20e3|\\uD83C[\\uDC00-\\uDFFF]';
//var emojiRegExp = '\\u00a9|\\u00ae|[\\u2000-\\u3300]|\\ud83c[\\ud000-\\udfff]|\\ud83d[\\ud000-\\udfff]|\\ud83e[\\ud000-\\udfff]';

18
src/lib/utils.js

@ -142,6 +142,24 @@ export function getRichValue (field) { @@ -142,6 +142,24 @@ export function getRichValue (field) {
return value
}
export function placeCaretAtEnd(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
export function getRichValueWithCaret (field) {
if (!field) {
return []

11
src/scss/partials/_chat.scss

@ -271,7 +271,7 @@ @@ -271,7 +271,7 @@
}
.message:not(.message-empty) + .attachment,
&.is-reply .attachment {
&.is-reply .message:not(.message-empty) + .attachment {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
@ -305,7 +305,7 @@ @@ -305,7 +305,7 @@
width: max-content;
}
img, video {
img:not(.emoji), video {
/* object-fit: contain; */
object-fit: cover;
width: 100%;
@ -493,7 +493,8 @@ @@ -493,7 +493,8 @@
}
> .name {
padding: .2675rem .6rem 0 .6rem;
/* padding: .2675rem .6rem 0 .6rem; */
padding: .32rem .6rem 0 .6rem;
font-weight: 500;
/* padding-bottom: 4px; */
color: $darkblue;
@ -603,6 +604,7 @@ @@ -603,6 +604,7 @@
}
&.forwarded .attachment,
&.is-reply .attachment,
&:not(.hide-name):not(.sticker) .attachment {
border-top-left-radius: 0;
border-top-right-radius: 0;
@ -688,7 +690,8 @@ @@ -688,7 +690,8 @@
border-radius: 12px 12px 0px 12px;
}
&.forwarded .attachment {
&.forwarded .attachment,
&.is-reply .attachment {
border-top-left-radius: 0;
border-top-right-radius: 0;
}

119
src/scss/style.scss

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
$placeholder-color: #9e9e9e;
$border-radius: 8px;
$border-radius-medium: 10px;
$border-radius-big: 12px;
$button-primary-background: #4EA4F6;
$success-color: #4DCD5E;
$color-error: #E53935;
@ -770,6 +772,7 @@ input:focus, button:focus { @@ -770,6 +772,7 @@ input:focus, button:focus {
cursor: pointer;
overflow: hidden;
position: relative;
padding: 0; // new
&:hover {
background: darken($button-primary-background, 8%);
@ -994,47 +997,17 @@ $width: 100px; @@ -994,47 +997,17 @@ $width: 100px;
}
span.popup-close {
/* position: absolute;
left: 20px;
top: 12.5px; */
/* width: 100%; */
height: 18px;
cursor: pointer;
color: $color-gray;
z-index: 3;
text-align: center;
justify-self: center;
line-height: 1;
transition: .2s;
svg {
max-width: 100%;
max-height: 100%;
}
path {
fill: $color-gray;
transition: .2s all;
}
&:hover path {
fill: #000;
}
/* &:before, &:after {
position: absolute;
left: 15px;
content: ' ';
height: 15px;
width: 1px;
background-color: #707579;
}
&:before {
transform: rotate(45deg);
&:hover {
color: #000;
}
&:after {
transform: rotate(-45deg);
} */
}
.popup.active .popup-container {
@ -1334,6 +1307,84 @@ div.scrollable::-webkit-scrollbar-thumb { @@ -1334,6 +1307,84 @@ div.scrollable::-webkit-scrollbar-thumb {
justify-content: flex-start;
}
.popup-send-photo {
.popup-container {
max-width: 420px;
max-height: 425px;
overflow: hidden;
/* padding: 12px 20px 50px; */
padding: 12px 20px 32.5px;
border-radius: $border-radius-medium;
}
.popup-header {
justify-content: space-between;
align-items: center;
margin-bottom: 12.5px;
.popup-close {
font-size: 1.5rem;
margin-left: .5rem;
}
.popup-title {
flex: 1;
padding: 0 2rem;
margin: 0;
font-size: 1.35rem;
font-weight: 500;
}
.btn-primary {
width: 80px;
height: 35px;
font-size: 1rem;
padding: 0;
border-radius: $border-radius-medium;
}
}
.popup-photo {
max-width: 378px;
max-height: 256px;
display: flex;
overflow: hidden;
justify-content: center;
width: fit-content;
border-radius: $border-radius-medium;
/* align-items: center; */
.document {
max-width: 100%;
overflow: hidden;
cursor: default;
.document-name {
font-weight: normal;
width: 100%;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
}
img {
object-fit: contain;
}
}
.input-field {
margin-top: 25px;
input {
height: 55px;
font-size: 1.15rem;
padding: 0 15px;
border-radius: $border-radius-medium;
}
}
}
.page-chats {
/* display: grid; */
/* grid-template-columns: 25% 50%; */

Loading…
Cancel
Save