WIP - swipe actions

This commit is contained in:
Owen LeJeune
2023-05-27 11:25:33 -04:00
parent 077e51f3cd
commit f24e0d2dfb
5 changed files with 68 additions and 18 deletions

View File

@@ -148,6 +148,8 @@ class TmdbClient: KoinComponent {
builder.header("Authorization", "Bearer ${BuildConfig.TMDB_Api_v4Key}") builder.header("Authorization", "Bearer ${BuildConfig.TMDB_Api_v4Key}")
} else { } else {
builder.header("Authorization", "Bearer ${SessionManager.currentSession!!.accessToken}") builder.header("Authorization", "Bearer ${SessionManager.currentSession!!.accessToken}")
}
if (shouldIncludeLanguageParam(url.encodedPathSegments)) {
val locale = Locale.current val locale = Locale.current
val languageCode = "${locale.language}-${locale.region}" val languageCode = "${locale.language}-${locale.region}"
val languageParam = QueryParam("language", languageCode) val languageParam = QueryParam("language", languageCode)
@@ -155,10 +157,23 @@ class TmdbClient: KoinComponent {
val newUrl = url.newBuilder().addQueryParams(languageParam).build() val newUrl = url.newBuilder().addQueryParams(languageParam).build()
builder.url(newUrl) builder.url(newUrl)
} }
if (url.encodedPathSegments.contains("list")) {
builder.header("Content-Type", "application/json;charset=utf8")
}
} }
return chain.proceed(builder.build()) return chain.proceed(builder.build())
} }
private fun shouldIncludeLanguageParam(urlSegments: List<String>): Boolean {
val ignoredRoutes = listOf("list", "auth")
for (route in ignoredRoutes) {
if (urlSegments.contains(route)) {
return false
}
}
return true
}
} }
} }

View File

@@ -14,10 +14,10 @@ interface ListV4Api {
): Response<MediaList> ): Response<MediaList>
@POST("list") @POST("list")
suspend fun createList(body: CreateListBody): Response<CreateListResponse> suspend fun createList(@Body body: CreateListBody): Response<CreateListResponse>
@PUT("list/{id}") @PUT("list/{id}")
suspend fun updateList(@Path("id") listId: Int, body: ListUpdateBody): Response<StatusResponse> suspend fun updateList(@Path("id") listId: Int, @Body body: ListUpdateBody): Response<StatusResponse>
@GET("list/{id}/clear") @GET("list/{id}/clear")
suspend fun clearList(@Path("id") listId: Int): Response<ClearListResponse> suspend fun clearList(@Path("id") listId: Int): Response<ClearListResponse>
@@ -26,13 +26,13 @@ interface ListV4Api {
suspend fun deleteList(@Path("id") listId: Int): Response<StatusResponse> suspend fun deleteList(@Path("id") listId: Int): Response<StatusResponse>
@POST("list/{id}/items") @POST("list/{id}/items")
suspend fun addItemsToList(@Path("id") listId: Int, body: AddToListBody): Response<AddToListResponse> suspend fun addItemsToList(@Path("id") listId: Int, @Body body: AddToListBody): Response<AddToListResponse>
@PUT("list/{id}/items") @PUT("list/{id}/items")
suspend fun updateListItems(@Path("id") listId: Int, body: UpdateListItemBody): Response<AddToListResponse> suspend fun updateListItems(@Path("id") listId: Int, @Body body: UpdateListItemBody): Response<AddToListResponse>
@DELETE("list/{id}/items") @HTTP(method = "DELETE", path = "/list/{id}/items", hasBody = true)
suspend fun deleteListItems(@Path("id") listId: Int, body: DeleteListItemsBody): Response<AddToListResponse> suspend fun deleteListItems(@Path("id") listId: Int, @Body body: DeleteListItemsBody): Response<AddToListResponse>
@GET("list/{id}/item_status") @GET("list/{id}/item_status")
suspend fun getListItemStatus(@Path("id") listId: Int, @Query("media_id") mediaId: Int, @Query("media_type") mediaType: String): Response<ListItemStatusResponse> suspend fun getListItemStatus(@Path("id") listId: Int, @Query("media_id") mediaId: Int, @Query("media_type") mediaType: String): Response<ListItemStatusResponse>

View File

@@ -4,6 +4,10 @@ import com.google.gson.annotations.SerializedName
import com.owenlejeune.tvtime.ui.screens.main.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.MediaViewType
class DeleteListItemsBody( class DeleteListItemsBody(
@SerializedName("items") val items: List<DeleteListItemsItem>
)
class DeleteListItemsItem(
@SerializedName("media_id") val id: Int, @SerializedName("media_id") val id: Int,
@SerializedName("media_type") val mediaType: MediaViewType @SerializedName("media_type") val mediaType: MediaViewType
) )

View File

@@ -1,6 +1,7 @@
package com.owenlejeune.tvtime.ui.screens.main package com.owenlejeune.tvtime.ui.screens.main
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.compose.animation.rememberSplineBasedDecay import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.* 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.MarkAsFavoriteBody
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.WatchlistBody 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.ListV4Service
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.ListItem import com.owenlejeune.tvtime.api.tmdb.api.v4.model.*
import com.owenlejeune.tvtime.api.tmdb.api.v4.model.MediaList
import com.owenlejeune.tvtime.extensions.WindowSizeClass import com.owenlejeune.tvtime.extensions.WindowSizeClass
import com.owenlejeune.tvtime.extensions.unlessEmpty import com.owenlejeune.tvtime.extensions.unlessEmpty
import com.owenlejeune.tvtime.preferences.AppPreferences import com.owenlejeune.tvtime.preferences.AppPreferences
@@ -63,10 +63,10 @@ fun ListDetailView(
) { ) {
val service = ListV4Service() val service = ListV4Service()
val listItem = remember { mutableStateOf<MediaList?>(null) } val parentList = remember { mutableStateOf<MediaList?>(null) }
itemId?.let { itemId?.let {
if (listItem.value == null) { if (parentList.value == null) {
fetchList(itemId, service, listItem) fetchList(itemId, service, parentList)
} }
} }
@@ -86,7 +86,7 @@ fun ListDetailView(
scrolledContainerColor = MaterialTheme.colorScheme.background, scrolledContainerColor = MaterialTheme.colorScheme.background,
titleContentColor = MaterialTheme.colorScheme.primary titleContentColor = MaterialTheme.colorScheme.primary
), ),
title = { Text(text = listItem.value?.name ?: "") }, title = { Text(text = parentList.value?.name ?: "") },
navigationIcon = { navigationIcon = {
IconButton( IconButton(
onClick = { appNavController.popBackStack() } onClick = { appNavController.popBackStack() }
@@ -102,7 +102,7 @@ fun ListDetailView(
} }
) { innerPadding -> ) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) { Box(modifier = Modifier.padding(innerPadding)) {
listItem.value?.let { mediaList -> parentList.value?.let { mediaList ->
Column( Column(
modifier = Modifier modifier = Modifier
.padding(all = 12.dp) .padding(all = 12.dp)
@@ -114,7 +114,8 @@ fun ListDetailView(
mediaList.results.forEach { listItem -> mediaList.results.forEach { listItem ->
ListItemView( ListItemView(
appNavController = appNavController, appNavController = appNavController,
listItem = listItem listItem = listItem,
list = parentList
) )
} }
} }
@@ -252,16 +253,22 @@ private fun RowScope.OverviewStatCard(
@Composable @Composable
private fun ListItemView( private fun ListItemView(
appNavController: NavController, appNavController: NavController,
listItem: ListItem listItem: ListItem,
list: MutableState<MediaList?>
) { ) {
val context = LocalContext.current
RevealSwipe ( RevealSwipe (
directions = setOf(RevealDirection.EndToStart), directions = setOf(RevealDirection.EndToStart),
hiddenContentEnd = { hiddenContentEnd = {
IconButton( IconButton(
modifier = Modifier.padding(horizontal = 15.dp), 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( Icon(
imageVector = Icons.Filled.Delete, imageVector = Icons.Filled.Delete,
@@ -496,4 +503,27 @@ private fun fetchList(
} }
} }
} }
}
private fun removeItemFromList(
itemId: Int,
itemType: MediaViewType,
service: ListV4Service,
list: MutableState<MediaList?>
) {
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())
}
}
} }

View File

@@ -290,6 +290,7 @@ object SessionManager: KoinComponent {
val Rated get() = arrayOf(RatedMovies, RatedTv, RatedEpisodes) val Rated get() = arrayOf(RatedMovies, RatedTv, RatedEpisodes)
val Favorites get() = arrayOf(FavoriteMovies, FavoriteTv) val Favorites get() = arrayOf(FavoriteMovies, FavoriteTv)
val Watchlist get() = arrayOf(WatchlistMovies, WatchlistTv) val Watchlist get() = arrayOf(WatchlistMovies, WatchlistTv)
val List get() = arrayOf(Changed.Lists)
} }
} }
} }