-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathStateChangeType.ts
138 lines (130 loc) · 4.66 KB
/
StateChangeType.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Copyright 2022 - 2024 Gnuxie <[email protected]>
// Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: AFL-3.0 AND Apache-2.0
//
// SPDX-FileAttributionText: <text>
// This modified file incorporates work from mjolnir
// https://github.com/matrix-org/mjolnir
// </text>
import { StateEvent } from '../MatrixTypes/Events';
export enum StateChangeType {
/**
* A state event that has content has been introduced where no previous state type-key pair had
* in the room's history. This also means where there are no previous redacted
* or blanked state events.
*/
Introduced = 'Introduced',
/**
* A state event that has content has been reintroduced where a blank or redacted state type-key
* pair had previously resided in the room state.
* The distinction between introduced and reintroduced are important, because
* an issuer can always treat introduced state in the timeline as a delta,
* but not reintroduced, modified or removed state.
*/
Reintroduced = 'Reintroduced',
/**
* This is a special case of introduced, where a state type-key pair has been
* introduced for the first time, but with empty content.
*/
IntroducedAsBlank = 'IntroducedAsBlank',
/**
* This is when a unique state event with empty content has been added
* where there was previously a state event with empty or entirely redacted content.
* Can alternatively be thought of as "ReintroducedAsEmpty".
*/
BlankedEmptyContent = 'BlankedEmptyContent',
/**
* A state event with empty content has been sent over a contentful event
* with the same type-key pair.
*/
BlankedContent = 'BlankedContent',
/**
* A redaction was sent for an existing state event that is being tracked
* and has removed all content keys.
*/
CompletelyRedacted = 'CompletelyRedacted',
/**
* A redaction was sent for an existing state event that is being tracked
* and has removed all content keys that are not protected by authorization rules.
* For example `membership` in a member event will not be removed.
*/
PartiallyRedacted = 'PartiallyRedacted',
/**
* There is an existing contentful state event for this type-key pair that has been replaced
* with a new contenful state event.
*/
SupersededContent = 'SupersededContent',
/**
* The events are the same, and the event is intact.
*/
NoChange = 'NoChange',
}
function isSameEvent(eventA: StateEvent, eventB: StateEvent): boolean {
return eventA.event_id === eventB.event_id;
}
function isEmptyOfContent(event: StateEvent): boolean {
return Object.keys(event.content).length === 0;
}
function isDifferenceInContentKeys(
eventA: StateEvent,
eventB: StateEvent
): boolean {
return (
Object.keys(eventA.content).length !== Object.keys(eventB.content).length
);
}
/**
* Calculate the change in the room state.
* This is used on a per event basis on state deltas or the result of `/state`.
* If calculating the effects of a redaction, apply the redaction first and then
* compare the original event with the redacted version.
* @param event A new event for a state type-key pair.
* @param existingState Any known existing state event for the type-key pair.
* @returns How the state was changed @see StateChangeType.
*/
export function calculateStateChange(
event: StateEvent,
existingState?: StateEvent
): StateChangeType {
if (isEmptyOfContent(event)) {
if (existingState === undefined) {
return StateChangeType.IntroducedAsBlank;
} else if (isSameEvent(event, existingState)) {
if (isEmptyOfContent(existingState)) {
return StateChangeType.NoChange;
} else {
return StateChangeType.CompletelyRedacted;
}
} else if (isEmptyOfContent(existingState)) {
return StateChangeType.BlankedEmptyContent;
} else {
return StateChangeType.BlankedContent;
}
} else if (existingState === undefined) {
return StateChangeType.Introduced;
} else if (isSameEvent(event, existingState)) {
if (isDifferenceInContentKeys(event, existingState)) {
return StateChangeType.PartiallyRedacted;
} else {
return StateChangeType.NoChange;
}
} else if (isEmptyOfContent(existingState)) {
return StateChangeType.Reintroduced;
} else {
return StateChangeType.SupersededContent;
}
}
export function isChanged(changeType: StateChangeType): boolean {
return changeType !== StateChangeType.NoChange;
}
export function isChangingContent(changeType: StateChangeType): boolean {
switch (changeType) {
case StateChangeType.BlankedEmptyContent:
case StateChangeType.IntroducedAsBlank:
case StateChangeType.NoChange:
return false;
default:
return true;
}
}