File indexing completed on 2024-12-08 07:33:46

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_connection);
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_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 Connection *SearchModel::connection() const
0078 {
0079     return m_connection;
0080 }
0081 
0082 void SearchModel::setConnection(Connection *connection)
0083 {
0084     m_connection = connection;
0085     Q_EMIT connectionChanged();
0086 }
0087 
0088 QVariant SearchModel::data(const QModelIndex &index, int role) const
0089 {
0090     auto row = index.row();
0091     const auto &event = *m_result->results[row].result;
0092 
0093     EventHandler eventHandler;
0094     eventHandler.setRoom(m_room);
0095     eventHandler.setEvent(&event);
0096 
0097     switch (role) {
0098     case DisplayRole:
0099         return eventHandler.getRichBody();
0100     case ShowAuthorRole:
0101         return true;
0102     case AuthorRole:
0103         return eventHandler.getAuthor();
0104     case ShowSectionRole:
0105         if (row == 0) {
0106             return true;
0107         }
0108         return event.originTimestamp().date() != m_result->results[row - 1].result->originTimestamp().date();
0109     case SectionRole:
0110         return eventHandler.getTimeString(true);
0111     case TimeRole:
0112         return eventHandler.getTime();
0113     case TimeStringRole:
0114         return eventHandler.getTimeString(false);
0115     case ShowReactionsRole:
0116         return false;
0117     case ShowReadMarkersRole:
0118         return false;
0119     case IsReplyRole:
0120         return eventHandler.hasReply();
0121     case ReplyIdRole:
0122         return eventHandler.hasReply();
0123     case ReplyAuthorRole:
0124         return eventHandler.getReplyAuthor();
0125     case ReplyDelegateTypeRole:
0126         return eventHandler.getReplyDelegateType();
0127     case ReplyDisplayRole:
0128         return eventHandler.getReplyRichBody();
0129     case ReplyMediaInfoRole:
0130         return eventHandler.getReplyMediaInfo();
0131     case IsPendingRole:
0132         return false;
0133     case ShowLinkPreviewRole:
0134         return false;
0135     case HighlightRole:
0136         return eventHandler.isHighlighted();
0137     case EventIdRole:
0138         return eventHandler.getId();
0139     case IsThreadedRole:
0140         return eventHandler.isThreaded();
0141     case ThreadRootRole:
0142         return eventHandler.threadRoot();
0143     }
0144     return DelegateType::Message;
0145 }
0146 
0147 int SearchModel::rowCount(const QModelIndex &parent) const
0148 {
0149     Q_UNUSED(parent);
0150     if (m_result.has_value()) {
0151         return m_result->results.size();
0152     }
0153     return 0;
0154 }
0155 
0156 QHash<int, QByteArray> SearchModel::roleNames() const
0157 {
0158     return {
0159         {DelegateTypeRole, "delegateType"},
0160         {DisplayRole, "display"},
0161         {AuthorRole, "author"},
0162         {ShowSectionRole, "showSection"},
0163         {SectionRole, "section"},
0164         {TimeRole, "time"},
0165         {TimeStringRole, "timeString"},
0166         {ShowAuthorRole, "showAuthor"},
0167         {EventIdRole, "eventId"},
0168         {ExcessReadMarkersRole, "excessReadMarkers"},
0169         {HighlightRole, "isHighlighted"},
0170         {ReadMarkersString, "readMarkersString"},
0171         {PlainTextRole, "plainText"},
0172         {VerifiedRole, "verified"},
0173         {ProgressInfoRole, "progressInfo"},
0174         {ShowReactionsRole, "showReactions"},
0175         {IsReplyRole, "isReply"},
0176         {ReplyAuthorRole, "replyAuthor"},
0177         {ReplyIdRole, "replyId"},
0178         {ReplyDelegateTypeRole, "replyDelegateType"},
0179         {ReplyDisplayRole, "replyDisplay"},
0180         {ReplyMediaInfoRole, "replyMediaInfo"},
0181         {ReactionRole, "reaction"},
0182         {ReadMarkersRole, "readMarkers"},
0183         {IsPendingRole, "isPending"},
0184         {ShowReadMarkersRole, "showReadMarkers"},
0185         {MimeTypeRole, "mimeType"},
0186         {ShowLinkPreviewRole, "showLinkPreview"},
0187         {LinkPreviewRole, "linkPreview"},
0188         {IsThreadedRole, "isThreaded"},
0189         {ThreadRootRole, "threadRoot"},
0190     };
0191 }
0192 
0193 NeoChatRoom *SearchModel::room() const
0194 {
0195     return m_room;
0196 }
0197 
0198 void SearchModel::setRoom(NeoChatRoom *room)
0199 {
0200     if (m_room) {
0201         disconnect(m_room, nullptr, this, nullptr);
0202     }
0203     m_room = room;
0204     Q_EMIT roomChanged();
0205 
0206     connect(m_room, &NeoChatRoom::replyLoaded, this, [this](const auto &eventId, const auto &replyId) {
0207         Q_UNUSED(replyId);
0208         const auto &results = m_result->results;
0209         auto it = std::find_if(results.begin(), results.end(), [eventId](const auto &event) {
0210             return event.result->id() == eventId;
0211         });
0212         if (it == results.end()) {
0213             return;
0214         }
0215         auto row = it - results.begin();
0216         Q_EMIT dataChanged(index(row, 0), index(row, 0), {ReplyDelegateTypeRole, ReplyDisplayRole, ReplyMediaInfoRole, ReplyAuthorRole});
0217     });
0218 }
0219 
0220 bool SearchModel::searching() const
0221 {
0222     return m_searching;
0223 }
0224 
0225 bool SearchModel::event(QEvent *event)
0226 {
0227     if (event->type() == QEvent::ApplicationPaletteChange) {
0228         Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {AuthorRole, ReadMarkersRole});
0229     }
0230     return QObject::event(event);
0231 }
0232 
0233 void SearchModel::setSearching(bool searching)
0234 {
0235     m_searching = searching;
0236     Q_EMIT searchingChanged();
0237 }
0238 
0239 #include "moc_searchmodel.cpp"