diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt index b82a8b5..b66c4ac 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/TmdbClient.kt @@ -148,6 +148,8 @@ class TmdbClient: KoinComponent { builder.header("Authorization", "Bearer ${BuildConfig.TMDB_Api_v4Key}") } else { builder.header("Authorization", "Bearer ${SessionManager.currentSession!!.accessToken}") + } + if (shouldIncludeLanguageParam(url.encodedPathSegments)) { val locale = Locale.current val languageCode = "${locale.language}-${locale.region}" val languageParam = QueryParam("language", languageCode) @@ -155,10 +157,23 @@ class TmdbClient: KoinComponent { val newUrl = url.newBuilder().addQueryParams(languageParam).build() builder.url(newUrl) } + if (url.encodedPathSegments.contains("list")) { + builder.header("Content-Type", "application/json;charset=utf8") + } } return chain.proceed(builder.build()) } + + private fun shouldIncludeLanguageParam(urlSegments: List): Boolean { + val ignoredRoutes = listOf("list", "auth") + for (route in ignoredRoutes) { + if (urlSegments.contains(route)) { + return false + } + } + return true + } } } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v4/ListV4Api.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v4/ListV4Api.kt index 811768f..bfb3e7b 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v4/ListV4Api.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v4/ListV4Api.kt @@ -14,10 +14,10 @@ interface ListV4Api { ): Response @POST("list") - suspend fun createList(body: CreateListBody): Response + suspend fun createList(@Body body: CreateListBody): Response @PUT("list/{id}") - suspend fun updateList(@Path("id") listId: Int, body: ListUpdateBody): Response + suspend fun updateList(@Path("id") listId: Int, @Body body: ListUpdateBody): Response @GET("list/{id}/clear") suspend fun clearList(@Path("id") listId: Int): Response @@ -26,13 +26,13 @@ interface ListV4Api { suspend fun deleteList(@Path("id") listId: Int): Response @POST("list/{id}/items") - suspend fun addItemsToList(@Path("id") listId: Int, body: AddToListBody): Response + suspend fun addItemsToList(@Path("id") listId: Int, @Body body: AddToListBody): Response @PUT("list/{id}/items") - suspend fun updateListItems(@Path("id") listId: Int, body: UpdateListItemBody): Response + suspend fun updateListItems(@Path("id") listId: Int, @Body body: UpdateListItemBody): Response - @DELETE("list/{id}/items") - suspend fun deleteListItems(@Path("id") listId: Int, body: DeleteListItemsBody): Response + @HTTP(method = "DELETE", path = "/list/{id}/items", hasBody = true) + suspend fun deleteListItems(@Path("id") listId: Int, @Body body: DeleteListItemsBody): Response @GET("list/{id}/item_status") suspend fun getListItemStatus(@Path("id") listId: Int, @Query("media_id") mediaId: Int, @Query("media_type") mediaType: String): Response diff --git a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v4/model/DeleteListItemsBody.kt b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v4/model/DeleteListItemsBody.kt index 215d460..658b92c 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v4/model/DeleteListItemsBody.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/api/tmdb/api/v4/model/DeleteListItemsBody.kt @@ -4,6 +4,10 @@ import com.google.gson.annotations.SerializedName import com.owenlejeune.tvtime.ui.screens.main.MediaViewType class DeleteListItemsBody( + @SerializedName("items") val items: List +) + +class DeleteListItemsItem( @SerializedName("media_id") val id: Int, @SerializedName("media_type") val mediaType: MediaViewType ) diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/ListDetailView.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/ListDetailView.kt index 94f0368..78cad73 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/ListDetailView.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/ListDetailView.kt @@ -1,6 +1,7 @@ package com.owenlejeune.tvtime.ui.screens.main import android.content.Context import android.content.Intent +import android.util.Log import android.widget.Toast import androidx.compose.animation.rememberSplineBasedDecay import androidx.compose.foundation.* @@ -35,8 +36,7 @@ import com.owenlejeune.tvtime.api.tmdb.api.v3.AccountService import com.owenlejeune.tvtime.api.tmdb.api.v3.model.MarkAsFavoriteBody import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchlistBody import com.owenlejeune.tvtime.api.tmdb.api.v4.ListV4Service -import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem -import com.owenlejeune.tvtime.api.tmdb.api.v4.model.MediaList +import com.owenlejeune.tvtime.api.tmdb.api.v4.model.* import com.owenlejeune.tvtime.extensions.WindowSizeClass import com.owenlejeune.tvtime.extensions.unlessEmpty import com.owenlejeune.tvtime.preferences.AppPreferences @@ -63,10 +63,10 @@ fun ListDetailView( ) { val service = ListV4Service() - val listItem = remember { mutableStateOf(null) } + val parentList = remember { mutableStateOf(null) } itemId?.let { - if (listItem.value == null) { - fetchList(itemId, service, listItem) + if (parentList.value == null) { + fetchList(itemId, service, parentList) } } @@ -86,7 +86,7 @@ fun ListDetailView( scrolledContainerColor = MaterialTheme.colorScheme.background, titleContentColor = MaterialTheme.colorScheme.primary ), - title = { Text(text = listItem.value?.name ?: "") }, + title = { Text(text = parentList.value?.name ?: "") }, navigationIcon = { IconButton( onClick = { appNavController.popBackStack() } @@ -102,7 +102,7 @@ fun ListDetailView( } ) { innerPadding -> Box(modifier = Modifier.padding(innerPadding)) { - listItem.value?.let { mediaList -> + parentList.value?.let { mediaList -> Column( modifier = Modifier .padding(all = 12.dp) @@ -114,7 +114,8 @@ fun ListDetailView( mediaList.results.forEach { listItem -> ListItemView( appNavController = appNavController, - listItem = listItem + listItem = listItem, + list = parentList ) } } @@ -252,16 +253,22 @@ private fun RowScope.OverviewStatCard( @Composable private fun ListItemView( appNavController: NavController, - listItem: ListItem + listItem: ListItem, + list: MutableState ) { - val context = LocalContext.current - RevealSwipe ( directions = setOf(RevealDirection.EndToStart), hiddenContentEnd = { IconButton( modifier = Modifier.padding(horizontal = 15.dp), - onClick = { Toast.makeText(context, "Remove from list", Toast.LENGTH_SHORT).show() } + onClick = { + removeItemFromList( + itemId = listItem.id, + itemType = listItem.mediaType, + service = ListV4Service(), + list = list + ) + } ) { Icon( imageVector = Icons.Filled.Delete, @@ -496,4 +503,27 @@ private fun fetchList( } } } +} + +private fun removeItemFromList( + itemId: Int, + itemType: MediaViewType, + service: ListV4Service, + list: MutableState +) { + CoroutineScope(Dispatchers.IO).launch { + val listId = list.value?.id ?: 0 + val removeItem = DeleteListItemsItem(itemId, itemType) + val result = service.deleteListItems(listId, DeleteListItemsBody(listOf(removeItem))) + if (result.isSuccessful) { + SessionManager.currentSession?.refresh(SessionManager.Session.Changed.List) + service.getList(listId).body()?.let { + withContext(Dispatchers.Main) { + list.value = it + } + } + } else { + Log.w("RemoveListItemError", result.toString()) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt b/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt index 267e12a..baf3fd3 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt @@ -290,6 +290,7 @@ object SessionManager: KoinComponent { val Rated get() = arrayOf(RatedMovies, RatedTv, RatedEpisodes) val Favorites get() = arrayOf(FavoriteMovies, FavoriteTv) val Watchlist get() = arrayOf(WatchlistMovies, WatchlistTv) + val List get() = arrayOf(Changed.Lists) } } }