Browse Source

Fix scheduled messages

master
Eduard Kuzmenko 2 years ago
parent
commit
f17e88cccb
  1. 61
      src/components/chat/bubbleGroups.ts
  2. 193
      src/components/chat/bubbles.ts
  3. 4
      src/components/chat/contextMenu.ts
  4. 6
      src/components/chat/input.ts

61
src/components/chat/bubbleGroups.ts

@ -94,6 +94,10 @@ class BubbleGroup {
return this.items[this.items.length - 1]; return this.items[this.items.length - 1];
} }
get lastTimestamp() {
return this.lastItem.timestamp;
}
get lastMid() { get lastMid() {
return this.lastItem.mid; return this.lastItem.mid;
} }
@ -143,33 +147,11 @@ class BubbleGroup {
insertItem(item: GroupItem) { insertItem(item: GroupItem) {
const {items} = this; const {items} = this;
const {timestamp, mid} = item; insertInDescendSortedArray(items, item, this.groups.sortGroupItemsKey);
if(this.chat.type === 'scheduled') {
let foundMidOnSameTimestamp = 0;
let i = 0, length = items.length;
for(; i < length; ++i) {
const {timestamp: _timestamp, mid: _mid} = items[i];
if(timestamp < _timestamp) {
break;
} else if(timestamp === _timestamp) {
foundMidOnSameTimestamp = _mid;
}
if(foundMidOnSameTimestamp && mid < foundMidOnSameTimestamp) {
break;
}
}
items.splice(i, 0, item);
} else {
// insertInDescendSortedArray(items, item, 'mid');
insertInDescendSortedArray(items, item, 'groupMid');
}
item.group = this; item.group = this;
if(items.length === 1) { if(items.length === 1) {
insertInDescendSortedArray(this.groups.groups, this, 'lastMid'); this.groups.insertGroup(this);
} }
} }
@ -231,7 +213,7 @@ class BubbleGroup {
return; return;
} }
const dateContainer = this.chat.bubbles.getDateContainerByTimestamp(this.firstTimestamp); const dateContainer = this.chat.bubbles.getDateContainerByTimestamp(this.dateTimestamp / 1000);
// const idx = this.groups.indexOf(group); // const idx = this.groups.indexOf(group);
const dateGroups = this.groups.groups.filter((_group) => _group.dateTimestamp === this.dateTimestamp); const dateGroups = this.groups.groups.filter((_group) => _group.dateTimestamp === this.dateTimestamp);
const dateGroupsLength = dateGroups.length; const dateGroupsLength = dateGroups.length;
@ -276,9 +258,14 @@ export default class BubbleGroups {
private itemsMap: Map<HTMLElement, GroupItem> = new Map(); private itemsMap: Map<HTMLElement, GroupItem> = new Map();
public groups: Array<BubbleGroup> = []; // descend sorted public groups: Array<BubbleGroup> = []; // descend sorted
private newGroupDiff = 121; // * 121 in scheduled messages private newGroupDiff = 121; // * 121 in scheduled messages
private sortItemsKey: Extract<keyof GroupItem, 'timestamp' | 'mid'>;
private sortGroupsKey: Extract<keyof BubbleGroup, 'lastMid' | 'lastTimestamp'>;
public sortGroupItemsKey: Extract<keyof GroupItem, 'groupMid' | 'timestamp'>;
constructor(private chat: Chat) { constructor(private chat: Chat) {
this.sortItemsKey = chat.type === 'scheduled' ? 'timestamp' : 'mid';
this.sortGroupsKey = chat.type === 'scheduled' ? 'lastTimestamp' : 'lastMid';
this.sortGroupItemsKey = /* chat.type === 'scheduled' ? 'timestamp' : */'groupMid';
} }
removeItem(item: GroupItem) { removeItem(item: GroupItem) {
@ -369,7 +356,7 @@ export default class BubbleGroups {
// item.group.insertItem(item); // item.group.insertItem(item);
indexOfAndSplice(this.itemsArr, item); indexOfAndSplice(this.itemsArr, item);
insertInDescendSortedArray(this.itemsArr, item, 'mid'); this.insertItemToArray(item, this.itemsArr);
} }
changeItemBubble(item: GroupItem, bubble: HTMLElement) { changeItemBubble(item: GroupItem, bubble: HTMLElement) {
@ -405,7 +392,7 @@ export default class BubbleGroups {
findGroupSiblingByItem(item: GroupItem, items: GroupItem[]) { findGroupSiblingByItem(item: GroupItem, items: GroupItem[]) {
items = items.slice(); items = items.slice();
const idx = insertInDescendSortedArray(items, item, 'mid'); const idx = this.insertItemToArray(item, items);
// return this.findGroupSiblingInSiblings(item, this.getSiblingsAtIndex(idx, items)); // return this.findGroupSiblingInSiblings(item, this.getSiblingsAtIndex(idx, items));
return this.findGroupSiblingInItems(item, items, idx); return this.findGroupSiblingInItems(item, items, idx);
} }
@ -436,8 +423,16 @@ export default class BubbleGroups {
this.addItemToCache(item); this.addItemToCache(item);
} }
insertItemToArray(item: GroupItem, array: GroupItem[]) {
return insertInDescendSortedArray(array, item, this.sortItemsKey);
}
insertGroup(group: BubbleGroup) {
return insertInDescendSortedArray(this.groups, group, this.sortGroupsKey);
}
addItemToCache(item: GroupItem) { addItemToCache(item: GroupItem) {
insertInDescendSortedArray(this.itemsArr, item, 'mid'); this.insertItemToArray(item, this.itemsArr);
this.itemsMap.set(item.bubble, item); this.itemsMap.set(item.bubble, item);
} }
@ -463,10 +458,11 @@ export default class BubbleGroups {
const {dateTimestamp} = this.chat.bubbles.getDateForDateContainer(timestamp); const {dateTimestamp} = this.chat.bubbles.getDateForDateContainer(timestamp);
const item: GroupItem = { const item: GroupItem = {
mid, mid,
groupMid: mid, groupMid: this.chat.type === 'scheduled' ? +`${(timestamp * 1000 - dateTimestamp) / 1000}.${mid}` : mid,
fromId: this.getMessageFromId(message), fromId: this.getMessageFromId(message),
bubble, bubble,
timestamp, // timestamp: this.chat.type === 'scheduled' ? +`${(timestamp * 1000 - dateTimestamp) / 1000}.${mid}` : timestamp,
timestamp,
dateTimestamp, dateTimestamp,
mounted: false, mounted: false,
single, single,
@ -505,7 +501,8 @@ export default class BubbleGroups {
} }
prepareForGrouping(bubble: HTMLElement, message: MyMessage) { prepareForGrouping(bubble: HTMLElement, message: MyMessage) {
if(this.getItemByBubble(bubble)) { const foundItem = this.getItemByBubble(bubble);
if(foundItem) { // should happen only on edit
debugger; debugger;
return; return;
} }

193
src/components/chat/bubbles.ts

@ -284,7 +284,7 @@ export default class ChatBubbles {
// will call when sent for update pos // will call when sent for update pos
this.listenerSetter.add(rootScope)('history_update', async({storageKey, mid, message}) => { this.listenerSetter.add(rootScope)('history_update', async({storageKey, mid, message}) => {
if(this.chat.messagesStorageKey !== storageKey) { if(this.chat.messagesStorageKey !== storageKey || this.chat.type === 'scheduled') {
return; return;
} }
@ -741,95 +741,95 @@ export default class ChatBubbles {
}); });
} }
if(!IS_MOBILE && this.chat.type !== 'pinned') { if(this.chat.type !== 'pinned' && this.chat.type !== 'scheduled') {
this.listenerSetter.add(container)('dblclick', async(e) => { if(!IS_MOBILE) {
if(this.chat.selection.isSelecting || this.listenerSetter.add(container)('dblclick', async(e) => {
!(await this.chat.canSend())) { if(this.chat.selection.isSelecting ||
return; !(await this.chat.canSend())) {
}
const target = e.target as HTMLElement;
const bubble = target.classList.contains('bubble') ?
target :
(target.classList.contains('document-selection') ? target.parentElement : null);
if(bubble && !bubble.classList.contains('bubble-first')) {
const mid = +bubble.dataset.mid;
const message = await this.chat.getMessage(mid);
if(message.pFlags.is_outgoing) {
return; return;
} }
this.chat.input.initMessageReply(mid); const target = e.target as HTMLElement;
} const bubble = target.classList.contains('bubble') ?
}); target :
} (target.classList.contains('document-selection') ? target.parentElement : null);
if(bubble && !bubble.classList.contains('bubble-first')) {
if(IS_TOUCH_SUPPORTED) { const mid = +bubble.dataset.mid;
const className = 'is-gesturing-reply'; const message = await this.chat.getMessage(mid);
const MAX = 64; if(message.pFlags.is_outgoing) {
const replyAfter = MAX * .75; return;
let shouldReply = false;
let target: HTMLElement;
let icon: HTMLElement;
handleHorizontalSwipe({
element: container,
verifyTouchTarget: async(e) => {
if(this.chat.selection.isSelecting || !(await this.chat.canSend())) {
return false;
}
// cancelEvent(e);
target = findUpClassName(e.target, 'bubble');
if(target) {
SetTransition(target, className, true, 250);
void target.offsetLeft; // reflow
if(!icon) {
icon = document.createElement('span');
icon.classList.add('tgico-reply_filled', 'bubble-gesture-reply-icon');
} else {
icon.classList.remove('is-visible');
icon.style.opacity = '';
} }
target/* .querySelector('.bubble-content') */.append(icon); this.chat.input.initMessageReply(mid);
}
return !!target;
},
onSwipe: (xDiff, yDiff) => {
shouldReply = xDiff >= replyAfter;
if(shouldReply && !icon.classList.contains('is-visible')) {
icon.classList.add('is-visible');
} }
icon.style.opacity = '' + Math.min(1, xDiff / replyAfter); });
} else if(IS_TOUCH_SUPPORTED) {
const x = -Math.max(0, Math.min(MAX, xDiff)); const className = 'is-gesturing-reply';
target.style.transform = `translateX(${x}px)`; const MAX = 64;
cancelContextMenuOpening(); const replyAfter = MAX * .75;
}, let shouldReply = false;
onReset: () => { let target: HTMLElement;
const _target = target; let icon: HTMLElement;
SetTransition(_target, className, false, 250, () => { handleHorizontalSwipe({
if(icon.parentElement === _target) { element: container,
icon.classList.remove('is-visible'); verifyTouchTarget: async(e) => {
icon.remove(); if(this.chat.selection.isSelecting || !(await this.chat.canSend())) {
return false;
} }
});
// cancelEvent(e);
fastRaf(() => { target = findUpClassName(e.target, 'bubble');
_target.style.transform = ``; if(target) {
SetTransition(target, className, true, 250);
if(shouldReply) { void target.offsetLeft; // reflow
const {mid} = _target.dataset;
this.chat.input.initMessageReply(+mid); if(!icon) {
shouldReply = false; icon = document.createElement('span');
icon.classList.add('tgico-reply_filled', 'bubble-gesture-reply-icon');
} else {
icon.classList.remove('is-visible');
icon.style.opacity = '';
}
target/* .querySelector('.bubble-content') */.append(icon);
} }
});
}, return !!target;
listenerOptions: {capture: true} },
}); onSwipe: (xDiff, yDiff) => {
shouldReply = xDiff >= replyAfter;
if(shouldReply && !icon.classList.contains('is-visible')) {
icon.classList.add('is-visible');
}
icon.style.opacity = '' + Math.min(1, xDiff / replyAfter);
const x = -Math.max(0, Math.min(MAX, xDiff));
target.style.transform = `translateX(${x}px)`;
cancelContextMenuOpening();
},
onReset: () => {
const _target = target;
SetTransition(_target, className, false, 250, () => {
if(icon.parentElement === _target) {
icon.classList.remove('is-visible');
icon.remove();
}
});
fastRaf(() => {
_target.style.transform = ``;
if(shouldReply) {
const {mid} = _target.dataset;
this.chat.input.initMessageReply(+mid);
shouldReply = false;
}
});
},
listenerOptions: {capture: true}
});
}
} }
} }
@ -2121,14 +2121,15 @@ export default class ChatBubbles {
this.deleteEmptyDateGroups(); this.deleteEmptyDateGroups();
if(!ignoreOnScroll) { if(!ignoreOnScroll) {
this.onScroll(); this.scrollable.onScroll();
// this.onScroll();
} }
} }
private setTopPadding(middleware = this.getMiddleware()) { private setTopPadding(middleware = this.getMiddleware()) {
let isPaddingNeeded = false; let isPaddingNeeded = false;
let setPaddingTo: HTMLElement; let setPaddingTo: HTMLElement;
if(!this.isTopPaddingSet) { if(!this.isTopPaddingSet && this.chat.type !== 'scheduled') {
const {clientHeight, scrollHeight} = this.scrollable.container; const {clientHeight, scrollHeight} = this.scrollable.container;
isPaddingNeeded = clientHeight === scrollHeight; isPaddingNeeded = clientHeight === scrollHeight;
/* const firstEl = this.chatInner.firstElementChild as HTMLElement; /* const firstEl = this.chatInner.firstElementChild as HTMLElement;
@ -3236,6 +3237,20 @@ export default class ChatBubbles {
}/* & { }/* & {
unmountIfFound?: boolean unmountIfFound?: boolean
} */>) { } */>) {
let modifiedGroups: typeof groups;
if(this.chat.type === 'scheduled') {
modifiedGroups = new Set();
items.forEach(({bubble, message}) => {
const item = this.bubbleGroups.getItemByBubble(bubble);
const group = item?.group;
if(group && item.message.date !== message.date) {
this.bubbleGroups.removeItem(item);
modifiedGroups.add(group);
}
});
}
items.forEach(({bubble, message}) => { items.forEach(({bubble, message}) => {
this.bubbleGroups.prepareForGrouping(bubble, message); this.bubbleGroups.prepareForGrouping(bubble, message);
}); });
@ -3245,11 +3260,17 @@ export default class ChatBubbles {
const avatarPromises = Array.from(groups).map((group) => { const avatarPromises = Array.from(groups).map((group) => {
if(group.avatar) return; if(group.avatar) return;
const firstItem = group.firstItem; const firstItem = group.firstItem;
if(this.chat.isAvatarNeeded(firstItem.message)) { if(firstItem && this.chat.isAvatarNeeded(firstItem.message)) {
return group.createAvatar(firstItem.message); return group.createAvatar(firstItem.message);
} }
}).filter(Boolean); }).filter(Boolean);
if(modifiedGroups) {
for(const group of modifiedGroups) {
groups.add(group);
}
}
return { return {
groups: [...groups], groups: [...groups],
avatarPromises avatarPromises

4
src/components/chat/contextMenu.ts

@ -39,6 +39,7 @@ import { attachContextMenuListener } from "../../helpers/dom/attachContextMenuLi
import filterAsync from "../../helpers/array/filterAsync"; import filterAsync from "../../helpers/array/filterAsync";
import appDownloadManager from "../../lib/appManagers/appDownloadManager"; import appDownloadManager from "../../lib/appManagers/appDownloadManager";
import { SERVICE_PEER_ID } from "../../lib/mtproto/mtproto_config"; import { SERVICE_PEER_ID } from "../../lib/mtproto/mtproto_config";
import { MessagesStorageKey } from "../../lib/appManagers/appMessagesManager";
export default class ChatContextMenu { export default class ChatContextMenu {
private buttons: (ButtonMenuItemOptions & {verify: () => boolean | Promise<boolean>, notDirect?: () => boolean, withSelection?: true, isSponsored?: true})[]; private buttons: (ButtonMenuItemOptions & {verify: () => boolean | Promise<boolean>, notDirect?: () => boolean, withSelection?: true, isSponsored?: true})[];
@ -340,8 +341,9 @@ export default class ChatContextMenu {
} }
for(const [peerId, mids] of this.chat.selection.selectedMids) { for(const [peerId, mids] of this.chat.selection.selectedMids) {
const storageKey: MessagesStorageKey = `${peerId}_${this.chat.type === 'scheduled' ? 'scheduled' : 'history'}`;
for(const mid of mids) { for(const mid of mids) {
const message = (await this.managers.appMessagesManager.getMessageByPeer(peerId, mid)) as Message.message; const message = (await this.managers.appMessagesManager.getMessageFromStorage(storageKey, mid)) as Message.message;
if(!!message.message) { if(!!message.message) {
return true; return true;
} }

6
src/components/chat/input.ts

@ -2384,7 +2384,11 @@ export default class ChatInput {
clearDraft: true clearDraft: true
}); });
this.onMessageSent(false, false); if(this.chat.type === 'scheduled') {
this.onMessageSent(true);
} else {
this.onMessageSent(false, false);
}
// this.onMessageSent(); // this.onMessageSent();
} }

Loading…
Cancel
Save