Merge lp:~phablet-team/history-service/trace_sqlite into lp:history-service
- trace_sqlite
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~phablet-team/history-service/trace_sqlite |
Merge into: | lp:history-service |
Diff against target: |
3373 lines (+1280/-447) 49 files modified
CMakeLists.txt (+5/-0) Ubuntu/History/historyeventmodel.cpp (+39/-42) Ubuntu/History/historyeventmodel.h (+2/-5) Ubuntu/History/historygroupedthreadsmodel.cpp (+54/-2) Ubuntu/History/historygroupedthreadsmodel.h (+5/-0) Ubuntu/History/historymodel.cpp (+108/-10) Ubuntu/History/historymodel.h (+11/-0) Ubuntu/History/historythreadmodel.cpp (+48/-9) Ubuntu/History/historythreadmodel.h (+2/-0) cmake/modules/GenerateTest.cmake (+1/-0) daemon/HistoryService.xml (+29/-0) daemon/callchannelobserver.cpp (+16/-2) daemon/callchannelobserver.h (+2/-1) daemon/historydaemon.cpp (+323/-186) daemon/historydaemon.h (+21/-10) daemon/historyservicedbus.cpp (+111/-8) daemon/historyservicedbus.h (+25/-0) daemon/main.cpp (+15/-5) daemon/textchannelobserver.cpp (+1/-14) daemon/textchannelobserver.h (+1/-2) plugins/sqlite/schema/v18.sql (+14/-0) plugins/sqlite/sqlitedatabase.cpp (+10/-0) plugins/sqlite/sqlitehistoryeventview.cpp (+8/-1) plugins/sqlite/sqlitehistoryplugin.cpp (+136/-53) plugins/sqlite/sqlitehistoryplugin.h (+6/-1) plugins/sqlite/sqlitehistorythreadview.cpp (+8/-1) src/contactmatcher.cpp (+57/-22) src/contactmatcher_p.h (+2/-0) src/eventview.cpp (+6/-2) src/eventview.h (+3/-1) src/manager.cpp (+24/-0) src/manager.h (+4/-1) src/managerdbus.cpp (+55/-10) src/managerdbus_p.h (+10/-2) src/participant.cpp (+9/-0) src/participant.h (+1/-0) src/plugin.h (+6/-0) src/thread.cpp (+16/-0) src/thread.h (+2/-0) src/threadview.cpp (+15/-0) src/threadview.h (+8/-0) src/threadview_p.h (+4/-0) src/utils.cpp (+16/-1) src/utils_p.h (+2/-0) tests/Ubuntu.History/HistoryEventModelTest.cpp (+1/-1) tests/daemon/DaemonTest.cpp (+0/-8) tests/libhistoryservice/ManagerTest.cpp (+16/-43) tests/plugins/sqlite/SqliteEventViewTest.cpp (+22/-2) tests/plugins/sqlite/SqlitePluginTest.cpp (+0/-2) |
To merge this branch: | bzr merge lp:~phablet-team/history-service/trace_sqlite |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
system-apps-ci-bot | continuous-integration | Approve | |
PS Jenkins bot | continuous-integration | Pending | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+297733@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-03-21.
Commit message
Make it possible to debug sqlite commands.
Description of the change
Make it possible to debug sqlite commands.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
- 229. By Gustavo Pichorim Boiko
-
Rebase this branch using the VOIP and IRC changes.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:229
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unmerged revisions
- 229. By Gustavo Pichorim Boiko
-
Rebase this branch using the VOIP and IRC changes.
- 228. By Gustavo Pichorim Boiko
-
Add option sqlite command tracing
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-08-16 21:06:10 +0000 |
3 | +++ CMakeLists.txt 2017-03-21 14:18:04 +0000 |
4 | @@ -50,6 +50,11 @@ |
5 | |
6 | find_program(DBUS_RUNNER dbus-test-runner) |
7 | |
8 | +option(TRACE_SQLITE "Print Sqlite commants to the log." off) |
9 | +if (${TRACE_SQLITE}) |
10 | + add_definitions(-DTRACE_SQLITE) |
11 | +endif() |
12 | + |
13 | add_definitions(-DQT_NO_KEYWORDS) |
14 | |
15 | include_directories( |
16 | |
17 | === modified file 'Ubuntu/History/historyeventmodel.cpp' |
18 | --- Ubuntu/History/historyeventmodel.cpp 2016-09-16 11:59:52 +0000 |
19 | +++ Ubuntu/History/historyeventmodel.cpp 2017-03-21 14:18:04 +0000 |
20 | @@ -1,5 +1,5 @@ |
21 | /* |
22 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
23 | + * Copyright (C) 2013-2017 Canonical, Ltd. |
24 | * |
25 | * Authors: |
26 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
27 | @@ -29,7 +29,7 @@ |
28 | #include <QTimerEvent> |
29 | |
30 | HistoryEventModel::HistoryEventModel(QObject *parent) : |
31 | - HistoryModel(parent), mCanFetchMore(true), mEventWritingTimer(0) |
32 | + HistoryModel(parent), mCanFetchMore(true) |
33 | { |
34 | // configure the roles |
35 | mRoles = HistoryModel::roleNames(); |
36 | @@ -95,10 +95,17 @@ |
37 | result = event.eventId(); |
38 | break; |
39 | case SenderIdRole: |
40 | - result = event.senderId(); |
41 | + result = History::ContactMatcher::normalizeId(event.senderId()); |
42 | break; |
43 | case SenderRole: |
44 | - result = History::ContactMatcher::instance()->contactInfo(event.accountId(), event.senderId()); |
45 | + if (mMatchContacts) { |
46 | + result = History::ContactMatcher::instance()->contactInfo(event.accountId(), event.senderId()); |
47 | + } else { |
48 | + QVariantMap map; |
49 | + map[History::FieldIdentifier] = event.senderId(); |
50 | + map[History::FieldAccountId] = event.accountId(); |
51 | + result = map; |
52 | + } |
53 | break; |
54 | case TimestampRole: |
55 | result = event.timestamp(); |
56 | @@ -168,17 +175,21 @@ |
57 | break; |
58 | case RemoteParticipantRole: |
59 | if (!voiceEvent.isNull()) { |
60 | - result = voiceEvent.remoteParticipant(); |
61 | + result = History::ContactMatcher::normalizeId(voiceEvent.remoteParticipant()); |
62 | } |
63 | break; |
64 | case SubjectAsAliasRole: |
65 | if (!textEvent.isNull()) { |
66 | - QVariantMap contactInfo = History::ContactMatcher::instance()->contactInfo(event.accountId(), textEvent.subject()); |
67 | - QString returnValue = contactInfo[History::FieldAlias].toString(); |
68 | - if (returnValue.isEmpty()) { |
69 | - returnValue = contactInfo[History::FieldIdentifier].toString(); |
70 | + if (mMatchContacts) { |
71 | + QVariantMap contactInfo = History::ContactMatcher::instance()->contactInfo(event.accountId(), textEvent.subject()); |
72 | + QString returnValue = contactInfo[History::FieldAlias].toString(); |
73 | + if (returnValue.isEmpty()) { |
74 | + returnValue = contactInfo[History::FieldIdentifier].toString(); |
75 | + } |
76 | + return returnValue; |
77 | + |
78 | } |
79 | - return returnValue; |
80 | + return textEvent.subject(); |
81 | } |
82 | break; |
83 | } |
84 | @@ -307,23 +318,6 @@ |
85 | return History::Manager::instance()->writeEvents(History::Events() << textEvent); |
86 | } |
87 | |
88 | -bool HistoryEventModel::markEventAsRead(const QString &accountId, const QString &threadId, const QString &eventId, int eventType) |
89 | -{ |
90 | - History::Event event = History::Manager::instance()->getSingleEvent((History::EventType)eventType, accountId, threadId, eventId); |
91 | - event.setNewEvent(false); |
92 | - if (event.type() == History::EventTypeText) { |
93 | - History::TextEvent textEvent = event; |
94 | - textEvent.setReadTimestamp(QDateTime::currentDateTime()); |
95 | - event = textEvent; |
96 | - } |
97 | - mEventWritingQueue << event; |
98 | - if (mEventWritingTimer != 0) { |
99 | - killTimer(mEventWritingTimer); |
100 | - } |
101 | - mEventWritingTimer = startTimer(500); |
102 | - return true; |
103 | -} |
104 | - |
105 | void HistoryEventModel::updateQuery() |
106 | { |
107 | // remove all events from the model |
108 | @@ -341,7 +335,7 @@ |
109 | mView->disconnect(this); |
110 | } |
111 | |
112 | - if (mFilter) { |
113 | + if (mFilter && mFilter->filter().isValid()) { |
114 | queryFilter = mFilter->filter(); |
115 | } else { |
116 | // we should not return anything if there is no filter |
117 | @@ -363,6 +357,9 @@ |
118 | SIGNAL(eventsRemoved(History::Events)), |
119 | SLOT(onEventsRemoved(History::Events))); |
120 | connect(mView.data(), |
121 | + SIGNAL(threadsRemoved(History::Threads)), |
122 | + SLOT(onThreadsRemoved(History::Threads))); |
123 | + connect(mView.data(), |
124 | SIGNAL(invalidated()), |
125 | SLOT(triggerQueryUpdate())); |
126 | |
127 | @@ -439,21 +436,21 @@ |
128 | // should be handle internally in History::EventView? |
129 | } |
130 | |
131 | -void HistoryEventModel::timerEvent(QTimerEvent *event) |
132 | +void HistoryEventModel::onThreadsRemoved(const History::Threads &threads) |
133 | { |
134 | - HistoryModel::timerEvent(event); |
135 | - if (event->timerId() == mEventWritingTimer) { |
136 | - killTimer(mEventWritingTimer); |
137 | - mEventWritingTimer = 0; |
138 | - |
139 | - if (mEventWritingQueue.isEmpty()) { |
140 | - return; |
141 | - } |
142 | - |
143 | - qDebug() << "Goint to update" << mEventWritingQueue.count() << "events."; |
144 | - if (History::Manager::instance()->writeEvents(mEventWritingQueue)) { |
145 | - qDebug() << "... succeeded!"; |
146 | - mEventWritingQueue.clear(); |
147 | + // When a thread is removed we don't get event removed signals, |
148 | + // so we compare and find if we have an event matching that thread. |
149 | + // in case we find it, we invalidate the whole view as there might be |
150 | + // out of date cached data on the daemon side |
151 | + int count = rowCount(); |
152 | + Q_FOREACH(const History::Thread &thread, threads) { |
153 | + for (int i = 0; i < count; ++i) { |
154 | + QModelIndex idx = index(i); |
155 | + if (idx.data(AccountIdRole).toString() == thread.accountId() && |
156 | + idx.data(ThreadIdRole).toString() == thread.threadId()) { |
157 | + triggerQueryUpdate(); |
158 | + return; |
159 | + } |
160 | } |
161 | } |
162 | } |
163 | |
164 | === modified file 'Ubuntu/History/historyeventmodel.h' |
165 | --- Ubuntu/History/historyeventmodel.h 2016-09-16 12:32:37 +0000 |
166 | +++ Ubuntu/History/historyeventmodel.h 2017-03-21 14:18:04 +0000 |
167 | @@ -1,5 +1,5 @@ |
168 | /* |
169 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
170 | + * Copyright (C) 2013-2017 Canonical, Ltd. |
171 | * |
172 | * Authors: |
173 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
174 | @@ -66,7 +66,6 @@ |
175 | |
176 | Q_INVOKABLE bool removeEvents(const QVariantList &eventsProperties); |
177 | Q_INVOKABLE bool writeEvents(const QVariantList &eventsProperties); |
178 | - Q_INVOKABLE bool markEventAsRead(const QString &accountId, const QString &threadId, const QString &eventId, int eventType); |
179 | Q_INVOKABLE bool removeEventAttachment(const QString &accountId, const QString &threadId, const QString &eventId, int eventType, const QString &attachmentId); |
180 | |
181 | protected Q_SLOTS: |
182 | @@ -74,9 +73,9 @@ |
183 | virtual void onEventsAdded(const History::Events &events); |
184 | virtual void onEventsModified(const History::Events &events); |
185 | virtual void onEventsRemoved(const History::Events &events); |
186 | + virtual void onThreadsRemoved(const History::Threads &threads); |
187 | |
188 | protected: |
189 | - void timerEvent(QTimerEvent *event); |
190 | History::Events fetchNextPage(); |
191 | |
192 | private: |
193 | @@ -85,8 +84,6 @@ |
194 | bool mCanFetchMore; |
195 | QHash<int, QByteArray> mRoles; |
196 | mutable QMap<History::TextEvent, QList<QVariant> > mAttachmentCache; |
197 | - History::Events mEventWritingQueue; |
198 | - int mEventWritingTimer; |
199 | }; |
200 | |
201 | #endif // HISTORYEVENTMODEL_H |
202 | |
203 | === modified file 'Ubuntu/History/historygroupedthreadsmodel.cpp' |
204 | --- Ubuntu/History/historygroupedthreadsmodel.cpp 2015-10-08 19:35:40 +0000 |
205 | +++ Ubuntu/History/historygroupedthreadsmodel.cpp 2017-03-21 14:18:04 +0000 |
206 | @@ -199,6 +199,21 @@ |
207 | } |
208 | } |
209 | |
210 | +History::Threads HistoryGroupedThreadsModel::restoreParticipants(const History::Threads &oldThreads, const History::Threads &newThreads) |
211 | +{ |
212 | + History::Threads updated = newThreads; |
213 | + for(History::Thread &thread : updated) { |
214 | + if (!thread.participants().isEmpty()) { |
215 | + continue; |
216 | + } |
217 | + int i = oldThreads.indexOf(thread); |
218 | + if (i >=0) { |
219 | + thread.addParticipants(oldThreads[i].participants()); |
220 | + } |
221 | + } |
222 | + return updated; |
223 | +} |
224 | + |
225 | void HistoryGroupedThreadsModel::updateQuery() |
226 | { |
227 | // remove all entries and call the query update |
228 | @@ -217,6 +232,7 @@ |
229 | processThreadGrouping(thread); |
230 | } |
231 | |
232 | + fetchParticipantsIfNeeded(threads); |
233 | notifyDataChanged(); |
234 | } |
235 | |
236 | @@ -225,7 +241,7 @@ |
237 | Q_FOREACH(const History::Thread &thread, threads) { |
238 | processThreadGrouping(thread); |
239 | } |
240 | - |
241 | + fetchParticipantsIfNeeded(threads); |
242 | notifyDataChanged(); |
243 | } |
244 | |
245 | @@ -238,6 +254,42 @@ |
246 | notifyDataChanged(); |
247 | } |
248 | |
249 | +void HistoryGroupedThreadsModel::onThreadParticipantsChanged(const History::Thread &thread, const History::Participants &added, const History::Participants &removed, const History::Participants &modified) |
250 | +{ |
251 | + int pos = existingPositionForEntry(thread); |
252 | + if (pos >= 0) { |
253 | + HistoryThreadGroup &group = mGroups[pos]; |
254 | + if (group.displayedThread == thread) { |
255 | + group.displayedThread.removeParticipants(removed); |
256 | + group.displayedThread.removeParticipants(modified); |
257 | + group.displayedThread.addParticipants(added); |
258 | + group.displayedThread.addParticipants(modified); |
259 | + } |
260 | + |
261 | + Q_FOREACH(const History::Thread &existingThread, group.threads) { |
262 | + if (existingThread == thread) { |
263 | + History::Thread modifiedThread = existingThread; |
264 | + group.threads.removeOne(existingThread); |
265 | + modifiedThread.removeParticipants(removed); |
266 | + modifiedThread.removeParticipants(modified); |
267 | + modifiedThread.addParticipants(added); |
268 | + modifiedThread.addParticipants(modified); |
269 | + group.threads.append(modifiedThread); |
270 | + } |
271 | + } |
272 | + QModelIndex idx = index(pos); |
273 | + Q_EMIT dataChanged(idx, idx); |
274 | + } |
275 | + |
276 | + // watch the contact info for the received participants |
277 | + Q_FOREACH(const History::Participant &participant, added) { |
278 | + watchContactInfo(thread.accountId(), participant.identifier(), participant.properties()); |
279 | + } |
280 | + Q_FOREACH(const History::Participant &participant, modified) { |
281 | + watchContactInfo(thread.accountId(), participant.identifier(), participant.properties()); |
282 | + } |
283 | +} |
284 | + |
285 | void HistoryGroupedThreadsModel::processThreadGrouping(const History::Thread &thread) |
286 | { |
287 | QVariantMap queryProperties; |
288 | @@ -262,7 +314,7 @@ |
289 | } |
290 | |
291 | HistoryThreadGroup &group = mGroups[pos]; |
292 | - group.threads = groupedThread.groupedThreads(); |
293 | + group.threads = restoreParticipants(group.threads, groupedThread.groupedThreads()); |
294 | |
295 | updateDisplayedThread(group); |
296 | markGroupAsChanged(group); |
297 | |
298 | === modified file 'Ubuntu/History/historygroupedthreadsmodel.h' |
299 | --- Ubuntu/History/historygroupedthreadsmodel.h 2015-09-29 20:34:22 +0000 |
300 | +++ Ubuntu/History/historygroupedthreadsmodel.h 2017-03-21 14:18:04 +0000 |
301 | @@ -67,12 +67,17 @@ |
302 | int existingPositionForEntry(const History::Thread &thread) const; |
303 | void removeGroup(const HistoryThreadGroup &group); |
304 | void updateDisplayedThread(HistoryThreadGroup &group); |
305 | + History::Threads restoreParticipants(const History::Threads &oldThreads, const History::Threads &newThreads); |
306 | |
307 | protected Q_SLOTS: |
308 | virtual void updateQuery(); |
309 | virtual void onThreadsAdded(const History::Threads &threads); |
310 | virtual void onThreadsModified(const History::Threads &threads); |
311 | virtual void onThreadsRemoved(const History::Threads &threads); |
312 | + void onThreadParticipantsChanged(const History::Thread &thread, |
313 | + const History::Participants &added, |
314 | + const History::Participants &removed, |
315 | + const History::Participants &modified) override; |
316 | |
317 | private Q_SLOTS: |
318 | void processThreadGrouping(const History::Thread &thread); |
319 | |
320 | === modified file 'Ubuntu/History/historymodel.cpp' |
321 | --- Ubuntu/History/historymodel.cpp 2016-11-24 12:22:11 +0000 |
322 | +++ Ubuntu/History/historymodel.cpp 2017-03-21 14:18:04 +0000 |
323 | @@ -28,13 +28,14 @@ |
324 | #include "textevent.h" |
325 | #include "manager.h" |
326 | #include "utils_p.h" |
327 | +#include "voiceevent.h" |
328 | #include <QTimerEvent> |
329 | #include <QCryptographicHash> |
330 | #include <QDebug> |
331 | |
332 | HistoryModel::HistoryModel(QObject *parent) : |
333 | QAbstractListModel(parent), mFilter(0), mSort(new HistoryQmlSort(this)), |
334 | - mType(EventTypeText), mMatchContacts(false), mUpdateTimer(0), mWaitingForQml(false) |
335 | + mType(EventTypeText), mMatchContacts(false), mUpdateTimer(0), mEventWritingTimer(0), mThreadWritingTimer(0), mWaitingForQml(false) |
336 | { |
337 | // configure the roles |
338 | mRoles[AccountIdRole] = "accountId"; |
339 | @@ -341,6 +342,20 @@ |
340 | return QString::null; |
341 | } |
342 | |
343 | +void HistoryModel::requestThreadParticipants(const QVariantList &threads) |
344 | +{ |
345 | + History::Threads theThreads; |
346 | + Q_FOREACH(const QVariant &threadVariant, threads) { |
347 | + History::Thread theThread = History::Thread::fromProperties(threadVariant.toMap()); |
348 | + // if the given thread already has the list of participants, there is no point |
349 | + // in fetching it again |
350 | + if (theThread.participants().isEmpty()) { |
351 | + theThreads << theThread; |
352 | + } |
353 | + } |
354 | + History::Manager::instance()->requestThreadParticipants(theThreads); |
355 | +} |
356 | + |
357 | bool HistoryModel::writeTextInformationEvent(const QString &accountId, const QString &threadId, const QStringList &participants, const QString &message, int informationType, const QString &subject) |
358 | { |
359 | if (participants.isEmpty() || threadId.isEmpty() || accountId.isEmpty()) { |
360 | @@ -385,7 +400,7 @@ |
361 | // FIXME: right now we might be grouping threads from different accounts, so we are not enforcing |
362 | // the accountId to be the same as the one from the contact info, but maybe we need to do that |
363 | // in the future? |
364 | - if (History::Utils::compareIds(accountId, participant.identifier(), identifier)) { |
365 | + if (History::Utils::compareIds(accountId, History::ContactMatcher::normalizeId(participant.identifier()), identifier)) { |
366 | changedIndexes << idx; |
367 | } |
368 | } |
369 | @@ -406,19 +421,52 @@ |
370 | |
371 | void HistoryModel::timerEvent(QTimerEvent *event) |
372 | { |
373 | - if (event->timerId() == mUpdateTimer && !mWaitingForQml) { |
374 | - killTimer(mUpdateTimer); |
375 | - mUpdateTimer = 0; |
376 | - updateQuery(); |
377 | + if (event->timerId() == mUpdateTimer) { |
378 | + if (!mWaitingForQml) { |
379 | + killTimer(mUpdateTimer); |
380 | + mUpdateTimer = 0; |
381 | + updateQuery(); |
382 | + } |
383 | + } else if (event->timerId() == mEventWritingTimer) { |
384 | + killTimer(mEventWritingTimer); |
385 | + mEventWritingTimer = 0; |
386 | + |
387 | + if (mEventWritingQueue.isEmpty()) { |
388 | + return; |
389 | + } |
390 | + |
391 | + qDebug() << "Goint to update" << mEventWritingQueue.count() << "events."; |
392 | + if (History::Manager::instance()->writeEvents(mEventWritingQueue)) { |
393 | + qDebug() << "... succeeded!"; |
394 | + mEventWritingQueue.clear(); |
395 | + } |
396 | + } else if (event->timerId() == mThreadWritingTimer) { |
397 | + killTimer(mThreadWritingTimer); |
398 | + mThreadWritingTimer = 0; |
399 | + |
400 | + if (mThreadWritingQueue.isEmpty()) { |
401 | + return; |
402 | + } |
403 | + |
404 | + History::Manager::instance()->markThreadsAsRead(mThreadWritingQueue); |
405 | + mThreadWritingQueue.clear(); |
406 | } |
407 | } |
408 | |
409 | bool HistoryModel::lessThan(const QVariantMap &left, const QVariantMap &right) const |
410 | { |
411 | - QVariant leftValue = left[sort()->sortField()]; |
412 | - QVariant rightValue = right[sort()->sortField()]; |
413 | - |
414 | - return leftValue < rightValue; |
415 | + QStringList leftFields = sort()->sortField().split(","); |
416 | + QStringList rightFields = sort()->sortField().split(","); |
417 | + |
418 | + while(!leftFields.isEmpty()) { |
419 | + QVariant leftValue = left[leftFields.takeFirst().trimmed()]; |
420 | + QVariant rightValue = right[rightFields.takeFirst().trimmed()]; |
421 | + |
422 | + if (leftValue != rightValue) { |
423 | + return leftValue < rightValue; |
424 | + } |
425 | + } |
426 | + return false; |
427 | } |
428 | |
429 | int HistoryModel::positionForItem(const QVariantMap &item) const |
430 | @@ -470,6 +518,56 @@ |
431 | return data; |
432 | } |
433 | |
434 | +bool HistoryModel::markEventAsRead(const QVariantMap &eventProperties) |
435 | +{ |
436 | + History::Event event; |
437 | + History::EventType type = (History::EventType) eventProperties[History::FieldType].toInt(); |
438 | + switch (type) { |
439 | + case History::EventTypeText: |
440 | + event = History::TextEvent::fromProperties(eventProperties); |
441 | + break; |
442 | + case History::EventTypeVoice: |
443 | + event = History::VoiceEvent::fromProperties(eventProperties); |
444 | + break; |
445 | + } |
446 | + |
447 | + event.setNewEvent(false); |
448 | + if (event.type() == History::EventTypeText) { |
449 | + History::TextEvent textEvent = event; |
450 | + textEvent.setReadTimestamp(QDateTime::currentDateTime()); |
451 | + event = textEvent; |
452 | + } |
453 | + // for repeated events, keep the last called one only |
454 | + if (mEventWritingQueue.contains(event)) { |
455 | + mEventWritingQueue.removeOne(event); |
456 | + } |
457 | + mEventWritingQueue << event; |
458 | + if (mEventWritingTimer != 0) { |
459 | + killTimer(mEventWritingTimer); |
460 | + } |
461 | + mEventWritingTimer = startTimer(500); |
462 | + return true; |
463 | +} |
464 | + |
465 | +void HistoryModel::markThreadsAsRead(const QVariantList &threadsProperties) |
466 | +{ |
467 | + Q_FOREACH(const QVariant &entry, threadsProperties) { |
468 | + QVariantMap threadProperties = entry.toMap(); |
469 | + History::Thread thread = History::Thread::fromProperties(threadProperties); |
470 | + if (!thread.isNull()) { |
471 | + if (mThreadWritingQueue.contains(thread)) { |
472 | + continue; |
473 | + } |
474 | + mThreadWritingQueue << thread; |
475 | + } |
476 | + } |
477 | + |
478 | + if (mThreadWritingTimer != 0) { |
479 | + killTimer(mThreadWritingTimer); |
480 | + } |
481 | + mThreadWritingTimer = startTimer(2000); |
482 | +} |
483 | + |
484 | void HistoryModel::classBegin() |
485 | { |
486 | mWaitingForQml = true; |
487 | |
488 | === modified file 'Ubuntu/History/historymodel.h' |
489 | --- Ubuntu/History/historymodel.h 2016-11-09 17:42:27 +0000 |
490 | +++ Ubuntu/History/historymodel.h 2017-03-21 14:18:04 +0000 |
491 | @@ -23,6 +23,8 @@ |
492 | #define HISTORYMODEL_H |
493 | |
494 | #include "types.h" |
495 | +#include "event.h" |
496 | +#include "thread.h" |
497 | #include "historyqmlfilter.h" |
498 | #include "historyqmlsort.h" |
499 | #include <QAbstractListModel> |
500 | @@ -166,6 +168,7 @@ |
501 | const QStringList &participants, |
502 | int matchFlags = (int)History::MatchCaseSensitive, |
503 | bool create = false); |
504 | + Q_INVOKABLE void requestThreadParticipants(const QVariantList &threads); |
505 | Q_INVOKABLE bool writeTextInformationEvent(const QString &accountId, |
506 | const QString &threadId, |
507 | const QStringList &participants, |
508 | @@ -175,6 +178,10 @@ |
509 | |
510 | Q_INVOKABLE virtual QVariant get(int row) const; |
511 | |
512 | + // Marking events and threads as read |
513 | + Q_INVOKABLE bool markEventAsRead(const QVariantMap &eventProperties); |
514 | + Q_INVOKABLE void markThreadsAsRead(const QVariantList &threadsProperties); |
515 | + |
516 | // QML parser status things |
517 | void classBegin(); |
518 | void componentComplete(); |
519 | @@ -206,6 +213,10 @@ |
520 | |
521 | private: |
522 | QHash<int, QByteArray> mRoles; |
523 | + History::Events mEventWritingQueue; |
524 | + int mEventWritingTimer; |
525 | + History::Threads mThreadWritingQueue; |
526 | + int mThreadWritingTimer; |
527 | int mUpdateTimer; |
528 | bool mWaitingForQml; |
529 | }; |
530 | |
531 | === modified file 'Ubuntu/History/historythreadmodel.cpp' |
532 | --- Ubuntu/History/historythreadmodel.cpp 2016-06-17 01:49:46 +0000 |
533 | +++ Ubuntu/History/historythreadmodel.cpp 2017-03-21 14:18:04 +0000 |
534 | @@ -26,6 +26,8 @@ |
535 | #include "voiceevent.h" |
536 | #include <QDBusMetaType> |
537 | |
538 | +#include <QDebug> |
539 | + |
540 | Q_DECLARE_METATYPE(History::TextEventAttachments) |
541 | Q_DECLARE_METATYPE(QList<QVariantMap>) |
542 | |
543 | @@ -190,7 +192,6 @@ |
544 | } |
545 | break; |
546 | } |
547 | - |
548 | return result; |
549 | } |
550 | |
551 | @@ -214,13 +215,6 @@ |
552 | mCanFetchMore = false; |
553 | Q_EMIT canFetchMoreChanged(); |
554 | } else { |
555 | - Q_FOREACH(const History::Thread &thread, threads) { |
556 | - // insert the identifiers in the contact map |
557 | - Q_FOREACH(const History::Participant &participant, thread.participants()) { |
558 | - watchContactInfo(thread.accountId(), participant.identifier(), participant.properties()); |
559 | - } |
560 | - } |
561 | - |
562 | beginInsertRows(QModelIndex(), mThreads.count(), mThreads.count() + threads.count() - 1); |
563 | mThreads << threads; |
564 | endInsertRows(); |
565 | @@ -294,6 +288,10 @@ |
566 | SIGNAL(threadsRemoved(History::Threads)), |
567 | SLOT(onThreadsRemoved(History::Threads))); |
568 | connect(mThreadView.data(), |
569 | + SIGNAL(threadParticipantsChanged(History::Thread, History::Participants, History::Participants, History::Participants)), |
570 | + SLOT(onThreadParticipantsChanged(History::Thread, History::Participants, History::Participants, History::Participants))); |
571 | + |
572 | + connect(mThreadView.data(), |
573 | SIGNAL(invalidated()), |
574 | SLOT(triggerQueryUpdate())); |
575 | |
576 | @@ -311,6 +309,43 @@ |
577 | fetchMore(QModelIndex()); |
578 | } |
579 | |
580 | + |
581 | +void HistoryThreadModel::onThreadParticipantsChanged(const History::Thread &thread, const History::Participants &added, const History::Participants &removed, const History::Participants &modified) |
582 | +{ |
583 | + int pos = mThreads.indexOf(thread); |
584 | + if (pos >= 0) { |
585 | + mThreads[pos].removeParticipants(removed); |
586 | + mThreads[pos].removeParticipants(modified); |
587 | + mThreads[pos].addParticipants(added); |
588 | + mThreads[pos].addParticipants(modified); |
589 | + QModelIndex idx = index(pos); |
590 | + Q_EMIT dataChanged(idx, idx); |
591 | + } |
592 | + |
593 | + // watch the contact info for the received participants |
594 | + Q_FOREACH(const History::Participant &participant, added) { |
595 | + watchContactInfo(thread.accountId(), participant.identifier(), participant.properties()); |
596 | + } |
597 | + Q_FOREACH(const History::Participant &participant, modified) { |
598 | + watchContactInfo(thread.accountId(), participant.identifier(), participant.properties()); |
599 | + } |
600 | +} |
601 | + |
602 | +void HistoryThreadModel::fetchParticipantsIfNeeded(const History::Threads &threads) |
603 | +{ |
604 | + History::Threads filtered; |
605 | + Q_FOREACH(const History::Thread &thread, threads) { |
606 | + if (thread.type() == History::EventTypeText && thread.participants().isEmpty() && |
607 | + (thread.chatType() != History::ChatTypeRoom || thread.accountId().startsWith("ofono"))) { |
608 | + filtered << thread; |
609 | + } |
610 | + } |
611 | + if (filtered.isEmpty()) { |
612 | + return; |
613 | + } |
614 | + History::Manager::instance()->requestThreadParticipants(filtered); |
615 | +} |
616 | + |
617 | void HistoryThreadModel::onThreadsAdded(const History::Threads &threads) |
618 | { |
619 | if (threads.isEmpty()) { |
620 | @@ -328,6 +363,7 @@ |
621 | mThreads.insert(pos, thread); |
622 | endInsertRows(); |
623 | } |
624 | + fetchParticipantsIfNeeded(threads); |
625 | } |
626 | |
627 | void HistoryThreadModel::onThreadsModified(const History::Threads &threads) |
628 | @@ -349,6 +385,7 @@ |
629 | if (!newThreads.isEmpty()) { |
630 | onThreadsAdded(newThreads); |
631 | } |
632 | + fetchParticipantsIfNeeded(threads); |
633 | } |
634 | |
635 | void HistoryThreadModel::onThreadsRemoved(const History::Threads &threads) |
636 | @@ -369,5 +406,7 @@ |
637 | |
638 | History::Threads HistoryThreadModel::fetchNextPage() |
639 | { |
640 | - return mThreadView->nextPage(); |
641 | + History::Threads threads = mThreadView->nextPage(); |
642 | + fetchParticipantsIfNeeded(threads); |
643 | + return threads; |
644 | } |
645 | |
646 | === modified file 'Ubuntu/History/historythreadmodel.h' |
647 | --- Ubuntu/History/historythreadmodel.h 2016-06-17 01:49:46 +0000 |
648 | +++ Ubuntu/History/historythreadmodel.h 2017-03-21 14:18:04 +0000 |
649 | @@ -76,8 +76,10 @@ |
650 | virtual void onThreadsAdded(const History::Threads &threads); |
651 | virtual void onThreadsModified(const History::Threads &threads); |
652 | virtual void onThreadsRemoved(const History::Threads &threads); |
653 | + virtual void onThreadParticipantsChanged(const History::Thread &thread, const History::Participants &added, const History::Participants &removed, const History::Participants &modified); |
654 | |
655 | protected: |
656 | + void fetchParticipantsIfNeeded(const History::Threads &threads); |
657 | History::Threads fetchNextPage(); |
658 | bool mCanFetchMore; |
659 | bool mGroupThreads; |
660 | |
661 | === modified file 'cmake/modules/GenerateTest.cmake' |
662 | --- cmake/modules/GenerateTest.cmake 2015-09-28 14:26:09 +0000 |
663 | +++ cmake/modules/GenerateTest.cmake 2017-03-21 14:18:04 +0000 |
664 | @@ -79,6 +79,7 @@ |
665 | set(ARG_ENVIRONMENT HOME=${TMPDIR} |
666 | HISTORY_PLUGIN_PATH=${CMAKE_BINARY_DIR}/plugins/sqlite |
667 | HISTORY_SQLITE_DBPATH=:memory: |
668 | + HISTORY_LOCK_FILE=${TMPDIR}/history-service.lock |
669 | MC_ACCOUNT_DIR=${TMPDIR} |
670 | MC_MANAGER_DIR=${TMPDIR}) |
671 | endif () |
672 | |
673 | === modified file 'daemon/HistoryService.xml' |
674 | --- daemon/HistoryService.xml 2016-04-15 22:23:21 +0000 |
675 | +++ daemon/HistoryService.xml 2017-03-21 14:18:04 +0000 |
676 | @@ -35,6 +35,15 @@ |
677 | <arg type="a{sv}" direction="out"/> |
678 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/> |
679 | </method> |
680 | + <method name="ParticipantsForThreads"> |
681 | + <dox:d><![CDATA[ |
682 | + Return the participants for the given threads |
683 | + ]]></dox:d> |
684 | + <arg name="threadIds" type="a(a{sv})" direction="in"/> |
685 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QList < QVariantMap >"/> |
686 | + <arg name="participants" type="a(a{sv})" direction="out"/> |
687 | + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList < QVariantMap >"/> |
688 | + </method> |
689 | <method name="WriteEvents"> |
690 | <dox:d><![CDATA[ |
691 | Write the given events to the storage. |
692 | @@ -62,6 +71,13 @@ |
693 | <arg type="b" direction="out"/> |
694 | <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QList < QVariantMap >"/> |
695 | </method> |
696 | + <method name="MarkThreadsAsRead"> |
697 | + <dox:d><![CDATA[ |
698 | + Mark an entire thread as read |
699 | + ]]></dox:d> |
700 | + <arg name="threads" type="a(a{sv})" direction="in"/> |
701 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QList < QVariantMap >"/> |
702 | + </method> |
703 | <method name="QueryThreads"> |
704 | <dox:d><![CDATA[ |
705 | Creates a threads view with the given filter and sort order. |
706 | @@ -159,5 +175,18 @@ |
707 | <arg name="events" type="a(a{sv})"/> |
708 | <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QList < QVariantMap >"/> |
709 | </signal> |
710 | + <signal name="ThreadParticipantsChanged"> |
711 | + <dox:d><![CDATA[ |
712 | + Participants changed in a certain thread changed. |
713 | + ]]></dox:d> |
714 | + <arg name="thread" type="a{sv}"/> |
715 | + <arg name="added" type="a(a{sv})"/> |
716 | + <arg name="removed" type="a(a{sv})"/> |
717 | + <arg name="modified" type="a(a{sv})"/> |
718 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/> |
719 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QList < QVariantMap >"/> |
720 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QList < QVariantMap >"/> |
721 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QList < QVariantMap >"/> |
722 | + </signal> |
723 | </interface> |
724 | </node> |
725 | |
726 | === modified file 'daemon/callchannelobserver.cpp' |
727 | --- daemon/callchannelobserver.cpp 2013-07-12 14:30:18 +0000 |
728 | +++ daemon/callchannelobserver.cpp 2017-03-21 14:18:04 +0000 |
729 | @@ -40,6 +40,7 @@ |
730 | SLOT(onCallStateChanged(Tp::CallState))); |
731 | |
732 | mChannels.append(callChannel); |
733 | + mCallStates[callChannel.data()] = callChannel->callState(); |
734 | } |
735 | |
736 | |
737 | @@ -51,11 +52,24 @@ |
738 | } |
739 | |
740 | switch (state) { |
741 | - case Tp::CallStateEnded: |
742 | - Q_EMIT callEnded(Tp::CallChannelPtr(channel)); |
743 | + case Tp::CallStateEnded: { |
744 | + bool incoming = !channel->isRequested(); |
745 | + bool missed = incoming && channel->callStateReason().reason == Tp::CallStateChangeReasonNoAnswer; |
746 | + |
747 | + // If the call state is not missed at this point, we need to check from which state we transitioned to ended, |
748 | + // if from Initialised, it means it was indeed missed |
749 | + if (incoming && !missed) { |
750 | + missed = mCallStates[channel] == Tp::CallStateInitialised; |
751 | + } |
752 | + mCallStates.remove(channel); |
753 | + mChannels.removeOne(Tp::CallChannelPtr(channel)); |
754 | + Q_EMIT callEnded(Tp::CallChannelPtr(channel), missed); |
755 | break; |
756 | + } |
757 | case Tp::CallStateActive: |
758 | channel->setProperty("activeTimestamp", QDateTime::currentDateTime()); |
759 | + default: |
760 | + mCallStates[channel] = state; |
761 | break; |
762 | } |
763 | } |
764 | |
765 | === modified file 'daemon/callchannelobserver.h' |
766 | --- daemon/callchannelobserver.h 2013-07-12 14:30:18 +0000 |
767 | +++ daemon/callchannelobserver.h 2017-03-21 14:18:04 +0000 |
768 | @@ -35,13 +35,14 @@ |
769 | void onCallChannelAvailable(Tp::CallChannelPtr callChannel); |
770 | |
771 | Q_SIGNALS: |
772 | - void callEnded(Tp::CallChannelPtr callChannel); |
773 | + void callEnded(Tp::CallChannelPtr callChannel, bool missed); |
774 | |
775 | protected Q_SLOTS: |
776 | void onCallStateChanged(Tp::CallState state); |
777 | |
778 | private: |
779 | QList<Tp::CallChannelPtr> mChannels; |
780 | + QMap<Tp::CallChannel*,Tp::CallState> mCallStates; |
781 | }; |
782 | |
783 | #endif // CALLCHANNELOBSERVER_H |
784 | |
785 | === modified file 'daemon/historydaemon.cpp' |
786 | --- daemon/historydaemon.cpp 2016-11-24 02:02:32 +0000 |
787 | +++ daemon/historydaemon.cpp 2017-03-21 14:18:04 +0000 |
788 | @@ -1,5 +1,5 @@ |
789 | /* |
790 | - * Copyright (C) 2013-2016 Canonical, Ltd. |
791 | + * Copyright (C) 2013-2017 Canonical, Ltd. |
792 | * |
793 | * Authors: |
794 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
795 | @@ -85,10 +85,11 @@ |
796 | { |
797 | Q_FOREACH (QVariant participant, thread[History::FieldParticipants].toList()) { |
798 | // found if same identifier and as member into thread info |
799 | + QVariantMap participantMap = participant.toMap(); |
800 | if (History::Utils::compareIds(thread[History::FieldAccountId].toString(), |
801 | contact->id(), |
802 | - participant.toMap()[History::FieldIdentifier].toString()) && |
803 | - participant.toMap()[History::FieldParticipantState].toUInt() == History::ParticipantStateRegular) |
804 | + participantMap[History::FieldIdentifier].toString()) && |
805 | + participantMap[History::FieldParticipantState].toUInt() == History::ParticipantStateRegular) |
806 | { |
807 | return true; |
808 | } |
809 | @@ -131,8 +132,8 @@ |
810 | History::TelepathyHelper::instance()->registerChannelObserver(); |
811 | |
812 | connect(&mCallObserver, |
813 | - SIGNAL(callEnded(Tp::CallChannelPtr)), |
814 | - SLOT(onCallEnded(Tp::CallChannelPtr))); |
815 | + SIGNAL(callEnded(Tp::CallChannelPtr, bool)), |
816 | + SLOT(onCallEnded(Tp::CallChannelPtr, bool))); |
817 | connect(&mTextObserver, |
818 | SIGNAL(messageReceived(Tp::TextChannelPtr,Tp::ReceivedMessage)), |
819 | SLOT(onMessageReceived(Tp::TextChannelPtr,Tp::ReceivedMessage))); |
820 | @@ -140,11 +141,11 @@ |
821 | SIGNAL(messageSent(Tp::TextChannelPtr,Tp::Message,QString)), |
822 | SLOT(onMessageSent(Tp::TextChannelPtr,Tp::Message,QString))); |
823 | connect(&mTextObserver, |
824 | - SIGNAL(messageRead(Tp::TextChannelPtr,Tp::ReceivedMessage)), |
825 | - SLOT(onMessageRead(Tp::TextChannelPtr,Tp::ReceivedMessage))); |
826 | - connect(&mTextObserver, |
827 | SIGNAL(channelAvailable(Tp::TextChannelPtr)), |
828 | SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); |
829 | + connect(&mTextObserver, |
830 | + SIGNAL(textChannelInvalidated(Tp::TextChannelPtr)), |
831 | + SLOT(onTextChannelInvalidated(Tp::TextChannelPtr))); |
832 | |
833 | // FIXME: we need to do this in a better way, but for now this should do |
834 | mProtocolFlags["ofono"] = History::MatchPhoneNumber; |
835 | @@ -163,23 +164,39 @@ |
836 | |
837 | void HistoryDaemon::onRolesChanged(const HandleRolesMap &added, const HandleRolesMap &removed) |
838 | { |
839 | - Q_UNUSED(added); |
840 | - Q_UNUSED(removed); |
841 | - |
842 | ChannelInterfaceRolesInterface *roles_interface = qobject_cast<ChannelInterfaceRolesInterface*>(sender()); |
843 | - RolesMap roles = roles_interface->getRoles(); |
844 | - |
845 | Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()->parent())); |
846 | + RolesMap rolesMap; |
847 | + if (!mRolesMap.contains(channel->objectPath())) { |
848 | + rolesMap = roles_interface->getRoles(); |
849 | + } else { |
850 | + rolesMap = mRolesMap[channel->objectPath()]; |
851 | + } |
852 | + |
853 | + QMapIterator<uint, uint> it(removed); |
854 | + while (it.hasNext()) { |
855 | + it.next(); |
856 | + rolesMap.remove(it.key()); |
857 | + } |
858 | + |
859 | + QMapIterator<uint, uint> it2(added); |
860 | + while (it2.hasNext()) { |
861 | + it2.next(); |
862 | + rolesMap[it2.key()] = it2.value(); |
863 | + } |
864 | + |
865 | + mRolesMap[channel->objectPath()] = rolesMap; |
866 | + |
867 | QVariantMap properties = propertiesFromChannel(channel); |
868 | QVariantMap thread = threadForProperties(channel->property(History::FieldAccountId).toString(), |
869 | History::EventTypeText, |
870 | properties, |
871 | matchFlagsForChannel(channel), |
872 | false); |
873 | - |
874 | - writeRolesInformationEvents(thread, channel, roles); |
875 | - |
876 | - updateRoomRoles(channel, roles); |
877 | + if (!thread.isEmpty()) { |
878 | + writeRolesInformationEvents(thread, channel, rolesMap); |
879 | + updateRoomRoles(channel, rolesMap); |
880 | + } |
881 | } |
882 | |
883 | QVariantMap HistoryDaemon::propertiesFromChannel(const Tp::ChannelPtr &textChannel) |
884 | @@ -187,55 +204,60 @@ |
885 | QVariantMap properties; |
886 | QVariantList participants; |
887 | QStringList participantIds; |
888 | - |
889 | - ChannelInterfaceRolesInterface *roles_interface = textChannel->optionalInterface<ChannelInterfaceRolesInterface>(); |
890 | - RolesMap roles; |
891 | - if (roles_interface) { |
892 | - roles = roles_interface->getRoles(); |
893 | - } |
894 | - |
895 | - Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) { |
896 | - QVariantMap contactProperties; |
897 | - contactProperties[History::FieldAlias] = contact->alias(); |
898 | - contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString(); |
899 | - contactProperties[History::FieldIdentifier] = contact->id(); |
900 | - contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular; |
901 | - contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
902 | - participantIds << contact->id(); |
903 | - participants << contactProperties; |
904 | - } |
905 | - |
906 | - Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupRemotePendingContacts(false)) { |
907 | - QVariantMap contactProperties; |
908 | - contactProperties[History::FieldAlias] = contact->alias(); |
909 | - contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString(); |
910 | - contactProperties[History::FieldIdentifier] = contact->id(); |
911 | - contactProperties[History::FieldParticipantState] = History::ParticipantStateRemotePending; |
912 | - contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
913 | - participantIds << contact->id(); |
914 | - participants << contactProperties; |
915 | - } |
916 | - |
917 | - Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupLocalPendingContacts(false)) { |
918 | - QVariantMap contactProperties; |
919 | - contactProperties[History::FieldAlias] = contact->alias(); |
920 | - contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString(); |
921 | - contactProperties[History::FieldIdentifier] = contact->id(); |
922 | - contactProperties[History::FieldParticipantState] = History::ParticipantStateLocalPending; |
923 | - contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
924 | - participantIds << contact->id(); |
925 | - participants << contactProperties; |
926 | - } |
927 | - |
928 | - if (participants.isEmpty() && textChannel->targetHandleType() == Tp::HandleTypeContact && |
929 | - textChannel->targetContact() == textChannel->connection()->selfContact()) { |
930 | - QVariantMap contactProperties; |
931 | - contactProperties[History::FieldAlias] = textChannel->targetContact()->alias(); |
932 | - contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString(); |
933 | - contactProperties[History::FieldIdentifier] = textChannel->targetContact()->id(); |
934 | - contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular; |
935 | - participantIds << textChannel->targetContact()->id(); |
936 | - participants << contactProperties; |
937 | + QString accountId = textChannel->property(History::FieldAccountId).toString(); |
938 | + |
939 | + if (History::Utils::shouldIncludeParticipants(accountId, fromTelepathyHandleType(textChannel->targetHandleType()))) { |
940 | + ChannelInterfaceRolesInterface *roles_interface = textChannel->optionalInterface<ChannelInterfaceRolesInterface>(); |
941 | + RolesMap roles; |
942 | + if (roles_interface) { |
943 | + if (mRolesMap.contains(textChannel->objectPath())) { |
944 | + roles = mRolesMap[textChannel->objectPath()]; |
945 | + } |
946 | + } |
947 | + |
948 | + Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) { |
949 | + QVariantMap contactProperties; |
950 | + contactProperties[History::FieldAlias] = contact->alias(); |
951 | + contactProperties[History::FieldAccountId] = accountId; |
952 | + contactProperties[History::FieldIdentifier] = contact->id(); |
953 | + contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular; |
954 | + contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
955 | + participantIds << contact->id(); |
956 | + participants << contactProperties; |
957 | + } |
958 | + |
959 | + Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupRemotePendingContacts(false)) { |
960 | + QVariantMap contactProperties; |
961 | + contactProperties[History::FieldAlias] = contact->alias(); |
962 | + contactProperties[History::FieldAccountId] = accountId; |
963 | + contactProperties[History::FieldIdentifier] = contact->id(); |
964 | + contactProperties[History::FieldParticipantState] = History::ParticipantStateRemotePending; |
965 | + contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
966 | + participantIds << contact->id(); |
967 | + participants << contactProperties; |
968 | + } |
969 | + |
970 | + Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupLocalPendingContacts(false)) { |
971 | + QVariantMap contactProperties; |
972 | + contactProperties[History::FieldAlias] = contact->alias(); |
973 | + contactProperties[History::FieldAccountId] = accountId; |
974 | + contactProperties[History::FieldIdentifier] = contact->id(); |
975 | + contactProperties[History::FieldParticipantState] = History::ParticipantStateLocalPending; |
976 | + contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
977 | + participantIds << contact->id(); |
978 | + participants << contactProperties; |
979 | + } |
980 | + |
981 | + if (participants.isEmpty() && textChannel->targetHandleType() == Tp::HandleTypeContact && |
982 | + textChannel->targetContact() == textChannel->connection()->selfContact()) { |
983 | + QVariantMap contactProperties; |
984 | + contactProperties[History::FieldAlias] = textChannel->targetContact()->alias(); |
985 | + contactProperties[History::FieldAccountId] = accountId; |
986 | + contactProperties[History::FieldIdentifier] = textChannel->targetContact()->id(); |
987 | + contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular; |
988 | + participantIds << textChannel->targetContact()->id(); |
989 | + participants << contactProperties; |
990 | + } |
991 | } |
992 | |
993 | // We map chatType directly from telepathy targetHandleType: None, Contact, Room |
994 | @@ -314,6 +336,39 @@ |
995 | return thread; |
996 | } |
997 | |
998 | +QString HistoryDaemon::threadIdForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties, History::MatchFlags matchFlags, bool create) |
999 | +{ |
1000 | + if (!mBackend) { |
1001 | + return QString::null; |
1002 | + } |
1003 | + |
1004 | + QString threadId = mBackend->threadIdForProperties(accountId, |
1005 | + type, |
1006 | + properties, |
1007 | + matchFlags); |
1008 | + if (threadId.isEmpty() && create) { |
1009 | + QVariantMap thread = mBackend->createThreadForProperties(accountId, type, properties); |
1010 | + if (!thread.isEmpty()) { |
1011 | + if (properties.contains("Requested") && properties[History::FieldChatType].toInt() == History::ChatTypeRoom) { |
1012 | + QVariantMap map = thread[History::FieldChatRoomInfo].toMap(); |
1013 | + map["Requested"] = properties["Requested"]; |
1014 | + thread[History::FieldChatRoomInfo] = map; |
1015 | + } |
1016 | + mDBus.notifyThreadsAdded(QList<QVariantMap>() << thread); |
1017 | + threadId = thread[History::FieldThreadId].toString(); |
1018 | + } |
1019 | + } |
1020 | + return threadId; |
1021 | +} |
1022 | + |
1023 | +QList<QVariantMap> HistoryDaemon::participantsForThreads(const QList<QVariantMap> &threadIds) |
1024 | +{ |
1025 | + if (!mBackend) { |
1026 | + return QList<QVariantMap>(); |
1027 | + } |
1028 | + return mBackend->participantsForThreads(threadIds); |
1029 | +} |
1030 | + |
1031 | QString HistoryDaemon::queryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties) |
1032 | { |
1033 | if (!mBackend) { |
1034 | @@ -370,7 +425,7 @@ |
1035 | return mBackend->getSingleEvent((History::EventType)type, accountId, threadId, eventId); |
1036 | } |
1037 | |
1038 | -bool HistoryDaemon::writeEvents(const QList<QVariantMap> &events, const QVariantMap &properties) |
1039 | +bool HistoryDaemon::writeEvents(const QList<QVariantMap> &events, const QVariantMap &properties, bool notify) |
1040 | { |
1041 | if (!mBackend) { |
1042 | return false; |
1043 | @@ -407,7 +462,9 @@ |
1044 | threads[hash] = thread; |
1045 | |
1046 | // set the participants field in the event |
1047 | - savedEvent[History::FieldParticipants] = thread[History::FieldParticipants]; |
1048 | + if (type == History::EventTypeVoice) { |
1049 | + savedEvent[History::FieldParticipants] = thread[History::FieldParticipants]; |
1050 | + } |
1051 | |
1052 | |
1053 | // check if the event was a new one or a modification to an existing one |
1054 | @@ -427,13 +484,13 @@ |
1055 | mBackend->endBatchOperation(); |
1056 | |
1057 | // and last but not least, notify the results |
1058 | - if (!newEvents.isEmpty()) { |
1059 | + if (!newEvents.isEmpty() && notify) { |
1060 | mDBus.notifyEventsAdded(newEvents); |
1061 | } |
1062 | - if (!modifiedEvents.isEmpty()) { |
1063 | + if (!modifiedEvents.isEmpty() && notify) { |
1064 | mDBus.notifyEventsModified(modifiedEvents); |
1065 | } |
1066 | - if (!threads.isEmpty()) { |
1067 | + if (!threads.isEmpty() && notify) { |
1068 | mDBus.notifyThreadsModified(threads.values()); |
1069 | } |
1070 | return true; |
1071 | @@ -512,44 +569,46 @@ |
1072 | return true; |
1073 | } |
1074 | |
1075 | +void HistoryDaemon::markThreadsAsRead(const QList<QVariantMap> &threads) |
1076 | +{ |
1077 | + if (!mBackend) { |
1078 | + return; |
1079 | + } |
1080 | + |
1081 | + QList<QVariantMap> modifiedThreads; |
1082 | + |
1083 | + Q_FOREACH(const QVariantMap &thread, threads) { |
1084 | + mBackend->beginBatchOperation(); |
1085 | + QVariantMap newThread = mBackend->markThreadAsRead(thread); |
1086 | + if (!newThread.isEmpty()) { |
1087 | + modifiedThreads << newThread; |
1088 | + } |
1089 | + |
1090 | + mBackend->endBatchOperation(); |
1091 | + } |
1092 | + |
1093 | + if (!modifiedThreads.isEmpty()) { |
1094 | + mDBus.notifyThreadsModified(modifiedThreads); |
1095 | + } |
1096 | +} |
1097 | + |
1098 | bool HistoryDaemon::removeThreads(const QList<QVariantMap> &threads) |
1099 | { |
1100 | if (!mBackend) { |
1101 | return false; |
1102 | } |
1103 | |
1104 | - // In order to remove a thread all we have to do is to remove all its items |
1105 | - // then it is going to be removed by removeEvents() once it detects the thread is |
1106 | - // empty. |
1107 | - QList<QVariantMap> events; |
1108 | - QMap<QString, QVariantMap> removedEmptyThreads; |
1109 | + // If the thread has events |
1110 | + mBackend->beginBatchOperation(); |
1111 | Q_FOREACH(const QVariantMap &thread, threads) { |
1112 | - QList<QVariantMap> thisEvents = mBackend->eventsForThread(thread); |
1113 | - if (thisEvents.isEmpty()) { |
1114 | - mBackend->beginBatchOperation(); |
1115 | - if (!mBackend->removeThread(thread)) { |
1116 | - mBackend->rollbackBatchOperation(); |
1117 | - return false; |
1118 | - } |
1119 | - mBackend->endBatchOperation(); |
1120 | - QString hash = hashThread(thread); |
1121 | - removedEmptyThreads[hash] = thread; |
1122 | - continue; |
1123 | - } |
1124 | - events += thisEvents; |
1125 | - } |
1126 | - |
1127 | - if (!removedEmptyThreads.isEmpty()) { |
1128 | - mDBus.notifyThreadsRemoved(removedEmptyThreads.values()); |
1129 | - } |
1130 | - |
1131 | - if (events.size() > 0) { |
1132 | - if(removeEvents(events)) { |
1133 | - return true; |
1134 | - } |
1135 | - } |
1136 | - |
1137 | - return false; |
1138 | + if (!mBackend->removeThread(thread)) { |
1139 | + mBackend->rollbackBatchOperation(); |
1140 | + return false; |
1141 | + } |
1142 | + } |
1143 | + mBackend->endBatchOperation(); |
1144 | + mDBus.notifyThreadsRemoved(threads); |
1145 | + return true; |
1146 | } |
1147 | |
1148 | void HistoryDaemon::onObserverCreated() |
1149 | @@ -562,7 +621,7 @@ |
1150 | &mTextObserver, SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); |
1151 | } |
1152 | |
1153 | -void HistoryDaemon::onCallEnded(const Tp::CallChannelPtr &channel) |
1154 | +void HistoryDaemon::onCallEnded(const Tp::CallChannelPtr &channel, bool missed) |
1155 | { |
1156 | QVariantMap properties = propertiesFromChannel(channel); |
1157 | QVariantList participants; |
1158 | @@ -592,7 +651,6 @@ |
1159 | // FIXME: check if checking for isRequested() is enough |
1160 | bool incoming = !channel->isRequested(); |
1161 | int duration; |
1162 | - bool missed = incoming && channel->callStateReason().reason == Tp::CallStateChangeReasonNoAnswer; |
1163 | |
1164 | if (!missed) { |
1165 | QDateTime activeTime = channel->property("activeTimestamp").toDateTime(); |
1166 | @@ -615,11 +673,35 @@ |
1167 | writeEvents(QList<QVariantMap>() << event, properties); |
1168 | } |
1169 | |
1170 | +void HistoryDaemon::onTextChannelInvalidated(const Tp::TextChannelPtr channel) |
1171 | +{ |
1172 | + mRolesMap.remove(channel->objectPath()); |
1173 | + QString accountId = channel->property(History::FieldAccountId).toString(); |
1174 | + QVariantMap properties = propertiesFromChannel(channel); |
1175 | + |
1176 | + // first try to fetch the existing thread to see if there is any. |
1177 | + QVariantMap thread = threadForProperties(accountId, |
1178 | + History::EventTypeText, |
1179 | + properties, |
1180 | + matchFlagsForChannel(channel), |
1181 | + false); |
1182 | + |
1183 | + QVariantMap roomInfo = thread[History::FieldChatRoomInfo].toMap(); |
1184 | + if ((roomInfo.contains("Persistent") && !roomInfo["Persistent"].toBool()) && History::TelepathyHelper::instance()->accountForId(accountId)->protocolName() != "ofono") { |
1185 | + writeInformationEvent(thread, History::InformationTypeSelfLeaving); |
1186 | + // update backend |
1187 | + updateRoomProperties(channel, QVariantMap{{"Joined", false}}); |
1188 | + } |
1189 | + |
1190 | + channel->disconnect(this); |
1191 | +} |
1192 | + |
1193 | void HistoryDaemon::onTextChannelAvailable(const Tp::TextChannelPtr channel) |
1194 | { |
1195 | // for Rooms we need to explicitly create the thread to allow users to send messages to groups even |
1196 | // before they receive any message. |
1197 | // for other types, we can wait until messages are received |
1198 | + bool notify = false; |
1199 | if (channel->targetHandleType() == Tp::HandleTypeRoom) { |
1200 | QString accountId = channel->property(History::FieldAccountId).toString(); |
1201 | QVariantMap properties = propertiesFromChannel(channel); |
1202 | @@ -641,12 +723,13 @@ |
1203 | |
1204 | // write information event including all initial invitees |
1205 | Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) { |
1206 | - writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias()); |
1207 | + writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias(), QString(), QString(), false); |
1208 | } |
1209 | |
1210 | // update participants only if the thread is not available previously. Otherwise we'll wait for membersChanged event |
1211 | // for reflect in conversation information events for modified participants. |
1212 | - updateRoomParticipants(channel); |
1213 | + updateRoomParticipants(channel, false); |
1214 | + notify = true; |
1215 | } |
1216 | |
1217 | // write an entry saying you joined the group if 'joined' flag in thread is false and modify that flag. |
1218 | @@ -657,7 +740,8 @@ |
1219 | writeInformationEvent(thread, History::InformationTypeSelfJoined); |
1220 | } |
1221 | // update backend |
1222 | - updateRoomProperties(channel, QVariantMap{{"Joined", true}}); |
1223 | + updateRoomProperties(channel, QVariantMap{{"Joined", true}}, false); |
1224 | + notify = true; |
1225 | } |
1226 | |
1227 | Tp::AbstractInterface *room_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomInterface>(); |
1228 | @@ -685,6 +769,10 @@ |
1229 | |
1230 | connect(roles_interface, SIGNAL(RolesChanged(const HandleRolesMap&, const HandleRolesMap&)), SLOT(onRolesChanged(const HandleRolesMap&, const HandleRolesMap&))); |
1231 | } |
1232 | + |
1233 | + if (notify) { |
1234 | + updateRoomParticipants(channel, true); |
1235 | + } |
1236 | } |
1237 | |
1238 | void HistoryDaemon::onGroupMembersChanged(const Tp::Contacts &groupMembersAdded, |
1239 | @@ -702,6 +790,8 @@ |
1240 | bool hasRemotePendingMembersAdded = groupRemotePendingMembersAdded.size() > 0; |
1241 | bool hasMembersAdded = groupMembersAdded.size() > 0; |
1242 | bool hasMembersRemoved = groupMembersRemoved.size() > 0; |
1243 | + Tp::ContactPtr selfContact = channel->connection()->selfContact(); |
1244 | + bool selfContactIsPending = channel->groupRemotePendingContacts(true).contains(selfContact); |
1245 | |
1246 | if (hasRemotePendingMembersAdded || hasMembersAdded || hasMembersRemoved) { |
1247 | properties = propertiesFromChannel(channel); |
1248 | @@ -710,19 +800,35 @@ |
1249 | properties, |
1250 | matchFlagsForChannel(channel), |
1251 | false); |
1252 | - if (!thread.isEmpty()) { |
1253 | + if (!thread.isEmpty() && !selfContactIsPending) { |
1254 | + QList<QVariantMap> added; |
1255 | + QList<QVariantMap> removed; |
1256 | + QList<QVariantMap> modified; |
1257 | if (hasRemotePendingMembersAdded) { |
1258 | Q_FOREACH (const Tp::ContactPtr& contact, groupRemotePendingMembersAdded) { |
1259 | if (!foundInThread(contact, thread)) { |
1260 | - writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias()); |
1261 | + writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias(), QString(), QString(), true); |
1262 | + QVariantMap participant; |
1263 | + participant[History::FieldIdentifier] = contact->id(); |
1264 | + participant[History::FieldAlias] = contact->alias(); |
1265 | + participant[History::FieldParticipantState] = History::ParticipantStateRemotePending; |
1266 | + added << participant; |
1267 | } |
1268 | } |
1269 | + |
1270 | } |
1271 | if (hasMembersAdded) { |
1272 | Q_FOREACH (const Tp::ContactPtr& contact, groupMembersAdded) { |
1273 | // if this member was not previously regular member in thread, notify about his join |
1274 | - if (!foundAsMemberInThread(contact, thread)) { |
1275 | - writeInformationEvent(thread, History::InformationTypeJoined, contact->alias()); |
1276 | + if (!foundAsMemberInThread(contact, thread) && contact->id() != channel->groupSelfContact()->id()) { |
1277 | + |
1278 | + writeInformationEvent(thread, History::InformationTypeJoined, contact->alias(), QString(), QString(), true); |
1279 | + |
1280 | + QVariantMap participant; |
1281 | + participant[History::FieldIdentifier] = contact->id(); |
1282 | + participant[History::FieldAlias] = contact->alias(); |
1283 | + participant[History::FieldParticipantState] = History::ParticipantStateRegular; |
1284 | + added << participant; |
1285 | } |
1286 | } |
1287 | } |
1288 | @@ -741,27 +847,34 @@ |
1289 | break; |
1290 | } |
1291 | } |
1292 | - writeInformationEvent(thread, type); |
1293 | - // update backend |
1294 | - updateRoomProperties(channel, QVariantMap{{"Joined", false}}); |
1295 | + if (thread[History::FieldChatRoomInfo].toMap()["Joined"].toBool()) { |
1296 | + writeInformationEvent(thread, type); |
1297 | + // update backend |
1298 | + updateRoomProperties(channel, QVariantMap{{"Joined", false}}, true); |
1299 | + } |
1300 | } |
1301 | else // don't notify any other group member removal if we are leaving the group |
1302 | { |
1303 | Q_FOREACH (const Tp::ContactPtr& contact, groupMembersRemoved) { |
1304 | // inform about removed members other than us |
1305 | if (contact->id() != channel->groupSelfContact()->id()) { |
1306 | - writeInformationEvent(thread, History::InformationTypeLeaving, contact->alias()); |
1307 | + writeInformationEvent(thread, History::InformationTypeLeaving, contact->alias(), QString(), QString(), true); |
1308 | } |
1309 | + QVariantMap participant; |
1310 | + participant[History::FieldIdentifier] = contact->id(); |
1311 | + participant[History::FieldAlias] = contact->alias(); |
1312 | + removed << participant; |
1313 | } |
1314 | } |
1315 | } |
1316 | + mDBus.notifyThreadParticipantsChanged(thread, added, removed, QList<QVariantMap>()); |
1317 | } |
1318 | } |
1319 | |
1320 | - updateRoomParticipants(channel); |
1321 | + updateRoomParticipants(channel, !selfContactIsPending); |
1322 | } |
1323 | |
1324 | -void HistoryDaemon::updateRoomParticipants(const Tp::TextChannelPtr channel) |
1325 | +void HistoryDaemon::updateRoomParticipants(const Tp::TextChannelPtr channel, bool notify) |
1326 | { |
1327 | if (!channel) { |
1328 | return; |
1329 | @@ -772,8 +885,14 @@ |
1330 | |
1331 | ChannelInterfaceRolesInterface *roles_interface = channel->optionalInterface<ChannelInterfaceRolesInterface>(); |
1332 | RolesMap roles; |
1333 | + |
1334 | if (roles_interface) { |
1335 | - roles = roles_interface->getRoles(); |
1336 | + if (!mRolesMap.contains(channel->objectPath())) { |
1337 | + roles = roles_interface->getRoles(); |
1338 | + mRolesMap[channel->objectPath()] = roles; |
1339 | + } else { |
1340 | + roles = mRolesMap[channel->objectPath()]; |
1341 | + } |
1342 | } |
1343 | |
1344 | Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) { |
1345 | @@ -811,12 +930,14 @@ |
1346 | QString accountId = channel->property(History::FieldAccountId).toString(); |
1347 | QString threadId = channel->targetId(); |
1348 | if (mBackend->updateRoomParticipants(accountId, threadId, History::EventTypeText, participants)) { |
1349 | - QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap()); |
1350 | - mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread); |
1351 | + if (notify) { |
1352 | + QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap()); |
1353 | + mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread); |
1354 | + } |
1355 | } |
1356 | } |
1357 | |
1358 | -void HistoryDaemon::updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap) |
1359 | +void HistoryDaemon::updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap, bool notify) |
1360 | { |
1361 | if (!channel) { |
1362 | return; |
1363 | @@ -841,8 +962,10 @@ |
1364 | QString accountId = channel->property(History::FieldAccountId).toString(); |
1365 | QString threadId = channel->targetId(); |
1366 | if (mBackend->updateRoomParticipantsRoles(accountId, threadId, History::EventTypeText, participantsRoles)) { |
1367 | - QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap()); |
1368 | - mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread); |
1369 | + if (notify) { |
1370 | + QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap()); |
1371 | + mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread); |
1372 | + } |
1373 | } |
1374 | |
1375 | // update self roles in room properties |
1376 | @@ -865,28 +988,41 @@ |
1377 | updateRoomProperties(accountId, threadId, type, properties, invalidated); |
1378 | } |
1379 | |
1380 | -void HistoryDaemon::updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties) |
1381 | +void HistoryDaemon::updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties, bool notify) |
1382 | { |
1383 | QString accountId = channel->property(History::FieldAccountId).toString(); |
1384 | QString threadId = channel->targetId(); |
1385 | History::EventType type = History::EventTypeText; |
1386 | - updateRoomProperties(accountId, threadId, type, properties, QStringList()); |
1387 | + updateRoomProperties(accountId, threadId, type, properties, QStringList(), notify); |
1388 | } |
1389 | |
1390 | -void HistoryDaemon::updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated) |
1391 | +void HistoryDaemon::updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated, bool notify) |
1392 | { |
1393 | if (mBackend->updateRoomInfo(accountId, threadId, type, properties, invalidated)) { |
1394 | - QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap()); |
1395 | - mDBus.notifyThreadsModified(QList<QVariantMap>() << thread); |
1396 | + if (notify) { |
1397 | + QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap()); |
1398 | + mDBus.notifyThreadsModified(QList<QVariantMap>() << thread); |
1399 | + } |
1400 | } |
1401 | } |
1402 | |
1403 | void HistoryDaemon::onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message) |
1404 | { |
1405 | QString eventId; |
1406 | - Tp::MessagePart header = message.header(); |
1407 | QString senderId; |
1408 | + |
1409 | + QString accountId = textChannel->property(History::FieldAccountId).toString(); |
1410 | + QString threadId = textChannel->property(History::FieldThreadId).toString(); |
1411 | QVariantMap properties = propertiesFromChannel(textChannel); |
1412 | + |
1413 | + if (threadId.isNull()) { |
1414 | + threadId = threadIdForProperties(accountId, |
1415 | + History::EventTypeText, |
1416 | + properties, |
1417 | + matchFlagsForChannel(textChannel), |
1418 | + true); |
1419 | + } |
1420 | + |
1421 | History::MessageStatus status = History::MessageStatusUnknown; |
1422 | if (!message.sender() || message.sender()->handle().at(0) == textChannel->connection()->selfHandle()) { |
1423 | senderId = "self"; |
1424 | @@ -909,7 +1045,7 @@ |
1425 | if (message.isDeliveryReport() && message.deliveryDetails().hasOriginalToken()) { |
1426 | // at this point we assume the delivery report is for a message that was already |
1427 | // sent and properly saved at our database, so we can safely get it here to update |
1428 | - QVariantMap textEvent = getSingleEventFromTextChannel(textChannel, message.deliveryDetails().originalToken()); |
1429 | + QVariantMap textEvent = getSingleEvent(History::EventTypeText, accountId, threadId, message.deliveryDetails().originalToken()); |
1430 | if (textEvent.isEmpty()) { |
1431 | qWarning() << "Cound not find the original event to update with delivery details."; |
1432 | return; |
1433 | @@ -922,52 +1058,21 @@ |
1434 | return; |
1435 | } |
1436 | |
1437 | - History::MessageStatus status; |
1438 | - switch (message.deliveryDetails().status()) { |
1439 | - case Tp::DeliveryStatusAccepted: |
1440 | - status = History::MessageStatusAccepted; |
1441 | - break; |
1442 | - case Tp::DeliveryStatusDeleted: |
1443 | - status = History::MessageStatusDeleted; |
1444 | - break; |
1445 | - case Tp::DeliveryStatusDelivered: |
1446 | - status = History::MessageStatusDelivered; |
1447 | - break; |
1448 | - case Tp::DeliveryStatusPermanentlyFailed: |
1449 | - status = History::MessageStatusPermanentlyFailed; |
1450 | - break; |
1451 | - case Tp::DeliveryStatusRead: |
1452 | - status = History::MessageStatusRead; |
1453 | - break; |
1454 | - case Tp::DeliveryStatusTemporarilyFailed: |
1455 | - status = History::MessageStatusTemporarilyFailed; |
1456 | - break; |
1457 | - case Tp::DeliveryStatusUnknown: |
1458 | - status = History::MessageStatusUnknown; |
1459 | - break; |
1460 | - } |
1461 | - |
1462 | - textEvent[History::FieldMessageStatus] = (int) status; |
1463 | + textEvent[History::FieldMessageStatus] = (int) fromTelepathyDeliveryStatus(message.deliveryDetails().status()); |
1464 | if (!writeEvents(QList<QVariantMap>() << textEvent, properties)) { |
1465 | qWarning() << "Failed to save the new message status!"; |
1466 | } |
1467 | |
1468 | return; |
1469 | } |
1470 | - |
1471 | - QVariantMap thread = threadForProperties(textChannel->property(History::FieldAccountId).toString(), |
1472 | - History::EventTypeText, |
1473 | - properties, |
1474 | - matchFlagsForChannel(textChannel), |
1475 | - true); |
1476 | int count = 1; |
1477 | QList<QVariantMap> attachments; |
1478 | History::MessageType type = History::MessageTypeText; |
1479 | QString subject; |
1480 | |
1481 | if (message.hasNonTextContent()) { |
1482 | - QString normalizedAccountId = QString(QCryptographicHash::hash(thread[History::FieldAccountId].toString().toLatin1(), QCryptographicHash::Md5).toHex()); |
1483 | - QString normalizedThreadId = QString(QCryptographicHash::hash(thread[History::FieldThreadId].toString().toLatin1(), QCryptographicHash::Md5).toHex()); |
1484 | + QString normalizedAccountId = QString(QCryptographicHash::hash(accountId.toLatin1(), QCryptographicHash::Md5).toHex()); |
1485 | + QString normalizedThreadId = QString(QCryptographicHash::hash(threadId.toLatin1(), QCryptographicHash::Md5).toHex()); |
1486 | QString normalizedEventId = QString(QCryptographicHash::hash(eventId.toLatin1(), QCryptographicHash::Md5).toHex()); |
1487 | QString mmsStoragePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); |
1488 | |
1489 | @@ -1000,8 +1105,8 @@ |
1490 | file.close(); |
1491 | |
1492 | QVariantMap attachment; |
1493 | - attachment[History::FieldAccountId] = thread[History::FieldAccountId]; |
1494 | - attachment[History::FieldThreadId] = thread[History::FieldThreadId]; |
1495 | + attachment[History::FieldAccountId] = accountId; |
1496 | + attachment[History::FieldThreadId] = threadId; |
1497 | attachment[History::FieldEventId] = eventId; |
1498 | attachment[History::FieldAttachmentId] = part["identifier"].variant(); |
1499 | attachment[History::FieldContentType] = part["content-type"].variant(); |
1500 | @@ -1013,8 +1118,8 @@ |
1501 | |
1502 | QVariantMap event; |
1503 | event[History::FieldType] = History::EventTypeText; |
1504 | - event[History::FieldAccountId] = thread[History::FieldAccountId]; |
1505 | - event[History::FieldThreadId] = thread[History::FieldThreadId]; |
1506 | + event[History::FieldAccountId] = accountId; |
1507 | + event[History::FieldThreadId] = threadId; |
1508 | event[History::FieldEventId] = eventId; |
1509 | event[History::FieldSenderId] = senderId; |
1510 | event[History::FieldTimestamp] = message.received().toString("yyyy-MM-ddTHH:mm:ss.zzz"); |
1511 | @@ -1058,22 +1163,6 @@ |
1512 | |
1513 | } |
1514 | |
1515 | -void HistoryDaemon::onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message) |
1516 | -{ |
1517 | - QVariantMap textEvent = getSingleEventFromTextChannel(textChannel, message.messageToken()); |
1518 | - QVariantMap properties = propertiesFromChannel(textChannel); |
1519 | - |
1520 | - if (textEvent.isEmpty()) { |
1521 | - qWarning() << "Cound not find the original event to update with newEvent = false."; |
1522 | - return; |
1523 | - } |
1524 | - |
1525 | - textEvent[History::FieldNewEvent] = false; |
1526 | - if (!writeEvents(QList<QVariantMap>() << textEvent, properties)) { |
1527 | - qWarning() << "Failed to save the new message status!"; |
1528 | - } |
1529 | -} |
1530 | - |
1531 | void HistoryDaemon::onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken) |
1532 | { |
1533 | QVariantMap properties = propertiesFromChannel(textChannel); |
1534 | @@ -1191,7 +1280,7 @@ |
1535 | return reply.value(); |
1536 | } |
1537 | |
1538 | -void HistoryDaemon::writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject, const QString &sender, const QString &text) |
1539 | +void HistoryDaemon::writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject, const QString &sender, const QString &text, bool notify) |
1540 | { |
1541 | History::TextEvent historyEvent = History::TextEvent(thread[History::FieldAccountId].toString(), |
1542 | thread[History::FieldThreadId].toString(), |
1543 | @@ -1207,7 +1296,7 @@ |
1544 | QDateTime::currentDateTime(), |
1545 | subject, |
1546 | type); |
1547 | - writeEvents(QList<QVariantMap>() << historyEvent.properties(), thread); |
1548 | + writeEvents(QList<QVariantMap>() << historyEvent.properties(), thread, notify); |
1549 | } |
1550 | |
1551 | void HistoryDaemon::writeRoomChangesInformationEvents(const QVariantMap &thread, const QVariantMap &interfaceProperties) |
1552 | @@ -1265,3 +1354,51 @@ |
1553 | } |
1554 | } |
1555 | } |
1556 | + |
1557 | +History::MessageStatus HistoryDaemon::fromTelepathyDeliveryStatus(Tp::DeliveryStatus deliveryStatus) |
1558 | +{ |
1559 | + History::MessageStatus status; |
1560 | + switch (deliveryStatus) { |
1561 | + case Tp::DeliveryStatusAccepted: |
1562 | + status = History::MessageStatusAccepted; |
1563 | + break; |
1564 | + case Tp::DeliveryStatusDeleted: |
1565 | + status = History::MessageStatusDeleted; |
1566 | + break; |
1567 | + case Tp::DeliveryStatusDelivered: |
1568 | + status = History::MessageStatusDelivered; |
1569 | + break; |
1570 | + case Tp::DeliveryStatusPermanentlyFailed: |
1571 | + status = History::MessageStatusPermanentlyFailed; |
1572 | + break; |
1573 | + case Tp::DeliveryStatusRead: |
1574 | + status = History::MessageStatusRead; |
1575 | + break; |
1576 | + case Tp::DeliveryStatusTemporarilyFailed: |
1577 | + status = History::MessageStatusTemporarilyFailed; |
1578 | + break; |
1579 | + case Tp::DeliveryStatusUnknown: |
1580 | + status = History::MessageStatusUnknown; |
1581 | + break; |
1582 | + } |
1583 | + |
1584 | + return status; |
1585 | +} |
1586 | + |
1587 | +History::ChatType HistoryDaemon::fromTelepathyHandleType(const Tp::HandleType &type) |
1588 | +{ |
1589 | + History::ChatType chatType; |
1590 | + switch(type) { |
1591 | + case Tp::HandleTypeContact: |
1592 | + chatType = History::ChatTypeContact; |
1593 | + break; |
1594 | + case Tp::HandleTypeNone: |
1595 | + chatType = History::ChatTypeNone; |
1596 | + break; |
1597 | + case Tp::HandleTypeRoom: |
1598 | + chatType = History::ChatTypeRoom; |
1599 | + break; |
1600 | + } |
1601 | + |
1602 | + return chatType; |
1603 | +} |
1604 | |
1605 | === modified file 'daemon/historydaemon.h' |
1606 | --- daemon/historydaemon.h 2016-10-20 13:56:10 +0000 |
1607 | +++ daemon/historydaemon.h 2017-03-21 14:18:04 +0000 |
1608 | @@ -1,5 +1,5 @@ |
1609 | /* |
1610 | - * Copyright (C) 2013-2016 Canonical, Ltd. |
1611 | + * Copyright (C) 2013-2017 Canonical, Ltd. |
1612 | * |
1613 | * Authors: |
1614 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
1615 | @@ -42,29 +42,36 @@ |
1616 | |
1617 | static HistoryDaemon *instance(); |
1618 | |
1619 | - static QVariantMap propertiesFromChannel(const Tp::ChannelPtr &textChannel); |
1620 | + QVariantMap propertiesFromChannel(const Tp::ChannelPtr &textChannel); |
1621 | QVariantMap threadForProperties(const QString &accountId, |
1622 | History::EventType type, |
1623 | const QVariantMap &properties, |
1624 | History::MatchFlags matchFlags = History::MatchCaseSensitive, |
1625 | bool create = true); |
1626 | + QString threadIdForProperties(const QString &accountId, |
1627 | + History::EventType type, |
1628 | + const QVariantMap &properties, |
1629 | + History::MatchFlags matchFlags = History::MatchCaseSensitive, |
1630 | + bool create = true); |
1631 | + QList<QVariantMap> participantsForThreads(const QList<QVariantMap> &threadIds); |
1632 | QString queryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties); |
1633 | QString queryEvents(int type, const QVariantMap &sort, const QVariantMap &filter); |
1634 | QVariantMap getSingleThread(int type, const QString &accountId, const QString &threadId, const QVariantMap &properties); |
1635 | QVariantMap getSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId); |
1636 | QVariantMap getSingleEventFromTextChannel(const Tp::TextChannelPtr textChannel, const QString &messageId); |
1637 | |
1638 | - bool writeEvents(const QList<QVariantMap> &events, const QVariantMap &properties); |
1639 | + bool writeEvents(const QList<QVariantMap> &events, const QVariantMap &properties, bool notify = true); |
1640 | bool removeEvents(const QList<QVariantMap> &events); |
1641 | bool removeThreads(const QList<QVariantMap> &threads); |
1642 | + void markThreadsAsRead(const QList<QVariantMap> &threads); |
1643 | |
1644 | private Q_SLOTS: |
1645 | void onObserverCreated(); |
1646 | - void onCallEnded(const Tp::CallChannelPtr &channel); |
1647 | + void onCallEnded(const Tp::CallChannelPtr &channel, bool missed); |
1648 | void onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); |
1649 | - void onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); |
1650 | void onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken); |
1651 | void onTextChannelAvailable(const Tp::TextChannelPtr channel); |
1652 | + void onTextChannelInvalidated(const Tp::TextChannelPtr channel); |
1653 | void onRoomPropertiesChanged(const QVariantMap &properties,const QStringList &invalidated); |
1654 | void onGroupMembersChanged(const Tp::Contacts &groupMembersAdded, const Tp::Contacts &groupLocalPendingMembersAdded, |
1655 | const Tp::Contacts &groupRemotePendingMembersAdded, const Tp::Contacts &groupMembersRemoved, |
1656 | @@ -73,18 +80,21 @@ |
1657 | |
1658 | protected: |
1659 | History::MatchFlags matchFlagsForChannel(const Tp::ChannelPtr &channel); |
1660 | - void updateRoomParticipants(const Tp::TextChannelPtr channel); |
1661 | - void updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap); |
1662 | + void updateRoomParticipants(const Tp::TextChannelPtr channel, bool notify = true); |
1663 | + void updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap, bool notify = true); |
1664 | QString hashThread(const QVariantMap &thread); |
1665 | static QVariantMap getInterfaceProperties(const Tp::AbstractInterface *interface); |
1666 | - void updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties); |
1667 | - void updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated); |
1668 | + void updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties, bool notify = true); |
1669 | + void updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated, bool notify = true); |
1670 | |
1671 | - void writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject = QString(), const QString &sender = QString("self"), const QString &text = QString()); |
1672 | + void writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject = QString(), const QString &sender = QString("self"), const QString &text = QString(), bool notify = true); |
1673 | |
1674 | void writeRoomChangesInformationEvents(const QVariantMap &thread, const QVariantMap &interfaceProperties); |
1675 | void writeRolesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap); |
1676 | void writeRolesChangesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap); |
1677 | + |
1678 | + static History::MessageStatus fromTelepathyDeliveryStatus(Tp::DeliveryStatus deliveryStatus); |
1679 | + static History::ChatType fromTelepathyHandleType(const Tp::HandleType &type); |
1680 | private: |
1681 | HistoryDaemon(QObject *parent = 0); |
1682 | |
1683 | @@ -93,6 +103,7 @@ |
1684 | QMap<QString, History::MatchFlags> mProtocolFlags; |
1685 | History::PluginPtr mBackend; |
1686 | HistoryServiceDBus mDBus; |
1687 | + QMap<QString, RolesMap> mRolesMap; |
1688 | }; |
1689 | |
1690 | #endif |
1691 | |
1692 | === modified file 'daemon/historyservicedbus.cpp' |
1693 | --- daemon/historyservicedbus.cpp 2016-11-24 01:56:01 +0000 |
1694 | +++ daemon/historyservicedbus.cpp 2017-03-21 14:18:04 +0000 |
1695 | @@ -27,7 +27,7 @@ |
1696 | Q_DECLARE_METATYPE(QList< QVariantMap >) |
1697 | |
1698 | HistoryServiceDBus::HistoryServiceDBus(QObject *parent) : |
1699 | - QObject(parent), mAdaptor(0) |
1700 | + QObject(parent), mAdaptor(0), mSignalsTimer(-1) |
1701 | { |
1702 | qDBusRegisterMetaType<QList<QVariantMap> >(); |
1703 | } |
1704 | @@ -47,32 +47,52 @@ |
1705 | |
1706 | void HistoryServiceDBus::notifyThreadsAdded(const QList<QVariantMap> &threads) |
1707 | { |
1708 | - Q_EMIT ThreadsAdded(threads); |
1709 | + qDebug() << __PRETTY_FUNCTION__ << threads.count(); |
1710 | + mThreadsAdded << threads; |
1711 | + triggerSignals(); |
1712 | } |
1713 | |
1714 | void HistoryServiceDBus::notifyThreadsModified(const QList<QVariantMap> &threads) |
1715 | { |
1716 | - Q_EMIT ThreadsModified(threads); |
1717 | + qDebug() << __PRETTY_FUNCTION__ << threads.count(); |
1718 | + mThreadsModified << threads; |
1719 | + triggerSignals(); |
1720 | } |
1721 | |
1722 | void HistoryServiceDBus::notifyThreadsRemoved(const QList<QVariantMap> &threads) |
1723 | { |
1724 | - Q_EMIT ThreadsRemoved(threads); |
1725 | + qDebug() << __PRETTY_FUNCTION__ << threads.count(); |
1726 | + mThreadsRemoved << threads; |
1727 | + triggerSignals(); |
1728 | } |
1729 | |
1730 | void HistoryServiceDBus::notifyEventsAdded(const QList<QVariantMap> &events) |
1731 | { |
1732 | - Q_EMIT EventsAdded(events); |
1733 | + qDebug() << __PRETTY_FUNCTION__ << events.count(); |
1734 | + mEventsAdded << events; |
1735 | + triggerSignals(); |
1736 | } |
1737 | |
1738 | void HistoryServiceDBus::notifyEventsModified(const QList<QVariantMap> &events) |
1739 | { |
1740 | - Q_EMIT EventsModified(events); |
1741 | + qDebug() << __PRETTY_FUNCTION__ << events.count(); |
1742 | + mEventsModified << events; |
1743 | + triggerSignals(); |
1744 | } |
1745 | |
1746 | void HistoryServiceDBus::notifyEventsRemoved(const QList<QVariantMap> &events) |
1747 | { |
1748 | - Q_EMIT EventsRemoved(events); |
1749 | + qDebug() << __PRETTY_FUNCTION__ << events.count(); |
1750 | + mEventsRemoved << events; |
1751 | + triggerSignals(); |
1752 | +} |
1753 | + |
1754 | +void HistoryServiceDBus::notifyThreadParticipantsChanged(const QVariantMap &thread, |
1755 | + const QList<QVariantMap> &added, |
1756 | + const QList<QVariantMap> &removed, |
1757 | + const QList<QVariantMap> &modified) |
1758 | +{ |
1759 | + Q_EMIT ThreadParticipantsChanged(thread, added, removed, modified); |
1760 | } |
1761 | |
1762 | QVariantMap HistoryServiceDBus::ThreadForProperties(const QString &accountId, |
1763 | @@ -85,7 +105,12 @@ |
1764 | (History::EventType) type, |
1765 | properties, |
1766 | (History::MatchFlags) matchFlags, |
1767 | - create); |
1768 | + create); |
1769 | +} |
1770 | + |
1771 | +QList<QVariantMap> HistoryServiceDBus::ParticipantsForThreads(const QList<QVariantMap> &threadIds) |
1772 | +{ |
1773 | + return HistoryDaemon::instance()->participantsForThreads(threadIds); |
1774 | } |
1775 | |
1776 | QVariantMap HistoryServiceDBus::ThreadForParticipants(const QString &accountId, |
1777 | @@ -114,6 +139,11 @@ |
1778 | return HistoryDaemon::instance()->removeThreads(threads); |
1779 | } |
1780 | |
1781 | +void HistoryServiceDBus::MarkThreadsAsRead(const QList<QVariantMap> &threads) |
1782 | +{ |
1783 | + return HistoryDaemon::instance()->markThreadsAsRead(threads); |
1784 | +} |
1785 | + |
1786 | bool HistoryServiceDBus::RemoveEvents(const QList<QVariantMap> &events) |
1787 | { |
1788 | return HistoryDaemon::instance()->removeEvents(events); |
1789 | @@ -139,3 +169,76 @@ |
1790 | return HistoryDaemon::instance()->getSingleEvent(type, accountId, threadId, eventId); |
1791 | } |
1792 | |
1793 | +void HistoryServiceDBus::timerEvent(QTimerEvent *event) |
1794 | +{ |
1795 | + qDebug() << __PRETTY_FUNCTION__ << event->timerId(); |
1796 | + if (event->timerId() == mSignalsTimer) { |
1797 | + killTimer(mSignalsTimer); |
1798 | + mSignalsTimer = -1; |
1799 | + processSignals(); |
1800 | + } |
1801 | +} |
1802 | + |
1803 | +void HistoryServiceDBus::filterDuplicatesAndAdd(QList<QVariantMap> &targetList, const QList<QVariantMap> newItems, const QStringList &propertiesToCompare) |
1804 | +{ |
1805 | + Q_FOREACH (const QVariantMap &item, newItems) { |
1806 | + Q_FOREACH(const QVariantMap &existing, targetList) { |
1807 | + bool found = true; |
1808 | + Q_FOREACH(const QString &prop, propertiesToCompare) { |
1809 | + if (item[prop] != existing[prop]) { |
1810 | + found = false; |
1811 | + break; |
1812 | + } |
1813 | + } |
1814 | + |
1815 | + if (!found) { |
1816 | + targetList << item; |
1817 | + } |
1818 | + } |
1819 | + } |
1820 | +} |
1821 | + |
1822 | +void HistoryServiceDBus::triggerSignals() |
1823 | +{ |
1824 | + qDebug() << __PRETTY_FUNCTION__; |
1825 | + if (mSignalsTimer >= 0) { |
1826 | + killTimer(mSignalsTimer); |
1827 | + } |
1828 | + |
1829 | + mSignalsTimer = startTimer(100); |
1830 | +} |
1831 | + |
1832 | +void HistoryServiceDBus::processSignals() |
1833 | +{ |
1834 | + qDebug() << __PRETTY_FUNCTION__; |
1835 | + if (!mThreadsAdded.isEmpty()) { |
1836 | + Q_EMIT ThreadsAdded(mThreadsAdded); |
1837 | + mThreadsAdded.clear(); |
1838 | + } |
1839 | + |
1840 | + if (!mThreadsModified.isEmpty()) { |
1841 | + Q_EMIT ThreadsModified(mThreadsModified); |
1842 | + mThreadsModified.clear(); |
1843 | + } |
1844 | + |
1845 | + if (!mThreadsRemoved.isEmpty()) { |
1846 | + Q_EMIT ThreadsRemoved(mThreadsRemoved); |
1847 | + mThreadsRemoved.clear(); |
1848 | + } |
1849 | + |
1850 | + if (!mEventsAdded.isEmpty()) { |
1851 | + Q_EMIT EventsAdded(mEventsAdded); |
1852 | + mEventsAdded.clear(); |
1853 | + } |
1854 | + |
1855 | + if (!mEventsModified.isEmpty()) { |
1856 | + Q_EMIT EventsModified(mEventsModified); |
1857 | + mEventsModified.clear(); |
1858 | + } |
1859 | + |
1860 | + if (!mEventsRemoved.isEmpty()) { |
1861 | + Q_EMIT EventsRemoved(mEventsRemoved); |
1862 | + mEventsRemoved.clear(); |
1863 | + } |
1864 | +} |
1865 | + |
1866 | |
1867 | === modified file 'daemon/historyservicedbus.h' |
1868 | --- daemon/historyservicedbus.h 2016-06-17 01:49:46 +0000 |
1869 | +++ daemon/historyservicedbus.h 2017-03-21 14:18:04 +0000 |
1870 | @@ -39,6 +39,10 @@ |
1871 | void notifyThreadsAdded(const QList<QVariantMap> &threads); |
1872 | void notifyThreadsModified(const QList<QVariantMap> &threads); |
1873 | void notifyThreadsRemoved(const QList<QVariantMap> &threads); |
1874 | + void notifyThreadParticipantsChanged(const QVariantMap &thread, |
1875 | + const QList<QVariantMap> &added, |
1876 | + const QList<QVariantMap> &removed, |
1877 | + const QList<QVariantMap> &modified); |
1878 | |
1879 | void notifyEventsAdded(const QList<QVariantMap> &events); |
1880 | void notifyEventsModified(const QList<QVariantMap> &events); |
1881 | @@ -55,9 +59,11 @@ |
1882 | const QVariantMap &properties, |
1883 | int matchFlags, |
1884 | bool create); |
1885 | + QList<QVariantMap> ParticipantsForThreads(const QList<QVariantMap> &threadIds); |
1886 | bool WriteEvents(const QList <QVariantMap> &events); |
1887 | bool RemoveThreads(const QList <QVariantMap> &threads); |
1888 | bool RemoveEvents(const QList <QVariantMap> &events); |
1889 | + void MarkThreadsAsRead(const QList <QVariantMap> &threads); |
1890 | |
1891 | // views |
1892 | QString QueryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties); |
1893 | @@ -70,13 +76,32 @@ |
1894 | void ThreadsAdded(const QList<QVariantMap> &threads); |
1895 | void ThreadsModified(const QList<QVariantMap> &threads); |
1896 | void ThreadsRemoved(const QList<QVariantMap> &threads); |
1897 | + void ThreadParticipantsChanged(const QVariantMap &thread, |
1898 | + const QList<QVariantMap> &added, |
1899 | + const QList<QVariantMap> &removed, |
1900 | + const QList<QVariantMap> &modified); |
1901 | |
1902 | void EventsAdded(const QList<QVariantMap> &events); |
1903 | void EventsModified(const QList<QVariantMap> &events); |
1904 | void EventsRemoved(const QList<QVariantMap> &events); |
1905 | |
1906 | +protected: |
1907 | + void timerEvent(QTimerEvent *event) override; |
1908 | + |
1909 | +protected Q_SLOTS: |
1910 | + void filterDuplicatesAndAdd(QList<QVariantMap> &targetList, const QList<QVariantMap> newItems, const QStringList &propertiesToCompare); |
1911 | + void triggerSignals(); |
1912 | + void processSignals(); |
1913 | + |
1914 | private: |
1915 | HistoryServiceAdaptor *mAdaptor; |
1916 | + QList<QVariantMap> mThreadsAdded; |
1917 | + QList<QVariantMap> mThreadsModified; |
1918 | + QList<QVariantMap> mThreadsRemoved; |
1919 | + QList<QVariantMap> mEventsAdded; |
1920 | + QList<QVariantMap> mEventsModified; |
1921 | + QList<QVariantMap> mEventsRemoved; |
1922 | + int mSignalsTimer; |
1923 | }; |
1924 | |
1925 | #endif // HISTORYSERVICEDBUS_H |
1926 | |
1927 | === modified file 'daemon/main.cpp' |
1928 | --- daemon/main.cpp 2016-03-30 14:03:29 +0000 |
1929 | +++ daemon/main.cpp 2017-03-21 14:18:04 +0000 |
1930 | @@ -20,16 +20,26 @@ |
1931 | */ |
1932 | |
1933 | #include "historydaemon.h" |
1934 | +#include <QLockFile> |
1935 | +#include <QDir> |
1936 | |
1937 | bool checkApplicationRunning() |
1938 | { |
1939 | - bool result = false; |
1940 | - QDBusReply<bool> reply = QDBusConnection::sessionBus().interface()->isServiceRegistered(History::DBusService); |
1941 | - if (reply.isValid()) { |
1942 | - result = reply.value(); |
1943 | + QString lockPath = qgetenv("HISTORY_LOCK_FILE"); |
1944 | + if (lockPath.isEmpty()) { |
1945 | + lockPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); |
1946 | + QDir dir(lockPath); |
1947 | + if (!dir.exists("history-service") && !dir.mkpath("history-service")) { |
1948 | + qCritical() << "Failed to create dir"; |
1949 | + // in case we fail to create the lock, better not even start the application |
1950 | + return true; |
1951 | + } |
1952 | + dir.cd("history-service"); |
1953 | + lockPath = dir.absoluteFilePath("history-daemon.lock"); |
1954 | } |
1955 | |
1956 | - return result; |
1957 | + static QLockFile *lockFile = new QLockFile(lockPath); |
1958 | + return !lockFile->tryLock(); |
1959 | } |
1960 | int main(int argc, char **argv) |
1961 | { |
1962 | |
1963 | === modified file 'daemon/textchannelobserver.cpp' |
1964 | --- daemon/textchannelobserver.cpp 2016-08-26 14:10:25 +0000 |
1965 | +++ daemon/textchannelobserver.cpp 2017-03-21 14:18:04 +0000 |
1966 | @@ -39,10 +39,6 @@ |
1967 | connect(textChannel.data(), |
1968 | SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString)), |
1969 | SLOT(onMessageSent(Tp::Message,Tp::MessageSendingFlags,QString))); |
1970 | - connect(textChannel.data(), |
1971 | - SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)), |
1972 | - SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&))); |
1973 | - |
1974 | Q_EMIT channelAvailable(textChannel); |
1975 | |
1976 | // process the messages that are already pending in the channel |
1977 | @@ -57,6 +53,7 @@ |
1978 | { |
1979 | Tp::TextChannelPtr textChannel(qobject_cast<Tp::TextChannel*>(sender())); |
1980 | mChannels.removeAll(textChannel); |
1981 | + Q_EMIT textChannelInvalidated(textChannel); |
1982 | } |
1983 | |
1984 | void TextChannelObserver::onMessageReceived(const Tp::ReceivedMessage &message) |
1985 | @@ -82,13 +79,3 @@ |
1986 | |
1987 | Q_EMIT messageSent(textChannel, message, sentMessageToken); |
1988 | } |
1989 | - |
1990 | -void TextChannelObserver::onPendingMessageRemoved(const Tp::ReceivedMessage &message) |
1991 | -{ |
1992 | - Tp::TextChannelPtr textChannel(qobject_cast<Tp::TextChannel*>(sender())); |
1993 | - if (textChannel.isNull()) { |
1994 | - return; |
1995 | - } |
1996 | - |
1997 | - Q_EMIT messageRead(textChannel, message); |
1998 | -} |
1999 | |
2000 | === modified file 'daemon/textchannelobserver.h' |
2001 | --- daemon/textchannelobserver.h 2016-08-26 14:10:25 +0000 |
2002 | +++ daemon/textchannelobserver.h 2017-03-21 14:18:04 +0000 |
2003 | @@ -37,8 +37,8 @@ |
2004 | |
2005 | Q_SIGNALS: |
2006 | void channelAvailable(const Tp::TextChannelPtr textChannel); |
2007 | + void textChannelInvalidated(const Tp::TextChannelPtr textChannel); |
2008 | void messageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); |
2009 | - void messageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); |
2010 | void messageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken); |
2011 | |
2012 | protected: |
2013 | @@ -49,7 +49,6 @@ |
2014 | void onTextChannelInvalidated(); |
2015 | void onMessageReceived(const Tp::ReceivedMessage &message); |
2016 | void onMessageSent(const Tp::Message &message, Tp::MessageSendingFlags flags, const QString &sentMessageToken); |
2017 | - void onPendingMessageRemoved(const Tp::ReceivedMessage &message); |
2018 | |
2019 | private: |
2020 | QList<Tp::TextChannelPtr> mChannels; |
2021 | |
2022 | === added file 'plugins/sqlite/schema/v18.sql' |
2023 | --- plugins/sqlite/schema/v18.sql 1970-01-01 00:00:00 +0000 |
2024 | +++ plugins/sqlite/schema/v18.sql 2017-03-21 14:18:04 +0000 |
2025 | @@ -0,0 +1,14 @@ |
2026 | +CREATE TRIGGER text_threads_delete_trigger AFTER DELETE ON threads |
2027 | +FOR EACH ROW WHEN old.type=0 |
2028 | +BEGIN |
2029 | + DELETE FROM text_events WHERE |
2030 | + accountId=old.accountId AND |
2031 | + threadId=old.threadId; |
2032 | +END; |
2033 | +CREATE TRIGGER voice_threads_delete_trigger AFTER DELETE ON threads |
2034 | +FOR EACH ROW WHEN old.type=1 |
2035 | +BEGIN |
2036 | + DELETE FROM voice_events WHERE |
2037 | + accountId=old.accountId AND |
2038 | + threadId=old.threadId; |
2039 | +END; |
2040 | |
2041 | === modified file 'plugins/sqlite/sqlitedatabase.cpp' |
2042 | --- plugins/sqlite/sqlitedatabase.cpp 2016-11-08 19:43:53 +0000 |
2043 | +++ plugins/sqlite/sqlitedatabase.cpp 2017-03-21 14:18:04 +0000 |
2044 | @@ -183,6 +183,12 @@ |
2045 | return true; |
2046 | } |
2047 | |
2048 | + |
2049 | +void trace(void *something, const char *query) |
2050 | +{ |
2051 | + qDebug() << "SQLITE TRACE:" << query; |
2052 | +} |
2053 | + |
2054 | bool SQLiteDatabase::createOrUpdateDatabase() |
2055 | { |
2056 | bool create = !QFile(mDatabasePath).exists(); |
2057 | @@ -199,6 +205,10 @@ |
2058 | // and also create the normalizeId function |
2059 | sqlite3_create_function(handle, "normalizeId", 2, SQLITE_ANY, NULL, &normalizeId, NULL, NULL); |
2060 | |
2061 | +#ifdef TRACE_SQLITE |
2062 | + sqlite3_trace(handle, &trace, NULL); |
2063 | +#endif |
2064 | + |
2065 | parseVersionInfo(); |
2066 | |
2067 | QSqlQuery query(mDatabase); |
2068 | |
2069 | === modified file 'plugins/sqlite/sqlitehistoryeventview.cpp' |
2070 | --- plugins/sqlite/sqlitehistoryeventview.cpp 2015-01-28 23:08:01 +0000 |
2071 | +++ plugins/sqlite/sqlitehistoryeventview.cpp 2017-03-21 14:18:04 +0000 |
2072 | @@ -42,7 +42,14 @@ |
2073 | QString condition = mPlugin->filterToString(filter, filterValues); |
2074 | QString order; |
2075 | if (!sort.sortField().isNull()) { |
2076 | - order = QString("ORDER BY %1 %2").arg(sort.sortField(), sort.sortOrder() == Qt::AscendingOrder ? "ASC" : "DESC"); |
2077 | + // WORKAROUND: Supports multiple fields by split it using ',' |
2078 | + Q_FOREACH(const QString& field, sort.sortField().split(",")) { |
2079 | + order += QString("%1 %2, ") |
2080 | + .arg(field.trimmed()) |
2081 | + .arg(sort.sortOrder() == Qt::AscendingOrder ? "ASC" : "DESC"); |
2082 | + } |
2083 | + |
2084 | + order = QString("ORDER BY %1").arg(order.mid(0, order.lastIndexOf(","))); |
2085 | // FIXME: check case sensitiviy |
2086 | } |
2087 | |
2088 | |
2089 | === modified file 'plugins/sqlite/sqlitehistoryplugin.cpp' |
2090 | --- plugins/sqlite/sqlitehistoryplugin.cpp 2016-11-24 01:50:48 +0000 |
2091 | +++ plugins/sqlite/sqlitehistoryplugin.cpp 2017-03-21 14:18:04 +0000 |
2092 | @@ -306,6 +306,54 @@ |
2093 | return new SQLiteHistoryEventView(this, type, sort, filter); |
2094 | } |
2095 | |
2096 | +QVariantMap SQLiteHistoryPlugin::markThreadAsRead(const QVariantMap &thread) |
2097 | +{ |
2098 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
2099 | + |
2100 | + if (thread[History::FieldAccountId].toString().isEmpty() || |
2101 | + thread[History::FieldThreadId].toString().isEmpty()) { |
2102 | + return QVariantMap(); |
2103 | + } |
2104 | + |
2105 | + // first check if the thread actually has anything to change |
2106 | + query.prepare("SELECT unreadCount from threads WHERE accountId=:accountId AND threadId=:threadId AND type=:type"); |
2107 | + query.bindValue(":accountId", thread[History::FieldAccountId].toString()); |
2108 | + query.bindValue(":threadId", thread[History::FieldThreadId].toString()); |
2109 | + query.bindValue(":type", (uint)History::EventTypeText); |
2110 | + if (!query.exec() || !query.next()) { |
2111 | + qCritical() << "Failed to verify the unread messages of the thread. Error:" << query.lastError(); |
2112 | + return QVariantMap(); |
2113 | + } |
2114 | + |
2115 | + |
2116 | + int unreadCount = query.value(0).toUInt(); |
2117 | + if (unreadCount == 0) { |
2118 | + // no messages to ack, so no need to update anything |
2119 | + return QVariantMap(); |
2120 | + } |
2121 | + |
2122 | + query.prepare("UPDATE text_events SET newEvent=:newEvent WHERE accountId=:accountId AND threadId=:threadId AND newEvent=1"); |
2123 | + query.bindValue(":accountId", thread[History::FieldAccountId].toString()); |
2124 | + query.bindValue(":threadId", thread[History::FieldThreadId].toString()); |
2125 | + query.bindValue(":newEvent", false); |
2126 | + |
2127 | + if (!query.exec()) { |
2128 | + qCritical() << "Failed to mark thread as read: Error:" << query.lastError(); |
2129 | + return QVariantMap(); |
2130 | + } |
2131 | + |
2132 | + QVariantMap existingThread = getSingleThread((History::EventType) thread[History::FieldType].toInt(), |
2133 | + thread[History::FieldAccountId].toString(), |
2134 | + thread[History::FieldThreadId].toString(), |
2135 | + QVariantMap()); |
2136 | + if (!existingThread.isEmpty()) { |
2137 | + addThreadsToCache(QList<QVariantMap>() << existingThread); |
2138 | + return existingThread; |
2139 | + } |
2140 | + |
2141 | + return QVariantMap(); |
2142 | +} |
2143 | + |
2144 | QVariantMap SQLiteHistoryPlugin::threadForProperties(const QString &accountId, |
2145 | History::EventType type, |
2146 | const QVariantMap &properties, |
2147 | @@ -315,8 +363,6 @@ |
2148 | return QVariantMap(); |
2149 | } |
2150 | |
2151 | - QSqlQuery query(SQLiteDatabase::instance()->database()); |
2152 | - |
2153 | History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toUInt(); |
2154 | |
2155 | if (chatType == History::ChatTypeRoom) { |
2156 | @@ -332,6 +378,62 @@ |
2157 | return threadForParticipants(accountId, type, participants.identifiers(), matchFlags); |
2158 | } |
2159 | |
2160 | +QString SQLiteHistoryPlugin::threadIdForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties, History::MatchFlags matchFlags) |
2161 | +{ |
2162 | + if (properties.isEmpty()) { |
2163 | + return QString::null; |
2164 | + } |
2165 | + |
2166 | + // if chat type is room, just get the threadId directly |
2167 | + History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toUInt(); |
2168 | + if (chatType == History::ChatTypeRoom) { |
2169 | + QString threadId = properties[History::FieldThreadId].toString(); |
2170 | + return threadId; |
2171 | + } |
2172 | + |
2173 | + // if chat type is anything else, fallback to returning the threadId from the participants list |
2174 | + History::Participants participants = History::Participants::fromVariant(properties[History::FieldParticipantIds]); |
2175 | + return threadForParticipants(accountId, type, participants.identifiers(), matchFlags)[History::FieldThreadId].toString(); |
2176 | +} |
2177 | + |
2178 | +QList<QVariantMap> SQLiteHistoryPlugin::participantsForThreads(const QList<QVariantMap> &threadIds) |
2179 | +{ |
2180 | + QList<QVariantMap> results; |
2181 | + Q_FOREACH(const QVariantMap &thread, threadIds) { |
2182 | + QString accountId = thread[History::FieldAccountId].toString(); |
2183 | + QString threadId = thread[History::FieldThreadId].toString(); |
2184 | + History::EventType type = (History::EventType)thread[History::FieldType].toUInt(); |
2185 | + QVariantMap result = thread; |
2186 | + |
2187 | + QSqlQuery query; |
2188 | + query.prepare("SELECT normalizedId, alias, state, roles FROM thread_participants " |
2189 | + "WHERE accountId=:accountId AND threadId=:threadId AND type=:type"); |
2190 | + query.bindValue(":accountId", accountId); |
2191 | + query.bindValue(":threadId", threadId); |
2192 | + query.bindValue(":type", type); |
2193 | + QVariantList participants; |
2194 | + if (!query.exec()) { |
2195 | + qWarning() << "Failed to retrieve participants. Error:" << query.lastError().text() << query.lastQuery(); |
2196 | + results << result; |
2197 | + continue; |
2198 | + } |
2199 | + |
2200 | + while (query.next()) { |
2201 | + QVariantMap participant; |
2202 | + QString identifier = query.value(0).toString(); |
2203 | + participant[History::FieldIdentifier] = identifier; |
2204 | + participant[History::FieldAlias] = query.value(1); |
2205 | + participant[History::FieldParticipantState] = query.value(2); |
2206 | + participant[History::FieldParticipantRoles] = query.value(3); |
2207 | + participants << History::ContactMatcher::instance()->contactInfo(accountId, identifier, true, participant); |
2208 | + } |
2209 | + |
2210 | + result[History::FieldParticipants] = participants; |
2211 | + results << result; |
2212 | + } |
2213 | + return results; |
2214 | +} |
2215 | + |
2216 | QVariantMap SQLiteHistoryPlugin::threadForParticipants(const QString &accountId, |
2217 | History::EventType type, |
2218 | const QStringList &participants, |
2219 | @@ -1093,22 +1195,6 @@ |
2220 | << "threads.unreadCount" |
2221 | << "threads.lastEventTimestamp"; |
2222 | |
2223 | - // get the participants in the query already |
2224 | - fields << "(SELECT group_concat(thread_participants.participantId, \"|,|\") " |
2225 | - "FROM thread_participants WHERE thread_participants.accountId=threads.accountId " |
2226 | - "AND thread_participants.threadId=threads.threadId " |
2227 | - "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as participants"; |
2228 | - |
2229 | - fields << "(SELECT group_concat(thread_participants.state, \"|,|\") " |
2230 | - "FROM thread_participants WHERE thread_participants.accountId=threads.accountId " |
2231 | - "AND thread_participants.threadId=threads.threadId " |
2232 | - "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as state"; |
2233 | - |
2234 | - fields << "(SELECT group_concat(thread_participants.roles, \"|,|\") " |
2235 | - "FROM thread_participants WHERE thread_participants.accountId=threads.accountId " |
2236 | - "AND thread_participants.threadId=threads.threadId " |
2237 | - "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as roles"; |
2238 | - |
2239 | QStringList extraFields; |
2240 | QString table; |
2241 | |
2242 | @@ -1136,6 +1222,7 @@ |
2243 | QList<QVariantMap> SQLiteHistoryPlugin::parseThreadResults(History::EventType type, QSqlQuery &query, const QVariantMap &properties) |
2244 | { |
2245 | QList<QVariantMap> threads; |
2246 | + QList<QVariantMap> threadsWithoutParticipants; |
2247 | QSqlQuery attachmentsQuery(SQLiteDatabase::instance()->database()); |
2248 | QList<QVariantMap> attachments; |
2249 | bool grouped = false; |
2250 | @@ -1170,31 +1257,11 @@ |
2251 | thread[History::FieldEventId] = query.value(2); |
2252 | thread[History::FieldCount] = query.value(3); |
2253 | thread[History::FieldUnreadCount] = query.value(4); |
2254 | - QStringList participants = query.value(6).toString().split("|,|", QString::SkipEmptyParts); |
2255 | - QList<int> participantStatus; |
2256 | - QStringList participantStatusString = query.value(7).toString().split("|,|", QString::SkipEmptyParts); |
2257 | - Q_FOREACH(const QString &statusString, participantStatusString) { |
2258 | - participantStatus << statusString.toUInt(); |
2259 | - } |
2260 | - QStringList participantRolesString = query.value(8).toString().split("|,|", QString::SkipEmptyParts); |
2261 | - QList<int> participantRoles; |
2262 | - Q_FOREACH(const QString &rolesString, participantRolesString) { |
2263 | - participantRoles << rolesString.toUInt(); |
2264 | - } |
2265 | - QVariantList contactList; |
2266 | - QVariantList contactInfo = History::ContactMatcher::instance()->contactInfo(accountId, participants, true); |
2267 | - for (int i = 0; i < contactInfo.count(); ++i) { |
2268 | - QVariantMap map = contactInfo[i].toMap(); |
2269 | - map["state"] = participantStatus[i]; |
2270 | - map["roles"] = participantRoles[i]; |
2271 | - contactList << map; |
2272 | - } |
2273 | - thread[History::FieldParticipants] = contactList; |
2274 | |
2275 | // the generic event fields |
2276 | - thread[History::FieldSenderId] = query.value(9); |
2277 | + thread[History::FieldSenderId] = query.value(6); |
2278 | thread[History::FieldTimestamp] = toLocalTimeString(query.value(5).toDateTime()); |
2279 | - thread[History::FieldNewEvent] = query.value(10).toBool(); |
2280 | + thread[History::FieldNewEvent] = query.value(7).toBool(); |
2281 | |
2282 | // the next step is to get the last event |
2283 | switch (type) { |
2284 | @@ -1225,13 +1292,13 @@ |
2285 | thread[History::FieldAttachments] = QVariant::fromValue(attachments); |
2286 | attachments.clear(); |
2287 | } |
2288 | - thread[History::FieldMessage] = query.value(11); |
2289 | - thread[History::FieldMessageType] = query.value(12); |
2290 | - thread[History::FieldMessageStatus] = query.value(13); |
2291 | - thread[History::FieldReadTimestamp] = toLocalTimeString(query.value(14).toDateTime()); |
2292 | - thread[History::FieldChatType] = query.value(15).toUInt(); |
2293 | + thread[History::FieldMessage] = query.value(8); |
2294 | + thread[History::FieldMessageType] = query.value(9); |
2295 | + thread[History::FieldMessageStatus] = query.value(10); |
2296 | + thread[History::FieldReadTimestamp] = toLocalTimeString(query.value(11).toDateTime()); |
2297 | + thread[History::FieldChatType] = query.value(12).toUInt(); |
2298 | |
2299 | - if (thread[History::FieldChatType].toInt() == 2) { |
2300 | + if (thread[History::FieldChatType].toInt() == History::ChatTypeRoom) { |
2301 | QVariantMap chatRoomInfo; |
2302 | QSqlQuery query1(SQLiteDatabase::instance()->database()); |
2303 | |
2304 | @@ -1291,15 +1358,28 @@ |
2305 | |
2306 | thread[History::FieldChatRoomInfo] = chatRoomInfo; |
2307 | } |
2308 | + if (!History::Utils::shouldIncludeParticipants(History::Thread::fromProperties(thread))) { |
2309 | + thread.remove(History::FieldParticipants); |
2310 | + threadsWithoutParticipants << thread; |
2311 | + } else { |
2312 | + threads << thread; |
2313 | + } |
2314 | break; |
2315 | case History::EventTypeVoice: |
2316 | - thread[History::FieldMissed] = query.value(12); |
2317 | - thread[History::FieldDuration] = query.value(11); |
2318 | - thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(13).toString(), true); |
2319 | + thread[History::FieldMissed] = query.value(9); |
2320 | + thread[History::FieldDuration] = query.value(8); |
2321 | + thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(10).toString(), true); |
2322 | + threads << thread; |
2323 | break; |
2324 | } |
2325 | - threads << thread; |
2326 | } |
2327 | + |
2328 | + // get the participants |
2329 | + threads = participantsForThreads(threads); |
2330 | + |
2331 | + // and append the threads with no participants |
2332 | + threads << threadsWithoutParticipants; |
2333 | + |
2334 | return threads; |
2335 | } |
2336 | |
2337 | @@ -1317,7 +1397,8 @@ |
2338 | QString queryText; |
2339 | switch (type) { |
2340 | case History::EventTypeText: |
2341 | - participantsField = participantsField.arg("text_events", QString::number(type)); |
2342 | + // for text events we don't need the participants at all |
2343 | + participantsField = "\"\" as participants"; |
2344 | queryText = QString("SELECT accountId, threadId, eventId, senderId, timestamp, newEvent, %1, " |
2345 | "message, messageType, messageStatus, readTimestamp, subject, informationType FROM text_events %2 %3").arg(participantsField, modifiedCondition, order); |
2346 | break; |
2347 | @@ -1353,8 +1434,10 @@ |
2348 | event[History::FieldSenderId] = query.value(3); |
2349 | event[History::FieldTimestamp] = toLocalTimeString(query.value(4).toDateTime()); |
2350 | event[History::FieldNewEvent] = query.value(5).toBool(); |
2351 | - QStringList participants = query.value(6).toString().split("|,|"); |
2352 | - event[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true); |
2353 | + if (type != History::EventTypeText) { |
2354 | + QStringList participants = query.value(6).toString().split("|,|"); |
2355 | + event[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true); |
2356 | + } |
2357 | |
2358 | switch (type) { |
2359 | case History::EventTypeText: |
2360 | |
2361 | === modified file 'plugins/sqlite/sqlitehistoryplugin.h' |
2362 | --- plugins/sqlite/sqlitehistoryplugin.h 2016-09-21 17:44:39 +0000 |
2363 | +++ plugins/sqlite/sqlitehistoryplugin.h 2017-03-21 14:18:04 +0000 |
2364 | @@ -59,7 +59,11 @@ |
2365 | History::EventType type, |
2366 | const QVariantMap &properties, |
2367 | History::MatchFlags matchFlags = History::MatchCaseSensitive); |
2368 | - |
2369 | + QString threadIdForProperties(const QString &accountId, |
2370 | + History::EventType type, |
2371 | + const QVariantMap &properties, |
2372 | + History::MatchFlags matchFlags = History::MatchCaseSensitive) override; |
2373 | + QList<QVariantMap> participantsForThreads(const QList<QVariantMap> &threadIds) override; |
2374 | QList<QVariantMap> eventsForThread(const QVariantMap &thread); |
2375 | |
2376 | QVariantMap getSingleThread(History::EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties = QVariantMap()); |
2377 | @@ -73,6 +77,7 @@ |
2378 | bool updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles); |
2379 | bool updateRoomInfo(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated = QStringList()); |
2380 | bool removeThread(const QVariantMap &thread); |
2381 | + QVariantMap markThreadAsRead(const QVariantMap &thread); |
2382 | |
2383 | History::EventWriteResult writeTextEvent(const QVariantMap &event); |
2384 | bool removeTextEvent(const QVariantMap &event); |
2385 | |
2386 | === modified file 'plugins/sqlite/sqlitehistorythreadview.cpp' |
2387 | --- plugins/sqlite/sqlitehistorythreadview.cpp 2016-11-24 01:56:01 +0000 |
2388 | +++ plugins/sqlite/sqlitehistorythreadview.cpp 2017-03-21 14:18:04 +0000 |
2389 | @@ -43,7 +43,14 @@ |
2390 | QString condition = mPlugin->filterToString(filter, filterValues); |
2391 | QString order; |
2392 | if (!sort.sortField().isNull()) { |
2393 | - order = QString("ORDER BY %1 %2").arg(sort.sortField(), sort.sortOrder() == Qt::AscendingOrder ? "ASC" : "DESC"); |
2394 | + // WORKAROUND: Supports multiple fields by split it using ',' |
2395 | + Q_FOREACH(const QString& field, sort.sortField().split(",")) { |
2396 | + order += QString("%1 %2, ") |
2397 | + .arg(field.trimmed()) |
2398 | + .arg(sort.sortOrder() == Qt::AscendingOrder ? "ASC" : "DESC"); |
2399 | + } |
2400 | + |
2401 | + order = QString("ORDER BY %1").arg(order.mid(0, order.lastIndexOf(","))); |
2402 | // FIXME: check case sensitiviy |
2403 | } |
2404 | |
2405 | |
2406 | === modified file 'src/contactmatcher.cpp' |
2407 | --- src/contactmatcher.cpp 2016-06-17 01:49:46 +0000 |
2408 | +++ src/contactmatcher.cpp 2017-03-21 14:18:04 +0000 |
2409 | @@ -99,21 +99,22 @@ |
2410 | { |
2411 | InternalContactMap &internalMap = mContactMap[accountId]; |
2412 | |
2413 | + |
2414 | + QString normalizedId = normalizeId(identifier); |
2415 | + |
2416 | + QVariantMap map; |
2417 | // first do a simple string match on the map |
2418 | - if (internalMap.contains(identifier)) { |
2419 | - return internalMap[identifier]; |
2420 | - } |
2421 | - |
2422 | - QVariantMap map; |
2423 | - |
2424 | - // and if there was no match, asynchronously request the info, and return an empty map for now |
2425 | - if (History::TelepathyHelper::instance()->ready()) { |
2426 | - map = requestContactInfo(accountId, identifier, synchronous); |
2427 | + if (internalMap.contains(normalizedId)) { |
2428 | + map = internalMap[normalizedId]; |
2429 | + } else if (History::TelepathyHelper::instance()->ready()) { |
2430 | + // and if there was no match, asynchronously request the info, and return an empty map for now |
2431 | + map = requestContactInfo(accountId, normalizedId, synchronous); |
2432 | } else if (!synchronous) { |
2433 | - RequestInfo info{accountId, identifier}; |
2434 | + RequestInfo info{accountId, normalizedId}; |
2435 | mPendingRequests.append(info); |
2436 | } |
2437 | - map[History::FieldIdentifier] = identifier; |
2438 | + |
2439 | + map[History::FieldIdentifier] = normalizedId; |
2440 | map[History::FieldAccountId] = accountId; |
2441 | |
2442 | QMapIterator<QString, QVariant> i(properties); |
2443 | @@ -124,7 +125,7 @@ |
2444 | } |
2445 | } |
2446 | |
2447 | - mContactMap[accountId][identifier] = map; |
2448 | + mContactMap[accountId][normalizedId] = map; |
2449 | return map; |
2450 | } |
2451 | |
2452 | @@ -309,10 +310,17 @@ |
2453 | */ |
2454 | QVariantMap ContactMatcher::requestContactInfo(const QString &accountId, const QString &identifier, bool synchronous) |
2455 | { |
2456 | + QString normalizedId = normalizeId(identifier); |
2457 | QStringList addressableVCardFields = addressableFields(accountId); |
2458 | + |
2459 | + QVariantMap contactInfo; |
2460 | + contactInfo[History::FieldIdentifier] = identifier; |
2461 | + contactInfo[History::FieldAccountId] = accountId; |
2462 | + |
2463 | if (addressableVCardFields.isEmpty()) { |
2464 | + mContactMap[accountId][identifier] = contactInfo; |
2465 | // FIXME: add support for generic accounts |
2466 | - return QVariantMap(); |
2467 | + return contactInfo; |
2468 | } |
2469 | |
2470 | bool phoneCompare = addressableVCardFields.contains("tel"); |
2471 | @@ -328,7 +336,7 @@ |
2472 | QContactUnionFilter topLevelFilter; |
2473 | Q_FOREACH(const QString &field, addressableVCardFields) { |
2474 | if (field == "tel") { |
2475 | - topLevelFilter.append(QContactPhoneNumber::match(identifier)); |
2476 | + topLevelFilter.append(QContactPhoneNumber::match(normalizedId)); |
2477 | } else { |
2478 | // FIXME: handle more fields |
2479 | // rely on a generic field filter |
2480 | @@ -340,7 +348,7 @@ |
2481 | QContactDetailFilter valueFilter = QContactDetailFilter(); |
2482 | valueFilter.setDetailType(QContactExtendedDetail::Type, QContactExtendedDetail::FieldData); |
2483 | valueFilter.setMatchFlags(QContactFilter::MatchExactly); |
2484 | - valueFilter.setValue(identifier); |
2485 | + valueFilter.setValue(normalizedId); |
2486 | |
2487 | QContactIntersectionFilter intersectionFilter; |
2488 | intersectionFilter.append(nameFilter); |
2489 | @@ -353,10 +361,11 @@ |
2490 | if (synchronous) { |
2491 | QList<QContact> contacts = mManager->contacts(topLevelFilter, QList<QContactSortOrder>(), hint); |
2492 | if (contacts.isEmpty()) { |
2493 | - return QVariantMap(); |
2494 | + mContactMap[accountId][identifier] = contactInfo; |
2495 | + return contactInfo; |
2496 | } |
2497 | // for synchronous requests, return the results right away. |
2498 | - return matchAndUpdate(accountId, identifier, contacts.first()); |
2499 | + return matchAndUpdate(accountId, normalizedId, contacts.first()); |
2500 | } else { |
2501 | // check if there is a request already going on for the given contact |
2502 | Q_FOREACH(const RequestInfo &info, mRequests.values()) { |
2503 | @@ -365,7 +374,7 @@ |
2504 | continue; |
2505 | } |
2506 | |
2507 | - if (info.identifier == identifier) { |
2508 | + if (info.identifier == normalizedId) { |
2509 | // if so, just wait for it to finish |
2510 | return QVariantMap(); |
2511 | } |
2512 | @@ -381,7 +390,7 @@ |
2513 | |
2514 | RequestInfo info; |
2515 | info.accountId = accountId; |
2516 | - info.identifier = identifier; |
2517 | + info.identifier = normalizedId; |
2518 | mRequests[request] = info; |
2519 | request->start(); |
2520 | } |
2521 | @@ -414,7 +423,6 @@ |
2522 | QStringList fields = addressableFields(accountId); |
2523 | bool match = false; |
2524 | |
2525 | - int fieldsCount = fields.count(); |
2526 | Q_FOREACH(const QString &field, fields) { |
2527 | if (field == "tel") { |
2528 | QList<QContactDetail> details = contact.details(QContactDetail::TypePhoneNumber); |
2529 | @@ -463,12 +471,26 @@ |
2530 | return mAddressableFields[accountId]; |
2531 | } |
2532 | |
2533 | + // FIXME: hardcoding account IDs here is not a good idea, we have to fix addressable fields on |
2534 | + // the protocols themselves |
2535 | + if (accountId.startsWith("irc/irc")) { |
2536 | + QStringList empty; |
2537 | + mAddressableFields[accountId] = empty; |
2538 | + return empty; |
2539 | + } |
2540 | + |
2541 | Tp::AccountPtr account = History::TelepathyHelper::instance()->accountForId(accountId); |
2542 | QStringList fields; |
2543 | if (!account.isNull()) { |
2544 | fields = account->protocolInfo().addressableVCardFields(); |
2545 | - mAddressableFields[accountId] = fields; |
2546 | - } |
2547 | + } |
2548 | + |
2549 | + // fallback to phone number matching in case everything else fails |
2550 | + if (fields.isEmpty()) { |
2551 | + fields << "tel"; |
2552 | + } |
2553 | + |
2554 | + mAddressableFields[accountId] = fields; |
2555 | |
2556 | return fields; |
2557 | } |
2558 | @@ -478,4 +500,17 @@ |
2559 | return (map.contains(History::FieldContactId) && !map[History::FieldContactId].toString().isEmpty()); |
2560 | } |
2561 | |
2562 | +QString ContactMatcher::normalizeId(const QString &id) |
2563 | +{ |
2564 | + QString normalizedId = id; |
2565 | + |
2566 | + // FIXME: this is a hack so that SIP URIs get converted into phone numbers for contact matching |
2567 | + if (normalizedId.startsWith("sip:")) { |
2568 | + normalizedId.remove("sip:").remove(QRegularExpression("@.*$")); |
2569 | + } |
2570 | + |
2571 | + return normalizedId; |
2572 | +} |
2573 | + |
2574 | + |
2575 | } |
2576 | |
2577 | === modified file 'src/contactmatcher_p.h' |
2578 | --- src/contactmatcher_p.h 2016-06-17 01:49:46 +0000 |
2579 | +++ src/contactmatcher_p.h 2017-03-21 14:18:04 +0000 |
2580 | @@ -51,6 +51,8 @@ |
2581 | // this will only watch for contact changes affecting the identifier, but won't fetch contact info |
2582 | void watchIdentifier(const QString &accountId, const QString &identifier, const QVariantMap ¤tInfo = QVariantMap()); |
2583 | |
2584 | + static QString normalizeId(const QString &id); |
2585 | + |
2586 | Q_SIGNALS: |
2587 | void contactInfoChanged(const QString &acountId, const QString &identifier, const QVariantMap &contactInfo); |
2588 | |
2589 | |
2590 | === modified file 'src/eventview.cpp' |
2591 | --- src/eventview.cpp 2015-10-01 19:44:45 +0000 |
2592 | +++ src/eventview.cpp 2017-03-21 14:18:04 +0000 |
2593 | @@ -1,5 +1,5 @@ |
2594 | /* |
2595 | - * Copyright (C) 2013 Canonical, Ltd. |
2596 | + * Copyright (C) 2013-2017 Canonical, Ltd. |
2597 | * |
2598 | * Authors: |
2599 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
2600 | @@ -53,7 +53,7 @@ |
2601 | } |
2602 | |
2603 | if (filterNull || filter.match(event.properties())) { |
2604 | - filtered << events; |
2605 | + filtered << event; |
2606 | } |
2607 | } |
2608 | |
2609 | @@ -128,6 +128,10 @@ |
2610 | connect(Manager::instance(), |
2611 | SIGNAL(eventsRemoved(History::Events)), |
2612 | SLOT(_d_eventsRemoved(History::Events))); |
2613 | + // we don't filter thread signals |
2614 | + connect(Manager::instance(), |
2615 | + SIGNAL(threadsRemoved(History::Threads)), |
2616 | + SIGNAL(threadsRemoved(History::Threads))); |
2617 | } |
2618 | |
2619 | EventView::~EventView() |
2620 | |
2621 | === modified file 'src/eventview.h' |
2622 | --- src/eventview.h 2013-09-17 23:05:35 +0000 |
2623 | +++ src/eventview.h 2017-03-21 14:18:04 +0000 |
2624 | @@ -1,5 +1,5 @@ |
2625 | /* |
2626 | - * Copyright (C) 2013 Canonical, Ltd. |
2627 | + * Copyright (C) 2013-2017 Canonical, Ltd. |
2628 | * |
2629 | * Authors: |
2630 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
2631 | @@ -24,6 +24,7 @@ |
2632 | |
2633 | #include "types.h" |
2634 | #include "event.h" |
2635 | +#include "thread.h" |
2636 | #include "filter.h" |
2637 | #include "sort.h" |
2638 | #include <QObject> |
2639 | @@ -50,6 +51,7 @@ |
2640 | void eventsAdded(const History::Events &events); |
2641 | void eventsModified(const History::Events &events); |
2642 | void eventsRemoved(const History::Events &events); |
2643 | + void threadsRemoved(const History::Threads &threads); |
2644 | void invalidated(); |
2645 | |
2646 | private: |
2647 | |
2648 | === modified file 'src/manager.cpp' |
2649 | --- src/manager.cpp 2016-06-17 01:49:46 +0000 |
2650 | +++ src/manager.cpp 2017-03-21 14:18:04 +0000 |
2651 | @@ -65,6 +65,9 @@ |
2652 | SIGNAL(threadsRemoved(History::Threads)), |
2653 | SIGNAL(threadsRemoved(History::Threads))); |
2654 | connect(d->dbus.data(), |
2655 | + SIGNAL(threadParticipantsChanged(History::Thread, History::Participants, History::Participants, History::Participants)), |
2656 | + SIGNAL(threadParticipantsChanged(History::Thread, History::Participants, History::Participants, History::Participants))); |
2657 | + connect(d->dbus.data(), |
2658 | SIGNAL(eventsAdded(History::Events)), |
2659 | SIGNAL(eventsAdded(History::Events))); |
2660 | connect(d->dbus.data(), |
2661 | @@ -104,6 +107,13 @@ |
2662 | return self; |
2663 | } |
2664 | |
2665 | +void Manager::markThreadsAsRead(const History::Threads &threads) |
2666 | +{ |
2667 | + Q_D(Manager); |
2668 | + |
2669 | + d->dbus->markThreadsAsRead(threads); |
2670 | +} |
2671 | + |
2672 | ThreadViewPtr Manager::queryThreads(EventType type, |
2673 | const Sort &sort, |
2674 | const Filter &filter, |
2675 | @@ -154,6 +164,20 @@ |
2676 | return d->dbus->threadForProperties(accountId, type, properties, matchFlags, create); |
2677 | } |
2678 | |
2679 | +/** |
2680 | + * @brief Request the list of participants of the given threads to the service |
2681 | + * @param threads The threads to be filled |
2682 | + * |
2683 | + * This is an asychronous request. When finished, the signal @ref threadParticipantsChanged |
2684 | + * will be emitted for the given threads. |
2685 | + */ |
2686 | +void Manager::requestThreadParticipants(const Threads &threads) |
2687 | +{ |
2688 | + Q_D(Manager); |
2689 | + |
2690 | + d->dbus->requestThreadParticipants(threads); |
2691 | +} |
2692 | + |
2693 | Thread Manager::getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties) |
2694 | { |
2695 | Q_D(Manager); |
2696 | |
2697 | === modified file 'src/manager.h' |
2698 | --- src/manager.h 2016-06-17 01:49:46 +0000 |
2699 | +++ src/manager.h 2017-03-21 14:18:04 +0000 |
2700 | @@ -66,19 +66,22 @@ |
2701 | const QVariantMap &properties, |
2702 | History::MatchFlags matchFlags = History::MatchCaseSensitive, |
2703 | bool create = false); |
2704 | - |
2705 | + void requestThreadParticipants(const History::Threads &threads); |
2706 | Thread getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties = QVariantMap()); |
2707 | |
2708 | bool writeEvents(const History::Events &events); |
2709 | bool removeThreads(const Threads &threads); |
2710 | bool removeEvents(const Events &events); |
2711 | |
2712 | + void markThreadsAsRead(const History::Threads &thread); |
2713 | + |
2714 | bool isServiceRunning() const; |
2715 | |
2716 | Q_SIGNALS: |
2717 | void threadsAdded(const History::Threads &threads); |
2718 | void threadsModified(const History::Threads &threads); |
2719 | void threadsRemoved(const History::Threads &threads); |
2720 | + void threadParticipantsChanged(const History::Thread &thread, const History::Participants &added, const History::Participants &removed, const History::Participants &modified); |
2721 | |
2722 | void eventsAdded(const History::Events &events); |
2723 | void eventsModified(const History::Events &events); |
2724 | |
2725 | === modified file 'src/managerdbus.cpp' |
2726 | --- src/managerdbus.cpp 2016-06-17 01:49:46 +0000 |
2727 | +++ src/managerdbus.cpp 2017-03-21 14:18:04 +0000 |
2728 | @@ -28,6 +28,8 @@ |
2729 | #include <QDBusReply> |
2730 | #include <QDBusMetaType> |
2731 | |
2732 | +#include <QDebug> |
2733 | + |
2734 | Q_DECLARE_METATYPE(QList< QVariantMap >) |
2735 | |
2736 | namespace History |
2737 | @@ -50,6 +52,12 @@ |
2738 | connection.connect(DBusService, DBusObjectPath, DBusInterface, "ThreadsRemoved", |
2739 | this, SLOT(onThreadsRemoved(QList<QVariantMap>))); |
2740 | |
2741 | + connection.connect(DBusService, DBusObjectPath, DBusInterface, "ThreadParticipantsChanged", |
2742 | + this, SLOT(onThreadParticipantsChanged(QVariantMap, |
2743 | + QList<QVariantMap>, |
2744 | + QList<QVariantMap>, |
2745 | + QList<QVariantMap>))); |
2746 | + |
2747 | connection.connect(DBusService, DBusObjectPath, DBusInterface, "EventsAdded", |
2748 | this, SLOT(onEventsAdded(QList<QVariantMap>))); |
2749 | connection.connect(DBusService, DBusObjectPath, DBusInterface, "EventsModified", |
2750 | @@ -70,6 +78,16 @@ |
2751 | return threadForProperties(accountId, type, properties, matchFlags, create); |
2752 | } |
2753 | |
2754 | +void ManagerDBus::markThreadsAsRead(const History::Threads &threads) |
2755 | +{ |
2756 | + QList<QVariantMap> threadMap = threadsToProperties(threads); |
2757 | + if (threadMap.isEmpty()) { |
2758 | + return; |
2759 | + } |
2760 | + |
2761 | + mInterface.asyncCall("MarkThreadsAsRead", QVariant::fromValue(threadMap)); |
2762 | +} |
2763 | + |
2764 | Thread ManagerDBus::threadForProperties(const QString &accountId, |
2765 | EventType type, |
2766 | const QVariantMap &properties, |
2767 | @@ -87,6 +105,29 @@ |
2768 | return thread; |
2769 | } |
2770 | |
2771 | +void ManagerDBus::requestThreadParticipants(const Threads &threads) |
2772 | +{ |
2773 | + QList<QVariantMap> ids; |
2774 | + Q_FOREACH(const Thread &thread, threads) { |
2775 | + QVariantMap id; |
2776 | + id[History::FieldAccountId] = thread.accountId(); |
2777 | + id[History::FieldThreadId] = thread.threadId(); |
2778 | + id[History::FieldType] = thread.type(); |
2779 | + ids << id; |
2780 | + } |
2781 | + |
2782 | + QDBusPendingCall call = mInterface.asyncCall("ParticipantsForThreads", QVariant::fromValue(ids)); |
2783 | + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); |
2784 | + connect(watcher, &QDBusPendingCallWatcher::finished, [this, threads](QDBusPendingCallWatcher *watcher) { |
2785 | + QDBusPendingReply<QList<QVariantMap> > reply = *watcher; |
2786 | + Q_FOREACH(const QVariantMap &map, reply.value()) { |
2787 | + History::Thread thread = History::Thread::fromProperties(map); |
2788 | + Q_EMIT threadParticipantsChanged(thread, History::Participants(), History::Participants(), thread.participants()); |
2789 | + watcher->deleteLater(); |
2790 | + } |
2791 | + }); |
2792 | +} |
2793 | + |
2794 | bool ManagerDBus::writeEvents(const Events &events) |
2795 | { |
2796 | QList<QVariantMap> eventMap = eventsToProperties(events); |
2797 | @@ -108,11 +149,8 @@ |
2798 | return false; |
2799 | } |
2800 | |
2801 | - QDBusReply<bool> reply = mInterface.call("RemoveThreads", QVariant::fromValue(threadMap)); |
2802 | - if (!reply.isValid()) { |
2803 | - return false; |
2804 | - } |
2805 | - return reply.value(); |
2806 | + mInterface.asyncCall("RemoveThreads", QVariant::fromValue(threadMap)); |
2807 | + return true; |
2808 | } |
2809 | |
2810 | bool ManagerDBus::removeEvents(const Events &events) |
2811 | @@ -122,11 +160,7 @@ |
2812 | return false; |
2813 | } |
2814 | |
2815 | - QDBusReply<bool> reply = mInterface.call("RemoveEvents", QVariant::fromValue(eventMap)); |
2816 | - if (!reply.isValid()) { |
2817 | - return false; |
2818 | - } |
2819 | - return reply.value(); |
2820 | + mInterface.asyncCall("RemoveEvents", QVariant::fromValue(eventMap)); |
2821 | } |
2822 | |
2823 | Thread ManagerDBus::getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties) |
2824 | @@ -168,6 +202,17 @@ |
2825 | Q_EMIT threadsRemoved(threadsFromProperties(threads)); |
2826 | } |
2827 | |
2828 | +void ManagerDBus::onThreadParticipantsChanged(const QVariantMap &thread, |
2829 | + const QList<QVariantMap> &added, |
2830 | + const QList<QVariantMap> &removed, |
2831 | + const QList<QVariantMap> &modified) |
2832 | +{ |
2833 | + Q_EMIT threadParticipantsChanged(threadsFromProperties(QList<QVariantMap>() << thread).first(), |
2834 | + Participants::fromVariantMapList(added), |
2835 | + Participants::fromVariantMapList(removed), |
2836 | + Participants::fromVariantMapList(modified)); |
2837 | +} |
2838 | + |
2839 | void ManagerDBus::onEventsAdded(const QList<QVariantMap> &events) |
2840 | { |
2841 | Q_EMIT eventsAdded(eventsFromProperties(events)); |
2842 | |
2843 | === modified file 'src/managerdbus_p.h' |
2844 | --- src/managerdbus_p.h 2016-06-17 01:49:46 +0000 |
2845 | +++ src/managerdbus_p.h 2017-03-21 14:18:04 +0000 |
2846 | @@ -50,18 +50,23 @@ |
2847 | const QVariantMap &properties, |
2848 | History::MatchFlags matchFlags, |
2849 | bool create); |
2850 | - |
2851 | + void requestThreadParticipants(const History::Threads &threads); |
2852 | bool writeEvents(const History::Events &events); |
2853 | bool removeThreads(const Threads &threads); |
2854 | bool removeEvents(const Events &events); |
2855 | Thread getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties = QVariantMap()); |
2856 | Event getSingleEvent(EventType type, const QString &accountId, const QString &threadId, const QString &eventId); |
2857 | + void markThreadsAsRead(const History::Threads &threads); |
2858 | |
2859 | Q_SIGNALS: |
2860 | // signals that will be triggered after processing bus signals |
2861 | void threadsAdded(const History::Threads &threads); |
2862 | void threadsModified(const History::Threads &threads); |
2863 | void threadsRemoved(const History::Threads &threads); |
2864 | + void threadParticipantsChanged(const History::Thread &thread, |
2865 | + const History::Participants &added, |
2866 | + const History::Participants &removed, |
2867 | + const History::Participants &modified); |
2868 | |
2869 | void eventsAdded(const History::Events &events); |
2870 | void eventsModified(const History::Events &events); |
2871 | @@ -71,7 +76,10 @@ |
2872 | void onThreadsAdded(const QList<QVariantMap> &threads); |
2873 | void onThreadsModified(const QList<QVariantMap> &threads); |
2874 | void onThreadsRemoved(const QList<QVariantMap> &threads); |
2875 | - |
2876 | + void onThreadParticipantsChanged(const QVariantMap &thread, |
2877 | + const QList<QVariantMap> &added, |
2878 | + const QList<QVariantMap> &removed, |
2879 | + const QList<QVariantMap> &modified); |
2880 | void onEventsAdded(const QList<QVariantMap> &events); |
2881 | void onEventsModified(const QList<QVariantMap> &events); |
2882 | void onEventsRemoved(const QList<QVariantMap> &events); |
2883 | |
2884 | === modified file 'src/participant.cpp' |
2885 | --- src/participant.cpp 2016-11-24 01:04:37 +0000 |
2886 | +++ src/participant.cpp 2017-03-21 14:18:04 +0000 |
2887 | @@ -233,6 +233,15 @@ |
2888 | return participants; |
2889 | } |
2890 | |
2891 | +Participants Participants::fromVariantMapList(const QList<QVariantMap> &list) |
2892 | +{ |
2893 | + Participants participants; |
2894 | + Q_FOREACH(const QVariantMap& entry, list) { |
2895 | + participants << Participant::fromProperties(entry); |
2896 | + } |
2897 | + return participants; |
2898 | +} |
2899 | + |
2900 | QVariantList Participants::toVariantList() const |
2901 | { |
2902 | QVariantList list; |
2903 | |
2904 | === modified file 'src/participant.h' |
2905 | --- src/participant.h 2016-11-24 01:04:37 +0000 |
2906 | +++ src/participant.h 2017-03-21 14:18:04 +0000 |
2907 | @@ -79,6 +79,7 @@ |
2908 | QStringList identifiers() const; |
2909 | static Participants fromVariant(const QVariant &variant); |
2910 | static Participants fromVariantList(const QVariantList &list); |
2911 | + static Participants fromVariantMapList(const QList<QVariantMap> &list); |
2912 | static Participants fromStringList(const QStringList &list); |
2913 | QVariantList toVariantList() const; |
2914 | History::Participants filterByState(uint state) const; |
2915 | |
2916 | === modified file 'src/plugin.h' |
2917 | --- src/plugin.h 2016-09-21 17:44:39 +0000 |
2918 | +++ src/plugin.h 2017-03-21 14:18:04 +0000 |
2919 | @@ -65,6 +65,11 @@ |
2920 | EventType type, |
2921 | const QVariantMap &properties, |
2922 | History::MatchFlags matchFlags = History::MatchCaseSensitive) = 0; |
2923 | + virtual QString threadIdForProperties(const QString &accountId, |
2924 | + EventType type, |
2925 | + const QVariantMap &properties, |
2926 | + History::MatchFlags matchFlags = History::MatchCaseSensitive) = 0; |
2927 | + virtual QList<QVariantMap> participantsForThreads(const QList<QVariantMap> &threadIds) = 0; |
2928 | |
2929 | virtual QList<QVariantMap> eventsForThread(const QVariantMap &thread) = 0; |
2930 | |
2931 | @@ -75,6 +80,7 @@ |
2932 | virtual bool updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles) { return false; }; |
2933 | virtual bool updateRoomInfo(const QString &accountId, const QString &threadId, EventType type, const QVariantMap &properties, const QStringList &invalidated = QStringList()) { return false; }; |
2934 | virtual bool removeThread(const QVariantMap &thread) { return false; } |
2935 | + virtual QVariantMap markThreadAsRead(const QVariantMap &thread) { return QVariantMap(); } |
2936 | |
2937 | virtual EventWriteResult writeTextEvent(const QVariantMap &event) { return EventWriteError; } |
2938 | virtual bool removeTextEvent(const QVariantMap &event) { return false; } |
2939 | |
2940 | === modified file 'src/thread.cpp' |
2941 | --- src/thread.cpp 2016-07-12 02:08:11 +0000 |
2942 | +++ src/thread.cpp 2017-03-21 14:18:04 +0000 |
2943 | @@ -192,6 +192,22 @@ |
2944 | return selfData < otherData; |
2945 | } |
2946 | |
2947 | +void Thread::removeParticipants(const Participants &participants) |
2948 | +{ |
2949 | + Q_D(Thread); |
2950 | + Q_FOREACH(const Participant &participant, participants) { |
2951 | + d->participants.removeAll(participant); |
2952 | + } |
2953 | +} |
2954 | + |
2955 | +void Thread::addParticipants(const Participants &participants) |
2956 | +{ |
2957 | + Q_D(Thread); |
2958 | + Q_FOREACH(const Participant &participant, participants) { |
2959 | + d->participants.append(participant); |
2960 | + } |
2961 | +} |
2962 | + |
2963 | QVariantMap Thread::properties() const |
2964 | { |
2965 | Q_D(const Thread); |
2966 | |
2967 | === modified file 'src/thread.h' |
2968 | --- src/thread.h 2016-07-12 01:59:06 +0000 |
2969 | +++ src/thread.h 2017-03-21 14:18:04 +0000 |
2970 | @@ -73,6 +73,8 @@ |
2971 | ChatType chatType() const; |
2972 | Threads groupedThreads() const; |
2973 | QVariantMap chatRoomInfo() const; |
2974 | + void addParticipants(const History::Participants &participants); |
2975 | + void removeParticipants(const History::Participants &participants); |
2976 | |
2977 | bool isNull() const; |
2978 | bool operator==(const Thread &other) const; |
2979 | |
2980 | === modified file 'src/threadview.cpp' |
2981 | --- src/threadview.cpp 2015-10-01 19:44:45 +0000 |
2982 | +++ src/threadview.cpp 2017-03-21 14:18:04 +0000 |
2983 | @@ -89,6 +89,18 @@ |
2984 | } |
2985 | } |
2986 | |
2987 | +void ThreadViewPrivate::_d_threadParticipantsChanged(const History::Thread &thread, |
2988 | + const History::Participants &added, |
2989 | + const History::Participants &removed, |
2990 | + const History::Participants &modified) |
2991 | +{ |
2992 | + Q_Q(ThreadView); |
2993 | + Threads filtered = filteredThreads(History::Threads() << thread); |
2994 | + if (!filtered.isEmpty()) { |
2995 | + Q_EMIT q->threadParticipantsChanged(filtered.first(), added, removed, modified); |
2996 | + } |
2997 | +} |
2998 | + |
2999 | // ------------- ThreadView ------------------------------------------------------- |
3000 | |
3001 | ThreadView::ThreadView(History::EventType type, |
3002 | @@ -132,6 +144,9 @@ |
3003 | connect(Manager::instance(), |
3004 | SIGNAL(threadsRemoved(History::Threads)), |
3005 | SLOT(_d_threadsRemoved(History::Threads))); |
3006 | + connect(Manager::instance(), |
3007 | + SIGNAL(threadParticipantsChanged(History::Thread, History::Participants, History::Participants, History::Participants)), |
3008 | + SLOT(_d_threadParticipantsChanged(History::Thread, History::Participants, History::Participants, History::Participants))); |
3009 | } |
3010 | |
3011 | ThreadView::~ThreadView() |
3012 | |
3013 | === modified file 'src/threadview.h' |
3014 | --- src/threadview.h 2015-09-21 20:05:06 +0000 |
3015 | +++ src/threadview.h 2017-03-21 14:18:04 +0000 |
3016 | @@ -52,12 +52,20 @@ |
3017 | void threadsAdded(const History::Threads &threads); |
3018 | void threadsModified(const History::Threads &threads); |
3019 | void threadsRemoved(const History::Threads &threads); |
3020 | + void threadParticipantsChanged(const History::Thread &thread, |
3021 | + const History::Participants &added, |
3022 | + const History::Participants &removed, |
3023 | + const History::Participants &modified); |
3024 | void invalidated(); |
3025 | |
3026 | private: |
3027 | Q_PRIVATE_SLOT(d_func(), void _d_threadsAdded(const History::Threads &threads)) |
3028 | Q_PRIVATE_SLOT(d_func(), void _d_threadsModified(const History::Threads &threads)) |
3029 | Q_PRIVATE_SLOT(d_func(), void _d_threadsRemoved(const History::Threads &threads)) |
3030 | + Q_PRIVATE_SLOT(d_func(), void _d_threadParticipantsChanged(const History::Thread &thread, |
3031 | + const History::Participants &added, |
3032 | + const History::Participants &removed, |
3033 | + const History::Participants &modified)) |
3034 | QScopedPointer<ThreadViewPrivate> d_ptr; |
3035 | |
3036 | }; |
3037 | |
3038 | === modified file 'src/threadview_p.h' |
3039 | --- src/threadview_p.h 2013-09-17 21:33:34 +0000 |
3040 | +++ src/threadview_p.h 2017-03-21 14:18:04 +0000 |
3041 | @@ -50,6 +50,10 @@ |
3042 | void _d_threadsAdded(const History::Threads &threads); |
3043 | void _d_threadsModified(const History::Threads &threads); |
3044 | void _d_threadsRemoved(const History::Threads &threads); |
3045 | + void _d_threadParticipantsChanged(const History::Thread &thread, |
3046 | + const History::Participants &added, |
3047 | + const History::Participants &removed, |
3048 | + const History::Participants &modified); |
3049 | |
3050 | ThreadView *q_ptr; |
3051 | }; |
3052 | |
3053 | === modified file 'src/utils.cpp' |
3054 | --- src/utils.cpp 2016-11-08 16:02:18 +0000 |
3055 | +++ src/utils.cpp 2017-03-21 14:18:04 +0000 |
3056 | @@ -50,6 +50,7 @@ |
3057 | if (protocolFlags.isEmpty()) { |
3058 | protocolFlags["ofono"] = MatchPhoneNumber; |
3059 | protocolFlags["multimedia"] = MatchPhoneNumber; |
3060 | + protocolFlags["sip"] = MatchPhoneNumber; |
3061 | } |
3062 | |
3063 | QString protocol = protocolFromAccountId(accountId); |
3064 | @@ -57,7 +58,7 @@ |
3065 | return protocolFlags[protocol]; |
3066 | } |
3067 | |
3068 | - // default to this value |
3069 | + // default to phone number matching for now |
3070 | return History::MatchCaseSensitive; |
3071 | } |
3072 | |
3073 | @@ -175,4 +176,18 @@ |
3074 | return QVariant(); |
3075 | } |
3076 | |
3077 | +bool Utils::shouldIncludeParticipants(const Thread &thread) |
3078 | +{ |
3079 | + return shouldIncludeParticipants(thread.accountId(), thread.chatType()); |
3080 | +} |
3081 | + |
3082 | +bool Utils::shouldIncludeParticipants(const QString &accountId, const ChatType &type) |
3083 | +{ |
3084 | + // FIXME: this is obviously incorrect. we have to query the protocol files as a final solution |
3085 | + if (protocolFromAccountId(accountId) == "irc") { |
3086 | + return type != ChatTypeRoom; |
3087 | + } |
3088 | + return true; |
3089 | +} |
3090 | + |
3091 | } |
3092 | |
3093 | === modified file 'src/utils_p.h' |
3094 | --- src/utils_p.h 2016-11-08 16:02:18 +0000 |
3095 | +++ src/utils_p.h 2017-03-21 14:18:04 +0000 |
3096 | @@ -36,6 +36,8 @@ |
3097 | static bool compareParticipants(const QStringList &participants1, const QStringList &participants2, MatchFlags flags); |
3098 | static bool compareNormalizedParticipants(const QStringList &participants1, const QStringList &participants2, MatchFlags flags); |
3099 | static bool shouldGroupThread(const Thread &thread); |
3100 | + static bool shouldIncludeParticipants(const Thread &thread); |
3101 | + static bool shouldIncludeParticipants(const QString &accountId, const History::ChatType &type); |
3102 | static QString normalizeId(const QString &accountId, const QString &id); |
3103 | static QVariant getUserValue(const QString &interface, const QString &propName); |
3104 | |
3105 | |
3106 | === modified file 'tests/Ubuntu.History/HistoryEventModelTest.cpp' |
3107 | --- tests/Ubuntu.History/HistoryEventModelTest.cpp 2016-09-09 20:00:09 +0000 |
3108 | +++ tests/Ubuntu.History/HistoryEventModelTest.cpp 2017-03-21 14:18:04 +0000 |
3109 | @@ -1,5 +1,5 @@ |
3110 | /* |
3111 | - * Copyright (C) 2016 Canonical, Ltd. |
3112 | + * Copyright (C) 2016-2017 Canonical, Ltd. |
3113 | * |
3114 | * This file is part of history-service. |
3115 | * |
3116 | |
3117 | === modified file 'tests/daemon/DaemonTest.cpp' |
3118 | --- tests/daemon/DaemonTest.cpp 2016-11-03 13:20:17 +0000 |
3119 | +++ tests/daemon/DaemonTest.cpp 2017-03-21 14:18:04 +0000 |
3120 | @@ -141,8 +141,6 @@ |
3121 | History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>(); |
3122 | QCOMPARE(threads.count(), 1); |
3123 | History::Thread thread = threads.first(); |
3124 | - QCOMPARE(thread.participants().count(), 1); |
3125 | - QCOMPARE(thread.participants().first().identifier(), sender); |
3126 | |
3127 | QTRY_COMPARE(threadsModifiedSpy.count(), 1); |
3128 | threads = threadsModifiedSpy.first().first().value<History::Threads>(); |
3129 | @@ -252,8 +250,6 @@ |
3130 | History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>(); |
3131 | QCOMPARE(threads.count(), 1); |
3132 | History::Thread thread = threads.first(); |
3133 | - QCOMPARE(thread.participants().count(), 1); |
3134 | - QCOMPARE(thread.participants().first().identifier(), recipient); |
3135 | |
3136 | QTRY_COMPARE(threadsModifiedSpy.count(), 1); |
3137 | threads = threadsModifiedSpy.first().first().value<History::Threads>(); |
3138 | @@ -296,8 +292,6 @@ |
3139 | History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>(); |
3140 | QCOMPARE(threads.count(), 1); |
3141 | History::Thread thread = threads.first(); |
3142 | - QCOMPARE(thread.participants().count(), 1); |
3143 | - QCOMPARE(thread.participants().first().identifier(), callerId); |
3144 | |
3145 | QTRY_COMPARE(threadsModifiedSpy.count(), 1); |
3146 | threads = threadsModifiedSpy.first().first().value<History::Threads>(); |
3147 | @@ -357,8 +351,6 @@ |
3148 | History::Threads threads = threadsAddedSpy.first().first().value<History::Threads>(); |
3149 | QCOMPARE(threads.count(), 1); |
3150 | History::Thread thread = threads.first(); |
3151 | - QCOMPARE(thread.participants().count(), 1); |
3152 | - QCOMPARE(thread.participants().first().identifier(), phoneNumber); |
3153 | |
3154 | QTRY_COMPARE(threadsModifiedSpy.count(), 1); |
3155 | threads = threadsModifiedSpy.first().first().value<History::Threads>(); |
3156 | |
3157 | === modified file 'tests/libhistoryservice/ManagerTest.cpp' |
3158 | --- tests/libhistoryservice/ManagerTest.cpp 2015-09-23 21:52:48 +0000 |
3159 | +++ tests/libhistoryservice/ManagerTest.cpp 2017-03-21 14:18:04 +0000 |
3160 | @@ -96,7 +96,6 @@ |
3161 | |
3162 | QCOMPARE(thread.accountId(), accountId); |
3163 | QCOMPARE(thread.type(), type); |
3164 | - QCOMPARE(thread.participants().identifiers(), participants); |
3165 | |
3166 | // now try to get the thread again to see if it is returned correctly |
3167 | History::Thread sameThread = mManager->threadForParticipants(accountId, type, participantsToMatch, matchFlags, false); |
3168 | @@ -137,14 +136,16 @@ |
3169 | |
3170 | void ManagerTest::testWriteEvents() |
3171 | { |
3172 | + QString textParticipant("textParticipant"); |
3173 | + QString voiceParticipant("voiceParticipant"); |
3174 | // create two threads, one for voice and one for text |
3175 | History::Thread textThread = mManager->threadForParticipants("textAccountId", |
3176 | History::EventTypeText, |
3177 | - QStringList()<< "textParticipant", |
3178 | + QStringList()<< textParticipant, |
3179 | History::MatchCaseSensitive, true); |
3180 | History::Thread voiceThread = mManager->threadForParticipants("voiceAccountId", |
3181 | History::EventTypeVoice, |
3182 | - QStringList()<< "voiceParticipant", |
3183 | + QStringList()<< voiceParticipant, |
3184 | History::MatchCaseSensitive, true); |
3185 | // insert some text and voice events |
3186 | History::Events events; |
3187 | @@ -152,7 +153,7 @@ |
3188 | History::TextEvent textEvent(textThread.accountId(), |
3189 | textThread.threadId(), |
3190 | QString("eventId%1").arg(i), |
3191 | - textThread.participants().first().identifier(), |
3192 | + textParticipant, |
3193 | QDateTime::currentDateTime(), |
3194 | true, |
3195 | QString("Hello world %1").arg(i), |
3196 | @@ -163,7 +164,7 @@ |
3197 | History::VoiceEvent voiceEvent(voiceThread.accountId(), |
3198 | voiceThread.threadId(), |
3199 | QString("eventId%1").arg(i), |
3200 | - voiceThread.participants().first().identifier(), |
3201 | + voiceParticipant, |
3202 | QDateTime::currentDateTime(), |
3203 | true, |
3204 | true); |
3205 | @@ -214,14 +215,16 @@ |
3206 | |
3207 | void ManagerTest::testRemoveEvents() |
3208 | { |
3209 | + QString textParticipant("textParticipant"); |
3210 | + QString voiceParticipant("voiceParticipant"); |
3211 | // create two threads, one for voice and one for text |
3212 | History::Thread textThread = mManager->threadForParticipants("textRemovableAccount", |
3213 | History::EventTypeText, |
3214 | - QStringList()<< "textParticipant", |
3215 | + QStringList()<< textParticipant, |
3216 | History::MatchCaseSensitive, true); |
3217 | History::Thread voiceThread = mManager->threadForParticipants("voiceRemovableAccount", |
3218 | History::EventTypeVoice, |
3219 | - QStringList()<< "voiceParticipant", |
3220 | + QStringList()<< voiceParticipant, |
3221 | History::MatchCaseSensitive, true); |
3222 | // insert some text and voice events |
3223 | History::Events events; |
3224 | @@ -229,7 +232,7 @@ |
3225 | History::TextEvent textEvent(textThread.accountId(), |
3226 | textThread.threadId(), |
3227 | QString("eventToBeRemoved%1").arg(i), |
3228 | - textThread.participants().first().identifier(), |
3229 | + textParticipant, |
3230 | QDateTime::currentDateTime(), |
3231 | true, |
3232 | QString("Hello world %1").arg(i), |
3233 | @@ -239,7 +242,7 @@ |
3234 | History::VoiceEvent voiceEvent(voiceThread.accountId(), |
3235 | voiceThread.threadId(), |
3236 | QString("eventToBeRemoved%1").arg(i), |
3237 | - voiceThread.participants().first().identifier(), |
3238 | + voiceParticipant, |
3239 | QDateTime::currentDateTime(), |
3240 | true, |
3241 | true); |
3242 | @@ -280,14 +283,16 @@ |
3243 | |
3244 | void ManagerTest::testGetSingleEvent() |
3245 | { |
3246 | + QString textParticipant("textSingleParticipant"); |
3247 | + QString voiceParticipant("voiceSingleParticipant"); |
3248 | // create two threads, one for voice and one for text |
3249 | History::Thread textThread = mManager->threadForParticipants("textSingleAccount", |
3250 | History::EventTypeText, |
3251 | - QStringList()<< "textSingleParticipant", |
3252 | + QStringList()<< textParticipant, |
3253 | History::MatchCaseSensitive, true); |
3254 | History::Thread voiceThread = mManager->threadForParticipants("voiceSingleAccount", |
3255 | History::EventTypeVoice, |
3256 | - QStringList()<< "voiceSingleParticipant", |
3257 | + QStringList()<< voiceParticipant, |
3258 | History::MatchCaseSensitive, true); |
3259 | |
3260 | // now add two events |
3261 | @@ -348,43 +353,11 @@ |
3262 | History::Threads threads; |
3263 | threads << textThread << voiceThread; |
3264 | |
3265 | - // insert some text and voice events |
3266 | - History::Events events; |
3267 | - for (int i = 0; i < 50; ++i) { |
3268 | - History::TextEvent textEvent(textThread.accountId(), |
3269 | - textThread.threadId(), |
3270 | - QString("eventToBeRemoved%1").arg(i), |
3271 | - textThread.participants().first().identifier(), |
3272 | - QDateTime::currentDateTime(), |
3273 | - true, |
3274 | - QString("Hello world %1").arg(i), |
3275 | - History::MessageTypeText); |
3276 | - events.append(textEvent); |
3277 | - |
3278 | - History::VoiceEvent voiceEvent(voiceThread.accountId(), |
3279 | - voiceThread.threadId(), |
3280 | - QString("eventToBeRemoved%1").arg(i), |
3281 | - voiceThread.participants().first().identifier(), |
3282 | - QDateTime::currentDateTime(), |
3283 | - true, |
3284 | - true); |
3285 | - events.append(voiceEvent); |
3286 | - } |
3287 | - |
3288 | - QVERIFY(mManager->writeEvents(events)); |
3289 | - |
3290 | - QSignalSpy eventsRemovedSpy(mManager, SIGNAL(eventsRemoved(History::Events))); |
3291 | QSignalSpy threadsRemovedSpy(mManager, SIGNAL(threadsRemoved(History::Threads))); |
3292 | |
3293 | QVERIFY(mManager->removeThreads(threads)); |
3294 | - QTRY_COMPARE(eventsRemovedSpy.count(), 1); |
3295 | QTRY_COMPARE(threadsRemovedSpy.count(), 1); |
3296 | |
3297 | - History::Events removedEvents = eventsRemovedSpy.first().first().value<History::Events>(); |
3298 | - qSort(removedEvents); |
3299 | - qSort(events); |
3300 | - QCOMPARE(removedEvents, events); |
3301 | - |
3302 | History::Threads removedThreads = threadsRemovedSpy.first().first().value<History::Threads>(); |
3303 | qSort(removedThreads); |
3304 | qSort(threads); |
3305 | |
3306 | === modified file 'tests/plugins/sqlite/SqliteEventViewTest.cpp' |
3307 | --- tests/plugins/sqlite/SqliteEventViewTest.cpp 2013-12-09 21:18:14 +0000 |
3308 | +++ tests/plugins/sqlite/SqliteEventViewTest.cpp 2017-03-21 14:18:04 +0000 |
3309 | @@ -39,6 +39,7 @@ |
3310 | void testNextPage(); |
3311 | void testFilter(); |
3312 | void testSort(); |
3313 | + void testSortWithMultipleFields(); |
3314 | |
3315 | private: |
3316 | SQLiteHistoryPlugin *mPlugin; |
3317 | @@ -128,7 +129,26 @@ |
3318 | QCOMPARE(allEvents.first()[History::FieldEventId].toString(), QString("event%1").arg(EVENT_COUNT-1)); |
3319 | QCOMPARE(allEvents.last()[History::FieldEventId].toString(), QString("event00")); |
3320 | delete view; |
3321 | - |
3322 | +} |
3323 | + |
3324 | +void SqliteEventViewTest::testSortWithMultipleFields() |
3325 | +{ |
3326 | + History::Sort ascendingSort(QString("%1, %2").arg(History::FieldAccountId).arg(History::FieldEventId), Qt::AscendingOrder); |
3327 | + //History::Sort ascendingSort(QString("%1").arg(History::FieldEventId), Qt::AscendingOrder); |
3328 | + History::PluginEventView *view = mPlugin->queryEvents(History::EventTypeText, ascendingSort); |
3329 | + QVERIFY(view->IsValid()); |
3330 | + QList<QVariantMap> allEvents; |
3331 | + QList<QVariantMap> events = view->NextPage(); |
3332 | + while (!events.isEmpty()) { |
3333 | + allEvents << events; |
3334 | + events = view->NextPage(); |
3335 | + } |
3336 | + |
3337 | + QCOMPARE(allEvents[0][History::FieldEventId].toString(), QString("event00")); |
3338 | + QCOMPARE(allEvents[0][History::FieldAccountId].toString(), QString("account0")); |
3339 | + QCOMPARE(allEvents[1][History::FieldEventId].toString(), QString("event01")); |
3340 | + QCOMPARE(allEvents[1][History::FieldAccountId].toString(), QString("account0")); |
3341 | + delete view; |
3342 | } |
3343 | |
3344 | void SqliteEventViewTest::populateDatabase() |
3345 | @@ -136,7 +156,7 @@ |
3346 | mPlugin->beginBatchOperation(); |
3347 | |
3348 | // create two threads of each type |
3349 | - for (int i = 0; i < 2; ++i) { |
3350 | + for (int i = 1; i >= 0; --i) { |
3351 | QVariantMap voiceThread = mPlugin->createThreadForParticipants(QString("account%1").arg(i), |
3352 | History::EventTypeVoice, |
3353 | QStringList() << QString("participant%1").arg(i)); |
3354 | |
3355 | === modified file 'tests/plugins/sqlite/SqlitePluginTest.cpp' |
3356 | --- tests/plugins/sqlite/SqlitePluginTest.cpp 2016-09-09 20:00:09 +0000 |
3357 | +++ tests/plugins/sqlite/SqlitePluginTest.cpp 2017-03-21 14:18:04 +0000 |
3358 | @@ -189,7 +189,6 @@ |
3359 | QCOMPARE(retrievedThread[History::FieldType], thread[History::FieldType]); |
3360 | QCOMPARE(retrievedThread[History::FieldCount], thread[History::FieldCount]); |
3361 | QCOMPARE(retrievedThread[History::FieldUnreadCount], thread[History::FieldUnreadCount]); |
3362 | - QCOMPARE(retrievedThread[History::FieldParticipants], thread[History::FieldParticipants]); |
3363 | } |
3364 | |
3365 | void SqlitePluginTest::testEmptyThreadForParticipants() |
3366 | @@ -219,7 +218,6 @@ |
3367 | QCOMPARE(retrievedThread[History::FieldType], thread[History::FieldType]); |
3368 | QCOMPARE(retrievedThread[History::FieldCount], thread[History::FieldCount]); |
3369 | QCOMPARE(retrievedThread[History::FieldUnreadCount], thread[History::FieldUnreadCount]); |
3370 | - QCOMPARE(retrievedThread[History::FieldParticipants], thread[History::FieldParticipants]); |
3371 | |
3372 | // FIXME: check that the last event data is also present |
3373 | } |
FAILED: Continuous integration, rev:228 /jenkins. canonical. com/system- apps/job/ lp-history- service- ci/3/ /jenkins. canonical. com/system- apps/job/ build/635 /jenkins. canonical. com/system- apps/job/ test-0- autopkgtest/ label=phone- armhf,release= vivid+overlay, testname= default/ 83 /jenkins. canonical. com/system- apps/job/ test-0- autopkgtest/ label=phone- armhf,release= xenial+ overlay, testname= default/ 83/console /jenkins. canonical. com/system- apps/job/ test-0- autopkgtest/ label=phone- armhf,release= yakkety, testname= default/ 83/console /jenkins. canonical. com/system- apps/job/ build-0- fetch/635 /jenkins. canonical. com/system- apps/job/ build-1- sourcepkg/ release= vivid+overlay/ 604 /jenkins. canonical. com/system- apps/job/ build-1- sourcepkg/ release= xenial+ overlay/ 604 /jenkins. canonical. com/system- apps/job/ build-1- sourcepkg/ release= yakkety/ 604 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 597/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 597/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 597/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 597/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 597/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 597/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 597/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 597/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/system- apps/job/ build-2- binpkg/ arch=i386, release= yakkety/ 597 /jenkins. canonical. com/system- apps/job/ build-2- binpk...
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/