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

193
src/components/chat/bubbles.ts

@ -284,7 +284,7 @@ export default class ChatBubbles { @@ -284,7 +284,7 @@ export default class ChatBubbles {
// will call when sent for update pos
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;
}
@ -741,95 +741,95 @@ export default class ChatBubbles { @@ -741,95 +741,95 @@ export default class ChatBubbles {
});
}
if(!IS_MOBILE && this.chat.type !== 'pinned') {
this.listenerSetter.add(container)('dblclick', async(e) => {
if(this.chat.selection.isSelecting ||
!(await this.chat.canSend())) {
return;
}
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) {
if(this.chat.type !== 'pinned' && this.chat.type !== 'scheduled') {
if(!IS_MOBILE) {
this.listenerSetter.add(container)('dblclick', async(e) => {
if(this.chat.selection.isSelecting ||
!(await this.chat.canSend())) {
return;
}
this.chat.input.initMessageReply(mid);
}
});
}
if(IS_TOUCH_SUPPORTED) {
const className = 'is-gesturing-reply';
const MAX = 64;
const replyAfter = MAX * .75;
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 = '';
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;
}
target/* .querySelector('.bubble-content') */.append(icon);
}
return !!target;
},
onSwipe: (xDiff, yDiff) => {
shouldReply = xDiff >= replyAfter;
if(shouldReply && !icon.classList.contains('is-visible')) {
icon.classList.add('is-visible');
this.chat.input.initMessageReply(mid);
}
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();
});
} else if(IS_TOUCH_SUPPORTED) {
const className = 'is-gesturing-reply';
const MAX = 64;
const replyAfter = MAX * .75;
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;
}
});
fastRaf(() => {
_target.style.transform = ``;
if(shouldReply) {
const {mid} = _target.dataset;
this.chat.input.initMessageReply(+mid);
shouldReply = 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);
}
});
},
listenerOptions: {capture: true}
});
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);
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 { @@ -2121,14 +2121,15 @@ export default class ChatBubbles {
this.deleteEmptyDateGroups();
if(!ignoreOnScroll) {
this.onScroll();
this.scrollable.onScroll();
// this.onScroll();
}
}
private setTopPadding(middleware = this.getMiddleware()) {
let isPaddingNeeded = false;
let setPaddingTo: HTMLElement;
if(!this.isTopPaddingSet) {
if(!this.isTopPaddingSet && this.chat.type !== 'scheduled') {
const {clientHeight, scrollHeight} = this.scrollable.container;
isPaddingNeeded = clientHeight === scrollHeight;
/* const firstEl = this.chatInner.firstElementChild as HTMLElement;
@ -3236,6 +3237,20 @@ export default class ChatBubbles { @@ -3236,6 +3237,20 @@ export default class ChatBubbles {
}/* & {
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}) => {
this.bubbleGroups.prepareForGrouping(bubble, message);
});
@ -3245,11 +3260,17 @@ export default class ChatBubbles { @@ -3245,11 +3260,17 @@ export default class ChatBubbles {
const avatarPromises = Array.from(groups).map((group) => {
if(group.avatar) return;
const firstItem = group.firstItem;
if(this.chat.isAvatarNeeded(firstItem.message)) {
if(firstItem && this.chat.isAvatarNeeded(firstItem.message)) {
return group.createAvatar(firstItem.message);
}
}).filter(Boolean);
if(modifiedGroups) {
for(const group of modifiedGroups) {
groups.add(group);
}
}
return {
groups: [...groups],
avatarPromises

4
src/components/chat/contextMenu.ts

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

6
src/components/chat/input.ts

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

Loading…
Cancel
Save