Changeset View
Standalone View
src/core/commentsmodel.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (C) 2019 Dan Leinir Turthra Jensen <admin@leinir.dk> | ||||
3 | * | ||||
4 | * This library is free software; you can redistribute it and/or | ||||
5 | * modify it under the terms of the GNU Lesser General Public | ||||
6 | * License as published by the Free Software Foundation; either | ||||
7 | * version 2.1 of the License, or (at your option) version 3, or any | ||||
8 | * later version accepted by the membership of KDE e.V. (or its | ||||
9 | * successor approved by the membership of KDE e.V.), which shall | ||||
10 | * act as a proxy defined in Section 6 of version 3 of the license. | ||||
11 | * | ||||
12 | * This library is distributed in the hope that it will be useful, | ||||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
15 | * Lesser General Public License for more details. | ||||
16 | * | ||||
17 | * You should have received a copy of the GNU Lesser General Public | ||||
18 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | ||||
19 | * | ||||
20 | */ | ||||
21 | | ||||
22 | #include "commentsmodel.h" | ||||
23 | | ||||
24 | #include "entryinternal.h" | ||||
25 | #include "engine.h" | ||||
26 | #include "knewstuffcore_debug.h" | ||||
27 | | ||||
28 | #include <KLocalizedString> | ||||
29 | | ||||
30 | #include <QDateTime> | ||||
31 | #include <QTimer> | ||||
32 | | ||||
33 | namespace KNSCore { | ||||
34 | class CommentsModel::Private { | ||||
35 | public: | ||||
36 | Private(CommentsModel *qq) | ||||
37 | : q(qq) | ||||
38 | {} | ||||
39 | CommentsModel* q = nullptr; | ||||
40 | Engine* engine = nullptr; | ||||
41 | | ||||
42 | EntryInternal entry; | ||||
43 | | ||||
44 | QList<std::shared_ptr<KNSCore::Comment>> comments; | ||||
45 | | ||||
46 | enum FetchOptions { | ||||
47 | NoOption, | ||||
48 | ClearModel | ||||
49 | }; | ||||
50 | bool fetchThrottle = false; | ||||
51 | void fetch(FetchOptions option = NoOption) { | ||||
52 | if (fetchThrottle) { | ||||
53 | return; | ||||
54 | } | ||||
55 | fetchThrottle = true; | ||||
56 | QTimer::singleShot(1, q, [this](){ | ||||
57 | fetchThrottle = false; | ||||
58 | }); | ||||
59 | // Sanity checks, because we need a few things to be correct before we can actually fetch comments... | ||||
60 | if (!engine) { | ||||
61 | qCWarning(KNEWSTUFFCORE) << "CommentsModel must be parented on a KNSCore::Engine instance to be able to fetch comments"; | ||||
62 | } | ||||
63 | if (!entry.isValid()) { | ||||
64 | qCWarning(KNEWSTUFFCORE) << "Without an entry to fetch comments for, CommentsModel cannot fetch comments for it"; | ||||
65 | } | ||||
66 | | ||||
67 | if (engine && entry.isValid()) { | ||||
68 | QSharedPointer<Provider> provider = engine->provider(entry.providerId()); | ||||
69 | if (option == ClearModel) { | ||||
70 | emit q->beginResetModel(); | ||||
71 | comments.clear(); | ||||
72 | provider->disconnect(q); | ||||
73 | q->connect(provider.data(), &Provider::commentsLoaded, q, [=](const QList<std::shared_ptr<KNSCore::Comment>> &newComments){ | ||||
74 | QList<std::shared_ptr<KNSCore::Comment>> actualNewComments; | ||||
75 | for (std::shared_ptr<KNSCore::Comment> comment : newComments) { | ||||
76 | bool commentIsKnown = false; | ||||
77 | for (std::shared_ptr<KNSCore::Comment> existingComment : comments) { | ||||
78 | if (existingComment->id == comment->id) { | ||||
79 | commentIsKnown = true; | ||||
80 | break; | ||||
81 | } | ||||
82 | } | ||||
83 | if (commentIsKnown) { | ||||
84 | continue; | ||||
85 | } | ||||
86 | actualNewComments << comment; | ||||
87 | } | ||||
88 | if (actualNewComments.count() > 0) { | ||||
89 | q->beginInsertRows(QModelIndex(), comments.count(), comments.count() + actualNewComments.count() - 1); | ||||
90 | qDebug() << "Appending" << actualNewComments.count() << "new comments"; | ||||
91 | comments.append(actualNewComments); | ||||
92 | q->endInsertRows(); | ||||
93 | } | ||||
94 | }); | ||||
95 | emit q->endResetModel(); | ||||
96 | } | ||||
97 | int commentsPerPage = 100; | ||||
98 | int pageToLoad = comments.count() / commentsPerPage; | ||||
99 | qDebug() << "Loading comments, page" << pageToLoad << "with current comment count" << comments.count() << "out of a total of" << entry.numberOfComments(); | ||||
100 | provider->loadComments(entry, commentsPerPage, pageToLoad); | ||||
ahiemstra: Categorise or drop :) | |||||
101 | } | ||||
102 | } | ||||
103 | }; | ||||
104 | } | ||||
105 | | ||||
106 | KNSCore::CommentsModel::CommentsModel(Engine* parent) | ||||
107 | : QAbstractListModel(parent) | ||||
108 | , d(new Private(this)) | ||||
109 | { | ||||
110 | d->engine = parent; | ||||
111 | } | ||||
112 | | ||||
113 | KNSCore::CommentsModel::~CommentsModel() | ||||
114 | { | ||||
115 | delete d; | ||||
116 | } | ||||
117 | | ||||
118 | QHash<int, QByteArray> KNSCore::CommentsModel::roleNames() const | ||||
119 | { | ||||
120 | QHash<int, QByteArray> roles; | ||||
121 | roles[IdRole] = "id"; | ||||
122 | roles[SubjectRole] = "subject"; | ||||
123 | roles[TextRole] = "text"; | ||||
124 | roles[ChildCountRole] = "childCound"; | ||||
125 | roles[UsernameRole] = "username"; | ||||
126 | roles[DateRole] = "date"; | ||||
127 | roles[ScoreRole] = "score"; | ||||
128 | roles[ParentIndexRole] = "parentIndex"; | ||||
129 | roles[DepthRole] = "depth"; | ||||
130 | return roles; | ||||
131 | } | ||||
132 | | ||||
133 | QVariant KNSCore::CommentsModel::data(const QModelIndex& index, int role) const | ||||
134 | { | ||||
135 | QVariant value; | ||||
136 | if (checkIndex(index)) { | ||||
Might want to add a call to checkIndex here (https://doc.qt.io/qt-5/qabstractitemmodel.html#checkIndex), it would obsolete some additional checking you do later. ahiemstra: Might want to add a call to checkIndex here (https://doc.qt.io/qt-5/qabstractitemmodel. | |||||
After confirming that 5.11 (which introduces that function) is indeed acceptable for Frameworks, i'm aaaall over that ;) Very handy little function, that :) leinir: After confirming that 5.11 (which introduces that function) is indeed acceptable for Frameworks… | |||||
137 | std::shared_ptr<KNSCore::Comment> comment = d->comments[index.row()]; | ||||
138 | switch (role) | ||||
139 | { | ||||
140 | case IdRole: | ||||
141 | value.setValue(comment->id); | ||||
142 | break; | ||||
143 | case SubjectRole: | ||||
144 | value.setValue(comment->subject); | ||||
145 | break; | ||||
146 | case TextRole: | ||||
147 | value.setValue(comment->text); | ||||
148 | break; | ||||
149 | case ChildCountRole: | ||||
150 | value.setValue(comment->childCount); | ||||
151 | break; | ||||
152 | case UsernameRole: | ||||
153 | value.setValue(comment->username); | ||||
154 | break; | ||||
155 | case DateRole: | ||||
156 | value.setValue(comment->date); | ||||
157 | break; | ||||
158 | case ScoreRole: | ||||
159 | value.setValue(comment->score); | ||||
160 | break; | ||||
161 | case ParentIndexRole: | ||||
162 | { | ||||
163 | int idx{-1}; | ||||
164 | if (comment->parent) { | ||||
165 | d->comments.indexOf(std::shared_ptr<KNSCore::Comment>(comment->parent)); | ||||
166 | } | ||||
167 | value.setValue(idx); | ||||
168 | } | ||||
169 | break; | ||||
170 | case DepthRole: | ||||
171 | { | ||||
172 | int depth{0}; | ||||
173 | if (comment->parent) { | ||||
174 | std::shared_ptr<KNSCore::Comment> child = comment->parent; | ||||
175 | while (child) { | ||||
176 | ++depth; | ||||
177 | child = child->parent; | ||||
178 | } | ||||
Since you're already checking for child being true (aka not-null), this if becomes rather superfluous. ahiemstra: Since you're already checking for `child` being true (aka not-null), this if becomes rather… | |||||
leinir: Hmm... leftovers from earlier versions of that function, there :) | |||||
179 | } | ||||
180 | value.setValue(depth); | ||||
181 | } | ||||
182 | break; | ||||
183 | default: | ||||
184 | value.setValue(i18nc("The value returned for an unknown role when requesting data from the model.", "")); | ||||
185 | break; | ||||
broulik: Any particular reason not do just return a null `QVariant()`? | |||||
Hmm... This actually is supposed to be "Unknown model role"... It's kind of corner-casey, but i've noticed in the past that something other than just an invalid QVariant is occasionally useful for those times where you've, say, missed the h in "depth" ;) Usually this wouldn't be hit, of course, so it's not actually expensive in any real way. But yes, compared to this, QVariant() would be the better option. leinir: Hmm... This actually is supposed to be "Unknown model role"... It's kind of corner-casey, but… | |||||
186 | } | ||||
187 | } | ||||
188 | return value; | ||||
189 | } | ||||
190 | | ||||
191 | QVariant KNSCore::CommentsModel::data(int index, int role) const | ||||
192 | { | ||||
193 | return data(CommentsModel::index(index), role); | ||||
194 | } | ||||
195 | | ||||
196 | int KNSCore::CommentsModel::rowCount(const QModelIndex& parent) const | ||||
197 | { | ||||
198 | if (parent.isValid()) | ||||
199 | return 0; | ||||
200 | return d->comments.count(); | ||||
201 | } | ||||
202 | | ||||
203 | bool KNSCore::CommentsModel::canFetchMore(const QModelIndex& parent) const | ||||
204 | { | ||||
205 | if (parent.isValid()) | ||||
206 | return false; | ||||
207 | if (d->entry.numberOfComments() > d->comments.count()) | ||||
208 | return true; | ||||
209 | return false; | ||||
210 | } | ||||
211 | | ||||
212 | void KNSCore::CommentsModel::fetchMore(const QModelIndex& parent) | ||||
213 | { | ||||
214 | if(parent.isValid()) | ||||
215 | return; | ||||
216 | d->fetch(); | ||||
217 | } | ||||
218 | | ||||
219 | const KNSCore::EntryInternal & KNSCore::CommentsModel::entry() const | ||||
220 | { | ||||
221 | return d->entry; | ||||
222 | } | ||||
223 | | ||||
224 | void KNSCore::CommentsModel::setEntry(const KNSCore::EntryInternal& newEntry) | ||||
225 | { | ||||
226 | d->entry = newEntry; | ||||
227 | d->fetch(Private::ClearModel); | ||||
228 | emit entryChanged(); | ||||
229 | } |
Categorise or drop :)