mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Receive typing notifications (#88)
This commit is contained in:
parent
88349eae90
commit
d60c2b76e3
10 changed files with 171 additions and 32 deletions
|
@ -125,8 +125,8 @@ endif()
|
|||
#
|
||||
set(SRC_FILES
|
||||
src/AvatarProvider.cc
|
||||
src/ChatPage.cc
|
||||
src/Cache.cc
|
||||
src/ChatPage.cc
|
||||
src/Deserializable.cc
|
||||
src/EmojiCategory.cc
|
||||
src/EmojiItemDelegate.cc
|
||||
|
@ -135,9 +135,6 @@ set(SRC_FILES
|
|||
src/EmojiProvider.cc
|
||||
src/ImageItem.cc
|
||||
src/ImageOverlayDialog.cc
|
||||
src/TimelineItem.cc
|
||||
src/TimelineView.cc
|
||||
src/TimelineViewManager.cc
|
||||
src/InputValidator.cc
|
||||
src/JoinRoomDialog.cc
|
||||
src/LeaveRoomDialog.cc
|
||||
|
@ -147,21 +144,25 @@ set(SRC_FILES
|
|||
src/MainWindow.cc
|
||||
src/MatrixClient.cc
|
||||
src/Profile.cc
|
||||
src/RoomInfoListItem.cc
|
||||
src/RoomMessages.cc
|
||||
src/RoomList.cc
|
||||
src/RoomState.cc
|
||||
src/QuickSwitcher.cc
|
||||
src/Register.cc
|
||||
src/RegisterPage.cc
|
||||
src/RoomInfoListItem.cc
|
||||
src/RoomList.cc
|
||||
src/RoomMessages.cc
|
||||
src/RoomState.cc
|
||||
src/Splitter.cc
|
||||
src/Sync.cc
|
||||
src/TextInputWidget.cc
|
||||
src/TrayIcon.cc
|
||||
src/TimelineItem.cc
|
||||
src/TimelineView.cc
|
||||
src/TimelineViewManager.cc
|
||||
src/TopRoomBar.cc
|
||||
src/TrayIcon.cc
|
||||
src/TypingDisplay.cc
|
||||
src/UserInfoWidget.cc
|
||||
src/Versions.cc
|
||||
src/WelcomePage.cc
|
||||
src/QuickSwitcher.cc
|
||||
src/main.cc
|
||||
|
||||
src/ui/Avatar.cc
|
||||
|
@ -222,23 +223,24 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
include/ImageItem.h
|
||||
include/ImageOverlayDialog.h
|
||||
include/JoinRoomDialog.h
|
||||
include/TimelineItem.h
|
||||
include/TimelineView.h
|
||||
include/TimelineViewManager.h
|
||||
include/LeaveRoomDialog.h
|
||||
include/LoginPage.h
|
||||
include/LogoutDialog.h
|
||||
include/MainWindow.h
|
||||
include/MatrixClient.h
|
||||
include/QuickSwitcher.h
|
||||
include/RegisterPage.h
|
||||
include/RoomInfoListItem.h
|
||||
include/RoomList.h
|
||||
include/Splitter.h
|
||||
include/UserInfoWidget.h
|
||||
include/TextInputWidget.h
|
||||
include/TimelineItem.h
|
||||
include/TimelineView.h
|
||||
include/TimelineViewManager.h
|
||||
include/TopRoomBar.h
|
||||
include/TrayIcon.h
|
||||
include/TextInputWidget.h
|
||||
include/QuickSwitcher.h
|
||||
include/TypingDisplay.h
|
||||
include/UserInfoWidget.h
|
||||
include/WelcomePage.h
|
||||
|
||||
include/ui/Avatar.h
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "TextInputWidget.h"
|
||||
#include "TimelineViewManager.h"
|
||||
#include "TopRoomBar.h"
|
||||
#include "TypingDisplay.h"
|
||||
#include "UserInfoWidget.h"
|
||||
|
||||
class ChatPage : public QWidget
|
||||
|
@ -68,6 +69,7 @@ protected:
|
|||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
private:
|
||||
void updateTypingUsers(const QString &roomid, const QList<QString> &user_ids);
|
||||
void updateDisplayNames(const RoomState &state);
|
||||
void loadStateFromCache();
|
||||
void showQuickSwitcher();
|
||||
|
@ -92,6 +94,7 @@ private:
|
|||
|
||||
TopRoomBar *top_bar_;
|
||||
TextInputWidget *text_input_;
|
||||
TypingDisplay *typingDisplay_;
|
||||
|
||||
QTimer *sync_timer_;
|
||||
int sync_interval_;
|
||||
|
@ -104,6 +107,9 @@ private:
|
|||
QMap<QString, RoomState> state_manager_;
|
||||
QMap<QString, QSharedPointer<RoomSettings>> settingsManager_;
|
||||
|
||||
// Keeps track of the users currently typing on each room.
|
||||
QMap<QString, QList<QString>> typingUsers_;
|
||||
|
||||
QuickSwitcher *quickSwitcher_ = nullptr;
|
||||
OverlayModal *quickSwitcherModal_ = nullptr;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace conf
|
|||
static const int fontSize = 12;
|
||||
static const int emojiSize = 14;
|
||||
static const int headerFontSize = 21;
|
||||
static const int typingNotificationFontSize = 11;
|
||||
|
||||
// Window geometry.
|
||||
namespace window
|
||||
|
|
|
@ -142,23 +142,30 @@ Timeline::limited() const
|
|||
return limited_;
|
||||
}
|
||||
|
||||
// TODO: Add support for ehpmeral, account_data, undread_notifications
|
||||
// TODO: Add support for account_data, undread_notifications
|
||||
class JoinedRoom : public Deserializable
|
||||
{
|
||||
public:
|
||||
inline State state() const;
|
||||
inline Timeline timeline() const;
|
||||
inline QList<QString> typingUserIDs() const;
|
||||
|
||||
void deserialize(const QJsonValue &data) override;
|
||||
|
||||
private:
|
||||
State state_;
|
||||
Timeline timeline_;
|
||||
/* Ephemeral ephemeral_; */
|
||||
QList<QString> typingUserIDs_;
|
||||
/* AccountData account_data_; */
|
||||
/* UnreadNotifications unread_notifications_; */
|
||||
};
|
||||
|
||||
inline QList<QString>
|
||||
JoinedRoom::typingUserIDs() const
|
||||
{
|
||||
return typingUserIDs_;
|
||||
}
|
||||
|
||||
inline State
|
||||
JoinedRoom::state() const
|
||||
{
|
||||
|
|
21
include/TypingDisplay.h
Normal file
21
include/TypingDisplay.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QWidget>
|
||||
|
||||
class TypingDisplay : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TypingDisplay(QWidget *parent = nullptr);
|
||||
|
||||
void setUsers(const QStringList &user_ids);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QString text_;
|
||||
int leftPadding_;
|
||||
};
|
|
@ -102,7 +102,9 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
|
|||
mainContentLayout_->addWidget(view_manager_);
|
||||
|
||||
text_input_ = new TextInputWidget(this);
|
||||
typingDisplay_ = new TypingDisplay(this);
|
||||
contentLayout_->addWidget(text_input_);
|
||||
contentLayout_->addWidget(typingDisplay_);
|
||||
|
||||
user_info_widget_ = new UserInfoWidget(sideBarTopWidget_);
|
||||
sideBarTopWidgetLayout_->addWidget(user_info_widget_);
|
||||
|
@ -117,6 +119,15 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
|
|||
connect(
|
||||
top_bar_, &TopRoomBar::leaveRoom, this, [=]() { client_->leaveRoom(current_room_); });
|
||||
|
||||
connect(room_list_, &RoomList::roomChanged, this, [=](const QString &roomid) {
|
||||
QStringList users;
|
||||
|
||||
if (typingUsers_.contains(roomid))
|
||||
users = typingUsers_[roomid];
|
||||
|
||||
typingDisplay_->setUsers(users);
|
||||
});
|
||||
|
||||
connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo);
|
||||
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit);
|
||||
connect(
|
||||
|
@ -308,6 +319,8 @@ ChatPage::syncCompleted(const SyncResponse &response)
|
|||
auto joined = response.rooms().join();
|
||||
|
||||
for (auto it = joined.constBegin(); it != joined.constEnd(); it++) {
|
||||
updateTypingUsers(it.key(), it.value().typingUserIDs());
|
||||
|
||||
RoomState room_state;
|
||||
|
||||
// Merge the new updates for rooms that we are tracking.
|
||||
|
@ -620,6 +633,22 @@ ChatPage::removeRoom(const QString &room_id)
|
|||
room_list_->removeRoom(room_id, room_id == current_room_);
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::updateTypingUsers(const QString &roomid, const QList<QString> &user_ids)
|
||||
{
|
||||
QStringList users;
|
||||
|
||||
for (const auto uid : user_ids)
|
||||
users.append(TimelineViewManager::displayName(uid));
|
||||
|
||||
users.sort();
|
||||
|
||||
if (current_room_ == roomid)
|
||||
typingDisplay_->setUsers(users);
|
||||
|
||||
typingUsers_.insert(roomid, users);
|
||||
}
|
||||
|
||||
ChatPage::~ChatPage()
|
||||
{
|
||||
sync_timer_->stop();
|
||||
|
|
|
@ -611,8 +611,9 @@ void
|
|||
MatrixClient::sync() noexcept
|
||||
{
|
||||
QJsonObject filter{ { "room",
|
||||
QJsonObject{ { "include_leave", true },
|
||||
{ "ephemeral", QJsonObject{ { "limit", 0 } } } } },
|
||||
QJsonObject{
|
||||
{ "include_leave", true },
|
||||
} },
|
||||
{ "presence", QJsonObject{ { "limit", 0 } } } };
|
||||
|
||||
QUrlQuery query;
|
||||
|
|
16
src/Sync.cc
16
src/Sync.cc
|
@ -168,7 +168,21 @@ JoinedRoom::deserialize(const QJsonValue &data)
|
|||
if (!ephemeral.value("events").isArray())
|
||||
qWarning() << "join/ephemeral/events should be an array";
|
||||
|
||||
// TODO: Implement ephemeral handling
|
||||
auto ephemeralEvents = ephemeral.value("events").toArray();
|
||||
|
||||
for (const auto e : ephemeralEvents) {
|
||||
auto obj = e.toObject();
|
||||
|
||||
if (obj.contains("type") && obj.value("type") == "m.typing") {
|
||||
auto ids = obj.value("content")
|
||||
.toObject()
|
||||
.value("user_ids")
|
||||
.toArray();
|
||||
|
||||
for (const auto uid : ids)
|
||||
typingUserIDs_.push_back(uid.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,13 +45,14 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
|||
{
|
||||
setFont(QFont("Emoji One"));
|
||||
|
||||
setFixedHeight(45);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
setCursor(Qt::ArrowCursor);
|
||||
setStyleSheet("background-color: #fff; height: 45px;");
|
||||
setStyleSheet("background-color: #fff;");
|
||||
|
||||
topLayout_ = new QHBoxLayout();
|
||||
topLayout_->setSpacing(2);
|
||||
topLayout_->setMargin(4);
|
||||
topLayout_->setSpacing(0);
|
||||
topLayout_->setContentsMargins(5, 15, 0, 5);
|
||||
|
||||
QIcon send_file_icon;
|
||||
send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off);
|
||||
|
@ -63,18 +64,19 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
|||
|
||||
spinner_ = new LoadingIndicator(this);
|
||||
spinner_->setColor("#acc7dc");
|
||||
spinner_->setFixedHeight(40);
|
||||
spinner_->setFixedWidth(40);
|
||||
spinner_->setFixedHeight(32);
|
||||
spinner_->setFixedWidth(32);
|
||||
spinner_->hide();
|
||||
|
||||
QFont font;
|
||||
font.setPixelSize(conf::fontSize);
|
||||
|
||||
input_ = new FilteredTextEdit(this);
|
||||
input_->setFixedHeight(45);
|
||||
input_->setFixedHeight(32);
|
||||
input_->setFont(font);
|
||||
input_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
input_->setPlaceholderText(tr("Write a message..."));
|
||||
input_->setStyleSheet("color: #333333; border-radius: 0; padding-top: 10px;");
|
||||
input_->setStyleSheet("color: #333333; border: none; margin: 0 5px");
|
||||
|
||||
sendMessageBtn_ = new FlatButton(this);
|
||||
sendMessageBtn_->setForegroundColor(QColor("#acc7dc"));
|
||||
|
|
56
src/TypingDisplay.cc
Normal file
56
src/TypingDisplay.cc
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QPoint>
|
||||
|
||||
#include "Config.h"
|
||||
#include "TypingDisplay.h"
|
||||
|
||||
TypingDisplay::TypingDisplay(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, leftPadding_{ 57 }
|
||||
{
|
||||
QFont font;
|
||||
font.setPixelSize(conf::typingNotificationFontSize);
|
||||
|
||||
setFixedHeight(QFontMetrics(font).height() + 2);
|
||||
}
|
||||
|
||||
void
|
||||
TypingDisplay::setUsers(const QStringList &uid)
|
||||
{
|
||||
if (uid.isEmpty())
|
||||
text_.clear();
|
||||
else
|
||||
text_ = uid.join(", ");
|
||||
|
||||
if (uid.size() == 1)
|
||||
text_ += tr(" is typing ...");
|
||||
else if (uid.size() > 1)
|
||||
text_ += tr(" are typing ...");
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void
|
||||
TypingDisplay::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPen pen(QColor("#333"));
|
||||
|
||||
QFont font;
|
||||
font.setPixelSize(conf::typingNotificationFontSize);
|
||||
font.setWeight(40);
|
||||
font.setItalic(true);
|
||||
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
p.setFont(font);
|
||||
p.setPen(pen);
|
||||
|
||||
QRect region = rect();
|
||||
region.translate(leftPadding_, 0);
|
||||
|
||||
QFontMetrics fm(font);
|
||||
text_ = fm.elidedText(text_, Qt::ElideRight, width() - 3 * leftPadding_);
|
||||
|
||||
p.drawText(region, Qt::AlignTop, text_);
|
||||
}
|
Loading…
Reference in a new issue