Add write support for m.emote events

closes #41
This commit is contained in:
Konstantinos Sideris 2017-09-03 11:43:45 +03:00
parent a44a4f36af
commit b5ae84c3c3
11 changed files with 747 additions and 686 deletions

View File

@ -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;
} }

View File

@ -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();
} }

View File

@ -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,

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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

View File

@ -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()

View File

@ -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_);
} }

View File

@ -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);
} }

View File

@ -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