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? +}