From 28d5e9c7a0c41b561855da7fbb7f21754b82c885 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 9 Oct 2019 16:19:58 +1100 Subject: [PATCH] Added Public chat VC --- Signal.xcodeproj/project.pbxproj | 4 + Signal/src/Loki/NewPublicChatVC.swift | 97 +++++++++++++++++++ .../HomeView/HomeViewController.m | 27 +++++- .../translations/en.lproj/Localizable.strings | 7 ++ .../Loki/API/Group Chat/LokiGroupChat.swift | 2 +- .../API/Group Chat/LokiGroupChatAPI.swift | 17 ++++ .../Group Chat/LokiPublicChatManager.swift | 20 +++- 7 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 Signal/src/Loki/NewPublicChatVC.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 1658f38da..cd9ad0abc 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 241C6315231F64CE00B4198E /* CGFloat+Rounding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */; }; 241C6316231F64CE00B4198E /* UIColor+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 241C6310231F5C4400B4198E /* UIColor+Helper.swift */; }; 24A830A22293CD0100F4CAC0 /* LokiP2PServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */; }; + 24BD2609234DA2050008EB0A /* NewPublicChatVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */; }; 2AE2882E4C2B96BFFF9EE27C /* Pods_SignalShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F94C85CB0B235DA37F68ED0 /* Pods_SignalShareExtension.framework */; }; 3403B95D20EA9527001A1F44 /* OWSContactShareButtonsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */; }; 34074F61203D0CBE004596AE /* OWSSounds.m in Sources */ = {isa = PBXBuildFile; fileRef = 34074F5F203D0CBD004596AE /* OWSSounds.m */; }; @@ -678,6 +679,7 @@ 241C6310231F5C4400B4198E /* UIColor+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Helper.swift"; sourceTree = ""; }; 241C6312231F5F1D00B4198E /* CGFloat+Rounding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Rounding.swift"; sourceTree = ""; }; 24A830A12293CD0100F4CAC0 /* LokiP2PServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LokiP2PServer.swift; sourceTree = ""; }; + 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewPublicChatVC.swift; sourceTree = ""; }; 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalMessaging.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3403B95B20EA9526001A1F44 /* OWSContactShareButtonsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactShareButtonsView.m; sourceTree = ""; }; 3403B95C20EA9527001A1F44 /* OWSContactShareButtonsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactShareButtonsView.h; sourceTree = ""; }; @@ -2649,6 +2651,7 @@ B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */, B8258491230FA5DA001B41CB /* ScanQRCodeVC.h */, B8258492230FA5E9001B41CB /* ScanQRCodeVC.m */, + 24BD2608234DA2050008EB0A /* NewPublicChatVC.swift */, ); path = Loki; sourceTree = ""; @@ -3841,6 +3844,7 @@ B821F2F82272CED3002C88C0 /* DisplayNameVC.swift in Sources */, 34D8C0271ED3673300188D7C /* DebugUIMessages.m in Sources */, B885D5F62334A32100EE0D8E /* UIView+Constraint.swift in Sources */, + 24BD2609234DA2050008EB0A /* NewPublicChatVC.swift in Sources */, 34DBF003206BD5A500025978 /* OWSMessageTextView.m in Sources */, 34D1F0B41F86D31D0066283D /* ConversationCollectionView.m in Sources */, 34B3F8821E8DF1700035BE1A /* NewContactThreadViewController.m in Sources */, diff --git a/Signal/src/Loki/NewPublicChatVC.swift b/Signal/src/Loki/NewPublicChatVC.swift new file mode 100644 index 000000000..1fb11a536 --- /dev/null +++ b/Signal/src/Loki/NewPublicChatVC.swift @@ -0,0 +1,97 @@ + +@objc(LKNewPublicChatVC) +final class NewPublicChatVC : OWSViewController { + + // MARK: Components + private lazy var serverUrlTextField: UITextField = { + let result = UITextField() + result.textColor = Theme.primaryColor + result.font = UIFont.ows_dynamicTypeBodyClamped + let placeholder = NSMutableAttributedString(string: NSLocalizedString("Enter a Server URL", comment: "")) + placeholder.addAttribute(.foregroundColor, value: Theme.placeholderColor, range: NSRange(location: 0, length: placeholder.length)) + result.attributedPlaceholder = placeholder + result.tintColor = UIColor.lokiGreen() + result.keyboardAppearance = .dark + return result + }() + + private lazy var addButton: OWSFlatButton = { + let addButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() + let addButtonHeight = addButtonFont.pointSize * 48 / 17 + let addButton = OWSFlatButton.button(title: NSLocalizedString("Add", comment: ""), font: addButtonFont, titleColor: .white, backgroundColor: .lokiGreen(), target: self, selector: #selector(handleNextButtonTapped)) + addButton.autoSetDimension(.height, toSize: addButtonHeight) + return addButton + }() + + // MARK: Lifecycle + override func viewDidLoad() { + // Background color & margins + view.backgroundColor = Theme.backgroundColor + view.layoutMargins = .zero + // Navigation bar + navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close)) + title = NSLocalizedString("Add Public Chat Server", comment: "") + // Separator + let separator = UIView() + separator.autoSetDimension(.height, toSize: 1 / UIScreen.main.scale) + separator.backgroundColor = Theme.hairlineColor + + updateButton(enabled: true) + + // Stack view + let stackView = UIStackView(arrangedSubviews: [ + serverUrlTextField, + UIView.vStretchingSpacer(), + addButton + ]) + stackView.axis = .vertical + stackView.alignment = .fill + stackView.layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + stackView.isLayoutMarginsRelativeArrangement = true + view.addSubview(stackView) + stackView.autoPinWidthToSuperview() + stackView.autoPin(toTopLayoutGuideOf: self, withInset: 0) + autoPinView(toBottomOfViewControllerOrKeyboard: stackView, avoidNotch: true) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + serverUrlTextField.becomeFirstResponder() + } + + // MARK: Interaction + @objc private func close() { + dismiss(animated: true, completion: nil) + } + + @objc private func handleNextButtonTapped() { + let serverURL = (serverUrlTextField.text?.trimmingCharacters(in: .whitespaces) ?? "").lowercased().replacingOccurrences(of: "http://", with: "https://") + guard let url = URL(string: serverURL), let scheme = url.scheme, scheme == "https", let _ = url.host else { + showAlert(title: NSLocalizedString("Invalid server URL provided", comment: ""), message: NSLocalizedString("Please make sure you have provided the full url", comment: "")) + return + } + + updateButton(enabled: false) + + // TODO: Upon adding we should fetch previous messages + LokiPublicChatManager.shared.addChat(server: serverURL, channel: 1) + .done(on: .main) { _ in + self.presentingViewController!.dismiss(animated: true, completion: nil) + } + .catch(on: .main) { e in + self.updateButton(enabled: true) + self.showAlert(title: NSLocalizedString("Failed to connect to server", comment: "")) + } + } + + private func showAlert(title: String, message: String = "") { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) + presentAlert(alert) + } + + private func updateButton(enabled: Bool) { + addButton.setEnabled(enabled) + addButton.setTitle(enabled ? NSLocalizedString("Add", comment: "") : NSLocalizedString("Connecting to server", comment: "")) + } +} diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 1b20159b6..bcdfec4f5 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -787,11 +787,21 @@ typedef NS_ENUM(NSInteger, HomeViewControllerSection) { self.navigationItem.leftBarButtonItem = settingsButton; SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, settingsButton); - self.navigationItem.rightBarButtonItem = - [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose - target:self - action:@selector(showNewConversationView) - accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"compose")]; + UIBarButtonItem *newConversation = [[UIBarButtonItem alloc] + initWithBarButtonSystemItem:UIBarButtonSystemItemCompose + target:self + action:@selector(showNewConversationView) + accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"compose")]; + + UIBarButtonItem *newServer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd + target:self + action:@selector(showNewPublicChatView) + accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"addServer")]; + + self.navigationItem.rightBarButtonItems = @[ + newConversation, + newServer, + ]; } - (void)settingsButtonPressed:(id)sender @@ -861,6 +871,13 @@ typedef NS_ENUM(NSInteger, HomeViewControllerSection) { */ } +- (void)showNewPublicChatView +{ + LKNewPublicChatVC *newPublicChatVC = [LKNewPublicChatVC new]; + OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:newPublicChatVC]; + [self.navigationController presentViewController:navigationController animated:YES completion:nil]; +} + - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index addb842c8..b838d0ed9 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2551,6 +2551,8 @@ "Type an optional password for added security" = "Type an optional password for added security"; "Password (Optional)" = "Password (Optional)"; "Next" = "Next"; +"Add" = "Add"; +"Connecting to server" = "Connecting to server..."; "Please save the seed below in a safe location. It can be used to restore your account if you lose access, or to migrate to a new device." = "Please save the seed below in a safe location. It can be used to restore your account if you lose access, or to migrate to a new device."; "Restore your account by entering your seed below." = "Restore your account by entering your seed below."; "Copy" = "Copy"; @@ -2569,6 +2571,7 @@ "Start a Conversation" = "Start a Conversation"; "Invalid public key" = "Invalid public key"; "No search results" = "No search results"; +"Failed to connect to server" = "Failed to connect to server"; "Calculating proof of work" = "Calculating proof of work"; "Failed to calculate proof of work." = "Failed to calculate proof of work."; "Share Public Key" = "Share Public Key"; @@ -2594,7 +2597,9 @@ "Loki Messenger" = "Loki Messenger"; "Privacy Policy" = "Privacy Policy"; "New Conversation" = "New Conversation"; +"Add Public Chat Server" = "Add Public Chat Server"; "Enter a Public Key" = "Enter a Public Key"; +"Enter a Server URL" = "Enter a Server URL"; "For example: 059abcf223aa8c10e3dc2d623688b75dd25896794717e4a9c486772664fc95e41e." = "For example: 059abcf223aa8c10e3dc2d623688b75dd25896794717e4a9c486772664fc95e41e."; "Invalid Public Key" = "Invalid Public Key"; "Please check the public key you entered and try again." = "Please check the public key you entered and try again."; @@ -2636,3 +2641,5 @@ "Your device has been linked successfully" = "Your device has been linked successfully"; "Link" = "Link"; "Anonymous" = "Anonymous"; +"Invalid server URL provided" = "Invalid server URL provided"; +"Please make sure you have provided the full url" = "Please make sure you have provided the full url. E.g https://public-chat-server.url/"; diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift index ee93c86aa..c07bbd6cf 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChat.swift @@ -22,7 +22,7 @@ public final class LokiGroupChat : NSObject, NSCoding { @objc public init(channel: UInt64, server: String, displayName: String, isDeletable: Bool) { self.channel = channel - self.server = server + self.server = server.lowercased() self.displayName = displayName self.isDeletable = isDeletable } diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift index 4ca2b58e2..072c12caf 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiGroupChatAPI.swift @@ -36,6 +36,12 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } } + private static func removeLastMessageServerID(for group: UInt64, on server: String) { + storage.dbReadWriteConnection.readWrite { transaction in + transaction.removeObject(forKey: "\(server).\(group)", inCollection: lastMessageServerIDCollection) + } + } + private static func getLastDeletionServerID(for group: UInt64, on server: String) -> UInt? { var result: UInt? = nil storage.dbReadConnection.read { transaction in @@ -50,6 +56,12 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } } + private static func removeLastDeletionServerID(for group: UInt64, on server: String) { + storage.dbReadWriteConnection.readWrite { transaction in + transaction.removeObject(forKey: "\(server).\(group)", inCollection: lastDeletionServerIDCollection) + } + } + // MARK: Public API public static func getMessages(for group: UInt64, on server: String) -> Promise<[LokiGroupMessage]> { print("[Loki] Getting messages for group chat with ID: \(group) on server: \(server).") @@ -228,6 +240,11 @@ public final class LokiGroupChatAPI : LokiDotNetAPI { } } + public static func resetLastMessageCache(for group: UInt64, on server: String) { + removeLastMessageServerID(for: group, on: server) + removeLastDeletionServerID(for: group, on: server) + } + // MARK: Public API (Obj-C) @objc(getMessagesForGroup:onServer:) public static func objc_getMessages(for group: UInt64, on server: String) -> AnyPromise { diff --git a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift index 313fbbf6b..18e3b27dc 100644 --- a/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift +++ b/SignalServiceKit/src/Loki/API/Group Chat/LokiPublicChatManager.swift @@ -46,7 +46,10 @@ public final class LokiPublicChatManager: NSObject { } public func addChat(server: String, channel: UInt64) -> Promise { - guard let ourHexEncodedPublicKey = ourHexEncodedPublicKey else { return Promise(error: Error.userPublicKeyNotFound) } + if let existingChat = getChat(server: server, channel: channel) { + return Promise.value(self.addChat(server: server, channel: channel, name: existingChat.displayName)) + } + return LokiGroupChatAPI.getAuthToken(for: server).then { token in return LokiGroupChatAPI.getChannelInfo(channel, on: server) }.map { channelInfo -> LokiGroupChat in @@ -112,10 +115,23 @@ public final class LokiPublicChatManager: NSObject { @objc private func onThreadDeleted(_ notification: Notification) { guard let threadId = notification.userInfo?["threadId"] as? String else { return } + + // Reset the last message cache + if let chat = self.chats[threadId] { + LokiGroupChatAPI.resetLastMessageCache(for: chat.channel, on: chat.server) + } + + // Remove the chat from the db storage.dbReadWriteConnection.readWrite { transaction in self.storage.removeGroupChat(for: threadId, in: transaction) } - + refreshChatsAndPollers() } + + private func getChat(server: String, channel: UInt64) -> LokiGroupChat? { + return chats.values.first { chat in + return chat.server == server && chat.channel == channel + } + } }