File indexing completed on 2024-04-21 11:34:52
0001 /* 0002 SPDX-FileCopyrightText: 2019 Dan Leinir Turthra Jensen <admin@leinir.dk> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "commentsmodel.h" 0008 0009 #include "entryinternal.h" 0010 #include "knewstuffcore_debug.h" 0011 0012 #include <KLocalizedString> 0013 0014 #include <QTimer> 0015 0016 namespace KNSCore 0017 { 0018 class CommentsModelPrivate 0019 { 0020 public: 0021 CommentsModelPrivate(CommentsModel *qq) 0022 : q(qq) 0023 { 0024 } 0025 CommentsModel *const q; 0026 Engine *engine = nullptr; 0027 0028 EntryInternal entry; 0029 0030 QList<std::shared_ptr<KNSCore::Comment>> comments; 0031 0032 enum FetchOptions { 0033 NoOption, 0034 ClearModel, 0035 }; 0036 bool fetchThrottle = false; 0037 void fetch(FetchOptions option = NoOption) 0038 { 0039 if (fetchThrottle) { 0040 return; 0041 } 0042 fetchThrottle = true; 0043 QTimer::singleShot(1, q, [this]() { 0044 fetchThrottle = false; 0045 }); 0046 // Sanity checks, because we need a few things to be correct before we can actually fetch comments... 0047 if (!engine) { 0048 qCWarning(KNEWSTUFFCORE) << "CommentsModel must be parented on a KNSCore::Engine instance to be able to fetch comments"; 0049 } 0050 if (!entry.isValid()) { 0051 qCWarning(KNEWSTUFFCORE) << "Without an entry to fetch comments for, CommentsModel cannot fetch comments for it"; 0052 } 0053 0054 if (engine && entry.isValid()) { 0055 QSharedPointer<Provider> provider = engine->provider(entry.providerId()); 0056 if (option == ClearModel) { 0057 q->beginResetModel(); 0058 comments.clear(); 0059 provider->disconnect(q); 0060 q->connect(provider.data(), &Provider::commentsLoaded, q, [=](const QList<std::shared_ptr<KNSCore::Comment>> &newComments) { 0061 QList<std::shared_ptr<KNSCore::Comment>> actualNewComments; 0062 for (const std::shared_ptr<KNSCore::Comment> &comment : newComments) { 0063 bool commentIsKnown = false; 0064 for (const std::shared_ptr<KNSCore::Comment> &existingComment : std::as_const(comments)) { 0065 if (existingComment->id == comment->id) { 0066 commentIsKnown = true; 0067 break; 0068 } 0069 } 0070 if (commentIsKnown) { 0071 continue; 0072 } 0073 actualNewComments << comment; 0074 } 0075 if (actualNewComments.count() > 0) { 0076 q->beginInsertRows(QModelIndex(), comments.count(), comments.count() + actualNewComments.count() - 1); 0077 qCDebug(KNEWSTUFFCORE) << "Appending" << actualNewComments.count() << "new comments"; 0078 comments.append(actualNewComments); 0079 q->endInsertRows(); 0080 } 0081 }); 0082 q->endResetModel(); 0083 } 0084 int commentsPerPage = 100; 0085 int pageToLoad = comments.count() / commentsPerPage; 0086 qCDebug(KNEWSTUFFCORE) << "Loading comments, page" << pageToLoad << "with current comment count" << comments.count() << "out of a total of" 0087 << entry.numberOfComments(); 0088 Q_EMIT provider->loadComments(entry, commentsPerPage, pageToLoad); 0089 } 0090 } 0091 }; 0092 } 0093 0094 KNSCore::CommentsModel::CommentsModel(Engine *parent) 0095 : QAbstractListModel(parent) 0096 , d(new CommentsModelPrivate(this)) 0097 { 0098 d->engine = parent; 0099 } 0100 0101 KNSCore::CommentsModel::~CommentsModel() = default; 0102 0103 QHash<int, QByteArray> KNSCore::CommentsModel::roleNames() const 0104 { 0105 // clang-format off 0106 static const QHash<int, QByteArray> roles{ 0107 {IdRole, "id"}, 0108 {SubjectRole, "subject"}, 0109 {TextRole, "text"}, 0110 {ChildCountRole, "childCound"}, 0111 {UsernameRole, "username"}, 0112 {DateRole, "date"}, 0113 {ScoreRole, "score"}, 0114 {ParentIndexRole, "parentIndex"}, 0115 {DepthRole, "depth"} 0116 }; 0117 // clang-format on 0118 return roles; 0119 } 0120 0121 QVariant KNSCore::CommentsModel::data(const QModelIndex &index, int role) const 0122 { 0123 QVariant value; 0124 if (checkIndex(index)) { 0125 std::shared_ptr<KNSCore::Comment> comment = d->comments[index.row()]; 0126 switch (role) { 0127 case IdRole: 0128 value.setValue(comment->id); 0129 break; 0130 case SubjectRole: 0131 value.setValue(comment->subject); 0132 break; 0133 case TextRole: 0134 value.setValue(comment->text); 0135 break; 0136 case ChildCountRole: 0137 value.setValue(comment->childCount); 0138 break; 0139 case UsernameRole: 0140 value.setValue(comment->username); 0141 break; 0142 case DateRole: 0143 value.setValue(comment->date); 0144 break; 0145 case ScoreRole: 0146 value.setValue(comment->score); 0147 break; 0148 case ParentIndexRole: { 0149 int idx{-1}; 0150 if (comment->parent) { 0151 idx = d->comments.indexOf(comment->parent); 0152 } 0153 value.setValue(idx); 0154 } break; 0155 case DepthRole: { 0156 int depth{0}; 0157 if (comment->parent) { 0158 std::shared_ptr<KNSCore::Comment> child = comment->parent; 0159 while (child) { 0160 ++depth; 0161 child = child->parent; 0162 } 0163 } 0164 value.setValue(depth); 0165 break; 0166 } 0167 default: 0168 value.setValue(i18nc("The value returned for an unknown role when requesting data from the model.", "Unknown CommentsModel role")); 0169 break; 0170 } 0171 } 0172 return value; 0173 } 0174 0175 int KNSCore::CommentsModel::rowCount(const QModelIndex &parent) const 0176 { 0177 if (parent.isValid()) { 0178 return 0; 0179 } 0180 return d->comments.count(); 0181 } 0182 0183 bool KNSCore::CommentsModel::canFetchMore(const QModelIndex &parent) const 0184 { 0185 if (parent.isValid()) { 0186 return false; 0187 } 0188 if (d->entry.numberOfComments() > d->comments.count()) { 0189 return true; 0190 } 0191 return false; 0192 } 0193 0194 void KNSCore::CommentsModel::fetchMore(const QModelIndex &parent) 0195 { 0196 if (parent.isValid()) { 0197 return; 0198 } 0199 d->fetch(); 0200 } 0201 0202 const KNSCore::EntryInternal &KNSCore::CommentsModel::entry() const 0203 { 0204 return d->entry; 0205 } 0206 0207 void KNSCore::CommentsModel::setEntry(const KNSCore::EntryInternal &newEntry) 0208 { 0209 d->entry = newEntry; 0210 d->fetch(CommentsModelPrivate::ClearModel); 0211 Q_EMIT entryChanged(); 0212 } 0213 0214 #include "moc_commentsmodel.cpp"