Merge branch 'master' of https://github.com/oxen-io/session-ios into voice-calls-2

pull/560/head
ryanzhao 3 years ago
commit 6e07c56e7d

@ -26,6 +26,6 @@ Copyright 2011 Whisper Systems
Copyright 2013-2017 Open Whisper Systems
Copyright 2019-2021 The Loki Project
Copyright 2019-2021 The Oxen Project
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html

@ -5088,7 +5088,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 288;
CURRENT_PROJECT_VERSION = 294;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@ -5113,7 +5113,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.11.13;
MARKETING_VERSION = 1.11.15;
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -5161,7 +5161,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 288;
CURRENT_PROJECT_VERSION = 294;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = SUQ8J2PCT7;
ENABLE_NS_ASSERTIONS = NO;
@ -5191,7 +5191,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.11.13;
MARKETING_VERSION = 1.11.15;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -5227,7 +5227,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 288;
CURRENT_PROJECT_VERSION = 294;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@ -5250,7 +5250,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.11.13;
MARKETING_VERSION = 1.11.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension";
@ -5301,7 +5301,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 288;
CURRENT_PROJECT_VERSION = 294;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = SUQ8J2PCT7;
ENABLE_NS_ASSERTIONS = NO;
@ -5329,7 +5329,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.11.13;
MARKETING_VERSION = 1.11.15;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension";
@ -6237,7 +6237,7 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 288;
CURRENT_PROJECT_VERSION = 294;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -6277,7 +6277,7 @@
"$(SRCROOT)",
);
LLVM_LTO = NO;
MARKETING_VERSION = 1.11.13;
MARKETING_VERSION = 1.11.15;
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
@ -6309,7 +6309,7 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 288;
CURRENT_PROJECT_VERSION = 294;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -6349,7 +6349,7 @@
"$(SRCROOT)",
);
LLVM_LTO = NO;
MARKETING_VERSION = 1.11.13;
MARKETING_VERSION = 1.11.15;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
PRODUCT_NAME = Session;

@ -367,6 +367,13 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
snInputView.hideMentionsUI()
self.oldText = newText
}
func showInputAccessoryView() {
UIView.animate(withDuration: 0.25, animations: {
self.inputAccessoryView?.isHidden = false
self.inputAccessoryView?.alpha = 1
})
}
// MARK: View Item Interaction
func handleViewItemLongPressed(_ viewItem: ConversationViewItem) {
@ -568,19 +575,12 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
return
}
func showInputAccessoryView() {
UIView.animate(withDuration: 0.25, animations: {
self.inputAccessoryView?.isHidden = false
self.inputAccessoryView?.alpha = 1
})
}
if viewItem.interaction.interactionType() == .outgoingMessage,
let message = viewItem.interaction as? TSMessage, message.serverHash != nil {
let alertVC = UIAlertController.init(title: nil, message: nil, preferredStyle: .actionSheet)
let deleteLocallyAction = UIAlertAction.init(title: NSLocalizedString("delete_message_for_me", comment: ""), style: .destructive) { _ in
self.deleteLocally(viewItem)
showInputAccessoryView()
self.showInputAccessoryView()
}
alertVC.addAction(deleteLocallyAction)
@ -590,12 +590,12 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
}
let deleteRemotelyAction = UIAlertAction.init(title: title, style: .destructive) { _ in
self.deleteForEveryone(viewItem)
showInputAccessoryView()
self.showInputAccessoryView()
}
alertVC.addAction(deleteRemotelyAction)
let cancelAction = UIAlertAction.init(title: NSLocalizedString("TXT_CANCEL_TITLE", comment: ""), style: .cancel) {_ in
showInputAccessoryView()
self.showInputAccessoryView()
}
alertVC.addAction(cancelAction)
@ -682,10 +682,24 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
func openURL(_ url: URL) {
// URLs can be unsafe, so always ask the user whether they want to open one
let urlModal = URLModal(url: url)
urlModal.modalPresentationStyle = .overFullScreen
urlModal.modalTransitionStyle = .crossDissolve
present(urlModal, animated: true, completion: nil)
let title = NSLocalizedString("modal_open_url_title", comment: "")
let message = String(format: NSLocalizedString("modal_open_url_explanation", comment: ""), url.absoluteString)
let alertVC = UIAlertController.init(title: title, message: message, preferredStyle: .actionSheet)
let openAction = UIAlertAction.init(title: NSLocalizedString("modal_open_url_button_title", comment: ""), style: .default) { _ in
UIApplication.shared.open(url, options: [:], completionHandler: nil)
self.showInputAccessoryView()
}
alertVC.addAction(openAction)
let copyAction = UIAlertAction.init(title: NSLocalizedString("modal_copy_url_button_title", comment: ""), style: .default) { _ in
UIPasteboard.general.string = url.absoluteString
self.showInputAccessoryView()
}
alertVC.addAction(copyAction)
let cancelAction = UIAlertAction.init(title: NSLocalizedString("cancel", comment: ""), style: .cancel) {_ in
self.showInputAccessoryView()
}
alertVC.addAction(cancelAction)
self.presentAlert(alertVC)
}
func joinOpenGroup(name: String, url: String) {

@ -258,10 +258,8 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let text = snInputView.text
if !text.isEmpty {
Storage.write { transaction in
self.thread.setDraft(text, transaction: transaction)
}
Storage.write { transaction in
self.thread.setDraft(text, transaction: transaction)
}
inputAccessoryView?.resignFirstResponder()
}

@ -1158,7 +1158,7 @@ NS_ASSUME_NONNULL_BEGIN
break;
}
uint64_t viewItemTimestamp = viewItem.interaction.timestampForUI;
uint64_t viewItemTimestamp = viewItem.interaction.timestamp;
OWSAssertDebug(viewItemTimestamp > 0);
BOOL shouldShowDate = NO;
@ -1225,7 +1225,7 @@ NS_ASSUME_NONNULL_BEGIN
NSAttributedString *_Nullable senderName = nil;
OWSInteractionType interactionType = viewItem.interaction.interactionType;
NSString *timestampText = [DateUtil formatTimestampShort:viewItem.interaction.timestampForUI];
NSString *timestampText = [DateUtil formatTimestampShort:viewItem.interaction.timestamp];
if (interactionType == OWSInteractionType_OutgoingMessage) {
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)viewItem.interaction;

@ -10,15 +10,35 @@ final class ExpandingAttachmentsButton : UIView, InputViewButtonDelegate {
private lazy var cameraButtonContainerBottomConstraint = cameraButtonContainer.pin(.bottom, to: .bottom, of: self)
// MARK: UI Components
lazy var gifButton = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_gif_black"), delegate: self, hasOpaqueBackground: true)
lazy var gifButton: InputViewButton = {
let result = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_gif_black"), delegate: self, hasOpaqueBackground: true)
result.accessibilityLabel = NSLocalizedString("accessibility_gif_button", comment: "")
return result
}()
lazy var gifButtonContainer = container(for: gifButton)
lazy var documentButton = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_document_black"), delegate: self, hasOpaqueBackground: true)
lazy var documentButton: InputViewButton = {
let result = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_document_black"), delegate: self, hasOpaqueBackground: true)
result.accessibilityLabel = NSLocalizedString("accessibility_document_button", comment: "")
return result
}()
lazy var documentButtonContainer = container(for: documentButton)
lazy var libraryButton = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_camera_roll_black"), delegate: self, hasOpaqueBackground: true)
lazy var libraryButton: InputViewButton = {
let result = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_camera_roll_black"), delegate: self, hasOpaqueBackground: true)
result.accessibilityLabel = NSLocalizedString("accessibility_library_button", comment: "")
return result
}()
lazy var libraryButtonContainer = container(for: libraryButton)
lazy var cameraButton = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_camera_black"), delegate: self, hasOpaqueBackground: true)
lazy var cameraButton: InputViewButton = {
let result = InputViewButton(icon: #imageLiteral(resourceName: "actionsheet_camera_black"), delegate: self, hasOpaqueBackground: true)
result.accessibilityLabel = NSLocalizedString("accessibility_camera_button", comment: "")
return result
}()
lazy var cameraButtonContainer = container(for: cameraButton)
lazy var mainButton = InputViewButton(icon: #imageLiteral(resourceName: "ic_plus_24"), delegate: self)
lazy var mainButton: InputViewButton = {
let result = InputViewButton(icon: #imageLiteral(resourceName: "ic_plus_24"), delegate: self)
result.accessibilityLabel = NSLocalizedString("accessibility_expanding_attachments_button", comment: "")
return result
}()
lazy var mainButtonContainer = container(for: mainButton)
// MARK: Lifecycle
@ -66,6 +86,7 @@ final class ExpandingAttachmentsButton : UIView, InputViewButtonDelegate {
// MARK: Animation
private func expandOrCollapse() {
if isExpanded {
mainButton.accessibilityLabel = NSLocalizedString("accessibility_main_button_collapse", comment: "")
let expandedButtonSize = InputViewButton.expandedSize
let spacing: CGFloat = 4
cameraButtonContainerBottomConstraint.constant = -1 * (expandedButtonSize + spacing)
@ -79,6 +100,7 @@ final class ExpandingAttachmentsButton : UIView, InputViewButtonDelegate {
self.layoutIfNeeded()
}
} else {
mainButton.accessibilityLabel = NSLocalizedString("accessibility_expanding_attachments_button", comment: "")
[ gifButtonContainerBottomConstraint, documentButtonContainerBottomConstraint, libraryButtonContainerBottomConstraint, cameraButtonContainerBottomConstraint ].forEach {
$0.constant = 0
}

@ -26,6 +26,8 @@ public final class InputTextView : UITextView, UITextViewDelegate {
super.init(frame: CGRect.zero, textContainer: nil)
setUpViewHierarchy()
self.delegate = self
self.isAccessibilityElement = true
self.accessibilityLabel = NSLocalizedString("vc_conversation_input_prompt", comment: "")
}
public override init(frame: CGRect, textContainer: NSTextContainer?) {

@ -22,11 +22,17 @@ final class InputView : UIView, InputViewButtonDelegate, InputTextViewDelegate,
// MARK: UI Components
private lazy var attachmentsButton = ExpandingAttachmentsButton(delegate: delegate)
private lazy var voiceMessageButton = InputViewButton(icon: #imageLiteral(resourceName: "Microphone"), delegate: self)
private lazy var voiceMessageButton: InputViewButton = {
let result = InputViewButton(icon: #imageLiteral(resourceName: "Microphone"), delegate: self)
result.accessibilityLabel = NSLocalizedString("VOICE_MESSAGE_TOO_SHORT_ALERT_TITLE", comment: "")
result.accessibilityHint = NSLocalizedString("VOICE_MESSAGE_TOO_SHORT_ALERT_MESSAGE", comment: "")
return result
}()
private lazy var sendButton: InputViewButton = {
let result = InputViewButton(icon: #imageLiteral(resourceName: "ArrowUp"), isSendButton: true, delegate: self)
result.isHidden = true
result.accessibilityLabel = NSLocalizedString("ATTACHMENT_APPROVAL_SEND_BUTTON", comment: "")
return result
}()
private lazy var voiceMessageButtonContainer = container(for: voiceMessageButton)

@ -25,6 +25,7 @@ final class InputViewButton : UIView {
self.hasOpaqueBackground = hasOpaqueBackground
super.init(frame: CGRect.zero)
setUpViewHierarchy()
self.isAccessibilityElement = true
}
override init(frame: CGRect) {

@ -221,6 +221,10 @@ class PhotoCollectionContents {
class PhotoCollection {
private let collection: PHAssetCollection
// The user never sees this collection, but we use it for a null object pattern
// when the user has denied photos access.
static let empty = PhotoCollection(collection: PHAssetCollection())
init(collection: PHAssetCollection) {
self.collection = collection
@ -275,11 +279,30 @@ class PhotoLibrary: NSObject, PHPhotoLibraryChangeObserver {
deinit {
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
private lazy var fetchOptions: PHFetchOptions = {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "endDate", ascending: true)]
return fetchOptions
}()
func defaultPhotoCollection() -> PhotoCollection {
guard let photoCollection = allPhotoCollections().first else {
owsFail("Could not locate Camera Roll.")
var fetchedCollection: PhotoCollection?
PHAssetCollection.fetchAssetCollections(
with: .smartAlbum,
subtype: .smartAlbumUserLibrary,
options: fetchOptions
).enumerateObjects { collection, _, stop in
fetchedCollection = PhotoCollection(collection: collection)
stop.pointee = true
}
guard let photoCollection = fetchedCollection else {
Logger.info("Using empty photo collection.")
assert(PHPhotoLibrary.authorizationStatus() == .denied)
return PhotoCollection.empty
}
return photoCollection
}

@ -531,6 +531,7 @@
"modal_open_url_title" = "URL öffnen?";
"modal_open_url_explanation" = "Möchten Sie %@ wirklich öffnen?";
"modal_open_url_button_title" = "Öffnen";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "%@ entsperren?";
"modal_blocked_explanation" = "Sind Sie sicher, dass Sie %@ entsperren möchten?";
"modal_blocked_button_title" = "Entsperren";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Open URL?";
"modal_open_url_explanation" = "Are you sure you want to open %@?";
"modal_open_url_button_title" = "Open";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Unblock %@?";
"modal_blocked_explanation" = "Are you sure you want to unblock %@?";
"modal_blocked_button_title" = "Unblock";
@ -557,4 +558,11 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";

@ -531,6 +531,7 @@
"modal_open_url_title" = "¿Abrir URL?";
"modal_open_url_explanation" = "¿Estás seguro de que quieres abrir %@?";
"modal_open_url_button_title" = "Abrir";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "¿Desbloquear a %@?";
"modal_blocked_explanation" = "¿Estás seguro de que quieres desbloquear a %@?";
"modal_blocked_button_title" = "Desbloquear";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "URL باز شود؟";
"modal_open_url_explanation" = "آیا مطمئن هستید که میخواهید %@ را باز کنید؟";
"modal_open_url_button_title" = "باز کن";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Unblock %@?";
"modal_blocked_explanation" = "Are you sure you want to unblock %@?";
"modal_blocked_button_title" = "رفع مسدودی";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Avataanko URL?";
"modal_open_url_explanation" = "Oletko varma, että haluat avata linkin %@?";
"modal_open_url_button_title" = "Avaa";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Poista esto henkilöltä %@?";
"modal_blocked_explanation" = "Oletko varma, että haluat poistaa eston henkilöltä %@?";
"modal_blocked_button_title" = "Poista esto";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Ouvrir l'URL?";
"modal_open_url_explanation" = "Êtes-vous sûr de vouloir ouvrir %@?";
"modal_open_url_button_title" = "Ouvrir";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Débloquer %@?";
"modal_blocked_explanation" = "Confirmez-vous le déblocage de %@ ?";
"modal_blocked_button_title" = "Débloquer";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "यूआरएल खोलें";
"modal_open_url_explanation" = "क्या आप वाकई %@ खोलना चाहते हैं?";
"modal_open_url_button_title" = "खोलें";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "%@ अनब्लॉक करें?";
"modal_blocked_explanation" = "क्या आप वाकई %@ को अनब्लॉक करना चाहते हैं?";
"modal_blocked_button_title" = "अनब्लॉक करें";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Otvori poveznicu?";
"modal_open_url_explanation" = "Jeste li sigurni da želite otvoriti %@?";
"modal_open_url_button_title" = "Otvori";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Deblokiraj %@?";
"modal_blocked_explanation" = "Jeste li sigurni da želite deblokirati %@?";
"modal_blocked_button_title" = "Deblokiraj";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Open URL?";
"modal_open_url_explanation" = "Are you sure you want to open %@?";
"modal_open_url_button_title" = "Open";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Unblock %@?";
"modal_blocked_explanation" = "Are you sure you want to unblock %@?";
"modal_blocked_button_title" = "Unblock";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Aprire URL?";
"modal_open_url_explanation" = "Sei sicuro di voler aprire %@?";
"modal_open_url_button_title" = "Apri";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Sbloccare %@?";
"modal_blocked_explanation" = "Sei sicuro di voler sbloccare %@?";
"modal_blocked_button_title" = "Sblocca";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "URLを開きますか";
"modal_open_url_explanation" = "%@を本当に開いてもよろしいですか?";
"modal_open_url_button_title" = "開く";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "%@のブロックを解除しますか?";
"modal_blocked_explanation" = "%@のブロックを解除してもよろしいですか?";
"modal_blocked_button_title" = "ブロックを解除する";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "URL openen?";
"modal_open_url_explanation" = "Weet u zeker dat u %@ wilt openen?";
"modal_open_url_button_title" = "Openen";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Blokkering opheffen voor %@?";
"modal_blocked_explanation" = "Weet je zeker dat je %@ weer wilt toelaten?";
"modal_blocked_button_title" = "Deblokkeren";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Otworzyć URL?";
"modal_open_url_explanation" = "Czy na pewno chcesz otworzyć %@?";
"modal_open_url_button_title" = "Otwórz";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Odblokować %@?";
"modal_blocked_explanation" = "Czy na pewno chcesz odblokować %@?";
"modal_blocked_button_title" = "Odblokuj";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Abrir URL?";
"modal_open_url_explanation" = "Você tem certeza que deseja abrir %@/?";
"modal_open_url_button_title" = "Abrir";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Desbloquear %@?";
"modal_blocked_explanation" = "Você tem certeza que deseja desbloquear %@?";
"modal_blocked_button_title" = "Desbloquear";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Открыть ссылку?";
"modal_open_url_explanation" = "Вы уверены, что хотите открыть %@?";
"modal_open_url_button_title" = "Открыть";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Разблокировать %@?";
"modal_blocked_explanation" = "Вы уверены, что хотите разблокировать %@?";
"modal_blocked_button_title" = "Разблокировать";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Otvoriť URL?";
"modal_open_url_explanation" = "Ste si istý, že chcete otvoriť %@?";
"modal_open_url_button_title" = "Otvoriť";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Odblokovať %@?";
"modal_blocked_explanation" = "Ste si istý/á, že chcete odblokovať %@?";
"modal_blocked_button_title" = "Odblokovať";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Öppna URL?";
"modal_open_url_explanation" = "Är du säker på att du vill öppna %@?";
"modal_open_url_button_title" = "Öppna";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Avblockera %@?";
"modal_blocked_explanation" = "Är du säker på att du vill avblockera %@?";
"modal_blocked_button_title" = "Avblockera";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "เปิดลิงค์ URL ไหม";
"modal_open_url_explanation" = "แน่ใจไหมว่าคุณต้องกาเปิดดู %@";
"modal_open_url_button_title" = "เปิด";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "เลิกบล็อก %@ ไหม";
"modal_blocked_explanation" = "แน่ใจไหมว่าคุณต้องการเลิกบล็อก %@";
"modal_blocked_button_title" = "เลิกบล็อก";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "Open URL?";
"modal_open_url_explanation" = "Are you sure you want to open %@?";
"modal_open_url_button_title" = "Open";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "Unblock %@?";
"modal_blocked_explanation" = "Are you sure you want to unblock %@?";
"modal_blocked_button_title" = "Unblock";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "打開連結?";
"modal_open_url_explanation" = "您確定要打開 %@ 嗎?";
"modal_open_url_button_title" = "打開";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "解除封鎖 %@ 嗎?";
"modal_blocked_explanation" = "您確定要解除封鎖 %@ 嗎?";
"modal_blocked_button_title" = "解除封鎖";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -531,6 +531,7 @@
"modal_open_url_title" = "打开链接?";
"modal_open_url_explanation" = "确定要打开 %@ 吗?";
"modal_open_url_button_title" = "打开";
"modal_copy_url_button_title" = "Copy Link";
"modal_blocked_title" = "从黑名单中移除 %@ 吗?";
"modal_blocked_explanation" = "确定解除屏蔽%@吗?";
"modal_blocked_button_title" = "解除屏蔽";
@ -557,3 +558,9 @@
"context_menu_save" = "Save";
"context_menu_ban_user" = "Ban User";
"context_menu_ban_and_delete_all" = "Ban and Delete All";
"accessibility_expanding_attachments_button" = "Add attachments";
"accessibility_gif_button" = "Gif";
"accessibility_document_button" = "Document";
"accessibility_library_button" = "Photo library";
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";

@ -122,6 +122,14 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
}
func continueWithSeed(_ seed: Data) {
if (seed.count != 16) {
let alert = UIAlertController(title: NSLocalizedString("invalid_recovery_phrase", comment: ""), message: NSLocalizedString("Please check the Recovery Phrase and try again.", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: { _ in
self.scanQRCodeWrapperVC.startCapture()
}))
presentAlert(alert)
return
}
let (ed25519KeyPair, x25519KeyPair) = KeyPairUtilities.generate(from: seed)
Onboarding.Flow.link.preregister(with: seed, ed25519KeyPair: ed25519KeyPair, x25519KeyPair: x25519KeyPair)
TSAccountManager.sharedInstance().didRegister()

@ -1,4 +1,5 @@
import UIKit
import SessionUIKit
final class ConversationCell : UITableViewCell {
var threadViewModel: ThreadViewModel! { didSet { update() } }
@ -37,6 +38,26 @@ final class ConversationCell : UITableViewCell {
return result
}()
private lazy var hasMentionView: UIView = {
let result = UIView()
result.backgroundColor = Colors.accent
let size = ConversationCell.unreadCountViewSize
result.set(.width, to: size)
result.set(.height, to: size)
result.layer.masksToBounds = true
result.layer.cornerRadius = size / 2
return result
}()
private lazy var hasMentionLabel: UILabel = {
let result = UILabel()
result.font = .boldSystemFont(ofSize: Values.verySmallFontSize)
result.textColor = Colors.text
result.text = "@"
result.textAlignment = .center
return result
}()
private lazy var timestampLabel: UILabel = {
let result = UILabel()
result.font = .systemFont(ofSize: Values.smallFontSize)
@ -98,9 +119,12 @@ final class ConversationCell : UITableViewCell {
// Unread count view
unreadCountView.addSubview(unreadCountLabel)
unreadCountLabel.pin(to: unreadCountView)
// Has mention view
hasMentionView.addSubview(hasMentionLabel)
hasMentionLabel.pin(to: hasMentionView)
// Label stack view
let topLabelSpacer = UIView.hStretchingSpacer()
let topLabelStackView = UIStackView(arrangedSubviews: [ displayNameLabel, unreadCountView, topLabelSpacer, timestampLabel ])
let topLabelStackView = UIStackView(arrangedSubviews: [ displayNameLabel, unreadCountView, hasMentionView, topLabelSpacer, timestampLabel ])
topLabelStackView.axis = .horizontal
topLabelStackView.alignment = .center
topLabelStackView.spacing = Values.smallSpacing / 2 // Effectively Values.smallSpacing because there'll be spacing before and after the invisible spacer
@ -176,6 +200,7 @@ final class ConversationCell : UITableViewCell {
unreadCountLabel.text = unreadCount < 100 ? "\(unreadCount)" : "99+"
let fontSize = (unreadCount < 100) ? Values.verySmallFontSize : 8
unreadCountLabel.font = .boldSystemFont(ofSize: fontSize)
hasMentionView.isHidden = !threadViewModel.hasUnreadMentions
profilePictureView.update(for: thread)
displayNameLabel.text = getDisplayName()
timestampLabel.text = DateUtil.formatDate(forDisplay: threadViewModel.lastMessageDate)

@ -71,4 +71,10 @@ final class ScanQRCodeWrapperVC : BaseVC {
@objc private func close() {
presentingViewController?.dismiss(animated: true, completion: nil)
}
public func startCapture() {
DispatchQueue.main.async { [weak self] in
self?.scanQRCodeVC.startCapture()
}
}
}

@ -162,4 +162,3 @@ public final class AttachmentDownloadJob : NSObject, Job, NSCoding { // NSObject
delegate?.handleJobFailed(self, with: error)
}
}

@ -19,7 +19,6 @@ public extension TSIncomingMessage {
expiresInSeconds: !isOpenGroupMessage ? expiration : 0, // Ensure we don't ever expire open group messages
quotedMessage: quotedMessage,
linkPreview: linkPreview,
serverTimestamp: visibleMessage.openGroupServerTimestamp as NSNumber?,
wasReceivedByUD: true,
openGroupInvitationName: visibleMessage.openGroupInvitation?.name,
openGroupInvitationURL: visibleMessage.openGroupInvitation?.url,

@ -12,8 +12,6 @@ NS_ASSUME_NONNULL_BEGIN
@interface TSIncomingMessage : TSMessage <OWSReadTracking>
@property (nonatomic, readonly, nullable) NSNumber *serverTimestamp;
@property (nonatomic, readonly) BOOL wasReceivedByUD;
@property (nonatomic, readonly) BOOL isUserMentioned;
@ -61,7 +59,6 @@ NS_ASSUME_NONNULL_BEGIN
expiresInSeconds:(uint32_t)expiresInSeconds
quotedMessage:(nullable TSQuotedMessage *)quotedMessage
linkPreview:(nullable OWSLinkPreview *)linkPreview
serverTimestamp:(nullable NSNumber *)serverTimestamp
wasReceivedByUD:(BOOL)wasReceivedByUD
openGroupInvitationName:(nullable NSString *)openGroupInvitationName
openGroupInvitationURL:(nullable NSString *)openGroupInvitationURL

@ -21,8 +21,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, getter=wasRead) BOOL read;
@property (nonatomic, nullable) NSNumber *serverTimestamp;
@end
#pragma mark -
@ -52,7 +50,6 @@ NS_ASSUME_NONNULL_BEGIN
expiresInSeconds:(uint32_t)expiresInSeconds
quotedMessage:(nullable TSQuotedMessage *)quotedMessage
linkPreview:(nullable OWSLinkPreview *)linkPreview
serverTimestamp:(nullable NSNumber *)serverTimestamp
wasReceivedByUD:(BOOL)wasReceivedByUD
openGroupInvitationName:(nullable NSString *)openGroupInvitationName
openGroupInvitationURL:(nullable NSString *)openGroupInvitationURL
@ -77,7 +74,6 @@ NS_ASSUME_NONNULL_BEGIN
_authorId = authorId;
_sourceDeviceId = sourceDeviceId;
_read = NO;
_serverTimestamp = serverTimestamp;
_wasReceivedByUD = wasReceivedByUD;
_notificationIdentifier = nil;

@ -38,9 +38,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value);
@property (nonatomic, readonly) uint64_t timestamp;
@property (nonatomic, readonly) uint64_t sortId;
@property (nonatomic, readonly) uint64_t receivedAtTimestamp;
@property (nonatomic, readonly) BOOL shouldUseServerTime;
- (uint64_t)timestampForUI;
- (NSDate *)dateForUI;
@ -80,6 +77,8 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value);
- (void)saveNextSortIdWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
NS_SWIFT_NAME(saveNextSortId(transaction:));
- (void)updateTimestamp:(uint64_t)timestamp;
@end
NS_ASSUME_NONNULL_END

@ -165,17 +165,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
#pragma mark Date operations
- (uint64_t)timestampForUI
{
// We always want to show the sent timestamp. In the case of one-on-one, closed group and V2 open group messages we get
// this from the protobuf. In the case of V1 open group messages we get it from the envelope in which the message is
// wrapped, which gets parsed to `serverTimestamp` in that case.
if ([self isKindOfClass:TSIncomingMessage.class] && ((TSIncomingMessage *)self).isOpenGroupMessage && ((TSIncomingMessage *)self).serverTimestamp != nil) {
return ((TSIncomingMessage *)self).serverTimestamp.unsignedLongLongValue;
}
return _timestamp;
}
- (uint64_t)timestampForLegacySorting
{
return self.timestamp;
@ -183,7 +172,7 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
- (NSDate *)dateForUI
{
return [NSDate ows_dateWithMillisecondsSince1970:self.timestampForUI];
return [NSDate ows_dateWithMillisecondsSince1970:self.timestamp];
}
- (NSDate *)receivedAtDate
@ -229,12 +218,6 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
- (uint64_t)sortId
{
// We always want to sort on the sent timestamp. In the case of one-on-one, closed group and V2 open group messages we get
// this from the protobuf. In the case of V1 open group messages we get it from the envelope in which the message is
// wrapped, which gets parsed to `serverTimestamp` in that case.
if ([self isKindOfClass:TSIncomingMessage.class] && ((TSIncomingMessage *)self).isOpenGroupMessage && ((TSIncomingMessage *)self).serverTimestamp != nil) {
return ((TSIncomingMessage *)self).serverTimestamp.unsignedLongLongValue;
}
return self.timestamp;
}
@ -280,6 +263,12 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
[self saveWithTransaction:transaction];
}
- (void)updateTimestamp:(uint64_t)timestamp
{
_timestamp = timestamp;
}
@end
NS_ASSUME_NONNULL_END

@ -159,6 +159,9 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
#pragma mark - Update With... Methods
- (void)updateOpenGroupServerID:(uint64_t)openGroupServerID
serverTimeStamp:(uint64_t)timestamp;
// This method is used to record a successful send to one recipient.
- (void)updateWithSentRecipient:(NSString *)recipientId
wasSentByUD:(BOOL)wasSentByUD

@ -450,6 +450,12 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
#pragma mark - Update With... Methods
- (void)updateOpenGroupServerID:(uint64_t)openGroupServerID serverTimeStamp:(uint64_t)timestamp
{
self.openGroupServerMessageID = openGroupServerID;
[super updateTimestamp:timestamp];
}
- (void)updateWithSendingError:(NSError *)error transaction:(YapDatabaseReadWriteTransaction *)transaction
{
[self applyChangeToSelfAndLatestCopy:transaction

@ -317,7 +317,7 @@ public final class MessageSender : NSObject {
OpenGroupAPIV2.send(openGroupMessage, to: room, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
message.openGroupServerMessageID = given(openGroupMessage.serverID) { UInt64($0) }
storage.write(with: { transaction in
MessageSender.handleSuccessfulMessageSend(message, to: destination, using: transaction)
MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: openGroupMessage.sentTimestamp, using: transaction)
seal.fulfill(())
}, completion: { })
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
@ -330,7 +330,7 @@ public final class MessageSender : NSObject {
}
// MARK: Success & Failure Handling
public static func handleSuccessfulMessageSend(_ message: Message, to destination: Message.Destination, isSyncMessage: Bool = false, using transaction: Any) {
public static func handleSuccessfulMessageSend(_ message: Message, to destination: Message.Destination, serverTimestamp: UInt64? = nil, isSyncMessage: Bool = false, using transaction: Any) {
let transaction = transaction as! YapDatabaseReadWriteTransaction
// Get the visible message if possible
if let tsMessage = TSOutgoingMessage.find(withTimestamp: message.sentTimestamp!) {
@ -338,8 +338,13 @@ public final class MessageSender : NSObject {
// will be replaced by the hash value of the sync message. Since the hash value of the
// real message has no use when we delete a message. It is OK to let it be.
tsMessage.serverHash = message.serverHash
// Track the open group server message ID
tsMessage.openGroupServerMessageID = message.openGroupServerMessageID ?? 0
// Track the open group server message ID and update server timestamp
if let openGroupServerMessageID = message.openGroupServerMessageID, let timestamp = serverTimestamp {
// Use server timestamp for open group messages
// Otherwise the quote messages may not be able
// to be found by the timestamp on other devices
tsMessage.updateOpenGroupServerID(openGroupServerMessageID, serverTimeStamp: timestamp)
}
// Mark the message as sent
var recipients = [ message.recipient! ]
if case .closedGroup(_) = destination, let threadID = message.threadID, // threadID should always be set at this point

@ -5,6 +5,7 @@ import PromiseKit
public final class ClosedGroupPoller : NSObject {
private var isPolling: [String:Bool] = [:]
private var timers: [String:Timer] = [:]
private let internalQueue: DispatchQueue = DispatchQueue(label:"isPollingQueue")
// MARK: Settings
private static let minPollInterval: Double = 2
@ -40,8 +41,11 @@ public final class ClosedGroupPoller : NSObject {
public func startPolling(for groupPublicKey: String) {
guard !isPolling(for: groupPublicKey) else { return }
setUpPolling(for: groupPublicKey)
// Might be a race condition that the setUpPolling finishes too soon,
// and the timer is not created, if we mark the group as is polling
// after setUpPolling. So the poller may not work, thus misses messages.
isPolling[groupPublicKey] = true
setUpPolling(for: groupPublicKey)
}
@objc public func stop() {
@ -51,8 +55,8 @@ public final class ClosedGroupPoller : NSObject {
}
public func stopPolling(for groupPublicKey: String) {
timers[groupPublicKey]?.invalidate()
isPolling[groupPublicKey] = false
timers[groupPublicKey]?.invalidate()
}
// MARK: Private API
@ -134,6 +138,6 @@ public final class ClosedGroupPoller : NSObject {
// MARK: Convenience
private func isPolling(for groupPublicKey: String) -> Bool {
return isPolling[groupPublicKey] ?? false
return internalQueue.sync{ isPolling[groupPublicKey] ?? false }
}
}

@ -73,7 +73,6 @@ public final class OpenGroupPollerV2 : NSObject {
let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: message.sentTimestamp)
envelope.setContent(data)
envelope.setSource(message.sender!) // Safe because messages with a nil sender are filtered out
envelope.setServerTimestamp(message.sentTimestamp)
do {
let data = try envelope.buildSerializedData()
let (message, proto) = try MessageReceiver.parse(data, openGroupMessageServerID: UInt64(message.serverID!), isRetry: false, using: transaction)

@ -57,6 +57,9 @@ BOOL IsNoteToSelfEnabled(void);
- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction
NS_SWIFT_NAME(unreadMessageCount(transaction:));
- (BOOL)hasUnreadMentionMessageWithTransaction:(YapDatabaseReadTransaction *)transaction
NS_SWIFT_NAME(hasUnreadMentionMessage(transaction:));
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction;
/**

@ -264,6 +264,32 @@ BOOL IsNoteToSelfEnabled(void)
return count;
}
- (BOOL)hasUnreadMentionMessageWithTransaction:(YapDatabaseReadTransaction *)transaction
{
__block BOOL hasUnreadMention = false;
YapDatabaseViewTransaction *unreadMessages = [transaction ext:TSUnreadDatabaseViewExtensionName];
[unreadMessages enumerateKeysAndObjectsInGroup:self.uniqueId
usingBlock:^(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) {
if (![object isKindOfClass:[TSIncomingMessage class]]) {
return;
}
TSIncomingMessage* unreadMessage = (TSIncomingMessage*)object;
if (unreadMessage.read) {
NSLog(@"Found an already read message in the * unread * messages list.");
return;
}
if (unreadMessage.isUserMentioned) {
hasUnreadMention = true;
*stop = YES;
}
}];
return hasUnreadMention;
}
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {

@ -48,7 +48,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension
case let visibleMessage as VisibleMessage:
let tsIncomingMessageID = try MessageReceiver.handleVisibleMessage(visibleMessage, associatedWithProto: proto, openGroupID: nil, isBackgroundPoll: false, using: transaction)
guard let tsIncomingMessage = TSIncomingMessage.fetch(uniqueId: tsIncomingMessageID, transaction: transaction) else {
return self.handleFailure(for: notificationContent)
return self.completeSilenty()
}
let thread = tsIncomingMessage.thread(with: transaction)
if thread.isMuted {

@ -15,6 +15,7 @@ public class ThreadViewModel: NSObject {
@objc public let name: String
@objc public let isMuted: Bool
@objc public let isOnlyNotifyingForMentions: Bool
@objc public let hasUnreadMentions: Bool
var isContactThread: Bool {
return !isGroupThread
@ -49,6 +50,7 @@ public class ThreadViewModel: NSObject {
self.unreadCount = thread.unreadMessageCount(transaction: transaction)
self.hasUnreadMessages = unreadCount > 0
self.hasUnreadMentions = thread.hasUnreadMentionMessage(transaction: transaction)
}
@objc

Loading…
Cancel
Save