diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj index c9543211fb34d35b32f6a92311e7d10a7a82ac99..b6db618c71f2a5c171f031185b68cd6a8a1089e4 100644 --- a/Ring/Ring.xcodeproj/project.pbxproj +++ b/Ring/Ring.xcodeproj/project.pbxproj @@ -80,6 +80,8 @@ 04399B141D1C341A00E99CD9 /* libx264.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399AE21D1C341A00E99CD9 /* libx264.a */; }; 04399B151D1C341A00E99CD9 /* libyaml-cpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */; }; 0586C94B1F684DF600613517 /* UIImage+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0586C94A1F684DF600613517 /* UIImage+Helpers.swift */; }; + 0E403F811F7D797300C80BC2 /* MessageCellGenerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E403F801F7D797300C80BC2 /* MessageCellGenerated.swift */; }; + 0E403F831F7D79B000C80BC2 /* MessageCellGenerated.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E403F821F7D79B000C80BC2 /* MessageCellGenerated.xib */; }; 0EE1B54E1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE1B54D1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift */; }; 0EE1B5501F75AD4700BA98EE /* VCardUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE1B54F1F75AD4700BA98EE /* VCardUtils.swift */; }; 0EEFBA3C1F83DA21000EDBAD /* libsecp256k1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EEFBA3B1F83DA21000EDBAD /* libsecp256k1.a */; }; @@ -296,6 +298,8 @@ 04399AE21D1C341A00E99CD9 /* libx264.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libx264.a; path = ../fat/lib/libx264.a; sourceTree = "<group>"; }; 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libyaml-cpp.a"; path = "../fat/lib/libyaml-cpp.a"; sourceTree = "<group>"; }; 0586C94A1F684DF600613517 /* UIImage+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Helpers.swift"; sourceTree = "<group>"; }; + 0E403F801F7D797300C80BC2 /* MessageCellGenerated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCellGenerated.swift; sourceTree = "<group>"; }; + 0E403F821F7D79B000C80BC2 /* MessageCellGenerated.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessageCellGenerated.xib; sourceTree = "<group>"; }; 0EE1B54D1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CNContactVCardSerialization+Helpers.swift"; sourceTree = "<group>"; }; 0EE1B54F1F75AD4700BA98EE /* VCardUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VCardUtils.swift; sourceTree = "<group>"; }; 0EEFBA3B1F83DA21000EDBAD /* libsecp256k1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsecp256k1.a; path = ../DEPS/arm64/lib/libsecp256k1.a; sourceTree = "<group>"; }; @@ -940,6 +944,8 @@ 1A2D18F21F292D7200B2C785 /* MessageCellReceived.xib */, 1A2D18F31F292D7200B2C785 /* MessageCellSent.swift */, 1A2D18F41F292D7200B2C785 /* MessageCellSent.xib */, + 0E403F801F7D797300C80BC2 /* MessageCellGenerated.swift */, + 0E403F821F7D79B000C80BC2 /* MessageCellGenerated.xib */, ); path = Cells; sourceTree = "<group>"; @@ -1152,6 +1158,7 @@ 1A2D18F61F292D7200B2C785 /* MessageCellReceived.xib in Resources */, 1A2D18EF1F291A0100B2C785 /* MeDetailViewController.storyboard in Resources */, 1A2D18B11F2915B600B2C785 /* SmartlistViewController.storyboard in Resources */, + 0E403F831F7D79B000C80BC2 /* MessageCellGenerated.xib in Resources */, 04399A031D1C2D9D00E99CD9 /* Images.xcassets in Resources */, 1A2041841F1EA0FC00C08435 /* CreateAccountViewController.storyboard in Resources */, 1A2D18ED1F2919D800B2C785 /* MeViewController.storyboard in Resources */, @@ -1294,6 +1301,7 @@ 1A5DC0371F35675E0075E8EF /* ContactRequestCell.swift in Sources */, 1A20417C1F1E56FF00C08435 /* WelcomeViewModel.swift in Sources */, 1A5DC03D1F35678D0075E8EF /* ContactRequestItem.swift in Sources */, + 0E403F811F7D797300C80BC2 /* MessageCellGenerated.swift in Sources */, 1A2041821F1E906B00C08435 /* CreateProfileViewModel.swift in Sources */, 1A0C4EE31F1D673600550433 /* InjectionBag.swift in Sources */, 564C44641E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift in Sources */, diff --git a/Ring/Ring/Extensions/Chameleon+Ring.swift b/Ring/Ring/Extensions/Chameleon+Ring.swift index 7ebb62a1685c6ac8f6a6b122305625531548c5f8..0146a074cd2ef35b9bc268b935d8a2fff51d41f9 100644 --- a/Ring/Ring/Extensions/Chameleon+Ring.swift +++ b/Ring/Ring/Extensions/Chameleon+Ring.swift @@ -54,5 +54,8 @@ extension Chameleon { MessageBubble.appearance(whenContainedInInstancesOf: [MessageCellReceived.self]).tintColor = secondaryContentColor MessageBubble.appearance(whenContainedInInstancesOf: [MessageCellReceived.self]).backgroundColor = secondaryColor UILabel.appearance(whenContainedInInstancesOf: [MessageBubble.self, MessageCellReceived.self]).textColor = secondaryContentColor + + MessageBubble.appearance(whenContainedInInstancesOf: [MessageCellGenerated.self]).tintColor = UIColor.clear + MessageBubble.appearance(whenContainedInInstancesOf: [MessageCellGenerated.self]).backgroundColor = UIColor.clear } } diff --git a/Ring/Ring/Features/ContactRequests/ContactRequestsViewModel.swift b/Ring/Ring/Features/ContactRequests/ContactRequestsViewModel.swift index bac3eabc8bf48e94b9006428a185ef48fd9c05df..70aa514c893d1af498af64d2b90f08979121b314 100644 --- a/Ring/Ring/Features/ContactRequests/ContactRequestsViewModel.swift +++ b/Ring/Ring/Features/ContactRequests/ContactRequestsViewModel.swift @@ -26,13 +26,16 @@ class ContactRequestsViewModel: ViewModel { let contactsService: ContactsService let accountsService: AccountsService + let conversationService: ConversationsService let nameService: NameService fileprivate let disposeBag = DisposeBag() + fileprivate let log = SwiftyBeaver.self required init(with injectionBag: InjectionBag) { self.contactsService = injectionBag.contactsService self.accountsService = injectionBag.accountService + self.conversationService = injectionBag.conversationsService self.nameService = injectionBag.nameService } @@ -62,6 +65,26 @@ class ContactRequestsViewModel: ViewModel { func accept(withItem item: ContactRequestItem) -> Observable<Void> { let acceptCompleted = self.contactsService.accept(contactRequest: item.contactRequest, withAccount: self.accountsService.currentAccount!) + let accountHelper = AccountModelHelper(withAccount: self.accountsService.currentAccount!) + self.conversationService.saveMessage(withContent: + GeneratedMessageType.receivedContactRequest.rawValue, + byAuthor: accountHelper.ringId!, + toConversationWith: item.contactRequest.ringId, + currentAccountId: (self.accountsService.currentAccount?.id)!, generated: true) + .subscribe(onCompleted: { [unowned self] in + self.log.debug("Message saved") + }) + .disposed(by: disposeBag) + self.conversationService.saveMessage(withContent: + GeneratedMessageType.contactRequestAccepted.rawValue, + byAuthor: accountHelper.ringId!, + toConversationWith: item.contactRequest.ringId, + currentAccountId: (self.accountsService.currentAccount?.id)!, generated: true) + .subscribe(onCompleted: { [unowned self] in + self.log.debug("Message saved") + }) + .disposed(by: disposeBag) + if let vCard = item.contactRequest.vCard { let saveVCardCompleted = self.contactsService.saveVCard(vCard: vCard, forContactWithRingId: item.contactRequest.ringId) return Observable<Void>.zip(acceptCompleted, saveVCardCompleted) { _, _ in diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellGenerated.swift b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellGenerated.swift new file mode 100644 index 0000000000000000000000000000000000000000..2fd92806700164efea1d0a37392eac54c3f73c0b --- /dev/null +++ b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellGenerated.swift @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 Savoir-faire Linux Inc. + * + * Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import Foundation +import Reusable + +class MessageCellGenerated: UITableViewCell, NibReusable { + + @IBOutlet weak var bubble: MessageBubble! + @IBOutlet weak var messageLabel: UILabel! +} diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellGenerated.xib b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellGenerated.xib new file mode 100644 index 0000000000000000000000000000000000000000..84e845047a4bef715839a7d6d62f49acfc89e897 --- /dev/null +++ b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellGenerated.xib @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> + <device id="retina4_7" orientation="portrait"> + <adaptation id="fullscreen"/> + </device> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <objects> + <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> + <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> + <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="60" id="3QB-g7-MaS" customClass="MessageCellGenerated" customModule="Ring" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="510" height="47"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3QB-g7-MaS" id="Dkz-SA-3Af"> + <rect key="frame" x="0.0" y="0.0" width="510" height="46.5"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="xVQ-Jk-Sxy" customClass="MessageBubble" customModule="Ring" customModuleProvider="target"> + <rect key="frame" x="179" y="8" width="152.5" height="30.5"/> + <subviews> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label Label Label Label " lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ijf-jY-sqW"> + <rect key="frame" x="8" y="4" width="136.5" height="22.5"/> + <fontDescription key="fontDescription" type="system" pointSize="12"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> + <constraints> + <constraint firstAttribute="bottom" secondItem="ijf-jY-sqW" secondAttribute="bottom" constant="4" id="SxF-yH-rea"/> + <constraint firstItem="ijf-jY-sqW" firstAttribute="leading" secondItem="xVQ-Jk-Sxy" secondAttribute="leading" constant="8" id="Wqz-dB-u71"/> + <constraint firstItem="ijf-jY-sqW" firstAttribute="top" secondItem="xVQ-Jk-Sxy" secondAttribute="top" constant="4" id="bc9-iJ-EjK"/> + <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="30" id="hS0-Te-OEU"/> + <constraint firstAttribute="trailing" secondItem="ijf-jY-sqW" secondAttribute="trailing" constant="8" id="jne-iL-4tV"/> + </constraints> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius"> + <real key="value" value="4"/> + </userDefinedRuntimeAttribute> + </userDefinedRuntimeAttributes> + </view> + </subviews> + <color key="tintColor" name="controlColor" catalog="System" colorSpace="catalog"/> + <constraints> + <constraint firstItem="xVQ-Jk-Sxy" firstAttribute="centerX" secondItem="Dkz-SA-3Af" secondAttribute="centerX" id="2E6-jB-N9L"/> + <constraint firstAttribute="bottom" secondItem="xVQ-Jk-Sxy" secondAttribute="bottom" constant="8" id="Qbn-zO-KWj"/> + <constraint firstItem="xVQ-Jk-Sxy" firstAttribute="top" secondItem="Dkz-SA-3Af" secondAttribute="top" constant="8" id="R6Q-PY-A3m"/> + </constraints> + </tableViewCellContentView> + <connections> + <outlet property="bubble" destination="xVQ-Jk-Sxy" id="dRd-NH-FPh"/> + <outlet property="messageLabel" destination="ijf-jY-sqW" id="Wcu-8D-wWf"/> + </connections> + <point key="canvasLocation" x="-411" y="-132"/> + </tableViewCell> + </objects> +</document> diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift index 3af4ed4e78169a6e32b94b96ee774d58a9a737dc..bf31333fc01b7c8e0736a8ff256da30caac5c43f 100644 --- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift +++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift @@ -81,7 +81,8 @@ class ConversationViewController: UIViewController, UITextFieldDelegate, Storybo //invite button let inviteItem = UIBarButtonItem() inviteItem.image = UIImage(named: "add_person") - inviteItem.rx.tap.subscribe(onNext: { [unowned self] in + inviteItem.rx.tap.throttle(0.5, scheduler: MainScheduler.instance) + .subscribe(onNext: { [unowned self] in self.inviteItemTapped() }).disposed(by: self.disposeBag) @@ -118,6 +119,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate, Storybo //Register cell self.tableView.register(cellType: MessageCellSent.self) self.tableView.register(cellType: MessageCellReceived.self) + self.tableView.register(cellType: MessageCellGenerated.self) //Bind the TableView to the ViewModel self.viewModel.messages.subscribe(onNext: { [weak self] (messageViewModels) in @@ -203,6 +205,12 @@ extension ConversationViewController: UITableViewDataSource { return cell } + if messageViewModel.bubblePosition() == .generated { + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: MessageCellGenerated.self) + cell.messageLabel.text = messageViewModel.content + return cell + } + let cell = tableView.dequeueReusableCell(for: indexPath, cellType: MessageCellSent.self) cell.messageLabel.text = messageViewModel.content return cell diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift index 6fd3b91196b264ca3b8a4f269f94005cd865cb98..16b7bd75655ecdad5e9259a61bf284260fbe71a8 100644 --- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift +++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift @@ -2,6 +2,7 @@ * Copyright (C) 2017 Savoir-faire Linux Inc. * * Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com> + * Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -85,9 +86,16 @@ class ConversationViewModel: ViewModel { if let contact = contact { self.inviteButtonIsAvailable.onNext(!contact.confirmed) } - self.contactsService.contactStatus.subscribe(onNext: { contact in - self.inviteButtonIsAvailable.onNext(!contact.confirmed) - }).disposed(by: self.disposeBag) + self.contactsService.contactStatus.filter({ cont in + return cont.ringId == contact?.ringId + }) + .subscribe(onNext: { [unowned self] cont in + + self.inviteButtonIsAvailable.onNext(!cont.confirmed) + if cont.confirmed { + self.generateMessage(ofType: GeneratedMessageType.contactRequestAccepted) + } + }).disposed(by: self.disposeBag) // subscribe to presence updates for the conversation's associated contact self.presenceService @@ -216,13 +224,13 @@ class ConversationViewModel: ViewModel { to: self.conversation.recipientRingId) .subscribe(onCompleted: { [unowned self] in let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!) - self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: self.conversation.recipientRingId) + self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: self.conversation.recipientRingId, generated: false) }).disposed(by: self.disposeBag) } - fileprivate func saveMessage(withContent content: String, byAuthor author: String, toConversationWith account: String) { + fileprivate func saveMessage(withContent content: String, byAuthor author: String, toConversationWith account: String, generated: Bool) { self.conversationsService - .saveMessage(withContent: content, byAuthor: author, toConversationWith: account, currentAccountId: (accountService.currentAccount?.id)!) + .saveMessage(withContent: content, byAuthor: author, toConversationWith: account, currentAccountId: (accountService.currentAccount?.id)!, generated: generated) .subscribe(onCompleted: { [unowned self] in self.log.debug("Message saved") }) @@ -245,12 +253,45 @@ class ConversationViewModel: ViewModel { } func sendContactRequest() { + + let contactExists = self.contactsService.contact(withRingId: self.conversation.recipientRingId) != nil ? true : false self.accountService.loadVCard(forAccounr: self.accountService.currentAccount!) - .subscribe(onSuccess: { card in - self.contactsService.sendContactRequest(toContactRingId: self.conversation.recipientRingId, vCard: card, withAccount: self.accountService.currentAccount!).subscribe(onCompleted: { - self.log.info("contact request sent") - }).disposed(by: self.disposeBag) + .subscribe(onSuccess: { [unowned self] (card) in + self.contactsService.sendContactRequest(toContactRingId: self.conversation.recipientRingId, vCard: card, withAccount: self.accountService.currentAccount!) + .subscribe(onCompleted: { + if !contactExists { + self.generateMessage(ofType: GeneratedMessageType.sendContactRequest) + } + self.log.info("contact request sent") + }).disposed(by: self.disposeBag) + }, onError: { [unowned self] _ in + self.contactsService.sendContactRequest(toContactRingId: self.conversation.recipientRingId, vCard: nil, withAccount: self.accountService.currentAccount!) + .subscribe(onCompleted: { + if !contactExists { + self.generateMessage(ofType: GeneratedMessageType.sendContactRequest) + } + self.log.info("contact request sent") + }).disposed(by: self.disposeBag) + }).disposed(by: self.disposeBag) + } + func generateMessage(ofType messageType: GeneratedMessageType) { + if self.generatedMessageExists(ofType: messageType) { + return + } + + let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!) + self.saveMessage(withContent: + messageType.rawValue, byAuthor: accountHelper.ringId!, toConversationWith: self.conversation.recipientRingId, generated: true) + } + + func generatedMessageExists(ofType messageType: GeneratedMessageType) -> Bool { + for message in self.conversation.messages + where message.content == messageType.rawValue { + + return true + } + return false } } diff --git a/Ring/Ring/Features/Conversations/Conversation/MessageViewModel.swift b/Ring/Ring/Features/Conversations/Conversation/MessageViewModel.swift index 5843907308b8bb41005cee8c62472bd7de3aa253..b766246ef1b25a0e8202fb56764e6135969493a3 100644 --- a/Ring/Ring/Features/Conversations/Conversation/MessageViewModel.swift +++ b/Ring/Ring/Features/Conversations/Conversation/MessageViewModel.swift @@ -23,6 +23,13 @@ import RxSwift enum BubblePosition { case received case sent + case generated +} + +enum GeneratedMessageType: String { + case sendContactRequest = "The invitation has been sent" + case receivedContactRequest = "Contact request received" + case contactRequestAccepted = "ACCEPTED" } class MessageViewModel { @@ -41,13 +48,15 @@ class MessageViewModel { } func bubblePosition() -> BubblePosition { - + if self.message.isGenerated { + return .generated + } let accountHelper = AccountModelHelper(withAccount: accountService.currentAccount!) if self.message.author == accountHelper.ringId! { return .sent } else { - return .received + return.received } } } diff --git a/Ring/Ring/Models/MessageModel.swift b/Ring/Ring/Models/MessageModel.swift index bc1d91d184acfa39e4573775e41148540faef3db..1a2631b1ca87b6297dfd40ed834f9efcb139708e 100644 --- a/Ring/Ring/Models/MessageModel.swift +++ b/Ring/Ring/Models/MessageModel.swift @@ -27,6 +27,7 @@ class MessageModel: Object { dynamic var content: String = "" dynamic var author: String = "" dynamic var status: MessageStatus = .unknown + dynamic var isGenerated: Bool = false convenience init(withId id: Int64, receivedDate: Date, content: String, author: String) { self.init() @@ -35,5 +36,6 @@ class MessageModel: Object { self.content = content self.author = author self.status = .unknown + self.isGenerated = false } } diff --git a/Ring/Ring/Services/ContactsService.swift b/Ring/Ring/Services/ContactsService.swift index 3b31fb34ac5ea2c6c1423dc050e5e99262f7b844..5d39b5a3c64b1ed23442d79a353abad4353d1e74 100644 --- a/Ring/Ring/Services/ContactsService.swift +++ b/Ring/Ring/Services/ContactsService.swift @@ -70,7 +70,9 @@ class ContactsService { return ContactModel(withDictionary: contactDict) }) { for contact in contacts { - self.contacts.value.append(contact) + if self.contacts.value.index(of: contact) == nil { + self.contacts.value.append(contact) + } } } } @@ -212,16 +214,11 @@ extension ContactsService: ContactsAdapterDelegate { func contactAdded(contact uri: String, withAccountId accountId: String, confirmed: Bool) { //Update trust request list self.removeContactRequest(withRingId: uri) - - //if contact list is empty thats mean app just starts and we receive all contacts was added - if self.contacts.value.isEmpty { - return - } // update contact status if let contact = self.contact(withRingId: uri) { if contact.confirmed != confirmed { contact.confirmed = confirmed - contactStatus.onNext(contact) + self.contactStatus.onNext(contact) } } //sync contacts with daemon contacts diff --git a/Ring/Ring/Services/ConversationsService.swift b/Ring/Ring/Services/ConversationsService.swift index d8d177665f91cc847a9612b8c72ff8e497d11fa3..d4cc1a010f7954c8a0663481c10c28f5b5511d4a 100644 --- a/Ring/Ring/Services/ConversationsService.swift +++ b/Ring/Ring/Services/ConversationsService.swift @@ -40,6 +40,7 @@ class ConversationsService: MessagesAdapterDelegate { var conversations: Observable<Results<ConversationModel>> init(withMessageAdapter adapter: MessagesAdapter) { + guard let realm = try? Realm() else { fatalError("Enable to instantiate Realm") } @@ -50,6 +51,7 @@ class ConversationsService: MessagesAdapterDelegate { conversations = Observable.collection(from: results, synchronousStart: true) MessagesAdapter.delegate = self + } func sendMessage(withContent content: String, @@ -63,7 +65,11 @@ class ConversationsService: MessagesAdapterDelegate { let accountHelper = AccountModelHelper(withAccount: senderAccount) if accountHelper.ringId! != recipientRingId { - _ = self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: recipientRingId, currentAccountId: senderAccount.id) + _ = self.saveMessage(withContent: content, + byAuthor: accountHelper.ringId!, + toConversationWith: recipientRingId, + currentAccountId: senderAccount.id, + generated: false) } completable(.completed) @@ -90,10 +96,14 @@ class ConversationsService: MessagesAdapterDelegate { func saveMessage(withContent content: String, byAuthor author: String, toConversationWith recipientRingId: String, - currentAccountId: String) -> Completable { + currentAccountId: String, + generated: Bool?) -> Completable { return Completable.create(subscribe: { [unowned self] completable in let message = MessageModel(withId: 0, receivedDate: Date(), content: content, author: author) + if let generated = generated { + message.isGenerated = generated + } //Get conversations for this sender var currentConversation = self.results.filter({ conversation in @@ -185,7 +195,8 @@ class ConversationsService: MessagesAdapterDelegate { self.saveMessage(withContent: content, byAuthor: senderAccount, toConversationWith: senderAccount, - currentAccountId: receiverAccountId) + currentAccountId: receiverAccountId, + generated: false) .subscribe(onCompleted: { [unowned self] in self.log.info("Message saved") })