Implement image uploads (#24)

This commit is contained in:
Konstantinos Sideris 2017-09-10 12:58:00 +03:00
parent ed36bdb037
commit edff71bc24
22 changed files with 1017 additions and 916 deletions

View file

@ -26,47 +26,52 @@
#include "MatrixClient.h"
namespace events = matrix::events;
namespace msgs = matrix::events::messages;
namespace msgs = matrix::events::messages;
class ImageItem : public QWidget
{
Q_OBJECT
Q_OBJECT
public:
ImageItem(QSharedPointer<MatrixClient> client,
const events::MessageEvent<msgs::Image> &event,
QWidget *parent = nullptr);
ImageItem(QSharedPointer<MatrixClient> client,
const events::MessageEvent<msgs::Image> &event,
QWidget *parent = nullptr);
void setImage(const QPixmap &image);
ImageItem(QSharedPointer<MatrixClient> client,
const QString &url,
const QString &filename,
QWidget *parent = nullptr);
QSize sizeHint() const override;
void setImage(const QPixmap &image);
QSize sizeHint() const override;
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
private slots:
void imageDownloaded(const QString &event_id, const QPixmap &img);
void imageDownloaded(const QString &event_id, const QPixmap &img);
private:
void scaleImage();
void openUrl();
void scaleImage();
void openUrl();
int max_width_ = 500;
int max_height_ = 300;
int max_width_ = 500;
int max_height_ = 300;
int width_;
int height_;
int width_;
int height_;
QPixmap scaled_image_;
QPixmap image_;
QPixmap scaled_image_;
QPixmap image_;
QUrl url_;
QString text_;
QUrl url_;
QString text_;
int bottom_height_ = 30;
int bottom_height_ = 30;
events::MessageEvent<msgs::Image> event_;
events::MessageEvent<msgs::Image> event_;
QSharedPointer<MatrixClient> client_;
QSharedPointer<MatrixClient> client_;
};

View file

@ -23,8 +23,8 @@
#include <QVBoxLayout>
#include <QWidget>
#include "CircularProgress.h"
#include "FlatButton.h"
#include "LoadingIndicator.h"
#include "MatrixClient.h"
#include "OverlayModal.h"
#include "RaisedButton.h"
@ -32,68 +32,68 @@
class LoginPage : public QWidget
{
Q_OBJECT
Q_OBJECT
public:
LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
~LoginPage();
LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
~LoginPage();
void reset();
void reset();
signals:
void backButtonClicked();
void backButtonClicked();
private slots:
// Callback for the back button.
void onBackButtonClicked();
// Callback for the back button.
void onBackButtonClicked();
// Callback for the login button.
void onLoginButtonClicked();
// Callback for the login button.
void onLoginButtonClicked();
// Callback for probing the server found in the mxid
void onMatrixIdEntered();
// Callback for probing the server found in the mxid
void onMatrixIdEntered();
// Callback for probing the manually entered server
void onServerAddressEntered();
// Callback for probing the manually entered server
void onServerAddressEntered();
// Displays errors produced during the login.
void loginError(QString error_message);
// Displays errors produced during the login.
void loginError(QString error_message);
// Callback for errors produced during server probing
void versionError(QString error_message);
// Callback for errors produced during server probing
void versionError(QString error_message);
// Callback for successful server probing
void versionSuccess();
// Callback for successful server probing
void versionSuccess();
private:
bool isMatrixIdValid();
bool isMatrixIdValid();
QVBoxLayout *top_layout_;
QVBoxLayout *top_layout_;
QHBoxLayout *top_bar_layout_;
QHBoxLayout *logo_layout_;
QHBoxLayout *button_layout_;
QHBoxLayout *top_bar_layout_;
QHBoxLayout *logo_layout_;
QHBoxLayout *button_layout_;
QLabel *logo_;
QLabel *error_label_;
QLabel *logo_;
QLabel *error_label_;
QHBoxLayout *serverLayout_;
QHBoxLayout *matrixidLayout_;
CircularProgress *spinner_;
QLabel *errorIcon_;
QString inferredServerAddress_;
QHBoxLayout *serverLayout_;
QHBoxLayout *matrixidLayout_;
LoadingIndicator *spinner_;
QLabel *errorIcon_;
QString inferredServerAddress_;
FlatButton *back_button_;
RaisedButton *login_button_;
FlatButton *back_button_;
RaisedButton *login_button_;
QWidget *form_widget_;
QHBoxLayout *form_wrapper_;
QVBoxLayout *form_layout_;
QWidget *form_widget_;
QHBoxLayout *form_wrapper_;
QVBoxLayout *form_layout_;
TextField *matrixid_input_;
TextField *password_input_;
TextField *serverInput_;
TextField *matrixid_input_;
TextField *password_input_;
TextField *serverInput_;
// Matrix client API provider.
QSharedPointer<MatrixClient> client_;
// Matrix client API provider.
QSharedPointer<MatrixClient> client_;
};

View file

@ -21,7 +21,7 @@
#include <QSharedPointer>
#include "ChatPage.h"
#include "CircularProgress.h"
#include "LoadingIndicator.h"
#include "LoginPage.h"
#include "MatrixClient.h"
#include "OverlayModal.h"
@ -32,64 +32,64 @@
class MainWindow : public QMainWindow
{
Q_OBJECT
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static MainWindow *instance();
void saveCurrentWindowSize();
static MainWindow *instance();
void saveCurrentWindowSize();
protected:
void closeEvent(QCloseEvent *event);
void closeEvent(QCloseEvent *event);
private slots:
// Handle interaction with the tray icon.
void iconActivated(QSystemTrayIcon::ActivationReason reason);
// Handle interaction with the tray icon.
void iconActivated(QSystemTrayIcon::ActivationReason reason);
// Show the welcome page in the main window.
void showWelcomePage();
// Show the welcome page in the main window.
void showWelcomePage();
// Show the login page in the main window.
void showLoginPage();
// Show the login page in the main window.
void showLoginPage();
// Show the register page in the main window.
void showRegisterPage();
// Show the register page in the main window.
void showRegisterPage();
// Show the chat page and start communicating with the given access token.
void showChatPage(QString user_id, QString home_server, QString token);
// Show the chat page and start communicating with the given access token.
void showChatPage(QString user_id, QString home_server, QString token);
void removeOverlayProgressBar();
void removeOverlayProgressBar();
private:
bool hasActiveUser();
void restoreWindowSize();
bool hasActiveUser();
void restoreWindowSize();
static MainWindow *instance_;
static MainWindow *instance_;
// The initial welcome screen.
WelcomePage *welcome_page_;
// The initial welcome screen.
WelcomePage *welcome_page_;
// The login screen.
LoginPage *login_page_;
// The login screen.
LoginPage *login_page_;
// The register page.
RegisterPage *register_page_;
// The register page.
RegisterPage *register_page_;
// A stacked widget that handles the transitions between widgets.
SlidingStackWidget *sliding_stack_;
// A stacked widget that handles the transitions between widgets.
SlidingStackWidget *sliding_stack_;
// The main chat area.
ChatPage *chat_page_;
// The main chat area.
ChatPage *chat_page_;
// Used to hide undefined states between page transitions.
OverlayModal *progress_modal_;
CircularProgress *spinner_;
// Used to hide undefined states between page transitions.
OverlayModal *progress_modal_;
LoadingIndicator *spinner_;
// Matrix Client API provider.
QSharedPointer<MatrixClient> client_;
// Matrix Client API provider.
QSharedPointer<MatrixClient> client_;
// Tray icon that shows the unread message count.
TrayIcon *trayIcon_;
// Tray icon that shows the unread message count.
TrayIcon *trayIcon_;
};

View file

@ -39,7 +39,8 @@ public:
void sync() noexcept;
void sendRoomMessage(matrix::events::MessageEventType ty,
const QString &roomid,
const QString &msg) noexcept;
const QString &msg,
const QString &url = "") noexcept;
void login(const QString &username, const QString &password) noexcept;
void registerUser(const QString &username,
const QString &password,
@ -50,6 +51,7 @@ public:
void fetchOwnAvatar(const QUrl &avatar_url);
void downloadImage(const QString &event_id, const QUrl &url);
void messages(const QString &room_id, const QString &from_token, int limit = 20) noexcept;
void uploadImage(const QString &roomid, const QString &filename);
inline QUrl getHomeServer();
inline int transactionId();
@ -77,6 +79,7 @@ signals:
const QString &homeserver,
const QString &token);
void versionSuccess();
void imageUploaded(const QString &roomid, const QString &filename, const QString &url);
void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
void userAvatarRetrieved(const QString &userId, const QImage &img);
@ -102,6 +105,7 @@ private:
GetProfile,
Image,
InitialSync,
ImageUpload,
Login,
Logout,
Messages,
@ -118,6 +122,7 @@ private:
void onGetOwnProfileResponse(QNetworkReply *reply);
void onImageResponse(QNetworkReply *reply);
void onInitialSyncResponse(QNetworkReply *reply);
void onImageUploadResponse(QNetworkReply *reply);
void onLoginResponse(QNetworkReply *reply);
void onLogoutResponse(QNetworkReply *reply);
void onMessagesResponse(QNetworkReply *reply);
@ -129,7 +134,10 @@ private:
void onVersionsResponse(QNetworkReply *reply);
// Client API prefix.
QString api_url_;
QString clientApiUrl_;
// Media API prefix.
QString mediaApiUrl_;
// The Matrix server used for communication.
QUrl server_;

View file

@ -24,6 +24,10 @@
#include "EmojiPickButton.h"
#include "FlatButton.h"
#include "Image.h"
#include "LoadingIndicator.h"
namespace msgs = matrix::events::messages;
static const QString EMOTE_COMMAND("/me ");
@ -48,6 +52,8 @@ public:
public slots:
void onSendButtonClicked();
void openFileSelection();
void hideUploadSpinner();
inline void focusLineEdit();
private slots:
@ -56,16 +62,20 @@ private slots:
signals:
void sendTextMessage(QString msg);
void sendEmoteMessage(QString msg);
void uploadImage(QString filename);
private:
void showUploadSpinner();
QString parseEmoteCommand(const QString &cmd);
QHBoxLayout *top_layout_;
QHBoxLayout *topLayout_;
FilteredTextEdit *input_;
FlatButton *send_file_button_;
FlatButton *send_message_button_;
EmojiPickButton *emoji_button_;
LoadingIndicator *spinner_;
FlatButton *sendFileBtn_;
FlatButton *sendMessageBtn_;
EmojiPickButton *emojiBtn_;
};
inline void

View file

@ -50,11 +50,14 @@ public:
QWidget *parent = 0);
// For local messages.
// m.text & m.emote
TimelineItem(events::MessageEventType ty,
const QString &userid,
QString body,
bool withSender,
QWidget *parent = 0);
// m.image
TimelineItem(ImageItem *item, const QString &userid, bool withSender, QWidget *parent = 0);
TimelineItem(ImageItem *img,
const events::MessageEvent<msgs::Image> &e,

View file

@ -85,6 +85,7 @@ public:
// Add new events at the end of the timeline.
int addEvents(const Timeline &timeline);
void addUserMessage(matrix::events::MessageEventType ty, const QString &msg, int txn_id);
void addUserMessage(const QString &url, const QString &filename, int txn_id);
void updatePendingMessage(int txn_id, QString event_id);
void scrollDown();
@ -108,11 +109,11 @@ private:
// Used to determine whether or not we should prefix a message with the sender's name.
bool isSenderRendered(const QString &user_id, TimelineDirection direction);
template<class T>
bool isPendingMessage(const events::MessageEvent<T> &e, const QString &userid);
template<class T>
void removePendingMessage(const events::MessageEvent<T> &e);
bool isPendingMessage(const QString &eventid,
const QString &body,
const QString &sender,
const QString &userid);
void removePendingMessage(const QString &eventid, const QString &body);
inline bool isDuplicate(const QString &event_id);
@ -159,32 +160,3 @@ TimelineView::isDuplicate(const QString &event_id)
{
return eventIds_.contains(event_id);
}
template<class T>
bool
TimelineView::isPendingMessage(const events::MessageEvent<T> &e, const QString &local_userid)
{
if (e.sender() != local_userid)
return false;
for (const auto &msg : pending_msgs_) {
if (msg.event_id == e.eventId() || msg.body == e.content().body())
return true;
}
return false;
}
template<class T>
void
TimelineView::removePendingMessage(const events::MessageEvent<T> &e)
{
for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); it++) {
int index = std::distance(pending_msgs_.begin(), it);
if (it->event_id == e.eventId() || it->body == e.content().body()) {
pending_msgs_.removeAt(index);
break;
}
}
}

View file

@ -56,6 +56,7 @@ public slots:
void setHistoryView(const QString &room_id);
void sendTextMessage(const QString &msg);
void sendEmoteMessage(const QString &msg);
void sendImageMessage(const QString &roomid, const QString &filename, const QString &url);
private slots:
void messageSent(const QString &eventid, const QString &roomid, int txnid);

View file

@ -1,120 +0,0 @@
#pragma once
#include <QObject>
#include <QProgressBar>
#include "Theme.h"
class CircularProgressDelegate;
class CircularProgress : public QProgressBar
{
Q_OBJECT
Q_PROPERTY(qreal lineWidth WRITE setLineWidth READ lineWidth)
Q_PROPERTY(qreal size WRITE setSize READ size)
Q_PROPERTY(QColor color WRITE setColor READ color)
public:
explicit CircularProgress(QWidget *parent = nullptr);
~CircularProgress();
void setProgressType(ui::ProgressType type);
void setLineWidth(qreal width);
void setSize(int size);
void setColor(const QColor &color);
ui::ProgressType progressType() const;
qreal lineWidth() const;
int size() const;
QColor color() const;
QSize sizeHint() const override;
protected:
void paintEvent(QPaintEvent *event) override;
private:
CircularProgressDelegate *delegate_;
ui::ProgressType progress_type_;
QColor color_;
// Circle width.
qreal width_;
// Circle radius.
int size_;
// Animation duration.
int duration_;
};
class CircularProgressDelegate : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal dashOffset WRITE setDashOffset READ dashOffset)
Q_PROPERTY(qreal dashLength WRITE setDashLength READ dashLength)
Q_PROPERTY(int angle WRITE setAngle READ angle)
public:
explicit CircularProgressDelegate(CircularProgress *parent);
~CircularProgressDelegate();
inline void setDashOffset(qreal offset);
inline void setDashLength(qreal length);
inline void setAngle(int angle);
inline qreal dashOffset() const;
inline qreal dashLength() const;
inline int angle() const;
private:
CircularProgress *const progress_;
qreal dash_offset_;
qreal dash_length_;
int angle_;
};
inline void
CircularProgressDelegate::setDashOffset(qreal offset)
{
dash_offset_ = offset;
progress_->update();
}
inline void
CircularProgressDelegate::setDashLength(qreal length)
{
dash_length_ = length;
progress_->update();
}
inline void
CircularProgressDelegate::setAngle(int angle)
{
angle_ = angle;
progress_->update();
}
inline qreal
CircularProgressDelegate::dashOffset() const
{
return dash_offset_;
}
inline qreal
CircularProgressDelegate::dashLength() const
{
return dash_length_;
}
inline int
CircularProgressDelegate::angle() const
{
return angle_;
}

View file

@ -0,0 +1,49 @@
#pragma once
#include <QColor>
#include <QPaintEvent>
#include <QPainter>
#include <QTimer>
#include <QWidget>
class LoadingIndicator : public QWidget
{
Q_OBJECT
public:
LoadingIndicator(QWidget *parent = 0);
virtual ~LoadingIndicator();
void paintEvent(QPaintEvent *e);
void start();
void stop();
QColor color()
{
return color_;
}
void setColor(QColor color)
{
color_ = color;
}
int interval()
{
return interval_;
}
void setInterval(int interval)
{
interval_ = interval;
}
private slots:
void onTimeout();
private:
int interval_;
int angle_;
QColor color_;
QTimer *timer_;
};