mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Add unread indicator.
// FREEBIE
This commit is contained in:
parent
745172a6d1
commit
ac458cc7ad
|
@ -72,6 +72,9 @@
|
||||||
34D5CCB11EAE7E7F005515DB /* SelectRecipientViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCB01EAE7E7F005515DB /* SelectRecipientViewController.m */; };
|
34D5CCB11EAE7E7F005515DB /* SelectRecipientViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D5CCB01EAE7E7F005515DB /* SelectRecipientViewController.m */; };
|
||||||
34DFCB851E8E04B500053165 /* AddToBlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */; };
|
34DFCB851E8E04B500053165 /* AddToBlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */; };
|
||||||
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; };
|
34E3E5681EC4B19400495BAC /* AudioProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */; };
|
||||||
|
34F3089C1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F3089B1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m */; };
|
||||||
|
34F3089F1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F3089E1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m */; };
|
||||||
|
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F308A11ECB469700BB7697 /* OWSBezierPathView.m */; };
|
||||||
34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */; };
|
34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */; };
|
||||||
450573FE1E78A06D00615BB4 /* OWS103EnableVideoCalling.m in Sources */ = {isa = PBXBuildFile; fileRef = 450573FD1E78A06D00615BB4 /* OWS103EnableVideoCalling.m */; };
|
450573FE1E78A06D00615BB4 /* OWS103EnableVideoCalling.m in Sources */ = {isa = PBXBuildFile; fileRef = 450573FD1E78A06D00615BB4 /* OWS103EnableVideoCalling.m */; };
|
||||||
4505C2BF1E648EA300CEBF41 /* ExperienceUpgrade.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4505C2BE1E648EA300CEBF41 /* ExperienceUpgrade.swift */; };
|
4505C2BF1E648EA300CEBF41 /* ExperienceUpgrade.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4505C2BE1E648EA300CEBF41 /* ExperienceUpgrade.swift */; };
|
||||||
|
@ -472,6 +475,12 @@
|
||||||
34DFCB831E8E04B400053165 /* AddToBlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToBlockListViewController.h; sourceTree = "<group>"; };
|
34DFCB831E8E04B400053165 /* AddToBlockListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddToBlockListViewController.h; sourceTree = "<group>"; };
|
||||||
34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToBlockListViewController.m; sourceTree = "<group>"; };
|
34DFCB841E8E04B500053165 /* AddToBlockListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddToBlockListViewController.m; sourceTree = "<group>"; };
|
||||||
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProgressView.swift; sourceTree = "<group>"; };
|
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioProgressView.swift; sourceTree = "<group>"; };
|
||||||
|
34F3089A1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSUnreadIndicatorInteraction.h; sourceTree = "<group>"; };
|
||||||
|
34F3089B1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSUnreadIndicatorInteraction.m; sourceTree = "<group>"; };
|
||||||
|
34F3089D1ECA580B00BB7697 /* OWSUnreadIndicatorCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUnreadIndicatorCell.h; sourceTree = "<group>"; };
|
||||||
|
34F3089E1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSUnreadIndicatorCell.m; sourceTree = "<group>"; };
|
||||||
|
34F308A01ECB469700BB7697 /* OWSBezierPathView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBezierPathView.h; sourceTree = "<group>"; };
|
||||||
|
34F308A11ECB469700BB7697 /* OWSBezierPathView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBezierPathView.m; sourceTree = "<group>"; };
|
||||||
34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = "<group>"; };
|
34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSAnyTouchGestureRecognizer.h; path = views/OWSAnyTouchGestureRecognizer.h; sourceTree = "<group>"; };
|
||||||
34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAnyTouchGestureRecognizer.m; path = views/OWSAnyTouchGestureRecognizer.m; sourceTree = "<group>"; };
|
34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSAnyTouchGestureRecognizer.m; path = views/OWSAnyTouchGestureRecognizer.m; sourceTree = "<group>"; };
|
||||||
450573FC1E78A06D00615BB4 /* OWS103EnableVideoCalling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWS103EnableVideoCalling.h; path = Migrations/OWS103EnableVideoCalling.h; sourceTree = "<group>"; };
|
450573FC1E78A06D00615BB4 /* OWS103EnableVideoCalling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWS103EnableVideoCalling.h; path = Migrations/OWS103EnableVideoCalling.h; sourceTree = "<group>"; };
|
||||||
|
@ -1307,6 +1316,8 @@
|
||||||
4531C9C21DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.h */,
|
4531C9C21DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.h */,
|
||||||
4531C9C31DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m */,
|
4531C9C31DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m */,
|
||||||
3453D8E91EC0D4ED003F9E6F /* OWSAlerts.swift */,
|
3453D8E91EC0D4ED003F9E6F /* OWSAlerts.swift */,
|
||||||
|
34F308A01ECB469700BB7697 /* OWSBezierPathView.h */,
|
||||||
|
34F308A11ECB469700BB7697 /* OWSBezierPathView.m */,
|
||||||
45C681B91D305C080050903A /* OWSCallCollectionViewCell.h */,
|
45C681B91D305C080050903A /* OWSCallCollectionViewCell.h */,
|
||||||
45C681BA1D305C080050903A /* OWSCallCollectionViewCell.m */,
|
45C681BA1D305C080050903A /* OWSCallCollectionViewCell.m */,
|
||||||
45C681C01D305C9E0050903A /* OWSCallCollectionViewCell.xib */,
|
45C681C01D305C9E0050903A /* OWSCallCollectionViewCell.xib */,
|
||||||
|
@ -1327,7 +1338,11 @@
|
||||||
45F2B1961D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib */,
|
45F2B1961D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib */,
|
||||||
34330AA11E79686200DF2FB9 /* OWSProgressView.h */,
|
34330AA11E79686200DF2FB9 /* OWSProgressView.h */,
|
||||||
34330AA21E79686200DF2FB9 /* OWSProgressView.m */,
|
34330AA21E79686200DF2FB9 /* OWSProgressView.m */,
|
||||||
|
34F3089D1ECA580B00BB7697 /* OWSUnreadIndicatorCell.h */,
|
||||||
|
34F3089E1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m */,
|
||||||
45A6DAD51EBBF85500893231 /* ReminderView.swift */,
|
45A6DAD51EBBF85500893231 /* ReminderView.swift */,
|
||||||
|
34F3089A1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.h */,
|
||||||
|
34F3089B1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m */,
|
||||||
);
|
);
|
||||||
name = Views;
|
name = Views;
|
||||||
path = views;
|
path = views;
|
||||||
|
@ -2045,6 +2060,7 @@
|
||||||
343D3D9B1E9283F100165CA4 /* BlockListUIUtils.m in Sources */,
|
343D3D9B1E9283F100165CA4 /* BlockListUIUtils.m in Sources */,
|
||||||
34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */,
|
34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */,
|
||||||
76EB063A18170B33006006FC /* FunctionalUtil.m in Sources */,
|
76EB063A18170B33006006FC /* FunctionalUtil.m in Sources */,
|
||||||
|
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */,
|
||||||
76EB058A18170B33006006FC /* Release.m in Sources */,
|
76EB058A18170B33006006FC /* Release.m in Sources */,
|
||||||
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */,
|
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */,
|
||||||
450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */,
|
450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */,
|
||||||
|
@ -2074,6 +2090,7 @@
|
||||||
45666F561D9B2827008FE134 /* OWSScrubbingLogFormatter.m in Sources */,
|
45666F561D9B2827008FE134 /* OWSScrubbingLogFormatter.m in Sources */,
|
||||||
45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */,
|
45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */,
|
||||||
45C681C61D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m in Sources */,
|
45C681C61D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m in Sources */,
|
||||||
|
34F3089F1ECA580B00BB7697 /* OWSUnreadIndicatorCell.m in Sources */,
|
||||||
34B3F8861E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m in Sources */,
|
34B3F8861E8DF1700035BE1A /* NotificationSettingsOptionsViewController.m in Sources */,
|
||||||
452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */,
|
452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */,
|
||||||
452EA0971EA662330078744B /* AttachmentPointerAdapter.swift in Sources */,
|
452EA0971EA662330078744B /* AttachmentPointerAdapter.swift in Sources */,
|
||||||
|
@ -2158,6 +2175,7 @@
|
||||||
34B3F87D1E8DF1700035BE1A /* FullImageViewController.m in Sources */,
|
34B3F87D1E8DF1700035BE1A /* FullImageViewController.m in Sources */,
|
||||||
45666F7B1D9C0533008FE134 /* OWSDatabaseMigration.m in Sources */,
|
45666F7B1D9C0533008FE134 /* OWSDatabaseMigration.m in Sources */,
|
||||||
B90418E6183E9DD40038554A /* DateUtil.m in Sources */,
|
B90418E6183E9DD40038554A /* DateUtil.m in Sources */,
|
||||||
|
34F3089C1ECA4CDB00BB7697 /* TSUnreadIndicatorInteraction.m in Sources */,
|
||||||
45B201761DAECBFE00C461E0 /* HighlightableLabel.swift in Sources */,
|
45B201761DAECBFE00C461E0 /* HighlightableLabel.swift in Sources */,
|
||||||
459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */,
|
459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */,
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,6 +15,7 @@ typedef NS_ENUM(NSInteger, TSMessageAdapterType) {
|
||||||
TSErrorMessageAdapter,
|
TSErrorMessageAdapter,
|
||||||
TSMediaAttachmentAdapter,
|
TSMediaAttachmentAdapter,
|
||||||
TSGenericTextMessageAdapter, // Used when message direction is unknown (outgoing or incoming)
|
TSGenericTextMessageAdapter, // Used when message direction is unknown (outgoing or incoming)
|
||||||
|
TSUnreadIndicatorAdapter,
|
||||||
};
|
};
|
||||||
|
|
||||||
@protocol OWSMessageData <JSQMessageData, OWSMessageEditing>
|
@protocol OWSMessageData <JSQMessageData, OWSMessageEditing>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#import "TSMessageAdapter.h"
|
||||||
#import "AttachmentSharing.h"
|
#import "AttachmentSharing.h"
|
||||||
#import "OWSCall.h"
|
#import "OWSCall.h"
|
||||||
#import "Signal-Swift.h"
|
#import "Signal-Swift.h"
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
#import "TSIncomingMessage.h"
|
#import "TSIncomingMessage.h"
|
||||||
#import "TSInfoMessage.h"
|
#import "TSInfoMessage.h"
|
||||||
#import "TSOutgoingMessage.h"
|
#import "TSOutgoingMessage.h"
|
||||||
|
#import "TSUnreadIndicatorInteraction.h"
|
||||||
#import <MobileCoreServices/MobileCoreServices.h>
|
#import <MobileCoreServices/MobileCoreServices.h>
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
@ -119,6 +121,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
adapter.senderDisplayName = NSLocalizedString(@"ME_STRING", @"");
|
adapter.senderDisplayName = NSLocalizedString(@"ME_STRING", @"");
|
||||||
adapter.messageType = TSOutgoingMessageAdapter;
|
adapter.messageType = TSOutgoingMessageAdapter;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
OWSAssert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([interaction isKindOfClass:[TSIncomingMessage class]] ||
|
if ([interaction isKindOfClass:[TSIncomingMessage class]] ||
|
||||||
|
@ -216,11 +220,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
displayString:@""];
|
displayString:@""];
|
||||||
return call;
|
return call;
|
||||||
}
|
}
|
||||||
} else {
|
} else if ([interaction isKindOfClass:[TSUnreadIndicatorInteraction class]]) {
|
||||||
|
adapter.messageType = TSUnreadIndicatorAdapter;
|
||||||
|
} else if ([interaction isKindOfClass:[TSErrorMessage class]]) {
|
||||||
TSErrorMessage *errorMessage = (TSErrorMessage *)interaction;
|
TSErrorMessage *errorMessage = (TSErrorMessage *)interaction;
|
||||||
adapter.errorMessageType = errorMessage.errorType;
|
adapter.errorMessageType = errorMessage.errorType;
|
||||||
adapter.messageBody = errorMessage.description;
|
adapter.messageBody = errorMessage.description;
|
||||||
adapter.messageType = TSErrorMessageAdapter;
|
adapter.messageType = TSErrorMessageAdapter;
|
||||||
|
} else {
|
||||||
|
OWSAssert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([interaction isKindOfClass:[TSOutgoingMessage class]]) {
|
if ([interaction isKindOfClass:[TSOutgoingMessage class]]) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#import "OWSMessagesBubblesSizeCalculator.h"
|
#import "OWSMessagesBubblesSizeCalculator.h"
|
||||||
#import "OWSOutgoingMessageCollectionViewCell.h"
|
#import "OWSOutgoingMessageCollectionViewCell.h"
|
||||||
#import "OWSUnknownContactBlockOfferMessage.h"
|
#import "OWSUnknownContactBlockOfferMessage.h"
|
||||||
|
#import "OWSUnreadIndicatorCell.h"
|
||||||
#import "PropertyListPreferences.h"
|
#import "PropertyListPreferences.h"
|
||||||
#import "Signal-Swift.h"
|
#import "Signal-Swift.h"
|
||||||
#import "SignalKeyingStorage.h"
|
#import "SignalKeyingStorage.h"
|
||||||
|
@ -707,6 +708,8 @@ typedef enum : NSUInteger {
|
||||||
_composeOnOpen = keyboardOnViewAppearing;
|
_composeOnOpen = keyboardOnViewAppearing;
|
||||||
_callOnOpen = callOnViewAppearing;
|
_callOnOpen = callOnViewAppearing;
|
||||||
|
|
||||||
|
[ThreadUtil createUnreadMessagesIndicatorIfNecessary:thread storageManager:self.storageManager];
|
||||||
|
|
||||||
[self markAllMessagesAsRead];
|
[self markAllMessagesAsRead];
|
||||||
|
|
||||||
[self.uiDatabaseConnection beginLongLivedReadTransaction];
|
[self.uiDatabaseConnection beginLongLivedReadTransaction];
|
||||||
|
@ -813,6 +816,9 @@ typedef enum : NSUInteger {
|
||||||
[self.collectionView registerNib:[OWSCallCollectionViewCell nib]
|
[self.collectionView registerNib:[OWSCallCollectionViewCell nib]
|
||||||
forCellWithReuseIdentifier:[OWSCallCollectionViewCell cellReuseIdentifier]];
|
forCellWithReuseIdentifier:[OWSCallCollectionViewCell cellReuseIdentifier]];
|
||||||
|
|
||||||
|
[self.collectionView registerClass:[OWSUnreadIndicatorCell class]
|
||||||
|
forCellWithReuseIdentifier:[OWSUnreadIndicatorCell cellReuseIdentifier]];
|
||||||
|
|
||||||
[self.collectionView registerNib:[OWSDisplayedMessageCollectionViewCell nib]
|
[self.collectionView registerNib:[OWSDisplayedMessageCollectionViewCell nib]
|
||||||
forCellWithReuseIdentifier:[OWSDisplayedMessageCollectionViewCell cellReuseIdentifier]];
|
forCellWithReuseIdentifier:[OWSDisplayedMessageCollectionViewCell cellReuseIdentifier]];
|
||||||
|
|
||||||
|
@ -1685,6 +1691,9 @@ typedef enum : NSUInteger {
|
||||||
case TSOutgoingMessageAdapter: {
|
case TSOutgoingMessageAdapter: {
|
||||||
cell = [self loadOutgoingCellForMessage:message atIndexPath:indexPath];
|
cell = [self loadOutgoingCellForMessage:message atIndexPath:indexPath];
|
||||||
} break;
|
} break;
|
||||||
|
case TSUnreadIndicatorAdapter: {
|
||||||
|
cell = [self loadUnreadIndicatorCell:indexPath];
|
||||||
|
} break;
|
||||||
default: {
|
default: {
|
||||||
DDLogWarn(@"using default cell constructor for message: %@", message);
|
DDLogWarn(@"using default cell constructor for message: %@", message);
|
||||||
cell = (JSQMessagesCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath];
|
cell = (JSQMessagesCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath];
|
||||||
|
@ -1712,6 +1721,7 @@ typedef enum : NSUInteger {
|
||||||
|
|
||||||
if (![cell isKindOfClass:[OWSIncomingMessageCollectionViewCell class]]) {
|
if (![cell isKindOfClass:[OWSIncomingMessageCollectionViewCell class]]) {
|
||||||
DDLogError(@"%@ Unexpected cell type: %@", self.tag, cell);
|
DDLogError(@"%@ Unexpected cell type: %@", self.tag, cell);
|
||||||
|
OWSAssert(0);
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1732,6 +1742,7 @@ typedef enum : NSUInteger {
|
||||||
|
|
||||||
if (![cell isKindOfClass:[OWSOutgoingMessageCollectionViewCell class]]) {
|
if (![cell isKindOfClass:[OWSOutgoingMessageCollectionViewCell class]]) {
|
||||||
DDLogError(@"%@ Unexpected cell type: %@", self.tag, cell);
|
DDLogError(@"%@ Unexpected cell type: %@", self.tag, cell);
|
||||||
|
OWSAssert(0);
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1752,6 +1763,18 @@ typedef enum : NSUInteger {
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (JSQMessagesCollectionViewCell *)loadUnreadIndicatorCell:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
OWSAssert(indexPath);
|
||||||
|
|
||||||
|
OWSUnreadIndicatorCell *cell =
|
||||||
|
[self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSUnreadIndicatorCell cellReuseIdentifier]
|
||||||
|
forIndexPath:indexPath];
|
||||||
|
[cell configure];
|
||||||
|
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
- (OWSCallCollectionViewCell *)loadCallCellForCall:(OWSCall *)call atIndexPath:(NSIndexPath *)indexPath
|
- (OWSCallCollectionViewCell *)loadCallCellForCall:(OWSCall *)call atIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
OWSCallCollectionViewCell *callCell = [self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSCallCollectionViewCell cellReuseIdentifier]
|
OWSCallCollectionViewCell *callCell = [self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSCallCollectionViewCell cellReuseIdentifier]
|
||||||
|
@ -2222,6 +2245,7 @@ typedef enum : NSUInteger {
|
||||||
[self handleErrorMessageTap:(TSErrorMessage *)interaction];
|
[self handleErrorMessageTap:(TSErrorMessage *)interaction];
|
||||||
break;
|
break;
|
||||||
case TSCallAdapter:
|
case TSCallAdapter:
|
||||||
|
case TSUnreadIndicatorAdapter:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DDLogDebug(@"Unhandled bubble touch for interaction: %@.", interaction);
|
DDLogDebug(@"Unhandled bubble touch for interaction: %@.", interaction);
|
||||||
|
|
|
@ -27,6 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
contactsManager:(OWSContactsManager *)contactsManager
|
contactsManager:(OWSContactsManager *)contactsManager
|
||||||
blockingManager:(OWSBlockingManager *)blockingManager;
|
blockingManager:(OWSBlockingManager *)blockingManager;
|
||||||
|
|
||||||
|
+ (void)createUnreadMessagesIndicatorIfNecessary:(TSThread *)thread storageManager:(TSStorageManager *)storageManager;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#import "ThreadUtil.h"
|
#import "ThreadUtil.h"
|
||||||
#import "OWSContactsManager.h"
|
#import "OWSContactsManager.h"
|
||||||
#import "Signal-Swift.h"
|
#import "Signal-Swift.h"
|
||||||
|
#import "TSUnreadIndicatorInteraction.h"
|
||||||
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
|
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
|
||||||
#import <SignalServiceKit/OWSBlockingManager.h>
|
#import <SignalServiceKit/OWSBlockingManager.h>
|
||||||
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
|
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
|
||||||
|
@ -178,6 +179,58 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (void)createUnreadMessagesIndicatorIfNecessary:(TSThread *)thread storageManager:(TSStorageManager *)storageManager
|
||||||
|
{
|
||||||
|
OWSAssert(thread);
|
||||||
|
OWSAssert(storageManager);
|
||||||
|
|
||||||
|
[storageManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||||
|
|
||||||
|
NSMutableArray *indicators = [NSMutableArray new];
|
||||||
|
__block TSMessage *firstUnreadMessage = nil;
|
||||||
|
// TODO: Will this approach be prohibitively expensive?
|
||||||
|
[[transaction ext:TSMessageDatabaseViewExtensionName]
|
||||||
|
enumerateRowsInGroup:thread.uniqueId
|
||||||
|
usingBlock:^(
|
||||||
|
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
|
||||||
|
|
||||||
|
if ([object isKindOfClass:[TSUnreadIndicatorInteraction class]]) {
|
||||||
|
[indicators addObject:object];
|
||||||
|
} else if ([object isKindOfClass:[TSIncomingMessage class]]) {
|
||||||
|
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)object;
|
||||||
|
if (!incomingMessage.wasRead) {
|
||||||
|
if (!firstUnreadMessage) {
|
||||||
|
firstUnreadMessage = incomingMessage;
|
||||||
|
} else {
|
||||||
|
OWSAssert([[firstUnreadMessage receiptDateForSorting]
|
||||||
|
compare:[incomingMessage receiptDateForSorting]]
|
||||||
|
== NSOrderedAscending);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
for (TSUnreadIndicatorInteraction *indicator in indicators) {
|
||||||
|
[indicator removeWithTransaction:transaction];
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL shouldHaveIndicator = firstUnreadMessage != nil;
|
||||||
|
if (!shouldHaveIndicator) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DDLogInfo(@"%@ Creating TSUnreadIndicatorInteraction", self.tag);
|
||||||
|
|
||||||
|
// We want the block offer to appear just before the first unread incoming
|
||||||
|
// message in the conversation timeline.
|
||||||
|
uint64_t indicatorTimestamp = firstUnreadMessage.timestamp - 1;
|
||||||
|
|
||||||
|
TSUnreadIndicatorInteraction *indicator =
|
||||||
|
[[TSUnreadIndicatorInteraction alloc] initWithTimestamp:indicatorTimestamp thread:thread];
|
||||||
|
[indicator saveWithTransaction:transaction];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Logging
|
#pragma mark - Logging
|
||||||
|
|
||||||
+ (NSString *)tag
|
+ (NSString *)tag
|
||||||
|
|
22
Signal/src/views/OWSBezierPathView.h
Normal file
22
Signal/src/views/OWSBezierPathView.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef void (^ConfigureShapeLayerBlock)(CAShapeLayer *layer, CGRect bounds);
|
||||||
|
|
||||||
|
@interface OWSBezierPathView : UIView
|
||||||
|
|
||||||
|
// Configure the view with this method if it uses a single Bezier path.
|
||||||
|
- (void)setConfigureShapeLayerBlock:(ConfigureShapeLayerBlock)configureShapeLayerBlock;
|
||||||
|
|
||||||
|
// Configure the view with this method if it uses multiple Bezier paths.
|
||||||
|
//
|
||||||
|
// Paths will be rendered in back-to-front order.
|
||||||
|
- (void)setConfigureShapeLayerBlocks:(NSArray<ConfigureShapeLayerBlock> *)configureShapeLayerBlocks;
|
||||||
|
|
||||||
|
// This method forces the view to reconstruct its layer content. It shouldn't
|
||||||
|
// be necessary to call this unless the ConfigureShapeLayerBlocks depend on external
|
||||||
|
// state which has changed.
|
||||||
|
- (void)updateLayers;
|
||||||
|
|
||||||
|
@end
|
110
Signal/src/views/OWSBezierPathView.m
Normal file
110
Signal/src/views/OWSBezierPathView.m
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "OWSBezierPathView.h"
|
||||||
|
|
||||||
|
@interface OWSBezierPathView ()
|
||||||
|
|
||||||
|
@property (nonatomic) NSArray<ConfigureShapeLayerBlock> *configureShapeLayerBlocks;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation OWSBezierPathView
|
||||||
|
|
||||||
|
- (id)init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
[self initCommon];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)initWithFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self) {
|
||||||
|
[self initCommon];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)initCommon
|
||||||
|
{
|
||||||
|
self.opaque = NO;
|
||||||
|
self.userInteractionEnabled = NO;
|
||||||
|
self.backgroundColor = [UIColor clearColor];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
BOOL didChangeSize = !CGSizeEqualToSize(frame.size, self.frame.size);
|
||||||
|
|
||||||
|
[super setFrame:frame];
|
||||||
|
|
||||||
|
if (didChangeSize) {
|
||||||
|
[self updateLayers];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setBounds:(CGRect)bounds
|
||||||
|
{
|
||||||
|
BOOL didChangeSize = !CGSizeEqualToSize(bounds.size, self.bounds.size);
|
||||||
|
|
||||||
|
[super setBounds:bounds];
|
||||||
|
|
||||||
|
if (didChangeSize) {
|
||||||
|
[self updateLayers];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setConfigureShapeLayerBlock:(ConfigureShapeLayerBlock)configureShapeLayerBlock
|
||||||
|
{
|
||||||
|
OWSAssert(configureShapeLayerBlock);
|
||||||
|
|
||||||
|
[self setConfigureShapeLayerBlocks:@[ configureShapeLayerBlock ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setConfigureShapeLayerBlocks:(NSArray<ConfigureShapeLayerBlock> *)configureShapeLayerBlocks
|
||||||
|
{
|
||||||
|
OWSAssert(configureShapeLayerBlocks.count > 0);
|
||||||
|
|
||||||
|
_configureShapeLayerBlocks = configureShapeLayerBlocks;
|
||||||
|
|
||||||
|
[self updateLayers];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateLayers
|
||||||
|
{
|
||||||
|
if (self.bounds.size.width <= 0.f || self.bounds.size.height <= 0.f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CALayer *layer in self.layer.sublayers) {
|
||||||
|
[layer removeFromSuperlayer];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ConfigureShapeLayerBlock configureShapeLayerBlock in self.configureShapeLayerBlocks) {
|
||||||
|
CAShapeLayer *shapeLayer = [CAShapeLayer new];
|
||||||
|
configureShapeLayerBlock(shapeLayer, self.bounds);
|
||||||
|
[self.layer addSublayer:shapeLayer];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self setNeedsDisplay];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Logging
|
||||||
|
|
||||||
|
+ (NSString *)tag
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)tag
|
||||||
|
{
|
||||||
|
return self.class.tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
12
Signal/src/views/OWSUnreadIndicatorCell.h
Normal file
12
Signal/src/views/OWSUnreadIndicatorCell.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <JSQMessagesViewController/JSQMessagesCollectionViewCell.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface OWSUnreadIndicatorCell : JSQMessagesCollectionViewCell
|
||||||
|
|
||||||
|
- (void)configure;
|
||||||
|
|
||||||
|
@end
|
77
Signal/src/views/OWSUnreadIndicatorCell.m
Normal file
77
Signal/src/views/OWSUnreadIndicatorCell.m
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "OWSUnreadIndicatorCell.h"
|
||||||
|
#import "OWSBezierPathView.h"
|
||||||
|
#import "UIColor+OWS.h"
|
||||||
|
#import "UIFont+OWS.h"
|
||||||
|
#import <JSQMessagesViewController/UIView+JSQMessages.h>
|
||||||
|
|
||||||
|
@interface OWSUnreadIndicatorCell ()
|
||||||
|
|
||||||
|
@property (nonatomic) UILabel *label;
|
||||||
|
@property (nonatomic) OWSBezierPathView *leftPathView;
|
||||||
|
@property (nonatomic) OWSBezierPathView *rightPathView;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
@implementation OWSUnreadIndicatorCell
|
||||||
|
|
||||||
|
+ (NSString *)cellReuseIdentifier
|
||||||
|
{
|
||||||
|
return NSStringFromClass([self class]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)configure
|
||||||
|
{
|
||||||
|
self.backgroundColor = [UIColor whiteColor];
|
||||||
|
|
||||||
|
if (!self.label) {
|
||||||
|
self.label = [UILabel new];
|
||||||
|
self.label.text = NSLocalizedString(
|
||||||
|
@"MESSAGES_VIEW_UNREAD_INDICATOR", @"Indicator that separates read from unread messages.");
|
||||||
|
self.label.textColor = [UIColor ows_infoMessageBorderColor];
|
||||||
|
self.label.font = [UIFont ows_mediumFontWithSize:12.f];
|
||||||
|
[self.contentView addSubview:self.label];
|
||||||
|
|
||||||
|
CGFloat kLineThickness = 0.5f;
|
||||||
|
CGFloat kLineMargin = 5.f;
|
||||||
|
ConfigureShapeLayerBlock configureShapeLayerBlock = ^(CAShapeLayer *layer, CGRect bounds) {
|
||||||
|
OWSCAssert(layer);
|
||||||
|
|
||||||
|
CGRect pathBounds
|
||||||
|
= CGRectMake(0, (bounds.size.height - kLineThickness) * 0.5f, bounds.size.width, kLineThickness);
|
||||||
|
pathBounds = CGRectInset(pathBounds, kLineMargin, 0);
|
||||||
|
UIBezierPath *path = [UIBezierPath bezierPathWithRect:pathBounds];
|
||||||
|
layer.path = path.CGPath;
|
||||||
|
layer.fillColor = [UIColor ows_infoMessageBorderColor].CGColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.leftPathView = [OWSBezierPathView new];
|
||||||
|
self.leftPathView.configureShapeLayerBlock = configureShapeLayerBlock;
|
||||||
|
[self.contentView addSubview:self.leftPathView];
|
||||||
|
|
||||||
|
self.rightPathView = [OWSBezierPathView new];
|
||||||
|
self.rightPathView.configureShapeLayerBlock = configureShapeLayerBlock;
|
||||||
|
[self.contentView addSubview:self.rightPathView];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutSubviews
|
||||||
|
{
|
||||||
|
CGSize labelSize = [self.label sizeThatFits:CGSizeZero];
|
||||||
|
self.label.frame = CGRectMake(round(self.bounds.origin.x + (self.bounds.size.width - labelSize.width) * 0.5f),
|
||||||
|
round(self.bounds.origin.y + (self.bounds.size.height - labelSize.height) * 0.5f),
|
||||||
|
labelSize.width,
|
||||||
|
labelSize.height);
|
||||||
|
self.leftPathView.frame = CGRectMake(0, 0, self.label.frame.origin.x, self.bounds.size.height);
|
||||||
|
self.rightPathView.frame = CGRectMake(self.label.frame.origin.x + self.label.frame.size.width,
|
||||||
|
0,
|
||||||
|
self.bounds.size.width - (self.label.frame.origin.x + self.label.frame.size.width),
|
||||||
|
self.bounds.size.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
17
Signal/src/views/TSUnreadIndicatorInteraction.h
Normal file
17
Signal/src/views/TSUnreadIndicatorInteraction.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "TSMessage.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface TSUnreadIndicatorInteraction : TSMessage
|
||||||
|
|
||||||
|
- (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
44
Signal/src/views/TSUnreadIndicatorInteraction.m
Normal file
44
Signal/src/views/TSUnreadIndicatorInteraction.m
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "TSUnreadIndicatorInteraction.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@implementation TSUnreadIndicatorInteraction
|
||||||
|
|
||||||
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||||
|
{
|
||||||
|
return [super initWithCoder:coder];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithTimestamp:(uint64_t)timestamp thread:(TSThread *)thread
|
||||||
|
{
|
||||||
|
self = [super initWithTimestamp:timestamp
|
||||||
|
inThread:thread
|
||||||
|
messageBody:nil
|
||||||
|
attachmentIds:@[]
|
||||||
|
expiresInSeconds:0
|
||||||
|
expireStartedAt:0];
|
||||||
|
|
||||||
|
if (!self) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSDate *)receiptDateForSorting
|
||||||
|
{
|
||||||
|
// Always use date, since we're creating these interactions after the fact
|
||||||
|
// and back-dating them.
|
||||||
|
//
|
||||||
|
// By default [TSMessage receiptDateForSorting] will prefer to use receivedAtDate
|
||||||
|
// which is not back-dated.
|
||||||
|
return self.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -700,6 +700,9 @@
|
||||||
/* The subtitle for the messages view title indicates that the title can be tapped to access settings for this conversation. */
|
/* The subtitle for the messages view title indicates that the title can be tapped to access settings for this conversation. */
|
||||||
"MESSAGES_VIEW_TITLE_SUBTITLE" = "Tap here for settings";
|
"MESSAGES_VIEW_TITLE_SUBTITLE" = "Tap here for settings";
|
||||||
|
|
||||||
|
/* Indicator that separates read from unread messages. */
|
||||||
|
"MESSAGES_VIEW_UNREAD_INDICATOR" = "Unread Messages";
|
||||||
|
|
||||||
/* {{number of minutes}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{5 minutes}}'. See other *_TIME_AMOUNT strings */
|
/* {{number of minutes}} embedded in strings, e.g. 'Alice updated disappearing messages expiration to {{5 minutes}}'. See other *_TIME_AMOUNT strings */
|
||||||
"MINUTES_TIME_AMOUNT" = "%u minutes";
|
"MINUTES_TIME_AMOUNT" = "%u minutes";
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue