From 92fa136769415930fb4277e72091390810f94c99 Mon Sep 17 00:00:00 2001 From: santi Date: Wed, 11 Jun 2025 18:23:15 +0200 Subject: [PATCH] Mejora de xml para que se vea mucho mas formal! --- app/src/main/java.zip | Bin 68906 -> 69230 bytes .../santiparra/yomitrack/api/ApiClient.java | 16 ++ .../santiparra/yomitrack/api/ApiService.java | 207 +++++++++++++++--- .../yomitrack/db/entities/AnimeEntity.java | 52 ++++- .../yomitrack/db/entities/MangaEntity.java | 53 ++++- .../yomitrack/db/entities/UserEntity.java | 29 ++- .../yomitrack/model/AniListMedia.java | 59 ++++- .../yomitrack/model/ApiResponse.java | 9 - .../adapters/airing/AiringViewHolder.java | 41 ---- .../adapters/airing/AnimeViewHolder.java | 41 ---- .../anilist_adapter/AniListSearchAdapter.java | 54 ++++- .../adapters/anime_adapter/AnimeAdapter.java | 75 ++++++- .../BrowseGridAdapter.java | 65 ------ .../BrowseSectionAdapter.java | 60 ----- .../adapters/homeadapter/HomeAdapter.java | 71 ------ .../adapters/homeadapter/HomeCardAdapter.java | 64 +++++- .../adapters/manga_adapter/MangaAdapter.java | 68 +++++- .../manga_adapter/MangaSearchAdapter.java | 55 +++++ .../RecentActivityAdapter.java | 111 ++++++---- .../sectionadapter/SectionAdapter.java | 63 ------ .../fragments/addanime/AddAnimeFragment.java | 52 ++--- .../fragments/addmanga/AddMangaFragment.java | 16 +- .../fragments/anime_list/FragmentAnime.java | 50 ++++- .../editanime/EditAnimeFragment.java | 10 +- .../editmanga/EditMangaFragment.java | 84 +++---- .../ui/fragments/home/FragmentHome.java | 4 - .../fragments/manga_list/FragmentManga.java | 44 +++- app/src/main/res.zip | Bin 11137407 -> 11129665 bytes app/src/main/res/drawable/progress_bar.xml | 3 +- .../res/layout/fragment_forgot_password.xml | 7 - app/src/main/res/layout/item_section.xml | 30 --- 31 files changed, 883 insertions(+), 610 deletions(-) delete mode 100644 app/src/main/java/com/santiparra/yomitrack/model/ApiResponse.java delete mode 100644 app/src/main/java/com/santiparra/yomitrack/model/adapters/airing/AiringViewHolder.java delete mode 100644 app/src/main/java/com/santiparra/yomitrack/model/adapters/airing/AnimeViewHolder.java delete mode 100644 app/src/main/java/com/santiparra/yomitrack/model/adapters/browser_section_adapter/BrowseGridAdapter.java delete mode 100644 app/src/main/java/com/santiparra/yomitrack/model/adapters/browser_section_adapter/BrowseSectionAdapter.java delete mode 100644 app/src/main/java/com/santiparra/yomitrack/model/adapters/homeadapter/HomeAdapter.java delete mode 100644 app/src/main/java/com/santiparra/yomitrack/model/adapters/sectionadapter/SectionAdapter.java delete mode 100644 app/src/main/res/layout/item_section.xml diff --git a/app/src/main/java.zip b/app/src/main/java.zip index 5d9949357e7a6220be136ed50b36acf145d3a6ee..ba60348ffa7fa5c016a5c40326d3773105fd15e8 100644 GIT binary patch delta 40137 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 delta 38720 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 diff --git a/app/src/main/java/com/santiparra/yomitrack/api/ApiClient.java b/app/src/main/java/com/santiparra/yomitrack/api/ApiClient.java index 5017e9d..f659ffb 100644 --- a/app/src/main/java/com/santiparra/yomitrack/api/ApiClient.java +++ b/app/src/main/java/com/santiparra/yomitrack/api/ApiClient.java @@ -7,18 +7,34 @@ import retrofit2.converter.gson.GsonConverterFactory; public class ApiClient { + /** + * URL base del servidor donde está alojada la API del backend. + *

Nota: "10.0.2.2" es una dirección especial utilizada para acceder al localhost + * del host desde el emulador de Android.

+ */ private static final String BASE_URL = "http://10.0.2.2:3000/"; + + /** Instancia única de Retrofit. */ private static Retrofit retrofit = null; + /** + * Devuelve una instancia de Retrofit configurada con la URL base, un cliente HTTP + * con un interceptor para loguear las peticiones/respuestas, y un convertidor JSON. + * + * @return una instancia única de Retrofit lista para usar con las interfaces de servicio. + */ public static Retrofit getClient() { if (retrofit == null) { + // Interceptor para mostrar logs del cuerpo de la petición y respuesta HTTP HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BODY); + // Cliente HTTP personalizado con el interceptor de logging OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging) .build(); + // Construcción de la instancia de Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .client(client) diff --git a/app/src/main/java/com/santiparra/yomitrack/api/ApiService.java b/app/src/main/java/com/santiparra/yomitrack/api/ApiService.java index d67d525..a1a76e4 100644 --- a/app/src/main/java/com/santiparra/yomitrack/api/ApiService.java +++ b/app/src/main/java/com/santiparra/yomitrack/api/ApiService.java @@ -14,36 +14,58 @@ import com.santiparra.yomitrack.model.RegisterResponse; import com.santiparra.yomitrack.model.UserStatsResponse; import com.santiparra.yomitrack.utils.ActivityLog; - - import java.util.List; import java.util.Map; import retrofit2.Call; -import retrofit2.http.Body; -import retrofit2.http.DELETE; -import retrofit2.http.Field; -import retrofit2.http.FormUrlEncoded; -import retrofit2.http.GET; -import retrofit2.http.HTTP; -import retrofit2.http.POST; -import retrofit2.http.PUT; -import retrofit2.http.Path; -import retrofit2.http.Query; +import retrofit2.http.*; +/** + * Interfaz ApiService que define todos los endpoints disponibles + * para la comunicación entre la app YomiTrack y el backend. + * + *

Usa Retrofit para declarar métodos HTTP de forma declarativa.

+ */ public interface ApiService { - // ---------------- Usuario ---------------- + // -------------------- USUARIO -------------------- + + /** + * Registra un nuevo usuario en la aplicación. + * + * @param user Objeto con los datos del usuario. + * @return respuesta del backend con éxito o error. + */ @POST("users/register") Call registerUser(@Body UserEntity user); + /** + * Inicia sesión con un usuario existente. + * + * @param user Objeto con email y contraseña. + * @return respuesta con los datos del usuario autenticado. + */ @POST("users/login") Call loginUser(@Body UserEntity user); + /** + * Solicita un enlace de recuperación de contraseña al correo proporcionado. + * + * @param email correo electrónico del usuario. + * @return respuesta de estado de la operación. + */ @FormUrlEncoded @POST("users/forgot-password") Call forgotPassword(@Field("email") String email); + /** + * Restablece la contraseña del usuario con un token válido. + * + * @param email correo electrónico del usuario. + * @param token token enviado al correo. + * @param newPassword nueva contraseña. + * @return respuesta de la operación. + */ @FormUrlEncoded @POST("users/reset-password") Call resetPassword( @@ -52,11 +74,25 @@ public interface ApiService { @Field("newPassword") String newPassword ); - // ---------------- Anime ---------------- + // -------------------- ANIME -------------------- + + /** + * Inserta un nuevo anime en la lista del usuario. + * + * @param anime Objeto con los datos del anime. + * @return respuesta del backend. + */ @POST("anime/add") Call insertAnime(@Body AnimeEntity anime); - // Scroll infinito: obtener lista paginada + /** + * Obtiene la lista paginada de animes de un usuario. + * + * @param userId ID del usuario. + * @param page número de página. + * @param size cantidad de ítems por página. + * @return página con lista de animes. + */ @GET("/anime/list/{userId}") Call getAnimes( @Path("userId") int userId, @@ -64,16 +100,57 @@ public interface ApiService { @Query("size") int size ); + /** + * Actualiza un anime existente. + * + * @param animeId ID del anime. + * @param anime Objeto con datos actualizados. + * @return respuesta del backend. + */ @PUT("anime/{id}") Call updateAnime(@Path("id") int animeId, @Body AnimeEntity anime); + /** + * Elimina un anime por su ID. + * + * @param id ID del anime. + * @return respuesta del backend. + */ @DELETE("anime/delete/{id}") Call deleteAnime(@Path("id") int id); - // ---------------- Manga ---------------- + /** + * Obtiene animes por estado para un usuario específico. + * + * @param userId ID del usuario. + * @param status estado deseado (e.g., Watching, Completed). + * @return lista de animes con dicho estado. + */ + @GET("anime/user/{userId}/status/{status}") + Call> getAnimeByUserAndStatus( + @Path("userId") int userId, + @Path("status") String status + ); + + // -------------------- MANGA -------------------- + + /** + * Inserta un nuevo manga en la lista del usuario. + * + * @param manga Objeto con los datos del manga. + * @return respuesta del backend. + */ @POST("manga/add") Call insertManga(@Body MangaEntity manga); + /** + * Obtiene la lista paginada de mangas de un usuario. + * + * @param userId ID del usuario. + * @param page número de página. + * @param size cantidad de ítems por página. + * @return página con lista de mangas. + */ @GET("/manga/list/{userId}") Call getMangas( @Path("userId") int userId, @@ -81,57 +158,125 @@ public interface ApiService { @Query("size") int size ); + /** + * Obtiene mangas por estado para un usuario específico. + * + * @param userId ID del usuario. + * @param status estado deseado (e.g., Reading, Completed). + * @return lista de mangas con dicho estado. + */ @GET("manga/user/{userId}/status/{status}") Call> getMangaByUserAndStatus( @Path("userId") int userId, @Path("status") String status ); + /** + * Actualiza un manga existente. + * + * @param mangaId ID del manga. + * @param manga Objeto con datos actualizados. + * @return respuesta del backend. + */ @PUT("manga/{id}") Call updateManga(@Path("id") int mangaId, @Body MangaEntity manga); + /** + * Elimina un manga por su ID. + * + * @param id ID del manga. + * @return respuesta del backend. + */ @DELETE("manga/delete/{id}") Call deleteManga(@Path("id") int id); - // ---------------- Activity ------------------- + // -------------------- ACTIVIDAD -------------------- + /** + * Obtiene estadísticas de anime y manga del usuario. + * + * @param userId ID del usuario. + * @return objeto con estadísticas. + */ @GET("users/{id}/stats") Call getUserStats(@Path("id") int userId); + /** + * Obtiene el historial de actividad de un usuario. + * + * @param userId ID del usuario. + * @return lista de actividades. + */ @GET("api/activity/list/{userId}") Call> getActivityLog(@Path("userId") int userId); + /** + * Obtiene los comentarios de una actividad específica. + * + * @param activityId ID de la actividad. + * @return lista de comentarios. + */ @GET("api/activity/comments/{activityId}") Call> getCommentsByActivity(@Path("activityId") int activityId); + /** + * Verifica si un usuario dio like a una actividad. + * + * @param userId ID del usuario. + * @param activityId ID de la actividad. + * @return objeto JSON con resultado (true/false). + */ @GET("/api/activity/like/{userId}/{activityId}") Call checkLike( @Path("userId") int userId, @Path("activityId") int activityId ); - @GET("anime/user/{userId}/status/{status}") - Call> getAnimeByUserAndStatus( - @Path("userId") int userId, - @Path("status") String status - ); - + /** + * Envía un like a una actividad. + * + * @param body JSON con userId y activityId. + * @return respuesta del backend. + */ @POST("api/activity/like") Call postLike(@Body JsonObject body); - @POST("api/activity/comment") - Call postComment(@Body JsonObject body); - - @POST("api/activity/add") - Call postActivity(@Body Map body); - + /** + * Elimina un like de una actividad. + * + * @param body JSON con userId y activityId. + * @return respuesta del backend. + */ @HTTP(method = "DELETE", path = "api/activity/like/remove", hasBody = true) Call deleteLike(@Body JsonObject body); + /** + * Publica un comentario en una actividad. + * + * @param body JSON con userId, activityId, texto y otros datos. + * @return respuesta del backend. + */ + @POST("api/activity/comment") + Call postComment(@Body JsonObject body); - // ---------------- AniList API ---------------- + /** + * Publica una nueva actividad. + * + * @param body mapa con los datos de la actividad (userId, tipo, contenido...). + * @return respuesta del backend. + */ + @POST("api/activity/add") + Call postActivity(@Body Map body); + // -------------------- ANIList API -------------------- + + /** + * Busca animes o mangas en AniList. + * + * @param query término de búsqueda. + * @param type tipo de media: "ANIME" o "MANGA". + * @return lista de resultados obtenidos desde AniList. + */ @GET("anilist/search") Call> searchAniList(@Query("query") String query, @Query("type") String type); - } diff --git a/app/src/main/java/com/santiparra/yomitrack/db/entities/AnimeEntity.java b/app/src/main/java/com/santiparra/yomitrack/db/entities/AnimeEntity.java index e35a6cd..39ec8f4 100644 --- a/app/src/main/java/com/santiparra/yomitrack/db/entities/AnimeEntity.java +++ b/app/src/main/java/com/santiparra/yomitrack/db/entities/AnimeEntity.java @@ -4,22 +4,55 @@ import java.io.Serializable; /** * Entidad que representa un anime guardado por el usuario en la base de datos local. + * Implementa Serializable para facilitar el paso entre componentes. */ public class AnimeEntity implements Serializable { + /** ID único del anime en la base de datos. */ private int id; + + /** ID del usuario al que pertenece este anime. */ private int userId; + + /** Título del anime. */ private String title; + + /** Puntuación asignada por el usuario (por ejemplo, del 1 al 10). */ private int score; + + /** Progreso actual del usuario (número de episodios vistos). */ private int progress; + + /** Estado del anime (Watching, Completed, Paused, etc.). */ private String status; + + /** Tipo del anime (TV, Movie, OVA, etc.). */ private String type; + + /** URL de la imagen de portada del anime. */ private String imageUrl; + + /** Número total de episodios del anime. */ private int totalEpisodes; + /** + * Constructor vacío requerido por algunas librerías como Room o Retrofit. + */ public AnimeEntity() { } + /** + * Constructor completo de la entidad Anime. + * + * @param id ID único del anime. + * @param title Título del anime. + * @param status Estado del anime. + * @param userId ID del usuario propietario. + * @param imageUrl URL de la imagen del anime. + * @param progress Episodios vistos. + * @param score Puntuación asignada. + * @param totalEpisodes Total de episodios del anime. + */ public AnimeEntity(int id, String title, String status, int userId, String imageUrl, int progress, int score, int totalEpisodes) { this.id = id; this.title = title; @@ -31,75 +64,92 @@ public class AnimeEntity implements Serializable { this.totalEpisodes = totalEpisodes; } - // Getters y Setters + /** @return ID del anime. */ public int getId() { return id; } + /** @param id ID del anime. */ public void setId(int id) { this.id = id; } + /** @return ID del usuario. */ public int getUserId() { return userId; } + /** @param userId ID del usuario. */ public void setUserId(int userId) { this.userId = userId; } + /** @return Título del anime. */ public String getTitle() { return title; } + /** @param title Título del anime. */ public void setTitle(String title) { this.title = title; } + /** @return Puntuación del anime. */ public int getScore() { return score; } + /** @param score Puntuación del anime. */ public void setScore(int score) { this.score = score; } + /** @return Progreso del usuario (episodios vistos). */ public int getProgress() { return progress; } + /** @param progress Episodios vistos por el usuario. */ public void setProgress(int progress) { this.progress = progress; } + /** @return Estado del anime. */ public String getStatus() { return status; } + /** @param status Estado del anime. */ public void setStatus(String status) { this.status = status; } + /** @return Tipo de anime (TV, Movie, etc.). */ public String getType() { return type; } + /** @param type Tipo de anime. */ public void setType(String type) { this.type = type; } + /** @return URL de la imagen del anime. */ public String getImageUrl() { return imageUrl; } + /** @param imageUrl URL de la imagen del anime. */ public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } + /** @return Total de episodios del anime. */ public int getTotalEpisodes() { return totalEpisodes; } + /** @param totalEpisodes Número total de episodios. */ public void setTotalEpisodes(int totalEpisodes) { this.totalEpisodes = totalEpisodes; } diff --git a/app/src/main/java/com/santiparra/yomitrack/db/entities/MangaEntity.java b/app/src/main/java/com/santiparra/yomitrack/db/entities/MangaEntity.java index 4af8fbf..91e5630 100644 --- a/app/src/main/java/com/santiparra/yomitrack/db/entities/MangaEntity.java +++ b/app/src/main/java/com/santiparra/yomitrack/db/entities/MangaEntity.java @@ -4,22 +4,55 @@ import java.io.Serializable; /** * Entidad que representa un manga guardado por el usuario en la base de datos local. + * Implementa Serializable para permitir su paso entre actividades y fragmentos. */ public class MangaEntity implements Serializable { + /** ID único del manga en la base de datos. */ private int id; + + /** ID del usuario al que pertenece este manga. */ private int userId; + + /** Título del manga. */ private String title; + + /** Puntuación asignada por el usuario (por ejemplo, del 1 al 10). */ private int score; + + /** Progreso actual del usuario (capítulos leídos). */ private int progress; + + /** Estado del manga (Reading, Completed, On-Hold, etc.). */ private String status; + + /** Tipo del manga (Manga, Manhwa, Doujinshi, etc.). */ private String type; + + /** URL de la imagen de portada del manga. */ private String imageUrl; + + /** Número total de capítulos del manga. */ private int totalChapters; + /** + * Constructor vacío necesario para serialización y frameworks como Room o Retrofit. + */ public MangaEntity() { } + /** + * Constructor completo para inicializar una instancia de manga. + * + * @param id ID único del manga. + * @param title Título del manga. + * @param status Estado actual del manga. + * @param userId ID del usuario que lo añadió. + * @param imageUrl URL de la portada del manga. + * @param progress Capítulos leídos. + * @param score Puntuación dada por el usuario. + * @param totalChapters Total de capítulos disponibles. + */ public MangaEntity(int id, String title, String status, int userId, String imageUrl, int progress, int score, int totalChapters) { this.id = id; this.title = title; @@ -31,76 +64,92 @@ public class MangaEntity implements Serializable { this.totalChapters = totalChapters; } - // Getters y Setters - + /** @return ID del manga. */ public int getId() { return id; } + /** @param id ID del manga. */ public void setId(int id) { this.id = id; } + /** @return ID del usuario propietario del manga. */ public int getUserId() { return userId; } + /** @param userId ID del usuario propietario. */ public void setUserId(int userId) { this.userId = userId; } + /** @return Título del manga. */ public String getTitle() { return title; } + /** @param title Título del manga. */ public void setTitle(String title) { this.title = title; } + /** @return Puntuación asignada al manga. */ public int getScore() { return score; } + /** @param score Puntuación del manga. */ public void setScore(int score) { this.score = score; } + /** @return Capítulos leídos por el usuario. */ public int getProgress() { return progress; } + /** @param progress Capítulos que el usuario ha leído. */ public void setProgress(int progress) { this.progress = progress; } + /** @return Estado del manga (Reading, Dropped, etc.). */ public String getStatus() { return status; } + /** @param status Estado actual del manga. */ public void setStatus(String status) { this.status = status; } + /** @return Tipo de manga (e.g., Manhwa, Doujinshi). */ public String getType() { return type; } + /** @param type Tipo de manga. */ public void setType(String type) { this.type = type; } + /** @return URL de la imagen del manga. */ public String getImageUrl() { return imageUrl; } + /** @param imageUrl URL de la imagen de portada. */ public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } + /** @return Total de capítulos del manga. */ public int getTotalChapters() { return totalChapters; } + /** @param totalChapters Cantidad total de capítulos. */ public void setTotalChapters(int totalChapters) { this.totalChapters = totalChapters; } diff --git a/app/src/main/java/com/santiparra/yomitrack/db/entities/UserEntity.java b/app/src/main/java/com/santiparra/yomitrack/db/entities/UserEntity.java index df6dbff..89726b3 100644 --- a/app/src/main/java/com/santiparra/yomitrack/db/entities/UserEntity.java +++ b/app/src/main/java/com/santiparra/yomitrack/db/entities/UserEntity.java @@ -3,60 +3,87 @@ package com.santiparra.yomitrack.db.entities; import com.google.gson.annotations.SerializedName; /** - * Entidad que representa un usuario. + * Entidad que representa un usuario dentro del sistema. + * Utilizada tanto para autenticación como para registro y obtención de datos del backend. */ public class UserEntity { + /** Identificador único del usuario. */ @SerializedName("id") private int id; + /** Nombre de usuario utilizado para iniciar sesión y mostrar en el perfil. */ @SerializedName("username") private String username; + /** Contraseña del usuario (debe manejarse con cuidado y encriptación en producción). */ @SerializedName("password") private String password; + + /** Dirección de correo electrónico del usuario. */ @SerializedName("email") private String email; + /** + * Constructor utilizado para iniciar sesión con username y contraseña. + * + * @param username nombre de usuario. + * @param password contraseña del usuario. + */ public UserEntity(String username, String password) { this.username = username; this.password = password; } + /** + * Constructor utilizado para registrar un nuevo usuario. + * + * @param username nombre de usuario. + * @param email correo electrónico. + * @param password contraseña del usuario. + */ public UserEntity(String username, String email, String password) { this.username = username; this.email = email; this.password = password; } + /** @return ID del usuario. */ public int getId() { return id; } + /** @param id ID del usuario. */ public void setId(int id) { this.id = id; } + /** @return nombre de usuario. */ public String getUsername() { return username; } + /** @param username nombre de usuario. */ public void setUsername(String username) { this.username = username; } + /** @return contraseña del usuario. */ public String getPassword() { return password; } + /** @param password contraseña del usuario. */ public void setPassword(String password) { this.password = password; } + /** @return correo electrónico del usuario. */ public String getEmail() { return email; } + /** @param email correo electrónico del usuario. */ public void setEmail(String email) { this.email = email; } diff --git a/app/src/main/java/com/santiparra/yomitrack/model/AniListMedia.java b/app/src/main/java/com/santiparra/yomitrack/model/AniListMedia.java index e4ba69d..8789283 100644 --- a/app/src/main/java/com/santiparra/yomitrack/model/AniListMedia.java +++ b/app/src/main/java/com/santiparra/yomitrack/model/AniListMedia.java @@ -1,32 +1,75 @@ package com.santiparra.yomitrack.model; +/** + * Modelo de datos que representa un resultado de búsqueda desde la API de AniList. + * Utilizado tanto para anime como manga. + */ public class AniListMedia { + + /** ID único del media (anime o manga) proporcionado por AniList. */ private int id; + + /** Título del anime o manga. */ private String title; + + /** URL de la imagen de portada del anime o manga. */ private String imageUrl; + /** + * Constructor vacío necesario para serialización/deserialización automática. + */ public AniListMedia() {} + /** + * Devuelve el ID del media. + * + * @return ID del anime o manga. + */ public int getId() { return id; } - public String getTitle() { - return title; - } - - public String getImageUrl() { - return imageUrl; - } - + /** + * Establece el ID del media. + * + * @param id ID del anime o manga. + */ public void setId(int id) { this.id = id; } + /** + * Devuelve el título del media. + * + * @return título del anime o manga. + */ + public String getTitle() { + return title; + } + + /** + * Establece el título del media. + * + * @param title título del anime o manga. + */ public void setTitle(String title) { this.title = title; } + /** + * Devuelve la URL de la imagen de portada. + * + * @return URL de la imagen. + */ + public String getImageUrl() { + return imageUrl; + } + + /** + * Establece la URL de la imagen de portada. + * + * @param imageUrl URL de la imagen. + */ public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } diff --git a/app/src/main/java/com/santiparra/yomitrack/model/ApiResponse.java b/app/src/main/java/com/santiparra/yomitrack/model/ApiResponse.java deleted file mode 100644 index a95b32e..0000000 --- a/app/src/main/java/com/santiparra/yomitrack/model/ApiResponse.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.santiparra.yomitrack.model; - -public class ApiResponse { - private String message; - - public String getMessage() { - return message; - } -} diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/airing/AiringViewHolder.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/airing/AiringViewHolder.java deleted file mode 100644 index 827e45e..0000000 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/airing/AiringViewHolder.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.santiparra.yomitrack.model.adapters.airing; - -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import com.bumptech.glide.Glide; -import com.santiparra.yomitrack.R; -import com.santiparra.yomitrack.model.ItemModel; - -public class AiringViewHolder extends RecyclerView.ViewHolder { - - public ImageView imageView; - public TextView titleTextView; - public TextView progressTextView; - - public AiringViewHolder(@NonNull View itemView) { - super(itemView); - imageView = itemView.findViewById(R.id.mediaImage); - titleTextView = itemView.findViewById(R.id.titleTextView); - progressTextView = itemView.findViewById(R.id.progressTextView); - } - - public void bind(ItemModel item) { - titleTextView.setText(item.getTitle()); - progressTextView.setText("Progress: " + item.getProgress()); - - if (item.getImageUrl() != null && !item.getImageUrl().isEmpty()) { - Glide.with(itemView.getContext()) - .load(item.getImageUrl()) - .placeholder(R.drawable.placeholder_image) - .error(R.drawable.error_image) - .into(imageView); - } else { - imageView.setImageResource(R.drawable.placeholder_image); - } - } -} diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/airing/AnimeViewHolder.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/airing/AnimeViewHolder.java deleted file mode 100644 index e630cbc..0000000 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/airing/AnimeViewHolder.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.santiparra.yomitrack.model.adapters.airing; - -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import com.bumptech.glide.Glide; -import com.santiparra.yomitrack.R; -import com.santiparra.yomitrack.model.ItemModel; - -public class AnimeViewHolder extends RecyclerView.ViewHolder { - - public ImageView imageView; - public TextView titleTextView; - public TextView progressTextView; - - public AnimeViewHolder(@NonNull View itemView) { - super(itemView); - imageView = itemView.findViewById(R.id.mediaImage); - titleTextView = itemView.findViewById(R.id.titleTextView); - progressTextView = itemView.findViewById(R.id.progressTextView); - } - - public void bind(ItemModel item) { - titleTextView.setText(item.getTitle()); - progressTextView.setText("Progress: " + item.getProgress()); - - if (item.getImageUrl() != null && !item.getImageUrl().isEmpty()) { - Glide.with(itemView.getContext()) - .load(item.getImageUrl()) - .placeholder(R.drawable.placeholder_image) - .error(R.drawable.error_image) - .into(imageView); - } else { - imageView.setImageResource(R.drawable.placeholder_image); - } - } -} diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/anilist_adapter/AniListSearchAdapter.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/anilist_adapter/AniListSearchAdapter.java index 6c28d19..e28055f 100644 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/anilist_adapter/AniListSearchAdapter.java +++ b/app/src/main/java/com/santiparra/yomitrack/model/adapters/anilist_adapter/AniListSearchAdapter.java @@ -20,7 +20,6 @@ import com.santiparra.yomitrack.api.ApiService; import com.santiparra.yomitrack.db.entities.AnimeEntity; import com.santiparra.yomitrack.db.entities.MangaEntity; import com.santiparra.yomitrack.model.AniListMedia; -import com.santiparra.yomitrack.utils.ActivityLog; import com.santiparra.yomitrack.utils.DateUtils; import java.util.HashMap; @@ -31,18 +30,36 @@ import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; +/** + * Adaptador para mostrar resultados de búsqueda provenientes de AniList + * y permitir al usuario añadir animes o mangas a su lista local mediante la API. + */ public class AniListSearchAdapter extends RecyclerView.Adapter { private final List mediaList; private final Context context; private final String mediaType; + /** + * Constructor del adaptador. + * + * @param context contexto de la aplicación. + * @param mediaList lista de resultados de búsqueda de AniList. + * @param mediaType tipo de media ("ANIME" o "MANGA"). + */ public AniListSearchAdapter(Context context, List mediaList, String mediaType) { this.context = context; this.mediaList = mediaList; this.mediaType = mediaType; } + /** + * Infla el layout de cada ítem de la lista. + * + * @param parent el ViewGroup padre. + * @param viewType el tipo de vista. + * @return ViewHolder que contiene la vista del ítem. + */ @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -50,6 +67,12 @@ public class AniListSearchAdapter extends RecyclerView.Adapter() { @Override public void onResponse(Call call, Response response) { Toast.makeText(context, "Anime añadido", Toast.LENGTH_SHORT).show(); - // 🔁 Registrar actividad usando Map + // Registrar actividad Map body = new HashMap<>(); body.put("userId", userId); body.put("action", "añadió"); @@ -96,7 +121,9 @@ public class AniListSearchAdapter extends RecyclerView.Adapter() { @Override - public void onResponse(Call call, Response response) {} + public void onResponse(Call call, Response response) { + // Actividad registrada (sin acción adicional) + } @Override public void onFailure(Call call, Throwable t) { @@ -112,6 +139,7 @@ public class AniListSearchAdapter extends RecyclerView.Adapter() { @Override public void onResponse(Call call, Response response) { @@ -156,16 +185,35 @@ public class AniListSearchAdapter extends RecyclerView.Adapter { + /** Vista normal por defecto. */ public static final int VIEW_NORMAL = 0; + + /** Vista compacta. */ public static final int VIEW_COMPACT = 1; + + /** Vista ampliada. */ public static final int VIEW_LARGE = 2; + /** Lista de animes a mostrar. */ private List animeList; + + /** Tipo de vista actual. */ private int viewType; + + /** Listener para clics normales (edición). */ private final OnAnimeClickListener onEditClick; + + /** Listener para clics prolongados (acciones extendidas). */ private final OnAnimeClickListener onLongClick; + /** + * Constructor del adaptador. + * + * @param animeList lista de animes. + * @param viewType tipo de vista a usar (normal, compacta, grande). + * @param onEditClick callback para clics normales. + * @param onLongClick callback para clics prolongados. + */ public AnimeAdapter(List animeList, int viewType, OnAnimeClickListener onEditClick, OnAnimeClickListener onLongClick) { @@ -38,13 +61,23 @@ public class AnimeAdapter extends RecyclerView.Adapter newList) { - this.animeList = newList != null ? newList : new ArrayList<>(); + this.animeList = new ArrayList<>(newList); // o .clear() + .addAll() notifyDataSetChanged(); } @@ -145,11 +178,37 @@ public class AnimeAdapter extends RecyclerView.Adapter { - - private final List items; - - public BrowseGridAdapter(List items) { - this.items = items; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_browse_card, parent, false); - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - ItemModel item = items.get(position); - holder.title.setText(item.getTitle()); - Glide.with(holder.itemView.getContext()) - .load(item.getImageUrl()) - .placeholder(R.drawable.placeholder_image) - .into(holder.cover); - - // 👉 Animación - Animation animation = AnimationUtils.loadAnimation(holder.itemView.getContext(), R.anim.item_animation_fade_scale); - holder.itemView.startAnimation(animation); - } - - @Override - public int getItemCount() { - return items.size(); - } - - static class ViewHolder extends RecyclerView.ViewHolder { - ImageView cover; - TextView title; - - ViewHolder(View itemView) { - super(itemView); - cover = itemView.findViewById(R.id.imageViewCover); - title = itemView.findViewById(R.id.textViewTitle); - } - } -} - diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/browser_section_adapter/BrowseSectionAdapter.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/browser_section_adapter/BrowseSectionAdapter.java deleted file mode 100644 index 33d1c83..0000000 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/browser_section_adapter/BrowseSectionAdapter.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.santiparra.yomitrack.model.adapters.browser_section_adapter; - - - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.santiparra.yomitrack.R; -import com.santiparra.yomitrack.model.BrowseSection; -import com.santiparra.yomitrack.model.adapters.homeadapter.HomeAdapter; - -import java.util.List; - -public class BrowseSectionAdapter extends RecyclerView.Adapter { - - private final List sectionList; - - public BrowseSectionAdapter(List sectionList) { - this.sectionList = sectionList; - } - - @NonNull - @Override - public BrowseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_section, parent, false); - return new BrowseViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull BrowseViewHolder holder, int position) { - BrowseSection section = sectionList.get(position); - holder.sectionTitle.setText(section.getTitle()); - - HomeAdapter adapter = new HomeAdapter(section.getItems(), section.getTitle()); - holder.recyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext(), LinearLayoutManager.HORIZONTAL, false)); - holder.recyclerView.setAdapter(adapter); - } - - @Override - public int getItemCount() { - return sectionList.size(); - } - - static class BrowseViewHolder extends RecyclerView.ViewHolder { - TextView sectionTitle; - RecyclerView recyclerView; - - public BrowseViewHolder(@NonNull View itemView) { - super(itemView); - sectionTitle = itemView.findViewById(R.id.sectionTitle); - recyclerView = itemView.findViewById(R.id.sectionRecyclerView); - } - } -} diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/homeadapter/HomeAdapter.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/homeadapter/HomeAdapter.java deleted file mode 100644 index 86d32fa..0000000 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/homeadapter/HomeAdapter.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.santiparra.yomitrack.model.adapters.homeadapter; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import com.santiparra.yomitrack.R; -import com.santiparra.yomitrack.model.ItemModel; -import com.santiparra.yomitrack.model.adapters.airing.AiringViewHolder; -import com.santiparra.yomitrack.model.adapters.airing.AnimeViewHolder; - -import java.util.List; - -public class HomeAdapter extends RecyclerView.Adapter { - - private List itemList; - private String sectionTitle; - - private static final int TYPE_AIRING = 0; - private static final int TYPE_ANIME_MANGA = 1; - - public HomeAdapter(List itemList, String sectionTitle) { - this.itemList = itemList; - this.sectionTitle = sectionTitle; - } - - @Override - public int getItemViewType(int position) { - if (sectionTitle.equalsIgnoreCase("Airing")) { - return TYPE_AIRING; - } else { - return TYPE_ANIME_MANGA; - } - } - - @NonNull - @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_media_card, parent, false); - - // Ajustamos manualmente el ancho - ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); - view.setLayoutParams(layoutParams); - - if (viewType == TYPE_AIRING) { - return new AiringViewHolder(view); - } else { - return new AnimeViewHolder(view); - } - } - - @Override - public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { - ItemModel item = itemList.get(position); - - if (holder instanceof AiringViewHolder) { - ((AiringViewHolder) holder).bind(item); - } else if (holder instanceof AnimeViewHolder) { - ((AnimeViewHolder) holder).bind(item); - } - } - - @Override - public int getItemCount() { - return itemList.size(); - } -} diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/homeadapter/HomeCardAdapter.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/homeadapter/HomeCardAdapter.java index 4f30552..2a079a5 100644 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/homeadapter/HomeCardAdapter.java +++ b/app/src/main/java/com/santiparra/yomitrack/model/adapters/homeadapter/HomeCardAdapter.java @@ -17,20 +17,48 @@ import com.santiparra.yomitrack.model.ItemModel; import java.util.List; +/** + * Adaptador para el RecyclerView del fragmento de inicio (Home). + * Muestra tarjetas con imagen, título y progreso de animes o mangas recientes. + */ public class HomeCardAdapter extends RecyclerView.Adapter { + /** + * Interfaz para manejar clics en los ítems del RecyclerView. + */ public interface OnItemClickListener { + /** + * Se llama cuando el usuario hace clic sobre un ítem. + * + * @param item el ítem seleccionado. + */ void onItemClick(ItemModel item); } + /** Lista de ítems a mostrar (animes o mangas). */ private final List itemList; + + /** Listener que maneja clics en los ítems. */ private final OnItemClickListener listener; + /** + * Constructor del adaptador. + * + * @param itemList lista de ítems a mostrar. + * @param listener manejador de eventos de clic. + */ public HomeCardAdapter(List itemList, OnItemClickListener listener) { this.itemList = itemList; this.listener = listener; } + /** + * Infla la vista para un ítem del RecyclerView. + * + * @param parent vista contenedora. + * @param viewType tipo de vista (no usado aquí). + * @return instancia de ViewHolder. + */ @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -38,26 +66,58 @@ public class HomeCardAdapter extends RecyclerView.Adapter listener.onItemClick(item)); } + /** + * Devuelve la cantidad de ítems en la lista. + * + * @return número total de ítems. + */ @Override public int getItemCount() { return itemList.size(); } + /** + * ViewHolder que representa cada tarjeta (ítem) en el RecyclerView. + */ public static class ViewHolder extends RecyclerView.ViewHolder { - TextView title, progress; + + /** Texto que muestra el título del ítem. */ + TextView title; + + /** Texto que muestra el progreso del ítem. */ + TextView progress; + + /** Imagen de portada del ítem. */ ImageView cover; + + /** Tarjeta contenedora del ítem. */ CardView card; + /** + * Constructor del ViewHolder. + * + * @param itemView vista del ítem inflada desde el layout. + */ public ViewHolder(@NonNull View itemView) { super(itemView); title = itemView.findViewById(R.id.itemTitle); diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/manga_adapter/MangaAdapter.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/manga_adapter/MangaAdapter.java index f91ea4b..6954268 100644 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/manga_adapter/MangaAdapter.java +++ b/app/src/main/java/com/santiparra/yomitrack/model/adapters/manga_adapter/MangaAdapter.java @@ -1,6 +1,5 @@ package com.santiparra.yomitrack.model.adapters.manga_adapter; -import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -17,17 +16,41 @@ import com.santiparra.yomitrack.db.entities.MangaEntity; import java.util.List; +/** + * Adaptador para mostrar una lista de mangas en un RecyclerView con distintos tipos de vista: + * normal, compacta y grande. Soporta clics normales y prolongados. + */ public class MangaAdapter extends RecyclerView.Adapter { + /** Vista estándar. */ public static final int VIEW_NORMAL = 0; + + /** Vista compacta. */ public static final int VIEW_COMPACT = 1; + + /** Vista grande. */ public static final int VIEW_LARGE = 2; + /** Lista de mangas a mostrar. */ private List mangaList; + + /** Tipo de vista actual. */ private int viewType; + + /** Listener para clic corto (edición). */ private final OnMangaClickListener onEditClick; + + /** Listener para clic prolongado (acciones extendidas). */ private final OnMangaClickListener onLongClick; + /** + * Constructor del adaptador. + * + * @param mangaList lista de mangas a mostrar. + * @param viewType tipo de vista deseado. + * @param onEditClick callback para clics normales. + * @param onLongClick callback para clics prolongados. + */ public MangaAdapter(List mangaList, int viewType, OnMangaClickListener onEditClick, OnMangaClickListener onLongClick) { @@ -124,21 +147,51 @@ public class MangaAdapter extends RecyclerView.Adapter newList) { + mangaList.clear(); + mangaList.addAll(newList); + notifyDataSetChanged(); + } + + /** + * ViewHolder para representar un ítem de manga en el RecyclerView. + */ public static class MangaViewHolder extends RecyclerView.ViewHolder { ImageView imageCover; TextView textTitle, textStatus, textProgress, textScore, textType; View statusDot; + /** + * Constructor del ViewHolder. + * + * @param itemView vista inflada del ítem. + */ public MangaViewHolder(@NonNull View itemView) { super(itemView); imageCover = itemView.findViewById(R.id.imageCover); @@ -151,12 +204,15 @@ public class MangaAdapter extends RecyclerView.Adapter newList) { - this.mangaList = newList; - notifyDataSetChanged(); - } - + /** + * Interfaz para manejar clics sobre ítems de manga. + */ public interface OnMangaClickListener { + /** + * Se ejecuta cuando se hace clic en un manga. + * + * @param manga el ítem clicado. + */ void onClick(MangaEntity manga); } } diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/manga_adapter/MangaSearchAdapter.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/manga_adapter/MangaSearchAdapter.java index 2d760fb..dcd74f4 100644 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/manga_adapter/MangaSearchAdapter.java +++ b/app/src/main/java/com/santiparra/yomitrack/model/adapters/manga_adapter/MangaSearchAdapter.java @@ -15,25 +15,58 @@ import com.santiparra.yomitrack.model.AniListMedia; import java.util.List; +/** + * Adaptador para mostrar resultados de búsqueda de mangas desde AniList. + * Utilizado en el fragmento de búsqueda para permitir seleccionar un manga. + */ public class MangaSearchAdapter extends RecyclerView.Adapter { + /** Lista de mangas obtenidos desde la API de AniList. */ private List mangaList; + + /** Listener para manejar clics en los ítems del RecyclerView. */ private final OnMangaClickListener clickListener; + /** + * Interfaz que define el callback cuando se hace clic en un manga. + */ public interface OnMangaClickListener { + /** + * Método invocado al hacer clic sobre un manga. + * + * @param manga objeto de AniList clicado. + */ void onClick(AniListMedia manga); } + /** + * Constructor del adaptador. + * + * @param mangaList lista de resultados de búsqueda. + * @param clickListener listener para manejar el clic en cada ítem. + */ public MangaSearchAdapter(List mangaList, OnMangaClickListener clickListener) { this.mangaList = mangaList; this.clickListener = clickListener; } + /** + * Reemplaza la lista de mangas actual por una nueva y actualiza el RecyclerView. + * + * @param mangaList nueva lista de mangas. + */ public void setMangaList(List mangaList) { this.mangaList = mangaList; notifyDataSetChanged(); } + /** + * Infla el layout para un ítem individual del RecyclerView. + * + * @param parent el ViewGroup padre. + * @param viewType tipo de vista (no utilizado aquí). + * @return instancia del ViewHolder. + */ @NonNull @Override public SearchViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -41,6 +74,12 @@ public class MangaSearchAdapter extends RecyclerView.Adapter clickListener.onClick(manga)); } + /** + * Devuelve la cantidad total de mangas en la lista. + * + * @return tamaño de la lista. + */ @Override public int getItemCount() { return mangaList != null ? mangaList.size() : 0; } + /** + * ViewHolder que representa cada ítem del RecyclerView. + */ static class SearchViewHolder extends RecyclerView.ViewHolder { + /** Imagen de portada del manga. */ ImageView imageCover; + + /** Título del manga. */ TextView title; + /** + * Constructor del ViewHolder. + * + * @param itemView vista inflada del ítem. + */ public SearchViewHolder(@NonNull View itemView) { super(itemView); imageCover = itemView.findViewById(R.id.imageCover); diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/recentactivity_adapter/RecentActivityAdapter.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/recentactivity_adapter/RecentActivityAdapter.java index 06776e0..3e43e33 100644 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/recentactivity_adapter/RecentActivityAdapter.java +++ b/app/src/main/java/com/santiparra/yomitrack/model/adapters/recentactivity_adapter/RecentActivityAdapter.java @@ -7,6 +7,7 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; @@ -18,11 +19,24 @@ import com.santiparra.yomitrack.model.RecentActivityModel; import java.util.List; +/** + * Adaptador para mostrar la actividad reciente del usuario en forma de tarjetas. + * Cada tarjeta puede incluir una acción, portada, comentarios, botón de like y comentar. + */ public class RecentActivityAdapter extends RecyclerView.Adapter { + /** Lista de actividades recientes. */ private List activityList; + + /** ID del usuario actualmente logueado (para comentarios y likes). */ private final int currentUserId; + /** + * Constructor del adaptador. + * + * @param activityList lista de actividades a mostrar. + * @param currentUserId ID del usuario actual. + */ public RecentActivityAdapter(List activityList, int currentUserId) { this.activityList = activityList; this.currentUserId = currentUserId; @@ -31,7 +45,8 @@ public class RecentActivityAdapter extends RecyclerView.Adapter { + boolean newLike = !comment.isLiked(); + comment.setLiked(newLike); + likeButton.setImageResource(newLike ? R.drawable.ic_heart_filled : R.drawable.ic_heart_outline); + likeButton.setColorFilter(commentView.getContext().getColor(newLike ? R.color.pink : R.color.gray)); }); - replyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int adapterPos = holder.getAdapterPosition(); - if (adapterPos == RecyclerView.NO_POSITION) return; - RecentActivityModel activityItem = activityList.get(adapterPos); - CommentDialog dialog = new CommentDialog( - holder.itemView.getContext(), - currentUserId, - activityItem.getId(), - () -> notifyItemChanged(adapterPos), - comment.getUsername() - ); - dialog.show(); - } - }); - - holder.commentContainer.addView(commentView); - } - - holder.commentButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { + // Manejo de respuesta a un comentario + replyButton.setOnClickListener(v -> { int adapterPos = holder.getAdapterPosition(); if (adapterPos == RecyclerView.NO_POSITION) return; RecentActivityModel activityItem = activityList.get(adapterPos); @@ -115,39 +109,72 @@ public class RecentActivityAdapter extends RecyclerView.Adapter notifyItemChanged(adapterPos) + () -> notifyItemChanged(adapterPos), + comment.getUsername() ); dialog.show(); - } + }); + + holder.commentContainer.addView(commentView); + } + + // Botón para añadir nuevo comentario a la actividad + holder.commentButton.setOnClickListener(v -> { + int adapterPos = holder.getAdapterPosition(); + if (adapterPos == RecyclerView.NO_POSITION) return; + RecentActivityModel activityItem = activityList.get(adapterPos); + CommentDialog dialog = new CommentDialog( + holder.itemView.getContext(), + currentUserId, + activityItem.getId(), + () -> notifyItemChanged(adapterPos) + ); + dialog.show(); }); - holder.likeButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - activity.liked = !activity.liked; - holder.likeButton.setImageResource(activity.liked ? R.drawable.ic_heart_filled : R.drawable.ic_heart_outline); - holder.likeButton.setColorFilter(holder.itemView.getContext().getColor( - activity.liked ? R.color.pink : R.color.textPrimary)); - } + // Botón de like para la actividad + holder.likeButton.setOnClickListener(v -> { + activity.liked = !activity.liked; + holder.likeButton.setImageResource(activity.liked ? R.drawable.ic_heart_filled : R.drawable.ic_heart_outline); + holder.likeButton.setColorFilter(holder.itemView.getContext().getColor( + activity.liked ? R.color.pink : R.color.textPrimary)); }); } + /** + * Devuelve la cantidad de actividades en la lista. + * + * @return tamaño de la lista. + */ @Override public int getItemCount() { return activityList.size(); } + /** + * Reemplaza la lista actual por una nueva y actualiza la vista. + * + * @param newList nueva lista de actividades. + */ public void updateData(List newList) { this.activityList = newList; notifyDataSetChanged(); } + /** + * ViewHolder que representa una tarjeta de actividad reciente. + */ static class ActivityViewHolder extends RecyclerView.ViewHolder { TextView user, action, title, time; ImageView image; ImageButton likeButton, commentButton; LinearLayout commentContainer; + /** + * Constructor del ViewHolder. + * + * @param itemView vista inflada del ítem. + */ public ActivityViewHolder(@NonNull View itemView) { super(itemView); user = itemView.findViewById(R.id.activityUser); diff --git a/app/src/main/java/com/santiparra/yomitrack/model/adapters/sectionadapter/SectionAdapter.java b/app/src/main/java/com/santiparra/yomitrack/model/adapters/sectionadapter/SectionAdapter.java deleted file mode 100644 index a759b59..0000000 --- a/app/src/main/java/com/santiparra/yomitrack/model/adapters/sectionadapter/SectionAdapter.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.santiparra.yomitrack.model.adapters.sectionadapter; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.santiparra.yomitrack.R; -import com.santiparra.yomitrack.model.ItemModel; -import com.santiparra.yomitrack.model.adapters.homeadapter.HomeAdapter; - -import java.util.List; -import java.util.Map; - -public class SectionAdapter extends RecyclerView.Adapter { - - private final List sectionTitles; - private final Map> sectionItems; - - public SectionAdapter(List sectionTitles, Map> sectionItems) { - this.sectionTitles = sectionTitles; - this.sectionItems = sectionItems; - } - - @NonNull - @Override - public SectionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_section, parent, false); - return new SectionViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull SectionViewHolder holder, int position) { - String sectionTitle = sectionTitles.get(position); - holder.title.setText(sectionTitle); - - List fullList = sectionItems.get(sectionTitle); - - HomeAdapter adapter = new HomeAdapter(fullList, sectionTitle); - holder.recyclerView.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext(), LinearLayoutManager.HORIZONTAL, false)); - holder.recyclerView.setAdapter(adapter); - } - - @Override - public int getItemCount() { - return sectionTitles.size(); - } - - static class SectionViewHolder extends RecyclerView.ViewHolder { - TextView title; - RecyclerView recyclerView; - - public SectionViewHolder(@NonNull View itemView) { - super(itemView); - title = itemView.findViewById(R.id.sectionTitle); - recyclerView = itemView.findViewById(R.id.sectionRecyclerView); - } - } -} diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addanime/AddAnimeFragment.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addanime/AddAnimeFragment.java index 3eb114e..188704f 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addanime/AddAnimeFragment.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addanime/AddAnimeFragment.java @@ -75,18 +75,12 @@ public class AddAnimeFragment extends Fragment { private void setupSpinners() { ArrayAdapter statusAdapter = ArrayAdapter.createFromResource( - requireContext(), - R.array.anime_status_array, - R.layout.item_spinner - ); - statusAdapter.setDropDownViewResource(R.layout.item_spinner); // ✅ blanco también al desplegar + requireContext(), R.array.anime_status_array, R.layout.item_spinner); + statusAdapter.setDropDownViewResource(R.layout.item_spinner); statusSpinner.setAdapter(statusAdapter); ArrayAdapter typeAdapter = ArrayAdapter.createFromResource( - requireContext(), - R.array.anime_type_array, - R.layout.item_spinner - ); + requireContext(), R.array.anime_type_array, R.layout.item_spinner); typeAdapter.setDropDownViewResource(R.layout.item_spinner); typeSpinner.setAdapter(typeAdapter); } @@ -128,23 +122,16 @@ public class AddAnimeFragment extends Fragment { String status = statusSpinner.getSelectedItem().toString(); String type = typeSpinner.getSelectedItem().toString(); - int score = 0; - int progress = 0; - try { - score = Integer.parseInt(scoreEditText.getText().toString()); - progress = Integer.parseInt(progressEditText.getText().toString()); - } catch (NumberFormatException ignored) { - } + int score = parseIntOrZero(scoreEditText.getText().toString()); + int progress = parseIntOrZero(progressEditText.getText().toString()); AnimeEntity anime = new AnimeEntity(); anime.setUserId(userId); anime.setTitle(selected.getTitle()); - if (selected.getImageUrl() == null || selected.getImageUrl().isEmpty()) { - selectedImageUrl = "android.resource://" + requireContext().getPackageName() + "/" + R.drawable.sample_cover; - } else { - selectedImageUrl = selected.getImageUrl(); - } + selectedImageUrl = (selected.getImageUrl() == null || selected.getImageUrl().isEmpty()) + ? "android.resource://" + requireContext().getPackageName() + "/" + R.drawable.sample_cover + : selected.getImageUrl(); anime.setImageUrl(selectedImageUrl); anime.setStatus(status); @@ -158,6 +145,7 @@ public class AddAnimeFragment extends Fragment { if (response.isSuccessful() && response.body() != null) { Toast.makeText(getContext(), response.body().getMessage(), Toast.LENGTH_SHORT).show(); registrarActividad(anime.getTitle()); + notificarAñadido(); requireActivity().getSupportFragmentManager().popBackStack(); } else { Toast.makeText(getContext(), "Error al guardar anime", Toast.LENGTH_SHORT).show(); @@ -182,17 +170,29 @@ public class AddAnimeFragment extends Fragment { @Override public void onResponse(Call call, Response response) { Log.d("ACTIVITY_POST", "Código de respuesta: " + response.code()); - if (!response.isSuccessful()) { - Log.e("ACTIVITY_POST", "Error en response: " + response.errorBody()); - } } @Override public void onFailure(Call call, Throwable t) { Log.e("ACTIVITY_POST", "Error al registrar actividad: " + t.getMessage(), t); - if (!isAdded()) return; - Toast.makeText(getContext(), "Error al registrar actividad", Toast.LENGTH_SHORT).show(); + if (isAdded()) { + Toast.makeText(getContext(), "Error al registrar actividad", Toast.LENGTH_SHORT).show(); + } } }); } + + private void notificarAñadido() { + Bundle result = new Bundle(); + result.putBoolean("anime_added", true); + getParentFragmentManager().setFragmentResult("anime_add_request", result); + } + + private int parseIntOrZero(String value) { + try { + return Integer.parseInt(value.trim()); + } catch (NumberFormatException e) { + return 0; + } + } } diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addmanga/AddMangaFragment.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addmanga/AddMangaFragment.java index 0ca99c4..17c7d38 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addmanga/AddMangaFragment.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/addmanga/AddMangaFragment.java @@ -90,7 +90,6 @@ public class AddMangaFragment extends Fragment { typeSpinner.setAdapter(typeAdapter); } - private void setupRecycler() { searchAdapter = new MangaSearchAdapter(new ArrayList<>(), this::onMangaSelected); searchResults.setAdapter(searchAdapter); @@ -98,8 +97,7 @@ public class AddMangaFragment extends Fragment { private void setupSearch() { searchEditText.setOnEditorActionListener((TextView v, int actionId, KeyEvent event) -> { - if (actionId == EditorInfo.IME_ACTION_SEARCH || - (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) { + if (actionId == EditorInfo.IME_ACTION_SEARCH || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) { String query = searchEditText.getText().toString().trim(); if (!query.isEmpty()) { api.searchAniList(query, "MANGA").enqueue(new Callback>() { @@ -133,8 +131,7 @@ public class AddMangaFragment extends Fragment { try { score = Integer.parseInt(scoreEditText.getText().toString()); progress = Integer.parseInt(progressEditText.getText().toString()); - } catch (NumberFormatException ignored) { - } + } catch (NumberFormatException ignored) {} MangaEntity manga = new MangaEntity(); manga.setUserId(userId); @@ -158,6 +155,9 @@ public class AddMangaFragment extends Fragment { if (response.isSuccessful() && response.body() != null) { Toast.makeText(getContext(), response.body().getMessage(), Toast.LENGTH_SHORT).show(); registrarActividad(manga.getTitle()); + Bundle result = new Bundle(); + result.putBoolean("manga_added", true); + getParentFragmentManager().setFragmentResult("manga_add_request", result); requireActivity().getSupportFragmentManager().popBackStack(); } else { Toast.makeText(getContext(), "Error al guardar manga", Toast.LENGTH_SHORT).show(); @@ -180,11 +180,7 @@ public class AddMangaFragment extends Fragment { api.postActivity(actividad).enqueue(new Callback() { @Override - public void onResponse(Call call, Response response) { - if (!response.isSuccessful()) { - // Puedes logear el error si lo deseas - } - } + public void onResponse(Call call, Response response) {} @Override public void onFailure(Call call, Throwable t) { diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/anime_list/FragmentAnime.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/anime_list/FragmentAnime.java index c51f223..48d9580 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/anime_list/FragmentAnime.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/anime_list/FragmentAnime.java @@ -59,15 +59,12 @@ public class FragmentAnime extends Fragment { @Nullable @Override - public View onCreateView(@NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_alist, container, false); } @Override - public void onViewCreated(@NonNull View view, - @Nullable Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); initViews(view); @@ -76,6 +73,7 @@ public class FragmentAnime extends Fragment { setupSearchListener(); setupFab(view); setupInsets(view); + setupResultListener(); SharedPreferences prefs = requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE); userId = prefs.getInt("user_id", -1); @@ -84,7 +82,7 @@ public class FragmentAnime extends Fragment { Toast.makeText(getContext(), "Error: sesión no iniciada", Toast.LENGTH_SHORT).show(); return; } - // Mostrar el nombre del usuario + String username = prefs.getString("username", "Usuario"); TextView textViewUsername = view.findViewById(R.id.textViewUsername); textViewUsername.setText(username); @@ -124,6 +122,30 @@ public class FragmentAnime extends Fragment { }); } + private void setupResultListener() { + getParentFragmentManager().setFragmentResultListener("anime_add_request", this, (requestKey, bundle) -> { + if (bundle.getBoolean("anime_added", false)) { + currentPage = 1; + animeList.clear(); + adapter.updateList(new ArrayList<>()); + loadMoreAnimes(currentPage); + } + }); + + getParentFragmentManager().setFragmentResultListener("anime_delete_request", this, (requestKey, bundle) -> { + int deletedId = bundle.getInt("anime_id", -1); + if (deletedId != -1) { + for (int i = 0; i < animeList.size(); i++) { + if (animeList.get(i).getId() == deletedId) { + animeList.remove(i); + adapter.updateList(animeList); + break; + } + } + } + }); + } + private void setupViewButtons() { btnViewCompact.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_COMPACT)); btnViewNormal.setOnClickListener(v -> setViewType(AnimeAdapter.VIEW_NORMAL)); @@ -197,9 +219,8 @@ public class FragmentAnime extends Fragment { if (response.isSuccessful() && response.body() != null) { Toast.makeText(requireContext(), response.body().getMessage(), Toast.LENGTH_SHORT).show(); - currentPage = 1; - animeList.clear(); - loadMoreAnimes(currentPage); + animeList.remove(anime); + adapter.updateList(animeList); } else { Toast.makeText(requireContext(), "Error al eliminar", Toast.LENGTH_SHORT).show(); } @@ -225,8 +246,15 @@ public class FragmentAnime extends Fragment { if (response.isSuccessful() && response.body() != null) { List nuevos = response.body().getData(); - animeList.addAll(nuevos); - adapter.notifyItemRangeInserted(animeList.size() - nuevos.size(), nuevos.size()); + if (page == 1) { + animeList.clear(); + animeList.addAll(nuevos); + adapter.updateList(animeList); + } else { + int start = animeList.size(); + animeList.addAll(nuevos); + adapter.notifyItemRangeInserted(start, nuevos.size()); + } isLoading = response.body().isHasNextPage(); } else { isLoading = false; diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/editanime/EditAnimeFragment.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/editanime/EditAnimeFragment.java index b58feb2..192943b 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/editanime/EditAnimeFragment.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/editanime/EditAnimeFragment.java @@ -67,7 +67,7 @@ public class EditAnimeFragment extends Fragment { String[] typeArray = getResources().getStringArray(R.array.anime_type_array); ArrayAdapter statusAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, statusArray); - statusAdapter.setDropDownViewResource(R.layout.item_spinner); // Aplica color blanco en lista desplegable también + statusAdapter.setDropDownViewResource(R.layout.item_spinner); spinnerStatus.setAdapter(statusAdapter); ArrayAdapter typeAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, typeArray); @@ -142,8 +142,6 @@ public class EditAnimeFragment extends Fragment { api.updateAnime(anime.getId(), anime).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { - Log.d("API_RESPONSE", "onResponse ejecutado: " + response.body()); - if (!isAdded()) return; if (response.isSuccessful()) { @@ -160,7 +158,6 @@ public class EditAnimeFragment extends Fragment { @Override public void onFailure(Call call, Throwable t) { - Log.e("API_RESPONSE", "onFailure ejecutado: " + t.getMessage(), t); if (!isAdded()) return; Toast.makeText(requireContext(), "Fallo en la conexión", Toast.LENGTH_SHORT).show(); } @@ -179,6 +176,10 @@ public class EditAnimeFragment extends Fragment { Toast.makeText(requireContext(), "Anime eliminado", Toast.LENGTH_SHORT).show(); requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE) .edit().putBoolean("refresh_profile", true).apply(); + Bundle result = new Bundle(); + result.putBoolean("anime_deleted", true); + result.putInt("anime_id", anime.getId()); + getParentFragmentManager().setFragmentResult("anime_delete_request", result); requireActivity().getSupportFragmentManager().popBackStack(); } else { Toast.makeText(requireContext(), "Error al eliminar", Toast.LENGTH_SHORT).show(); @@ -231,7 +232,6 @@ public class EditAnimeFragment extends Fragment { public void onResponse(Call call, Response response) { Log.d("ACTIVITY_DELETE", "Actividad de eliminación registrada"); - if (getParentFragment() instanceof FragmentProfile) { ((FragmentProfile) getParentFragment()).loadActivity(); } diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/editmanga/EditMangaFragment.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/editmanga/EditMangaFragment.java index 9fb0c63..8b0b472 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/editmanga/EditMangaFragment.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/editmanga/EditMangaFragment.java @@ -61,30 +61,18 @@ public class EditMangaFragment extends Fragment { fillFields(); - String[] statusArray = getResources().getStringArray(R.array.manga_status_array); - String[] typeArray = getResources().getStringArray(R.array.manga_type_array); - - ArrayAdapter statusAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, statusArray); + ArrayAdapter statusAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, + getResources().getStringArray(R.array.manga_status_array)); statusAdapter.setDropDownViewResource(R.layout.item_spinner); spinnerStatus.setAdapter(statusAdapter); - ArrayAdapter typeAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, typeArray); + ArrayAdapter typeAdapter = new ArrayAdapter<>(requireContext(), R.layout.item_spinner, + getResources().getStringArray(R.array.manga_type_array)); typeAdapter.setDropDownViewResource(R.layout.item_spinner); spinnerType.setAdapter(typeAdapter); - for (int i = 0; i < statusArray.length; i++) { - if (statusArray[i].equalsIgnoreCase(manga.getStatus())) { - spinnerStatus.setSelection(i); - break; - } - } - - for (int i = 0; i < typeArray.length; i++) { - if (typeArray[i].equalsIgnoreCase(manga.getType())) { - spinnerType.setSelection(i); - break; - } - } + setSpinnerSelection(spinnerStatus, manga.getStatus()); + setSpinnerSelection(spinnerType, manga.getType()); buttonSave.setOnClickListener(v -> saveChanges()); @@ -104,6 +92,15 @@ public class EditMangaFragment extends Fragment { editTextProgress.setText(String.valueOf(manga.getProgress())); } + private void setSpinnerSelection(Spinner spinner, String value) { + for (int i = 0; i < spinner.getCount(); i++) { + if (spinner.getItemAtPosition(i).toString().equalsIgnoreCase(value)) { + spinner.setSelection(i); + break; + } + } + } + private void saveChanges() { if (manga == null) { Toast.makeText(requireContext(), "Error: Manga no cargado", Toast.LENGTH_SHORT).show(); @@ -128,14 +125,11 @@ public class EditMangaFragment extends Fragment { return; } - String status = spinnerStatus.getSelectedItem().toString(); - String type = spinnerType.getSelectedItem().toString(); - manga.setTitle(title); manga.setScore(score); manga.setProgress(progress); - manga.setStatus(status); - manga.setType(type); + manga.setStatus(spinnerStatus.getSelectedItem().toString()); + manga.setType(spinnerType.getSelectedItem().toString()); api.updateManga(manga.getId(), manga).enqueue(new Callback() { @Override @@ -144,7 +138,7 @@ public class EditMangaFragment extends Fragment { if (response.isSuccessful() && response.body() != null) { Toast.makeText(requireContext(), response.body().getMessage(), Toast.LENGTH_SHORT).show(); - registrarActividad(manga.getTitle(), manga.getImageUrl()); + registrarActividad("update de un manga", manga.getTitle(), manga.getImageUrl()); requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE) .edit().putBoolean("refresh_profile", true).apply(); requireActivity().getSupportFragmentManager().popBackStack(); @@ -162,7 +156,7 @@ public class EditMangaFragment extends Fragment { } private void deleteManga() { - registrarActividadEliminacionManga(manga.getTitle(), manga.getImageUrl()); + registrarActividad("eliminó un manga", manga.getTitle(), manga.getImageUrl()); api.deleteManga(manga.getId()).enqueue(new Callback() { @Override @@ -173,6 +167,12 @@ public class EditMangaFragment extends Fragment { Toast.makeText(requireContext(), "Manga eliminado", Toast.LENGTH_SHORT).show(); requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE) .edit().putBoolean("refresh_profile", true).apply(); + + Bundle result = new Bundle(); + result.putBoolean("manga_deleted", true); + result.putInt("manga_id", manga.getId()); + getParentFragmentManager().setFragmentResult("manga_delete_request", result); + requireActivity().getSupportFragmentManager().popBackStack(); } else { Toast.makeText(requireContext(), "Error al eliminar", Toast.LENGTH_SHORT).show(); @@ -187,44 +187,20 @@ public class EditMangaFragment extends Fragment { }); } - private void registrarActividad(String titulo, String imagen) { + private void registrarActividad(String action, String titulo, String imagen) { int userId = requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE).getInt("user_id", -1); if (userId == -1) return; Map actividad = new HashMap<>(); actividad.put("userId", userId); - actividad.put("action", "update de un manga"); + actividad.put("action", action); actividad.put("mediaTitle", titulo); actividad.put("imageUrl", imagen); api.postActivity(actividad).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { - Log.d("ACTIVITY_DELETE", "Actividad de edicion registrada"); - } - - @Override - public void onFailure(Call call, Throwable t) { - Log.e("ACTIVITY_DELETE", "Error al registrar actividad: " + t.getMessage(), t); - } - }); - } - - private void registrarActividadEliminacionManga(String titulo, String imagen) { - int userId = requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE).getInt("user_id", -1); - if (userId == -1) return; - - Map actividad = new HashMap<>(); - actividad.put("userId", userId); - actividad.put("action", "eliminó un manga"); - actividad.put("mediaTitle", titulo); - actividad.put("imageUrl", imagen); - - api.postActivity(actividad).enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - Log.d("ACTIVITY_DELETE", "Actividad de eliminación registrada"); - + Log.d("ACTIVITY", "Actividad registrada: " + action); if (getParentFragment() instanceof FragmentProfile) { ((FragmentProfile) getParentFragment()).loadActivity(); } @@ -232,10 +208,8 @@ public class EditMangaFragment extends Fragment { @Override public void onFailure(Call call, Throwable t) { - Log.e("ACTIVITY_DELETE", "Error al registrar actividad: " + t.getMessage(), t); + Log.e("ACTIVITY", "Error al registrar actividad: " + t.getMessage(), t); } }); } - - } diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/home/FragmentHome.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/home/FragmentHome.java index 69edc41..9216b48 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/home/FragmentHome.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/home/FragmentHome.java @@ -1,6 +1,4 @@ -// FragmentHome.java package com.santiparra.yomitrack.ui.fragments.home; - import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; @@ -15,13 +13,11 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - import com.bumptech.glide.Glide; import com.google.gson.JsonObject; import com.santiparra.yomitrack.R; diff --git a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/manga_list/FragmentManga.java b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/manga_list/FragmentManga.java index ec213df..73234f0 100644 --- a/app/src/main/java/com/santiparra/yomitrack/ui/fragments/manga_list/FragmentManga.java +++ b/app/src/main/java/com/santiparra/yomitrack/ui/fragments/manga_list/FragmentManga.java @@ -70,6 +70,7 @@ public class FragmentManga extends Fragment { initViews(view); setupListeners(); setupRecyclerView(); + setupResultListeners(); api = ApiClient.getClient().create(ApiService.class); SharedPreferences prefs = requireContext().getSharedPreferences("user_session", Context.MODE_PRIVATE); @@ -79,10 +80,9 @@ public class FragmentManga extends Fragment { Toast.makeText(getContext(), "Error: sesión no iniciada", Toast.LENGTH_SHORT).show(); return; } - // Mostrar el nombre del usuario + String username = prefs.getString("username", "Usuario"); - TextView textViewUsername = view.findViewById(R.id.textViewUsername); - textViewUsername.setText(username); + ((TextView) view.findViewById(R.id.textViewUsername)).setText(username); setViewType(currentViewType); loadMoreMangas(currentPage); @@ -151,15 +151,37 @@ public class FragmentManga extends Fragment { }); } + private void setupResultListeners() { + getParentFragmentManager().setFragmentResultListener("manga_add_request", this, (key, bundle) -> { + if (bundle.getBoolean("manga_added", false)) { + currentPage = 1; + mangaList.clear(); + adapter.updateList(new ArrayList<>()); + loadMoreMangas(currentPage); + } + }); + + getParentFragmentManager().setFragmentResultListener("manga_delete_request", this, (key, bundle) -> { + int deletedId = bundle.getInt("manga_id", -1); + if (deletedId != -1) { + for (int i = 0; i < mangaList.size(); i++) { + if (mangaList.get(i).getId() == deletedId) { + mangaList.remove(i); + adapter.notifyItemRemoved(i); + break; + } + } + } + }); + } + private void setViewType(int viewType) { currentViewType = viewType; - if (viewType == MangaAdapter.VIEW_LARGE) { recyclerView.setLayoutManager(new GridLayoutManager(requireContext(), 2)); } else { recyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); } - adapter = new MangaAdapter(mangaList, viewType, this::showEditDialog, this::deleteManga); recyclerView.setAdapter(adapter); } @@ -187,12 +209,15 @@ public class FragmentManga extends Fragment { @Override public void onResponse(Call call, Response response) { if (!isAdded()) return; - if (response.isSuccessful()) { + for (int i = 0; i < mangaList.size(); i++) { + if (mangaList.get(i).getId() == manga.getId()) { + mangaList.remove(i); + adapter.notifyItemRemoved(i); + break; + } + } Toast.makeText(requireContext(), response.body() != null ? response.body().getMessage() : "Manga eliminado", Toast.LENGTH_SHORT).show(); - currentPage = 1; - mangaList.clear(); - loadMoreMangas(currentPage); } else { Toast.makeText(requireContext(), "Error al eliminar", Toast.LENGTH_SHORT).show(); } @@ -214,7 +239,6 @@ public class FragmentManga extends Fragment { @Override public void onResponse(Call call, Response response) { if (!isAdded()) return; - if (response.isSuccessful() && response.body() != null) { List nuevos = response.body().getData(); mangaList.addAll(nuevos); diff --git a/app/src/main/res.zip b/app/src/main/res.zip index b332b7faa0625e075cc37676d54ffc82f607ff87..3106affe6bcb22c9122ee9dd520f6c5829b4ed22 100644 GIT binary patch delta 5077 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 diff --git a/app/src/main/res/drawable/progress_bar.xml b/app/src/main/res/drawable/progress_bar.xml index 3cc0b0b..9fdd773 100644 --- a/app/src/main/res/drawable/progress_bar.xml +++ b/app/src/main/res/drawable/progress_bar.xml @@ -7,11 +7,10 @@ - - + diff --git a/app/src/main/res/layout/fragment_forgot_password.xml b/app/src/main/res/layout/fragment_forgot_password.xml index b504833..9467c79 100644 --- a/app/src/main/res/layout/fragment_forgot_password.xml +++ b/app/src/main/res/layout/fragment_forgot_password.xml @@ -59,13 +59,6 @@ android:textColor="@android:color/white" android:layout_marginBottom="12dp" /> -