Commit 6c728dd1 authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇
Browse files

[Notifications] Implement JobViewV3

This implements a new JobViewV3 which uses extensible variant maps rather than individual function calls,
allowing for compression of calls and extensibility.
The new API uses infoMessage correctly as a state message, e.g. "Connecting to host" rather than mixing it with the "Copying..." heading.
It also supports an "immediate" flag that the caller can use to indicate progress should be immediately shown,
so in cases where the user is likely to want to use the file afterwards (e.g. download through p-b-i or receiving a file
through KDE Connect) a job popup is shown even for small/short transfers.

Differential Revision: https://phabricator.kde.org/D23293
parent 2d4742d8
......@@ -33,7 +33,16 @@ Job::Job(uint id, QObject *parent)
: QObject(parent)
, d(new JobPrivate(id, this))
{
d->m_created = QDateTime::currentDateTimeUtc();
d->m_created = QDateTime::currentDateTimeUtc();
// These properties are used in generating the pretty job text
connect(d, &JobPrivate::infoMessageChanged, this, &Job::textChanged);
connect(this, &Job::processedFilesChanged, this, &Job::textChanged);
connect(this, &Job::totalFilesChanged, this, &Job::textChanged);
connect(this, &Job::descriptionValue1Changed, this, &Job::textChanged);
connect(this, &Job::descriptionValue2Changed, this, &Job::textChanged);
connect(this, &Job::destUrlChanged, this, &Job::textChanged);
connect(this, &Job::errorTextChanged, this, &Job::textChanged);
}
Job::~Job() = default;
......
......@@ -154,6 +154,10 @@ QString JobPrivate::text() const
return m_errorText;
}
if (!m_infoMessage.isEmpty()) {
return m_infoMessage;
}
const QString currentFileName = descriptionUrl().fileName();
const QString destUrlString = prettyDestUrl();
......@@ -285,6 +289,8 @@ void JobPrivate::setSpeed(quint64 bytesPerSecond)
updateHasDetails();
}
// NOTE infoMessage isn't supposed to be the "Copying..." heading but e.g. a "Connecting to server..." status message
// JobViewV1/V2 got that wrong but JobView3 uses "title" and "infoMessage" correctly respectively.
void JobPrivate::setInfoMessage(const QString &infoMessage)
{
updateField(infoMessage, m_summary, &Job::summaryChanged);
......@@ -338,7 +344,56 @@ void JobPrivate::terminate(uint errorCode, const QString &errorMessage, const QV
void JobPrivate::update(const QVariantMap &properties)
{
// TODO
sendErrorReply(QDBusError::NotSupported, QStringLiteral("JobViewV3 update is not yet implemented."));
Q_UNUSED(properties)
auto end = properties.end();
auto it = properties.find(QStringLiteral("title"));
if (it != end) {
updateField(it->toString(), m_summary, &Job::summaryChanged);
}
it = properties.find(QStringLiteral("infoMessage"));
if (it != end) {
// InfoMessage is exposed via text()/BodyRole, not via public API, hence no public signal
const QString infoMessage = it->toString();
if (m_infoMessage != infoMessage) {
m_infoMessage = it->toString();
emit infoMessageChanged();
}
}
it = properties.find(QStringLiteral("percent"));
if (it != end) {
setPercent(it->toUInt());
}
it = properties.find(QStringLiteral("destUrl"));
if (it != end) {
const QUrl destUrl = QUrl(it->toUrl().adjusted(QUrl::StripTrailingSlash)); // urgh
updateField(destUrl, m_destUrl, &Job::destUrlChanged);
}
it = properties.find(QStringLiteral("speed"));
if (it != end) {
setSpeed(it->value<qulonglong>());
}
updateFieldFromProperties(properties, QStringLiteral("processedFiles"), m_processedFiles, &Job::processedFilesChanged);
updateFieldFromProperties(properties, QStringLiteral("processedBytes"), m_processedBytes, &Job::processedBytesChanged);
updateFieldFromProperties(properties, QStringLiteral("processedDirectories"), m_processedDirectories, &Job::processedDirectoriesChanged);
updateFieldFromProperties(properties, QStringLiteral("totalFiles"), m_totalFiles, &Job::totalFilesChanged);
updateFieldFromProperties(properties, QStringLiteral("totalBytes"), m_totalBytes, &Job::totalBytesChanged);
updateFieldFromProperties(properties, QStringLiteral("totalDirectories"), m_totalDirectories, &Job::totalDirectoriesChanged);
updateFieldFromProperties(properties, QStringLiteral("descriptionLabel1"), m_descriptionLabel1, &Job::descriptionLabel1Changed);
updateFieldFromProperties(properties, QStringLiteral("descriptionValue1"), m_descriptionValue1, &Job::descriptionValue1Changed);
updateFieldFromProperties(properties, QStringLiteral("descriptionLabel2"), m_descriptionLabel2, &Job::descriptionLabel2Changed);
updateFieldFromProperties(properties, QStringLiteral("descriptionValue2"), m_descriptionValue2, &Job::descriptionValue2Changed);
it = properties.find(QStringLiteral("suspended"));
if (it != end) {
setSuspended(it->toBool());
}
updateHasDetails();
}
......@@ -72,6 +72,8 @@ public:
signals:
void closed();
void infoMessageChanged();
// DBus
// V1 and V2
void suspendRequested();
......@@ -95,6 +97,19 @@ private:
return false;
}
template<typename T> bool updateFieldFromProperties(const QVariantMap &properties,
const QString &keyName,
T &target,
void (Job::*changeSignal)())
{
auto it = properties.find(keyName);
if (it == properties.end()) {
return false;
}
return updateField(it->value<T>(), target, changeSignal);
}
static QSharedPointer<KFilePlacesModel> createPlacesModel();
static QUrl localFileOrUrl(const QString &stringUrl);
......@@ -111,6 +126,7 @@ private:
QDateTime m_updated;
QString m_summary;
QString m_infoMessage;
QString m_desktopEntry;
QString m_applicationName;
......
......@@ -324,6 +324,9 @@ QDBusObjectPath JobsModelPrivate::requestView(const QString &desktopEntry,
connect(job, &Job::summaryChanged, this, [this, job] {
scheduleUpdate(job, Notifications::SummaryRole);
});
connect(job, &Job::textChanged, this, [this, job] {
scheduleUpdate(job, Notifications::BodyRole);
});
connect(job, &Job::stateChanged, this, [this, job] {
scheduleUpdate(job, Notifications::JobStateRole);
// Timeout and Closable depend on state, signal a change for those, too
......@@ -349,26 +352,7 @@ QDBusObjectPath JobsModelPrivate::requestView(const QString &desktopEntry,
scheduleUpdate(job, Notifications::DismissedRole);
});
// The following are used in generating the pretty job text
connect(job, &Job::processedFilesChanged, this, [this, job] {
scheduleUpdate(job, Notifications::BodyRole);
});
connect(job, &Job::totalFilesChanged, this, [this, job] {
scheduleUpdate(job, Notifications::BodyRole);
});
connect(job, &Job::descriptionValue1Changed, this, [this, job] {
scheduleUpdate(job, Notifications::BodyRole);
});
connect(job, &Job::descriptionValue2Changed, this, [this, job] {
scheduleUpdate(job, Notifications::BodyRole);
});
connect(job, &Job::destUrlChanged, this, [this, job] {
scheduleUpdate(job, Notifications::BodyRole);
emitJobUrlsChanged();
});
connect(job, &Job::errorTextChanged, this, [this, job] {
scheduleUpdate(job, Notifications::BodyRole);
});
connect(job, &Job::destUrlChanged, this, &JobsModelPrivate::emitJobUrlsChanged);
connect(job->d, &JobPrivate::closed, this, [this, job] {
remove(job);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment