edit message (need to do media) & scroll chat restore position

This commit is contained in:
Eduard Kuzmenko 2020-02-14 23:15:41 +07:00
parent 250c866285
commit 41b5769c16
14 changed files with 268 additions and 71 deletions

View File

@ -263,7 +263,7 @@
<div class="btn-menu bottom-right">
<div class="btn-menu-item menu-new-group tgico-newgroup rp">New Group</div>
<div class="btn-menu-item menu-contacts tgico-user rp">Contacts</div>
<div class="btn-menu-item menu-archive tgico-archive rp">Archived</div>
<div class="btn-menu-item menu-archive tgico-archive rp">Archived <span class="archived-count"></span></div>
<div class="btn-menu-item menu-saved tgico-savedmessages rp">Saved</div>
<div class="btn-menu-item menu-settings tgico-settings rp">Settings</div>
<div class="btn-menu-item menu-help tgico-help rp">Help</div>

View File

@ -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;

View File

@ -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) {

View File

@ -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', {

View File

@ -44,6 +44,7 @@ export class AppMessagesManager {
public pendingTopMsgs: any = {};
public sendFilePromise: CancellablePromise<void> = 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<number> {
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});
}

View File

@ -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');

View File

@ -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<any>;
//private loadDialogsPromise: Promise<any>;
private loadDialogsPromise: ReturnType<AppMessagesManager["getConversations"]>;
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) {

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -211,6 +211,6 @@
}
.unread-muted, .tgico-pinnedchat {
background: #cecece;
background: #c5c9cc;
}
}
}

View File

@ -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;

View File

@ -162,8 +162,7 @@
}
#content-docs {
padding: 15px;
padding-top: 3px;
padding: 3px 15px;
.document {
padding-left: 5rem;

View File

@ -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;
}
}
}
}