2022-07-08 18:28:28 +03:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-09 01:42:44 +03:00
|
|
|
for (const auto &alias : qAsConst(aliases)) {
|
2022-07-08 18:28:28 +03:00
|
|
|
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();
|
2022-07-09 01:42:44 +03:00
|
|
|
for (const auto &e : qAsConst(aliases)) {
|
2022-07-08 18:28:28 +03:00
|
|
|
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)));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|