Browse Source

send media popup & menu & from clipboard & autofocus input

master
Eduard Kuzmenko 5 years ago
parent
commit
04123d0ba6
  1. 197
      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

197
src/lib/appManagers/appImManager.ts

@ -1,5 +1,5 @@
import apiManager from '../mtproto/apiManager'; 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 appUsersManager from "./appUsersManager";
import appMessagesManager from "./appMessagesManager"; import appMessagesManager from "./appMessagesManager";
import appPeersManager from "./appPeersManager"; import appPeersManager from "./appPeersManager";
@ -102,9 +102,35 @@ class ChatInput {
public lastUrl = ''; public lastUrl = '';
public lastTimeType = 0; 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() { constructor() {
this.toggleEmoticons = this.pageEl.querySelector('.toggle-emoticons') as HTMLButtonElement; 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) => { this.messageInput.addEventListener('keydown', (e: KeyboardEvent) => {
if(e.key == 'Enter') { if(e.key == 'Enter') {
if(e.shiftKey) { if(e.shiftKey) {
@ -116,18 +142,18 @@ class ChatInput {
}); });
this.messageInput.addEventListener('input', (e) => { 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 value = this.messageInput.innerText;
let entities = RichTextProcessor.parseEntities(value); let entities = RichTextProcessor.parseEntities(value);
console.log('messageInput entities', entities); //console.log('messageInput entities', entities);
let entityUrl = entities.find(e => e._ == 'messageEntityUrl'); let entityUrl = entities.find(e => e._ == 'messageEntityUrl');
if(entityUrl) { // need to get webpage if(entityUrl) { // need to get webpage
let url = value.slice(entityUrl.offset, entityUrl.offset + entityUrl.length); let url = value.slice(entityUrl.offset, entityUrl.offset + entityUrl.length);
console.log('messageInput url:', url); //console.log('messageInput url:', url);
if(this.lastUrl != url) { if(this.lastUrl != url) {
this.lastUrl = url; this.lastUrl = url;
@ -136,7 +162,7 @@ class ChatInput {
hash: 0 hash: 0
}).then((webpage: any) => { }).then((webpage: any) => {
if(this.lastUrl != url) return; if(this.lastUrl != url) return;
console.log(webpage); //console.log(webpage);
appImManager.replyElements.titleEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.site_name || webpage.title || ''); appImManager.replyElements.titleEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.site_name || webpage.title || '');
appImManager.replyElements.subtitleEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.description || webpage.url || ''); appImManager.replyElements.subtitleEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.description || webpage.url || '');
@ -187,14 +213,12 @@ class ChatInput {
event.preventDefault(); event.preventDefault();
}); });
this.messageInput.addEventListener('paste', (e) => { /* this.messageInput.addEventListener('paste', (e) => {
e.preventDefault(); e.preventDefault();
// @ts-ignore // @ts-ignore
let text = (e.originalEvent || e).clipboardData.getData('text/plain'); let text = (e.originalEvent || e).clipboardData.getData('text/plain');
// console.log('messageInput paste', text); // console.log('messageInput paste', text);
let entities = RichTextProcessor.parseEntities(text);
text = RichTextProcessor.wrapEmojiText(text); text = RichTextProcessor.wrapEmojiText(text);
// console.log('messageInput paste after', text); // console.log('messageInput paste after', text);
@ -205,28 +229,112 @@ class ChatInput {
// @ts-ignore // @ts-ignore
//console.log('paste text', text, ); //console.log('paste text', text, );
window.document.execCommand('insertHTML', false, 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) => { this.fileInput.addEventListener('change', (e) => {
var file = (e.target as HTMLInputElement & EventTarget).files[0]; var file = (e.target as HTMLInputElement & EventTarget).files[0];
if(!file) { if(!file) {
return; return;
} }
console.log('selected file:', file, typeof(file)); attachFile(file);
}, false);
this.fileInput.value = ''; this.attachMenu.media.addEventListener('click', () => {
willAttach = 'media';
this.fileInput.click();
});
appMessagesManager.sendFile(appImManager.peerID, file, {isMedia: true}); this.attachMenu.document.addEventListener('click', () => {
appImManager.scroll.scrollTop = appImManager.scroll.scrollHeight; willAttach = 'document';
this.fileInput.click();
});
/* MTProto.apiFileManager.uploadFile(file).then((inputFile) => { document.addEventListener('paste', (event) => {
console.log('uploaded smthn', inputFile); if(!appImManager.peerID || this.attachMediaPopUp.container.classList.contains('active')) {
}); */ return;
}, false); }
this.pageEl.querySelector('#attach-file').addEventListener('click', () => { // @ts-ignore
this.fileInput.click(); 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', () => { this.btnSend.addEventListener('click', () => {
@ -497,7 +605,7 @@ export class AppImManager {
return; 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 messageID = +target.getAttribute('message-id');
let message = appMessagesManager.getMessage(messageID); let message = appMessagesManager.getMessage(messageID);
@ -540,6 +648,45 @@ export class AppImManager {
this.btnMenuMute.addEventListener('click', () => this.mutePeer()); this.btnMenuMute.addEventListener('click', () => this.mutePeer());
this.btnMute.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 => { this.chatInner.addEventListener('contextmenu', e => {
let bubble: HTMLDivElement = null; let bubble: HTMLDivElement = null;
@ -1273,8 +1420,14 @@ export class AppImManager {
let img = new Image(); let img = new Image();
img.src = URL.createObjectURL(pending.file); 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); attachmentDiv.append(img);
preloader.attach(attachmentDiv, false); preloader.attach(attachmentDiv, false);
bubble.classList.add('hide-name', 'photo');
break; break;
} }
@ -1478,7 +1631,7 @@ export class AppImManager {
let nameDiv = document.createElement('div'); let nameDiv = document.createElement('div');
nameDiv.classList.add('name'); nameDiv.classList.add('name');
nameDiv.innerHTML = 'Forwarded from ' + title; nameDiv.innerHTML = 'Forwarded from ' + title;
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID); nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
bubble.append(nameDiv); bubble.append(nameDiv);
} }
} else { } else {
@ -1549,7 +1702,7 @@ export class AppImManager {
let nameDiv = document.createElement('div'); let nameDiv = document.createElement('div');
nameDiv.classList.add('name'); nameDiv.classList.add('name');
nameDiv.innerHTML = title; nameDiv.innerHTML = title;
nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID); nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false);
bubble.append(nameDiv); bubble.append(nameDiv);
} else if(!message.reply_to_mid) { } else if(!message.reply_to_mid) {
bubble.classList.add('hide-name'); bubble.classList.add('hide-name');

6
src/lib/appManagers/appMessagesManager.ts

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

2
src/lib/appManagers/appSidebarLeft.ts

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

2
src/lib/richtextprocessor.js

@ -8,7 +8,7 @@ var EmojiHelper = {
var emojiData = Config.Emoji; var emojiData = Config.Emoji;
var emojiIconSize = emojiData.img_size; 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; 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 = '\\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]'; //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) {
return value 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) { export function getRichValueWithCaret (field) {
if (!field) { if (!field) {
return [] return []

11
src/scss/partials/_chat.scss

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

119
src/scss/style.scss

@ -1,5 +1,7 @@
$placeholder-color: #9e9e9e; $placeholder-color: #9e9e9e;
$border-radius: 8px; $border-radius: 8px;
$border-radius-medium: 10px;
$border-radius-big: 12px;
$button-primary-background: #4EA4F6; $button-primary-background: #4EA4F6;
$success-color: #4DCD5E; $success-color: #4DCD5E;
$color-error: #E53935; $color-error: #E53935;
@ -770,6 +772,7 @@ input:focus, button:focus {
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
padding: 0; // new
&:hover { &:hover {
background: darken($button-primary-background, 8%); background: darken($button-primary-background, 8%);
@ -994,47 +997,17 @@ $width: 100px;
} }
span.popup-close { span.popup-close {
/* position: absolute;
left: 20px;
top: 12.5px; */
/* width: 100%; */
height: 18px;
cursor: pointer; cursor: pointer;
color: $color-gray;
z-index: 3; z-index: 3;
text-align: center; text-align: center;
justify-self: center; justify-self: center;
line-height: 1; line-height: 1;
transition: .2s;
svg { &:hover {
max-width: 100%; color: #000;
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);
} }
&:after {
transform: rotate(-45deg);
} */
} }
.popup.active .popup-container { .popup.active .popup-container {
@ -1334,6 +1307,84 @@ div.scrollable::-webkit-scrollbar-thumb {
justify-content: flex-start; 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 { .page-chats {
/* display: grid; */ /* display: grid; */
/* grid-template-columns: 25% 50%; */ /* grid-template-columns: 25% 50%; */

Loading…
Cancel
Save