Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F114485
D204.1732553473.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
D204.1732553473.diff
View Options
diff --git a/src/client/actions/ephemeral.cpp b/src/client/actions/ephemeral.cpp
--- a/src/client/actions/ephemeral.cpp
+++ b/src/client/actions/ephemeral.cpp
@@ -50,7 +50,13 @@
m.addJob(std::move(job));
- return { std::move(m), lager::noop };
+ auto userId = m.userId;
+ auto [next, _] = ClientModel::update(std::move(m), UpdateRoomAction{
+ a.roomId,
+ UpdateLocalReadMarkerAction{a.eventId, userId},
+ });
+
+ return { std::move(next), lager::noop };
}
ClientResult processResponse(ClientModel m, PostReceiptResponse r)
diff --git a/src/client/room/room-model.hpp b/src/client/room/room-model.hpp
--- a/src/client/room/room-model.hpp
+++ b/src/client/room/room-model.hpp
@@ -166,6 +166,13 @@
std::string myUserId;
};
+ /// Update the local read marker, removing any read notifications before it.
+ struct UpdateLocalReadMarkerAction
+ {
+ std::string localReadMarker;
+ std::string myUserId;
+ };
+
inline bool operator==(const PendingRoomKeyEvent &a, const PendingRoomKeyEvent &b)
{
return a.txnId == b.txnId && a.messages == b.messages;
@@ -340,7 +347,8 @@
UpdateJoinedMemberCountAction,
UpdateInvitedMemberCountAction,
AddLocalNotificationsAction,
- RemoveReadLocalNotificationsAction
+ RemoveReadLocalNotificationsAction,
+ UpdateLocalReadMarkerAction
>;
static RoomModel update(RoomModel r, Action a);
diff --git a/src/client/room/room-model.cpp b/src/client/room/room-model.cpp
--- a/src/client/room/room-model.cpp
+++ b/src/client/room/room-model.cpp
@@ -20,6 +20,21 @@
inline const auto receiptTypes = immer::flex_vector<std::string>{"m.read", "m.read.private"};
+template<class Func>
+static std::string getMaxInTimeline(std::string a, std::string b, Func sortKey)
+{
+ if (a.empty() && b.empty()) {
+ return std::string();
+ } else {
+ // for an unexisting event, event id is empty and timestamp is 0
+ // for an existing event, event id is not empty and timestamp >= 0
+ // so this handles all cases even when we do not have the corresponding event
+ return std::max(a, b, [=](const std::string &x, const std::string &y) {
+ return sortKey(x) < sortKey(y);
+ });
+ }
+}
+
namespace Kazv
{
PendingRoomKeyEvent makePendingRoomKeyEventV0(std::string txnId, Event event, immer::map<std::string, immer::flex_vector<std::string>> devices)
@@ -328,7 +343,11 @@
return r;
},
[&](AddLocalNotificationsAction a) {
- auto readReceiptForCurrentUser = r.readReceipts.count(a.myUserId) ? r.readReceipts[a.myUserId].eventId : ""s;
+ auto k = [r](const auto &id) {
+ return sortKeyForTimelineEvent(r.messages[id]);
+ };
+
+ auto readReceiptForCurrentUser = getMaxInTimeline(r.readReceipts[a.myUserId].eventId, r.localReadMarker, k);
auto newEventIds = intoImmer(
immer::flex_vector<std::string>{},
@@ -336,10 +355,6 @@
a.newEvents
);
- auto k = [r](const auto &id) {
- return sortKeyForTimelineEvent(r.messages[id]);
- };
-
auto needToAddPredicate = [a, r, k, readReceiptForCurrentUser](const auto &eid) {
if (!readReceiptForCurrentUser.empty()
&& k(readReceiptForCurrentUser) >= k(eid)) {
@@ -355,12 +370,6 @@
return r;
},
[&](RemoveReadLocalNotificationsAction a) {
- if (!r.readReceipts.count(a.myUserId)) {
- return r;
- }
-
- auto rr = r.readReceipts[a.myUserId].eventId;
-
auto k = [r](const auto &id) {
return sortKeyForTimelineEvent(r.messages[id]);
};
@@ -368,6 +377,15 @@
return k(a) < k(b);
};
+ auto rr = getMaxInTimeline(
+ r.readReceipts[a.myUserId].eventId,
+ r.localReadMarker,
+ k
+ );
+ if (rr.empty()) {
+ return r;
+ }
+
auto it = std::upper_bound(
r.unreadNotificationEventIds.begin(),
r.unreadNotificationEventIds.end(),
@@ -385,6 +403,11 @@
r.unreadNotificationEventIds = std::move(r.unreadNotificationEventIds).erase(0, it.index());
}
return r;
+ },
+ [&](UpdateLocalReadMarkerAction a) {
+ r.localReadMarker = a.localReadMarker;
+ auto next = RoomModel::update(std::move(r), RemoveReadLocalNotificationsAction{a.myUserId});
+ return next;
}
);
}
diff --git a/src/client/room/room.hpp b/src/client/room/room.hpp
--- a/src/client/room/room.hpp
+++ b/src/client/room/room.hpp
@@ -340,6 +340,13 @@
/* lager::reader<bool> */
KAZV_WRAP_ATTR(RoomModel, roomCursor(), membersFullyLoaded);
+ /**
+ * Get the local read marker in this room.
+ *
+ * @return a lager::reader of a std::string of the local read marker.
+ */
+ auto localReadMarker() const -> lager::reader<std::string>;
+
/**
* Get the ids of the heroes of the room.
*
diff --git a/src/client/room/room.cpp b/src/client/room/room.cpp
--- a/src/client/room/room.cpp
+++ b/src/client/room/room.cpp
@@ -278,6 +278,12 @@
return roomCursor()[&RoomModel::encrypted];
}
+ auto Room::localReadMarker() const
+ -> lager::reader<std::string>
+ {
+ return roomCursor()[&RoomModel::localReadMarker];
+ }
+
auto Room::heroIds() const
-> lager::reader<immer::flex_vector<std::string>>
{
diff --git a/src/tests/client/room/read-receipt-test.cpp b/src/tests/client/room/read-receipt-test.cpp
--- a/src/tests/client/room/read-receipt-test.cpp
+++ b/src/tests/client/room/read-receipt-test.cpp
@@ -163,6 +163,25 @@
REQUIRE(readers2 == expected2);
}
+TEST_CASE("localReadMarker", "[client][room][receipt]")
+{
+ boost::asio::io_context io;
+ AsioPromiseHandler ph{io.get_executor()};
+
+ auto tl = EventList{
+ makeEvent(),
+ };
+ auto room = makeRoom(withRoomTimeline(tl));
+ room.localReadMarker = tl[0].id();
+ auto model = makeClient(withRoom(room));
+ auto store = createTestClientStoreFrom(model, ph);
+ auto client = Client(store.reader().map([](auto c) { return SdkModel{c}; }),
+ store,
+ std::nullopt);
+ auto r = client.room(room.roomId);
+ REQUIRE(r.localReadMarker().get() == tl[0].id());
+}
+
TEST_CASE("Posting receipts", "[client][room][receipt]")
{
auto r = makeRoom();
@@ -217,6 +236,25 @@
});
}
+static auto pushRules = PushRulesDesc(Event(R"({
+ "type": "m.push_rules",
+ "content": {
+ "global": {
+ "override": [{
+ "rule_id": "moe.kazv.mxc.some-rule",
+ "default": true,
+ "enabled": true,
+ "conditions": [{
+ "kind": "event_match",
+ "key": "type",
+ "pattern": "moe.kazv.mxc.test-event"
+ }],
+ "actions": ["notify"]
+ }]
+ }
+ }
+})"_json));
+
TEST_CASE("AddLocalNotificationsAction", "[client][room][receipt]")
{
auto myUserId = "@mew:example.com"s;
@@ -243,24 +281,7 @@
WHEN("push rules notifies some") {
auto next = RoomModel::update(r, AddLocalNotificationsAction{
newEvents,
- PushRulesDesc(Event(R"({
- "type": "m.push_rules",
- "content": {
- "global": {
- "override": [{
- "rule_id": "moe.kazv.mxc.some-rule",
- "default": true,
- "enabled": true,
- "conditions": [{
- "kind": "event_match",
- "key": "type",
- "pattern": "moe.kazv.mxc.test-event"
- }],
- "actions": ["notify"]
- }]
- }
- }
-})"_json)),
+ pushRules,
myUserId,
});
REQUIRE(
@@ -271,6 +292,48 @@
}
);
}
+
+ WHEN("push rules notifies some but is before local read marker, only things after local read marker is added") {
+ r.localReadMarker = newEvents[3].id();
+ auto next = RoomModel::update(r, AddLocalNotificationsAction{
+ newEvents,
+ pushRules,
+ myUserId,
+ });
+
+ REQUIRE(
+ next.unreadNotificationEventIds
+ == immer::flex_vector<std::string>{
+ newEvents[4].id(),
+ }
+ );
+ }
+}
+
+TEST_CASE("PostReceiptAction should update local read marker and remove read notifications")
+{
+ auto myUserId = "@mew:example.com"s;
+
+ auto newEvents = EventList{
+ makeEvent(withEventType("moe.kazv.mxc.test-event")),
+ makeEvent(withEventType("moe.kazv.mxc.test-event")),
+ makeEvent(withEventType("moe.kazv.mxc.test-event")),
+ };
+ auto r = makeRoom(withRoomTimeline(newEvents));
+ r.readReceipts = {{myUserId, ReadReceipt{newEvents[0].id(), 0}}};
+ r = RoomModel::update(r, AddLocalNotificationsAction{
+ newEvents,
+ pushRules,
+ myUserId,
+ });
+
+ auto client = makeClient(withRoom(r));
+ client.userId = myUserId;
+ auto receiptPos = newEvents[1].id();
+ auto [next, _] = ClientModel::update(client, PostReceiptAction{r.roomId, receiptPos});
+ auto nextRoom = next.roomList.rooms.at(r.roomId);
+ REQUIRE(nextRoom.localReadMarker == receiptPos);
+ REQUIRE(nextRoom.unreadNotificationEventIds == immer::flex_vector<std::string>{newEvents[2].id()});
}
TEST_CASE("RemoveReadLocalNotificationsAction", "[client][room][receipt]")
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 25, 8:51 AM (10 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39920
Default Alt Text
D204.1732553473.diff (9 KB)
Attached To
Mode
D204: Update local read marker before posting read receipt
Attached
Detach File
Event Timeline
Log In to Comment