|
|
|
@ -9,31 +9,69 @@ import PromiseKit
|
|
|
|
|
// • Document the expected cases for everything.
|
|
|
|
|
// • Express those cases in tests.
|
|
|
|
|
|
|
|
|
|
/// See [The Session Friend Request Protocol](https://github.com/loki-project/session-protocol-docs/wiki/Friend-Requests) for more information.
|
|
|
|
|
@objc(LKFriendRequestProtocol)
|
|
|
|
|
public final class FriendRequestProtocol : NSObject {
|
|
|
|
|
|
|
|
|
|
internal static var storage: OWSPrimaryStorage { OWSPrimaryStorage.shared() }
|
|
|
|
|
|
|
|
|
|
// MARK: - General
|
|
|
|
|
@objc(shouldInputBarBeEnabledForThread:)
|
|
|
|
|
public static func shouldInputBarBeEnabled(for thread: TSThread) -> Bool {
|
|
|
|
|
// Friend requests have nothing to do with groups, so if this isn't a contact thread the input bar should be enabled
|
|
|
|
|
guard let thread = thread as? TSContactThread else { return true }
|
|
|
|
|
// If this is a note to self, the input bar should be enabled
|
|
|
|
|
if SessionProtocol.isMessageNoteToSelf(thread) { return true }
|
|
|
|
|
let contactID = thread.contactIdentifier()
|
|
|
|
|
var linkedDeviceThreads: Set<TSContactThread> = []
|
|
|
|
|
storage.dbReadConnection.read { transaction in
|
|
|
|
|
linkedDeviceThreads = LokiDatabaseUtilities.getLinkedDeviceThreads(for: contactID, in: transaction)
|
|
|
|
|
}
|
|
|
|
|
// If the current user is friends with any of the other user's devices, the input bar should be enabled
|
|
|
|
|
if linkedDeviceThreads.contains(where: { $0.isContactFriend }) { return true }
|
|
|
|
|
// If no friend request has been sent, the input bar should be enabled
|
|
|
|
|
if !linkedDeviceThreads.contains(where: { $0.hasPendingFriendRequest }) { return true }
|
|
|
|
|
// There must be a pending friend request
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc(shouldAttachmentButtonBeEnabledForThread:)
|
|
|
|
|
public static func shouldAttachmentButtonBeEnabled(for thread: TSThread) -> Bool {
|
|
|
|
|
// Friend requests have nothing to do with groups, so if this isn't a contact thread the attachment button should be enabled
|
|
|
|
|
guard let thread = thread as? TSContactThread else { return true }
|
|
|
|
|
// If this is a note to self, the attachment button should be enabled
|
|
|
|
|
if SessionProtocol.isMessageNoteToSelf(thread) { return true }
|
|
|
|
|
let contactID = thread.contactIdentifier()
|
|
|
|
|
var linkedDeviceThreads: Set<TSContactThread> = []
|
|
|
|
|
storage.dbReadConnection.read { transaction in
|
|
|
|
|
linkedDeviceThreads = LokiDatabaseUtilities.getLinkedDeviceThreads(for: contactID, in: transaction)
|
|
|
|
|
}
|
|
|
|
|
// If the current user is friends with any of the other user's devices, the attachment button should be enabled
|
|
|
|
|
if linkedDeviceThreads.contains(where: { $0.isContactFriend }) { return true }
|
|
|
|
|
// If no friend request has been sent, the attachment button should be disabled
|
|
|
|
|
if !linkedDeviceThreads.contains(where: { $0.hasPendingFriendRequest }) { return false }
|
|
|
|
|
// There must be a pending friend request
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Sending
|
|
|
|
|
@objc(acceptFriendRequest:in:using:)
|
|
|
|
|
public static func acceptFriendRequest(_ friendRequest: TSIncomingMessage, in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
|
|
|
|
|
@objc(acceptFriendRequestFrom:in:using:)
|
|
|
|
|
public static func acceptFriendRequest(from hexEncodedPublicKey: String, in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
|
|
|
|
|
// Accept all outstanding friend requests associated with this user and try to establish sessions with the
|
|
|
|
|
// subset of their devices that haven't sent a friend request.
|
|
|
|
|
let senderID = friendRequest.authorId
|
|
|
|
|
let linkedDeviceThreads = LokiDatabaseUtilities.getLinkedDeviceThreads(for: senderID, in: transaction)
|
|
|
|
|
let linkedDeviceThreads = LokiDatabaseUtilities.getLinkedDeviceThreads(for: hexEncodedPublicKey, in: transaction) // This doesn't create new threads if they don't exist yet
|
|
|
|
|
for thread in linkedDeviceThreads {
|
|
|
|
|
if thread.hasPendingFriendRequest {
|
|
|
|
|
// TODO: The Obj-C implementation was actually sending this to self.thread. I'm assuming that's not what we meant.
|
|
|
|
|
sendFriendRequestAcceptanceMessage(to: senderID, in: thread, using: transaction)
|
|
|
|
|
sendFriendRequestAcceptanceMessage(to: thread.contactIdentifier(), in: thread, using: transaction) // NOT hexEncodedPublicKey
|
|
|
|
|
thread.saveFriendRequestStatus(.friends, with: transaction)
|
|
|
|
|
} else {
|
|
|
|
|
let autoGeneratedFRMessageSend = MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessageSend(for: senderID, in: transaction)
|
|
|
|
|
let autoGeneratedFRMessageSend = MultiDeviceProtocol.getAutoGeneratedMultiDeviceFRMessageSend(for: thread.contactIdentifier(), in: transaction) // NOT hexEncodedPublicKey
|
|
|
|
|
OWSDispatch.sendingQueue().async {
|
|
|
|
|
let messageSender = SSKEnvironment.shared.messageSender
|
|
|
|
|
messageSender.sendMessage(autoGeneratedFRMessageSend)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
thread.saveFriendRequestStatus(.friends, with: transaction)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc(sendFriendRequestAcceptanceMessageToHexEncodedPublicKey:in:using:)
|
|
|
|
@ -46,7 +84,8 @@ public final class FriendRequestProtocol : NSObject {
|
|
|
|
|
@objc(declineFriendRequest:in:using:)
|
|
|
|
|
public static func declineFriendRequest(_ friendRequest: TSIncomingMessage, in thread: TSThread, using transaction: YapDatabaseReadWriteTransaction) {
|
|
|
|
|
thread.saveFriendRequestStatus(.none, with: transaction)
|
|
|
|
|
// Delete pre keys
|
|
|
|
|
// Delete the pre key bundle for the given contact. This ensures that if we send a
|
|
|
|
|
// new message after this, it restarts the friend request process from scratch.
|
|
|
|
|
let senderID = friendRequest.authorId
|
|
|
|
|
storage.removePreKeyBundle(forContact: senderID, transaction: transaction)
|
|
|
|
|
}
|
|
|
|
@ -65,7 +104,7 @@ public final class FriendRequestProtocol : NSObject {
|
|
|
|
|
// This can happen if Alice sent Bob a friend request, Bob declined, but then Bob changed his
|
|
|
|
|
// mind and sent a friend request to Alice. In this case we want Alice to auto-accept the request
|
|
|
|
|
// and send a friend request accepted message back to Bob. We don't check that sending the
|
|
|
|
|
// friend request accepted message succeeded. Even if it doesn't, the thread's current friend
|
|
|
|
|
// friend request accepted message succeeds. Even if it doesn't, the thread's current friend
|
|
|
|
|
// request status will be set to LKThreadFriendRequestStatusFriends for Alice making it possible
|
|
|
|
|
// for Alice to send messages to Bob. When Bob receives a message, his thread's friend request status
|
|
|
|
|
// will then be set to LKThreadFriendRequestStatusFriends. If we do check for a successful send
|
|
|
|
@ -101,11 +140,13 @@ public final class FriendRequestProtocol : NSObject {
|
|
|
|
|
existingFriendRequestMessage.isFriendRequest {
|
|
|
|
|
existingFriendRequestMessage.saveFriendRequestStatus(.accepted, with: transaction)
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
// Send our P2P details
|
|
|
|
|
if let addressMessage = LokiP2PAPI.onlineBroadcastMessage(forThread: thread) {
|
|
|
|
|
let messageSenderJobQueue = SSKEnvironment.shared.messageSenderJobQueue
|
|
|
|
|
messageSenderJobQueue.add(message: addressMessage, transaction: transaction)
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc(handleFriendRequestMessageIfNeeded:associatedWith:wrappedIn:in:using:)
|
|
|
|
@ -146,8 +187,7 @@ public final class FriendRequestProtocol : NSObject {
|
|
|
|
|
let linkedDeviceThreads = LokiDatabaseUtilities.getLinkedDeviceThreads(for: hexEncodedPublicKey, in: transaction)
|
|
|
|
|
for thread in linkedDeviceThreads {
|
|
|
|
|
thread.enumerateInteractions(with: transaction) { interaction, _ in
|
|
|
|
|
guard let incomingMessage = interaction as? TSIncomingMessage,
|
|
|
|
|
incomingMessage.friendRequestStatus != .none else { return }
|
|
|
|
|
guard let incomingMessage = interaction as? TSIncomingMessage, incomingMessage.friendRequestStatus != .none else { return }
|
|
|
|
|
incomingMessage.saveFriendRequestStatus(.none, with: transaction)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|