2023-02-22 01:48:49 +03:00
|
|
|
// SPDX-FileCopyrightText: Nheko Contributors
|
2021-03-05 02:35:15 +03:00
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
2020-11-01 01:24:07 +03:00
|
|
|
#pragma once
|
|
|
|
|
2022-03-21 00:49:33 +03:00
|
|
|
#include <QIODevice>
|
2022-03-21 02:48:27 +03:00
|
|
|
#include <QImage>
|
2020-11-01 01:24:07 +03:00
|
|
|
#include <QObject>
|
2023-06-19 02:38:40 +03:00
|
|
|
#include <QQmlEngine>
|
2022-03-21 00:49:33 +03:00
|
|
|
#include <QSize>
|
2021-12-28 22:09:08 +03:00
|
|
|
#include <QStringList>
|
2020-11-17 04:37:43 +03:00
|
|
|
#include <QTimer>
|
2022-03-21 07:49:12 +03:00
|
|
|
#include <QUrl>
|
2022-03-21 00:49:33 +03:00
|
|
|
#include <QVariantList>
|
2023-06-19 02:38:40 +03:00
|
|
|
|
2020-11-09 05:12:37 +03:00
|
|
|
#include <deque>
|
2022-03-21 00:49:33 +03:00
|
|
|
#include <memory>
|
2020-11-01 01:24:07 +03:00
|
|
|
|
2020-11-15 06:52:49 +03:00
|
|
|
#include <mtx/common.hpp>
|
2023-10-31 18:38:15 +03:00
|
|
|
#include <mtx/events/common.hpp>
|
2020-11-15 06:52:49 +03:00
|
|
|
|
2020-11-01 01:24:07 +03:00
|
|
|
class TimelineModel;
|
2021-07-21 02:03:38 +03:00
|
|
|
class CombinedImagePackModel;
|
2020-11-15 06:52:49 +03:00
|
|
|
class QMimeData;
|
2020-11-25 19:02:23 +03:00
|
|
|
class QDropEvent;
|
2020-11-01 01:24:07 +03:00
|
|
|
|
2022-09-24 11:36:26 +03:00
|
|
|
struct DeleteLaterDeleter
|
|
|
|
{
|
|
|
|
void operator()(QObject *p)
|
|
|
|
{
|
|
|
|
if (p)
|
|
|
|
p->deleteLater();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-21 01:44:11 +03:00
|
|
|
enum class MarkdownOverride
|
|
|
|
{
|
2021-09-18 01:22:33 +03:00
|
|
|
NOT_SPECIFIED, // no override set
|
|
|
|
ON,
|
|
|
|
OFF,
|
2023-01-31 20:22:12 +03:00
|
|
|
CMARK,
|
2021-01-21 01:44:11 +03:00
|
|
|
};
|
|
|
|
|
2022-10-10 15:38:29 +03:00
|
|
|
class MediaUpload final : public QObject
|
2022-03-21 00:49:33 +03:00
|
|
|
{
|
|
|
|
Q_OBJECT
|
2023-06-19 02:38:40 +03:00
|
|
|
|
|
|
|
QML_ELEMENT
|
|
|
|
QML_UNCREATABLE("")
|
|
|
|
|
2022-03-21 07:05:29 +03:00
|
|
|
Q_PROPERTY(int mediaType READ type NOTIFY mediaTypeChanged)
|
2022-03-21 07:49:12 +03:00
|
|
|
// https://stackoverflow.com/questions/33422265/pass-qimage-to-qml/68554646#68554646
|
|
|
|
Q_PROPERTY(QUrl thumbnail READ thumbnailDataUrl NOTIFY thumbnailChanged)
|
2022-03-21 00:49:33 +03:00
|
|
|
// Q_PROPERTY(QString humanSize READ humanSize NOTIFY huSizeChanged)
|
2022-03-21 07:05:29 +03:00
|
|
|
Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)
|
2022-03-21 00:49:33 +03:00
|
|
|
|
|
|
|
// thumbnail video
|
|
|
|
// https://stackoverflow.com/questions/26229633/display-on-screen-using-qabstractvideosurface
|
|
|
|
|
|
|
|
public:
|
|
|
|
enum MediaType
|
|
|
|
{
|
|
|
|
File,
|
|
|
|
Image,
|
|
|
|
Video,
|
|
|
|
Audio,
|
|
|
|
};
|
2022-03-21 07:05:29 +03:00
|
|
|
Q_ENUM(MediaType)
|
2022-03-21 00:49:33 +03:00
|
|
|
|
|
|
|
explicit MediaUpload(std::unique_ptr<QIODevice> data,
|
2022-10-04 00:57:30 +03:00
|
|
|
const QString &mimetype,
|
|
|
|
const QString &originalFilename,
|
2022-03-21 00:49:33 +03:00
|
|
|
bool encrypt,
|
|
|
|
QObject *parent = nullptr);
|
|
|
|
|
2022-03-21 07:05:29 +03:00
|
|
|
[[nodiscard]] int type() const
|
|
|
|
{
|
|
|
|
if (mimeClass_ == u"video")
|
|
|
|
return MediaType::Video;
|
|
|
|
else if (mimeClass_ == u"audio")
|
|
|
|
return MediaType::Audio;
|
|
|
|
else if (mimeClass_ == u"image")
|
|
|
|
return MediaType::Image;
|
|
|
|
else
|
|
|
|
return MediaType::File;
|
|
|
|
}
|
2022-03-21 00:49:33 +03:00
|
|
|
[[nodiscard]] QString url() const { return url_; }
|
|
|
|
[[nodiscard]] QString mimetype() const { return mimetype_; }
|
|
|
|
[[nodiscard]] QString mimeClass() const { return mimeClass_; }
|
|
|
|
[[nodiscard]] QString filename() const { return originalFilename_; }
|
|
|
|
[[nodiscard]] QString blurhash() const { return blurhash_; }
|
|
|
|
[[nodiscard]] uint64_t size() const { return size_; }
|
2022-03-21 02:48:27 +03:00
|
|
|
[[nodiscard]] uint64_t duration() const { return duration_; }
|
2022-03-21 00:49:33 +03:00
|
|
|
[[nodiscard]] std::optional<mtx::crypto::EncryptedFile> encryptedFile_()
|
|
|
|
{
|
|
|
|
return encryptedFile;
|
|
|
|
}
|
2022-03-21 03:24:53 +03:00
|
|
|
[[nodiscard]] std::optional<mtx::crypto::EncryptedFile> thumbnailEncryptedFile_()
|
|
|
|
{
|
|
|
|
return thumbnailEncryptedFile;
|
|
|
|
}
|
2022-03-21 00:49:33 +03:00
|
|
|
[[nodiscard]] QSize dimensions() const { return dimensions_; }
|
|
|
|
|
2022-03-21 03:24:53 +03:00
|
|
|
QImage thumbnailImg() const { return thumbnail_; }
|
|
|
|
QString thumbnailUrl() const { return thumbnailUrl_; }
|
2022-03-21 07:49:12 +03:00
|
|
|
QUrl thumbnailDataUrl() const;
|
2022-03-21 03:24:53 +03:00
|
|
|
[[nodiscard]] uint64_t thumbnailSize() const { return thumbnailSize_; }
|
|
|
|
|
2022-03-21 07:05:29 +03:00
|
|
|
void setFilename(QString fn)
|
|
|
|
{
|
|
|
|
if (fn != originalFilename_) {
|
|
|
|
originalFilename_ = std::move(fn);
|
|
|
|
emit filenameChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-21 00:49:33 +03:00
|
|
|
signals:
|
|
|
|
void uploadComplete(MediaUpload *self, QString url);
|
|
|
|
void uploadFailed(MediaUpload *self);
|
2022-03-21 07:05:29 +03:00
|
|
|
void filenameChanged();
|
2022-03-21 07:49:12 +03:00
|
|
|
void thumbnailChanged();
|
2022-03-21 07:05:29 +03:00
|
|
|
void mediaTypeChanged();
|
2022-03-21 00:49:33 +03:00
|
|
|
|
|
|
|
public slots:
|
|
|
|
void startUpload();
|
|
|
|
|
|
|
|
private slots:
|
2022-03-21 07:49:12 +03:00
|
|
|
void setThumbnail(QImage img)
|
|
|
|
{
|
|
|
|
this->thumbnail_ = std::move(img);
|
|
|
|
emit thumbnailChanged();
|
|
|
|
}
|
2022-03-21 00:49:33 +03:00
|
|
|
|
|
|
|
public:
|
|
|
|
// void uploadThumbnail(QImage img);
|
|
|
|
|
|
|
|
std::unique_ptr<QIODevice> source;
|
|
|
|
QByteArray data;
|
|
|
|
QString mimetype_;
|
|
|
|
QString mimeClass_;
|
|
|
|
QString originalFilename_;
|
|
|
|
QString blurhash_;
|
|
|
|
QString thumbnailUrl_;
|
|
|
|
QString url_;
|
2022-03-21 03:24:53 +03:00
|
|
|
std::optional<mtx::crypto::EncryptedFile> encryptedFile, thumbnailEncryptedFile;
|
2022-03-21 00:49:33 +03:00
|
|
|
|
2022-03-21 02:48:27 +03:00
|
|
|
QImage thumbnail_;
|
|
|
|
|
2022-03-21 00:49:33 +03:00
|
|
|
QSize dimensions_;
|
2022-03-21 03:24:53 +03:00
|
|
|
uint64_t size_ = 0;
|
|
|
|
uint64_t thumbnailSize_ = 0;
|
|
|
|
uint64_t duration_ = 0;
|
2022-03-21 00:49:33 +03:00
|
|
|
bool encrypt_;
|
|
|
|
};
|
|
|
|
|
2022-10-10 15:38:29 +03:00
|
|
|
class InputBar final : public QObject
|
2020-11-09 05:12:37 +03:00
|
|
|
{
|
2021-09-18 01:22:33 +03:00
|
|
|
Q_OBJECT
|
|
|
|
Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged)
|
2023-02-28 02:06:24 +03:00
|
|
|
Q_PROPERTY(
|
|
|
|
bool containsInvalidCommand READ containsInvalidCommand NOTIFY containsInvalidCommandChanged)
|
2023-03-08 03:10:42 +03:00
|
|
|
Q_PROPERTY(bool containsIncompleteCommand READ containsIncompleteCommand NOTIFY
|
|
|
|
containsIncompleteCommandChanged)
|
2023-02-28 02:06:24 +03:00
|
|
|
Q_PROPERTY(QString currentCommand READ currentCommand NOTIFY currentCommandChanged)
|
2024-03-08 20:43:59 +03:00
|
|
|
Q_PROPERTY(QStringList mentions READ mentions NOTIFY mentionsChanged)
|
2021-09-18 01:22:33 +03:00
|
|
|
Q_PROPERTY(QString text READ text NOTIFY textChanged)
|
2022-03-21 00:49:33 +03:00
|
|
|
Q_PROPERTY(QVariantList uploads READ uploads NOTIFY uploadsChanged)
|
2020-11-01 01:24:07 +03:00
|
|
|
|
|
|
|
public:
|
2021-12-03 03:29:15 +03:00
|
|
|
explicit InputBar(TimelineModel *parent)
|
2021-09-18 01:22:33 +03:00
|
|
|
: QObject()
|
|
|
|
, room(parent)
|
|
|
|
{
|
|
|
|
typingRefresh_.setInterval(10'000);
|
|
|
|
typingRefresh_.setSingleShot(true);
|
|
|
|
typingTimeout_.setInterval(5'000);
|
|
|
|
typingTimeout_.setSingleShot(true);
|
|
|
|
connect(&typingRefresh_, &QTimer::timeout, this, &InputBar::startTyping);
|
|
|
|
connect(&typingTimeout_, &QTimer::timeout, this, &InputBar::stopTyping);
|
|
|
|
}
|
2020-11-01 01:24:07 +03:00
|
|
|
|
2022-03-21 00:49:33 +03:00
|
|
|
QVariantList uploads() const;
|
|
|
|
|
2020-11-01 01:24:07 +03:00
|
|
|
public slots:
|
2021-12-03 03:34:34 +03:00
|
|
|
[[nodiscard]] QString text() const;
|
2021-09-18 01:22:33 +03:00
|
|
|
QString previousText();
|
|
|
|
QString nextText();
|
2021-12-03 03:54:43 +03:00
|
|
|
void setText(const QString &newText);
|
2021-09-18 01:22:33 +03:00
|
|
|
|
2024-03-08 20:43:59 +03:00
|
|
|
[[nodiscard]] QStringList mentions() const { return mentions_; }
|
|
|
|
void addMention(QString m, QString text);
|
|
|
|
void removeMention(QString m);
|
|
|
|
|
|
|
|
void storeForEdit()
|
|
|
|
{
|
2024-06-09 16:56:09 +03:00
|
|
|
textBeforeEdit = text();
|
|
|
|
mentionsBefore = mentions_;
|
|
|
|
mentionTextsBefore = mentionTexts_;
|
2024-06-09 16:30:30 +03:00
|
|
|
containsAtRoomBefore = containsAtRoom_;
|
2024-03-08 20:43:59 +03:00
|
|
|
emit mentionsChanged();
|
|
|
|
}
|
|
|
|
void restoreAfterEdit()
|
|
|
|
{
|
2024-06-09 16:56:09 +03:00
|
|
|
mentions_ = mentionsBefore;
|
|
|
|
mentionTexts_ = mentionTextsBefore;
|
2024-06-09 16:30:30 +03:00
|
|
|
containsAtRoom_ = containsAtRoomBefore;
|
2024-03-08 20:43:59 +03:00
|
|
|
mentionsBefore.clear();
|
|
|
|
mentionTextsBefore.clear();
|
|
|
|
setText(textBeforeEdit);
|
|
|
|
textBeforeEdit.clear();
|
|
|
|
emit mentionsChanged();
|
|
|
|
}
|
|
|
|
void replaceMentions(QStringList newMentions, QStringList newMentionTexts)
|
|
|
|
{
|
|
|
|
if (newMentions.size() != newMentionTexts.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
mentions_ = newMentions;
|
|
|
|
mentionTexts_ = newMentionTexts;
|
2024-06-09 16:30:30 +03:00
|
|
|
|
|
|
|
this->containsAtRoom_ = newMentions.contains(u"@room");
|
|
|
|
|
2024-03-08 20:43:59 +03:00
|
|
|
emit mentionsChanged();
|
|
|
|
}
|
|
|
|
|
2023-02-28 02:06:24 +03:00
|
|
|
bool containsInvalidCommand() const { return containsInvalidCommand_; }
|
2023-03-08 03:10:42 +03:00
|
|
|
bool containsIncompleteCommand() const { return containsIncompleteCommand_; }
|
2023-02-28 02:06:24 +03:00
|
|
|
QString currentCommand() const { return currentCommand_; }
|
2021-09-18 01:22:33 +03:00
|
|
|
|
|
|
|
void send();
|
2022-06-13 14:16:49 +03:00
|
|
|
bool tryPasteAttachment(bool fromMouse);
|
|
|
|
bool insertMimeData(const QMimeData *data);
|
2021-12-03 03:54:43 +03:00
|
|
|
void updateState(int selectionStart, int selectionEnd, int cursorPosition, const QString &text);
|
2021-09-18 01:22:33 +03:00
|
|
|
void openFileSelection();
|
2021-12-03 03:34:34 +03:00
|
|
|
[[nodiscard]] bool uploading() const { return uploading_; }
|
2021-12-03 03:54:43 +03:00
|
|
|
void message(const QString &body,
|
2021-09-18 01:22:33 +03:00
|
|
|
MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED,
|
|
|
|
bool rainbowify = false);
|
|
|
|
void reaction(const QString &reactedEvent, const QString &reactionKey);
|
2023-05-19 04:15:55 +03:00
|
|
|
void sticker(QStringList descriptor);
|
2020-11-01 01:24:07 +03:00
|
|
|
|
2022-03-21 00:49:33 +03:00
|
|
|
void acceptUploads();
|
|
|
|
void declineUploads();
|
|
|
|
|
2020-11-17 04:37:43 +03:00
|
|
|
private slots:
|
2021-09-18 01:22:33 +03:00
|
|
|
void startTyping();
|
|
|
|
void stopTyping();
|
2020-11-17 04:37:43 +03:00
|
|
|
|
2022-10-04 00:57:30 +03:00
|
|
|
void finalizeUpload(MediaUpload *upload, const QString &url);
|
2022-03-21 00:49:33 +03:00
|
|
|
void removeRunUpload(MediaUpload *upload);
|
|
|
|
|
2020-11-09 05:12:37 +03:00
|
|
|
signals:
|
2021-09-18 01:22:33 +03:00
|
|
|
void textChanged(QString newText);
|
|
|
|
void uploadingChanged(bool value);
|
2023-02-28 02:06:24 +03:00
|
|
|
void containsInvalidCommandChanged();
|
2024-03-08 20:43:59 +03:00
|
|
|
void mentionsChanged();
|
2023-03-08 03:10:42 +03:00
|
|
|
void containsIncompleteCommandChanged();
|
2023-02-28 02:06:24 +03:00
|
|
|
void currentCommandChanged();
|
2022-03-21 00:49:33 +03:00
|
|
|
void uploadsChanged();
|
2020-11-09 05:12:37 +03:00
|
|
|
|
2020-11-01 01:24:07 +03:00
|
|
|
private:
|
2021-12-03 03:54:43 +03:00
|
|
|
void emote(const QString &body, bool rainbowify);
|
|
|
|
void notice(const QString &body, bool rainbowify);
|
2022-12-10 18:17:15 +03:00
|
|
|
void confetti(const QString &body, bool rainbowify);
|
2023-04-01 22:40:58 +03:00
|
|
|
void rainfall(const QString &body);
|
2023-03-26 03:02:50 +03:00
|
|
|
void customMsgtype(const QString &msgtype, const QString &body);
|
2023-03-02 01:04:17 +03:00
|
|
|
bool command(const QString &name, QString args);
|
2021-09-18 01:22:33 +03:00
|
|
|
void image(const QString &filename,
|
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &file,
|
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
|
|
|
uint64_t dsize,
|
|
|
|
const QSize &dimensions,
|
2022-03-21 07:49:12 +03:00
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &thumbnailEncryptedFile,
|
|
|
|
const QString &thumbnailUrl,
|
|
|
|
uint64_t thumbnailSize,
|
|
|
|
const QSize &thumbnailDimensions,
|
2021-09-18 01:22:33 +03:00
|
|
|
const QString &blurhash);
|
|
|
|
void file(const QString &filename,
|
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &encryptedFile,
|
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
|
|
|
uint64_t dsize);
|
|
|
|
void audio(const QString &filename,
|
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &file,
|
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
2022-03-21 02:48:27 +03:00
|
|
|
uint64_t dsize,
|
|
|
|
uint64_t duration);
|
2021-09-18 01:22:33 +03:00
|
|
|
void video(const QString &filename,
|
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &file,
|
|
|
|
const QString &url,
|
|
|
|
const QString &mime,
|
2022-03-21 02:48:27 +03:00
|
|
|
uint64_t dsize,
|
|
|
|
uint64_t duration,
|
2022-03-21 03:24:53 +03:00
|
|
|
const QSize &dimensions,
|
|
|
|
const std::optional<mtx::crypto::EncryptedFile> &thumbnailEncryptedFile,
|
2022-03-21 07:49:12 +03:00
|
|
|
const QString &thumbnailUrl,
|
|
|
|
uint64_t thumbnailSize,
|
|
|
|
const QSize &thumbnailDimensions,
|
|
|
|
const QString &blurhash);
|
2021-09-18 01:22:33 +03:00
|
|
|
|
2023-03-08 03:10:42 +03:00
|
|
|
QPair<QString, QString> getCommandAndArgs() const { return getCommandAndArgs(text()); }
|
|
|
|
QPair<QString, QString> getCommandAndArgs(const QString ¤tText) const;
|
2022-09-30 04:27:05 +03:00
|
|
|
mtx::common::Relations generateRelations() const;
|
2024-03-08 20:43:59 +03:00
|
|
|
mtx::common::Mentions generateMentions();
|
2022-09-30 04:27:05 +03:00
|
|
|
|
2022-03-21 00:49:33 +03:00
|
|
|
void startUploadFromPath(const QString &path);
|
|
|
|
void startUploadFromMimeData(const QMimeData &source, const QString &format);
|
|
|
|
void startUpload(std::unique_ptr<QIODevice> dev, const QString &orgPath, const QString &format);
|
2021-09-18 01:22:33 +03:00
|
|
|
void setUploading(bool value)
|
|
|
|
{
|
|
|
|
if (value != uploading_) {
|
|
|
|
uploading_ = value;
|
|
|
|
emit uploadingChanged(value);
|
2020-11-15 06:52:49 +03:00
|
|
|
}
|
2021-09-18 01:22:33 +03:00
|
|
|
}
|
|
|
|
|
2024-03-08 20:43:59 +03:00
|
|
|
void updateTextContentProperties(const QString &t, bool textDeleted = false);
|
2021-09-18 01:22:33 +03:00
|
|
|
|
2023-12-20 03:44:19 +03:00
|
|
|
void toggleIgnore(const QString &user, const bool ignored);
|
2023-12-18 02:17:43 +03:00
|
|
|
|
2021-09-18 01:22:33 +03:00
|
|
|
QTimer typingRefresh_;
|
|
|
|
QTimer typingTimeout_;
|
|
|
|
TimelineModel *room;
|
|
|
|
std::deque<QString> history_;
|
|
|
|
std::size_t history_index_ = 0;
|
|
|
|
int selectionStart = 0, selectionEnd = 0, cursorPosition = 0;
|
2023-03-08 03:10:42 +03:00
|
|
|
bool uploading_ = false;
|
|
|
|
bool containsAtRoom_ = false;
|
|
|
|
bool containsInvalidCommand_ = false;
|
|
|
|
bool containsIncompleteCommand_ = false;
|
2023-02-28 02:06:24 +03:00
|
|
|
QString currentCommand_;
|
2024-03-08 20:43:59 +03:00
|
|
|
QStringList mentions_, mentionTexts_;
|
|
|
|
// store stuff during edits
|
|
|
|
QStringList mentionsBefore, mentionTextsBefore;
|
|
|
|
QString textBeforeEdit;
|
2024-06-09 16:30:30 +03:00
|
|
|
bool containsAtRoomBefore = false;
|
2022-03-21 00:49:33 +03:00
|
|
|
|
|
|
|
using UploadHandle = std::unique_ptr<MediaUpload, DeleteLaterDeleter>;
|
|
|
|
std::vector<UploadHandle> unconfirmedUploads;
|
|
|
|
std::vector<UploadHandle> runningUploads;
|
2020-11-01 01:24:07 +03:00
|
|
|
};
|