Clean up group update handling

This commit is contained in:
nielsandriesse 2021-05-05 10:38:09 +10:00
parent 49f3b9f7db
commit 955e3abdad
6 changed files with 56 additions and 83 deletions

View File

@ -10,8 +10,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface TSInfoMessage : TSMessage <OWSReadTracking> @interface TSInfoMessage : TSMessage <OWSReadTracking>
typedef NS_ENUM(NSInteger, TSInfoMessageType) { typedef NS_ENUM(NSInteger, TSInfoMessageType) {
TSInfoMessageTypeGroupUpdate, TSInfoMessageTypeGroupCreated,
TSInfoMessageTypeGroupQuit, TSInfoMessageTypeGroupUpdated,
TSInfoMessageTypeGroupCurrentUserLeft,
TSInfoMessageTypeDisappearingMessagesUpdate, TSInfoMessageTypeDisappearingMessagesUpdate,
TSInfoMessageTypeScreenshotNotification, TSInfoMessageTypeScreenshotNotification,
TSInfoMessageTypeMediaSavedNotification TSInfoMessageTypeMediaSavedNotification

View File

@ -103,9 +103,9 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction - (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
{ {
switch (_messageType) { switch (_messageType) {
case TSInfoMessageTypeGroupQuit: case TSInfoMessageTypeGroupCurrentUserLeft:
return NSLocalizedString(@"GROUP_YOU_LEFT", nil); return NSLocalizedString(@"GROUP_YOU_LEFT", nil);
case TSInfoMessageTypeGroupUpdate: case TSInfoMessageTypeGroupUpdated:
return _customMessage != nil ? _customMessage : NSLocalizedString(@"GROUP_UPDATED", nil); return _customMessage != nil ? _customMessage : NSLocalizedString(@"GROUP_UPDATED", nil);
default: default:
break; break;

View File

@ -398,7 +398,7 @@ extension MessageReceiver {
thread = TSGroupThread.getOrCreateThread(with: group, transaction: transaction) thread = TSGroupThread.getOrCreateThread(with: group, transaction: transaction)
thread.save(with: transaction) thread.save(with: transaction)
// Notify the user // Notify the user
let infoMessage = TSInfoMessage(timestamp: messageSentTimestamp, in: thread, messageType: .groupUpdate) let infoMessage = TSInfoMessage(timestamp: messageSentTimestamp, in: thread, messageType: .groupCreated)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
} }
// Add the group to the user's set of public keys to poll for // Add the group to the user's set of public keys to poll for
@ -470,7 +470,7 @@ extension MessageReceiver {
// Notify the user if needed // Notify the user if needed
guard name != group.groupName else { return } guard name != group.groupName else { return }
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: message.sentTimestamp!, in: thread, messageType: .groupUpdate, customMessage: updateInfo) let infoMessage = TSInfoMessage(timestamp: message.sentTimestamp!, in: thread, messageType: .groupUpdated, customMessage: updateInfo)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
} }
} }
@ -504,7 +504,7 @@ extension MessageReceiver {
// Notify the user if needed // Notify the user if needed
guard members != Set(group.groupMemberIds) else { return } guard members != Set(group.groupMemberIds) else { return }
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: message.sentTimestamp!, in: thread, messageType: .groupUpdate, customMessage: updateInfo) let infoMessage = TSInfoMessage(timestamp: message.sentTimestamp!, in: thread, messageType: .groupUpdated, customMessage: updateInfo)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
} }
} }
@ -548,7 +548,7 @@ extension MessageReceiver {
thread.setGroupModel(newGroupModel, with: transaction) thread.setGroupModel(newGroupModel, with: transaction)
// Notify the user if needed // Notify the user if needed
guard members != Set(group.groupMemberIds) else { return } guard members != Set(group.groupMemberIds) else { return }
let infoMessageType: TSInfoMessageType = wasCurrentUserRemoved ? .groupQuit : .groupUpdate let infoMessageType: TSInfoMessageType = wasCurrentUserRemoved ? .groupCurrentUserLeft : .groupUpdated
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: message.sentTimestamp!, in: thread, messageType: infoMessageType, customMessage: updateInfo) let infoMessage = TSInfoMessage(timestamp: message.sentTimestamp!, in: thread, messageType: infoMessageType, customMessage: updateInfo)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
@ -581,8 +581,14 @@ extension MessageReceiver {
thread.setGroupModel(newGroupModel, with: transaction) thread.setGroupModel(newGroupModel, with: transaction)
// Notify the user if needed // Notify the user if needed
guard members != Set(group.groupMemberIds) else { return } guard members != Set(group.groupMemberIds) else { return }
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let contact = Storage.shared.getContact(with: message.sender!)
let infoMessage = TSInfoMessage(timestamp: message.sentTimestamp!, in: thread, messageType: .groupUpdate, customMessage: updateInfo) let updateInfo: String
if let displayName = contact?.displayName(for: Contact.Context.regular) {
updateInfo = String(format: NSLocalizedString("GROUP_MEMBER_LEFT", comment: ""), displayName)
} else {
updateInfo = NSLocalizedString("GROUP_UPDATED", comment: "")
}
let infoMessage = TSInfoMessage(timestamp: message.sentTimestamp!, in: thread, messageType: .groupUpdated, customMessage: updateInfo)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
} }
} }

View File

@ -41,7 +41,7 @@ extension MessageSender {
// Notify the PN server // Notify the PN server
promises.append(PushNotificationAPI.performOperation(.subscribe, for: groupPublicKey, publicKey: userPublicKey)) promises.append(PushNotificationAPI.performOperation(.subscribe, for: groupPublicKey, publicKey: userPublicKey))
// Notify the user // Notify the user
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupUpdate) let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupCreated)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
// Return // Return
return when(fulfilled: promises).map2 { thread } return when(fulfilled: promises).map2 { thread }
@ -137,7 +137,7 @@ extension MessageSender {
thread.setGroupModel(newGroupModel, with: transaction) thread.setGroupModel(newGroupModel, with: transaction)
// Notify the user // Notify the user
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupUpdate, customMessage: updateInfo) let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupUpdated, customMessage: updateInfo)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
// Return // Return
return Promise.value(()) return Promise.value(())
@ -182,7 +182,7 @@ extension MessageSender {
thread.setGroupModel(newGroupModel, with: transaction) thread.setGroupModel(newGroupModel, with: transaction)
// Notify the user // Notify the user
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupUpdate, customMessage: updateInfo) let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupUpdated, customMessage: updateInfo)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
// Return // Return
return Promise.value(()) return Promise.value(())
@ -233,7 +233,7 @@ extension MessageSender {
// Notify the user if needed (not if only zombie members were removed) // Notify the user if needed (not if only zombie members were removed)
if !membersToRemove.subtracting(oldZombies).isEmpty { if !membersToRemove.subtracting(oldZombies).isEmpty {
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupUpdate, customMessage: updateInfo) let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupUpdated, customMessage: updateInfo)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
} }
// Return // Return
@ -280,7 +280,7 @@ extension MessageSender {
thread.setGroupModel(newGroupModel, with: transaction) thread.setGroupModel(newGroupModel, with: transaction)
// Notify the user // Notify the user
let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel) let updateInfo = group.getInfoStringAboutUpdate(to: newGroupModel)
let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupUpdate, customMessage: updateInfo) let infoMessage = TSInfoMessage(timestamp: NSDate.ows_millisecondTimeStamp(), in: thread, messageType: .groupCurrentUserLeft, customMessage: updateInfo)
infoMessage.save(with: transaction) infoMessage.save(with: transaction)
// Return // Return
return promise return promise

View File

@ -21,7 +21,6 @@ extern const int32_t kGroupIdLength;
@property (nullable, readonly, nonatomic) NSString *groupName; @property (nullable, readonly, nonatomic) NSString *groupName;
@property (readonly, nonatomic) NSData *groupId; @property (readonly, nonatomic) NSData *groupId;
@property (nonatomic) GroupType groupType; @property (nonatomic) GroupType groupType;
@property (nonatomic) NSMutableSet<NSString *> *removedMembers;
#if TARGET_OS_IOS #if TARGET_OS_IOS
@property (nullable, nonatomic, strong) UIImage *groupImage; @property (nullable, nonatomic, strong) UIImage *groupImage;
@ -36,7 +35,7 @@ extern const int32_t kGroupIdLength;
- (BOOL)isEqual:(id)other; - (BOOL)isEqual:(id)other;
- (BOOL)isEqualToGroupModel:(TSGroupModel *)model; - (BOOL)isEqualToGroupModel:(TSGroupModel *)model;
- (NSString *)getInfoStringAboutUpdateTo:(TSGroupModel *)model; - (NSString *)getInfoStringAboutUpdateTo:(TSGroupModel *)model;
- (void)updateGroupId: (NSData *)newGroupId;
#endif #endif
@end @end

View File

@ -20,6 +20,11 @@ const int32_t kGroupIdLength = 16;
@implementation TSGroupModel @implementation TSGroupModel
- (nullable NSString *)groupName
{
return _groupName.filterStringForDisplay;
}
#if TARGET_OS_IOS #if TARGET_OS_IOS
- (instancetype)initWithTitle:(nullable NSString *)title - (instancetype)initWithTitle:(nullable NSString *)title
memberIds:(NSArray<NSString *> *)memberIds memberIds:(NSArray<NSString *> *)memberIds
@ -30,7 +35,7 @@ const int32_t kGroupIdLength = 16;
{ {
_groupName = title; _groupName = title;
_groupMemberIds = [memberIds copy]; _groupMemberIds = [memberIds copy];
_groupImage = image; // image is stored in DB _groupImage = image;
_groupType = groupType; _groupType = groupType;
_groupId = groupId; _groupId = groupId;
_groupAdminIds = [adminIds copy]; _groupAdminIds = [adminIds copy];
@ -54,10 +59,6 @@ const int32_t kGroupIdLength = 16;
if (_groupAdminIds == nil) { if (_groupAdminIds == nil) {
_groupAdminIds = [NSArray new]; _groupAdminIds = [NSArray new];
} }
if (_removedMembers == nil) {
_removedMembers = [NSMutableSet new];
}
return self; return self;
} }
@ -97,75 +98,56 @@ const int32_t kGroupIdLength = 16;
} }
- (NSString *)getInfoStringAboutUpdateTo:(TSGroupModel *)newModel { - (NSString *)getInfoStringAboutUpdateTo:(TSGroupModel *)newModel {
// This is only invoked for group * changes *, i.e. not when a group is created.
NSString *userPublicKey = [SNGeneralUtilities getUserPublicKey];
NSString *updatedGroupInfoString = @""; NSString *updatedGroupInfoString = @"";
if (self == newModel) { if (self == newModel) {
return NSLocalizedString(@"GROUP_UPDATED", @""); return NSLocalizedString(@"GROUP_UPDATED", @"");
} }
// Name change
if (![_groupName isEqual:newModel.groupName]) { if (![_groupName isEqual:newModel.groupName]) {
if (newModel.groupName.length == 0) { updatedGroupInfoString = [updatedGroupInfoString stringByAppendingString:[NSString stringWithFormat:NSLocalizedString(@"GROUP_TITLE_CHANGED", @""), newModel.groupName]];
updatedGroupInfoString = [updatedGroupInfoString stringByAppendingString:@"Closed group created"];
} else {
updatedGroupInfoString = [updatedGroupInfoString stringByAppendingString:[NSString stringWithFormat:NSLocalizedString(@"GROUP_TITLE_CHANGED", @""), newModel.groupName]];
}
}
if (_groupImage != nil && newModel.groupImage != nil &&
!([UIImagePNGRepresentation(_groupImage) isEqualToData:UIImagePNGRepresentation(newModel.groupImage)])) {
updatedGroupInfoString =
[updatedGroupInfoString stringByAppendingString:NSLocalizedString(@"GROUP_AVATAR_CHANGED", @"")];
} }
// Added & removed members
NSSet *oldMembers = [NSSet setWithArray:_groupMemberIds]; NSSet *oldMembers = [NSSet setWithArray:_groupMemberIds];
NSSet *newMembers = [NSSet setWithArray:newModel.groupMemberIds]; NSSet *newMembers = [NSSet setWithArray:newModel.groupMemberIds];
NSMutableSet *membersWhoJoined = [NSMutableSet setWithSet:newMembers]; NSMutableSet *addedMembers = newMembers.mutableCopy;
[membersWhoJoined minusSet:oldMembers]; [addedMembers minusSet:oldMembers];
NSMutableSet *membersWhoLeft = [NSMutableSet setWithSet:oldMembers]; NSMutableSet *removedMembers = oldMembers.mutableCopy;
[membersWhoLeft minusSet:newMembers]; [removedMembers minusSet:newMembers];
[membersWhoLeft minusSet:newModel.removedMembers];
NSMutableSet *removedMembersMinusSelf = removedMembers.mutableCopy;
[removedMembersMinusSelf minusSet:[NSSet setWithObject:userPublicKey]];
if ([membersWhoLeft count] > 0) { if (removedMembersMinusSelf.count > 0) {
NSArray *oldMembersNames = [[membersWhoLeft allObjects] map:^NSString*(NSString* publicKey) { NSArray *removedMemberNames = [removedMembers.allObjects map:^NSString *(NSString *publicKey) {
return [[LKStorage.shared getContactWithSessionID:publicKey] displayNameFor:SNContactContextRegular] ?: publicKey; SNContact *contact = [LKStorage.shared getContactWithSessionID:publicKey];
return [contact displayNameFor:SNContactContextRegular] ?: publicKey;
}]; }];
NSString *format = removedMembers.count > 1 ? NSLocalizedString(@"GROUP_MEMBERS_REMOVED", @"") : NSLocalizedString(@"GROUP_MEMBER_REMOVED", @"");
updatedGroupInfoString = [updatedGroupInfoString updatedGroupInfoString = [updatedGroupInfoString
stringByAppendingString:[NSString stringByAppendingString:[NSString
stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_LEFT", @""), stringWithFormat: format,
[oldMembersNames componentsJoinedByString:@", "]]]; [removedMemberNames componentsJoinedByString:@", "]]];
} }
if (membersWhoJoined.count > 0) { if (addedMembers.count > 0) {
NSArray *newMembersNames = [[membersWhoJoined allObjects] map:^NSString*(NSString* publicKey) { NSArray *addedMemberNames = [[addedMembers allObjects] map:^NSString*(NSString* publicKey) {
return [[LKStorage.shared getContactWithSessionID:publicKey] displayNameFor:SNContactContextRegular] ?: publicKey; SNContact *contact = [LKStorage.shared getContactWithSessionID:publicKey];
return [contact displayNameFor:SNContactContextRegular] ?: publicKey;
}]; }];
updatedGroupInfoString = [updatedGroupInfoString updatedGroupInfoString = [updatedGroupInfoString
stringByAppendingString:[NSString stringByAppendingString:[NSString
stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_JOINED", @""), stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_JOINED", @""),
[newMembersNames componentsJoinedByString:@", "]]]; [addedMemberNames componentsJoinedByString:@", "]]];
} }
if (newModel.removedMembers.count > 0) { if ([removedMembers containsObject:userPublicKey]) {
if ([newModel.removedMembers containsObject:[SNGeneralUtilities getUserPublicKey]]) { updatedGroupInfoString = [updatedGroupInfoString stringByAppendingString:NSLocalizedString(@"YOU_WERE_REMOVED", @"")];
updatedGroupInfoString = [updatedGroupInfoString
stringByAppendingString:NSLocalizedString(@"YOU_WERE_REMOVED", @"")];
} else {
NSArray *removedMemberNames = [newModel.removedMembers.allObjects map:^NSString*(NSString* publicKey) {
return [[LKStorage.shared getContactWithSessionID:publicKey] displayNameFor:SNContactContextRegular] ?: publicKey;
}];
if ([removedMemberNames count] > 1) {
updatedGroupInfoString = [updatedGroupInfoString
stringByAppendingString:[NSString
stringWithFormat:NSLocalizedString(@"GROUP_MEMBERS_REMOVED", @""),
[removedMemberNames componentsJoinedByString:@", "]]];
}
else {
updatedGroupInfoString = [updatedGroupInfoString
stringByAppendingString:[NSString
stringWithFormat:NSLocalizedString(@"GROUP_MEMBER_REMOVED", @""),
removedMemberNames[0]]];
}
}
} }
// Return
if ([updatedGroupInfoString length] == 0) { if ([updatedGroupInfoString length] == 0) {
updatedGroupInfoString = NSLocalizedString(@"GROUP_UPDATED", @""); updatedGroupInfoString = NSLocalizedString(@"GROUP_UPDATED", @"");
} }
@ -174,21 +156,6 @@ const int32_t kGroupIdLength = 16;
#endif #endif
- (nullable NSString *)groupName
{
return _groupName.filterStringForDisplay;
}
- (void)setRemovedMembers:(NSMutableSet<NSString *> *)removedMembers
{
_removedMembers = removedMembers;
}
- (void)updateGroupId: (NSData *)newGroupId
{
_groupId = newGroupId;
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END