Merge pull request #361 from oxen-io/unread-count
Show Unread Count on Scroll to Bottom Button
This commit is contained in:
commit
1ba98c5f99
|
@ -5153,7 +5153,7 @@
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 201;
|
CURRENT_PROJECT_VERSION = 202;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||||
|
@ -5222,7 +5222,7 @@
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 201;
|
CURRENT_PROJECT_VERSION = 202;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
@ -5283,7 +5283,7 @@
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 201;
|
CURRENT_PROJECT_VERSION = 202;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||||
|
@ -5353,7 +5353,7 @@
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 201;
|
CURRENT_PROJECT_VERSION = 202;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
@ -6238,7 +6238,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CURRENT_PROJECT_VERSION = 201;
|
CURRENT_PROJECT_VERSION = 202;
|
||||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -6306,7 +6306,7 @@
|
||||||
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
CURRENT_PROJECT_VERSION = 201;
|
CURRENT_PROJECT_VERSION = 202;
|
||||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|
|
@ -354,6 +354,7 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
|
||||||
self.scrollButton.alpha = 0
|
self.scrollButton.alpha = 0
|
||||||
UIView.animate(withDuration: 0.25) {
|
UIView.animate(withDuration: 0.25) {
|
||||||
self.scrollButton.alpha = self.getScrollButtonOpacity()
|
self.scrollButton.alpha = self.getScrollButtonOpacity()
|
||||||
|
self.unreadCountView.alpha = self.scrollButton.alpha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.contextMenuVC = contextMenuVC
|
self.contextMenuVC = contextMenuVC
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversationSettingsViewDelegate, ConversationSearchControllerDelegate, UITableViewDataSource, UITableViewDelegate {
|
final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversationSettingsViewDelegate, ConversationSearchControllerDelegate, UITableViewDataSource, UITableViewDelegate {
|
||||||
let thread: TSThread
|
let thread: TSThread
|
||||||
let focusedMessageID: String? // This isn't actually used ATM
|
let focusedMessageID: String? // This isn't actually used ATM
|
||||||
|
var unreadViewItems: [ConversationViewItem] = []
|
||||||
var didConstrainScrollButton = false // Part of a workaround to get the scroll button to show up in the right place
|
var didConstrainScrollButton = false // Part of a workaround to get the scroll button to show up in the right place
|
||||||
// Search
|
// Search
|
||||||
var isShowingSearchUI = false
|
var isShowingSearchUI = false
|
||||||
|
@ -90,6 +91,25 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
||||||
|
|
||||||
lazy var snInputView = InputView(delegate: self)
|
lazy var snInputView = InputView(delegate: self)
|
||||||
|
|
||||||
|
lazy var unreadCountView: UIView = {
|
||||||
|
let result = UIView()
|
||||||
|
result.backgroundColor = Colors.text.withAlphaComponent(Values.veryLowOpacity)
|
||||||
|
let size = ConversationVC.unreadCountViewSize
|
||||||
|
result.set(.width, to: size)
|
||||||
|
result.set(.height, to: size)
|
||||||
|
result.layer.masksToBounds = true
|
||||||
|
result.layer.cornerRadius = size / 2
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
lazy var unreadCountLabel: UILabel = {
|
||||||
|
let result = UILabel()
|
||||||
|
result.font = .boldSystemFont(ofSize: Values.verySmallFontSize)
|
||||||
|
result.textColor = Colors.text
|
||||||
|
result.textAlignment = .center
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
lazy var scrollButton = ScrollToBottomButton(delegate: self)
|
lazy var scrollButton = ScrollToBottomButton(delegate: self)
|
||||||
|
|
||||||
lazy var blockedBanner: InfoBanner = {
|
lazy var blockedBanner: InfoBanner = {
|
||||||
|
@ -109,6 +129,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// MARK: Settings
|
// MARK: Settings
|
||||||
|
static let unreadCountViewSize: CGFloat = 20
|
||||||
/// The table view's bottom inset (content will have this distance to the bottom if the table view is fully scrolled down).
|
/// The table view's bottom inset (content will have this distance to the bottom if the table view is fully scrolled down).
|
||||||
static let bottomInset = Values.mediumSpacing
|
static let bottomInset = Values.mediumSpacing
|
||||||
/// The table view will start loading more content when the content offset becomes less than this.
|
/// The table view will start loading more content when the content offset becomes less than this.
|
||||||
|
@ -123,6 +144,11 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
||||||
self.thread = thread
|
self.thread = thread
|
||||||
self.focusedMessageID = focusedMessageID
|
self.focusedMessageID = focusedMessageID
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
var unreadCount: UInt = 0
|
||||||
|
Storage.read { transaction in
|
||||||
|
unreadCount = self.thread.unreadMessageCount(transaction: transaction)
|
||||||
|
}
|
||||||
|
unreadViewItems = unreadCount != 0 ? [ConversationViewItem](viewItems[viewItems.endIndex - Int(unreadCount) ..< viewItems.endIndex]) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
|
@ -142,6 +168,13 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
||||||
messagesTableView.pin(to: view)
|
messagesTableView.pin(to: view)
|
||||||
view.addSubview(scrollButton)
|
view.addSubview(scrollButton)
|
||||||
scrollButton.pin(.right, to: .right, of: view, withInset: -16)
|
scrollButton.pin(.right, to: .right, of: view, withInset: -16)
|
||||||
|
// Unread count view
|
||||||
|
view.addSubview(unreadCountView)
|
||||||
|
unreadCountView.addSubview(unreadCountLabel)
|
||||||
|
unreadCountLabel.pin(to: unreadCountView)
|
||||||
|
unreadCountView.centerYAnchor.constraint(equalTo: scrollButton.topAnchor).isActive = true
|
||||||
|
unreadCountView.center(.horizontal, in: scrollButton)
|
||||||
|
updateUnreadCountView()
|
||||||
// Blocked banner
|
// Blocked banner
|
||||||
addOrRemoveBlockedBanner()
|
addOrRemoveBlockedBanner()
|
||||||
// Notifications
|
// Notifications
|
||||||
|
@ -174,6 +207,8 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
if unreadCount > 0, let viewItem = self.viewItems[ifValid: self.viewItems.count - Int(unreadCount)], let interactionID = viewItem.interaction.uniqueId {
|
if unreadCount > 0, let viewItem = self.viewItems[ifValid: self.viewItems.count - Int(unreadCount)], let interactionID = viewItem.interaction.uniqueId {
|
||||||
self.scrollToInteraction(with: interactionID, position: .top, isAnimated: false)
|
self.scrollToInteraction(with: interactionID, position: .top, isAnimated: false)
|
||||||
|
self.scrollButton.alpha = self.getScrollButtonOpacity()
|
||||||
|
self.unreadCountView.alpha = self.scrollButton.alpha
|
||||||
} else {
|
} else {
|
||||||
self.scrollToBottom(isAnimated: false)
|
self.scrollToBottom(isAnimated: false)
|
||||||
}
|
}
|
||||||
|
@ -267,6 +302,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
||||||
UIView.animate(withDuration: 0.25) {
|
UIView.animate(withDuration: 0.25) {
|
||||||
self.messagesTableView.keyboardHeight = 0
|
self.messagesTableView.keyboardHeight = 0
|
||||||
self.scrollButton.alpha = self.getScrollButtonOpacity()
|
self.scrollButton.alpha = self.getScrollButtonOpacity()
|
||||||
|
self.unreadCountView.alpha = self.scrollButton.alpha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,7 +448,22 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
scrollButton.alpha = getScrollButtonOpacity()
|
scrollButton.alpha = getScrollButtonOpacity()
|
||||||
|
unreadCountView.alpha = scrollButton.alpha
|
||||||
autoLoadMoreIfNeeded()
|
autoLoadMoreIfNeeded()
|
||||||
|
updateUnreadCountView()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUnreadCountView() {
|
||||||
|
let visibleViewItems = (messagesTableView.indexPathsForVisibleRows ?? []).map { viewItems[$0.row] }
|
||||||
|
for visibleItem in visibleViewItems {
|
||||||
|
guard let index = unreadViewItems.firstIndex(where: { $0 === visibleItem }) else { continue }
|
||||||
|
unreadViewItems.remove(at: index)
|
||||||
|
}
|
||||||
|
let unreadCount = unreadViewItems.count
|
||||||
|
unreadCountLabel.text = unreadCount < 100 ? "\(unreadCount)" : "99+"
|
||||||
|
let fontSize = (unreadCount < 100) ? Values.verySmallFontSize : 8
|
||||||
|
unreadCountLabel.font = .boldSystemFont(ofSize: fontSize)
|
||||||
|
unreadCountView.isHidden = (unreadCount == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func autoLoadMoreIfNeeded() {
|
func autoLoadMoreIfNeeded() {
|
||||||
|
|
|
@ -98,7 +98,6 @@ final class ConversationCell : UITableViewCell {
|
||||||
// Unread count view
|
// Unread count view
|
||||||
unreadCountView.addSubview(unreadCountLabel)
|
unreadCountView.addSubview(unreadCountLabel)
|
||||||
unreadCountLabel.pin(to: unreadCountView)
|
unreadCountLabel.pin(to: unreadCountView)
|
||||||
unreadCountLabel.text = "4"
|
|
||||||
// Label stack view
|
// Label stack view
|
||||||
let topLabelSpacer = UIView.hStretchingSpacer()
|
let topLabelSpacer = UIView.hStretchingSpacer()
|
||||||
let topLabelStackView = UIStackView(arrangedSubviews: [ displayNameLabel, unreadCountView, topLabelSpacer, timestampLabel ])
|
let topLabelStackView = UIStackView(arrangedSubviews: [ displayNameLabel, unreadCountView, topLabelSpacer, timestampLabel ])
|
||||||
|
|
Loading…
Reference in New Issue