Browse Source

context menu & delete messages & pin message

master
Eduard Kuzmenko 5 years ago
parent
commit
fd6a39c2bc
  1. 38
      src/components/misc.ts
  2. 30
      src/components/pageIm.ts
  3. 22
      src/components/pageSignUp.ts
  4. 174
      src/lib/appManagers/appImManager.ts
  5. 1
      src/lib/appManagers/appMessagesManager.ts
  6. 4
      src/lib/lottieLoader.ts
  7. 11
      src/lib/utils.js
  8. 43
      src/scss/partials/_chat.scss
  9. 7
      src/scss/partials/_chatlist.scss
  10. 22
      src/scss/style.scss

38
src/components/misc.ts

@ -782,3 +782,41 @@ export function formatPhoneNumber(str: string) {
return {formatted: str, country}; return {formatted: str, country};
} }
let onMouseMove = (e: MouseEvent) => {
let rect = openedMenu.getBoundingClientRect();
let {clientX, clientY} = e;
let diffX = clientX >= rect.right ? clientX - rect.right : rect.left - clientX;
let diffY = clientY >= rect.bottom ? clientY - rect.bottom : rect.top - clientY;
if(diffX >= 100 || diffY >= 100) {
openedMenu.classList.remove('active');
openedMenu.parentElement.classList.remove('menu-open');
//openedMenu.parentElement.click();
}
//console.log('mousemove', diffX, diffY);
};
let openedMenu: HTMLDivElement = null;
export function openBtnMenu(menuElement: HTMLDivElement) {
if(openedMenu) {
openedMenu.classList.remove('active');
openedMenu.parentElement.classList.remove('menu-open');
}
openedMenu = menuElement;
openedMenu.classList.add('active');
window.addEventListener('click', () => {
if(openedMenu) {
openedMenu.parentElement.classList.remove('menu-open');
openedMenu.classList.remove('active');
openedMenu = null;
}
window.removeEventListener('mousemove', onMouseMove);
}, {once: true});
window.addEventListener('mousemove', onMouseMove);
}

30
src/components/pageIm.ts

@ -1,5 +1,5 @@
//import { appImManager, appMessagesManager, appDialogsManager, apiUpdatesManager, appUsersManager } from "../lib/services"; //import { appImManager, appMessagesManager, appDialogsManager, apiUpdatesManager, appUsersManager } from "../lib/services";
import { horizontalMenu, wrapSticker, MTDocument, LazyLoadQueue } from "./misc"; import { horizontalMenu, wrapSticker, MTDocument, LazyLoadQueue, openBtnMenu } from "./misc";
import Scrollable from './scrollable'; import Scrollable from './scrollable';
import { whichChild, findUpTag } from "../lib/utils"; import { whichChild, findUpTag } from "../lib/utils";
@ -712,27 +712,13 @@ export default () => import('../lib/services').then(services => {
toggleEmoticons.classList.toggle('active'); toggleEmoticons.classList.toggle('active');
}; */ }; */
let openedMenu: HTMLDivElement = null;
let onMouseMove = (e: MouseEvent) => {
let rect = openedMenu.getBoundingClientRect();
let {clientX, clientY} = e;
let diffX = clientX >= rect.right ? clientX - rect.right : rect.left - clientX;
let diffY = clientY >= rect.bottom ? clientY - rect.bottom : rect.top - clientY;
if(diffX >= 100 || diffY >= 100) {
openedMenu.parentElement.click();
}
//console.log('mousemove', diffX, diffY);
};
Array.from(document.getElementsByClassName('btn-menu-toggle')).forEach((el) => { Array.from(document.getElementsByClassName('btn-menu-toggle')).forEach((el) => {
el.addEventListener('click', (e) => { el.addEventListener('click', (e) => {
console.log('click pageIm'); console.log('click pageIm');
if(!el.classList.contains('btn-menu-toggle')) return false; if(!el.classList.contains('btn-menu-toggle')) return false;
window.removeEventListener('mousemove', onMouseMove); //window.removeEventListener('mousemove', onMouseMove);
openedMenu = el.querySelector('.btn-menu'); let openedMenu = el.querySelector('.btn-menu') as HTMLDivElement;
e.cancelBubble = true; e.cancelBubble = true;
if(el.classList.contains('menu-open')) { if(el.classList.contains('menu-open')) {
@ -740,16 +726,8 @@ export default () => import('../lib/services').then(services => {
openedMenu.classList.remove('active'); openedMenu.classList.remove('active');
} else { } else {
el.classList.add('menu-open'); el.classList.add('menu-open');
openedMenu.classList.add('active');
window.addEventListener('click', () => {
//(el as HTMLDivElement).click();
el.classList.remove('menu-open');
openedMenu.classList.remove('active');
window.removeEventListener('mousemove', onMouseMove);
}, {once: true});
window.addEventListener('mousemove', onMouseMove); openBtnMenu(openedMenu);
} }
}); });
}); });

22
src/components/pageSignUp.ts

@ -18,22 +18,6 @@ export default (_authCode: typeof authCode) => {
let pageElement = document.body.getElementsByClassName('page-signUp')[0] as HTMLDivElement; let pageElement = document.body.getElementsByClassName('page-signUp')[0] as HTMLDivElement;
pageElement.style.display = ''; pageElement.style.display = '';
let findUpClassName = (el: Element | HTMLElement, className: string): HTMLElement => {
while(el.parentNode) {
// @ts-ignore
el = el.parentNode;
if(el.classList.contains(className))
return <HTMLElement>el;
}
return null;
};
Array.from(document.body.getElementsByClassName('popup-close')).forEach(el => {
let popup = findUpClassName(el, 'popup');
el.addEventListener('click', () => {
popup.classList.remove('is-visible');
});
});
const avatarInput = document.getElementById('avatar-input') as HTMLInputElement; const avatarInput = document.getElementById('avatar-input') as HTMLInputElement;
const avatarPopup = pageElement.getElementsByClassName('popup-avatar')[0]; const avatarPopup = pageElement.getElementsByClassName('popup-avatar')[0];
const avatarPreview = pageElement.querySelector('#canvas-avatar') as HTMLCanvasElement; const avatarPreview = pageElement.querySelector('#canvas-avatar') as HTMLCanvasElement;
@ -46,7 +30,7 @@ export default (_authCode: typeof authCode) => {
(avatarPopup.getElementsByClassName('popup-close')[0] as HTMLButtonElement) (avatarPopup.getElementsByClassName('popup-close')[0] as HTMLButtonElement)
.addEventListener('click', function(this, e) { .addEventListener('click', function(this, e) {
/* let popup = findUpClassName(this, 'popup'); /* let popup = findUpClassName(this, 'popup');
popup.classList.remove('is-visible'); */ popup.classList.remove('active'); */
setTimeout(() => { setTimeout(() => {
cropper.removeHandlers(); cropper.removeHandlers();
@ -67,7 +51,7 @@ export default (_authCode: typeof authCode) => {
// apply // apply
avatarPopup.getElementsByClassName('btn-crop')[0].addEventListener('click', () => { avatarPopup.getElementsByClassName('btn-crop')[0].addEventListener('click', () => {
cropper.crop(); cropper.crop();
avatarPopup.classList.remove('is-visible'); avatarPopup.classList.remove('active');
cropper.removeHandlers(); cropper.removeHandlers();
avatarPreview.toBlob(blob => { avatarPreview.toBlob(blob => {
@ -120,7 +104,7 @@ export default (_authCode: typeof authCode) => {
avatarInput.value = ''; avatarInput.value = '';
}; };
avatarPopup.classList.add('is-visible'); avatarPopup.classList.add('active');
//console.log(contents); //console.log(contents);
}; };

174
src/lib/appManagers/appImManager.ts

@ -1,10 +1,10 @@
import apiManager from '../mtproto/apiManager'; import apiManager from '../mtproto/apiManager';
import { $rootScope, isElementInViewport, numberWithCommas } from "../utils"; import { $rootScope, isElementInViewport, numberWithCommas, findUpClassName } 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";
import appProfileManager from "./appProfileManager"; import appProfileManager from "./appProfileManager";
import { ProgressivePreloader, wrapDocument, wrapSticker, wrapVideo, wrapPhoto } from "../../components/misc"; import { ProgressivePreloader, wrapDocument, wrapSticker, wrapVideo, wrapPhoto, openBtnMenu } from "../../components/misc";
import appDialogsManager from "./appDialogsManager"; import appDialogsManager from "./appDialogsManager";
import { RichTextProcessor } from "../richtextprocessor"; import { RichTextProcessor } from "../richtextprocessor";
import appPhotosManager from "./appPhotosManager"; import appPhotosManager from "./appPhotosManager";
@ -16,6 +16,7 @@ import appMediaViewer from "./appMediaViewer";
import appSidebarLeft from "./appSidebarLeft"; import appSidebarLeft from "./appSidebarLeft";
import appChatsManager from "./appChatsManager"; import appChatsManager from "./appChatsManager";
import appMessagesIDsManager from "./appMessagesIDsManager"; import appMessagesIDsManager from "./appMessagesIDsManager";
import apiUpdatesManager from './apiUpdatesManager';
console.log('appImManager included!'); console.log('appImManager included!');
@ -133,13 +134,29 @@ export class AppImManager {
private topbar: HTMLDivElement = null; private topbar: HTMLDivElement = null;
private chatInput: HTMLDivElement = null; private chatInput: HTMLDivElement = null;
scrolledAll: boolean; private scrolledAll: boolean;
public contextMenu = document.getElementById('bubble-contextmenu') as HTMLDivElement;
private contextMenuPin = this.contextMenu.querySelector('.menu-pin') as HTMLDivElement;
private contextMenuMsgID: number;
private popupDeleteMessage: {
popupEl?: HTMLDivElement,
deleteBothBtn?: HTMLButtonElement,
deleteMeBtn?: HTMLButtonElement,
cancelBtn?: HTMLButtonElement
} = {};
constructor() { constructor() {
this.log = logger('IM'); this.log = logger('IM');
this.preloader = new ProgressivePreloader(null, false); this.preloader = new ProgressivePreloader(null, false);
this.popupDeleteMessage.popupEl = this.pageEl.querySelector('.popup-delete-message') as HTMLDivElement;
this.popupDeleteMessage.deleteBothBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-both') as HTMLButtonElement;
this.popupDeleteMessage.deleteMeBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-me') as HTMLButtonElement;
this.popupDeleteMessage.cancelBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-close') as HTMLButtonElement;
apiManager.getUserID().then((id) => { apiManager.getUserID().then((id) => {
this.myID = id; this.myID = id;
}); });
@ -256,6 +273,111 @@ 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());
this.chatInner.addEventListener('contextmenu', e => {
let bubble = findUpClassName(e.target, 'bubble');
if(bubble) {
e.preventDefault();
e.cancelBubble = true;
let msgID = 0;
for(let id in this.bubbles) {
if(this.bubbles[id] === bubble) {
msgID = +id;
break;
}
}
if(!msgID) return;
if(this.myID == this.peerID ||
(this.peerID < 0 && !appPeersManager.isChannel(this.peerID) && !appPeersManager.isMegagroup(this.peerID))) {
this.contextMenuPin.style.display = '';
} else this.contextMenuPin.style.display = 'none';
this.contextMenuMsgID = msgID;
let side = bubble.parentElement.classList.contains('in') ? 'left' : 'right';
this.contextMenu.classList.remove('bottom-left', 'bottom-right');
this.contextMenu.classList.add(side == 'left' ? 'bottom-right' : 'bottom-left');
let {clientX, clientY} = e;
this.contextMenu.style.left = (side == 'right' ? clientX - this.contextMenu.scrollWidth : clientX) + 'px';
if((clientY + this.contextMenu.scrollHeight) > window.innerHeight) {
this.contextMenu.style.top = (window.innerHeight - this.contextMenu.scrollHeight) + 'px';
} else {
this.contextMenu.style.top = clientY + 'px';
}
//this.contextMenu.classList.add('active');
openBtnMenu(this.contextMenu);
this.log('contextmenu', e, bubble, msgID, side);
}
});
this.contextMenu.querySelector('.menu-copy').addEventListener('click', () => {
let message = appMessagesManager.getMessage(this.contextMenuMsgID);
let str = message ? message.message : '';
var textArea = document.createElement("textarea");
textArea.value = str;
textArea.style.position = "fixed"; //avoid scrolling to bottom
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Oops, unable to copy', err);
}
document.body.removeChild(textArea);
});
this.contextMenu.querySelector('.menu-delete').addEventListener('click', () => {
if(this.peerID == this.myID) {
this.popupDeleteMessage.deleteBothBtn.style.display = 'none';
this.popupDeleteMessage.deleteMeBtn.innerText = 'DELETE';
} else {
this.popupDeleteMessage.deleteBothBtn.style.display = '';
this.popupDeleteMessage.deleteMeBtn.innerText = 'DELETE JUST FOR ME';
if(this.peerID > 0) {
let title = appPeersManager.getPeerTitle(this.peerID);
this.popupDeleteMessage.deleteBothBtn.innerText = 'DELETE FOR ME AND ' + title.split(' ')[0];
} else {
this.popupDeleteMessage.deleteBothBtn.innerText = 'DELETE FOR ALL';
}
}
this.popupDeleteMessage.popupEl.classList.add('active');
});
this.contextMenuPin.addEventListener('click', () => {
apiManager.invokeApi('messages.updatePinnedMessage', {
flags: 0,
peer: appPeersManager.getInputPeerByID(this.peerID),
id: this.contextMenuMsgID
}).then(updates => {
this.log('pinned updates:', updates);
apiUpdatesManager.processUpdateMessage(updates);
});
});
this.popupDeleteMessage.deleteBothBtn.addEventListener('click', () => {
this.deleteMessages(true);
this.popupDeleteMessage.cancelBtn.click();
});
this.popupDeleteMessage.deleteMeBtn.addEventListener('click', () => {
this.deleteMessages(false);
this.popupDeleteMessage.cancelBtn.click();
});
this.updateStatusInterval = window.setInterval(() => this.updateStatus(), 50e3); this.updateStatusInterval = window.setInterval(() => this.updateStatus(), 50e3);
this.updateStatus(); this.updateStatus();
setInterval(() => this.setPeerStatus(), 60e3); setInterval(() => this.setPeerStatus(), 60e3);
@ -263,6 +385,36 @@ export class AppImManager {
this.loadMediaQueueProcess(); this.loadMediaQueueProcess();
} }
public deleteMessages(revoke = false) {
let flags = revoke ? 1 : 0;
let ids = [this.contextMenuMsgID];
apiManager.invokeApi('messages.deleteMessages', {
flags: flags,
revoke: revoke,
id: ids
}).then((affectedMessages: any) => {
this.log('deleted messages:', affectedMessages);
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updatePts',
pts: affectedMessages.pts,
pts_count: affectedMessages.pts_count
}
});
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: {
_: 'updateDeleteMessages',
messages: ids
}
});
});
}
public loadMediaQueuePush(cb: () => Promise<void>) { public loadMediaQueuePush(cb: () => Promise<void>) {
this.loadMediaQueue.push(cb); this.loadMediaQueue.push(cb);
this.loadMediaQueueProcess(); this.loadMediaQueueProcess();
@ -1344,6 +1496,22 @@ export class AppImManager {
this.log('updateNotifySettings', peerID, notify_settings); this.log('updateNotifySettings', peerID, notify_settings);
break; break;
} }
case 'updateChatPinnedMessage':
case 'updateUserPinnedMessage': {
let {id} = update;
this.log('updateUserPinnedMessage', update);
this.pinnedMsgID = id;
// hz nado li tut appMessagesIDsManager.getFullMessageID(update.max_id, channelID);
let peerID = update.user_id || -update.chat_id || -update.channel_id;
if(peerID == this.peerID) {
appMessagesManager.wrapSingleMessage(id);
}
break;
}
} }
} }
} }

1
src/lib/appManagers/appMessagesManager.ts

@ -7,7 +7,6 @@ import { nextRandomInt, bigint } from "../bin_utils";
import { MTProto, telegramMeWebService } from "../mtproto/mtproto"; import { MTProto, telegramMeWebService } from "../mtproto/mtproto";
import apiUpdatesManager from "./apiUpdatesManager"; import apiUpdatesManager from "./apiUpdatesManager";
import appPhotosManager from "./appPhotosManager"; import appPhotosManager from "./appPhotosManager";
import appProfileManager from "./appProfileManager";
import AppStorage from '../storage'; import AppStorage from '../storage';
import AppPeersManager from "./appPeersManager"; import AppPeersManager from "./appPeersManager";

4
src/lib/lottieLoader.ts

@ -33,7 +33,7 @@ class LottieLoader {
if(canvas && isElementInViewport(container)) { if(canvas && isElementInViewport(container)) {
let c = container.firstElementChild as HTMLCanvasElement; let c = container.firstElementChild as HTMLCanvasElement;
if(!c.height && !c.width) { if(!c.height && !c.width) {
console.log('lottie need resize'); //console.log('lottie need resize');
animation.resize(); animation.resize();
} }
} }
@ -104,7 +104,7 @@ class LottieLoader {
public getAnimation(el: HTMLElement, group = '') { public getAnimation(el: HTMLElement, group = '') {
let groups = group ? [group] : Object.keys(this.animations); let groups = group ? [group] : Object.keys(this.animations);
console.log('getAnimation', groups, this.animations); //console.log('getAnimation', groups, this.animations);
for(let group of groups) { for(let group of groups) {
let animations = this.animations[group]; let animations = this.animations[group];

11
src/lib/utils.js

@ -324,6 +324,17 @@ export function numberWithCommas(x) {
return parts.join("."); return parts.join(".");
} }
export function findUpClassName(el, className) {
if(el.classList.contains(className)) return el; // 03.02.2020
while(el.parentNode) {
el = el.parentNode;
if(el.classList.contains(className))
return el;
}
return null;
}
export function findUpTag(el, tag) { export function findUpTag(el, tag) {
if(el.tagName == tag) return el; // 03.02.2020 if(el.tagName == tag) return el; // 03.02.2020

43
src/scss/partials/_chat.scss

@ -883,6 +883,12 @@
} */ } */
} }
#bubble-contextmenu {
position: fixed;
right: auto;
bottom: auto;
}
.emoji-dropdown { .emoji-dropdown {
position: absolute; position: absolute;
left: 0; left: 0;
@ -1088,3 +1094,40 @@
} }
} }
} }
.popup {
&.popup-delete-message {
.popup-header {
margin-bottom: 1rem;
}
}
.popup-buttons {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
button {
background: none;
outline: none;
border: none;
padding: .5rem .5rem;
text-transform: uppercase;
transition: .2s;
border-radius: $border-radius;
cursor: pointer;
color: $blue;
&:hover {
background-color: rgba(112, 117, 121, 0.08);
}
& + button {
margin-top: .5rem;
}
}
}
}

7
src/scss/partials/_chatlist.scss

@ -64,7 +64,7 @@
position: relative; position: relative;
cursor: pointer; cursor: pointer;
padding: 0 8.5px; padding: 0 8.5px;
margin: 0 8.5px; margin: 0 8.5px 0 8px;
overflow: hidden; overflow: hidden;
&:hover { &:hover {
@ -146,11 +146,12 @@
} }
.message-status { .message-status {
margin-right: .25rem; margin-right: .1rem;
margin-top: .3rem;
&[class*=" tgico-"] { &[class*=" tgico-"] {
color: $success-color; color: $success-color;
font-size: 1.15rem; font-size: 1.25rem;
} }
} }

22
src/scss/style.scss

@ -175,6 +175,20 @@ input {
transform-origin: top left; transform-origin: top left;
} }
&.top-left {
top: initial;
right: 0;
bottom: 100%;
transform-origin: bottom right;
}
&.top-right {
top: initial;
left: 0;
bottom: 100%;
transform-origin: bottom left;
}
> div { > div {
display: flex; display: flex;
position: relative; position: relative;
@ -925,7 +939,7 @@ $width: 100px;
} }
.popup { .popup {
position: fixed; position: fixed!important;
left: 0; left: 0;
top: 0; top: 0;
height: 100%; height: 100%;
@ -948,7 +962,7 @@ $width: 100px;
justify-content: center; justify-content: center;
} }
.popup.is-visible { .popup.active {
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
-webkit-transition: opacity 0.3s 0s, visibility 0s 0s; -webkit-transition: opacity 0.3s 0s, visibility 0s 0s;
@ -977,7 +991,7 @@ $width: 100px;
transition-duration: 0.3s; transition-duration: 0.3s;
} }
.popup-close { span.popup-close {
/* position: absolute; /* position: absolute;
left: 20px; left: 20px;
top: 12.5px; */ top: 12.5px; */
@ -1021,7 +1035,7 @@ $width: 100px;
} */ } */
} }
.popup.is-visible .popup-container { .popup.active .popup-container {
-webkit-transform: translateY(0); -webkit-transform: translateY(0);
-moz-transform: translateY(0); -moz-transform: translateY(0);
-ms-transform: translateY(0); -ms-transform: translateY(0);

Loading…
Cancel
Save