@@ -343,11 +343,10 @@ class Q_DECL_HIDDEN Room::Private {
343343
344344 /* ! Apply redaction to the timeline
345345 *
346- * Tries to find an event in the timeline and redact it; deletes the
347- * redaction event whether the redacted event was found or not.
348- * \return true if the event has been found and redacted; false otherwise
346+ * Tries to find events in the timeline and redact them.
347+ * \return the list of event ids that were NOT found and redacted
349348 */
350- bool processRedaction (const RedactionEvent& redaction);
349+ QStringList processRedaction (const RedactionEvent& redaction);
351350
352351 /* ! Apply a new revision of the event to the timeline
353352 *
@@ -2836,59 +2835,67 @@ RoomEventPtr makeRedacted(const RoomEvent& target,
28362835 return loadEvent<RoomEvent>(originalJson);
28372836}
28382837
2839- bool Room::Private::processRedaction (const RedactionEvent& redaction)
2838+ QStringList Room::Private::processRedaction (const RedactionEvent& redaction)
28402839{
2840+ QStringList unredactedIds;
28412841 // Can't use findInTimeline because it returns a const iterator, and
28422842 // we need to change the underlying TimelineItem.
2843- const auto pIdx = eventsIndex.constFind (redaction.redactedEvent ());
2844- if (pIdx == eventsIndex.cend ())
2845- return false ;
2846-
2847- Q_ASSERT (q->isValidIndex (*pIdx));
2843+ const auto & eventIds = redaction.redactedEvents ();
2844+ for (const auto & evtId: eventIds) {
2845+ const auto pIdx = eventsIndex.constFind (evtId);
2846+ if (pIdx == eventsIndex.cend ()) {
2847+ unredactedIds.push_back (evtId);
2848+ continue ;
2849+ }
28482850
2849- auto & ti = timeline[Timeline::size_type (*pIdx - q->minTimelineIndex ())];
2850- if (ti->isRedacted () && ti->redactedBecause ()->id () == redaction.id ()) {
2851- qCDebug (EVENTS) << " Redaction" << redaction.id () << " of event"
2852- << ti->id () << " already done, skipping" ;
2853- return true ;
2854- }
2855- if (ti->is <RoomMessageEvent>())
2856- FileMetadataMap::remove (id, ti->id ());
2851+ Q_ASSERT (q->isValidIndex (*pIdx));
28572852
2858- // Make a new event from the redacted JSON and put it in the timeline
2859- // instead of the redacted one. oldEvent will be deleted on return.
2860- auto oldEvent = ti.replaceEvent (makeRedacted (*ti, redaction));
2861- qCDebug (EVENTS) << " Redacted" << oldEvent->id () << " with" << redaction.id ();
2862- if (oldEvent->isStateEvent ()) {
2863- // Check whether the old event was a part of current state; if it was,
2864- // update the current state to the redacted event object.
2865- const auto currentStateEvt =
2866- currentState.get (oldEvent->matrixType (), oldEvent->stateKey ());
2867- Q_ASSERT (currentStateEvt);
2868- if (currentStateEvt == oldEvent.get ()) {
2869- // Historical states can't be in currentState
2870- Q_ASSERT (ti.index () >= 0 );
2871- qCDebug (STATE).nospace ()
2872- << " Redacting state " << oldEvent->matrixType () << " /"
2873- << oldEvent->stateKey ();
2874- // Retarget the current state to the newly made event.
2875- if (q->processStateEvent (*ti))
2876- emit q->namesChanged (q);
2877- updateDisplayname ();
2853+ auto & ti = timeline[Timeline::size_type (*pIdx - q->minTimelineIndex ())];
2854+ if (ti->isRedacted () && ti->redactedBecause ()->id () == redaction.id ()) {
2855+ qCDebug (EVENTS) << " Redaction" << redaction.id () << " of event"
2856+ << ti->id () << " already done, skipping" ;
2857+ continue ;
28782858 }
2879- }
2880- if (const auto * reaction = eventCast<ReactionEvent>(oldEvent)) {
2881- const auto & content = reaction->content ().value ;
2882- const std::pair lookupKey { content.eventId , content.type };
2883- if (relations.contains (lookupKey)) {
2884- relations[lookupKey].removeOne (reaction);
2885- emit q->updatedEvent (content.eventId );
2859+ if (ti->is <RoomMessageEvent>())
2860+ FileMetadataMap::remove (id, ti->id ());
2861+
2862+
2863+ // Make a new event from the redacted JSON and put it in the timeline
2864+ // instead of the redacted one. oldEvent will be deleted on return.
2865+ auto oldEvent = ti.replaceEvent (makeRedacted (*ti, redaction));
2866+ qCDebug (EVENTS) << " Redacted" << oldEvent->id () << " with"
2867+ << redaction.id ();
2868+ if (oldEvent->isStateEvent ()) {
2869+ // Check whether the old event was a part of current state; if it was,
2870+ // update the current state to the redacted event object.
2871+ const auto currentStateEvt =
2872+ currentState.get (oldEvent->matrixType (), oldEvent->stateKey ());
2873+ Q_ASSERT (currentStateEvt);
2874+ if (currentStateEvt == oldEvent.get ()) {
2875+ // Historical states can't be in currentState
2876+ Q_ASSERT (ti.index () >= 0 );
2877+ qCDebug (STATE).nospace ()
2878+ << " Redacting state " << oldEvent->matrixType () << " /"
2879+ << oldEvent->stateKey ();
2880+ // Retarget the current state to the newly made event.
2881+ if (q->processStateEvent (*ti))
2882+ emit q->namesChanged (q);
2883+ updateDisplayname ();
2884+ }
2885+ }
2886+ if (const auto * reaction = eventCast<ReactionEvent>(oldEvent)) {
2887+ const auto & content = reaction->content ().value ;
2888+ const std::pair lookupKey { content.eventId , content.type };
2889+ if (relations.contains (lookupKey)) {
2890+ relations[lookupKey].removeOne (reaction);
2891+ emit q->updatedEvent (conten.eventId );
2892+ }
28862893 }
2894+ q->onRedaction (*oldEvent, *ti);
2895+ emit q->replacedEvent (ti.event (), std::to_address (oldEvent));
2896+ // By now, all references to oldEvent must have been updated to ti.event()
28872897 }
2888- q->onRedaction (*oldEvent, *ti);
2889- emit q->replacedEvent (ti.event (), std::to_address (oldEvent));
2890- // By now, all references to oldEvent must have been updated to ti.event()
2891- return true ;
2898+ return unredactedIds;
28922899}
28932900
28942901/* * Make a replaced event
@@ -3011,19 +3018,22 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events)
30113018 auto it = std::find_if (events.begin (), events.end (), isEditing);
30123019 for (const auto & eptr : RoomEventsRange (it, events.end ())) {
30133020 if (auto * r = eventCast<RedactionEvent>(eptr)) {
3014- // Try to find the target in the timeline, then in the batch.
3015- if (processRedaction (*r))
3016- continue ;
3017- if (auto targetIt = std::find_if (events.begin (), events.end (),
3018- [id = r->redactedEvent ()](const RoomEventPtr& ep) {
3019- return ep->id () == id;
3020- }); targetIt != events.end ())
3021- *targetIt = makeRedacted (**targetIt, *r);
3022- else
3023- qCDebug (STATE)
3024- << " Redaction" << r->id () << " ignored: target event"
3025- << r->redactedEvent () << " is not found" ;
3026- // If the target event comes later, it comes already redacted.
3021+ // Try to find the targets in the timeline, then in the batch.
3022+ const auto unredactedIds = processRedaction (*r);
3023+ for (const auto & idToRedact: unredactedIds) {
3024+ if (auto targetIt =
3025+ std::find_if (events.begin (), it,
3026+ [&idToRedact](const RoomEventPtr& ep) {
3027+ return ep->id () == idToRedact;
3028+ });
3029+ targetIt != it)
3030+ *targetIt = makeRedacted (**targetIt, *r);
3031+ else
3032+ qCDebug (EVENTS)
3033+ << " Target event" << idToRedact << " in redaction"
3034+ << r->id () << " is not found" ;
3035+ // If the target event comes later, it comes already redacted.
3036+ }
30273037 }
30283038 if (auto * msg = eventCast<RoomMessageEvent>(eptr);
30293039 msg && !msg->replacedEvent ().isEmpty ()) {
0 commit comments