Merge branch 'dev' into feature/session-id-blinding-part-2

# Conflicts:
#	Session/Conversations/ConversationVC+Interaction.swift
#	SessionMessagingKit/Open Groups/OpenGroupManagerV2.swift
pull/592/head
Morgan Pretty 3 years ago
commit cb288ca09c

@ -229,7 +229,7 @@ public class ConversationMessageMapping: NSObject {
let indexPtr: UnsafeMutablePointer<UInt> = UnsafeMutablePointer<UInt>.allocate(capacity: 1)
let wasFound = view.getGroup(nil, index: indexPtr, forKey: uniqueId, inCollection: TSInteraction.collection())
guard wasFound else {
owsFailDebug("Could not find interaction.")
SNLog("Could not find interaction.")
return nil
}
let index = indexPtr.pointee

@ -3,6 +3,7 @@ import CoreServices
import Photos
import PhotosUI
import Sodium
import PromiseKit
import SessionUtilitiesKit
import SignalUtilitiesKit
@ -11,6 +12,16 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
ConversationTitleViewDelegate {
func handleTitleViewTapped() {
// Don't take the user to settings for message requests
guard
let contactThread: TSContactThread = thread as? TSContactThread,
let contact: Contact = Storage.shared.getContact(with: contactThread.contactSessionID()),
contact.isApproved,
contact.didApproveMe
else {
return
}
openSettings()
}
@ -253,71 +264,109 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
let linkPreviewDraft = snInputView.linkPreviewInfo?.draft
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
viewModel.appendUnsavedOutgoingTextMessage(tsMessage)
Storage.write(with: { transaction in
message.linkPreview = VisibleMessage.LinkPreview.from(linkPreviewDraft, using: transaction)
}, completion: { [weak self] in
tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview)
Storage.shared.write(
with: { transaction in
tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction)
},
completion: { [weak self] in
// At this point the TSOutgoingMessage should have its link preview set, so we can scroll to the bottom knowing
// the height of the new message cell
self?.scrollToBottom(isAnimated: false)
}
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
for: self.thread,
with: transaction,
isNewThread: !oldThreadShouldBeVisible,
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
)
Storage.shared.write(
with: { transaction in
self?.approveMessageRequestIfNeeded(
for: self?.thread,
with: (transaction as! YapDatabaseReadWriteTransaction),
isNewThread: !oldThreadShouldBeVisible,
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
.map { [weak self] _ in
self?.viewModel.appendUnsavedOutgoingTextMessage(tsMessage)
Storage.write(with: { transaction in
message.linkPreview = VisibleMessage.LinkPreview.from(linkPreviewDraft, using: transaction)
}, completion: { [weak self] in
tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview)
Storage.shared.write(
with: { transaction in
tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction)
},
completion: { [weak self] in
// At this point the TSOutgoingMessage should have its link preview set, so we can scroll to the bottom knowing
// the height of the new message cell
self?.scrollToBottom(isAnimated: false)
}
)
},
completion: { [weak self] in
Storage.shared.write { transaction in
MessageSender.send(message, with: [], in: thread, using: transaction as! YapDatabaseReadWriteTransaction)
}
self?.handleMessageSent()
}
)
})
}
// Show an error indicating that approving the thread failed
promise.catch(on: DispatchQueue.main) { [weak self] _ in
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
promise.retainUntilComplete()
})
}
func sendAttachments(_ attachments: [SignalAttachment], with text: String, onComplete: (() -> ())? = nil) {
guard !showBlockedModalIfNeeded() else { return }
for attachment in attachments {
if attachment.hasError {
return showErrorAlert(for: attachment, onDismiss: onComplete)
}
}
let thread = self.thread
let sentTimestamp: UInt64 = NSDate.millisecondTimestamp()
let message = VisibleMessage()
message.sentTimestamp = NSDate.millisecondTimestamp()
message.sentTimestamp = sentTimestamp
message.text = replaceMentions(in: text)
// Note: 'shouldBeVisible' is set to true the first time a thread is saved so we can
// use it to determine if the user is creating a new thread and update the 'isApproved'
// flags appropriately
let oldThreadShouldBeVisible: Bool = thread.shouldBeVisible
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
Storage.write(with: { transaction in
tsMessage.save(with: transaction)
// The new message cell is inserted at this point, but the TSOutgoingMessage doesn't have its attachment yet
}, completion: { [weak self] in
Storage.write(with: { transaction in
MessageSender.send(message, with: attachments, in: thread, using: transaction)
}, completion: { [weak self] in
// At this point the TSOutgoingMessage should have its attachments set, so we can scroll to the bottom knowing
// the height of the new message cell
self?.scrollToBottom(isAnimated: false)
})
self?.handleMessageSent()
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
for: self.thread,
with: transaction,
isNewThread: !oldThreadShouldBeVisible,
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
)
.map { [weak self] _ in
Storage.write(
with: { transaction in
tsMessage.save(with: transaction)
// The new message cell is inserted at this point, but the TSOutgoingMessage doesn't have its attachment yet
},
completion: { [weak self] in
Storage.write(with: { transaction in
MessageSender.send(message, with: attachments, in: thread, using: transaction)
}, completion: { [weak self] in
// At this point the TSOutgoingMessage should have its attachments set, so we can scroll to the bottom knowing
// the height of the new message cell
self?.scrollToBottom(isAnimated: false)
})
self?.handleMessageSent()
// Attachment successfully sent - dismiss the screen
onComplete?()
}
)
}
// Show an error indicating that approving the thread failed
promise.catch(on: DispatchQueue.main) { [weak self] _ in
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
// Attachment successfully sent - dismiss the screen
onComplete?()
promise.retainUntilComplete()
})
}
@ -1178,8 +1227,8 @@ extension ConversationVC {
navigationController?.popToViewController(viewControllers[messageRequestsIndex - 1], animated: true)
}
fileprivate func approveMessageRequestIfNeeded(for thread: TSThread?, with transaction: YapDatabaseReadWriteTransaction, isNewThread: Bool, timestamp: UInt64) {
guard let contactThread: TSContactThread = thread as? TSContactThread else { return }
fileprivate func approveMessageRequestIfNeeded(for thread: TSThread?, with transaction: YapDatabaseReadWriteTransaction, isNewThread: Bool, timestamp: UInt64) -> Promise<Void> {
guard let contactThread: TSContactThread = thread as? TSContactThread else { return Promise.value(()) }
// If the contact doesn't exist then we should create it so we can store the 'isApproved' state
// (it'll be updated with correct profile info if they accept the message request so this
@ -1187,64 +1236,93 @@ extension ConversationVC {
let sessionId: String = contactThread.contactSessionID()
let contact: Contact = (Storage.shared.getContact(with: sessionId) ?? Contact(sessionID: sessionId))
if !contact.isApproved {
// Default 'didApproveMe' to true for the person approving the message request
contact.isApproved = true
contact.didApproveMe = (contact.didApproveMe || !isNewThread)
Storage.shared.setContact(contact, using: transaction)
// If we aren't creating a new thread (ie. sending a message request) then send a
// messageRequestResponse back to the sender (this allows the sender to know that
// they have been approved and can now use this contact in closed groups)
if !isNewThread {
guard !contact.isApproved else { return Promise.value(()) }
return Promise.value(())
.then { [weak self] _ -> Promise<Void> in
guard !isNewThread else { return Promise.value(()) }
guard let strongSelf = self else { return Promise(error: MessageSender.Error.noThread) }
// If we aren't creating a new thread (ie. sending a message request) then send a
// messageRequestResponse back to the sender (this allows the sender to know that
// they have been approved and can now use this contact in closed groups)
let (promise, seal) = Promise<Void>.pending()
let messageRequestResponse: MessageRequestResponse = MessageRequestResponse(
isApproved: true
)
messageRequestResponse.sentTimestamp = timestamp
MessageSender.send(messageRequestResponse, in: contactThread, using: transaction)
// Show a loading indicator
ModalActivityIndicatorViewController.present(fromViewController: strongSelf, canCancel: false) { _ in
seal.fulfill(())
}
return promise
.then { MessageSender.sendNonDurably(messageRequestResponse, in: contactThread, using: transaction) }
.map { _ in
if self?.presentedViewController is ModalActivityIndicatorViewController {
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
}
}
}
// Hide the 'messageRequestView' since the request has been approved and force a config
// sync to propagate the contact approval state (both must run on the main thread)
DispatchQueue.main.async { [weak self] in
let messageRequestViewWasVisible: Bool = (self?.messageRequestView.isHidden == false)
.map { _ in
// Default 'didApproveMe' to true for the person approving the message request
contact.isApproved = true
contact.didApproveMe = (contact.didApproveMe || !isNewThread)
Storage.shared.setContact(contact, using: transaction)
UIView.animate(withDuration: 0.3) {
self?.messageRequestView.isHidden = true
self?.scrollButtonMessageRequestsBottomConstraint?.isActive = false
self?.scrollButtonBottomConstraint?.isActive = true
// Hide the 'messageRequestView' since the request has been approved and force a config
// sync to propagate the contact approval state (both must run on the main thread)
DispatchQueue.main.async { [weak self] in
let messageRequestViewWasVisible: Bool = (self?.messageRequestView.isHidden == false)
// Update the table content inset and offset to account for the dissapearance of
// the messageRequestsView
if messageRequestViewWasVisible {
let messageRequestsOffset: CGFloat = ((self?.messageRequestView.bounds.height ?? 0) + 16)
let oldContentInset: UIEdgeInsets = (self?.messagesTableView.contentInset ?? UIEdgeInsets.zero)
self?.messagesTableView.contentInset = UIEdgeInsets(
top: 0,
leading: 0,
bottom: max(oldContentInset.bottom - messageRequestsOffset, 0),
trailing: 0
)
UIView.animate(withDuration: 0.3) {
self?.messageRequestView.isHidden = true
self?.scrollButtonMessageRequestsBottomConstraint?.isActive = false
self?.scrollButtonBottomConstraint?.isActive = true
// Update the table content inset and offset to account for the dissapearance of
// the messageRequestsView
if messageRequestViewWasVisible {
let messageRequestsOffset: CGFloat = ((self?.messageRequestView.bounds.height ?? 0) + 16)
let oldContentInset: UIEdgeInsets = (self?.messagesTableView.contentInset ?? UIEdgeInsets.zero)
self?.messagesTableView.contentInset = UIEdgeInsets(
top: 0,
leading: 0,
bottom: max(oldContentInset.bottom - messageRequestsOffset, 0),
trailing: 0
)
}
}
// Update UI
self?.updateNavBarButtons()
// Send a sync message with the details of the contact
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.forceSyncConfigurationNowIfNeeded(with: transaction).retainUntilComplete()
}
}
// Send a sync message with the details of the contact
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.forceSyncConfigurationNowIfNeeded(with: transaction).retainUntilComplete()
}
}
}
}
@objc func acceptMessageRequest() {
Storage.write { [weak self] transaction in
self?.approveMessageRequestIfNeeded(
for: self?.thread,
Storage.write { transaction in
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
for: self.thread,
with: transaction,
isNewThread: false,
timestamp: NSDate.millisecondTimestamp()
)
// Show an error indicating that approving the thread failed
promise.catch(on: DispatchQueue.main) { [weak self] _ in
let alert = UIAlertController(title: "Session", message: NSLocalizedString("MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
promise.retainUntilComplete()
}
}

@ -1,6 +1,7 @@
import UIKit
import SessionUIKit
import SessionMessagingKit
import UIKit
// TODO:
// Slight paging glitch when scrolling up and loading more content
@ -474,30 +475,47 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
// MARK: Updating
func updateNavBarButtons() {
navigationItem.hidesBackButton = isShowingSearchUI
if isShowingSearchUI {
navigationItem.leftBarButtonItem = nil
navigationItem.rightBarButtonItems = []
} else {
}
else {
navigationItem.leftBarButtonItem = UIViewController.createOWSBackButton(withTarget: self, selector: #selector(handleBackPressed))
let rightBarButtonItem: UIBarButtonItem
if thread is TSContactThread {
let size = Values.verySmallProfilePictureSize
let profilePictureView = ProfilePictureView()
profilePictureView.accessibilityLabel = "Settings button"
profilePictureView.size = size
profilePictureView.update(for: thread)
profilePictureView.set(.width, to: size)
profilePictureView.set(.height, to: size)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(openSettings))
profilePictureView.addGestureRecognizer(tapGestureRecognizer)
rightBarButtonItem = UIBarButtonItem(customView: profilePictureView)
} else {
rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "Gear"), style: .plain, target: self, action: #selector(openSettings))
if let contactThread: TSContactThread = thread as? TSContactThread {
// Don't show the settings button for message requests
if let contact: Contact = Storage.shared.getContact(with: contactThread.contactSessionID()), contact.isApproved, contact.didApproveMe {
let size = Values.verySmallProfilePictureSize
let profilePictureView = ProfilePictureView()
profilePictureView.size = size
profilePictureView.update(for: thread)
profilePictureView.set(.width, to: size)
profilePictureView.set(.height, to: size)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(openSettings))
profilePictureView.addGestureRecognizer(tapGestureRecognizer)
let rightBarButtonItem: UIBarButtonItem = UIBarButtonItem(customView: profilePictureView)
rightBarButtonItem.accessibilityLabel = "Settings button"
rightBarButtonItem.isAccessibilityElement = true
navigationItem.rightBarButtonItem = rightBarButtonItem
}
else {
// Note: Adding an empty button because without it the title alignment is busted (Note: The size was
// taken from the layout inspector for the back button in Xcode
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: UIView(frame: CGRect(x: 0, y: 0, width: 37, height: 44)))
}
}
else {
let rightBarButtonItem: UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "Gear"), style: .plain, target: self, action: #selector(openSettings))
rightBarButtonItem.accessibilityLabel = "Settings button"
rightBarButtonItem.isAccessibilityElement = true
navigationItem.rightBarButtonItem = rightBarButtonItem
}
rightBarButtonItem.accessibilityLabel = "Settings button"
rightBarButtonItem.isAccessibilityElement = true
navigationItem.rightBarButtonItem = rightBarButtonItem
}
}
@ -806,9 +824,12 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
}
func markAllAsRead() {
guard !thread.isMessageRequest() else { return }
guard let lastSortID = viewItems.last?.interaction.sortId else { return }
OWSReadReceiptManager.shared().markAsReadLocally(beforeSortId: lastSortID, thread: thread)
OWSReadReceiptManager.shared().markAsReadLocally(
beforeSortId: lastSortID,
thread: thread,
trySendReadReceipt: !thread.isMessageRequest()
)
SSKEnvironment.shared.disappearingMessagesJob.cleanupMessagesWhichFailedToStartExpiringFromNow()
}
@ -912,7 +933,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
let searchBar = searchController.uiSearchController.searchBar
searchBar.searchBarStyle = .minimal
searchBar.barStyle = .black
searchBar.tintColor = Colors.accent
searchBar.tintColor = Colors.text
let searchIcon = UIImage(named: "searchbar_search")!.asTintedImage(color: Colors.searchBarPlaceholder)
searchBar.setImage(searchIcon, for: .search, state: UIControl.State.normal)
let clearIcon = UIImage(named: "searchbar_clear")!.asTintedImage(color: Colors.searchBarPlaceholder)

@ -277,7 +277,7 @@ CGFloat kIconViewLength = 24;
contents.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen");
BOOL isNoteToSelf = self.thread.isNoteToSelf;
__weak OWSConversationSettingsViewController *weakSelf = self;
OWSTableSection *section = [OWSTableSection new];
@ -332,7 +332,7 @@ CGFloat kIconViewLength = 24;
} actionBlock:^{
[weakSelf tappedConversationSearch];
}]];
// Disappearing messages
if (![self isOpenGroup]) {
[section addItem:[OWSTableItem itemWithCustomCellBlock:^{
@ -358,13 +358,6 @@ CGFloat kIconViewLength = 24;
switchView.on = strongSelf.disappearingMessagesConfiguration.isEnabled;
[switchView addTarget:strongSelf action:@selector(disappearingMessagesSwitchValueDidChange:)
forControlEvents:UIControlEventValueChanged];
// Disable Disappearing Messages if the conversation hasn't been approved
if (!self.thread.isGroupThread) {
TSContactThread *thread = (TSContactThread *)self.thread;
SNContact *contact = [LKStorage.shared getContactWithSessionID:thread.contactSessionID];
[switchView setEnabled:(contact.isApproved && contact.didApproveMe)];
}
UIStackView *topRow =
[[UIStackView alloc] initWithArrangedSubviews:@[ iconView, rowLabel, switchView ]];
@ -438,13 +431,6 @@ CGFloat kIconViewLength = 24;
[slider autoPinTrailingToSuperviewMargin];
[slider autoPinBottomToSuperviewMargin];
// Disable Disappearing Messages slider if the conversation hasn't been approved (just in case)
if (!self.thread.isGroupThread) {
TSContactThread *thread = (TSContactThread *)self.thread;
SNContact *contact = [LKStorage.shared getContactWithSessionID:thread.contactSessionID];
[slider setEnabled:(contact.isApproved && contact.didApproveMe)];
}
cell.userInteractionEnabled = !strongSelf.hasLeftGroup;
cell.accessibilityIdentifier = ACCESSIBILITY_IDENTIFIER_WITH_NAME(

@ -73,13 +73,17 @@ final class ConversationTitleView : UIView {
private func getTitle() -> String {
if let thread = thread as? TSGroupThread {
return thread.groupModel.groupName!
} else if thread.isNoteToSelf() {
}
else if thread.isNoteToSelf() {
return "Note to Self"
} else {
}
else {
let sessionID = (thread as! TSContactThread).contactSessionID()
var result = sessionID
Storage.read { transaction in
result = Storage.shared.getContact(with: sessionID)?.displayName(for: .regular) ?? "Anonymous"
let displayName: String = ((Storage.shared.getContact(with: sessionID)?.displayName(for: .regular)) ?? sessionID)
let middleTruncatedHexKey: String = "\(sessionID.prefix(4))...\(sessionID.suffix(4))"
result = (displayName == sessionID ? middleTruncatedHexKey : displayName)
}
return result
}

@ -6,6 +6,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
private var threads: YapDatabaseViewMappings!
private var threadViewModelCache: [String:ThreadViewModel] = [:] // Thread ID to ThreadViewModel
private var tableViewTopConstraint: NSLayoutConstraint!
private var unreadMessageRequestCount: UInt = 0
private var messageRequestCount: UInt {
threads.numberOfItems(inGroup: TSMessageRequestGroup)
@ -196,7 +197,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: MessageRequestsCell.reuseIdentifier) as! MessageRequestsCell
cell.update(with: Int(messageRequestCount))
cell.update(with: Int(unreadMessageRequestCount))
return cell
default:
@ -263,6 +264,14 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
}
}
// Update the number of unread message requests
unreadMessageRequestCount = OWSMessageUtils.sharedManager().unreadMessageRequestCount()
// If there are no unread message requests then hide the message request banner
if unreadMessageRequestCount == 0 {
CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = true
}
return reload()
}
}
@ -286,11 +295,21 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
// If we need to unhide the message request row and then re-insert it
if !messageRequestChanges.isEmpty {
if tableView.numberOfRows(inSection: 0) == 1 && Int(messageRequestCount) <= 0 {
// Update the number of unread message requests
unreadMessageRequestCount = OWSMessageUtils.sharedManager().unreadMessageRequestCount()
// If there are no unread message requests then hide the message request banner
if unreadMessageRequestCount == 0 && tableView.numberOfRows(inSection: 0) == 1 {
CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = true
tableView.deleteRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
else if tableView.numberOfRows(inSection: 0) == 0 && Int(messageRequestCount) > 0 && !CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] {
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
else {
if tableView.numberOfRows(inSection: 0) == 1 && Int(messageRequestCount) <= 0 {
tableView.deleteRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
else if tableView.numberOfRows(inSection: 0) == 0 && Int(messageRequestCount) > 0 && !CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] {
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
}
}
@ -393,11 +412,13 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
profilePictureViewContainer.addSubview(pathStatusView)
pathStatusView.pin(.trailing, to: .trailing, of: profilePictureViewContainer)
pathStatusView.pin(.bottom, to: .bottom, of: profilePictureViewContainer)
// Left bar button item
let leftBarButtonItem = UIBarButtonItem(customView: profilePictureViewContainer)
leftBarButtonItem.accessibilityLabel = "Settings button"
leftBarButtonItem.isAccessibilityElement = true
navigationItem.leftBarButtonItem = leftBarButtonItem
// Right bar button item - search button
let rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(showSearchUI))
rightBarButtonItem.accessibilityLabel = "Search button"
@ -542,6 +563,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv
@objc private func openSettings() {
let settingsVC = SettingsVC()
let navigationController = OWSNavigationController(rootViewController: settingsVC)
navigationController.modalPresentationStyle = .fullScreen
present(navigationController, animated: true, completion: nil)
}

@ -63,23 +63,16 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
return result
}()
private lazy var clearAllButton: UIButton = {
let result: UIButton = UIButton()
private lazy var clearAllButton: Button = {
let result: Button = Button(style: .destructiveOutline, size: .large)
result.translatesAutoresizingMaskIntoConstraints = false
result.clipsToBounds = true
result.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
result.setTitle(NSLocalizedString("MESSAGE_REQUESTS_CLEAR_ALL", comment: ""), for: .normal)
result.setTitleColor(Colors.destructive, for: .normal)
result.setBackgroundImage(
Colors.destructive
.withAlphaComponent(isDarkMode ? 0.2 : 0.06)
.toImage(isDarkMode: isDarkMode),
for: .highlighted
)
result.isHidden = true
result.layer.cornerRadius = (NewConversationButtonSet.collapsedButtonSize / 2)
result.layer.borderColor = Colors.destructive.cgColor
result.layer.borderWidth = 1.5
result.addTarget(self, action: #selector(clearAllTapped), for: .touchUpInside)
return result
@ -163,10 +156,11 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
clearAllButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
clearAllButton.bottomAnchor.constraint(
equalTo: view.bottomAnchor,
constant: -Values.newConversationButtonBottomOffset // Negative due to how the constraint is set up
equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -Values.largeSpacing
),
clearAllButton.widthAnchor.constraint(equalToConstant: 155),
// Note: The '182' is to match the 'Next' button on the New DM page (which doesn't have a fixed width)
clearAllButton.widthAnchor.constraint(equalToConstant: 182),
clearAllButton.heightAnchor.constraint(equalToConstant: NewConversationButtonSet.collapsedButtonSize)
])
}

@ -76,7 +76,7 @@ class MessageRequestsCell: UITableViewCell {
}()
private func setUpViewHierarchy() {
backgroundColor = Colors.cellBackground
backgroundColor = Colors.cellPinned
selectedBackgroundView = UIView()
selectedBackgroundView?.backgroundColor = Colors.cellSelected

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -620,6 +620,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -611,6 +611,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -610,6 +610,7 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";

@ -139,9 +139,6 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("vc_settings_title", comment: ""))
// Navigation bar buttons
let backButton = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
backButton.tintColor = Colors.text
navigationItem.backBarButtonItem = backButton
updateNavigationBarButtons()
// Profile picture view
let profilePictureTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditProfilePictureUI))
@ -254,8 +251,6 @@ final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
pathStatusView.pin(.leading, to: .trailing, of: pathButton.titleLabel!, withInset: Values.smallSpacing)
pathStatusView.autoVCenterInSuperview()
pathButton.titleEdgeInsets = UIEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: Values.smallSpacing)
return [
getSeparator(),
pathButton,

@ -46,6 +46,7 @@ class BaseVC : UIViewController {
internal func setUpNavBarStyle() {
guard let navigationBar = navigationController?.navigationBar else { return }
if #available(iOS 15.0, *) {
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
@ -59,6 +60,11 @@ class BaseVC : UIViewController {
navigationBar.isTranslucent = false
navigationBar.barTintColor = Colors.navigationBarBackground
}
// Back button (to appear on pushed screen)
let backButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
backButton.tintColor = Colors.text
navigationItem.backBarButtonItem = backButton
}
internal func setNavBarTitle(_ title: String, customFontSize: CGFloat? = nil) {

@ -358,15 +358,20 @@ final class ConversationCell : UITableViewCell {
if threadViewModel.isGroupThread {
if threadViewModel.name.isEmpty {
return "Unknown Group"
} else {
}
else {
return threadViewModel.name
}
} else {
}
else {
if threadViewModel.threadRecord.isNoteToSelf() {
return NSLocalizedString("NOTE_TO_SELF", comment: "")
} else {
let hexEncodedPublicKey = threadViewModel.contactSessionID!
return Storage.shared.getContact(with: hexEncodedPublicKey)?.displayName(for: .regular) ?? hexEncodedPublicKey
}
else {
let hexEncodedPublicKey: String = threadViewModel.contactSessionID!
let displayName: String = (Storage.shared.getContact(with: hexEncodedPublicKey)?.displayName(for: .regular) ?? hexEncodedPublicKey)
let middleTruncatedHexKey: String = "\(hexEncodedPublicKey.prefix(4))...\(hexEncodedPublicKey.suffix(4))"
return (displayName == hexEncodedPublicKey ? middleTruncatedHexKey : displayName)
}
}
}

@ -140,16 +140,16 @@ NS_ASSUME_NONNULL_BEGIN
return YES;
}
- (void)markAsReadNowWithSendReadReceipt:(BOOL)sendReadReceipt
- (void)markAsReadNowWithTrySendReadReceipt:(BOOL)trySendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction;
{
[self markAsReadAtTimestamp:[NSDate millisecondTimestamp]
sendReadReceipt:sendReadReceipt
trySendReadReceipt:trySendReadReceipt
transaction:transaction];
}
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
sendReadReceipt:(BOOL)sendReadReceipt
trySendReadReceipt:(BOOL)trySendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction;
{
if (_read && readTimestamp >= self.expireStartedAt) {
@ -174,7 +174,7 @@ NS_ASSUME_NONNULL_BEGIN
expirationStartedAt:readTimestamp
transaction:transaction];
if (sendReadReceipt) {
if (trySendReadReceipt) {
[OWSReadReceiptManager.sharedManager messageWasReadLocally:self];
}
}

@ -134,7 +134,7 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
}
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
sendReadReceipt:(BOOL)sendReadReceipt
trySendReadReceipt:(BOOL)trySendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
if (_read) {
@ -144,7 +144,7 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
_read = YES;
[self saveWithTransaction:transaction];
// Ignore sendReadReceipt, it doesn't apply to info messages.
// Ignore trySendReadReceipt, it doesn't apply to info messages.
}
@end

@ -488,7 +488,8 @@ public final class OpenGroupManager: NSObject {
// https://143.198.213.225:443/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c
// 143.198.213.255:80/main?public_key=658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c
let useTLS = (url.scheme == "https")
let room = String(url.path.dropFirst()) // Drop the leading slash
let updatedPath = (url.path.starts(with: "/r/") ? url.path.substring(from: 2) : url.path)
let room = String(updatedPath.dropFirst()) // Drop the leading slash
let queryParts = query.split(separator: "=")
guard !room.isEmpty && !room.contains("/"), queryParts.count == 2, queryParts[0] == "public_key" else { return nil }
let publicKey = String(queryParts[1])

@ -394,7 +394,7 @@ extension MessageReceiver {
if let tsOutgoingMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) as? TSOutgoingMessage,
let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) {
// Mark previous messages as read if there is a sync message
OWSReadReceiptManager.shared().markAsReadLocally(beforeSortId: tsOutgoingMessage.sortId, thread: thread)
OWSReadReceiptManager.shared().markAsReadLocally(beforeSortId: tsOutgoingMessage.sortId, thread: thread, trySendReadReceipt: true)
}
// Update the contact's approval status of the current user if needed (if we are getting messages from

@ -45,7 +45,7 @@ extern NSString *const kIncomingMessageMarkedAsReadNotification;
// This method can be called from any thread.
- (void)messageWasReadLocally:(TSIncomingMessage *)message;
- (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread;
- (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread trySendReadReceipt:(BOOL)trySendReadReceipt;
#pragma mark - Settings

@ -180,13 +180,13 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
#pragma mark - Mark as Read Locally
- (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread
- (void)markAsReadLocallyBeforeSortId:(uint64_t)sortId thread:(TSThread *)thread trySendReadReceipt:(BOOL)trySendReadReceipt
{
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self markAsReadBeforeSortId:sortId
thread:thread
readTimestamp:[NSDate millisecondTimestamp]
wasLocal:YES
trySendReadReceipt:trySendReadReceipt
transaction:transaction];
}];
}
@ -254,7 +254,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
- (void)markAsReadBeforeSortId:(uint64_t)sortId
thread:(TSThread *)thread
readTimestamp:(uint64_t)readTimestamp
wasLocal:(BOOL)wasLocal
trySendReadReceipt:(BOOL)trySendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
NSMutableArray<id<OWSReadTracking>> *newlyReadList = [NSMutableArray new];
@ -285,7 +285,7 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
}
for (id<OWSReadTracking> readItem in newlyReadList) {
[readItem markAsReadAtTimestamp:readTimestamp sendReadReceipt:wasLocal transaction:transaction];
[readItem markAsReadAtTimestamp:readTimestamp trySendReadReceipt:trySendReadReceipt transaction:transaction];
}
}

@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
* Used both for *responding* to a remote read receipt and in response to the local user's activity.
*/
- (void)markAsReadAtTimestamp:(uint64_t)readTimestamp
sendReadReceipt:(BOOL)sendReadReceipt
trySendReadReceipt:(BOOL)trySendReadReceipt
transaction:(YapDatabaseReadWriteTransaction *)transaction;
@end

@ -309,7 +309,7 @@ BOOL IsNoteToSelfEnabled(void)
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {
[message markAsReadAtTimestamp:[NSDate ows_millisecondTimeStamp] sendReadReceipt:YES transaction:transaction];
[message markAsReadAtTimestamp:[NSDate ows_millisecondTimeStamp] trySendReadReceipt:YES transaction:transaction];
}
}

@ -6,7 +6,7 @@ public final class Button : UIButton {
private var heightConstraint: NSLayoutConstraint!
public enum Style {
case unimportant, regular, prominentOutline, prominentFilled, regularBorderless
case unimportant, regular, prominentOutline, prominentFilled, regularBorderless, destructiveOutline
}
public enum Size {
@ -41,6 +41,7 @@ public final class Button : UIButton {
case .prominentOutline: fillColor = UIColor.clear
case .prominentFilled: fillColor = isLightMode ? Colors.text : Colors.accent
case .regularBorderless: fillColor = UIColor.clear
case .destructiveOutline: fillColor = UIColor.clear
}
let borderColor: UIColor
switch style {
@ -49,6 +50,7 @@ public final class Button : UIButton {
case .prominentOutline: borderColor = isLightMode ? Colors.text : Colors.accent
case .prominentFilled: borderColor = isLightMode ? Colors.text : Colors.accent
case .regularBorderless: borderColor = UIColor.clear
case .destructiveOutline: borderColor = Colors.destructive
}
let textColor: UIColor
switch style {
@ -57,6 +59,7 @@ public final class Button : UIButton {
case .prominentOutline: textColor = isLightMode ? Colors.text : Colors.accent
case .prominentFilled: textColor = isLightMode ? UIColor.white : Colors.text
case .regularBorderless: textColor = Colors.text
case .destructiveOutline: textColor = Colors.destructive
}
let height: CGFloat
switch size {

@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)sharedManager;
- (NSUInteger)unreadMessagesCount;
- (NSUInteger)unreadMessageRequestCount;
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread;
- (void)updateApplicationBadgeCount;

@ -112,6 +112,38 @@ NS_ASSUME_NONNULL_BEGIN
return numberOfItems;
}
- (NSUInteger)unreadMessageRequestCount {
__block NSUInteger count = 0;
[LKStorage readWithBlock:^(YapDatabaseReadTransaction *transaction) {
YapDatabaseViewTransaction *unreadMessages = [transaction ext:TSUnreadDatabaseViewExtensionName];
NSArray<NSString *> *allGroups = [unreadMessages allGroups];
// FIXME: Confusingly, `allGroups` includes contact threads as well
for (NSString *groupID in allGroups) {
TSThread *thread = [TSThread fetchObjectWithUniqueID:groupID transaction:transaction];
// Only increase the count for message requests
if (!thread.isMessageRequest) { continue; }
[unreadMessages enumerateKeysAndObjectsInGroup:groupID
usingBlock:^(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) {
if (![object conformsToProtocol:@protocol(OWSReadTracking)]) {
return;
}
id<OWSReadTracking> unread = (id<OWSReadTracking>)object;
if (unread.read) {
NSLog(@"Found an already read message in the * unread * messages list.");
return;
}
count += 1;
*stop = YES;
}];
}
}];
return count;
}
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread
{
__block NSUInteger numberOfItems;

@ -25,8 +25,6 @@ public class ModalActivityIndicatorViewController: OWSViewController {
return result
}()
var presentTimer: Timer?
var wasDimissed: Bool = false
// MARK: Initializers
@ -127,41 +125,7 @@ public class ModalActivityIndicatorViewController: OWSViewController {
self.spinner.startAnimating()
// Hide the the modal and wait for a second before revealing it,
// to avoid "blipping" in the modal during short blocking operations.
//
// NOTE: It will still intercept user interactions while hidden, as it
// should.
let kPresentationDelaySeconds = TimeInterval(1)
self.presentTimer?.invalidate()
self.presentTimer = Timer.weakScheduledTimer(withTimeInterval: kPresentationDelaySeconds, target: self, selector: #selector(presentTimerFired), userInfo: nil, repeats: false)
}
public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
clearTimer()
}
public override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.spinner.stopAnimating()
clearTimer()
}
private func clearTimer() {
self.presentTimer?.invalidate()
self.presentTimer = nil
}
@objc func presentTimerFired() {
AssertIsOnMainThread()
clearTimer()
// Fade in the modal.
// Fade in the modal
UIView.animate(withDuration: 0.35) {
self.view.layer.opacity = 1.0
}

@ -28,7 +28,7 @@ public final class ViewControllerUtilities : NSObject {
}
// Set up back button
if hasCustomBackButton {
let backButton = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
let backButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
backButton.tintColor = Colors.text
vc.navigationItem.backBarButtonItem = backButton
}

Loading…
Cancel
Save