From 38a17d526d624ff409cfb6416a7c18c4565b89a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Mon, 24 Apr 2017 13:33:13 +0200 Subject: [PATCH 1/8] Keep the last movie fetched URL --- src/public/js/actions/actionCreators.js | 26 +++++++++++++++++-------- src/public/js/reducers/movies.js | 14 +++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js index 1dc6e68..1e371d8 100644 --- a/src/public/js/actions/actionCreators.js +++ b/src/public/js/actions/actionCreators.js @@ -85,6 +85,15 @@ export function getUserInfos() { // Movies // ====================== +export function updateLastMovieFetchUrl(url) { + return { + type: 'UPDATE_LAST_MOVIE_FETCH_URL', + payload: { + url: url, + }, + } +} + export function selectMovie(imdbId) { return { type: 'SELECT_MOVIE', @@ -113,13 +122,6 @@ export function searchMovies(search) { ) } -export function exploreMovies(source, category) { - return request( - 'EXPLORE_MOVIES', - configureAxios().get(`/movies/explore?source=${encodeURI(source)}&category=${encodeURI(category)}`) - ) -} - export function getMovieDetails(imdbId) { return request( 'MOVIE_GET_DETAILS', @@ -173,10 +175,18 @@ export function updateMovieWishlistStore(imdbId, wishlisted) { export function fetchMovies(url) { return request( 'MOVIE_LIST_FETCH', - configureAxios().get(url) + configureAxios().get(url), + [ + updateLastMovieFetchUrl(url), + ] ) } +export function exploreMovies(source, category) { + const url = `/movies/explore?source=${encodeURI(source)}&category=${encodeURI(category)}`; + return fetchMovies(url); +} + // ====================== // Shows // ====================== diff --git a/src/public/js/reducers/movies.js b/src/public/js/reducers/movies.js index 3cf44bf..7335dde 100644 --- a/src/public/js/reducers/movies.js +++ b/src/public/js/reducers/movies.js @@ -5,6 +5,7 @@ const defaultState = { perPage: 30, selectedImdbId: "", fetchingDetails: false, + lastFetchUrl: "", exploreOptions: {}, search: "", }; @@ -28,15 +29,6 @@ export default function movieStore(state = defaultState, action) { selectedImdbId: selectedImdbId, loading: false, }) - case 'EXPLORE_MOVIES_PENDING': - return Object.assign({}, state, { - loading: true, - }) - case 'EXPLORE_MOVIES_FULFILLED': - return Object.assign({}, state, { - movies: action.payload.data, - loading: false, - }) case 'SEARCH_MOVIES_PENDING': return Object.assign({}, state, { loading: true, @@ -63,6 +55,10 @@ export default function movieStore(state = defaultState, action) { return Object.assign({}, state, { exploreOptions: action.payload.data, }) + case 'UPDATE_LAST_MOVIE_FETCH_URL': + return Object.assign({}, state, { + lastFetchUrl: action.payload.url, + }) case 'DELETE_MOVIE': return Object.assign({}, state, { movies: state.movies.filter((e) => (e.imdb_id !== action.imdbId)), From c1bd0bd3bc5af8e1c5702f107b0ac0d58c595c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Fri, 19 May 2017 23:06:20 +0200 Subject: [PATCH 2/8] Update routes and auth * Use the onEnter hook of the router to fetch data * Remove the plugin to check if the user is authenticated, this is now done by the onEnter function of the router * Update the backend to search using GET queries * Cleanup the (now useless) code in the components to fetch the datas --- package.json | 1 - src/internal/movies/handlers.go | 18 +- src/internal/shows/handlers.go | 16 +- src/public/js/actions/actionCreators.js | 38 ++-- src/public/js/app.js | 162 +++++++++++---- src/public/js/components/buttons/actions.js | 1 + .../js/components/list/explorerOptions.js | 30 --- src/public/js/components/movies/actions.js | 1 + src/public/js/components/movies/list.js | 9 +- src/public/js/components/navbar.js | 38 +--- src/public/js/reducers/movies.js | 13 -- src/public/js/reducers/shows.js | 9 - src/routes.go | 4 +- yarn.lock | 187 +++++++----------- 14 files changed, 223 insertions(+), 304 deletions(-) diff --git a/package.json b/package.json index 3877a1d..5543d11 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,6 @@ "react-router-bootstrap": "^0.23.1", "react-router-redux": "^4.0.7", "redux": "^3.6.0", - "redux-auth-wrapper": "^0.9.0", "redux-logger": "^2.7.4", "redux-thunk": "^2.1.0" }, diff --git a/src/internal/movies/handlers.go b/src/internal/movies/handlers.go index 9b73f2b..fda3117 100644 --- a/src/internal/movies/handlers.go +++ b/src/internal/movies/handlers.go @@ -1,7 +1,6 @@ package movies import ( - "encoding/json" "errors" "fmt" "log" @@ -104,17 +103,8 @@ func RefreshMovieHandler(env *web.Env, w http.ResponseWriter, r *http.Request) e // SearchMovie will search movie func SearchMovie(env *web.Env, w http.ResponseWriter, r *http.Request) error { - var data struct { - Key string `json:"key"` - } - err := json.NewDecoder(r.Body).Decode(&data) - if err != nil { - return env.RenderError(w, errors.New("failed to get the search key")) - } - - if data.Key == "" { - return env.RenderError(w, errors.New("no given key")) - } + vars := mux.Vars(r) + search := vars["search"] v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) @@ -144,7 +134,7 @@ func SearchMovie(env *web.Env, w http.ResponseWriter, r *http.Request) error { searchers := env.Config.MovieSearchers // Search for the movie with all the Searchers for _, searcher := range searchers { - result, err := searcher.SearchMovie(data.Key, env.Log) + result, err := searcher.SearchMovie(search, env.Log) if err != nil { env.Log.Errorf("error while searching movie : %s", err) continue @@ -152,7 +142,7 @@ func SearchMovie(env *web.Env, w http.ResponseWriter, r *http.Request) error { movies = append(movies, result...) } - env.Log.Debugf("got %d movies doing search %q", len(movies), data.Key) + env.Log.Debugf("got %d movies doing search %q", len(movies), search) movieList := []*Movie{} // For each movie found, fill the details for _, m := range movies { diff --git a/src/internal/shows/handlers.go b/src/internal/shows/handlers.go index 41bb025..3d3a011 100644 --- a/src/internal/shows/handlers.go +++ b/src/internal/shows/handlers.go @@ -123,16 +123,8 @@ func RefreshShowHandler(env *web.Env, w http.ResponseWriter, r *http.Request) er // SearchShow will search a show func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error { - var data struct { - Key string `json:"key"` - } - if err := json.NewDecoder(r.Body).Decode(&data); err != nil { - return env.RenderError(w, errors.New("failed to get the search key")) - } - - if data.Key == "" { - return env.RenderError(w, errors.New("no given key")) - } + vars := mux.Vars(r) + search := vars["search"] v := auth.GetCurrentUser(r, env.Log) user, ok := v.(*users.User) @@ -144,7 +136,7 @@ func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error { searchers := env.Config.ShowSearchers // Iterate on all the searchers to search for the show for _, searcher := range searchers { - result, err := searcher.SearchShow(data.Key, env.Log) + result, err := searcher.SearchShow(search, env.Log) if err != nil { env.Log.Errorf("error while searching show : %s", err) continue @@ -153,7 +145,7 @@ func SearchShow(env *web.Env, w http.ResponseWriter, r *http.Request) error { shows = append(shows, result...) } - env.Log.Debugf("got %d shows doing search %q", len(shows), data.Key) + env.Log.Debugf("got %d shows doing search %q", len(shows), search) client, err := user.NewPapiClient() if err != nil { diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js index 1e371d8..e93fcef 100644 --- a/src/public/js/actions/actionCreators.js +++ b/src/public/js/actions/actionCreators.js @@ -108,20 +108,6 @@ export function getMovieExploreOptions() { ) } -export function deleteMovieFromStore(imdbId) { - return { - type: 'DELETE_MOVIE', - imdbId - } -} - -export function searchMovies(search) { - return request( - 'SEARCH_MOVIES', - configureAxios().post('/movies/search', search) - ) -} - export function getMovieDetails(imdbId) { return request( 'MOVIE_GET_DETAILS', @@ -130,14 +116,17 @@ export function getMovieDetails(imdbId) { } export function deleteMovie(imdbId) { - return request( - 'MOVIE_DELETE', - configureAxios().delete(`/movies/${imdbId}`), - [ - addAlertOk("Movie deleted"), - deleteMovieFromStore(imdbId), - ], - ) + return { + type: 'MOVIE_DELETE', + imdbId + } + // return request( + // 'MOVIE_DELETE', + // configureAxios().delete(`/movies/${imdbId}`), + // [ + // addAlertOk("Movie deleted"), + // ], + // ) } export function addMovieToWishlist(imdbId) { @@ -182,11 +171,6 @@ export function fetchMovies(url) { ) } -export function exploreMovies(source, category) { - const url = `/movies/explore?source=${encodeURI(source)}&category=${encodeURI(category)}`; - return fetchMovies(url); -} - // ====================== // Shows // ====================== diff --git a/src/public/js/app.js b/src/public/js/app.js index 0bb2d7f..c97a80e 100644 --- a/src/public/js/app.js +++ b/src/public/js/app.js @@ -22,9 +22,8 @@ import React from 'react' import ReactDOM from 'react-dom' import { bindActionCreators } from 'redux' import { Provider, connect } from 'react-redux' -import { Router, Route, IndexRoute, IndexRedirect, Link, hashHistory } from 'react-router' +import { Router } from 'react-router' import { routerActions } from 'react-router-redux' -import { UserAuthWrapper } from 'redux-auth-wrapper' // Root reducer import rootReducer from './reducers/index' @@ -77,48 +76,131 @@ function mapDispatchToProps(dispatch) { const App = connect(mapStateToProps, mapDispatchToProps)(Main); -// Redirects to /login by default -const UserIsAuthenticated = UserAuthWrapper({ - authSelector: state => state.userStore, - redirectAction: routerActions.replace, - wrapperDisplayName: 'UserIsAuthenticated', - predicate: user => user.isLogged, - failureRedirectPath: '/users/login', -}) +const loginCheck = function(nextState, replace, next, f) { + const state = store.getState(); + const isLogged = state.userStore.isLogged; + if (!isLogged) { + replace('/users/login'); + } else { + f(); + } -// TODO find a better way -const MovieListPolochon = (props) => ( - -) -const MovieListWishlisted = (props) => ( - -) + next(); +} -const ShowListPolochon = (props) => ( - -) -const ShowListWishlisted = (props) => ( - -) +const routes = { + path: '/', + component: App, + indexRoute: {onEnter: ({params}, replace) => replace('/movies/explore/yts/seeds')}, + childRoutes: [ + { path: '/users/login' , component: UserLoginForm }, + { path: '/users/signup' , component: UserSignUp }, + { path: '/users/edit' , component: UserEdit }, + { path: '/users/signup' , component: UserSignUp }, + { + path: '/users/logout', + onEnter: function(nextState, replace, next) { + store.dispatch(actionCreators.userLogout()); + replace('/users/login'); + next(); + }, + }, + { + path: '/movies/search/:search', + component: MovieList, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + store.dispatch(actionCreators.fetchMovies(`/movies/search/${nextState.params.search}`)); + }); + }, + }, + { + path: '/movies/polochon', + component: MovieList, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + store.dispatch(actionCreators.fetchMovies('/movies/polochon')); + }); + }, + }, + { + path: '/movies/explore/:source/:category', + component: MovieList, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + var state = store.getState(); + // Fetch the explore options + if (Object.keys(state.movieStore.exploreOptions).length === 0) { + store.dispatch(actionCreators.getMovieExploreOptions()); + } + store.dispatch(actionCreators.fetchMovies(`/movies/explore?source=${nextState.params.source}&category=${nextState.params.category}`)); + }); + }, + }, + { + path: '/movies/wishlist', + component: MovieList, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + store.dispatch(actionCreators.fetchMovies('/wishlist/movies')); + }); + }, + }, + { + path: '/shows/search/:search', + component: ShowList, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + store.dispatch(actionCreators.fetchShows(`/shows/search/${nextState.params.search}`)); + }); + }, + }, + { + path: '/shows/polochon', + component: ShowList, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + store.dispatch(actionCreators.fetchShows('/shows/polochon')); + }); + }, + }, + { + path: '/shows/wishlist', + component: ShowList, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + store.dispatch(actionCreators.fetchShows('/wishlist/shows')); + }); + }, + }, + { + path: '/shows/details/:imdbId', + component: ShowDetails, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + store.dispatch(actionCreators.fetchShows(`/shows/search/${nextState.params.imdbId}`)); + }); + }, + }, + { + path: '/shows/explore/:source/:category', + component: ShowList, + onEnter: function(nextState, replace, next) { + loginCheck(nextState, replace, next, function() { + var state = store.getState(); + // Fetch the explore options + if (Object.keys(state.showStore.exploreOptions).length === 0) { + store.dispatch(actionCreators.getShowExploreOptions()); + } + store.dispatch(actionCreators.fetchShows(`/shows/explore?source=${nextState.params.source}&category=${nextState.params.category}`)); + }); + }, + }, + ], +} ReactDOM.render(( - - - - - - - - - - - - - - - - - + ),document.getElementById('app')); diff --git a/src/public/js/components/buttons/actions.js b/src/public/js/components/buttons/actions.js index eecf765..c7ee3e1 100644 --- a/src/public/js/components/buttons/actions.js +++ b/src/public/js/components/buttons/actions.js @@ -44,6 +44,7 @@ export class DeleteButton extends React.Component { handleClick(e) { e.preventDefault(); this.props.deleteFunc(this.props.resourceId); + this.props.fetchFunc(); } render() { return ( diff --git a/src/public/js/components/list/explorerOptions.js b/src/public/js/components/list/explorerOptions.js index bd8b83d..96999cf 100644 --- a/src/public/js/components/list/explorerOptions.js +++ b/src/public/js/components/list/explorerOptions.js @@ -4,28 +4,17 @@ import { Form, FormGroup, FormControl, ControlLabel } from 'react-bootstrap' export default class ExplorerOptions extends React.Component { constructor(props) { super(props); - if (Object.keys(this.props.options).length === 0) { - this.props.fetchOptions(); - } - - // Initial explore - if (this.propsValid(props)) { - props.explore(props.params.source, props.params.category); - } - this.handleSourceChange = this.handleSourceChange.bind(this); this.handleCategoryChange = this.handleCategoryChange.bind(this); } handleSourceChange(event) { let source = event.target.value; let category = this.props.options[event.target.value][0]; - this.props.explore(source, category); this.props.router.push(`/${this.props.type}/explore/${source}/${category}`); } handleCategoryChange(event) { let source = this.props.params.source; let category = event.target.value; - this.props.explore(source, category); this.props.router.push(`/${this.props.type}/explore/${source}/${category}`); } propsValid(props) { @@ -38,25 +27,6 @@ export default class ExplorerOptions extends React.Component { } return true; } - componentWillUpdate(nextProps, nextState) { - // Check props - if (!this.propsValid(nextProps)) { - return - } - - // No previous params - if (!this.props.params.source && !this.props.params.category) { - this.props.explore(this.props.params.source, this.props.params.category); - return; - } - - // Explore params changed - if ((this.props.params.source !== nextProps.params.source) - || (this.props.params.category !== nextProps.params.category)) { - this.props.explore(nextProps.params.source, nextProps.params.category); - return; - } - } prettyName(name) { return name.replace("_", " ") .split(" ") diff --git a/src/public/js/components/movies/actions.js b/src/public/js/components/movies/actions.js index 74cea59..5f67002 100644 --- a/src/public/js/components/movies/actions.js +++ b/src/public/js/components/movies/actions.js @@ -15,6 +15,7 @@ export default function ActionsButton(props) { props.fetchMovies(props.lastFetchUrl)} isUserAdmin={props.isUserAdmin} /> } diff --git a/src/public/js/components/movies/list.js b/src/public/js/components/movies/list.js index fb39b7d..61fc8de 100644 --- a/src/public/js/components/movies/list.js +++ b/src/public/js/components/movies/list.js @@ -21,6 +21,8 @@ function MovieButtons(props) { wishlisted={props.movie.wishlisted} addToWishlist={props.addToWishlist} deleteFromWishlist={props.deleteFromWishlist} + lastFetchUrl={props.lastFetchUrl} + fetchMovies={props.fetchMovies} /> {props.movie.torrents && @@ -43,11 +45,6 @@ export default class MovieList extends React.Component { constructor(props) { super(props); } - componentWillMount() { - if (this.props.moviesUrl) { - this.props.fetchMovies(this.props.moviesUrl); - } - } render() { const movies = this.props.movieStore.movies; const selectedMovieId = this.props.movieStore.selectedImdbId; @@ -87,6 +84,8 @@ export default class MovieList extends React.Component { isUserAdmin={this.props.userStore.isAdmin} addToWishlist={this.props.addMovieToWishlist} deleteFromWishlist={this.props.deleteMovieFromWishlist} + fetchMovies={this.props.fetchMovies} + lastFetchUrl={this.props.lastFetchUrl} /> } diff --git a/src/public/js/components/navbar.js b/src/public/js/components/navbar.js index 68f16d3..6d410c5 100644 --- a/src/public/js/components/navbar.js +++ b/src/public/js/components/navbar.js @@ -1,7 +1,4 @@ import React from 'react' -import { store } from '../store' -import { isUserLoggedIn } from '../actions/actionCreators' - import { Nav, Navbar, NavItem, NavDropdown, MenuItem } from 'react-bootstrap' import { LinkContainer } from 'react-router-bootstrap' import { Control, Form } from 'react-redux-form'; @@ -20,20 +17,15 @@ export default function NavBar(props) { - + @@ -56,38 +46,14 @@ class Search extends React.Component { constructor(props) { super(props); this.handleSearch = this.handleSearch.bind(this); - this.search(this.props); } handleSearch() { - if (this.props.search === "") { - return; - } - this.search(this.props); this.props.router.push(`${this.props.path}/${encodeURI(this.props.search)}`); } isActive() { const location = this.props.router.getCurrentLocation().pathname; return (location.indexOf(this.props.pathMatch) !== -1) } - search(props) { - if (!this.isActive()) { - return; - } - - // Search from the props if defined - if (props.search !== "") { - props.searchFunc({ key: props.search }); - return; - } - - // Search from the url params - if (props.params - && props.params.search - && props.params.search !== "") { - props.searchFunc({ key: props.params.search }); - return; - } - } render() { if (!this.isActive()) { return null; @@ -152,7 +118,7 @@ function UserDropdown(props) { Edit - + Logout diff --git a/src/public/js/reducers/movies.js b/src/public/js/reducers/movies.js index 7335dde..9e52409 100644 --- a/src/public/js/reducers/movies.js +++ b/src/public/js/reducers/movies.js @@ -29,15 +29,6 @@ export default function movieStore(state = defaultState, action) { selectedImdbId: selectedImdbId, loading: false, }) - case 'SEARCH_MOVIES_PENDING': - return Object.assign({}, state, { - loading: true, - }) - case 'SEARCH_MOVIES_FULFILLED': - return Object.assign({}, state, { - movies: action.payload.data, - loading: false, - }) case 'MOVIE_GET_DETAILS_PENDING': return Object.assign({}, state, { fetchingDetails: true, @@ -59,10 +50,6 @@ export default function movieStore(state = defaultState, action) { return Object.assign({}, state, { lastFetchUrl: action.payload.url, }) - case 'DELETE_MOVIE': - return Object.assign({}, state, { - movies: state.movies.filter((e) => (e.imdb_id !== action.imdbId)), - }) 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/reducers/shows.js b/src/public/js/reducers/shows.js index 28ad5d0..ea76dbd 100644 --- a/src/public/js/reducers/shows.js +++ b/src/public/js/reducers/shows.js @@ -55,15 +55,6 @@ export default function showStore(state = defaultState, action) { return Object.assign({}, state, { show: updateEpisode(Object.assign({}, state.show), false, action.payload.data), }) - case 'SEARCH_SHOWS_PENDING': - return Object.assign({}, state, { - loading: true, - }) - case 'SEARCH_SHOWS_FULFILLED': - return Object.assign({}, state, { - shows: action.payload.data, - loading: false, - }) case 'EXPLORE_SHOWS_PENDING': return Object.assign({}, state, { loading: true, diff --git a/src/routes.go b/src/routes.go index 02a7292..1569f09 100644 --- a/src/routes.go +++ b/src/routes.go @@ -20,7 +20,7 @@ func setupRoutes(env *web.Env) { env.Handle("/movies/polochon", movies.PolochonMoviesHandler).WithRole(users.UserRole).Methods("GET") env.Handle("/movies/explore", extmedias.ExploreMovies).WithRole(users.UserRole).Methods("GET") env.Handle("/movies/explore/options", extmedias.MovieExplorerOptions).WithRole(users.UserRole).Methods("GET") - env.Handle("/movies/search", movies.SearchMovie).WithRole(users.UserRole).Methods("POST") + env.Handle("/movies/search/{search}", movies.SearchMovie).WithRole(users.UserRole).Methods("GET") env.Handle("/movies/{id:tt[0-9]+}", movies.PolochonDeleteHandler).WithRole(users.UserRole).Methods("DELETE") env.Handle("/movies/{id:tt[0-9]+}/refresh", movies.RefreshMovieHandler).WithRole(users.UserRole).Methods("POST") env.Handle("/movies/{id:tt[0-9]+}/subtitles/refresh", movies.RefreshMovieSubtitlesHandler).WithRole(users.UserRole).Methods("POST") @@ -30,7 +30,7 @@ func setupRoutes(env *web.Env) { env.Handle("/shows/polochon", shows.PolochonShowsHandler).WithRole(users.UserRole).Methods("GET") env.Handle("/shows/explore", extmedias.ExploreShows).WithRole(users.UserRole).Methods("GET") env.Handle("/shows/explore/options", extmedias.ShowExplorerOptions).WithRole(users.UserRole).Methods("GET") - env.Handle("/shows/search", shows.SearchShow).WithRole(users.UserRole).Methods("POST") + env.Handle("/shows/search/{search}", shows.SearchShow).WithRole(users.UserRole).Methods("GET") env.Handle("/shows/{id:tt[0-9]+}", shows.GetDetailsHandler).WithRole(users.UserRole).Methods("GET") env.Handle("/shows/{id:tt[0-9]+}/refresh", shows.RefreshShowHandler).WithRole(users.UserRole).Methods("POST") env.Handle("/shows/{id:tt[0-9]+}/seasons/{season:[0-9]+}/episodes/{episode:[0-9]+}", shows.RefreshEpisodeHandler).WithRole(users.UserRole).Methods("POST") diff --git a/yarn.lock b/yarn.lock index 91e1c8b..2f85230 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,5 +1,11 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 + + +Base64@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.2.1.tgz#ba3a4230708e186705065e66babdd4c35cf60028" + abbrev@1: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" @@ -735,10 +741,6 @@ base64-js@^1.0.2: version "1.2.0" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" -Base64@~0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.2.1.tgz#ba3a4230708e186705065e66babdd4c35cf60028" - bcrypt-pbkdf@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" @@ -1539,6 +1541,12 @@ glob-watcher@^0.0.6: dependencies: gaze "^0.5.1" +glob2base@^0.0.12: + version "0.0.12" + resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" + dependencies: + find-index "^0.1.1" + glob@^4.3.1: version "4.5.3" resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" @@ -1567,12 +1575,6 @@ glob@~3.1.21: inherits "1" minimatch "~0.2.11" -glob2base@^0.0.12: - version "0.0.12" - resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" - dependencies: - find-index "^0.1.1" - global-modules@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" @@ -1765,7 +1767,7 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0, hoist-non-react-statics@1.2.0: +hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" @@ -1848,14 +1850,14 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - inherits@1: version "1.0.2" resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" +inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" @@ -1872,18 +1874,12 @@ interpret@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" -invariant@^2.0.0, invariant@^2.2.0, invariant@^2.2.1: +invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1, invariant@~2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54" dependencies: loose-envify "^1.0.0" -invariant@^2.1.0, invariant@~2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" - dependencies: - loose-envify "^1.0.0" - is-absolute-url@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.0.0.tgz#9c4b20b0e5c0cbef9a479a367ede6f991679f359" @@ -2032,14 +2028,14 @@ is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" -isarray@^1.0.0, isarray@~1.0.0, isarray@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + isexe@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" @@ -2186,7 +2182,7 @@ load-script@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" -loader-utils@^0.2.11, loader-utils@^0.2.5, loader-utils@^0.2.7, loader-utils@~0.2.2, loader-utils@~0.2.5, loader-utils@0.2.x: +loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.5, loader-utils@^0.2.7, loader-utils@~0.2.2, loader-utils@~0.2.5: version "0.2.16" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" dependencies: @@ -2265,7 +2261,7 @@ lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" -lodash.isempty@^4.2.1, lodash.isempty@4.4.0: +lodash.isempty@^4.2.1: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" @@ -2322,18 +2318,10 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" -lodash@^4.10.0: - version "4.17.2" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.2.tgz#34a3055babe04ce42467b607d700072c7ff6bf42" - -lodash@^4.2.0: +lodash@^4.10.0, lodash@^4.2.0, lodash@^4.2.1: version "4.16.6" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" -lodash@^4.2.1: - version "4.17.0" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.0.tgz#93f4466e5ab73e5a1f1216c34eea11535f0a8df5" - lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" @@ -2431,14 +2419,14 @@ mime-types@^2.1.12, mime-types@~2.1.7: dependencies: mime-db "~1.24.0" -mime@^1.2.11: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" - mime@1.2.x: version "1.2.11" resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" +mime@^1.2.11: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + minimatch@^2.0.1: version "2.0.10" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" @@ -2458,19 +2446,15 @@ minimatch@~0.2.11: lru-cache "2" sigmund "~1.0.0" +minimist@0.0.8, minimist@~0.0.1: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -3024,14 +3008,14 @@ prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" -punycode@^1.2.4, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + q@^1.1.2: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" @@ -3040,14 +3024,7 @@ qs@~6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" -query-string@^4.1.0, query-string@^4.2.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.2.3.tgz#9f27273d207a25a8ee4c7b8c74dcd45d556db822" - dependencies: - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -query-string@^4.2.3: +query-string@^4.1.0, query-string@^4.2.2, query-string@^4.2.3: version "4.3.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.1.tgz#54baada6713eafc92be75c47a731f2ebd09cd11d" dependencies: @@ -3191,27 +3168,6 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -readable-stream@^1.0.27-1, readable-stream@^1.1.13, readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.1.tgz#c459a6687ad6195f936b959870776edef27a7655" - dependencies: - buffer-shims "^1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - "readable-stream@>=1.0.33-1 <1.1.0-0": version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" @@ -3221,10 +3177,20 @@ readable-stream@^1.0.27-1, readable-stream@^1.1.13, readable-stream@~1.1.9: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" +readable-stream@^1.0.27-1, readable-stream@^1.1.13, readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@^2.0.0 || ^1.1.13", readable-stream@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + dependencies: + buffer-shims "^1.0.0" core-util-is "~1.0.0" inherits "~2.0.1" isarray "~1.0.0" @@ -3232,11 +3198,10 @@ readable-stream@~2.0.0: string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@~2.1.4: - version "2.1.5" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@~2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" dependencies: - buffer-shims "^1.0.0" core-util-is "~1.0.0" inherits "~2.0.1" isarray "~1.0.0" @@ -3289,13 +3254,6 @@ redux: loose-envify "^1.1.0" symbol-observable "^1.0.2" -redux-auth-wrapper: - version "0.9.0" - resolved "https://registry.yarnpkg.com/redux-auth-wrapper/-/redux-auth-wrapper-0.9.0.tgz#4456a73f44ea8b1e996906127feffac890ba6913" - dependencies: - hoist-non-react-statics "1.2.0" - lodash.isempty "4.4.0" - redux-logger: version "2.7.4" resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-2.7.4.tgz#891e5d29e7f111d08b5781a237b9965b5858c7f8" @@ -3411,7 +3369,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@^2.2.8, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: +rimraf@2, rimraf@^2.2.8, rimraf@~2.5.1, rimraf@~2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -3425,11 +3383,11 @@ sax@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" -semver@^4.1.0: +"semver@2 || 3 || 4 || 5", semver@^4.1.0: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" -semver@~5.3.0, "semver@2 || 3 || 4 || 5": +semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -3545,10 +3503,6 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" -string_decoder@~0.10.25, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -3557,6 +3511,10 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +string_decoder@~0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -3647,10 +3605,6 @@ tar@~2.2.1: fstream "^1.0.2" inherits "2" -through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - through2@^0.6.1: version "0.6.5" resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" @@ -3665,6 +3619,10 @@ through2@^2.0.0: readable-stream "~2.0.0" xtend "~4.0.0" +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + tildify@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" @@ -3778,7 +3736,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@~0.10.3, util@0.10.3: +util@0.10.3, util@~0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -3936,19 +3894,19 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -xtend@^4.0.0, "xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0: +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -3960,4 +3918,3 @@ yargs@~3.10.0: cliui "^2.1.0" decamelize "^1.0.0" window-size "0.1.0" - From fce19bf3d8940d9dc3f4a00feea1050a16531bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Fri, 19 May 2017 23:56:39 +0200 Subject: [PATCH 3/8] Re-fetch the last fetched URL after deleting a movie This keeps the store consistent with the backend --- src/public/js/actions/actionCreators.js | 21 +++++++++------------ src/public/js/components/buttons/actions.js | 3 +-- src/public/js/components/list/posters.js | 2 -- src/public/js/components/movies/actions.js | 2 +- src/public/js/components/movies/list.js | 2 +- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js index e93fcef..1c07542 100644 --- a/src/public/js/actions/actionCreators.js +++ b/src/public/js/actions/actionCreators.js @@ -115,18 +115,15 @@ export function getMovieDetails(imdbId) { ) } -export function deleteMovie(imdbId) { - return { - type: 'MOVIE_DELETE', - imdbId - } - // return request( - // 'MOVIE_DELETE', - // configureAxios().delete(`/movies/${imdbId}`), - // [ - // addAlertOk("Movie deleted"), - // ], - // ) +export function deleteMovie(imdbId, lastFetchUrl) { + return request( + 'MOVIE_DELETE', + configureAxios().delete(`/movies/${imdbId}`), + [ + fetchMovies(lastFetchUrl), + addAlertOk("Movie deleted"), + ], + ) } export function addMovieToWishlist(imdbId) { diff --git a/src/public/js/components/buttons/actions.js b/src/public/js/components/buttons/actions.js index c7ee3e1..04a9338 100644 --- a/src/public/js/components/buttons/actions.js +++ b/src/public/js/components/buttons/actions.js @@ -43,8 +43,7 @@ export class DeleteButton extends React.Component { } handleClick(e) { e.preventDefault(); - this.props.deleteFunc(this.props.resourceId); - this.props.fetchFunc(); + this.props.deleteFunc(this.props.resourceId, this.props.lastFetchUrl); } render() { return ( diff --git a/src/public/js/components/list/posters.js b/src/public/js/components/list/posters.js index db0ec9c..cabd1a0 100644 --- a/src/public/js/components/list/posters.js +++ b/src/public/js/components/list/posters.js @@ -93,9 +93,7 @@ export default class ListPosters extends React.Component { display={!displayFilter} params={this.props.params} router={this.props.router} - fetchOptions={this.props.fetchExploreOptions} options={this.props.exploreOptions} - explore={this.props.explore} /> props.fetchMovies(props.lastFetchUrl)} isUserAdmin={props.isUserAdmin} /> } diff --git a/src/public/js/components/movies/list.js b/src/public/js/components/movies/list.js index 61fc8de..402db75 100644 --- a/src/public/js/components/movies/list.js +++ b/src/public/js/components/movies/list.js @@ -85,7 +85,7 @@ export default class MovieList extends React.Component { addToWishlist={this.props.addMovieToWishlist} deleteFromWishlist={this.props.deleteMovieFromWishlist} fetchMovies={this.props.fetchMovies} - lastFetchUrl={this.props.lastFetchUrl} + lastFetchUrl={this.props.movieStore.lastFetchUrl} /> } From 614d1ab11e95dae4d556371d31ee4b9ecbf74c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Sat, 20 May 2017 00:08:07 +0200 Subject: [PATCH 4/8] Keep the last shows fetched URL --- src/public/js/actions/actionCreators.js | 28 ++++++++++++------------- src/public/js/app.js | 8 +++++-- src/public/js/reducers/shows.js | 5 +++++ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js index 1c07542..dc51d9b 100644 --- a/src/public/js/actions/actionCreators.js +++ b/src/public/js/actions/actionCreators.js @@ -175,14 +175,10 @@ export function fetchMovies(url) { export function fetchShows(url) { return request( 'SHOW_LIST_FETCH', - configureAxios().get(url) - ) -} - -export function searchShows(search) { - return request( - 'SEARCH_SHOWS', - configureAxios().post('/shows/search', search) + configureAxios().get(url), + [ + updateLastShowsFetchUrl(url), + ] ) } @@ -256,13 +252,6 @@ export function updateShowWishlistStore(imdbId, wishlisted, season = null, episo } } -export function exploreShows(source, category) { - return request( - 'EXPLORE_SHOWS', - configureAxios().get(`/shows/explore?source=${encodeURI(source)}&category=${encodeURI(category)}`) - ) -} - export function getShowExploreOptions() { return request( 'SHOW_GET_EXPLORE_OPTIONS', @@ -277,6 +266,15 @@ export function selectShow(imdbId) { } } +export function updateLastShowsFetchUrl(url) { + return { + type: 'UPDATE_LAST_SHOWS_FETCH_URL', + payload: { + url: url, + }, + } +} + // ====================== // AddTorrent // ====================== diff --git a/src/public/js/app.js b/src/public/js/app.js index c97a80e..2583361 100644 --- a/src/public/js/app.js +++ b/src/public/js/app.js @@ -133,7 +133,9 @@ const routes = { if (Object.keys(state.movieStore.exploreOptions).length === 0) { store.dispatch(actionCreators.getMovieExploreOptions()); } - store.dispatch(actionCreators.fetchMovies(`/movies/explore?source=${nextState.params.source}&category=${nextState.params.category}`)); + store.dispatch(actionCreators.fetchMovies( + `/movies/explore?source=${encodeURI(nextState.params.source)}&category=${encodeURI(nextState.params.category)}` + )); }); }, }, @@ -192,7 +194,9 @@ const routes = { if (Object.keys(state.showStore.exploreOptions).length === 0) { store.dispatch(actionCreators.getShowExploreOptions()); } - store.dispatch(actionCreators.fetchShows(`/shows/explore?source=${nextState.params.source}&category=${nextState.params.category}`)); + store.dispatch(actionCreators.fetchShows( + `/shows/explore?source=${encodeURI(nextState.params.source)}&category=${encodeURI(nextState.params.category)}` + )); }); }, }, diff --git a/src/public/js/reducers/shows.js b/src/public/js/reducers/shows.js index ea76dbd..4bf5ab1 100644 --- a/src/public/js/reducers/shows.js +++ b/src/public/js/reducers/shows.js @@ -9,6 +9,7 @@ const defaultState = { }, search: "", getDetails: false, + lastShowsFetchUrl: "", exploreOptions: {}, }; @@ -73,6 +74,10 @@ export default function showStore(state = defaultState, action) { shows: updateShowsStoreWishlist(state.shows.slice(), action.payload), show: updateShowStoreWishlist(Object.assign({}, state.show), action.payload), }) + case 'UPDATE_LAST_SHOWS_FETCH_URL': + return Object.assign({}, state, { + lastShowsFetchUrl: action.payload.url, + }) case 'SELECT_SHOW': // Don't select the show if we're fetching another show's details if (state.fetchingDetails) { From 76b2859d8824f35f0fbd743379a512dd8df30760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Sat, 20 May 2017 00:33:44 +0200 Subject: [PATCH 5/8] Improve the login function If a token is in the localStorage of the browser we now assume that the user is already logged in. If that's no the case, he will be redirected to the login page. --- src/public/js/actions/actionCreators.js | 6 ---- src/public/js/app.js | 40 +++++++++++++++---------- src/public/js/reducers/users.js | 8 ++--- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js index dc51d9b..962276e 100644 --- a/src/public/js/actions/actionCreators.js +++ b/src/public/js/actions/actionCreators.js @@ -38,12 +38,6 @@ export function userLogout() { } } -export function isUserLoggedIn() { - return { - type: 'IS_USER_LOGGED_IN', - } -} - export function loginUser(username, password) { return request( 'USER_LOGIN', diff --git a/src/public/js/app.js b/src/public/js/app.js index 2583361..3c2dac6 100644 --- a/src/public/js/app.js +++ b/src/public/js/app.js @@ -44,21 +44,16 @@ import UserLoginForm from './components/users/login' import UserEdit from './components/users/edit' import UserSignUp from './components/users/signup' -class Main extends React.Component { - componentWillMount() { - this.props.isUserLoggedIn(); - } - render() { - return ( -
- - -
- {React.cloneElement(this.props.children, this.props)} -
+function Main(props) { + return ( +
+ + +
+ {React.cloneElement(props.children, props)}
- ); - } +
+ ); } function mapStateToProps(state) { @@ -79,8 +74,21 @@ const App = connect(mapStateToProps, mapDispatchToProps)(Main); const loginCheck = function(nextState, replace, next, f) { const state = store.getState(); const isLogged = state.userStore.isLogged; - if (!isLogged) { - replace('/users/login'); + let token = localStorage.getItem('token'); + + // Let's check if the user has a token, if he does let's assume he's logged + // in. If that's not the case he will be logged out on the fisrt query + if (token !== "") { + store.dispatch({ + type: 'USER_SET_TOKEN', + payload: { + token: token, + }, + }); + } + + if (!isLogged && token === "") { + replace('/users/login'); } else { f(); } diff --git a/src/public/js/reducers/users.js b/src/public/js/reducers/users.js index f909267..894982e 100644 --- a/src/public/js/reducers/users.js +++ b/src/public/js/reducers/users.js @@ -20,12 +20,8 @@ export default function userStore(state = defaultState, action) { return logoutUser(state) } return updateFromToken(state, action.payload.data.token) - case 'IS_USER_LOGGED_IN': - let localToken = localStorage.getItem('token'); - if (!localToken || localToken === "") { - return state; - } - return updateFromToken(state, localToken) + case 'USER_SET_TOKEN': + return updateFromToken(state, action.payload.token) case 'USER_LOGOUT': return logoutUser(state) case 'GET_USER_FULFILLED': From 6c5d5e11fd9941385b2c2758fd3c592e5df490ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Sat, 20 May 2017 00:41:59 +0200 Subject: [PATCH 6/8] Reset the filter after fetching --- src/public/js/reducers/movies.js | 2 ++ src/public/js/reducers/shows.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/public/js/reducers/movies.js b/src/public/js/reducers/movies.js index 9e52409..d33dcd6 100644 --- a/src/public/js/reducers/movies.js +++ b/src/public/js/reducers/movies.js @@ -27,6 +27,8 @@ export default function movieStore(state = defaultState, action) { return Object.assign({}, state, { movies: action.payload.data, selectedImdbId: selectedImdbId, + filter: defaultState.filter, + perPage: defaultState.perPage, loading: false, }) case 'MOVIE_GET_DETAILS_PENDING': diff --git a/src/public/js/reducers/shows.js b/src/public/js/reducers/shows.js index 4bf5ab1..e48a31d 100644 --- a/src/public/js/reducers/shows.js +++ b/src/public/js/reducers/shows.js @@ -28,6 +28,8 @@ export default function showStore(state = defaultState, action) { return Object.assign({}, state, { shows: action.payload.data, selectedImdbId: selectedImdbId, + filter: defaultState.filter, + perPage: defaultState.perPage, loading: false, }) case 'SHOW_GET_DETAILS_PENDING': From c42687ddd572bd37dcc8d53fff45e334f2a4a7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Sat, 20 May 2017 01:24:05 +0200 Subject: [PATCH 7/8] Remove the search from the redux-store Let's use uncontrolled component, this will be much more simple to read and understand --- src/public/js/components/navbar.js | 25 +++++++++---------------- src/public/js/reducers/movies.js | 1 - src/public/js/reducers/shows.js | 1 - 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/public/js/components/navbar.js b/src/public/js/components/navbar.js index 6d410c5..5ed89bd 100644 --- a/src/public/js/components/navbar.js +++ b/src/public/js/components/navbar.js @@ -1,7 +1,6 @@ import React from 'react' import { Nav, Navbar, NavItem, NavDropdown, MenuItem } from 'react-bootstrap' import { LinkContainer } from 'react-router-bootstrap' -import { Control, Form } from 'react-redux-form'; export default function NavBar(props) { return ( @@ -19,20 +18,14 @@ export default function NavBar(props) { @@ -47,8 +40,9 @@ class Search extends React.Component { super(props); this.handleSearch = this.handleSearch.bind(this); } - handleSearch() { - this.props.router.push(`${this.props.path}/${encodeURI(this.props.search)}`); + handleSearch(ev) { + ev.preventDefault(); + this.props.router.push(`${this.props.path}/${encodeURI(this.input.value)}`); } isActive() { const location = this.props.router.getCurrentLocation().pathname; @@ -60,16 +54,15 @@ class Search extends React.Component { } return( - -
- + this.handleSearch(ev)}> + this.input = input} /> - -
+ +
); } } diff --git a/src/public/js/reducers/movies.js b/src/public/js/reducers/movies.js index d33dcd6..5d0bd69 100644 --- a/src/public/js/reducers/movies.js +++ b/src/public/js/reducers/movies.js @@ -7,7 +7,6 @@ const defaultState = { fetchingDetails: false, lastFetchUrl: "", exploreOptions: {}, - search: "", }; export default function movieStore(state = defaultState, action) { diff --git a/src/public/js/reducers/shows.js b/src/public/js/reducers/shows.js index e48a31d..f808770 100644 --- a/src/public/js/reducers/shows.js +++ b/src/public/js/reducers/shows.js @@ -7,7 +7,6 @@ const defaultState = { show: { seasons: [], }, - search: "", getDetails: false, lastShowsFetchUrl: "", exploreOptions: {}, From 15ec1bbecdd7a0b076ed5a646e1469e85191adf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Delattre?= Date: Sat, 20 May 2017 02:04:58 +0200 Subject: [PATCH 8/8] Fix empty result while having items in store Let's initialise the state of the component will the actual number of items instead of 0 --- src/public/js/components/list/posters.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/public/js/components/list/posters.js b/src/public/js/components/list/posters.js index cabd1a0..c273354 100644 --- a/src/public/js/components/list/posters.js +++ b/src/public/js/components/list/posters.js @@ -15,11 +15,8 @@ const DEFAULT_ADD_EXTRA_ITEMS = 30; export default class ListPosters extends React.Component { constructor(props) { super(props); - this.state = { - items: 0, - hasMore: false, - }; this.loadMore = this.loadMore.bind(this); + this.state = this.getNextState(props); } loadMore() { // Nothing to do if the app is loading @@ -35,7 +32,7 @@ export default class ListPosters extends React.Component { } getNextState(props) { let totalListSize = props.data.length; - let currentListSize = this.state.items; + let currentListSize = (this.state && this.state.items) ? this.state.items : 0; let nextListSize = currentListSize + DEFAULT_ADD_EXTRA_ITEMS; let hasMore = true;