From e71c5bfb8463aa32c960185ecd386fc0f06e7ef0 Mon Sep 17 00:00:00 2001 From: Lucas BEE Date: Sat, 20 May 2017 12:59:10 +0200 Subject: [PATCH] Add subtitles in frontend Update backend to match polochon --- src/internal/movies/handlers.go | 15 ++- src/internal/movies/movies.go | 18 ++-- src/internal/shows/episodes.go | 16 ++- src/internal/shows/handlers.go | 18 +++- src/internal/subtitles/subtitles.go | 8 ++ src/public/js/actions/subtitles.js | 36 +++++++ src/public/js/components/buttons/subtitles.js | 97 +++++++++++++++++++ src/public/js/components/movies/list.js | 14 ++- src/public/js/components/shows/details.js | 53 ++++++---- src/public/js/reducers/movies.js | 30 ++++-- src/public/js/reducers/shows.js | 37 +++++-- src/public/js/reducers/torrents.js | 2 +- src/public/js/reducers/users.js | 8 +- src/public/js/requests.js | 12 ++- src/public/less/app.less | 7 +- 15 files changed, 301 insertions(+), 70 deletions(-) create mode 100644 src/internal/subtitles/subtitles.go create mode 100644 src/public/js/actions/subtitles.js create mode 100644 src/public/js/components/buttons/subtitles.js diff --git a/src/internal/movies/handlers.go b/src/internal/movies/handlers.go index 49cc7b1..e1f39f0 100644 --- a/src/internal/movies/handlers.go +++ b/src/internal/movies/handlers.go @@ -325,12 +325,23 @@ func RefreshMovieSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http.R return env.RenderError(w, err) } - err = client.UpdateSubtitles(&papi.Movie{ImdbID: id}) + movie := &papi.Movie{ImdbID: id} + refreshSubs, err := client.UpdateSubtitles(movie) if err != nil { return env.RenderError(w, err) } - return env.RenderOK(w, "Subtitles refreshed") + subs := []subtitles.Subtitle{} + for _, lang := range refreshSubs { + subtitleURL, _ := client.SubtitleURL(movie, lang) + subs = append(subs, subtitles.Subtitle{ + Language: lang, + URL: subtitleURL, + VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", id, lang), + }) + } + + return env.RenderJSON(w, subs) } // DownloadVVTSubtitle returns a vvt subtitle for the movie diff --git a/src/internal/movies/movies.go b/src/internal/movies/movies.go index 9b07363..c96e767 100644 --- a/src/internal/movies/movies.go +++ b/src/internal/movies/movies.go @@ -12,6 +12,7 @@ import ( "github.com/odwrtw/polochon/lib" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/backend" + "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/subtitles" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/users" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web" ) @@ -31,12 +32,7 @@ func (m *Movie) MarshalJSON() ([]byte, error) { type Alias Movie var downloadURL string - type Subtitle struct { - Language string `json:"language"` - URL string `json:"url"` - VVTFile string `json:"vvt_file"` - } - var subtitles []Subtitle + var subs []subtitles.Subtitle // If the episode is present, fill the downloadURL if m.pMovie != nil { // Get the DownloadURL @@ -44,7 +40,7 @@ func (m *Movie) MarshalJSON() ([]byte, error) { // Append the Subtitles for _, l := range m.pMovie.Subtitles { subtitleURL, _ := m.client.SubtitleURL(m.pMovie, l) - subtitles = append(subtitles, Subtitle{ + subs = append(subs, subtitles.Subtitle{ Language: l, URL: subtitleURL, VVTFile: fmt.Sprintf("/movies/%s/subtitles/%s", m.ImdbID, l), @@ -55,14 +51,14 @@ func (m *Movie) MarshalJSON() ([]byte, error) { // Marshal the movie with its polochon_url movieToMarshal := &struct { *Alias - PolochonURL string `json:"polochon_url"` - PosterURL string `json:"poster_url"` - Subtitles []Subtitle `json:"subtitles"` + PolochonURL string `json:"polochon_url"` + PosterURL string `json:"poster_url"` + Subtitles []subtitles.Subtitle `json:"subtitles"` }{ Alias: (*Alias)(m), PolochonURL: downloadURL, PosterURL: m.PosterURL(), - Subtitles: subtitles, + Subtitles: subs, } return json.Marshal(movieToMarshal) diff --git a/src/internal/shows/episodes.go b/src/internal/shows/episodes.go index 269e59f..b53b897 100644 --- a/src/internal/shows/episodes.go +++ b/src/internal/shows/episodes.go @@ -9,6 +9,7 @@ import ( polochon "github.com/odwrtw/polochon/lib" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/backend" + "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/subtitles" "gitlab.quimbo.fr/odwrtw/canape-sql/src/internal/web" ) @@ -24,12 +25,7 @@ func (e *Episode) MarshalJSON() ([]byte, error) { type alias Episode var downloadURL string - type Subtitle struct { - Language string `json:"language"` - URL string `json:"url"` - VVTFile string `json:"vvt_file"` - } - var subtitles []Subtitle + var subs []subtitles.Subtitle // If the episode is present, fill the downloadURL if e.pShow != nil { pEpisode := e.pShow.GetEpisode(e.Season, e.Episode) @@ -45,7 +41,7 @@ func (e *Episode) MarshalJSON() ([]byte, error) { // Append the Subtitles for _, l := range pEpisode.Subtitles { subtitleURL, _ := e.client.SubtitleURL(pEpisode, l) - subtitles = append(subtitles, Subtitle{ + subs = append(subs, subtitles.Subtitle{ Language: l, URL: subtitleURL, VVTFile: fmt.Sprintf("/shows/%s/seasons/%d/episodes/%d/subtitles/%s", e.ShowImdbID, e.Season, e.Episode, l), @@ -57,12 +53,12 @@ func (e *Episode) MarshalJSON() ([]byte, error) { // Marshal the episode with its polochon_url episodeToMarshal := &struct { *alias - PolochonURL string `json:"polochon_url"` - Subtitles []Subtitle `json:"subtitles"` + PolochonURL string `json:"polochon_url"` + Subtitles []subtitles.Subtitle `json:"subtitles"` }{ alias: (*alias)(e), PolochonURL: downloadURL, - Subtitles: subtitles, + Subtitles: subs, } return json.Marshal(episodeToMarshal) diff --git a/src/internal/shows/handlers.go b/src/internal/shows/handlers.go index 5a3b9fe..7b12ea0 100644 --- a/src/internal/shows/handlers.go +++ b/src/internal/shows/handlers.go @@ -374,16 +374,28 @@ func RefreshEpisodeSubtitlesHandler(env *web.Env, w http.ResponseWriter, r *http return env.RenderError(w, err) } - err = client.UpdateSubtitles(&papi.Episode{ + e := &papi.Episode{ ShowImdbID: id, Season: season, Episode: episode, - }) + } + + refreshedSubs, err := client.UpdateSubtitles(e) if err != nil { return env.RenderError(w, err) } - return env.RenderOK(w, "Subtitles refreshed") + subs := []subtitles.Subtitle{} + for _, lang := range refreshedSubs { + subtitleURL, _ := client.SubtitleURL(e, lang) + subs = append(subs, subtitles.Subtitle{ + Language: lang, + URL: subtitleURL, + VVTFile: fmt.Sprintf("/shows/%s/seasons/%d/episodes/%d/subtitles/%s", e.ShowImdbID, e.Season, e.Episode, lang), + }) + } + + return env.RenderJSON(w, subs) } // DownloadVVTSubtitle returns a vvt subtitle for the movie diff --git a/src/internal/subtitles/subtitles.go b/src/internal/subtitles/subtitles.go new file mode 100644 index 0000000..e5a4127 --- /dev/null +++ b/src/internal/subtitles/subtitles.go @@ -0,0 +1,8 @@ +package subtitles + +// Subtitle represents a Subtitle +type Subtitle struct { + Language string `json:"language"` + URL string `json:"url"` + VVTFile string `json:"vvt_file"` +} diff --git a/src/public/js/actions/subtitles.js b/src/public/js/actions/subtitles.js new file mode 100644 index 0000000..843ed43 --- /dev/null +++ b/src/public/js/actions/subtitles.js @@ -0,0 +1,36 @@ +import { configureAxios, request } from '../requests' + +import { addAlertOk } from './alerts' + +export function refreshSubtitles(type, id, season, episode) { + switch (type) { + case 'movie': + var resourceURL = `/movies/${id}` + return request( + 'MOVIE_SUBTITLES_UPDATE', + configureAxios().post(`${resourceURL}/subtitles/refresh`), + [ + addAlertOk("Subtitles refreshed"), + ], + { + imdb_id: id, + }, + ) + case 'episode': + var resourceURL = `/shows/${id}/seasons/${season}/episodes/${episode}` + return request( + 'EPISODE_SUBTITLES_UPDATE', + configureAxios().post(`${resourceURL}/subtitles/refresh`), + [ + addAlertOk("Subtitles refreshed"), + ], + { + imdb_id: id, + season: season, + episode: episode, + }, + ) + default: + console.log("refreshSubtitles - Unknown type " + type) + } +} diff --git a/src/public/js/components/buttons/subtitles.js b/src/public/js/components/buttons/subtitles.js new file mode 100644 index 0000000..6a96c63 --- /dev/null +++ b/src/public/js/components/buttons/subtitles.js @@ -0,0 +1,97 @@ +import React from 'react' + +import { DropdownButton, MenuItem } from 'react-bootstrap' + +export default class SubtitlesButton extends React.Component { + constructor(props) { + super(props); + this.handleClick = this.handleClick.bind(this); + } + handleClick(e, url) { + e.preventDefault(); + if (this.props.fetching) { + return + } + // Refresh the subtitles + this.props.refreshSubtitles(this.props.type, this.props.resourceID, this.props.data.season, this.props.data.episode); + } + render() { + // If there is no URL, the resource is not in polochon, we won't be able to download subtitles + if (this.props.url === "") { + return null; + } + + // Build the button + const entries = buildMenuItems(this.props.subtitles); + let btnSize = "small"; + if (this.props.xs) { + btnSize = "xsmall"; + } + + return ( + + {entries.map(function(e, index) { + switch (e.type) { + case 'action': + return ( + this.handleClick(event, e.url)}> + {this.props.fetchingSubtitles || + + + Refresh + + } + {this.props.fetchingSubtitles && + + + Refreshing + + } + + ); + case 'divider': + return ( + + ); + case 'entry': + return ( + + {e.lang} + + ); + } + }, this)} + + ); + } +} + +function buildMenuItems(subtitles) { + // Build the array of entries + let entries = []; + + // Push the refresh button + entries.push({ + type: "action", + value: "Refresh", + }); + + // If there is no subtitles, stop here + if (!subtitles) { + return entries; + } + + // Push the divider + entries.push({ type: "divider" }); + // Push the subtitles + for (let sub of subtitles) { + entries.push({ + type: "entry", + // Take only the last part of fr_FR + lang: sub.language.split("_")[1], + url: sub.url, + }); + } + + return entries; +} diff --git a/src/public/js/components/movies/list.js b/src/public/js/components/movies/list.js index 91237c3..67d2841 100644 --- a/src/public/js/components/movies/list.js +++ b/src/public/js/components/movies/list.js @@ -2,10 +2,12 @@ import React from 'react' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import { addTorrent } from '../../actions/torrents' +import { refreshSubtitles } from '../../actions/subtitles' import { addMovieToWishlist, deleteMovieFromWishlist, getMovieDetails, selectMovie } from '../../actions/movies' import DownloadButton from '../buttons/download' +import SubtitlesButton from '../buttons/subtitles' import TorrentsButton from './torrents' import ActionsButton from './actions' import ListPosters from '../list/posters' @@ -16,7 +18,7 @@ function mapStateToProps(state) { } const mapDispatchToProps = (dipatch) => bindActionCreators({ selectMovie, getMovieDetails, addTorrent, - addMovieToWishlist, deleteMovieFromWishlist }, dipatch) + addMovieToWishlist, deleteMovieFromWishlist, refreshSubtitles }, dipatch) function MovieButtons(props) { const imdb_link = `http://www.imdb.com/title/${props.movie.imdb_id}`; @@ -48,6 +50,15 @@ function MovieButtons(props) { subtitles={props.movie.subtitles} /> + + IMDB @@ -96,6 +107,7 @@ class MovieList extends React.Component { addToWishlist={this.props.addMovieToWishlist} deleteFromWishlist={this.props.deleteMovieFromWishlist} lastFetchUrl={this.props.movieStore.lastFetchUrl} + refreshSubtitles={this.props.refreshSubtitles} /> } diff --git a/src/public/js/components/shows/details.js b/src/public/js/components/shows/details.js index bb39410..1935d65 100644 --- a/src/public/js/components/shows/details.js +++ b/src/public/js/components/shows/details.js @@ -2,11 +2,13 @@ import React from 'react' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import { addTorrent } from '../../actions/torrents' +import { refreshSubtitles } from '../../actions/subtitles' import { addShowToWishlist, deleteFromWishlist, getEpisodeDetails, updateEpisodeDetailsStore, updateShowDetails } from '../../actions/shows' import Loader from '../loader/loader' import DownloadButton from '../buttons/download' +import SubtitlesButton from '../buttons/subtitles' import { OverlayTrigger, Tooltip } from 'react-bootstrap' @@ -18,7 +20,8 @@ function mapStateToProps(state) { } const mapDispatchToProps = (dispatch) => bindActionCreators({addTorrent, addShowToWishlist, deleteFromWishlist, - updateShowDetails, updateEpisodeDetailsStore, getEpisodeDetails }, dispatch) + updateShowDetails, updateEpisodeDetailsStore, getEpisodeDetails, + refreshSubtitles }, dispatch) class ShowDetails extends React.Component { render() { @@ -39,6 +42,7 @@ class ShowDetails extends React.Component { addToWishlist={this.props.addShowToWishlist} getEpisodeDetails={this.props.getEpisodeDetails} updateEpisodeDetailsStore={this.props.updateEpisodeDetailsStore} + refreshSubtitles={this.props.refreshSubtitles} /> ); @@ -113,6 +117,7 @@ function SeasonsList(props){ addToWishlist={props.addToWishlist} getEpisodeDetails={props.getEpisodeDetails} updateEpisodeDetailsStore={props.updateEpisodeDetailsStore} + refreshSubtitles={props.refreshSubtitles} /> ) @@ -159,6 +164,7 @@ class Season extends React.Component { addToWishlist={this.props.addToWishlist} getEpisodeDetails={this.props.getEpisodeDetails} updateEpisodeDetailsStore={this.props.updateEpisodeDetailsStore} + refreshSubtitles={this.props.refreshSubtitles} /> ) }, this)} @@ -181,8 +187,17 @@ function Episode(props) { {props.data.episode} {props.data.title} - - + + + {props.data.torrents && props.data.torrents.map(function(torrent, index) { let key = `${props.data.season}-${props.data.episode}-${torrent.source}-${torrent.quality}`; return ( @@ -196,7 +211,6 @@ function Episode(props) { + this.handleClick(e, this.props.data.url)} @@ -306,7 +320,7 @@ class TrackButton extends React.Component { Track show from here ); return ( - + this.handleClick(e)}> @@ -326,24 +340,23 @@ class GetDetailsButton extends React.Component { return } this.props.updateEpisodeDetailsStore(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode); + console.log(this.props.data); this.props.getEpisodeDetails(this.props.data.show_imdb_id, this.props.data.season, this.props.data.episode); } render() { return ( - - this.handleClick(e)}> - {this.props.data.fetching || - - Refresh - - } - {this.props.data.fetching && - - Refreshing - - } - - + this.handleClick(e)}> + {this.props.data.fetching || + + Refresh + + } + {this.props.data.fetching && + + Refreshing + + } + ); } } diff --git a/src/public/js/reducers/movies.js b/src/public/js/reducers/movies.js index 5d0bd69..e669bab 100644 --- a/src/public/js/reducers/movies.js +++ b/src/public/js/reducers/movies.js @@ -18,13 +18,13 @@ export default function movieStore(state = defaultState, action) { case 'MOVIE_LIST_FETCH_FULFILLED': let selectedImdbId = ""; // Select the first movie - if (action.payload.data.length > 0) { + if (action.payload.response.data.length > 0) { // Sort by year - action.payload.data.sort((a,b) => b.year - a.year); - selectedImdbId = action.payload.data[0].imdb_id; + action.payload.response.data.sort((a,b) => b.year - a.year); + selectedImdbId = action.payload.response.data[0].imdb_id; } return Object.assign({}, state, { - movies: action.payload.data, + movies: action.payload.response.data, selectedImdbId: selectedImdbId, filter: defaultState.filter, perPage: defaultState.perPage, @@ -36,7 +36,7 @@ export default function movieStore(state = defaultState, action) { }) case 'MOVIE_GET_DETAILS_FULFILLED': return Object.assign({}, state, { - movies: updateMovieDetails(state.movies.slice(), action.payload.data.imdb_id, action.payload.data), + movies: updateMovieDetails(state.movies.slice(), action.payload.response.data.imdb_id, action.payload.response.data), fetchingDetails: false, }) case 'MOVIE_UPDATE_STORE_WISHLIST': @@ -45,12 +45,21 @@ export default function movieStore(state = defaultState, action) { }) case 'MOVIE_GET_EXPLORE_OPTIONS_FULFILLED': return Object.assign({}, state, { - exploreOptions: action.payload.data, + exploreOptions: action.payload.response.data, }) case 'UPDATE_LAST_MOVIE_FETCH_URL': return Object.assign({}, state, { lastFetchUrl: action.payload.url, }) + case 'MOVIE_SUBTITLES_UPDATE_PENDING': + return Object.assign({}, state, { + movies: updateMovieSubtitles(state.movies.slice(), state.selectedImdbId, true), + }) + case 'MOVIE_SUBTITLES_UPDATE_FULFILLED': + console.log("payload :", action.payload); + return Object.assign({}, state, { + movies: updateMovieSubtitles(state.movies.slice(), state.selectedImdbId, false, action.payload.response.data), + }) case 'SELECT_MOVIE': // Don't select the movie if we're fetching another movie's details if (state.fetchingDetails) { @@ -76,3 +85,12 @@ function updateStoreWishlist(movies, imdbId, wishlisted) { movies[index].wishlisted = wishlisted; return movies } + +function updateMovieSubtitles(movies, imdbId, fetching, data = null) { + let index = movies.map((el) => el.imdb_id).indexOf(imdbId); + if (data) { + movies[index].subtitles = data; + } + movies[index].fetchingSubtitles = fetching; + return movies +} diff --git a/src/public/js/reducers/shows.js b/src/public/js/reducers/shows.js index f808770..1f5773e 100644 --- a/src/public/js/reducers/shows.js +++ b/src/public/js/reducers/shows.js @@ -21,11 +21,12 @@ export default function showStore(state = defaultState, action) { case 'SHOW_LIST_FETCH_FULFILLED': let selectedImdbId = ""; // Select the first show - if (action.payload.data.length > 0) { - selectedImdbId = action.payload.data[0].imdb_id; + console.log("Hey", action.payload); + if (action.payload.response.data.length > 0) { + selectedImdbId = action.payload.response.data[0].imdb_id; } return Object.assign({}, state, { - shows: action.payload.data, + shows: action.payload.response.data, selectedImdbId: selectedImdbId, filter: defaultState.filter, perPage: defaultState.perPage, @@ -37,7 +38,7 @@ export default function showStore(state = defaultState, action) { }) case 'SHOW_GET_DETAILS_FULFILLED': return Object.assign({}, state, { - shows: updateShowDetails(state.shows.slice(), action.payload.data), + shows: updateShowDetails(state.shows.slice(), action.payload.response.data), getDetails: false, }) case 'SHOW_FETCH_DETAILS_PENDING': @@ -46,7 +47,7 @@ export default function showStore(state = defaultState, action) { }) case 'SHOW_FETCH_DETAILS_FULFILLED': return Object.assign({}, state, { - show: sortEpisodes(action.payload.data), + show: sortEpisodes(action.payload.response.data), loading: false, }) case 'EPISODE_GET_DETAILS': @@ -55,7 +56,7 @@ export default function showStore(state = defaultState, action) { }) case 'EPISODE_GET_DETAILS_FULFILLED': return Object.assign({}, state, { - show: updateEpisode(Object.assign({}, state.show), false, action.payload.data), + show: updateEpisode(Object.assign({}, state.show), false, action.payload.response.data), }) case 'EXPLORE_SHOWS_PENDING': return Object.assign({}, state, { @@ -63,12 +64,12 @@ export default function showStore(state = defaultState, action) { }) case 'EXPLORE_SHOWS_FULFILLED': return Object.assign({}, state, { - shows: action.payload.data, + shows: action.payload.response.data, loading: false, }) case 'SHOW_GET_EXPLORE_OPTIONS_FULFILLED': return Object.assign({}, state, { - exploreOptions: action.payload.data, + exploreOptions: action.payload.response.data, }) case 'SHOW_UPDATE_STORE_WISHLIST': return Object.assign({}, state, { @@ -79,6 +80,15 @@ export default function showStore(state = defaultState, action) { return Object.assign({}, state, { lastShowsFetchUrl: action.payload.url, }) + case 'EPISODE_SUBTITLES_UPDATE_PENDING': + return Object.assign({}, state, { + show: updateEpisodeSubtitles(Object.assign({}, state.show), action.payload.main.season, action.payload.main.episode, true), + }) + case 'EPISODE_SUBTITLES_UPDATE_FULFILLED': + console.log("payload :", action.payload); + return Object.assign({}, state, { + show: updateEpisodeSubtitles(Object.assign({}, state.show), action.payload.main.season, action.payload.main.episode, false, action.payload.response.data), + }) case 'SELECT_SHOW': // Don't select the show if we're fetching another show's details if (state.fetchingDetails) { @@ -113,6 +123,17 @@ function updateEpisode(show, fetching, data = null) { return show } +function updateEpisodeSubtitles(show, season, episode, fetching, data = null) { + let seasonIndex = show.seasons.map((el) => el.season).indexOf(season.toString()); + let episodeIndex = show.seasons[seasonIndex].episodes.map((el) => el.episode).indexOf(episode); + + if (data) { + show.seasons[seasonIndex].episodes[episodeIndex].subtitles = data; + } + show.seasons[seasonIndex].episodes[episodeIndex].fetchingSubtitles = fetching; + return show +} + function sortEpisodes(show) { let episodes = show.episodes; delete show["episodes"]; diff --git a/src/public/js/reducers/torrents.js b/src/public/js/reducers/torrents.js index fa5586e..4b3a8b4 100644 --- a/src/public/js/reducers/torrents.js +++ b/src/public/js/reducers/torrents.js @@ -12,7 +12,7 @@ export default function torrentStore(state = defaultState, action) { case 'TORRENTS_FETCH_FULFILLED': return state.merge(fromJS({ fetching: false, - torrents: action.payload.data, + torrents: action.payload.response.data, })); default: return state diff --git a/src/public/js/reducers/users.js b/src/public/js/reducers/users.js index d039fb2..eac6063 100644 --- a/src/public/js/reducers/users.js +++ b/src/public/js/reducers/users.js @@ -17,18 +17,18 @@ export default function userStore(state = defaultState, action) { userLoading: true, }) case 'USER_LOGIN_FULFILLED': - if (action.payload.status === "error") { + if (action.payload.response.status === "error") { return logoutUser(state) } - return updateFromToken(state, action.payload.data.token) + return updateFromToken(state, action.payload.response.data.token) case 'USER_SET_TOKEN': return updateFromToken(state, action.payload.token) case 'USER_LOGOUT': return logoutUser(state) case 'GET_USER_FULFILLED': return Object.assign({}, state, { - polochonToken: action.payload.data.token, - polochonUrl: action.payload.data.url, + polochonToken: action.payload.response.data.token, + polochonUrl: action.payload.response.data.url, }) default: return state; diff --git a/src/public/js/requests.js b/src/public/js/requests.js index e865a82..a11bf8f 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, callbackEvents = null) { +export function request(eventPrefix, promise, callbackEvents = null, mainPayload = null) { // Events const pending = `${eventPrefix}_PENDING`; const fulfilled = `${eventPrefix}_FULFILLED`; @@ -24,6 +24,9 @@ export function request(eventPrefix, promise, callbackEvents = null) { return function(dispatch) { dispatch({ type: pending, + payload: { + main: mainPayload, + } }) promise .then(response => { @@ -33,12 +36,16 @@ export function request(eventPrefix, promise, callbackEvents = null) { type: 'ADD_ALERT_ERROR', payload: { message: response.data.message, + main: mainPayload, } }) } dispatch({ type: fulfilled, - payload: response.data, + payload: { + response: response.data, + main: mainPayload, + }, }) if (callbackEvents) { for (let event of callbackEvents) { @@ -57,6 +64,7 @@ export function request(eventPrefix, promise, callbackEvents = null) { type: 'ADD_ALERT_ERROR', payload: { message: error.response.data, + main: mainPayload, } }) }) diff --git a/src/public/less/app.less b/src/public/less/app.less index e09fb75..c07306f 100644 --- a/src/public/less/app.less +++ b/src/public/less/app.less @@ -46,8 +46,11 @@ body { max-height: 300px; } -.episode-button { - padding-right: 5px; +.episode-buttons { + display: flex; + span { + margin-left: 5px; + } } .navbar {