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

0001 // SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
0002 // SPDX-License-Identifier: GPL-2.0-or-later
0003 
0004 #include "locationsmodel.h"
0005 
0006 #include <QGuiApplication>
0007 
0008 using namespace Quotient;
0009 
0010 LocationsModel::LocationsModel(QObject *parent)
0011     : QAbstractListModel(parent)
0012 {
0013     connect(this, &LocationsModel::roomChanged, this, [this]() {
0014         for (const auto &event : m_room->messageEvents()) {
0015             if (!is<RoomMessageEvent>(*event)) {
0016                 continue;
0017             }
0018             if (event->contentJson()["msgtype"_ls] == "m.location"_ls) {
0019                 const auto &e = *event;
0020                 addLocation(eventCast<const RoomMessageEvent>(&e));
0021             }
0022         }
0023         connect(m_room, &NeoChatRoom::aboutToAddHistoricalMessages, this, [this](const auto &events) {
0024             for (const auto &event : events) {
0025                 if (!is<RoomMessageEvent>(*event)) {
0026                     continue;
0027                 }
0028                 if (event->contentJson()["msgtype"_ls] == "m.location"_ls) {
0029                     const auto &e = *event;
0030                     addLocation(eventCast<const RoomMessageEvent>(&e));
0031                 }
0032             }
0033         });
0034         connect(m_room, &NeoChatRoom::aboutToAddNewMessages, this, [this](const auto &events) {
0035             for (const auto &event : events) {
0036                 if (!is<RoomMessageEvent>(*event)) {
0037                     continue;
0038                 }
0039                 if (event->contentJson()["msgtype"_ls] == "m.location"_ls) {
0040                     const auto &e = *event;
0041                     addLocation(eventCast<const RoomMessageEvent>(&e));
0042                 }
0043             }
0044         });
0045     });
0046 
0047     connect(this, &LocationsModel::rowsInserted, this, &LocationsModel::boundingBoxChanged);
0048 }
0049 
0050 void LocationsModel::addLocation(const RoomMessageEvent *event)
0051 {
0052     const auto uri = event->contentJson()["org.matrix.msc3488.location"_ls]["uri"_ls].toString();
0053     const auto parts = uri.mid(4).split(QLatin1Char(','));
0054     if (parts.size() < 2) {
0055         qWarning() << "invalid geo: URI" << uri;
0056         return;
0057     }
0058     const auto latitude = parts[0].toFloat();
0059     const auto longitude = parts[1].toFloat();
0060     beginInsertRows(QModelIndex(), m_locations.size(), m_locations.size() + 1);
0061     m_locations += LocationData{
0062         .eventId = event->id(),
0063         .latitude = latitude,
0064         .longitude = longitude,
0065         .content = event->contentJson(),
0066         .author = m_room->user(event->senderId()),
0067     };
0068     endInsertRows();
0069 }
0070 
0071 NeoChatRoom *LocationsModel::room() const
0072 {
0073     return m_room;
0074 }
0075 
0076 void LocationsModel::setRoom(NeoChatRoom *room)
0077 {
0078     if (m_room) {
0079         disconnect(this, nullptr, m_room, nullptr);
0080     }
0081     m_room = room;
0082     Q_EMIT roomChanged();
0083 }
0084 
0085 QHash<int, QByteArray> LocationsModel::roleNames() const
0086 {
0087     return {
0088         {LongitudeRole, "longitude"},
0089         {LatitudeRole, "latitude"},
0090         {TextRole, "text"},
0091         {AssetRole, "asset"},
0092         {AuthorRole, "author"},
0093     };
0094 }
0095 
0096 QVariant LocationsModel::data(const QModelIndex &index, int roleName) const
0097 {
0098     auto row = index.row();
0099     if (roleName == LongitudeRole) {
0100         return m_locations[row].longitude;
0101     } else if (roleName == LatitudeRole) {
0102         return m_locations[row].latitude;
0103     } else if (roleName == TextRole) {
0104         return m_locations[row].content["body"_ls].toString();
0105     } else if (roleName == AssetRole) {
0106         return m_locations[row].content["org.matrix.msc3488.asset"_ls].toObject()["type"_ls].toString();
0107     } else if (roleName == AuthorRole) {
0108         return m_room->getUser(m_locations[row].author);
0109     }
0110     return {};
0111 }
0112 
0113 int LocationsModel::rowCount(const QModelIndex &parent) const
0114 {
0115     Q_UNUSED(parent);
0116     return m_locations.size();
0117 }
0118 
0119 QRectF LocationsModel::boundingBox() const
0120 {
0121     QRectF bbox(QPointF(180.0, 90.0), QPointF(-180.0, -90.0));
0122     for (auto i = 0; i < rowCount(); ++i) {
0123         const auto lat = data(index(i, 0), LatitudeRole).toDouble();
0124         const auto lon = data(index(i, 0), LongitudeRole).toDouble();
0125 
0126         bbox.setLeft(std::min(bbox.left(), lon));
0127         bbox.setRight(std::max(bbox.right(), lon));
0128         bbox.setTop(std::min(bbox.top(), lat));
0129         bbox.setBottom(std::max(bbox.bottom(), lat));
0130     }
0131     return bbox;
0132 }
0133 
0134 bool LocationsModel::event(QEvent *event)
0135 {
0136     if (event->type() == QEvent::ApplicationPaletteChange) {
0137         Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {AuthorRole});
0138     }
0139     return QObject::event(event);
0140 }
0141 
0142 #include "moc_locationsmodel.cpp"