ConversationViewModel.swift 12.3 KB
Newer Older
1
2
3
4
/*
 *  Copyright (C) 2017 Savoir-faire Linux Inc.
 *
 *  Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
5
 *  Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 *  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 UIKit
import RxSwift
24
import RealmSwift
25
import SwiftyBeaver
26

27
class ConversationViewModel: ViewModel {
28

29
30
31
32
33
    /**
     logguer
     */
    private let log = SwiftyBeaver.self

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    //Services
    private let conversationsService: ConversationsService
    private let accountService: AccountsService
    private let nameService: NameService
    private let contactsService: ContactsService
    private let presenceService: PresenceService
    private let injectionBag: InjectionBag

    required init(with injectionBag: InjectionBag) {
        self.injectionBag = injectionBag
        self.accountService = injectionBag.accountService
        self.conversationsService = injectionBag.conversationsService
        self.nameService = injectionBag.nameService
        self.contactsService = injectionBag.contactsService
        self.presenceService = injectionBag.presenceService

        dateFormatter.dateStyle = .medium
        hourFormatter.dateFormat = "HH:mm"
    }

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    var conversation: ConversationModel! {
        didSet {
            //Create observable from sorted conversations and flatMap them to view models
            self.messages = self.conversationsService.conversations.map({ [unowned self] conversations in
                return conversations.filter({ conv in
                    let recipient1 = conv.recipientRingId
                    let recipient2 = self.conversation.recipientRingId
                    if recipient1 == recipient2 {
                        return true
                    }
                    return false
                }).flatMap({ conversation in
                    conversation.messages.map({ [unowned self] message in
                        return MessageViewModel(withInjectionBag: self.injectionBag, withMessage: message)
                    })
                })
            }).observeOn(MainScheduler.instance)

72
            let contactRingId = self.conversation.recipientRingId
73

74
75
76
77
78
79
80
81
82
83
84
85
86
            let contact = self.contactsService.contact(withRingId: contactRingId)

            self.contactsService.loadVCard(forContactWithRingId: contactRingId)
                .subscribe(onSuccess: { vCard in
                    guard let imageData = vCard.imageData else {
                        self.log.warning("vCard for ringId: \(contactRingId) has no image")
                        return
                    }
                    self.profileImageData = imageData
                })
                .disposed(by: self.disposeBag)

            if let contact = contact {
87
88
                self.inviteButtonIsAvailable.onNext(!contact.confirmed)
            }
89
            self.contactsService.contactStatus.filter({ cont in
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
90
                return cont.ringId == contactRingId
91
92
93
94
95
96
97
98
            })
                .subscribe(onNext: { [unowned self] cont in

                    self.inviteButtonIsAvailable.onNext(!cont.confirmed)
                    if cont.confirmed {
                        self.generateMessage(ofType: GeneratedMessageType.contactRequestAccepted)
                    }
                }).disposed(by: self.disposeBag)
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
            // subscribe to presence updates for the conversation's associated contact
            self.presenceService
                .sharedResponseStream
                .filter({ presenceUpdateEvent in
                    return presenceUpdateEvent.eventType == ServiceEventType.presenceUpdated
                        && presenceUpdateEvent.getEventInput(.uri) == contact?.ringId
                })
                .subscribe(onNext: { [unowned self] presenceUpdateEvent in
                    if let uri: String = presenceUpdateEvent.getEventInput(.uri) {
                        self.contactPresence.onNext(self.presenceService.contactPresence[uri]!)
                    }
                })
                .disposed(by: disposeBag)

114
115
116
117
118
119
            if let contactUserName = contact?.userName {
                self.userName.onNext(contactUserName)
            } else {

                let recipientRingId = self.conversation.recipientRingId

120
                // Return an observer for the username lookup
121
122
123
124
125
                self.nameService.usernameLookupStatus
                    .filter({ lookupNameResponse in
                        return lookupNameResponse.address != nil &&
                            lookupNameResponse.address == recipientRingId
                    }).subscribe(onNext: { [unowned self] lookupNameResponse in
126
                        if let name = lookupNameResponse.name, !name.isEmpty {
127
128
129
130
131
132
133
134
135
136
137
138
                            self.userName.onNext(name)
                            contact?.userName = name
                        } else if let address = lookupNameResponse.address {
                            self.userName.onNext(address)
                        }
                    }).disposed(by: disposeBag)

                self.nameService.lookupAddress(withAccount: "", nameserver: "", address: self.conversation.recipientRingId)
            }
        }
    }

139
140
141
142
143
144
145
    private lazy var realm: Realm = {
        guard let realm = try? Realm() else {
            fatalError("Enable to instantiate Realm")
        }

        return realm
    }()
146
147
148
149
150
151
152
153
154

    //Displays the entire date ( for messages received before the current week )
    private let dateFormatter = DateFormatter()

    //Displays the hour of the message reception ( for messages received today )
    private let hourFormatter = DateFormatter()

    private let disposeBag = DisposeBag()

155
    var messages: Observable<[MessageViewModel]>!
156

157
    var userName = BehaviorSubject(value: "")
158

159
160
    var profileImageData: Data?

161
    var inviteButtonIsAvailable = BehaviorSubject(value: true)
162

163
    var contactPresence = BehaviorSubject(value: false)
164

165
166
167
168
169
    var unreadMessages: String {
       return self.unreadMessagesCount.description
    }

    var hasUnreadMessages: Bool {
170
        return unreadMessagesCount > 0
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
    }

    var lastMessage: String {
        if let lastMessage = conversation.messages.last?.content {
            return lastMessage
        } else {
            return ""
        }
    }

    var lastMessageReceivedDate: String {

        guard let lastMessageDate = self.conversation.messages.last?.receivedDate else {
            return ""
        }

        let dateToday = Date()

        //Get components from today date
        let todayWeekOfYear = Calendar.current.component(.weekOfYear, from: dateToday)
        let todayDay = Calendar.current.component(.day, from: dateToday)
        let todayMonth = Calendar.current.component(.month, from: dateToday)
        let todayYear = Calendar.current.component(.year, from: dateToday)

        //Get components from last message date
        let weekOfYear = Calendar.current.component(.weekOfYear, from: lastMessageDate)
        let day = Calendar.current.component(.day, from: lastMessageDate)
        let month = Calendar.current.component(.month, from: lastMessageDate)
        let year = Calendar.current.component(.year, from: lastMessageDate)

        if todayDay == day && todayMonth == month && todayYear == year {
            return hourFormatter.string(from: lastMessageDate)
        } else if day == todayDay - 1 {
204
            return L10n.Smartlist.yesterday
205
206
207
208
209
210
        } else if todayYear == year && todayWeekOfYear == weekOfYear {
            return lastMessageDate.dayOfWeek()
        } else {
            return dateFormatter.string(from: lastMessageDate)
        }
    }
211
212
213
214
215

    var hideNewMessagesLabel: Bool {
        return self.unreadMessagesCount == 0
    }

216
    var hideDate: Bool {
217
        return self.conversation.messages.isEmpty
218
219
    }

220
221
222
223
    func sendMessage(withContent content: String) {
        self.conversationsService
            .sendMessage(withContent: content,
                         from: accountService.currentAccount!,
224
225
                         to: self.conversation.recipientRingId)
            .subscribe(onCompleted: { [unowned self] in
226
                self.log.debug("Message sent")
227
            }).disposed(by: self.disposeBag)
228
229
    }

230
231
232
233
234
    fileprivate func saveMessage(withId messageId: String,
                                 withContent content: String,
                                 byAuthor author: String,
                                 toConversationWith account: String,
                                 generated: Bool) {
235
        self.conversationsService
236
237
238
239
240
            .saveMessage(withId: messageId,
                         withContent: content,
                         byAuthor: author,
                         toConversationWith: account,
                         currentAccountId: (accountService.currentAccount?.id)!, generated: generated)
241
242
            .subscribe(onCompleted: { [unowned self] in
                self.log.debug("Message saved")
243
            })
244
            .disposed(by: disposeBag)
245
246
247
248
249
    }

    func setMessagesAsRead() {
        self.conversationsService
            .setMessagesAsRead(forConversation: self.conversation)
250
251
            .subscribe(onCompleted: { [unowned self] in
                self.log.debug("Message set as read")
252
            }).disposed(by: disposeBag)
253
254
255
256
257
258
259
260
    }

    fileprivate var unreadMessagesCount: Int {
        let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!)
        return self.conversation.messages.filter({ message in
            return message.status != .read && message.author != accountHelper.ringId!
        }).count
    }
261
262

    func sendContactRequest() {
263
        let contactExists =  self.contactsService.contact(withRingId: self.conversation.recipientRingId) != nil ? true : false
264
        VCardUtils.loadVCard(named: VCardFiles.myProfile.rawValue, inFolder: VCardFolders.profile.rawValue)
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
            .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)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
282
            }).disposed(by: self.disposeBag)
283
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
284

285
286
287
288
289
290
    func generateMessage(ofType messageType: GeneratedMessageType) {
        if self.generatedMessageExists(ofType: messageType) {
            return
        }

        let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!)
291
292
293
294
295
        self.saveMessage(withId: "",
                         withContent: messageType.rawValue,
                         byAuthor: accountHelper.ringId!,
                         toConversationWith: self.conversation.recipientRingId,
                         generated: true)
296
297
298
299
300
301
302
303
304
    }

    func generatedMessageExists(ofType messageType: GeneratedMessageType) -> Bool {
        for message in self.conversation.messages
            where message.content == messageType.rawValue {

                return true
        }
        return false
305
    }
306
}