Skip to content

Commit

Permalink
Merge branch 'develop' into t3chguy/fix/21466
Browse files Browse the repository at this point in the history
  • Loading branch information
t3chguy committed Mar 24, 2022
2 parents 05e0c17 + 6192325 commit f547514
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 46 deletions.
9 changes: 1 addition & 8 deletions src/@types/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,7 @@ export enum RelationType {
Annotation = "m.annotation",
Replace = "m.replace",
Reference = "m.reference",
/**
* Note, "io.element.thread" is hardcoded
* Should be replaced with "m.thread" once MSC3440 lands
* Can not use `UnstableValue` as TypeScript does not
* allow computed values in enums
* https://github.com/microsoft/TypeScript/issues/27976
*/
Thread = "io.element.thread",
Thread = "m.thread",
}

export enum MsgType {
Expand Down
10 changes: 9 additions & 1 deletion src/models/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,17 @@ export interface IEvent {
unsigned: IUnsigned;
redacts?: string;

// v1 legacy fields
/**
* @deprecated
*/
user_id?: string;
/**
* @deprecated
*/
prev_content?: IContent;
/**
* @deprecated
*/
age?: number;
}

Expand Down
168 changes: 131 additions & 37 deletions src/models/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { Direction, EventTimeline } from "./event-timeline";
import { getHttpUriForMxc } from "../content-repo";
import * as utils from "../utils";
import { normalize } from "../utils";
import { IEvent, MatrixEvent } from "./event";
import { IEvent, IThreadBundledRelationship, MatrixEvent } from "./event";
import { EventStatus } from "./event-status";
import { RoomMember } from "./room-member";
import { IRoomSummary, RoomSummary } from "./room-summary";
Expand All @@ -32,6 +32,7 @@ import { TypedReEmitter } from '../ReEmitter';
import {
EventType, RoomCreateTypeField, RoomType, UNSTABLE_ELEMENT_FUNCTIONAL_USERS,
EVENT_VISIBILITY_CHANGE_TYPE,
RelationType,
} from "../@types/event";
import { IRoomVersionsCapability, MatrixClient, PendingEventOrdering, RoomVersionStability } from "../client";
import { GuestAccess, HistoryVisibility, JoinRule, ResizeMethod } from "../@types/partials";
Expand Down Expand Up @@ -149,6 +150,7 @@ export interface ICreateFilterOpts {
// timeline. Useful to disable for some filters that can't be achieved by the
// client in an efficient manner
prepopulateTimeline?: boolean;
useSyncEvents?: boolean;
pendingEvents?: boolean;
}

Expand All @@ -168,6 +170,7 @@ export enum RoomEvent {
type EmittedEvents = RoomEvent
| ThreadEvent.New
| ThreadEvent.Update
| ThreadEvent.NewReply
| RoomEvent.Timeline
| RoomEvent.TimelineReset;

Expand Down Expand Up @@ -1344,6 +1347,7 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
filter: Filter,
{
prepopulateTimeline = true,
useSyncEvents = true,
pendingEvents = true,
}: ICreateFilterOpts = {},
): EventTimelineSet {
Expand All @@ -1356,8 +1360,10 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
RoomEvent.Timeline,
RoomEvent.TimelineReset,
]);
this.filteredTimelineSets[filter.filterId] = timelineSet;
this.timelineSets.push(timelineSet);
if (useSyncEvents) {
this.filteredTimelineSets[filter.filterId] = timelineSet;
this.timelineSets.push(timelineSet);
}

const unfilteredLiveTimeline = this.getLiveTimeline();
// Not all filter are possible to replicate client-side only
Expand Down Expand Up @@ -1385,7 +1391,7 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
timeline.getPaginationToken(EventTimeline.BACKWARDS),
EventTimeline.BACKWARDS,
);
} else {
} else if (useSyncEvents) {
const livePaginationToken = unfilteredLiveTimeline.getPaginationToken(Direction.Forward);
timelineSet
.getLiveTimeline()
Expand All @@ -1403,41 +1409,54 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
return timelineSet;
}

private async createThreadTimelineSet(filterType?: ThreadFilterType): Promise<EventTimelineSet> {
let timelineSet: EventTimelineSet;
if (Thread.hasServerSideSupport) {
const myUserId = this.client.getUserId();
const filter = new Filter(myUserId);
private async getThreadListFilter(filterType = ThreadFilterType.All): Promise<Filter> {
const myUserId = this.client.getUserId();
const filter = new Filter(myUserId);

const definition: IFilterDefinition = {
"room": {
"timeline": {
[FILTER_RELATED_BY_REL_TYPES.name]: [THREAD_RELATION_TYPE.name],
},
const definition: IFilterDefinition = {
"room": {
"timeline": {
[FILTER_RELATED_BY_REL_TYPES.name]: [THREAD_RELATION_TYPE.name],
},
};
},
};

if (filterType === ThreadFilterType.My) {
definition.room.timeline[FILTER_RELATED_BY_SENDERS.name] = [myUserId];
}
if (filterType === ThreadFilterType.My) {
definition.room.timeline[FILTER_RELATED_BY_SENDERS.name] = [myUserId];
}

filter.setDefinition(definition);
const filterId = await this.client.getOrCreateFilter(
`THREAD_PANEL_${this.roomId}_${filterType}`,
filter,
);

filter.filterId = filterId;

return filter;
}

private async createThreadTimelineSet(filterType?: ThreadFilterType): Promise<EventTimelineSet> {
let timelineSet: EventTimelineSet;
if (Thread.hasServerSideSupport) {
const filter = await this.getThreadListFilter(filterType);

filter.setDefinition(definition);
const filterId = await this.client.getOrCreateFilter(
`THREAD_PANEL_${this.roomId}_${filterType}`,
filter,
);
filter.filterId = filterId;
timelineSet = this.getOrCreateFilteredTimelineSet(
filter,
{
prepopulateTimeline: false,
useSyncEvents: false,
pendingEvents: false,
},
);

// An empty pagination token allows to paginate from the very bottom of
// the timeline set.
timelineSet.getLiveTimeline().setPaginationToken("", EventTimeline.BACKWARDS);
// Right now we completely by-pass the pagination to be able to order
// the events by last reply to a thread
// Once the server can help us with that, we should uncomment the line
// below
// timelineSet.getLiveTimeline().setPaginationToken("", EventTimeline.BACKWARDS);
} else {
timelineSet = new EventTimelineSet(this, {
pendingEvents: false,
Expand All @@ -1458,6 +1477,78 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
return timelineSet;
}

public threadsReady = false;

public async fetchRoomThreads(): Promise<void> {
if (this.threadsReady) {
return;
}

const allThreadsFilter = await this.getThreadListFilter();

const { chunk: events } = await this.client.createMessagesRequest(
this.roomId,
"",
Number.MAX_SAFE_INTEGER,
Direction.Backward,
allThreadsFilter,
);

const orderedByLastReplyEvents = events
.map(this.client.getEventMapper())
.sort((eventA, eventB) => {
/**
* `origin_server_ts` in a decentralised world is far from ideal
* but for lack of any better, we will have to use this
* Long term the sorting should be handled by homeservers and this
* is only meant as a short term patch
*/
const threadAMetadata = eventA
.getServerAggregatedRelation<IThreadBundledRelationship>(RelationType.Thread);
const threadBMetadata = eventB
.getServerAggregatedRelation<IThreadBundledRelationship>(RelationType.Thread);
return threadAMetadata.latest_event.origin_server_ts - threadBMetadata.latest_event.origin_server_ts;
});

const myThreads = orderedByLastReplyEvents.filter(event => {
const threadRelationship = event
.getServerAggregatedRelation<IThreadBundledRelationship>(RelationType.Thread);
return threadRelationship.current_user_participated;
});

const roomState = this.getLiveTimeline().getState(EventTimeline.FORWARDS);
for (const event of orderedByLastReplyEvents) {
this.threadsTimelineSets[0].addLiveEvent(
event,
DuplicateStrategy.Ignore,
false,
roomState,
);
}
for (const event of myThreads) {
this.threadsTimelineSets[1].addLiveEvent(
event,
DuplicateStrategy.Ignore,
false,
roomState,
);
}

this.client.decryptEventIfNeeded(orderedByLastReplyEvents[orderedByLastReplyEvents.length -1]);
this.client.decryptEventIfNeeded(myThreads[myThreads.length -1]);

this.threadsReady = true;

this.on(ThreadEvent.NewReply, this.onThreadNewReply);
}

private onThreadNewReply(thread: Thread): void {
for (const timelineSet of this.threadsTimelineSets) {
timelineSet.removeEvent(thread.id);
timelineSet.addLiveEvent(thread.rootEvent);
}
}

/**
* Forget the timelineSet for this room with the given filter
*
Expand Down Expand Up @@ -1549,6 +1640,7 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>
this.threads.set(thread.id, thread);
this.reEmitter.reEmit(thread, [
ThreadEvent.Update,
ThreadEvent.NewReply,
RoomEvent.Timeline,
RoomEvent.TimelineReset,
]);
Expand All @@ -1559,19 +1651,21 @@ export class Room extends TypedEventEmitter<EmittedEvents, RoomEventHandlerMap>

this.emit(ThreadEvent.New, thread, toStartOfTimeline);

this.threadsTimelineSets.forEach(timelineSet => {
if (thread.rootEvent) {
if (Thread.hasServerSideSupport) {
timelineSet.addLiveEvent(thread.rootEvent);
} else {
timelineSet.addEventToTimeline(
thread.rootEvent,
timelineSet.getLiveTimeline(),
toStartOfTimeline,
);
if (this.threadsReady) {
this.threadsTimelineSets.forEach(timelineSet => {
if (thread.rootEvent) {
if (Thread.hasServerSideSupport) {
timelineSet.addLiveEvent(thread.rootEvent);
} else {
timelineSet.addEventToTimeline(
thread.rootEvent,
timelineSet.getLiveTimeline(),
toStartOfTimeline,
);
}
}
}
});
});
}

return thread;
}
Expand Down

0 comments on commit f547514

Please sign in to comment.