Merge branch 'ui_details_merge'

This commit is contained in:
Dessalines 2018-12-01 15:48:44 -07:00
commit b4a6b3779a
8 changed files with 118 additions and 79 deletions

View File

@ -25,6 +25,5 @@ node_modules
*~ *~
test/data/result.json test/data/result.json
package-lock.json package-lock.json
*.orig *.orig

View File

@ -1,18 +1,60 @@
.table-fixed { /* globally disable dotted outlines on anchors */
table-layout: fixed; a:focus {
outline: none;
}
a::-moz-focus-inner {
border: 0;
} }
.search-name-col { .search-name-col {
width: 50%; width: 50%;
} }
.search-name-cell { .search-name-cell {
word-wrap: break-word; word-wrap: break-word;
} }
.table-fixed {
table-layout: fixed;
}
/* add padding to first and last cell */
.table-padding th:first-child, .table-padding td:first-child {
padding-left: 1em !important;
}
.table-padding th:last-child, .table-padding td:last-child {
padding-right: 1em !important;
}
.table thead th {
border: 0px;
}
.table tr td {
vertical-align: middle !important;
}
/* Colors */
.navbar-purple { .navbar-purple {
background-color: #673ab7; background-color: #673ab7;
-webkit-box-shadow: 0 8px 6px -6px #999; }
-moz-box-shadow: 0 8px 6px -6px #999;
box-shadow: 0 8px 6px -6px #999; .table-hover-purple tbody tr:hover {
} background-color: rgba(103, 58, 183, 0.2) !important;
}
/* Bootstrap fixes */
.rounded-right {
/* disable radius on left when using on right side */
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.no-outline, .no-outline:focus {
/* disable colored outline on btn or input */
box-shadow: none !important;
outline: none !important;
}

View File

@ -13,16 +13,13 @@ export class Home extends Component<any, any> {
onboard() { onboard() {
return ( return (
<div> <p class="text-justify">
<br />
<a href={repoUrl}>Torrents.csv</a> is a <i>collaborative</i> git repository of torrents, consisting of a single, searchable <code>torrents.csv</code> file. Its initially populated with a January 2017 backup of the pirate bay, and new torrents are periodically added from various torrents sites. It comes with a self-hostable webserver, a command line search, and a folder scanner to add torrents.<br /><br /> <a href={repoUrl}>Torrents.csv</a> is a <i>collaborative</i> git repository of torrents, consisting of a single, searchable <code>torrents.csv</code> file. Its initially populated with a January 2017 backup of the pirate bay, and new torrents are periodically added from various torrents sites. It comes with a self-hostable webserver, a command line search, and a folder scanner to add torrents.<br /><br />
<a href={repoUrl}>Torrents.csv</a> will only store torrents with at least one seeder to keep the file small, will be periodically purged of non-seeded torrents, and sorted by seeders descending.<br /><br /> <a href={repoUrl}>Torrents.csv</a> will only store torrents with at least one seeder to keep the file small, will be periodically purged of non-seeded torrents, and sorted by seeders descending.<br /><br />
To request more torrents, or add your own, go <a href={repoUrl}>here</a>.<br /><br /> To request more torrents, or add your own, go <a href={repoUrl}>here</a>.<br /><br />
Made with <a href="https://www.rust-lang.org">Rust</a>, <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>, <a href="https://actix.rs/">Actix</a>, <a href="https://www.infernojs.org">Inferno</a>, and <a href="https://www.typescriptlang.org/">Typescript</a>. Made with <a href="https://www.rust-lang.org">Rust</a>, <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>, <a href="https://actix.rs/">Actix</a>, <a href="https://www.infernojs.org">Inferno</a>, and <a href="https://www.typescriptlang.org/">Typescript</a>.
</p>
</div>
); );
} }
} }

View File

@ -23,37 +23,33 @@ export class Navbar extends Component<any, State> {
render() { render() {
return ( return (
<div>{this.navbar()}</div> <div class="sticky-top">{this.navbar()}</div>
) )
} }
navbar() { navbar() {
return ( return (
<nav class="navbar navbar-dark navbar-purple"> <nav class="navbar navbar-dark navbar-purple p-1 shadow">
<a class="navbar-brand" href="#"> <a class="navbar-brand mx-1" href="#">
<i class="fas fa-fw fa-database mr-1"></i> <i class="fas fa-fw fa-database mr-1"></i>
Torrents.csv Torrents.csv
</a> </a>
<ul class="navbar-nav ml-auto"> <div class="navbar-nav ml-auto mr-2">
<li class="nav-item"> <a class="nav-item nav-link" href={repoUrl}><i class="fab fa-fw fa-github"></i></a>
<a class="nav-link" href={repoUrl}><i class="fab fa-fw fa-github"></i></a>
</li>
</ul>
<div class="col-12 col-sm-6">
{this.searchForm()}
</div> </div>
{this.searchForm()}
</nav> </nav>
); );
} }
// TODO
// https://www.codeply.com/go/xBVaM3q5X4/bootstrap-4-navbar-search-full-width
searchForm() { searchForm() {
return ( return (
<form class="my-2 my-lg-0 d-inline w-100" onSubmit={linkEvent(this, this.search)}> <form class="col-12 col-sm-6 m-0 px-1" onSubmit={linkEvent(this, this.search)}>
<div class="input-group"> <div class="input-group w-100">
<input value={this.state.searchParams.q} onInput={linkEvent(this, this.searchChange)} type="text" class="form-control" placeholder="Search..." /> <input class="form-control border-0 no-outline" type="search" placeholder="Search..." aria-label="Search..." required
<button type="submit" class="btn btn-light border border-left-0 border-radius-left-0"> value={this.state.searchParams.q}
onInput={linkEvent(this, this.searchChange)}></input>
<button class="btn btn-light bg-white border-0 rounded-right no-outline" type="submit">
<i className="fas fa-fw fa-search"></i> <i className="fas fa-fw fa-search"></i>
</button> </button>
</div> </div>

View File

@ -76,32 +76,35 @@ export class Search extends Component<any, State> {
render() { render() {
return ( return (
<div> <div>
<div className={this.state.results.torrents[0] ? "container-fluid" : "container"}> {
<div class="row mt-2"> this.state.searching ?
<div class="col-12"> this.spinner() : this.state.results.torrents[0] ?
{ this.table() : this.noResults()
this.state.searching ? }
this.spinner() </div>
: );
this.state.results.torrents[0] ? this.table() : this.noResults() }
}
</div> spinner() {
</div> return (
</div> <div class="text-center m-5 p-5">
<i class="fas fa-spinner fa-spin fa-5x"></i>
</div> </div>
); );
} }
noResults() { noResults() {
return ( return (
<h1>No Results</h1> <div class="text-center m-5 p-5">
<h1>No Results</h1>
</div>
) )
} }
table() { table() {
return ( return (
<div> <div>
<table class="table table-fixed table-hover table-sm table-striped"> <table class="table table-fixed table-hover table-sm table-striped table-hover-purple table-padding">
<thead> <thead>
<tr> <tr>
<th class="search-name-col">Name</th> <th class="search-name-col">Name</th>
@ -117,16 +120,25 @@ export class Search extends Component<any, State> {
<tr> <tr>
<td class="search-name-cell">{torrent.name}</td> <td class="search-name-cell">{torrent.name}</td>
<td class="text-right text-muted">{humanFileSize(torrent.size_bytes, true)}</td> <td class="text-right text-muted">{humanFileSize(torrent.size_bytes, true)}</td>
<td class="text-right text-success"><div><i class="fas fa-fw fa-arrow-up"></i>{torrent.seeders}</div></td> <td class="text-right text-success"><i class="fas fa-fw fa-arrow-up d-none d-sm-inline mr-1"></i>{torrent.seeders}</td>
<td class="text-right text-danger d-none d-md-table-cell"><i class="fas fa-fw fa-arrow-down"></i>{torrent.leechers}</td> <td class="text-right text-danger d-none d-md-table-cell"><i class="fas fa-fw fa-arrow-down mr-1"></i>{torrent.leechers}</td>
<td class="text-right text-muted d-none d-md-table-cell" data-balloon={`Scraped ${moment(torrent.scraped_date * 1000).fromNow()}`} data-balloon-pos="down"> <td class="text-right text-muted d-none d-md-table-cell"
data-balloon={`Scraped ${moment(torrent.scraped_date * 1000).fromNow()}`}
data-balloon-pos="down">
{moment(torrent.created_unix * 1000).fromNow()} {moment(torrent.created_unix * 1000).fromNow()}
</td> </td>
<td class="text-right"> <td class="text-right">
<a data-balloon="Magnet link" data-balloon-pos="left" class="btn btn-sm" href={magnetLink(torrent.infohash, torrent.name)}> <a class="btn btn-sm no-outline p-1"
href={magnetLink(torrent.infohash, torrent.name)}
data-balloon="Magnet link"
data-balloon-pos="left">
<i class="fas fa-fw fa-magnet"></i> <i class="fas fa-fw fa-magnet"></i>
</a> </a>
<a data-balloon="Report Torrent" data-balloon-pos="left" class="btn btn-sm d-none d-md-inline" href={`https://gitlab.com/dessalines/torrents.csv/issues/new?issue[assignee_id]=&issue[milestone_id]=&issue[title]=Report%20Torrent%20infohash%20${torrent.infohash}`}> <a class="btn btn-sm no-outline p-1 d-none d-sm-inline"
href={`https://gitlab.com/dessalines/torrents.csv/issues/new?issue[title]=Report%20Torrent%20infohash%20${torrent.infohash}`}
target="_blank"
data-balloon="Report Torrent"
data-balloon-pos="left">
<i class="fas fa-fw fa-flag"></i> <i class="fas fa-fw fa-flag"></i>
</a> </a>
</td> </td>
@ -139,29 +151,21 @@ export class Search extends Component<any, State> {
); );
} }
spinner() {
return (
<div class="text-center">
<i class="fas fa-spinner fa-spin fa-5x"></i>
</div>
);
}
paginator() { paginator() {
return ( return (
<nav> <nav>
<ul class="pagination"> <ul class="pagination justify-content-center">
<li className={(this.state.searchParams.page == 1) ? "page-item disabled" : "page-item"}> <li className={(this.state.searchParams.page == 1) ? "page-item disabled" : "page-item"}>
<button class="page-link" <button class="page-link"
onClick={linkEvent({ i: this, nextPage: false }, this.switchPage)} onClick={linkEvent({ i: this, nextPage: false }, this.switchPage)}>
> Previous
Previous</button> </button>
</li> </li>
<li class="page-item"> <li class="page-item">
<button class="page-link" <button class="page-link"
onClick={linkEvent({ i: this, nextPage: true }, this.switchPage)}> onClick={linkEvent({ i: this, nextPage: true }, this.switchPage)}>
Next Next
</button> </button>
</li> </li>
</ul> </ul>
</nav> </nav>
@ -169,10 +173,8 @@ export class Search extends Component<any, State> {
} }
switchPage(a: { i: Search, nextPage: boolean }, event) { switchPage(a: { i: Search, nextPage: boolean }, event) {
let newSearch = a.i.state.searchParams; let newSearch = a.i.state.searchParams;
newSearch.page += (a.nextPage) ? 1 : -1; newSearch.page += (a.nextPage) ? 1 : -1;
a.i.props.history.push(`/search/${newSearch.q}/${newSearch.page}`); a.i.props.history.push(`/search/${newSearch.q}/${newSearch.page}`);
} }
}
}

View File

@ -8,10 +8,8 @@
<link rel="shortcut icon" type="image/ico" href="/favicon.ico" /> <link rel="shortcut icon" type="image/ico" href="/favicon.ico" />
<title>Torrents.csv</title> <title>Torrents.csv</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
crossorigin="anonymous"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU"
crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/balloon-css/0.5.0/balloon.min.css">
</head> </head>

View File

@ -13,8 +13,8 @@ class Index extends Component<any, any> {
render() { render() {
return ( return (
<HashRouter> <HashRouter>
<div> <Navbar />
<Navbar /> <div class="mt-3 p-0">
<Switch> <Switch>
<Route exact path="/" component={Home} /> <Route exact path="/" component={Home} />
<Route path={`/search/:q/:page`} component={Search} /> <Route path={`/search/:q/:page`} component={Search} />

View File

@ -36,9 +36,9 @@ ajax-request@^1.2.0:
utils-extend "^1.0.7" utils-extend "^1.0.7"
ajv@^6.5.5: ajv@^6.5.5:
version "6.5.5" version "6.6.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.5.tgz#cf97cdade71c6399a92c6d6c4177381291b781a1" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.1.tgz#6360f5ed0d80f232cc2b294c362d5dc2e538dd61"
integrity sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg== integrity sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==
dependencies: dependencies:
fast-deep-equal "^2.0.1" fast-deep-equal "^2.0.1"
fast-json-stable-stringify "^2.0.0" fast-json-stable-stringify "^2.0.0"
@ -1662,12 +1662,12 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
js-tokens "^3.0.0 || ^4.0.0" js-tokens "^3.0.0 || ^4.0.0"
lru-cache@^4.0.1: lru-cache@^4.0.1:
version "4.1.4" version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.4.tgz#51cc46e8e6d9530771c857e24ccc720ecdbcc031" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
integrity sha512-EPstzZ23znHUVLKj+lcXO1KvZkrlw+ZirdwvOmnAnA/1PB4ggyXJ77LRkCqkff+ShQ+cqoxCxLQOh4cKITO5iA== integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
dependencies: dependencies:
pseudomap "^1.0.2" pseudomap "^1.0.2"
yallist "^3.0.2" yallist "^2.1.2"
map-cache@^0.2.2: map-cache@^0.2.2:
version "0.2.2" version "0.2.2"
@ -3153,6 +3153,11 @@ y18n@^3.2.1:
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.0, yallist@^3.0.2: yallist@^3.0.0, yallist@^3.0.2:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"