Page MenuHomePhorge

D204.1732981336.diff
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

D204.1732981336.diff

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

Mime Type
text/plain
Expires
Sat, Nov 30, 7:42 AM (21 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41633
Default Alt Text
D204.1732981336.diff (9 KB)

Event Timeline