diff --git a/src/components/scrollable.ts b/src/components/scrollable.ts index 581e26ab..1bbd7970 100644 --- a/src/components/scrollable.ts +++ b/src/components/scrollable.ts @@ -254,6 +254,12 @@ export default class Scrollable { while((isElementInViewport(this.paddingBottomDiv) || isScrolledIntoView(this.paddingBottomDiv)) && this.paddings.down) { let child = this.hiddenElements.down.shift(); + if(!child) { + this.paddings.down = 0; + this.paddingBottomDiv.style.height = '0px'; + break; + } + splitUp.append(child); this.paddings.down -= child.scrollHeight; diff --git a/src/lib/appManagers/appDocsManager.ts b/src/lib/appManagers/appDocsManager.ts index 1cb036b9..dd73a47d 100644 --- a/src/lib/appManagers/appDocsManager.ts +++ b/src/lib/appManagers/appDocsManager.ts @@ -1,5 +1,4 @@ import apiFileManager, { CancellablePromise } from '../mtproto/apiFileManager'; -import {$rootScope} from '../utils'; import FileManager from '../filemanager'; import {RichTextProcessor} from '../richtextprocessor'; //import { MTDocument } from '../../components/misc'; @@ -233,7 +232,7 @@ class AppDocsManager { //historyDoc.progress.cancel = downloadPromise.cancel; - console.log('return downloadPromise:', downloadPromise); + //console.log('return downloadPromise:', downloadPromise); return downloadPromise; } @@ -253,7 +252,7 @@ class AppDocsManager { console.log('saved doc', doc); }); - console.log('got promise from downloadDoc', promise); + //console.log('got promise from downloadDoc', promise); return {promise}; } catch(err) { diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 573c89fe..2ea149d4 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -73,10 +73,10 @@ class ScrollPosition { //console.log('scrollPosition restore 2', this.node.scrollHeight, (this.node.scrollHeight //- this.previousScrollHeightMinusTop) + 'px', this.container); - //if(this.readyFor === 'up') { + if(this.readyFor === 'up' || appImManager.scroll.parentElement.classList.contains('scrolled-down')) { this.container.scrollTop = this.node.scrollHeight - this.previousScrollHeightMinusTop; - //} + } // 'down' doesn't need to be special cased unless the // content was flowing upwards, which would only happen @@ -122,7 +122,17 @@ class ChatInput { captionInput?: HTMLInputElement } = {}; + public replyElements: { + container?: HTMLDivElement, + cancelBtn?: HTMLButtonElement, + titleEl?: HTMLDivElement, + subtitleEl?: HTMLDivElement + } = {}; + public willSendWebPage: any = null; + public replyToMsgID = 0; + public editMsgID = 0; + public noWebPage = false; constructor() { this.toggleEmoticons = this.pageEl.querySelector('.toggle-emoticons') as HTMLButtonElement; @@ -138,6 +148,11 @@ class ChatInput { this.attachMediaPopUp.mediaContainer = this.attachMediaPopUp.container.querySelector('.popup-photo') as HTMLDivElement; this.attachMediaPopUp.captionInput = this.attachMediaPopUp.container.querySelector('input') as HTMLInputElement; + this.replyElements.container = this.pageEl.querySelector('.reply-wrapper') as HTMLDivElement; + this.replyElements.cancelBtn = this.replyElements.container.querySelector('.reply-cancel') as HTMLButtonElement; + this.replyElements.titleEl = this.replyElements.container.querySelector('.reply-title') as HTMLDivElement; + this.replyElements.subtitleEl = this.replyElements.container.querySelector('.reply-subtitle') as HTMLDivElement; + this.messageInput.addEventListener('keydown', (e: KeyboardEvent) => { if(e.key == 'Enter') { /* if(e.ctrlKey || e.metaKey) { @@ -177,13 +192,12 @@ class ChatInput { }).then((webpage: any) => { appWebPagesManager.saveWebPage(webpage); if(this.lastUrl != url) return; - //console.log(webpage); - - appImManager.replyElements.titleEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.site_name || webpage.title || ''); - appImManager.replyElements.subtitleEl.innerHTML = RichTextProcessor.wrapEmojiText(webpage.description || webpage.url || ''); - appImManager.replyElements.container.classList.add('active'); - appImManager.replyToMsgID = 0; - appImManager.noWebPage = false; + console.log('got webpage: ', webpage); + + this.setTopInfo(webpage.site_name || webpage.title, webpage.description || webpage.url); + + this.replyToMsgID = 0; + this.noWebPage = false; this.willSendWebPage = webpage; }); } @@ -408,6 +422,26 @@ class ChatInput { lottieLoader.checkAnimations(false, EMOTICONSSTICKERGROUP); }, 0/* 200 */); }; + + this.replyElements.cancelBtn.addEventListener('click', () => { + this.replyElements.container.classList.remove('active'); + this.replyToMsgID = 0; + + if(this.editMsgID) { + if(this.willSendWebPage) { + let message = appMessagesManager.getMessage(this.editMsgID); + this.setTopInfo('Editing', message.message); + } else { + this.editMsgID = 0; + this.messageInput.innerHTML = ''; + this.btnSend.classList.remove('tgico-send'); + this.btnSend.classList.add('tgico-microphone2'); + } + } + + this.noWebPage = true; + this.willSendWebPage = null; + }); } public serializeNodes(nodes: Node[]): string { @@ -430,14 +464,23 @@ class ChatInput { //return; this.lastUrl = ''; - appMessagesManager.sendText(appImManager.peerID, str, { - replyToMsgID: appImManager.replyToMsgID == 0 ? undefined : appImManager.replyToMsgID, - noWebPage: appImManager.noWebPage, - webPage: this.willSendWebPage - }); - appImManager.replyToMsgID = 0; - appImManager.noWebPage = false; - appImManager.replyElements.container.classList.remove('active'); + + if(this.editMsgID) { + appMessagesManager.editMessage(this.editMsgID, str, { + noWebPage: this.noWebPage + }); + } else { + appMessagesManager.sendText(appImManager.peerID, str, { + replyToMsgID: this.replyToMsgID == 0 ? undefined : this.replyToMsgID, + noWebPage: this.noWebPage, + webPage: this.willSendWebPage + }); + } + + this.editMsgID = 0; + this.replyToMsgID = 0; + this.noWebPage = false; + this.replyElements.container.classList.remove('active'); appImManager.scroll.scrollTop = appImManager.scroll.scrollHeight; this.willSendWebPage = null; this.messageInput.innerText = ''; @@ -445,6 +488,23 @@ class ChatInput { this.btnSend.classList.remove('tgico-send'); this.btnSend.classList.add('tgico-microphone2'); }; + + public setTopInfo(title: string, subtitle: string, input?: string) { + //appImManager.scrollPosition.prepareFor('down'); + + this.replyElements.titleEl.innerHTML = title ? RichTextProcessor.wrapEmojiText(title) : ''; + this.replyElements.subtitleEl.innerHTML = subtitle ? RichTextProcessor.wrapEmojiText(subtitle) : ''; + this.replyElements.container.classList.add('active'); + + if(input !== undefined) { + this.messageInput.innerHTML = input ? RichTextProcessor.wrapRichText(input) : ''; + + this.btnSend.classList.remove('tgico-microphone2'); + this.btnSend.classList.add('tgico-send'); + } + + //appImManager.scrollPosition.restore(); + } } export class AppImManager { @@ -508,13 +568,6 @@ export class AppImManager { private contextMenuEdit = this.contextMenu.querySelector('.menu-edit') as HTMLDivElement; private contextMenuMsgID: number; - public replyElements: { - container?: HTMLDivElement, - cancelBtn?: HTMLButtonElement, - titleEl?: HTMLDivElement, - subtitleEl?: HTMLDivElement - } = {}; - private popupDeleteMessage: { popupEl?: HTMLDivElement, deleteBothBtn?: HTMLButtonElement, @@ -522,9 +575,6 @@ export class AppImManager { cancelBtn?: HTMLButtonElement } = {}; - public replyToMsgID = 0; - public noWebPage = false; - constructor() { this.log = logger('IM'); @@ -537,11 +587,6 @@ export class AppImManager { this.popupDeleteMessage.deleteMeBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-me') as HTMLButtonElement; this.popupDeleteMessage.cancelBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-close') as HTMLButtonElement; - this.replyElements.container = this.pageEl.querySelector('.reply-wrapper') as HTMLDivElement; - this.replyElements.cancelBtn = this.replyElements.container.querySelector('.reply-cancel') as HTMLButtonElement; - this.replyElements.titleEl = this.replyElements.container.querySelector('.reply-title') as HTMLDivElement; - this.replyElements.subtitleEl = this.replyElements.container.querySelector('.reply-subtitle') as HTMLDivElement; - apiManager.getUserID().then((id) => { this.myID = id; }); @@ -931,11 +976,16 @@ export class AppImManager { this.contextMenu.querySelector('.menu-reply').addEventListener('click', () => { let message = appMessagesManager.getMessage(this.contextMenuMsgID); - let title = appPeersManager.getPeerTitle(message.fromID); - this.replyElements.titleEl.innerHTML = title; - this.replyElements.subtitleEl.innerHTML = message.message ? RichTextProcessor.wrapEmojiText(message.message) : ''; - this.replyElements.container.classList.add('active'); - this.replyToMsgID = this.contextMenuMsgID; + this.chatInputC.setTopInfo(appPeersManager.getPeerTitle(message.fromID), message.message); + this.chatInputC.replyToMsgID = this.contextMenuMsgID; + this.chatInputC.editMsgID = 0; + }); + + this.contextMenuEdit.addEventListener('click', () => { + let message = appMessagesManager.getMessage(this.contextMenuMsgID); + this.chatInputC.setTopInfo('Editing', message.message, message.message); + this.chatInputC.replyToMsgID = 0; + this.chatInputC.editMsgID = this.contextMenuMsgID; }); this.contextMenuPin.addEventListener('click', () => { @@ -949,13 +999,6 @@ export class AppImManager { }); }); - this.replyElements.cancelBtn.addEventListener('click', () => { - this.replyElements.container.classList.remove('active'); - this.replyToMsgID = 0; - this.noWebPage = true; - this.chatInputC.willSendWebPage = null; - }); - this.popupDeleteMessage.deleteBothBtn.addEventListener('click', () => { this.deleteMessages(true); this.popupDeleteMessage.cancelBtn.click(); @@ -1322,7 +1365,7 @@ export class AppImManager { // clear input this.chatInputC.messageInput.innerHTML = ''; - this.replyElements.cancelBtn.click(); + this.chatInputC.replyElements.cancelBtn.click(); // clear messages this.chatInner.innerHTML = ''; @@ -1894,7 +1937,8 @@ export class AppImManager { let originalText = ''; if(originalMessage.message) { originalText = RichTextProcessor.wrapRichText(originalMessage.message, { - entities: originalMessage.totalEntities + entities: originalMessage.totalEntities, + noLinebreaks: true }); } @@ -1945,7 +1989,7 @@ export class AppImManager { nameDiv.style.color = appPeersManager.getPeerColorByID(message.fromID, false); nameDiv.dataset.peerID = message.fromID; bubble.append(nameDiv); - } else if(!message.reply_to_mid) { + } else /* if(!message.reply_to_mid) */ { bubble.classList.add('hide-name'); } @@ -1975,6 +2019,8 @@ export class AppImManager { bubble.append(avatarDiv); } + } else { + bubble.classList.add('hide-name'); } if(message._ == 'messageService') { @@ -2211,10 +2257,10 @@ export class AppImManager { }; if(!this.muted) { - settings.flags |= 2 << 1; + settings.flags |= 1 << 2; settings.mute_until = 2147483646; } else { - settings.flags |= 1 << 1; + settings.flags |= 2; } apiManager.invokeApi('account.updateNotifySettings', { diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 558e8eed..16ae53f3 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -44,6 +44,7 @@ export class AppMessagesManager { public pendingTopMsgs: any = {}; public sendFilePromise: CancellablePromise = Promise.resolve(); public tempID = -1; + public tempFinalizeCallbacks: any = {}; public dialogsIndex: any = SearchIndexManager.createIndex(); public cachedResults: any = {query: false}; @@ -163,6 +164,70 @@ export class AppMessagesManager { return sendEntites; } + public editMessage(messageID: number, text: string, options: { + noWebPage?: boolean + } = {}) { + if(typeof(text) !== 'string' || !this.canEditMessage(messageID)) { + return Promise.reject(); + } + + if(messageID < 0) { + if(this.tempFinalizeCallbacks[messageID] === undefined) { + this.tempFinalizeCallbacks[messageID] = {} + } + + let promise = new Promise((resolve, reject) => { + this.tempFinalizeCallbacks[messageID].edit = (mid: number) => { + console.log('invoke callback', mid) + this.editMessage(mid, text).then(resolve, reject); + } + }); + + return promise; + } + + var entities: any = []; + text = RichTextProcessor.parseMarkdown(text, entities) + + var message = this.getMessage(messageID); + var peerID = this.getMessagePeer(message); + var flags = 0; + let noWebPage = options.noWebPage || false; + + if(noWebPage) { + flags |= 2; + } + + if(text) { + flags |= 8 | 1 << 11; + } + + /* if(message.media) { + flags |= 1 << 14; + } */ + + return apiManager.invokeApi('messages.editMessage', { + flags: flags, + peer: AppPeersManager.getInputPeerByID(peerID), + id: appMessagesIDsManager.getMessageLocalID(messageID), + message: text, + media: message.media, + entities: this.getInputEntities(entities), + no_webpage: noWebPage, + }).then((updates) => { + apiUpdatesManager.processUpdateMessage(updates) + }, (error) => { + if(error && error.type == 'MESSAGE_NOT_MODIFIED') { + error.handled = true; + return; + } + if(error && error.type == 'MESSAGE_EMPTY') { + error.handled = true; + } + return Promise.reject(error); + }); + } + public sendText(peerID: number, text: string, options: { entities?: any[], replyToMsgID?: number, @@ -764,11 +829,12 @@ export class AppMessagesManager { if(/* isSearch || */this.allDialogsLoaded[folderID] || curDialogStorage.length >= offset + limit) { return Promise.resolve({ - dialogs: curDialogStorage.slice(offset, offset + limit) + dialogs: curDialogStorage.slice(offset, offset + limit), + count: curDialogStorage.length }); } - return this.getTopMessages(limit, folderID).then(() => { + return this.getTopMessages(limit, folderID).then(count => { let curDialogStorage = this.dialogsStorage.dialogs; if(folderID > 0) { @@ -789,12 +855,13 @@ export class AppMessagesManager { //console.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogsStorage.dialogs.length); return { - dialogs: curDialogStorage.slice(offset, offset + limit) + dialogs: curDialogStorage.slice(offset, offset + limit), + count: count }; }); } - public getTopMessages(limit: number, folderID = -1) { + public getTopMessages(limit: number, folderID = -1): Promise { var dialogs = this.dialogsStorage.dialogs; var offsetDate = 0; var offsetID = 0; @@ -802,6 +869,12 @@ export class AppMessagesManager { var offsetIndex = 0; var flags = 0; + if(folderID > 0) { + dialogs = dialogs.filter(d => d.folder_id == folderID); + } else { + dialogs = dialogs.filter(d => d.folder_id != 1); + } + if(this.dialogsOffsetDate[folderID]) { offsetDate = this.dialogsOffsetDate[folderID] + serverTimeManager.serverTimeOffset; offsetIndex = this.dialogsOffsetDate[folderID] * 0x10000; @@ -888,6 +961,8 @@ export class AppMessagesManager { } else { $rootScope.$broadcast('dialogs_multiupdate', {}); } + + return dialogsResult.count; }); } @@ -1232,6 +1307,25 @@ export class AppMessagesManager { return true; } + public canEditMessage(messageID: number) { + if (!this.messagesStorage[messageID]) { + return false + } + var message = this.messagesStorage[messageID] + if (!message || + !message.canBeEdited) { + return false + } + if (this.getMessagePeer(message) == appUsersManager.getSelf().id) { + return true + } + if (message.date < tsNow(true) - 2 * 86400 || + !message.pFlags.out) { + return false + } + return true + } + public applyConversations(dialogsResult: any) { appUsersManager.saveApiUsers(dialogsResult.users); appChatsManager.saveApiChats(dialogsResult.chats); @@ -2875,7 +2969,20 @@ export class AppMessagesManager { return false } + /* public finalizePendingMessageCallbacks(tempID: number, mid: number) { + $rootScope.$broadcast('message_sent', {tempID, mid}); + } */ + public finalizePendingMessageCallbacks(tempID: number, mid: number) { + var callbacks = this.tempFinalizeCallbacks[tempID]; + console.warn(dT(), callbacks, tempID); + if(callbacks !== undefined) { + callbacks.forEach((callback: any) => { + callback(mid); + }); + delete this.tempFinalizeCallbacks[tempID]; + } + $rootScope.$broadcast('message_sent', {tempID, mid}); } diff --git a/src/lib/appManagers/appPhotosManager.ts b/src/lib/appManagers/appPhotosManager.ts index 1a3d22c2..d7420fb6 100644 --- a/src/lib/appManagers/appPhotosManager.ts +++ b/src/lib/appManagers/appPhotosManager.ts @@ -228,7 +228,7 @@ export class AppPhotosManager { } */ if(isPhoto/* && photoSize.size >= 1e6 */) { - console.log('Photos downloadFile exec', photo); + //console.log('Photos downloadFile exec', photo); /* let promise = apiFileManager.downloadFile(photo.dc_id, location, photoSize.size); let blob = await promise; @@ -241,7 +241,7 @@ export class AppPhotosManager { return blob; */ return apiFileManager.downloadFile(photo.dc_id, location, photoSize.size); } else { - console.log('Photos downloadSmallFile exec', photo, location); + //console.log('Photos downloadSmallFile exec', photo, location); return apiFileManager.downloadSmallFile(location); } } else return Promise.reject('no photoSize'); diff --git a/src/lib/appManagers/appSidebarLeft.ts b/src/lib/appManagers/appSidebarLeft.ts index 7809a39b..462a7e93 100644 --- a/src/lib/appManagers/appSidebarLeft.ts +++ b/src/lib/appManagers/appSidebarLeft.ts @@ -1,7 +1,7 @@ import { logger } from "../polyfill"; import { putPreloader, formatPhoneNumber } from "../../components/misc"; import Scrollable from '../../components/scrollable'; -import appMessagesManager from "./appMessagesManager"; +import appMessagesManager, { AppMessagesManager } from "./appMessagesManager"; import appDialogsManager from "./appDialogsManager"; import { isElementInViewport, numberWithCommas } from "../utils"; import appMessagesIDsManager from "./appMessagesIDsManager"; @@ -47,6 +47,7 @@ class AppSidebarLeft { private menuEl = this.toolsBtn.querySelector('.btn-menu'); private savedBtn = this.menuEl.querySelector('.menu-saved'); private archivedBtn = this.menuEl.querySelector('.menu-archive'); + private archivedCount = this.archivedBtn.querySelector('.archived-count') as HTMLSpanElement; private listsContainer: HTMLDivElement = null; @@ -56,7 +57,8 @@ class AppSidebarLeft { private chatsOffsetIndex = 0; private chatsPreloader: HTMLDivElement; private chatsLoadCount = 0; - private loadDialogsPromise: Promise; + //private loadDialogsPromise: Promise; + private loadDialogsPromise: ReturnType; private log = logger('SL'); @@ -248,6 +250,11 @@ class AppSidebarLeft { }); } + if(archived) { + let count = result.count; + this.archivedCount.innerText = '' + count; + } + this.log('loaded ' + this.chatsLoadCount + ' dialogs by offset:', offset, result, this.scroll.hiddenElements); this.scroll.onScroll(); } catch(err) { diff --git a/src/lib/appManagers/appSidebarRight.ts b/src/lib/appManagers/appSidebarRight.ts index 7090d3ea..ea182b5c 100644 --- a/src/lib/appManagers/appSidebarRight.ts +++ b/src/lib/appManagers/appSidebarRight.ts @@ -303,7 +303,7 @@ class AppSidebarRight { } } - //console.log('come back down to my knees', message); + //this.log('come back down to my knees', message); let div = wrapDocument(message.media.document, true); this.sharedMedia.contentDocuments.append(div); diff --git a/src/lib/mtproto/apiFileManager.ts b/src/lib/mtproto/apiFileManager.ts index 6944fffa..f1273314 100644 --- a/src/lib/mtproto/apiFileManager.ts +++ b/src/lib/mtproto/apiFileManager.ts @@ -314,7 +314,7 @@ export class ApiFileManager { } } - this.log('arriba'); + //this.log('arriba'); //var deferred = $q.defer() let deferredHelper: any = {notify: () => {}}; @@ -468,7 +468,7 @@ export class ApiFileManager { } }; - console.log(deferred, deferred.notify, deferred.cancel); + //console.log(deferred, deferred.notify, deferred.cancel); if(!toFileEntry) { this.cachedDownloadPromises[fileName] = deferred; diff --git a/src/scss/partials/_chat.scss b/src/scss/partials/_chat.scss index 9afc427b..1b9db58d 100644 --- a/src/scss/partials/_chat.scss +++ b/src/scss/partials/_chat.scss @@ -485,7 +485,6 @@ .quote { // padding-left: .5rem; padding-left: 0.55rem; - margin-top: 6px; max-width: 100%; overflow: hidden; width: 100%; @@ -518,8 +517,18 @@ white-space: nowrap; position: absolute; top: 0; + + .quote { + margin-top: 0; + } } } + + .quote .text { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } } .message { @@ -673,6 +682,10 @@ //padding-top: .2675rem; padding-top: 6px; } + + &.hide-name:not(.sticker):not(.emoji-big) .box:not(.web) .quote { + margin-top: 6px; + } &:not(.sticker):not(.emoji-big):not(.round):last-child:after { position: absolute; diff --git a/src/scss/partials/_chatlist.scss b/src/scss/partials/_chatlist.scss index eedefa9f..e82bd31a 100644 --- a/src/scss/partials/_chatlist.scss +++ b/src/scss/partials/_chatlist.scss @@ -211,6 +211,6 @@ } .unread-muted, .tgico-pinnedchat { - background: #cecece; + background: #c5c9cc; } -} \ No newline at end of file +} diff --git a/src/scss/partials/_leftSidebar.scss b/src/scss/partials/_leftSidebar.scss index 2366808d..d4582cbe 100644 --- a/src/scss/partials/_leftSidebar.scss +++ b/src/scss/partials/_leftSidebar.scss @@ -38,6 +38,25 @@ } } + .sidebar-tools-button .btn-menu { + width: 217px; + + .archived-count { + border-radius: 12px; + min-width: 24px; + padding: 0 8px; + height: 24px; + text-align: center; + line-height: 24px; + color: #fff; + font-weight: 500; + background-color: #c5c9cc; + justify-self: flex-end; + position: absolute; + right: 16px; + } + } + .search-group { width: 100%; border-bottom: 1px solid #DADCE0; diff --git a/src/scss/partials/_sidebar.scss b/src/scss/partials/_sidebar.scss index 0fe03652..8f6dff46 100644 --- a/src/scss/partials/_sidebar.scss +++ b/src/scss/partials/_sidebar.scss @@ -162,8 +162,7 @@ } #content-docs { - padding: 15px; - padding-top: 3px; + padding: 3px 15px; .document { padding-left: 5rem; diff --git a/src/scss/style.scss b/src/scss/style.scss index 354093d7..5278cd4f 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -1282,7 +1282,8 @@ div.scrollable::-webkit-scrollbar-thumb { width: 4px; //margin-left: 2px; background-color: #000; - cursor: grab; + //cursor: grab; + cursor: default; opacity: 0; transition-property: opacity,width,right; transition-duration: .2s; @@ -1553,4 +1554,4 @@ div.scrollable::-webkit-scrollbar-thumb { color: #000; } } -} \ No newline at end of file +}