Skip to content

Commit

Permalink
feat: nicer add-torrent workflow in the qt client. (#1410)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
ckerr committed Aug 27, 2020
1 parent 1bb9e2e commit cf9b81e
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 12 deletions.
2 changes: 1 addition & 1 deletion qt/AddData.cc
Expand Up @@ -121,7 +121,7 @@ QString AddData::readableShortName() const
switch (type)
{
case FILENAME:
return QFileInfo(filename).fileName();
return QFileInfo(filename).baseName();

case URL:
return url.path().split(QLatin1Char('/')).last();
Expand Down
56 changes: 46 additions & 10 deletions qt/Session.cc
Expand Up @@ -311,6 +311,9 @@ Session::Session(QString config_dir, Prefs& prefs) :
connect(&rpc_, SIGNAL(dataSendProgress()), this, SIGNAL(dataSendProgress()));
connect(&rpc_, SIGNAL(networkResponse(QNetworkReply::NetworkError, QString)), this,
SIGNAL(networkResponse(QNetworkReply::NetworkError, QString)));

duplicates_timer_.setSingleShot(true);
connect(&duplicates_timer_, &QTimer::timeout, this, &Session::onDuplicatesTimer);
}

Session::~Session()
Expand Down Expand Up @@ -1100,23 +1103,30 @@ void Session::addTorrent(AddData const& add_me, tr_variant* args, bool trash_ori
d->show();
});

q->add([add_me](RpcResponse const& r)
q->add([this, add_me](RpcResponse const& r)
{
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 (name)
if (session_has_torrent && !add_me.filename.isEmpty())
{
auto* d = new QMessageBox(QMessageBox::Warning, tr("Add Torrent"),
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();
QFile(add_me.filename).rename(QStringLiteral("%1.added").arg(add_me.filename));
}
});

Expand All @@ -1133,6 +1143,32 @@ void Session::addTorrent(AddData const& add_me, tr_variant* args, bool trash_ori
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)
{
tr_variant args;
Expand Down
6 changes: 6 additions & 0 deletions qt/Session.h
Expand Up @@ -15,6 +15,7 @@
#include <QObject>
#include <QString>
#include <QStringList>
#include <QTimer>

#include <libtransmission/transmission.h>
#include <libtransmission/quark.h>
Expand All @@ -24,6 +25,7 @@
#include "RpcQueue.h"
#include "Torrent.h"
#include "Typedefs.h"
#include "Utils.h" // std::hash<QString>

class AddData;
class Prefs;
Expand Down Expand Up @@ -120,6 +122,7 @@ class Session : public QObject
public slots:
void addTorrent(AddData const& addme);
void launchWebInterface();
void onDuplicatesTimer();
void queueMoveBottom(torrent_ids_t const& torrentIds = {});
void queueMoveDown(torrent_ids_t const& torrentIds = {});
void queueMoveTop(torrent_ids_t const& torrentIds = {});
Expand Down Expand Up @@ -176,4 +179,7 @@ public slots:
bool is_definitely_local_session_ = true;
RpcClient rpc_;
torrent_ids_t const RecentlyActiveIDs = { -1 };

std::map<QString, QString> duplicates_;
QTimer duplicates_timer_;
};
4 changes: 3 additions & 1 deletion qt/TorrentFilter.cc
Expand Up @@ -253,7 +253,9 @@ bool TorrentFilter::filterAcceptsRow(int source_row, QModelIndex const& source_p
if (accepts)
{
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;
Expand Down

0 comments on commit cf9b81e

Please sign in to comment.