Skip to content

Commit 7106eee

Browse files
committed
Call notifications are deletable
// FREEBIE
1 parent 405990a commit 7106eee

File tree

10 files changed

+200
-111
lines changed

10 files changed

+200
-111
lines changed

Signal/src/Models/OWSCall.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
#import "OWSMessageData.h"
55
#import "TSMessageAdapter.h"
66

7+
NS_ASSUME_NONNULL_BEGIN
8+
9+
@class TSCall;
10+
711
typedef enum : NSUInteger {
812
kCallOutgoing = 1,
913
kCallIncoming = 2,
@@ -15,6 +19,18 @@ typedef enum : NSUInteger {
1519

1620
@interface OWSCall : NSObject <OWSMessageData, NSCoding, NSCopying>
1721

22+
#pragma mark - Initialization
23+
24+
- (instancetype)initWithCallRecord:(TSCall *)callRecord;
25+
26+
- (instancetype)initWithCallerId:(NSString *)callerId
27+
callerDisplayName:(NSString *)callerDisplayName
28+
date:(nullable NSDate *)date
29+
status:(CallStatus)status
30+
displayString:(NSString *)detailString NS_DESIGNATED_INITIALIZER;
31+
32+
- (instancetype)init NS_UNAVAILABLE;
33+
1834
/*
1935
* Returns the string Id of the user who initiated the call
2036
*/
@@ -41,14 +57,8 @@ typedef enum : NSUInteger {
4157
*/
4258
@property (nonatomic, copy) NSString *detailString;
4359

44-
#pragma mark - Initialization
45-
46-
- (instancetype)initWithCallerId:(NSString *)callerId
47-
callerDisplayName:(NSString *)callerDisplayName
48-
date:(NSDate *)date
49-
status:(CallStatus)status
50-
displayString:(NSString *)detailString NS_DESIGNATED_INITIALIZER;
51-
5260
- (NSString *)dateText;
5361

5462
@end
63+
64+
NS_ASSUME_NONNULL_END

Signal/src/Models/OWSCall.m

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved.
33

44
#import "OWSCall.h"
5+
#import "TSCall.h"
6+
#import "TSContactThread.h"
57
#import <JSQMessagesViewController/JSQMessagesTimestampFormatter.h>
68
#import <JSQMessagesViewController/UIImage+JSQMessages.h>
9+
#import <SignalServiceKit/TSCall.h>
10+
11+
NS_ASSUME_NONNULL_BEGIN
712

813
@interface OWSCall ()
914

@@ -13,26 +18,76 @@ @interface OWSCall ()
1318
@property (nonatomic) BOOL shouldStartExpireTimer;
1419
@property (nonatomic) uint64_t expiresAtSeconds;
1520
@property (nonatomic) uint32_t expiresInSeconds;
21+
@property (nonatomic) TSInteraction *interaction;
1622

1723
@end
1824

1925
@implementation OWSCall
2026

2127
#pragma mark - Initialzation
2228

23-
- (id)init
29+
- (instancetype)initWithCallRecord:(TSCall *)callRecord
2430
{
25-
NSAssert(NO,
26-
@"%s is not a valid initializer for %@. Use %@ instead",
27-
__PRETTY_FUNCTION__,
28-
[self class],
29-
NSStringFromSelector(@selector(initWithCallerId:callerDisplayName:date:status:displayString:)));
30-
return [self initWithCallerId:nil callerDisplayName:nil date:nil status:0 displayString:nil];
31+
TSThread *thread = callRecord.thread;
32+
TSContactThread *contactThread;
33+
if ([thread isKindOfClass:[TSContactThread class]]) {
34+
contactThread = (TSContactThread *)thread;
35+
} else {
36+
DDLogError(@"%@ Unexpected thread type: %@", self.tag, thread);
37+
}
38+
39+
40+
CallStatus status = 0;
41+
switch (callRecord.callType) {
42+
case RPRecentCallTypeOutgoing:
43+
status = kCallOutgoing;
44+
break;
45+
case RPRecentCallTypeMissed:
46+
status = kCallMissed;
47+
break;
48+
case RPRecentCallTypeIncoming:
49+
status = kCallIncoming;
50+
break;
51+
default:
52+
status = kCallIncoming;
53+
break;
54+
}
55+
56+
NSString *name = contactThread.name;
57+
NSString *detailString;
58+
switch (status) {
59+
case kCallMissed:
60+
detailString = [NSString stringWithFormat:NSLocalizedString(@"MSGVIEW_MISSED_CALL", nil), name];
61+
break;
62+
case kCallIncoming:
63+
detailString = [NSString stringWithFormat:NSLocalizedString(@"MSGVIEW_RECEIVED_CALL", nil), name];
64+
break;
65+
case kCallOutgoing:
66+
detailString = [NSString stringWithFormat:NSLocalizedString(@"MSGVIEW_YOU_CALLED", nil), name];
67+
break;
68+
default:
69+
detailString = @"";
70+
break;
71+
}
72+
73+
self = [self initWithCallerId:contactThread.contactIdentifier
74+
callerDisplayName:name
75+
date:callRecord.date
76+
status:status
77+
displayString:detailString];
78+
79+
if (!self) {
80+
return self;
81+
}
82+
83+
_interaction = callRecord;
84+
85+
return self;
3186
}
3287

3388
- (instancetype)initWithCallerId:(NSString *)senderId
3489
callerDisplayName:(NSString *)senderDisplayName
35-
date:(NSDate *)date
90+
date:(nullable NSDate *)date
3691
status:(CallStatus)status
3792
displayString:(NSString *)detailString
3893
{
@@ -105,11 +160,18 @@ - (NSString *)description
105160

106161
- (BOOL)canPerformEditingAction:(SEL)action
107162
{
108-
return NO;
163+
return action == @selector(delete:);
109164
}
110165

111166
- (void)performEditingAction:(SEL)action
112167
{
168+
// Deletes are always handled by TSMessageAdapter
169+
if (action == @selector(delete:)) {
170+
DDLogDebug(@"%@ Deleting interaction with uniqueId: %@", self.tag, self.interaction.uniqueId);
171+
[self.interaction remove];
172+
return;
173+
}
174+
113175
// Shouldn't get here, as only supported actions should be exposed via canPerformEditingAction
114176
NSString *actionString = NSStringFromSelector(action);
115177
DDLogError(@"%@ '%@' action unsupported", self.tag, actionString);
@@ -124,7 +186,7 @@ - (BOOL)isMediaMessage
124186

125187
#pragma mark - NSCoding
126188

127-
- (instancetype)initWithCoder:(NSCoder *)aDecoder
189+
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
128190
{
129191
NSString *senderId = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(senderId))];
130192
NSString *senderDisplayName = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(senderDisplayName))];
@@ -149,7 +211,7 @@ - (void)encodeWithCoder:(NSCoder *)aCoder
149211

150212
#pragma mark - NSCopying
151213

152-
- (instancetype)copyWithZone:(NSZone *)zone
214+
- (instancetype)copyWithZone:(nullable NSZone *)zone
153215
{
154216
return [[[self class] allocWithZone:zone] initWithCallerId:self.senderId
155217
callerDisplayName:self.senderDisplayName
@@ -181,3 +243,5 @@ - (NSString *)tag
181243
}
182244

183245
@end
246+
247+
NS_ASSUME_NONNULL_END

Signal/src/Models/OWSMessagesBubblesSizeCalculator.m

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) 2016 Open Whisper Systems. All rights reserved.
22

33
#import "OWSMessagesBubblesSizeCalculator.h"
4+
#import "OWSCall.h"
45
#import "OWSDisplayedMessageCollectionViewCell.h"
56
#import "TSMessageAdapter.h"
67
#import "UIFont+OWS.h"
@@ -49,6 +50,10 @@ - (CGSize)messageBubbleSizeForMessageData:(id<JSQMessageData>)messageData
4950
}
5051
}
5152

53+
if ([messageData isKindOfClass:[OWSCall class]]) {
54+
return [self messageBubbleSizeForCallData:messageData atIndexPath:indexPath withLayout:layout];
55+
}
56+
5257
CGSize size;
5358
// BEGIN HACK iOS10EmojiBug see: https://github.com/WhisperSystems/Signal-iOS/issues/1368
5459
BOOL isIOS10OrGreater =
@@ -243,6 +248,39 @@ - (CGSize)messageBubbleSizeForInfoMessageData:(id<JSQMessageData>)messageData
243248
return finalSize;
244249
}
245250

251+
- (CGSize)messageBubbleSizeForCallData:(id<JSQMessageData>)messageData
252+
atIndexPath:(NSIndexPath *)indexPath
253+
withLayout:(JSQMessagesCollectionViewFlowLayout *)layout
254+
{
255+
NSValue *cachedSize = [self.cache objectForKey:@([messageData messageHash])];
256+
if (cachedSize != nil) {
257+
return [cachedSize CGSizeValue];
258+
}
259+
260+
CGFloat horizontalInsetsTotal = 0.0;
261+
CGFloat maximumTextWidth = [self textBubbleWidthForLayout:layout] - horizontalInsetsTotal;
262+
263+
CGRect stringRect = [[messageData text]
264+
boundingRectWithSize:CGSizeMake(maximumTextWidth, CGFLOAT_MAX)
265+
options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
266+
attributes:@{
267+
NSFontAttributeName : [UIFont ows_dynamicTypeBodyFont]
268+
} // Hack to use a slightly larger than actual font, because I'm seeing messages with higher line
269+
// count get clipped.
270+
context:nil];
271+
272+
CGSize stringSize = CGRectIntegral(stringRect).size;
273+
274+
CGFloat verticalInsets = 0;
275+
CGFloat finalWidth = maximumTextWidth + horizontalInsetsTotal;
276+
277+
CGSize finalSize = CGSizeMake(finalWidth, stringSize.height + verticalInsets);
278+
279+
[self.cache setObject:[NSValue valueWithCGSize:finalSize] forKey:@([messageData messageHash])];
280+
281+
return finalSize;
282+
}
283+
246284
@end
247285

248286
NS_ASSUME_NONNULL_END

Signal/src/Models/TSMessageAdapaters/OWSMessageData.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ typedef NS_ENUM(NSInteger, TSMessageAdapterType) {
1919
@protocol OWSMessageData <JSQMessageData, OWSMessageEditing>
2020

2121
@property (nonatomic, readonly) TSMessageAdapterType messageType;
22+
@property (nonatomic, readonly) TSInteraction *interaction;
2223
@property (nonatomic, readonly) BOOL isExpiringMessage;
2324
@property (nonatomic, readonly) BOOL shouldStartExpireTimer;
2425
@property (nonatomic, readonly) uint64_t expiresAtSeconds;

Signal/src/Models/TSMessageAdapaters/OWSMessageEditing.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright © 2016 Open Whisper Systems. All rights reserved.
22
// Created by Michael Kirk on 3/11/16.
33

4+
@class TSInteraction;
5+
46
NS_ASSUME_NONNULL_BEGIN
57

68
@protocol OWSMessageEditing <NSObject>

Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
2020

2121
+ (id<OWSMessageData>)messageViewDataWithInteraction:(TSInteraction *)interaction inThread:(TSThread *)thread;
2222

23-
@property TSInteraction *interaction;
23+
@property (nonatomic) TSInteraction *interaction;
2424

2525
@end
2626

Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,8 @@ - (instancetype)initWithInteraction:(TSInteraction *)interaction
169169
}
170170
}
171171
} else if ([interaction isKindOfClass:[TSCall class]]) {
172-
adapter.messageBody = @"Placeholder for TSCalls";
173-
adapter.messageType = TSCallAdapter;
174-
OWSCall *call = [self owsCallForTSCall:(TSCall *)interaction thread:(TSContactThread *)thread];
175-
return call;
172+
TSCall *callRecord = (TSCall *)interaction;
173+
return [[OWSCall alloc] initWithCallRecord:callRecord];
176174
} else if ([interaction isKindOfClass:[TSInfoMessage class]]) {
177175
TSInfoMessage *infoMessage = (TSInfoMessage *)interaction;
178176
adapter.infoMessageType = infoMessage.messageType;
@@ -209,48 +207,6 @@ - (instancetype)initWithInteraction:(TSInteraction *)interaction
209207
return adapter;
210208
}
211209

212-
+ (OWSCall *)owsCallForTSCall:(TSCall *)call thread:(TSContactThread *)thread {
213-
CallStatus status = 0;
214-
NSString *name = thread.name;
215-
NSString *detailString = @"";
216-
217-
switch (call.callType) {
218-
case RPRecentCallTypeOutgoing:
219-
status = kCallOutgoing;
220-
break;
221-
case RPRecentCallTypeMissed:
222-
status = kCallMissed;
223-
break;
224-
case RPRecentCallTypeIncoming:
225-
status = kCallIncoming;
226-
break;
227-
default:
228-
status = kCallIncoming;
229-
break;
230-
}
231-
232-
switch (status) {
233-
case kCallMissed:
234-
detailString = [NSString stringWithFormat:NSLocalizedString(@"MSGVIEW_MISSED_CALL", nil), name];
235-
break;
236-
case kCallIncoming:
237-
detailString = [NSString stringWithFormat:NSLocalizedString(@"MSGVIEW_RECEIVED_CALL", nil), name];
238-
break;
239-
case kCallOutgoing:
240-
detailString = [NSString stringWithFormat:NSLocalizedString(@"MSGVIEW_YOU_CALLED", nil), name];
241-
break;
242-
default:
243-
break;
244-
}
245-
246-
OWSCall *owsCall = [[OWSCall alloc] initWithCallerId:thread.contactIdentifier
247-
callerDisplayName:thread.name
248-
date:call.date
249-
status:status
250-
displayString:detailString];
251-
return owsCall;
252-
}
253-
254210
- (NSString *)senderId {
255211
if (_senderId) {
256212
return _senderId;

Signal/src/view controllers/MessagesViewController.m

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -829,12 +829,18 @@ - (OWSCallCollectionViewCell *)loadCallCellForCall:(OWSCall *)call atIndexPath:(
829829
[attributedText setAttributes:@{ NSFontAttributeName: regularFont }
830830
range:range];
831831
}
832-
callCell.cellLabel.attributedText = attributedText;
833-
callCell.cellLabel.numberOfLines = 0; // uses as many lines as it needs
834-
callCell.cellLabel.textColor = [UIColor ows_materialBlueColor];
832+
callCell.textView.text = nil;
833+
callCell.textView.attributedText = attributedText;
835834

835+
callCell.textView.textAlignment = NSTextAlignmentCenter;
836+
callCell.textView.textColor = [UIColor ows_materialBlueColor];
836837
callCell.layer.shouldRasterize = YES;
837838
callCell.layer.rasterizationScale = [UIScreen mainScreen].scale;
839+
840+
// Disable text selectability. Specifying this in prepareForReuse/awakeFromNib was not sufficient.
841+
callCell.textView.userInteractionEnabled = NO;
842+
callCell.textView.selectable = NO;
843+
838844
return callCell;
839845
}
840846

@@ -865,6 +871,7 @@ - (OWSDisplayedMessageCollectionViewCell *)loadInfoMessageCellForMessage:(OWSInf
865871
// Disable text selectability. Specifying this in prepareForReuse/awakeFromNib was not sufficient.
866872
infoCell.textView.userInteractionEnabled = NO;
867873
infoCell.textView.selectable = NO;
874+
868875
infoCell.messageBubbleContainerView.layer.borderColor = [[UIColor ows_infoMessageBorderColor] CGColor];
869876
infoCell.headerImageView.image = [UIImage imageNamed:@"warning_white"];
870877

@@ -876,9 +883,11 @@ - (OWSDisplayedMessageCollectionViewCell *)loadErrorMessageCellForMessage:(OWSEr
876883
{
877884
OWSDisplayedMessageCollectionViewCell *errorCell = [self loadDisplayedMessageCollectionViewCellForIndexPath:indexPath];
878885
errorCell.textView.text = [errorMessage text];
886+
879887
// Disable text selectability. Specifying this in prepareForReuse/awakeFromNib was not sufficient.
880888
errorCell.textView.userInteractionEnabled = NO;
881889
errorCell.textView.selectable = NO;
890+
882891
errorCell.messageBubbleContainerView.layer.borderColor = [[UIColor ows_errorMessageBorderColor] CGColor];
883892
errorCell.headerImageView.image = [UIImage imageNamed:@"error_white"];
884893

0 commit comments

Comments
 (0)