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