Merge branch 'dev' into disappearing-message-redesign

pull/941/head
ryanzhao 2 years ago
commit f855e71d61

@ -6040,7 +6040,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 385;
CURRENT_PROJECT_VERSION = 387;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@ -6065,7 +6065,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.2.1;
MARKETING_VERSION = 2.2.2;
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -6113,7 +6113,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 385;
CURRENT_PROJECT_VERSION = 387;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = SUQ8J2PCT7;
ENABLE_NS_ASSERTIONS = NO;
@ -6143,7 +6143,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.2.1;
MARKETING_VERSION = 2.2.2;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -6179,7 +6179,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 385;
CURRENT_PROJECT_VERSION = 387;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@ -6202,7 +6202,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.2.1;
MARKETING_VERSION = 2.2.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension";
@ -6253,7 +6253,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 385;
CURRENT_PROJECT_VERSION = 387;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = SUQ8J2PCT7;
ENABLE_NS_ASSERTIONS = NO;
@ -6281,7 +6281,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.2.1;
MARKETING_VERSION = 2.2.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension";
@ -7181,7 +7181,7 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 385;
CURRENT_PROJECT_VERSION = 387;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -7220,7 +7220,7 @@
"$(SRCROOT)",
);
LLVM_LTO = NO;
MARKETING_VERSION = 2.2.1;
MARKETING_VERSION = 2.2.2;
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
@ -7253,7 +7253,7 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 385;
CURRENT_PROJECT_VERSION = 387;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -7292,7 +7292,7 @@
"$(SRCROOT)",
);
LLVM_LTO = NO;
MARKETING_VERSION = 2.2.1;
MARKETING_VERSION = 2.2.2;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
PRODUCT_NAME = Session;

@ -314,7 +314,7 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat
.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
guard !updatedName.isEmpty else {
return showError(title: "vc_create_closed_group_group_name_missing_error".lowercased())
return showError(title: "vc_create_closed_group_group_name_missing_error".localized())
}
guard updatedName.count < 64 else {
return showError(title: "vc_create_closed_group_group_name_too_long_error".localized())

@ -29,6 +29,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
var shouldHighlightNextScrollToInteraction: Bool = false
var scrollButtonBottomConstraint: NSLayoutConstraint?
var scrollButtonMessageRequestsBottomConstraint: NSLayoutConstraint?
var scrollButtonPendingMessageRequestInfoBottomConstraint: NSLayoutConstraint?
var messageRequestsViewBotomConstraint: NSLayoutConstraint?
// Search
@ -54,9 +55,10 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
var scrollDistanceToBottomBeforeUpdate: CGFloat?
var baselineKeyboardHeight: CGFloat = 0
/// This flag is true between `viewDidAppear` and `viewWillDisappear` and is used to prevent keyboard changes
/// from trying to animate (as the animations can cause staggering with push transitions)
var viewIsFocussed = false
/// These flags are true between `viewDid/Will Appear/Disappear` and is used to prevent keyboard changes
/// from trying to animate (as the animations can cause buggy transitions)
var viewIsDisappearing = false
var viewIsAppearing = false
// Reaction
var currentReactionListSheet: ReactionListSheet?
@ -234,7 +236,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
result.text = "MESSAGE_REQUESTS_INFO".localized()
result.themeTextColor = .textSecondary
result.textAlignment = .center
result.numberOfLines = 2
result.numberOfLines = 0
return result
}()
@ -268,6 +270,23 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
return result
}()
private lazy var pendingMessageRequestExplanationLabel: UILabel = {
let result: UILabel = UILabel()
result.translatesAutoresizingMaskIntoConstraints = false
result.setContentCompressionResistancePriority(.required, for: .vertical)
result.font = UIFont.systemFont(ofSize: 12)
result.text = "MESSAGE_REQUEST_PENDING_APPROVAL_INFO".localized()
result.themeTextColor = .textSecondary
result.textAlignment = .center
result.numberOfLines = 0
result.isHidden = (
!self.messageRequestView.isHidden ||
self.viewModel.threadData.threadRequiresApproval == false
)
return result
}()
// MARK: - Settings
@ -322,6 +341,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
// Message requests view & scroll to bottom
view.addSubview(scrollButton)
view.addSubview(messageRequestView)
view.addSubview(pendingMessageRequestExplanationLabel)
messageRequestView.addSubview(messageRequestBlockButton)
messageRequestView.addSubview(messageRequestDescriptionLabel)
@ -335,6 +355,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
self.scrollButtonBottomConstraint = scrollButton.pin(.bottom, to: .bottom, of: view, withInset: -16)
self.scrollButtonBottomConstraint?.isActive = false // Note: Need to disable this to avoid a conflict with the other bottom constraint
self.scrollButtonMessageRequestsBottomConstraint = scrollButton.pin(.bottom, to: .top, of: messageRequestView, withInset: -16)
self.scrollButtonPendingMessageRequestInfoBottomConstraint = scrollButton.pin(.bottom, to: .top, of: pendingMessageRequestExplanationLabel, withInset: -16)
messageRequestBlockButton.pin(.top, to: .top, of: messageRequestView, withInset: 10)
messageRequestBlockButton.center(.horizontal, in: messageRequestView)
@ -352,6 +373,10 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
messageRequestDeleteButton.pin(.right, to: .right, of: messageRequestView, withInset: -20)
messageRequestDeleteButton.pin(.bottom, to: .bottom, of: messageRequestView)
messageRequestDeleteButton.set(.width, to: .width, of: messageRequestAcceptButton)
pendingMessageRequestExplanationLabel.pin(.left, to: .left, of: messageRequestView, withInset: 40)
pendingMessageRequestExplanationLabel.pin(.right, to: .right, of: messageRequestView, withInset: -40)
pendingMessageRequestExplanationLabel.pin(.bottom, to: .bottom, of: messageRequestView, withInset: -16)
// Unread count view
view.addSubview(unreadCountView)
@ -399,6 +424,8 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
super.viewWillAppear(animated)
startObservingChanges()
viewIsAppearing = true
}
override func viewDidAppear(_ animated: Bool) {
@ -407,7 +434,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
// Flag that the initial layout has been completed (the flag blocks and unblocks a number
// of different behaviours)
didFinishInitialLayout = true
viewIsFocussed = true
viewIsAppearing = false
if delayFirstResponder || isShowingSearchUI {
delayFirstResponder = false
@ -426,7 +453,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
viewIsFocussed = false
viewIsDisappearing = true
// Don't set the draft or resign the first responder if we are replacing the thread (want the keyboard
// to appear to remain focussed)
@ -442,6 +469,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
mediaCache.removeAllObjects()
hasReloadedThreadDataAfterDisappearance = false
viewIsDisappearing = false
}
@objc func applicationDidBecomeActive(_ notification: Notification) {
@ -578,18 +606,34 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
{
updateNavBarButtons(threadData: updatedThreadData, initialVariant: viewModel.initialThreadVariant)
let messageRequestsViewWasVisible: Bool = (messageRequestView.isHidden == false)
let messageRequestsViewWasVisible: Bool = (
messageRequestView.isHidden == false
)
let pendingMessageRequestInfoWasVisible: Bool = (
pendingMessageRequestExplanationLabel.isHidden == false
)
UIView.animate(withDuration: 0.3) { [weak self] in
self?.messageRequestView.isHidden = (
updatedThreadData.threadIsMessageRequest == false ||
updatedThreadData.threadRequiresApproval == true
)
self?.pendingMessageRequestExplanationLabel.isHidden = (
self?.messageRequestView.isHidden == false ||
updatedThreadData.threadRequiresApproval == false
)
self?.scrollButtonMessageRequestsBottomConstraint?.isActive = (
updatedThreadData.threadIsMessageRequest == true
self?.messageRequestView.isHidden == false
)
self?.scrollButtonPendingMessageRequestInfoBottomConstraint?.isActive = (
self?.scrollButtonPendingMessageRequestInfoBottomConstraint?.isActive == false &&
self?.pendingMessageRequestExplanationLabel.isHidden == false
)
self?.scrollButtonBottomConstraint?.isActive = (
self?.scrollButtonMessageRequestsBottomConstraint?.isActive == false &&
self?.scrollButtonPendingMessageRequestInfoBottomConstraint?.isActive == false
)
self?.scrollButtonBottomConstraint?.isActive = (updatedThreadData.threadIsMessageRequest == false)
// Update the table content inset and offset to account for
// the dissapearance of the messageRequestsView
@ -603,6 +647,16 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
trailing: 0
)
}
else if pendingMessageRequestInfoWasVisible {
let messageRequestsOffset: CGFloat = ((self?.pendingMessageRequestExplanationLabel.bounds.height ?? 0) + (16 * 2))
let oldContentInset: UIEdgeInsets = (self?.tableView.contentInset ?? UIEdgeInsets.zero)
self?.tableView.contentInset = UIEdgeInsets(
top: 0,
leading: 0,
bottom: max(oldContentInset.bottom - messageRequestsOffset, 0),
trailing: 0
)
}
}
}
@ -1069,7 +1123,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
// MARK: - Notifications
@objc func handleKeyboardWillChangeFrameNotification(_ notification: Notification) {
guard viewIsFocussed || !didFinishInitialLayout else { return }
guard !viewIsDisappearing else { return }
// Please refer to https://github.com/mapbox/mapbox-navigation-ios/issues/1600
// and https://stackoverflow.com/a/25260930 to better understand what we are
@ -1094,11 +1148,12 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
let keyboardTop = (UIScreen.main.bounds.height - keyboardRect.minY)
let messageRequestsOffset: CGFloat = (messageRequestView.isHidden ? 0 : messageRequestView.bounds.height + 16)
let pendingMessageRequestsOffset: CGFloat = (pendingMessageRequestExplanationLabel.isHidden ? 0 : (pendingMessageRequestExplanationLabel.bounds.height + (16 * 2)))
let oldContentInset: UIEdgeInsets = tableView.contentInset
let newContentInset: UIEdgeInsets = UIEdgeInsets(
top: 0,
leading: 0,
bottom: (Values.mediumSpacing + keyboardTop + messageRequestsOffset),
bottom: (Values.mediumSpacing + keyboardTop + messageRequestsOffset + pendingMessageRequestsOffset),
trailing: 0
)
let newContentOffsetY: CGFloat = (tableView.contentOffset.y + (newContentInset.bottom - oldContentInset.bottom))
@ -1117,7 +1172,7 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
}
// Perform the changes (don't animate if the initial layout hasn't been completed)
guard hasDoneLayout && didFinishInitialLayout else {
guard hasDoneLayout && didFinishInitialLayout && !viewIsAppearing else {
UIView.performWithoutAnimation {
changes()
}
@ -1134,8 +1189,6 @@ final class ConversationVC: BaseVC, ConversationSearchControllerDelegate, UITabl
}
@objc func handleKeyboardWillHideNotification(_ notification: Notification) {
guard viewIsFocussed else { return }
// Please refer to https://github.com/mapbox/mapbox-navigation-ios/issues/1600
// and https://stackoverflow.com/a/25260930 to better understand what we are
// doing with the UIViewAnimationOptions

@ -227,6 +227,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
// Remaining constraints
authorLabel.pin(.leading, to: .leading, of: snContentView, withInset: VisibleMessageCell.authorLabelInset)
authorLabel.pin(.trailing, to: .trailing, of: self, withInset: -Values.mediumSpacing)
// Under bubble content
addSubview(underBubbleStackView)
@ -1052,6 +1053,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
) -> TappableLabel {
let isOutgoing: Bool = (cellViewModel.variant == .standardOutgoing)
let result: TappableLabel = TappableLabel()
result.setContentCompressionResistancePriority(.required, for: .vertical)
result.themeBackgroundColor = .clear
result.isOpaque = false
result.isUserInteractionEnabled = true

@ -214,8 +214,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
BackgroundPoller.isValid = false
// Suspend database
NotificationCenter.default.post(name: Database.suspendNotification, object: self)
if CurrentAppContext().isInBackground() {
// Suspend database
NotificationCenter.default.post(name: Database.suspendNotification, object: self)
}
SNLog("Background poll failed due to manual timeout")
completionHandler(.failed)
@ -233,8 +235,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
BackgroundPoller.isValid = false
// Suspend database
NotificationCenter.default.post(name: Database.suspendNotification, object: self)
if CurrentAppContext().isInBackground() {
// Suspend database
NotificationCenter.default.post(name: Database.suspendNotification, object: self)
}
cancelTimer.invalidate()
completionHandler(result)

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -597,4 +597,4 @@
"MESSAGE_STATE_SENT" = "sent";
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -598,3 +598,4 @@
"YOU_DISAPPERING_MESSAGES_INFO_ENABLE" = "You have set messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_UPDATE" = "You have changed messages to disappear %@ after they have been %@";
"YOU_DISAPPERING_MESSAGES_INFO_DISABLE" = "You have turned off disappearing messages";
"MESSAGE_REQUEST_PENDING_APPROVAL_INFO" = "You will be able to send voice messages and attachments once the recipient has approved this message request";

@ -227,6 +227,7 @@ public enum OpenGroupAPI {
public static func capabilities(
_ db: Database,
server: String,
forceBlinded: Bool = false,
using dependencies: SMKDependencies = SMKDependencies()
) -> Promise<(OnionRequestResponseInfoType, Capabilities)> {
return OpenGroupAPI
@ -236,6 +237,7 @@ public enum OpenGroupAPI {
server: server,
endpoint: .capabilities
),
forceBlinded: forceBlinded,
using: dependencies
)
.decoded(as: Capabilities.self, on: OpenGroupAPI.workQueue, using: dependencies)
@ -1260,6 +1262,7 @@ public enum OpenGroupAPI {
messageBytes: Bytes,
for serverName: String,
fallbackSigningType signingType: SessionId.Prefix,
forceBlinded: Bool = false,
using dependencies: SMKDependencies = SMKDependencies()
) -> (publicKey: String, signature: Bytes)? {
guard
@ -1279,7 +1282,7 @@ public enum OpenGroupAPI {
.defaulting(to: [])
// If we have no capabilities or if the server supports blinded keys then sign using the blinded key
if capabilities.isEmpty || capabilities.contains(.blind) {
if forceBlinded || capabilities.isEmpty || capabilities.contains(.blind) {
guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else {
return nil
}
@ -1326,6 +1329,7 @@ public enum OpenGroupAPI {
request: URLRequest,
for serverName: String,
with serverPublicKey: String,
forceBlinded: Bool = false,
using dependencies: SMKDependencies = SMKDependencies()
) -> URLRequest? {
guard let url: URL = request.url else { return nil }
@ -1366,7 +1370,7 @@ public enum OpenGroupAPI {
.appending(contentsOf: bodyHash ?? [])
/// Sign the above message
guard let signResult: (publicKey: String, signature: Bytes) = sign(db, messageBytes: messageBytes, for: serverName, fallbackSigningType: .unblinded, using: dependencies) else {
guard let signResult: (publicKey: String, signature: Bytes) = sign(db, messageBytes: messageBytes, for: serverName, fallbackSigningType: .unblinded, forceBlinded: forceBlinded, using: dependencies) else {
return nil
}
@ -1386,6 +1390,7 @@ public enum OpenGroupAPI {
private static func send<T: Encodable>(
_ db: Database,
request: Request<T, Endpoint>,
forceBlinded: Bool = false,
using dependencies: SMKDependencies = SMKDependencies()
) -> Promise<(OnionRequestResponseInfoType, Data?)> {
let urlRequest: URLRequest
@ -1406,7 +1411,7 @@ public enum OpenGroupAPI {
guard let publicKey: String = maybePublicKey else { return Promise(error: OpenGroupAPIError.noPublicKey) }
// Attempt to sign the request with the new auth
guard let signedRequest: URLRequest = sign(db, request: urlRequest, for: request.server, with: publicKey, using: dependencies) else {
guard let signedRequest: URLRequest = sign(db, request: urlRequest, for: request.server, with: publicKey, forceBlinded: forceBlinded, using: dependencies) else {
return Promise(error: OpenGroupAPIError.signingFailed)
}

@ -84,7 +84,7 @@ extension OpenGroupAPI {
let (promise, seal) = Promise<Void>.pending()
promise.retainUntilComplete()
Threading.pollerQueue.async {
let pollingLogic: () -> Void = {
dependencies.storage
.read { db -> Promise<(Int64, PollResponse)> in
let failureCount: Int64 = (try? OpenGroup
@ -150,7 +150,7 @@ extension OpenGroupAPI {
error: error
)
.done(on: OpenGroupAPI.workQueue) { [weak self] didHandleError in
if !didHandleError {
if !didHandleError && isBackgroundPollerValid() {
// Increase the failure count
let pollFailureCount: Int64 = Storage.shared
.read { db in
@ -181,6 +181,14 @@ extension OpenGroupAPI {
}
}
// If this was run via the background poller then don't run on the pollerQueue
if calledFromBackgroundPoller {
pollingLogic()
}
else {
Threading.pollerQueue.async { pollingLogic() }
}
return promise
}
@ -213,11 +221,12 @@ extension OpenGroupAPI {
OpenGroupAPI.capabilities(
db,
server: server,
forceBlinded: true,
using: dependencies
)
}
.then(on: OpenGroupAPI.workQueue) { [weak self] _, responseBody -> Promise<Void> in
guard let strongSelf = self else { return Promise.value(()) }
guard let strongSelf = self, isBackgroundPollerValid() else { return Promise.value(()) }
// Handle the updated capabilities and re-trigger the poll
strongSelf.isPolling = false

@ -16,6 +16,9 @@ public enum OnionRequestAPIError: LocalizedError {
switch self {
case .httpRequestFailedAtDestination(let statusCode, let data, let destination):
if statusCode == 429 { return "Rate limited." }
if let processedResponseBodyData: Data = OnionRequestAPI.process(bencodedData: data)?.body, let errorResponse: String = String(data: processedResponseBodyData, encoding: .utf8) {
return "HTTP request failed at destination (\(destination)) with status code: \(statusCode), error body: \(errorResponse)."
}
if let errorResponse: String = String(data: data, encoding: .utf8) {
return "HTTP request failed at destination (\(destination)) with status code: \(statusCode), error body: \(errorResponse)."
}

@ -700,72 +700,86 @@ public enum OnionRequestAPI: OnionRequestAPIType {
do {
let data: Data = try AESGCM.decrypt(responseData, with: destinationSymmetricKey)
// The data will be in the form of `l123:jsone` or `l123:json456:bodye` so we need to break the data into
// parts to properly process it
guard let responseString: String = String(data: data, encoding: .ascii), responseString.starts(with: "l") else {
return seal.reject(HTTP.Error.invalidResponse)
}
let stringParts: [String.SubSequence] = responseString.split(separator: ":")
guard stringParts.count > 1, let infoLength: Int = Int(stringParts[0].suffix(from: stringParts[0].index(stringParts[0].startIndex, offsetBy: 1))) else {
return seal.reject(HTTP.Error.invalidResponse)
}
let infoStringStartIndex: String.Index = responseString.index(responseString.startIndex, offsetBy: "l\(infoLength):".count)
let infoStringEndIndex: String.Index = responseString.index(infoStringStartIndex, offsetBy: infoLength)
let infoString: String = String(responseString[infoStringStartIndex..<infoStringEndIndex])
guard let infoStringData: Data = infoString.data(using: .utf8), let responseInfo: ResponseInfo = try? JSONDecoder().decode(ResponseInfo.self, from: infoStringData) else {
// Process the bencoded response
guard let processedResponse: (info: ResponseInfo, body: Data?) = process(bencodedData: data) else {
return seal.reject(HTTP.Error.invalidResponse)
}
// Custom handle a clock out of sync error (v4 returns '425' but included the '406' just in case)
guard responseInfo.code != 406 && responseInfo.code != 425 else {
// Custom handle a clock out of sync error (v4 returns '425' but included the '406'
// just in case)
guard processedResponse.info.code != 406 && processedResponse.info.code != 425 else {
SNLog("The user's clock is out of sync with the service node network.")
return seal.reject(SnodeAPIError.clockOutOfSync)
}
guard responseInfo.code != 401 else { // Signature verification failed
guard processedResponse.info.code != 401 else { // Signature verification failed
SNLog("Failed to verify the signature.")
return seal.reject(SnodeAPIError.signatureVerificationFailed)
}
// Handle error status codes
guard 200...299 ~= responseInfo.code else {
guard 200...299 ~= processedResponse.info.code else {
return seal.reject(
OnionRequestAPIError.httpRequestFailedAtDestination(
statusCode: UInt(responseInfo.code),
statusCode: UInt(processedResponse.info.code),
data: data,
destination: destination
)
)
}
// If there is no data in the response then just return the ResponseInfo
guard responseString.count > "l\(infoLength)\(infoString)e".count else {
return seal.fulfill((responseInfo, nil))
}
// Extract the response data as well
let dataString: String = String(responseString.suffix(from: infoStringEndIndex))
let dataStringParts: [String.SubSequence] = dataString.split(separator: ":")
guard dataStringParts.count > 1, let finalDataLength: Int = Int(dataStringParts[0]), let suffixData: Data = "e".data(using: .utf8) else {
return seal.reject(HTTP.Error.invalidResponse)
}
let dataBytes: Array<UInt8> = Array(data)
let dataEndIndex: Int = (dataBytes.count - suffixData.count)
let dataStartIndex: Int = (dataEndIndex - finalDataLength)
let finalDataBytes: ArraySlice<UInt8> = dataBytes[dataStartIndex..<dataEndIndex]
let finalData: Data = Data(finalDataBytes)
return seal.fulfill((responseInfo, finalData))
return seal.fulfill(processedResponse)
}
catch {
return seal.reject(error)
}
}
}
public static func process(bencodedData data: Data) -> (info: ResponseInfo, body: Data?)? {
// The data will be in the form of `l123:jsone` or `l123:json456:bodye` so we need to break the data
// into parts to properly process it
guard let responseString: String = String(data: data, encoding: .ascii), responseString.starts(with: "l") else {
return nil
}
let stringParts: [String.SubSequence] = responseString.split(separator: ":")
guard stringParts.count > 1, let infoLength: Int = Int(stringParts[0].suffix(from: stringParts[0].index(stringParts[0].startIndex, offsetBy: 1))) else {
return nil
}
let infoStringStartIndex: String.Index = responseString.index(responseString.startIndex, offsetBy: "l\(infoLength):".count)
let infoStringEndIndex: String.Index = responseString.index(infoStringStartIndex, offsetBy: infoLength)
let infoString: String = String(responseString[infoStringStartIndex..<infoStringEndIndex])
guard let infoStringData: Data = infoString.data(using: .utf8), let responseInfo: ResponseInfo = try? JSONDecoder().decode(ResponseInfo.self, from: infoStringData) else {
return nil
}
// Custom handle a clock out of sync error (v4 returns '425' but included the '406' just in case)
guard responseInfo.code != 406 && responseInfo.code != 425 else { return nil }
guard responseInfo.code != 401 else { return nil }
// If there is no data in the response then just return the ResponseInfo
guard responseString.count > "l\(infoLength)\(infoString)e".count else {
return (responseInfo, nil)
}
// Extract the response data as well
let dataString: String = String(responseString.suffix(from: infoStringEndIndex))
let dataStringParts: [String.SubSequence] = dataString.split(separator: ":")
guard dataStringParts.count > 1, let finalDataLength: Int = Int(dataStringParts[0]), let suffixData: Data = "e".data(using: .utf8) else {
return nil
}
let dataBytes: Array<UInt8> = Array(data)
let dataEndIndex: Int = (dataBytes.count - suffixData.count)
let dataStartIndex: Int = (dataEndIndex - finalDataLength)
let finalDataBytes: ArraySlice<UInt8> = dataBytes[dataStartIndex..<dataEndIndex]
let finalData: Data = Data(finalDataBytes)
return (responseInfo, finalData)
}
}

Loading…
Cancel
Save