diff options
Diffstat (limited to 'src/AliasEditModel.cpp')
-rw-r--r-- | src/AliasEditModel.cpp | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/src/AliasEditModel.cpp b/src/AliasEditModel.cpp new file mode 100644 index 00000000..1ca7f5e6 --- /dev/null +++ b/src/AliasEditModel.cpp @@ -0,0 +1,336 @@ +// SPDX-FileCopyrightText: 2022 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "AliasEditModel.h" + +#include <QSharedPointer> + +#include <set> + +#include <mtx/responses/common.hpp> + +#include "Cache.h" +#include "Cache_p.h" +#include "ChatPage.h" +#include "Logging.h" +#include "MatrixClient.h" +#include "timeline/Permissions.h" +#include "timeline/TimelineModel.h" + +AliasEditingModel::AliasEditingModel(const std::string &rid, QObject *parent) + : QAbstractListModel(parent) + , room_id(rid) + , aliasEvent(cache::client() + ->getStateEvent<mtx::events::state::CanonicalAlias>(room_id) + .value_or(mtx::events::StateEvent<mtx::events::state::CanonicalAlias>{}) + .content) + , canSendStateEvent( + Permissions(QString::fromStdString(rid)).canChange(qml_mtx_events::CanonicalAlias)) +{ + std::set<std::string> seen_aliases; + + if (!aliasEvent.alias.empty()) { + aliases.push_back(Entry{aliasEvent.alias, true, true, false}); + seen_aliases.insert(aliasEvent.alias); + } + + for (const auto &alias : aliasEvent.alt_aliases) { + if (!seen_aliases.count(alias)) { + aliases.push_back(Entry{aliasEvent.alias, false, true, false}); + seen_aliases.insert(aliasEvent.alias); + } + } + + for (const auto &alias : aliases) { + fetchAliasesStatus(alias.alias); + } + fetchPublishedAliases(); +} + +void +AliasEditingModel::fetchPublishedAliases() +{ + auto job = QSharedPointer<FetchPublishedAliasesJob>::create(); + connect(job.data(), + &FetchPublishedAliasesJob::advertizedAliasesFetched, + this, + &AliasEditingModel::updatePublishedAliases); + http::client()->list_room_aliases( + room_id, [job](const mtx::responses::Aliases &aliasesFetched, mtx::http::RequestErr) { + emit job->advertizedAliasesFetched(std::move(aliasesFetched.aliases)); + }); +} + +void +AliasEditingModel::fetchAliasesStatus(const std::string &alias) +{ + auto job = QSharedPointer<FetchPublishedAliasesJob>::create(); + connect( + job.data(), &FetchPublishedAliasesJob::aliasFetched, this, &AliasEditingModel::updateAlias); + http::client()->resolve_room_alias( + alias, [job, alias](const mtx::responses::RoomId &roomIdFetched, mtx::http::RequestErr e) { + if (!e) + emit job->aliasFetched(alias, std::move(roomIdFetched.room_id)); + }); +} + +QHash<int, QByteArray> +AliasEditingModel::roleNames() const +{ + return { + {Name, "name"}, + {IsPublished, "isPublished"}, + {IsCanonical, "isCanonical"}, + {IsAdvertized, "isAdvertized"}, + }; +} + +QVariant +AliasEditingModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= aliases.size()) + return {}; + + const auto &entry = aliases.at(index.row()); + + switch (role) { + case Name: + return QString::fromStdString(entry.alias); + case IsPublished: + return entry.published; + case IsCanonical: + return entry.canonical; + case IsAdvertized: + return entry.advertized; + } + + return {}; +} + +bool +AliasEditingModel::deleteAlias(int row) +{ + if (row < 0 || row >= aliases.size() || aliases.at(row).alias.empty()) + return false; + + auto alias = aliases.at(row); + + beginRemoveRows(QModelIndex(), row, row); + aliases.remove(row); + endRemoveRows(); + + if (alias.published) + http::client()->delete_room_alias(alias.alias, [alias](mtx::http::RequestErr e) { + if (e) { + nhlog::net()->error("Failed to delete {}: {}", alias.alias, *e); + ChatPage::instance()->showNotification( + tr("Failed to unpublish alias %1: %2") + .arg(QString::fromStdString(alias.alias), + QString::fromStdString(e->matrix_error.error))); + } + }); + + if (aliasEvent.alias == alias.alias) + aliasEvent.alias.clear(); + + for (size_t i = 0; i < aliasEvent.alt_aliases.size(); i++) { + if (aliasEvent.alt_aliases[i] == alias.alias) { + aliasEvent.alt_aliases.erase(aliasEvent.alt_aliases.begin() + i); + break; + } + } + + return true; +} + +void +AliasEditingModel::addAlias(QString newAlias) +{ + const auto aliasStr = newAlias.toStdString(); + for (const auto &e : aliases) { + if (e.alias == aliasStr) { + return; + } + } + + beginInsertRows(QModelIndex(), aliases.length(), aliases.length()); + if (aliasEvent.alias.empty()) + aliasEvent.alias = aliasStr; + else + aliasEvent.alt_aliases.push_back(aliasStr); + aliases.push_back( + Entry{aliasStr, aliasEvent.alias.empty() && canSendStateEvent, canSendStateEvent, false}); + endInsertRows(); + + auto job = QSharedPointer<FetchPublishedAliasesJob>::create(); + connect( + job.data(), &FetchPublishedAliasesJob::aliasFetched, this, &AliasEditingModel::updateAlias); + auto room = room_id; + http::client()->add_room_alias( + aliasStr, room_id, [job, aliasStr, room](mtx::http::RequestErr e) { + if (e) { + nhlog::net()->error("Failed to publish {}: {}", aliasStr, *e); + ChatPage::instance()->showNotification( + tr("Failed to unpublish alias %1: %2") + .arg(QString::fromStdString(aliasStr), + QString::fromStdString(e->matrix_error.error))); + emit job->aliasFetched(aliasStr, ""); + } else { + emit job->aliasFetched(aliasStr, room); + } + }); +} + +void +AliasEditingModel::makeCanonical(int row) +{ + if (!canSendStateEvent || row < 0 || row >= aliases.size() || aliases.at(row).alias.empty()) + return; + + auto moveAlias = aliases.at(row).alias; + + if (!aliasEvent.alias.empty()) { + for (qsizetype i = 0; i < aliases.size(); i++) { + if (moveAlias == aliases[i].alias) { + if (aliases[i].canonical) { + aliases[i].canonical = false; + aliasEvent.alt_aliases.push_back(aliasEvent.alias); + emit dataChanged(index(i), index(i), {IsCanonical}); + } + break; + } + } + } + + aliasEvent.alias = moveAlias; + for (auto i = aliasEvent.alt_aliases.begin(); i != aliasEvent.alt_aliases.end(); ++i) { + if (*i == moveAlias) { + aliasEvent.alt_aliases.erase(i); + break; + } + } + aliases[row].canonical = true; + aliases[row].advertized = true; + emit dataChanged(index(row), index(row), {IsCanonical, IsAdvertized}); +} + +void +AliasEditingModel::togglePublish(int row) +{ + if (row < 0 || row >= aliases.size() || aliases.at(row).alias.empty()) + return; + auto aliasStr = aliases[row].alias; + + auto job = QSharedPointer<FetchPublishedAliasesJob>::create(); + connect( + job.data(), &FetchPublishedAliasesJob::aliasFetched, this, &AliasEditingModel::updateAlias); + auto room = room_id; + if (!aliases[row].published) + http::client()->add_room_alias( + aliasStr, room_id, [job, aliasStr, room](mtx::http::RequestErr e) { + if (e) { + nhlog::net()->error("Failed to publish {}: {}", aliasStr, *e); + ChatPage::instance()->showNotification( + tr("Failed to unpublish alias %1: %2") + .arg(QString::fromStdString(aliasStr), + QString::fromStdString(e->matrix_error.error))); + emit job->aliasFetched(aliasStr, ""); + } else { + emit job->aliasFetched(aliasStr, room); + } + }); + else + http::client()->delete_room_alias(aliasStr, [job, aliasStr, room](mtx::http::RequestErr e) { + if (e) { + nhlog::net()->error("Failed to unpublish {}: {}", aliasStr, *e); + ChatPage::instance()->showNotification( + tr("Failed to unpublish alias %1: %2") + .arg(QString::fromStdString(aliasStr), + QString::fromStdString(e->matrix_error.error))); + emit job->aliasFetched(aliasStr, room); + } else { + emit job->aliasFetched(aliasStr, ""); + } + }); +} + +void +AliasEditingModel::toggleAdvertize(int row) +{ + if (!canSendStateEvent || row < 0 || row >= aliases.size() || aliases.at(row).alias.empty()) + return; + + auto &moveAlias = aliases[row]; + if (aliasEvent.alias == moveAlias.alias) { + moveAlias.canonical = false; + moveAlias.advertized = false; + aliasEvent.alias.clear(); + emit dataChanged(index(row), index(row), {IsAdvertized, IsCanonical}); + } else if (moveAlias.advertized) { + for (auto i = aliasEvent.alt_aliases.begin(); i != aliasEvent.alt_aliases.end(); ++i) { + if (*i == moveAlias.alias) { + aliasEvent.alt_aliases.erase(i); + moveAlias.advertized = false; + emit dataChanged(index(row), index(row), {IsAdvertized}); + break; + } + } + } else { + aliasEvent.alt_aliases.push_back(moveAlias.alias); + moveAlias.advertized = true; + emit dataChanged(index(row), index(row), {IsAdvertized}); + } +} + +void +AliasEditingModel::updateAlias(std::string alias, std::string target) +{ + for (qsizetype i = 0; i < aliases.size(); i++) { + auto &e = aliases[i]; + if (e.alias == alias) { + e.published = (target == room_id); + emit dataChanged(index(i), index(i), {IsPublished}); + } + } +} + +void +AliasEditingModel::updatePublishedAliases(std::vector<std::string> advAliases) +{ + for (const auto &advAlias : advAliases) { + bool found = false; + for (qsizetype i = 0; i < aliases.size(); i++) { + auto &alias = aliases[i]; + if (alias.alias == advAlias) { + alias.published = true; + emit dataChanged(index(i), index(i), {IsPublished}); + found = true; + break; + } + + if (!found) { + beginInsertRows(QModelIndex(), aliases.size(), aliases.size()); + aliases.push_back(Entry{advAlias, false, false, true}); + endInsertRows(); + } + } + } +} + +void +AliasEditingModel::commit() +{ + if (!canSendStateEvent) + return; + + http::client()->send_state_event( + room_id, aliasEvent, [](const mtx::responses::EventId &, mtx::http::RequestErr e) { + if (e) { + nhlog::net()->error("Failed to send Alias event: {}", *e); + ChatPage::instance()->showNotification( + tr("Failed to update aliases: %1") + .arg(QString::fromStdString(e->matrix_error.error))); + } + }); +} |