Skip to content

Commit

Permalink
Room::getEvent()
Browse files Browse the repository at this point in the history
You can now request an arbitrary event in the room, and save it for
future use (within a single running session, same as timeline).
  • Loading branch information
KitsuneRal committed Dec 15, 2024
1 parent 913c693 commit 56b5ab3
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 6 deletions.
45 changes: 39 additions & 6 deletions Quotient/room.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ class Q_DECL_HIDDEN Room::Private {

Timeline timeline;
PendingEvents unsyncedEvents;
std::unordered_map<EventId, event_ptr_tt<const RoomEvent>> cachedEvents;
std::unordered_map<EventId, std::vector<EventPromise>> cachedEventPromises;
QHash<QString, TimelineItem::index_t> eventsIndex;
// A map from event id/relation type pairs to a vector of event pointers. Not using QMultiHash,
// because we want to quickly return a number of relations for a given event without enumerating
Expand Down Expand Up @@ -258,11 +260,23 @@ class Q_DECL_HIDDEN Room::Private {
return changes;
}
void addRelation(const ReactionEvent& reactionEvt);
void addRelations(auto from, auto to)
void finishEventPromises(const RoomEvent& evt)
{
for (auto it = from; it != to; ++it)
if (const auto* reaction = it->template viewAs<ReactionEvent>())
addRelation(*reaction);
if (auto it = cachedEventPromises.find(evt.id()); it != cachedEventPromises.cend()) {
for (auto& p : it->second) {
p.addResult(std::cref(evt));
p.finish();
}
cachedEventPromises.erase(it);
}
}
void afterAddedMessages(auto from, auto to)
{
for (const auto& ti : std::ranges::subrange(from, to)) {
switchOnType(*ti, std::bind_front(&Private::addRelation, this));
finishEventPromises(*ti);
cachedEvents.erase(ti->id());
}
}

Changes addNewMessageEvents(RoomEvents&& events);
Expand Down Expand Up @@ -2382,6 +2396,25 @@ JobHandle<GetRoomEventsJob> Room::getPreviousContent(int limit, const QString& f
return d->getPreviousContent(limit, filter);
}

EventFuture Room::getEvent(const QString& eventId)
{
if (auto timelineIt = findInTimeline(eventId); timelineIt != historyEdge())
return makeReadyValueFuture(std::cref(**timelineIt));
auto baseStateObjects = d->baseState | std::views::values;
if (auto stateIt = std::ranges::find(baseStateObjects, eventId, &RoomEvent::id);
stateIt != end(baseStateObjects))
return makeReadyValueFuture(std::cref<RoomEvent>(**stateIt));
if (auto cachedIt = d->cachedEvents.find(eventId); cachedIt != d->cachedEvents.end())
return makeReadyValueFuture(std::cref(*cachedIt->second));

connection()->callApi<GetOneRoomEventJob>(id(), eventId).then([this](RoomEventPtr&& pEvt) {
const auto [it, cachedEventInserted] = d->cachedEvents.emplace(pEvt->id(), std::move(pEvt));
QUO_CHECK(cachedEventInserted);
d->finishEventPromises(*(it->second));
});
return d->cachedEventPromises[eventId].emplace_back().future();
}

EventFuture Room::ensureEvent(const QString& eventId, quint16 maxWaitSeconds)
{
if (auto eventIt = findInTimeline(eventId); eventIt != historyEdge())
Expand Down Expand Up @@ -3058,7 +3091,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
}

if (totalInserted > 0) {
addRelations(from, syncEdge());
afterAddedMessages(from, syncEdge());

qCDebug(MESSAGES) << "Room" << q->objectName() << "received"
<< totalInserted << "new events; the last event is now"
Expand Down Expand Up @@ -3117,7 +3150,7 @@ std::pair<Room::Changes, Room::rev_iter_t> Room::Private::addHistoricalMessageEv
q->onAddHistoricalTimelineEvents(from);
emit q->addedMessages(timeline.front().index(), from->index());

addRelations(from, historyEdge());
afterAddedMessages(from, historyEdge());
Q_ASSERT(timeline.size() == timelineSize + insertedSize);
if (insertedSize > 9 || et.nsecsElapsed() >= ProfilerMinNsecs)
qCDebug(PROFILER) << "Added" << insertedSize << "historical event(s) to" << q->objectName()
Expand Down
9 changes: 9 additions & 0 deletions Quotient/room.h
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,15 @@ class QUOTIENT_API Room : public QObject {

QJsonArray exportMegolmSessions();

//! \brief Obtain an arbitrary room event by its id
//!
//! Looks through the timeline, state events that arrived out of the timeline, and finally
//! cached individual events; if the event is not found locally, requests this one event from
//! the homeserver.
//! \return a ready future with the event reference if an event with \p eventId is found locally;
//! otherwise, a running future connected to the homeserver request
EventFuture getEvent(const QString& eventId);

//! \brief Loads the message history until the specified event id is found
//!
//! This is potentially heavy; clients should use this sparingly. One intended use case is
Expand Down

0 comments on commit 56b5ab3

Please sign in to comment.