Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Room retention event implementation #7809

Merged
merged 3 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading