#include "TimelineModel.h" #include "Logging.h" #include "Utils.h" namespace { template QString eventId(const T &event) { return QString::fromStdString(event.event_id); } template QString roomId(const T &event) { return QString::fromStdString(event.room_id); } template QString senderId(const T &event) { return QString::fromStdString(event.sender); } } TimelineModel::TimelineModel(QString room_id, QObject *parent) : QAbstractListModel(parent) , room_id_(room_id) { connect( this, &TimelineModel::oldMessagesRetrieved, this, &TimelineModel::addBackwardsEvents); } QHash TimelineModel::roleNames() const { return { {Type, "type"}, {Body, "body"}, {FormattedBody, "formattedBody"}, {UserId, "userId"}, {UserName, "userName"}, {Timestamp, "timestamp"}, }; } int TimelineModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); nhlog::ui()->info("current order size: {}", eventOrder.size()); return (int)this->eventOrder.size(); } QVariant TimelineModel::data(const QModelIndex &index, int role) const { nhlog::ui()->info("data"); if (index.row() < 0 && index.row() >= (int)eventOrder.size()) return QVariant(); QString id = eventOrder[index.row()]; switch (role) { case UserId: return QVariant(boost::apply_visitor( [](const auto &e) -> QString { return senderId(e); }, events.value(id))); default: return QVariant(); } } void TimelineModel::addEvents(const mtx::responses::Timeline &events) { if (isInitialSync) { prev_batch_token_ = QString::fromStdString(events.prev_batch); isInitialSync = false; } nhlog::ui()->info("add {} events", events.events.size()); std::vector ids; for (const auto &e : events.events) { QString id = boost::apply_visitor([](const auto &e) -> QString { return eventId(e); }, e); this->events.insert(id, e); ids.push_back(id); nhlog::ui()->info("add event {}", id.toStdString()); } beginInsertRows(QModelIndex(), static_cast(this->events.size()), static_cast(this->events.size() + ids.size() - 1)); this->eventOrder.insert(this->eventOrder.end(), ids.begin(), ids.end()); endInsertRows(); } void TimelineModel::fetchHistory() { if (paginationInProgress) { nhlog::ui()->warn("Already loading older messages"); return; } paginationInProgress = true; mtx::http::MessagesOpts opts; opts.room_id = room_id_.toStdString(); opts.from = prev_batch_token_.toStdString(); nhlog::ui()->info("Paginationg room {}", opts.room_id); http::client()->messages( opts, [this, opts](const mtx::responses::Messages &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->error("failed to call /messages ({}): {} - {}", opts.room_id, mtx::errors::to_string(err->matrix_error.errcode), err->matrix_error.error); return; } emit oldMessagesRetrieved(std::move(res)); }); } void TimelineModel::addBackwardsEvents(const mtx::responses::Messages &msgs) { nhlog::ui()->info("add {} backwards events", msgs.chunk.size()); std::vector ids; for (const auto &e : msgs.chunk) { QString id = boost::apply_visitor([](const auto &e) -> QString { return eventId(e); }, e); this->events.insert(id, e); ids.push_back(id); nhlog::ui()->info("add event {}", id.toStdString()); } beginInsertRows(QModelIndex(), 0, static_cast(ids.size() - 1)); this->eventOrder.insert(this->eventOrder.begin(), ids.rbegin(), ids.rend()); endInsertRows(); prev_batch_token_ = QString::fromStdString(msgs.end); paginationInProgress = false; } QColor TimelineModel::userColor(QString id, QColor background) { if (!userColors.contains(id)) userColors.insert( id, QColor(utils::generateContrastingHexColor(id, background.name()))); return userColors.value(id); }