Skip to content

Commit d6f1c6c

Browse files
authored
Fix thread & main timeline partitioning logic (#2264)
1 parent 4360ae7 commit d6f1c6c

File tree

11 files changed

+1238
-1041
lines changed

11 files changed

+1238
-1041
lines changed

spec/integ/matrix-client-methods.spec.js

Lines changed: 164 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -556,34 +556,36 @@ describe("MatrixClient", function() {
556556
});
557557

558558
describe("partitionThreadedEvents", function() {
559+
const room = new Room("!STrMRsukXHtqQdSeHa:matrix.org", client, userId);
560+
559561
it("returns empty arrays when given an empty arrays", function() {
560562
const events = [];
561-
const [timeline, threaded] = client.partitionThreadedEvents(events);
563+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
562564
expect(timeline).toEqual([]);
563565
expect(threaded).toEqual([]);
564566
});
565567

566568
it("copies pre-thread in-timeline vote events onto both timelines", function() {
567569
client.clientOpts = { experimentalThreadSupport: true };
568570

569-
const eventMessageInThread = buildEventMessageInThread();
570571
const eventPollResponseReference = buildEventPollResponseReference();
571572
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
573+
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
572574

573575
const events = [
576+
eventPollStartThreadRoot,
574577
eventMessageInThread,
575578
eventPollResponseReference,
576-
eventPollStartThreadRoot,
577579
];
578580
// Vote has no threadId yet
579581
expect(eventPollResponseReference.threadId).toBeFalsy();
580582

581-
const [timeline, threaded] = client.partitionThreadedEvents(events);
583+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
582584

583585
expect(timeline).toEqual([
584586
// The message that was sent in a thread is missing
585-
eventPollResponseReference,
586587
eventPollStartThreadRoot,
588+
eventPollResponseReference,
587589
]);
588590

589591
// The vote event has been copied into the thread
@@ -592,33 +594,34 @@ describe("MatrixClient", function() {
592594
expect(eventRefWithThreadId.threadId).toBeTruthy();
593595

594596
expect(threaded).toEqual([
597+
eventPollStartThreadRoot,
595598
eventMessageInThread,
596599
eventRefWithThreadId,
597-
// Thread does not see thread root
598600
]);
599601
});
600602

601603
it("copies pre-thread in-timeline reactions onto both timelines", function() {
602604
client.clientOpts = { experimentalThreadSupport: true };
603605

604-
const eventMessageInThread = buildEventMessageInThread();
605-
const eventReaction = buildEventReaction();
606606
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
607+
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
608+
const eventReaction = buildEventReaction(eventPollStartThreadRoot);
607609

608610
const events = [
611+
eventPollStartThreadRoot,
609612
eventMessageInThread,
610613
eventReaction,
611-
eventPollStartThreadRoot,
612614
];
613615

614-
const [timeline, threaded] = client.partitionThreadedEvents(events);
616+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
615617

616618
expect(timeline).toEqual([
617-
eventReaction,
618619
eventPollStartThreadRoot,
620+
eventReaction,
619621
]);
620622

621623
expect(threaded).toEqual([
624+
eventPollStartThreadRoot,
622625
eventMessageInThread,
623626
withThreadId(eventReaction, eventPollStartThreadRoot.getId()),
624627
]);
@@ -628,23 +631,24 @@ describe("MatrixClient", function() {
628631
client.clientOpts = { experimentalThreadSupport: true };
629632

630633
const eventPollResponseReference = buildEventPollResponseReference();
631-
const eventMessageInThread = buildEventMessageInThread();
632634
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
635+
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
633636

634637
const events = [
638+
eventPollStartThreadRoot,
635639
eventPollResponseReference,
636640
eventMessageInThread,
637-
eventPollStartThreadRoot,
638641
];
639642

640-
const [timeline, threaded] = client.partitionThreadedEvents(events);
643+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
641644

642645
expect(timeline).toEqual([
643-
eventPollResponseReference,
644646
eventPollStartThreadRoot,
647+
eventPollResponseReference,
645648
]);
646649

647650
expect(threaded).toEqual([
651+
eventPollStartThreadRoot,
648652
withThreadId(eventPollResponseReference, eventPollStartThreadRoot.getId()),
649653
eventMessageInThread,
650654
]);
@@ -653,36 +657,37 @@ describe("MatrixClient", function() {
653657
it("copies post-thread in-timeline reactions onto both timelines", function() {
654658
client.clientOpts = { experimentalThreadSupport: true };
655659

656-
const eventReaction = buildEventReaction();
657-
const eventMessageInThread = buildEventMessageInThread();
658660
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
661+
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
662+
const eventReaction = buildEventReaction(eventPollStartThreadRoot);
659663

660664
const events = [
661-
eventReaction,
662-
eventMessageInThread,
663665
eventPollStartThreadRoot,
666+
eventMessageInThread,
667+
eventReaction,
664668
];
665669

666-
const [timeline, threaded] = client.partitionThreadedEvents(events);
670+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
667671

668672
expect(timeline).toEqual([
669-
eventReaction,
670673
eventPollStartThreadRoot,
674+
eventReaction,
671675
]);
672676

673677
expect(threaded).toEqual([
674-
withThreadId(eventReaction, eventPollStartThreadRoot.getId()),
678+
eventPollStartThreadRoot,
675679
eventMessageInThread,
680+
withThreadId(eventReaction, eventPollStartThreadRoot.getId()),
676681
]);
677682
});
678683

679684
it("sends room state events to the main timeline only", function() {
680685
client.clientOpts = { experimentalThreadSupport: true };
681686
// This is based on recording the events in a real room:
682687

683-
const eventMessageInThread = buildEventMessageInThread();
684-
const eventPollResponseReference = buildEventPollResponseReference();
685688
const eventPollStartThreadRoot = buildEventPollStartThreadRoot();
689+
const eventPollResponseReference = buildEventPollResponseReference();
690+
const eventMessageInThread = buildEventMessageInThread(eventPollStartThreadRoot);
686691
const eventRoomName = buildEventRoomName();
687692
const eventEncryption = buildEventEncryption();
688693
const eventGuestAccess = buildEventGuestAccess();
@@ -693,9 +698,9 @@ describe("MatrixClient", function() {
693698
const eventCreate = buildEventCreate();
694699

695700
const events = [
696-
eventMessageInThread,
697-
eventPollResponseReference,
698701
eventPollStartThreadRoot,
702+
eventPollResponseReference,
703+
eventMessageInThread,
699704
eventRoomName,
700705
eventEncryption,
701706
eventGuestAccess,
@@ -705,12 +710,12 @@ describe("MatrixClient", function() {
705710
eventMember,
706711
eventCreate,
707712
];
708-
const [timeline, threaded] = client.partitionThreadedEvents(events);
713+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
709714

710715
expect(timeline).toEqual([
711716
// The message that was sent in a thread is missing
712-
eventPollResponseReference,
713717
eventPollStartThreadRoot,
718+
eventPollResponseReference,
714719
eventRoomName,
715720
eventEncryption,
716721
eventGuestAccess,
@@ -721,11 +726,95 @@ describe("MatrixClient", function() {
721726
eventCreate,
722727
]);
723728

724-
// Thread should contain only stuff that happened in the thread -
725-
// no thread root, and no room state events
729+
// Thread should contain only stuff that happened in the thread - no room state events
726730
expect(threaded).toEqual([
727-
eventMessageInThread,
731+
eventPollStartThreadRoot,
728732
withThreadId(eventPollResponseReference, eventPollStartThreadRoot.getId()),
733+
eventMessageInThread,
734+
]);
735+
});
736+
737+
it("sends redactions of reactions to thread responses to thread timeline only", () => {
738+
client.clientOpts = { experimentalThreadSupport: true };
739+
740+
const threadRootEvent = buildEventPollStartThreadRoot();
741+
const eventMessageInThread = buildEventMessageInThread(threadRootEvent);
742+
const threadedReaction = buildEventReaction(eventMessageInThread);
743+
const threadedReactionRedaction = buildEventRedaction(threadedReaction);
744+
745+
const events = [
746+
threadRootEvent,
747+
eventMessageInThread,
748+
threadedReaction,
749+
threadedReactionRedaction,
750+
];
751+
752+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
753+
754+
expect(timeline).toEqual([
755+
threadRootEvent,
756+
]);
757+
758+
expect(threaded).toEqual([
759+
threadRootEvent,
760+
eventMessageInThread,
761+
threadedReaction,
762+
threadedReactionRedaction,
763+
]);
764+
});
765+
766+
it("sends reply to reply to thread root outside of thread to main timeline only", () => {
767+
client.clientOpts = { experimentalThreadSupport: true };
768+
769+
const threadRootEvent = buildEventPollStartThreadRoot();
770+
const eventMessageInThread = buildEventMessageInThread(threadRootEvent);
771+
const directReplyToThreadRoot = buildEventReply(threadRootEvent);
772+
const replyToReply = buildEventReply(directReplyToThreadRoot);
773+
774+
const events = [
775+
threadRootEvent,
776+
eventMessageInThread,
777+
directReplyToThreadRoot,
778+
replyToReply,
779+
];
780+
781+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
782+
783+
expect(timeline).toEqual([
784+
threadRootEvent,
785+
directReplyToThreadRoot,
786+
replyToReply,
787+
]);
788+
789+
expect(threaded).toEqual([
790+
threadRootEvent,
791+
eventMessageInThread,
792+
]);
793+
});
794+
795+
it("sends reply to thread responses to thread timeline only", () => {
796+
client.clientOpts = { experimentalThreadSupport: true };
797+
798+
const threadRootEvent = buildEventPollStartThreadRoot();
799+
const eventMessageInThread = buildEventMessageInThread(threadRootEvent);
800+
const replyToThreadResponse = buildEventReply(eventMessageInThread);
801+
802+
const events = [
803+
threadRootEvent,
804+
eventMessageInThread,
805+
replyToThreadResponse,
806+
];
807+
808+
const [timeline, threaded] = client.partitionThreadedEvents(room, events);
809+
810+
expect(timeline).toEqual([
811+
threadRootEvent,
812+
]);
813+
814+
expect(threaded).toEqual([
815+
threadRootEvent,
816+
eventMessageInThread,
817+
replyToThreadResponse,
729818
]);
730819
});
731820
});
@@ -737,16 +826,16 @@ function withThreadId(event, newThreadId) {
737826
return ret;
738827
}
739828

740-
const buildEventMessageInThread = () => new MatrixEvent({
829+
const buildEventMessageInThread = (root) => new MatrixEvent({
741830
"age": 80098509,
742831
"content": {
743832
"algorithm": "m.megolm.v1.aes-sha2",
744833
"ciphertext": "ENCRYPTEDSTUFF",
745834
"device_id": "XISFUZSKHH",
746835
"m.relates_to": {
747-
"event_id": "$VLS2ojbPmxb6x8ECetn45hmND6cRDcjgv-j-to9m7Vo",
836+
"event_id": root.getId(),
748837
"m.in_reply_to": {
749-
"event_id": "$VLS2ojbPmxb6x8ECetn45hmND6cRDcjgv-j-to9m7Vo",
838+
"event_id": root.getId(),
750839
},
751840
"rel_type": "m.thread",
752841
},
@@ -784,10 +873,10 @@ const buildEventPollResponseReference = () => new MatrixEvent({
784873
"user_id": "@andybalaam-test1:matrix.org",
785874
});
786875

787-
const buildEventReaction = () => new MatrixEvent({
876+
const buildEventReaction = (event) => new MatrixEvent({
788877
"content": {
789878
"m.relates_to": {
790-
"event_id": "$VLS2ojbPmxb6x8ECetn45hmND6cRDcjgv-j-to9m7Vo",
879+
"event_id": event.getId(),
791880
"key": "🤗",
792881
"rel_type": "m.annotation",
793882
},
@@ -803,6 +892,22 @@ const buildEventReaction = () => new MatrixEvent({
803892
"room_id": "!STrMRsukXHtqQdSeHa:matrix.org",
804893
});
805894

895+
const buildEventRedaction = (event) => new MatrixEvent({
896+
"content": {
897+
898+
},
899+
"origin_server_ts": 1643977249239,
900+
"sender": "@andybalaam-test1:matrix.org",
901+
"redacts": event.getId(),
902+
"type": "m.room.redaction",
903+
"unsigned": {
904+
"age": 22597,
905+
"transaction_id": "m1643977249073.17",
906+
},
907+
"event_id": "$86B2b-x3LgE4DlV4y24b7UHnt72LIA3rzjvMysTtAfB",
908+
"room_id": "!STrMRsukXHtqQdSeHa:matrix.org",
909+
});
910+
806911
const buildEventPollStartThreadRoot = () => new MatrixEvent({
807912
"age": 80108647,
808913
"content": {
@@ -821,6 +926,29 @@ const buildEventPollStartThreadRoot = () => new MatrixEvent({
821926
"user_id": "@andybalaam-test1:matrix.org",
822927
});
823928

929+
const buildEventReply = (target) => new MatrixEvent({
930+
"age": 80098509,
931+
"content": {
932+
"algorithm": "m.megolm.v1.aes-sha2",
933+
"ciphertext": "ENCRYPTEDSTUFF",
934+
"device_id": "XISFUZSKHH",
935+
"m.relates_to": {
936+
"m.in_reply_to": {
937+
"event_id": target.getId(),
938+
},
939+
},
940+
"sender_key": "i3N3CtG/CD2bGB8rA9fW6adLYSDvlUhf2iuU73L65Vg",
941+
"session_id": "Ja11R/KG6ua0wdk8zAzognrxjio1Gm/RK2Gn6lFL804",
942+
},
943+
"event_id": target.getId() + Math.random(),
944+
"origin_server_ts": 1643815466378,
945+
"room_id": "!STrMRsukXHtqQdSeHa:matrix.org",
946+
"sender": "@andybalaam-test1:matrix.org",
947+
"type": "m.room.encrypted",
948+
"unsigned": { "age": 80098509 },
949+
"user_id": "@andybalaam-test1:matrix.org",
950+
});
951+
824952
const buildEventRoomName = () => new MatrixEvent({
825953
"age": 80123249,
826954
"content": {

spec/integ/matrix-client-syncing.spec.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -735,8 +735,7 @@ describe("MatrixClient syncing", function() {
735735
expect(tok).toEqual("pagTok");
736736
}),
737737

738-
// first flush the filter request; this will make syncLeftRooms
739-
// make its /sync call
738+
// first flush the filter request; this will make syncLeftRooms make its /sync call
740739
httpBackend.flush("/filter").then(function() {
741740
return httpBackend.flushAllExpected();
742741
}),

0 commit comments

Comments
 (0)