Signal-iOS/SignalServiceKit/src/Messages/OWSGroupCallMessage.m
Jordan Rose e5e4e4b2bf Fix message for an ongoing group call with three people
There's no reason to special-case "three" vs "four or more" in our
current format (rather than using "three or more"), and in fact the
"three" case wasn't using correct English (it was correct in the
PluralAware table, though). Update both the in-call title text and the
conversation item to use the PluralAware "many" version for "three or
more" people in a call.
2022-08-15 13:42:05 -07:00

277 lines
11 KiB
Objective-C

//
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
#import "OWSGroupCallMessage.h"
#import "FunctionalUtil.h"
#import "TSGroupThread.h"
#import <SignalCoreKit/NSDate+OWS.h>
#import <SignalServiceKit/SignalServiceKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSGroupCallMessage ()
@property (nonatomic, getter=wasRead) BOOL read;
@property (nonatomic, nullable, readonly) NSString *eraId;
@property (nonatomic, nullable) NSArray<NSString *> *joinedMemberUuids;
@property (nonatomic, nullable) NSString *creatorUuid;
@property (nonatomic) BOOL hasEnded;
@end
#pragma mark -
@implementation OWSGroupCallMessage
- (instancetype)initWithEraId:(NSString *)eraId
joinedMemberUuids:(NSArray<NSUUID *> *)joinedMemberUuids
creatorUuid:(nullable NSUUID *)creatorUuid
thread:(TSGroupThread *)thread
sentAtTimestamp:(uint64_t)sentAtTimestamp
{
self = [super initInteractionWithTimestamp:sentAtTimestamp thread:thread];
if (!self) {
return self;
}
_eraId = eraId;
_joinedMemberUuids = [joinedMemberUuids map:^(NSUUID *uuid) { return uuid.UUIDString; }];
_creatorUuid = creatorUuid.UUIDString;
return self;
}
// --- CODE GENERATION MARKER
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run `sds_codegen.sh`.
// clang-format off
- (instancetype)initWithGrdbId:(int64_t)grdbId
uniqueId:(NSString *)uniqueId
receivedAtTimestamp:(uint64_t)receivedAtTimestamp
sortId:(uint64_t)sortId
timestamp:(uint64_t)timestamp
uniqueThreadId:(NSString *)uniqueThreadId
creatorUuid:(nullable NSString *)creatorUuid
eraId:(nullable NSString *)eraId
hasEnded:(BOOL)hasEnded
joinedMemberUuids:(nullable NSArray<NSString *> *)joinedMemberUuids
read:(BOOL)read
{
self = [super initWithGrdbId:grdbId
uniqueId:uniqueId
receivedAtTimestamp:receivedAtTimestamp
sortId:sortId
timestamp:timestamp
uniqueThreadId:uniqueThreadId];
if (!self) {
return self;
}
_creatorUuid = creatorUuid;
_eraId = eraId;
_hasEnded = hasEnded;
_joinedMemberUuids = joinedMemberUuids;
_read = read;
return self;
}
// clang-format on
// --- CODE GENERATION MARKER
- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
return [super initWithCoder:coder];
}
- (NSArray<SignalServiceAddress *> *)joinedMemberAddresses
{
return [self.joinedMemberUuids
map:^(NSString *uuidString) { return [[SignalServiceAddress alloc] initWithUuidString:uuidString]; }];
}
- (nullable SignalServiceAddress *)creatorAddress
{
if (self.creatorUuid) {
return [[SignalServiceAddress alloc] initWithUuidString:self.creatorUuid];
} else {
return nil;
}
}
- (OWSInteractionType)interactionType
{
return OWSInteractionType_Call;
}
#pragma mark - OWSReadTracking
- (uint64_t)expireStartedAt
{
return 0;
}
- (BOOL)shouldAffectUnreadCounts
{
return YES;
}
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
thread:(TSThread *)thread
circumstance:(OWSReceiptCircumstance)circumstance
transaction:(SDSAnyWriteTransaction *)transaction
{
OWSAssertDebug(transaction);
if (self.read) {
return;
}
OWSLogDebug(@"marking as read uniqueId: %@ which has timestamp: %llu", self.uniqueId, self.timestamp);
[self anyUpdateGroupCallMessageWithTransaction:transaction
block:^(OWSGroupCallMessage *groupCallMessage) {
groupCallMessage.read = YES;
}];
// Ignore `circumstance` - we never send read receipts for calls.
}
#pragma mark - Methods
- (NSString *)previewTextWithTransaction:(SDSAnyReadTransaction *)transaction
{
if (self.hasEnded) {
return OWSLocalizedString(
@"GROUP_CALL_ENDED_MESSAGE", @"Text in conversation view for a group call that has since ended");
} else if (self.creatorAddress.isLocalAddress) {
return OWSLocalizedString(@"GROUP_CALL_STARTED_BY_YOU", @"Text explaining that you started a group call.");
} else if (self.creatorAddress) {
NSString *creatorDisplayName = [self participantNameForAddress:self.creatorAddress transaction:transaction];
NSString *formatString = OWSLocalizedString(@"GROUP_CALL_STARTED_MESSAGE_FORMAT",
@"Text explaining that someone started a group call. Embeds {{call creator display name}}");
return [NSString stringWithFormat:formatString, creatorDisplayName];
} else {
return OWSLocalizedString(@"GROUP_CALL_SOMEONE_STARTED_MESSAGE",
@"Text in conversation view for a group call that someone started. We don't know who");
}
}
- (NSString *)systemTextWithTransaction:(SDSAnyReadTransaction *)transaction
{
NSString *threeOrMoreFormat = NSLocalizedStringFromTableInBundle(@"GROUP_CALL_MANY_PEOPLE_HERE_%d",
@"PluralAware",
NSBundle.mainBundle.appBundle,
@"Text explaining that there are three or more people in the group call. Embeds {{ %1$@ participantCount-2, "
@"%2$@ participant1, %3$@ participant2 }}");
NSString *twoFormat = OWSLocalizedString(@"GROUP_CALL_TWO_PEOPLE_HERE_FORMAT",
@"Text explaining that there are two people in the group call. Embeds {{ %1$@ participant1, %2$@ participant2 "
@"}}");
NSString *onlyCreatorFormat = OWSLocalizedString(@"GROUP_CALL_STARTED_MESSAGE_FORMAT",
@"Text explaining that someone started a group call. Embeds {{call creator display name}}");
NSString *youCreatedString
= OWSLocalizedString(@"GROUP_CALL_STARTED_BY_YOU", @"Text explaining that you started a group call.");
NSString *onlyYouString
= OWSLocalizedString(@"GROUP_CALL_YOU_ARE_HERE", @"Text explaining that you are in the group call.");
NSString *onlyOneFormat = OWSLocalizedString(@"GROUP_CALL_ONE_PERSON_HERE_FORMAT",
@"Text explaining that there is one person in the group call. Embeds {member name}");
NSString *endedString = OWSLocalizedString(
@"GROUP_CALL_ENDED_MESSAGE", @"Text in conversation view for a group call that has since ended");
NSString *someoneString = OWSLocalizedString(@"GROUP_CALL_SOMEONE_STARTED_MESSAGE",
@"Text in conversation view for a group call that someone started. We don't know who");
// Sort the addresses to prioritize the local user and originator, then the rest of the participants alphabetically
NSArray<SignalServiceAddress *> *sortedAddresses = [self.joinedMemberAddresses
sortedArrayUsingComparator:^NSComparisonResult(SignalServiceAddress *obj1, SignalServiceAddress *obj2) {
if ([obj1 isEqualToAddress:obj2]) {
return NSOrderedSame;
} else if (obj1.isLocalAddress || obj2.isLocalAddress) {
return obj1.isLocalAddress ? NSOrderedAscending : NSOrderedDescending;
} else if ([obj1 isEqualToAddress:self.creatorAddress] || [obj2 isEqualToAddress:self.creatorAddress]) {
return [obj1 isEqualToAddress:self.creatorAddress] ? NSOrderedAscending : NSOrderedDescending;
} else {
NSString *compareString1 = [self.contactsManager comparableNameForAddress:obj1 transaction:transaction];
NSString *compareString2 = [self.contactsManager comparableNameForAddress:obj2 transaction:transaction];
return [compareString1 compare:compareString2];
}
}];
if (self.hasEnded) {
return endedString;
} else if (sortedAddresses.count >= 3) {
NSString *firstName = [self participantNameForAddress:sortedAddresses[0] transaction:transaction];
NSString *secondName = [self participantNameForAddress:sortedAddresses[1] transaction:transaction];
return [NSString localizedStringWithFormat:threeOrMoreFormat, sortedAddresses.count - 2, firstName, secondName];
} else if (sortedAddresses.count == 2) {
NSString *firstName = [self participantNameForAddress:sortedAddresses[0] transaction:transaction];
NSString *secondName = [self participantNameForAddress:sortedAddresses[1] transaction:transaction];
return [NSString stringWithFormat:twoFormat, firstName, secondName];
} else if (sortedAddresses.count == 1 && [sortedAddresses[0] isEqualToAddress:self.creatorAddress]) {
if (sortedAddresses[0].isLocalAddress) {
return youCreatedString;
} else {
NSString *name = [self participantNameForAddress:sortedAddresses[0] transaction:transaction];
return [NSString stringWithFormat:onlyCreatorFormat, name];
}
} else if (sortedAddresses.count == 1 && sortedAddresses[0].isLocalAddress) {
return onlyYouString;
} else if (sortedAddresses.count == 1) {
NSString *name = [self participantNameForAddress:sortedAddresses[0] transaction:transaction];
return [NSString stringWithFormat:onlyOneFormat, name];
} else {
return someoneString;
}
}
- (void)updateWithHasEnded:(BOOL)hasEnded transaction:(SDSAnyWriteTransaction *)transaction
{
[self anyUpdateGroupCallMessageWithTransaction:transaction
block:^(OWSGroupCallMessage *message) {
message.hasEnded = hasEnded;
message.joinedMemberUuids = @[];
}];
}
- (void)updateWithJoinedMemberUuids:(NSArray<NSUUID *> *)joinedMemberUuids
creatorUuid:(NSUUID *)uuid
transaction:(SDSAnyWriteTransaction *)transaction
{
[self anyUpdateGroupCallMessageWithTransaction:transaction
block:^(OWSGroupCallMessage *message) {
message.hasEnded = joinedMemberUuids.count == 0;
message.creatorUuid = uuid.UUIDString;
message.joinedMemberUuids = [joinedMemberUuids
map:^(NSUUID *uuid) { return uuid.UUIDString; }];
}];
}
#pragma mark - Private
- (NSString *)participantNameForAddress:(SignalServiceAddress *)address transaction:(SDSAnyReadTransaction *)transaction
{
if (address.isLocalAddress) {
return OWSLocalizedString(@"YOU", "Second person pronoun to represent the local user.");
} else {
return [self.contactsManager displayNameForAddress:address transaction:transaction];
}
}
@end
NS_ASSUME_NONNULL_END