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