mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
feat(db): Hookup file history to the rest of the system
This commit is contained in:
parent
567ddfb203
commit
d9b39b3102
|
@ -39,6 +39,7 @@
|
|||
#include <QPainter>
|
||||
#include <QVariantAnimation>
|
||||
|
||||
#include <cassert>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
|
@ -148,10 +149,11 @@ void FileTransferWidget::autoAcceptTransfer(const QString& path)
|
|||
|
||||
// Do not automatically accept the file-transfer if the path is not writable.
|
||||
// The user can still accept it manually.
|
||||
if (tryRemoveFile(filepath))
|
||||
if (tryRemoveFile(filepath)) {
|
||||
Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath);
|
||||
else
|
||||
} else {
|
||||
qWarning() << "Cannot write to " << filepath;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileTransferWidget::isActive() const
|
||||
|
@ -161,8 +163,9 @@ bool FileTransferWidget::isActive() const
|
|||
|
||||
void FileTransferWidget::acceptTransfer(const QString& filepath)
|
||||
{
|
||||
if (filepath.isEmpty())
|
||||
if (filepath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// test if writable
|
||||
if (!tryRemoveFile(filepath)) {
|
||||
|
@ -290,17 +293,19 @@ void FileTransferWidget::onFileTransferFinished(ToxFile file)
|
|||
|
||||
void FileTransferWidget::fileTransferRemotePausedUnpaused(ToxFile file, bool paused)
|
||||
{
|
||||
if (paused)
|
||||
if (paused) {
|
||||
onFileTransferPaused(file);
|
||||
else
|
||||
} else {
|
||||
onFileTransferResumed(file);
|
||||
}
|
||||
}
|
||||
|
||||
void FileTransferWidget::fileTransferBrokenUnbroken(ToxFile file, bool broken)
|
||||
{
|
||||
// TODO: Handle broken transfer differently once we have resuming code
|
||||
if (broken)
|
||||
if (broken) {
|
||||
onFileTransferCancelled(file);
|
||||
}
|
||||
}
|
||||
|
||||
QString FileTransferWidget::getHumanReadableSize(qint64 size)
|
||||
|
@ -308,16 +313,18 @@ QString FileTransferWidget::getHumanReadableSize(qint64 size)
|
|||
static const char* suffix[] = {"B", "kiB", "MiB", "GiB", "TiB"};
|
||||
int exp = 0;
|
||||
|
||||
if (size > 0)
|
||||
if (size > 0) {
|
||||
exp = std::min((int)(log(size) / log(1024)), (int)(sizeof(suffix) / sizeof(suffix[0]) - 1));
|
||||
}
|
||||
|
||||
return QString().setNum(size / pow(1024, exp), 'f', exp > 1 ? 2 : 0).append(suffix[exp]);
|
||||
}
|
||||
|
||||
void FileTransferWidget::updateWidgetColor(ToxFile const& file)
|
||||
{
|
||||
if (lastStatus == file.status)
|
||||
if (lastStatus == file.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (file.status) {
|
||||
case ToxFile::INITIALIZING:
|
||||
|
@ -332,13 +339,16 @@ void FileTransferWidget::updateWidgetColor(ToxFile const& file)
|
|||
case ToxFile::FINISHED:
|
||||
setBackgroundColor(Style::getColor(Style::Green), true);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FileTransferWidget::updateWidgetText(ToxFile const& file)
|
||||
{
|
||||
if (lastStatus == file.status)
|
||||
if (lastStatus == file.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (file.status) {
|
||||
case ToxFile::INITIALIZING:
|
||||
|
@ -361,29 +371,33 @@ void FileTransferWidget::updateWidgetText(ToxFile const& file)
|
|||
break;
|
||||
case ToxFile::FINISHED:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FileTransferWidget::updatePreview(ToxFile const& file)
|
||||
{
|
||||
if (lastStatus == file.status)
|
||||
if (lastStatus == file.status) {
|
||||
return;
|
||||
|
||||
switch (file.status) {
|
||||
case ToxFile::INITIALIZING:
|
||||
case ToxFile::PAUSED:
|
||||
case ToxFile::TRANSMITTING:
|
||||
case ToxFile::BROKEN:
|
||||
case ToxFile::CANCELED:
|
||||
if (file.direction == ToxFile::SENDING) {
|
||||
showPreview(file.filePath);
|
||||
}
|
||||
break;
|
||||
case ToxFile::FINISHED:
|
||||
showPreview(file.filePath);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (file.status) {
|
||||
case ToxFile::INITIALIZING:
|
||||
case ToxFile::PAUSED:
|
||||
case ToxFile::TRANSMITTING:
|
||||
case ToxFile::BROKEN:
|
||||
case ToxFile::CANCELED:
|
||||
if (file.direction == ToxFile::SENDING) {
|
||||
showPreview(file.filePath);
|
||||
}
|
||||
break;
|
||||
case ToxFile::FINISHED:
|
||||
showPreview(file.filePath);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FileTransferWidget::updateFileProgress(ToxFile const& file)
|
||||
|
@ -395,8 +409,9 @@ void FileTransferWidget::updateFileProgress(ToxFile const& file)
|
|||
fileProgress.resetSpeed();
|
||||
break;
|
||||
case ToxFile::TRANSMITTING: {
|
||||
if (!fileProgress.needsUpdate())
|
||||
if (!fileProgress.needsUpdate()) {
|
||||
break;
|
||||
}
|
||||
|
||||
fileProgress.addSample(file);
|
||||
auto speed = fileProgress.getSpeed();
|
||||
|
@ -426,13 +441,16 @@ void FileTransferWidget::updateFileProgress(ToxFile const& file)
|
|||
ui->etaLabel->hide();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FileTransferWidget::updateSignals(ToxFile const& file)
|
||||
{
|
||||
if (lastStatus == file.status)
|
||||
if (lastStatus == file.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (file.status) {
|
||||
case ToxFile::CANCELED:
|
||||
|
@ -445,13 +463,16 @@ void FileTransferWidget::updateSignals(ToxFile const& file)
|
|||
case ToxFile::PAUSED:
|
||||
case ToxFile::TRANSMITTING:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FileTransferWidget::setupButtons(ToxFile const& file)
|
||||
{
|
||||
if (lastStatus == file.status)
|
||||
if (lastStatus == file.status) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (file.status) {
|
||||
case ToxFile::TRANSMITTING:
|
||||
|
@ -510,27 +531,30 @@ void FileTransferWidget::setupButtons(ToxFile const& file)
|
|||
ui->rightButton->show();
|
||||
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FileTransferWidget::handleButton(QPushButton* btn)
|
||||
{
|
||||
if (fileInfo.direction == ToxFile::SENDING) {
|
||||
if (btn->objectName() == "cancel")
|
||||
if (btn->objectName() == "cancel") {
|
||||
Core::getInstance()->cancelFileSend(fileInfo.friendId, fileInfo.fileNum);
|
||||
else if (btn->objectName() == "pause")
|
||||
} else if (btn->objectName() == "pause") {
|
||||
Core::getInstance()->pauseResumeFileSend(fileInfo.friendId, fileInfo.fileNum);
|
||||
else if (btn->objectName() == "resume")
|
||||
} else if (btn->objectName() == "resume") {
|
||||
Core::getInstance()->pauseResumeFileSend(fileInfo.friendId, fileInfo.fileNum);
|
||||
}
|
||||
} else // receiving or paused
|
||||
{
|
||||
if (btn->objectName() == "cancel")
|
||||
if (btn->objectName() == "cancel") {
|
||||
Core::getInstance()->cancelFileRecv(fileInfo.friendId, fileInfo.fileNum);
|
||||
else if (btn->objectName() == "pause")
|
||||
} else if (btn->objectName() == "pause") {
|
||||
Core::getInstance()->pauseResumeFileRecv(fileInfo.friendId, fileInfo.fileNum);
|
||||
else if (btn->objectName() == "resume")
|
||||
} else if (btn->objectName() == "resume") {
|
||||
Core::getInstance()->pauseResumeFileRecv(fileInfo.friendId, fileInfo.fileNum);
|
||||
else if (btn->objectName() == "accept") {
|
||||
} else if (btn->objectName() == "accept") {
|
||||
QString path =
|
||||
QFileDialog::getSaveFileName(Q_NULLPTR,
|
||||
tr("Save a file", "Title of the file saving dialog"),
|
||||
|
@ -575,7 +599,8 @@ void FileTransferWidget::showPreview(const QString& filename)
|
|||
ui->previewButton->setIcon(QIcon(iconPixmap));
|
||||
ui->previewButton->setIconSize(iconPixmap.size());
|
||||
ui->previewButton->show();
|
||||
// Show mouseover preview, but make sure it's not larger than 50% of the screen width/height
|
||||
// Show mouseover preview, but make sure it's not larger than 50% of the screen
|
||||
// width/height
|
||||
const QRect desktopSize = QApplication::desktop()->screenGeometry();
|
||||
const int maxPreviewWidth{desktopSize.width() / 2};
|
||||
const int maxPreviewHeight{desktopSize.height() / 2};
|
||||
|
@ -616,7 +641,8 @@ QPixmap FileTransferWidget::scaleCropIntoSquare(const QPixmap& source, const int
|
|||
{
|
||||
QPixmap result;
|
||||
|
||||
// Make sure smaller-than-icon images (at least one dimension is smaller) will not be upscaled
|
||||
// Make sure smaller-than-icon images (at least one dimension is smaller) will not be
|
||||
// upscaled
|
||||
if (source.width() < targetSize || source.height() < targetSize) {
|
||||
result = source;
|
||||
} else {
|
||||
|
@ -626,10 +652,11 @@ QPixmap FileTransferWidget::scaleCropIntoSquare(const QPixmap& source, const int
|
|||
|
||||
// Then, image has to be cropped (if needed) so it will not overflow rectangle
|
||||
// Only one dimension will be bigger after Qt::KeepAspectRatioByExpanding
|
||||
if (result.width() > targetSize)
|
||||
if (result.width() > targetSize) {
|
||||
return result.copy((result.width() - targetSize) / 2, 0, targetSize, targetSize);
|
||||
else if (result.height() > targetSize)
|
||||
} else if (result.height() > targetSize) {
|
||||
return result.copy(0, (result.height() - targetSize) / 2, targetSize, targetSize);
|
||||
}
|
||||
|
||||
// Picture was rectangle in the first place, no cropping
|
||||
return result;
|
||||
|
@ -639,8 +666,9 @@ int FileTransferWidget::getExifOrientation(const char* data, const int size)
|
|||
{
|
||||
ExifData* exifData = exif_data_new_from_data(reinterpret_cast<const unsigned char*>(data), size);
|
||||
|
||||
if (!exifData)
|
||||
if (!exifData) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int orientation = 0;
|
||||
const ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
|
||||
|
@ -689,8 +717,9 @@ void FileTransferWidget::applyTransformation(const int orientation, QImage& imag
|
|||
|
||||
void FileTransferWidget::updateWidget(ToxFile const& file)
|
||||
{
|
||||
if (fileInfo != file)
|
||||
if (fileInfo != file) {
|
||||
return;
|
||||
}
|
||||
|
||||
fileInfo = file;
|
||||
|
||||
|
@ -709,9 +738,10 @@ void FileTransferWidget::updateWidget(ToxFile const& file)
|
|||
// trigger repaint
|
||||
switch (file.status) {
|
||||
case ToxFile::TRANSMITTING:
|
||||
if (!bTransmitNeedsUpdate)
|
||||
if (!bTransmitNeedsUpdate) {
|
||||
break;
|
||||
// fallthrough
|
||||
}
|
||||
// fallthrough
|
||||
default:
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -55,8 +55,9 @@ void ToxFileProgress::addSample(ToxFile const& file)
|
|||
meanData[meanIndex++] = bytesPerSec;
|
||||
|
||||
double meanBytesPerSec = 0.0;
|
||||
for (size_t i = 0; i < TRANSFER_ROLLING_AVG_COUNT; ++i)
|
||||
for (size_t i = 0; i < TRANSFER_ROLLING_AVG_COUNT; ++i) {
|
||||
meanBytesPerSec += meanData[i];
|
||||
}
|
||||
meanBytesPerSec /= static_cast<qreal>(TRANSFER_ROLLING_AVG_COUNT);
|
||||
|
||||
lastTick = now;
|
||||
|
|
|
@ -64,6 +64,7 @@ History::History(std::shared_ptr<RawDatabase> db)
|
|||
}
|
||||
|
||||
connect(this, &History::fileInsertionReady, this, &History::onFileInsertionReady);
|
||||
connect(this, &History::fileInserted, this, &History::onFileInserted);
|
||||
|
||||
db->execLater(
|
||||
"CREATE TABLE IF NOT EXISTS peers (id INTEGER PRIMARY KEY, public_key TEXT NOT NULL "
|
||||
|
@ -274,6 +275,8 @@ void History::onFileInsertionReady(FileDbInsertionData data)
|
|||
|
||||
// peerId is guaranteed to be inserted since we just used it in addNewMessage
|
||||
auto peerId = peers[data.friendPk];
|
||||
// Copy to pass into labmda for later
|
||||
auto fileId = data.fileId;
|
||||
queries +=
|
||||
RawDatabase::Query(QStringLiteral("INSERT INTO file_transfers (chat_id, file_restart_id, "
|
||||
"file_path, file_name, file_size, direction, file_state) "
|
||||
|
@ -282,7 +285,13 @@ void History::onFileInsertionReady(FileDbInsertionData data)
|
|||
.arg(data.size)
|
||||
.arg(static_cast<int>(data.direction))
|
||||
.arg(ToxFile::CANCELED),
|
||||
{data.fileId.toUtf8(), data.filePath.toUtf8(), data.fileName}, {});
|
||||
{data.fileId.toUtf8(), data.filePath.toUtf8(), data.fileName},
|
||||
[weakThis, fileId](int64_t id) {
|
||||
auto pThis = weakThis.lock();
|
||||
if (pThis) {
|
||||
emit pThis->fileInserted(id, fileId);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
queries += RawDatabase::Query(QStringLiteral("UPDATE history "
|
||||
|
@ -293,6 +302,37 @@ void History::onFileInsertionReady(FileDbInsertionData data)
|
|||
db->execLater(queries);
|
||||
}
|
||||
|
||||
void History::onFileInserted(int64_t dbId, QString fileId)
|
||||
{
|
||||
auto& fileInfo = fileInfos[fileId];
|
||||
if (fileInfo.finished) {
|
||||
db->execLater(generateFileFinished(dbId, fileInfo.success, fileInfo.filePath));
|
||||
fileInfos.remove(fileId);
|
||||
} else {
|
||||
fileInfo.finished = false;
|
||||
fileInfo.fileId = dbId;
|
||||
}
|
||||
}
|
||||
|
||||
RawDatabase::Query History::generateFileFinished(int64_t id, bool success, const QString& filePath)
|
||||
{
|
||||
auto file_state = success ? ToxFile::FINISHED : ToxFile::CANCELED;
|
||||
if (filePath.length()) {
|
||||
return RawDatabase::Query(QStringLiteral("UPDATE file_transfers "
|
||||
"SET file_state = %1, file_path = ? "
|
||||
"WHERE id = %2")
|
||||
.arg(file_state)
|
||||
.arg(id),
|
||||
{filePath.toUtf8()});
|
||||
} else {
|
||||
return RawDatabase::Query(QStringLiteral("UPDATE file_transfers "
|
||||
"SET finished = %1 "
|
||||
"WHERE id = %2")
|
||||
.arg(file_state)
|
||||
.arg(id));
|
||||
}
|
||||
}
|
||||
|
||||
void History::addNewFileMessage(const QString& friendPk, const QString& fileId,
|
||||
const QByteArray& fileName, const QString& filePath, int64_t size,
|
||||
const QString& sender, const QDateTime& time, QString const& dispName)
|
||||
|
@ -365,6 +405,19 @@ void History::addNewMessage(const QString& friendPk, const QString& message, con
|
|||
insertIdCallback));
|
||||
}
|
||||
|
||||
void History::setFileFinished(const QString& fileId, bool success, const QString& filePath)
|
||||
{
|
||||
auto& fileInfo = fileInfos[fileId];
|
||||
if (fileInfo.fileId == -1) {
|
||||
fileInfo.finished = true;
|
||||
fileInfo.success = success;
|
||||
fileInfo.filePath = filePath;
|
||||
} else {
|
||||
db->execLater(generateFileFinished(fileInfo.fileId, success, filePath));
|
||||
}
|
||||
|
||||
fileInfos.remove(fileId);
|
||||
}
|
||||
/**
|
||||
* @brief Fetches chat messages from the database.
|
||||
* @param friendPk Friend publick key to fetch.
|
||||
|
@ -578,24 +631,44 @@ QList<History::HistMessage> History::getChatHistory(const QString& friendPk, con
|
|||
auto rowCallback = [&messages](const QVector<QVariant>& row) {
|
||||
// dispName and message could have null bytes, QString::fromUtf8
|
||||
// truncates on null bytes so we strip them
|
||||
messages += {row[0].toLongLong(),
|
||||
row[1].isNull(),
|
||||
QDateTime::fromMSecsSinceEpoch(row[2].toLongLong()),
|
||||
row[3].toString(),
|
||||
QString::fromUtf8(row[4].toByteArray().replace('\0', "")),
|
||||
row[5].toString(),
|
||||
QString::fromUtf8(row[6].toByteArray().replace('\0', ""))};
|
||||
auto id = row[0].toLongLong();
|
||||
auto isOfflineMessage = row[1].isNull();
|
||||
auto timestamp = QDateTime::fromMSecsSinceEpoch(row[2].toLongLong());
|
||||
auto friend_key = row[3].toString();
|
||||
auto display_name = QString::fromUtf8(row[4].toByteArray().replace('\0', ""));
|
||||
auto sender_key = row[5].toString();
|
||||
if (row[7].isNull()) {
|
||||
messages += {id, isOfflineMessage, timestamp, friend_key,
|
||||
display_name, sender_key, row[6].toString()};
|
||||
} else {
|
||||
ToxFile file;
|
||||
file.fileKind = TOX_FILE_KIND_DATA;
|
||||
file.resumeFileId = row[7].toString().toUtf8();
|
||||
file.filePath = QString::fromUtf8(row[8].toByteArray().replace('\0', ""));
|
||||
file.fileName =
|
||||
QString::fromUtf8(row[9].toByteArray())
|
||||
.toUtf8(); // for some reason the path has to be utf8 parsed even though it went in as a straight array
|
||||
file.filesize = row[10].toLongLong();
|
||||
file.direction = static_cast<ToxFile::FileDirection>(row[11].toLongLong());
|
||||
file.status = static_cast<ToxFile::FileStatus>(row[12].toInt());
|
||||
messages +=
|
||||
{id, isOfflineMessage, timestamp, friend_key, display_name, sender_key, file};
|
||||
}
|
||||
};
|
||||
|
||||
// Don't forget to update the rowCallback if you change the selected columns!
|
||||
QString queryText =
|
||||
QString("SELECT history.id, faux_offline_pending.id, timestamp, "
|
||||
"chat.public_key, aliases.display_name, sender.public_key, "
|
||||
"message FROM history "
|
||||
"message, file_transfers.file_restart_id, "
|
||||
"file_transfers.file_path, file_transfers.file_name, "
|
||||
"file_transfers.file_size, file_transfers.direction, "
|
||||
"file_transfers.file_state FROM history "
|
||||
"LEFT JOIN faux_offline_pending ON history.id = faux_offline_pending.id "
|
||||
"JOIN peers chat ON chat_id = chat.id "
|
||||
"JOIN peers chat ON history.chat_id = chat.id "
|
||||
"JOIN aliases ON sender_alias = aliases.id "
|
||||
"JOIN peers sender ON aliases.owner = sender.id "
|
||||
"LEFT JOIN file_transfers ON history.file_id = file_transfers.id "
|
||||
"WHERE timestamp BETWEEN %1 AND %2 AND chat.public_key='%3'")
|
||||
.arg(from.toMSecsSinceEpoch())
|
||||
.arg(to.toMSecsSinceEpoch())
|
||||
|
|
|
@ -22,11 +22,14 @@
|
|||
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QVector>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <tox/toxencryptsave.h>
|
||||
|
||||
#include "src/core/toxfile.h"
|
||||
#include "src/core/toxpk.h"
|
||||
#include "src/persistence/db/rawdatabase.h"
|
||||
#include "src/widget/searchtypes.h"
|
||||
|
@ -34,6 +37,60 @@
|
|||
class Profile;
|
||||
class HistoryKeeper;
|
||||
|
||||
enum class HistMessageContentType
|
||||
{
|
||||
message,
|
||||
file
|
||||
};
|
||||
|
||||
class HistMessageContent
|
||||
{
|
||||
public:
|
||||
HistMessageContent(QString message)
|
||||
: data(std::make_shared<QString>(std::move(message)))
|
||||
, type(HistMessageContentType::message)
|
||||
{}
|
||||
|
||||
HistMessageContent(ToxFile file)
|
||||
: data(std::make_shared<ToxFile>(std::move(file)))
|
||||
, type(HistMessageContentType::file)
|
||||
{}
|
||||
|
||||
HistMessageContentType getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
QString& asMessage()
|
||||
{
|
||||
assert(type == HistMessageContentType::message);
|
||||
return *static_cast<QString*>(data.get());
|
||||
}
|
||||
|
||||
ToxFile& asFile()
|
||||
{
|
||||
assert(type == HistMessageContentType::file);
|
||||
return *static_cast<ToxFile*>(data.get());
|
||||
}
|
||||
|
||||
const QString& asMessage() const
|
||||
{
|
||||
assert(type == HistMessageContentType::message);
|
||||
return *static_cast<QString*>(data.get());
|
||||
}
|
||||
|
||||
const ToxFile& asFile() const
|
||||
{
|
||||
assert(type == HistMessageContentType::file);
|
||||
return *static_cast<ToxFile*>(data.get());
|
||||
}
|
||||
|
||||
private:
|
||||
// Not really shared but shared_ptr has support for shared_ptr<void>
|
||||
std::shared_ptr<void> data;
|
||||
HistMessageContentType type;
|
||||
};
|
||||
|
||||
struct FileDbInsertionData
|
||||
{
|
||||
FileDbInsertionData();
|
||||
|
@ -58,20 +115,32 @@ public:
|
|||
QString sender, QString message)
|
||||
: chat{chat}
|
||||
, sender{sender}
|
||||
, message{message}
|
||||
, dispName{dispName}
|
||||
, timestamp{timestamp}
|
||||
, id{id}
|
||||
, isSent{isSent}
|
||||
, content(std::move(message))
|
||||
{}
|
||||
|
||||
HistMessage(qint64 id, bool isSent, QDateTime timestamp, QString chat, QString dispName,
|
||||
QString sender, ToxFile file)
|
||||
: chat{chat}
|
||||
, sender{sender}
|
||||
, dispName{dispName}
|
||||
, timestamp{timestamp}
|
||||
, id{id}
|
||||
, isSent{isSent}
|
||||
, content(std::move(file))
|
||||
{}
|
||||
|
||||
|
||||
QString chat;
|
||||
QString sender;
|
||||
QString message;
|
||||
QString dispName;
|
||||
QDateTime timestamp;
|
||||
qint64 id;
|
||||
bool isSent;
|
||||
HistMessageContent content;
|
||||
};
|
||||
|
||||
struct DateMessages
|
||||
|
@ -98,6 +167,8 @@ public:
|
|||
const QByteArray& fileName, const QString& filePath, int64_t size,
|
||||
const QString& sender, const QDateTime& time, QString const& dispName);
|
||||
|
||||
void setFileFinished(const QString& fileId, bool success, const QString& filePath);
|
||||
|
||||
QList<HistMessage> getChatHistoryFromDate(const QString& friendPk, const QDateTime& from,
|
||||
const QDateTime& to);
|
||||
QList<HistMessage> getChatHistoryDefaultNum(const QString& friendPk);
|
||||
|
@ -116,19 +187,34 @@ protected:
|
|||
|
||||
signals:
|
||||
void fileInsertionReady(FileDbInsertionData data);
|
||||
void fileInserted(int64_t dbId, QString fileId);
|
||||
|
||||
private slots:
|
||||
void onFileInsertionReady(FileDbInsertionData data);
|
||||
void onFileInserted(int64_t dbId, QString fileId);
|
||||
|
||||
private:
|
||||
QList<HistMessage> getChatHistory(const QString& friendPk, const QDateTime& from,
|
||||
const QDateTime& to, int numMessages);
|
||||
|
||||
static RawDatabase::Query generateFileFinished(int64_t fileId, bool success,
|
||||
const QString& filePath);
|
||||
void dbSchemaUpgrade();
|
||||
|
||||
std::shared_ptr<RawDatabase> db;
|
||||
|
||||
|
||||
QHash<QString, int64_t> peers;
|
||||
struct FileInfo
|
||||
{
|
||||
bool finished = false;
|
||||
bool success = false;
|
||||
QString filePath;
|
||||
int64_t fileId = -1;
|
||||
};
|
||||
|
||||
// This needs to be a shared pointer to avoid callback lifetime issues
|
||||
QHash<QString, FileInfo> fileInfos;
|
||||
};
|
||||
|
||||
#endif // HISTORY_H
|
||||
|
|
|
@ -161,6 +161,9 @@ ChatForm::ChatForm(Friend* chatFriend, History* history)
|
|||
connect(core, &Core::fileReceiveRequested, this, &ChatForm::onFileRecvRequest);
|
||||
connect(profile, &Profile::friendAvatarChanged, this, &ChatForm::onAvatarChanged);
|
||||
connect(core, &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
||||
connect(core, &Core::fileTransferFinished, this, &ChatForm::onFileTransferFinished);
|
||||
connect(core, &Core::fileTransferCancelled, this, &ChatForm::onFileTransferCancelled);
|
||||
connect(core, &Core::fileTransferBrokenUnbroken, this, &ChatForm::onFileTransferBrokenUnbroken);
|
||||
connect(core, &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
|
||||
connect(core, &Core::receiptRecieved, this, &ChatForm::onReceiptReceived);
|
||||
connect(core, &Core::friendMessageReceived, this, &ChatForm::onFriendMessageReceived);
|
||||
|
@ -312,9 +315,35 @@ void ChatForm::startFileSend(ToxFile file)
|
|||
|
||||
insertChatMessage(
|
||||
ChatMessage::createFileTransferMessage(name, file, true, QDateTime::currentDateTime()));
|
||||
|
||||
if (history && Settings::getInstance().getEnableLogging()) {
|
||||
auto selfPk = Core::getInstance()->getSelfId().toString();
|
||||
auto pk = f->getPublicKey().toString();
|
||||
auto name = Core::getInstance()->getUsername();
|
||||
history->addNewFileMessage(pk, file.resumeFileId, file.fileName, file.filePath,
|
||||
file.filesize, selfPk, QDateTime::currentDateTime(), name);
|
||||
}
|
||||
|
||||
Widget::getInstance()->updateFriendActivity(f);
|
||||
}
|
||||
|
||||
void ChatForm::onFileTransferFinished(ToxFile file)
|
||||
{
|
||||
history->setFileFinished(file.resumeFileId, true, file.filePath);
|
||||
}
|
||||
|
||||
void ChatForm::onFileTransferBrokenUnbroken(ToxFile file, bool broken)
|
||||
{
|
||||
if (broken) {
|
||||
history->setFileFinished(file.resumeFileId, false, file.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::onFileTransferCancelled(ToxFile file)
|
||||
{
|
||||
history->setFileFinished(file.resumeFileId, false, file.filePath);
|
||||
}
|
||||
|
||||
void ChatForm::onFileRecvRequest(ToxFile file)
|
||||
{
|
||||
if (file.friendId != f->getId()) {
|
||||
|
@ -331,9 +360,17 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
|||
|
||||
ChatMessage::Ptr msg =
|
||||
ChatMessage::createFileTransferMessage(name, file, false, QDateTime::currentDateTime());
|
||||
|
||||
insertChatMessage(msg);
|
||||
|
||||
if (history && Settings::getInstance().getEnableLogging()) {
|
||||
auto pk = f->getPublicKey().toString();
|
||||
auto name = f->getDisplayedName();
|
||||
history->addNewFileMessage(pk, file.resumeFileId, file.fileName, file.filePath,
|
||||
file.filesize, pk, QDateTime::currentDateTime(), name);
|
||||
}
|
||||
ChatLineContentProxy* proxy = static_cast<ChatLineContentProxy*>(msg->getContent(1));
|
||||
|
||||
assert(proxy->getWidgetType() == ChatLineContentProxy::FileTransferWidgetType);
|
||||
FileTransferWidget* tfWidget = static_cast<FileTransferWidget*>(proxy->getWidget());
|
||||
|
||||
|
@ -794,6 +831,11 @@ void ChatForm::handleLoadedMessages(QList<History::HistMessage> newHistMsgs, boo
|
|||
MessageMetadata const metadata = getMessageMetadata(histMessage);
|
||||
lastDate = addDateLineIfNeeded(chatLines, lastDate, histMessage, metadata);
|
||||
auto msg = chatMessageFromHistMessage(histMessage, metadata);
|
||||
|
||||
if (!msg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (processUndelivered) {
|
||||
sendLoadedMessage(msg, metadata);
|
||||
}
|
||||
|
@ -838,7 +880,9 @@ ChatForm::MessageMetadata ChatForm::getMessageMetadata(History::HistMessage cons
|
|||
const QDateTime msgDateTime = histMessage.timestamp.toLocalTime();
|
||||
const bool isSelf = Core::getInstance()->getSelfId().getPublicKey() == authorPk;
|
||||
const bool needSending = !histMessage.isSent && isSelf;
|
||||
const bool isAction = histMessage.message.startsWith(ACTION_PREFIX, Qt::CaseInsensitive);
|
||||
const bool isAction =
|
||||
histMessage.content.getType() == HistMessageContentType::message
|
||||
&& histMessage.content.asMessage().startsWith(ACTION_PREFIX, Qt::CaseInsensitive);
|
||||
const qint64 id = histMessage.id;
|
||||
return {isSelf, needSending, isAction, id, authorPk, msgDateTime};
|
||||
}
|
||||
|
@ -848,11 +892,30 @@ ChatMessage::Ptr ChatForm::chatMessageFromHistMessage(History::HistMessage const
|
|||
{
|
||||
ToxPk authorPk(ToxId(histMessage.sender).getPublicKey());
|
||||
QString authorStr = getMsgAuthorDispName(authorPk, histMessage.dispName);
|
||||
QString messageText =
|
||||
metadata.isAction ? histMessage.message.mid(ACTION_PREFIX.length()) : histMessage.message;
|
||||
ChatMessage::MessageType type = metadata.isAction ? ChatMessage::ACTION : ChatMessage::NORMAL;
|
||||
QDateTime dateTime = metadata.needSending ? QDateTime() : metadata.msgDateTime;
|
||||
auto msg = ChatMessage::createChatMessage(authorStr, messageText, type, metadata.isSelf, dateTime);
|
||||
|
||||
|
||||
ChatMessage::Ptr msg;
|
||||
|
||||
switch (histMessage.content.getType()) {
|
||||
case HistMessageContentType::message: {
|
||||
ChatMessage::MessageType type = metadata.isAction ? ChatMessage::ACTION : ChatMessage::NORMAL;
|
||||
auto& message = histMessage.content.asMessage();
|
||||
QString messageText = metadata.isAction ? message.mid(ACTION_PREFIX.length()) : message;
|
||||
|
||||
msg = ChatMessage::createChatMessage(authorStr, messageText, type, metadata.isSelf, dateTime);
|
||||
break;
|
||||
}
|
||||
case HistMessageContentType::file: {
|
||||
auto& file = histMessage.content.asFile();
|
||||
bool isMe = file.direction == ToxFile::SENDING;
|
||||
msg = ChatMessage::createFileTransferMessage(authorStr, file, isMe, dateTime);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (!metadata.isAction && needsToHideName(authorPk, metadata.msgDateTime)) {
|
||||
msg->hideSender();
|
||||
}
|
||||
|
@ -1135,13 +1198,19 @@ void ChatForm::onExportChat()
|
|||
|
||||
QString buffer;
|
||||
for (const auto& it : msgs) {
|
||||
if (it.content.getType() != HistMessageContentType::message) {
|
||||
continue;
|
||||
}
|
||||
QString timestamp = it.timestamp.time().toString("hh:mm:ss");
|
||||
QString datestamp = it.timestamp.date().toString("yyyy-MM-dd");
|
||||
ToxPk authorPk(ToxId(it.sender).getPublicKey());
|
||||
QString author = getMsgAuthorDispName(authorPk, it.dispName);
|
||||
|
||||
buffer = buffer
|
||||
% QString{datestamp % '\t' % timestamp % '\t' % author % '\t' % it.message % '\n'};
|
||||
if (it.content.getType() == HistMessageContentType::message) {
|
||||
buffer = buffer
|
||||
% QString{datestamp % '\t' % timestamp % '\t' % author % '\t'
|
||||
% it.content.asMessage() % '\n'};
|
||||
}
|
||||
}
|
||||
file.write(buffer.toUtf8());
|
||||
file.close();
|
||||
|
|
|
@ -68,11 +68,14 @@ signals:
|
|||
|
||||
public slots:
|
||||
void startFileSend(ToxFile file);
|
||||
void onFileTransferFinished(ToxFile file);
|
||||
void onFileTransferCancelled(ToxFile file);
|
||||
void onFileTransferBrokenUnbroken(ToxFile file, bool broken);
|
||||
void onFileRecvRequest(ToxFile file);
|
||||
void onAvInvite(uint32_t friendId, bool video);
|
||||
void onAvStart(uint32_t friendId, bool video);
|
||||
void onAvEnd(uint32_t friendId, bool error);
|
||||
void onAvatarChanged(const ToxPk &friendPk, const QPixmap& pic);
|
||||
void onAvatarChanged(const ToxPk& friendPk, const QPixmap& pic);
|
||||
void onFileNameChanged(const ToxPk& friendPk);
|
||||
void clearChatArea();
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user