File indexing completed on 2024-05-05 05:01:25

0001 // SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "searchmodel.h"
0005 
0006 #include "eventhandler.h"
0007 #include "messageeventmodel.h"
0008 #include "neochatroom.h"
0009 
0010 #include <QGuiApplication>
0011 
0012 #include <Quotient/connection.h>
0013 #include <Quotient/events/stickerevent.h>
0014 
0015 #include <KLocalizedString>
0016 
0017 using namespace Quotient;
0018 
0019 // TODO search only in the current room
0020 
0021 SearchModel::SearchModel(QObject *parent)
0022     : QAbstractListModel(parent)
0023 {
0024 }
0025 
0026 QString SearchModel::searchText() const
0027 {
0028     return m_searchText;
0029 }
0030 
0031 void SearchModel::setSearchText(const QString &searchText)
0032 {
0033     m_searchText = searchText;
0034     Q_EMIT searchTextChanged();
0035 }
0036 
0037 void SearchModel::search()
0038 {
0039     Q_ASSERT(m_room);
0040     setSearching(true);
0041     if (m_job) {
0042         m_job->abandon();
0043         m_job = nullptr;
0044     }
0045 
0046     RoomEventFilter filter;
0047     filter.unreadThreadNotifications = none;
0048     filter.lazyLoadMembers = true;
0049     filter.includeRedundantMembers = false;
0050     filter.notRooms = QStringList();
0051     filter.rooms = QStringList{m_room->id()};
0052     filter.containsUrl = false;
0053 
0054     SearchJob::RoomEventsCriteria criteria{
0055         .searchTerm = m_searchText,
0056         .keys = {},
0057         .filter = filter,
0058         .orderBy = "recent"_ls,
0059         .eventContext = SearchJob::IncludeEventContext{3, 3, true},
0060         .includeState = false,
0061         .groupings = none,
0062 
0063     };
0064 
0065     auto job = m_room->connection()->callApi<SearchJob>(SearchJob::Categories{criteria});
0066     m_job = job;
0067     connect(job, &BaseJob::finished, this, [this, job] {
0068         beginResetModel();
0069         m_result = job->searchCategories().roomEvents;
0070         endResetModel();
0071         setSearching(false);
0072         m_job = nullptr;
0073         // TODO error handling
0074     });
0075 }
0076 
0077 QVariant SearchModel::data(const QModelIndex &index, int role) const
0078 {
0079     auto row = index.row();
0080     const auto &event = *m_result->results[row].result;
0081 
0082     EventHandler eventHandler(m_room, &event);
0083 
0084     switch (role) {
0085     case DisplayRole:
0086         return eventHandler.getRichBody();
0087     case ShowAuthorRole:
0088         return true;
0089     case AuthorRole:
0090         return eventHandler.getAuthor();
0091     case ShowSectionRole:
0092         if (row == 0) {
0093             return true;
0094         }
0095         return event.originTimestamp().date() != m_result->results[row - 1].result->originTimestamp().date();
0096     case SectionRole:
0097         return eventHandler.getTimeString(true);
0098     case TimeRole:
0099         return eventHandler.getTime();
0100     case TimeStringRole:
0101         return eventHandler.getTimeString(false);
0102     case ShowReactionsRole:
0103         return false;
0104     case ShowReadMarkersRole:
0105         return false;
0106     case IsReplyRole:
0107         return eventHandler.hasReply();
0108     case ReplyIdRole:
0109         return eventHandler.hasReply();
0110     case ReplyAuthorRole:
0111         return eventHandler.getReplyAuthor();
0112     case ReplyDelegateTypeRole:
0113         return eventHandler.getReplyDelegateType();
0114     case ReplyDisplayRole:
0115         return eventHandler.getReplyRichBody();
0116     case ReplyMediaInfoRole:
0117         return eventHandler.getReplyMediaInfo();
0118     case IsPendingRole:
0119         return false;
0120     case ShowLinkPreviewRole:
0121         return false;
0122     case HighlightRole:
0123         return eventHandler.isHighlighted();
0124     case EventIdRole:
0125         return eventHandler.getId();
0126     case IsThreadedRole:
0127         return eventHandler.isThreaded();
0128     case ThreadRootRole:
0129         return eventHandler.threadRoot();
0130     }
0131     return DelegateType::Message;
0132 }
0133 
0134 int SearchModel::rowCount(const QModelIndex &parent) const
0135 {
0136     Q_UNUSED(parent);
0137     if (m_result.has_value()) {
0138         return m_result->results.size();
0139     }
0140     return 0;
0141 }
0142 
0143 QHash<int, QByteArray> SearchModel::roleNames() const
0144 {
0145     return {
0146         {DelegateTypeRole, "delegateType"},
0147         {DisplayRole, "display"},
0148         {AuthorRole, "author"},
0149         {ShowSectionRole, "showSection"},
0150         {SectionRole, "section"},
0151         {TimeRole, "time"},
0152         {TimeStringRole, "timeString"},
0153         {ShowAuthorRole, "showAuthor"},
0154         {EventIdRole, "eventId"},
0155         {ExcessReadMarkersRole, "excessReadMarkers"},
0156         {HighlightRole, "isHighlighted"},
0157         {ReadMarkersString, "readMarkersString"},
0158         {PlainTextRole, "plainText"},
0159         {VerifiedRole, "verified"},
0160         {ProgressInfoRole, "progressInfo"},
0161         {ShowReactionsRole, "showReactions"},
0162         {IsReplyRole, "isReply"},
0163         {ReplyAuthorRole, "replyAuthor"},
0164         {ReplyIdRole, "replyId"},
0165         {ReplyDelegateTypeRole, "replyDelegateType"},
0166         {ReplyDisplayRole, "replyDisplay"},
0167         {ReplyMediaInfoRole, "replyMediaInfo"},
0168         {ReactionRole, "reaction"},
0169         {ReadMarkersRole, "readMarkers"},
0170         {IsPendingRole, "isPending"},
0171         {ShowReadMarkersRole, "showReadMarkers"},
0172         {MimeTypeRole, "mimeType"},
0173         {ShowLinkPreviewRole, "showLinkPreview"},
0174         {LinkPreviewRole, "linkPreview"},
0175         {IsThreadedRole, "isThreaded"},
0176         {ThreadRootRole, "threadRoot"},
0177     };
0178 }
0179 
0180 NeoChatRoom *SearchModel::room() const
0181 {
0182     return m_room;
0183 }
0184 
0185 void SearchModel::setRoom(NeoChatRoom *room)
0186 {
0187     if (m_room) {
0188         disconnect(m_room, nullptr, this, nullptr);
0189     }
0190     m_room = room;
0191     Q_EMIT roomChanged();
0192 
0193     connect(m_room, &NeoChatRoom::replyLoaded, this, [this](const auto &eventId, const auto &replyId) {
0194         Q_UNUSED(replyId);
0195         const auto &results = m_result->results;
0196         auto it = std::find_if(results.begin(), results.end(), [eventId](const auto &event) {
0197             return event.result->id() == eventId;
0198         });
0199         if (it == results.end()) {
0200             return;
0201         }
0202         auto row = it - results.begin();
0203         Q_EMIT dataChanged(index(row, 0), index(row, 0), {ReplyDelegateTypeRole, ReplyDisplayRole, ReplyMediaInfoRole, ReplyAuthorRole});
0204     });
0205 }
0206 
0207 bool SearchModel::searching() const
0208 {
0209     return m_searching;
0210 }
0211 
0212 bool SearchModel::event(QEvent *event)
0213 {
0214     if (event->type() == QEvent::ApplicationPaletteChange) {
0215         Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {AuthorRole, ReadMarkersRole});
0216     }
0217     return QObject::event(event);
0218 }
0219 
0220 void SearchModel::setSearching(bool searching)
0221 {
0222     m_searching = searching;
0223     Q_EMIT searchingChanged();
0224 }
0225 
0226 #include "moc_searchmodel.cpp"