Browse Source

Sliced history

master
Eduard Kuzmenko 4 years ago
parent
commit
adc411824e
  1. 12
      src/components/chat/bubbles.ts
  2. 187
      src/helpers/slicedArray.ts
  3. 32
      src/lib/appManagers/appMessagesManager.ts

12
src/components/chat/bubbles.ts

@ -45,6 +45,7 @@ import { fastRaf } from "../../helpers/schedulers"; @@ -45,6 +45,7 @@ import { fastRaf } from "../../helpers/schedulers";
import { deferredPromise } from "../../helpers/cancellablePromise";
import RepliesElement from "./replies";
import DEBUG from "../../config/debug";
import { SliceEnd } from "../../helpers/slicedArray";
const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS = ['messageActionHistoryClear'];
@ -2696,7 +2697,7 @@ export default class ChatBubbles { @@ -2696,7 +2697,7 @@ export default class ChatBubbles {
additionMsgIds = [additionMsgId];
} else {
const historyStorage = this.appMessagesManager.getHistoryStorage(peerId, this.chat.threadId);
if(historyStorage.history.length < loadCount) {
if(historyStorage.history.length < loadCount && !historyStorage.history.slice.isEnd(SliceEnd.Both)) {
additionMsgIds = historyStorage.history.slice.slice();
// * filter last album, because we don't know is it the last item
@ -2718,7 +2719,7 @@ export default class ChatBubbles { @@ -2718,7 +2719,7 @@ export default class ChatBubbles {
let resultPromise: Promise<any>;
//const isFirstMessageRender = !!additionMsgID && result instanceof Promise && !appMessagesManager.getMessage(additionMsgID).grouped_id;
const isAdditionRender = additionMsgIds?.length;
const isAdditionRender = additionMsgIds?.length && result instanceof Promise;
const isFirstMessageRender = (this.isFirstLoad && backLimit && result instanceof Promise) || isAdditionRender;
if(isAdditionRender) {
resultPromise = result as Promise<any>;
@ -2729,18 +2730,17 @@ export default class ChatBubbles { @@ -2729,18 +2730,17 @@ export default class ChatBubbles {
this.isFirstLoad = false;
const processResult = (historyResult: typeof result) => {
/* if(this.chat.type === 'discussion' && 'offsetIdOffset' in historyResult) {
const isTopEnd = historyResult.offsetIdOffset >= (historyResult.count - loadCount);
if(this.chat.type === 'discussion' && 'offsetIdOffset' in historyResult) {
//this.log('discussion got history', loadCount, backLimit, historyResult, isTopEnd);
// * inject discussion start
if(isTopEnd) {
if(historyResult.history.isEnd(SliceEnd.Top)) {
const serviceStartMessageId = this.appMessagesManager.threadsServiceMessagesIdsStorage[this.peerId + '_' + this.chat.threadId];
if(serviceStartMessageId) historyResult.history.push(serviceStartMessageId);
historyResult.history.push(...this.chat.getMidsByMid(this.chat.threadId).reverse());
this.scrollable.loadedAll.top = true;
}
} */
}
};
const sup = (result: HistoryResult) => {

187
src/helpers/slicedArray.ts

@ -4,25 +4,132 @@ @@ -4,25 +4,132 @@
type ItemType = number;
export class Slice extends Array<ItemType> {
constructor(protected slicedArray: SlicedArray, items: ItemType[] = []) {
super(...items);
export enum SliceEnd {
None = 0,
Top = 1,
Bottom = 2,
Both = 4
};
export interface Slice extends Array<ItemType> {
slicedArray: SlicedArray;
end: SliceEnd;
isEnd: (side: SliceEnd) => boolean;
setEnd: (side: SliceEnd) => void;
}
}
export interface SliceConstructor {
new(...items: ItemType[]): Slice;
}
// TODO: Clear empty arrays after deleting items
export default class SlicedArray {
private slices: Slice[]/* = [[7,6,5],[4,3,2],[1,0,-1]] */;
private sliceConstructor: SliceConstructor;
constructor() {
this.slices = [new Slice(this)];
const self = this;
this.sliceConstructor = class Slice extends Array<ItemType> implements Slice {
slicedArray: SlicedArray;
end: SliceEnd = SliceEnd.None;
constructor(...items: ItemType[]) {
super(...items);
this.slicedArray = self;
}
isEnd(side: SliceEnd) {
if(this.end & side) {
return true;
}
if(side === SliceEnd.Top) {
const slice = self.last;
return slice.end & side ? this.includes(slice[slice.length - 1]) : false;
} else if(side === SliceEnd.Bottom) {
const slice = self.first;
return slice.end & side ? this.includes(slice[0]) : false;
}/* else if(side === SliceEnd.Both) {
} */
return false;
}
setEnd(side: SliceEnd) {
this.end |= side;
if(side !== SliceEnd.Both && this.end & SliceEnd.Top && this.end & SliceEnd.Bottom) {
this.end |= SliceEnd.Both;
}
}
}
const first = this.constructSlice();
first.setEnd(SliceEnd.Bottom);
this.slices = [first];
}
public constructSlice(...items: ItemType[]) {
//const slice = new Slice(this, ...items);
const slice = new this.sliceConstructor(...items);
return slice;
// ! code below will slow execution in 15 times
/* const self = this;
const p: Slice = new Proxy(slice, {
get: function(target, name: any) {
if(name === 'constructor') {
const p = new Proxy(Slice, {
construct: (target, args) => {
return self.constructSlice(...args);
}
});
return p;
}
return target[name];
}
});
return p; */
/*
var p = slicedArray.constructSlice();
p.length = 100000;
p.fill(255);
var a = new Array(100000);
a.fill(255);
var b = 0;
var perf = performance.now();
for(var i = 0; i < p.length; ++i) {
b += p[i];
}
console.log('perf 1', performance.now() - perf);
b = 0;
perf = performance.now();
for(var i = 0; i < a.length; ++i) {
b += a[i];
}
console.log('perf 2', performance.now() - perf);
*/
}
public insertSlice(slice: ItemType[]) {
if(!this.slices[0].length) {
this.slices[0].push(...slice);
if(!slice.length) {
return;
}
const first = this.slices[0];
if(!first.length) {
first.push(...slice);
return;
}
@ -59,7 +166,7 @@ export default class SlicedArray { @@ -59,7 +166,7 @@ export default class SlicedArray {
}
}
this.slices.splice(insertIndex, 0, new Slice(this, slice));
this.slices.splice(insertIndex, 0, this.constructSlice(...slice));
}
this.flatten();
@ -76,6 +183,7 @@ export default class SlicedArray { @@ -76,6 +183,7 @@ export default class SlicedArray {
const upperIndex = prevSlice.indexOf(nextSlice[0]);
if(upperIndex !== -1) {
prevSlice.setEnd(nextSlice.end);
this.slices.splice(i + 1, 1);
length--;
@ -85,9 +193,17 @@ export default class SlicedArray { @@ -85,9 +193,17 @@ export default class SlicedArray {
}
// *
get first() {
return this.slices[0];
}
get last() {
return this.slices[this.slices.length - 1];
}
get slice() {
return this.slices[0];
return this.first;
}
get length() {
@ -107,9 +223,10 @@ export default class SlicedArray { @@ -107,9 +223,10 @@ export default class SlicedArray {
}
public findSliceOffset(maxId: number) {
let slice: Slice;
for(let i = 0; i < this.slices.length; ++i) {
let offset = 0;
const slice = this.slices[i];
slice = this.slices[i];
if(slice.length < 2) {
continue;
}
@ -128,6 +245,13 @@ export default class SlicedArray { @@ -128,6 +245,13 @@ export default class SlicedArray {
}
}
if(slice && slice.isEnd(SliceEnd.Top)) {
return {
slice,
offset: slice.length
};
}
return undefined;
}
@ -135,6 +259,7 @@ export default class SlicedArray { @@ -135,6 +259,7 @@ export default class SlicedArray {
public sliceMe(offsetId: number, add_offset: number, limit: number) {
let slice = this.slice;
let offset = 0;
let sliceOffset = 0;
if(offsetId) {
const pos = this.findSliceOffset(offsetId);
@ -143,30 +268,46 @@ export default class SlicedArray { @@ -143,30 +268,46 @@ export default class SlicedArray {
}
slice = pos.slice;
offset = pos.offset;
offset = sliceOffset = pos.offset;
if(slice.includes(offsetId) && add_offset < 0) {
add_offset += 1;
if(slice.includes(offsetId)) {
sliceOffset += 1;
}
/* if(slice.includes(offsetId) && add_offset < 0) {
add_offset += 1;
} */
}
let sliceEnd = offset + add_offset + limit;
let sliceStart = Math.max(sliceOffset + add_offset, 0);
let sliceEnd = sliceOffset + add_offset + limit;
//const fixHalfBackLimit = add_offset && !(limit / add_offset % 2) && (sliceEnd % 2) ? 1 : 0;
//sliceEnd += fixHalfBackLimit;
const sliced = slice.slice(sliceStart, sliceEnd) as Slice;
const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit;
const bottomWasMeantToLoad = Math.abs(add_offset);
const topFulfilled = (slice.length - sliceOffset) >= topWasMeantToLoad || slice.isEnd(SliceEnd.Top);
const bottomFulfilled = (sliceOffset - bottomWasMeantToLoad) >= 0 || slice.isEnd(SliceEnd.Bottom);
//console.log('sliceMe', topFulfilled, bottomFulfilled);
return {
slice: slice.slice(Math.max(offset + add_offset, 0), sliceEnd),
offsetIdOffset: offset
slice: sliced,
offsetIdOffset: offset,
fulfilled: SliceEnd.None | (topFulfilled && bottomFulfilled ? SliceEnd.Both : ((topFulfilled ? SliceEnd.Top : SliceEnd.None) | (bottomFulfilled ? SliceEnd.Bottom : SliceEnd.None)))
};
}
public unshift(item: ItemType) {
this.slice.unshift(item);
public unshift(...items: ItemType[]) {
this.first.unshift(...items);
}
/* public push(item: ItemType) {
this.slice.push(item);
} */
public push(...items: ItemType[]) {
this.last.push(...items);
}
public delete(item: ItemType) {
const found = this.findSlice(item);

32
src/lib/appManagers/appMessagesManager.ts

@ -36,7 +36,7 @@ import pushHeavyTask from "../../helpers/heavyQueue"; @@ -36,7 +36,7 @@ import pushHeavyTask from "../../helpers/heavyQueue";
import { getFileNameByLocation } from "../../helpers/fileName";
import appProfileManager from "./appProfileManager";
import DEBUG, { MOUNT_CLASS_TO } from "../../config/debug";
import SlicedArray from "../../helpers/slicedArray";
import SlicedArray, { Slice, SliceEnd } from "../../helpers/slicedArray";
//console.trace('include');
// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет
@ -59,8 +59,8 @@ export type HistoryStorage = { @@ -59,8 +59,8 @@ export type HistoryStorage = {
export type HistoryResult = {
count: number,
history: number[],
offsetIdOffset?: number
history: Slice,
offsetIdOffset?: number,
};
export type Dialog = MTDialog.dialog;
@ -2124,7 +2124,7 @@ export class AppMessagesManager { @@ -2124,7 +2124,7 @@ export class AppMessagesManager {
*/
public getServerMessageId(messageId: number) {
const q = AppMessagesManager.MESSAGE_ID_OFFSET;
if(messageId <= q) {
if(messageId < q) { // id 0 -> mid 0xFFFFFFFF, so 0xFFFFFFFF must convert to 0
return messageId;
}
@ -3307,12 +3307,13 @@ export class AppMessagesManager { @@ -3307,12 +3307,13 @@ export class AppMessagesManager {
const threadKey = message.peerId + '_' + message.mid;
if(this.threadsServiceMessagesIdsStorage[threadKey]) return;
const maxMessageId = this.getServerMessageId(Math.max(...this.getMidsByMessage(message)));
const serviceStartMessage: Message.messageService = {
_: 'messageService',
pFlags: {
is_single: true
} as any,
id: this.generateMessageId(message.id, true),
id: this.generateMessageId(maxMessageId, true),
date: message.date,
from_id: {_: 'peerUser', user_id: 0}/* message.from_id */,
peer_id: message.peer_id,
@ -4629,7 +4630,7 @@ export class AppMessagesManager { @@ -4629,7 +4630,7 @@ export class AppMessagesManager {
}
const haveSlice = historyStorage.history.sliceMe(maxId, offset, limit);
if(haveSlice && (haveSlice.slice.length === limit || haveSlice.slice.includes(historyStorage.maxId))) {
if(haveSlice && (haveSlice.slice.length === limit || (haveSlice.fulfilled & SliceEnd.Both))) {
return {
count: historyStorage.count,
history: haveSlice.slice,
@ -4641,16 +4642,19 @@ export class AppMessagesManager { @@ -4641,16 +4642,19 @@ export class AppMessagesManager {
const slice = historyStorage.history.sliceMe(maxId, offset, limit);
return {
count: historyStorage.count,
history: slice?.slice || [],
history: slice?.slice || historyStorage.history.constructSlice(),
offsetIdOffset: slice?.offsetIdOffset || historyStorage.count
};
});
}
public fillHistoryStorage(peerId: number, maxId: number, fullLimit: number, offset: number, historyStorage: HistoryStorage, threadId?: number): Promise<void> {
return this.requestHistory(peerId, maxId, fullLimit, offset, undefined, threadId).then((historyResult) => {
public fillHistoryStorage(peerId: number, offset_id: number, limit: number, add_offset: number, historyStorage: HistoryStorage, threadId?: number): Promise<void> {
return this.requestHistory(peerId, offset_id, limit, add_offset, undefined, threadId).then((historyResult) => {
historyStorage.count = (historyResult as MessagesMessages.messagesMessagesSlice).count || historyResult.messages.length;
const offsetIdOffset = (historyResult as MessagesMessages.messagesMessagesSlice).offset_id_offset || 0;
const isTopEnd = offsetIdOffset >= (historyStorage.count - limit) || historyStorage.count < (limit + add_offset);
/* if(!maxId && historyResult.messages.length) {
maxId = this.incrementMessageId((historyResult.messages[0] as MyMessage).mid, 1);
}
@ -4666,18 +4670,22 @@ export class AppMessagesManager { @@ -4666,18 +4670,22 @@ export class AppMessagesManager {
const mids = historyResult.messages.map((message) => (message as MyMessage).mid);
// * add bound manually.
// * offset_id will be inclusive only if there is 'add_offset' <= -1 (-1 - will only include the 'offset_id')
if(maxId && !mids.includes(maxId)) {
if(offset_id && !mids.includes(offset_id) && offsetIdOffset < historyStorage.count) {
let i = 0;
for(const length = mids.length; i < length; ++i) {
if(maxId > mids[i]) {
if(offset_id > mids[i]) {
break;
}
}
mids.splice(i, 0, maxId);
mids.splice(i, 0, offset_id);
}
historyStorage.history.insertSlice(mids);
if(isTopEnd) {
historyStorage.history.last.setEnd(SliceEnd.Top);
}
/* const isBackLimit = offset < 0 && -offset !== fullLimit;
if(isBackLimit) {

Loading…
Cancel
Save