diff --git a/Config/AppConfiguration.swift b/Config/AppConfiguration.swift
index 9ae6356e1..d06442e09 100644
--- a/Config/AppConfiguration.swift
+++ b/Config/AppConfiguration.swift
@@ -33,7 +33,8 @@ class AppConfiguration: CommonConfiguration {
// Get modular widget events in rooms histories
MXKAppSettings.standard()?.addSupportedEventTypes([kWidgetMatrixEventTypeString,
- kWidgetModularEventTypeString])
+ kWidgetModularEventTypeString,
+ BwiBuildSettings.bwiUserLabelEventTypeString])
// Hide undecryptable messages that were sent while the user was not in the room
MXKAppSettings.standard()?.hidePreJoinedUndecryptableEvents = true
diff --git a/Riot/Assets/de.lproj/Bwi.strings b/Riot/Assets/de.lproj/Bwi.strings
index 650807a03..680735bc4 100644
--- a/Riot/Assets/de.lproj/Bwi.strings
+++ b/Riot/Assets/de.lproj/Bwi.strings
@@ -37,3 +37,14 @@
"bwi_settings_developer_unrestrict_user" = "Nutzereinschränkung aufheben";
"bwi_mdm_logout_message" = "Die Konfiguration hat sich geändert. Bitte melde dich neu an.";
+
+"bwi_room_member_section_userlabels" = "Funktion in %@";
+"bwi_room_member_userlabels_description" = "Als Administrator kannst du für Personen im Raum eine Funktion hinzufügen und entfernen.\nDiese ist für alle Raummitglieder sichtbar.";
+"bwi_room_member_userlabels_textfield_placeholder" = "Funktion hinzufügen";
+"bwi_room_member_userlabels_button_save" = "Sichern";
+"bwi_room_member_userlabels_button_delete" = "Löschen";
+
+"bwi_timeline_userlabel_removed_for_user" = "für %@ die Funktion entfernt: %@";
+"bwi_timeline_userlabel_added_for_user" = "%@ zur Funktion ernannt: %@";
+"bwi_timeline_userlabel_prefix_you" = "Du hast %@";
+"bwi_timeline_userlabel_prefix_other_user" = "%@ hat %@";
diff --git a/Riot/Assets/en.lproj/Bwi.strings b/Riot/Assets/en.lproj/Bwi.strings
index 54c660494..f59dd36d1 100644
--- a/Riot/Assets/en.lproj/Bwi.strings
+++ b/Riot/Assets/en.lproj/Bwi.strings
@@ -37,3 +37,14 @@
"bwi_settings_developer_unrestrict_user" = "Remove user restriction";
"bwi_mdm_logout_message" = "The configuration has changed. Please reconnect.";
+
+"bwi_room_member_section_userlabels" = "Function in %@";
+"bwi_room_member_userlabels_description" = "As an admin, you can add and remove a function label for a room member. This function label is visible to all room members.";
+"bwi_room_member_userlabels_textfield_placeholder" = "Edit Funktion";
+"bwi_room_member_userlabels_button_save" = "Save";
+"bwi_room_member_userlabels_button_delete" = "Delete";
+
+"bwi_timeline_userlabel_removed_for_user" = "removed for user %@ the function: %@";
+"bwi_timeline_userlabel_added_for_user" = "added for user %@ the function: %@";
+"bwi_timeline_userlabel_prefix_you" = "You %@s";
+"bwi_timeline_userlabel_prefix_other_user" = "%@s";
diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m
index 7218e751c..c4a6a1e29 100644
--- a/Riot/Modules/Authentication/AuthenticationViewController.m
+++ b/Riot/Modules/Authentication/AuthenticationViewController.m
@@ -1554,6 +1554,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
{
// Update custom servers
[self showCustomHomeserver:wellKnown.homeServer.baseUrl andIdentityServer:wellKnown.identityServer.baseUrl];
+ [self setHomeServerTextFieldText:wellKnown.homeServer.baseUrl];
}
}
}
diff --git a/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberDetailsViewController.h b/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberDetailsViewController.h
index 80843ee4a..eb8846c4a 100644
--- a/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberDetailsViewController.h
+++ b/Riot/Modules/MatrixKit/Controllers/MXKRoomMemberDetailsViewController.h
@@ -42,7 +42,9 @@ typedef enum : NSUInteger
MXKRoomMemberDetailsActionMention,
MXKRoomMemberDetailsActionSecurity,
MXKRoomMemberDetailsActionSecurityInformation,
- MXKRoomMemberDetailsActionPermalink
+ MXKRoomMemberDetailsActionPermalink,
+ MXKRoomMemberDetailsActionBwiUserLabelDescription,
+ MXKRoomMemberDetailsActionBwiUserLabel
} MXKRoomMemberDetailsAction;
diff --git a/Riot/Modules/MatrixKit/Models/Contact/MXKContact.h b/Riot/Modules/MatrixKit/Models/Contact/MXKContact.h
index a927577e9..fdf591daa 100644
--- a/Riot/Modules/MatrixKit/Models/Contact/MXKContact.h
+++ b/Riot/Modules/MatrixKit/Models/Contact/MXKContact.h
@@ -87,6 +87,11 @@ extern NSString *const kMXKContactDefaultContactPrefixId;
*/
@property (nonatomic, readonly) NSString* matrixAvatarURL;
+/**
+ bwi Nicknames
+ */
+@property (nonatomic) NSString* bwiUserLabel;
+
/**
Reset the current thumbnail if it is retrieved from a matrix url. May be used in case of the matrix avatar url change.
A new thumbnail will be automatically restored from the contact data.
diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m
index 2e196bc59..174ee5b2b 100644
--- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m
+++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m
@@ -43,6 +43,7 @@ static NSString *const kHTMLATagRegexPattern = @"([^<]*)";
*/
NSDataDetector *linkDetector;
}
+
@end
@implementation MXKEventFormatter
@@ -311,7 +312,7 @@ static NSString *const kHTMLATagRegexPattern = @"([^<]*)";
{
// Check we can output the error
NSParameterAssert(error);
-
+
*error = MXKEventFormatterErrorNone;
// Filter the events according to their type.
diff --git a/Riot/Modules/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextFieldAndButton.m b/Riot/Modules/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextFieldAndButton.m
index 29ef52a56..72ac2fa59 100644
--- a/Riot/Modules/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextFieldAndButton.m
+++ b/Riot/Modules/MatrixKit/Views/MXKTableViewCell/MXKTableViewCellWithTextFieldAndButton.m
@@ -50,7 +50,6 @@
- (IBAction)textFieldEditingChanged:(id)sender
{
- self.mxkButton.enabled = (self.mxkTextField.text.length != 0);
}
@end
diff --git a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.h b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.h
index 18f6bef28..7aa986fd2 100644
--- a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.h
+++ b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.h
@@ -18,6 +18,6 @@
#import "DeviceTableViewCell.h"
-@interface RoomMemberDetailsViewController : MXKRoomMemberDetailsViewController
+@interface RoomMemberDetailsViewController : MXKRoomMemberDetailsViewController
@end
diff --git a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m
index cd3381052..65e6d19de 100644
--- a/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m
+++ b/Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m
@@ -33,7 +33,7 @@
#define TABLEVIEW_ROW_CELL_HEIGHT 46
#define TABLEVIEW_SECTION_HEADER_HEIGHT 28
-@interface RoomMemberDetailsViewController ()
+@interface RoomMemberDetailsViewController ()
{
RoomMemberTitleView* memberTitleView;
@@ -58,6 +58,12 @@
NSMutableArray *directChatsArray;
NSInteger directChatsIndex;
+ /**
+ List of the direct chats (room ids) with this member.
+ */
+ NSMutableArray *bwiUserLabelsArray;
+ NSInteger bwiUserLabelsIndex;
+
/**
Devices
*/
@@ -80,6 +86,9 @@
The current visibility of the status bar in this view controller.
*/
BOOL isStatusBarHidden;
+
+ UITextField *bwiUserLabelTextField;
+ UIButton *bwiUserLabelButton;
}
@property (weak, nonatomic) IBOutlet UIView *roomMemberAvatarHeaderBackground;
@@ -107,6 +116,8 @@
@property(nonatomic) AnalyticsScreenTracker *screenTracker;
+
+@property(nonatomic, strong) UserLabelDefaultService *bwiUserLabelService;
@end
@implementation RoomMemberDetailsViewController
@@ -139,6 +150,7 @@
adminActionsArray = [[NSMutableArray alloc] init];
otherActionsArray = [[NSMutableArray alloc] init];
directChatsArray = [[NSMutableArray alloc] init];
+ bwiUserLabelsArray = [[NSMutableArray alloc] init];
// Keep visible the status bar by default.
isStatusBarHidden = NO;
@@ -225,6 +237,8 @@
{
[self.tableView reloadData];
}
+
+ [self updateUserLabelTheme];
[self setNeedsStatusBarAppearanceUpdate];
}
@@ -291,6 +305,7 @@
otherActionsArray = nil;
directChatsArray = nil;
devicesArray = nil;
+ bwiUserLabelsArray = nil;
if (UIApplicationWillChangeStatusBarOrientationNotificationObserver)
{
@@ -331,6 +346,14 @@
}
}
+- (void)displayRoomMember:(MXRoomMember*)roomMember withMatrixRoom:(MXRoom*)room
+{
+ [super displayRoomMember:roomMember withMatrixRoom:room];
+
+ self.bwiUserLabelService = [[UserLabelDefaultService alloc] init];
+ [self.bwiUserLabelService setRoom:self.mxRoom];
+}
+
#pragma mark -
- (UIImage*)picturePlaceholder
@@ -413,6 +436,10 @@
[directChatsArray addObject:directRoomId];
}
}
+
+ if (BwiBuildSettings.bwiUserLabelsMemberDetailsVisible) {
+ self.roomMemberUserIdLabel.text = [self tmpUserLabel];
+ }
}
// Complete data update and reload table view
@@ -519,6 +546,7 @@
[adminActionsArray removeAllObjects];
[otherActionsArray removeAllObjects];
+ [bwiUserLabelsArray removeAllObjects];
// Consider the case of the user himself
if (self.isRoomMemberCurrentUser)
@@ -672,9 +700,13 @@
{
securityActionsArray = @[@(MXKRoomMemberDetailsActionSecurity)];
}
+
+ if ( BwiBuildSettings.bwiUserLabelsAdminSettingsVisible && oneSelfPowerLevel >= RoomPowerLevelAdmin) {
+ [bwiUserLabelsArray addObject:@(MXKRoomMemberDetailsActionBwiUserLabelDescription)];
+ [bwiUserLabelsArray addObject:@(MXKRoomMemberDetailsActionBwiUserLabel)];
+ }
- securityIndex = adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = -1;
-
+ securityIndex = adminToolsIndex = otherActionsIndex = directChatsIndex = devicesIndex = bwiUserLabelsIndex = -1;
if (securityActionsArray.count)
{
@@ -685,6 +717,12 @@
{
otherActionsIndex = sectionCount++;
}
+
+ if (bwiUserLabelsArray.count && !self.mxRoom.isDirect)
+ {
+ bwiUserLabelsIndex = sectionCount++;
+ }
+
if (adminActionsArray.count && !self.mxRoom.isDirect)
{
adminToolsIndex = sectionCount++;
@@ -725,6 +763,10 @@
{
return (devicesArray.count);
}
+ else if (section == bwiUserLabelsIndex)
+ {
+ return (bwiUserLabelsArray.count);
+ }
return 0;
}
@@ -751,6 +793,10 @@
{
return [VectorL10n roomParticipantsActionSectionDevices];
}
+ else if (section == bwiUserLabelsIndex)
+ {
+ return [NSString stringWithFormat:NSLocalizedStringFromTable(@"bwi_room_member_section_userlabels", @"Bwi", nil), self.mxRoom.summary.displayname];
+ }
return nil;
}
@@ -820,6 +866,12 @@
case MXKRoomMemberDetailsActionPermalink:
title = [VectorL10n roomParticipantsActionPermalink];
break;
+ case MXKRoomMemberDetailsActionBwiUserLabelDescription:
+ title = @"bwi functions desc";
+ break;
+ case MXKRoomMemberDetailsActionBwiUserLabel:
+ title = NSLocalizedStringFromTable(@"bwi_room_participants_action_userlabels", @"Bwi", nil);
+ break;
default:
break;
}
@@ -998,6 +1050,16 @@
cell = roomCell;
}
+ else if (indexPath.section == bwiUserLabelsIndex)
+ {
+ NSNumber *actionNumber = bwiUserLabelsArray[indexPath.row];
+
+ if (actionNumber.unsignedIntegerValue == MXKRoomMemberDetailsActionBwiUserLabelDescription) {
+ cell = [self bwiCellForFunctionDescription:tableView withIndex:indexPath];
+ } else {
+ cell = [self bwiCellForFunction:tableView withIndex:indexPath];
+ }
+ }
else if (indexPath.section == devicesIndex)
{
DeviceTableViewCell *deviceCell = [tableView dequeueReusableCellWithIdentifier:[DeviceTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
@@ -1434,4 +1496,115 @@
keyVerificationCoordinatorBridgePresenter = nil;
}
+#pragma mark - bwi functions
+
+- (UITableViewCell*)bwiCellForFunctionDescription:(UITableView*)tableView withIndex:(NSIndexPath*)indexPath {
+ MXKTableViewCell *functionDescriptionCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier] forIndexPath:indexPath];
+
+ functionDescriptionCell.textLabel.backgroundColor = [UIColor clearColor];
+ functionDescriptionCell.textLabel.numberOfLines = 0;
+ functionDescriptionCell.textLabel.text = NSLocalizedStringFromTable(@"bwi_room_member_userlabels_description", @"Bwi", nil);
+ functionDescriptionCell.textLabel.font = [UIFont systemFontOfSize:14.0];
+ functionDescriptionCell.textLabel.textColor = ThemeService.shared.theme.headerTextPrimaryColor;
+
+ functionDescriptionCell.selectionStyle = UITableViewCellSelectionStyleNone;
+ functionDescriptionCell.accessoryType = UITableViewCellAccessoryNone;
+ functionDescriptionCell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
+ functionDescriptionCell.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
+
+ // extend background color to safe area
+ UIView *bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
+ bgView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
+ functionDescriptionCell.backgroundView = bgView;
+
+ return functionDescriptionCell;
+}
+
+- (UITableViewCell*)bwiCellForFunction:(UITableView*)tableView withIndex:(NSIndexPath*)indexPath {
+ MXKTableViewCellWithTextFieldAndButton *functionCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithTextFieldAndButton defaultReuseIdentifier]];
+ if (!functionCell)
+ {
+ functionCell = [[MXKTableViewCellWithTextFieldAndButton alloc] init];
+ }
+
+ functionCell.mxkTextField.text = [self tmpUserLabel];
+ functionCell.mxkTextField.keyboardType = UIKeyboardTypeDefault;
+ functionCell.mxkTextField.clearButtonMode = UITextFieldViewModeAlways;
+ functionCell.mxkTextField.rightViewMode = UITextFieldViewModeAlways;
+ functionCell.mxkButton.enabled = YES;
+
+ [functionCell.mxkButton addTarget:self action:@selector(onBwiUserLabelButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
+
+ bwiUserLabelTextField = functionCell.mxkTextField;
+ bwiUserLabelTextField.delegate = self;
+
+ bwiUserLabelButton = functionCell.mxkButton;
+
+ [self updateUserLabelButtonText];
+ [self updateUserLabelTheme];
+
+ return functionCell;
+}
+
+- (void)onBwiUserLabelButtonPressed:(id)sender {
+ NSString* permFunction = [self.bwiUserLabelService getUserLabelWithUser:self.mxRoomMember.userId];
+
+ MXWeakify(self);
+
+ [self.bwiUserLabelService setCompletion:^(BOOL success) {
+ if (success) {
+ [weakself updateUserLabelButtonText];
+ [weakself updateMemberInfo];
+ }
+ }];
+
+ if (permFunction && [permFunction isEqualToString:bwiUserLabelTextField.text]) {
+ [self.bwiUserLabelService setUserLabelWithUserLabel:nil user:self.mxRoomMember.userId];
+ bwiUserLabelTextField.text = nil;
+ } else {
+ [self.bwiUserLabelService setUserLabelWithUserLabel:bwiUserLabelTextField.text user:self.mxRoomMember.userId];
+ }
+}
+
+- (void)updateUserLabelTheme {
+ bwiUserLabelTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"bwi_room_member_userlabels_textfield_placeholder", @"Bwi", nil) attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.textQuinaryColor}];
+
+ bwiUserLabelTextField.textColor = ThemeService.shared.theme.textSecondaryColor;
+ bwiUserLabelTextField.tintColor = ThemeService.shared.theme.tintColor;
+ bwiUserLabelTextField.backgroundColor = ThemeService.shared.theme.backgroundColor;
+}
+
+- (NSString*)tmpUserLabel {
+ NSString* label;
+ if (bwiUserLabelTextField && bwiUserLabelTextField.text.length > 0) {
+ label = bwiUserLabelTextField.text;
+ } else {
+ label = [self.bwiUserLabelService getUserLabelWithUser:self.mxRoomMember.userId];
+ }
+
+ return label;
+}
+
+- (void)updateUserLabelButtonText {
+ NSString* tmpFunction = bwiUserLabelTextField.text;
+ NSString* permFunction = [self.bwiUserLabelService getUserLabelWithUser:self.mxRoomMember.userId];
+
+ NSString* title;
+
+ if (permFunction && [permFunction isEqualToString:tmpFunction]) {
+ title = NSLocalizedStringFromTable(@"bwi_room_member_userlabels_button_delete", @"Bwi", nil);
+ } else {
+ title = NSLocalizedStringFromTable(@"bwi_room_member_userlabels_button_save", @"Bwi", nil);
+ }
+
+ [bwiUserLabelButton setTitle:title forState:UIControlStateNormal];
+ [bwiUserLabelButton setTitle:title forState:UIControlStateHighlighted];
+}
+
+#pragma mark bwi textfield delegate
+
+-(void) textFieldDidChangeSelection:(UITextField *)textField {
+ [self updateUserLabelButtonText];
+}
+
@end
diff --git a/Riot/Modules/Room/Members/RoomParticipantsViewController.h b/Riot/Modules/Room/Members/RoomParticipantsViewController.h
index ada3003f3..f03587cf9 100644
--- a/Riot/Modules/Room/Members/RoomParticipantsViewController.h
+++ b/Riot/Modules/Room/Members/RoomParticipantsViewController.h
@@ -60,6 +60,16 @@
*/
NSMutableArray *invitedParticipants;
+ /**
+ The current list of joined members.
+ */
+ NSMutableArray *adminParticipants;
+
+ /**
+ The current list of joined members.
+ */
+ NSMutableArray *modParticipants;
+
/**
The contact used to describe the current user (nil if the user is not a participant of the room).
*/
diff --git a/Riot/Modules/Room/Members/RoomParticipantsViewController.m b/Riot/Modules/Room/Members/RoomParticipantsViewController.m
index 6b94b3dbf..0e08c2f6d 100644
--- a/Riot/Modules/Room/Members/RoomParticipantsViewController.m
+++ b/Riot/Modules/Room/Members/RoomParticipantsViewController.m
@@ -60,8 +60,11 @@
id kThemeServiceDidChangeThemeNotificationObserver;
RoomParticipantsInviteCoordinatorBridgePresenter *invitePresenter;
+
}
+@property (nonatomic, strong) UserLabelDefaultService* bwiUserLabelService;
+
@end
@implementation RoomParticipantsViewController
@@ -472,6 +475,9 @@
self.searchBarHeader.hidden = YES;
}
+ self.bwiUserLabelService = [[UserLabelDefaultService alloc] init];
+ [self.bwiUserLabelService setRoom:self.mxRoom];
+
// Refresh the members list.
[self refreshParticipantsFromRoomMembers];
@@ -1067,6 +1073,14 @@
{
participantCell.contactDisplayNameLabel.text = [roomState.members memberName:contact.mxMember.userId];
}
+
+ if (BwiBuildSettings.bwiUserLabelsParticipantsVisible) {
+ contact.bwiUserLabel = [self.bwiUserLabelService getUserLabelWithUser:contact.mxMember.userId];
+
+ if (!participantCell.powerLevelLabel.text) {
+ participantCell.powerLevelLabel.text = contact.bwiUserLabel;
+ }
+ }
}
}
diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m
index fa25da1a8..466b45a17 100644
--- a/Riot/Utils/EventFormatter.m
+++ b/Riot/Utils/EventFormatter.m
@@ -47,6 +47,9 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
*/
NSCalendar *calendar;
}
+
+@property (nonatomic, strong) UserLabelDefaultService* bwiUserLabelService;
+
@end
@implementation EventFormatter
@@ -89,7 +92,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
return string;
}
-- (BOOL)shouldDisplayEvent:(MXEvent *)event {
+- (BOOL)shouldDisplayEvent:(MXEvent *)event {
return event.eventType == MXEventTypeRoomMessage
&& !event.isEditEvent
&& !event.isRedactedEvent;
@@ -225,6 +228,10 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
// Build the attributed string with the right font and color for the events
return [self renderString:displayText forEvent:event];
}
+ } else if (event.eventType == MXEventTypeCustom) {
+ if (BwiBuildSettings.bwiUserLabelsTimelineEventVisible && [event.type isEqualToString:BwiBuildSettings.bwiUserLabelEventTypeString]) {
+ return [self bwiRenderUserLabel:event];
+ }
}
switch (event.eventType)
@@ -438,6 +445,8 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
defaultRoomSummaryUpdater.showRoomTypeStrings = @[
MXRoomTypeStringSpace
];
+
+ self.bwiUserLabelService = [[UserLabelDefaultService alloc] init];
}
return self;
}
@@ -605,4 +614,77 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm";
return roomPredecessorAttributedString;
}
+#pragma mark - bwi Addition
+
+- (NSString*)senderDisplayNameForEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState
+{
+ NSString* displayName = [super senderDisplayNameForEvent:event withRoomState:roomState];
+
+ if (BwiBuildSettings.bwiUserLabelsTimelineDisplayNameVisible) {
+ [self.bwiUserLabelService setRoom:[mxSession vc_roomWithIdOrAlias:event.roomId]];
+
+ NSString* function = [self.bwiUserLabelService getUserLabelWithUser:event.sender];
+
+ if (function) {
+ displayName = [displayName stringByAppendingFormat:@" [%@]", function];
+ }
+ }
+
+ return displayName;
+}
+
+- (NSAttributedString*)bwiRenderUserLabel:(MXEvent*)event {
+ NSMutableDictionary *addedLabels = [[NSMutableDictionary alloc] init];
+ NSMutableDictionary *removedLabels = [[NSMutableDictionary alloc] init];
+
+ if (event.prevContent == nil) {
+ [addedLabels addEntriesFromDictionary:event.content];
+ } else {
+ for (NSString *key in [event.content allKeys]) {
+ if (!event.prevContent[key] || ![event.prevContent[key] isEqualToString:event.content[key]] ) {
+ addedLabels[key] = event.content[key];
+ }
+ }
+ for (NSString *key in [event.prevContent allKeys]) {
+ if (!event.content[key] ) {
+ removedLabels[key] = event.prevContent[key];
+ }
+ }
+ }
+
+ return [self renderString:[self userLabelEventSummary:event added:addedLabels removed:removedLabels] forEvent:event];
+}
+
+- (NSString*)userLabelEventSummary:(MXEvent*)event added:(NSDictionary*)addedLabels removed:(NSDictionary*)removedLabels {
+ NSMutableArray *differences = [[NSMutableArray alloc] init];
+
+ for (NSString* key in [addedLabels allKeys]) {
+ [differences addObject:[NSString stringWithFormat:NSLocalizedStringFromTable(@"bwi_timeline_userlabel_added_for_user", @"Bwi", nil), [self displayNameForUserId:key], addedLabels[key]]];
+ }
+ for (NSString* key in [removedLabels allKeys]) {
+ [differences addObject:[NSString stringWithFormat:NSLocalizedStringFromTable(@"bwi_timeline_userlabel_removed_for_user", @"Bwi", nil), [self displayNameForUserId:key], removedLabels[key]]];
+ }
+
+ if (differences.count == 0) {
+ return nil;
+ }
+
+ NSString *summary = [differences componentsJoinedByString:@","];
+
+ if ([event.sender isEqualToString:mxSession.myUserId]) {
+ return [NSString stringWithFormat:NSLocalizedStringFromTable(@"bwi_timeline_userlabel_prefix_you", @"Bwi", nil), summary];
+ } else {
+ return [NSString stringWithFormat:NSLocalizedStringFromTable(@"bwi_timeline_userlabel_prefix_other_user", @"Bwi", nil), summary];
+ }
+}
+
+- (NSString*)displayNameForUserId:(NSString*)userId {
+ MXUser *user = [mxSession userWithUserId:userId];
+ if (user) {
+ return user.displayname;
+ } else {
+ return userId;
+ }
+}
+
@end
diff --git a/bwi/BwiBuildSettings.swift b/bwi/BwiBuildSettings.swift
index fcef7dadb..5b221285b 100644
--- a/bwi/BwiBuildSettings.swift
+++ b/bwi/BwiBuildSettings.swift
@@ -52,4 +52,12 @@ final class BwiBuildSettings: NSObject {
static let bwiSettingsShowSecureBackupSection = false
static let bwiSettingsShowCrossSigningSection = false
+
+ static let bwiUserLabelsAdminSettingsVisible = true
+ static let bwiUserLabelsMemberDetailsVisible = true
+ static let bwiUserLabelsParticipantsVisible = true
+ static let bwiUserLabelsTimelineDisplayNameVisible = true
+ static let bwiUserLabelsTimelineEventVisible = true
+
+ static let bwiUserLabelEventTypeString = "de.bwi.room.user_function_labels"
}
diff --git a/bwi/UserLabels/UserLabelDefaultService.swift b/bwi/UserLabels/UserLabelDefaultService.swift
new file mode 100644
index 000000000..3b5148916
--- /dev/null
+++ b/bwi/UserLabels/UserLabelDefaultService.swift
@@ -0,0 +1,103 @@
+//
+/*
+ * Copyright (c) 2022 BWI GmbH
+ *
+ * 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
+import MatrixSDK
+
+class UserLabelDefaultService : NSObject {
+ var currentRoom: MXRoom? = nil
+ var completion: ((Bool) -> Void)? = nil
+}
+
+extension UserLabelDefaultService : UserLabelService {
+ @objc func setCompletion(_ completion:@escaping((Bool) -> Void)) {
+ self.completion = completion
+ }
+
+ @objc func setRoom(_ room: MXRoom) {
+ self.currentRoom = room
+ }
+
+ @objc func setUserLabel(userLabel: String?, user: String) {
+ guard let room = self.currentRoom, let completion = self.completion else {
+ return
+ }
+
+ room.state { (state) in
+ guard let state = state else {
+ return
+ }
+
+ if state.powerLevels.events[BwiBuildSettings.bwiUserLabelEventTypeString] == nil {
+ if var newContent = state.powerLevels.jsonDictionary() as? [String : Any] {
+ if var events = newContent["events"] as? [String : Any] {
+
+ events[BwiBuildSettings.bwiUserLabelEventTypeString] = RoomPowerLevel.admin.rawValue
+ newContent["events"] = events
+
+ room.sendStateEvent(.roomPowerLevels, content: newContent, stateKey: "", completion: { response in
+ if response.isFailure {
+ completion(false)
+ }
+ })
+ }
+
+ }
+ }
+
+ if var currentLabel = state.stateEvents(with: .custom(BwiBuildSettings.bwiUserLabelEventTypeString))?.last?.content {
+ if userLabel != nil {
+ currentLabel[user] = userLabel
+ } else {
+ currentLabel.removeValue(forKey: user)
+ }
+
+ room.sendStateEvent(.custom(BwiBuildSettings.bwiUserLabelEventTypeString), content: currentLabel, stateKey: "", completion: { response in
+ completion(response.isSuccess)
+ })
+ } else if userLabel != nil {
+ var newUserLabel = Dictionary()
+ newUserLabel[user] = userLabel
+ room.sendStateEvent(.custom(BwiBuildSettings.bwiUserLabelEventTypeString), content: newUserLabel, stateKey: "", completion: { response in
+ completion(response.isSuccess)
+ })
+ }
+ }
+ }
+
+ @objc func getUserLabel(user: String) -> String? {
+ guard let room = self.currentRoom else {
+ return nil
+ }
+
+ var label: String? = nil
+
+ room.state { (state) in
+ guard let state = state else {
+ return
+ }
+
+ if let labels = state.stateEvents(with: .custom(BwiBuildSettings.bwiUserLabelEventTypeString))?.last?.content {
+ if let tmp = labels[user] as? String {
+ label = tmp
+ }
+ }
+ }
+
+ return label
+ }
+}
diff --git a/bwi/UserLabels/UserLabelService.swift b/bwi/UserLabels/UserLabelService.swift
new file mode 100644
index 000000000..9b9f11c82
--- /dev/null
+++ b/bwi/UserLabels/UserLabelService.swift
@@ -0,0 +1,29 @@
+//
+/*
+ * Copyright (c) 2022 BWI GmbH
+ *
+ * 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
+import MatrixSDK
+
+protocol UserLabelService {
+ func setRoom(_ room: MXRoom)
+
+ func setCompletion(_ completion:@escaping((Bool) -> Void))
+
+ func setUserLabel( userLabel: String?, user: String)
+
+ func getUserLabel( user: String) -> String?
+}