summary refs log tree commit diff
path: root/include
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2018-06-09 16:03:14 +0300
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2018-06-09 16:03:14 +0300
commitb89257a34b2a98b737f4ae544f7e436b9000b240 (patch)
tree81d7f355721541afbd91dc9a085abbb4666f3565 /include
parentInstall missing dependencies in travis-ci/appveyor (diff)
downloadnheko-b89257a34b2a98b737f4ae544f7e436b9000b240.tar.xz
Migrate to mtxclient for the http calls
Diffstat (limited to 'include')
-rw-r--r--include/AvatarProvider.h18
-rw-r--r--include/Cache.h3
-rw-r--r--include/ChatPage.h65
-rw-r--r--include/CommunitiesList.h2
-rw-r--r--include/Logging.hpp18
-rw-r--r--include/LoginPage.h33
-rw-r--r--include/MainWindow.h3
-rw-r--r--include/MatrixClient.h290
-rw-r--r--include/RegisterPage.h5
-rw-r--r--include/RoomList.h2
-rw-r--r--include/TextInputWidget.h10
-rw-r--r--include/dialogs/ReCaptcha.hpp2
-rw-r--r--include/dialogs/RoomSettings.hpp3
-rw-r--r--include/timeline/TimelineItem.h14
-rw-r--r--include/timeline/TimelineView.h65
-rw-r--r--include/timeline/TimelineViewManager.h6
-rw-r--r--include/timeline/widgets/AudioItem.h7
-rw-r--r--include/timeline/widgets/FileItem.h7
-rw-r--r--include/timeline/widgets/ImageItem.h10
19 files changed, 219 insertions, 344 deletions
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 &notifications); - -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;