From 2f9396be3f86801c5e739ffa33f5977eb75adfd7 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sat, 8 Feb 2020 13:03:09 +0700 Subject: [PATCH] fix static stickers & added button menu & user typing action --- .DS_Store | Bin 10244 -> 10244 bytes src/components/pageIm.ts | 48 ++++++ src/lib/appManagers/apiUpdatesManager.ts | 13 +- src/lib/appManagers/appDialogsManager.ts | 22 ++- src/lib/appManagers/appImManager.ts | 35 +++- src/lib/appManagers/appMessagesManager.ts | 11 ++ src/lib/appManagers/appPhotosManager.ts | 8 +- src/lib/appManagers/appSidebarLeft.ts | 22 ++- src/lib/appManagers/appSidebarRight.ts | 6 + src/lib/crypto/crypto_utils.ts | 2 +- src/lib/mtproto/apiFileManager.ts | 12 +- src/lib/mtproto/networker.ts | 2 +- src/lib/mtproto/networkerFactory.ts | 2 +- src/lib/mtproto/transports/abridged.ts | 2 +- src/lib/mtproto/transports/http.ts | 2 +- src/lib/mtproto/transports/transport.ts | 2 +- src/lib/mtproto/transports/websocket.ts | 191 +--------------------- src/scss/partials/_chat.scss | 14 +- src/scss/partials/_sidebar.scss | 4 - src/scss/style.scss | 81 +++++++++ 20 files changed, 249 insertions(+), 230 deletions(-) diff --git a/.DS_Store b/.DS_Store index 8ce924c1067e2d087115d4f83ce177a36c319531..9dd56de43c2e1cab938baee7b39a86f5e45eb0b8 100644 GIT binary patch delta 42 ycmZn(XbG6$mJU^hRb`eq)14eXomN%S&qo-dWby0IaQaWlKZR`$vFBs>5_*AFNF delta 372 zcmZn(XbG6$jIU^hRb_GTV|4eZWp3`Gp732ES4sW= diff --git a/src/components/pageIm.ts b/src/components/pageIm.ts index 1e9e3d10..080fdc1c 100644 --- a/src/components/pageIm.ts +++ b/src/components/pageIm.ts @@ -533,15 +533,24 @@ export default () => import('../lib/services').then(services => { } }); + let lastTimeType = 0; messageInput.addEventListener('input', function(this: typeof messageInput, e) { //console.log('messageInput input', this.innerText, serializeNodes(Array.from(messageInput.childNodes))); if(!this.innerText.trim() && !serializeNodes(Array.from(messageInput.childNodes)).trim()) { this.innerHTML = ''; btnSend.classList.remove('tgico-send'); btnSend.classList.add('tgico-microphone2'); + + appImManager.setTyping('sendMessageCancelAction'); } else if(!btnSend.classList.contains('tgico-send')) { btnSend.classList.add('tgico-send'); btnSend.classList.remove('tgico-microphone2'); + + let time = Date.now(); + if(time - lastTimeType >= 6000) { + lastTimeType = time; + appImManager.setTyping('sendMessageTypingAction'); + } } }); @@ -749,6 +758,45 @@ export default () => import('../lib/services').then(services => { 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) => { + el.addEventListener('click', (e) => { + window.removeEventListener('mousemove', onMouseMove); + openedMenu = el.querySelector('.btn-menu'); + e.cancelBubble = true; + + if(el.classList.contains('menu-open')) { + el.classList.remove('menu-open'); + openedMenu.classList.remove('active'); + } else { + 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); + } + }); + }); + loadDialogs().then(result => { onScroll(); diff --git a/src/lib/appManagers/apiUpdatesManager.ts b/src/lib/appManagers/apiUpdatesManager.ts index 7d5f4e07..2d57993e 100644 --- a/src/lib/appManagers/apiUpdatesManager.ts +++ b/src/lib/appManagers/apiUpdatesManager.ts @@ -1,4 +1,5 @@ -import { MTProto } from "../mtproto/mtproto"; +import apiManager from '../mtproto/apiManager'; +import networkerFactory from '../mtproto/networkerFactory'; import { dT, $rootScope, tsNow } from "../utils"; import appPeersManager from "./appPeersManager"; import appUsersManager from "./appUsersManager"; @@ -26,7 +27,7 @@ export class ApiUpdatesManager { public myID = 0; constructor() { - MTProto.apiManager.getUserID().then((id) => { + apiManager.getUserID().then((id) => { this.myID = id; }); } @@ -193,7 +194,7 @@ export class ApiUpdatesManager { updatesState.syncPending = false; } - MTProto.apiManager.invokeApi('updates.getDifference', { + apiManager.invokeApi('updates.getDifference', { pts: updatesState.pts, date: updatesState.date, qts: -1 @@ -267,7 +268,7 @@ export class ApiUpdatesManager { channelState.syncPending = false; } // console.log(dT(), 'Get channel diff', appChatsManager.getChat(channelID), channelState.pts) - MTProto.apiManager.invokeApi('updates.getChannelDifference', { + apiManager.invokeApi('updates.getChannelDifference', { channel: appChatsManager.getChannelInput(channelID), filter: {_: 'channelMessagesFilterEmpty'}, pts: channelState.pts, @@ -499,8 +500,8 @@ export class ApiUpdatesManager { } public attach() { - MTProto.networkerFactory.setUpdatesProcessor(this.processUpdateMessage.bind(this)); - MTProto.apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult: any) => { + networkerFactory.setUpdatesProcessor(this.processUpdateMessage.bind(this)); + apiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then((stateResult: any) => { this.updatesState.seq = stateResult.seq; this.updatesState.pts = stateResult.pts; this.updatesState.date = stateResult.date; diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index d9b93088..9a811dea 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -1,10 +1,10 @@ -import { MTProto } from "../mtproto/mtproto"; +import apiManager from "../mtproto/apiManager"; +import apiFileManager from '../mtproto/apiFileManager'; import { $rootScope, findUpTag } from "../utils"; import appImManager from "./appImManager"; import appPeersManager from './appPeersManager'; import appMessagesManager from "./appMessagesManager"; import appUsersManager from "./appUsersManager"; -import appSidebarRight from "./appSidebarRight"; import { RichTextProcessor } from "../richtextprocessor"; import { ripple } from "../../components/misc"; @@ -23,14 +23,13 @@ export class AppDialogsManager { public pinnedChatList = document.getElementById('dialogs-pinned') as HTMLUListElement; public chatList = document.getElementById('dialogs') as HTMLUListElement; - private topbar: HTMLDivElement = null; - private chatInput: HTMLDivElement = null; + public myID = 0; public doms: {[x: number]: any} = {}; constructor() { - MTProto.apiManager.getUserID().then((id) => { + apiManager.getUserID().then((id) => { this.myID = id; }); @@ -40,8 +39,6 @@ export class AppDialogsManager { }); //let chatClosedDiv = document.getElementById('chat-closed'); - this.topbar = document.getElementById('topbar') as HTMLDivElement; - this.chatInput = document.getElementById('chat-input') as HTMLDivElement; this.setListClickListener(this.pinnedChatList); this.setListClickListener(this.chatList); @@ -52,21 +49,22 @@ export class AppDialogsManager { let target = e.target as HTMLElement; let elem = target.tagName != 'LI' ? findUpTag(target, 'LI') : target; + if(!elem) { + return; + } + if(elem) { /* if(chatClosedDiv) { chatClosedDiv.style.display = 'none'; } */ - this.topbar.style.display = this.chatInput.style.display = ''; if(onFound) onFound(); - let peerID = +elem.getAttribute('data-peerID'); let lastMsgID = +elem.getAttribute('data-mid'); appImManager.setPeer(peerID, lastMsgID); } else /* if(chatClosedDiv) */ { - appSidebarRight.toggleSidebar(false); - this.topbar.style.display = this.chatInput.style.display = 'none'; + appImManager.setPeer(0); //chatClosedDiv.style.display = ''; } }); @@ -114,7 +112,7 @@ export class AppDialogsManager { return true; } - let res = await MTProto.apiFileManager.downloadSmallFile({ + let res = await apiFileManager.downloadSmallFile({ _: 'inputPeerPhotoFileLocation', dc_id: location.dc_id, flags: 0, diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 95ce257b..9bfcc5d6 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -1,4 +1,4 @@ -import { MTProto } from "../mtproto/mtproto"; +import apiManager from '../mtproto/apiManager'; import { $rootScope, isElementInViewport, numberWithCommas } from "../utils"; import appUsersManager from "./appUsersManager"; import appMessagesManager from "./appMessagesManager"; @@ -88,6 +88,7 @@ class ScrollPosition { export class AppImManager { public pageEl = document.querySelector('.page-chats') as HTMLDivElement; public btnMute = this.pageEl.querySelector('.tool-mute') as HTMLButtonElement; + public btnMenuMute = this.pageEl.querySelector('.menu-mute') as HTMLButtonElement; public avatarEl = document.getElementById('im-avatar') as HTMLDivElement; public titleEl = document.getElementById('im-title') as HTMLDivElement; public subtitleEl = document.getElementById('im-subtitle') as HTMLDivElement; @@ -129,15 +130,21 @@ export class AppImManager { private typingTimeouts: {[peerID: number]: number} = {}; private typingUsers: {[userID: number]: number} = {} // to peerID + private topbar: HTMLDivElement = null; + private chatInput: HTMLDivElement = null; + constructor() { this.log = logger('IM'); this.preloader = new ProgressivePreloader(null, false); - MTProto.apiManager.getUserID().then((id) => { + apiManager.getUserID().then((id) => { this.myID = id; }); + this.topbar = document.getElementById('topbar') as HTMLDivElement; + this.chatInput = document.getElementById('chat-input') as HTMLDivElement; + $rootScope.$on('user_auth', (e: CustomEvent) => { let userAuth = e.detail; this.myID = userAuth ? userAuth.id : 0; @@ -328,7 +335,7 @@ export class AppImManager { if(!this.myID) return Promise.resolve(); appUsersManager.setUserStatus(this.myID, this.offline); - return MTProto.apiManager.invokeApi('account.updateStatus', { + return apiManager.invokeApi('account.updateStatus', { offline: this.offline }, {noErrorBox: true}); } @@ -542,6 +549,13 @@ export class AppImManager { } public setPeer(peerID: number, lastMsgID = 0) { + if(peerID == 0) { + appSidebarRight.toggleSidebar(false); + this.topbar.style.display = this.chatInput.style.display = 'none'; + this.cleanup(); + return Promise.resolve(false); + } + let samePeer = this.peerID == peerID; if(samePeer && !testScroll && !lastMsgID) { @@ -596,6 +610,7 @@ export class AppImManager { this.titleEl.innerText = appSidebarRight.profileElements.name.innerText = dom.titleSpan.innerText; + this.topbar.style.display = this.chatInput.style.display = ''; appSidebarRight.toggleSidebar(true); return Promise.all([ @@ -634,6 +649,20 @@ export class AppImManager { }); } + public setTyping(action: any): Promise { + if(!this.peerID) return Promise.resolve(false); + + if(typeof(action) == 'string') { + action = {_: action}; + } + + let input = appPeersManager.getInputPeerByID(this.peerID); + return apiManager.invokeApi('messages.setTyping', { + peer: input, + action: action + }) as Promise; + } + public updateUnreadByDialog(dialog: any) { let maxID = dialog.read_outbox_max_id; diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 31cdf3af..ba341b6c 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -15,6 +15,7 @@ import ServerTimeManager from "../mtproto/serverTimeManager"; import apiFileManager, { CancellablePromise } from "../mtproto/apiFileManager"; import { MTDocument, ProgressivePreloader } from "../../components/misc"; import appDocsManager from "./appDocsManager"; +import appImManager from "./appImManager"; type HistoryStorage = { count: number | null, @@ -390,24 +391,30 @@ export class AppMessagesManager { caption = RichTextProcessor.parseMarkdown(caption, entities); } + let actionName = ''; if(!options.isMedia) { attachType = 'document'; apiFileName = 'document.' + fileType.split('/')[1]; + actionName = 'sendMessageUploadDocumentAction'; } else if(isDocument) { // maybe it's a sticker attachType = 'document'; apiFileName = ''; } else if(['image/jpeg', 'image/png', 'image/bmp'].indexOf(fileType) >= 0) { attachType = 'photo'; apiFileName = 'photo.' + fileType.split('/')[1]; + actionName = 'sendMessageUploadPhotoAction'; } else if(fileType.substr(0, 6) == 'audio/' || ['video/ogg'].indexOf(fileType) >= 0) { attachType = 'audio'; apiFileName = 'audio.' + (fileType.split('/')[1] == 'ogg' ? 'ogg' : 'mp3'); + actionName = 'sendMessageUploadAudioAction'; } else if(fileType.substr(0, 6) == 'video/') { attachType = 'video'; apiFileName = 'video.mp4'; + actionName = 'sendMessageUploadVideoAction'; } else { attachType = 'document'; apiFileName = 'document.' + fileType.split('/')[1]; + actionName = 'sendMessageUploadDocumentAction'; } // console.log(attachType, apiFileName, file.type) @@ -457,6 +464,7 @@ export class AppMessagesManager { preloader.preloader.onclick = () => { console.log('cancelling upload', media); + appImManager.setTyping('sendMessageCancelAction'); media.progress.cancel(); }; @@ -504,6 +512,8 @@ export class AppMessagesManager { uploadPromise: ReturnType = null; let invoke = (flags: number, inputMedia: any) => { + appImManager.setTyping('sendMessageCancelAction'); + return MTProto.apiManager.invokeApi('messages.sendMedia', { flags: flags, peer: AppPeersManager.getInputPeerByID(peerID), @@ -604,6 +614,7 @@ export class AppMessagesManager { console.log('upload progress', progress); media.progress.done = progress.done; media.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total)); + appImManager.setTyping({_: actionName, progress: media.progress.percent | 0}); preloader.setProgress(media.progress.percent); // lol, nice $rootScope.$broadcast('history_update', {peerID: peerID}); }; diff --git a/src/lib/appManagers/appPhotosManager.ts b/src/lib/appManagers/appPhotosManager.ts index 651e02e4..b9dd9aac 100644 --- a/src/lib/appManagers/appPhotosManager.ts +++ b/src/lib/appManagers/appPhotosManager.ts @@ -88,6 +88,8 @@ export class AppPhotosManager { //console.log('choosePhotoSize', photo); let sizes = photo.sizes || photo.thumbs; + if(!sizes) return bestPhotoSize; + sizes.forEach((photoSize: typeof bestPhotoSize) => { if(!photoSize.w || !photoSize.h) return; @@ -190,7 +192,7 @@ export class AppPhotosManager { return photoSize; } - public async preloadPhoto(photoID: any, photoSize?: any): Promise { + public async preloadPhoto(photoID: any, photoSize?: MTPhotoSize): Promise { let photo: any = null; if(typeof(photoID) === 'string') { @@ -207,7 +209,7 @@ export class AppPhotosManager { photoSize = this.choosePhotoSize(photo, fullWidth, fullHeight); } - if(photoSize) { + if(photoSize && photoSize._ != 'photoSizeEmpty') { photoSize.preloaded = true; // maybe it's a thumb @@ -242,7 +244,7 @@ export class AppPhotosManager { console.log('Photos downloadSmallFile exec', photo, location); return MTProto.apiFileManager.downloadSmallFile(location); } - } else return Promise.reject('no fullPhotoSize'); + } else return Promise.reject('no photoSize'); } public getPhoto(photoID: string) { diff --git a/src/lib/appManagers/appSidebarLeft.ts b/src/lib/appManagers/appSidebarLeft.ts index 5dea6645..543d3372 100644 --- a/src/lib/appManagers/appSidebarLeft.ts +++ b/src/lib/appManagers/appSidebarLeft.ts @@ -2,14 +2,19 @@ import { logger } from "../polyfill"; import { scrollable } from "../../components/misc"; import appMessagesManager from "./appMessagesManager"; import appDialogsManager from "./appDialogsManager"; -import { isElementInViewport } from "../utils"; +import { isElementInViewport, $rootScope } from "../utils"; import appMessagesIDsManager from "./appMessagesIDsManager"; +import apiManager from '../mtproto/apiManager'; +import appImManager from "./appImManager"; class AppSidebarLeft { private sidebarEl = document.querySelector('.page-chats .chats-container') as HTMLDivElement; private searchInput = document.getElementById('global-search') as HTMLInputElement; private toolsBtn = this.sidebarEl.querySelector('.sidebar-tools-button') as HTMLButtonElement; private searchContainer = this.sidebarEl.querySelector('#search-container') as HTMLDivElement; + + private menuEl = this.toolsBtn.querySelector('.btn-menu'); + private savedBtn = this.menuEl.querySelector('.menu-saved'); private listsContainer: HTMLDivElement = null; private searchMessagesList: HTMLUListElement = null; @@ -27,9 +32,24 @@ class AppSidebarLeft { private query = ''; + public myID = 0; + constructor() { this.listsContainer = scrollable(this.searchContainer); this.searchMessagesList = document.createElement('ul'); + + apiManager.getUserID().then((id) => { + this.myID = id; + }); + + $rootScope.$on('user_auth', (e: CustomEvent) => { + let userAuth = e.detail; + this.myID = userAuth ? userAuth.id : 0; + }); + + this.savedBtn.addEventListener('click', () => { + appImManager.setPeer(this.myID); + }); this.listsContainer.addEventListener('scroll', this.onSidebarScroll.bind(this)); diff --git a/src/lib/appManagers/appSidebarRight.ts b/src/lib/appManagers/appSidebarRight.ts index b23c7d1d..70f567b6 100644 --- a/src/lib/appManagers/appSidebarRight.ts +++ b/src/lib/appManagers/appSidebarRight.ts @@ -380,6 +380,12 @@ class AppSidebarRight { } else { appImManager.btnMute.style.display = 'none'; } + + appImManager.btnMenuMute.classList.remove('tgico-mute', 'tgico-unmute'); + appImManager.btnMenuMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute'); + let rp = appImManager.btnMenuMute.firstElementChild; + appImManager.btnMenuMute.innerText = muted ? 'Unmute' : 'Mute'; + appImManager.btnMenuMute.appendChild(rp); } //this.loadSidebarMedia(); diff --git a/src/lib/crypto/crypto_utils.ts b/src/lib/crypto/crypto_utils.ts index 92f5c354..801c87b7 100644 --- a/src/lib/crypto/crypto_utils.ts +++ b/src/lib/crypto/crypto_utils.ts @@ -6,7 +6,7 @@ import {str2bigInt, bpe, equalsInt, greater, divide_, one, bigInt2str, powMod} from 'leemon'; // @ts-ignore -import {BigInteger, SecureRandom} from 'jsbn'; +import {BigInteger} from 'jsbn'; import CryptoJS from './crypto.js'; import { addPadding, bytesToHex, bytesFromHex, nextRandomInt, bytesFromBigInt, dT, bytesFromWords } from '../bin_utils'; diff --git a/src/lib/mtproto/apiFileManager.ts b/src/lib/mtproto/apiFileManager.ts index e5e0f4b0..e92f373e 100644 --- a/src/lib/mtproto/apiFileManager.ts +++ b/src/lib/mtproto/apiFileManager.ts @@ -179,6 +179,8 @@ export class ApiFileManager { return Promise.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'}); } + this.log('downloadSmallFile', location, options); + let dcID = options.dcID || location.dc_id; let mimeType = options.mimeType || 'image/jpeg'; var fileName = this.getFileName(location); @@ -270,7 +272,7 @@ export class ApiFileManager { var cachedPromise = this.cachedSavePromises[fileName] || this.cachedDownloadPromises[fileName]; var fileStorage = this.getFileStorage(); - this.log('downloadFile', fileStorage.name, fileName, fileName.length, location, arguments); + //this.log('downloadFile', fileStorage.name, fileName, fileName.length, location, arguments); if(cachedPromise) { if(toFileEntry) { @@ -281,7 +283,7 @@ export class ApiFileManager { }); } - this.log('downloadFile cachedPromise'); + //this.log('downloadFile cachedPromise'); if(size) { let blob = await cachedPromise; @@ -296,7 +298,7 @@ export class ApiFileManager { } } - this.log('arriba'); + //this.log('arriba'); //var deferred = $q.defer() let deferredHelper: any = {notify: () => {}}; @@ -321,7 +323,7 @@ export class ApiFileManager { }; fileStorage.getFile(fileName, size).then(async(blob: Blob) => { - this.log('is that i wanted'); + //this.log('is that i wanted'); if(blob.size < size) { this.log('downloadFile need to deleteFile 2, wrong size:', blob.size, size); @@ -338,7 +340,7 @@ export class ApiFileManager { } //}, () => { }).catch(() => { - this.log('not i wanted'); + //this.log('not i wanted'); var fileWriterPromise = toFileEntry ? FileManager.getFileWriter(toFileEntry) : fileStorage.getFileWriter(fileName, mimeType); var processDownloaded = (bytes: any) => { diff --git a/src/lib/mtproto/networker.ts b/src/lib/mtproto/networker.ts index 72a36cea..fecb3f88 100644 --- a/src/lib/mtproto/networker.ts +++ b/src/lib/mtproto/networker.ts @@ -100,7 +100,7 @@ class MTPNetworker { this.log = logger('NET-' + dcID + (this.upload ? '-U' : '')); - this.log('constructor', this.authKey, this.authKeyID, this.serverSalt); + this.log('constructor'/* , this.authKey, this.authKeyID, this.serverSalt */); this.updateSession(); diff --git a/src/lib/mtproto/networkerFactory.ts b/src/lib/mtproto/networkerFactory.ts index e6c219e5..c141b6de 100644 --- a/src/lib/mtproto/networkerFactory.ts +++ b/src/lib/mtproto/networkerFactory.ts @@ -34,7 +34,7 @@ export class NetworkerFactory { } public getNetworker(dcID: number, authKey: number[], authKeyID: Uint8Array, serverSalt: number[], options: any) { - console.log(dT(), 'NetworkerFactory: creating new instance of MTPNetworker:', dcID, options); + //console.log(dT(), 'NetworkerFactory: creating new instance of MTPNetworker:', dcID, options); return new MTPNetworker(dcID, authKey, authKeyID, serverSalt, options); } } diff --git a/src/lib/mtproto/transports/abridged.ts b/src/lib/mtproto/transports/abridged.ts index 17c93d51..a11057de 100644 --- a/src/lib/mtproto/transports/abridged.ts +++ b/src/lib/mtproto/transports/abridged.ts @@ -11,7 +11,7 @@ class AbridgedPacketCodec { header = new Uint8Array([len]); } else { header = new Uint8Array([0x7f, ...addPadding(bytesFromHex(len.toString(16)).reverse(), 3, true)/* .reverse() */]); - console.log('got nobody cause im braindead', header, len); + //console.log('got nobody cause im braindead', header, len); } return new Uint8Array([...header, ...data]); diff --git a/src/lib/mtproto/transports/http.ts b/src/lib/mtproto/transports/http.ts index 1b84c1d7..06306379 100644 --- a/src/lib/mtproto/transports/http.ts +++ b/src/lib/mtproto/transports/http.ts @@ -8,7 +8,7 @@ export default class HTTP extends MTTransport { send = (data: Uint8Array) => { return fetch(this.url, {method: 'POST', body: data}).then(response => { - console.log('http response', response/* , response.arrayBuffer() */); + //console.log('http response', response/* , response.arrayBuffer() */); if(response.status != 200) { response.arrayBuffer().then(buffer => { diff --git a/src/lib/mtproto/transports/transport.ts b/src/lib/mtproto/transports/transport.ts index 58883f21..dbbe7705 100644 --- a/src/lib/mtproto/transports/transport.ts +++ b/src/lib/mtproto/transports/transport.ts @@ -3,5 +3,5 @@ export default abstract class MTTransport { } - abstract send: (data: Uint8Array/* , msgKey?: Uint8Array*/) => Promise; + abstract send: (data: Uint8Array) => Promise; } diff --git a/src/lib/mtproto/transports/websocket.ts b/src/lib/mtproto/transports/websocket.ts index 1d9ea8ce..3e57a15d 100644 --- a/src/lib/mtproto/transports/websocket.ts +++ b/src/lib/mtproto/transports/websocket.ts @@ -4,27 +4,15 @@ import aesjs from 'aes-js'; import abridgetPacketCodec from './abridged'; import {MTPNetworker} from '../networker'; import { logger } from '../../polyfill'; -//import '../../types.d.ts'; export class Obfuscation { - /** Encription Cipher */ public enc: aesjs.ModeOfOperation.ModeOfOperationCTR; - - /** Decription Cipher */ public dec: aesjs.ModeOfOperation.ModeOfOperationCTR; - //public initPayload: Uint8Array; - - /** - * Creates initialization payload for establishing web-socket connection - */ public init() { - //if(this.initPayload) return this.initPayload; - const initPayload = new Uint8Array(64); initPayload.randomize(); - //initPayload.set(new Uint8Array(bytesFromHex('8546029e63835e4138142813963d2987482dd6126089a1852ffadec149b4375c568dd0591d6b66cc95cec4b280b16f82fb6461ee1842b26fafc9ea76991ea4b1'))); - + while(true) { let val = (initPayload[3] << 24) | (initPayload[2] << 16) | (initPayload[1] << 8) | (initPayload[0]); let val2 = (initPayload[7] << 24) | (initPayload[6] << 16) | (initPayload[5] << 8) | (initPayload[4]); @@ -41,15 +29,9 @@ export class Obfuscation { initPayload.randomize(); } - //initPayload[0] = 0xFF; - //let h = initPayload.hex; - //console.log('initPayload.hex', initPayload.hex); ////////////////////////initPayload.subarray(60, 62).hex = dcID; - //console.log(initPayload.hex, h == initPayload.hex); - //console.log('initPayload', initPayload.hex); const reversedPayload = initPayload.slice().reverse(); - //console.log('initPayload', initPayload.hex); let encKey = initPayload.slice(8, 40); let encIv = initPayload.slice(40, 56); @@ -61,43 +43,28 @@ export class Obfuscation { initPayload.set(abridgetPacketCodec.obfuscateTag, 56); const encrypted = this.encode(initPayload); - //console.log('encrypted', encrypted.hex); initPayload.set(encrypted.slice(56, 64), 56); - //console.log('initPayload.hex', initPayload.hex); - return /* this.initPayload = */ initPayload; + return initPayload; } - /** - * Obfuscates data - */ public encode(payload: Uint8Array) { return this.enc.encrypt(payload); } - - /** - * Decodes obfuscated data - */ public decode(data: Uint8Array) { return this.dec.encrypt(data); } } -//let obfuscation = new Obfuscation(); - export default class Socket extends MTTransport { - /** Connection handler */ ws: WebSocket | undefined; - /** Pending requests */ - pending: Array<{resolve?: any, reject?: any, body?: Uint8Array/* , msgKey?: Uint8Array */}> = []; + pending: Array<{resolve?: any, reject?: any, body?: Uint8Array}> = []; - /** WebSocket connecting flag */ connected = false; - /** Instance transport */ transport = 'websocket'; obfuscation = new Obfuscation(); @@ -108,9 +75,6 @@ export default class Socket extends MTTransport { debug = false; - /** - * Creates new web socket handler - */ constructor(dcID: number, url: string) { super(dcID, url); @@ -129,65 +93,24 @@ export default class Socket extends MTTransport { this.ws.close(1000); } - this.ws = new WebSocket(/* dcConfigurator.chooseServer(this.dcID, false, 'websocket') */this.url, 'binary'); + this.ws = new WebSocket(this.url, 'binary'); this.ws.binaryType = 'arraybuffer'; this.ws.onopen = this.handleOpen; this.ws.onclose = this.handleClose; this.ws.onmessage = this.handleMessage; }; - /** - * Handles onopen event at websocket object - */ handleOpen = () => { this.log('opened'); - //obfuscation.init(); - //this.ws.send(new Uint8Array(bytesFromHex('ffc8b30b09c27d42791242f256b5186d3716f6f4f28121cf10cadc2196e496f092f97d13ed2c5a8b7181ca08ebe18e714ccac1cd60e88c4989bb4255682331c0'))); this.ws.send(this.obfuscation.init()); this.connected = true; this.releasePending(); - - /* let request = new TLSerialization({mtproto: true}); - - //let nonce = new Uint8Array(bytesFromHex('a370ec66e6e03c7b83843f3dfda22fd4').reverse()); - let nonce = new Uint8Array(16).randomize(); - - request.storeMethod('req_pq_multi', {nonce: nonce}); - - let body = request.getBytes(true) as Uint8Array; - - this.send(body); */ - - //this.ws.send(new Uint8Array(bytesFromHex('0e3cae6322eb93d4fc09d924fb17eb7887f1002679dedec754a6130f31e66c19016e6f3693a803b0a44d0567fcd01fe6a38b70fd328d3ebe9302f73454edd93a')).buffer); - //initPayload.slice(56).raw = encrypted.slice(56).raw; - - /* const { dc, thread, protocol } = this.cfg; - const payload = { - dc, thread, protocol, transport: this.transport, - }; - - async('transport_init', payload, (initPayload: Bytes) => { - if (!this.ws) return; - - this.ws.send(initPayload.buffer.buffer); - - this.isConnecting = false; - - log(this.cfg.dc, 'ready'); - - this.releasePending(); - }); */ }; - /** - * Handles onclose event at websocket object - */ handleClose = (event: CloseEvent) => { this.log('closed', event); - //this.emit('disconnected'); - //this.pending = []; this.connected = false; this.pending.length = 0; @@ -197,16 +120,8 @@ export default class Socket extends MTTransport { this.log('trying to reconnect...'); this.connect(); - //this.cfg.resolveError(this.cfg.dc, this.cfg.thread, this.transport, this.lastNonce || '', event.code, event.reason); }; - /* set networker(networker: MTPNetworker) { - this.networker = networker; - } */ - - /** - * Handles onmessage event at websocket object - */ handleMessage = (event: MessageEvent) => { this.debug && this.log('<-', 'handleMessage', event); @@ -230,51 +145,9 @@ export default class Socket extends MTTransport { } pending.resolve(data); - - /* try { - let deserializer = new TLDeserialization(data.buffer, {mtproto: true}); - let auth_key_id = deserializer.fetchLong('auth_key_id'); - if(auth_key_id != 0) console.error('auth_key_id != 0', auth_key_id); - - let msg_id = deserializer.fetchLong('msg_id'); - if(msg_id == 0) console.error('msg_id == 0', msg_id); - - let msg_len = deserializer.fetchInt('msg_len'); - if(!msg_len) console.error('no msg_len', msg_len); - - let response = deserializer.fetchObject('ResPQ'); - console.log(response); - } catch(e) { - console.error('mtpSendPlainRequest: deserialization went bad', e); - throw e; - } */ - /* const authKey = this.svc.getAuthKey(this.cfg.dc); - const { dc, thread } = this.cfg; - const payload = { - dc, thread, transport: this.transport, authKey: authKey ? authKey.key : '', msg: new Bytes(event.data), - }; - - if (!event.data) return; - - async('transport_decrypt', payload, (msg: Message | PlainMessage | Bytes) => { - if (msg instanceof PlainMessage) this.lastNonce = msg.nonce; - if (msg instanceof Message || msg instanceof PlainMessage) { - this.cfg.resolve(msg, { - dc: this.cfg.dc, - thread: this.cfg.thread, - transport: this.transport, - msgID: msg.id, - }); - } else { - throw new Error(`Unexpected answer: ${msg.hex}`); - } - }); */ }; - - /** - * Method sends bytes to server via web socket. - */ - send = (body: Uint8Array/* , msgKey?: Uint8Array */) => { + + send = (body: Uint8Array) => { this.debug && this.log('-> body length to pending:', body.length); if(this.networker) { @@ -289,58 +162,6 @@ export default class Socket extends MTTransport { return promise; } - - /* let promise = new Promise((resolve, reject) => { - this.pending.push({resolve, reject, body}); - - this.releasePending(); - }); - - return promise; */ - - /* // let msg_id = '6784284127384679768';//timeManager.generateID(); - let msg_id = timeManager.generateID(); - console.log('generated msg_id:', msg_id); - let packed = new TLSerialization({ - mtproto: true - }); - //packed.storeRawBytes([0x0a]); - packed.storeLong(0); - packed.storeLong(msg_id); - packed.storeInt(body.byteLength); - packed.storeRawBytes(body); - - console.log('packed', (packed.getBytes(true) as Uint8Array).hex, - bytesToHex([...new Uint8Array(bigStringInt(msg_id).toByteArray())])); - - let toEncode = abridgetPacketCodec.encodePacket(packed.getBytes(true) as Uint8Array); - - console.log('send req_pq:', toEncode.hex); - //let enc = obfuscation.encode(request.getBytes(true) as Uint8Array); - let enc = obfuscation.encode(toEncode); - //let enc = new Uint8Array(bytesFromHex('b6f899247854750f879db416e95fd41145e8f7f910741b50c02a20025d3f9cbd09b09f3306be378c43')); - //let enc = obfuscation.encode(new Uint8Array(bytesFromHex('00000000000000000424ec94a191265e14000000f18e7ebef8c6203ebc2ae31b44a3aafd8afdf367'))); - console.log('send req_pq:', enc.hex); - this.ws.send(enc); */ - - //if (msg instanceof PlainMessage) this.lastNonce = msg.nonce; - - /* if(this.ws && this.ws.readyState === 1) { - const authKey = this.svc.getAuthKey(this.cfg.dc); - const { dc, thread } = this.cfg; - const payload = { - msg, dc, thread, transport: this.transport, authKey: authKey ? authKey.key : '', - }; - - async('transport_encrypt', payload, (data: Bytes) => { - if (this.ws) this.ws.send(data.buffer.buffer); - }); - - this.releasePending(); - return; - } */ - - //this.pending.push(msg); } releasePending() { diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index c1c228b1..690c79ae 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -227,11 +227,6 @@ } } - img, video { - /* object-fit: contain; */ - object-fit: cover; - } - .emoji { height: 18px; width: 18px; @@ -296,6 +291,10 @@ max-width: 300px; max-height: 300px; + img { + object-fit: contain; + } + .message.message-empty { display: none; } @@ -336,6 +335,11 @@ max-width: 380px; max-height: 380px; } + + img, video { + /* object-fit: contain; */ + object-fit: cover; + } } &.video { diff --git a/src/scss/partials/_sidebar.scss b/src/scss/partials/_sidebar.scss index eabff923..b0234c9f 100644 --- a/src/scss/partials/_sidebar.scss +++ b/src/scss/partials/_sidebar.scss @@ -30,10 +30,6 @@ } } -.sidebar-menu-button > div { - display: none; -} - .profile-content { .profile-name { text-align: center; diff --git a/src/scss/style.scss b/src/scss/style.scss index a92537a9..ae2269d4 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -127,6 +127,86 @@ input { } } +.danger { + color: $color-error!important; +} + +.btn-menu-toggle { + position: relative; + overflow: visible; + + &.menu-open { + background-color: rgba(112, 117, 121, 0.08); + } + + .btn-menu { + visibility: hidden; + position: absolute; + background: #fff; + box-shadow: 0 5px 8px 1px rgba(0,0,0,.24); + z-index: 1; + top: 100%; + margin-top: 8px; + padding: 9px 0; + border-radius: $border-radius; + opacity: 0; + transform: scale(.8); + transition-property: opacity,transform,visibility; + transition-duration: .2s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + font-size: 16px; + + &.active { + visibility: visible; + opacity: 1; + transform: scale(1); + } + + &.bottom-left { + right: 0; + top: 100%; + transform-origin: top right; + } + + &.bottom-right { + left: 0; + top: 100%; + transform-origin: top left; + } + + > div { + display: flex; + position: relative; + padding: 0 40px 0 20px; + height: 56px; + cursor: pointer; + background-position: 16px center; + background-size: 24px 24px; + background-repeat: no-repeat; + color: #000; + text-transform: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + align-items: center; + + &:hover { + background-color: rgba(112, 117, 121, 0.06); + } + + &:before { + color: $color-gray; + font-size: 1.5rem; + margin-right: 35px; + } + + &.danger:before { + color: $color-error; + } + } + } +} + .user-avatar { color: #fff; width: 52px; @@ -200,6 +280,7 @@ input { height: 100%; overflow: hidden; background: transparent; + border-radius: inherit; } .c-ripple__circle {