diff --git a/src/internal/movies/handlers.go b/src/internal/movies/handlers.go
index 9416a4c..982fb88 100644
--- a/src/internal/movies/handlers.go
+++ b/src/internal/movies/handlers.go
@@ -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})
+}
diff --git a/src/main.go b/src/main.go
index cbea35a..f0d8982 100644
--- a/src/main.go
+++ b/src/main.go
@@ -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")
diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js
index 32354e6..c630e2a 100644
--- a/src/public/js/actions/actionCreators.js
+++ b/src/public/js/actions/actionCreators.js
@@ -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"),
+ ],
)
}
diff --git a/src/public/js/components/movies/actions.js b/src/public/js/components/movies/actions.js
new file mode 100644
index 0000000..e78e634
--- /dev/null
+++ b/src/public/js/components/movies/actions.js
@@ -0,0 +1,72 @@
+import React from 'react'
+
+import { DropdownButton, MenuItem } from 'react-bootstrap'
+
+export default function ActionsButton(props) {
+ return (
+
+
+ {(props.isUserAdmin && props.hasMovie) &&
+
+ }
+
+ );
+}
+
+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 (
+
+ );
+ }
+}
+
+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 (
+
+ );
+ }
+}
diff --git a/src/public/js/components/movies/list.js b/src/public/js/components/movies/list.js
index a2aa0f2..37866b7 100644
--- a/src/public/js/components/movies/list.js
+++ b/src/public/js/components/movies/list.js
@@ -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 (
-
+ );
}
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}
/>
}
diff --git a/src/public/js/components/navbar.js b/src/public/js/components/navbar.js
index a21ec94..3f9bbab 100644
--- a/src/public/js/components/navbar.js
+++ b/src/public/js/components/navbar.js
@@ -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 (
+
+
+
+
+ Canapé
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+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 (
-
-
-
-
- Canapé
-
-
-
-
-
-
- {displayMovieSearch &&
-
-
-
- }
- {displayShowSearch &&
-
-
-
- }
-
-
-
+
+ return(
+
+
+
+ );
+ }
+}
+
+function MoviesDropdown(props) {
+ return(
+
+ );
+}
+
+function ShowsDropdown(props) {
+ return(
+
+ );
+}
+
+function UserDropdown(props) {
+ if (props.username !== "") {
+ return (
+
+ );
+ } else {
+ return(
+
);
}
}
diff --git a/src/public/js/reducers/movies.js b/src/public/js/reducers/movies.js
index a05da07..83f9bd1 100644
--- a/src/public/js/reducers/movies.js
+++ b/src/public/js/reducers/movies.js
@@ -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) {
diff --git a/src/public/js/requests.js b/src/public/js/requests.js
index cf64b98..25424c8 100644
--- a/src/public/js/requests.js
+++ b/src/public/js/requests.js
@@ -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 => {