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

Use only latest profiles in the room timeline (labs) #5733

Merged
merged 8 commits into from
Mar 7, 2022
1 change: 1 addition & 0 deletions Config/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ final class BuildSettings: NSObject {
static var isRoomScreenEnableMessageBubblesByDefault: Bool {
return self.roomScreenTimelineDefaultStyleIdentifier == .bubble
}
static let roomScreenUseOnlyLatestProfiles: Bool = false
aringenbach marked this conversation as resolved.
Show resolved Hide resolved

/// Allow split view detail view stacking
static let allowSplitViewDetailsScreenStacking: Bool = true
Expand Down
1 change: 1 addition & 0 deletions Riot/Assets/en.lproj/Vector.strings
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ Tap the + to start adding people.";
"settings_labs_enable_ringing_for_group_calls" = "Ring for group calls";
"settings_labs_enabled_polls" = "Polls";
"settings_labs_enable_threads" = "Threaded messaging";
"settings_labs_use_only_latest_profiles" = "Use only latest profiles";
aringenbach marked this conversation as resolved.
Show resolved Hide resolved

"settings_version" = "Version %@";
"settings_olm_version" = "Olm Version %@";
Expand Down
4 changes: 4 additions & 0 deletions Riot/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4935,6 +4935,10 @@ public class VectorL10n: NSObject {
public static var settingsLabsMessageReaction: String {
return VectorL10n.tr("Vector", "settings_labs_message_reaction")
}
/// Use only latest profiles
public static var settingsLabsUseOnlyLatestProfiles: String {
return VectorL10n.tr("Vector", "settings_labs_use_only_latest_profiles")
}
/// LINKS
public static var settingsLinks: String {
return VectorL10n.tr("Vector", "settings_links")
Expand Down
10 changes: 9 additions & 1 deletion Riot/Managers/Settings/RiotSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,18 @@ final class RiotSettings: NSObject {

@UserDefault(key: "roomScreenEnableMessageBubbles", defaultValue: BuildSettings.isRoomScreenEnableMessageBubblesByDefault, storage: defaults)
var roomScreenEnableMessageBubbles

var roomTimelineStyleIdentifier: RoomTimelineStyleIdentifier {
return self.roomScreenEnableMessageBubbles ? .bubble : .plain
}

/// A setting used to display the latest known profile (display name + avatar) in the timeline
/// for the sender, rather than the profile at the time of the event.
///
/// Note: this is set up from Room perspective, which means that if a user updates their profile after
/// leaving a Room, it will show up the latest profile used in the Room rather than the latest overall.
@UserDefault(key: "roomScreenUseOnlyLatestProfiles", defaultValue: BuildSettings.roomScreenUseOnlyLatestProfiles, storage: defaults)
var roomScreenUseOnlyLatestProfiles

// MARK: - Room Contextual Menu

Expand Down
35 changes: 29 additions & 6 deletions Riot/Modules/MatrixKit/Models/Room/MXKRoomBubbleCellData.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

#import "MXKTools.h"

#import "GeneratedInterface-Swift.h"

@implementation MXKRoomBubbleCellData
@synthesize senderId, targetId, roomId, senderDisplayName, senderAvatarUrl, senderAvatarPlaceholder, targetDisplayName, targetAvatarUrl, targetAvatarPlaceholder, isEncryptedRoom, isPaginationFirstBubble, shouldHideSenderInformation, date, isIncoming, isAttachmentWithThumbnail, isAttachmentWithIcon, attachment, senderFlair;
@synthesize textMessage, attributedTextMessage, attributedTextMessageWithoutPositioningSpace;
Expand All @@ -35,12 +37,12 @@ @implementation MXKRoomBubbleCellData

#pragma mark - MXKRoomBubbleCellDataStoring

- (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomState andRoomDataSource:(MXKRoomDataSource *)roomDataSource2
- (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomState andRoomDataSource:(MXKRoomDataSource *)roomDataSource
{
self = [self init];
if (self)
{
roomDataSource = roomDataSource2;
self->roomDataSource = roomDataSource;

// Initialize read receipts
self.readReceipts = [NSMutableDictionary dictionary];
Expand All @@ -55,12 +57,14 @@ - (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomS
senderId = event.sender;
targetId = [event.type isEqualToString:kMXEventTypeStringRoomMember] ? event.stateKey : nil;
roomId = roomDataSource.roomId;
senderDisplayName = [roomDataSource.eventFormatter senderDisplayNameForEvent:event withRoomState:roomState];
senderAvatarUrl = [roomDataSource.eventFormatter senderAvatarUrlForEvent:event withRoomState:roomState];

MXRoomState *profileRoomState = RiotSettings.shared.roomScreenUseOnlyLatestProfiles ? roomDataSource.roomState : roomState;
aringenbach marked this conversation as resolved.
Show resolved Hide resolved
[self setRoomState:profileRoomState];
senderAvatarPlaceholder = nil;
targetDisplayName = [roomDataSource.eventFormatter targetDisplayNameForEvent:event withRoomState:roomState];
targetAvatarUrl = [roomDataSource.eventFormatter targetAvatarUrlForEvent:event withRoomState:roomState];
targetAvatarPlaceholder = nil;

// Encryption status should always rely on the `MXRoomState`
// from the event rather than the latest.
isEncryptedRoom = roomState.isEncrypted;
isIncoming = ([event.sender isEqualToString:roomDataSource.mxSession.myUser.userId] == NO);

Expand Down Expand Up @@ -103,6 +107,25 @@ - (void)dealloc
bubbleComponents = nil;
}

- (void)setRoomState:(MXRoomState *)roomState;
{
MXEvent* firstEvent = self.events.firstObject;

if (firstEvent == nil || roomState == nil)
{
return;
}

senderDisplayName = [roomDataSource.eventFormatter senderDisplayNameForEvent:firstEvent
withRoomState:roomState];
senderAvatarUrl = [roomDataSource.eventFormatter senderAvatarUrlForEvent:firstEvent
withRoomState:roomState];
targetDisplayName = [roomDataSource.eventFormatter targetDisplayNameForEvent:firstEvent
withRoomState:roomState];
targetAvatarUrl = [roomDataSource.eventFormatter targetAvatarUrlForEvent:firstEvent
withRoomState:roomState];
}

- (NSUInteger)updateEvent:(NSString *)eventId withEvent:(MXEvent *)event
{
NSUInteger count = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,15 @@
*/
- (instancetype)initWithEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState andRoomDataSource:(MXKRoomDataSource*)roomDataSource;

/**
Sets the `MXRoomState` for a buble cell. This allows to adapt the display
of a cell with a different room state than its historical. This won't update critical
flag/status, such as `isEncryptedRoom`.

@param roomState the `MXRoomState` to use for this cell.
*/
- (void)setRoomState:(MXRoomState *)roomState;

/**
Update the event because its sent state changed or it is has been redacted.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@

#import "MXKRoomBubbleCellDataWithAppendingMode.h"

#import "GeneratedInterface-Swift.h"

static NSAttributedString *messageSeparator = nil;

@implementation MXKRoomBubbleCellDataWithAppendingMode

#pragma mark - MXKRoomBubbleCellDataStoring

- (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomState andRoomDataSource:(MXKRoomDataSource *)roomDataSource2
- (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomState andRoomDataSource:(MXKRoomDataSource *)roomDataSource
{
self = [super initWithEvent:event andRoomState:roomState andRoomDataSource:roomDataSource2];
self = [super initWithEvent:event andRoomState:roomState andRoomDataSource:roomDataSource];
if (self)
{
// Set default settings
Expand All @@ -46,8 +48,9 @@ - (BOOL)addEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState
}

// Check sender information
NSString *eventSenderName = [roomDataSource.eventFormatter senderDisplayNameForEvent:event withRoomState:roomState];
NSString *eventSenderAvatar = [roomDataSource.eventFormatter senderAvatarUrlForEvent:event withRoomState:roomState];
MXRoomState *profileRoomState = RiotSettings.shared.roomScreenUseOnlyLatestProfiles ? roomDataSource.roomState : roomState;
NSString *eventSenderName = [roomDataSource.eventFormatter senderDisplayNameForEvent:event withRoomState:profileRoomState];
NSString *eventSenderAvatar = [roomDataSource.eventFormatter senderAvatarUrlForEvent:event withRoomState:profileRoomState];
if ((self.senderDisplayName || eventSenderName) &&
([self.senderDisplayName isEqualToString:eventSenderName] == NO))
{
Expand Down
26 changes: 26 additions & 0 deletions Riot/Modules/MatrixKit/Models/Room/MXKRoomDataSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#import "MXKSendReplyEventStringLocalizer.h"
#import "MXKSlashCommands.h"

#import "GeneratedInterface-Swift.h"

const BOOL USE_THREAD_TIMELINE = YES;

#pragma mark - Constant definitions
Expand Down Expand Up @@ -1009,6 +1011,11 @@ - (void)refreshEventListeners:(NSArray *)liveEventTypesFilterForMessages
liveEventsListener = [_timeline listenToEventsOfTypes:liveEventTypesFilterForMessages onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {

MXStrongifyAndReturnIfNil(self);

if (event.eventType == MXEventTypeRoomMember && event.isUserProfileChange)
{
[self refreshProfilesIfNeeded];
}

if (MXTimelineDirectionForwards == direction)
{
Expand Down Expand Up @@ -4321,4 +4328,23 @@ - (void)virtualRoomsDidChange:(NSNotification *)notification
self.secondaryRoomId = [self.mxSession virtualRoomOf:self.roomId];
}

#pragma mark - Use Only Latest Profiles

/**
Refreshes the avatars and display names if needed. This has no effect
if `roomScreenUseOnlyLatestProfiles` is disabled.
*/
- (void)refreshProfilesIfNeeded
{
if (RiotSettings.shared.roomScreenUseOnlyLatestProfiles)
{
@synchronized (bubbles) {
for (id<MXKRoomBubbleCellDataStoring> bubble in bubbles)
{
[bubble setRoomState:self.roomState];
}
}
}
}

@end
4 changes: 2 additions & 2 deletions Riot/Modules/Room/CellData/RoomBubbleCellData.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ - (instancetype)init
return self;
}

- (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomState andRoomDataSource:(MXKRoomDataSource *)roomDataSource2
- (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomState andRoomDataSource:(MXKRoomDataSource *)roomDataSource
{
self = [super initWithEvent:event andRoomState:roomState andRoomDataSource:roomDataSource2];
self = [super initWithEvent:event andRoomState:roomState andRoomDataSource:roomDataSource];

if (self)
{
Expand Down
6 changes: 3 additions & 3 deletions Riot/Modules/Room/Search/DataSources/RoomSearchDataSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ @interface RoomSearchDataSource ()

@implementation RoomSearchDataSource

- (instancetype)initWithRoomDataSource:(MXKRoomDataSource *)roomDataSource2
- (instancetype)initWithRoomDataSource:(MXKRoomDataSource *)roomDataSource
{
self = [super initWithMatrixSession:roomDataSource2.mxSession];
self = [super initWithMatrixSession:roomDataSource.mxSession];
if (self)
{
roomDataSource = roomDataSource2;
self->roomDataSource = roomDataSource;

// The messages search is limited to the room data.
self.roomEventFilter.rooms = @[roomDataSource.roomId];
Expand Down
21 changes: 20 additions & 1 deletion Riot/Modules/Settings/SettingsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ typedef NS_ENUM(NSUInteger, LABS_ENABLE)
{
LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX = 0,
LABS_ENABLE_THREADS_INDEX,
LABS_ENABLE_MESSAGE_BUBBLES_INDEX
LABS_ENABLE_MESSAGE_BUBBLES_INDEX,
LABS_USE_ONLY_LATEST_PROFILES_INDEX
};

typedef NS_ENUM(NSUInteger, SECURITY)
Expand Down Expand Up @@ -572,6 +573,7 @@ - (void)updateSections
[sectionLabs addRowWithTag:LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX];
[sectionLabs addRowWithTag:LABS_ENABLE_THREADS_INDEX];
[sectionLabs addRowWithTag:LABS_ENABLE_MESSAGE_BUBBLES_INDEX];
[sectionLabs addRowWithTag:LABS_USE_ONLY_LATEST_PROFILES_INDEX];
sectionLabs.headerTitle = [VectorL10n settingsLabs];
if (sectionLabs.hasAnyRows)
{
Expand Down Expand Up @@ -2462,6 +2464,18 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
{
cell = [self buildMessageBubblesCellForTableView:tableView atIndexPath:indexPath];
}
else if (row == LABS_USE_ONLY_LATEST_PROFILES_INDEX)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];

labelAndSwitchCell.mxkLabel.text = VectorL10n.settingsLabsUseOnlyLatestProfiles;
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.roomScreenUseOnlyLatestProfiles;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;

[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleUseOnlyLatestProfiles:) forControlEvents:UIControlEventTouchUpInside];

cell = labelAndSwitchCell;
}
}
else if (section == SECTION_TAG_FLAIR)
{
Expand Down Expand Up @@ -3244,6 +3258,11 @@ - (void)toggleCommunityFlair:(UISwitch *)sender
}
}

- (void)toggleUseOnlyLatestProfiles:(UISwitch *)sender
{
RiotSettings.shared.roomScreenUseOnlyLatestProfiles = sender.isOn;
}

- (void)markAllAsRead:(id)sender
{
// Feedback: disable button and run activity indicator
Expand Down
6 changes: 4 additions & 2 deletions Riot/Utils/EventFormatter.m
Original file line number Diff line number Diff line change
Expand Up @@ -425,10 +425,12 @@ - (NSString*)senderAvatarUrlForEvent:(MXEvent*)event withRoomState:(MXRoomState*

// Check whether this avatar url is updated by the current event (This happens in case of new joined member)
NSString* membership = event.content[@"membership"];
if (membership && [membership isEqualToString:@"join"] && [event.content[@"avatar_url"] length])
NSString* eventAvatarUrl = event.content[@"avatar_url"];
NSString* prevEventAvatarUrl = event.prevContent[@"avatar_url"];
if (membership && [membership isEqualToString:@"join"] && [eventAvatarUrl length] && ![eventAvatarUrl isEqualToString:prevEventAvatarUrl])
{
// Use the actual avatar
senderAvatarUrl = event.content[@"avatar_url"];
senderAvatarUrl = eventAvatarUrl;
}

// We ignore non mxc avatar url (The identicons are removed here).
Expand Down
1 change: 1 addition & 0 deletions changelog.d/5726.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Labs/Room: Add a setting to use only latest sender profiles