Adding Iframe expand
- Adding Iframe Expand. Fixes #32 - Fix issue with table sorting. Fixes #33 - Changing all to h4s. Fixes #30
This commit is contained in:
parent
7d302e1801
commit
6ae9f639e6
8 changed files with 35 additions and 216 deletions
|
@ -6,6 +6,8 @@ import { UserOperation, Community, Post as PostI, GetPostResponse, PostResponse,
|
|||
import { WebSocketService, UserService } from '../services';
|
||||
import { msgOp, hotRank,mdToHtml } from '../utils';
|
||||
|
||||
declare const Sortable: any;
|
||||
|
||||
interface CommunitiesState {
|
||||
communities: Array<Community>;
|
||||
}
|
||||
|
@ -29,12 +31,17 @@ export class Communities extends Component<any, CommunitiesState> {
|
|||
WebSocketService.Instance.listCommunities();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let table = document.querySelector('#community_table');
|
||||
Sortable.initTable(table);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class="container-fluid">
|
||||
<h4>Communities</h4>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover" data-sortable>
|
||||
<table id="community_table" class="table table-sm table-hover" data-sortable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
|
|
|
@ -13,7 +13,7 @@ export class CreateCommunity extends Component<any, any> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-4">
|
||||
<h3>Create Forum</h3>
|
||||
<h4>Create Forum</h4>
|
||||
<CommunityForm onCreate={this.handleCommunityCreate}/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,7 +13,7 @@ export class CreatePost extends Component<any, any> {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 mb-4">
|
||||
<h3>Create a Post</h3>
|
||||
<h4>Create a Post</h4>
|
||||
<PostForm onCreate={this.handlePostCreate}/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -62,7 +62,7 @@ export class Login extends Component<any, State> {
|
|||
return (
|
||||
<div>
|
||||
<form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
|
||||
<h3>Login</h3>
|
||||
<h4>Login</h4>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Email or Username</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -88,7 +88,7 @@ export class Login extends Component<any, State> {
|
|||
registerForm() {
|
||||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||
<h3>Sign Up</h3>
|
||||
<h4>Sign Up</h4>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">Username</label>
|
||||
<div class="col-sm-10">
|
||||
|
|
|
@ -10,6 +10,7 @@ import { mdToHtml } from '../utils';
|
|||
|
||||
interface PostListingState {
|
||||
showEdit: boolean;
|
||||
iframeExpanded: boolean;
|
||||
}
|
||||
|
||||
interface PostListingProps {
|
||||
|
@ -22,7 +23,8 @@ interface PostListingProps {
|
|||
export class PostListing extends Component<PostListingProps, PostListingState> {
|
||||
|
||||
private emptyState: PostListingState = {
|
||||
showEdit: false
|
||||
showEdit: false,
|
||||
iframeExpanded: false
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
|
@ -56,11 +58,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
</div>
|
||||
<div className="ml-4">
|
||||
{post.url
|
||||
? <h5 className="mb-0">
|
||||
? <h4 className="mb-0">
|
||||
<a className="text-white" href={post.url}>{post.name}</a>
|
||||
<small><a className="ml-2 text-muted font-italic" href={post.url}>{(new URL(post.url)).hostname}</a></small>
|
||||
</h5>
|
||||
: <h5 className="mb-0"><Link className="text-white" to={`/post/${post.id}`}>{post.name}</Link></h5>
|
||||
{ !this.state.iframeExpanded
|
||||
? <span class="pointer ml-2 text-muted small" title="Expand here" onClick={linkEvent(this, this.handleIframeExpandClick)}>+</span>
|
||||
:
|
||||
<span>
|
||||
<span class="pointer ml-2 text-muted small" onClick={linkEvent(this, this.handleIframeExpandClick)}>-</span>
|
||||
<div class="embed-responsive embed-responsive-1by1">
|
||||
<iframe scrolling="yes" class="embed-responsive-item" src={post.url}></iframe>
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
</h4>
|
||||
: <h4 className="mb-0"><Link className="text-white" to={`/post/${post.id}`}>{post.name}</Link></h4>
|
||||
}
|
||||
</div>
|
||||
<div className="details ml-4 mb-1">
|
||||
|
@ -149,5 +161,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
};
|
||||
WebSocketService.Instance.editPost(deleteForm);
|
||||
}
|
||||
|
||||
handleIframeExpandClick(i: PostListing, event) {
|
||||
i.state.iframeExpanded = !i.state.iframeExpanded;
|
||||
i.setState(i.state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ export class Post extends Component<any, PostState> {
|
|||
newComments() {
|
||||
return (
|
||||
<div class="sticky-top">
|
||||
<h5>New Comments</h5>
|
||||
<h4>New Comments</h4>
|
||||
{this.state.comments.map(comment =>
|
||||
<CommentNodes nodes={[{comment: comment}]} noIndent />
|
||||
)}
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
import { Component, linkEvent } from 'inferno';
|
||||
import * as 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;
|
||||
}
|
||||
|
||||
export class Search extends Component<any, State> {
|
||||
|
||||
state: State = {
|
||||
results: {
|
||||
torrents: []
|
||||
},
|
||||
searchParams: {
|
||||
q: "",
|
||||
page: 1,
|
||||
type_: 'torrent'
|
||||
},
|
||||
searching: false
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.state.searchParams = {
|
||||
page: Number(this.props.match.params.page),
|
||||
q: this.props.match.params.q,
|
||||
type_: this.props.match.params.type_
|
||||
}
|
||||
this.search();
|
||||
}
|
||||
|
||||
// Re-do search if the props have changed
|
||||
componentDidUpdate(lastProps, lastState, snapshot) {
|
||||
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,
|
||||
type_: this.props.match.params.type_
|
||||
}
|
||||
this.search();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
search() {
|
||||
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}&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">Name</th>
|
||||
<th class="text-right">Size</th>
|
||||
<th class="text-right">Seeds</th>
|
||||
<th class="text-right d-none d-md-table-cell">Leeches</th>
|
||||
<th class="text-right d-none d-md-table-cell">Created</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, true)}</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>
|
||||
{torrent.seeders}
|
||||
</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>
|
||||
{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">
|
||||
{moment(torrent.created_unix * 1000).fromNow()}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-sm no-outline p-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 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">
|
||||
<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 }, event) {
|
||||
let newSearch = a.i.state.searchParams;
|
||||
newSearch.page += (a.nextPage) ? 1 : -1;
|
||||
a.i.props.history.push(`/search/${newSearch.type_}/${newSearch.q}/${newSearch.page}`);
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
|||
</div>
|
||||
}
|
||||
<hr />
|
||||
<h5>Moderators</h5>
|
||||
<h4>Moderators</h4>
|
||||
{this.props.moderators.map(mod =>
|
||||
<Link to={`/user/${mod.user_id}`}>{mod.user_name}</Link>
|
||||
)}
|
||||
|
|
Reference in a new issue