mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-23 03:18:49 +03:00
Handle matrix scheme
Link opening only works on Linux for now. See https://github.com/matrix-org/matrix-doc/pull/2312
This commit is contained in:
parent
cc9de7f3b0
commit
39f9b7d90a
7 changed files with 216 additions and 17 deletions
|
@ -8,3 +8,4 @@ Type=Application
|
||||||
Categories=Network;InstantMessaging;Qt;
|
Categories=Network;InstantMessaging;Qt;
|
||||||
StartupWMClass=nheko
|
StartupWMClass=nheko
|
||||||
Terminal=false
|
Terminal=false
|
||||||
|
MimeType=x-scheme-handler/matrix;
|
||||||
|
|
|
@ -2221,6 +2221,34 @@ Cache::getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb)
|
||||||
return QString("1");
|
return QString("1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<mtx::events::state::CanonicalAlias>
|
||||||
|
Cache::getRoomAliases(const std::string &roomid)
|
||||||
|
{
|
||||||
|
using namespace mtx::events;
|
||||||
|
using namespace mtx::events::state;
|
||||||
|
|
||||||
|
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
|
||||||
|
auto statesdb = getStatesDb(txn, roomid);
|
||||||
|
|
||||||
|
lmdb::val event;
|
||||||
|
bool res = lmdb::dbi_get(
|
||||||
|
txn, statesdb, lmdb::val(to_string(mtx::events::EventType::RoomCanonicalAlias)), event);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
try {
|
||||||
|
StateEvent<CanonicalAlias> msg =
|
||||||
|
json::parse(std::string_view(event.data(), event.size()));
|
||||||
|
|
||||||
|
return msg.content;
|
||||||
|
} catch (const json::exception &e) {
|
||||||
|
nhlog::db()->warn("failed to parse m.room.canonical_alias event: {}",
|
||||||
|
e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
Cache::getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb)
|
Cache::getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb)
|
||||||
{
|
{
|
||||||
|
|
|
@ -81,6 +81,7 @@ public:
|
||||||
std::vector<std::string> joinedRooms();
|
std::vector<std::string> joinedRooms();
|
||||||
|
|
||||||
QMap<QString, RoomInfo> roomInfo(bool withInvites = true);
|
QMap<QString, RoomInfo> roomInfo(bool withInvites = true);
|
||||||
|
std::optional<mtx::events::state::CanonicalAlias> getRoomAliases(const std::string &roomid);
|
||||||
std::map<QString, bool> invites();
|
std::map<QString, bool> invites();
|
||||||
|
|
||||||
//! Calculate & return the name of the room.
|
//! Calculate & return the name of the room.
|
||||||
|
|
140
src/ChatPage.cpp
140
src/ChatPage.cpp
|
@ -918,6 +918,8 @@ ChatPage::joinRoom(const QString &room)
|
||||||
} catch (const lmdb::error &e) {
|
} catch (const lmdb::error &e) {
|
||||||
emit showNotification(tr("Failed to remove invite: %1").arg(e.what()));
|
emit showNotification(tr("Failed to remove invite: %1").arg(e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
room_list_->highlightSelectedRoom(QString::fromStdString(room_id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1268,3 +1270,141 @@ ChatPage::decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescriptio
|
||||||
cache::storeSecret(secretName, decrypted);
|
cache::storeSecret(secretName, decrypted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ChatPage::startChat(QString userid)
|
||||||
|
{
|
||||||
|
auto joined_rooms = cache::joinedRooms();
|
||||||
|
auto room_infos = cache::getRoomInfo(joined_rooms);
|
||||||
|
|
||||||
|
for (std::string room_id : joined_rooms) {
|
||||||
|
if (room_infos[QString::fromStdString(room_id)].member_count == 2) {
|
||||||
|
auto room_members = cache::roomMembers(room_id);
|
||||||
|
if (std::find(room_members.begin(),
|
||||||
|
room_members.end(),
|
||||||
|
(userid).toStdString()) != room_members.end()) {
|
||||||
|
room_list_->highlightSelectedRoom(QString::fromStdString(room_id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mtx::requests::CreateRoom req;
|
||||||
|
req.preset = mtx::requests::Preset::PrivateChat;
|
||||||
|
req.visibility = mtx::requests::Visibility::Private;
|
||||||
|
if (utils::localUser() != userid)
|
||||||
|
req.invite = {userid.toStdString()};
|
||||||
|
emit ChatPage::instance()->createRoom(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString
|
||||||
|
mxidFromSegments(QStringRef sigil, QStringRef mxid)
|
||||||
|
{
|
||||||
|
if (mxid.isEmpty())
|
||||||
|
return "";
|
||||||
|
|
||||||
|
auto mxid_ = QUrl::fromPercentEncoding(mxid.toUtf8());
|
||||||
|
|
||||||
|
if (sigil == "user") {
|
||||||
|
return "@" + mxid_;
|
||||||
|
} else if (sigil == "roomid") {
|
||||||
|
return "!" + mxid_;
|
||||||
|
} else if (sigil == "room") {
|
||||||
|
return "#" + mxid_;
|
||||||
|
} else if (sigil == "group") {
|
||||||
|
return "+" + mxid_;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ChatPage::handleMatrixUri(const QByteArray &uri)
|
||||||
|
{
|
||||||
|
nhlog::ui()->info("Received uri! {}", uri.toStdString());
|
||||||
|
QUrl uri_{QString::fromUtf8(uri)};
|
||||||
|
|
||||||
|
if (uri_.scheme() != "matrix")
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto tempPath = uri_.path(QUrl::ComponentFormattingOption::FullyEncoded);
|
||||||
|
if (tempPath.startsWith('/'))
|
||||||
|
tempPath.remove(0, 1);
|
||||||
|
auto segments = tempPath.splitRef('/');
|
||||||
|
|
||||||
|
if (segments.size() != 2 && segments.size() != 4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto sigil1 = segments[0];
|
||||||
|
auto mxid1 = mxidFromSegments(sigil1, segments[1]);
|
||||||
|
if (mxid1.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString mxid2;
|
||||||
|
if (segments.size() == 4 && segments[2] == "event") {
|
||||||
|
if (segments[3].isEmpty())
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
mxid2 = "$" + QUrl::fromPercentEncoding(segments[3].toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> vias;
|
||||||
|
QString action;
|
||||||
|
|
||||||
|
for (QString item : uri_.query(QUrl::ComponentFormattingOption::FullyEncoded).split('&')) {
|
||||||
|
nhlog::ui()->info("item: {}", item.toStdString());
|
||||||
|
|
||||||
|
if (item.startsWith("action=")) {
|
||||||
|
action = item.remove("action=");
|
||||||
|
} else if (item.startsWith("via=")) {
|
||||||
|
vias.push_back(
|
||||||
|
QUrl::fromPercentEncoding(item.remove("via=").toUtf8()).toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sigil1 == "user") {
|
||||||
|
if (action.isEmpty()) {
|
||||||
|
view_manager_->activeTimeline()->openUserProfile(mxid1);
|
||||||
|
} else if (action == "chat") {
|
||||||
|
this->startChat(mxid1);
|
||||||
|
}
|
||||||
|
} else if (sigil1 == "roomid") {
|
||||||
|
auto joined_rooms = cache::joinedRooms();
|
||||||
|
auto targetRoomId = mxid1.toStdString();
|
||||||
|
|
||||||
|
for (auto roomid : joined_rooms) {
|
||||||
|
if (roomid == targetRoomId) {
|
||||||
|
room_list_->highlightSelectedRoom(mxid1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == "join") {
|
||||||
|
joinRoom(mxid1);
|
||||||
|
}
|
||||||
|
} else if (sigil1 == "room") {
|
||||||
|
auto joined_rooms = cache::joinedRooms();
|
||||||
|
auto targetRoomAlias = mxid1.toStdString();
|
||||||
|
|
||||||
|
for (auto roomid : joined_rooms) {
|
||||||
|
auto aliases = cache::client()->getRoomAliases(roomid);
|
||||||
|
if (aliases) {
|
||||||
|
if (aliases->alias == targetRoomAlias) {
|
||||||
|
room_list_->highlightSelectedRoom(
|
||||||
|
QString::fromStdString(roomid));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == "join") {
|
||||||
|
joinRoom(mxid1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ChatPage::handleMatrixUri(const QUrl &uri)
|
||||||
|
{
|
||||||
|
handleMatrixUri(uri.toString(QUrl::ComponentFormattingOption::FullyEncoded).toUtf8());
|
||||||
|
}
|
||||||
|
|
|
@ -110,6 +110,10 @@ public:
|
||||||
mtx::presence::PresenceState currentPresence() const;
|
mtx::presence::PresenceState currentPresence() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
void handleMatrixUri(const QByteArray &uri);
|
||||||
|
void handleMatrixUri(const QUrl &uri);
|
||||||
|
|
||||||
|
void startChat(QString userid);
|
||||||
void leaveRoom(const QString &room_id);
|
void leaveRoom(const QString &room_id);
|
||||||
void createRoom(const mtx::requests::CreateRoom &req);
|
void createRoom(const mtx::requests::CreateRoom &req);
|
||||||
void joinRoom(const QString &room);
|
void joinRoom(const QString &room);
|
||||||
|
|
52
src/main.cpp
52
src/main.cpp
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
#include <QDesktopServices>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
|
|
||||||
|
#include "ChatPage.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
|
@ -128,34 +130,43 @@ main(int argc, char *argv[])
|
||||||
// This is some hacky programming, but it's necessary (AFAIK?) to get the unique config name
|
// This is some hacky programming, but it's necessary (AFAIK?) to get the unique config name
|
||||||
// parsed before the SingleApplication userdata is set.
|
// parsed before the SingleApplication userdata is set.
|
||||||
QString userdata{""};
|
QString userdata{""};
|
||||||
|
QString matrixUri;
|
||||||
for (int i = 0; i < argc; ++i) {
|
for (int i = 0; i < argc; ++i) {
|
||||||
if (QString{argv[i]}.startsWith("--profile=")) {
|
QString arg{argv[i]};
|
||||||
QString q{argv[i]};
|
if (arg.startsWith("--profile=")) {
|
||||||
q.remove("--profile=");
|
arg.remove("--profile=");
|
||||||
userdata = q;
|
userdata = arg;
|
||||||
} else if (QString{argv[i]}.startsWith("--p=")) {
|
} else if (arg.startsWith("--p=")) {
|
||||||
QString q{argv[i]};
|
arg.remove("-p=");
|
||||||
q.remove("-p=");
|
userdata = arg;
|
||||||
userdata = q;
|
} else if (arg == "--profile" || arg == "-p") {
|
||||||
} else if (QString{argv[i]} == "--profile" || QString{argv[i]} == "-p") {
|
|
||||||
if (i < argc - 1) // if i is less than argc - 1, we still have a parameter
|
if (i < argc - 1) // if i is less than argc - 1, we still have a parameter
|
||||||
// left to process as the name
|
// left to process as the name
|
||||||
{
|
{
|
||||||
++i; // the next arg is the name, so increment
|
++i; // the next arg is the name, so increment
|
||||||
userdata = QString{argv[i]};
|
userdata = QString{argv[i]};
|
||||||
}
|
}
|
||||||
|
} else if (arg.startsWith("matrix:")) {
|
||||||
|
matrixUri = arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleApplication app(argc,
|
SingleApplication app(argc,
|
||||||
argv,
|
argv,
|
||||||
false,
|
true,
|
||||||
SingleApplication::Mode::User |
|
SingleApplication::Mode::User |
|
||||||
SingleApplication::Mode::ExcludeAppPath |
|
SingleApplication::Mode::ExcludeAppPath |
|
||||||
SingleApplication::Mode::ExcludeAppVersion,
|
SingleApplication::Mode::ExcludeAppVersion |
|
||||||
|
SingleApplication::Mode::SecondaryNotification,
|
||||||
100,
|
100,
|
||||||
userdata);
|
userdata);
|
||||||
|
|
||||||
|
if (app.isSecondary()) {
|
||||||
|
// open uri in main instance
|
||||||
|
app.sendMessage(matrixUri.toUtf8());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
@ -245,6 +256,25 @@ main(int argc, char *argv[])
|
||||||
w.activateWindow();
|
w.activateWindow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
&app,
|
||||||
|
&SingleApplication::receivedMessage,
|
||||||
|
ChatPage::instance(),
|
||||||
|
[&](quint32, QByteArray message) { ChatPage::instance()->handleMatrixUri(message); });
|
||||||
|
|
||||||
|
QMetaObject::Connection uriConnection;
|
||||||
|
if (app.isPrimary() && !matrixUri.isEmpty()) {
|
||||||
|
uriConnection = QObject::connect(ChatPage::instance(),
|
||||||
|
&ChatPage::contentLoaded,
|
||||||
|
ChatPage::instance(),
|
||||||
|
[&uriConnection, matrixUri]() {
|
||||||
|
ChatPage::instance()->handleMatrixUri(
|
||||||
|
matrixUri.toUtf8());
|
||||||
|
QObject::disconnect(uriConnection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
QDesktopServices::setUrlHandler("matrix", ChatPage::instance(), "handleMatrixUri");
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
// Temporary solution for the emoji picker until
|
// Temporary solution for the emoji picker until
|
||||||
// nheko has a proper menu bar with more functionality.
|
// nheko has a proper menu bar with more functionality.
|
||||||
|
|
|
@ -202,12 +202,7 @@ UserProfile::kickUser()
|
||||||
void
|
void
|
||||||
UserProfile::startChat()
|
UserProfile::startChat()
|
||||||
{
|
{
|
||||||
mtx::requests::CreateRoom req;
|
ChatPage::instance()->startChat(this->userid_);
|
||||||
req.preset = mtx::requests::Preset::PrivateChat;
|
|
||||||
req.visibility = mtx::requests::Visibility::Private;
|
|
||||||
if (utils::localUser() != this->userid_)
|
|
||||||
req.invite = {this->userid_.toStdString()};
|
|
||||||
emit ChatPage::instance()->createRoom(req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
Loading…
Reference in a new issue