diff --git a/Signal/src/Loki/Settings/DeviceLinksVC.swift b/Signal/src/Loki/Settings/DeviceLinksVC.swift index 4cf729b31..9e0067075 100644 --- a/Signal/src/Loki/Settings/DeviceLinksVC.swift +++ b/Signal/src/Loki/Settings/DeviceLinksVC.swift @@ -112,17 +112,17 @@ final class DeviceLinksVC : UIViewController, UITableViewDataSource, UITableView func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { defer { tableView.deselectRow(at: indexPath, animated: true) } - let device = deviceLinks[indexPath.row].other + let deviceLink = deviceLinks[indexPath.row] let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) sheet.addAction(UIAlertAction(title: NSLocalizedString("Change Name", comment: ""), style: .default) { [weak self] _ in guard let self = self else { return } let deviceNameModal = DeviceNameModal() - deviceNameModal.device = device + deviceNameModal.device = deviceLink.other deviceNameModal.delegate = self self.present(deviceNameModal, animated: true, completion: nil) }) - sheet.addAction(UIAlertAction(title: NSLocalizedString("Unlink", comment: ""), style: .destructive) { _ in - // TODO: Implement + sheet.addAction(UIAlertAction(title: NSLocalizedString("Unlink", comment: ""), style: .destructive) { [weak self] _ in + self?.removeDeviceLink(deviceLink) }) sheet.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { _ in }) present(sheet, animated: true, completion: nil) @@ -132,6 +132,19 @@ final class DeviceLinksVC : UIViewController, UITableViewDataSource, UITableView dismiss(animated: true, completion: nil) updateUI() } + + private func removeDeviceLink(_ deviceLink: DeviceLink) { + LokiStorageAPI.removeDeviceLink(deviceLink).done { [weak self] in + guard let thread = TSContactThread.fetch(uniqueId: TSContactThread.threadId(fromContactId: deviceLink.other.hexEncodedPublicKey)) else { return } + let unlinkDeviceMessage = UnlinkDeviceMessage(thread: thread)! + ThreadUtil.enqueue(unlinkDeviceMessage) + self?.updateDeviceLinks() + }.catch { [weak self] _ in + let alert = UIAlertController(title: NSLocalizedString("Couldn't Unlink Device", comment: ""), message: NSLocalizedString("Please check your internet connection and try again", comment: ""), preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), accessibilityIdentifier: nil, style: .default, handler: nil)) + self?.present(alert, animated: true, completion: nil) + } + } } // MARK: - Cell diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 1881bc64e..61e0d0979 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2665,3 +2665,5 @@ "Enter a Name" = "Enter a Name"; "Error" = "Error"; "Please pick a name" = "Please pick a name"; +"Couldn't Unlink Device" = "Couldn't Unlink Device"; +"Please check your internet connection and try again" = "Please check your internet connection and try again"; diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index b0f6e4655..843758b2f 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -5,6 +5,7 @@ NS_ASSUME_NONNULL_BEGIN @class LKDeviceLinkMessage; +@class LKUnlinkDeviceMessage; @class OWSBlockingManager; @class OWSContact; @class OWSContactsManager; @@ -47,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN + (TSOutgoingMessage *)enqueueFriendRequestAcceptanceMessageInThread:(TSThread *)thread; + (void)enqueueDeviceLinkMessage:(LKDeviceLinkMessage *)message; ++ (void)enqueueUnlinkDeviceMessage:(LKUnlinkDeviceMessage *)message; + (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)fullMessageText inThread:(TSThread *)thread diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index 5eafda059..7a1bbfa4a 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -101,6 +101,13 @@ typedef void (^BuildOutgoingMessageCompletionBlock)(TSOutgoingMessage *savedMess }]; } ++ (void)enqueueUnlinkDeviceMessage:(LKUnlinkDeviceMessage *)message +{ + [self.dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { + [self.messageSenderJobQueue addMessage:message transaction:transaction]; + }]; +} + + (TSOutgoingMessage *)enqueueMessageWithText:(NSString *)fullMessageText inThread:(TSThread *)thread quotedReplyModel:(nullable OWSQuotedReplyModel *)quotedReplyModel diff --git a/SignalServiceKit/protobuf/SignalService.proto b/SignalServiceKit/protobuf/SignalService.proto index 8674ba7e8..d1a646026 100644 --- a/SignalServiceKit/protobuf/SignalService.proto +++ b/SignalServiceKit/protobuf/SignalService.proto @@ -148,6 +148,7 @@ message DataMessage { END_SESSION = 1; EXPIRATION_TIMER_UPDATE = 2; PROFILE_KEY_UPDATE = 4; + UNLINK_DEVICE = 8; } message Quote { @@ -247,7 +248,7 @@ message DataMessage { // Loki: A custom message for our profile message LokiProfile { optional string displayName = 1; - optional AttachmentPointer avatar = 2; + optional string profilePicture = 2; } optional string body = 1; diff --git a/SignalServiceKit/src/Loki/API/LokiStorageAPI.swift b/SignalServiceKit/src/Loki/API/LokiStorageAPI.swift index b0346ec78..761a5ed57 100644 --- a/SignalServiceKit/src/Loki/API/LokiStorageAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiStorageAPI.swift @@ -92,7 +92,7 @@ public final class LokiStorageAPI : LokiDotNetAPI { let url = URL(string: "\(server)/users/me")! let request = TSRequest(url: url, method: "PATCH", parameters: parameters) request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] - return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { _ in }.recover(on: DispatchQueue.global()) { error in + return TSNetworkManager.shared().perform(request, withCompletionQueue: DispatchQueue.global()).map { _ in }.retryingIfNeeded(maxRetryCount: 8).recover(on: DispatchQueue.global()) { error in print("Couldn't update device links due to error: \(error).") throw error } diff --git a/SignalServiceKit/src/Loki/Messaging/LKDeviceLinkMessage.h b/SignalServiceKit/src/Loki/Messaging/LKDeviceLinkMessage.h index 0fc0f1f47..396c34e83 100644 --- a/SignalServiceKit/src/Loki/Messaging/LKDeviceLinkMessage.h +++ b/SignalServiceKit/src/Loki/Messaging/LKDeviceLinkMessage.h @@ -3,7 +3,6 @@ typedef NS_ENUM(NSUInteger, LKDeviceLinkMessageKind) { LKDeviceLinkMessageKindRequest = 1, LKDeviceLinkMessageKindAuthorization = 2, - LKDeviceLinkMessageKindRevocation = 3 }; NS_SWIFT_NAME(DeviceLinkMessage) diff --git a/SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.h b/SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.h new file mode 100644 index 000000000..227b5b0a9 --- /dev/null +++ b/SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.h @@ -0,0 +1,8 @@ +#import "TSOutgoingMessage.h" + +NS_SWIFT_NAME(UnlinkDeviceMessage) +@interface LKUnlinkDeviceMessage : TSOutgoingMessage + +- (instancetype)initWithThread:(TSThread *)thread; + +@end diff --git a/SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.m b/SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.m new file mode 100644 index 000000000..2728333c5 --- /dev/null +++ b/SignalServiceKit/src/Loki/Messaging/LKUnlinkDeviceMessage.m @@ -0,0 +1,26 @@ +#import "LKUnlinkDeviceMessage.h" +#import +#import + +@implementation LKUnlinkDeviceMessage + +#pragma mark Initialization +- (instancetype)initWithThread:(TSThread *)thread { + return [self initOutgoingMessageWithTimestamp:NSDate.ows_millisecondTimeStamp inThread:thread messageBody:@"" attachmentIds:[NSMutableArray new] + expiresInSeconds:0 expireStartedAt:0 isVoiceMessage:NO groupMetaMessage:TSGroupMetaMessageUnspecified quotedMessage:nil contactShare:nil linkPreview:nil]; +} + +#pragma mark Building +- (nullable SSKProtoDataMessageBuilder *)dataMessageBuilder +{ + SSKProtoDataMessageBuilder *builder = super.dataMessageBuilder; + if (builder == nil) { return nil; } + [builder setFlags:SSKProtoDataMessageFlagsUnlinkDevice]; + return builder; +} + +#pragma mark Settings +- (BOOL)shouldSyncTranscript { return NO; } +- (BOOL)shouldBeSaved { return NO; } + +@end diff --git a/SignalServiceKit/src/Protos/Generated/SSKProto.swift b/SignalServiceKit/src/Protos/Generated/SSKProto.swift index e7ab23ce4..9082aaa2c 100644 --- a/SignalServiceKit/src/Protos/Generated/SSKProto.swift +++ b/SignalServiceKit/src/Protos/Generated/SSKProto.swift @@ -3433,6 +3433,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { case endSession = 1 case expirationTimerUpdate = 2 case profileKeyUpdate = 4 + case unlinkDevice = 8 } private class func SSKProtoDataMessageFlagsWrap(_ value: SignalServiceProtos_DataMessage.Flags) -> SSKProtoDataMessageFlags { @@ -3440,6 +3441,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { case .endSession: return .endSession case .expirationTimerUpdate: return .expirationTimerUpdate case .profileKeyUpdate: return .profileKeyUpdate + case .unlinkDevice: return .unlinkDevice } } @@ -3448,6 +3450,7 @@ extension SSKProtoDataMessageLokiProfile.SSKProtoDataMessageLokiProfileBuilder { case .endSession: return .endSession case .expirationTimerUpdate: return .expirationTimerUpdate case .profileKeyUpdate: return .profileKeyUpdate + case .unlinkDevice: return .unlinkDevice } } diff --git a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift index 755380d94..ccad121ed 100644 --- a/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift +++ b/SignalServiceKit/src/Protos/Generated/SignalService.pb.swift @@ -881,6 +881,7 @@ struct SignalServiceProtos_DataMessage { case endSession // = 1 case expirationTimerUpdate // = 2 case profileKeyUpdate // = 4 + case unlinkDevice // = 8 init() { self = .endSession @@ -891,6 +892,7 @@ struct SignalServiceProtos_DataMessage { case 1: self = .endSession case 2: self = .expirationTimerUpdate case 4: self = .profileKeyUpdate + case 8: self = .unlinkDevice default: return nil } } @@ -900,6 +902,7 @@ struct SignalServiceProtos_DataMessage { case .endSession: return 1 case .expirationTimerUpdate: return 2 case .profileKeyUpdate: return 4 + case .unlinkDevice: return 8 } } @@ -3476,6 +3479,7 @@ extension SignalServiceProtos_DataMessage.Flags: SwiftProtobuf._ProtoNameProvidi 1: .same(proto: "END_SESSION"), 2: .same(proto: "EXPIRATION_TIMER_UPDATE"), 4: .same(proto: "PROFILE_KEY_UPDATE"), + 8: .same(proto: "UNLINK_DEVICE"), ] }