Comments alpha version

This commit is contained in:
Eduard Kuzmenko 2020-12-20 05:54:35 +02:00
parent 16ed79a74d
commit 7e464418a6
23 changed files with 614 additions and 292 deletions

View File

@ -66,7 +66,7 @@ function wrapVoiceMessage(audioEl: AudioElement) {
audioEl.classList.add('is-voice');
const message = audioEl.message;
const doc = message.media.document as MyDocument;
const doc = (message.media.document || message.media.webpage.document) as MyDocument;
const isOut = message.fromId == rootScope.myId && message.peerId != rootScope.myId;
let isUnread = message && message.pFlags.media_unread;
if(isUnread) {
@ -254,7 +254,7 @@ function wrapVoiceMessage(audioEl: AudioElement) {
function wrapAudio(audioEl: AudioElement) {
const withTime = audioEl.withTime;
const doc = audioEl.message.media.document;
const doc = audioEl.message.media.document || audioEl.message.media.webpage.document;
const title = doc.audioTitle || doc.file_name;
let subtitle = doc.audioPerformer ? RichTextProcessor.wrapPlainText(doc.audioPerformer) : '';
@ -334,7 +334,7 @@ export default class AudioElement extends HTMLElement {
this.classList.add('audio');
const doc = this.message.media.document;
const doc = this.message.media.document || this.message.media.webpage.document;
const uploading = this.message.pFlags.is_outgoing;
const durationStr = String(doc.duration | 0).toHHMMSS(true);
@ -360,7 +360,7 @@ export default class AudioElement extends HTMLElement {
audioTimeDiv.innerHTML = durationStr;
const onLoad = (autoload = true) => {
const audio = this.audio = appMediaPlaybackController.addMedia(this.message.peerId, this.message.media.document, this.message.mid, autoload);
const audio = this.audio = appMediaPlaybackController.addMedia(this.message.peerId, this.message.media.document || this.message.media.webpage.document, this.message.mid, autoload);
this.onTypeDisconnect = onTypeLoad();

View File

@ -37,6 +37,7 @@ import Chat from "./chat";
import ListenerSetter from "../../helpers/listenerSetter";
import PollElement from "../poll";
import AudioElement from "../audio";
import { MessageReplies, MessageReplyHeader } from "../../layer";
const IGNORE_ACTIONS = ['messageActionHistoryClear'];
@ -104,7 +105,7 @@ export default class ChatBubbles {
public replyFollowHistory: number[] = [];
constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appStickersManager: AppStickersManager, private appUsersManager: AppUsersManager, private appInlineBotsManager: AppInlineBotsManager, private appPhotosManager: AppPhotosManager, private appDocsManager: AppDocsManager, private appPeersManager: AppPeersManager, private appChatsManager: AppChatsManager) {
this.chat.log.error('Bubbles construction');
//this.chat.log.error('Bubbles construction');
this.listenerSetter = new ListenerSetter();
@ -358,10 +359,9 @@ export default class ChatBubbles {
this.listenerSetter.add(rootScope, 'dialog_unread', (e) => {
const info = e.detail;
const dialog = this.appMessagesManager.getDialogByPeerId(info.peerId)[0];
if(dialog?.peerId == this.peerId) {
if(info.peerId == this.peerId) {
this.chat.input.setUnreadCount();
this.updateUnreadByDialog(dialog);
this.updateUnreadByDialog();
}
});
@ -510,6 +510,21 @@ export default class ChatBubbles {
return;
}
const commentsDiv: HTMLElement = findUpClassName(target, 'replies-footer');
if(commentsDiv) {
const mid = +bubble.dataset.mid;
const message = this.chat.getMessage(mid);
const replies = message.replies as MessageReplies;
if(replies) {
this.appMessagesManager.getDiscussionMessage(this.peerId, mid).then(result => {
const message = result.messages[0];
this.chat.appImManager.setInnerPeer(-replies.channel_id, (message as MyMessage).mid, 'discussion');
});
}
return;
}
//this.log('chatInner click:', target);
const isVideoComponentElement = target.tagName == 'SPAN';
/* if(isVideoComponentElement) {
@ -634,7 +649,12 @@ export default class ChatBubbles {
if(isReplyClick && bubble.classList.contains('is-reply')/* || bubble.classList.contains('forwarded') */) {
this.replyFollowHistory.push(+bubble.dataset.mid);
let originalMessageId = +bubble.getAttribute('data-original-mid');
this.chat.appImManager.setInnerPeer(this.peerId, originalMessageId);
if(this.chat.type === 'discussion') {
this.chat.appImManager.setPeer(this.peerId, originalMessageId);
} else {
this.chat.appImManager.setInnerPeer(this.peerId, originalMessageId);
}
//this.chat.setPeer(this.peerId, originalMessageId);
}
} else if(target.tagName == 'IMG' && target.parentElement.tagName == "AVATAR-ELEMENT") {
@ -673,14 +693,15 @@ export default class ChatBubbles {
const mid = this.replyFollowHistory.pop();
this.chat.setPeer(this.peerId, mid);
} else {
const dialog = this.appMessagesManager.getDialogByPeerId(this.peerId)[0];
this.chat.setPeer(this.peerId/* , dialog.top_message */);
// const dialog = this.appMessagesManager.getDialogByPeerId(this.peerId)[0];
if(dialog) {
this.chat.setPeer(this.peerId/* , dialog.top_message */);
} else {
this.log('will scroll down 3');
this.scroll.scrollTop = this.scroll.scrollHeight;
}
// if(dialog) {
// this.chat.setPeer(this.peerId/* , dialog.top_message */);
// } else {
// this.log('will scroll down 3');
// this.scroll.scrollTop = this.scroll.scrollHeight;
// }
}
}
@ -745,10 +766,11 @@ export default class ChatBubbles {
if(this.scrolledAllDown) return;
let dialog = this.appMessagesManager.getDialogByPeerId(this.peerId)[0];
//let dialog = this.appMessagesManager.getDialogByPeerId(this.peerId)[0];
const historyStorage = this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId);
// if scroll down after search
if(!top && (!dialog || history.indexOf(dialog.top_message) === -1)/* && this.chat.type == 'chat' */) {
if(!top && history.indexOf(historyStorage.maxId) === -1/* && this.chat.type == 'chat' */) {
this.log('Will load more (down) history by maxId:', history[history.length - 1], history);
/* false && */this.getHistory(history[history.length - 1], false, true, undefined, justLoad);
}
@ -833,14 +855,15 @@ export default class ChatBubbles {
}
}
public updateUnreadByDialog(dialog: Dialog) {
let maxId = this.peerId == rootScope.myId ? dialog.read_inbox_max_id : dialog.read_outbox_max_id;
public updateUnreadByDialog() {
const historyStorage = this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId);
const maxId = this.peerId == rootScope.myId ? historyStorage.readMaxId : historyStorage.readOutboxMaxId;
///////this.log('updateUnreadByDialog', maxId, dialog, this.unreadOut);
for(let msgId of this.unreadOut) {
for(const msgId of this.unreadOut) {
if(msgId > 0 && msgId <= maxId) {
let bubble = this.bubbles[msgId];
const bubble = this.bubbles[msgId];
if(bubble) {
bubble.classList.remove('is-sent');
bubble.classList.add('is-read');
@ -884,6 +907,14 @@ export default class ChatBubbles {
return;
}
if(this.chat.threadId) {
mids = mids.filter(mid => {
const message = this.chat.getMessage(mid);
const replyTo = message.reply_to as MessageReplyHeader;
return replyTo && (replyTo.reply_to_top_id || replyTo.reply_to_msg_id) === this.chat.threadId;
});
}
mids = mids.filter(mid => !this.bubbles[mid]);
mids.forEach((mid: number) => {
const message = this.chat.getMessage(mid);
@ -993,7 +1024,7 @@ export default class ChatBubbles {
}
public destroy() {
this.chat.log.error('Bubbles destroying');
//this.chat.log.error('Bubbles destroying');
this.scrollable.onScrolledTop = this.scrollable.onScrolledBottom = this.scrollable.onAdditionalScroll = null;
@ -1068,15 +1099,16 @@ export default class ChatBubbles {
const samePeer = this.peerId == peerId;
const dialog = this.appMessagesManager.getDialogByPeerId(peerId)[0] || null;
let topMessage = lastMsgId <= 0 ? lastMsgId : dialog?.top_message ?? 0;
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId);
let topMessage = lastMsgId <= 0 ? lastMsgId : historyStorage.maxId ?? 0;
const isTarget = lastMsgId !== undefined;
if(!isTarget && dialog) {
if(dialog.unread_count && !samePeer) {
lastMsgId = dialog.read_inbox_max_id;
if(!isTarget && historyStorage.maxId) {
const isUnread = this.appMessagesManager.isHistoryUnread(peerId, this.chat.threadId);
if(/* dialog.unread_count */isUnread && !samePeer) {
lastMsgId = historyStorage.readMaxId;
} else {
lastMsgId = dialog.top_message;
lastMsgId = historyStorage.maxId;
//lastMsgID = topMessage;
}
}
@ -1090,7 +1122,7 @@ export default class ChatBubbles {
this.scrollable.scrollIntoView(mounted.bubble);
this.highlightBubble(mounted.bubble);
this.chat.setListenerResult('setPeer', lastMsgId, false);
} else if(dialog && !isJump) {
} else if(historyStorage.maxId && !isJump) {
//this.log('will scroll down', this.scroll.scrollTop, this.scroll.scrollHeight);
this.scroll.scrollTop = this.scroll.scrollHeight;
this.chat.setListenerResult('setPeer', lastMsgId, true);
@ -1108,7 +1140,7 @@ export default class ChatBubbles {
this.replyFollowHistory.length = 0;
}
this.log('setPeer peerId:', this.peerId, dialog, lastMsgId, topMessage);
this.log('setPeer peerId:', this.peerId, historyStorage, lastMsgId, topMessage);
// add last message, bc in getHistory will load < max_id
const additionMsgId = isJump || this.chat.type !== 'chat' ? 0 : topMessage;
@ -1187,14 +1219,14 @@ export default class ChatBubbles {
this.lazyLoadQueue.unlock();
//if(dialog && lastMsgID && lastMsgID != topMessage && (this.bubbles[lastMsgID] || this.firstUnreadBubble)) {
if(dialog && (isTarget || isJump)) {
if(historyStorage.maxId && (isTarget || isJump)) {
if(this.scrollable.scrollLocked) {
clearTimeout(this.scrollable.scrollLocked);
this.scrollable.scrollLocked = 0;
}
const fromUp = maxBubbleId > 0 && (maxBubbleId < lastMsgId || lastMsgId < 0);
const forwardingUnread = dialog.read_inbox_max_id == lastMsgId && !isTarget;
const forwardingUnread = historyStorage.readMaxId === lastMsgId && !isTarget;
if(!fromUp && (samePeer || forwardingUnread)) {
this.scrollable.scrollTop = this.scrollable.scrollHeight;
} else if(fromUp/* && (samePeer || forwardingUnread) */) {
@ -1228,12 +1260,15 @@ export default class ChatBubbles {
this.log('scrolledAllDown:', this.scrolledAllDown);
//if(!this.unreaded.length && dialog) { // lol
if(this.scrolledAllDown && dialog) { // lol
this.appMessagesManager.readHistory(peerId, dialog.top_message);
if(this.scrolledAllDown && historyStorage.maxId) { // lol
this.appMessagesManager.readHistory(peerId, historyStorage.maxId);
}
if(dialog?.pFlags?.unread_mark) {
this.appMessagesManager.markDialogUnread(peerId, true);
if(this.chat.type === 'chat') {
const dialog = this.appMessagesManager.getDialogByPeerId(peerId)[0];
if(dialog?.pFlags.unread_mark) {
this.appMessagesManager.markDialogUnread(peerId, true);
}
}
this.chatInner.classList.remove('disable-hover', 'is-scrolling'); // warning, performance!
@ -1653,6 +1688,8 @@ export default class ChatBubbles {
bubble.classList.add(status);
}
const withReplyFooter = message.replies && message.replies.pFlags.comments;
const isOut = our && (!message.fwd_from || this.peerId != rootScope.myId);
let nameContainer = bubbleContainer;
@ -1693,7 +1730,7 @@ export default class ChatBubbles {
const photo = this.appPhotosManager.getPhoto(message.id);
//if(photo._ == 'photoEmpty') break;
this.log('will wrap pending photo:', pending, message, photo);
const withTail = !isAndroid && !message.message;
const withTail = !isAndroid && !message.message && !withReplyFooter;
if(withTail) bubble.classList.add('with-media-tail');
wrapPhoto({
photo, message,
@ -1714,7 +1751,7 @@ export default class ChatBubbles {
let doc = this.appDocsManager.getDoc(message.id);
//if(doc._ == 'documentEmpty') break;
this.log('will wrap pending video:', pending, message, doc);
const withTail = !isAndroid && !isApple && doc.type != 'round' && !message.message;
const withTail = !isAndroid && !isApple && doc.type != 'round' && !message.message && !withReplyFooter;
if(withTail) bubble.classList.add('with-media-tail');
wrapVideo({
doc,
@ -1789,7 +1826,7 @@ export default class ChatBubbles {
break;
}
const withTail = !isAndroid && !message.message;
const withTail = !isAndroid && !message.message && !withReplyFooter;
if(withTail) bubble.classList.add('with-media-tail');
wrapPhoto({
photo,
@ -1917,7 +1954,7 @@ export default class ChatBubbles {
box.append(quote);
//bubble.prepend(box);
bubbleContainer.prepend(timeSpan, box);
messageDiv.insertBefore(box, messageDiv.lastElementChild);
//this.log('night running', bubble.scrollHeight);
@ -1973,7 +2010,7 @@ export default class ChatBubbles {
chat: this.chat
});
} else {
const withTail = !isAndroid && !isApple && doc.type != 'round' && !message.message;
const withTail = !isAndroid && !isApple && doc.type != 'round' && !message.message && withReplyFooter;
if(withTail) bubble.classList.add('with-media-tail');
wrapVideo({
doc,
@ -2123,7 +2160,7 @@ export default class ChatBubbles {
nameContainer.append(nameDiv);
}
} else {
if(message.reply_to_mid) {
if(message.reply_to_mid && message.reply_to_mid !== this.chat.threadId) {
let originalMessage = this.chat.type === 'scheduled' ? this.appMessagesManager.getMessageByPeer(this.peerId, message.reply_to_mid) : this.chat.getMessage(message.reply_to_mid);
let originalPeerTitle = this.appPeersManager.getPeerTitle(originalMessage.fromId || originalMessage.fwdFromId, true) || '';
@ -2186,7 +2223,7 @@ export default class ChatBubbles {
if(savedFrom) {
const goto = document.createElement('div');
goto.classList.add('bubble-beside-button', 'goto-original', 'tgico-next');
goto.classList.add('bubble-beside-button', 'goto-original', 'tgico-arrow-next');
bubbleContainer.append(goto);
bubble.dataset.savedFrom = savedFrom;
bubble.classList.add('with-beside-button');
@ -2201,6 +2238,15 @@ export default class ChatBubbles {
this.bubbleGroups.updateGroupByMessageId(message.mid);
}
if(withReplyFooter) {
MessageRender.renderReplies({
bubble,
bubbleContainer,
message,
messageDiv
});
}
return bubble;
}
@ -2229,14 +2275,9 @@ export default class ChatBubbles {
}
} */
let dialog = this.appMessagesManager.getDialogByPeerId(this.peerId)[0];
if(dialog && dialog.top_message) {
for(let mid of history) {
if(mid == dialog.top_message) {
this.scrolledAllDown = true;
break;
}
}
const historyStorage = this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId);
if(history.includes(historyStorage.maxId)) {
this.scrolledAllDown = true;
}
//console.time('appImManager render history');
@ -2332,21 +2373,12 @@ export default class ChatBubbles {
public requestHistory(maxId: number, loadCount: number, backLimit: number) {
//const middleware = this.getMiddleware();
if(this.chat.type === 'chat') {
return this.appMessagesManager.getHistory(this.peerId, maxId, loadCount, backLimit);
if(this.chat.type === 'chat' || this.chat.type === 'discussion') {
return this.appMessagesManager.getHistory(this.peerId, maxId, loadCount, backLimit, this.chat.threadId);
} else if(this.chat.type === 'pinned') {
const promise = this.appMessagesManager.getSearch(this.peerId, '', {_: 'inputMessagesFilterPinned'}, maxId, loadCount, 0, backLimit)
.then(value => ({history: value.history.map(m => m.mid)}));
/* if(maxId) {
promise.then(result => {
if(!middleware()) return;
this.messagesCount = result.count;
this.chat.topbar.setTitle();
});
} */
return promise;
} else if(this.chat.type === 'scheduled') {
return this.appMessagesManager.getScheduledMessages(this.peerId).then(mids => {
@ -2403,7 +2435,7 @@ export default class ChatBubbles {
let additionMsgIds: number[];
if(additionMsgId && !isBackLimit) {
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId);
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId);
if(historyStorage.history.length < loadCount) {
additionMsgIds = historyStorage.history.slice();
@ -2554,8 +2586,8 @@ export default class ChatBubbles {
// preload more
//if(!isFirstMessageRender) {
if(this.chat.type === 'chat') {
const storage = this.appMessagesManager.getHistoryStorage(peerId);
if(this.chat.type === 'chat' || this.chat.type === 'discussion') {
const storage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId);
const isMaxIdInHistory = storage.history.indexOf(maxId) !== -1;
if(isMaxIdInHistory) { // * otherwise it is a search or jump
setTimeout(() => {
@ -2578,10 +2610,11 @@ export default class ChatBubbles {
return;
}
let dialog = this.appMessagesManager.getDialogByPeerId(this.peerId)[0];
if(!dialog?.unread_count) return;
const historyStorage = this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId);
const isUnread = this.appMessagesManager.isHistoryUnread(this.peerId, this.chat.threadId);
if(!isUnread) return;
let maxId = dialog.read_inbox_max_id;
let maxId = historyStorage.readMaxId;
maxId = Object.keys(this.bubbles).filter(mid => !this.bubbles[mid].classList.contains('is-out')).map(i => +i).sort((a, b) => a - b).find(i => i > maxId);
if(maxId && this.bubbles[maxId]) {
@ -2591,7 +2624,7 @@ export default class ChatBubbles {
this.firstUnreadBubble = null;
}
if(maxId != dialog.top_message) {
if(maxId !== historyStorage.maxId) {
bubble.classList.add('is-first-unread');
}

View File

@ -36,6 +36,7 @@ export default class Chat extends EventListenerBase<{
public contextMenu: ChatContextMenu;
public peerId = 0;
public threadId: number;
public setPeerPromise: Promise<void>;
public peerChanged: boolean;
@ -55,7 +56,7 @@ export default class Chat extends EventListenerBase<{
// * constructor end
this.log = logger('CHAT', LogLevels.log | LogLevels.warn | LogLevels.debug | LogLevels.error);
this.log.error('Chat construction');
//this.log.error('Chat construction');
this.container.append(this.backgroundEl);
this.appImManager.chatsContainer.append(this.container);
@ -95,6 +96,9 @@ export default class Chat extends EventListenerBase<{
} else if(this.type === 'scheduled') {
this.bubbles.constructScheduledHelpers();
this.input.constructPeerHelpers();
} else if(this.type === 'discussion') {
this.bubbles.constructPeerHelpers();
this.input.constructPeerHelpers();
}
this.container.classList.add('type-' + this.type);
@ -102,7 +106,7 @@ export default class Chat extends EventListenerBase<{
}
public destroy() {
const perf = performance.now();
//const perf = performance.now();
this.topbar.destroy();
this.bubbles.destroy();
@ -116,7 +120,7 @@ export default class Chat extends EventListenerBase<{
this.container.remove();
this.log.error('Chat destroy time:', performance.now() - perf);
//this.log.error('Chat destroy time:', performance.now() - perf);
}
public cleanup() {
@ -132,6 +136,11 @@ export default class Chat extends EventListenerBase<{
this.init = null;
}
if(this.type === 'discussion' && !this.threadId) {
this.threadId = lastMsgId;
lastMsgId = 0;
}
//console.time('appImManager setPeer');
//console.time('appImManager setPeer pre promise');
////console.time('appImManager: pre render start');
@ -178,9 +187,9 @@ export default class Chat extends EventListenerBase<{
appSidebarRight.sharedMediaTab.setLoadMutex(this.setPeerPromise);
appSidebarRight.sharedMediaTab.loadSidebarMedia(true);
this.setPeerPromise.then(() => {
/* this.setPeerPromise.then(() => {
appSidebarRight.sharedMediaTab.loadSidebarMedia(false);
});
}); */
return this.setPeerPromise;
}

View File

@ -89,7 +89,7 @@ export default class ChatInput {
private scrollOffsetTop = 0;
private scrollDiff = 0;
private helperType: Exclude<ChatInputHelperType, 'webpage'>;
public helperType: Exclude<ChatInputHelperType, 'webpage'>;
private helperFunc: () => void;
private helperWaitingForward: boolean;
@ -382,7 +382,8 @@ export default class ChatInput {
duration,
waveform: result.waveform,
objectURL: result.url,
replyToMsgId: this.replyToMsgId
replyToMsgId: this.replyToMsgId,
threadId: this.chat.threadId
});
this.onMessageSent(false, true);
@ -455,7 +456,7 @@ export default class ChatInput {
}
public destroy() {
this.chat.log.error('Input destroying');
//this.chat.log.error('Input destroying');
emoticonsDropdown.events.onOpen.findAndSplice(f => f == this.onEmoticonsOpen);
emoticonsDropdown.events.onClose.findAndSplice(f => f == this.onEmoticonsClose);
@ -1108,6 +1109,7 @@ export default class ChatInput {
} else {
this.appMessagesManager.sendText(this.chat.peerId, str, {
replyToMsgId: this.replyToMsgId,
threadId: this.chat.threadId,
noWebPage: this.noWebPage,
webPage: this.willSendWebPage,
scheduleDate: this.scheduleDate,
@ -1151,6 +1153,7 @@ export default class ChatInput {
this.appMessagesManager.sendFile(this.chat.peerId, document, {
isMedia: true,
replyToMsgId: this.replyToMsgId,
threadId: this.chat.threadId,
silent: this.sendSilent,
scheduleDate: this.scheduleDate
});
@ -1235,7 +1238,7 @@ export default class ChatInput {
this.willSendWebPage = null;
}
this.replyToMsgId = undefined;
this.replyToMsgId = this.chat.threadId;
this.forwardingMids.length = 0;
this.forwardingFromPeerId = 0;
this.editMsgId = undefined;

View File

@ -1,6 +1,9 @@
import { getFullDate } from "../../helpers/date";
import { formatNumber } from "../../helpers/number";
import { MessageReplies } from "../../layer";
import appPeersManager from "../../lib/appManagers/appPeersManager";
import RichTextProcessor from "../../lib/richtextprocessor";
import { ripple } from "../ripple";
import Chat from "./chat";
type Message = any;
@ -58,4 +61,56 @@ export namespace MessageRender {
return timeSpan;
};
export const renderReplies = ({bubble, bubbleContainer, message, messageDiv}: {
bubble: HTMLElement,
bubbleContainer: HTMLElement,
message: any,
messageDiv: HTMLElement
}) => {
const replies = message.replies as MessageReplies;
const isFooter = !bubble.classList.contains('sticker') && !bubble.classList.contains('emoji-big');
if(isFooter) {
const container = document.createElement('div');
container.classList.add('replies-footer');
let leftHTML = '', lastStyle = '';
if(replies?.recent_repliers) {
leftHTML += '<div class="replies-footer-avatars">'
/**
* MACOS, ANDROID - без реверса
* WINDOWS DESKTOP - реверс
* все приложения накладывают аватарку первую на вторую, а в макете зато вторая на первую, ЛОЛ!
*/
let l: string[] = [];
replies.recent_repliers/* .slice().reverse() */.forEach((peer, idx) => {
lastStyle = idx == 0 ? '' : `style="transform: translateX(-${idx * 12}px);"`;
l.push(`<avatar-element class="avatar-32" dialog="0" peer="${appPeersManager.getPeerId(peer)}" ${lastStyle}></avatar-element>`);
});
leftHTML += l.reverse().join('') + '</div>';
} else {
leftHTML = '<span class="tgico-comments"></span>';
}
let text: string;
if(replies?.replies) {
text = replies.replies + ' ' + (replies.replies > 1 ? 'Comments' : 'Comment');
} else {
text = 'Leave a Comment';
}
if(replies) {
if(replies.read_max_id < replies.max_id) {
container.classList.add('is-unread');
}
}
container.innerHTML = `${leftHTML}<span class="replies-footer-text" ${lastStyle}>${text}</span><span class="tgico-next"></span>`;
const rippleContainer = document.createElement('div');
container.append(rippleContainer);
ripple(rippleContainer);
bubbleContainer.prepend(container);
}
};
}

View File

@ -304,9 +304,7 @@ export default class ChatSelection {
this.cancelSelection();
})
});
}
if(this.chat.type === 'chat' || this.chat.type === 'pinned') {
} else {
this.selectionForwardBtn = Button('btn-primary btn-transparent selection-container-forward', {icon: 'forward'});
this.selectionForwardBtn.append('Forward');
this.listenerSetter.add(this.selectionForwardBtn, 'click', () => {

View File

@ -50,7 +50,7 @@ export default class ChatTopbar {
}
public construct() {
this.chat.log.error('Topbar construction');
//this.chat.log.error('Topbar construction');
this.container = document.createElement('div');
this.container.classList.add('sidebar-header', 'topbar');
@ -320,7 +320,7 @@ export default class ChatTopbar {
};
public destroy() {
this.chat.log.error('Topbar destroying');
//this.chat.log.error('Topbar destroying');
this.listenerSetter.removeAll();
mediaSizes.removeListener('changeScreen', this.onChangeScreen);
@ -386,7 +386,6 @@ export default class ChatTopbar {
public setTitle(count?: number) {
let title = '';
if(this.chat.type === 'pinned') {
//title = !count ? 'Pinned Messages' : (count === 1 ? 'Pinned Message' : (count + ' Pinned Messages'));
title = [count > 1 ? count : false, 'Pinned Messages'].filter(Boolean).join(' ');
if(count === undefined) {
@ -408,10 +407,8 @@ export default class ChatTopbar {
}
} else if(this.chat.type === 'scheduled') {
if(this.peerId === rootScope.myId) {
//title = !count ? 'Reminders' : (count === 1 ? 'Reminder' : (count + ' Reminders'));
title = [count > 1 ? count : false, 'Reminders'].filter(Boolean).join(' ');
} else {
//title = !count ? 'Scheduled Messages' : (count === 1 ? 'Scheduled Message' : (count + ' Scheduled Messages'));
title = [count > 1 ? count : false, 'Scheduled Messages'].filter(Boolean).join(' ');
}
@ -420,6 +417,17 @@ export default class ChatTopbar {
this.setTitle(mids.length);
});
}
} else if(this.chat.type === 'discussion') {
title = [count > 1 ? count : false, 'Comments'].filter(Boolean).join(' ');
if(count === undefined) {
Promise.all([
this.appMessagesManager.getHistory(this.peerId, 0, 1, 0, this.chat.threadId),
Promise.resolve()
]).then(() => {
this.setTitle(this.appMessagesManager.getHistoryStorage(this.peerId, this.chat.threadId).count);
});
}
} else if(this.chat.type === 'chat') {
if(this.peerId == rootScope.myId) title = 'Saved Messages';
else title = this.appPeersManager.getPeerTitle(this.peerId);

View File

@ -531,7 +531,7 @@ export default class PollElement extends HTMLElement {
*/
results.recent_voters/* .slice().reverse() */.forEach((userId, idx) => {
const style = idx == 0 ? '' : `style="transform: translateX(-${idx * 3}px);"`;
html += `<avatar-element dialog="0" peer="${userId}" ${style}></avatar-element>`;
html += `<avatar-element class="avatar-18" dialog="0" peer="${userId}" ${style}></avatar-element>`;
});
this.avatarsDiv.innerHTML = html;
}

View File

@ -241,10 +241,16 @@ export default class PopupCreatePoll extends PopupElement {
//console.log('Will try to create poll:', inputMediaPoll);
this.chat.appMessagesManager.sendOther(this.chat.peerId, inputMediaPoll, {
threadId: this.chat.threadId,
replyToMsgId: this.chat.input.replyToMsgId,
scheduleDate: this.chat.input.scheduleDate,
silent: this.chat.input.sendSilent
});
if(this.chat.input.helperType === 'reply') {
this.chat.input.clearHelper();
}
this.chat.input.onMessageSent(false, false);
}

View File

@ -28,7 +28,8 @@ export default class PopupDatePicker extends PopupElement {
noTitle: true,
minDate: Date,
maxDate: Date
withTime: true
withTime: true,
showOverflowMonths: true
}> & PopupOptions = {}) {
super('popup-date-picker', options.noButtons ? [] : [{
text: 'CANCEL',
@ -242,7 +243,7 @@ export default class PopupDatePicker extends PopupElement {
});
}
this.btnConfirm.innerText = 'Send ' + dayStr + ' at ' + ('00' + this.hoursInputField.value).slice(-2) + ':' + ('00' + this.minutesInputField.value).slice(-2);
this.btnConfirm.firstChild.nodeValue = 'Send ' + dayStr + ' at ' + ('00' + this.hoursInputField.value).slice(-2) + ':' + ('00' + this.minutesInputField.value).slice(-2);
}
}
@ -251,6 +252,21 @@ export default class PopupDatePicker extends PopupElement {
this.title.innerText = splitted[0] + ', ' + splitted[1] + ' ' + splitted[2];
}
private renderElement(disabled: boolean, innerText = '') {
const el = document.createElement('button');
el.classList.add('btn-icon', 'date-picker-month-date');
if(disabled) {
el.setAttribute('disabled', 'true');
}
if(innerText) {
el.innerText = innerText;
}
return el;
}
public setMonth() {
this.monthTitle.innerText = months[this.selectedMonth.getMonth()] + ' ' + this.selectedMonth.getFullYear();
@ -263,8 +279,9 @@ export default class PopupDatePicker extends PopupElement {
const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
this.month.append(...days.map(s => {
const el = document.createElement('span');
el.innerText = s;
const el = this.renderElement(true, s);
el.classList.remove('date-picker-month-date');
el.classList.add('date-picker-month-day');
return el;
}));
@ -274,23 +291,24 @@ export default class PopupDatePicker extends PopupElement {
let dayIndex = firstDate.getDay() - 1;
if(dayIndex == -1) dayIndex = days.length - 1;
const clonedDate = new Date(firstDate.getTime());
clonedDate.setDate(clonedDate.getDate() - dayIndex - 1);
// Padding first week
for(let i = 0; i < dayIndex; ++i) {
const el = document.createElement('span');
this.month.append(el);
if(this.options.showOverflowMonths) {
clonedDate.setDate(clonedDate.getDate() + 1);
this.month.append(this.renderElement(true, '' + clonedDate.getDate()));
} else {
this.month.append(this.renderElement(true));
}
}
do {
const date = firstDate.getDate();
const el = document.createElement('button');
el.classList.add('btn-icon');
el.innerText = '' + date;
const el = this.renderElement(firstDate > this.maxDate || firstDate < this.minDate, '' + date);
el.dataset.timestamp = '' + firstDate.getTime();
if(firstDate > this.maxDate || firstDate < this.minDate) {
el.setAttribute('disabled', 'true');
}
if(firstDate.getTime() === this.selectedDate.getTime()) {
this.selectedEl = el;
el.classList.add('active');
@ -301,6 +319,14 @@ export default class PopupDatePicker extends PopupElement {
firstDate.setDate(date + 1);
} while(firstDate.getDate() !== 1);
const remainder = this.month.childElementCount % 7;
if(this.options.showOverflowMonths && remainder) {
for(let i = remainder; i < 7; ++i) {
this.month.append(this.renderElement(true, '' + firstDate.getDate()));
firstDate.setDate(firstDate.getDate() + 1);
}
}
this.container.classList.toggle('is-max-lines', (this.month.childElementCount / 7) > 6);
this.monthsContainer.append(this.month);

View File

@ -11,7 +11,7 @@ export default class PopupDeleteMessages {
const firstName = appPeersManager.getPeerTitle(peerId, false, true);
mids = mids.slice();
const callback = (revoke: boolean) => {
const callback = (revoke?: true) => {
onConfirm && onConfirm();
if(type === 'scheduled') {
appMessagesManager.deleteScheduledMessages(peerId, mids);
@ -28,13 +28,13 @@ export default class PopupDeleteMessages {
buttons = [{
text: 'DELETE',
isDanger: true,
callback: () => callback(false)
callback: () => callback()
}];
} else {
buttons = [{
text: 'DELETE JUST FOR ME',
isDanger: true,
callback: () => callback(false)
callback: () => callback()
}];
if(peerId > 0) {

View File

@ -162,20 +162,26 @@ export default class PopupNewMedia extends PopupElement {
this.chat.appMessagesManager.sendAlbum(peerId, w.sendFileDetails.map(d => d.file), Object.assign({
caption,
replyToMsgId: input.replyToMsgId,
threadId: this.chat.threadId,
isMedia: willAttach.isMedia,
silent,
scheduleDate
}, w));
caption = undefined;
input.replyToMsgId = undefined;
input.replyToMsgId = this.chat.threadId;
}
} else {
if(caption) {
if(willAttach.sendFileDetails.length > 1) {
this.chat.appMessagesManager.sendText(peerId, caption, {replyToMsgId: input.replyToMsgId, silent, scheduleDate});
this.chat.appMessagesManager.sendText(peerId, caption, {
replyToMsgId: input.replyToMsgId,
threadId: this.chat.threadId,
silent,
scheduleDate
});
caption = '';
input.replyToMsgId = undefined;
//input.replyToMsgId = undefined;
}
}
@ -185,14 +191,16 @@ export default class PopupNewMedia extends PopupElement {
isMedia: willAttach.isMedia,
caption,
replyToMsgId: input.replyToMsgId,
threadId: this.chat.threadId,
silent,
scheduleDate
}, params));
caption = '';
input.replyToMsgId = undefined;
return promise;
});
input.replyToMsgId = this.chat.threadId;
}
//Promise.all(promises);

View File

@ -21,7 +21,8 @@ export default class PopupSchedule extends PopupDatePicker {
date.setDate(date.getDate() - 1);
return date;
})(),
withTime: true
withTime: true,
showOverflowMonths: true
});
this.element.classList.add('popup-schedule');

View File

@ -29,7 +29,7 @@ export default class AppAddMembersTab implements SliderTab {
return;
}
this.nextBtn.classList.remove('tgico-next');
this.nextBtn.classList.remove('tgico-arrow-next');
this.nextBtn.disabled = true;
putPreloader(this.nextBtn);
this.selector.freezed = true;
@ -61,7 +61,7 @@ export default class AppAddMembersTab implements SliderTab {
this.nextBtn.innerHTML = '';
this.nextBtn.disabled = false;
this.nextBtn.classList.add('tgico-next');
this.nextBtn.classList.add('tgico-arrow-next');
this.nextBtn.classList.toggle('is-visible', skippable);
appSidebarLeft.selectTab(AppSidebarLeft.SLIDERITEMSIDS.addMembers);

View File

@ -351,7 +351,7 @@ export function wrapDocument({message, withTime, uploading, fontWeight}: {
}): HTMLElement {
if(!fontWeight) fontWeight = 500;
const doc = message.media.document;
const doc = message.media.document || message.media.webpage.document;
if(doc.type == 'audio' || doc.type == 'voice') {
const audioElement = new AudioElement();
audioElement.setAttribute('message-id', '' + message.mid);

View File

@ -455,9 +455,9 @@ export class AppImManager {
appSidebarRight.sharedMediaTab.loadSidebarMedia(true);
appSidebarRight.sharedMediaTab.fillProfileElements();
setTimeout(() => {
/* setTimeout(() => {
appSidebarRight.sharedMediaTab.loadSidebarMedia(false);
});
}); */
}
setTimeout(() => {

View File

@ -4,7 +4,7 @@ import { tsNow } from "../../helpers/date";
import { copy, defineNotNumerableProperties, getObjectKeysAndSort } from "../../helpers/object";
import { randomLong } from "../../helpers/random";
import { splitStringByLength, limitSymbols } from "../../helpers/string";
import { Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputNotifyPeer, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PhotoSize, SendMessageAction, Update } from "../../layer";
import { Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputNotifyPeer, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PhotoSize, SendMessageAction, Update } from "../../layer";
import { InvokeApiOptions } from "../../types";
import { langPack } from "../langPack";
import { logger, LogLevels } from "../logger";
@ -43,8 +43,11 @@ export type HistoryStorage = {
history: number[],
pending: number[],
maxId?: number,
readPromise?: Promise<boolean>,
readMaxId?: number,
readOutboxMaxId?: number,
maxOutId?: number,
reply_markup?: any
};
@ -89,6 +92,11 @@ export class AppMessagesManager {
public historiesStorage: {
[peerId: string]: HistoryStorage
} = {};
public threadsStorage: {
[peerId: string]: {
[threadId: string]: HistoryStorage
}
} = {};
public searchesStorage: {
[peerId: string]: Partial<{
[inputFilter in MyInputMessagesFilter]: {
@ -386,6 +394,7 @@ export class AppMessagesManager {
public sendText(peerId: number, text: string, options: Partial<{
entities: any[],
replyToMsgId: number,
threadId: number,
viaBotId: number,
queryId: string,
resultId: string,
@ -462,7 +471,7 @@ export class AppMessagesManager {
date: options.scheduleDate || (tsNow(true) + serverTimeManager.serverTimeOffset),
message: text,
random_id: randomIdS,
reply_to: {reply_to_msg_id: replyToMsgId},
reply_to: this.generateReplyHeader(options.replyToMsgId, options.threadId),
via_bot_id: options.viaBotId,
reply_markup: options.reply_markup,
entities: entities,
@ -579,6 +588,7 @@ export class AppMessagesManager {
isMedia: true,
replyToMsgId: number,
threadId: number,
caption: string,
entities: MessageEntity[],
width: number,
@ -785,7 +795,7 @@ export class AppMessagesManager {
document: file
} : media,
random_id: randomIdS,
reply_to: {reply_to_msg_id: replyToMsgId},
reply_to: this.generateReplyHeader(options.replyToMsgId, options.threadId),
views: asChannel && 1,
pending: true
};
@ -923,6 +933,7 @@ export class AppMessagesManager {
isMedia: true,
entities: MessageEntity[],
replyToMsgId: number,
threadId: number,
caption: string,
sendFileDetails: Partial<{
duration: number,
@ -958,13 +969,15 @@ export class AppMessagesManager {
isMedia: options.isMedia,
scheduleDate: options.scheduleDate,
silent: options.silent,
replyToMsgId,
threadId: options.threadId,
...details
};
if(idx === 0) {
o.caption = caption;
o.entities = entities;
o.replyToMsgId = replyToMsgId;
//o.replyToMsgId = replyToMsgId;
}
return this.sendFile(peerId, file, o).message;
@ -1058,6 +1071,7 @@ export class AppMessagesManager {
public sendOther(peerId: number, inputMedia: any, options: Partial<{
replyToMsgId: number,
threadId: number,
viaBotId: number,
reply_markup: any,
clearDraft: true,
@ -1181,7 +1195,7 @@ export class AppMessagesManager {
message: '',
media,
random_id: randomIdS,
reply_to: {reply_to_msg_id: replyToMsgId},
reply_to: this.generateReplyHeader(options.replyToMsgId, options.threadId),
via_bot_id: options.viaBotId,
reply_markup: options.reply_markup,
views: asChannel && 1,
@ -1299,6 +1313,19 @@ export class AppMessagesManager {
}
}
private generateReplyHeader(replyToMsgId: number, replyToTopId?: number) {
const header = {
_: 'messageReplyHeader',
reply_to_msg_id: replyToMsgId,
} as MessageReplyHeader;
if(replyToTopId && replyToTopId !== replyToMsgId) {
header.reply_to_top_id = replyToTopId;
}
return header;
}
private setDialogIndexByMessage(dialog: MTDialog.dialog, message: MyMessage) {
if(!dialog.pFlags.pinned || !dialog.index) {
dialog.index = this.dialogsStorage.generateDialogIndex(message.date);
@ -1309,6 +1336,9 @@ export class AppMessagesManager {
const dialog = this.getDialogByPeerId(message.peerId)[0];
if(dialog) {
dialog.top_message = message.mid;
const historyStorage = this.getHistoryStorage(message.peerId);
historyStorage.maxId = message.mid;
this.setDialogIndexByMessage(dialog, message);
@ -1436,6 +1466,11 @@ export class AppMessagesManager {
});
}
public isHistoryUnread(peerId: number, threadId?: number) {
const historyStorage = this.getHistoryStorage(peerId, threadId);
return (peerId > 0 ? Math.max(historyStorage.readMaxId, historyStorage.readOutboxMaxId) : historyStorage.readMaxId) < historyStorage.maxId;
}
public getTopMessages(limit: number, folderId: number): Promise<number> {
const dialogs = this.dialogsStorage.getFolder(folderId);
let offsetId = 0;
@ -1492,7 +1527,7 @@ export class AppMessagesManager {
(dialogsResult.dialogs as Dialog[]).forEachReverse(dialog => {
//const d = Object.assign({}, dialog);
// ! нужно передавать folderId, так как по папке != 0 нет свойства folder_id
this.saveConversation(dialog, folderId);
this.saveConversation(dialog, dialog.folder_id ?? folderId);
/* if(dialog.peerId == -1213511294) {
this.log.error('lun bot', folderId, d);
@ -1505,9 +1540,11 @@ export class AppMessagesManager {
// ! это может случиться, если запрос идёт не по папке 0, а по 1. почему-то read'ов нет
// ! в итоге, чтобы получить 1 диалог, делается первый запрос по папке 0, потом запрос для архивных по папке 1, и потом ещё перезагрузка архивного диалога
if(!dialog.read_inbox_max_id && !dialog.read_outbox_max_id) {
if(!this.getLocalMessageId(dialog.read_inbox_max_id) && !this.getLocalMessageId(dialog.read_outbox_max_id)) {
noIdsDialogs[dialog.peerId] = dialog;
this.log.error('noIdsDialogs', dialog);
/* if(dialog.peerId == -1213511294) {
this.log.error('lun bot', folderId);
} */
@ -1622,7 +1659,7 @@ export class AppMessagesManager {
public getMessageById(messageId: number) {
for(const peerId in this.messagesStorageByPeerId) {
if(appPeersManager.isChannel(-+peerId)) {
if(appPeersManager.isChannel(+peerId)) {
continue;
}
@ -1849,7 +1886,7 @@ export class AppMessagesManager {
public generateMessageId(messageId: number, temp = false) {
const q = 0xFFFFFFFF;
const num = temp ? ++this.tempNum : 0;
if(messageId > q) {
if(messageId >= q) {
if(temp) {
return messageId + (num & 0xFFFF);
}
@ -1865,7 +1902,7 @@ export class AppMessagesManager {
*/
public getLocalMessageId(messageId: number) {
const q = 0xFFFFFFFF;
if(messageId < q) {
if(messageId <= q) {
return messageId;
}
@ -1928,9 +1965,17 @@ export class AppMessagesManager {
}
// this.log(dT(), 'msg unread', mid, apiMessage.pFlags.out, dialog && dialog[apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id'])
if(message.reply_to && message.reply_to.reply_to_msg_id) {
//message.reply_to_mid = message.reply_to.reply_to_msg_id;
message.reply_to_mid = this.generateMessageId(message.reply_to.reply_to_msg_id);
if(message.reply_to) {
if(message.reply_to.reply_to_msg_id) {
message.reply_to.reply_to_msg_id = message.reply_to_mid = this.generateMessageId(message.reply_to.reply_to_msg_id);
}
if(message.reply_to.reply_to_top_id) message.reply_to.reply_to_top_id = this.generateMessageId(message.reply_to.reply_to_top_id);
}
if(message.replies) {
if(message.replies.max_id) message.replies.max_id = this.generateMessageId(message.replies.max_id);
if(message.replies.read_max_id) message.replies.read_max_id = this.generateMessageId(message.replies.read_max_id);
}
const overwriting = !!message.peerId;
@ -1945,7 +1990,8 @@ export class AppMessagesManager {
if(message.peerId == myId/* && !message.from_id && !message.fwd_from */) {
message.fromId = message.fwd_from ? (message.fwd_from.from_id ? appPeersManager.getPeerId(message.fwd_from.from_id) : 0) : myId;
} else {
message.fromId = message.pFlags.post || (!message.pFlags.out && !message.from_id) ? peerId : appPeersManager.getPeerId(message.from_id);
//message.fromId = message.pFlags.post || (!message.pFlags.out && !message.from_id) ? peerId : appPeersManager.getPeerId(message.from_id);
message.fromId = message.pFlags.post || !message.from_id ? peerId : appPeersManager.getPeerId(message.from_id);
}
const fwdHeader = message.fwd_from;
@ -2449,6 +2495,7 @@ export class AppMessagesManager {
if(!topMessage
|| (this.getMessageByPeer(peerId, topPendingMessage) as MyMessage).date > (this.getMessageByPeer(peerId, topMessage) as MyMessage).date) {
dialog.top_message = topMessage = topPendingMessage;
this.getHistoryStorage(peerId).maxId = topPendingMessage;
}
}
@ -2546,14 +2593,15 @@ export class AppMessagesManager {
}
}
const wasDialogBefore = this.getDialogByPeerId(peerId)[0];
dialog.top_message = mid;
dialog.read_inbox_max_id = this.generateMessageId(dialog.read_inbox_max_id);
dialog.read_outbox_max_id = this.generateMessageId(dialog.read_outbox_max_id);
dialog.read_inbox_max_id = wasDialogBefore && !dialog.read_inbox_max_id ? wasDialogBefore.read_inbox_max_id : this.generateMessageId(dialog.read_inbox_max_id);
dialog.read_outbox_max_id = wasDialogBefore && !dialog.read_outbox_max_id ? wasDialogBefore.read_outbox_max_id : this.generateMessageId(dialog.read_outbox_max_id);
if(!dialog.hasOwnProperty('folder_id')) {
if(dialog._ == 'dialog') {
// ! СЛОЖНО ! СМОТРИ В getTopMessages
const wasDialogBefore = this.getDialogByPeerId(peerId)[0];
dialog.folder_id = wasDialogBefore ? wasDialogBefore.folder_id : folderId;
}/* else if(dialog._ == 'dialogFolder') {
dialog.folder_id = dialog.folder.id;
@ -2562,12 +2610,9 @@ export class AppMessagesManager {
dialog.peerId = peerId;
this.dialogsStorage.generateIndexForDialog(dialog);
this.dialogsStorage.pushDialog(dialog, message.date);
// Because we saved message without dialog present
if(message.mid > 0) {
if(message.mid > dialog[message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) message.pFlags.unread = true;
if(mid > 0) {
if(mid > dialog[message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) message.pFlags.unread = true;
else delete message.pFlags.unread;
}
@ -2584,13 +2629,16 @@ export class AppMessagesManager {
historyStorage[mid > 0 ? 'history' : 'pending'].push(mid);
}
historyStorage.maxId = mid;
historyStorage.readMaxId = dialog.read_inbox_max_id;
historyStorage.readOutboxMaxId = dialog.read_outbox_max_id;
if(channelId && dialog.pts) {
apiUpdatesManager.addChannelState(channelId, dialog.pts);
}
//if(this.filtersStorage.inited) {
//this.filtersStorage.processDialog(dialog);
//}
this.dialogsStorage.generateIndexForDialog(dialog);
this.dialogsStorage.pushDialog(dialog, message.date);
}
public mergeReplyKeyboard(historyStorage: HistoryStorage, message: any) {
@ -2679,9 +2727,24 @@ export class AppMessagesManager {
});
}
public getSearchNew({peerId, query, inputFilter, maxId, limit, offsetRate, backLimit, threadId}: {
peerId: number,
maxId: number,
limit?: number,
offsetRate?: number,
backLimit?: number,
threadId?: number,
query?: string,
inputFilter?: {
_: MyInputMessagesFilter
},
}) {
return this.getSearch(peerId, query, inputFilter, maxId, limit, offsetRate, backLimit, threadId);
}
public getSearch(peerId = 0, query: string = '', inputFilter: {
_: MyInputMessagesFilter
} = {_: 'inputMessagesFilterEmpty'}, maxId: number, limit = 20, offsetRate = 0, backLimit = 0): Promise<{
} = {_: 'inputMessagesFilterEmpty'}, maxId: number, limit = 20, offsetRate = 0, backLimit = 0, threadId = 0): Promise<{
count: number,
next_rate: number,
offset_id_offset: number,
@ -2704,7 +2767,7 @@ export class AppMessagesManager {
};
// * костыль для limit 1, если нужно и получить сообщение, и узнать количество сообщений
if(peerId && !backLimit && !maxId && !query && limit !== 1/* && inputFilter._ !== 'inputMessagesFilterPinned' */) {
if(peerId && !backLimit && !maxId && !query && limit !== 1 && !threadId/* && inputFilter._ !== 'inputMessagesFilterPinned' */) {
storage = beta ?
this.getSearchStorage(peerId, inputFilter._) :
this.getHistoryStorage(peerId);
@ -2860,19 +2923,20 @@ export class AppMessagesManager {
add_offset: backLimit ? -backLimit : 0,
max_id: 0,
min_id: 0,
hash: 0
hash: 0,
top_msg_id: threadId
}, {
//timeout: APITIMEOUT,
noErrorBox: true
});
} else {
var offsetDate = 0;
var offsetPeerId = 0;
var offsetId = 0;
var offsetMessage = maxId && this.getMessageByPeer(peerId, maxId);
//var offsetDate = 0;
let offsetPeerId = 0;
let offsetId = 0;
let offsetMessage = maxId && this.getMessageByPeer(peerId, maxId);
if(offsetMessage && offsetMessage.date) {
offsetDate = offsetMessage.date + serverTimeManager.serverTimeOffset;
//offsetDate = offsetMessage.date + serverTimeManager.serverTimeOffset;
offsetId = offsetMessage.id;
offsetPeerId = this.getMessagePeer(offsetMessage);
}
@ -2885,7 +2949,7 @@ export class AppMessagesManager {
offset_rate: offsetRate,
offset_peer: appPeersManager.getInputPeerById(offsetPeerId),
offset_id: offsetId,
limit
limit,
}, {
//timeout: APITIMEOUT,
noErrorBox: true
@ -2930,6 +2994,25 @@ export class AppMessagesManager {
});
}
public getDiscussionMessage(peerId: number, mid: number) {
return apiManager.invokeApi('messages.getDiscussionMessage', {
peer: appPeersManager.getInputPeerById(peerId),
msg_id: this.getLocalMessageId(mid)
}).then(result => {
appChatsManager.saveApiChats(result.chats);
appUsersManager.saveApiUsers(result.users);
this.saveMessages(result.messages);
const message = result.messages[0] as MyMessage;
const historyStorage = this.getHistoryStorage(message.peerId, message.mid);
historyStorage.maxId = this.generateMessageId(result.max_id) || 0;
historyStorage.readMaxId = this.generateMessageId(result.read_inbox_max_id) || 0;
historyStorage.readOutboxMaxId = this.generateMessageId(result.read_outbox_max_id) || 0;
return result;
});
}
handleNewMessages = () => {
clearTimeout(this.newMessagesHandlePromise);
this.newMessagesHandlePromise = 0;
@ -2972,12 +3055,12 @@ export class AppMessagesManager {
}
}
public deleteMessages(peerId: number, mids: number[], revoke: boolean) {
public deleteMessages(peerId: number, mids: number[], revoke?: true) {
let promise: Promise<any>;
mids = mids.map(mid => this.getLocalMessageId(mid));
if(peerId < 0 && appPeersManager.isChannel(-peerId)) {
if(peerId < 0 && appPeersManager.isChannel(peerId)) {
const channelId = -peerId;
const channel = appChatsManager.getChat(channelId);
if(!channel.pFlags.creator && !(channel.pFlags.editor && channel.pFlags.megagroup)) {
@ -3015,7 +3098,7 @@ export class AppMessagesManager {
});
} else {
promise = apiManager.invokeApi('messages.deleteMessages', {
revoke: revoke || undefined,
revoke,
id: mids
}).then((affectedMessages) => {
apiUpdatesManager.processUpdateMessage({
@ -3035,24 +3118,10 @@ export class AppMessagesManager {
public readHistory(peerId: number, maxId = 0) {
// console.trace('start read')
if(!this.isHistoryUnread(peerId)) return Promise.resolve(true);
const isChannel = appPeersManager.isChannel(peerId);
const historyStorage = this.getHistoryStorage(peerId);
const foundDialog = this.getDialogByPeerId(peerId)[0];
if(!foundDialog || !foundDialog.unread_count) {
if(!historyStorage.history.length) {
return Promise.resolve(false);
}
let foundUnread = !!historyStorage.history.find(messageId => {
const message = this.getMessagesStorage(peerId)[messageId];
return message && !message.pFlags.out && message.pFlags.unread;
});
if(!foundUnread) {
return Promise.resolve(false);
}
}
if(!historyStorage.readMaxId || maxId > historyStorage.readMaxId) {
historyStorage.readMaxId = maxId;
@ -3066,7 +3135,7 @@ export class AppMessagesManager {
if(isChannel) {
apiPromise = apiManager.invokeApi('channels.readHistory', {
channel: appChatsManager.getChannelInput(-peerId),
max_id: maxId
max_id: this.getLocalMessageId(maxId)
}).then((res) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
@ -3082,7 +3151,7 @@ export class AppMessagesManager {
} else {
apiPromise = apiManager.invokeApi('messages.readHistory', {
peer: appPeersManager.getInputPeerById(peerId),
max_id: maxId
max_id: this.getLocalMessageId(maxId)
}).then((affectedMessages) => {
apiUpdatesManager.processUpdateMessage({
_: 'updateShort',
@ -3111,8 +3180,6 @@ export class AppMessagesManager {
if(historyStorage.readMaxId > maxId) {
this.readHistory(peerId, historyStorage.readMaxId);
} else {
delete historyStorage.readMaxId;
}
});
@ -3152,7 +3219,13 @@ export class AppMessagesManager {
}
}
public getHistoryStorage(peerId: number) {
public getHistoryStorage(peerId: number, threadId?: number) {
if(threadId) {
threadId = this.getLocalMessageId(threadId);
if(!this.threadsStorage[peerId]) this.threadsStorage[peerId] = {};
return this.threadsStorage[peerId][threadId] ?? (this.threadsStorage[peerId][threadId] = {count: null, history: [], pending: []});
}
return this.historiesStorage[peerId] ?? (this.historiesStorage[peerId] = {count: null, history: [], pending: []});
}
@ -3427,7 +3500,6 @@ export class AppMessagesManager {
case 'updateEditChannelMessage': {
const message = update.message as MyMessage;
const peerId = this.getMessagePeer(message);
//const mid = message.id;
const mid = this.generateMessageId(message.id);
const storage = this.getMessagesStorage(peerId);
if(storage[mid] === undefined) {
@ -3456,22 +3528,7 @@ export class AppMessagesManager {
mid
});
const groupId = (message as Message.message).grouped_id;
/* if(this.pinnedMessagesStorage[peerId]) {
let pinnedMid: number;
if(groupId) {
const mids = this.getMidsByAlbum(groupId);
pinnedMid = mids.find(mid => this.pinnedMessagesStorage[peerId] == mid);
} else if(this.pinnedMessagesStorage[peerId] == mid) {
pinnedMid = mid;
}
if(pinnedMid) {
rootScope.broadcast('peer_pinned_message', peerId);
}
} */
if(isTopMessage || groupId) {
if(isTopMessage || (message as Message.message).grouped_id) {
const updatedDialogs: {[peerId: number]: Dialog} = {};
updatedDialogs[peerId] = dialog;
rootScope.broadcast('dialogs_multiupdate', updatedDialogs);
@ -3480,27 +3537,34 @@ export class AppMessagesManager {
break;
}
case 'updateReadChannelDiscussionInbox':
case 'updateReadChannelDiscussionOutbox':
case 'updateReadHistoryInbox':
case 'updateReadHistoryOutbox':
case 'updateReadChannelInbox':
case 'updateReadChannelOutbox': {
const channelId: number = (update as Update.updateReadChannelInbox).channel_id;
//const maxId = update.max_id;
const maxId = this.generateMessageId(update.max_id);
const channelId = (update as Update.updateReadChannelInbox).channel_id;
const maxId = this.generateMessageId((update as Update.updateReadChannelInbox).max_id || (update as Update.updateReadChannelDiscussionInbox).read_max_id);
const threadId = this.generateMessageId((update as Update.updateReadChannelDiscussionInbox).top_msg_id);
const peerId = channelId ? -channelId : appPeersManager.getPeerId((update as Update.updateReadHistoryInbox).peer);
const isOut = update._ == 'updateReadHistoryOutbox' || update._ == 'updateReadChannelOutbox' ? true : undefined;
const isOut = update._ === 'updateReadHistoryOutbox' || update._ === 'updateReadChannelOutbox' || update._ === 'updateReadChannelDiscussionOutbox' ? true : undefined;
const storage = this.getMessagesStorage(peerId);
const history = getObjectKeysAndSort(storage, 'desc');
const foundDialog = this.getDialogByPeerId(peerId)[0];
const history = getObjectKeysAndSort(this.getMessagesStorage(peerId), 'desc');
const stillUnreadCount = (update as Update.updateReadChannelInbox).still_unread_count;
let newUnreadCount = 0;
let foundAffected = false;
//this.log.warn(dT(), 'read', peerId, isOut ? 'out' : 'in', maxId)
const historyStorage = this.getHistoryStorage(peerId, threadId);
if(peerId > 0 && isOut) {
appUsersManager.forceUserOnline(peerId);
}
const storage = this.getMessagesStorage(peerId);
for(let i = 0, length = history.length; i < length; i++) {
const messageId = history[i];
if(messageId > maxId) {
@ -3508,9 +3572,6 @@ export class AppMessagesManager {
}
const message = storage[messageId];
if(!message) {
continue;
}
if(message.pFlags.out != isOut) {
continue;
@ -3519,6 +3580,13 @@ export class AppMessagesManager {
if(!message.pFlags.unread) {
break;
}
if(threadId) {
const replyTo = message.reply_to as MessageReplyHeader;
if(!replyTo || (replyTo.reply_to_top_id || replyTo.reply_to_msg_id) !== threadId) {
continue;
}
}
// this.log.warn('read', messageId, message.pFlags.unread, message)
if(message.pFlags.unread) {
@ -3527,29 +3595,31 @@ export class AppMessagesManager {
foundAffected = true;
}
if(!message.pFlags.out) {
if(foundDialog) {
newUnreadCount = --foundDialog.unread_count;
}
if(!message.pFlags.out && !threadId && stillUnreadCount === undefined) {
newUnreadCount = --foundDialog.unread_count;
//NotificationsManager.cancel('msg' + messageId); // warning
}
}
}
if(foundDialog) {
if(!isOut && newUnreadCount && foundDialog.top_message <= maxId) {
newUnreadCount = foundDialog.unread_count = 0;
if(isOut) historyStorage.readOutboxMaxId = maxId;
else historyStorage.readMaxId = maxId;
if(!threadId && foundDialog) {
if(isOut) foundDialog.read_outbox_max_id = maxId;
else foundDialog.read_inbox_max_id = maxId;
if(!isOut) {
if(newUnreadCount < 0 || !this.isHistoryUnread(peerId)) {
foundDialog.unread_count = 0;
} else if(newUnreadCount && foundDialog.top_message > maxId) {
foundDialog.unread_count = newUnreadCount;
}
}
foundDialog[isOut ? 'read_outbox_max_id' : 'read_inbox_max_id'] = maxId;
rootScope.broadcast('dialog_unread', {peerId});
}
// need be commented for read out messages
//if(newUnreadCount != 0 || !isOut) { // fix 16.11.2019 (maybe not)
//////////this.log.warn(dT(), 'cnt', peerId, newUnreadCount, isOut, foundDialog, update, foundAffected);
rootScope.broadcast('dialog_unread', {peerId, count: newUnreadCount});
//}
if(foundAffected) {
rootScope.broadcast('messages_read');
}
@ -3625,10 +3695,7 @@ export class AppMessagesManager {
if(historyUpdated.unread) {
foundDialog.unread_count -= historyUpdated.unread;
rootScope.broadcast('dialog_unread', {
peerId,
count: foundDialog.unread_count
});
rootScope.broadcast('dialog_unread', {peerId});
}
if(historyUpdated.msgs[foundDialog.top_message]) {
@ -4061,12 +4128,16 @@ export class AppMessagesManager {
});
}
public getHistory(peerId: number, maxId = 0, limit: number, backLimit?: number) {
public getHistory(peerId: number, maxId = 0, limit: number, backLimit?: number, threadId?: number) {
if(threadId) {
threadId = this.getLocalMessageId(threadId);
}
if(this.migratedFromTo[peerId]) {
peerId = this.migratedFromTo[peerId];
}
const historyStorage = this.getHistoryStorage(peerId);
const historyStorage = this.getHistoryStorage(peerId, threadId);
const unreadOffset = 0;
const unreadSkip = false;
@ -4126,7 +4197,7 @@ export class AppMessagesManager {
limit += backLimit;
}
return this.requestHistory(reqPeerId, maxId, limit, offset).then((historyResult) => {
return this.requestHistory(reqPeerId, maxId, limit, offset, undefined, threadId).then((historyResult) => {
historyStorage.count = (historyResult as MessagesMessages.messagesMessagesSlice).count || historyResult.messages.length;
if(isMigrated) {
historyStorage.count++;
@ -4150,7 +4221,7 @@ export class AppMessagesManager {
});
}
return this.fillHistoryStorage(peerId, maxId, limit, historyStorage).then(() => {
return this.fillHistoryStorage(peerId, maxId, limit, historyStorage, threadId).then(() => {
offset = 0;
if(maxId > 0) {
for(offset = 0; offset < historyStorage.history.length; offset++) {
@ -4174,10 +4245,10 @@ export class AppMessagesManager {
});
}
public fillHistoryStorage(peerId: number, maxId: number, fullLimit: number, historyStorage: HistoryStorage): Promise<boolean> {
public fillHistoryStorage(peerId: number, maxId: number, fullLimit: number, historyStorage: HistoryStorage, threadId?: number): Promise<boolean> {
// this.log('fill history storage', peerId, maxId, fullLimit, angular.copy(historyStorage))
const offset = (this.migratedFromTo[peerId] && !maxId) ? 1 : 0;
return this.requestHistory(peerId, maxId, fullLimit, offset).then((historyResult) => {
return this.requestHistory(peerId, maxId, fullLimit, offset, undefined, threadId).then((historyResult) => {
historyStorage.count = (historyResult as MessagesMessages.messagesMessagesSlice).count || historyResult.messages.length;
if(!maxId && historyResult.messages.length) {
@ -4228,9 +4299,9 @@ export class AppMessagesManager {
}
}
return this.fillHistoryStorage(peerId, maxId, fullLimit, historyStorage);
return this.fillHistoryStorage(peerId, maxId, fullLimit, historyStorage, threadId);
} else if(totalCount < historyStorage.count) {
return this.fillHistoryStorage(peerId, maxId, fullLimit, historyStorage);
return this.fillHistoryStorage(peerId, maxId, fullLimit, historyStorage, threadId);
}
}
@ -4251,15 +4322,16 @@ export class AppMessagesManager {
return result;
}
public requestHistory(peerId: number, maxId: number, limit = 0, offset = 0, offsetDate = 0): Promise<Exclude<MessagesMessages, MessagesMessages.messagesMessagesNotModified>> {
public requestHistory(peerId: number, maxId: number, limit = 0, offset = 0, offsetDate = 0, threadId = 0): Promise<Exclude<MessagesMessages, MessagesMessages.messagesMessagesNotModified>> {
const isChannel = appPeersManager.isChannel(peerId);
//console.trace('requestHistory', peerId, maxId, limit, offset);
//rootScope.broadcast('history_request');
const promise = apiManager.invokeApi('messages.getHistory', {
const options = {
peer: appPeersManager.getInputPeerById(peerId),
msg_id: threadId,
offset_id: this.getLocalMessageId(maxId) || 0,
offset_date: offsetDate,
add_offset: offset,
@ -4267,18 +4339,16 @@ export class AppMessagesManager {
max_id: 0,
min_id: 0,
hash: 0
}, {
};
const promise: ReturnType<AppMessagesManager['requestHistory']> = apiManager.invokeApi(threadId ? 'messages.getReplies' : 'messages.getHistory', options, {
//timeout: APITIMEOUT,
noErrorBox: true
}) as ReturnType<AppMessagesManager['requestHistory']>;
}) as any;
return promise.then((historyResult) => {
this.log('requestHistory result:', peerId, historyResult, maxId, limit, offset);
/* if(historyResult._ == 'messages.messagesNotModified') {
return historyResult as any;
} */
appUsersManager.saveApiUsers(historyResult.users);
appChatsManager.saveApiChats(historyResult.chats);
this.saveMessages(historyResult.messages);
@ -4295,47 +4365,16 @@ export class AppMessagesManager {
}
// will load more history if last message is album grouped (because it can be not last item)
const historyStorage = this.getHistoryStorage(peerId);
const historyStorage = this.getHistoryStorage(peerId, threadId);
// historyResult.messages: desc sorted
if(length && (historyResult.messages[length - 1] as Message.message).grouped_id
&& (historyStorage.history.length + historyResult.messages.length) < (historyResult as MessagesMessages.messagesMessagesSlice).count) {
return this.requestHistory(peerId, (historyResult.messages[length - 1] as Message.message).mid, 10, 0).then((_historyResult) => {
return this.requestHistory(peerId, (historyResult.messages[length - 1] as Message.message).mid, 10, 0, offsetDate, threadId).then((_historyResult) => {
return historyResult;
});
}
// don't need the intro now
/* if(peerId < 0 || !appUsersManager.isBot(peerId) || (length == limit && limit < historyResult.count)) {
return historyResult;
} */
return historyResult as any;
/* return appProfileManager.getProfile(peerId).then((userFull: any) => {
var description = userFull.bot_info && userFull.bot_info.description;
if(description) {
var messageId = this.tempId--;
var message = {
_: 'messageService',
id: messageId,
from_id: peerId,
peer_id: appPeersManager.getOutputPeer(peerId),
pFlags: {},
date: tsNow(true) + serverTimeManager.serverTimeOffset,
action: {
_: 'messageActionBotIntro',
description: description
}
}
this.saveMessages([message]);
historyResult.messages.push(message);
if(historyResult.count) {
historyResult.count++;
}
}
return historyResult;
}); */
}, (error) => {
switch (error.type) {
case 'CHANNEL_PRIVATE':

View File

@ -20,7 +20,7 @@ type BroadcastEvents = {
'filter_order': number[],
'dialog_draft': {peerId: number, draft: any, index: number},
'dialog_unread': {peerId: number, count?: number},
'dialog_unread': {peerId: number},
'dialog_flush': {peerId: number},
'dialog_drop': {peerId: number, dialog?: Dialog},
'dialog_migrate': {migrateFrom: number, migrateTo: number},

View File

@ -107,4 +107,9 @@ avatar-element {
--size: 32px;
--multiplier: 1.6875;
}
&.avatar-18 {
--size: 18px;
--multiplier: 3;
}
}

View File

@ -714,7 +714,8 @@ $bubble-margin: .25rem;
}
.web {
margin-top: -6px !important;
padding-top: 1px;
margin: 4px 0 -5px 1px;
// margin-bottom: 5px;
max-width: 100%;
overflow: hidden;
@ -775,11 +776,6 @@ $bubble-margin: .25rem;
.web, .reply {
font-size: .95rem;
// margin: .25rem;
margin: 4px 4px 0 6px;
//padding: .25rem;
padding: 4px;
border-radius: 4px;
//transition: anim(background-color);
/* &:hover {
@ -829,9 +825,10 @@ $bubble-margin: .25rem;
}
.reply {
margin-bottom: 6px;
margin-top: 0;
padding: 4px;
margin: 0 4px 6px 4px;
cursor: pointer;
border-radius: 4px;
&-content {
max-width: 300px;
@ -881,6 +878,7 @@ $bubble-margin: .25rem;
line-height: 21px;
word-break: break-word;
white-space: pre-wrap; // * fix spaces on line begin
position: relative;
/* * {
overflow: hidden;
@ -1479,6 +1477,70 @@ $bubble-margin: .25rem;
audio-element, poll-element {
white-space: normal; // * fix due to .message white-space prewrap
}
.replies-footer {
height: 50px;
border-top: 1px solid #dadce0;
position: relative;
display: flex;
align-items: center;
padding: 0 .5rem;
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
color: $color-blue;
min-width: 15rem;
user-select: none;
.tgico-comments, .tgico-next {
font-size: 1.4375rem;
}
&-text {
font-weight: 500;
margin-left: 13px;
display: flex;
align-items: center;
}
&-avatars {
display: flex;
flex-direction: row-reverse;
avatar-element {
border: 2px solid #fff;
cursor: pointer;
}
}
.tgico-next {
position: absolute;
right: .5rem;
}
.rp {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
border-radius: inherit;
cursor: pointer;
}
&.is-unread {
.replies-footer-text {
&:after {
content: " ";
background-color: $color-blue;
width: .5rem;
height: .5rem;
margin-left: .75rem;
border-radius: 50%;
}
}
}
}
}
// * fix scroll with only 1 bubble
@ -1988,7 +2050,7 @@ $bubble-margin: .25rem;
}
&.is-link:before {
content: $tgico-next;
content: $tgico-arrow-next;
position: absolute;
right: 2px;
top: 2px;
@ -2251,11 +2313,7 @@ poll-element {
}
avatar-element {
width: 18px;
height: 18px;
border: 1px solid #fff;
line-height: 20px;
font-size: 10px;
cursor: pointer;
}

View File

@ -366,7 +366,7 @@
.tgico-forward:before {
content: "\e96d";
}
.tgico-next:before {
.tgico-arrow-next:before {
content: "\e96e";
}
.tgico-unlock:before {

View File

@ -123,7 +123,7 @@ $tgico-play: "\e96a";
$tgico-pause: "\e96b";
$tgico-reply: "\e96c";
$tgico-forward: "\e96d";
$tgico-next: "\e96e";
$tgico-arrow-next: "\e96e";
$tgico-unlock: "\e96f";
$tgico-lock: "\e970";
$tgico-data: "\e971";

View File

@ -79,15 +79,7 @@
width: 100%;
justify-content: center;
span, .btn-icon {
// justify-self: center;
// width: 40px;
// height: 40px;
// font-size: 1rem;
// color: #707579;
// display: flex;
// justify-content: center;
// align-items: center;
.btn-icon {
justify-self: center;
width: 38px;
height: 38px;
@ -96,6 +88,14 @@
display: flex;
justify-content: center;
align-items: center;
&:disabled {
opacity: 1;
}
}
&-date:disabled {
color: #c2c3c4;
}
.btn-icon:not(:disabled) {
@ -109,4 +109,77 @@
}
}
}
}
.popup-schedule {
.popup-header {
justify-content: space-between;
.btn-icon {
font-size: 22px;
}
.popup-close {
color: #52585d;
}
}
.popup-container {
min-width: 420px;
width: 420px;
}
.date-picker {
&-month {
&-title {
font-weight: 500;
font-size: 20px;
margin-left: -5rem;
}
.btn-icon {
font-weight: 500;
font-size: 15px;
}
&-date:disabled {
color: #9ba3a8 !important;
}
&-day {
font-weight: bold;
color: black !important;
font-size: 14px !important;
}
}
&-time {
display: flex;
justify-content: center;
margin-bottom: 1.5rem;
.input-field {
width: 80px;
&-input {
text-align: center;
}
}
&-delimiter {
padding: 14px 20px;
}
}
&-controls {
.btn-icon {
color: #2a8ee4;
&:disabled {
visibility: visible;
opacity: 1;
}
}
}
}
}