Merge lp:~phablet-team/history-service/thread_search_model into lp:history-service
- thread_search_model
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~phablet-team/history-service/thread_search_model |
Merge into: | lp:history-service |
Diff against target: |
1280 lines (+645/-114) 21 files modified
Ubuntu/History/CMakeLists.txt (+1/-1) Ubuntu/History/historygroupedthreadsmodel.cpp (+25/-8) Ubuntu/History/historythreadmodel.cpp (+172/-16) Ubuntu/History/historythreadmodel.h (+24/-0) daemon/historydaemon.cpp (+37/-11) daemon/historydaemon.h (+11/-0) daemon/historyservicedbus.cpp (+0/-30) daemon/historyservicedbus.h (+0/-8) plugins/sqlite/sqlitehistoryplugin.cpp (+5/-5) plugins/sqlite/sqlitehistoryplugin.h (+1/-1) plugins/sqlite/sqlitehistorythreadview.cpp (+226/-4) plugins/sqlite/sqlitehistorythreadview.h (+28/-1) src/PluginThreadView.xml (+28/-0) src/contactmatcher.cpp (+9/-1) src/pluginthreadview.cpp (+19/-1) src/pluginthreadview.h (+11/-2) src/thread.cpp (+13/-4) src/thread.h (+1/-0) src/threadview.cpp (+25/-15) src/threadview.h (+3/-3) src/threadview_p.h (+6/-3) |
To merge this branch: | bzr merge lp:~phablet-team/history-service/thread_search_model |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
system-apps-ci-bot | continuous-integration | Needs Fixing | |
PS Jenkins bot | continuous-integration | Needs Fixing | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+286656@code.launchpad.net |
Commit message
Add search capabilities to the thread models.
Description of the change
Add search capabilities to the thread models.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 231. By Gustavo Pichorim Boiko
-
Fix the interaction with the cached thread grouping.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:231
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 232. By Gustavo Pichorim Boiko
-
Instead of having NextPage() as virtual directly, add a fetchNextPage() virtual
function so that we can use NextPage to preprocess the results. - 233. By Gustavo Pichorim Boiko
-
Revert the approach: the grouping will have to be implemented in the plugin itself.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:233
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 234. By Gustavo Pichorim Boiko
-
Start implementing the grouping all in server side.
- 235. By Gustavo Pichorim Boiko
-
Emit the signals in the manager itself so that they can be used in the views.
- 236. By Gustavo Pichorim Boiko
-
Make it possible to handle thread changes in the server side view.
- 237. By Gustavo Pichorim Boiko
-
Get the signals from the thread view itself and not from the manager as
it will have the threads already filtered.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:237
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 238. By Gustavo Pichorim Boiko
-
Start to add code to handle thread changes in the server side of the thread view.
Also fix the SqlitePluginTest.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:238
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 239. By Gustavo Pichorim Boiko
-
Fix ContactMatcher.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:239
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 240. By Gustavo Pichorim Boiko
-
Fix parsing grouped threads.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:240
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 241. By Gustavo Pichorim Boiko
-
Implement the group updating.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:241
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 242. By Gustavo Pichorim Boiko
-
Fix the dbus connection of signals.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:242
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 243. By Gustavo Pichorim Boiko
-
Handle thread removing and adding.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:243
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:243
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 244. By Gustavo Pichorim Boiko
-
Merge trunk
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:244
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unmerged revisions
- 244. By Gustavo Pichorim Boiko
-
Merge trunk
- 243. By Gustavo Pichorim Boiko
-
Handle thread removing and adding.
- 242. By Gustavo Pichorim Boiko
-
Fix the dbus connection of signals.
- 241. By Gustavo Pichorim Boiko
-
Implement the group updating.
- 240. By Gustavo Pichorim Boiko
-
Fix parsing grouped threads.
- 239. By Gustavo Pichorim Boiko
-
Fix ContactMatcher.
- 238. By Gustavo Pichorim Boiko
-
Start to add code to handle thread changes in the server side of the thread view.
Also fix the SqlitePluginTest. - 237. By Gustavo Pichorim Boiko
-
Get the signals from the thread view itself and not from the manager as
it will have the threads already filtered. - 236. By Gustavo Pichorim Boiko
-
Make it possible to handle thread changes in the server side view.
- 235. By Gustavo Pichorim Boiko
-
Emit the signals in the manager itself so that they can be used in the views.
Preview Diff
1 | === modified file 'Ubuntu/History/CMakeLists.txt' |
2 | --- Ubuntu/History/CMakeLists.txt 2015-09-28 14:26:09 +0000 |
3 | +++ Ubuntu/History/CMakeLists.txt 2017-01-26 18:33:44 +0000 |
4 | @@ -26,7 +26,7 @@ |
5 | historyqmltexteventattachment.h |
6 | historyqmlunionfilter.h |
7 | historythreadmodel.h |
8 | -) |
9 | + ) |
10 | |
11 | include_directories( |
12 | ${CMAKE_SOURCE_DIR}/src |
13 | |
14 | === modified file 'Ubuntu/History/historygroupedthreadsmodel.cpp' |
15 | --- Ubuntu/History/historygroupedthreadsmodel.cpp 2015-10-08 19:35:40 +0000 |
16 | +++ Ubuntu/History/historygroupedthreadsmodel.cpp 2017-01-26 18:33:44 +0000 |
17 | @@ -247,25 +247,42 @@ |
18 | removeThreadFromGroup(thread); |
19 | return; |
20 | } |
21 | + |
22 | + // now filter the threads according to the search term |
23 | + History::Threads filtered = filterThreads(groupedThread.groupedThreads()); |
24 | + if (filtered.isEmpty()) { |
25 | + return; |
26 | + } |
27 | + |
28 | + bool needsUpdating = false; |
29 | + if (!filtered.contains(groupedThread)) { |
30 | + groupedThread = filtered.first(); |
31 | + needsUpdating = true; |
32 | + } else { |
33 | + // call matchThread just to get the event updated |
34 | + matchThread(groupedThread); |
35 | + } |
36 | + |
37 | int pos = existingPositionForEntry(groupedThread); |
38 | |
39 | // if the group is empty, we need to insert it into the map |
40 | if (pos < 0) { |
41 | HistoryThreadGroup group; |
42 | int newPos = positionForItem(groupedThread.properties()); |
43 | - group.threads = groupedThread.groupedThreads(); |
44 | + group.threads = filtered; |
45 | group.displayedThread = groupedThread; |
46 | beginInsertRows(QModelIndex(), newPos, newPos); |
47 | mGroups.insert(newPos, group); |
48 | endInsertRows(); |
49 | - return; |
50 | + if (needsUpdating) { |
51 | + updateDisplayedThread(mGroups[newPos]); |
52 | + } |
53 | + } else { |
54 | + HistoryThreadGroup &group = mGroups[pos]; |
55 | + group.threads = filtered; |
56 | + updateDisplayedThread(group); |
57 | + markGroupAsChanged(group); |
58 | } |
59 | - |
60 | - HistoryThreadGroup &group = mGroups[pos]; |
61 | - group.threads = groupedThread.groupedThreads(); |
62 | - |
63 | - updateDisplayedThread(group); |
64 | - markGroupAsChanged(group); |
65 | } |
66 | |
67 | void HistoryGroupedThreadsModel::removeThreadFromGroup(const History::Thread &thread) |
68 | |
69 | === modified file 'Ubuntu/History/historythreadmodel.cpp' |
70 | --- Ubuntu/History/historythreadmodel.cpp 2016-06-17 01:49:46 +0000 |
71 | +++ Ubuntu/History/historythreadmodel.cpp 2017-01-26 18:33:44 +0000 |
72 | @@ -19,10 +19,13 @@ |
73 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
74 | */ |
75 | |
76 | +#include "eventview.h" |
77 | #include "historythreadmodel.h" |
78 | #include "historyqmltexteventattachment.h" |
79 | +#include "intersectionfilter.h" |
80 | #include "manager.h" |
81 | #include "threadview.h" |
82 | +#include "utils_p.h" |
83 | #include "voiceevent.h" |
84 | #include <QDBusMetaType> |
85 | |
86 | @@ -38,6 +41,7 @@ |
87 | mRoles = HistoryModel::roleNames(); |
88 | mRoles[CountRole] = "count"; |
89 | mRoles[UnreadCountRole] = "unreadCount"; |
90 | + mRoles[ThreadsRole] = "threads"; |
91 | mRoles[ChatType] = "chatType"; |
92 | mRoles[ChatRoomInfo] = "chatRoomInfo"; |
93 | |
94 | @@ -100,12 +104,38 @@ |
95 | |
96 | QVariant result; |
97 | switch (role) { |
98 | - case CountRole: |
99 | - result = thread.count(); |
100 | - break; |
101 | - case UnreadCountRole: |
102 | - result = thread.unreadCount(); |
103 | - break; |
104 | + case CountRole: { |
105 | + if (thread.groupedThreads().isEmpty()) { |
106 | + result = thread.count(); |
107 | + } else { |
108 | + int count = 0; |
109 | + Q_FOREACH(const History::Thread &groupedThread, thread.groupedThreads()) { |
110 | + count += groupedThread.count(); |
111 | + } |
112 | + result = count; |
113 | + } |
114 | + break; |
115 | + } |
116 | + case UnreadCountRole: { |
117 | + if (thread.groupedThreads().isEmpty()) { |
118 | + result = thread.unreadCount(); |
119 | + } else { |
120 | + int count = 0; |
121 | + Q_FOREACH(const History::Thread &groupedThread, thread.groupedThreads()) { |
122 | + count += groupedThread.unreadCount(); |
123 | + } |
124 | + result = count; |
125 | + } |
126 | + break; |
127 | + } |
128 | + case ThreadsRole: { |
129 | + QVariantList threads; |
130 | + Q_FOREACH(const History::Thread &groupedThread, thread.groupedThreads()) { |
131 | + threads << groupedThread.properties(); |
132 | + } |
133 | + result = threads; |
134 | + break; |
135 | + } |
136 | case ChatType: |
137 | result = thread.chatType(); |
138 | break; |
139 | @@ -251,6 +281,32 @@ |
140 | return History::Manager::instance()->removeThreads(threads); |
141 | } |
142 | |
143 | +QString HistoryThreadModel::searchTerm() const |
144 | +{ |
145 | + return mSearchTerm; |
146 | +} |
147 | + |
148 | +void HistoryThreadModel::setSearchTerm(const QString &term) |
149 | +{ |
150 | + mSearchTerm = term.toLower().trimmed(); |
151 | + Q_EMIT searchTermChanged(); |
152 | + updateQuery(); |
153 | +} |
154 | + |
155 | +QString HistoryThreadModel::groupingProperty() const |
156 | +{ |
157 | + return mGroupingProperty; |
158 | +} |
159 | + |
160 | +void HistoryThreadModel::setGroupingProperty(const QString &value) |
161 | +{ |
162 | + mGroupingProperty = value; |
163 | + mGroupThreads = !mGroupingProperty.isEmpty(); |
164 | + Q_EMIT groupingPropertyChanged(); |
165 | + |
166 | + triggerQueryUpdate(); |
167 | +} |
168 | + |
169 | void HistoryThreadModel::updateQuery() |
170 | { |
171 | // remove all events from the model |
172 | @@ -280,19 +336,25 @@ |
173 | |
174 | QVariantMap properties; |
175 | if (mGroupThreads) { |
176 | - properties[History::FieldGroupingProperty] = History::FieldParticipants; |
177 | + properties[History::FieldGroupingProperty] = mGroupingProperty; |
178 | } |
179 | |
180 | mThreadView = History::Manager::instance()->queryThreads((History::EventType)mType, querySort, queryFilter, properties); |
181 | connect(mThreadView.data(), |
182 | - SIGNAL(threadsAdded(History::Threads)), |
183 | - SLOT(onThreadsAdded(History::Threads))); |
184 | - connect(mThreadView.data(), |
185 | - SIGNAL(threadsModified(History::Threads)), |
186 | - SLOT(onThreadsModified(History::Threads))); |
187 | - connect(mThreadView.data(), |
188 | - SIGNAL(threadsRemoved(History::Threads)), |
189 | - SLOT(onThreadsRemoved(History::Threads))); |
190 | + &History::ThreadView::threadsAdded, |
191 | + [&](History::Threads threads){ |
192 | + onThreadsAdded(filterThreads(threads)); |
193 | + }); |
194 | + connect(mThreadView.data(), |
195 | + &History::ThreadView::threadsModified, |
196 | + [&](History::Threads threads){ |
197 | + onThreadsModified(filterThreads(threads)); |
198 | + }); |
199 | + connect(mThreadView.data(), |
200 | + &History::ThreadView::threadsRemoved, |
201 | + [&](History::Threads threads){ |
202 | + onThreadsRemoved(filterThreads(threads)); |
203 | + }); |
204 | connect(mThreadView.data(), |
205 | SIGNAL(invalidated()), |
206 | SLOT(triggerQueryUpdate())); |
207 | @@ -369,5 +431,99 @@ |
208 | |
209 | History::Threads HistoryThreadModel::fetchNextPage() |
210 | { |
211 | - return mThreadView->nextPage(); |
212 | + History::Threads filtered; |
213 | + while (filtered.isEmpty()) { |
214 | + History::Threads threads = mThreadView->nextPage(); |
215 | + if (threads.isEmpty()) { |
216 | + return threads; |
217 | + } |
218 | + filtered = filterThreads(threads); |
219 | + } |
220 | + return filtered; |
221 | +} |
222 | + |
223 | +bool HistoryThreadModel::matchThread(History::Thread &thread) |
224 | +{ |
225 | + if (mSearchTerm.isEmpty()) { |
226 | + return true; |
227 | + } |
228 | + |
229 | + // first try to match the contact alias or the identifier |
230 | + Q_FOREACH(const History::Participant &participant, thread.participants()) { |
231 | + if (participant.alias().toLower().contains(mSearchTerm)) { |
232 | + return true; |
233 | + } |
234 | + |
235 | + if (History::Utils::compareIds(thread.accountId(), participant.identifier(), mSearchTerm)) { |
236 | + return true; |
237 | + } else if (participant.identifier().contains(mSearchTerm)) { |
238 | + return true; |
239 | + } |
240 | + } |
241 | + |
242 | + // now the more expensive search: query the events to see if any match |
243 | + History::Sort sort; |
244 | + sort.setSortField(History::FieldTimestamp); |
245 | + sort.setSortOrder(Qt::DescendingOrder); |
246 | + |
247 | + History::IntersectionFilter topLevelFilter; |
248 | + |
249 | + // we need to match accountId, threadId and the text |
250 | + History::Filter filter; |
251 | + filter.setFilterProperty(History::FieldAccountId); |
252 | + filter.setFilterValue(thread.accountId()); |
253 | + topLevelFilter.append(filter); |
254 | + |
255 | + filter.setFilterProperty(History::FieldThreadId); |
256 | + filter.setFilterValue(thread.threadId()); |
257 | + topLevelFilter.append(filter); |
258 | + |
259 | + filter.setFilterProperty(History::FieldMessage); |
260 | + filter.setFilterValue(mSearchTerm); |
261 | + filter.setMatchFlags(History::MatchContains); |
262 | + topLevelFilter.append(filter); |
263 | + |
264 | + History::EventViewPtr eventView = History::Manager::instance()->queryEvents(History::EventTypeText, |
265 | + sort, |
266 | + topLevelFilter); |
267 | + if (eventView.isNull()) { |
268 | + return false; |
269 | + } |
270 | + |
271 | + if (!eventView->isValid()) { |
272 | + eventView->deleteLater(); |
273 | + return false; |
274 | + } |
275 | + |
276 | + // now check if we have events |
277 | + History::Events events = eventView->nextPage(); |
278 | + if (events.isEmpty()) { |
279 | + eventView->deleteLater(); |
280 | + return false; |
281 | + } |
282 | + |
283 | + QVariantMap threadProperties = thread.properties(); |
284 | + History::TextEvent event = events.first(); |
285 | + QVariantMap eventProperties = event.properties(); |
286 | + Q_FOREACH(const QString &key, eventProperties.keys()) { |
287 | + threadProperties[key] = eventProperties[key]; |
288 | + } |
289 | + |
290 | + // overwrite the thread with the one matching the search |
291 | + thread = History::Thread::fromProperties(threadProperties); |
292 | + eventView->deleteLater(); |
293 | + |
294 | + // if we got here, the thread already contains the matching event and matches the filters |
295 | + return true; |
296 | +} |
297 | + |
298 | +History::Threads HistoryThreadModel::filterThreads(const History::Threads &threads) |
299 | +{ |
300 | + History::Threads filtered; |
301 | + Q_FOREACH(History::Thread thread, threads) { |
302 | + if (matchThread(thread)) { |
303 | + filtered << thread; |
304 | + } |
305 | + } |
306 | + return filtered; |
307 | } |
308 | |
309 | === modified file 'Ubuntu/History/historythreadmodel.h' |
310 | --- Ubuntu/History/historythreadmodel.h 2016-06-17 01:49:46 +0000 |
311 | +++ Ubuntu/History/historythreadmodel.h 2017-01-26 18:33:44 +0000 |
312 | @@ -34,11 +34,21 @@ |
313 | { |
314 | Q_OBJECT |
315 | Q_ENUMS(ThreadRole) |
316 | + Q_PROPERTY(QString searchTerm |
317 | + READ searchTerm |
318 | + WRITE setSearchTerm |
319 | + NOTIFY searchTermChanged) |
320 | + Q_PROPERTY(QString groupingProperty |
321 | + READ groupingProperty |
322 | + WRITE setGroupingProperty |
323 | + NOTIFY groupingPropertyChanged) |
324 | + |
325 | |
326 | public: |
327 | |
328 | enum ThreadRole { |
329 | CountRole = HistoryModel::LastRole, |
330 | + ThreadsRole, |
331 | UnreadCountRole, |
332 | ChatType, |
333 | ChatRoomInfo, |
334 | @@ -71,6 +81,16 @@ |
335 | |
336 | Q_INVOKABLE bool removeThreads(const QVariantList &threadsProperties); |
337 | |
338 | + QString searchTerm() const; |
339 | + void setSearchTerm(const QString &term); |
340 | + |
341 | + QString groupingProperty() const; |
342 | + void setGroupingProperty(const QString &value); |
343 | + |
344 | +Q_SIGNALS: |
345 | + void searchTermChanged(); |
346 | + void groupingPropertyChanged(); |
347 | + |
348 | protected Q_SLOTS: |
349 | virtual void updateQuery(); |
350 | virtual void onThreadsAdded(const History::Threads &threads); |
351 | @@ -79,12 +99,16 @@ |
352 | |
353 | protected: |
354 | History::Threads fetchNextPage(); |
355 | + virtual bool matchThread(History::Thread &thread); |
356 | + virtual History::Threads filterThreads(const History::Threads &threads); |
357 | bool mCanFetchMore; |
358 | bool mGroupThreads; |
359 | + QString mSearchTerm; |
360 | |
361 | private: |
362 | History::ThreadViewPtr mThreadView; |
363 | History::Threads mThreads; |
364 | + QString mGroupingProperty; |
365 | QHash<int, QByteArray> mRoles; |
366 | mutable QMap<History::TextEvent, QList<QVariant> > mAttachmentCache; |
367 | }; |
368 | |
369 | === modified file 'daemon/historydaemon.cpp' |
370 | --- daemon/historydaemon.cpp 2016-11-24 02:02:32 +0000 |
371 | +++ daemon/historydaemon.cpp 2017-01-26 18:33:44 +0000 |
372 | @@ -146,6 +146,20 @@ |
373 | SIGNAL(channelAvailable(Tp::TextChannelPtr)), |
374 | SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); |
375 | |
376 | + // connect the signals that need to be relayed into DBus |
377 | + connect(this, SIGNAL(threadsAdded(QList<QVariantMap>)), |
378 | + &mDBus, SIGNAL(ThreadsAdded(QList<QVariantMap>))); |
379 | + connect(this, SIGNAL(threadsModified(QList<QVariantMap>)), |
380 | + &mDBus, SIGNAL(ThreadsModified(QList<QVariantMap>))); |
381 | + connect(this, SIGNAL(threadsRemoved(QList<QVariantMap>)), |
382 | + &mDBus, SIGNAL(ThreadsRemoved(QList<QVariantMap>))); |
383 | + connect(this, SIGNAL(eventsAdded(QList<QVariantMap>)), |
384 | + &mDBus, SIGNAL(EventsAdded(QList<QVariantMap>))); |
385 | + connect(this, SIGNAL(eventsModified(QList<QVariantMap>)), |
386 | + &mDBus, SIGNAL(EventsModified(QList<QVariantMap>))); |
387 | + connect(this, SIGNAL(eventsRemoved(QList<QVariantMap>)), |
388 | + &mDBus, SIGNAL(EventsRemoved(QList<QVariantMap>))); |
389 | + |
390 | // FIXME: we need to do this in a better way, but for now this should do |
391 | mProtocolFlags["ofono"] = History::MatchPhoneNumber; |
392 | mProtocolFlags["multimedia"] = History::MatchPhoneNumber; |
393 | @@ -308,7 +322,7 @@ |
394 | map["Requested"] = properties["Requested"]; |
395 | thread[History::FieldChatRoomInfo] = map; |
396 | } |
397 | - mDBus.notifyThreadsAdded(QList<QVariantMap>() << thread); |
398 | + Q_EMIT threadsAdded(QList<QVariantMap>() << thread); |
399 | } |
400 | } |
401 | return thread; |
402 | @@ -328,6 +342,8 @@ |
403 | return QString::null; |
404 | } |
405 | |
406 | + setupThreadView(view); |
407 | + |
408 | // FIXME: maybe we should keep a list of views to manually remove them at some point? |
409 | view->setParent(this); |
410 | return view->objectPath(); |
411 | @@ -428,13 +444,13 @@ |
412 | |
413 | // and last but not least, notify the results |
414 | if (!newEvents.isEmpty()) { |
415 | - mDBus.notifyEventsAdded(newEvents); |
416 | + Q_EMIT eventsAdded(newEvents); |
417 | } |
418 | if (!modifiedEvents.isEmpty()) { |
419 | - mDBus.notifyEventsModified(modifiedEvents); |
420 | + Q_EMIT eventsModified(modifiedEvents); |
421 | } |
422 | if (!threads.isEmpty()) { |
423 | - mDBus.notifyThreadsModified(threads.values()); |
424 | + Q_EMIT threadsModified(threads.values()); |
425 | } |
426 | return true; |
427 | } |
428 | @@ -502,12 +518,12 @@ |
429 | |
430 | mBackend->endBatchOperation(); |
431 | |
432 | - mDBus.notifyEventsRemoved(events); |
433 | + Q_EMIT eventsRemoved(events); |
434 | if (!removedThreads.isEmpty()) { |
435 | - mDBus.notifyThreadsRemoved(removedThreads.values()); |
436 | + Q_EMIT threadsRemoved(removedThreads.values()); |
437 | } |
438 | if (!modifiedThreads.isEmpty()) { |
439 | - mDBus.notifyThreadsModified(modifiedThreads.values()); |
440 | + Q_EMIT threadsModified(modifiedThreads.values()); |
441 | } |
442 | return true; |
443 | } |
444 | @@ -540,7 +556,7 @@ |
445 | } |
446 | |
447 | if (!removedEmptyThreads.isEmpty()) { |
448 | - mDBus.notifyThreadsRemoved(removedEmptyThreads.values()); |
449 | + Q_EMIT threadsRemoved(removedEmptyThreads.values()); |
450 | } |
451 | |
452 | if (events.size() > 0) { |
453 | @@ -812,7 +828,7 @@ |
454 | QString threadId = channel->targetId(); |
455 | if (mBackend->updateRoomParticipants(accountId, threadId, History::EventTypeText, participants)) { |
456 | QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap()); |
457 | - mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread); |
458 | + Q_EMIT threadsModified(QList<QVariantMap>() << updatedThread); |
459 | } |
460 | } |
461 | |
462 | @@ -842,7 +858,7 @@ |
463 | QString threadId = channel->targetId(); |
464 | if (mBackend->updateRoomParticipantsRoles(accountId, threadId, History::EventTypeText, participantsRoles)) { |
465 | QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap()); |
466 | - mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread); |
467 | + Q_EMIT threadsModified(QList<QVariantMap>() << updatedThread); |
468 | } |
469 | |
470 | // update self roles in room properties |
471 | @@ -877,7 +893,7 @@ |
472 | { |
473 | if (mBackend->updateRoomInfo(accountId, threadId, type, properties, invalidated)) { |
474 | QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap()); |
475 | - mDBus.notifyThreadsModified(QList<QVariantMap>() << thread); |
476 | + Q_EMIT threadsModified(QList<QVariantMap>() << thread); |
477 | } |
478 | } |
479 | |
480 | @@ -1181,6 +1197,16 @@ |
481 | return hash; |
482 | } |
483 | |
484 | +void HistoryDaemon::setupThreadView(History::PluginThreadView *view) |
485 | +{ |
486 | + connect(this, SIGNAL(threadsAdded(QList<QVariantMap>)), |
487 | + view, SLOT(onThreadsAdded(QList<QVariantMap>))); |
488 | + connect(this, SIGNAL(threadsModified(QList<QVariantMap>)), |
489 | + view, SLOT(onThreadsModified(QList<QVariantMap>))); |
490 | + connect(this, SIGNAL(threadsRemoved(QList<QVariantMap>)), |
491 | + view, SLOT(onThreadsRemoved(QList<QVariantMap>))); |
492 | +} |
493 | + |
494 | QVariantMap HistoryDaemon::getInterfaceProperties(const Tp::AbstractInterface *interface) |
495 | { |
496 | QDBusInterface propsInterface(interface->service(), interface->path(), "org.freedesktop.DBus.Properties"); |
497 | |
498 | === modified file 'daemon/historydaemon.h' |
499 | --- daemon/historydaemon.h 2016-10-20 13:56:10 +0000 |
500 | +++ daemon/historydaemon.h 2017-01-26 18:33:44 +0000 |
501 | @@ -58,6 +58,15 @@ |
502 | bool removeEvents(const QList<QVariantMap> &events); |
503 | bool removeThreads(const QList<QVariantMap> &threads); |
504 | |
505 | +Q_SIGNALS: |
506 | + void threadsAdded(const QList<QVariantMap> &threads); |
507 | + void threadsModified(const QList<QVariantMap> &threads); |
508 | + void threadsRemoved(const QList<QVariantMap> &threads); |
509 | + |
510 | + void eventsAdded(const QList<QVariantMap> &events); |
511 | + void eventsModified(const QList<QVariantMap> &events); |
512 | + void eventsRemoved(const QList<QVariantMap> &events); |
513 | + |
514 | private Q_SLOTS: |
515 | void onObserverCreated(); |
516 | void onCallEnded(const Tp::CallChannelPtr &channel); |
517 | @@ -76,6 +85,8 @@ |
518 | void updateRoomParticipants(const Tp::TextChannelPtr channel); |
519 | void updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap); |
520 | QString hashThread(const QVariantMap &thread); |
521 | + void setupThreadView(History::PluginThreadView *view); |
522 | + |
523 | static QVariantMap getInterfaceProperties(const Tp::AbstractInterface *interface); |
524 | void updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties); |
525 | void updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated); |
526 | |
527 | === modified file 'daemon/historyservicedbus.cpp' |
528 | --- daemon/historyservicedbus.cpp 2016-11-24 01:56:01 +0000 |
529 | +++ daemon/historyservicedbus.cpp 2017-01-26 18:33:44 +0000 |
530 | @@ -45,36 +45,6 @@ |
531 | return QDBusConnection::sessionBus().registerService(History::DBusService); |
532 | } |
533 | |
534 | -void HistoryServiceDBus::notifyThreadsAdded(const QList<QVariantMap> &threads) |
535 | -{ |
536 | - Q_EMIT ThreadsAdded(threads); |
537 | -} |
538 | - |
539 | -void HistoryServiceDBus::notifyThreadsModified(const QList<QVariantMap> &threads) |
540 | -{ |
541 | - Q_EMIT ThreadsModified(threads); |
542 | -} |
543 | - |
544 | -void HistoryServiceDBus::notifyThreadsRemoved(const QList<QVariantMap> &threads) |
545 | -{ |
546 | - Q_EMIT ThreadsRemoved(threads); |
547 | -} |
548 | - |
549 | -void HistoryServiceDBus::notifyEventsAdded(const QList<QVariantMap> &events) |
550 | -{ |
551 | - Q_EMIT EventsAdded(events); |
552 | -} |
553 | - |
554 | -void HistoryServiceDBus::notifyEventsModified(const QList<QVariantMap> &events) |
555 | -{ |
556 | - Q_EMIT EventsModified(events); |
557 | -} |
558 | - |
559 | -void HistoryServiceDBus::notifyEventsRemoved(const QList<QVariantMap> &events) |
560 | -{ |
561 | - Q_EMIT EventsRemoved(events); |
562 | -} |
563 | - |
564 | QVariantMap HistoryServiceDBus::ThreadForProperties(const QString &accountId, |
565 | int type, |
566 | const QVariantMap &properties, |
567 | |
568 | === modified file 'daemon/historyservicedbus.h' |
569 | --- daemon/historyservicedbus.h 2016-06-17 01:49:46 +0000 |
570 | +++ daemon/historyservicedbus.h 2017-01-26 18:33:44 +0000 |
571 | @@ -36,14 +36,6 @@ |
572 | |
573 | bool connectToBus(); |
574 | |
575 | - void notifyThreadsAdded(const QList<QVariantMap> &threads); |
576 | - void notifyThreadsModified(const QList<QVariantMap> &threads); |
577 | - void notifyThreadsRemoved(const QList<QVariantMap> &threads); |
578 | - |
579 | - void notifyEventsAdded(const QList<QVariantMap> &events); |
580 | - void notifyEventsModified(const QList<QVariantMap> &events); |
581 | - void notifyEventsRemoved(const QList<QVariantMap> &events); |
582 | - |
583 | // functions exposed on DBUS |
584 | QVariantMap ThreadForParticipants(const QString &accountId, |
585 | int type, |
586 | |
587 | === modified file 'plugins/sqlite/sqlitehistoryplugin.cpp' |
588 | --- plugins/sqlite/sqlitehistoryplugin.cpp 2016-11-24 01:50:48 +0000 |
589 | +++ plugins/sqlite/sqlitehistoryplugin.cpp 2017-01-26 18:33:44 +0000 |
590 | @@ -479,10 +479,10 @@ |
591 | return result; |
592 | } |
593 | |
594 | - QList<QVariantMap> results = parseThreadResults(type, query, properties); |
595 | + History::Threads results = parseThreadResults(type, query, properties); |
596 | query.clear(); |
597 | if (!results.isEmpty()) { |
598 | - result = results.first(); |
599 | + result = results.first().properties(); |
600 | } |
601 | |
602 | return result; |
603 | @@ -1133,9 +1133,9 @@ |
604 | return queryText; |
605 | } |
606 | |
607 | -QList<QVariantMap> SQLiteHistoryPlugin::parseThreadResults(History::EventType type, QSqlQuery &query, const QVariantMap &properties) |
608 | +History::Threads SQLiteHistoryPlugin::parseThreadResults(History::EventType type, QSqlQuery &query, const QVariantMap &properties) |
609 | { |
610 | - QList<QVariantMap> threads; |
611 | + History::Threads threads; |
612 | QSqlQuery attachmentsQuery(SQLiteDatabase::instance()->database()); |
613 | QList<QVariantMap> attachments; |
614 | bool grouped = false; |
615 | @@ -1298,7 +1298,7 @@ |
616 | thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(13).toString(), true); |
617 | break; |
618 | } |
619 | - threads << thread; |
620 | + threads << History::Thread::fromProperties(thread); |
621 | } |
622 | return threads; |
623 | } |
624 | |
625 | === modified file 'plugins/sqlite/sqlitehistoryplugin.h' |
626 | --- plugins/sqlite/sqlitehistoryplugin.h 2016-09-21 17:44:39 +0000 |
627 | +++ plugins/sqlite/sqlitehistoryplugin.h 2017-01-26 18:33:44 +0000 |
628 | @@ -86,7 +86,7 @@ |
629 | |
630 | // functions to be used internally |
631 | QString sqlQueryForThreads(History::EventType type, const QString &condition, const QString &order); |
632 | - QList<QVariantMap> parseThreadResults(History::EventType type, QSqlQuery &query, const QVariantMap &properties = QVariantMap()); |
633 | + History::Threads parseThreadResults(History::EventType type, QSqlQuery &query, const QVariantMap &properties = QVariantMap()); |
634 | |
635 | QString sqlQueryForEvents(History::EventType type, const QString &condition, const QString &order); |
636 | QList<QVariantMap> parseEventResults(History::EventType type, QSqlQuery &query); |
637 | |
638 | === modified file 'plugins/sqlite/sqlitehistorythreadview.cpp' |
639 | --- plugins/sqlite/sqlitehistorythreadview.cpp 2016-11-24 01:56:01 +0000 |
640 | +++ plugins/sqlite/sqlitehistorythreadview.cpp 2017-01-26 18:33:44 +0000 |
641 | @@ -1,5 +1,5 @@ |
642 | /* |
643 | - * Copyright (C) 2013 Canonical, Ltd. |
644 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
645 | * |
646 | * Authors: |
647 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
648 | @@ -35,6 +35,10 @@ |
649 | : History::PluginThreadView(), mPlugin(plugin), mType(type), mSort(sort), |
650 | mFilter(filter), mPageSize(15), mQuery(SQLiteDatabase::instance()->database()), mOffset(0), mValid(true), mQueryProperties(properties) |
651 | { |
652 | + qDebug() << __PRETTY_FUNCTION__; |
653 | + |
654 | + // connect to the thread signals in the daemon to filter the threads |
655 | + |
656 | mTemporaryTable = QString("threadview%1%2").arg(QString::number((qulonglong)this), QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz")); |
657 | mQuery.setForwardOnly(true); |
658 | |
659 | @@ -80,7 +84,9 @@ |
660 | |
661 | QList<QVariantMap> SQLiteHistoryThreadView::NextPage() |
662 | { |
663 | - QList<QVariantMap> threads; |
664 | + qDebug() << __PRETTY_FUNCTION__; |
665 | + History::Threads threads; |
666 | + QList<QVariantMap> props; |
667 | |
668 | // now prepare for selecting from it |
669 | mQuery.prepare(QString("SELECT * FROM %1 LIMIT %2 OFFSET %3").arg(mTemporaryTable, |
670 | @@ -89,17 +95,233 @@ |
671 | qCritical() << "Error:" << mQuery.lastError() << mQuery.lastQuery(); |
672 | mValid = false; |
673 | Q_EMIT Invalidated(); |
674 | - return threads; |
675 | + return props; |
676 | } |
677 | |
678 | threads = mPlugin->parseThreadResults(mType, mQuery, mQueryProperties); |
679 | mOffset += mPageSize; |
680 | mQuery.clear(); |
681 | |
682 | - return threads; |
683 | + Q_FOREACH(const History::Thread &thread, threads) { |
684 | + if (threadIsInGroup(thread)) { |
685 | + continue; |
686 | + } |
687 | + createThreadGroup(thread); |
688 | + props << thread.properties(); |
689 | + } |
690 | + return props; |
691 | } |
692 | |
693 | bool SQLiteHistoryThreadView::IsValid() const |
694 | { |
695 | return mValid; |
696 | } |
697 | + |
698 | +bool SQLiteHistoryThreadView::threadIsInGroup(const History::Thread &thread) |
699 | +{ |
700 | + Q_FOREACH(const HistoryThreadGroup &group, mGroups) { |
701 | + if (group.threads.contains(thread)) { |
702 | + return true; |
703 | + } |
704 | + } |
705 | + return false; |
706 | +} |
707 | + |
708 | +void SQLiteHistoryThreadView::createThreadGroup(const History::Thread &thread) |
709 | +{ |
710 | + HistoryThreadGroup group; |
711 | + group.displayedThread = thread; |
712 | + group.threads = thread.groupedThreads(); |
713 | + mGroups << group; |
714 | +} |
715 | + |
716 | +History::Threads SQLiteHistoryThreadView::fromList(const QList<QVariantMap> &threads) |
717 | +{ |
718 | + History::Threads result; |
719 | + Q_FOREACH(const QVariantMap &map, threads) { |
720 | + result << History::Thread::fromProperties(map); |
721 | + } |
722 | + return result; |
723 | +} |
724 | + |
725 | +QList<QVariantMap> SQLiteHistoryThreadView::getGroupedThreads(const QList<QVariantMap> &originalThreads) |
726 | +{ |
727 | + bool needsGrouping = mQueryProperties.contains(History::FieldGroupingProperty); |
728 | + QList<QVariantMap> grouped; |
729 | + Q_FOREACH(const QVariantMap &originalThread, originalThreads) { |
730 | + if (needsGrouping) { |
731 | + QVariantMap groupedThread = mPlugin->getSingleThread((History::EventType)originalThread[History::FieldType].toInt(), |
732 | + originalThread[History::FieldAccountId].toString(), |
733 | + originalThread[History::FieldThreadId].toString(), |
734 | + mQueryProperties); |
735 | + grouped << groupedThread; |
736 | + } else { |
737 | + grouped << originalThread; |
738 | + } |
739 | + } |
740 | + return grouped; |
741 | +} |
742 | + |
743 | +HistoryThreadGroup SQLiteHistoryThreadView::updateDisplayedThread(HistoryThreadGroup group) |
744 | +{ |
745 | + if (group.threads.isEmpty()) { |
746 | + return group; |
747 | + } |
748 | + |
749 | + History::Thread displayedThread = group.threads.first(); |
750 | + Q_FOREACH(const History::Thread &thread, group.threads) { |
751 | + if (thread.lastEvent().timestamp() > displayedThread.lastEvent().timestamp()) { |
752 | + displayedThread = thread; |
753 | + } |
754 | + } |
755 | + group.displayedThread = displayedThread; |
756 | + return group; |
757 | +} |
758 | + |
759 | +void SQLiteHistoryThreadView::notifyChanges(const QList<QVariantMap> added, const QList<QVariantMap> removed, const QList<QVariantMap> modified) |
760 | +{ |
761 | + qDebug() << "Added:" << added.count() << "Removed:" << removed.count() << "Modified:" << modified.count(); |
762 | + |
763 | + if (!removed.isEmpty()) { |
764 | + Q_EMIT ThreadsRemoved(removed); |
765 | + } |
766 | + |
767 | + if (!added.isEmpty()) { |
768 | + Q_EMIT ThreadsAdded(added); |
769 | + } |
770 | + |
771 | + if (!modified.isEmpty()) { |
772 | + Q_EMIT ThreadsModified(modified); |
773 | + } |
774 | +} |
775 | + |
776 | +void SQLiteHistoryThreadView::onThreadsAdded(const QList<QVariantMap> &threads) |
777 | +{ |
778 | + qDebug() << __PRETTY_FUNCTION__; |
779 | + |
780 | + History::Threads groupedThreads = fromList(getGroupedThreads(threads)); |
781 | + |
782 | + QList<QVariantMap> addedThreads; |
783 | + QList<QVariantMap> modifiedThreads; |
784 | + QList<QVariantMap> removedThreads; |
785 | + |
786 | + // now we need to iterate over the existing groups to see if this new thread belongs to any existing group |
787 | + Q_FOREACH(const History::Thread &groupedThread, groupedThreads) { |
788 | + bool found = false; |
789 | + for (int i = 0; i < mGroups.count(); ++i) { |
790 | + HistoryThreadGroup &group = mGroups[i]; |
791 | + if (groupedThread.groupedThreads().contains(group.displayedThread)) { |
792 | + // append the new thread to the group. |
793 | + group.threads.append(groupedThread); |
794 | + HistoryThreadGroup updatedGroup = updateDisplayedThread(group); |
795 | + if (updatedGroup.displayedThread != group.displayedThread) { |
796 | + // as the displayed thread changed, we need to remove the old thread |
797 | + // and add the new one |
798 | + removedThreads << group.displayedThread.properties(); |
799 | + addedThreads << updatedGroup.displayedThread.properties(); |
800 | + } else { |
801 | + // the thread was just updated, notify as such |
802 | + modifiedThreads << updatedGroup.displayedThread.properties(); |
803 | + } |
804 | + group = updatedGroup; |
805 | + found = true; |
806 | + break; |
807 | + } |
808 | + } |
809 | + |
810 | + // in case it is not found, we need to create a group for the thread |
811 | + if (!found) { |
812 | + HistoryThreadGroup group; |
813 | + group.displayedThread = groupedThread; |
814 | + group.threads = groupedThread.groupedThreads(); |
815 | + mGroups << group; |
816 | + addedThreads << groupedThread.properties(); |
817 | + } |
818 | + } |
819 | + |
820 | + // the modified threads here are threads that are currently displayed but they need updating |
821 | + // since the group itself got one extra thread, so get the latest version of them. |
822 | + notifyChanges(addedThreads, removedThreads, getGroupedThreads(modifiedThreads)); |
823 | +} |
824 | + |
825 | +void SQLiteHistoryThreadView::onThreadsModified(const QList<QVariantMap> &threads) |
826 | +{ |
827 | + qDebug() << __PRETTY_FUNCTION__; |
828 | + History::Threads groupedThreads = fromList(getGroupedThreads(threads)); |
829 | + |
830 | + QList<QVariantMap> addedThreads; |
831 | + QList<QVariantMap> modifiedThreads; |
832 | + QList<QVariantMap> removedThreads; |
833 | + |
834 | + // now we need to check if the thread is already displayed and if so, |
835 | + // check if the displayed thread changed. |
836 | + Q_FOREACH(const History::Thread &groupedThread, groupedThreads) { |
837 | + bool found = false; |
838 | + for (int i = 0; i < mGroups.count(); ++i) { |
839 | + HistoryThreadGroup &group = mGroups[i]; |
840 | + if (group.threads.contains(groupedThread)) { |
841 | + // remove the old instance and append the new one |
842 | + group.threads.removeOne(groupedThread); |
843 | + group.threads.append(groupedThread); |
844 | + HistoryThreadGroup updatedGroup = updateDisplayedThread(group); |
845 | + if (updatedGroup.displayedThread != group.displayedThread) { |
846 | + // as the displayed thread changed, we need to remove the old thread |
847 | + // and add the new one |
848 | + removedThreads << group.displayedThread.properties(); |
849 | + addedThreads << updatedGroup.displayedThread.properties(); |
850 | + } else { |
851 | + // the thread was just updated, notify as such |
852 | + modifiedThreads << updatedGroup.displayedThread.properties(); |
853 | + } |
854 | + group = updatedGroup; |
855 | + found = true; |
856 | + break; |
857 | + } |
858 | + } |
859 | + |
860 | + // in case it is not found, we need to create a group for the thread |
861 | + if (!found) { |
862 | + HistoryThreadGroup group; |
863 | + group.displayedThread = groupedThread; |
864 | + group.threads = groupedThread.groupedThreads(); |
865 | + mGroups << group; |
866 | + addedThreads << groupedThread.properties(); |
867 | + } |
868 | + } |
869 | + |
870 | + // the threads marked as "added" are existing threads that were not displayed before |
871 | + // and that are displayed now. So in order to make sure they are up-to-date, get the latest |
872 | + // version of the grouping for each of them. |
873 | + notifyChanges(getGroupedThreads(addedThreads), removedThreads, modifiedThreads); |
874 | +} |
875 | + |
876 | +void SQLiteHistoryThreadView::onThreadsRemoved(const QList<QVariantMap> &threads) |
877 | +{ |
878 | + qDebug() << __PRETTY_FUNCTION__; |
879 | + QList<QVariantMap> addedThreads; |
880 | + QList<QVariantMap> modifiedThreads; |
881 | + QList<QVariantMap> removedThreads; |
882 | + |
883 | + // try to find the thread in the groups to notify its removal |
884 | + Q_FOREACH(const History::Thread &thread, fromList(threads)) { |
885 | + for (int i = 0; i < mGroups.count(); ++i) { |
886 | + HistoryThreadGroup &group = mGroups[i]; |
887 | + if (group.threads.contains(thread)) { |
888 | + group.threads.removeOne(thread); |
889 | + if (group.displayedThread == thread) { |
890 | + removedThreads << thread.properties(); |
891 | + if (!group.threads.isEmpty()) { |
892 | + group = updateDisplayedThread(group); |
893 | + addedThreads << group.displayedThread.properties(); |
894 | + } |
895 | + } else { |
896 | + modifiedThreads << group.displayedThread.properties(); |
897 | + } |
898 | + break; |
899 | + } |
900 | + } |
901 | + } |
902 | + |
903 | + // we need to update the added and modified threads to get the correct group buddies |
904 | + notifyChanges(getGroupedThreads(addedThreads), removedThreads, getGroupedThreads(modifiedThreads)); |
905 | +} |
906 | |
907 | === modified file 'plugins/sqlite/sqlitehistorythreadview.h' |
908 | --- plugins/sqlite/sqlitehistorythreadview.h 2015-09-21 20:05:06 +0000 |
909 | +++ plugins/sqlite/sqlitehistorythreadview.h 2017-01-26 18:33:44 +0000 |
910 | @@ -1,5 +1,5 @@ |
911 | /* |
912 | - * Copyright (C) 2013 Canonical, Ltd. |
913 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
914 | * |
915 | * Authors: |
916 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
917 | @@ -30,6 +30,16 @@ |
918 | |
919 | class SQLiteHistoryPlugin; |
920 | |
921 | +class HistoryThreadGroup { |
922 | +public: |
923 | + History::Thread displayedThread; |
924 | + History::Threads threads; |
925 | + |
926 | + bool operator==(const HistoryThreadGroup &other) const; |
927 | +}; |
928 | + |
929 | +typedef QList<HistoryThreadGroup> HistoryThreadGroupList; |
930 | + |
931 | class SQLiteHistoryThreadView : public History::PluginThreadView |
932 | { |
933 | Q_OBJECT |
934 | @@ -44,6 +54,22 @@ |
935 | QList<QVariantMap> NextPage(); |
936 | bool IsValid() const; |
937 | |
938 | +public Q_SLOTS: |
939 | + virtual void onThreadsAdded(const QList<QVariantMap> &threads); |
940 | + virtual void onThreadsModified(const QList<QVariantMap> &threads); |
941 | + virtual void onThreadsRemoved(const QList<QVariantMap> &threads); |
942 | + |
943 | +protected: |
944 | + bool threadIsInGroup(const History::Thread &thread); |
945 | + void createThreadGroup(const History::Thread &thread); |
946 | + History::Threads fromList(const QList<QVariantMap> &threads); |
947 | + QList<QVariantMap> getGroupedThreads(const QList<QVariantMap> &originalThreads); |
948 | + HistoryThreadGroup updateDisplayedThread(HistoryThreadGroup group); |
949 | + |
950 | + void notifyChanges(const QList<QVariantMap> added, |
951 | + const QList<QVariantMap> removed, |
952 | + const QList<QVariantMap> modified); |
953 | + |
954 | private: |
955 | History::EventType mType; |
956 | History::Sort mSort; |
957 | @@ -55,6 +81,7 @@ |
958 | int mOffset; |
959 | bool mValid; |
960 | QVariantMap mQueryProperties; |
961 | + HistoryThreadGroupList mGroups; |
962 | }; |
963 | |
964 | #endif // SQLITEHISTORYTHREADVIEW_H |
965 | |
966 | === modified file 'src/PluginThreadView.xml' |
967 | --- src/PluginThreadView.xml 2013-09-13 19:24:03 +0000 |
968 | +++ src/PluginThreadView.xml 2017-01-26 18:33:44 +0000 |
969 | @@ -33,5 +33,33 @@ |
970 | Notifies that this view is no longer valid. |
971 | ]]></dox:d> |
972 | </signal> |
973 | + <signal name="ThreadsAdded"> |
974 | + <dox:d><![CDATA[ |
975 | + Notifies that new threads were added. |
976 | + ]]></dox:d> |
977 | + <arg type="a(a{sv})" direction="in"/> |
978 | + <arg type="a(a{sv})" direction="out"/> |
979 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QList < QVariantMap >"/> |
980 | + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList < QVariantMap >"/> |
981 | + </signal> |
982 | + <signal name="ThreadsModified"> |
983 | + <dox:d><![CDATA[ |
984 | + Notifies that threads were modified. |
985 | + ]]></dox:d> |
986 | + <arg type="a(a{sv})" direction="in"/> |
987 | + <arg type="a(a{sv})" direction="out"/> |
988 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QList < QVariantMap >"/> |
989 | + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList < QVariantMap >"/> |
990 | + </signal> |
991 | + <signal name="ThreadsRemoved"> |
992 | + <dox:d><![CDATA[ |
993 | + Notifies that threads were removed. |
994 | + ]]></dox:d> |
995 | + <arg type="a(a{sv})" direction="in"/> |
996 | + <arg type="a(a{sv})" direction="out"/> |
997 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QList < QVariantMap >"/> |
998 | + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList < QVariantMap >"/> |
999 | + </signal> |
1000 | + |
1001 | </interface> |
1002 | </node> |
1003 | |
1004 | === modified file 'src/contactmatcher.cpp' |
1005 | --- src/contactmatcher.cpp 2016-06-17 01:49:46 +0000 |
1006 | +++ src/contactmatcher.cpp 2017-01-26 18:33:44 +0000 |
1007 | @@ -112,6 +112,10 @@ |
1008 | } else if (!synchronous) { |
1009 | RequestInfo info{accountId, identifier}; |
1010 | mPendingRequests.append(info); |
1011 | + map[History::FieldAlias] = ""; |
1012 | + map[History::FieldAvatar] = ""; |
1013 | + map[History::FieldContactId] = ""; |
1014 | + map[History::FieldDetailProperties] = QVariantMap(); |
1015 | } |
1016 | map[History::FieldIdentifier] = identifier; |
1017 | map[History::FieldAccountId] = accountId; |
1018 | @@ -196,7 +200,7 @@ |
1019 | QString identifier = it2.key(); |
1020 | |
1021 | Q_FOREACH(const QContact &contact, contacts) { |
1022 | - bool previousMatch = (contactInfo.contains(History::FieldContactId) && |
1023 | + bool previousMatch = (hasMatch(contactInfo) && |
1024 | contactInfo[History::FieldContactId].toString() == contact.id().toString()); |
1025 | QVariantMap map = matchAndUpdate(accountId, identifier, contact); |
1026 | if (hasMatch(map)){ |
1027 | @@ -406,6 +410,10 @@ |
1028 | QVariantMap contactInfo; |
1029 | contactInfo[History::FieldIdentifier] = identifier; |
1030 | contactInfo[History::FieldAccountId] = accountId; |
1031 | + contactInfo[History::FieldAlias] = ""; |
1032 | + contactInfo[History::FieldAvatar] = ""; |
1033 | + contactInfo[History::FieldContactId] = ""; |
1034 | + contactInfo[History::FieldDetailProperties] = QVariantMap(); |
1035 | |
1036 | if (contact.isEmpty()) { |
1037 | return contactInfo; |
1038 | |
1039 | === modified file 'src/pluginthreadview.cpp' |
1040 | --- src/pluginthreadview.cpp 2016-11-24 01:56:01 +0000 |
1041 | +++ src/pluginthreadview.cpp 2017-01-26 18:33:44 +0000 |
1042 | @@ -1,5 +1,5 @@ |
1043 | /* |
1044 | - * Copyright (C) 2013 Canonical, Ltd. |
1045 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
1046 | * |
1047 | * Authors: |
1048 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
1049 | @@ -71,4 +71,22 @@ |
1050 | return d->objectPath; |
1051 | } |
1052 | |
1053 | +void PluginThreadView::onThreadsAdded(const QList<QVariantMap> &threads) |
1054 | +{ |
1055 | + // the default implementation just relay the signal |
1056 | + Q_EMIT ThreadsAdded(threads); |
1057 | +} |
1058 | + |
1059 | +void PluginThreadView::onThreadsModified(const QList<QVariantMap> &threads) |
1060 | +{ |
1061 | + // the default implementation just relay the signal |
1062 | + Q_EMIT ThreadsModified(threads); |
1063 | +} |
1064 | + |
1065 | +void PluginThreadView::onThreadsRemoved(const QList<QVariantMap> &threads) |
1066 | +{ |
1067 | + // the default implementation just relay the signal |
1068 | + Q_EMIT ThreadsRemoved(threads); |
1069 | +} |
1070 | + |
1071 | } |
1072 | |
1073 | === modified file 'src/pluginthreadview.h' |
1074 | --- src/pluginthreadview.h 2013-11-19 17:52:53 +0000 |
1075 | +++ src/pluginthreadview.h 2017-01-26 18:33:44 +0000 |
1076 | @@ -1,5 +1,5 @@ |
1077 | /* |
1078 | - * Copyright (C) 2013 Canonical, Ltd. |
1079 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
1080 | * |
1081 | * Authors: |
1082 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
1083 | @@ -26,6 +26,7 @@ |
1084 | #include <QDBusContext> |
1085 | #include <QScopedPointer> |
1086 | #include <QVariantMap> |
1087 | +#include <Thread> |
1088 | |
1089 | namespace History { |
1090 | |
1091 | @@ -41,14 +42,22 @@ |
1092 | |
1093 | // DBus exposed methods |
1094 | Q_NOREPLY void Destroy(); |
1095 | + virtual bool IsValid() const; |
1096 | virtual QList<QVariantMap> NextPage() = 0; |
1097 | - virtual bool IsValid() const; |
1098 | |
1099 | // other methods |
1100 | QString objectPath() const; |
1101 | |
1102 | Q_SIGNALS: |
1103 | void Invalidated(); |
1104 | + void ThreadsAdded(const QList<QVariantMap> &threads); |
1105 | + void ThreadsModified(const QList<QVariantMap> &threads); |
1106 | + void ThreadsRemoved(const QList<QVariantMap> &threads); |
1107 | + |
1108 | +public Q_SLOTS: |
1109 | + virtual void onThreadsAdded(const QList<QVariantMap> &threads); |
1110 | + virtual void onThreadsModified(const QList<QVariantMap> &threads); |
1111 | + virtual void onThreadsRemoved(const QList<QVariantMap> &threads); |
1112 | |
1113 | private: |
1114 | QScopedPointer<PluginThreadViewPrivate> d_ptr; |
1115 | |
1116 | === modified file 'src/thread.cpp' |
1117 | --- src/thread.cpp 2016-07-12 02:08:11 +0000 |
1118 | +++ src/thread.cpp 2017-01-26 18:33:44 +0000 |
1119 | @@ -185,6 +185,11 @@ |
1120 | return true; |
1121 | } |
1122 | |
1123 | +bool Thread::operator!=(const Thread &other) const |
1124 | +{ |
1125 | + return !(*this == other); |
1126 | +} |
1127 | + |
1128 | bool Thread::operator<(const Thread &other) const |
1129 | { |
1130 | QString selfData = QString::number(type()) + accountId() + threadId(); |
1131 | @@ -280,10 +285,14 @@ |
1132 | while (!argument.atEnd()) { |
1133 | QVariantMap props; |
1134 | QVariant variant; |
1135 | - argument >> variant; |
1136 | - QDBusArgument innerArgument = variant.value<QDBusArgument>(); |
1137 | - if (!innerArgument.atEnd()) { |
1138 | - innerArgument >> props; |
1139 | + if (argument.currentType() == QDBusArgument::MapType) { |
1140 | + argument >> props; |
1141 | + } else { |
1142 | + argument >> variant; |
1143 | + QDBusArgument innerArgument = variant.value<QDBusArgument>(); |
1144 | + if (!innerArgument.atEnd()) { |
1145 | + innerArgument >> props; |
1146 | + } |
1147 | } |
1148 | threads << Thread::fromProperties(props); |
1149 | } |
1150 | |
1151 | === modified file 'src/thread.h' |
1152 | --- src/thread.h 2016-07-12 01:59:06 +0000 |
1153 | +++ src/thread.h 2017-01-26 18:33:44 +0000 |
1154 | @@ -76,6 +76,7 @@ |
1155 | |
1156 | bool isNull() const; |
1157 | bool operator==(const Thread &other) const; |
1158 | + bool operator!=(const Thread &other) const; |
1159 | bool operator<(const Thread &other) const; |
1160 | |
1161 | virtual QVariantMap properties() const; |
1162 | |
1163 | === modified file 'src/threadview.cpp' |
1164 | --- src/threadview.cpp 2015-10-01 19:44:45 +0000 |
1165 | +++ src/threadview.cpp 2017-01-26 18:33:44 +0000 |
1166 | @@ -59,36 +59,48 @@ |
1167 | return filtered; |
1168 | } |
1169 | |
1170 | -void ThreadViewPrivate::_d_threadsAdded(const History::Threads &threads) |
1171 | +void ThreadViewPrivate::_d_threadsAdded(const QList<QVariantMap> &threads) |
1172 | { |
1173 | + qDebug() << __PRETTY_FUNCTION__ << threads.count(); |
1174 | Q_Q(ThreadView); |
1175 | |
1176 | - Threads filtered = filteredThreads(threads); |
1177 | + Threads filtered = filteredThreads(parseThreads(threads)); |
1178 | if (!filtered.isEmpty()) { |
1179 | Q_EMIT q->threadsAdded(filtered); |
1180 | } |
1181 | } |
1182 | |
1183 | -void ThreadViewPrivate::_d_threadsModified(const Threads &threads) |
1184 | +void ThreadViewPrivate::_d_threadsModified(const QList<QVariantMap> &threads) |
1185 | { |
1186 | + qDebug() << __PRETTY_FUNCTION__ << threads.count(); |
1187 | Q_Q(ThreadView); |
1188 | |
1189 | - Threads filtered = filteredThreads(threads); |
1190 | + Threads filtered = filteredThreads(parseThreads(threads)); |
1191 | if (!filtered.isEmpty()) { |
1192 | Q_EMIT q->threadsModified(filtered); |
1193 | } |
1194 | } |
1195 | |
1196 | -void ThreadViewPrivate::_d_threadsRemoved(const Threads &threads) |
1197 | +void ThreadViewPrivate::_d_threadsRemoved(const QList<QVariantMap> &threads) |
1198 | { |
1199 | + qDebug() << __PRETTY_FUNCTION__ << threads.count(); |
1200 | Q_Q(ThreadView); |
1201 | |
1202 | - Threads filtered = filteredThreads(threads); |
1203 | + Threads filtered = filteredThreads(parseThreads(threads)); |
1204 | if (!filtered.isEmpty()) { |
1205 | Q_EMIT q->threadsRemoved(filtered); |
1206 | } |
1207 | } |
1208 | |
1209 | +Threads ThreadViewPrivate::parseThreads(const QList<QVariantMap> &threads) |
1210 | +{ |
1211 | + Threads result; |
1212 | + Q_FOREACH(const QVariantMap &thread, threads) { |
1213 | + result << Thread::fromProperties(thread); |
1214 | + } |
1215 | + return result; |
1216 | +} |
1217 | + |
1218 | // ------------- ThreadView ------------------------------------------------------- |
1219 | |
1220 | ThreadView::ThreadView(History::EventType type, |
1221 | @@ -123,15 +135,13 @@ |
1222 | d_ptr->dbus = new QDBusInterface(History::DBusService, d_ptr->objectPath, History::ThreadViewInterface, |
1223 | QDBusConnection::sessionBus(), this); |
1224 | |
1225 | - connect(Manager::instance(), |
1226 | - SIGNAL(threadsAdded(History::Threads)), |
1227 | - SLOT(_d_threadsAdded(History::Threads))); |
1228 | - connect(Manager::instance(), |
1229 | - SIGNAL(threadsModified(History::Threads)), |
1230 | - SLOT(_d_threadsModified(History::Threads))); |
1231 | - connect(Manager::instance(), |
1232 | - SIGNAL(threadsRemoved(History::Threads)), |
1233 | - SLOT(_d_threadsRemoved(History::Threads))); |
1234 | + QDBusConnection connection = QDBusConnection::sessionBus(); |
1235 | + connection.connect(d_ptr->dbus->service(), d_ptr->dbus->path(), d_ptr->dbus->interface(), "ThreadsAdded", |
1236 | + this, SLOT(_d_threadsAdded(QList<QVariantMap>))); |
1237 | + connection.connect(d_ptr->dbus->service(), d_ptr->dbus->path(), d_ptr->dbus->interface(), "ThreadsModified", |
1238 | + this, SLOT(_d_threadsModified(QList<QVariantMap>))); |
1239 | + connection.connect(d_ptr->dbus->service(), d_ptr->dbus->path(), d_ptr->dbus->interface(), "ThreadsRemoved", |
1240 | + this, SLOT(_d_threadsRemoved(QList<QVariantMap>))); |
1241 | } |
1242 | |
1243 | ThreadView::~ThreadView() |
1244 | |
1245 | === modified file 'src/threadview.h' |
1246 | --- src/threadview.h 2015-09-21 20:05:06 +0000 |
1247 | +++ src/threadview.h 2017-01-26 18:33:44 +0000 |
1248 | @@ -55,9 +55,9 @@ |
1249 | void invalidated(); |
1250 | |
1251 | private: |
1252 | - Q_PRIVATE_SLOT(d_func(), void _d_threadsAdded(const History::Threads &threads)) |
1253 | - Q_PRIVATE_SLOT(d_func(), void _d_threadsModified(const History::Threads &threads)) |
1254 | - Q_PRIVATE_SLOT(d_func(), void _d_threadsRemoved(const History::Threads &threads)) |
1255 | + Q_PRIVATE_SLOT(d_func(), void _d_threadsAdded(const QList<QVariantMap> &threads)) |
1256 | + Q_PRIVATE_SLOT(d_func(), void _d_threadsModified(const QList<QVariantMap> &threads)) |
1257 | + Q_PRIVATE_SLOT(d_func(), void _d_threadsRemoved(const QList<QVariantMap> &threads)) |
1258 | QScopedPointer<ThreadViewPrivate> d_ptr; |
1259 | |
1260 | }; |
1261 | |
1262 | === modified file 'src/threadview_p.h' |
1263 | --- src/threadview_p.h 2013-09-17 21:33:34 +0000 |
1264 | +++ src/threadview_p.h 2017-01-26 18:33:44 +0000 |
1265 | @@ -47,9 +47,12 @@ |
1266 | Threads filteredThreads(const Threads &threads); |
1267 | |
1268 | // private slots |
1269 | - void _d_threadsAdded(const History::Threads &threads); |
1270 | - void _d_threadsModified(const History::Threads &threads); |
1271 | - void _d_threadsRemoved(const History::Threads &threads); |
1272 | + void _d_threadsAdded(const QList<QVariantMap> &threads); |
1273 | + void _d_threadsModified(const QList<QVariantMap> &threads); |
1274 | + void _d_threadsRemoved(const QList<QVariantMap> &threads); |
1275 | + |
1276 | + protected: |
1277 | + Threads parseThreads(const QList<QVariantMap> &threads); |
1278 | |
1279 | ThreadView *q_ptr; |
1280 | }; |
PASSED: Continuous integration, rev:230 jenkins. qa.ubuntu. com/job/ history- service- ci/322/ jenkins. qa.ubuntu. com/job/ history- service- vivid-amd64- ci/160 jenkins. qa.ubuntu. com/job/ history- service- vivid-armhf- ci/161 jenkins. qa.ubuntu. com/job/ history- service- vivid-armhf- ci/161/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ history- service- vivid-i386- ci/160
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/history- service- ci/322/ rebuild
http://