mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
parent
82ec022f9c
commit
dcddea6fb8
14 changed files with 133 additions and 334 deletions
|
@ -303,7 +303,6 @@ set(SRC_FILES
|
|||
src/Utils.cpp
|
||||
src/WelcomePage.cpp
|
||||
src/popups/PopupItem.cpp
|
||||
src/popups/ReplyPopup.cpp
|
||||
src/popups/SuggestionsPopup.cpp
|
||||
src/popups/UserMentions.cpp
|
||||
src/main.cpp
|
||||
|
@ -501,7 +500,6 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
src/UserSettingsPage.h
|
||||
src/WelcomePage.h
|
||||
src/popups/PopupItem.h
|
||||
src/popups/ReplyPopup.h
|
||||
src/popups/SuggestionsPopup.h
|
||||
src/popups/UserMentions.h
|
||||
)
|
||||
|
|
|
@ -285,7 +285,7 @@ Page {
|
|||
|
||||
id: replyPopup
|
||||
|
||||
visible: timelineManager.replyingEvent && chat.model
|
||||
visible: chat.model && chat.model.reply
|
||||
// Height of child, plus margins, plus border
|
||||
height: replyPreview.height + 10
|
||||
color: colors.base
|
||||
|
@ -300,7 +300,7 @@ Page {
|
|||
anchors.rightMargin: 20
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
modelData: chat.model ? chat.model.getDump(timelineManager.replyingEvent) : {}
|
||||
modelData: chat.model ? chat.model.getDump(chat.model.reply) : {}
|
||||
userColor: timelineManager.userColor(modelData.userId, colors.window)
|
||||
}
|
||||
|
||||
|
@ -318,7 +318,7 @@ Page {
|
|||
ToolTip.visible: closeReplyButton.hovered
|
||||
ToolTip.text: qsTr("Close")
|
||||
|
||||
onClicked: timelineManager.closeReply()
|
||||
onClicked: chat.model.reply = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -303,10 +303,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
text_input_,
|
||||
&TextInputWidget::uploadMedia,
|
||||
this,
|
||||
[this](QSharedPointer<QIODevice> dev,
|
||||
QString mimeClass,
|
||||
const QString &fn,
|
||||
const std::optional<RelatedInfo> &related) {
|
||||
[this](QSharedPointer<QIODevice> dev, QString mimeClass, const QString &fn) {
|
||||
if (!dev->open(QIODevice::ReadOnly)) {
|
||||
emit uploadFailed(
|
||||
QString("Error while reading media: %1").arg(dev->errorString()));
|
||||
|
@ -358,8 +355,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
mime = mime.name(),
|
||||
size = payload.size(),
|
||||
dimensions,
|
||||
blurhash,
|
||||
related](const mtx::responses::ContentURI &res, mtx::http::RequestErr err) {
|
||||
blurhash](const mtx::responses::ContentURI &res, mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
emit uploadFailed(
|
||||
tr("Failed to upload media. Please try again."));
|
||||
|
@ -378,8 +374,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
mime,
|
||||
size,
|
||||
dimensions,
|
||||
blurhash,
|
||||
related);
|
||||
blurhash);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -398,8 +393,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
QString mime,
|
||||
qint64 dsize,
|
||||
QSize dimensions,
|
||||
QString blurhash,
|
||||
const std::optional<RelatedInfo> &related) {
|
||||
QString blurhash) {
|
||||
text_input_->hideUploadSpinner();
|
||||
|
||||
if (encryptedFile)
|
||||
|
@ -413,17 +407,16 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
mime,
|
||||
dsize,
|
||||
dimensions,
|
||||
blurhash,
|
||||
related);
|
||||
blurhash);
|
||||
else if (mimeClass == "audio")
|
||||
view_manager_->queueAudioMessage(
|
||||
roomid, filename, encryptedFile, url, mime, dsize, related);
|
||||
roomid, filename, encryptedFile, url, mime, dsize);
|
||||
else if (mimeClass == "video")
|
||||
view_manager_->queueVideoMessage(
|
||||
roomid, filename, encryptedFile, url, mime, dsize, related);
|
||||
roomid, filename, encryptedFile, url, mime, dsize);
|
||||
else
|
||||
view_manager_->queueFileMessage(
|
||||
roomid, filename, encryptedFile, url, mime, dsize, related);
|
||||
roomid, filename, encryptedFile, url, mime, dsize);
|
||||
});
|
||||
|
||||
connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar);
|
||||
|
@ -548,14 +541,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
});
|
||||
|
||||
connect(this, &ChatPage::dropToLoginPageCb, this, &ChatPage::dropToLoginPage);
|
||||
connect(this, &ChatPage::messageReply, text_input_, &TextInputWidget::addReply);
|
||||
connect(this, &ChatPage::messageReply, this, [this](const RelatedInfo &related) {
|
||||
view_manager_->updateReplyingEvent(QString::fromStdString(related.related_event));
|
||||
});
|
||||
connect(view_manager_,
|
||||
&TimelineViewManager::replyClosed,
|
||||
text_input_,
|
||||
&TextInputWidget::closeReplyPopup);
|
||||
|
||||
instance_ = this;
|
||||
}
|
||||
|
@ -596,6 +581,12 @@ ChatPage::resetUI()
|
|||
showUnreadMessageNotification(0);
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::focusMessageInput()
|
||||
{
|
||||
this->text_input_->focusLineEdit();
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::deleteConfigs()
|
||||
{
|
||||
|
|
|
@ -85,6 +85,7 @@ public:
|
|||
//! Show the room/group list (if it was visible).
|
||||
void showSideBars();
|
||||
void initiateLogout();
|
||||
void focusMessageInput();
|
||||
|
||||
public slots:
|
||||
void leaveRoom(const QString &room_id);
|
||||
|
@ -99,8 +100,6 @@ signals:
|
|||
void connectionLost();
|
||||
void connectionRestored();
|
||||
|
||||
void messageReply(const RelatedInfo &related);
|
||||
|
||||
void notificationsRetrieved(const mtx::responses::Notifications &);
|
||||
void highlightedNotifsRetrieved(const mtx::responses::Notifications &,
|
||||
const QPoint widgetPos);
|
||||
|
@ -114,8 +113,7 @@ signals:
|
|||
const QString &mime,
|
||||
qint64 dsize,
|
||||
const QSize &dimensions,
|
||||
const QString &blurhash,
|
||||
const std::optional<RelatedInfo> &related);
|
||||
const QString &blurhash);
|
||||
|
||||
void contentLoaded();
|
||||
void closing();
|
||||
|
|
|
@ -46,7 +46,6 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
|||
: QTextEdit{parent}
|
||||
, history_index_{0}
|
||||
, suggestionsPopup_{parent}
|
||||
, replyPopup_{parent}
|
||||
, previewDialog_{parent}
|
||||
{
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
|
@ -73,10 +72,6 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
|||
&FilteredTextEdit::uploadData);
|
||||
|
||||
connect(this, &FilteredTextEdit::resultsRetrieved, this, &FilteredTextEdit::showResults);
|
||||
connect(&replyPopup_, &ReplyPopup::userSelected, this, [](const QString &text) {
|
||||
// TODO: Show user avatar window.
|
||||
nhlog::ui()->info("User selected: " + text.toStdString());
|
||||
});
|
||||
connect(
|
||||
&suggestionsPopup_, &SuggestionsPopup::itemSelected, this, [this](const QString &text) {
|
||||
suggestionsPopup_.hide();
|
||||
|
@ -90,8 +85,6 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
|||
cursor.insertText(text);
|
||||
});
|
||||
|
||||
connect(&replyPopup_, &ReplyPopup::cancel, this, [this]() { closeReply(); });
|
||||
|
||||
// For cycling through the suggestions by hitting tab.
|
||||
connect(this,
|
||||
&FilteredTextEdit::selectNextSuggestion,
|
||||
|
@ -174,17 +167,6 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
if (replyPopup_.isVisible()) {
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Escape:
|
||||
closeReply();
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event->key()) {
|
||||
case Qt::Key_At:
|
||||
atTriggerPosition_ = textCursor().position();
|
||||
|
@ -218,7 +200,6 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event)
|
|||
if (!(event->modifiers() & Qt::ShiftModifier)) {
|
||||
stopTyping();
|
||||
submit();
|
||||
closeReply();
|
||||
} else {
|
||||
QTextEdit::keyPressEvent(event);
|
||||
}
|
||||
|
@ -416,30 +397,17 @@ FilteredTextEdit::submit()
|
|||
auto name = text.mid(1, command_end - 1);
|
||||
auto args = text.mid(command_end + 1);
|
||||
if (name.isEmpty() || name == "/") {
|
||||
message(args, related);
|
||||
message(args);
|
||||
} else {
|
||||
command(name, args);
|
||||
}
|
||||
} else {
|
||||
message(std::move(text), std::move(related));
|
||||
message(std::move(text));
|
||||
}
|
||||
|
||||
related = {};
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
void
|
||||
FilteredTextEdit::showReplyPopup(const RelatedInfo &related_)
|
||||
{
|
||||
QPoint pos = viewport()->mapToGlobal(this->pos());
|
||||
|
||||
replyPopup_.setReplyContent(related_);
|
||||
replyPopup_.move(pos.x(), pos.y() - replyPopup_.height() - 10);
|
||||
replyPopup_.setFixedWidth(this->parentWidget()->width());
|
||||
replyPopup_.show();
|
||||
}
|
||||
|
||||
void
|
||||
FilteredTextEdit::textChanged()
|
||||
{
|
||||
|
@ -456,9 +424,7 @@ FilteredTextEdit::uploadData(const QByteArray data,
|
|||
|
||||
emit startedUpload();
|
||||
|
||||
emit media(buffer, mediaType, filename, related);
|
||||
related = {};
|
||||
closeReply();
|
||||
emit media(buffer, mediaType, filename);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -599,7 +565,7 @@ void
|
|||
TextInputWidget::command(QString command, QString args)
|
||||
{
|
||||
if (command == "me") {
|
||||
sendEmoteMessage(args, input_->related);
|
||||
sendEmoteMessage(args);
|
||||
} else if (command == "join") {
|
||||
sendJoinRoomRequest(args);
|
||||
} else if (command == "invite") {
|
||||
|
@ -611,16 +577,14 @@ TextInputWidget::command(QString command, QString args)
|
|||
} else if (command == "unban") {
|
||||
sendUnbanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
|
||||
} else if (command == "shrug") {
|
||||
sendTextMessage("¯\\_(ツ)_/¯", input_->related);
|
||||
sendTextMessage("¯\\_(ツ)_/¯");
|
||||
} else if (command == "fliptable") {
|
||||
sendTextMessage("(╯°□°)╯︵ ┻━┻", input_->related);
|
||||
sendTextMessage("(╯°□°)╯︵ ┻━┻");
|
||||
} else if (command == "unfliptable") {
|
||||
sendTextMessage(" ┯━┯╭( º _ º╭)", input_->related);
|
||||
sendTextMessage(" ┯━┯╭( º _ º╭)");
|
||||
} else if (command == "sovietflip") {
|
||||
sendTextMessage("ノ┬─┬ノ ︵ ( \\o°o)\\", input_->related);
|
||||
sendTextMessage("ノ┬─┬ノ ︵ ( \\o°o)\\");
|
||||
}
|
||||
|
||||
input_->related = std::nullopt;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -640,9 +604,7 @@ TextInputWidget::openFileSelection()
|
|||
|
||||
QSharedPointer<QFile> file{new QFile{fileName, this}};
|
||||
|
||||
emit uploadMedia(file, format, QFileInfo(fileName).fileName(), input_->related);
|
||||
input_->related = {};
|
||||
input_->closeReply();
|
||||
emit uploadMedia(file, format, QFileInfo(fileName).fileName());
|
||||
|
||||
showUploadSpinner();
|
||||
}
|
||||
|
@ -687,16 +649,3 @@ TextInputWidget::paintEvent(QPaintEvent *)
|
|||
|
||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||
}
|
||||
|
||||
void
|
||||
TextInputWidget::addReply(const RelatedInfo &related)
|
||||
{
|
||||
// input_->setText(QString("> %1: %2\n\n").arg(username).arg(msg));
|
||||
input_->setFocus();
|
||||
|
||||
// input_->showReplyPopup(related);
|
||||
auto cursor = input_->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
input_->setTextCursor(cursor);
|
||||
input_->related = related;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
#include "dialogs/PreviewUploadOverlay.h"
|
||||
#include "emoji/PickButton.h"
|
||||
#include "popups/ReplyPopup.h"
|
||||
#include "popups/SuggestionsPopup.h"
|
||||
|
||||
struct SearchResult;
|
||||
|
@ -49,27 +48,15 @@ public:
|
|||
QSize minimumSizeHint() const override;
|
||||
|
||||
void submit();
|
||||
void showReplyPopup(const RelatedInfo &related_);
|
||||
void closeReply()
|
||||
{
|
||||
replyPopup_.hide();
|
||||
related = {};
|
||||
}
|
||||
|
||||
// Used for replies
|
||||
std::optional<RelatedInfo> related;
|
||||
|
||||
signals:
|
||||
void heightChanged(int height);
|
||||
void startedTyping();
|
||||
void stoppedTyping();
|
||||
void startedUpload();
|
||||
void message(QString, const std::optional<RelatedInfo> &);
|
||||
void message(QString msg);
|
||||
void command(QString name, QString args);
|
||||
void media(QSharedPointer<QIODevice> data,
|
||||
QString mimeClass,
|
||||
const QString &filename,
|
||||
const std::optional<RelatedInfo> &related);
|
||||
void media(QSharedPointer<QIODevice> data, QString mimeClass, const QString &filename);
|
||||
|
||||
//! Trigger the suggestion popup.
|
||||
void showSuggestions(const QString &query);
|
||||
|
@ -97,7 +84,6 @@ private:
|
|||
QTimer *typingTimer_;
|
||||
|
||||
SuggestionsPopup suggestionsPopup_;
|
||||
ReplyPopup replyPopup_;
|
||||
|
||||
enum class AnchorType
|
||||
{
|
||||
|
@ -163,21 +149,18 @@ public slots:
|
|||
void openFileSelection();
|
||||
void hideUploadSpinner();
|
||||
void focusLineEdit() { input_->setFocus(); }
|
||||
void addReply(const RelatedInfo &related);
|
||||
void closeReplyPopup() { input_->closeReply(); }
|
||||
|
||||
private slots:
|
||||
void addSelectedEmoji(const QString &emoji);
|
||||
|
||||
signals:
|
||||
void sendTextMessage(const QString &msg, const std::optional<RelatedInfo> &related);
|
||||
void sendEmoteMessage(QString msg, const std::optional<RelatedInfo> &related);
|
||||
void sendTextMessage(const QString &msg);
|
||||
void sendEmoteMessage(QString msg);
|
||||
void heightChanged(int height);
|
||||
|
||||
void uploadMedia(const QSharedPointer<QIODevice> data,
|
||||
QString mimeClass,
|
||||
const QString &filename,
|
||||
const std::optional<RelatedInfo> &related);
|
||||
const QString &filename);
|
||||
|
||||
void sendJoinRoomRequest(const QString &room);
|
||||
void sendInviteRoomRequest(const QString &userid, const QString &reason);
|
||||
|
|
|
@ -97,24 +97,24 @@ NotificationsManager::closeNotification(uint id)
|
|||
|
||||
void
|
||||
NotificationsManager::removeNotification(const QString &roomId, const QString &eventId)
|
||||
{
|
||||
roomEventId reId = {roomId, eventId};
|
||||
for (auto elem = notificationIds.begin(); elem != notificationIds.end(); ++elem) {
|
||||
if (elem.value().roomId != roomId)
|
||||
continue;
|
||||
|
||||
roomEventId reId = {roomId, eventId};
|
||||
for (auto elem = notificationIds.begin(); elem != notificationIds.end(); ++elem) {
|
||||
if (elem.value().roomId != roomId)
|
||||
continue;
|
||||
// close all notifications matching the eventId or having a lower
|
||||
// notificationId
|
||||
// This relies on the notificationId not wrapping around. This allows for
|
||||
// approximately 2,147,483,647 notifications, so it is a bit unlikely.
|
||||
// Otherwise we would need to store a 64bit counter instead.
|
||||
closeNotification(elem.key());
|
||||
|
||||
// close all notifications matching the eventId or having a lower
|
||||
// notificationId
|
||||
// This relies on the notificationId not wrapping around. This allows for
|
||||
// approximately 2,147,483,647 notifications, so it is a bit unlikely.
|
||||
// Otherwise we would need to store a 64bit counter instead.
|
||||
closeNotification(elem.key());
|
||||
|
||||
// FIXME: compare index of event id of the read receipt and the notification instead of just
|
||||
// the id to prevent read receipts of events without notification clearing all notifications
|
||||
// in that room!
|
||||
if (elem.value() == reId)
|
||||
break;
|
||||
// FIXME: compare index of event id of the read receipt and the notification instead
|
||||
// of just the id to prevent read receipts of events without notification clearing
|
||||
// all notifications in that room!
|
||||
if (elem.value() == reId)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -67,4 +67,3 @@ void NotificationsManager::notificationClosed(uint, uint) {}
|
|||
void
|
||||
NotificationsManager::removeNotification(const QString &roomId, const QString &eventId)
|
||||
{}
|
||||
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
#include <QLabel>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
|
||||
#include "../Config.h"
|
||||
#include "../Utils.h"
|
||||
#include "../ui/Avatar.h"
|
||||
#include "../ui/DropShadow.h"
|
||||
#include "../ui/TextLabel.h"
|
||||
#include "PopupItem.h"
|
||||
#include "ReplyPopup.h"
|
||||
|
||||
ReplyPopup::ReplyPopup(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, userItem_{nullptr}
|
||||
, msgLabel_{nullptr}
|
||||
, eventLabel_{nullptr}
|
||||
{
|
||||
setAttribute(Qt::WA_ShowWithoutActivating, true);
|
||||
setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint);
|
||||
|
||||
mainLayout_ = new QVBoxLayout(this);
|
||||
mainLayout_->setMargin(0);
|
||||
mainLayout_->setSpacing(0);
|
||||
|
||||
topLayout_ = new QHBoxLayout();
|
||||
topLayout_->setSpacing(0);
|
||||
topLayout_->setContentsMargins(13, 1, 13, 0);
|
||||
|
||||
userItem_ = new UserItem(this);
|
||||
connect(userItem_, &UserItem::clicked, this, &ReplyPopup::userSelected);
|
||||
topLayout_->addWidget(userItem_);
|
||||
|
||||
buttonLayout_ = new QHBoxLayout();
|
||||
buttonLayout_->setSpacing(0);
|
||||
buttonLayout_->setMargin(0);
|
||||
|
||||
topLayout_->addLayout(buttonLayout_);
|
||||
QFont f;
|
||||
f.setPointSizeF(f.pointSizeF());
|
||||
const int fontHeight = QFontMetrics(f).height();
|
||||
buttonSize_ = std::min(fontHeight, 20);
|
||||
|
||||
closeBtn_ = new FlatButton(this);
|
||||
closeBtn_->setToolTip(tr("Logout"));
|
||||
closeBtn_->setCornerRadius(buttonSize_ / 4);
|
||||
closeBtn_->setText("X");
|
||||
|
||||
QIcon icon;
|
||||
icon.addFile(":/icons/icons/ui/remove-symbol.png");
|
||||
|
||||
closeBtn_->setIcon(icon);
|
||||
closeBtn_->setIconSize(QSize(buttonSize_, buttonSize_));
|
||||
connect(closeBtn_, &FlatButton::clicked, this, [this]() { emit cancel(); });
|
||||
|
||||
buttonLayout_->addWidget(closeBtn_);
|
||||
|
||||
topLayout_->addLayout(buttonLayout_);
|
||||
|
||||
mainLayout_->addLayout(topLayout_);
|
||||
msgLabel_ = new TextLabel(this);
|
||||
msgLabel_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction);
|
||||
mainLayout_->addWidget(msgLabel_);
|
||||
eventLabel_ = new QLabel(this);
|
||||
mainLayout_->addWidget(eventLabel_);
|
||||
|
||||
setLayout(mainLayout_);
|
||||
}
|
||||
|
||||
void
|
||||
ReplyPopup::setReplyContent(const RelatedInfo &related)
|
||||
{
|
||||
// Update the current widget with the new data.
|
||||
userItem_->updateItem(related.quoted_user);
|
||||
|
||||
msgLabel_->setText(utils::getFormattedQuoteBody(related, "")
|
||||
.replace("<mx-reply>", "")
|
||||
.replace("</mx-reply>", ""));
|
||||
|
||||
// eventLabel_->setText(srcEvent);
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void
|
||||
ReplyPopup::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QStyleOption opt;
|
||||
opt.init(this);
|
||||
QPainter p(this);
|
||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||
}
|
||||
|
||||
void
|
||||
ReplyPopup::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->buttons() != Qt::RightButton) {
|
||||
emit clicked(eventLabel_->text());
|
||||
}
|
||||
|
||||
QWidget::mousePressEvent(event);
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "../ui/FlatButton.h"
|
||||
#include "../ui/TextLabel.h"
|
||||
|
||||
struct RelatedInfo;
|
||||
class UserItem;
|
||||
|
||||
class ReplyPopup : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ReplyPopup(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void setReplyContent(const RelatedInfo &related);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
signals:
|
||||
void userSelected(const QString &user);
|
||||
void clicked(const QString &text);
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
QHBoxLayout *topLayout_;
|
||||
QVBoxLayout *mainLayout_;
|
||||
QHBoxLayout *buttonLayout_;
|
||||
|
||||
UserItem *userItem_;
|
||||
FlatButton *closeBtn_;
|
||||
TextLabel *msgLabel_;
|
||||
QLabel *eventLabel_;
|
||||
|
||||
int buttonSize_;
|
||||
};
|
|
@ -800,6 +800,16 @@ TimelineModel::decryptEvent(const mtx::events::EncryptedEvent<mtx::events::msg::
|
|||
void
|
||||
TimelineModel::replyAction(QString id)
|
||||
{
|
||||
setReply(id);
|
||||
ChatPage::instance()->focusMessageInput();
|
||||
}
|
||||
|
||||
RelatedInfo
|
||||
TimelineModel::relatedInfo(QString id)
|
||||
{
|
||||
if (!events.contains(id))
|
||||
return {};
|
||||
|
||||
auto event = events.value(id);
|
||||
if (auto e =
|
||||
std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(&event)) {
|
||||
|
@ -815,10 +825,9 @@ TimelineModel::replyAction(QString id)
|
|||
related.quoted_formatted_body = mtx::accessors::formattedBodyWithFallback(event);
|
||||
related.quoted_formatted_body.remove(QRegularExpression(
|
||||
"<mx-reply>.*</mx-reply>", QRegularExpression::DotMatchesEverythingOption));
|
||||
nhlog::ui()->debug("after replacement: {}", related.quoted_body.toStdString());
|
||||
related.room = room_id_;
|
||||
|
||||
ChatPage::instance()->messageReply(related);
|
||||
return related;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -18,6 +18,7 @@ struct Timeline;
|
|||
struct Messages;
|
||||
struct ClaimKeys;
|
||||
}
|
||||
struct RelatedInfo;
|
||||
|
||||
namespace qml_mtx_events {
|
||||
Q_NAMESPACE
|
||||
|
@ -124,6 +125,7 @@ class TimelineModel : public QAbstractListModel
|
|||
int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
Q_PROPERTY(std::vector<QString> typingUsers READ typingUsers WRITE updateTypingUsers NOTIFY
|
||||
typingUsersChanged)
|
||||
Q_PROPERTY(QString reply READ reply WRITE setReply NOTIFY replyChanged RESET resetReply)
|
||||
|
||||
public:
|
||||
explicit TimelineModel(TimelineViewManager *manager,
|
||||
|
@ -191,6 +193,7 @@ public:
|
|||
void addEvents(const mtx::responses::Timeline &events);
|
||||
template<class T>
|
||||
void sendMessage(const T &msg);
|
||||
RelatedInfo relatedInfo(QString id);
|
||||
|
||||
public slots:
|
||||
void setCurrentIndex(int index);
|
||||
|
@ -206,6 +209,22 @@ public slots:
|
|||
}
|
||||
std::vector<QString> typingUsers() const { return typingUsers_; }
|
||||
|
||||
QString reply() const { return reply_; }
|
||||
void setReply(QString newReply)
|
||||
{
|
||||
if (reply_ != newReply) {
|
||||
reply_ = newReply;
|
||||
emit replyChanged(reply_);
|
||||
}
|
||||
}
|
||||
void resetReply()
|
||||
{
|
||||
if (!reply_.isEmpty()) {
|
||||
reply_ = "";
|
||||
emit replyChanged(reply_);
|
||||
}
|
||||
}
|
||||
|
||||
private slots:
|
||||
// Add old events at the top of the timeline.
|
||||
void addBackwardsEvents(const mtx::responses::Messages &msgs);
|
||||
|
@ -225,6 +244,7 @@ signals:
|
|||
void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
|
||||
void eventFetched(QString requestingEvent, mtx::events::collections::TimelineEvents event);
|
||||
void typingUsersChanged(std::vector<QString> users);
|
||||
void replyChanged(QString reply);
|
||||
|
||||
private:
|
||||
DecryptionResult decryptEvent(
|
||||
|
@ -254,6 +274,7 @@ private:
|
|||
bool isProcessingPending = false;
|
||||
|
||||
QString currentId;
|
||||
QString reply_;
|
||||
std::vector<QString> typingUsers_;
|
||||
|
||||
TimelineViewManager *manager_;
|
||||
|
|
|
@ -188,8 +188,11 @@ TimelineViewManager::initWithMessages(const std::map<QString, mtx::responses::Ti
|
|||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::queueTextMessage(const QString &msg, const std::optional<RelatedInfo> &related)
|
||||
TimelineViewManager::queueTextMessage(const QString &msg)
|
||||
{
|
||||
if (!timeline_)
|
||||
return;
|
||||
|
||||
mtx::events::msg::Text text = {};
|
||||
text.body = msg.trimmed().toStdString();
|
||||
|
||||
|
@ -203,13 +206,15 @@ TimelineViewManager::queueTextMessage(const QString &msg, const std::optional<Re
|
|||
text.format = "org.matrix.custom.html";
|
||||
}
|
||||
|
||||
if (related) {
|
||||
if (!timeline_->reply().isEmpty()) {
|
||||
auto related = timeline_->relatedInfo(timeline_->reply());
|
||||
|
||||
QString body;
|
||||
bool firstLine = true;
|
||||
for (const auto &line : related->quoted_body.split("\n")) {
|
||||
for (const auto &line : related.quoted_body.split("\n")) {
|
||||
if (firstLine) {
|
||||
firstLine = false;
|
||||
body = QString("> <%1> %2\n").arg(related->quoted_user).arg(line);
|
||||
body = QString("> <%1> %2\n").arg(related.quoted_user).arg(line);
|
||||
} else {
|
||||
body = QString("%1\n> %2\n").arg(body).arg(line);
|
||||
}
|
||||
|
@ -221,17 +226,17 @@ TimelineViewManager::queueTextMessage(const QString &msg, const std::optional<Re
|
|||
text.format = "org.matrix.custom.html";
|
||||
if (settings->isMarkdownEnabled())
|
||||
text.formatted_body =
|
||||
utils::getFormattedQuoteBody(*related, utils::markdownToHtml(msg))
|
||||
utils::getFormattedQuoteBody(related, utils::markdownToHtml(msg))
|
||||
.toStdString();
|
||||
else
|
||||
text.formatted_body =
|
||||
utils::getFormattedQuoteBody(*related, msg.toHtmlEscaped()).toStdString();
|
||||
utils::getFormattedQuoteBody(related, msg.toHtmlEscaped()).toStdString();
|
||||
|
||||
text.relates_to.in_reply_to.event_id = related->related_event;
|
||||
text.relates_to.in_reply_to.event_id = related.related_event;
|
||||
timeline_->resetReply();
|
||||
}
|
||||
|
||||
if (timeline_)
|
||||
timeline_->sendMessage(text);
|
||||
timeline_->sendMessage(text);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -247,6 +252,11 @@ TimelineViewManager::queueEmoteMessage(const QString &msg)
|
|||
emote.format = "org.matrix.custom.html";
|
||||
}
|
||||
|
||||
if (!timeline_->reply().isEmpty()) {
|
||||
emote.relates_to.in_reply_to.event_id = timeline_->reply().toStdString();
|
||||
timeline_->resetReply();
|
||||
}
|
||||
|
||||
if (timeline_)
|
||||
timeline_->sendMessage(emote);
|
||||
}
|
||||
|
@ -259,8 +269,7 @@ TimelineViewManager::queueImageMessage(const QString &roomid,
|
|||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const QSize &dimensions,
|
||||
const QString &blurhash,
|
||||
const std::optional<RelatedInfo> &related)
|
||||
const QString &blurhash)
|
||||
{
|
||||
mtx::events::msg::Image image;
|
||||
image.info.mimetype = mime.toStdString();
|
||||
|
@ -272,10 +281,13 @@ TimelineViewManager::queueImageMessage(const QString &roomid,
|
|||
image.info.w = dimensions.width();
|
||||
image.file = file;
|
||||
|
||||
if (related)
|
||||
image.relates_to.in_reply_to.event_id = related->related_event;
|
||||
auto model = models.value(roomid);
|
||||
if (!model->reply().isEmpty()) {
|
||||
image.relates_to.in_reply_to.event_id = model->reply().toStdString();
|
||||
model->resetReply();
|
||||
}
|
||||
|
||||
models.value(roomid)->sendMessage(image);
|
||||
model->sendMessage(image);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -285,8 +297,7 @@ TimelineViewManager::queueFileMessage(
|
|||
const std::optional<mtx::crypto::EncryptedFile> &encryptedFile,
|
||||
const QString &url,
|
||||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const std::optional<RelatedInfo> &related)
|
||||
uint64_t dsize)
|
||||
{
|
||||
mtx::events::msg::File file;
|
||||
file.info.mimetype = mime.toStdString();
|
||||
|
@ -295,10 +306,13 @@ TimelineViewManager::queueFileMessage(
|
|||
file.url = url.toStdString();
|
||||
file.file = encryptedFile;
|
||||
|
||||
if (related)
|
||||
file.relates_to.in_reply_to.event_id = related->related_event;
|
||||
auto model = models.value(roomid);
|
||||
if (!model->reply().isEmpty()) {
|
||||
file.relates_to.in_reply_to.event_id = model->reply().toStdString();
|
||||
model->resetReply();
|
||||
}
|
||||
|
||||
models.value(roomid)->sendMessage(file);
|
||||
model->sendMessage(file);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -307,8 +321,7 @@ TimelineViewManager::queueAudioMessage(const QString &roomid,
|
|||
const std::optional<mtx::crypto::EncryptedFile> &file,
|
||||
const QString &url,
|
||||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const std::optional<RelatedInfo> &related)
|
||||
uint64_t dsize)
|
||||
{
|
||||
mtx::events::msg::Audio audio;
|
||||
audio.info.mimetype = mime.toStdString();
|
||||
|
@ -317,10 +330,13 @@ TimelineViewManager::queueAudioMessage(const QString &roomid,
|
|||
audio.url = url.toStdString();
|
||||
audio.file = file;
|
||||
|
||||
if (related)
|
||||
audio.relates_to.in_reply_to.event_id = related->related_event;
|
||||
auto model = models.value(roomid);
|
||||
if (!model->reply().isEmpty()) {
|
||||
audio.relates_to.in_reply_to.event_id = model->reply().toStdString();
|
||||
model->resetReply();
|
||||
}
|
||||
|
||||
models.value(roomid)->sendMessage(audio);
|
||||
model->sendMessage(audio);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -329,8 +345,7 @@ TimelineViewManager::queueVideoMessage(const QString &roomid,
|
|||
const std::optional<mtx::crypto::EncryptedFile> &file,
|
||||
const QString &url,
|
||||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const std::optional<RelatedInfo> &related)
|
||||
uint64_t dsize)
|
||||
{
|
||||
mtx::events::msg::Video video;
|
||||
video.info.mimetype = mime.toStdString();
|
||||
|
@ -339,8 +354,11 @@ TimelineViewManager::queueVideoMessage(const QString &roomid,
|
|||
video.url = url.toStdString();
|
||||
video.file = file;
|
||||
|
||||
if (related)
|
||||
video.relates_to.in_reply_to.event_id = related->related_event;
|
||||
auto model = models.value(roomid);
|
||||
if (!model->reply().isEmpty()) {
|
||||
video.relates_to.in_reply_to.event_id = model->reply().toStdString();
|
||||
model->resetReply();
|
||||
}
|
||||
|
||||
models.value(roomid)->sendMessage(video);
|
||||
model->sendMessage(video);
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ class TimelineViewManager : public QObject
|
|||
TimelineModel *timeline MEMBER timeline_ READ activeTimeline NOTIFY activeTimelineChanged)
|
||||
Q_PROPERTY(
|
||||
bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged)
|
||||
Q_PROPERTY(QString replyingEvent READ getReplyingEvent WRITE updateReplyingEvent NOTIFY
|
||||
replyingEventChanged)
|
||||
|
||||
public:
|
||||
TimelineViewManager(QSharedPointer<UserSettings> userSettings, QWidget *parent = nullptr);
|
||||
|
@ -52,26 +50,13 @@ signals:
|
|||
void replyClosed();
|
||||
|
||||
public slots:
|
||||
void updateReplyingEvent(const QString &replyingEvent)
|
||||
{
|
||||
if (this->replyingEvent_ != replyingEvent) {
|
||||
this->replyingEvent_ = replyingEvent;
|
||||
emit replyingEventChanged(replyingEvent_);
|
||||
}
|
||||
}
|
||||
void closeReply()
|
||||
{
|
||||
this->updateReplyingEvent(nullptr);
|
||||
emit replyClosed();
|
||||
}
|
||||
QString getReplyingEvent() const { return replyingEvent_; }
|
||||
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
|
||||
void initWithMessages(const std::map<QString, mtx::responses::Timeline> &msgs);
|
||||
|
||||
void setHistoryView(const QString &room_id);
|
||||
void updateColorPalette();
|
||||
|
||||
void queueTextMessage(const QString &msg, const std::optional<RelatedInfo> &related);
|
||||
void queueTextMessage(const QString &msg);
|
||||
void queueEmoteMessage(const QString &msg);
|
||||
void queueImageMessage(const QString &roomid,
|
||||
const QString &filename,
|
||||
|
@ -80,29 +65,25 @@ public slots:
|
|||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const QSize &dimensions,
|
||||
const QString &blurhash,
|
||||
const std::optional<RelatedInfo> &related);
|
||||
const QString &blurhash);
|
||||
void queueFileMessage(const QString &roomid,
|
||||
const QString &filename,
|
||||
const std::optional<mtx::crypto::EncryptedFile> &file,
|
||||
const QString &url,
|
||||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const std::optional<RelatedInfo> &related);
|
||||
uint64_t dsize);
|
||||
void queueAudioMessage(const QString &roomid,
|
||||
const QString &filename,
|
||||
const std::optional<mtx::crypto::EncryptedFile> &file,
|
||||
const QString &url,
|
||||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const std::optional<RelatedInfo> &related);
|
||||
uint64_t dsize);
|
||||
void queueVideoMessage(const QString &roomid,
|
||||
const QString &filename,
|
||||
const std::optional<mtx::crypto::EncryptedFile> &file,
|
||||
const QString &url,
|
||||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const std::optional<RelatedInfo> &related);
|
||||
uint64_t dsize);
|
||||
|
||||
private:
|
||||
#ifdef USE_QUICK_VIEW
|
||||
|
@ -119,7 +100,6 @@ private:
|
|||
QHash<QString, QSharedPointer<TimelineModel>> models;
|
||||
TimelineModel *timeline_ = nullptr;
|
||||
bool isInitialSync_ = true;
|
||||
QString replyingEvent_;
|
||||
|
||||
QSharedPointer<UserSettings> settings;
|
||||
QHash<QString, QColor> userColors;
|
||||
|
|
Loading…
Reference in a new issue