Merge branch 'dev' into feature/session-id-blinding-part-2
# Conflicts: # Podfile # Podfile.lock # Session/Calls/Views & Modals/CallVideoView.swift # Session/Utilities/BackgroundPoller.swift # SessionSnodeKit/SnodeAPI.swift
This commit is contained in:
commit
07d1d8acee
20
Podfile
20
Podfile
|
@ -11,8 +11,7 @@ abstract_target 'GlobalDependencies' do
|
|||
# FIXME: If https://github.com/jedisct1/swift-sodium/pull/249 gets resolved then revert this back to the standard pod
|
||||
pod 'Sodium', :git => 'https://github.com/oxen-io/session-ios-swift-sodium.git', branch: 'session-build'
|
||||
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/oxen-io/session-ios-yap-database.git', branch: 'signal-release'
|
||||
# FIXME: If 'GoogleWebRTC' ever properly supports the arm64 simulators then remove the 'set_simulators_to_run_x86' post install step
|
||||
pod 'GoogleWebRTC'
|
||||
pod 'WebRTC-lib'
|
||||
pod 'SocketRocket', '~> 0.5.1'
|
||||
|
||||
target 'Session' do
|
||||
|
@ -85,7 +84,6 @@ target 'SessionUIKit'
|
|||
post_install do |installer|
|
||||
enable_whole_module_optimization_for_crypto_swift(installer)
|
||||
set_minimum_deployment_target(installer)
|
||||
set_simulators_to_run_x86(installer)
|
||||
end
|
||||
|
||||
def enable_whole_module_optimization_for_crypto_swift(installer)
|
||||
|
@ -106,19 +104,3 @@ def set_minimum_deployment_target(installer)
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Note: This is needed in order to build with the 'GoogleWebRTC' framework on M1 macs as well
|
||||
# as to allow it to run on the iOS simulator as they don't include an iOS arm64 simulator slice
|
||||
# in the framework (see https://stackoverflow.com/a/66094347 for more info and also
|
||||
# https://blog.sudeium.com/2021/06/18/build-for-x86-simulator-on-apple-silicon-macs/)
|
||||
#
|
||||
# Accoring to https://github.com/react-native-webrtc/react-native-webrtc/issues/1033 it also doesn't
|
||||
# support Catalyst at the moment so changes/updates would be needed if we wanted to add support
|
||||
def set_simulators_to_run_x86(installer)
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
# Force CocoaPods targets to always build for x86_64
|
||||
config.build_settings['ARCHS[sdk=iphonesimulator*]'] = 'x86_64'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
16
Podfile.lock
16
Podfile.lock
|
@ -21,7 +21,6 @@ PODS:
|
|||
- Curve25519Kit (2.1.0):
|
||||
- CocoaLumberjack
|
||||
- SignalCoreKit
|
||||
- GoogleWebRTC (1.1.31999)
|
||||
- Mantle (2.1.0):
|
||||
- Mantle/extobjc (= 2.1.0)
|
||||
- Mantle/extobjc (2.1.0)
|
||||
|
@ -40,7 +39,7 @@ PODS:
|
|||
- PromiseKit/UIKit (6.15.3):
|
||||
- PromiseKit/CorePromise
|
||||
- PureLayout (3.1.9)
|
||||
- Quick (4.0.0)
|
||||
- Quick (5.0.1)
|
||||
- Reachability (3.2)
|
||||
- SAMKeychain (1.5.3)
|
||||
- SignalCoreKit (1.0.0):
|
||||
|
@ -54,6 +53,7 @@ PODS:
|
|||
- SQLCipher/standard (4.5.0):
|
||||
- SQLCipher/common
|
||||
- SwiftProtobuf (1.5.0)
|
||||
- WebRTC-lib (96.0.0)
|
||||
- YapDatabase/SQLCipher (3.1.1):
|
||||
- YapDatabase/SQLCipher/Core (= 3.1.1)
|
||||
- YapDatabase/SQLCipher/Extensions (= 3.1.1)
|
||||
|
@ -127,7 +127,6 @@ DEPENDENCIES:
|
|||
- AFNetworking
|
||||
- CryptoSwift
|
||||
- Curve25519Kit (from `https://github.com/oxen-io/session-ios-curve-25519-kit.git`, branch `session-version`)
|
||||
- GoogleWebRTC
|
||||
- Mantle (from `https://github.com/signalapp/Mantle`, branch `signal-master`)
|
||||
- Nimble
|
||||
- NVActivityIndicatorView
|
||||
|
@ -140,6 +139,7 @@ DEPENDENCIES:
|
|||
- SocketRocket (~> 0.5.1)
|
||||
- Sodium (from `https://github.com/oxen-io/session-ios-swift-sodium.git`, branch `session-build`)
|
||||
- SwiftProtobuf (~> 1.5.0)
|
||||
- WebRTC-lib
|
||||
- YapDatabase/SQLCipher (from `https://github.com/oxen-io/session-ios-yap-database.git`, branch `signal-release`)
|
||||
- YYImage (from `https://github.com/signalapp/YYImage`)
|
||||
- ZXingObjC
|
||||
|
@ -149,7 +149,6 @@ SPEC REPOS:
|
|||
- AFNetworking
|
||||
- CocoaLumberjack
|
||||
- CryptoSwift
|
||||
- GoogleWebRTC
|
||||
- Nimble
|
||||
- NVActivityIndicatorView
|
||||
- OpenSSL-Universal
|
||||
|
@ -161,6 +160,7 @@ SPEC REPOS:
|
|||
- SocketRocket
|
||||
- SQLCipher
|
||||
- SwiftProtobuf
|
||||
- WebRTC-lib
|
||||
- ZXingObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
|
@ -207,14 +207,13 @@ SPEC CHECKSUMS:
|
|||
CocoaLumberjack: 543c79c114dadc3b1aba95641d8738b06b05b646
|
||||
CryptoSwift: a532e74ed010f8c95f611d00b8bbae42e9fe7c17
|
||||
Curve25519Kit: e63f9859ede02438ae3defc5e1a87e09d1ec7ee6
|
||||
GoogleWebRTC: b39a78c4f5cc6b0323415b9233db03a2faa7b0f0
|
||||
Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b
|
||||
Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84
|
||||
NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667
|
||||
OpenSSL-Universal: e7311447fd2419f57420c79524b641537387eff2
|
||||
PromiseKit: 3b2b6995e51a954c46dbc550ce3da44fbfb563c5
|
||||
PureLayout: 5fb5e5429519627d60d079ccb1eaa7265ce7cf88
|
||||
Quick: 6473349e43b9271a8d43839d9ba1c442ed1b7ac4
|
||||
Quick: 749aa754fd1e7d984f2000fe051e18a3a9809179
|
||||
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
|
||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||
SignalCoreKit: 1fbd8732163ef76de16cd1107d1fa3684b607e5d
|
||||
|
@ -222,10 +221,11 @@ SPEC CHECKSUMS:
|
|||
Sodium: a7d42cb46e789d2630fa552d35870b416ed055ae
|
||||
SQLCipher: 98dc22f27c0b1790d39e710d440f22a466ebdb59
|
||||
SwiftProtobuf: 241400280f912735c1e1b9fe675fdd2c6c4d42e2
|
||||
WebRTC-lib: 508fe02efa0c1a3a8867082a77d24c9be5d29aeb
|
||||
YapDatabase: b418a4baa6906e8028748938f9159807fd039af4
|
||||
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: e4f78b5555c81d9dc1377a9462bacc8bd662684e
|
||||
PODFILE CHECKSUM: 6edcaf484d4a1c402ab1d46770bbeda6982519f4
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
COCOAPODS: 1.11.3
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,12 +2,11 @@
|
|||
import WebRTC
|
||||
import Foundation
|
||||
|
||||
#if arch(arm64)
|
||||
// Note: 'RTCMTLVideoView' requires arm64 (so won't work on the simulator which
|
||||
// we need to build for x86_64 due to WebRTC not supporting arm64 simulator builds)
|
||||
typealias TargetView = RTCMTLVideoView
|
||||
#else
|
||||
#if targetEnvironment(simulator)
|
||||
// Note: 'RTCMTLVideoView' doesn't seem to work on the simulator so use 'RTCEAGLVideoView' instead
|
||||
typealias TargetView = RTCEAGLVideoView
|
||||
#else
|
||||
typealias TargetView = RTCMTLVideoView
|
||||
#endif
|
||||
|
||||
// MARK: RemoteVideoView
|
||||
|
@ -17,6 +16,7 @@ class RemoteVideoView: TargetView {
|
|||
override func renderFrame(_ frame: RTCVideoFrame?) {
|
||||
super.renderFrame(frame)
|
||||
guard let frame = frame else { return }
|
||||
|
||||
DispatchMainThreadSafe {
|
||||
let frameRatio = Double(frame.height) / Double(frame.width)
|
||||
let frameRotation = frame.rotation
|
||||
|
@ -48,7 +48,8 @@ class RemoteVideoView: TargetView {
|
|||
break
|
||||
}
|
||||
|
||||
#if arch(arm64)
|
||||
#if targetEnvironment(simulator)
|
||||
#else
|
||||
if let rotationOverride = rotationOverride {
|
||||
self.rotationOverride = NSNumber(value: rotationOverride.rawValue)
|
||||
if [ RTCVideoRotation._0, RTCVideoRotation._180 ].contains(rotationOverride) {
|
||||
|
@ -87,7 +88,8 @@ class LocalVideoView: TargetView {
|
|||
// sometimes the rotationOverride is not working
|
||||
// if it is only set once on initialization
|
||||
self.rotationOverride = NSNumber(value: RTCVideoRotation._0.rawValue)
|
||||
#if arch(arm64)
|
||||
#if targetEnvironment(simulator)
|
||||
#else
|
||||
self.videoContentMode = .scaleAspectFill
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -16,9 +16,16 @@ final class MiniCallView: UIView, RTCVideoViewDelegate {
|
|||
private var top: NSLayoutConstraint?
|
||||
private var bottom: NSLayoutConstraint?
|
||||
|
||||
#if arch(arm64)
|
||||
// Note: 'RTCMTLVideoView' requires arm64 (so won't work on the simulator which
|
||||
// we need to build for x86_64 due to WebRTC not supporting arm64 simulator builds)
|
||||
#if targetEnvironment(simulator)
|
||||
// Note: 'RTCMTLVideoView' doesn't seem to work on the simulator so use 'RTCEAGLVideoView' instead
|
||||
private lazy var remoteVideoView: RTCEAGLVideoView = {
|
||||
let result = RTCEAGLVideoView()
|
||||
result.delegate = self
|
||||
result.alpha = self.callVC.call.isRemoteVideoEnabled ? 1 : 0
|
||||
result.backgroundColor = .black
|
||||
return result
|
||||
}()
|
||||
#else
|
||||
private lazy var remoteVideoView: RTCMTLVideoView = {
|
||||
let result = RTCMTLVideoView()
|
||||
result.delegate = self
|
||||
|
@ -27,14 +34,6 @@ final class MiniCallView: UIView, RTCVideoViewDelegate {
|
|||
result.backgroundColor = .black
|
||||
return result
|
||||
}()
|
||||
#else
|
||||
private lazy var remoteVideoView: RTCEAGLVideoView = {
|
||||
let result = RTCEAGLVideoView()
|
||||
result.delegate = self
|
||||
result.alpha = self.callVC.call.isRemoteVideoEnabled ? 1 : 0
|
||||
result.backgroundColor = .black
|
||||
return result
|
||||
}()
|
||||
#endif
|
||||
|
||||
// MARK: Initialization
|
||||
|
|
|
@ -32,6 +32,8 @@ public class ConversationSearchController : NSObject {
|
|||
|
||||
@objc
|
||||
public let resultsBar: SearchResultsBar = SearchResultsBar()
|
||||
|
||||
private var lastSearchText: String?
|
||||
|
||||
// MARK: Initializer
|
||||
|
||||
|
@ -88,8 +90,10 @@ extension ConversationSearchController : UISearchResultsUpdating {
|
|||
return
|
||||
}
|
||||
let searchText = FullTextSearchFinder.normalize(text: rawSearchText)
|
||||
lastSearchText = searchText
|
||||
|
||||
guard searchText.count >= ConversationSearchController.kMinimumSearchTextLength else {
|
||||
lastSearchText = nil
|
||||
self.resultsBar.updateResults(resultSet: nil)
|
||||
self.delegate?.conversationSearchController(self, didUpdateSearchResults: nil)
|
||||
return
|
||||
|
@ -102,7 +106,7 @@ extension ConversationSearchController : UISearchResultsUpdating {
|
|||
}
|
||||
resultSet = self.dbSearcher.searchWithinConversation(thread: self.thread, searchText: searchText, transaction: transaction)
|
||||
}, completionBlock: { [weak self] in
|
||||
guard let self = self else {
|
||||
guard let self = self, searchText == self.lastSearchText else {
|
||||
return
|
||||
}
|
||||
self.resultsBar.updateResults(resultSet: resultSet)
|
||||
|
@ -121,7 +125,6 @@ extension ConversationSearchController : SearchResultsBarDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
BenchEventStart(title: "Conversation Search Nav", eventId: "Conversation Search Nav: \(searchResult.messageId)")
|
||||
self.delegate?.conversationSearchController(self, didSelectMessageId: searchResult.messageId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -438,12 +438,12 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
|||
Storage.write { transaction in
|
||||
self.thread.setDraft(text, transaction: transaction)
|
||||
}
|
||||
self.resignFirstResponder()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
mediaCache.removeAllObjects()
|
||||
self.resignFirstResponder()
|
||||
}
|
||||
|
||||
override func appDidBecomeActive(_ notification: Notification) {
|
||||
|
@ -1015,7 +1015,10 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
|
|||
}
|
||||
|
||||
func conversationSearchController(_ conversationSearchController: ConversationSearchController, didSelectMessageId interactionID: String) {
|
||||
scrollToInteraction(with: interactionID)
|
||||
scrollToInteraction(with: interactionID, highlighted: true)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
|
||||
self.highlightFocusedMessageIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
func scrollToInteraction(with interactionID: String, position: UITableView.ScrollPosition = .middle, isAnimated: Bool = true, highlighted: Bool = false) {
|
||||
|
|
|
@ -333,13 +333,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
selector:@selector(applicationWillEnterForeground:)
|
||||
name:OWSApplicationWillEnterForegroundNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[self addNotificationListeners];
|
||||
|
||||
[self touchDbAsync];
|
||||
}
|
||||
|
||||
- (void)touchDbAsync
|
||||
|
|
|
@ -146,7 +146,7 @@ final class LinkPreviewView : UIView {
|
|||
// Body text view
|
||||
bodyTextViewContainer.subviews.forEach { $0.removeFromSuperview() }
|
||||
if let viewItem = viewItem {
|
||||
let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: sentLinkPreviewTextColor, searchText: delegate.lastSearchedText, delegate: delegate)
|
||||
let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: sentLinkPreviewTextColor, delegate: delegate)
|
||||
self.bodyTextView = bodyTextView
|
||||
bodyTextViewContainer.addSubview(bodyTextView)
|
||||
bodyTextView.pin(to: bodyTextViewContainer, withInset: 12)
|
||||
|
|
|
@ -351,7 +351,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
|
|||
stackView.addArrangedSubview(quoteViewContainer)
|
||||
}
|
||||
// Body text view
|
||||
let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, searchText: delegate?.lastSearchedText, delegate: self)
|
||||
let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, delegate: self)
|
||||
self.bodyTextView = bodyTextView
|
||||
stackView.addArrangedSubview(bodyTextView)
|
||||
// Constraints
|
||||
|
@ -383,7 +383,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
|
|||
if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0 {
|
||||
let inset: CGFloat = 12
|
||||
let maxWidth = size.width - 2 * inset
|
||||
let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, searchText: delegate?.lastSearchedText, delegate: self)
|
||||
let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, delegate: self)
|
||||
self.bodyTextView = bodyTextView
|
||||
stackView.addArrangedSubview(UIView(wrapping: bodyTextView, withInsets: UIEdgeInsets(top: 0, left: inset, bottom: inset, right: inset)))
|
||||
}
|
||||
|
@ -420,9 +420,8 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
|
|||
let documentView = DocumentView(viewItem: viewItem, textColor: bodyLabelTextColor)
|
||||
stackView.addArrangedSubview(documentView)
|
||||
// Body text view
|
||||
if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0,
|
||||
let delegate = delegate { // delegate should always be set at this point
|
||||
let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, searchText: delegate.lastSearchedText, delegate: self)
|
||||
if let message = viewItem.interaction as? TSMessage, let body = message.body, body.count > 0 {
|
||||
let bodyTextView = VisibleMessageCell.getBodyTextView(for: viewItem, with: maxWidth, textColor: bodyLabelTextColor, delegate: self)
|
||||
self.bodyTextView = bodyTextView
|
||||
stackView.addArrangedSubview(bodyTextView)
|
||||
}
|
||||
|
@ -719,7 +718,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
|
|||
return isGroupThread && viewItem.shouldShowSenderProfilePicture && senderSessionID != nil
|
||||
}
|
||||
|
||||
static func getBodyTextView(for viewItem: ConversationViewItem, with availableWidth: CGFloat, textColor: UIColor, searchText: String?, delegate: UITextViewDelegate & BodyTextViewDelegate) -> UITextView {
|
||||
static func getBodyTextView(for viewItem: ConversationViewItem, with availableWidth: CGFloat, textColor: UIColor, delegate: UITextViewDelegate & BodyTextViewDelegate) -> UITextView {
|
||||
// Take care of:
|
||||
// • Highlighting mentions
|
||||
// • Linkification
|
||||
|
@ -733,20 +732,6 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
|
|||
.font : UIFont.systemFont(ofSize: getFontSize(for: viewItem))
|
||||
]
|
||||
let attributedText = NSMutableAttributedString(attributedString: MentionUtilities.highlightMentions(in: message.body ?? "", isOutgoingMessage: isOutgoing, threadID: viewItem.interaction.uniqueThreadId, attributes: attributes))
|
||||
if let searchText = searchText, searchText.count >= ConversationSearchController.kMinimumSearchTextLength {
|
||||
let normalizedSearchText = FullTextSearchFinder.normalize(text: searchText)
|
||||
do {
|
||||
let regex = try NSRegularExpression(pattern: NSRegularExpression.escapedPattern(for: normalizedSearchText), options: .caseInsensitive)
|
||||
let matches = regex.matches(in: attributedText.string, options: .withoutAnchoringBounds, range: NSRange(location: 0, length: (attributedText.string as NSString).length))
|
||||
for match in matches {
|
||||
guard match.range.location + match.range.length < attributedText.length else { continue }
|
||||
attributedText.addAttribute(.backgroundColor, value: UIColor.white, range: match.range)
|
||||
attributedText.addAttribute(.foregroundColor, value: UIColor.black, range: match.range)
|
||||
}
|
||||
} catch {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
result.attributedText = attributedText
|
||||
result.dataDetectorTypes = .link
|
||||
result.backgroundColor = .clear
|
||||
|
|
|
@ -92,6 +92,19 @@ class ImagePickerGridController: UICollectionViewController, PhotoLibraryDelegat
|
|||
selectionPanGesture.delegate = self
|
||||
self.selectionPanGesture = selectionPanGesture
|
||||
collectionView.addGestureRecognizer(selectionPanGesture)
|
||||
|
||||
if #available(iOS 14, *) {
|
||||
if PHPhotoLibrary.authorizationStatus(for: .readWrite) == .limited {
|
||||
let addSeletedPhotoButton = UIBarButtonItem.init(barButtonSystemItem: .add, target: self, action: #selector(addSelectedPhoto))
|
||||
self.navigationItem.rightBarButtonItem = addSeletedPhotoButton
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func addSelectedPhoto(_ sender: Any) {
|
||||
if #available(iOS 14, *) {
|
||||
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
|
||||
}
|
||||
}
|
||||
|
||||
var selectionPanGesture: UIPanGestureRecognizer?
|
||||
|
|
|
@ -56,10 +56,15 @@ extension AppDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
private func insertCallInfoMessage(for message: CallMessage, using transaction: YapDatabaseReadWriteTransaction) -> TSInfoMessage {
|
||||
private func insertCallInfoMessage(for message: CallMessage, using transaction: YapDatabaseReadWriteTransaction) -> TSInfoMessage? {
|
||||
guard let sender = message.sender, let uuid = message.uuid else { return nil }
|
||||
var receivedCalls = Storage.shared.getReceivedCalls(for: sender, using: transaction)
|
||||
guard !receivedCalls.contains(uuid) else { return nil }
|
||||
let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction)
|
||||
let infoMessage = TSInfoMessage.from(message, associatedWith: thread)
|
||||
infoMessage.save(with: transaction)
|
||||
receivedCalls.insert(uuid)
|
||||
Storage.shared.setReceivedCalls(to: receivedCalls, for: sender, using: transaction)
|
||||
return infoMessage
|
||||
}
|
||||
|
||||
|
@ -78,20 +83,22 @@ extension AppDelegate {
|
|||
guard CurrentAppContext().isMainApp else { return }
|
||||
guard let timestamp = message.sentTimestamp, TimestampUtils.isWithinOneMinute(timestamp: timestamp) else {
|
||||
// Add missed call message for call offer messages from more than one minute
|
||||
let infoMessage = self.insertCallInfoMessage(for: message, using: transaction)
|
||||
infoMessage.updateCallInfoMessage(.missed, using: transaction)
|
||||
let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction)
|
||||
SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction)
|
||||
if let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) {
|
||||
infoMessage.updateCallInfoMessage(.missed, using: transaction)
|
||||
let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction)
|
||||
SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction)
|
||||
}
|
||||
return
|
||||
}
|
||||
guard SSKPreferences.areCallsEnabled else {
|
||||
let infoMessage = self.insertCallInfoMessage(for: message, using: transaction)
|
||||
infoMessage.updateCallInfoMessage(.permissionDenied, using: transaction)
|
||||
let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction)
|
||||
SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction)
|
||||
let contactName = Storage.shared.getContact(with: message.sender!, using: transaction)?.displayName(for: Contact.Context.regular) ?? message.sender!
|
||||
DispatchQueue.main.async {
|
||||
self.showMissedCallTipsIfNeeded(caller: contactName)
|
||||
if let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) {
|
||||
infoMessage.updateCallInfoMessage(.permissionDenied, using: transaction)
|
||||
let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction)
|
||||
SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction)
|
||||
let contactName = Storage.shared.getContact(with: message.sender!, using: transaction)?.displayName(for: Contact.Context.regular) ?? message.sender!
|
||||
DispatchQueue.main.async {
|
||||
self.showMissedCallTipsIfNeeded(caller: contactName)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -106,7 +113,7 @@ extension AppDelegate {
|
|||
// Handle UI
|
||||
if let caller = message.sender, let uuid = message.uuid {
|
||||
let call = SessionCall(for: caller, uuid: uuid, mode: .answer)
|
||||
call.callMessageID = infoMessage.uniqueId
|
||||
call.callMessageID = infoMessage?.uniqueId
|
||||
self.showCallUIForCall(call)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ public final class BackgroundPoller: NSObject {
|
|||
return SnodeAPI.getRawMessages(from: snode, associatedWith: publicKey)
|
||||
.then(on: DispatchQueue.main) { rawResponse -> Promise<Void> in
|
||||
let (messages, lastRawMessage) = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: publicKey)
|
||||
var processedMessages: [JSON] = []
|
||||
let promises = messages
|
||||
.compactMap { json -> Promise<Void>? in
|
||||
// Use a best attempt approach here; we don't want to fail
|
||||
|
@ -60,6 +61,7 @@ public final class BackgroundPoller: NSObject {
|
|||
}
|
||||
|
||||
let job = MessageReceiveJob(data: data, serverHash: json["hash"] as? String, isBackgroundPoll: true)
|
||||
processedMessages.append(json)
|
||||
|
||||
return job.execute()
|
||||
}
|
||||
|
@ -72,10 +74,15 @@ public final class BackgroundPoller: NSObject {
|
|||
associatedWith: publicKey,
|
||||
from: lastRawMessage
|
||||
)
|
||||
SnodeAPI.updateReceivedMessages(
|
||||
from: processedMessages,
|
||||
associatedWith: publicKey
|
||||
)
|
||||
|
||||
return when(fulfilled: promises) // The promise returned by MessageReceiveJob never rejects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func getClosedGroupMessages(for publicKey: String) -> Promise<Void> {
|
||||
|
@ -84,7 +91,7 @@ public final class BackgroundPoller: NSObject {
|
|||
guard let snode = swarm.randomElement() else { throw SnodeAPI.Error.generic }
|
||||
|
||||
return attempt(maxRetryCount: 4, recoveringOn: DispatchQueue.main) {
|
||||
var promises: [SnodeAPI.RawResponsePromise] = []
|
||||
var promises: [Promise<Data>] = []
|
||||
var namespaces: [Int] = []
|
||||
|
||||
// We have to poll for both namespace 0 and -10 when hardfork == 19 && softfork == 0
|
||||
|
@ -108,6 +115,7 @@ public final class BackgroundPoller: NSObject {
|
|||
for result in results {
|
||||
if case .fulfilled(let rawResponse) = result {
|
||||
let (messages, lastRawMessage) = SnodeAPI.parseRawMessagesResponse(rawResponse, from: snode, associatedWith: publicKey)
|
||||
var processedMessages: [JSON] = []
|
||||
let jobPromises = messages.compactMap { json -> Promise<Void>? in
|
||||
|
||||
// Use a best attempt approach here; we don't want to fail
|
||||
|
@ -117,6 +125,7 @@ public final class BackgroundPoller: NSObject {
|
|||
}
|
||||
|
||||
let job = MessageReceiveJob(data: data, serverHash: json["hash"] as? String, isBackgroundPoll: true)
|
||||
processedMessages.append(json)
|
||||
|
||||
return job.execute()
|
||||
}
|
||||
|
@ -129,6 +138,10 @@ public final class BackgroundPoller: NSObject {
|
|||
associatedWith: publicKey,
|
||||
from: lastRawMessage
|
||||
)
|
||||
SnodeAPI.updateReceivedMessages(
|
||||
from: processedMessages,
|
||||
associatedWith: publicKey
|
||||
)
|
||||
|
||||
promises += jobPromises
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ extension WebRTCSession: RTCDataChannelDelegate {
|
|||
dataChannelConfiguration.isOrdered = true
|
||||
dataChannelConfiguration.isNegotiated = true
|
||||
dataChannelConfiguration.channelId = 548
|
||||
guard let dataChannel = peerConnection.dataChannel(forLabel: "CONTROL", configuration: dataChannelConfiguration) else {
|
||||
guard let dataChannel = peerConnection?.dataChannel(forLabel: "CONTROL", configuration: dataChannelConfiguration) else {
|
||||
SNLog("[Calls] Couldn't create data channel.")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ extension WebRTCSession {
|
|||
|
||||
public func handleICECandidates(_ candidate: [RTCIceCandidate]) {
|
||||
SNLog("[Calls] Received ICE candidate message.")
|
||||
candidate.forEach { peerConnection.add($0) }
|
||||
candidate.forEach { peerConnection?.add($0) }
|
||||
}
|
||||
|
||||
public func handleRemoteSDP(_ sdp: RTCSessionDescription, from sessionID: String) {
|
||||
SNLog("[Calls] Received remote SDP: \(sdp.sdp).")
|
||||
peerConnection.setRemoteDescription(sdp, completionHandler: { [weak self] error in
|
||||
peerConnection?.setRemoteDescription(sdp, completionHandler: { [weak self] error in
|
||||
if let error = error {
|
||||
SNLog("[Calls] Couldn't set SDP due to error: \(error).")
|
||||
} else {
|
||||
|
|
|
@ -35,7 +35,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
|
||||
/// Represents a WebRTC connection between the user and a remote peer. Provides methods to connect to a
|
||||
/// remote peer, maintain and monitor the connection, and close the connection once it's no longer needed.
|
||||
internal lazy var peerConnection: RTCPeerConnection = {
|
||||
internal lazy var peerConnection: RTCPeerConnection? = {
|
||||
let configuration = RTCConfiguration()
|
||||
if let defaultICEServer = defaultICEServer {
|
||||
configuration.iceServers = [ RTCIceServer(urlStrings: defaultICEServer.urls, username: defaultICEServer.username, credential: defaultICEServer.password) ]
|
||||
|
@ -67,7 +67,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
}()
|
||||
|
||||
internal lazy var remoteVideoTrack: RTCVideoTrack? = {
|
||||
return peerConnection.transceivers.first { $0.mediaType == .video }?.receiver.track as? RTCVideoTrack
|
||||
return peerConnection?.transceivers.first { $0.mediaType == .video }?.receiver.track as? RTCVideoTrack
|
||||
}()
|
||||
|
||||
// Data Channel
|
||||
|
@ -94,8 +94,8 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
self.uuid = uuid
|
||||
super.init()
|
||||
let mediaStreamTrackIDS = ["ARDAMS"]
|
||||
peerConnection.add(audioTrack, streamIds: mediaStreamTrackIDS)
|
||||
peerConnection.add(localVideoTrack, streamIds: mediaStreamTrackIDS)
|
||||
peerConnection?.add(audioTrack, streamIds: mediaStreamTrackIDS)
|
||||
peerConnection?.add(localVideoTrack, streamIds: mediaStreamTrackIDS)
|
||||
// Configure audio session
|
||||
configureAudioSession()
|
||||
|
||||
|
@ -125,12 +125,12 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
SNLog("[Calls] Sending offer message.")
|
||||
guard let thread = TSContactThread.fetch(for: sessionID, using: transaction) else { return Promise(error: Error.noThread) }
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
peerConnection.offer(for: mediaConstraints(isRestartingICEConnection)) { [weak self] sdp, error in
|
||||
peerConnection?.offer(for: mediaConstraints(isRestartingICEConnection)) { [weak self] sdp, error in
|
||||
if let error = error {
|
||||
seal.reject(error)
|
||||
} else {
|
||||
guard let self = self, let sdp = self.correctSessionDescription(sdp: sdp) else { preconditionFailure() }
|
||||
self.peerConnection.setLocalDescription(sdp) { error in
|
||||
self.peerConnection?.setLocalDescription(sdp) { error in
|
||||
if let error = error {
|
||||
print("Couldn't initiate call due to error: \(error).")
|
||||
return seal.reject(error)
|
||||
|
@ -157,12 +157,12 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
SNLog("[Calls] Sending answer message.")
|
||||
guard let thread = TSContactThread.fetch(for: sessionID, using: transaction) else { return Promise(error: Error.noThread) }
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
peerConnection.answer(for: mediaConstraints(false)) { [weak self] sdp, error in
|
||||
peerConnection?.answer(for: mediaConstraints(false)) { [weak self] sdp, error in
|
||||
if let error = error {
|
||||
seal.reject(error)
|
||||
} else {
|
||||
guard let self = self, let sdp = self.correctSessionDescription(sdp: sdp) else { preconditionFailure() }
|
||||
self.peerConnection.setLocalDescription(sdp) { error in
|
||||
self.peerConnection?.setLocalDescription(sdp) { error in
|
||||
if let error = error {
|
||||
print("Couldn't accept call due to error: \(error).")
|
||||
return seal.reject(error)
|
||||
|
@ -221,7 +221,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
}
|
||||
|
||||
public func dropConnection() {
|
||||
peerConnection.close()
|
||||
peerConnection?.close()
|
||||
}
|
||||
|
||||
private func mediaConstraints(_ isRestartingICEConnection: Bool) -> RTCMediaConstraints {
|
||||
|
@ -263,7 +263,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
|
|||
if state == .connected {
|
||||
delegate?.webRTCIsConnected()
|
||||
} else if state == .disconnected {
|
||||
if self.peerConnection.signalingState == .stable {
|
||||
if self.peerConnection?.signalingState == .stable {
|
||||
delegate?.reconnectIfNeeded()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
extension Storage {
|
||||
|
||||
private static let receivedCallsCollection = "LokiReceivedCallsCollection"
|
||||
|
||||
public func getReceivedCalls(for publicKey: String, using transaction: Any) -> Set<String> {
|
||||
var result: Set<String>?
|
||||
guard let transaction = transaction as? YapDatabaseReadTransaction else { return [] }
|
||||
result = transaction.object(forKey: publicKey, inCollection: Storage.receivedCallsCollection) as? Set<String>
|
||||
return result ?? []
|
||||
}
|
||||
|
||||
public func setReceivedCalls(to receivedCalls: Set<String>, for publicKey: String, using transaction: Any) {
|
||||
(transaction as! YapDatabaseReadWriteTransaction).setObject(receivedCalls, forKey: publicKey, inCollection: Storage.receivedCallsCollection)
|
||||
}
|
||||
}
|
|
@ -122,6 +122,7 @@ public final class ClosedGroupPoller : NSObject {
|
|||
if !rawMessages.isEmpty {
|
||||
SNLog("Received \(rawMessages.count) new message(s) in closed group with public key: \(groupPublicKey).")
|
||||
}
|
||||
var processedMessages: [JSON] = []
|
||||
rawMessages.forEach { json in
|
||||
guard let envelope = SNProtoEnvelope.from(json) else { return }
|
||||
do {
|
||||
|
@ -130,13 +131,15 @@ public final class ClosedGroupPoller : NSObject {
|
|||
SNMessagingKitConfiguration.shared.storage.write { transaction in
|
||||
SessionMessagingKit.JobQueue.shared.add(job, using: transaction)
|
||||
}
|
||||
processedMessages.append(json)
|
||||
} catch {
|
||||
SNLog("Failed to deserialize envelope due to error: \(error).")
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value
|
||||
// Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value & `receivedMessageHashes`
|
||||
SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, namespace: SnodeAPI.closedGroupNamespace, associatedWith: groupPublicKey, from: lastRawMessage)
|
||||
SnodeAPI.updateReceivedMessages(from: processedMessages, associatedWith: groupPublicKey)
|
||||
}
|
||||
promise.catch2 { error in
|
||||
SNLog("Polling failed for closed group with public key: \(groupPublicKey) due to error: \(error).")
|
||||
|
|
|
@ -100,6 +100,7 @@ public final class Poller : NSObject {
|
|||
if !messages.isEmpty {
|
||||
SNLog("Received \(messages.count) new message(s).")
|
||||
}
|
||||
var processedMessages: [JSON] = []
|
||||
messages.forEach { json in
|
||||
guard let envelope = SNProtoEnvelope.from(json) else { return }
|
||||
do {
|
||||
|
@ -108,13 +109,15 @@ public final class Poller : NSObject {
|
|||
SNMessagingKitConfiguration.shared.storage.write { transaction in
|
||||
SessionMessagingKit.JobQueue.shared.add(job, using: transaction)
|
||||
}
|
||||
processedMessages.append(json)
|
||||
} catch {
|
||||
SNLog("Failed to deserialize envelope due to error: \(error).")
|
||||
}
|
||||
}
|
||||
|
||||
// Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value
|
||||
// Now that the MessageReceiveJob's have been created we can update the `lastMessageHash` value & `receivedMessageHashes`
|
||||
SnodeAPI.updateLastMessageHashValueIfPossible(for: snode, namespace: SnodeAPI.defaultNamespace, associatedWith: userPublicKey, from: lastRawMessage)
|
||||
SnodeAPI.updateReceivedMessages(from: processedMessages, associatedWith: userPublicKey)
|
||||
|
||||
strongSelf.pollCount += 1
|
||||
if strongSelf.pollCount == Poller.maxPollCount {
|
||||
|
|
|
@ -132,6 +132,11 @@ public protocol SessionMessagingKitStorageProtocol {
|
|||
func setAttachmentState(to state: TSAttachmentPointerState, for pointer: TSAttachmentPointer, associatedWith tsIncomingMessageID: String, using transaction: Any)
|
||||
/// Also touches the associated message.
|
||||
func persist(_ stream: TSAttachmentStream, associatedWith tsIncomingMessageID: String, using transaction: Any)
|
||||
|
||||
// MARK: - Calls
|
||||
|
||||
func getReceivedCalls(for publicKey: String, using transaction: Any) -> Set<String>
|
||||
func setReceivedCalls(to receivedCalls: Set<String>, for publicKey: String, using transaction: Any)
|
||||
}
|
||||
|
||||
extension Storage: SessionMessagingKitStorageProtocol, SessionSnodeKitStorageProtocol {}
|
||||
|
|
|
@ -78,9 +78,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension
|
|||
guard case .preOffer = callMessage.kind else { return self.completeSilenty() }
|
||||
if !SSKPreferences.areCallsEnabled {
|
||||
if let sender = callMessage.sender, let thread = TSContactThread.fetch(for: sender, using: transaction), !thread.isMessageRequest(using: transaction) {
|
||||
let infoMessage = TSInfoMessage.from(callMessage, associatedWith: thread)
|
||||
infoMessage.updateCallInfoMessage(.permissionDenied, using: transaction)
|
||||
SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction)
|
||||
self.insertCallInfoMessage(for: callMessage, in: thread, reason: .permissionDenied, using: transaction)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -92,9 +90,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension
|
|||
message.kind = .endCall
|
||||
SNLog("[Calls] Sending end call message because there is an ongoing call.")
|
||||
MessageSender.sendNonDurably(message, in: thread, using: transaction).retainUntilComplete()
|
||||
let infoMessage = TSInfoMessage.from(callMessage, associatedWith: thread)
|
||||
infoMessage.updateCallInfoMessage(.missed, using: transaction)
|
||||
SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction)
|
||||
self.insertCallInfoMessage(for: callMessage, in: thread, reason: .missed, using: transaction)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -112,6 +108,18 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func insertCallInfoMessage(for message: CallMessage, in thread: TSThread, reason: TSInfoMessageCallState, using transaction: YapDatabaseReadWriteTransaction) {
|
||||
guard let sender = message.sender, let uuid = message.uuid else { return }
|
||||
var receivedCalls = Storage.shared.getReceivedCalls(for: sender, using: transaction)
|
||||
if !receivedCalls.contains(uuid) {
|
||||
let infoMessage = TSInfoMessage.from(message, associatedWith: thread)
|
||||
infoMessage.updateCallInfoMessage(reason, using: transaction)
|
||||
SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction)
|
||||
receivedCalls.insert(uuid)
|
||||
Storage.shared.setReceivedCalls(to: receivedCalls, for: sender, using: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
|
@ -263,6 +271,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension
|
|||
}
|
||||
|
||||
// MARK: Poll for open groups
|
||||
|
||||
private func pollForOpenGroups() -> [Promise<Void>] {
|
||||
var promises: [Promise<Void>] = []
|
||||
let servers = Set(Storage.shared.getAllOpenGroups().values.map { $0.server })
|
||||
|
|
|
@ -545,8 +545,7 @@ public final class SnodeAPI : NSObject {
|
|||
// MARK: Store
|
||||
|
||||
public static func sendMessage(_ message: SnodeMessage, isClosedGroupMessage: Bool, isConfigMessage: Bool) -> Promise<Set<Promise<Data>>> {
|
||||
let namespace = isClosedGroupMessage ? closedGroupNamespace : defaultNamespace
|
||||
return sendMessageUnauthenticated(message, namespace: namespace)
|
||||
return sendMessageUnauthenticated(message, isClosedGroupMessage: isClosedGroupMessage)
|
||||
}
|
||||
|
||||
// Not in use until we can batch delete and store config messages
|
||||
|
@ -588,19 +587,35 @@ public final class SnodeAPI : NSObject {
|
|||
return promise
|
||||
}
|
||||
|
||||
private static func sendMessageUnauthenticated(_ message: SnodeMessage, namespace: Int) -> Promise<Set<Promise<Data>>> {
|
||||
private static func sendMessageUnauthenticated(_ message: SnodeMessage, isClosedGroupMessage: Bool) -> Promise<Set<Promise<Data>>> {
|
||||
let (promise, seal) = Promise<Set<Promise<Data>>>.pending()
|
||||
let publicKey = Features.useTestnet ? message.recipient.removingIdPrefixIfNeeded() : message.recipient
|
||||
|
||||
Threading.workQueue.async {
|
||||
getTargetSnodes(for: publicKey).map2 { targetSnodes in
|
||||
var rawResponsePromises: Set<Promise<Data>> = Set()
|
||||
var parameters = message.toJSON()
|
||||
parameters["namespace"] = namespace
|
||||
return Set(targetSnodes.map { targetSnode in
|
||||
attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) {
|
||||
parameters["namespace"] = (isClosedGroupMessage ? closedGroupNamespace : defaultNamespace)
|
||||
|
||||
for targetSnode in targetSnodes {
|
||||
let rawResponsePromise = attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) {
|
||||
invoke(.sendMessage, on: targetSnode, associatedWith: publicKey, parameters: parameters)
|
||||
}
|
||||
})
|
||||
rawResponsePromises.insert(rawResponsePromise)
|
||||
}
|
||||
|
||||
// Send closed group messages to default namespace as well
|
||||
if hardfork == 19 && softfork == 0 && isClosedGroupMessage {
|
||||
parameters["namespace"] = defaultNamespace
|
||||
for targetSnode in targetSnodes {
|
||||
let rawResponsePromise = attempt(maxRetryCount: maxRetryCount, recoveringOn: Threading.workQueue) {
|
||||
invoke(.sendMessage, on: targetSnode, associatedWith: publicKey, parameters: parameters)
|
||||
}
|
||||
rawResponsePromises.insert(rawResponsePromise)
|
||||
}
|
||||
}
|
||||
|
||||
return rawResponsePromises
|
||||
}.done2 { seal.fulfill($0) }.catch2 { seal.reject($0) }
|
||||
}
|
||||
|
||||
|
@ -766,6 +781,20 @@ public final class SnodeAPI : NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
public static func updateReceivedMessages(from messages: [JSON], associatedWith publicKey: String) {
|
||||
let oldReceivedMessages = SNSnodeKitConfiguration.shared.storage.getReceivedMessages(for: publicKey)
|
||||
var newReceivedMessages = oldReceivedMessages
|
||||
for message in messages {
|
||||
guard let hash = message["hash"] as? String else { continue }
|
||||
newReceivedMessages.insert(hash)
|
||||
}
|
||||
if oldReceivedMessages != newReceivedMessages {
|
||||
SNSnodeKitConfiguration.shared.storage.writeSync { transaction in
|
||||
SNSnodeKitConfiguration.shared.storage.setReceivedMessages(to: newReceivedMessages, for: publicKey, using: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func removeDuplicates(from rawMessages: [JSON], associatedWith publicKey: String) -> [JSON] {
|
||||
let oldReceivedMessages = SNSnodeKitConfiguration.shared.storage.getReceivedMessages(for: publicKey)
|
||||
var newReceivedMessages = oldReceivedMessages
|
||||
|
@ -778,12 +807,6 @@ public final class SnodeAPI : NSObject {
|
|||
newReceivedMessages.insert(hash)
|
||||
return !isDuplicate
|
||||
}
|
||||
// Avoid the sync write transaction if possible
|
||||
if oldReceivedMessages != newReceivedMessages {
|
||||
SNSnodeKitConfiguration.shared.storage.writeSync { transaction in
|
||||
SNSnodeKitConfiguration.shared.storage.setReceivedMessages(to: newReceivedMessages, for: publicKey, using: transaction)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -815,6 +838,10 @@ public final class SnodeAPI : NSObject {
|
|||
// The snode is unreachable
|
||||
handleBadSnode()
|
||||
|
||||
case 404:
|
||||
// May caused by invalid open groups
|
||||
SNLog("Can't reach the server.")
|
||||
|
||||
case 406:
|
||||
SNLog("The user's clock is out of sync with the service node network.")
|
||||
return Error.clockOutOfSync
|
||||
|
|
|
@ -110,7 +110,6 @@ extension Storage {
|
|||
if now >= expirationDate {
|
||||
Storage.writeSync { transaction in
|
||||
self.removeLastMessageHashInfo(for: snode, namespace: namespace, associatedWith: publicKey, using: transaction)
|
||||
self.setReceivedMessages(to: Set(), for: publicKey, using: transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue