Merge branch 'actionButtons' into 'master'
Action buttons See merge request !34
This commit is contained in:
commit
76ca9f1d14
@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/odwrtw/papi"
|
||||
polochon "github.com/odwrtw/polochon/lib"
|
||||
@ -191,3 +192,38 @@ func SearchMovie(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
return env.RenderJSON(w, movieList)
|
||||
}
|
||||
|
||||
// DeleteHandler deletes the movie from polochon
|
||||
func DeleteHandler(env *web.Env, w http.ResponseWriter, r *http.Request) error {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
log := env.Log.WithFields(logrus.Fields{
|
||||
"imdb_id": id,
|
||||
"function": "movies.DeleteHandler",
|
||||
})
|
||||
log.Debugf("deleting movie")
|
||||
|
||||
v := auth.GetCurrentUser(r, env.Log)
|
||||
user, ok := v.(*users.User)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid user type")
|
||||
}
|
||||
|
||||
var polochonConfig config.UserPolochon
|
||||
err := user.GetConfig("polochon", &polochonConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := papi.New(polochonConfig.URL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if polochonConfig.Token != "" {
|
||||
client.SetToken(polochonConfig.Token)
|
||||
}
|
||||
|
||||
return client.Delete(&papi.Movie{ImdbID: id})
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ func main() {
|
||||
|
||||
env.Handle("/movies/polochon", movies.FromPolochon).WithRole(users.UserRole).Methods("GET")
|
||||
env.Handle("/movies/{id:tt[0-9]+}/get_details", movies.GetDetailsHandler).WithRole(users.UserRole).Methods("GET")
|
||||
env.Handle("/movies/{id:tt[0-9]+}", movies.DeleteHandler).WithRole(users.AdminRole).Methods("DELETE")
|
||||
env.Handle("/movies/explore", extmedias.Explore).WithRole(users.UserRole).Methods("GET")
|
||||
env.Handle("/movies/refresh", extmedias.Refresh).WithRole(users.UserRole).Methods("POST")
|
||||
env.Handle("/movies/search", movies.SearchMovie).WithRole(users.UserRole).Methods("POST")
|
||||
|
@ -61,7 +61,9 @@ export function updateUser(config) {
|
||||
return request(
|
||||
'USER_UPDATE',
|
||||
configureAxios().post('/users/edit', config),
|
||||
"User updated",
|
||||
[
|
||||
addAlertOk("User updated"),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
@ -90,6 +92,13 @@ export function selectMovie(imdbId) {
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteMovieFromStore(imdbId) {
|
||||
return {
|
||||
type: 'DELETE_MOVIE',
|
||||
imdbId
|
||||
}
|
||||
}
|
||||
|
||||
export function searchMovies(search) {
|
||||
return request(
|
||||
'SEARCH_MOVIES',
|
||||
@ -104,6 +113,17 @@ export function getMovieDetails(imdbId) {
|
||||
)
|
||||
}
|
||||
|
||||
export function deleteMovie(imdbId) {
|
||||
return request(
|
||||
'MOVIE_DELETE',
|
||||
configureAxios().delete(`/movies/${imdbId}`),
|
||||
[
|
||||
addAlertOk("Movie deleted"),
|
||||
deleteMovieFromStore(imdbId),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchMovies(url) {
|
||||
return request(
|
||||
'MOVIE_LIST_FETCH',
|
||||
@ -153,6 +173,8 @@ export function addTorrent(url) {
|
||||
configureAxios().post('/torrents', {
|
||||
url: url,
|
||||
}),
|
||||
"Torrent added",
|
||||
[
|
||||
addAlertOk("Torrent added"),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
72
src/public/js/components/movies/actions.js
Normal file
72
src/public/js/components/movies/actions.js
Normal file
@ -0,0 +1,72 @@
|
||||
import React from 'react'
|
||||
|
||||
import { DropdownButton, MenuItem } from 'react-bootstrap'
|
||||
|
||||
export default function ActionsButton(props) {
|
||||
return (
|
||||
<DropdownButton className="btn btn-default btn-sm" title="Actions" id="actions-button" dropup>
|
||||
<RefreshButton
|
||||
fetching={props.fetching}
|
||||
movieId={props.movieId}
|
||||
getDetails={props.getDetails}
|
||||
/>
|
||||
{(props.isUserAdmin && props.hasMovie) &&
|
||||
<DeleteButton
|
||||
movieId={props.movieId}
|
||||
deleteMovie={props.deleteMovie}
|
||||
isUserAdmin={props.isUserAdmin}
|
||||
/>
|
||||
}
|
||||
</DropdownButton>
|
||||
);
|
||||
}
|
||||
|
||||
class RefreshButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick(e) {
|
||||
e.preventDefault();
|
||||
if (this.props.fetching) {
|
||||
return
|
||||
}
|
||||
this.props.getDetails(this.props.movieId);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<MenuItem onClick={this.handleClick}>
|
||||
{this.props.fetching ||
|
||||
<span>
|
||||
<i className="fa fa-refresh"></i> Refresh
|
||||
</span>
|
||||
}
|
||||
{this.props.fetching &&
|
||||
<span>
|
||||
<i className="fa fa-spin fa-refresh"></i> Refreshing
|
||||
</span>
|
||||
}
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DeleteButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick(e) {
|
||||
e.preventDefault();
|
||||
this.props.deleteMovie(this.props.movieId);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<MenuItem onClick={this.handleClick}>
|
||||
<span>
|
||||
<i className="fa fa-trash"></i> Delete
|
||||
</span>
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,57 +1,43 @@
|
||||
import React from 'react'
|
||||
|
||||
import TorrentsButton from './torrents'
|
||||
import ActionsButton from './actions'
|
||||
import ListPosters from '../list/posters'
|
||||
import ListDetails from '../list/details'
|
||||
import Loader from '../loader/loader'
|
||||
|
||||
class MovieButtons extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick(e) {
|
||||
e.preventDefault();
|
||||
if (this.props.fetching) {
|
||||
return
|
||||
}
|
||||
this.props.getMovieDetails(this.props.movie.imdb_id);
|
||||
}
|
||||
render() {
|
||||
const imdb_link = `http://www.imdb.com/title/${this.props.movie.imdb_id}`;
|
||||
return (
|
||||
<div className="list-details-buttons btn-toolbar">
|
||||
<a type="button" className="btn btn-default btn-sm" onClick={this.handleClick}>
|
||||
{this.props.fetching ||
|
||||
<span>
|
||||
<i className="fa fa-refresh"></i> Refresh
|
||||
</span>
|
||||
}
|
||||
{this.props.fetching &&
|
||||
<span>
|
||||
<i className="fa fa-spin fa-refresh"></i> Refreshing
|
||||
</span>
|
||||
}
|
||||
function MovieButtons(props) {
|
||||
const imdb_link = `http://www.imdb.com/title/${props.movie.imdb_id}`;
|
||||
const hasMovie = (props.movie.polochon_url !== "")
|
||||
return (
|
||||
<div className="list-details-buttons btn-toolbar">
|
||||
{hasMovie &&
|
||||
<a type="button" className="btn btn-primary btn-sm" href={props.movie.polochon_url}>
|
||||
<i className="fa fa-download"></i> Download
|
||||
</a>
|
||||
{this.props.movie.polochon_url !== "" &&
|
||||
<a type="button" className="btn btn-primary btn-sm" href={this.props.movie.polochon_url}>
|
||||
<i className="fa fa-download"></i> Download
|
||||
</a>
|
||||
}
|
||||
}
|
||||
|
||||
{this.props.movie.torrents &&
|
||||
<TorrentsButton
|
||||
torrents={this.props.movie.torrents}
|
||||
addTorrent={this.props.addTorrent}
|
||||
/>
|
||||
}
|
||||
{props.movie.torrents &&
|
||||
<TorrentsButton
|
||||
torrents={props.movie.torrents}
|
||||
addTorrent={props.addTorrent}
|
||||
/>
|
||||
}
|
||||
|
||||
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
||||
<i className="fa fa-external-link"></i> IMDB
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<ActionsButton
|
||||
fetching={props.fetching}
|
||||
movieId={props.movie.imdb_id}
|
||||
getDetails={props.getMovieDetails}
|
||||
deleteMovie={props.deleteMovie}
|
||||
isUserAdmin={props.isUserAdmin}
|
||||
hasMovie={hasMovie}
|
||||
/>
|
||||
|
||||
<a type="button" className="btn btn-warning btn-sm" href={imdb_link}>
|
||||
<i className="fa fa-external-link"></i> IMDB
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default class MovieList extends React.Component {
|
||||
@ -109,6 +95,8 @@ export default class MovieList extends React.Component {
|
||||
fetching={this.props.movieStore.fetchingDetails}
|
||||
getMovieDetails={this.props.getMovieDetails}
|
||||
addTorrent={this.props.addTorrent}
|
||||
deleteMovie={this.props.deleteMovie}
|
||||
isUserAdmin={this.props.userStore.isAdmin}
|
||||
/>
|
||||
</ListDetails>
|
||||
}
|
||||
|
@ -6,102 +6,128 @@ import { Nav, Navbar, NavItem, NavDropdown, MenuItem } from 'react-bootstrap'
|
||||
import { LinkContainer } from 'react-router-bootstrap'
|
||||
import { Control, Form } from 'react-redux-form';
|
||||
|
||||
export default class NavBar extends React.Component {
|
||||
export default function NavBar(props) {
|
||||
return (
|
||||
<div>
|
||||
<Navbar fluid fixedTop collapseOnSelect>
|
||||
<Navbar.Header>
|
||||
<LinkContainer to="/">
|
||||
<Navbar.Brand><a href="#">Canapé</a></Navbar.Brand>
|
||||
</LinkContainer>
|
||||
<Navbar.Toggle />
|
||||
</Navbar.Header>
|
||||
<Navbar.Collapse>
|
||||
<MoviesDropdown />
|
||||
<ShowsDropdown />
|
||||
<UserDropdown
|
||||
username={props.userStore.username}
|
||||
logout={props.userLogout}
|
||||
/>
|
||||
<Search
|
||||
model="movieStore"
|
||||
model="movieStore.search"
|
||||
placeholder="Search movies"
|
||||
router={props.router}
|
||||
search={props.movieStore.search}
|
||||
path='/movies/search'
|
||||
pathMatch='movies'
|
||||
/>
|
||||
<Search
|
||||
model="showStore"
|
||||
model="showStore.search"
|
||||
placeholder="Search shows"
|
||||
router={props.router}
|
||||
search={props.showStore.search}
|
||||
path='/shows/search'
|
||||
pathMatch='shows'
|
||||
/>
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
class Search extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleMovieSearch = this.handleMovieSearch.bind(this);
|
||||
this.handleShowSearch = this.handleShowSearch.bind(this);
|
||||
this.handleSearch = this.handleSearch.bind(this);
|
||||
}
|
||||
handleMovieSearch() {
|
||||
this.props.router.push(`/movies/search/${encodeURI(this.props.movieStore.search)}`)
|
||||
}
|
||||
handleShowSearch() {
|
||||
this.props.router.push(`/shows/search/${encodeURI(this.props.showStore.search)}`)
|
||||
handleSearch() {
|
||||
this.props.router.push(`${this.props.path}/${encodeURI(this.props.search)}`)
|
||||
}
|
||||
render() {
|
||||
const username = this.props.userStore.username;
|
||||
const isLoggedIn = username !== "" ? true : false;
|
||||
const location = this.props.router.getCurrentLocation().pathname;
|
||||
let displayMovieSearch = false;
|
||||
let displayShowSearch = false;
|
||||
if (isLoggedIn && location.indexOf("movies") > -1)
|
||||
if (location.indexOf(this.props.pathMatch) === -1)
|
||||
{
|
||||
displayMovieSearch = true;
|
||||
return null;
|
||||
}
|
||||
if (isLoggedIn && location.indexOf("shows") > -1)
|
||||
{
|
||||
displayShowSearch = true;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Navbar fluid fixedTop collapseOnSelect>
|
||||
<Navbar.Header>
|
||||
<LinkContainer to="/">
|
||||
<Navbar.Brand><a href="#">Canapé</a></Navbar.Brand>
|
||||
</LinkContainer>
|
||||
<Navbar.Toggle />
|
||||
</Navbar.Header>
|
||||
<Navbar.Collapse>
|
||||
<Nav>
|
||||
<LinkContainer to="/movies/popular">
|
||||
<NavItem>Popular movies</NavItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to="/movies/polochon">
|
||||
<NavItem>Polochon movies</NavItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to="/shows/popular">
|
||||
<NavItem>Polochon shows</NavItem>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
<Nav pullRight>
|
||||
{isLoggedIn ||
|
||||
<LinkContainer to="/users/signup">
|
||||
<NavItem>Sign up</NavItem>
|
||||
</LinkContainer>
|
||||
}
|
||||
{isLoggedIn ||
|
||||
<LinkContainer to="/users/login">
|
||||
<NavItem>Login</NavItem>
|
||||
</LinkContainer>
|
||||
}
|
||||
{isLoggedIn &&
|
||||
<NavDropdown title={username} id="navbar-dropdown-right">
|
||||
<LinkContainer to="/users/edit">
|
||||
<MenuItem>Edit</MenuItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to="/users/logout" onClick={this.props.userLogout}>
|
||||
<MenuItem>Logout</MenuItem>
|
||||
</LinkContainer>
|
||||
</NavDropdown>
|
||||
}
|
||||
</Nav>
|
||||
{displayMovieSearch &&
|
||||
<Navbar.Form pullRight>
|
||||
<Form model="movieStore" className="input-group" onSubmit={() => this.handleMovieSearch()}>
|
||||
<Control.text
|
||||
model="movieStore.search"
|
||||
className="form-control"
|
||||
placeholder="Search movies"
|
||||
updateOn="change"
|
||||
/>
|
||||
</Form>
|
||||
</Navbar.Form>
|
||||
}
|
||||
{displayShowSearch &&
|
||||
<Navbar.Form pullRight>
|
||||
<Form model="showStore" className="input-group" onSubmit={() => this.handleShowSearch()}>
|
||||
<Control.text
|
||||
model="showStore.search"
|
||||
className="form-control"
|
||||
placeholder="Search shows"
|
||||
updateOn="change"
|
||||
/>
|
||||
</Form>
|
||||
</Navbar.Form>
|
||||
}
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
</div>
|
||||
|
||||
return(
|
||||
<Navbar.Form pullRight>
|
||||
<Form model={this.props.model} className="input-group" onSubmit={this.handleSearch}>
|
||||
<Control.text
|
||||
model={this.props.model}
|
||||
className="form-control"
|
||||
placeholder={this.props.placeholder}
|
||||
updateOn="change"
|
||||
/>
|
||||
</Form>
|
||||
</Navbar.Form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function MoviesDropdown(props) {
|
||||
return(
|
||||
<Nav>
|
||||
<NavDropdown title="Movies" id="navbar-movies-dropdown">
|
||||
<LinkContainer to="/movies/popular">
|
||||
<MenuItem>Popular</MenuItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to="/movies/polochon">
|
||||
<MenuItem>Polochon</MenuItem>
|
||||
</LinkContainer>
|
||||
</NavDropdown>
|
||||
</Nav>
|
||||
);
|
||||
}
|
||||
|
||||
function ShowsDropdown(props) {
|
||||
return(
|
||||
<Nav>
|
||||
<NavDropdown title="Shows" id="navbar-shows-dropdown">
|
||||
<LinkContainer to="/shows/popular">
|
||||
<NavItem>Popular</NavItem>
|
||||
</LinkContainer>
|
||||
</NavDropdown>
|
||||
</Nav>
|
||||
);
|
||||
}
|
||||
|
||||
function UserDropdown(props) {
|
||||
if (props.username !== "") {
|
||||
return (
|
||||
<Nav pullRight>
|
||||
<NavDropdown title={props.username} id="navbar-dropdown-right">
|
||||
<LinkContainer to="/users/edit">
|
||||
<MenuItem>Edit</MenuItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to="/users/logout" onClick={props.logout}>
|
||||
<MenuItem>Logout</MenuItem>
|
||||
</LinkContainer>
|
||||
</NavDropdown>
|
||||
</Nav>
|
||||
);
|
||||
} else {
|
||||
return(
|
||||
<Nav pullRight>
|
||||
<LinkContainer to="/users/signup">
|
||||
<NavItem>Sign up</NavItem>
|
||||
</LinkContainer>
|
||||
<LinkContainer to="/users/login">
|
||||
<NavItem>Login</NavItem>
|
||||
</LinkContainer>
|
||||
</Nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,11 @@ export default function movieStore(state = defaultState, action) {
|
||||
movies: movies,
|
||||
fetchingDetails: false,
|
||||
})
|
||||
case 'DELETE_MOVIE':
|
||||
return Object.assign({}, state, {
|
||||
movies: state.movies.filter((e) => (e.imdb_id !== action.imdbId)),
|
||||
fetchingDetails: false,
|
||||
})
|
||||
case 'SELECT_MOVIE':
|
||||
// Don't select the movie if we're fetching another movie's details
|
||||
if (state.fetchingDetails) {
|
||||
|
@ -16,7 +16,7 @@ export function configureAxios(headers = {}) {
|
||||
|
||||
// This function takes en event prefix to dispatch evens during the life of the
|
||||
// request, it also take a promise (axios request)
|
||||
export function request(eventPrefix, promise, successMessage = null) {
|
||||
export function request(eventPrefix, promise, callbackEvents = null) {
|
||||
// Events
|
||||
const pending = `${eventPrefix}_PENDING`;
|
||||
const fulfilled = `${eventPrefix}_FULFILLED`;
|
||||
@ -40,13 +40,10 @@ export function request(eventPrefix, promise, successMessage = null) {
|
||||
type: fulfilled,
|
||||
payload: response.data,
|
||||
})
|
||||
if (successMessage) {
|
||||
dispatch({
|
||||
type: 'ADD_ALERT_OK',
|
||||
payload: {
|
||||
message: successMessage,
|
||||
},
|
||||
})
|
||||
if (callbackEvents) {
|
||||
for (let event of callbackEvents) {
|
||||
dispatch(event);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user