Changing space indents on UI. Changing td aligns to bootstrap text-right
This commit is contained in:
parent
43ed6e0ba4
commit
a442341cd0
|
@ -1,5 +1,5 @@
|
||||||
[*.{js,jsx,ts,tsx,json}]
|
[*.{js,jsx,ts,tsx,json}]
|
||||||
indent_style = tab
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
|
@ -11,196 +11,196 @@ const container = document.getElementById('app');
|
||||||
|
|
||||||
class TorrentSearchComponent extends Component<any, State> {
|
class TorrentSearchComponent extends Component<any, State> {
|
||||||
|
|
||||||
state: State = {
|
state: State = {
|
||||||
results: {
|
results: {
|
||||||
torrents: []
|
torrents: []
|
||||||
},
|
},
|
||||||
searchParams: {
|
searchParams: {
|
||||||
q: "",
|
q: "",
|
||||||
page: 1
|
page: 1
|
||||||
},
|
},
|
||||||
searching: false
|
searching: false
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
search(i: TorrentSearchComponent, event) {
|
search(i: TorrentSearchComponent, event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (!!i.state.searchParams.q) {
|
if (!!i.state.searchParams.q) {
|
||||||
i.setState({ searching: true, results: { torrents: [] } });
|
i.setState({ searching: true, results: { torrents: [] } });
|
||||||
i.fetchData(i.state.searchParams)
|
i.fetchData(i.state.searchParams)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
if (!!results) {
|
if (!!results) {
|
||||||
i.setState({
|
i.setState({
|
||||||
results: results
|
results: results
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('request failed', error);
|
console.error('request failed', error);
|
||||||
}).then(() => i.setState({ searching: false }));
|
}).then(() => i.setState({ searching: false }));
|
||||||
} else {
|
} else {
|
||||||
i.setState({ results: { torrents: [] } });
|
i.setState({ results: { torrents: [] } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchData(searchParams: SearchParams): Promise<Results> {
|
fetchData(searchParams: SearchParams): Promise<Results> {
|
||||||
let q = encodeURI(searchParams.q);
|
let q = encodeURI(searchParams.q);
|
||||||
return fetch(`${endpoint}/service/search?q=${q}&page=${searchParams.page}`)
|
return fetch(`${endpoint}/service/search?q=${q}&page=${searchParams.page}`)
|
||||||
.then(data => data.text())
|
.then(data => data.text())
|
||||||
.then(csv => convertCsvToJson(csv));
|
.then(csv => convertCsvToJson(csv));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.navbar()}
|
{this.navbar()}
|
||||||
<div className={this.state.results.torrents[0]? "container-fluid" : "container"}>
|
<div className={this.state.results.torrents[0] ? "container-fluid" : "container"}>
|
||||||
<div class="row mt-2">
|
<div class="row mt-2">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
{
|
{
|
||||||
this.state.searching ?
|
this.state.searching ?
|
||||||
this.spinner()
|
this.spinner()
|
||||||
:
|
:
|
||||||
this.state.results.torrents[0] ? this.table() : this.onboard()
|
this.state.results.torrents[0] ? this.table() : this.onboard()
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
table() {
|
table() {
|
||||||
return (
|
return (
|
||||||
<div class="table-responsive-sm">
|
<div class="table-responsive-sm">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th align="right">Size</th>
|
<th class="text-right">Size</th>
|
||||||
<th align="right">Seeds</th>
|
<th class="text-right">Seeds</th>
|
||||||
<th align="right">Leeches</th>
|
<th class="text-right">Leeches</th>
|
||||||
<th>Created</th>
|
<th>Created</th>
|
||||||
<th>Scraped</th>
|
<th>Scraped</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{this.state.results.torrents.map(torrent => (
|
{this.state.results.torrents.map(torrent => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{torrent.name}</td>
|
<td>{torrent.name}</td>
|
||||||
<td align="right">{humanFileSize(torrent.size_bytes, true)}</td>
|
<td class="text-right">{humanFileSize(torrent.size_bytes, true)}</td>
|
||||||
<td align="right">{torrent.seeders}</td>
|
<td class="text-right">{torrent.seeders}</td>
|
||||||
<td align="right">{torrent.leechers}</td>
|
<td class="text-right">{torrent.leechers}</td>
|
||||||
<td>{moment(torrent.created_unix * 1000).fromNow()}</td>
|
<td>{moment(torrent.created_unix * 1000).fromNow()}</td>
|
||||||
<td>{moment(torrent.scraped_date * 1000).fromNow()}</td>
|
<td>{moment(torrent.scraped_date * 1000).fromNow()}</td>
|
||||||
<td align="right">
|
<td class="text-right">
|
||||||
<a href={magnetLink(torrent.infohash)}>
|
<a href={magnetLink(torrent.infohash)}>
|
||||||
<i class="fas fa-magnet"></i>
|
<i class="fas fa-magnet"></i>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{this.paginator()}
|
{this.paginator()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
navbar() {
|
navbar() {
|
||||||
return (
|
return (
|
||||||
<nav class="navbar navbar-light bg-light">
|
<nav class="navbar navbar-light bg-light">
|
||||||
<a class="navbar-brand" href="#">
|
<a class="navbar-brand" href="#">
|
||||||
<i class="fas fa-fw fa-database mr-1"></i>Torrents.csv
|
<i class="fas fa-fw fa-database mr-1"></i>Torrents.csv
|
||||||
</a>
|
</a>
|
||||||
<div class="col-12 col-sm-6">
|
<div class="col-12 col-sm-6">
|
||||||
{this.searchForm()}
|
{this.searchForm()}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// https://www.codeply.com/go/xBVaM3q5X4/bootstrap-4-navbar-search-full-width
|
// 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="my-2 my-lg-0 d-inline w-100" onSubmit={linkEvent(this, this.search)}>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input value={this.state.searchParams.q} onInput={linkEvent(this, this.searchChange)} type="text" class="form-control border" placeholder="Search..." />
|
<input value={this.state.searchParams.q} onInput={linkEvent(this, this.searchChange)} type="text" class="form-control border" placeholder="Search..." />
|
||||||
<span class="input-group-append"></span>
|
<span class="input-group-append"></span>
|
||||||
<button type="submit" class="btn btn-outline-secondary border border-left-0">
|
<button type="submit" class="btn btn-outline-secondary border border-left-0">
|
||||||
<i className={(this.state.searching) ? "fas fa-spinner fa-spin" : "fas fa-fw fa-search"}></i>
|
<i className={(this.state.searching) ? "fas fa-spinner fa-spin" : "fas fa-fw fa-search"}></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner() {
|
spinner() {
|
||||||
return (
|
return (
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<i class="fas fa-spinner fa-spin fa-5x"></i>
|
<i class="fas fa-spinner fa-spin fa-5x"></i>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onboard() {
|
||||||
|
let site: string = "https://gitlab.com/dessalines/torrents.csv";
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<a href={site}>Torrents.csv</a> is a collaborative, <b>vetted</b> 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 via a rust script.<br></br><br></br>
|
||||||
|
<a href={site}>Torrents.csv</a> will only store torrents with at least one seeder to keep the file small, and will be periodically purged of non-seeded torrents, and sorted by seeders descending.<br></br><br></br>
|
||||||
|
To request more torrents, or add your own to the file, go <a href={site}>here</a>.<br></br><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>.
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onboard() {
|
paginator() {
|
||||||
let site: string = "https://gitlab.com/dessalines/torrents.csv";
|
return (
|
||||||
return (
|
<nav>
|
||||||
<div>
|
<ul class="pagination">
|
||||||
<a href={site}>Torrents.csv</a> is a collaborative, <b>vetted</b> 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 via a rust script.<br></br><br></br>
|
<li className={(this.state.searchParams.page == 1) ? "page-item disabled" : "page-item"}>
|
||||||
<a href={site}>Torrents.csv</a> will only store torrents with at least one seeder to keep the file small, and will be periodically purged of non-seeded torrents, and sorted by seeders descending.<br></br><br></br>
|
<button class="page-link"
|
||||||
To request more torrents, or add your own to the file, go <a href={site}>here</a>.<br></br><br></br>
|
onClick={linkEvent({ i: this, nextPage: false }, this.switchPage)}
|
||||||
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>.
|
>
|
||||||
</div>
|
Previous</button>
|
||||||
);
|
</li>
|
||||||
}
|
<li class="page-item">
|
||||||
|
<button class="page-link"
|
||||||
paginator() {
|
onClick={linkEvent({ i: this, nextPage: true }, this.switchPage)}>
|
||||||
return (
|
Next
|
||||||
<nav>
|
|
||||||
<ul class="pagination">
|
|
||||||
<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>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchChange(i, event) {
|
searchChange(i, event) {
|
||||||
let searchParams: SearchParams = {
|
let searchParams: SearchParams = {
|
||||||
q: event.target.value,
|
q: event.target.value,
|
||||||
page: 1
|
page: 1
|
||||||
}
|
}
|
||||||
i.setState({ searchParams: searchParams });
|
i.setState({ searchParams: searchParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switchPage(a: { i: TorrentSearchComponent, nextPage: boolean }, event) {
|
switchPage(a: { i: TorrentSearchComponent, 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.setState({
|
a.i.setState({
|
||||||
searchParams: newSearch
|
searchParams: newSearch
|
||||||
});
|
});
|
||||||
|
|
||||||
a.i.search(a.i, event);
|
a.i.search(a.i, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
export interface SearchParams {
|
export interface SearchParams {
|
||||||
q: string;
|
q: string;
|
||||||
page: number;
|
page: number;
|
||||||
size?: number;
|
size?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Results {
|
export interface Results {
|
||||||
torrents: Array<Torrent>;
|
torrents: Array<Torrent>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Torrent {
|
export interface Torrent {
|
||||||
infohash: string;
|
infohash: string;
|
||||||
name: string;
|
name: string;
|
||||||
size_bytes: number;
|
size_bytes: number;
|
||||||
created_unix: number;
|
created_unix: number;
|
||||||
seeders: number;
|
seeders: number;
|
||||||
leechers: number;
|
leechers: number;
|
||||||
completed: number;
|
completed: number;
|
||||||
scraped_date: number;
|
scraped_date: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
results: Results;
|
results: Results;
|
||||||
searchParams: SearchParams;
|
searchParams: SearchParams;
|
||||||
searching: Boolean;
|
searching: Boolean;
|
||||||
}
|
}
|
Loading…
Reference in New Issue