Signal-iOS/SignalServiceKit/Calls/Group/OWSGroupCallMessage.m
2023-08-23 23:47:14 -05:00

283 lines
11 KiB
Objective-C

//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
#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;
/// These store ACI strings, but the properties say "UUID" for historical reasons.
@property (nonatomic, nullable) NSArray<NSString *> *joinedMemberUuids;
@property (nonatomic, nullable) NSString *creatorUuid;
@property (nonatomic) BOOL hasEnded;
@end
#pragma mark -
@implementation OWSGroupCallMessage
- (instancetype)initWithEraId:(NSString *)eraId
joinedMemberAcis:(NSArray<AciObjC *> *)joinedMemberAcis
creatorAci:(nullable AciObjC *)creatorAci
thread:(TSGroupThread *)thread
sentAtTimestamp:(uint64_t)sentAtTimestamp
{
self = [super initInteractionWithTimestamp:sentAtTimestamp thread:thread];
if (!self) {
return self;
}
_eraId = eraId;
_joinedMemberUuids = [joinedMemberAcis map:^(AciObjC *aci) { return aci.serviceIdUppercaseString; }];
_creatorUuid = creatorAci.serviceIdUppercaseString;
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 *aciString) { return [[SignalServiceAddress alloc] initWithAciString:aciString]; }];
}
- (nullable SignalServiceAddress *)creatorAddress
{
if (self.creatorUuid) {
return [[SignalServiceAddress alloc] initWithAciString: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
shouldClearNotifications:(BOOL)shouldClearNotifications
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)updateWithJoinedMemberAcis:(NSArray<AciObjC *> *)joinedMemberAcis
creatorAci:(AciObjC *)creatorAci
tx:(SDSAnyWriteTransaction *)tx
{
[self anyUpdateGroupCallMessageWithTransaction:tx
block:^(OWSGroupCallMessage *message) {
message.hasEnded = joinedMemberAcis.count == 0;
message.creatorUuid = creatorAci.serviceIdUppercaseString;
message.joinedMemberUuids =
[joinedMemberAcis map:^(AciObjC *joinedMemberAci) {
return joinedMemberAci.serviceIdUppercaseString;
}];
}];
}
#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