mirror of
https://github.com/transmission/transmission
synced 2025-01-30 19:03:04 +00:00
feat: nicer add-torrent workflow in the qt client. (#1410)
* If you accidentally try to add a lot of duplicates -- for example, by starting up with a lot of duplicate torrents in the watchdir -- then coalesce all of them into a single error dialog instead of spamming the desktop with a different dialog for every duplicate. * Make the duplicate torrent dialog's error message slightly terser to make it accommodate a long list of torrents: omit the ".torrent" file suffixes and show an abbreviated form of the existing torrent's hash. * Support searching by torrent hash in the filterbar's text entry. This is useful when copy/pasting the hash from the duplicate torrent error dialog and is also consistent with the GTK client behavior. * Copy the GTK client's behavior of appending ".added" to the end of .torrent files after they've been added to the session.
This commit is contained in:
parent
1bb9e2eef2
commit
cf9b81eb7d
4 changed files with 56 additions and 12 deletions
|
@ -121,7 +121,7 @@ QString AddData::readableShortName() const
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case FILENAME:
|
case FILENAME:
|
||||||
return QFileInfo(filename).fileName();
|
return QFileInfo(filename).baseName();
|
||||||
|
|
||||||
case URL:
|
case URL:
|
||||||
return url.path().split(QLatin1Char('/')).last();
|
return url.path().split(QLatin1Char('/')).last();
|
||||||
|
|
|
@ -311,6 +311,9 @@ Session::Session(QString config_dir, Prefs& prefs) :
|
||||||
connect(&rpc_, SIGNAL(dataSendProgress()), this, SIGNAL(dataSendProgress()));
|
connect(&rpc_, SIGNAL(dataSendProgress()), this, SIGNAL(dataSendProgress()));
|
||||||
connect(&rpc_, SIGNAL(networkResponse(QNetworkReply::NetworkError, QString)), this,
|
connect(&rpc_, SIGNAL(networkResponse(QNetworkReply::NetworkError, QString)), this,
|
||||||
SIGNAL(networkResponse(QNetworkReply::NetworkError, QString)));
|
SIGNAL(networkResponse(QNetworkReply::NetworkError, QString)));
|
||||||
|
|
||||||
|
duplicates_timer_.setSingleShot(true);
|
||||||
|
connect(&duplicates_timer_, &QTimer::timeout, this, &Session::onDuplicatesTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Session::~Session()
|
Session::~Session()
|
||||||
|
@ -1100,23 +1103,30 @@ void Session::addTorrent(AddData const& add_me, tr_variant* args, bool trash_ori
|
||||||
d->show();
|
d->show();
|
||||||
});
|
});
|
||||||
|
|
||||||
q->add([add_me](RpcResponse const& r)
|
q->add([this, add_me](RpcResponse const& r)
|
||||||
{
|
{
|
||||||
tr_variant* dup;
|
tr_variant* dup;
|
||||||
|
bool session_has_torrent = false;
|
||||||
|
|
||||||
if (!tr_variantDictFindDict(r.args.get(), TR_KEY_torrent_duplicate, &dup))
|
if (tr_variantDictFindDict(r.args.get(), TR_KEY_torrent_added, &dup))
|
||||||
{
|
{
|
||||||
return;
|
session_has_torrent = true;
|
||||||
|
}
|
||||||
|
else if (tr_variantDictFindDict(r.args.get(), TR_KEY_torrent_duplicate, &dup))
|
||||||
|
{
|
||||||
|
session_has_torrent = true;
|
||||||
|
|
||||||
|
auto const hash = dictFind<QString>(dup, TR_KEY_hashString);
|
||||||
|
if (hash)
|
||||||
|
{
|
||||||
|
duplicates_.try_emplace(add_me.readableShortName(), *hash);
|
||||||
|
duplicates_timer_.start(1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const name = dictFind<QString>(dup, TR_KEY_name);
|
if (session_has_torrent && !add_me.filename.isEmpty())
|
||||||
if (name)
|
|
||||||
{
|
{
|
||||||
auto* d = new QMessageBox(QMessageBox::Warning, tr("Add Torrent"),
|
QFile(add_me.filename).rename(QStringLiteral("%1.added").arg(add_me.filename));
|
||||||
tr(R"(<p><b>Unable to add "%1".</b></p><p>It is a duplicate of "%2" which is already added.</p>)").
|
|
||||||
arg(add_me.readableShortName()).arg(*name), QMessageBox::Close, qApp->activeWindow());
|
|
||||||
QObject::connect(d, &QMessageBox::rejected, d, &QMessageBox::deleteLater);
|
|
||||||
d->show();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1133,6 +1143,32 @@ void Session::addTorrent(AddData const& add_me, tr_variant* args, bool trash_ori
|
||||||
q->run();
|
q->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::onDuplicatesTimer()
|
||||||
|
{
|
||||||
|
decltype(duplicates_) duplicates;
|
||||||
|
duplicates.swap(duplicates_);
|
||||||
|
|
||||||
|
QStringList lines;
|
||||||
|
for (auto it : duplicates_)
|
||||||
|
{
|
||||||
|
lines.push_back(tr("%1 (copy of %2)")
|
||||||
|
.arg(it.first)
|
||||||
|
.arg(it.second.left(7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lines.empty())
|
||||||
|
{
|
||||||
|
lines.sort(Qt::CaseInsensitive);
|
||||||
|
auto* d = new QMessageBox(QMessageBox::Warning,
|
||||||
|
tr("Unable to add Duplicate Torrent(s)", "", lines.size()),
|
||||||
|
lines.join(QStringLiteral("\n")),
|
||||||
|
QMessageBox::Close,
|
||||||
|
qApp->activeWindow());
|
||||||
|
QObject::connect(d, &QMessageBox::rejected, d, &QMessageBox::deleteLater);
|
||||||
|
d->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Session::addTorrent(AddData const& add_me)
|
void Session::addTorrent(AddData const& add_me)
|
||||||
{
|
{
|
||||||
tr_variant args;
|
tr_variant args;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <libtransmission/transmission.h>
|
#include <libtransmission/transmission.h>
|
||||||
#include <libtransmission/quark.h>
|
#include <libtransmission/quark.h>
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
#include "RpcQueue.h"
|
#include "RpcQueue.h"
|
||||||
#include "Torrent.h"
|
#include "Torrent.h"
|
||||||
#include "Typedefs.h"
|
#include "Typedefs.h"
|
||||||
|
#include "Utils.h" // std::hash<QString>
|
||||||
|
|
||||||
class AddData;
|
class AddData;
|
||||||
class Prefs;
|
class Prefs;
|
||||||
|
@ -120,6 +122,7 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void addTorrent(AddData const& addme);
|
void addTorrent(AddData const& addme);
|
||||||
void launchWebInterface();
|
void launchWebInterface();
|
||||||
|
void onDuplicatesTimer();
|
||||||
void queueMoveBottom(torrent_ids_t const& torrentIds = {});
|
void queueMoveBottom(torrent_ids_t const& torrentIds = {});
|
||||||
void queueMoveDown(torrent_ids_t const& torrentIds = {});
|
void queueMoveDown(torrent_ids_t const& torrentIds = {});
|
||||||
void queueMoveTop(torrent_ids_t const& torrentIds = {});
|
void queueMoveTop(torrent_ids_t const& torrentIds = {});
|
||||||
|
@ -176,4 +179,7 @@ private:
|
||||||
bool is_definitely_local_session_ = true;
|
bool is_definitely_local_session_ = true;
|
||||||
RpcClient rpc_;
|
RpcClient rpc_;
|
||||||
torrent_ids_t const RecentlyActiveIDs = { -1 };
|
torrent_ids_t const RecentlyActiveIDs = { -1 };
|
||||||
|
|
||||||
|
std::map<QString, QString> duplicates_;
|
||||||
|
QTimer duplicates_timer_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -253,7 +253,9 @@ bool TorrentFilter::filterAcceptsRow(int source_row, QModelIndex const& source_p
|
||||||
if (accepts)
|
if (accepts)
|
||||||
{
|
{
|
||||||
auto const text = prefs_.getString(Prefs::FILTER_TEXT);
|
auto const text = prefs_.getString(Prefs::FILTER_TEXT);
|
||||||
accepts = text.isEmpty() || tor.name().contains(text, Qt::CaseInsensitive);
|
accepts = text.isEmpty() ||
|
||||||
|
tor.name().contains(text, Qt::CaseInsensitive) ||
|
||||||
|
tor.hashString().contains(text, Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
return accepts;
|
return accepts;
|
||||||
|
|
Loading…
Reference in a new issue