A煤n no tienes acceso a esta clase

Crea una cuenta y contin煤a viendo este curso

Fetch de estado inicial

11/23
Recursos

Aportes 15

Preguntas 5

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesi贸n.

Reto aceptado mi estimado Oscar!

// GUARDAR PELICULA DEL USUARIO

  • Primero cree mi action en el cual le paso el id del usuario y el id de la pelicula.
export const favoriteMovie = (userId, movie, cb) => (dispatch) => {
  const data = {
    userId,
    movieId: movie.id,
  };
  axios({
    url: '/user-movies',
    method: 'post',
    data,
  })
    .then(({ data }) => {
      const {
        data: { movieExist },
      } = data;

      const message = movieExist
        ? `${movie.title} ya esta en tus favoritos`
        : `${movie.title} fue agregada a tus favoritos`;

      !movieExist && dispatch(setFavorite(movie));

      cb(movieExist, message);
    })
    .catch((err) => dispatch(setError(err)));
};
  • Luego como tenemos ya la ruta del Server Render para agregar una pelicula a un usuario del curso de passport la agregue al server.js
app.post('/user-movies', async (req, res, next) => {
  try {
    const { body: userMovie } = req;
    const { token } = req.cookies;

    const { data, status } = await axios({
      url: `${config.apiUrl}/api/user-movies`,
      headers: { Authorization: `Bearer ${token}` },
      method: 'post',
      data: userMovie,
    });

    const {
      data: { movieExist },
    } = data;

    if (status !== 200 && status !== 201) {
      return next(boom.badImplementation());
    }

    const statusCode = movieExist ? 200 : 201;

    return res.status(statusCode).json(data);
  } catch (error) {
    next(error);
  }
});

// LISTAR PELICULAS DEL USUARIO

Luego en el renderApp hago la peticion para obtener las peliculas del usuario

let userMovies = await axios({
      url: `${config.apiUrl}/api/user-movies/?userId=${id}`,
      headers: {
        Authorization: `Bearer ${token}`,
      },
      method: 'get',
    });

Accedo a la data que me trae solo el userId y el movieId por lo que tengo que obtener toda la informacion de las peliculas filtrando del movieList para que se pinten correctamente en frontend.

userMovies = userMovies.data.data;

const myList = [];

    userMovies.forEach((userMovie) => {
      movieList.forEach((movie) => {
        if (movie._id === userMovie.movieId) {
          myList.push(movie);
        }
      });
    });

Por ultimo se la pasa al initialState

 initialState = {
      user: {
        email,
        name,
        id,
      },
      playing: {},
      myList,
      trends: movieList.filter(
        (movie) => movie.contentRating === 'PG' && movie._id
      ),
      originals: movieList.filter(
        (movie) => movie.contentRating === 'G' && movie._id
      ),
    };

Hola a todos!

Hubo muchas cositas que no fueron tan sencillas de cuadrar, les comparto mi repo con la funcionalidad de carousel para a帽adir y quitar, tambi茅n para reproducir los videos, espero les sirva.

Repo

Commit: ff1671f

Bueno este es mi aporte de como hacer lo de los agregar/eliminar favoritos, en realidad hay varias formas de hacerlo, la que hice fue no tocando el proyecto del api, el cual cuando creas un favorito se agrega un favorito de devuelve el _id de la tabla user_movies, y la misma que se usa para eliminar un favorito.
Estos son mis actions:

export const setFavorite = ({
  _id,
  cover,
  title,
  year,
  contentRating,
  duration,
}) => {
  return (dispatch) => {
    axios({
      url: '/user-movies',
      method: 'POST',
      data: {
        movieId: _id,
      },
    })
      .then(() => {
        dispatch(
          setFavoriteRequest({
            _id,
            cover,
            title,
            year,
            contentRating,
            duration,
          }),
        );
      })
      .catch((err) => dispatch(setError(err)));
  };
};

export const setFavoriteRequest = (payload) => ({
  type: 'SET_FAVORITE',
  payload,
});

export const deleteFavorite = (userMovieId) => {
  return (dispatch) => {
    axios({
      url: `/user-movies/${userMovieId}`,
      method: 'DELETE',
    })
      .then(() => {
        dispatch(deleteFavoriteRequest(userMovieId));
      })
      .catch((err) => dispatch(setError(err)));
  };
};

export const deleteFavoriteRequest = (payload) => ({
  type: 'DELETE_FAVORITE',
  payload,
});

agregu茅 los 2 actions para enviar el request al ssr
El carousel item hice un par de cambios, agregu茅 el nuevo usermovieid al props y lo uso cuando quiero borrar un favorito, el resto es lo mismo

const { _id, cover, title, year, contentRating, duration, userMovieId, isList } = props;
...
<img
  className='carousel-item__details--img'
  src={removeIcon}
  alt='Remove Icon'
  onClick={() => handleDeleteFavorite(userMovieId)}
/>

Luego en el SSR agregu茅 los 2 nuevos endpoints:

app.post('/user-movies', async (req, res, next) => {
  const { body: userMovie } = req;
  const { id, token } = req.cookies;
  try {
    const response = await axios({
      url: `${process.env.API_URL}/api/user-movies`,
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
      },
      data: {
        userId: id,
        movieId: userMovie.movieId,
      },
    });

    res.status(200).json(response.data);
  } catch (error) {
    next(error);
  }
});

app.delete('/user-movies/:userMovieId', async (req, res, next) => {
  const { userMovieId } = req.params;
  const { token } = req.cookies;

  try {
    const response = await axios({
      url: `${process.env.API_URL}/api/user-movies/${userMovieId}`,
      method: 'DELETE',
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });

    res.status(200).json(response.data);
  } catch (error) {
    next(error);
  }
});

adem谩s cambi茅 la l贸gica del initialState para poder agregar los videos favoritos en el key myList:

let initialState;
  const { email, name, id, token } = req.cookies;

  try {
    let movieList = await axios({
      url: `${process.env.API_URL}/api/movies`,
      method: 'GET',
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });
    movieList = movieList.data.data;

    let userMovieList = await axios({
      url: `${process.env.API_URL}/api/user-movies/?userId=${id}`,
      method: 'GET',
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });
    userMovieList = userMovieList.data.data;

    initialState = {
      user: {
        email,
        name,
        id,
      },
      playing: {},
      myList: movieList.filter((movie) => {
        return userMovieList.some(
          (userMovie) => movie._id === userMovie.movieId,
        );
      }).map((movie) => {
        const filteredMovie = userMovieList.find((userMovie) => movie._id === userMovie.movieId);
        if (filteredMovie) {
          // aqu铆 agrego el userMoviId que luego lo usar茅 para borrar favoritos
          movie.userMovieId = filteredMovie._id;
        }
        return movie;
      }),
      trends: movieList.filter(
        (movie) => movie.contentRating === 'PG' && movie._id,
      ),
      originals: movieList.filter(
        (movie) => movie.contentRating === 'G' && movie._id,
      ),
    };
  } catch (err) {
    initialState = {
      user: {},
      playing: {},
      myList: [],
      trends: [],
      originals: [],
    };
  }

Con eso ya funcionar铆a el agregar y borrar favoritos, se podr铆a agregar una l贸gica de validar si el favorito existe, igual se puede hacer de varias maneras, como validar en el estado, bloqueando el bot贸n de agregar favorito si es repetido y adem谩s en el api de tener que validarlo.

Hola, alguien m谩s tiene problemas con el token en la app鈥 a veces si se encuentra en las cookies y otras veces no. A煤n no he encontrado a que se debe este comportamiento.
Y tambi茅n que la app se esta renderizando dos veces, tengo esa sensaci贸n

Para quien le parezca raro el por qu茅 del Authorization: "Beare token" es una convenci贸n popular que viene de usar Auth0

Alguin sabe como puedo subir imagenes 驴?
quiero subir mi reto 鈥

Oigan pero en la data tenemos varios tipos de contentRating ademas de P y PG, que pasa con los dem谩s?

Challenge Accepted. (1 d铆a y medio de sufrimiento pero aqui voy)
.
Los handles de CarouselItem.jsx

  const handleSetFavorite = () => {
    props.setFavorite({
      _id, cover, title, year, contentRating, duration,
    });
  };

  const handleDeleteFavorite = () => {
    props.deleteFavorite(_id);
  };

Las Actions agregadas鈥

// [...]

export const setFavorite = (payload) => {
  const { _id } = payload;
  return (dispatch) => {
    axios({
      url: '/user-movies',
      method: 'post',
      data: {
        movieId: _id,
      },
    })
      .then(dispatch(setFavoriteRequest(payload)))
      .catch((err) => dispatch(setError(err)));
  };
};

export const deleteFavorite = (_id) => {
  return (dispatch) => {
    axios({
      url: `/user-movies/${_id}`,
      method: 'post',
    })
      .then(dispatch(deleteFavoriteRequest(_id)))
      .catch((err) => dispatch(setError(err)));
  };
};

// [...]

.
La ruta con m茅todo POST del render server, enviando la petici贸n a la API.

// [...]
app.post('/user-movies/:userMovieId', async function(req, res, next) {
  const { userMovieId } = req.params;
  const { id: userId, token } = req.cookies;

  try {
    const response = await axios({
      url: `${process.env.API_URL}/api/user-movies/${userMovieId}`,
      method: 'DELETE',
      headers: {
        Authorization: `Bearer ${token}`,
      },
      data: { userId }
    });

    res.status(200).json(response.data);
  } catch (error) {
    console.error('Fetch to API (user-movies/:userMovieId) failed.')
    next(error)
  }
});
// [...]

Setteo de State cada vez que carga

// [...]
const renderApp = async (req, res) => {

  let initialState;
  const { token, email, name, id } = req.cookies;

  try {
    let movieList = await axios({
      url:`${process.env.API_URL}/api/movies`,
      headers: { Authorization: `Bearer ${token}` },
      method: 'get',
    });

    let userMovieRelations = await axios ({
      url:`${process.env.API_URL}/api/user-movies?userId=${id}`,
      headers: { Authorization: `Bearer ${token}`},
      method: 'get'
    });

    userMovieRelations = userMovieRelations.data.data;
    movieList = movieList.data.data;

    const userMovieList = movieList.filter(
      movie => {
        let isUserMovie = userMovieRelations.some(
          userHas => userHas.movieId == movie._id
        );
      return isUserMovie;
      }
    );

    initialState = {
      user: {
        id, email, name,
    },
    myList: userMovieList,
    trends: movieList.filter(movie => movie.contentRating === 'PG' && movie._id),
    originals: movieList.filter(movie => movie.contentRating === 'G' && movie._id)
    };
  } catch(err){
    initialState = {
      user: {},
      myList: [],
      trends: [],
      originals: []
    }
  }
// [...]

Termin茅 haciendo un peque帽o tweak a la lib de mongo, porque para borrar le estoy pasando, en este caso, userMovieId y movieId juntos, en vez de pasar el id identificatorio del usermovie (lo quise hacer, pero no me sali贸. Mi idea era evitar hacer OTRO get).

  delete(collection, data) {
    
    let finalData;
    
    if (data.userMovieId) {
      const { userMovieId, userId } = data;
      finalData = { movieId: userMovieId, userId}
    } else {
      finalData = { _id: id }
    }

    return this.connect().then(db => {
      return db.collection(collection).deleteOne(finalData)
    }).then(() => { finalData });
  }
}

Reto finalizado!!!
POST

Primero cree el action para agregar mi pelicula a la lista de favoritos.

export const userMovies = ({ id, userId, cover, title, year, contentRating, duration }) => {
  return (dispatch) => {
    axios({
      url: '/user-movies',
      method: 'post',
      data: {
        movieId: id,
        userId,
      },
    })
      .then(() => {
        dispatch(setFavorite({ id, cover, title, year, contentRating, duration }));
      })
      .catch((error) => {
        dispatch(setError(error));
      });
  };
};

En carouselItem modifiqu茅 la funci贸n handleSetFavorite para sacar el id del usuario y antes de cualquier cosa validar en el estado actual que no est茅 la pel铆cula en los favoritos.

const handleSetFavorite = () => {
    const userId = document.cookie.match(/[0-9a-fA-F]{24}/)[0];
    const idExist = mylist.some((item) => item._id === id);

    if (!idExist) {
      userMovies({
        id, userId, cover, title, year, contentRating, duration,
      }, mylist);
    }
  };

GET
Para obtener las peliculas hice una petici贸n a la API en renderApp,
y con la function getUserMovies extraje la pelicula con el id y la guard茅 en myMovies la cual luego pas茅 a mylist.

let { data: { data: favorites } } = await axios({
      url: `${config.apiUrl}/api/user-movies?userId=${id}`,
      method: 'get',
      headers: { Authorization: `Bearer ${token}`},
    });

const myMovies = getUserMovie(movieList, favorites);

DELETE
para el delete lo unico que hice fu茅 crear un action

export const deleteUserMovies = (id) => {
  return (dispatch) => {
    axios({
      url: `/user-movies/${id}`,
      method: 'delete',
    })
      .then(() => {
        dispatch(deleteFavorite(id));
      })
      .catch(() => {
        dispatch(setError(error));
      });
  };
};```

Hola, los fetch siempre se tienen que utilizar de lado de servidor? o cual seria la forma correcta de configurar axios del frontend para hacer requests a otras routas del API sin tener que enviar el token manualmente en cada petici贸n?

Reto cumplido: agregar, eliminar y mostrar desde la BD, favoritos! 馃槃

<h3>Aqu铆 mi soluci贸n en Github al reto.</h3>

.
Como varios compa帽eros comentaban tuvo su grado de dificultad sobre todo en la parte de eliminar la pel铆cula de la base cuando el usuario la quitara de su lista.
.
En mi caso lo resolv铆 agreg谩ndole una propiedad al objeto de la pel铆cula a帽adida a Mi Lista para poder tener rastro del _id de creaci贸n en la base para as铆 poder llamarlo en la funci贸n para borrar.
.
Saludos.

gracias!

1- Lo primero es crearle un id valido a cada movie(el id debe ser creado basandose en el schema que recibe el Api Server para poder crear las movies de usuarios).

2- Modificar el CarouselItem que es donde se oprime el boton de enviar a favoritos (hay que hacer un mapStateToProps para traer a user y mylist).

<
const { id, cover, title, year, contentRating, duration, isList, mylist, user } = props;
    const handleSetFavorite = () => {
        const movieExist = mylist.find(movie => movie.id === id)
        if (!movieExist) {
            const movie = { id, cover, title, year, contentRating, duration }
            props.postFavorite(movie.id, movie, user)
        }
    }
>

3- Crear el action de postFavorite.

<
export const postFavorite = (id, movie, user) => {
    return (dispatch) => {
        const userMovie = {
            userId: user.id,
            movieId: id
        }
        axios.post('/user-movies', userMovie)
            .then(() => dispatch(setFavorite(movie)))
            .catch(error => dispatch(setError(error)))
    }
}
>

4- En el archivo src/server/server.js traer la ruta que se hizo para crear los user-movies app.post("/user-movies"鈥) .

<
app.post("/user-movies", async function (req, res, next) {
    try {
        const { body: userMovie } = req;
        const { token } = req.cookies;

        const { data, status } = await axios({
            url: `${config.apiUrl}/api/user-movies`,
            headers: { Authorization: `Bearer ${token}` },
            method: 'post',
            data: userMovie
        });

        if (status !== 201) {
            return next(boom.badImplementation());
        }

        res.status(201).json(data);

    } catch (error) {
        next(error)
    }
});
>

5- Crear el action de removeFavorite y en el componente CarouselItem modificar el handleDeleteFavorite para que llame a este action.

<
export const removeFavorite = (userMovieId) => {
    return (dispatch) => {
        axios.delete(`/user-movies/${userMovieId}`)
            .then(({ status }) => {
                if (status === 200) {
                    dispatch(deleteFavorite(userMovieId))
                }
            })
            .catch(error => dispatch(setError(error)))
    }
}
>

6- En el archivo src/server/server.js traer la ruta que se hizo para eliminar los user-movies app.delete("/user-movies/:userMovieId"鈥) y hacer las modificaciones correspondientes para que puedan entrar los valores que mandamos del action. Esto es porque el userMovieId que viene del action en los params no es el id que se va a eliminar en la base de datos, el id que se va a eliminar de la base de datos es un id que mongoDB crea cuando un usuario a帽ade una movie a favoritos.

<
app.delete("/user-movies/:userMovieId", async function (req, res, next) {
    try {
        const { userMovieId } = req.params;
        const { token, id } = req.cookies;

        let userMovies = await axios({
            url: `${config.apiUrl}/api/user-movies/?userId=${id}`,
            headers: { Authorization: `Bearer ${token}` },
            method: 'get'
        });

        userMovies = userMovies.data.data;

        const arrayMovies = userMovies.filter(movie => movie.movieId === userMovieId);

        const { data, status } = await axios({
            url: `${config.apiUrl}/api/user-movies/${arrayMovies[0]._id}`,
            headers: { Authorization: `Bearer ${token}` },
            method: 'delete'
        });

        if (status !== 200) {
            return next(boom.badImplementation());
        }

        res.status(200).json(data);

    } catch (error) {
        next(error)
    }
});
>

7- En el archivo src/server/server.js modificar el renderApp para que ademas de traer los originals y los trends, tambien me traiga mylist.

<
const renderApp = async (req, res) => {

    let initialState;
    const { token, email, name, id } = req.cookies;

    try {
        let movieList = await axios({
            url: `${config.apiUrl}/api/movies`,
            headers: { Authorization: `Bearer ${token}` },
            method: 'get'
        });
        movieList = movieList.data.data;

        let userList = await axios({
            url: `${config.apiUrl}/api/user-movies/?userId=${id}`,
            headers: { Authorization: `Bearer ${token}` },
            method: 'get'
        });
        userList = userList.data.data;

        initialState = {
            user: {
                id, email, name
            },
            mylist: userList.map(movie => movieList.find(x => x.id === movie.movieId)),
            trends: movieList.filter(movie => movie.contentRating === 'PG' || movie.contentRating === 'R' && movie._id),
            originals: movieList.filter(movie => movie.contentRating === 'G' && movie._id),
            searchContent: []
        }

    } catch (error) {
        initialState = {
            user: {},
            mylist: [],
            trends: [],
            originals: [],
            searchContent: []
        }
    }

    const store = createStore(reducer, initialState);
    const preloadedState = store.getState();
    const isLogged = (initialState.user.id);
    
    const html = renderToString(
        <Provider store={store}>
            <StaticRouter location={req.url} context={{}}>
                <Layout>
                    {renderRoutes(serverRoutes(isLogged))}
                </Layout>
            </StaticRouter>
        </Provider>,
    );

    res.set("Content-Security-Policy", "img-src * 'self' data: https:;");
    res.send(setResponse(html, preloadedState, req.hashManifest));
}
>