From 0df5f857a7aa5fa62fe19bc8f86788b7d3c6a373 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Tue, 14 Jan 2020 14:58:22 +1100 Subject: [PATCH] let users know when members are removed --- .../UpdateGroupViewController.m | 4 ++ .../translations/en.lproj/Localizable.strings | 9 +++++ .../src/Messages/OWSMessageManager.m | 4 ++ SignalServiceKit/src/Messages/TSGroupModel.h | 2 + SignalServiceKit/src/Messages/TSGroupModel.m | 40 +++++++++++++++++++ .../src/Protos/Generated/SSKProto.swift | 20 ++++++++++ .../Protos/Generated/SignalService.pb.swift | 8 ++++ 7 files changed, 87 insertions(+) diff --git a/Signal/src/ViewControllers/ThreadSettings/UpdateGroupViewController.m b/Signal/src/ViewControllers/ThreadSettings/UpdateGroupViewController.m index 42a1d0c02..07fc9d81c 100644 --- a/Signal/src/ViewControllers/ThreadSettings/UpdateGroupViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/UpdateGroupViewController.m @@ -48,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) UIImage *groupAvatar; @property (nonatomic, nullable) NSSet *previousMemberRecipientIds; @property (nonatomic) NSMutableSet *memberRecipientIds; +@property (nonatomic) NSMutableSet *removedRecipientIds; @property (nonatomic) BOOL hasUnsavedChanges; @@ -89,6 +90,7 @@ NS_ASSUME_NONNULL_BEGIN _avatarViewHelper.delegate = self; self.memberRecipientIds = [NSMutableSet new]; + self.removedRecipientIds = [NSMutableSet new]; } #pragma mark - View Lifecycle @@ -366,6 +368,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssertDebug(recipientId.length > 0); [self.memberRecipientIds removeObject:recipientId]; + [self.removedRecipientIds addObject:recipientId]; [self updateTableContents]; } @@ -381,6 +384,7 @@ NS_ASSUME_NONNULL_BEGIN image:self.groupAvatar groupId:self.thread.groupModel.groupId groupType:self.thread.groupModel.groupType]; + groupModel.removedMembers = self.removedRecipientIds; [self.conversationSettingsViewDelegate groupWasUpdated:groupModel]; } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 25122b4da..94374a665 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1020,6 +1020,15 @@ /* No comment provided by engineer. */ "GROUP_MEMBER_LEFT" = " %@ left the group. "; +/* No comment provided by engineer. */ +"GROUP_MEMBER_REMOVED" = " %@ was removed from the group. "; + +/* No comment provided by engineer. */ +"GROUP_MEMBERS_REMOVED" = " %@ were removed from the group. "; + +/* No comment provided by engineer. */ +"YOU_WERE_REMOVED" = " You were removed from the group. "; + /* Button label to add information to an unknown contact */ "GROUP_MEMBERS_ADD_CONTACT_INFO" = "Add Contact"; diff --git a/SignalServiceKit/src/Messages/OWSMessageManager.m b/SignalServiceKit/src/Messages/OWSMessageManager.m index 9c869df7e..d2b1aeddc 100644 --- a/SignalServiceKit/src/Messages/OWSMessageManager.m +++ b/SignalServiceKit/src/Messages/OWSMessageManager.m @@ -1296,6 +1296,8 @@ NS_ASSUME_NONNULL_BEGIN OWSLogWarn(@"Ignoring 'Request Group Info' message for group we no longer belong to."); return; } + + gThread.groupModel.removedMembers = [NSMutableSet setWithArray:dataMessage.group.removedMembers]; NSString *updateGroupInfo = [gThread.groupModel getInfoStringAboutUpdateTo:gThread.groupModel contactsManager:self.contactsManager]; @@ -1358,6 +1360,7 @@ NS_ASSUME_NONNULL_BEGIN if (groupId.length > 0) { NSMutableSet *newMemberIds = [NSMutableSet setWithArray:dataMessage.group.members]; + NSMutableSet *removedMemberIds = [NSMutableSet setWithArray:dataMessage.group.removedMembers]; for (NSString *recipientId in newMemberIds) { if (!recipientId.isValidE164) { OWSLogVerbose( @@ -1396,6 +1399,7 @@ NS_ASSUME_NONNULL_BEGIN image:oldGroupThread.groupModel.groupImage groupId:dataMessage.group.id groupType:oldGroupThread.groupModel.groupType]; + newGroupModel.removedMembers = removedMemberIds; NSString *updateGroupInfo = [newGroupThread.groupModel getInfoStringAboutUpdateTo:newGroupModel contactsManager:self.contactsManager]; newGroupThread.groupModel = newGroupModel; diff --git a/SignalServiceKit/src/Messages/TSGroupModel.h b/SignalServiceKit/src/Messages/TSGroupModel.h index 87919e106..d81a6d603 100644 --- a/SignalServiceKit/src/Messages/TSGroupModel.h +++ b/SignalServiceKit/src/Messages/TSGroupModel.h @@ -4,6 +4,7 @@ #import "ContactsManagerProtocol.h" #import "TSYapDatabaseObject.h" +#import "TSAccountManager.h" NS_ASSUME_NONNULL_BEGIN @@ -23,6 +24,7 @@ extern const int32_t kGroupIdLength; @property (nullable, readonly, nonatomic) NSString *groupName; @property (readonly, nonatomic) NSData *groupId; @property (nonatomic) GroupType groupType; +@property (nonatomic) NSMutableSet *removedMembers; #if TARGET_OS_IOS @property (nullable, nonatomic, strong) UIImage *groupImage; diff --git a/SignalServiceKit/src/Messages/TSGroupModel.m b/SignalServiceKit/src/Messages/TSGroupModel.m index 381d42fc1..deec9c0ec 100644 --- a/SignalServiceKit/src/Messages/TSGroupModel.m +++ b/SignalServiceKit/src/Messages/TSGroupModel.m @@ -50,6 +50,14 @@ const int32_t kGroupIdLength = 16; if (_groupMemberIds == nil) { _groupMemberIds = [NSArray new]; } + + if (_groupAdminIds == nil) { + _groupAdminIds = [NSArray new]; + } + + if (_removedMembers == nil) { + _removedMembers = [NSMutableSet new]; + } return self; } @@ -114,6 +122,7 @@ const int32_t kGroupIdLength = 16; NSMutableSet *membersWhoLeft = [NSMutableSet setWithSet:oldMembers]; [membersWhoLeft minusSet:newMembers]; + [membersWhoLeft minusSet:_removedMembers]; if ([membersWhoLeft count] > 0) { @@ -134,6 +143,32 @@ const int32_t kGroupIdLength = 16; stringByAppendingString:[NSString stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_JOINED", @""), [newMembersNames componentsJoinedByString:@", "]]]; } + + if ([_removedMembers count] > 0) { + NSString *masterDeviceHexEncodedPublicKey = [NSUserDefaults.standardUserDefaults stringForKey:@"masterDeviceHexEncodedPublicKey"]; + NSString *hexEncodedPublicKey = masterDeviceHexEncodedPublicKey != nil ? masterDeviceHexEncodedPublicKey : [TSAccountManager localNumber]; + if ([_removedMembers containsObject:hexEncodedPublicKey]) { + updatedGroupInfoString = [updatedGroupInfoString + stringByAppendingString:NSLocalizedString(@"YOU_WERE_REMOVED", @"")]; + } + else { + NSArray *removedMembersNames = [[_removedMembers allObjects] map:^NSString*(NSString* item) { + return [contactsManager displayNameForPhoneIdentifier:item]; + }]; + if ([removedMembersNames count] > 1) { + updatedGroupInfoString = [updatedGroupInfoString + stringByAppendingString:[NSString + stringWithFormat:NSLocalizedString(@"GROUP_MEMBERS_REMOVED", @""), + [removedMembersNames componentsJoinedByString:@", "]]]; + } + else { + updatedGroupInfoString = [updatedGroupInfoString + stringByAppendingString:[NSString + stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_REMOVED", @""), + [removedMembersNames componentsJoinedByString:@", "]]]; + } + } + } return updatedGroupInfoString; } @@ -150,6 +185,11 @@ const int32_t kGroupIdLength = 16; _groupAdminIds = groupAdminIds; } +- (void)setRemovedMembers:(NSMutableSet *)removedMembers +{ + _removedMembers = removedMembers; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index 3270835f5..26b8320be 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -5674,6 +5674,7 @@ extension SSKProtoAttachmentPointer.SSKProtoAttachmentPointerBuilder { if let _value = avatar { builder.setAvatar(_value) } + builder.setRemovedMembers(removedMembers) return builder } @@ -5707,10 +5708,25 @@ extension SSKProtoAttachmentPointer.SSKProtoAttachmentPointerBuilder { items.append(valueParam) proto.members = items } + + @objc public func removeMembers(_ valueParam: String) { + var items = proto.members + var removed = proto.removedMembers + if let index = items.index(of: valueParam) { + items.remove(at: index) + proto.members = items + removed.append(valueParam) + proto.removedMembers = removed + } + } @objc public func setMembers(_ wrappedItems: [String]) { proto.members = wrappedItems } + + @objc public func setRemovedMembers(_ wrappedItems: [String]) { + proto.removedMembers = wrappedItems + } @objc public func setAvatar(_ valueParam: SSKProtoAttachmentPointer) { proto.avatar = valueParam.proto @@ -5746,6 +5762,10 @@ extension SSKProtoAttachmentPointer.SSKProtoAttachmentPointerBuilder { @objc public var members: [String] { return proto.members } + + @objc public var removedMembers: [String] { + return proto.removedMembers + } private init(proto: SignalServiceProtos_GroupContext, id: Data, diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index 5532d9b2a..f2999144c 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -2309,6 +2309,11 @@ struct SignalServiceProtos_GroupContext { get {return _storage._members} set {_uniqueStorage()._members = newValue} } + + var removedMembers: [String] { + get {return _storage._removedMembers} + set {_uniqueStorage()._removedMembers = newValue} + } var avatar: SignalServiceProtos_AttachmentPointer { get {return _storage._avatar ?? SignalServiceProtos_AttachmentPointer()} @@ -4943,6 +4948,7 @@ extension SignalServiceProtos_GroupContext: SwiftProtobuf.Message, SwiftProtobuf var _type: SignalServiceProtos_GroupContext.TypeEnum? = nil var _name: String? = nil var _members: [String] = [] + var _removedMembers: [String] = [] var _avatar: SignalServiceProtos_AttachmentPointer? = nil static let defaultInstance = _StorageClass() @@ -4955,6 +4961,7 @@ extension SignalServiceProtos_GroupContext: SwiftProtobuf.Message, SwiftProtobuf _name = source._name _members = source._members _avatar = source._avatar + _removedMembers = source._removedMembers } } @@ -4975,6 +4982,7 @@ extension SignalServiceProtos_GroupContext: SwiftProtobuf.Message, SwiftProtobuf case 3: try decoder.decodeSingularStringField(value: &_storage._name) case 4: try decoder.decodeRepeatedStringField(value: &_storage._members) case 5: try decoder.decodeSingularMessageField(value: &_storage._avatar) + case 6: try decoder.decodeRepeatedStringField(value: &_storage._removedMembers) default: break } }