diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 0d3b0fb8e..07f7d960c 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -563,6 +563,7 @@ C3645350252449260045C478 /* VoiceMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C364534F252449260045C478 /* VoiceMessageView.swift */; }; C364535C252467900045C478 /* AudioUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C364535B252467900045C478 /* AudioUtilities.swift */; }; C374EEE225DA26740073A857 /* LinkPreviewModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C374EEE125DA26740073A857 /* LinkPreviewModal.swift */; }; + C374EEEB25DA3CA70073A857 /* ConversationTitleViewV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = C374EEEA25DA3CA70073A857 /* ConversationTitleViewV2.swift */; }; C379DCF4256735770002D4EB /* VisibleMessage+Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C379DCF3256735770002D4EB /* VisibleMessage+Attachment.swift */; }; C37F5385255B94F6002AEA92 /* SelectRecipientViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF34E255B6DC8007E1867 /* SelectRecipientViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; C37F5396255B95BD002AEA92 /* OWSAnyTouchGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1606,6 +1607,7 @@ C364534F252449260045C478 /* VoiceMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageView.swift; sourceTree = ""; }; C364535B252467900045C478 /* AudioUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioUtilities.swift; sourceTree = ""; }; C374EEE125DA26740073A857 /* LinkPreviewModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewModal.swift; sourceTree = ""; }; + C374EEEA25DA3CA70073A857 /* ConversationTitleViewV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTitleViewV2.swift; sourceTree = ""; }; C379DCF3256735770002D4EB /* VisibleMessage+Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleMessage+Attachment.swift"; sourceTree = ""; }; C379DCFD25673DBC0002D4EB /* TSAttachmentPointer+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSAttachmentPointer+Conversion.swift"; sourceTree = ""; }; C37F53E8255BA9BB002AEA92 /* Environment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Environment.h; sourceTree = ""; }; @@ -2251,6 +2253,7 @@ C374EEE125DA26740073A857 /* LinkPreviewModal.swift */, B82149C025D605C6009C0F2A /* InfoBanner.swift */, B8214A2A25D63EB9009C0F2A /* MessagesTableView.swift */, + C374EEEA25DA3CA70073A857 /* ConversationTitleViewV2.swift */, ); path = "Views & Modals"; sourceTree = ""; @@ -5056,6 +5059,7 @@ B8214A2B25D63EB9009C0F2A /* MessagesTableView.swift in Sources */, B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */, B821494625D4D6FF009C0F2A /* URLModal.swift in Sources */, + C374EEEB25DA3CA70073A857 /* ConversationTitleViewV2.swift in Sources */, 4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */, 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */, 344825C6211390C800DB4BD8 /* OWSOrphanDataCleaner.m in Sources */, diff --git a/Session/Conversations V2/ConversationVC.swift b/Session/Conversations V2/ConversationVC.swift index d6ade5075..fe4673686 100644 --- a/Session/Conversations V2/ConversationVC.swift +++ b/Session/Conversations V2/ConversationVC.swift @@ -3,7 +3,6 @@ // • Tapping replies // • Mentions // • Remaining send logic -// • Subtitle // • Slight paging glitch // • Scrolling bug // • Scroll button bug @@ -47,6 +46,8 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, UITableViewD }() // MARK: UI Components + private lazy var titleView = ConversationTitleViewV2(thread: thread) + lazy var messagesTableView: MessagesTableView = { let result = MessagesTableView() result.dataSource = self @@ -98,7 +99,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, UITableViewD setUpGradientBackground() // Nav bar setUpNavBarStyle() - setNavBarTitle(getTitle()) + navigationItem.titleView = titleView updateNavBarButtons() // Constraints view.addSubview(messagesTableView) @@ -390,21 +391,6 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, UITableViewD } // MARK: Convenience - private func getTitle() -> String { - if let thread = thread as? TSGroupThread { - return thread.groupModel.groupName! - } else if thread.isNoteToSelf() { - return "Note to Self" - } else { - let sessionID = thread.contactIdentifier()! - var result = sessionID - Storage.read { transaction in - result = Storage.shared.getContact(with: sessionID)?.displayName ?? "Anonymous" - } - return result - } - } - private func getScrollButtonOpacity() -> CGFloat { let contentOffsetY = messagesTableView.contentOffset.y let x = (lastPageTop - ConversationVC.bottomInset - contentOffsetY).clamp(0, .greatestFiniteMagnitude) diff --git a/Session/Conversations V2/Views & Modals/ConversationTitleViewV2.swift b/Session/Conversations V2/Views & Modals/ConversationTitleViewV2.swift new file mode 100644 index 000000000..1a7ebde4d --- /dev/null +++ b/Session/Conversations V2/Views & Modals/ConversationTitleViewV2.swift @@ -0,0 +1,109 @@ + +final class ConversationTitleViewV2 : UIView { + private let thread: TSThread + + override var intrinsicContentSize: CGSize { + return UIView.layoutFittingExpandedSize + } + + // MARK: UI Components + private lazy var titleLabel: UILabel = { + let result = UILabel() + result.textColor = Colors.text + result.font = .boldSystemFont(ofSize: Values.mediumFontSize) + result.lineBreakMode = .byTruncatingTail + return result + }() + + private lazy var subtitleLabel: UILabel = { + let result = UILabel() + result.textColor = Colors.text + result.font = .systemFont(ofSize: 13) + result.lineBreakMode = .byTruncatingTail + return result + }() + + // MARK: Lifecycle + init(thread: TSThread) { + self.thread = thread + super.init(frame: CGRect.zero) + initialize() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(thread:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(coder:) instead.") + } + + private func initialize() { + let stackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel ]) + stackView.axis = .vertical + stackView.alignment = .center + stackView.isLayoutMarginsRelativeArrangement = true + stackView.layoutMargins = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 0) + addSubview(stackView) + stackView.pin(to: self) + NotificationCenter.default.addObserver(self, selector: #selector(update), name: Notification.Name.groupThreadUpdated, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(update), name: Notification.Name.muteSettingUpdated, object: nil) + update() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: Updating + @objc private func update() { + titleLabel.text = getTitle() + let subtitle = getSubtitle() + subtitleLabel.attributedText = subtitle + let titleFontSize = (subtitle != nil) ? Values.mediumFontSize : Values.veryLargeFontSize + titleLabel.font = .boldSystemFont(ofSize: titleFontSize) + } + + // MARK: General + private func getTitle() -> String { + if let thread = thread as? TSGroupThread { + return thread.groupModel.groupName! + } else if thread.isNoteToSelf() { + return "Note to Self" + } else { + let sessionID = thread.contactIdentifier()! + var result = sessionID + Storage.read { transaction in + result = Storage.shared.getContact(with: sessionID)?.displayName ?? "Anonymous" + } + return result + } + } + + private func getSubtitle() -> NSAttributedString? { + if let muteEndDate = thread.mutedUntilDate, thread.isMuted { + let result = NSMutableAttributedString() + result.append(NSAttributedString(string: "\u{e067} ", attributes: [ .font : UIFont.ows_elegantIconsFont(10), .foregroundColor : Colors.text ])) + let formatter = DateFormatter() + formatter.locale = Locale.current + formatter.timeStyle = .medium + formatter.dateStyle = .medium + result.append(NSAttributedString(string: "Muted until " + formatter.string(from: muteEndDate))) + return result + } else if let thread = self.thread as? TSGroupThread { + var userCount: Int? + switch thread.groupModel.groupType { + case .closedGroup: userCount = thread.groupModel.groupMemberIds.count + case .openGroup: + if let openGroup = Storage.shared.getOpenGroup(for: self.thread.uniqueId!) { + userCount = Storage.shared.getUserCount(forOpenGroupWithID: openGroup.id) + } + default: break + } + if let userCount = userCount { + return NSAttributedString(string: "\(userCount) members") + } + } + return nil + } +} diff --git a/SessionMessagingKit/Threads/Notification+Thread.swift b/SessionMessagingKit/Threads/Notification+Thread.swift index 4254aff63..77b74d0ef 100644 --- a/SessionMessagingKit/Threads/Notification+Thread.swift +++ b/SessionMessagingKit/Threads/Notification+Thread.swift @@ -2,9 +2,11 @@ public extension Notification.Name { static let groupThreadUpdated = Notification.Name("groupThreadUpdated") + static let muteSettingUpdated = Notification.Name("muteSettingUpdated") } @objc public extension NSNotification { @objc static let groupThreadUpdated = Notification.Name.groupThreadUpdated.rawValue as NSString + @objc static let muteSettingUpdated = Notification.Name.muteSettingUpdated.rawValue as NSString } diff --git a/SessionMessagingKit/Threads/TSThread.m b/SessionMessagingKit/Threads/TSThread.m index 1a3aebe4f..aae930980 100644 --- a/SessionMessagingKit/Threads/TSThread.m +++ b/SessionMessagingKit/Threads/TSThread.m @@ -451,6 +451,10 @@ BOOL IsNoteToSelfEnabled(void) changeBlock:^(TSThread *thread) { [thread setMutedUntilDate:mutedUntilDate]; }]; + + [transaction addCompletionQueue:dispatch_get_main_queue() completionBlock:^{ + [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.muteSettingUpdated object:self.uniqueId]; + }]; } @end