Add typing indicators in home view.

This commit is contained in:
Matthew Chen 2018-11-01 10:43:13 -04:00
parent b063a49d56
commit 50381cc94c
4 changed files with 165 additions and 10 deletions

View File

@ -216,6 +216,7 @@
34B3F8821E8DF1700035BE1A /* NewContactThreadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8501E8DF1700035BE1A /* NewContactThreadViewController.m */; };
34B3F8851E8DF1700035BE1A /* NewGroupViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8551E8DF1700035BE1A /* NewGroupViewController.m */; };
34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F86E1E8DF1700035BE1A /* SignalsNavigationController.m */; };
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */; };
34B6D27420F664C900765BE2 /* OWSUnreadIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 34B6D27220F664C800765BE2 /* OWSUnreadIndicator.h */; settings = {ATTRIBUTES = (Public, ); }; };
34B6D27520F664C900765BE2 /* OWSUnreadIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B6D27320F664C800765BE2 /* OWSUnreadIndicator.m */; };
34BECE2B1F74C12700D7438D /* DebugUIStress.m in Sources */ = {isa = PBXBuildFile; fileRef = 34BECE2A1F74C12700D7438D /* DebugUIStress.m */; };
@ -866,6 +867,7 @@
34B3F8551E8DF1700035BE1A /* NewGroupViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NewGroupViewController.m; sourceTree = "<group>"; };
34B3F86D1E8DF1700035BE1A /* SignalsNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignalsNavigationController.h; sourceTree = "<group>"; };
34B3F86E1E8DF1700035BE1A /* SignalsNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignalsNavigationController.m; sourceTree = "<group>"; };
34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = "<group>"; };
34B6D27220F664C800765BE2 /* OWSUnreadIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSUnreadIndicator.h; sourceTree = "<group>"; };
34B6D27320F664C800765BE2 /* OWSUnreadIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSUnreadIndicator.m; sourceTree = "<group>"; };
34BECE291F74C12700D7438D /* DebugUIStress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIStress.h; sourceTree = "<group>"; };
@ -2215,10 +2217,11 @@
isa = PBXGroup;
children = (
452EA09D1EA7ABE00078744B /* AttachmentPointerView.swift */,
4C2F454E214C00E1004871FF /* AvatarTableViewCell.swift */,
34E3E5671EC4B19400495BAC /* AudioProgressView.swift */,
4C2F454E214C00E1004871FF /* AvatarTableViewCell.swift */,
451764291DE939FD00EDB8B9 /* ContactCell.swift */,
4523149F1F7E9E18003A428C /* DirectionalPanGestureRecognizer.swift */,
4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */,
45A663C41F92EC760027B59E /* GroupTableViewCell.swift */,
45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */,
34386A53207D271C009F5D9C /* NeverClearView.swift */,
@ -2234,8 +2237,8 @@
45A6DAD51EBBF85500893231 /* ReminderView.swift */,
450D19111F85236600970622 /* RemoteVideoView.h */,
450D19121F85236600970622 /* RemoteVideoView.m */,
4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */,
4CA5F792211E1F06008C2708 /* Toast.swift */,
34B6A902218B3F62007C4606 /* TypingIndicatorView.swift */,
);
name = Views;
path = views;
@ -3382,6 +3385,7 @@
457C87B82032645C008D52D6 /* DebugUINotifications.swift in Sources */,
4C13C9F620E57BA30089A98B /* ColorPickerViewController.swift in Sources */,
4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */,
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */,
340FC8D0205BF2FA007AEB0F /* OWSBackupIO.m in Sources */,
458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */,
4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */,

View File

@ -22,6 +22,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) UILabel *snippetLabel;
@property (nonatomic) UILabel *dateTimeLabel;
@property (nonatomic) MessageStatusView *messageStatusView;
@property (nonatomic) TypingIndicatorView *typingIndicatorView;
@property (nonatomic) UIStackView *previewStackView;
@property (nonatomic) UIView *unreadBadge;
@property (nonatomic) UILabel *unreadLabel;
@ -45,6 +47,11 @@ NS_ASSUME_NONNULL_BEGIN
return Environment.shared.contactsManager;
}
- (id<OWSTypingIndicators>)typingIndicators
{
return SSKEnvironment.shared.typingIndicators;
}
#pragma mark -
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(nullable NSString *)reuseIdentifier
@ -111,15 +118,25 @@ NS_ASSUME_NONNULL_BEGIN
self.snippetLabel.font = [self snippetFont];
self.snippetLabel.numberOfLines = 1;
self.snippetLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[self.snippetLabel setContentHuggingHorizontalLow];
[self.snippetLabel setCompressionResistanceHorizontalLow];
self.typingIndicatorView = [TypingIndicatorView new];
self.previewStackView = [[UIStackView alloc] initWithArrangedSubviews:@[
self.snippetLabel,
self.typingIndicatorView,
]];
self.previewStackView.axis = UILayoutConstraintAxisVertical;
self.previewStackView.alignment = UIStackViewAlignmentLeading;
[self.previewStackView setContentHuggingHorizontalLow];
[self.previewStackView setCompressionResistanceHorizontalLow];
UIStackView *bottomRowView = [[UIStackView alloc] initWithArrangedSubviews:@[
self.snippetLabel,
self.previewStackView,
self.messageStatusView,
]];
bottomRowView.axis = UILayoutConstraintAxisHorizontal;
bottomRowView.alignment = UIStackViewAlignmentLastBaseline;
bottomRowView.alignment = UIStackViewAlignmentCenter;
bottomRowView.spacing = 6.f;
UIStackView *vStackView = [[UIStackView alloc] initWithArrangedSubviews:@[
@ -203,6 +220,10 @@ NS_ASSUME_NONNULL_BEGIN
selector:@selector(otherUsersProfileDidChange:)
name:kNSNotificationName_OtherUsersProfileDidChange
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(typingIndicatorStateDidChange:)
name:[OWSTypingIndicatorsImpl typingIndicatorStateDidChange]
object:nil];
[self updateNameLabel];
[self updateAvatarView];
@ -215,7 +236,13 @@ NS_ASSUME_NONNULL_BEGIN
} else {
self.snippetLabel.attributedText = [self attributedSnippetForThread:thread isBlocked:isBlocked];
}
[self updatePreview];
CGFloat previewHeight = MAX(self.snippetLabel.font.lineHeight,
TypingIndicatorView.kMaxRadiusPt);
[self.viewConstraints addObjectsFromArray:@[
[self.previewStackView autoSetDimension:ALDimensionHeight
toSize:previewHeight],
]];
self.dateTimeLabel.text
= (overrideDate ? [self stringForDate:overrideDate] : [self stringForDate:thread.lastMessageDate]);
@ -500,6 +527,28 @@ NS_ASSUME_NONNULL_BEGIN
self.nameLabel.attributedText = name;
}
#pragma mark - Typing Indicators
- (void)updatePreview
{
if ([self.typingIndicators typingIndicatorsForThread:self.thread.threadRecord] != nil) {
self.snippetLabel.hidden = YES;
self.typingIndicatorView.hidden = NO;
[self.typingIndicatorView startAnimation];
} else {
self.snippetLabel.hidden = NO;
self.typingIndicatorView.hidden = YES;
[self.typingIndicatorView stopAnimation];
}
}
- (void)typingIndicatorStateDidChange:(NSNotification *)notification
{
OWSAssertIsOnMainThread();
[self updatePreview];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,101 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
@objc class TypingIndicatorView: UIStackView {
private let kDotMaxHSpacing: CGFloat = 8
@objc
public static let kMinRadiusPt: CGFloat = 6
@objc
public static let kMaxRadiusPt: CGFloat = 8
private let dot1 = DotView(dotType: .dotType1)
private let dot2 = DotView(dotType: .dotType2)
private let dot3 = DotView(dotType: .dotType3)
override public var isHidden: Bool {
didSet {
Logger.verbose("\(oldValue) -> \(isHidden)")
}
}
@available(*, unavailable, message:"use other constructor instead.")
required init(coder aDecoder: NSCoder) {
notImplemented()
}
@available(*, unavailable, message:"use other constructor instead.")
override init(frame: CGRect) {
notImplemented()
}
@objc
public init() {
super.init(frame: .zero)
// init(arrangedSubviews:...) is not a designated initializer.
addArrangedSubview(dot1)
addArrangedSubview(dot2)
addArrangedSubview(dot3)
self.axis = .horizontal
self.spacing = kDotMaxHSpacing
self.alignment = .center
}
@objc
public func startAnimation() {
}
@objc
public func stopAnimation() {
}
private enum DotType {
case dotType1
case dotType2
case dotType3
}
private class DotView: UIView {
private let dotType: DotType
private let shapeLayer = CAShapeLayer()
@available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) {
notImplemented()
}
@available(*, unavailable, message:"use other constructor instead.")
override init(frame: CGRect) {
notImplemented()
}
init(dotType: DotType) {
self.dotType = dotType
super.init(frame: .zero)
autoSetDimension(.width, toSize: kMaxRadiusPt)
autoSetDimension(.height, toSize: kMaxRadiusPt)
self.layer.addSublayer(shapeLayer)
updateLayer()
// self.text = text
//
// setupSubviews()
}
private func updateLayer() {
shapeLayer.fillColor = UIColor.ows_signalBlue.cgColor
let margin = (TypingIndicatorView.kMaxRadiusPt - TypingIndicatorView.kMinRadiusPt) * 0.5
let bezierPath = UIBezierPath(ovalIn: CGRect(x: margin, y: margin, width: TypingIndicatorView.kMinRadiusPt, height: TypingIndicatorView.kMinRadiusPt))
shapeLayer.path = bezierPath.cgPath
}
}
}

View File

@ -31,7 +31,7 @@ public protocol TypingIndicators: class {
//
// TODO: Use this method.
@objc
func typingIndicators(forThread thread: TSThread, recipientId: String) -> String?
func typingIndicators(forThread thread: TSThread) -> String?
@objc
func setTypingIndicatorsEnabled(value: Bool)
@ -45,7 +45,8 @@ public protocol TypingIndicators: class {
@objc(OWSTypingIndicatorsImpl)
public class TypingIndicatorsImpl: NSObject, TypingIndicators {
@objc public static let typingIndicatorStateDidChange = Notification.Name("typingIndicatorStateDidChange")
@objc
public static let typingIndicatorStateDidChange = Notification.Name("typingIndicatorStateDidChange")
private let kDatabaseCollection = "TypingIndicators"
private let kDatabaseKey_TypingIndicatorsEnabled = "kDatabaseKey_TypingIndicatorsEnabled"
@ -150,7 +151,7 @@ public class TypingIndicatorsImpl: NSObject, TypingIndicators {
}
@objc
public func typingIndicators(forThread thread: TSThread, recipientId: String) -> String? {
public func typingIndicators(forThread thread: TSThread) -> String? {
AssertIsOnMainThread()
var firstRecipientId: String?