diff --git a/include/AvatarProvider.h b/include/AvatarProvider.h
index ce82f2aa..4b4e15e9 100644
--- a/include/AvatarProvider.h
+++ b/include/AvatarProvider.h
@@ -20,15 +20,17 @@
#include <QImage>
#include <functional>
-class AvatarProvider : public QObject
+class AvatarProxy : public QObject
{
Q_OBJECT
-public:
- //! The callback is called with the downloaded avatar for the given user
- //! or the avatar is downloaded first and then saved for re-use.
- static void resolve(const QString &room_id,
- const QString &userId,
- QObject *receiver,
- std::function<void(QImage)> callback);
+signals:
+ void avatarDownloaded(const QByteArray &data);
};
+
+using AvatarCallback = std::function<void(QImage)>;
+
+namespace AvatarProvider {
+void
+resolve(const QString &room_id, const QString &user_id, QObject *receiver, AvatarCallback cb);
+}
diff --git a/include/Cache.h b/include/Cache.h
index d2574b76..afc7a148 100644
--- a/include/Cache.h
+++ b/include/Cache.h
@@ -192,7 +192,7 @@ public:
void saveState(const mtx::responses::Sync &res);
bool isInitialized() const;
- QString nextBatchToken() const;
+ std::string nextBatchToken() const;
void deleteData();
@@ -237,6 +237,7 @@ public:
{
return image(QString::fromStdString(url));
}
+ void saveImage(const std::string &url, const std::string &data);
void saveImage(const QString &url, const QByteArray &data);
RoomInfo singleRoomInfo(const std::string &room_id);
diff --git a/include/ChatPage.h b/include/ChatPage.h
index b6c431e4..e99e94ba 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -17,6 +17,8 @@
#pragma once
+#include <atomic>
+
#include <QFrame>
#include <QHBoxLayout>
#include <QMap>
@@ -50,9 +52,6 @@ constexpr int CONSENSUS_TIMEOUT = 1000;
constexpr int SHOW_CONTENT_TIMEOUT = 3000;
constexpr int TYPING_REFRESH_TIMEOUT = 10000;
-Q_DECLARE_METATYPE(mtx::responses::Rooms)
-Q_DECLARE_METATYPE(std::vector<std::string>)
-
class ChatPage : public QWidget
{
Q_OBJECT
@@ -71,7 +70,37 @@ public:
QSharedPointer<UserSettings> userSettings() { return userSettings_; }
void deleteConfigs();
+public slots:
+ void leaveRoom(const QString &room_id);
+
signals:
+ void connectionLost();
+ void connectionRestored();
+
+ void notificationsRetrieved(const mtx::responses::Notifications &);
+
+ void uploadFailed(const QString &msg);
+ void imageUploaded(const QString &roomid,
+ const QString &filename,
+ const QString &url,
+ const QString &mime,
+ qint64 dsize);
+ void fileUploaded(const QString &roomid,
+ const QString &filename,
+ const QString &url,
+ const QString &mime,
+ qint64 dsize);
+ void audioUploaded(const QString &roomid,
+ const QString &filename,
+ const QString &url,
+ const QString &mime,
+ qint64 dsize);
+ void videoUploaded(const QString &roomid,
+ const QString &filename,
+ const QString &url,
+ const QString &mime,
+ qint64 dsize);
+
void contentLoaded();
void closing();
void changeWindowTitle(const QString &msg);
@@ -82,30 +111,44 @@ signals:
void showOverlayProgressBar();
void startConsesusTimer();
+ void removeTimelineEvent(const QString &room_id, const QString &event_id);
+
+ void ownProfileOk();
+ void setUserDisplayName(const QString &name);
+ void setUserAvatar(const QImage &avatar);
+ void loggedOut();
+
+ void trySyncCb();
+ void tryInitialSyncCb();
+ void leftRoom(const QString &room_id);
+
void initializeRoomList(QMap<QString, RoomInfo>);
void initializeViews(const mtx::responses::Rooms &rooms);
void initializeEmptyViews(const std::vector<std::string> &rooms);
void syncUI(const mtx::responses::Rooms &rooms);
- void continueSync(const QString &next_batch);
void syncRoomlist(const std::map<QString, RoomInfo> &updates);
void syncTopBar(const std::map<QString, RoomInfo> &updates);
+ void dropToLoginPageCb(const QString &msg);
private slots:
void showUnreadMessageNotification(int count);
void updateTopBarAvatar(const QString &roomid, const QPixmap &img);
- void updateOwnProfileInfo(const QUrl &avatar_url, const QString &display_name);
void updateOwnCommunitiesInfo(const QList<QString> &own_communities);
- void initialSyncCompleted(const mtx::responses::Sync &response);
- void syncCompleted(const mtx::responses::Sync &response);
void changeTopRoomInfo(const QString &room_id);
void logout();
void removeRoom(const QString &room_id);
- //! Handles initial sync failures.
- void retryInitialSync(int status_code = -1);
+ void dropToLoginPage(const QString &msg);
+
+ void joinRoom(const QString &room);
+ void createRoom(const mtx::requests::CreateRoom &req);
+ void sendTypingNotifications();
private:
static ChatPage *instance_;
+ void tryInitialSync();
+ void trySync();
+
//! Check if the given room is currently open.
bool isRoomActive(const QString &room_id)
{
@@ -161,8 +204,8 @@ private:
// Safety net if consensus is not possible or too slow.
QTimer *showContentTimer_;
QTimer *consensusTimer_;
- QTimer *syncTimeoutTimer_;
- QTimer *initialSyncTimer_;
+ QTimer connectivityTimer_;
+ std::atomic_bool isConnected_;
QString current_room_;
QString current_community_;
diff --git a/include/CommunitiesList.h b/include/CommunitiesList.h
index 3299e7c4..78b9602e 100644
--- a/include/CommunitiesList.h
+++ b/include/CommunitiesList.h
@@ -23,12 +23,14 @@ public:
signals:
void communityChanged(const QString &id);
+ void avatarRetrieved(const QString &id, const QPixmap &img);
public slots:
void updateCommunityAvatar(const QString &id, const QPixmap &img);
void highlightSelectedCommunity(const QString &id);
private:
+ void fetchCommunityAvatar(const QString &id, const QString &avatarUrl);
void addGlobalItem() { addCommunity(QSharedPointer<Community>(new Community), "world"); }
//! Check whether or not a community id is currently managed.
diff --git a/include/Logging.hpp b/include/Logging.hpp
new file mode 100644
index 00000000..c301d80d
--- /dev/null
+++ b/include/Logging.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <memory>
+#include <spdlog/spdlog.h>
+
+namespace log {
+void
+init(const std::string &file);
+
+std::shared_ptr<spdlog::logger>
+main();
+
+std::shared_ptr<spdlog::logger>
+net();
+
+std::shared_ptr<spdlog::logger>
+db();
+}
diff --git a/include/LoginPage.h b/include/LoginPage.h
index 34a08df9..c52ccaa4 100644
--- a/include/LoginPage.h
+++ b/include/LoginPage.h
@@ -28,6 +28,12 @@ class OverlayModal;
class RaisedButton;
class TextField;
+namespace mtx {
+namespace responses {
+struct Login;
+}
+}
+
class LoginPage : public QWidget
{
Q_OBJECT
@@ -42,12 +48,19 @@ signals:
void loggingIn();
void errorOccurred();
+ //! Used to trigger the corresponding slot outside of the main thread.
+ void versionErrorCb(const QString &err);
+ void loginErrorCb(const QString &err);
+ void versionOkCb();
+
+ void loginOk(const mtx::responses::Login &res);
+
protected:
void paintEvent(QPaintEvent *event) override;
public slots:
// Displays errors produced during the login.
- void loginError(QString msg) { error_label_->setText(msg); }
+ void loginError(const QString &msg) { error_label_->setText(msg); }
private slots:
// Callback for the back button.
@@ -63,13 +76,25 @@ private slots:
void onServerAddressEntered();
// Callback for errors produced during server probing
- void versionError(QString error_message);
-
+ void versionError(const QString &error_message);
// Callback for successful server probing
- void versionSuccess();
+ void versionOk();
private:
bool isMatrixIdValid();
+ void checkHomeserverVersion();
+ std::string initialDeviceName()
+ {
+#if defined(Q_OS_MAC)
+ return "nheko on macOS";
+#elif defined(Q_OS_LINUX)
+ return "nheko on Linux";
+#elif defined(Q_OS_WIN)
+ return "nheko on Windows";
+#else
+ return "nheko";
+#endif
+ }
QVBoxLayout *top_layout_;
diff --git a/include/MainWindow.h b/include/MainWindow.h
index 0fbc7567..f0fa9a08 100644
--- a/include/MainWindow.h
+++ b/include/MainWindow.h
@@ -59,6 +59,7 @@ class MainWindow : public QMainWindow
public:
explicit MainWindow(QWidget *parent = 0);
+ ~MainWindow();
static MainWindow *instance() { return instance_; };
void saveCurrentWindowSize();
@@ -96,7 +97,7 @@ private slots:
void showUserSettingsPage() { pageStack_->setCurrentWidget(userSettingsPage_); }
//! Show the chat page and start communicating with the given access token.
- void showChatPage(QString user_id, QString home_server, QString token);
+ void showChatPage();
void showOverlayProgressBar();
void removeOverlayProgressBar();
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index eae57281..832d6cad 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -1,287 +1,25 @@
-/*
- * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
#pragma once
-#include <QFileInfo>
-#include <QJsonDocument>
-#include <QNetworkAccessManager>
-#include <QNetworkReply>
-#include <QNetworkRequest>
-#include <QUrl>
-#include <memory>
-#include <mtx.hpp>
-#include <mtx/errors.hpp>
-
-class DownloadMediaProxy : public QObject
-{
- Q_OBJECT
-
-signals:
- void imageDownloaded(const QPixmap &data);
- void fileDownloaded(const QByteArray &data);
- void avatarDownloaded(const QImage &img);
-};
+#include <QMetaType>
-class StateEventProxy : public QObject
-{
- Q_OBJECT
-
-signals:
- void stateEventSent();
- void stateEventError(const QString &msg);
-};
+#include <mtx/responses.hpp>
+#include <mtxclient/http/client.hpp>
+Q_DECLARE_METATYPE(mtx::responses::Login)
+Q_DECLARE_METATYPE(mtx::responses::Messages)
+Q_DECLARE_METATYPE(mtx::responses::Notifications)
+Q_DECLARE_METATYPE(mtx::responses::Rooms)
Q_DECLARE_METATYPE(mtx::responses::Sync)
-
-/*
- * MatrixClient provides the high level API to communicate with
- * a Matrix homeserver. All the responses are returned through signals.
- */
-class MatrixClient : public QNetworkAccessManager
-{
- Q_OBJECT
-public:
- MatrixClient(QObject *parent = 0);
-
- // Client API.
- void initialSync() noexcept;
- void sync() noexcept;
- template<class EventBody, mtx::events::EventType EventT>
- std::shared_ptr<StateEventProxy> sendStateEvent(const EventBody &body,
- const QString &roomId,
- const QString &stateKey = "");
- void sendRoomMessage(mtx::events::MessageType ty,
- int txnId,
- const QString &roomid,
- const QString &msg,
- const QString &mime,
- uint64_t media_size,
- const QString &url = "") noexcept;
- void login(const QString &username, const QString &password) noexcept;
- void registerUser(const QString &username,
- const QString &password,
- const QString &server,
- const QString &session = "") noexcept;
- void versions() noexcept;
- void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
- //! Download user's avatar.
- QSharedPointer<DownloadMediaProxy> fetchUserAvatar(const QUrl &avatarUrl);
- void fetchCommunityAvatar(const QString &communityId, const QUrl &avatarUrl);
- void fetchCommunityProfile(const QString &communityId);
- void fetchCommunityRooms(const QString &communityId);
- QSharedPointer<DownloadMediaProxy> downloadImage(const QUrl &url);
- QSharedPointer<DownloadMediaProxy> downloadFile(const QUrl &url);
- void messages(const QString &room_id, const QString &from_token, int limit = 30) noexcept;
- void uploadImage(const QString &roomid,
- const QString &filename,
- const QSharedPointer<QIODevice> data);
- void uploadFile(const QString &roomid,
- const QString &filename,
- const QSharedPointer<QIODevice> data);
- void uploadAudio(const QString &roomid,
- const QString &filename,
- const QSharedPointer<QIODevice> data);
- void uploadVideo(const QString &roomid,
- const QString &filename,
- const QSharedPointer<QIODevice> data);
- void uploadFilter(const QString &filter) noexcept;
- void joinRoom(const QString &roomIdOrAlias);
- void leaveRoom(const QString &roomId);
- void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000);
- void removeTypingNotification(const QString &roomid);
- void readEvent(const QString &room_id, const QString &event_id);
- void redactEvent(const QString &room_id, const QString &event_id);
- void inviteUser(const QString &room_id, const QString &user);
- void createRoom(const mtx::requests::CreateRoom &request);
- void getNotifications() noexcept;
-
- QUrl getHomeServer() { return server_; };
- int transactionId() { return txn_id_; };
- int incrementTransactionId() { return ++txn_id_; };
-
- void reset() noexcept;
-
-public slots:
- void getOwnProfile() noexcept;
- void getOwnCommunities() noexcept;
- void logout() noexcept;
-
- void setServer(const QString &server)
- {
- server_ = QUrl(QString("%1://%2").arg(serverProtocol_).arg(server));
- };
- void setAccessToken(const QString &token) { token_ = token; };
- void setNextBatchToken(const QString &next_batch) { next_batch_ = next_batch; };
-
-signals:
- void loginError(const QString &error);
- void registerError(const QString &error);
- void registrationFlow(const QString &user,
- const QString &pass,
- const QString &server,
- const QString &session);
- void versionError(const QString &error);
-
- void loggedOut();
- void invitedUser(const QString &room_id, const QString &user);
- void roomCreated(const QString &room_id);
-
- void loginSuccess(const QString &userid, const QString &homeserver, const QString &token);
- void registerSuccess(const QString &userid,
- const QString &homeserver,
- const QString &token);
- void versionSuccess();
- void uploadFailed(int statusCode, const QString &msg);
- void imageUploaded(const QString &roomid,
- const QString &filename,
- const QString &url,
- const QString &mime,
- uint64_t size);
- void fileUploaded(const QString &roomid,
- const QString &filename,
- const QString &url,
- const QString &mime,
- uint64_t size);
- void audioUploaded(const QString &roomid,
- const QString &filename,
- const QString &url,
- const QString &mime,
- uint64_t size);
- void videoUploaded(const QString &roomid,
- const QString &filename,
- const QString &url,
- const QString &mime,
- uint64_t size);
- void roomAvatarRetrieved(const QString &roomid,
- const QPixmap &img,
- const QString &url,
- const QByteArray &data);
- void userAvatarRetrieved(const QString &userId, const QImage &img);
- void communityAvatarRetrieved(const QString &communityId, const QPixmap &img);
- void communityProfileRetrieved(const QString &communityId, const QJsonObject &profile);
- void communityRoomsRetrieved(const QString &communityId, const QJsonObject &rooms);
-
- // Returned profile data for the user's account.
- void getOwnProfileResponse(const QUrl &avatar_url, const QString &display_name);
- void getOwnCommunitiesResponse(const QList<QString> &own_communities);
- void initialSyncCompleted(const mtx::responses::Sync &response);
- void initialSyncFailed(int status_code = -1);
- void syncCompleted(const mtx::responses::Sync &response);
- void syncFailed(const QString &msg);
- void joinFailed(const QString &msg);
- void messageSent(const QString &event_id, const QString &roomid, int txn_id);
- void messageSendFailed(const QString &roomid, int txn_id);
- void emoteSent(const QString &event_id, const QString &roomid, int txn_id);
- void messagesRetrieved(const QString &room_id, const mtx::responses::Messages &msgs);
- void joinedRoom(const QString &room_id);
- void leftRoom(const QString &room_id);
- void roomCreationFailed(const QString &msg);
-
- void redactionFailed(const QString &error);
- void redactionCompleted(const QString &room_id, const QString &event_id);
- void invalidToken();
- void syncError(const QString &error);
- void notificationsRetrieved(const mtx::responses::Notifications ¬ifications);
-
-private:
- QNetworkReply *makeUploadRequest(QSharedPointer<QIODevice> iodev);
- QJsonObject getUploadReply(QNetworkReply *reply);
- void setupAuth(QNetworkRequest &req)
- {
- req.setRawHeader("Authorization", QString("Bearer %1").arg(token_).toLocal8Bit());
- }
-
- // Client API prefix.
- QString clientApiUrl_;
-
- // Media API prefix.
- QString mediaApiUrl_;
-
- // The Matrix server used for communication.
- QUrl server_;
-
- // The access token used for authentication.
- QString token_;
-
- // Increasing transaction ID.
- int txn_id_;
-
- //! Token to be used for the next sync.
- QString next_batch_;
- //! http or https (default).
- QString serverProtocol_;
- //! Filter to be send as filter-param for (initial) /sync requests.
- QString filter_;
-};
+Q_DECLARE_METATYPE(std::string)
+Q_DECLARE_METATYPE(std::vector<std::string>);
namespace http {
-//! Initialize the http module
-void
-init();
-
-//! Retrieve the client instance.
-MatrixClient *
+namespace v2 {
+mtx::http::Client *
client();
}
-template<class EventBody, mtx::events::EventType EventT>
-std::shared_ptr<StateEventProxy>
-MatrixClient::sendStateEvent(const EventBody &body, const QString &roomId, const QString &stateKey)
-{
- QUrl endpoint(server_);
- endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/state/%2/%3")
- .arg(roomId)
- .arg(QString::fromStdString(to_string(EventT)))
- .arg(stateKey));
-
- QNetworkRequest request(QString(endpoint.toEncoded()));
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- setupAuth(request);
-
- auto proxy = std::shared_ptr<StateEventProxy>(new StateEventProxy,
- [](StateEventProxy *p) { p->deleteLater(); });
-
- auto serializedBody = nlohmann::json(body).dump();
- auto reply = put(request, QByteArray(serializedBody.data(), serializedBody.size()));
- connect(reply, &QNetworkReply::finished, this, [reply, proxy]() {
- reply->deleteLater();
-
- int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- auto data = reply->readAll();
-
- if (status == 0 || status >= 400) {
- try {
- mtx::errors::Error res = nlohmann::json::parse(data);
- emit proxy->stateEventError(QString::fromStdString(res.error));
- } catch (const std::exception &e) {
- emit proxy->stateEventError(QString::fromStdString(e.what()));
- }
-
- return;
- }
-
- try {
- mtx::responses::EventId res = nlohmann::json::parse(data);
- emit proxy->stateEventSent();
- } catch (const std::exception &e) {
- emit proxy->stateEventError(QString::fromStdString(e.what()));
- }
- });
-
- return proxy;
+//! Initialize the http module
+void
+init();
}
diff --git a/include/RegisterPage.h b/include/RegisterPage.h
index f4d97816..d02de7c4 100644
--- a/include/RegisterPage.h
+++ b/include/RegisterPage.h
@@ -44,6 +44,11 @@ signals:
void backButtonClicked();
void errorOccurred();
void registering();
+ void registerOk();
+ void registerErrorCb(const QString &msg);
+ void registrationFlow(const std::string &user,
+ const std::string &pass,
+ const std::string &session);
private slots:
void onBackButtonClicked();
diff --git a/include/RoomList.h b/include/RoomList.h
index 98d9443e..59b0e865 100644
--- a/include/RoomList.h
+++ b/include/RoomList.h
@@ -60,6 +60,8 @@ signals:
void acceptInvite(const QString &room_id);
void declineInvite(const QString &room_id);
void roomAvatarChanged(const QString &room_id, const QPixmap &img);
+ void joinRoom(const QString &room_id);
+ void updateRoomAvatarCb(const QString &room_id, const QPixmap &img);
public slots:
void updateRoomAvatar(const QString &roomid, const QPixmap &img);
diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h
index c679b9b2..af58c2c3 100644
--- a/include/TextInputWidget.h
+++ b/include/TextInputWidget.h
@@ -129,6 +129,16 @@ public:
QColor borderColor() const { return borderColor_; }
void setBorderColor(QColor &color) { borderColor_ = color; }
+ void disableInput()
+ {
+ input_->setEnabled(false);
+ input_->setPlaceholderText(tr("Connection lost. Nheko is trying to re-connect..."));
+ }
+ void enableInput()
+ {
+ input_->setEnabled(true);
+ input_->setPlaceholderText(tr("Write a message..."));
+ }
public slots:
void openFileSelection();
diff --git a/include/dialogs/ReCaptcha.hpp b/include/dialogs/ReCaptcha.hpp
index 1eda40c7..5f47b0eb 100644
--- a/include/dialogs/ReCaptcha.hpp
+++ b/include/dialogs/ReCaptcha.hpp
@@ -12,7 +12,7 @@ class ReCaptcha : public QWidget
Q_OBJECT
public:
- ReCaptcha(const QString &server, const QString &session, QWidget *parent = nullptr);
+ ReCaptcha(const QString &session, QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *event) override;
diff --git a/include/dialogs/RoomSettings.hpp b/include/dialogs/RoomSettings.hpp
index 375a531e..9a01d5c9 100644
--- a/include/dialogs/RoomSettings.hpp
+++ b/include/dialogs/RoomSettings.hpp
@@ -30,6 +30,9 @@ public:
signals:
void nameChanged(const QString &roomName);
+ void nameEventSentCb(const QString &newName);
+ void topicEventSentCb();
+ void stateEventErrorCb(const QString &msg);
private:
QString roomId_;
diff --git a/include/timeline/TimelineItem.h b/include/timeline/TimelineItem.h
index 9997ec1d..4dcca1a5 100644
--- a/include/timeline/TimelineItem.h
+++ b/include/timeline/TimelineItem.h
@@ -197,12 +197,24 @@ public:
void sendReadReceipt() const
{
if (!event_id_.isEmpty())
- http::client()->readEvent(room_id_, event_id_);
+ http::v2::client()->read_event(
+ room_id_.toStdString(),
+ event_id_.toStdString(),
+ [this](mtx::http::RequestErr err) {
+ if (err) {
+ qWarning() << QString("failed to read_event (%1, %2)")
+ .arg(room_id_, event_id_);
+ }
+ });
}
//! Add a user avatar for this event.
void addAvatar();
+signals:
+ void eventRedacted(const QString &event_id);
+ void redactionFailed(const QString &msg);
+
protected:
void paintEvent(QPaintEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
diff --git a/include/timeline/TimelineView.h b/include/timeline/TimelineView.h
index e6e35ccb..30af97fb 100644
--- a/include/timeline/TimelineView.h
+++ b/include/timeline/TimelineView.h
@@ -18,7 +18,6 @@
#pragma once
#include <QApplication>
-#include <QDebug>
#include <QLayout>
#include <QList>
#include <QQueue>
@@ -42,31 +41,13 @@ struct DescInfo;
struct PendingMessage
{
mtx::events::MessageType ty;
- int txn_id;
+ std::string txn_id;
QString body;
QString filename;
QString mime;
uint64_t media_size;
QString event_id;
TimelineItem *widget;
-
- PendingMessage(mtx::events::MessageType ty,
- int txn_id,
- QString body,
- QString filename,
- QString mime,
- uint64_t media_size,
- QString event_id,
- TimelineItem *widget)
- : ty(ty)
- , txn_id(txn_id)
- , body(body)
- , filename(filename)
- , mime(mime)
- , media_size(media_size)
- , event_id(event_id)
- , widget(widget)
- {}
};
// In which place new TimelineItems should be inserted.
@@ -129,7 +110,7 @@ public:
const QString &filename,
const QString &mime,
uint64_t size);
- void updatePendingMessage(int txn_id, QString event_id);
+ void updatePendingMessage(const std::string &txn_id, const QString &event_id);
void scrollDown();
QLabel *createDateSeparator(QDateTime datetime);
@@ -142,18 +123,21 @@ public slots:
void fetchHistory();
// Add old events at the top of the timeline.
- void addBackwardsEvents(const QString &room_id, const mtx::responses::Messages &msgs);
+ void addBackwardsEvents(const mtx::responses::Messages &msgs);
// Whether or not the initial batch has been loaded.
bool hasLoaded() { return scroll_layout_->count() > 1 || isTimelineFinished; }
- void handleFailedMessage(int txnid);
+ void handleFailedMessage(const std::string &txn_id);
private slots:
void sendNextPendingMessage();
signals:
void updateLastTimelineMessage(const QString &user, const DescInfo &info);
+ void messagesRetrieved(const mtx::responses::Messages &res);
+ void messageFailed(const std::string &txn_id);
+ void messageSent(const std::string &txn_id, const QString &event_id);
protected:
void paintEvent(QPaintEvent *event) override;
@@ -165,6 +149,13 @@ private:
QWidget *relativeWidget(TimelineItem *item, int dt) const;
+ //! Callback for all message sending.
+ void sendRoomMessageHandler(const std::string &txn_id,
+ const mtx::responses::EventId &res,
+ mtx::http::RequestErr err);
+
+ //! Call the /messages endpoint to fill the timeline.
+ void getMessages();
//! HACK: Fixing layout flickering when adding to the bottom
//! of the timeline.
void pushTimelineItem(TimelineItem *item)
@@ -230,8 +221,10 @@ private:
uint64_t origin_server_ts,
TimelineDirection direction);
- bool isPendingMessage(const QString &txnid, const QString &sender, const QString &userid);
- void removePendingMessage(const QString &txnid);
+ bool isPendingMessage(const std::string &txn_id,
+ const QString &sender,
+ const QString &userid);
+ void removePendingMessage(const std::string &txn_id);
bool isDuplicate(const QString &event_id) { return eventIds_.contains(event_id); }
@@ -320,9 +313,15 @@ TimelineView::addUserMessage(const QString &url,
// Keep track of the sender and the timestamp of the current message.
saveLastMessageInfo(local_user_, QDateTime::currentDateTime());
- int txn_id = http::client()->incrementTransactionId();
+ PendingMessage message;
+ message.ty = MsgType;
+ message.txn_id = mtx::client::utils::random_token();
+ message.body = url;
+ message.filename = trimmed;
+ message.mime = mime;
+ message.media_size = size;
+ message.widget = view_item;
- PendingMessage message(MsgType, txn_id, url, trimmed, mime, size, "", view_item);
handleNewUserMessage(message);
}
@@ -351,10 +350,10 @@ TimelineView::processMessageEvent(const Event &event, TimelineDirection directio
const auto event_id = QString::fromStdString(event.event_id);
const auto sender = QString::fromStdString(event.sender);
- const QString txnid = QString::fromStdString(event.unsigned_data.transaction_id);
- if ((!txnid.isEmpty() && isPendingMessage(txnid, sender, local_user_)) ||
+ const auto txn_id = event.unsigned_data.transaction_id;
+ if ((!txn_id.empty() && isPendingMessage(txn_id, sender, local_user_)) ||
isDuplicate(event_id)) {
- removePendingMessage(txnid);
+ removePendingMessage(txn_id);
return nullptr;
}
@@ -376,10 +375,10 @@ TimelineView::processMessageEvent(const Event &event, TimelineDirection directio
const auto event_id = QString::fromStdString(event.event_id);
const auto sender = QString::fromStdString(event.sender);
- const QString txnid = QString::fromStdString(event.unsigned_data.transaction_id);
- if ((!txnid.isEmpty() && isPendingMessage(txnid, sender, local_user_)) ||
+ const auto txn_id = event.unsigned_data.transaction_id;
+ if ((!txn_id.empty() && isPendingMessage(txn_id, sender, local_user_)) ||
isDuplicate(event_id)) {
- removePendingMessage(txnid);
+ removePendingMessage(txn_id);
return nullptr;
}
diff --git a/include/timeline/TimelineViewManager.h b/include/timeline/TimelineViewManager.h
index 308b83aa..9e31ecbf 100644
--- a/include/timeline/TimelineViewManager.h
+++ b/include/timeline/TimelineViewManager.h
@@ -56,6 +56,8 @@ signals:
void updateRoomsLastMessage(const QString &user, const DescInfo &info);
public slots:
+ void removeTimelineEvent(const QString &room_id, const QString &event_id);
+
void setHistoryView(const QString &room_id);
void queueTextMessage(const QString &msg);
void queueEmoteMessage(const QString &msg);
@@ -80,10 +82,6 @@ public slots:
const QString &mime,
uint64_t dsize);
-private slots:
- void messageSent(const QString &eventid, const QString &roomid, int txnid);
- void messageSendFailed(const QString &roomid, int txnid);
-
private:
//! Check if the given room id is managed by a TimelineView.
bool timelineViewExists(const QString &id) { return views_.find(id) != views_.end(); }
diff --git a/include/timeline/widgets/AudioItem.h b/include/timeline/widgets/AudioItem.h
index b31385d1..7b0781a2 100644
--- a/include/timeline/widgets/AudioItem.h
+++ b/include/timeline/widgets/AudioItem.h
@@ -69,9 +69,14 @@ protected:
void resizeEvent(QResizeEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
+signals:
+ void fileDownloadedCb(const QByteArray &data);
+
+private slots:
+ void fileDownloaded(const QByteArray &data);
+
private:
void init();
- void fileDownloaded(const QByteArray &data);
enum class AudioState
{
diff --git a/include/timeline/widgets/FileItem.h b/include/timeline/widgets/FileItem.h
index 09181d32..66543e79 100644
--- a/include/timeline/widgets/FileItem.h
+++ b/include/timeline/widgets/FileItem.h
@@ -52,15 +52,20 @@ public:
QColor iconColor() const { return iconColor_; }
QColor backgroundColor() const { return backgroundColor_; }
+signals:
+ void fileDownloadedCb(const QByteArray &data);
+
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
+private slots:
+ void fileDownloaded(const QByteArray &data);
+
private:
void openUrl();
void init();
- void fileDownloaded(const QByteArray &data);
QUrl url_;
QString text_;
diff --git a/include/timeline/widgets/ImageItem.h b/include/timeline/widgets/ImageItem.h
index b17b2d8b..e9d823f4 100644
--- a/include/timeline/widgets/ImageItem.h
+++ b/include/timeline/widgets/ImageItem.h
@@ -40,13 +40,17 @@ public:
uint64_t size,
QWidget *parent = nullptr);
- void setImage(const QPixmap &image);
-
QSize sizeHint() const override;
public slots:
//! Show a save as dialog for the image.
void saveAs();
+ void setImage(const QPixmap &image);
+ void saveImage(const QString &filename, const QByteArray &data);
+
+signals:
+ void imageDownloaded(const QPixmap &img);
+ void imageSaved(const QString &filename, const QByteArray &data);
protected:
void paintEvent(QPaintEvent *event) override;
@@ -57,7 +61,9 @@ protected:
bool isInteractive_ = true;
private:
+ void init();
void openUrl();
+ void downloadMedia(const QUrl &url);
int max_width_ = 500;
int max_height_ = 300;
|