mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
parent
a44a4f36af
commit
b5ae84c3c3
11 changed files with 747 additions and 686 deletions
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include <QtNetwork/QNetworkAccessManager>
|
#include <QtNetwork/QNetworkAccessManager>
|
||||||
|
|
||||||
|
#include "MessageEvent.h"
|
||||||
#include "Profile.h"
|
#include "Profile.h"
|
||||||
#include "RoomMessages.h"
|
#include "RoomMessages.h"
|
||||||
#include "Sync.h"
|
#include "Sync.h"
|
||||||
|
@ -29,145 +30,152 @@
|
||||||
*/
|
*/
|
||||||
class MatrixClient : public QNetworkAccessManager
|
class MatrixClient : public QNetworkAccessManager
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
MatrixClient(QString server, QObject *parent = 0);
|
MatrixClient(QString server, QObject *parent = 0);
|
||||||
|
|
||||||
// Client API.
|
// Client API.
|
||||||
void initialSync() noexcept;
|
void initialSync() noexcept;
|
||||||
void sync() noexcept;
|
void sync() noexcept;
|
||||||
void sendTextMessage(const QString &roomid, const QString &msg) noexcept;
|
void sendRoomMessage(matrix::events::MessageEventType ty,
|
||||||
void login(const QString &username, const QString &password) noexcept;
|
const QString &roomid,
|
||||||
void registerUser(const QString &username, const QString &password, const QString &server) noexcept;
|
const QString &msg) noexcept;
|
||||||
void versions() noexcept;
|
void login(const QString &username, const QString &password) noexcept;
|
||||||
void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
|
void registerUser(const QString &username,
|
||||||
void fetchUserAvatar(const QString &userId, const QUrl &avatarUrl);
|
const QString &password,
|
||||||
void fetchOwnAvatar(const QUrl &avatar_url);
|
const QString &server) noexcept;
|
||||||
void downloadImage(const QString &event_id, const QUrl &url);
|
void versions() noexcept;
|
||||||
void messages(const QString &room_id, const QString &from_token, int limit = 20) noexcept;
|
void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
|
||||||
|
void fetchUserAvatar(const QString &userId, const QUrl &avatarUrl);
|
||||||
|
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;
|
||||||
|
|
||||||
inline QUrl getHomeServer();
|
inline QUrl getHomeServer();
|
||||||
inline int transactionId();
|
inline int transactionId();
|
||||||
inline void incrementTransactionId();
|
inline void incrementTransactionId();
|
||||||
|
|
||||||
void reset() noexcept;
|
void reset() noexcept;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void getOwnProfile() noexcept;
|
void getOwnProfile() noexcept;
|
||||||
void logout() noexcept;
|
void logout() noexcept;
|
||||||
|
|
||||||
inline void setServer(const QString &server);
|
inline void setServer(const QString &server);
|
||||||
inline void setAccessToken(const QString &token);
|
inline void setAccessToken(const QString &token);
|
||||||
inline void setNextBatchToken(const QString &next_batch);
|
inline void setNextBatchToken(const QString &next_batch);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void loginError(const QString &error);
|
void loginError(const QString &error);
|
||||||
void registerError(const QString &error);
|
void registerError(const QString &error);
|
||||||
void versionError(const QString &error);
|
void versionError(const QString &error);
|
||||||
|
|
||||||
void loggedOut();
|
void loggedOut();
|
||||||
|
|
||||||
void loginSuccess(const QString &userid, const QString &homeserver, const QString &token);
|
void loginSuccess(const QString &userid, const QString &homeserver, const QString &token);
|
||||||
void registerSuccess(const QString &userid, const QString &homeserver, const QString &token);
|
void registerSuccess(const QString &userid,
|
||||||
void versionSuccess();
|
const QString &homeserver,
|
||||||
|
const QString &token);
|
||||||
|
void versionSuccess();
|
||||||
|
|
||||||
void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
|
void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
|
||||||
void userAvatarRetrieved(const QString &userId, const QImage &img);
|
void userAvatarRetrieved(const QString &userId, const QImage &img);
|
||||||
void ownAvatarRetrieved(const QPixmap &img);
|
void ownAvatarRetrieved(const QPixmap &img);
|
||||||
void imageDownloaded(const QString &event_id, const QPixmap &img);
|
void imageDownloaded(const QString &event_id, const QPixmap &img);
|
||||||
|
|
||||||
// Returned profile data for the user's account.
|
// Returned profile data for the user's account.
|
||||||
void getOwnProfileResponse(const QUrl &avatar_url, const QString &display_name);
|
void getOwnProfileResponse(const QUrl &avatar_url, const QString &display_name);
|
||||||
void initialSyncCompleted(const SyncResponse &response);
|
void initialSyncCompleted(const SyncResponse &response);
|
||||||
void syncCompleted(const SyncResponse &response);
|
void syncCompleted(const SyncResponse &response);
|
||||||
void syncFailed(const QString &msg);
|
void syncFailed(const QString &msg);
|
||||||
void messageSent(const QString &event_id, const QString &roomid, const int txn_id);
|
void messageSent(const QString &event_id, const QString &roomid, const int txn_id);
|
||||||
void messagesRetrieved(const QString &room_id, const RoomMessages &msgs);
|
void emoteSent(const QString &event_id, const QString &roomid, const int txn_id);
|
||||||
|
void messagesRetrieved(const QString &room_id, const RoomMessages &msgs);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onResponse(QNetworkReply *reply);
|
void onResponse(QNetworkReply *reply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Endpoint {
|
enum class Endpoint {
|
||||||
GetOwnAvatar,
|
GetOwnAvatar,
|
||||||
GetOwnProfile,
|
GetOwnProfile,
|
||||||
GetProfile,
|
GetProfile,
|
||||||
Image,
|
Image,
|
||||||
InitialSync,
|
InitialSync,
|
||||||
Login,
|
Login,
|
||||||
Logout,
|
Logout,
|
||||||
Messages,
|
Messages,
|
||||||
Register,
|
Register,
|
||||||
RoomAvatar,
|
RoomAvatar,
|
||||||
UserAvatar,
|
SendRoomMessage,
|
||||||
SendTextMessage,
|
Sync,
|
||||||
Sync,
|
UserAvatar,
|
||||||
Versions,
|
Versions,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Response handlers.
|
// Response handlers.
|
||||||
void onLoginResponse(QNetworkReply *reply);
|
void onGetOwnAvatarResponse(QNetworkReply *reply);
|
||||||
void onLogoutResponse(QNetworkReply *reply);
|
void onGetOwnProfileResponse(QNetworkReply *reply);
|
||||||
void onRegisterResponse(QNetworkReply *reply);
|
void onImageResponse(QNetworkReply *reply);
|
||||||
void onVersionsResponse(QNetworkReply *reply);
|
void onInitialSyncResponse(QNetworkReply *reply);
|
||||||
void onGetOwnProfileResponse(QNetworkReply *reply);
|
void onLoginResponse(QNetworkReply *reply);
|
||||||
void onGetOwnAvatarResponse(QNetworkReply *reply);
|
void onLogoutResponse(QNetworkReply *reply);
|
||||||
void onSendTextMessageResponse(QNetworkReply *reply);
|
void onMessagesResponse(QNetworkReply *reply);
|
||||||
void onInitialSyncResponse(QNetworkReply *reply);
|
void onRegisterResponse(QNetworkReply *reply);
|
||||||
void onSyncResponse(QNetworkReply *reply);
|
void onRoomAvatarResponse(QNetworkReply *reply);
|
||||||
void onRoomAvatarResponse(QNetworkReply *reply);
|
void onSendRoomMessage(QNetworkReply *reply);
|
||||||
void onUserAvatarResponse(QNetworkReply *reply);
|
void onSyncResponse(QNetworkReply *reply);
|
||||||
void onImageResponse(QNetworkReply *reply);
|
void onUserAvatarResponse(QNetworkReply *reply);
|
||||||
void onMessagesResponse(QNetworkReply *reply);
|
void onVersionsResponse(QNetworkReply *reply);
|
||||||
|
|
||||||
// Client API prefix.
|
// Client API prefix.
|
||||||
QString api_url_;
|
QString api_url_;
|
||||||
|
|
||||||
// The Matrix server used for communication.
|
// The Matrix server used for communication.
|
||||||
QUrl server_;
|
QUrl server_;
|
||||||
|
|
||||||
// The access token used for authentication.
|
// The access token used for authentication.
|
||||||
QString token_;
|
QString token_;
|
||||||
|
|
||||||
// Increasing transaction ID.
|
// Increasing transaction ID.
|
||||||
int txn_id_;
|
int txn_id_;
|
||||||
|
|
||||||
// Token to be used for the next sync.
|
// Token to be used for the next sync.
|
||||||
QString next_batch_;
|
QString next_batch_;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QUrl
|
inline QUrl
|
||||||
MatrixClient::getHomeServer()
|
MatrixClient::getHomeServer()
|
||||||
{
|
{
|
||||||
return server_;
|
return server_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int
|
inline int
|
||||||
MatrixClient::transactionId()
|
MatrixClient::transactionId()
|
||||||
{
|
{
|
||||||
return txn_id_;
|
return txn_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
MatrixClient::setServer(const QString &server)
|
MatrixClient::setServer(const QString &server)
|
||||||
{
|
{
|
||||||
server_ = QUrl(QString("https://%1").arg(server));
|
server_ = QUrl(QString("https://%1").arg(server));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
MatrixClient::setAccessToken(const QString &token)
|
MatrixClient::setAccessToken(const QString &token)
|
||||||
{
|
{
|
||||||
token_ = token;
|
token_ = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
MatrixClient::setNextBatchToken(const QString &next_batch)
|
MatrixClient::setNextBatchToken(const QString &next_batch)
|
||||||
{
|
{
|
||||||
next_batch_ = next_batch;
|
next_batch_ = next_batch;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
MatrixClient::incrementTransactionId()
|
MatrixClient::incrementTransactionId()
|
||||||
{
|
{
|
||||||
txn_id_ += 1;
|
txn_id_ += 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,49 +25,51 @@
|
||||||
#include "EmojiPickButton.h"
|
#include "EmojiPickButton.h"
|
||||||
#include "FlatButton.h"
|
#include "FlatButton.h"
|
||||||
|
|
||||||
|
static const QString EMOTE_COMMAND("/me ");
|
||||||
|
|
||||||
class FilteredTextEdit : public QTextEdit
|
class FilteredTextEdit : public QTextEdit
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit FilteredTextEdit(QWidget *parent = nullptr);
|
explicit FilteredTextEdit(QWidget *parent = nullptr);
|
||||||
void keyPressEvent(QKeyEvent *event);
|
void keyPressEvent(QKeyEvent *event);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void enterPressed();
|
void enterPressed();
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextInputWidget : public QWidget
|
class TextInputWidget : public QFrame
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TextInputWidget(QWidget *parent = 0);
|
TextInputWidget(QWidget *parent = 0);
|
||||||
~TextInputWidget();
|
~TextInputWidget();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onSendButtonClicked();
|
void onSendButtonClicked();
|
||||||
inline void focusLineEdit();
|
inline void focusLineEdit();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void addSelectedEmoji(const QString &emoji);
|
void addSelectedEmoji(const QString &emoji);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sendTextMessage(QString msg);
|
void sendTextMessage(QString msg);
|
||||||
|
void sendEmoteMessage(QString msg);
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHBoxLayout *top_layout_;
|
QString parseEmoteCommand(const QString &cmd);
|
||||||
FilteredTextEdit *input_;
|
|
||||||
|
|
||||||
FlatButton *send_file_button_;
|
QHBoxLayout *top_layout_;
|
||||||
FlatButton *send_message_button_;
|
FilteredTextEdit *input_;
|
||||||
EmojiPickButton *emoji_button_;
|
|
||||||
|
FlatButton *send_file_button_;
|
||||||
|
FlatButton *send_message_button_;
|
||||||
|
EmojiPickButton *emoji_button_;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
TextInputWidget::focusLineEdit()
|
TextInputWidget::focusLineEdit()
|
||||||
{
|
{
|
||||||
input_->setFocus();
|
input_->setFocus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,11 @@ public:
|
||||||
QWidget *parent = 0);
|
QWidget *parent = 0);
|
||||||
|
|
||||||
// For local messages.
|
// For local messages.
|
||||||
TimelineItem(const QString &userid, QString body, QWidget *parent = 0);
|
TimelineItem(events::MessageEventType ty,
|
||||||
TimelineItem(QString body, QWidget *parent = 0);
|
const QString &userid,
|
||||||
|
QString body,
|
||||||
|
bool withSender,
|
||||||
|
QWidget *parent = 0);
|
||||||
|
|
||||||
TimelineItem(ImageItem *img,
|
TimelineItem(ImageItem *img,
|
||||||
const events::MessageEvent<msgs::Image> &e,
|
const events::MessageEvent<msgs::Image> &e,
|
||||||
|
|
|
@ -27,8 +27,9 @@
|
||||||
#include "Sync.h"
|
#include "Sync.h"
|
||||||
#include "TimelineItem.h"
|
#include "TimelineItem.h"
|
||||||
|
|
||||||
#include "Image.h"
|
|
||||||
#include "Emote.h"
|
#include "Emote.h"
|
||||||
|
#include "Image.h"
|
||||||
|
#include "MessageEvent.h"
|
||||||
#include "Notice.h"
|
#include "Notice.h"
|
||||||
#include "RoomInfoListItem.h"
|
#include "RoomInfoListItem.h"
|
||||||
#include "Text.h"
|
#include "Text.h"
|
||||||
|
@ -83,7 +84,7 @@ public:
|
||||||
|
|
||||||
// Add new events at the end of the timeline.
|
// Add new events at the end of the timeline.
|
||||||
int addEvents(const Timeline &timeline);
|
int addEvents(const Timeline &timeline);
|
||||||
void addUserTextMessage(const QString &msg, int txn_id);
|
void addUserMessage(matrix::events::MessageEventType ty, const QString &msg, int txn_id);
|
||||||
void updatePendingMessage(int txn_id, QString event_id);
|
void updatePendingMessage(int txn_id, QString event_id);
|
||||||
void scrollDown();
|
void scrollDown();
|
||||||
|
|
||||||
|
@ -100,14 +101,19 @@ signals:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
void removePendingMessage(const events::MessageEvent<msgs::Text> &e);
|
|
||||||
void addTimelineItem(TimelineItem *item, TimelineDirection direction);
|
void addTimelineItem(TimelineItem *item, TimelineDirection direction);
|
||||||
void updateLastSender(const QString &user_id, TimelineDirection direction);
|
void updateLastSender(const QString &user_id, TimelineDirection direction);
|
||||||
void notifyForLastEvent();
|
void notifyForLastEvent();
|
||||||
|
|
||||||
// Used to determine whether or not we should prefix a message with the sender's name.
|
// Used to determine whether or not we should prefix a message with the sender's name.
|
||||||
bool isSenderRendered(const QString &user_id, TimelineDirection direction);
|
bool isSenderRendered(const QString &user_id, TimelineDirection direction);
|
||||||
bool isPendingMessage(const events::MessageEvent<msgs::Text> &e, const QString &userid);
|
|
||||||
|
template<class T>
|
||||||
|
bool isPendingMessage(const events::MessageEvent<T> &e, const QString &userid);
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
void removePendingMessage(const events::MessageEvent<T> &e);
|
||||||
|
|
||||||
inline bool isDuplicate(const QString &event_id);
|
inline bool isDuplicate(const QString &event_id);
|
||||||
|
|
||||||
// Return nullptr if the event couldn't be parsed.
|
// Return nullptr if the event couldn't be parsed.
|
||||||
|
@ -153,3 +159,32 @@ TimelineView::isDuplicate(const QString &event_id)
|
||||||
{
|
{
|
||||||
return eventIds_.contains(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
|
#include "MessageEvent.h"
|
||||||
#include "RoomInfoListItem.h"
|
#include "RoomInfoListItem.h"
|
||||||
#include "Sync.h"
|
#include "Sync.h"
|
||||||
#include "TimelineView.h"
|
#include "TimelineView.h"
|
||||||
|
@ -54,6 +55,7 @@ signals:
|
||||||
public slots:
|
public slots:
|
||||||
void setHistoryView(const QString &room_id);
|
void setHistoryView(const QString &room_id);
|
||||||
void sendTextMessage(const QString &msg);
|
void sendTextMessage(const QString &msg);
|
||||||
|
void sendEmoteMessage(const QString &msg);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void messageSent(const QString &eventid, const QString &roomid, int txnid);
|
void messageSent(const QString &eventid, const QString &roomid, int txnid);
|
||||||
|
|
|
@ -148,6 +148,11 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
|
||||||
view_manager_,
|
view_manager_,
|
||||||
SLOT(sendTextMessage(const QString &)));
|
SLOT(sendTextMessage(const QString &)));
|
||||||
|
|
||||||
|
connect(text_input_,
|
||||||
|
SIGNAL(sendEmoteMessage(const QString &)),
|
||||||
|
view_manager_,
|
||||||
|
SLOT(sendEmoteMessage(const QString &)));
|
||||||
|
|
||||||
connect(client_.data(),
|
connect(client_.data(),
|
||||||
SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
|
SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
|
||||||
this,
|
this,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -26,123 +26,133 @@
|
||||||
FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
||||||
: QTextEdit(parent)
|
: QTextEdit(parent)
|
||||||
{
|
{
|
||||||
setAcceptRichText(false);
|
setAcceptRichText(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FilteredTextEdit::keyPressEvent(QKeyEvent *event)
|
FilteredTextEdit::keyPressEvent(QKeyEvent *event)
|
||||||
{
|
{
|
||||||
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
|
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
|
||||||
emit enterPressed();
|
emit enterPressed();
|
||||||
else
|
else
|
||||||
QTextEdit::keyPressEvent(event);
|
QTextEdit::keyPressEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextInputWidget::TextInputWidget(QWidget *parent)
|
TextInputWidget::TextInputWidget(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QFrame(parent)
|
||||||
{
|
{
|
||||||
setFont(QFont("Emoji One"));
|
setFont(QFont("Emoji One"));
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
setCursor(Qt::ArrowCursor);
|
setCursor(Qt::ArrowCursor);
|
||||||
setStyleSheet("background-color: #f8fbfe; height: 45px;");
|
setStyleSheet("background-color: #f8fbfe; height: 45px;");
|
||||||
|
|
||||||
top_layout_ = new QHBoxLayout();
|
top_layout_ = new QHBoxLayout();
|
||||||
top_layout_->setSpacing(0);
|
top_layout_->setSpacing(0);
|
||||||
top_layout_->setMargin(0);
|
top_layout_->setMargin(0);
|
||||||
|
|
||||||
send_file_button_ = new FlatButton(this);
|
send_file_button_ = new FlatButton(this);
|
||||||
|
|
||||||
QIcon send_file_icon;
|
QIcon send_file_icon;
|
||||||
send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
send_file_button_->setForegroundColor(QColor("#acc7dc"));
|
send_file_button_->setForegroundColor(QColor("#acc7dc"));
|
||||||
send_file_button_->setIcon(send_file_icon);
|
send_file_button_->setIcon(send_file_icon);
|
||||||
send_file_button_->setIconSize(QSize(24, 24));
|
send_file_button_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
QFont font;
|
QFont font;
|
||||||
font.setPixelSize(conf::fontSize);
|
font.setPixelSize(conf::fontSize);
|
||||||
|
|
||||||
input_ = new FilteredTextEdit(this);
|
input_ = new FilteredTextEdit(this);
|
||||||
input_->setFixedHeight(45);
|
input_->setFixedHeight(45);
|
||||||
input_->setFont(font);
|
input_->setFont(font);
|
||||||
input_->setPlaceholderText(tr("Write a message..."));
|
input_->setPlaceholderText(tr("Write a message..."));
|
||||||
input_->setStyleSheet("color: #333333; border-radius: 0; padding-top: 10px;");
|
input_->setStyleSheet("color: #333333; border-radius: 0; padding-top: 10px;");
|
||||||
|
|
||||||
send_message_button_ = new FlatButton(this);
|
send_message_button_ = new FlatButton(this);
|
||||||
send_message_button_->setForegroundColor(QColor("#acc7dc"));
|
send_message_button_->setForegroundColor(QColor("#acc7dc"));
|
||||||
|
|
||||||
QIcon send_message_icon;
|
QIcon send_message_icon;
|
||||||
send_message_icon.addFile(":/icons/icons/share-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
send_message_icon.addFile(
|
||||||
send_message_button_->setIcon(send_message_icon);
|
":/icons/icons/share-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
send_message_button_->setIconSize(QSize(24, 24));
|
send_message_button_->setIcon(send_message_icon);
|
||||||
|
send_message_button_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
emoji_button_ = new EmojiPickButton(this);
|
emoji_button_ = new EmojiPickButton(this);
|
||||||
emoji_button_->setForegroundColor(QColor("#acc7dc"));
|
emoji_button_->setForegroundColor(QColor("#acc7dc"));
|
||||||
|
|
||||||
QIcon emoji_icon;
|
QIcon emoji_icon;
|
||||||
emoji_icon.addFile(":/icons/icons/smile.png", QSize(), QIcon::Normal, QIcon::Off);
|
emoji_icon.addFile(":/icons/icons/smile.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||||
emoji_button_->setIcon(emoji_icon);
|
emoji_button_->setIcon(emoji_icon);
|
||||||
emoji_button_->setIconSize(QSize(24, 24));
|
emoji_button_->setIconSize(QSize(24, 24));
|
||||||
|
|
||||||
top_layout_->addWidget(send_file_button_);
|
top_layout_->addWidget(send_file_button_);
|
||||||
top_layout_->addWidget(input_);
|
top_layout_->addWidget(input_);
|
||||||
top_layout_->addWidget(emoji_button_);
|
top_layout_->addWidget(emoji_button_);
|
||||||
top_layout_->addWidget(send_message_button_);
|
top_layout_->addWidget(send_message_button_);
|
||||||
|
|
||||||
setLayout(top_layout_);
|
setLayout(top_layout_);
|
||||||
|
|
||||||
connect(send_message_button_, SIGNAL(clicked()), this, SLOT(onSendButtonClicked()));
|
connect(send_message_button_, SIGNAL(clicked()), this, SLOT(onSendButtonClicked()));
|
||||||
connect(input_, SIGNAL(enterPressed()), send_message_button_, SIGNAL(clicked()));
|
connect(input_, SIGNAL(enterPressed()), send_message_button_, SIGNAL(clicked()));
|
||||||
connect(emoji_button_, SIGNAL(emojiSelected(const QString &)), this, SLOT(addSelectedEmoji(const QString &)));
|
connect(emoji_button_,
|
||||||
|
SIGNAL(emojiSelected(const QString &)),
|
||||||
|
this,
|
||||||
|
SLOT(addSelectedEmoji(const QString &)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TextInputWidget::addSelectedEmoji(const QString &emoji)
|
TextInputWidget::addSelectedEmoji(const QString &emoji)
|
||||||
{
|
{
|
||||||
QTextCursor cursor = input_->textCursor();
|
QTextCursor cursor = input_->textCursor();
|
||||||
|
|
||||||
QFont emoji_font("Emoji One");
|
QFont emoji_font("Emoji One");
|
||||||
emoji_font.setPixelSize(conf::emojiSize);
|
emoji_font.setPixelSize(conf::emojiSize);
|
||||||
|
|
||||||
QFont text_font("Open Sans");
|
QFont text_font("Open Sans");
|
||||||
text_font.setPixelSize(conf::fontSize);
|
text_font.setPixelSize(conf::fontSize);
|
||||||
|
|
||||||
QTextCharFormat charfmt;
|
QTextCharFormat charfmt;
|
||||||
charfmt.setFont(emoji_font);
|
charfmt.setFont(emoji_font);
|
||||||
input_->setCurrentCharFormat(charfmt);
|
input_->setCurrentCharFormat(charfmt);
|
||||||
|
|
||||||
input_->insertPlainText(emoji);
|
input_->insertPlainText(emoji);
|
||||||
cursor.movePosition(QTextCursor::End);
|
cursor.movePosition(QTextCursor::End);
|
||||||
|
|
||||||
charfmt.setFont(text_font);
|
charfmt.setFont(text_font);
|
||||||
input_->setCurrentCharFormat(charfmt);
|
input_->setCurrentCharFormat(charfmt);
|
||||||
|
|
||||||
input_->show();
|
input_->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TextInputWidget::onSendButtonClicked()
|
TextInputWidget::onSendButtonClicked()
|
||||||
{
|
{
|
||||||
auto msg_text = input_->document()->toPlainText().trimmed();
|
auto msgText = input_->document()->toPlainText().trimmed();
|
||||||
|
|
||||||
if (msg_text.isEmpty())
|
if (msgText.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
emit sendTextMessage(msg_text);
|
if (msgText.startsWith(EMOTE_COMMAND)) {
|
||||||
|
auto text = parseEmoteCommand(msgText);
|
||||||
|
|
||||||
input_->clear();
|
if (!text.isEmpty())
|
||||||
|
emit sendEmoteMessage(text);
|
||||||
|
} else {
|
||||||
|
emit sendTextMessage(msgText);
|
||||||
|
}
|
||||||
|
|
||||||
|
input_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
QString
|
||||||
TextInputWidget::paintEvent(QPaintEvent *event)
|
TextInputWidget::parseEmoteCommand(const QString &cmd)
|
||||||
{
|
{
|
||||||
Q_UNUSED(event);
|
auto text = cmd.right(cmd.size() - EMOTE_COMMAND.size()).trimmed();
|
||||||
|
|
||||||
QStyleOption option;
|
if (!text.isEmpty())
|
||||||
option.initFrom(this);
|
return text;
|
||||||
|
|
||||||
QPainter painter(this);
|
return QString("");
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextInputWidget::~TextInputWidget()
|
TextInputWidget::~TextInputWidget()
|
||||||
|
|
|
@ -67,46 +67,42 @@ TimelineItem::init()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For messages created locally. The avatar and the username are displayed.
|
* For messages created locally.
|
||||||
*/
|
*/
|
||||||
TimelineItem::TimelineItem(const QString &userid, QString body, QWidget *parent)
|
TimelineItem::TimelineItem(events::MessageEventType ty,
|
||||||
|
const QString &userid,
|
||||||
|
QString body,
|
||||||
|
bool withSender,
|
||||||
|
QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
descriptionMsg_ = { "You: ", userid, body, descriptiveTime(QDateTime::currentDateTime()) };
|
|
||||||
|
|
||||||
body.replace(URL_REGEX, URL_HTML);
|
|
||||||
auto displayName = TimelineViewManager::displayName(userid);
|
auto displayName = TimelineViewManager::displayName(userid);
|
||||||
|
auto timestamp = QDateTime::currentDateTime();
|
||||||
|
|
||||||
generateTimestamp(QDateTime::currentDateTime());
|
if (ty == events::MessageEventType::Emote) {
|
||||||
generateBody(displayName, body);
|
body = QString("* %1 %2").arg(displayName).arg(body);
|
||||||
|
descriptionMsg_ = { "", userid, body, descriptiveTime(timestamp) };
|
||||||
setupAvatarLayout(displayName);
|
} else {
|
||||||
|
descriptionMsg_ = {
|
||||||
mainLayout_->addLayout(headerLayout_);
|
"You: ", userid, body, descriptiveTime(QDateTime::currentDateTime())
|
||||||
mainLayout_->addWidget(body_);
|
};
|
||||||
|
}
|
||||||
AvatarProvider::resolve(userid, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For messages created locally. Only the text is displayed.
|
|
||||||
*/
|
|
||||||
TimelineItem::TimelineItem(QString body, QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
auto userid = settings.value("auth/user_id").toString();
|
|
||||||
|
|
||||||
init();
|
|
||||||
descriptionMsg_ = { "You: ", userid, body, descriptiveTime(QDateTime::currentDateTime()) };
|
|
||||||
|
|
||||||
body.replace(URL_REGEX, URL_HTML);
|
body.replace(URL_REGEX, URL_HTML);
|
||||||
|
generateTimestamp(timestamp);
|
||||||
|
|
||||||
generateTimestamp(QDateTime::currentDateTime());
|
if (withSender) {
|
||||||
generateBody(body);
|
generateBody(displayName, body);
|
||||||
|
setupAvatarLayout(displayName);
|
||||||
|
mainLayout_->addLayout(headerLayout_);
|
||||||
|
|
||||||
setupSimpleLayout();
|
AvatarProvider::resolve(userid, this);
|
||||||
|
} else {
|
||||||
|
generateBody(body);
|
||||||
|
setupSimpleLayout();
|
||||||
|
}
|
||||||
|
|
||||||
mainLayout_->addWidget(body_);
|
mainLayout_->addWidget(body_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,7 +289,10 @@ TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection dire
|
||||||
|
|
||||||
eventIds_[emote.eventId()] = true;
|
eventIds_[emote.eventId()] = true;
|
||||||
|
|
||||||
// TODO Check if it's a message waiting for validation
|
if (isPendingMessage(emote, local_user_)) {
|
||||||
|
removePendingMessage(emote);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto with_sender = isSenderRendered(emote.sender(), direction);
|
auto with_sender = isSenderRendered(emote.sender(), direction);
|
||||||
|
|
||||||
|
@ -452,55 +455,19 @@ TimelineView::updatePendingMessage(int txn_id, QString event_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
TimelineView::isPendingMessage(const events::MessageEvent<msgs::Text> &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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineView::removePendingMessage(const events::MessageEvent<msgs::Text> &e)
|
TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString &body, int txn_id)
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TimelineView::addUserTextMessage(const QString &body, int txn_id)
|
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
auto user_id = settings.value("auth/user_id").toString();
|
auto user_id = settings.value("auth/user_id").toString();
|
||||||
|
|
||||||
auto with_sender = lastSender_ != user_id;
|
auto with_sender = lastSender_ != user_id;
|
||||||
|
|
||||||
TimelineItem *view_item;
|
TimelineItem *view_item = new TimelineItem(ty, user_id, body, with_sender, scroll_widget_);
|
||||||
|
|
||||||
if (with_sender)
|
|
||||||
view_item = new TimelineItem(user_id, body, scroll_widget_);
|
|
||||||
else
|
|
||||||
view_item = new TimelineItem(body, scroll_widget_);
|
|
||||||
|
|
||||||
scroll_layout_->addWidget(view_item);
|
scroll_layout_->addWidget(view_item);
|
||||||
|
|
||||||
lastSender_ = user_id;
|
lastSender_ = user_id;
|
||||||
|
|
||||||
PendingMessage message(txn_id, body, "", view_item);
|
PendingMessage message(txn_id, body, "", view_item);
|
||||||
|
|
||||||
pending_msgs_.push_back(message);
|
pending_msgs_.push_back(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,8 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<MatrixClient> client, QW
|
||||||
{
|
{
|
||||||
setStyleSheet("QWidget { background: #f8fbfe; color: #e8e8e8; border: none;}");
|
setStyleSheet("QWidget { background: #f8fbfe; color: #e8e8e8; border: none;}");
|
||||||
|
|
||||||
connect(client_.data(),
|
connect(
|
||||||
SIGNAL(messageSent(const QString &, const QString &, int)),
|
client_.data(), &MatrixClient::messageSent, this, &TimelineViewManager::messageSent);
|
||||||
this,
|
|
||||||
SLOT(messageSent(const QString &, const QString &, int)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineViewManager::~TimelineViewManager()
|
TimelineViewManager::~TimelineViewManager()
|
||||||
|
@ -59,8 +57,19 @@ TimelineViewManager::sendTextMessage(const QString &msg)
|
||||||
auto room_id = active_room_;
|
auto room_id = active_room_;
|
||||||
auto view = views_[room_id];
|
auto view = views_[room_id];
|
||||||
|
|
||||||
view->addUserTextMessage(msg, client_->transactionId());
|
view->addUserMessage(matrix::events::MessageEventType::Text, msg, client_->transactionId());
|
||||||
client_->sendTextMessage(room_id, msg);
|
client_->sendRoomMessage(matrix::events::MessageEventType::Text, room_id, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TimelineViewManager::sendEmoteMessage(const QString &msg)
|
||||||
|
{
|
||||||
|
auto room_id = active_room_;
|
||||||
|
auto view = views_[room_id];
|
||||||
|
|
||||||
|
view->addUserMessage(
|
||||||
|
matrix::events::MessageEventType::Emote, msg, client_->transactionId());
|
||||||
|
client_->sendRoomMessage(matrix::events::MessageEventType::Emote, room_id, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
Loading…
Reference in a new issue