2020-07-03 01:04:56 +02:00

266 lines
8.7 KiB
TypeScript

import { Component, linkEvent } from 'inferno';
import moment from 'moment';
import { endpoint } from '../env';
import { SearchParams, Results, Torrent } from '../interfaces';
import { humanFileSize, magnetLink, getFileName } from '../utils';
interface State {
results: Results;
searchParams: SearchParams;
searching: Boolean;
}
function buildSearchURL(sort_key, {state: { searchParams }) {
console.log("got search URL sort_key", sort_key, "searchParams", searchParams, "thing", searchParams);
//let searchParams = thing.state.searchParams;
if (sort_key === searchParams.sort_key) {
const sort_dir = searchParams.sort_dir === "asc" ? "desc" : "asc";
searchParams.sort_dir = sort_dir;
console.log("no change in sort key from", sort_key, "so switch direction instead.")
} else {
console.log("change sort key from", searchParams.sort_key, "to", sort_key);
searchParams.sort_key = sort_key;
}
const url = `/#/search/${searchParams.type_}/${searchParams.q}/${searchParams.page}/${searchParams.sort_key}/${searchParams.sort_dir}`
return url;
}
export class Search extends Component<any, State> {
state: State = {
results: {
torrents: []
},
searchParams: {
q: "",
page: 1,
sort_key: 'Name',
sort_dir: 'Desc',
type_: 'torrent'
},
searching: false
};
constructor(props: any, context: any) {
super(props, context);
}
componentDidMount() {
console.log("got props", this.props);
this.state.searchParams = {
page: Number(this.props.match.params.page),
q: this.props.match.params.q,
sort_key: this.props.match.params.sort_key,
sort_dir: this.props.match.params.sort_dir,
type_: this.props.match.params.type_
}
this.search();
}
// Re-do search if the props have changed
componentDidUpdate(lastProps: any) {
if (lastProps.match && lastProps.match.params !== this.props.match.params) {
this.state.searchParams = {
page: Number(this.props.match.params.page),
q: this.props.match.params.q,
sort_key: this.props.match.params.sort_key,
sort_dir: this.props.match.params.sort_dir,
type_: this.props.match.params.type_
}
this.search();
}
}
search() {
console.log("in search, state", this.state);
if (!!this.state.searchParams.q) {
this.setState({ searching: true, results: { torrents: [] } });
this.fetchData(this.state.searchParams)
.then(torrents => {
if (!!torrents) {
this.setState({
results: {
torrents: torrents
}
});
}
}).catch(error => {
console.error('request failed', error);
}).then(() => this.setState({ searching: false }));
} else {
this.setState({ results: { torrents: [] } });
}
}
fetchData(searchParams: SearchParams): Promise<Array<Torrent>> {
let q = encodeURI(searchParams.q);
return fetch(`${endpoint}/service/search?q=${q}&page=${searchParams.page}&sort_key=${searchParams.sort_key}&sort_dir=${searchParams.sort_dir}&type_=${searchParams.type_}`)
.then(data => data.json());
}
render() {
return (
<div>
{
this.state.searching ?
this.spinner() : this.state.results.torrents[0] ?
this.torrentsTable()
: this.noResults()
}
</div>
);
}
spinner() {
return (
<div class="text-center m-5 p-5">
<svg class="icon icon-spinner spinner"><use xlinkHref="#icon-spinner"></use></svg>
</div>
);
}
noResults() {
return (
<div class="text-center m-5 p-5">
<h1>No Results</h1>
</div>
)
}
torrentsTable() {
return (
<div>
<table class="table table-fixed table-hover table-sm table-striped table-hover-purple table-padding">
<thead>
<tr>
<th class="search-name-col">
<a
href={buildSearchURL('Name', this)}>
Name
</a>
</th>
<th class="text-right">
<a
href={buildSearchURL('Size', this)}>
Size
</a>
</th>
<th class="text-right">
<a href="#">Seeds</a>
</th>
<th class="text-right d-none d-md-table-cell">
<a href="#">Leeches</a>
</th>
<th class="text-right d-none d-md-table-cell">
<a href="#">Scraped</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
{this.state.results.torrents.map(torrent => (
<tr>
{ !torrent.name ? (
<td className="path_column">
<a class="text-body"
href={magnetLink(torrent.infohash, torrent.path, torrent.index_)}>
{getFileName(torrent.path)}
</a>
</td>
) : (
<td class="search-name-cell">
<a class="text-body"
href={magnetLink(torrent.infohash, torrent.name, torrent.index_)}>
{torrent.name}
</a>
</td>
)}
<td class="text-right text-muted">{humanFileSize(torrent.size_bytes, false)}</td>
<td class="text-right text-success">
<svg class="icon icon-arrow-up d-none d-sm-inline mr-1"><use xlinkHref="#icon-arrow-up"></use></svg>
<span>{torrent.seeders}</span>
</td>
<td class="text-right text-danger d-none d-md-table-cell">
<svg class="icon icon-arrow-down mr-1"><use xlinkHref="#icon-arrow-down"></use></svg>
<span>{torrent.leechers}</span>
</td>
<td class="text-right text-muted d-none d-md-table-cell"
data-balloon={`Created ${moment(torrent.created_unix * 1000).fromNow()}`}
data-balloon-pos="down">
{moment(torrent.scraped_date * 1000).fromNow()}
</td>
<td class="text-right">
<span class="btn btn-sm no-outline px-1"
data-balloon="Copy Magnet link to clipboard"
data-balloon-pos="left"
data-href={magnetLink(torrent.infohash, (torrent.name) ? torrent.name : torrent.path, torrent.index_)}
onClick={this.copyLink}>
<svg class="icon icon-copy"><use xlinkHref="#icon-copy"></use></svg>
</span>
<a class="btn btn-sm no-outline px-1"
href={magnetLink(torrent.infohash, (torrent.name) ? torrent.name : torrent.path, torrent.index_)}
data-balloon="Magnet link"
data-balloon-pos="left">
<svg class="icon icon-magnet"><use xlinkHref="#icon-magnet"></use></svg>
</a>
<a class="btn btn-sm no-outline px-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">
<svg class="icon icon-flag"><use xlinkHref="#icon-flag"></use></svg>
</a>
</td>
</tr>
))}
</tbody>
</table>
{this.paginator()}
</div>
);
}
paginator() {
return (
<nav>
<ul class="pagination justify-content-center">
<li className={(this.state.searchParams.page == 1) ? "page-item disabled" : "page-item"}>
<button class="page-link"
onClick={linkEvent({ i: this, nextPage: false }, this.switchPage)}>
Previous
</button>
</li>
<li class="page-item">
<button class="page-link"
onClick={linkEvent({ i: this, nextPage: true }, this.switchPage)}>
Next
</button>
</li>
</ul>
</nav>
);
}
switchPage(a: { i: Search, nextPage: boolean }) {
let newSearch = a.i.state.searchParams;
newSearch.page += (a.nextPage) ? 1 : -1;
a.i.props.history.push(`/search/${newSearch.type_}/${newSearch.q}/${newSearch.page}/${newSearch.sort_key}/${newSearch.sort_dir}`);
}
copyLink(evt) {
const href = evt.currentTarget.dataset.href;
try {
navigator.clipboard.writeText(href)
.then(() => alert("Copied magnet URL to clipboard"));
} catch(err) {
alert("Could not copy magnet URL: " + href)
}
}
}