mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-23 03:18:49 +03:00
[WIP] Room-Verification Messages
This commit is contained in:
parent
3635c185e9
commit
2e20049b36
15 changed files with 392 additions and 484 deletions
|
@ -35,13 +35,8 @@ Flow {
|
||||||
ToolTip.text: modelData.users
|
ToolTip.text: modelData.users
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
<<<<<<< HEAD
|
|
||||||
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent)
|
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent)
|
||||||
timelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key)
|
TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key)
|
||||||
=======
|
|
||||||
console.debug("Picked " + model.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + model.selfReactedEvent)
|
|
||||||
TimelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, model.key, model.selfReactedEvent)
|
|
||||||
>>>>>>> Fix presence indicator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ Item {
|
||||||
// fancy reply, if this is a reply
|
// fancy reply, if this is a reply
|
||||||
Reply {
|
Reply {
|
||||||
visible: model.replyTo
|
visible: model.replyTo
|
||||||
modelData: chat.model.getDump(model.replyTo)
|
modelData: chat.model.getDump(model.replyTo,model.id)
|
||||||
userColor: TimelineManager.userColor(modelData.userId, colors.window)
|
userColor: TimelineManager.userColor(modelData.userId, colors.window)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -388,13 +388,8 @@ Page {
|
||||||
anchors.rightMargin: 20
|
anchors.rightMargin: 20
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : {}
|
modelData: chat.model ? chat.model.getDump(chat.model.reply, chat.model.id) : {}
|
||||||
userColor: timelineManager.userColor(modelData.userId, colors.window)
|
|
||||||
=======
|
|
||||||
modelData: chat.model ? chat.model.getDump(chat.model.reply) : {}
|
|
||||||
userColor: TimelineManager.userColor(modelData.userId, colors.window)
|
userColor: TimelineManager.userColor(modelData.userId, colors.window)
|
||||||
>>>>>>> Fix presence indicator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageButton {
|
ImageButton {
|
||||||
|
|
|
@ -90,7 +90,12 @@ ApplicationWindow{
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
profile.verifyUser();
|
var newFlow = profile.createFlow(true);
|
||||||
|
newFlow.userId = profile.userid;
|
||||||
|
newFlow.sender = true;
|
||||||
|
deviceVerificationList.add(newFlow.tranId);
|
||||||
|
var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest: true});
|
||||||
|
dialog.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,14 +197,16 @@ ApplicationWindow{
|
||||||
id: verifyButton
|
id: verifyButton
|
||||||
text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
|
text:(model.verificationStatus != VerificationStatus.VERIFIED)?"Verify":"Unverify"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var newFlow = deviceVerificationFlow.createObject(userProfileDialog,
|
var newFlow = profile.createFlow(false);
|
||||||
{userId : profile.userid, sender: true, deviceId : model.deviceId});
|
newFlow.userId = profile.userid;
|
||||||
|
newFlow.sender = true;
|
||||||
|
newFlow.deviceId = model.deviceId;
|
||||||
if(model.verificationStatus == VerificationStatus.VERIFIED){
|
if(model.verificationStatus == VerificationStatus.VERIFIED){
|
||||||
newFlow.unverify();
|
newFlow.unverify();
|
||||||
deviceVerificationList.updateProfile(newFlow.userId);
|
deviceVerificationList.updateProfile(newFlow.userId);
|
||||||
}else{
|
}else{
|
||||||
deviceVerificationList.add(newFlow.tranId);
|
deviceVerificationList.add(newFlow.tranId);
|
||||||
var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow});
|
var dialog = deviceVerificationDialog.createObject(userProfileDialog, {flow: newFlow,isRequest:false});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,9 @@ ApplicationWindow {
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
onClicked: { stack.replace(awaitingVerificationRequestAccept); flow.startVerificationRequest(); }
|
onClicked: {
|
||||||
|
stack.replace(awaitingVerificationRequestAccept);
|
||||||
|
isRequest?flow.sendVerificationRequest():flow.startVerificationRequest(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -562,12 +562,11 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
connect(
|
connect(
|
||||||
this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection);
|
this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection);
|
||||||
connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync, Qt::QueuedConnection);
|
connect(this, &ChatPage::trySyncCb, this, &ChatPage::trySync, Qt::QueuedConnection);
|
||||||
connect(
|
connect(this,
|
||||||
this,
|
&ChatPage::tryDelayedSyncCb,
|
||||||
&ChatPage::tryDelayedSyncCb,
|
this,
|
||||||
this,
|
[this]() { QTimer::singleShot(RETRY_TIMEOUT, this, &ChatPage::trySync); },
|
||||||
[this]() { QTimer::singleShot(RETRY_TIMEOUT, this, &ChatPage::trySync); },
|
Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
connect(this,
|
connect(this,
|
||||||
&ChatPage::newSyncResponse,
|
&ChatPage::newSyncResponse,
|
||||||
|
|
|
@ -22,6 +22,11 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow
|
||||||
this->sas = olm::client()->sas_init();
|
this->sas = olm::client()->sas_init();
|
||||||
this->isMacVerified = false;
|
this->isMacVerified = false;
|
||||||
|
|
||||||
|
connect(this->model_,
|
||||||
|
&TimelineModel::updateFlowEventId,
|
||||||
|
this,
|
||||||
|
[this](std::string event_id) { this->relation.in_reply_to.event_id = event_id; });
|
||||||
|
|
||||||
connect(timeout, &QTimer::timeout, this, [this]() {
|
connect(timeout, &QTimer::timeout, this, [this]() {
|
||||||
emit timedout();
|
emit timedout();
|
||||||
this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
|
this->cancelVerification(DeviceVerificationFlow::Error::Timeout);
|
||||||
|
@ -222,6 +227,9 @@ DeviceVerificationFlow::DeviceVerificationFlow(QObject *, DeviceVerificationFlow
|
||||||
if (msg.transaction_id.value() != this->transaction_id)
|
if (msg.transaction_id.value() != this->transaction_id)
|
||||||
return;
|
return;
|
||||||
} else if (msg.relates_to.has_value()) {
|
} else if (msg.relates_to.has_value()) {
|
||||||
|
// this is just a workaround
|
||||||
|
this->relation.in_reply_to.event_id =
|
||||||
|
msg.relates_to.value().in_reply_to.event_id;
|
||||||
if (msg.relates_to.value().in_reply_to.event_id !=
|
if (msg.relates_to.value().in_reply_to.event_id !=
|
||||||
this->relation.in_reply_to.event_id)
|
this->relation.in_reply_to.event_id)
|
||||||
return;
|
return;
|
||||||
|
@ -343,11 +351,8 @@ DeviceVerificationFlow::setType(Type type)
|
||||||
void
|
void
|
||||||
DeviceVerificationFlow::setSender(bool sender_)
|
DeviceVerificationFlow::setSender(bool sender_)
|
||||||
{
|
{
|
||||||
this->sender = sender_;
|
this->sender = sender_;
|
||||||
if (this->sender == true && this->type == DeviceVerificationFlow::Type::ToDevice)
|
this->transaction_id = http::client()->generate_txn_id();
|
||||||
this->transaction_id = http::client()->generate_txn_id();
|
|
||||||
else if (this->sender == true && this->type == DeviceVerificationFlow::Type::RoomMsg)
|
|
||||||
this->relation.in_reply_to.event_id = http::client()->generate_txn_id();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -380,19 +385,16 @@ DeviceVerificationFlow::acceptVerificationRequest()
|
||||||
|
|
||||||
body[this->toClient][this->deviceId.toStdString()] = req;
|
body[this->toClient][this->deviceId.toStdString()] = req;
|
||||||
|
|
||||||
http::client()
|
http::client()->send_to_device<mtx::events::msg::KeyVerificationAccept>(
|
||||||
->send_to_device<mtx::events::msg::KeyVerificationAccept,
|
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
||||||
mtx::events::EventType::KeyVerificationAccept>(
|
if (err)
|
||||||
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
nhlog::net()->warn("failed to accept verification request: {} {}",
|
||||||
if (err)
|
err->matrix_error.error,
|
||||||
nhlog::net()->warn(
|
static_cast<int>(err->status_code));
|
||||||
"failed to accept verification request: {} {}",
|
});
|
||||||
err->matrix_error.error,
|
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
|
||||||
static_cast<int>(err->status_code));
|
|
||||||
});
|
|
||||||
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
|
|
||||||
req.relates_to = this->relation;
|
req.relates_to = this->relation;
|
||||||
(model_.value())->sendMessage(req);
|
(model_)->sendMessage(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//! responds verification request
|
//! responds verification request
|
||||||
|
@ -410,18 +412,16 @@ DeviceVerificationFlow::sendVerificationReady()
|
||||||
|
|
||||||
body[this->toClient][this->deviceId.toStdString()] = req;
|
body[this->toClient][this->deviceId.toStdString()] = req;
|
||||||
|
|
||||||
http::client()
|
http::client()->send_to_device<mtx::events::msg::KeyVerificationReady>(
|
||||||
->send_to_device<mtx::events::msg::KeyVerificationReady,
|
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
||||||
mtx::events::EventType::KeyVerificationReady>(
|
if (err)
|
||||||
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
nhlog::net()->warn("failed to send verification ready: {} {}",
|
||||||
if (err)
|
err->matrix_error.error,
|
||||||
nhlog::net()->warn("failed to send verification ready: {} {}",
|
static_cast<int>(err->status_code));
|
||||||
err->matrix_error.error,
|
});
|
||||||
static_cast<int>(err->status_code));
|
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
|
||||||
});
|
|
||||||
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
|
|
||||||
req.relates_to = this->relation;
|
req.relates_to = this->relation;
|
||||||
(model_.value())->sendMessage(req);
|
(model_)->sendMessage(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//! accepts a verification
|
//! accepts a verification
|
||||||
|
@ -436,18 +436,16 @@ DeviceVerificationFlow::sendVerificationDone()
|
||||||
|
|
||||||
body[this->toClient][this->deviceId.toStdString()] = req;
|
body[this->toClient][this->deviceId.toStdString()] = req;
|
||||||
|
|
||||||
http::client()
|
http::client()->send_to_device<mtx::events::msg::KeyVerificationDone>(
|
||||||
->send_to_device<mtx::events::msg::KeyVerificationDone,
|
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
||||||
mtx::events::EventType::KeyVerificationDone>(
|
if (err)
|
||||||
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
nhlog::net()->warn("failed to send verification done: {} {}",
|
||||||
if (err)
|
err->matrix_error.error,
|
||||||
nhlog::net()->warn("failed to send verification done: {} {}",
|
static_cast<int>(err->status_code));
|
||||||
err->matrix_error.error,
|
});
|
||||||
static_cast<int>(err->status_code));
|
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
|
||||||
});
|
|
||||||
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
|
|
||||||
req.relates_to = this->relation;
|
req.relates_to = this->relation;
|
||||||
(model_.value())->sendMessage(req);
|
(model_)->sendMessage(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//! starts the verification flow
|
//! starts the verification flow
|
||||||
|
@ -470,19 +468,16 @@ DeviceVerificationFlow::startVerificationRequest()
|
||||||
this->canonical_json = nlohmann::json(req);
|
this->canonical_json = nlohmann::json(req);
|
||||||
body[this->toClient][this->deviceId.toStdString()] = req;
|
body[this->toClient][this->deviceId.toStdString()] = req;
|
||||||
|
|
||||||
http::client()
|
http::client()->send_to_device<mtx::events::msg::KeyVerificationStart>(
|
||||||
->send_to_device<mtx::events::msg::KeyVerificationStart,
|
this->transaction_id, body, [body](mtx::http::RequestErr err) {
|
||||||
mtx::events::EventType::KeyVerificationStart>(
|
if (err)
|
||||||
this->transaction_id, body, [body](mtx::http::RequestErr err) {
|
nhlog::net()->warn("failed to start verification request: {} {}",
|
||||||
if (err)
|
err->matrix_error.error,
|
||||||
nhlog::net()->warn(
|
static_cast<int>(err->status_code));
|
||||||
"failed to start verification request: {} {}",
|
});
|
||||||
err->matrix_error.error,
|
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
|
||||||
static_cast<int>(err->status_code));
|
|
||||||
});
|
|
||||||
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
|
|
||||||
req.relates_to = this->relation;
|
req.relates_to = this->relation;
|
||||||
(model_.value())->sendMessage(req);
|
(model_)->sendMessage(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//! sends a verification request
|
//! sends a verification request
|
||||||
|
@ -505,17 +500,20 @@ DeviceVerificationFlow::sendVerificationRequest()
|
||||||
|
|
||||||
body[this->toClient][this->deviceId.toStdString()] = req;
|
body[this->toClient][this->deviceId.toStdString()] = req;
|
||||||
|
|
||||||
http::client()
|
http::client()->send_to_device<mtx::events::msg::KeyVerificationRequest>(
|
||||||
->send_to_device<mtx::events::msg::KeyVerificationRequest,
|
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
||||||
mtx::events::EventType::KeyVerificationRequest>(
|
if (err)
|
||||||
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
nhlog::net()->warn("failed to send verification request: {} {}",
|
||||||
if (err)
|
err->matrix_error.error,
|
||||||
nhlog::net()->warn("failed to send verification request: {} {}",
|
static_cast<int>(err->status_code));
|
||||||
err->matrix_error.error,
|
});
|
||||||
static_cast<int>(err->status_code));
|
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
|
||||||
});
|
req.to = this->userId.toStdString();
|
||||||
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
|
req.msgtype = "m.key.verification.request";
|
||||||
(model_.value())->sendMessage(req);
|
req.body = "User is requesting to verify keys with you. However, your client does "
|
||||||
|
"not support this method, so you will need to use the legacy method of "
|
||||||
|
"key verification.";
|
||||||
|
(model_)->sendMessage(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//! cancels a verification flow
|
//! cancels a verification flow
|
||||||
|
@ -552,21 +550,18 @@ DeviceVerificationFlow::cancelVerification(DeviceVerificationFlow::Error error_c
|
||||||
|
|
||||||
body[this->toClient][deviceId.toStdString()] = req;
|
body[this->toClient][deviceId.toStdString()] = req;
|
||||||
|
|
||||||
http::client()
|
http::client()->send_to_device<mtx::events::msg::KeyVerificationCancel>(
|
||||||
->send_to_device<mtx::events::msg::KeyVerificationCancel,
|
this->transaction_id, body, [this](mtx::http::RequestErr err) {
|
||||||
mtx::events::EventType::KeyVerificationCancel>(
|
if (err)
|
||||||
this->transaction_id, body, [this](mtx::http::RequestErr err) {
|
nhlog::net()->warn("failed to cancel verification request: {} {}",
|
||||||
if (err)
|
err->matrix_error.error,
|
||||||
nhlog::net()->warn(
|
static_cast<int>(err->status_code));
|
||||||
"failed to cancel verification request: {} {}",
|
|
||||||
err->matrix_error.error,
|
|
||||||
static_cast<int>(err->status_code));
|
|
||||||
|
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
});
|
});
|
||||||
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
|
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
|
||||||
req.relates_to = this->relation;
|
req.relates_to = this->relation;
|
||||||
(model_.value())->sendMessage(req);
|
(model_)->sendMessage(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : Handle Blocking user better
|
// TODO : Handle Blocking user better
|
||||||
|
@ -595,18 +590,16 @@ DeviceVerificationFlow::sendVerificationKey()
|
||||||
|
|
||||||
body[this->toClient][deviceId.toStdString()] = req;
|
body[this->toClient][deviceId.toStdString()] = req;
|
||||||
|
|
||||||
http::client()
|
http::client()->send_to_device<mtx::events::msg::KeyVerificationKey>(
|
||||||
->send_to_device<mtx::events::msg::KeyVerificationKey,
|
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
||||||
mtx::events::EventType::KeyVerificationKey>(
|
if (err)
|
||||||
this->transaction_id, body, [](mtx::http::RequestErr err) {
|
nhlog::net()->warn("failed to send verification key: {} {}",
|
||||||
if (err)
|
err->matrix_error.error,
|
||||||
nhlog::net()->warn("failed to send verification key: {} {}",
|
static_cast<int>(err->status_code));
|
||||||
err->matrix_error.error,
|
});
|
||||||
static_cast<int>(err->status_code));
|
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
|
||||||
});
|
|
||||||
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
|
|
||||||
req.relates_to = this->relation;
|
req.relates_to = this->relation;
|
||||||
(model_.value())->sendMessage(req);
|
(model_)->sendMessage(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//! sends the mac of the keys
|
//! sends the mac of the keys
|
||||||
|
@ -639,23 +632,21 @@ DeviceVerificationFlow::sendVerificationMac()
|
||||||
req.transaction_id = this->transaction_id;
|
req.transaction_id = this->transaction_id;
|
||||||
body[this->toClient][deviceId.toStdString()] = req;
|
body[this->toClient][deviceId.toStdString()] = req;
|
||||||
|
|
||||||
http::client()
|
http::client()->send_to_device<mtx::events::msg::KeyVerificationMac>(
|
||||||
->send_to_device<mtx::events::msg::KeyVerificationMac,
|
this->transaction_id, body, [this](mtx::http::RequestErr err) {
|
||||||
mtx::events::EventType::KeyVerificationMac>(
|
if (err)
|
||||||
this->transaction_id, body, [this](mtx::http::RequestErr err) {
|
nhlog::net()->warn("failed to send verification MAC: {} {}",
|
||||||
if (err)
|
err->matrix_error.error,
|
||||||
nhlog::net()->warn("failed to send verification MAC: {} {}",
|
static_cast<int>(err->status_code));
|
||||||
err->matrix_error.error,
|
|
||||||
static_cast<int>(err->status_code));
|
|
||||||
|
|
||||||
if (this->isMacVerified == true)
|
if (this->isMacVerified == true)
|
||||||
this->acceptDevice();
|
this->acceptDevice();
|
||||||
else
|
else
|
||||||
this->isMacVerified = true;
|
this->isMacVerified = true;
|
||||||
});
|
});
|
||||||
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_.has_value()) {
|
} else if (this->type == DeviceVerificationFlow::Type::RoomMsg && model_) {
|
||||||
req.relates_to = this->relation;
|
req.relates_to = this->relation;
|
||||||
(model_.value())->sendMessage(req);
|
(model_)->sendMessage(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//! Completes the verification flow
|
//! Completes the verification flow
|
||||||
|
|
|
@ -126,6 +126,6 @@ private:
|
||||||
// for room messages
|
// for room messages
|
||||||
std::optional<std::string> room_id;
|
std::optional<std::string> room_id;
|
||||||
std::optional<std::string> event_id;
|
std::optional<std::string> event_id;
|
||||||
std::optional<TimelineModel *> model_;
|
TimelineModel *model_;
|
||||||
mtx::common::ReplyRelatesTo relation;
|
mtx::common::ReplyRelatesTo relation;
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,8 +37,15 @@ struct EventMsgType
|
||||||
template<class T>
|
template<class T>
|
||||||
mtx::events::MessageType operator()(const mtx::events::Event<T> &e)
|
mtx::events::MessageType operator()(const mtx::events::Event<T> &e)
|
||||||
{
|
{
|
||||||
if constexpr (is_detected<msgtype_t, T>::value)
|
if constexpr (is_detected<msgtype_t, T>::value) {
|
||||||
return mtx::events::getMessageType(e.content.msgtype);
|
if constexpr (std::is_same_v<std::optional<std::string>,
|
||||||
|
std::remove_cv_t<decltype(e.content.msgtype)>>)
|
||||||
|
return mtx::events::getMessageType(e.content.msgtype.value());
|
||||||
|
else if constexpr (std::is_same_v<
|
||||||
|
std::string,
|
||||||
|
std::remove_cv_t<decltype(e.content.msgtype)>>)
|
||||||
|
return mtx::events::getMessageType(e.content.msgtype);
|
||||||
|
}
|
||||||
return mtx::events::MessageType::Unknown;
|
return mtx::events::MessageType::Unknown;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
#include "Cache_p.h"
|
#include "Cache_p.h"
|
||||||
|
#include "ChatPage.h"
|
||||||
#include "EventAccessors.h"
|
#include "EventAccessors.h"
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
|
@ -31,41 +32,38 @@ EventStore::EventStore(std::string room_id, QObject *)
|
||||||
this->last = range->last;
|
this->last = range->last;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(
|
connect(this,
|
||||||
this,
|
&EventStore::eventFetched,
|
||||||
&EventStore::eventFetched,
|
this,
|
||||||
this,
|
[this](std::string id,
|
||||||
[this](std::string id,
|
std::string relatedTo,
|
||||||
std::string relatedTo,
|
mtx::events::collections::TimelineEvents timeline) {
|
||||||
mtx::events::collections::TimelineEvents timeline) {
|
cache::client()->storeEvent(room_id_, id, {timeline});
|
||||||
cache::client()->storeEvent(room_id_, id, {timeline});
|
|
||||||
|
|
||||||
if (!relatedTo.empty()) {
|
if (!relatedTo.empty()) {
|
||||||
auto idx = idToIndex(relatedTo);
|
auto idx = idToIndex(relatedTo);
|
||||||
if (idx)
|
if (idx)
|
||||||
emit dataChanged(*idx, *idx);
|
emit dataChanged(*idx, *idx);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
connect(
|
connect(this,
|
||||||
this,
|
&EventStore::oldMessagesRetrieved,
|
||||||
&EventStore::oldMessagesRetrieved,
|
this,
|
||||||
this,
|
[this](const mtx::responses::Messages &res) {
|
||||||
[this](const mtx::responses::Messages &res) {
|
uint64_t newFirst = cache::client()->saveOldMessages(room_id_, res);
|
||||||
//
|
if (newFirst == first)
|
||||||
uint64_t newFirst = cache::client()->saveOldMessages(room_id_, res);
|
fetchMore();
|
||||||
if (newFirst == first)
|
else {
|
||||||
fetchMore();
|
emit beginInsertRows(toExternalIdx(newFirst),
|
||||||
else {
|
toExternalIdx(this->first - 1));
|
||||||
emit beginInsertRows(toExternalIdx(newFirst),
|
this->first = newFirst;
|
||||||
toExternalIdx(this->first - 1));
|
emit endInsertRows();
|
||||||
this->first = newFirst;
|
emit fetchedMore();
|
||||||
emit endInsertRows();
|
}
|
||||||
emit fetchedMore();
|
},
|
||||||
}
|
Qt::QueuedConnection);
|
||||||
},
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
connect(this, &EventStore::processPending, this, [this]() {
|
connect(this, &EventStore::processPending, this, [this]() {
|
||||||
if (!current_txn.empty()) {
|
if (!current_txn.empty()) {
|
||||||
|
@ -116,48 +114,46 @@ EventStore::EventStore(std::string room_id, QObject *)
|
||||||
event->data);
|
event->data);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(
|
connect(this,
|
||||||
this,
|
&EventStore::messageFailed,
|
||||||
&EventStore::messageFailed,
|
this,
|
||||||
this,
|
[this](std::string txn_id) {
|
||||||
[this](std::string txn_id) {
|
if (current_txn == txn_id) {
|
||||||
if (current_txn == txn_id) {
|
current_txn_error_count++;
|
||||||
current_txn_error_count++;
|
if (current_txn_error_count > 10) {
|
||||||
if (current_txn_error_count > 10) {
|
nhlog::ui()->debug("failing txn id '{}'", txn_id);
|
||||||
nhlog::ui()->debug("failing txn id '{}'", txn_id);
|
cache::client()->removePendingStatus(room_id_, txn_id);
|
||||||
cache::client()->removePendingStatus(room_id_, txn_id);
|
current_txn_error_count = 0;
|
||||||
current_txn_error_count = 0;
|
}
|
||||||
}
|
}
|
||||||
}
|
QTimer::singleShot(1000, this, [this]() {
|
||||||
QTimer::singleShot(1000, this, [this]() {
|
nhlog::ui()->debug("timeout");
|
||||||
nhlog::ui()->debug("timeout");
|
this->current_txn = "";
|
||||||
this->current_txn = "";
|
emit processPending();
|
||||||
emit processPending();
|
});
|
||||||
});
|
},
|
||||||
},
|
Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
connect(
|
connect(this,
|
||||||
this,
|
&EventStore::messageSent,
|
||||||
&EventStore::messageSent,
|
this,
|
||||||
this,
|
[this](std::string txn_id, std::string event_id) {
|
||||||
[this](std::string txn_id, std::string event_id) {
|
nhlog::ui()->debug("sent {}", txn_id);
|
||||||
nhlog::ui()->debug("sent {}", txn_id);
|
|
||||||
|
|
||||||
http::client()->read_event(
|
http::client()->read_event(
|
||||||
room_id_, event_id, [this, event_id](mtx::http::RequestErr err) {
|
room_id_, event_id, [this, event_id](mtx::http::RequestErr err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
nhlog::net()->warn(
|
nhlog::net()->warn(
|
||||||
"failed to read_event ({}, {})", room_id_, event_id);
|
"failed to read_event ({}, {})", room_id_, event_id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cache::client()->removePendingStatus(room_id_, txn_id);
|
cache::client()->removePendingStatus(room_id_, txn_id);
|
||||||
this->current_txn = "";
|
this->current_txn = "";
|
||||||
this->current_txn_error_count = 0;
|
this->current_txn_error_count = 0;
|
||||||
emit processPending();
|
emit processPending();
|
||||||
},
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -245,6 +241,58 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
|
||||||
emit dataChanged(toExternalIdx(*idx), toExternalIdx(*idx));
|
emit dataChanged(toExternalIdx(*idx), toExternalIdx(*idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decrypting and checking some encrypted messages
|
||||||
|
if (auto encrypted =
|
||||||
|
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
|
||||||
|
&event)) {
|
||||||
|
auto event = decryptEvent({room_id_, encrypted->event_id}, *encrypted);
|
||||||
|
if (std::visit(
|
||||||
|
[](auto e) { return (e.sender != utils::localUser().toStdString()); },
|
||||||
|
*event)) {
|
||||||
|
if (auto msg = std::get_if<mtx::events::RoomEvent<
|
||||||
|
mtx::events::msg::KeyVerificationRequest>>(event)) {
|
||||||
|
last_verification_request_event = *msg;
|
||||||
|
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
|
||||||
|
mtx::events::msg::KeyVerificationCancel>>(event)) {
|
||||||
|
last_verification_cancel_event = *msg;
|
||||||
|
ChatPage::instance()->recievedDeviceVerificationCancel(
|
||||||
|
msg->content);
|
||||||
|
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
|
||||||
|
mtx::events::msg::KeyVerificationAccept>>(event)) {
|
||||||
|
ChatPage::instance()->recievedDeviceVerificationAccept(
|
||||||
|
msg->content);
|
||||||
|
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
|
||||||
|
mtx::events::msg::KeyVerificationKey>>(event)) {
|
||||||
|
ChatPage::instance()->recievedDeviceVerificationKey(
|
||||||
|
msg->content);
|
||||||
|
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
|
||||||
|
mtx::events::msg::KeyVerificationMac>>(event)) {
|
||||||
|
ChatPage::instance()->recievedDeviceVerificationMac(
|
||||||
|
msg->content);
|
||||||
|
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
|
||||||
|
mtx::events::msg::KeyVerificationReady>>(event)) {
|
||||||
|
ChatPage::instance()->recievedDeviceVerificationReady(
|
||||||
|
msg->content);
|
||||||
|
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
|
||||||
|
mtx::events::msg::KeyVerificationDone>>(event)) {
|
||||||
|
ChatPage::instance()->recievedDeviceVerificationDone(
|
||||||
|
msg->content);
|
||||||
|
} else if (auto msg = std::get_if<mtx::events::RoomEvent<
|
||||||
|
mtx::events::msg::KeyVerificationStart>>(event)) {
|
||||||
|
ChatPage::instance()->recievedDeviceVerificationStart(
|
||||||
|
msg->content, msg->sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_verification_request_event.has_value()) {
|
||||||
|
if (last_verification_request_event.value().origin_server_ts >
|
||||||
|
last_verification_cancel_event.origin_server_ts) {
|
||||||
|
emit startDMVerification(last_verification_request_event.value());
|
||||||
|
last_verification_request_event = {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +473,8 @@ EventStore::decryptEvent(const IdIndex &idx,
|
||||||
e.what());
|
e.what());
|
||||||
dummy.content.body =
|
dummy.content.body =
|
||||||
tr("-- Decryption Error (failed to retrieve megolm keys from db) --",
|
tr("-- Decryption Error (failed to retrieve megolm keys from db) --",
|
||||||
"Placeholder, when the message can't be decrypted, because the DB access "
|
"Placeholder, when the message can't be decrypted, because the DB "
|
||||||
|
"access "
|
||||||
"failed.")
|
"failed.")
|
||||||
.toStdString();
|
.toStdString();
|
||||||
return asCacheEntry(std::move(dummy));
|
return asCacheEntry(std::move(dummy));
|
||||||
|
@ -437,7 +486,8 @@ EventStore::decryptEvent(const IdIndex &idx,
|
||||||
e.what());
|
e.what());
|
||||||
dummy.content.body =
|
dummy.content.body =
|
||||||
tr("-- Decryption Error (%1) --",
|
tr("-- Decryption Error (%1) --",
|
||||||
"Placeholder, when the message can't be decrypted. In this case, the Olm "
|
"Placeholder, when the message can't be decrypted. In this case, the "
|
||||||
|
"Olm "
|
||||||
"decrytion returned an error, which is passed as %1.")
|
"decrytion returned an error, which is passed as %1.")
|
||||||
.arg(e.what())
|
.arg(e.what())
|
||||||
.toStdString();
|
.toStdString();
|
||||||
|
@ -470,11 +520,11 @@ EventStore::decryptEvent(const IdIndex &idx,
|
||||||
return asCacheEntry(std::move(temp_events[0]));
|
return asCacheEntry(std::move(temp_events[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
dummy.content.body =
|
dummy.content.body = tr("-- Encrypted Event (Unknown event type) --",
|
||||||
tr("-- Encrypted Event (Unknown event type) --",
|
"Placeholder, when the message was decrypted, but we "
|
||||||
"Placeholder, when the message was decrypted, but we couldn't parse it, because "
|
"couldn't parse it, because "
|
||||||
"Nheko/mtxclient don't support that event type yet.")
|
"Nheko/mtxclient don't support that event type yet.")
|
||||||
.toStdString();
|
.toStdString();
|
||||||
return asCacheEntry(std::move(dummy));
|
return asCacheEntry(std::move(dummy));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +552,8 @@ EventStore::get(std::string_view id, std::string_view related_to, bool decrypt)
|
||||||
mtx::http::RequestErr err) {
|
mtx::http::RequestErr err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
nhlog::net()->error(
|
nhlog::net()->error(
|
||||||
"Failed to retrieve event with id {}, which was "
|
"Failed to retrieve event with id {}, which "
|
||||||
|
"was "
|
||||||
"requested to show the replyTo for event {}",
|
"requested to show the replyTo for event {}",
|
||||||
relatedTo,
|
relatedTo,
|
||||||
id);
|
id);
|
||||||
|
|
|
@ -98,6 +98,8 @@ signals:
|
||||||
void processPending();
|
void processPending();
|
||||||
void messageSent(std::string txn_id, std::string event_id);
|
void messageSent(std::string txn_id, std::string event_id);
|
||||||
void messageFailed(std::string txn_id);
|
void messageFailed(std::string txn_id);
|
||||||
|
void startDMVerification(
|
||||||
|
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void addPending(mtx::events::collections::TimelineEvents event);
|
void addPending(mtx::events::collections::TimelineEvents event);
|
||||||
|
@ -118,4 +120,10 @@ private:
|
||||||
|
|
||||||
std::string current_txn;
|
std::string current_txn;
|
||||||
int current_txn_error_count = 0;
|
int current_txn_error_count = 0;
|
||||||
|
|
||||||
|
// probably not the best way to do
|
||||||
|
std::optional<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest>>
|
||||||
|
last_verification_request_event;
|
||||||
|
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel>
|
||||||
|
last_verification_cancel_event;
|
||||||
};
|
};
|
||||||
|
|
|
@ -186,12 +186,11 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
|
||||||
, room_id_(room_id)
|
, room_id_(room_id)
|
||||||
, manager_(manager)
|
, manager_(manager)
|
||||||
{
|
{
|
||||||
connect(
|
connect(this,
|
||||||
this,
|
&TimelineModel::redactionFailed,
|
||||||
&TimelineModel::redactionFailed,
|
this,
|
||||||
this,
|
[](const QString &msg) { emit ChatPage::instance()->showNotification(msg); },
|
||||||
[](const QString &msg) { emit ChatPage::instance()->showNotification(msg); },
|
Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection);
|
|
||||||
|
|
||||||
connect(this,
|
connect(this,
|
||||||
&TimelineModel::newMessageToSend,
|
&TimelineModel::newMessageToSend,
|
||||||
|
@ -200,17 +199,17 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
connect(this, &TimelineModel::addPendingMessageToStore, &events, &EventStore::addPending);
|
connect(this, &TimelineModel::addPendingMessageToStore, &events, &EventStore::addPending);
|
||||||
|
|
||||||
connect(
|
connect(&events,
|
||||||
&events,
|
&EventStore::dataChanged,
|
||||||
&EventStore::dataChanged,
|
this,
|
||||||
this,
|
[this](int from, int to) {
|
||||||
[this](int from, int to) {
|
nhlog::ui()->debug("data changed {} to {}",
|
||||||
nhlog::ui()->debug(
|
events.size() - to - 1,
|
||||||
"data changed {} to {}", events.size() - to - 1, events.size() - from - 1);
|
events.size() - from - 1);
|
||||||
emit dataChanged(index(events.size() - to - 1, 0),
|
emit dataChanged(index(events.size() - to - 1, 0),
|
||||||
index(events.size() - from - 1, 0));
|
index(events.size() - from - 1, 0));
|
||||||
},
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
|
|
||||||
connect(&events, &EventStore::beginInsertRows, this, [this](int from, int to) {
|
connect(&events, &EventStore::beginInsertRows, this, [this](int from, int to) {
|
||||||
int first = events.size() - to;
|
int first = events.size() - to;
|
||||||
|
@ -232,6 +231,12 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
|
||||||
connect(&events, &EventStore::newEncryptedImage, this, &TimelineModel::newEncryptedImage);
|
connect(&events, &EventStore::newEncryptedImage, this, &TimelineModel::newEncryptedImage);
|
||||||
connect(
|
connect(
|
||||||
&events, &EventStore::fetchedMore, this, [this]() { setPaginationInProgress(false); });
|
&events, &EventStore::fetchedMore, this, [this]() { setPaginationInProgress(false); });
|
||||||
|
connect(&events,
|
||||||
|
&EventStore::startDMVerification,
|
||||||
|
this,
|
||||||
|
[this](mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> msg) {
|
||||||
|
ChatPage::instance()->recievedRoomDeviceVerificationRequest(msg, this);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray>
|
QHash<int, QByteArray>
|
||||||
|
@ -613,187 +618,6 @@ TimelineModel::updateLastMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<QString>
|
|
||||||
TimelineModel::internalAddEvents(
|
|
||||||
const std::vector<mtx::events::collections::TimelineEvents> &timeline)
|
|
||||||
{
|
|
||||||
std::vector<QString> ids;
|
|
||||||
for (auto e : timeline) {
|
|
||||||
QString id = QString::fromStdString(mtx::accessors::event_id(e));
|
|
||||||
|
|
||||||
if (this->events.contains(id)) {
|
|
||||||
this->events.insert(id, e);
|
|
||||||
int idx = idToIndex(id);
|
|
||||||
emit dataChanged(index(idx, 0), index(idx, 0));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString txid = QString::fromStdString(mtx::accessors::transaction_id(e));
|
|
||||||
if (this->pending.removeOne(txid)) {
|
|
||||||
this->events.insert(id, e);
|
|
||||||
this->events.remove(txid);
|
|
||||||
int idx = idToIndex(txid);
|
|
||||||
if (idx < 0) {
|
|
||||||
nhlog::ui()->warn("Received index out of range");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
eventOrder[idx] = id;
|
|
||||||
emit dataChanged(index(idx, 0), index(idx, 0));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto redaction =
|
|
||||||
std::get_if<mtx::events::RedactionEvent<mtx::events::msg::Redaction>>(&e)) {
|
|
||||||
QString redacts = QString::fromStdString(redaction->redacts);
|
|
||||||
auto redacted = std::find(eventOrder.begin(), eventOrder.end(), redacts);
|
|
||||||
|
|
||||||
auto event = events.value(redacts);
|
|
||||||
if (auto reaction =
|
|
||||||
std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(
|
|
||||||
&event)) {
|
|
||||||
QString reactedTo =
|
|
||||||
QString::fromStdString(reaction->content.relates_to.event_id);
|
|
||||||
reactions[reactedTo].removeReaction(*reaction);
|
|
||||||
int idx = idToIndex(reactedTo);
|
|
||||||
if (idx >= 0)
|
|
||||||
emit dataChanged(index(idx, 0), index(idx, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redacted != eventOrder.end()) {
|
|
||||||
auto redactedEvent = std::visit(
|
|
||||||
[](const auto &ev)
|
|
||||||
-> mtx::events::RoomEvent<mtx::events::msg::Redacted> {
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::Redacted>
|
|
||||||
replacement = {};
|
|
||||||
replacement.event_id = ev.event_id;
|
|
||||||
replacement.room_id = ev.room_id;
|
|
||||||
replacement.sender = ev.sender;
|
|
||||||
replacement.origin_server_ts = ev.origin_server_ts;
|
|
||||||
replacement.type = ev.type;
|
|
||||||
return replacement;
|
|
||||||
},
|
|
||||||
e);
|
|
||||||
events.insert(redacts, redactedEvent);
|
|
||||||
|
|
||||||
int row = (int)std::distance(eventOrder.begin(), redacted);
|
|
||||||
emit dataChanged(index(row, 0), index(row, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
continue; // don't insert redaction into timeline
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto reaction =
|
|
||||||
std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(&e)) {
|
|
||||||
QString reactedTo =
|
|
||||||
QString::fromStdString(reaction->content.relates_to.event_id);
|
|
||||||
events.insert(id, e);
|
|
||||||
|
|
||||||
// remove local echo
|
|
||||||
if (!txid.isEmpty()) {
|
|
||||||
auto rCopy = *reaction;
|
|
||||||
rCopy.event_id = txid.toStdString();
|
|
||||||
reactions[reactedTo].removeReaction(rCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
reactions[reactedTo].addReaction(room_id_.toStdString(), *reaction);
|
|
||||||
int idx = idToIndex(reactedTo);
|
|
||||||
if (idx >= 0)
|
|
||||||
emit dataChanged(index(idx, 0), index(idx, 0));
|
|
||||||
continue; // don't insert reaction into timeline
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto event =
|
|
||||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&e)) {
|
|
||||||
auto e_ = decryptEvent(*event).event;
|
|
||||||
auto encInfo = mtx::accessors::file(e_);
|
|
||||||
|
|
||||||
if (encInfo)
|
|
||||||
emit newEncryptedImage(encInfo.value());
|
|
||||||
|
|
||||||
if (auto msg = std::get_if<
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest>>(
|
|
||||||
&e_)) {
|
|
||||||
last_verification_request_event = *msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto msg = std::get_if<
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel>>(
|
|
||||||
&e_)) {
|
|
||||||
last_verification_cancel_event = *msg;
|
|
||||||
ChatPage::instance()->recievedDeviceVerificationCancel(
|
|
||||||
msg->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto msg = std::get_if<
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept>>(
|
|
||||||
&e_)) {
|
|
||||||
ChatPage::instance()->recievedDeviceVerificationAccept(
|
|
||||||
msg->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto msg = std::get_if<
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey>>(&e_)) {
|
|
||||||
ChatPage::instance()->recievedDeviceVerificationKey(msg->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto msg = std::get_if<
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac>>(&e_)) {
|
|
||||||
ChatPage::instance()->recievedDeviceVerificationMac(msg->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto msg = std::get_if<
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady>>(
|
|
||||||
&e_)) {
|
|
||||||
ChatPage::instance()->recievedDeviceVerificationReady(msg->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto msg = std::get_if<
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone>>(&e_)) {
|
|
||||||
ChatPage::instance()->recievedDeviceVerificationDone(msg->content);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto msg = std::get_if<
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart>>(
|
|
||||||
&e_)) {
|
|
||||||
ChatPage::instance()->recievedDeviceVerificationStart(msg->content,
|
|
||||||
msg->sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->events.insert(id, e);
|
|
||||||
ids.push_back(id);
|
|
||||||
|
|
||||||
auto replyTo = mtx::accessors::in_reply_to_event(e);
|
|
||||||
auto qReplyTo = QString::fromStdString(replyTo);
|
|
||||||
if (!replyTo.empty() && !events.contains(qReplyTo)) {
|
|
||||||
http::client()->get_event(
|
|
||||||
this->room_id_.toStdString(),
|
|
||||||
replyTo,
|
|
||||||
[this, id, replyTo](
|
|
||||||
const mtx::events::collections::TimelineEvents &timeline,
|
|
||||||
mtx::http::RequestErr err) {
|
|
||||||
if (err) {
|
|
||||||
nhlog::net()->error(
|
|
||||||
"Failed to retrieve event with id {}, which was "
|
|
||||||
"requested to show the replyTo for event {}",
|
|
||||||
replyTo,
|
|
||||||
id.toStdString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
emit eventFetched(id, timeline);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_verification_request_event.origin_server_ts >
|
|
||||||
last_verification_cancel_event.origin_server_ts) {
|
|
||||||
ChatPage::instance()->recievedRoomDeviceVerificationRequest(
|
|
||||||
last_verification_request_event, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineModel::setCurrentIndex(int index)
|
TimelineModel::setCurrentIndex(int index)
|
||||||
{
|
{
|
||||||
|
@ -979,15 +803,18 @@ TimelineModel::markEventsAsRead(const std::vector<QString> &event_ids)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
void
|
void
|
||||||
TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json content)
|
TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent<T> msg)
|
||||||
{
|
{
|
||||||
const auto room_id = room_id_.toStdString();
|
const auto room_id = room_id_.toStdString();
|
||||||
|
|
||||||
using namespace mtx::events;
|
using namespace mtx::events;
|
||||||
using namespace mtx::identifiers;
|
using namespace mtx::identifiers;
|
||||||
|
|
||||||
json doc = {{"type", "m.room.message"}, {"content", content}, {"room_id", room_id}};
|
json doc = {
|
||||||
|
{"type", to_string(msg.type)}, {"content", json(msg.content)}, {"room_id", room_id}};
|
||||||
|
std::cout << doc.dump(2) << std::endl;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if we have already an outbound megolm session then we can use.
|
// Check if we have already an outbound megolm session then we can use.
|
||||||
|
@ -995,7 +822,7 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con
|
||||||
mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event;
|
mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event;
|
||||||
event.content =
|
event.content =
|
||||||
olm::encrypt_group_message(room_id, http::client()->device_id(), doc);
|
olm::encrypt_group_message(room_id, http::client()->device_id(), doc);
|
||||||
event.event_id = txn_id;
|
event.event_id = msg.event_id;
|
||||||
event.room_id = room_id;
|
event.room_id = room_id;
|
||||||
event.sender = http::client()->user_id().to_string();
|
event.sender = http::client()->user_id().to_string();
|
||||||
event.type = mtx::events::EventType::RoomEncrypted;
|
event.type = mtx::events::EventType::RoomEncrypted;
|
||||||
|
@ -1030,25 +857,26 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con
|
||||||
const auto members = cache::roomMembers(room_id);
|
const auto members = cache::roomMembers(room_id);
|
||||||
nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id);
|
nhlog::ui()->info("retrieved {} members for {}", members.size(), room_id);
|
||||||
|
|
||||||
auto keeper = std::make_shared<StateKeeper>([room_id, doc, txn_id, this]() {
|
auto keeper =
|
||||||
try {
|
std::make_shared<StateKeeper>([room_id, doc, txn_id = msg.event_id, this]() {
|
||||||
mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event;
|
try {
|
||||||
event.content = olm::encrypt_group_message(
|
mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> event;
|
||||||
room_id, http::client()->device_id(), doc);
|
event.content = olm::encrypt_group_message(
|
||||||
event.event_id = txn_id;
|
room_id, http::client()->device_id(), doc);
|
||||||
event.room_id = room_id;
|
event.event_id = txn_id;
|
||||||
event.sender = http::client()->user_id().to_string();
|
event.room_id = room_id;
|
||||||
event.type = mtx::events::EventType::RoomEncrypted;
|
event.sender = http::client()->user_id().to_string();
|
||||||
event.origin_server_ts = QDateTime::currentMSecsSinceEpoch();
|
event.type = mtx::events::EventType::RoomEncrypted;
|
||||||
|
event.origin_server_ts = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
|
||||||
emit this->addPendingMessageToStore(event);
|
emit this->addPendingMessageToStore(event);
|
||||||
} catch (const lmdb::error &e) {
|
} catch (const lmdb::error &e) {
|
||||||
nhlog::db()->critical("failed to save megolm outbound session: {}",
|
nhlog::db()->critical(
|
||||||
e.what());
|
"failed to save megolm outbound session: {}", e.what());
|
||||||
emit ChatPage::instance()->showNotification(
|
emit ChatPage::instance()->showNotification(
|
||||||
tr("Failed to encrypt event, sending aborted!"));
|
tr("Failed to encrypt event, sending aborted!"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mtx::requests::QueryKeys req;
|
mtx::requests::QueryKeys req;
|
||||||
for (const auto &member : members)
|
for (const auto &member : members)
|
||||||
|
@ -1056,7 +884,7 @@ TimelineModel::sendEncryptedMessage(const std::string txn_id, nlohmann::json con
|
||||||
|
|
||||||
http::client()->query_keys(
|
http::client()->query_keys(
|
||||||
req,
|
req,
|
||||||
[keeper = std::move(keeper), megolm_payload, txn_id, this](
|
[keeper = std::move(keeper), megolm_payload, txn_id = msg.event_id, this](
|
||||||
const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) {
|
const mtx::responses::QueryKeys &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: {} {}",
|
||||||
|
@ -1265,6 +1093,40 @@ struct SendMessageVisitor
|
||||||
: model_(model)
|
: model_(model)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg)
|
||||||
|
{
|
||||||
|
emit model_->updateFlowEventId(msg.event_id);
|
||||||
|
model_->sendEncryptedMessage(msg);
|
||||||
|
}
|
||||||
|
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady> &msg)
|
||||||
|
{
|
||||||
|
model_->sendEncryptedMessage(msg);
|
||||||
|
}
|
||||||
|
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart> &msg)
|
||||||
|
{
|
||||||
|
model_->sendEncryptedMessage(msg);
|
||||||
|
}
|
||||||
|
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept> &msg)
|
||||||
|
{
|
||||||
|
model_->sendEncryptedMessage(msg);
|
||||||
|
}
|
||||||
|
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac> &msg)
|
||||||
|
{
|
||||||
|
model_->sendEncryptedMessage(msg);
|
||||||
|
}
|
||||||
|
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey> &msg)
|
||||||
|
{
|
||||||
|
model_->sendEncryptedMessage(msg);
|
||||||
|
}
|
||||||
|
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone> &msg)
|
||||||
|
{
|
||||||
|
model_->sendEncryptedMessage(msg);
|
||||||
|
}
|
||||||
|
void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel> &msg)
|
||||||
|
{
|
||||||
|
model_->sendEncryptedMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
// Do-nothing operator for all unhandled events
|
// Do-nothing operator for all unhandled events
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void operator()(const mtx::events::Event<T> &)
|
void operator()(const mtx::events::Event<T> &)
|
||||||
|
@ -1280,7 +1142,7 @@ struct SendMessageVisitor
|
||||||
if (encInfo)
|
if (encInfo)
|
||||||
emit model_->newEncryptedImage(encInfo.value());
|
emit model_->newEncryptedImage(encInfo.value());
|
||||||
|
|
||||||
model_->sendEncryptedMessage(msg.event_id, nlohmann::json(msg.content));
|
model_->sendEncryptedMessage(msg);
|
||||||
} else {
|
} else {
|
||||||
emit model_->addPendingMessageToStore(msg);
|
emit model_->addPendingMessageToStore(msg);
|
||||||
}
|
}
|
||||||
|
@ -1300,20 +1162,6 @@ struct SendMessageVisitor
|
||||||
TimelineModel *model_;
|
TimelineModel *model_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
TimelineModel::processOnePendingMessage()
|
|
||||||
{
|
|
||||||
if (pending.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString txn_id_qstr = pending.first();
|
|
||||||
|
|
||||||
auto event = events.value(txn_id_qstr);
|
|
||||||
std::cout << "Inside the process one pending message" << std::endl;
|
|
||||||
std::cout << std::visit([](auto &e) { return json(e); }, event).dump(2) << std::endl;
|
|
||||||
std::visit(SendMessageVisitor{txn_id_qstr, this}, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
|
TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
|
||||||
{
|
{
|
||||||
|
@ -1359,18 +1207,7 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event)
|
||||||
event);
|
event);
|
||||||
}
|
}
|
||||||
|
|
||||||
internalAddEvents({event});
|
std::visit(SendMessageVisitor{this}, event);
|
||||||
|
|
||||||
QString txn_id_qstr = QString::fromStdString(mtx::accessors::event_id(event));
|
|
||||||
pending.push_back(txn_id_qstr);
|
|
||||||
if (!std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(&event)) {
|
|
||||||
beginInsertRows(QModelIndex(), 0, 0);
|
|
||||||
this->eventOrder.insert(this->eventOrder.begin(), txn_id_qstr);
|
|
||||||
endInsertRows();
|
|
||||||
}
|
|
||||||
updateLastMessage();
|
|
||||||
|
|
||||||
emit nextPendingMessage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -9,12 +9,8 @@
|
||||||
#include <mtxclient/http/errors.hpp>
|
#include <mtxclient/http/errors.hpp>
|
||||||
|
|
||||||
#include "CacheCryptoStructs.h"
|
#include "CacheCryptoStructs.h"
|
||||||
<<<<<<< HEAD
|
|
||||||
#include "EventStore.h"
|
#include "EventStore.h"
|
||||||
=======
|
|
||||||
#include "ReactionsModel.h"
|
|
||||||
#include "ui/UserProfile.h"
|
#include "ui/UserProfile.h"
|
||||||
>>>>>>> Refactor UserProfile
|
|
||||||
|
|
||||||
namespace mtx::http {
|
namespace mtx::http {
|
||||||
using RequestErr = const std::optional<mtx::http::ClientError> &;
|
using RequestErr = const std::optional<mtx::http::ClientError> &;
|
||||||
|
@ -271,8 +267,13 @@ signals:
|
||||||
|
|
||||||
void openProfile(UserProfile *profile);
|
void openProfile(UserProfile *profile);
|
||||||
|
|
||||||
|
void newMessageToSend(mtx::events::collections::TimelineEvents event);
|
||||||
|
void addPendingMessageToStore(mtx::events::collections::TimelineEvents event);
|
||||||
|
void updateFlowEventId(std::string event_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendEncryptedMessage(const std::string txn_id, nlohmann::json content);
|
template<typename T>
|
||||||
|
void sendEncryptedMessage(mtx::events::RoomEvent<T> msg);
|
||||||
void handleClaimedKeys(std::shared_ptr<StateKeeper> keeper,
|
void handleClaimedKeys(std::shared_ptr<StateKeeper> keeper,
|
||||||
const std::map<std::string, std::string> &room_key,
|
const std::map<std::string, std::string> &room_key,
|
||||||
const std::map<std::string, DevicePublicKeys> &pks,
|
const std::map<std::string, DevicePublicKeys> &pks,
|
||||||
|
@ -297,11 +298,6 @@ private:
|
||||||
std::vector<QString> typingUsers_;
|
std::vector<QString> typingUsers_;
|
||||||
|
|
||||||
TimelineViewManager *manager_;
|
TimelineViewManager *manager_;
|
||||||
// probably not the best way to do
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest>
|
|
||||||
last_verification_request_event;
|
|
||||||
mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel>
|
|
||||||
last_verification_cancel_event;
|
|
||||||
|
|
||||||
friend struct SendMessageVisitor;
|
friend struct SendMessageVisitor;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,13 +5,15 @@
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "mtx/responses/crypto.hpp"
|
#include "mtx/responses/crypto.hpp"
|
||||||
|
#include "timeline/TimelineModel.h"
|
||||||
|
|
||||||
#include <iostream> // only for debugging
|
#include <iostream> // only for debugging
|
||||||
|
|
||||||
UserProfile::UserProfile(QString roomid, QString userid, QObject *parent)
|
UserProfile::UserProfile(QString roomid, QString userid, TimelineModel *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, roomid_(roomid)
|
, roomid_(roomid)
|
||||||
, userid_(userid)
|
, userid_(userid)
|
||||||
|
, model(parent)
|
||||||
{
|
{
|
||||||
fetchDeviceList(this->userid_);
|
fetchDeviceList(this->userid_);
|
||||||
}
|
}
|
||||||
|
@ -185,27 +187,43 @@ UserProfile::startChat()
|
||||||
emit ChatPage::instance()->createRoom(req);
|
emit ChatPage::instance()->createRoom(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
DeviceVerificationFlow *
|
||||||
UserProfile::verifyUser()
|
UserProfile::createFlow(bool isVerifyUser)
|
||||||
{
|
{
|
||||||
std::cout << "Checking if to start to device verification or room message verification"
|
if (!isVerifyUser)
|
||||||
<< std::endl;
|
return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice));
|
||||||
auto joined_rooms = cache::joinedRooms();
|
else {
|
||||||
auto room_infos = cache::getRoomInfo(joined_rooms);
|
std::cout << "CHECKING IF IT TO START ROOM_VERIFICATION OR TO_DEVICE VERIFICATION"
|
||||||
|
<< std::endl;
|
||||||
|
auto joined_rooms = cache::joinedRooms();
|
||||||
|
auto room_infos = cache::getRoomInfo(joined_rooms);
|
||||||
|
|
||||||
for (std::string room_id : joined_rooms) {
|
for (std::string room_id : joined_rooms) {
|
||||||
if ((room_infos[QString::fromStdString(room_id)].member_count == 2) &&
|
if ((room_infos[QString::fromStdString(room_id)].member_count == 2) &&
|
||||||
cache::isRoomEncrypted(room_id)) {
|
cache::isRoomEncrypted(room_id)) {
|
||||||
auto room_members = cache::roomMembers(room_id);
|
auto room_members = cache::roomMembers(room_id);
|
||||||
if (std::find(room_members.begin(),
|
if (std::find(room_members.begin(),
|
||||||
room_members.end(),
|
room_members.end(),
|
||||||
(this->userid()).toStdString()) != room_members.end()) {
|
(this->userid()).toStdString()) !=
|
||||||
std::cout << "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id
|
room_members.end()) {
|
||||||
|
std::cout
|
||||||
|
<< "FOUND A ENCRYPTED ROOM WITH THIS USER : " << room_id
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
return;
|
if (this->roomid_.toStdString() == room_id) {
|
||||||
|
auto newflow = new DeviceVerificationFlow(
|
||||||
|
this, DeviceVerificationFlow::Type::RoomMsg);
|
||||||
|
newflow->setModel(this->model);
|
||||||
|
return (std::move(newflow));
|
||||||
|
} else {
|
||||||
|
std::cout << "FOUND A ENCRYPTED ROOM BUT CURRENTLY "
|
||||||
|
"NOT IN THAT ROOM : "
|
||||||
|
<< room_id << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl;
|
std::cout << "DIDN'T FIND A ENCRYPTED ROOM WITH THIS USER" << std::endl;
|
||||||
|
return (new DeviceVerificationFlow(this, DeviceVerificationFlow::Type::ToDevice));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -20,6 +20,7 @@ Q_ENUM_NS(Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeviceVerificationFlow;
|
class DeviceVerificationFlow;
|
||||||
|
class TimelineModel;
|
||||||
|
|
||||||
class DeviceInfo
|
class DeviceInfo
|
||||||
{
|
{
|
||||||
|
@ -83,7 +84,7 @@ class UserProfile : public QObject
|
||||||
Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT)
|
Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT)
|
||||||
Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT)
|
Q_PROPERTY(bool isUserVerified READ getUserStatus CONSTANT)
|
||||||
public:
|
public:
|
||||||
UserProfile(QString roomid, QString userid, QObject *parent = 0);
|
UserProfile(QString roomid, QString userid, TimelineModel *parent = nullptr);
|
||||||
|
|
||||||
DeviceInfoModel *deviceList();
|
DeviceInfoModel *deviceList();
|
||||||
|
|
||||||
|
@ -92,18 +93,19 @@ public:
|
||||||
QString avatarUrl();
|
QString avatarUrl();
|
||||||
bool getUserStatus();
|
bool getUserStatus();
|
||||||
|
|
||||||
|
Q_INVOKABLE DeviceVerificationFlow *createFlow(bool isVerifyUser);
|
||||||
Q_INVOKABLE void fetchDeviceList(const QString &userID);
|
Q_INVOKABLE void fetchDeviceList(const QString &userID);
|
||||||
Q_INVOKABLE void banUser();
|
Q_INVOKABLE void banUser();
|
||||||
// Q_INVOKABLE void ignoreUser();
|
// Q_INVOKABLE void ignoreUser();
|
||||||
Q_INVOKABLE void kickUser();
|
Q_INVOKABLE void kickUser();
|
||||||
Q_INVOKABLE void startChat();
|
Q_INVOKABLE void startChat();
|
||||||
Q_INVOKABLE void verifyUser();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString roomid_, userid_;
|
QString roomid_, userid_;
|
||||||
std::optional<std::string> cross_verified;
|
std::optional<std::string> cross_verified;
|
||||||
DeviceInfoModel deviceList_;
|
DeviceInfoModel deviceList_;
|
||||||
bool isUserVerified = false;
|
bool isUserVerified = false;
|
||||||
|
TimelineModel *model;
|
||||||
|
|
||||||
void callback_fn(const mtx::responses::QueryKeys &res,
|
void callback_fn(const mtx::responses::QueryKeys &res,
|
||||||
mtx::http::RequestErr err,
|
mtx::http::RequestErr err,
|
||||||
|
|
Loading…
Reference in a new issue