Merge pull request #308 from Nheko-Reborn/build-opt

Optimize build
This commit is contained in:
DeepBlueV7.X 2020-10-28 14:50:00 +01:00 committed by GitHub
commit fd4f173966
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 257 additions and 250 deletions

View File

@ -340,7 +340,7 @@ if(USE_BUNDLED_MTXCLIENT)
FetchContent_Declare( FetchContent_Declare(
MatrixClient MatrixClient
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
GIT_TAG 6432e89a3465e58ed838dd2abdcb0f91bd4f05b0 GIT_TAG ed6315563409ce9d47978ff2a2d771b863e375c5
) )
FetchContent_MakeAvailable(MatrixClient) FetchContent_MakeAvailable(MatrixClient)
else() else()
@ -600,6 +600,7 @@ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
target_precompile_headers(nheko target_precompile_headers(nheko
PRIVATE PRIVATE
<string> <string>
<algorithm>
) )
endif() endif()

View File

@ -3,3 +3,7 @@ hunter_config(
VERSION "1.70.0-p1" VERSION "1.70.0-p1"
CMAKE_ARGS IOSTREAMS_NO_BZIP2=1 CMAKE_ARGS IOSTREAMS_NO_BZIP2=1
) )
hunter_config(
nlohmann_json
CMAKE_ARGS JSON_MultipleHeaders=ON
)

View File

@ -146,7 +146,7 @@
"name": "mtxclient", "name": "mtxclient",
"sources": [ "sources": [
{ {
"commit": "6432e89a3465e58ed838dd2abdcb0f91bd4f05b0", "commit": "ed6315563409ce9d47978ff2a2d771b863e375c5",
"type": "git", "type": "git",
"url": "https://github.com/Nheko-Reborn/mtxclient.git" "url": "https://github.com/Nheko-Reborn/mtxclient.git"
} }

View File

@ -1469,22 +1469,22 @@ Cache::getRoomInfo(const std::vector<std::string> &rooms)
return room_info; return room_info;
} }
std::map<QString, mtx::responses::Timeline> std::vector<QString>
Cache::roomMessages() Cache::roomIds()
{ {
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
std::map<QString, mtx::responses::Timeline> msgs; std::vector<QString> rooms;
std::string room_id, unused; std::string room_id, unused;
auto roomsCursor = lmdb::cursor::open(txn, roomsDb_); auto roomsCursor = lmdb::cursor::open(txn, roomsDb_);
while (roomsCursor.get(room_id, unused, MDB_NEXT)) while (roomsCursor.get(room_id, unused, MDB_NEXT))
msgs.emplace(QString::fromStdString(room_id), mtx::responses::Timeline()); rooms.push_back(QString::fromStdString(room_id));
roomsCursor.close(); roomsCursor.close();
txn.commit(); txn.commit();
return msgs; return rooms;
} }
QMap<QString, mtx::responses::Notifications> QMap<QString, mtx::responses::Notifications>
@ -3376,6 +3376,46 @@ Cache::markUserKeysOutOfDate(lmdb::txn &txn,
}); });
} }
void
Cache::query_keys(const std::string &user_id,
std::function<void(const UserKeyCache &, mtx::http::RequestErr)> cb)
{
auto cache_ = cache::userKeys(user_id);
if (cache_.has_value()) {
if (!cache_->updated_at.empty() && cache_->updated_at == cache_->last_changed) {
cb(cache_.value(), {});
return;
}
}
mtx::requests::QueryKeys req;
req.device_keys[user_id] = {};
std::string last_changed;
if (cache_)
last_changed = cache_->last_changed;
req.token = last_changed;
http::client()->query_keys(req,
[cb, user_id, last_changed](const mtx::responses::QueryKeys &res,
mtx::http::RequestErr err) {
if (err) {
nhlog::net()->warn(
"failed to query device keys: {},{}",
err->matrix_error.errcode,
static_cast<int>(err->status_code));
cb({}, err);
return;
}
cache::updateUserKeys(last_changed, res);
auto keys = cache::userKeys(user_id);
cb(keys.value_or(UserKeyCache{}), err);
});
}
void void
to_json(json &j, const VerificationCache &info) to_json(json &j, const VerificationCache &info)
{ {
@ -3927,10 +3967,10 @@ setCurrentFormat()
instance_->setCurrentFormat(); instance_->setCurrentFormat();
} }
std::map<QString, mtx::responses::Timeline> std::vector<QString>
roomMessages() roomIds()
{ {
return instance_->roomMessages(); return instance_->roomIds();
} }
QMap<QString, mtx::responses::Notifications> QMap<QString, mtx::responses::Notifications>

View File

@ -28,11 +28,18 @@
#include <lmdb++.h> #include <lmdb++.h>
#endif #endif
#include <mtx/responses.hpp> #include <mtx/events/event_type.hpp>
#include <mtx/events/presence.hpp>
#include <mtx/responses/crypto.hpp>
#include <mtxclient/crypto/types.hpp>
#include "CacheCryptoStructs.h" #include "CacheCryptoStructs.h"
#include "CacheStructs.h" #include "CacheStructs.h"
namespace mtx::responses {
struct Notifications;
}
namespace cache { namespace cache {
void void
init(const QString &user_id); init(const QString &user_id);
@ -94,8 +101,6 @@ getRoomVersion(lmdb::txn &txn, lmdb::dbi &statesdb);
std::vector<RoomMember> std::vector<RoomMember>
getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30);
void
saveState(const mtx::responses::Sync &res);
bool bool
isInitialized(); isInitialized();
@ -128,9 +133,6 @@ setCurrentFormat();
bool bool
runMigrations(); runMigrations();
std::map<QString, mtx::responses::Timeline>
roomMessages();
QMap<QString, mtx::responses::Notifications> QMap<QString, mtx::responses::Notifications>
getTimelineMentions(); getTimelineMentions();
@ -182,22 +184,8 @@ saveImage(const QString &url, const QByteArray &data);
RoomInfo RoomInfo
singleRoomInfo(const std::string &room_id); singleRoomInfo(const std::string &room_id);
std::vector<std::string>
roomsWithStateUpdates(const mtx::responses::Sync &res);
std::vector<std::string>
roomsWithTagUpdates(const mtx::responses::Sync &res);
std::map<QString, RoomInfo> std::map<QString, RoomInfo>
getRoomInfo(const std::vector<std::string> &rooms); getRoomInfo(const std::vector<std::string> &rooms);
inline std::map<QString, RoomInfo>
roomUpdates(const mtx::responses::Sync &sync)
{
return getRoomInfo(roomsWithStateUpdates(sync));
}
inline std::map<QString, RoomInfo>
roomTagUpdates(const mtx::responses::Sync &sync)
{
return getRoomInfo(roomsWithTagUpdates(sync));
}
//! Calculates which the read status of a room. //! Calculates which the read status of a room.
//! Whether all the events in the timeline have been read. //! Whether all the events in the timeline have been read.

View File

@ -3,10 +3,8 @@
#include <map> #include <map>
#include <mutex> #include <mutex>
//#include <nlohmann/json.hpp> #include <mtx/responses/crypto.hpp>
#include <mtxclient/crypto/objects.hpp>
#include <mtx/responses.hpp>
#include <mtxclient/crypto/client.hpp>
// Extra information associated with an outbound megolm session. // Extra information associated with an outbound megolm session.
struct OutboundGroupSessionData struct OutboundGroupSessionData

View File

@ -33,8 +33,11 @@
#endif #endif
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <mtx/responses.hpp> #include <mtx/responses/messages.hpp>
#include <mtx/responses/notifications.hpp>
#include <mtx/responses/sync.hpp>
#include <mtxclient/crypto/client.hpp> #include <mtxclient/crypto/client.hpp>
#include <mtxclient/http/client.hpp>
#include "CacheCryptoStructs.h" #include "CacheCryptoStructs.h"
#include "CacheStructs.h" #include "CacheStructs.h"
@ -65,6 +68,8 @@ public:
void deleteUserKeys(lmdb::txn &txn, void deleteUserKeys(lmdb::txn &txn,
lmdb::dbi &db, lmdb::dbi &db,
const std::vector<std::string> &user_ids); const std::vector<std::string> &user_ids);
void query_keys(const std::string &user_id,
std::function<void(const UserKeyCache &, mtx::http::RequestErr)> cb);
// device & user verification cache // device & user verification cache
VerificationStatus verificationStatus(const std::string &user_id); VerificationStatus verificationStatus(const std::string &user_id);
@ -113,8 +118,7 @@ public:
void setCurrentFormat(); void setCurrentFormat();
bool runMigrations(); bool runMigrations();
std::map<QString, mtx::responses::Timeline> roomMessages(); std::vector<QString> roomIds();
QMap<QString, mtx::responses::Notifications> getTimelineMentions(); QMap<QString, mtx::responses::Notifications> getTimelineMentions();
//! Retrieve all the user ids from a room. //! Retrieve all the user ids from a room.

View File

@ -13,6 +13,7 @@
#include "MainWindow.h" #include "MainWindow.h"
#include "MatrixClient.h" #include "MatrixClient.h"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"
#include "Utils.h"
#include "WebRTCSession.h" #include "WebRTCSession.h"
#include "dialogs/AcceptCall.h" #include "dialogs/AcceptCall.h"
@ -33,8 +34,8 @@ std::vector<std::string>
getTurnURIs(const mtx::responses::TurnServer &turnServer); getTurnURIs(const mtx::responses::TurnServer &turnServer);
} }
CallManager::CallManager(QSharedPointer<UserSettings> userSettings) CallManager::CallManager(QSharedPointer<UserSettings> userSettings, QObject *parent)
: QObject() : QObject(parent)
, session_(WebRTCSession::instance()) , session_(WebRTCSession::instance())
, turnServerTimer_(this) , turnServerTimer_(this)
, settings_(userSettings) , settings_(userSettings)

View File

@ -24,7 +24,7 @@ class CallManager : public QObject
Q_OBJECT Q_OBJECT
public: public:
CallManager(QSharedPointer<UserSettings>); CallManager(QSharedPointer<UserSettings>, QObject *);
void sendInvite(const QString &roomid); void sendInvite(const QString &roomid);
void hangUp( void hangUp(

View File

@ -22,9 +22,12 @@
#include <QShortcut> #include <QShortcut>
#include <QtConcurrent> #include <QtConcurrent>
#include <mtx/responses.hpp>
#include "AvatarProvider.h" #include "AvatarProvider.h"
#include "Cache.h" #include "Cache.h"
#include "Cache_p.h" #include "Cache_p.h"
#include "CallManager.h"
#include "ChatPage.h" #include "ChatPage.h"
#include "DeviceVerificationFlow.h" #include "DeviceVerificationFlow.h"
#include "EventAccessors.h" #include "EventAccessors.h"
@ -69,7 +72,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
, isConnected_(true) , isConnected_(true)
, userSettings_{userSettings} , userSettings_{userSettings}
, notificationsManager(this) , notificationsManager(this)
, callManager_(userSettings) , callManager_(new CallManager(userSettings, this))
{ {
setObjectName("chatPage"); setObjectName("chatPage");
@ -126,7 +129,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
contentLayout_->setSpacing(0); contentLayout_->setSpacing(0);
contentLayout_->setMargin(0); contentLayout_->setMargin(0);
view_manager_ = new TimelineViewManager(&callManager_, this); view_manager_ = new TimelineViewManager(callManager_, this);
contentLayout_->addWidget(view_manager_->getWidget()); contentLayout_->addWidget(view_manager_->getWidget());
@ -434,8 +437,8 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
}); });
connect(text_input_, &TextInputWidget::callButtonPress, this, [this]() { connect(text_input_, &TextInputWidget::callButtonPress, this, [this]() {
if (callManager_.onActiveCall()) { if (callManager_->onActiveCall()) {
callManager_.hangUp(); callManager_->hangUp();
} else { } else {
if (auto roomInfo = cache::singleRoomInfo(current_room_.toStdString()); if (auto roomInfo = cache::singleRoomInfo(current_room_.toStdString());
roomInfo.member_count != 2) { roomInfo.member_count != 2) {
@ -454,7 +457,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
userSettings_, userSettings_,
MainWindow::instance()); MainWindow::instance());
connect(dialog, &dialogs::PlaceCall::voice, this, [this]() { connect(dialog, &dialogs::PlaceCall::voice, this, [this]() {
callManager_.sendInvite(current_room_); callManager_->sendInvite(current_room_);
}); });
utils::centerWidget(dialog, MainWindow::instance()); utils::centerWidget(dialog, MainWindow::instance());
dialog->show(); dialog->show();
@ -692,7 +695,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
const bool isInitialized = cache::isInitialized(); const bool isInitialized = cache::isInitialized();
const auto cacheVersion = cache::formatVersion(); const auto cacheVersion = cache::formatVersion();
callManager_.refreshTurnServer(); callManager_->refreshTurnServer();
if (!isInitialized) { if (!isInitialized) {
cache::setCurrentFormat(); cache::setCurrentFormat();
@ -762,7 +765,7 @@ ChatPage::loadStateFromCache()
cache::restoreSessions(); cache::restoreSessions();
olm::client()->load(cache::restoreOlmAccount(), STORAGE_SECRET_KEY); olm::client()->load(cache::restoreOlmAccount(), STORAGE_SECRET_KEY);
emit initializeEmptyViews(cache::roomMessages()); emit initializeEmptyViews(cache::client()->roomIds());
emit initializeRoomList(cache::roomInfo()); emit initializeRoomList(cache::roomInfo());
emit initializeMentions(cache::getTimelineMentions()); emit initializeMentions(cache::getTimelineMentions());
emit syncTags(cache::roomInfo().toStdMap()); emit syncTags(cache::roomInfo().toStdMap());
@ -969,13 +972,64 @@ ChatPage::startInitialSync()
opts.set_presence = currentPresence(); opts.set_presence = currentPresence();
http::client()->sync( http::client()->sync(
opts, opts, [this](const mtx::responses::Sync &res, mtx::http::RequestErr err) {
std::bind( // TODO: Initial Sync should include mentions as well...
&ChatPage::initialSyncHandler, this, std::placeholders::_1, std::placeholders::_2));
if (err) {
const auto error = QString::fromStdString(err->matrix_error.error);
const auto msg = tr("Please try to login again: %1").arg(error);
const auto err_code = mtx::errors::to_string(err->matrix_error.errcode);
const int status_code = static_cast<int>(err->status_code);
nhlog::net()->error("initial sync error: {} {}", status_code, err_code);
// non http related errors
if (status_code <= 0 || status_code >= 600) {
startInitialSync();
return;
}
switch (status_code) {
case 502:
case 504:
case 524: {
startInitialSync();
return;
}
default: {
emit dropToLoginPageCb(msg);
return;
}
}
}
nhlog::net()->info("initial sync completed");
try {
cache::client()->saveState(res);
olm::handle_to_device_messages(res.to_device.events);
emit initializeViews(std::move(res.rooms));
emit initializeRoomList(cache::roomInfo());
emit initializeMentions(cache::getTimelineMentions());
cache::calculateRoomReadStatus();
emit syncTags(cache::roomInfo().toStdMap());
} catch (const lmdb::error &e) {
nhlog::db()->error("failed to save state after initial sync: {}",
e.what());
startInitialSync();
return;
}
emit trySyncCb();
emit contentLoaded();
});
} }
void void
ChatPage::handleSyncResponse(mtx::responses::Sync res) ChatPage::handleSyncResponse(const mtx::responses::Sync &res)
{ {
nhlog::net()->debug("sync completed: {}", res.next_batch); nhlog::net()->debug("sync completed: {}", res.next_batch);
@ -984,16 +1038,16 @@ ChatPage::handleSyncResponse(mtx::responses::Sync res)
// TODO: fine grained error handling // TODO: fine grained error handling
try { try {
cache::saveState(res); cache::client()->saveState(res);
olm::handle_to_device_messages(res.to_device.events); olm::handle_to_device_messages(res.to_device.events);
auto updates = cache::roomUpdates(res); auto updates = cache::getRoomInfo(cache::client()->roomsWithStateUpdates(res));
emit syncRoomlist(updates); emit syncRoomlist(updates);
emit syncUI(res.rooms); emit syncUI(res.rooms);
emit syncTags(cache::roomTagUpdates(res)); emit syncTags(cache::getRoomInfo(cache::client()->roomsWithTagUpdates(res)));
// if we process a lot of syncs (1 every 200ms), this means we clean the // if we process a lot of syncs (1 every 200ms), this means we clean the
// db every 100s // db every 100s
@ -1068,7 +1122,7 @@ ChatPage::joinRoom(const QString &room)
const auto room_id = room.toStdString(); const auto room_id = room.toStdString();
http::client()->join_room( http::client()->join_room(
room_id, [this, room_id](const nlohmann::json &, mtx::http::RequestErr err) { room_id, [this, room_id](const mtx::responses::RoomId &, mtx::http::RequestErr err) {
if (err) { if (err) {
emit showNotification( emit showNotification(
tr("Failed to join room: %1") tr("Failed to join room: %1")
@ -1114,7 +1168,8 @@ void
ChatPage::leaveRoom(const QString &room_id) ChatPage::leaveRoom(const QString &room_id)
{ {
http::client()->leave_room( http::client()->leave_room(
room_id.toStdString(), [this, room_id](const json &, mtx::http::RequestErr err) { room_id.toStdString(),
[this, room_id](const mtx::responses::Empty &, mtx::http::RequestErr err) {
if (err) { if (err) {
emit showNotification( emit showNotification(
tr("Failed to leave room: %1") tr("Failed to leave room: %1")
@ -1289,62 +1344,6 @@ ChatPage::currentPresence() const
} }
} }
void
ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err)
{
// TODO: Initial Sync should include mentions as well...
if (err) {
const auto error = QString::fromStdString(err->matrix_error.error);
const auto msg = tr("Please try to login again: %1").arg(error);
const auto err_code = mtx::errors::to_string(err->matrix_error.errcode);
const int status_code = static_cast<int>(err->status_code);
nhlog::net()->error("initial sync error: {} {}", status_code, err_code);
// non http related errors
if (status_code <= 0 || status_code >= 600) {
startInitialSync();
return;
}
switch (status_code) {
case 502:
case 504:
case 524: {
startInitialSync();
return;
}
default: {
emit dropToLoginPageCb(msg);
return;
}
}
}
nhlog::net()->info("initial sync completed");
try {
cache::saveState(res);
olm::handle_to_device_messages(res.to_device.events);
emit initializeViews(std::move(res.rooms));
emit initializeRoomList(cache::roomInfo());
emit initializeMentions(cache::getTimelineMentions());
cache::calculateRoomReadStatus();
emit syncTags(cache::roomInfo().toStdMap());
} catch (const lmdb::error &e) {
nhlog::db()->error("failed to save state after initial sync: {}", e.what());
startInitialSync();
return;
}
emit trySyncCb();
emit contentLoaded();
}
void void
ChatPage::ensureOneTimeKeyCount(const std::map<std::string, uint16_t> &counts) ChatPage::ensureOneTimeKeyCount(const std::map<std::string, uint16_t> &counts)
{ {
@ -1453,51 +1452,11 @@ ChatPage::initiateLogout()
emit showOverlayProgressBar(); emit showOverlayProgressBar();
} }
void
ChatPage::query_keys(const std::string &user_id,
std::function<void(const UserKeyCache &, mtx::http::RequestErr)> cb)
{
auto cache_ = cache::userKeys(user_id);
if (cache_.has_value()) {
if (!cache_->updated_at.empty() && cache_->updated_at == cache_->last_changed) {
cb(cache_.value(), {});
return;
}
}
mtx::requests::QueryKeys req;
req.device_keys[user_id] = {};
std::string last_changed;
if (cache_)
last_changed = cache_->last_changed;
req.token = last_changed;
http::client()->query_keys(req,
[cb, user_id, last_changed](const mtx::responses::QueryKeys &res,
mtx::http::RequestErr err) {
if (err) {
nhlog::net()->warn(
"failed to query device keys: {},{}",
err->matrix_error.errcode,
static_cast<int>(err->status_code));
cb({}, err);
return;
}
cache::updateUserKeys(last_changed, res);
auto keys = cache::userKeys(user_id);
cb(keys.value_or(UserKeyCache{}), err);
});
}
template<typename T> template<typename T>
void void
ChatPage::connectCallMessage() ChatPage::connectCallMessage()
{ {
connect(&callManager_, connect(callManager_,
qOverload<const QString &, const T &>(&CallManager::newMessage), qOverload<const QString &, const T &>(&CallManager::newMessage),
view_manager_, view_manager_,
qOverload<const QString &, const T &>(&TimelineViewManager::queueCallMessage)); qOverload<const QString &, const T &>(&TimelineViewManager::queueCallMessage));

View File

@ -23,9 +23,10 @@
#include <variant> #include <variant>
#include <mtx/common.hpp> #include <mtx/common.hpp>
#include <mtx/requests.hpp> #include <mtx/events.hpp>
#include <mtx/responses.hpp> #include <mtx/events/encrypted.hpp>
#include <mtxclient/http/errors.hpp> #include <mtx/events/member.hpp>
#include <mtx/events/presence.hpp>
#include <QFrame> #include <QFrame>
#include <QHBoxLayout> #include <QHBoxLayout>
@ -37,11 +38,8 @@
#include "CacheCryptoStructs.h" #include "CacheCryptoStructs.h"
#include "CacheStructs.h" #include "CacheStructs.h"
#include "CallManager.h"
#include "CommunitiesList.h" #include "CommunitiesList.h"
#include "Utils.h"
#include "notifications/Manager.h" #include "notifications/Manager.h"
#include "popups/UserMentions.h"
class OverlayModal; class OverlayModal;
class QuickSwitcher; class QuickSwitcher;
@ -54,13 +52,25 @@ class UserInfoWidget;
class UserSettings; class UserSettings;
class NotificationsManager; class NotificationsManager;
class TimelineModel; class TimelineModel;
class CallManager;
constexpr int CONSENSUS_TIMEOUT = 1000; constexpr int CONSENSUS_TIMEOUT = 1000;
constexpr int SHOW_CONTENT_TIMEOUT = 3000; constexpr int SHOW_CONTENT_TIMEOUT = 3000;
constexpr int TYPING_REFRESH_TIMEOUT = 10000; constexpr int TYPING_REFRESH_TIMEOUT = 10000;
namespace mtx::http { namespace mtx::requests {
using RequestErr = const std::optional<mtx::http::ClientError> &; struct CreateRoom;
}
namespace mtx::responses {
struct Notifications;
struct Sync;
struct Timeline;
struct Rooms;
struct LeftRoom;
}
namespace popups {
class UserMentions;
} }
class ChatPage : public QWidget class ChatPage : public QWidget
@ -89,8 +99,6 @@ public:
//! Show the room/group list (if it was visible). //! Show the room/group list (if it was visible).
void showSideBars(); void showSideBars();
void initiateLogout(); void initiateLogout();
void query_keys(const std::string &req,
std::function<void(const UserKeyCache &, mtx::http::RequestErr)> cb);
void focusMessageInput(); void focusMessageInput();
QString status() const; QString status() const;
@ -145,12 +153,12 @@ signals:
void trySyncCb(); void trySyncCb();
void tryDelayedSyncCb(); void tryDelayedSyncCb();
void tryInitialSyncCb(); void tryInitialSyncCb();
void newSyncResponse(mtx::responses::Sync res); void newSyncResponse(const mtx::responses::Sync &res);
void leftRoom(const QString &room_id); void leftRoom(const QString &room_id);
void initializeRoomList(QMap<QString, RoomInfo>); void initializeRoomList(QMap<QString, RoomInfo>);
void initializeViews(const mtx::responses::Rooms &rooms); void initializeViews(const mtx::responses::Rooms &rooms);
void initializeEmptyViews(const std::map<QString, mtx::responses::Timeline> &msgs); void initializeEmptyViews(const std::vector<QString> &roomIds);
void initializeMentions(const QMap<QString, mtx::responses::Notifications> &notifs); void initializeMentions(const QMap<QString, mtx::responses::Notifications> &notifs);
void syncUI(const mtx::responses::Rooms &rooms); void syncUI(const mtx::responses::Rooms &rooms);
void syncRoomlist(const std::map<QString, RoomInfo> &updates); void syncRoomlist(const std::map<QString, RoomInfo> &updates);
@ -194,14 +202,11 @@ private slots:
void joinRoom(const QString &room); void joinRoom(const QString &room);
void sendTypingNotifications(); void sendTypingNotifications();
void handleSyncResponse(mtx::responses::Sync res); void handleSyncResponse(const mtx::responses::Sync &res);
private: private:
static ChatPage *instance_; static ChatPage *instance_;
//! Handler callback for initial sync. It doesn't run on the main thread so all
//! communication with the GUI should be done through signals.
void initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err);
void startInitialSync(); void startInitialSync();
void tryInitialSync(); void tryInitialSync();
void trySync(); void trySync();
@ -276,7 +281,7 @@ private:
QSharedPointer<UserSettings> userSettings_; QSharedPointer<UserSettings> userSettings_;
NotificationsManager notificationsManager; NotificationsManager notificationsManager;
CallManager callManager_; CallManager *callManager_;
}; };
template<class Collection> template<class Collection>

View File

@ -5,6 +5,7 @@
#include "Splitter.h" #include "Splitter.h"
#include <mtx/responses/groups.hpp> #include <mtx/responses/groups.hpp>
#include <nlohmann/json.hpp>
#include <QLabel> #include <QLabel>

View File

@ -1,8 +1,10 @@
#include "DeviceVerificationFlow.h" #include "DeviceVerificationFlow.h"
#include "Cache.h" #include "Cache.h"
#include "Cache_p.h"
#include "ChatPage.h" #include "ChatPage.h"
#include "Logging.h" #include "Logging.h"
#include "Utils.h"
#include "timeline/TimelineModel.h" #include "timeline/TimelineModel.h"
#include <QDateTime> #include <QDateTime>
@ -39,7 +41,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
auto user_id = userID.toStdString(); auto user_id = userID.toStdString();
this->toClient = mtx::identifiers::parse<mtx::identifiers::User>(user_id); this->toClient = mtx::identifiers::parse<mtx::identifiers::User>(user_id);
ChatPage::instance()->query_keys( cache::client()->query_keys(
user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) { user_id, [user_id, this](const UserKeyCache &res, mtx::http::RequestErr err) {
if (err) { if (err) {
nhlog::net()->warn("failed to query device keys: {},{}", nhlog::net()->warn("failed to query device keys: {},{}",
@ -57,7 +59,7 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *,
this->their_keys = res; this->their_keys = res;
}); });
ChatPage::instance()->query_keys( cache::client()->query_keys(
http::client()->user_id().to_string(), http::client()->user_id().to_string(),
[this](const UserKeyCache &res, mtx::http::RequestErr err) { [this](const UserKeyCache &res, mtx::http::RequestErr err) {
if (err) { if (err) {

View File

@ -3,6 +3,7 @@
#include <QObject> #include <QObject>
#include <mtx/responses/crypto.hpp> #include <mtx/responses/crypto.hpp>
#include <nlohmann/json.hpp>
#include "CacheCryptoStructs.h" #include "CacheCryptoStructs.h"
#include "Logging.h" #include "Logging.h"

View File

@ -1,5 +1,7 @@
#include "EventAccessors.h" #include "EventAccessors.h"
#include <nlohmann/json.hpp>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <type_traits> #include <type_traits>

View File

@ -23,6 +23,7 @@
#include <QShortcut> #include <QShortcut>
#include <mtx/requests.hpp> #include <mtx/requests.hpp>
#include <mtx/responses/login.hpp>
#include "Cache.h" #include "Cache.h"
#include "ChatPage.h" #include "ChatPage.h"
@ -54,8 +55,8 @@
MainWindow *MainWindow::instance_ = nullptr; MainWindow *MainWindow::instance_ = nullptr;
MainWindow::MainWindow(const QString profile, QWidget *parent) MainWindow::MainWindow(const QString profile, QWidget *parent)
: QMainWindow(parent), : QMainWindow(parent)
profile_{ profile } , profile_{profile}
{ {
setWindowTitle(0); setWindowTitle(0);
setObjectName("MainWindow"); setObjectName("MainWindow");
@ -104,8 +105,7 @@ MainWindow::MainWindow(const QString profile, QWidget *parent)
connect(chat_page_, &ChatPage::closing, this, &MainWindow::showWelcomePage); connect(chat_page_, &ChatPage::closing, this, &MainWindow::showWelcomePage);
connect( connect(
chat_page_, &ChatPage::showOverlayProgressBar, this, &MainWindow::showOverlayProgressBar); chat_page_, &ChatPage::showOverlayProgressBar, this, &MainWindow::showOverlayProgressBar);
connect( connect(chat_page_, &ChatPage::unreadMessages, this, &MainWindow::setWindowTitle);
chat_page_, &ChatPage::unreadMessages, this, &MainWindow::setWindowTitle);
connect(chat_page_, SIGNAL(unreadMessages(int)), trayIcon_, SLOT(setUnreadCount(int))); connect(chat_page_, SIGNAL(unreadMessages(int)), trayIcon_, SLOT(setUnreadCount(int)));
connect(chat_page_, &ChatPage::showLoginPage, this, [this](const QString &msg) { connect(chat_page_, &ChatPage::showLoginPage, this, [this](const QString &msg) {
login_page_->loginError(msg); login_page_->loginError(msg);
@ -185,8 +185,7 @@ MainWindow::setWindowTitle(int notificationCount)
QString name = "nheko"; QString name = "nheko";
if (!profile_.isEmpty()) if (!profile_.isEmpty())
name += " | " + profile_; name += " | " + profile_;
if (notificationCount > 0) if (notificationCount > 0) {
{
name.append(QString{" (%1)"}.arg(notificationCount)); name.append(QString{" (%1)"}.arg(notificationCount));
} }
QMainWindow::setWindowTitle(name); QMainWindow::setWindowTitle(name);

View File

@ -1,5 +1,7 @@
#include "MxcImageProvider.h" #include "MxcImageProvider.h"
#include <mtxclient/crypto/client.hpp>
#include "Cache.h" #include "Cache.h"
#include "Logging.h" #include "Logging.h"
#include "MatrixClient.h" #include "MatrixClient.h"

View File

@ -1,6 +1,7 @@
#include "Olm.h" #include "Olm.h"
#include <QObject> #include <QObject>
#include <nlohmann/json.hpp>
#include <variant> #include <variant>
#include "Cache.h" #include "Cache.h"
@ -20,6 +21,21 @@ auto client_ = std::make_unique<mtx::crypto::OlmClient>();
} }
namespace olm { namespace olm {
void
from_json(const nlohmann::json &obj, OlmMessage &msg)
{
if (obj.at("type") != "m.room.encrypted")
throw std::invalid_argument("invalid type for olm message");
if (obj.at("content").at("algorithm") != OLM_ALGO)
throw std::invalid_argument("invalid algorithm for olm message");
msg.sender = obj.at("sender");
msg.sender_key = obj.at("content").at("sender_key");
msg.ciphertext = obj.at("content")
.at("ciphertext")
.get<std::map<std::string, mtx::events::msg::OlmCipherContent>>();
}
mtx::crypto::OlmClient * mtx::crypto::OlmClient *
client() client()
@ -419,8 +435,8 @@ send_key_request_for(mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> e,
e.content.session_id); e.content.session_id);
mtx::events::msg::KeyRequest request; mtx::events::msg::KeyRequest request;
request.action = !cancel ? mtx::events::msg::RequestAction::Request request.action = !cancel ? mtx::events::msg::RequestAction::Request
: mtx::events::msg::RequestAction::Cancellation; : mtx::events::msg::RequestAction::Cancellation;
request.algorithm = MEGOLM_ALGO; request.algorithm = MEGOLM_ALGO;
request.room_id = e.room_id; request.room_id = e.room_id;
request.sender_key = e.content.sender_key; request.sender_key = e.content.sender_key;

View File

@ -40,21 +40,8 @@ struct OlmMessage
std::map<RecipientKey, mtx::events::msg::OlmCipherContent> ciphertext; std::map<RecipientKey, mtx::events::msg::OlmCipherContent> ciphertext;
}; };
inline void void
from_json(const nlohmann::json &obj, OlmMessage &msg) from_json(const nlohmann::json &obj, OlmMessage &msg);
{
if (obj.at("type") != "m.room.encrypted")
throw std::invalid_argument("invalid type for olm message");
if (obj.at("content").at("algorithm") != OLM_ALGO)
throw std::invalid_argument("invalid algorithm for olm message");
msg.sender = obj.at("sender");
msg.sender_key = obj.at("content").at("sender_key");
msg.ciphertext = obj.at("content")
.at("ciphertext")
.get<std::map<std::string, mtx::events::msg::OlmCipherContent>>();
}
mtx::crypto::OlmClient * mtx::crypto::OlmClient *
client(); client();

View File

@ -22,7 +22,7 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QWidget> #include <QWidget>
#include <mtx/responses.hpp> #include <mtx/responses/sync.hpp>
#include "CacheStructs.h" #include "CacheStructs.h"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"

View File

@ -453,8 +453,8 @@ FilteredTextEdit::completerRect()
auto item_height = completer_->popup()->sizeHintForRow(0); auto item_height = completer_->popup()->sizeHintForRow(0);
auto max_height = item_height * completer_->maxVisibleItems(); auto max_height = item_height * completer_->maxVisibleItems();
auto height = (completer_->completionCount() > completer_->maxVisibleItems()) auto height = (completer_->completionCount() > completer_->maxVisibleItems())
? max_height ? max_height
: completer_->completionCount() * item_height; : completer_->completionCount() * item_height;
rect.setWidth(completer_->popup()->sizeHintForColumn(0)); rect.setWidth(completer_->popup()->sizeHintForColumn(0));
rect.moveBottom(-height); rect.moveBottom(-height);
return rect; return rect;

View File

@ -17,6 +17,7 @@
#include <QApplication> #include <QApplication>
#include <QComboBox> #include <QComboBox>
#include <QCoreApplication>
#include <QFileDialog> #include <QFileDialog>
#include <QFontComboBox> #include <QFontComboBox>
#include <QFormLayout> #include <QFormLayout>
@ -36,7 +37,6 @@
#include <QString> #include <QString>
#include <QTextStream> #include <QTextStream>
#include <QtQml> #include <QtQml>
#include <QCoreApplication>
#include "Cache.h" #include "Cache.h"
#include "Config.h" #include "Config.h"
@ -450,7 +450,8 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
auto versionInfo = new QLabel(QString("%1 | %2").arg(nheko::version).arg(nheko::build_os)); auto versionInfo = new QLabel(QString("%1 | %2").arg(nheko::version).arg(nheko::build_os));
if (QCoreApplication::applicationName() != "nheko") if (QCoreApplication::applicationName() != "nheko")
versionInfo->setText(versionInfo->text() + " | " + tr("profile: %1").arg(QCoreApplication::applicationName())); versionInfo->setText(versionInfo->text() + " | " +
tr("profile: %1").arg(QCoreApplication::applicationName()));
versionInfo->setTextInteractionFlags(Qt::TextBrowserInteraction); versionInfo->setTextInteractionFlags(Qt::TextBrowserInteraction);
topBarLayout_ = new QHBoxLayout; topBarLayout_ = new QHBoxLayout;
@ -904,11 +905,7 @@ UserSettingsPage::importSessionKeys()
auto sessions = auto sessions =
mtx::crypto::decrypt_exported_sessions(payload, password.toStdString()); mtx::crypto::decrypt_exported_sessions(payload, password.toStdString());
cache::importSessionKeys(std::move(sessions)); cache::importSessionKeys(std::move(sessions));
} catch (const mtx::crypto::sodium_exception &e) { } catch (const std::exception &e) {
QMessageBox::warning(this, tr("Error"), e.what());
} catch (const lmdb::error &e) {
QMessageBox::warning(this, tr("Error"), e.what());
} catch (const nlohmann::json::exception &e) {
QMessageBox::warning(this, tr("Error"), e.what()); QMessageBox::warning(this, tr("Error"), e.what());
} }
} }
@ -956,11 +953,7 @@ UserSettingsPage::exportSessionKeys()
QTextStream out(&file); QTextStream out(&file);
out << prefix << newline << b64 << newline << suffix; out << prefix << newline << b64 << newline << suffix;
file.close(); file.close();
} catch (const mtx::crypto::sodium_exception &e) { } catch (const std::exception &e) {
QMessageBox::warning(this, tr("Error"), e.what());
} catch (const lmdb::error &e) {
QMessageBox::warning(this, tr("Error"), e.what());
} catch (const nlohmann::json::exception &e) {
QMessageBox::warning(this, tr("Error"), e.what()); QMessageBox::warning(this, tr("Error"), e.what());
} }
} }

View File

@ -638,7 +638,7 @@ utils::luminance(const QColor &col)
qreal lumRgb[3]; qreal lumRgb[3];
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
qreal v = colRgb[i] / 255.0; qreal v = colRgb[i] / 255.0;
v <= 0.03928 ? lumRgb[i] = v / 12.92 : lumRgb[i] = qPow((v + 0.055) / 1.055, 2.4); v <= 0.03928 ? lumRgb[i] = v / 12.92 : lumRgb[i] = qPow((v + 0.055) / 1.055, 2.4);
} }

View File

@ -17,6 +17,8 @@
#include <QVBoxLayout> #include <QVBoxLayout>
#include "dialogs/RoomSettings.h" #include "dialogs/RoomSettings.h"
#include <mtx/responses/common.hpp>
#include <mtx/responses/media.hpp>
#include "Cache.h" #include "Cache.h"
#include "ChatPage.h" #include "ChatPage.h"

View File

@ -107,33 +107,28 @@ main(int argc, char *argv[])
// needed for settings so need to register before any settings are read to prevent warnings // needed for settings so need to register before any settings are read to prevent warnings
qRegisterMetaType<UserSettings::Presence>(); qRegisterMetaType<UserSettings::Presence>();
// This is some hacky programming, but it's necessary (AFAIK?) to get the unique config name parsed // This is some hacky programming, but it's necessary (AFAIK?) to get the unique config name
// before the app name is set. // parsed before the app name is set.
QString appName{"nheko"}; QString appName{"nheko"};
for (int i = 0; i < argc; ++i) for (int i = 0; i < argc; ++i) {
{ if (QString{argv[i]}.startsWith("--profile=")) {
if (QString{argv[i]}.startsWith("--profile="))
{
QString q{argv[i]}; QString q{argv[i]};
q.remove("--profile="); q.remove("--profile=");
appName += "-" + q; appName += "-" + q;
} } else if (QString{argv[i]}.startsWith("--p=")) {
else if (QString{argv[i]}.startsWith("--p="))
{
QString q{argv[i]}; QString q{argv[i]};
q.remove("-p="); q.remove("-p=");
appName += "-" + q; appName += "-" + q;
} } else if (QString{argv[i]} == "--profile" || QString{argv[i]} == "-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
{ // left to process as the name
if (i < argc -1) // if i is less than argc - 1, we still have a parameter left to process as the name
{ {
++i; // the next arg is the name, so increment ++i; // the next arg is the name, so increment
appName += "-" + QString {argv[i]}; appName += "-" + QString{argv[i]};
} }
} }
} }
QCoreApplication::setApplicationName(appName); QCoreApplication::setApplicationName(appName);
QCoreApplication::setApplicationVersion(nheko::version); QCoreApplication::setApplicationVersion(nheko::version);
QCoreApplication::setOrganizationName("nheko"); QCoreApplication::setOrganizationName("nheko");
@ -168,11 +163,15 @@ main(int argc, char *argv[])
// This option is not actually parsed via Qt due to the need to parse it before the app // This option is not actually parsed via Qt due to the need to parse it before the app
// name is set. It only exists to keep Qt from complaining about the --profile/-p // name is set. It only exists to keep Qt from complaining about the --profile/-p
// option and thereby crashing the app. // option and thereby crashing the app.
QCommandLineOption configName(QStringList() << "p" << "profile", QCommandLineOption configName(
QCoreApplication::tr("Create a unique profile, which allows you to log into several accounts at the same time and start multiple instances of nheko."), QStringList() << "p"
QCoreApplication::tr("profile"), QCoreApplication::tr("profile name")); << "profile",
QCoreApplication::tr("Create a unique profile, which allows you to log into several "
"accounts at the same time and start multiple instances of nheko."),
QCoreApplication::tr("profile"),
QCoreApplication::tr("profile name"));
parser.addOption(configName); parser.addOption(configName);
parser.process(app); parser.process(app);
app.setWindowIcon(QIcon(":/logos/nheko.png")); app.setWindowIcon(QIcon(":/logos/nheko.png"));
@ -217,7 +216,7 @@ main(int argc, char *argv[])
appTranslator.load(QLocale(), "nheko", "_", ":/translations"); appTranslator.load(QLocale(), "nheko", "_", ":/translations");
app.installTranslator(&appTranslator); app.installTranslator(&appTranslator);
MainWindow w{ (appName == "nheko" ? "" : appName.remove("nheko-")) }; MainWindow w{(appName == "nheko" ? "" : appName.remove("nheko-"))};
// Move the MainWindow to the center // Move the MainWindow to the center
w.move(screenCenter(w.width(), w.height())); w.move(screenCenter(w.width(), w.height()));

View File

@ -19,6 +19,12 @@ SuggestionsPopup::SuggestionsPopup(QWidget *parent)
layout_->setSpacing(0); layout_->setSpacing(0);
} }
QString
SuggestionsPopup::displayName(QString room, QString user)
{
return cache::displayName(room, user);
}
void void
SuggestionsPopup::addRooms(const std::vector<RoomSearchResult> &rooms) SuggestionsPopup::addRooms(const std::vector<RoomSearchResult> &rooms)
{ {

View File

@ -22,7 +22,7 @@ public:
const auto &widget = qobject_cast<Item *>(item->widget()); const auto &widget = qobject_cast<Item *>(item->widget());
emit itemSelected( emit itemSelected(
cache::displayName(ChatPage::instance()->currentRoom(), widget->selectedText())); displayName(ChatPage::instance()->currentRoom(), widget->selectedText()));
resetSelection(); resetSelection();
} }
@ -47,6 +47,7 @@ signals:
void itemSelected(const QString &user); void itemSelected(const QString &user);
private: private:
QString displayName(QString roomid, QString userid);
void hoverSelection(); void hoverSelection();
void resetSelection() { selectedItem_ = -1; } void resetSelection() { selectedItem_ = -1; }
void selectFirstItem() { selectedItem_ = 0; } void selectFirstItem() { selectedItem_ = 0; }

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <mtx/responses.hpp> #include <mtx/responses/notifications.hpp>
#include <QMap> #include <QMap>
#include <QString> #include <QString>

View File

@ -3,6 +3,8 @@
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <mtx/responses/common.hpp>
#include "Cache.h" #include "Cache.h"
#include "Cache_p.h" #include "Cache_p.h"
#include "ChatPage.h" #include "ChatPage.h"
@ -10,6 +12,7 @@
#include "Logging.h" #include "Logging.h"
#include "MatrixClient.h" #include "MatrixClient.h"
#include "Olm.h" #include "Olm.h"
#include "Utils.h"
Q_DECLARE_METATYPE(Reaction) Q_DECLARE_METATYPE(Reaction)

View File

@ -11,6 +11,7 @@
#include "ChatPage.h" #include "ChatPage.h"
#include "ColorImageProvider.h" #include "ColorImageProvider.h"
#include "DelegateChooser.h" #include "DelegateChooser.h"
#include "DeviceVerificationFlow.h"
#include "Logging.h" #include "Logging.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "MatrixClient.h" #include "MatrixClient.h"
@ -450,13 +451,10 @@ TimelineViewManager::receivedSessionKey(const std::string &room_id, const std::s
} }
void void
TimelineViewManager::initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs) TimelineViewManager::initWithMessages(const std::vector<QString> &roomIds)
{ {
for (const auto &e : msgs) { for (const auto &roomId : roomIds)
addRoom(e.first); addRoom(roomId);
models.value(e.first)->addEvents(e.second);
}
} }
void void

View File

@ -7,11 +7,11 @@
#include <QWidget> #include <QWidget>
#include <mtx/common.hpp> #include <mtx/common.hpp>
#include <mtx/responses.hpp> #include <mtx/responses/messages.hpp>
#include <mtx/responses/sync.hpp>
#include "Cache.h" #include "Cache.h"
#include "CallManager.h" #include "CallManager.h"
#include "DeviceVerificationFlow.h"
#include "Logging.h" #include "Logging.h"
#include "TimelineModel.h" #include "TimelineModel.h"
#include "Utils.h" #include "Utils.h"
@ -24,6 +24,7 @@ class BlurhashProvider;
class ColorImageProvider; class ColorImageProvider;
class UserSettings; class UserSettings;
class ChatPage; class ChatPage;
class DeviceVerificationFlow;
class TimelineViewManager : public QObject class TimelineViewManager : public QObject
{ {
@ -93,7 +94,7 @@ signals:
public slots: public slots:
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids); void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
void receivedSessionKey(const std::string &room_id, const std::string &session_id); void receivedSessionKey(const std::string &room_id, const std::string &session_id);
void initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs); void initWithMessages(const std::vector<QString> &roomIds);
void setHistoryView(const QString &room_id); void setHistoryView(const QString &room_id);
void updateColorPalette(); void updateColorPalette();

View File

@ -117,7 +117,7 @@ UserProfile::fetchDeviceList(const QString &userID)
{ {
auto localUser = utils::localUser(); auto localUser = utils::localUser();
ChatPage::instance()->query_keys( cache::client()->query_keys(
userID.toStdString(), userID.toStdString(),
[other_user_id = userID.toStdString(), this](const UserKeyCache &other_user_keys, [other_user_id = userID.toStdString(), this](const UserKeyCache &other_user_keys,
mtx::http::RequestErr err) { mtx::http::RequestErr err) {
@ -129,7 +129,7 @@ UserProfile::fetchDeviceList(const QString &userID)
} }
// Finding if the User is Verified or not based on the Signatures // Finding if the User is Verified or not based on the Signatures
ChatPage::instance()->query_keys( cache::client()->query_keys(
utils::localUser().toStdString(), utils::localUser().toStdString(),
[other_user_id, other_user_keys, this](const UserKeyCache &res, [other_user_id, other_user_keys, this](const UserKeyCache &res,
mtx::http::RequestErr err) { mtx::http::RequestErr err) {

View File

@ -5,8 +5,6 @@
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include "MatrixClient.h"
namespace verification { namespace verification {
Q_NAMESPACE Q_NAMESPACE
@ -116,8 +114,4 @@ private:
bool isUserVerified = false; bool isUserVerified = false;
TimelineViewManager *manager; TimelineViewManager *manager;
TimelineModel *model; TimelineModel *model;
void callback_fn(const mtx::responses::QueryKeys &res,
mtx::http::RequestErr err,
std::string user_id);
}; };