Skip to content

Commit

Permalink
Merge pull request #7809 from element-hq/mauroromito/room_retention_i…
Browse files Browse the repository at this point in the history
…mplementation

Room retention event implementation
  • Loading branch information
Velin92 authored Jun 14, 2024
2 parents 55ec6f1 + 05f5519 commit 3b776f3
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 7 deletions.
12 changes: 6 additions & 6 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ PODS:
- LoggerAPI (1.9.200):
- Logging (~> 1.1)
- Logging (1.4.0)
- MatrixSDK (0.27.8):
- MatrixSDK/Core (= 0.27.8)
- MatrixSDK/Core (0.27.8):
- MatrixSDK (0.27.9):
- MatrixSDK/Core (= 0.27.9)
- MatrixSDK/Core (0.27.9):
- AFNetworking (~> 4.0.0)
- GZIP (~> 1.3.0)
- libbase58 (~> 0.1.4)
- MatrixSDKCrypto (= 0.4.2)
- OLMKit (~> 3.2.5)
- Realm (= 10.27.0)
- SwiftyBeaver (= 1.9.5)
- MatrixSDK/JingleCallStack (0.27.8):
- MatrixSDK/JingleCallStack (0.27.9):
- JitsiMeetSDKLite (= 8.1.2-lite)
- MatrixSDK/Core
- MatrixSDKCrypto (0.4.2)
Expand Down Expand Up @@ -187,7 +187,7 @@ SPEC CHECKSUMS:
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
Logging: beeb016c9c80cf77042d62e83495816847ef108b
MatrixSDK: 4c5a8572a481340ab233451ad36c1322d371fae5
MatrixSDK: 246fd1d3620afcbf8cb76794e9343ebf3cbf881b
MatrixSDKCrypto: 736069ee0a5ec12852ab3498bf2242acecc443fc
OLMKit: da115f16582e47626616874e20f7bb92222c7a51
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
Expand All @@ -209,4 +209,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: b622ffadc1a0fe5442787bd9023ca3d110384814

COCOAPODS: 1.15.2
COCOAPODS: 1.14.3
58 changes: 58 additions & 0 deletions Riot/Categories/MXRoomSummary.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// Copyright 2024 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

extension Notification.Name {
static let roomSummaryDidRemoveExpiredDataFromStore = Notification.Name(MXRoomSummary.roomSummaryDidRemoveExpiredDataFromStore)
}

extension MXRoomSummary {
@objc static let roomSummaryDidRemoveExpiredDataFromStore = "roomSummaryDidRemoveExpiredDataFromStore"
@objc static let roomRetentionStateEventType = "m.room.retention"

private enum Constants {
static let roomRetentionInDaysKey = "roomRetentionInDays"
}
/// Get the room messages retention period in days
func roomRetentionPeriodInDays() -> uint {
if let period = self.others[Constants.roomRetentionInDaysKey] as? uint {
return period
} else {
return 365
}
}

/// Get the timestamp below which the received messages must be removed from the store, and the display
@objc func minimumTimestamp() -> UInt64 {
let periodInMs = Tools.durationInMs(fromDays: self.roomRetentionPeriodInDays())
let currentTs = (UInt64)(Date().timeIntervalSince1970 * 1000)
return (currentTs - periodInMs)
}

/// Remove the expired messages from the store.
/// If some data are removed, this operation posts the notification: roomSummaryDidRemoveExpiredDataFromStore.
/// This operation does not commit the potential change. We let the caller trigger the commit when this is the more suitable.
///
/// Provide a boolean telling whether some data have been removed.
@objc func removeExpiredRoomContentsFromStore() -> Bool {
let ret = self.mxSession.store.removeAllMessagesSent(before: self.minimumTimestamp(), inRoom: roomId)
if ret {
NotificationCenter.default.post(name: .roomSummaryDidRemoveExpiredDataFromStore, object: self)
}
return ret
}
}
12 changes: 12 additions & 0 deletions Riot/Categories/MXSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,16 @@ extension MXSession {
matrixItemId: userId,
displayName: user?.displayname)
}

/// Clean the storage of a session by removing the expired contents.
@objc func removeExpiredMessages() {
var hasStoreChanged = false
for room in self.rooms {
hasStoreChanged = hasStoreChanged || room.summary.removeExpiredRoomContentsFromStore()
}

if hasStoreChanged {
self.store.commit?()
}
}
}
3 changes: 3 additions & 0 deletions Riot/Modules/Application/LegacyAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,9 @@ - (void)initMatrixSessions
[self registerNewRequestNotificationForSession:mxSession];

[self.pushNotificationService checkPushKitPushersInSession:mxSession];

// Clean the storage by removing expired data
[mxSession removeExpiredMessages];
}
else if (mxSession.state == MXSessionStateRunning)
{
Expand Down
106 changes: 106 additions & 0 deletions Riot/Modules/Room/DataSources/RoomDataSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ @interface RoomDataSource() <RoomReactionsViewModelDelegate, URLPreviewViewDeleg
{
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;

// The listener to the room retention changes.
id retentionListener;
}

// Observe key verification request changes
Expand Down Expand Up @@ -167,6 +170,31 @@ - (void)updateEventFormatter
self.eventFormatter.eventTypesFilterForMessages = [MXKAppSettings standardAppSettings].eventsFilterForMessages;
}

- (void)setDelegate:(id<MXKDataSourceDelegate>)delegate
{
[self unregisterRoomSummaryDidRemoveExpiredDataFromStoreNotifications];
[self removeRoomRetentionEventListener];

if (delegate && self.isLive)
{
if (self.room)
{
// Remove the potential expired messages from the store
if ([self.room.summary removeExpiredRoomContentsFromStore])
{
[self.mxSession.store commit];
}
[self addRoomRetentionEventListener];
}

// Observe room history flush (expired content data)
[self registerRoomSummaryDidRemoveExpiredDataFromStoreNotifications];
[self roomSummaryDidRemoveExpiredDataFromStore];
}

[super setDelegate:delegate];
}

- (void)destroy
{
if (kThemeServiceDidChangeThemeNotificationObserver)
Expand Down Expand Up @@ -197,6 +225,9 @@ - (void)destroy
[self.mxSession.aggregations.beaconAggregations removeListener:self.beaconInfoSummaryDeletionListener];
}

[self unregisterRoomSummaryDidRemoveExpiredDataFromStoreNotifications];
[self removeRoomRetentionEventListener];

[super destroy];
}

Expand Down Expand Up @@ -1242,4 +1273,79 @@ - (void)updateCurrentUserLocationSharingStatus
}
}

#pragma mark - roomSummaryDidRemoveExpiredDataFromStore notifications

- (void)registerRoomSummaryDidRemoveExpiredDataFromStoreNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(roomSummaryDidRemoveExpiredDataFromStore:) name:MXRoomSummary.roomSummaryDidRemoveExpiredDataFromStore object:nil];
}

- (void)unregisterRoomSummaryDidRemoveExpiredDataFromStoreNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:MXRoomSummary.roomSummaryDidRemoveExpiredDataFromStore object:nil];
}

- (void)roomSummaryDidRemoveExpiredDataFromStore:(NSNotification*)notification
{
MXRoomSummary *roomSummary = notification.object;
if (self.mxSession == roomSummary.mxSession && [self.roomId isEqualToString:roomSummary.roomId])
{
[self roomSummaryDidRemoveExpiredDataFromStore];
}
}

- (void)roomSummaryDidRemoveExpiredDataFromStore
{
// Check whether the first cell data refers to an expired event (this may be a state event
MXEvent *firstMessageEvent;
for (id<MXKRoomBubbleCellDataStoring> cellData in bubbles)
{
for (MXEvent *event in cellData.events)
{
if (!event.isState) {
firstMessageEvent = event;
break;
}
}

if (firstMessageEvent)
{
break;
}
}

if (firstMessageEvent && firstMessageEvent.originServerTs < self.room.summary.minimumTimestamp)
{
[self reload];
}
}

#pragma mark - room retention event listener

- (void)addRoomRetentionEventListener
{
// Register a listener to handle the room retention in live timelines
retentionListener = [self.timeline listenToEventsOfTypes:@[MXRoomSummary.roomRetentionStateEventType] onEvent:^(MXEvent *redactionEvent, MXTimelineDirection direction, MXRoomState *roomState) {

// Consider only live events
if (direction == MXTimelineDirectionForwards)
{
// Remove the potential expired messages from the store
if ([self.room.summary removeExpiredRoomContentsFromStore])
{
[self.mxSession.store commit];
}
}
}];
}

- (void)removeRoomRetentionEventListener
{
if (retentionListener)
{
[self.timeline removeListener:retentionListener];
retentionListener = nil;
}
}

@end
13 changes: 13 additions & 0 deletions Riot/Utils/Tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,17 @@
*/
+ (NSURL*)fixURLWithSeveralHashKeys:(NSURL*)url;

#pragma mark - Time utilities

/**
* Convert a number of days to a duration in ms.
*/
+ (uint64_t)durationInMsFromDays:(uint)days;

/**
* Convert a duration in ms to a number of days.
*/
+ (uint)numberOfDaysFromDurationInMs:(uint64_t)duration;


@end
12 changes: 12 additions & 0 deletions Riot/Utils/Tools.m
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,16 @@ + (NSURL *)fixURLWithSeveralHashKeys:(NSURL *)url
return fixedURL;
}

#pragma mark - Time utilities

+ (uint64_t)durationInMsFromDays:(uint)days
{
return days * (uint64_t)(86400000);
}

+ (uint)numberOfDaysFromDurationInMs:(uint64_t)duration
{
return (uint)(duration / 86400000);
}

@end

0 comments on commit 3b776f3

Please sign in to comment.