custom dropdown menu + setup account api service

This commit is contained in:
Owen LeJeune
2022-03-10 13:22:57 -05:00
parent 0ed5fa88f1
commit 45020832ff
24 changed files with 444 additions and 55 deletions

View File

@@ -0,0 +1,74 @@
package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.*
import retrofit2.Response
import retrofit2.http.*
interface AccountApi {
@GET("account")
suspend fun getAccountDetails(): Response<AccountDetails>
@GET("account/{id}/lists")
suspend fun getLists(
@Path("id") id: Int,
@Query("page") page: Int
): Response<AccountListResponse>
@GET("account/{id}/favorite/movies")
suspend fun getFavoriteMovies(
@Path("id") id: Int,
@Query("page") page: Int
): Response<FavoriteMediaResponse<FavoriteMovie>>
@GET("account/{id}/favorite/tv")
suspend fun getFavoriteTvShows(
@Path("id") id: Int,
@Query("page") page: Int
): Response<FavoriteMediaResponse<FavoriteTvSeries>>
// @Headers("Content-Type: application/json;charset=utf-8")
@POST("account/{id}/favorite")
suspend fun markAsFavorite(
@Path("id") id: Int,
@Body body: MarkAsFavoriteBody
): Response<StatusResponse>
@GET("account/{id}/rated/movies")
suspend fun getRatedMovies(
@Path("id") id: Int,
@Query("page") page: Int
): Response<RatedMediaResponse<RatedMovie>>
@GET("account/{id}/rated/tv")
suspend fun getRatedTvShows(
@Path("id") id: Int,
@Query("page") page: Int
): Response<RatedMediaResponse<RatedTv>>
@GET("account/{id}/rated/tv/episodes")
suspend fun getRatedTvEpisodes(
@Path("id") id: Int,
@Query("page") page: Int
): Response<RatedMediaResponse<RatedEpisode>>
@GET("account/{id}/watchlist/movies")
suspend fun getMovieWatchlist(
@Path("id") id: Int,
@Query("page") page: Int
): Response<WatchlistResponse<WatchlistMovie>>
@GET("account/{id}/watchlist/tv")
suspend fun getTvWatchlist(
@Path("id") id: Int,
@Query("page") page: Int
): Response<WatchlistResponse<WatchlistTvSeries>>
// @Headers("Content-Type: application/json;charset=utf-8")
@POST("account/{id}/watchlist")
suspend fun addToWatchlist(
@Path("id") id: Int,
@Body body: WatchlistBody
): Response<StatusResponse>
}

View File

@@ -0,0 +1,50 @@
package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.*
import retrofit2.Response
class AccountService {
private val accountService by lazy { TmdbClient().createAccountService() }
suspend fun getAccountDetails(): Response<AccountDetails> {
return accountService.getAccountDetails()
}
suspend fun getFavoriteMovies(accountId: Int, page: Int = 1): Response<FavoriteMediaResponse<FavoriteMovie>> {
return accountService.getFavoriteMovies(accountId, page)
}
suspend fun getFavoriteTvShows(accountId: Int, page: Int = 1): Response<FavoriteMediaResponse<FavoriteTvSeries>> {
return accountService.getFavoriteTvShows(accountId, page)
}
suspend fun markAsFavorite(accountId: Int, body: MarkAsFavoriteBody): Response<StatusResponse> {
return accountService.markAsFavorite(accountId, body)
}
suspend fun getRatedMovies(accountId: Int, page: Int = 1): Response<RatedMediaResponse<RatedMovie>> {
return accountService.getRatedMovies(accountId, page)
}
suspend fun getRatedTvShows(accountId: Int, page: Int = 1): Response<RatedMediaResponse<RatedTv>> {
return accountService.getRatedTvShows(accountId, page)
}
suspend fun getRatedTvEpisodes(accountId: Int, page: Int = 1): Response<RatedMediaResponse<RatedEpisode>> {
return accountService.getRatedTvEpisodes(accountId, page)
}
suspend fun getMovieWatchlist(accountId: Int, page: Int = 1): Response<WatchlistResponse<WatchlistMovie>> {
return accountService.getMovieWatchlist(accountId, page)
}
suspend fun getTvWatchlist(accountId: Int, page: Int = 1): Response<WatchlistResponse<WatchlistTvSeries>> {
return accountService.getTvWatchlist(accountId, page)
}
suspend fun addToWatchlist(accountId: Int, body: WatchlistBody): Response<StatusResponse> {
return accountService.addToWatchlist(accountId, body)
}
}

View File

@@ -17,9 +17,9 @@ interface DetailService {
suspend fun getReviews(id: Int): Response<ReviewResponse> suspend fun getReviews(id: Int): Response<ReviewResponse>
suspend fun postRating(id: Int, ratingBody: RatingBody): Response<RatingResponse> suspend fun postRating(id: Int, ratingBody: RatingBody): Response<StatusResponse>
suspend fun deleteRating(id: Int): Response<RatingResponse> suspend fun deleteRating(id: Int): Response<StatusResponse>
suspend fun getKeywords(id: Int): Response<KeywordsResponse> suspend fun getKeywords(id: Int): Response<KeywordsResponse>

View File

@@ -0,0 +1,9 @@
package com.owenlejeune.tvtime.api.tmdb
import com.google.gson.annotations.SerializedName
class MarkAsFavoriteBody(
@SerializedName("media_type") val mediaType: String, // media type
@SerializedName("media_id") val mediaId: Int,
@SerializedName("favorite") val isFavorite: Boolean
)

View File

@@ -47,25 +47,25 @@ interface MoviesApi {
@Path("id") id: Int, @Path("id") id: Int,
@Query("guest_session_id") guestSessionId: String, @Query("guest_session_id") guestSessionId: String,
@Body ratingBody: RatingBody @Body ratingBody: RatingBody
): Response<RatingResponse> ): Response<StatusResponse>
@POST("movie/{id}/rating") @POST("movie/{id}/rating")
suspend fun postMovieRatingAsUser( suspend fun postMovieRatingAsUser(
@Path("id") id: Int, @Path("id") id: Int,
@Query("session_id") sessionId: String, @Query("session_id") sessionId: String,
@Body ratingBody: RatingBody @Body ratingBody: RatingBody
): Response<RatingResponse> ): Response<StatusResponse>
@DELETE("movie/{id}/rating") @DELETE("movie/{id}/rating")
suspend fun deleteMovieReviewAsGuest( suspend fun deleteMovieReviewAsGuest(
@Path("id") id: Int, @Path("id") id: Int,
@Query("guest_session_id") guestSessionId: String @Query("guest_session_id") guestSessionId: String
): Response<RatingResponse> ): Response<StatusResponse>
@DELETE("movie/{id}/rating") @DELETE("movie/{id}/rating")
suspend fun deleteMovieReviewAsUser( suspend fun deleteMovieReviewAsUser(
@Path("id") id: Int, @Path("id") id: Int,
@Query("session_id") sessionId: String @Query("session_id") sessionId: String
): Response<RatingResponse> ): Response<StatusResponse>
} }

View File

@@ -53,7 +53,7 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
return movieService.getReviews(id) return movieService.getReviews(id)
} }
override suspend fun postRating(id: Int, rating: RatingBody): Response<RatingResponse> { override suspend fun postRating(id: Int, rating: RatingBody): Response<StatusResponse> {
val session = SessionManager.currentSession ?: throw Exception("Session must not be null") val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
return if (!session.isAuthorized) { return if (!session.isAuthorized) {
movieService.postMovieRatingAsGuest(id, session.sessionId, rating) movieService.postMovieRatingAsGuest(id, session.sessionId, rating)
@@ -62,7 +62,7 @@ class MoviesService: KoinComponent, DetailService, HomePageService {
} }
} }
override suspend fun deleteRating(id: Int): Response<RatingResponse> { override suspend fun deleteRating(id: Int): Response<StatusResponse> {
val session = SessionManager.currentSession ?: throw Exception("Session must not be null") val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
return if (!session.isAuthorized) { return if (!session.isAuthorized) {
movieService.deleteMovieReviewAsGuest(id, session.sessionId) movieService.deleteMovieReviewAsGuest(id, session.sessionId)

View File

@@ -5,6 +5,7 @@ import com.owenlejeune.tvtime.BuildConfig
import com.owenlejeune.tvtime.api.Client import com.owenlejeune.tvtime.api.Client
import com.owenlejeune.tvtime.api.QueryParam import com.owenlejeune.tvtime.api.QueryParam
import com.owenlejeune.tvtime.extensions.addQueryParams import com.owenlejeune.tvtime.extensions.addQueryParams
import com.owenlejeune.tvtime.utils.SessionManager
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
@@ -43,6 +44,10 @@ class TmdbClient: KoinComponent {
return client.create(GuestSessionApi::class.java) return client.create(GuestSessionApi::class.java)
} }
fun createAccountService(): AccountApi {
return client.create(AccountApi::class.java)
}
private inner class TmdbInterceptor: Interceptor { private inner class TmdbInterceptor: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey) val apiParam = QueryParam("api_key", BuildConfig.TMDB_ApiKey)
@@ -51,7 +56,13 @@ class TmdbClient: KoinComponent {
val languageCode = "${locale.language}-${locale.region}" val languageCode = "${locale.language}-${locale.region}"
val languageParam = QueryParam("language", languageCode) val languageParam = QueryParam("language", languageCode)
val request = chain.addQueryParams(apiParam, languageParam) var sessionIdParam: QueryParam? = null
val segments = chain.request().url().encodedPathSegments()
if (segments.size > 1 && segments[1].equals("account") && SessionManager.currentSession?.isAuthorized == true) {
sessionIdParam = QueryParam("session_id", SessionManager.currentSession!!.sessionId)
}
val request = chain.addQueryParams(apiParam, languageParam, sessionIdParam)
return chain.proceed(request) return chain.proceed(request)
} }

View File

@@ -47,25 +47,25 @@ interface TvApi {
@Path("id") id: Int, @Path("id") id: Int,
@Query("guest_session_id") guestSessionId: String, @Query("guest_session_id") guestSessionId: String,
@Body ratingBody: RatingBody @Body ratingBody: RatingBody
): Response<RatingResponse> ): Response<StatusResponse>
@POST("tv/{id}/rating") @POST("tv/{id}/rating")
suspend fun postTvRatingAsUser( suspend fun postTvRatingAsUser(
@Path("id") id: Int, @Path("id") id: Int,
@Query("session_id") sessionId: String, @Query("session_id") sessionId: String,
@Body ratingBody: RatingBody @Body ratingBody: RatingBody
): Response<RatingResponse> ): Response<StatusResponse>
@DELETE("tv/{id}/rating") @DELETE("tv/{id}/rating")
suspend fun deleteTvReviewAsGuest( suspend fun deleteTvReviewAsGuest(
@Path("id") id: Int, @Path("id") id: Int,
@Query("guest_session_id") guestSessionId: String @Query("guest_session_id") guestSessionId: String
): Response<RatingResponse> ): Response<StatusResponse>
@DELETE("tv/{id}/rating") @DELETE("tv/{id}/rating")
suspend fun deleteTvReviewAsUser( suspend fun deleteTvReviewAsUser(
@Path("id") id: Int, @Path("id") id: Int,
@Query("session_id") sessionId: String @Query("session_id") sessionId: String
): Response<RatingResponse> ): Response<StatusResponse>
} }

View File

@@ -53,7 +53,7 @@ class TvService: KoinComponent, DetailService, HomePageService {
return service.getReviews(id) return service.getReviews(id)
} }
override suspend fun postRating(id: Int, rating: RatingBody): Response<RatingResponse> { override suspend fun postRating(id: Int, rating: RatingBody): Response<StatusResponse> {
val session = SessionManager.currentSession ?: throw Exception("Session must not be null") val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
return if (!session.isAuthorized) { return if (!session.isAuthorized) {
service.postTvRatingAsGuest(id, session.sessionId, rating) service.postTvRatingAsGuest(id, session.sessionId, rating)
@@ -62,7 +62,7 @@ class TvService: KoinComponent, DetailService, HomePageService {
} }
} }
override suspend fun deleteRating(id: Int): Response<RatingResponse> { override suspend fun deleteRating(id: Int): Response<StatusResponse> {
val session = SessionManager.currentSession ?: throw Exception("Session must not be null") val session = SessionManager.currentSession ?: throw Exception("Session must not be null")
return if (!session.isAuthorized) { return if (!session.isAuthorized) {
service.deleteTvReviewAsGuest(id, session.sessionId) service.deleteTvReviewAsGuest(id, session.sessionId)

View File

@@ -0,0 +1,21 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class AccountDetails(
@SerializedName("avatar") val avatar: Avatar,
@SerializedName("id") val id: Int,
@SerializedName("iso_639_1") val languageCode: String,
@SerializedName("iso_3166_1") val countryCode: String,
@SerializedName("name") val name: String,
@SerializedName("include_adult") val includeAdult: Boolean,
@SerializedName("username") val username: String
)
class Avatar(
@SerializedName("gravatar") val gravatar: Gravatar
)
class Gravatar(
@SerializedName("hash") val hash: String
)

View File

@@ -0,0 +1,13 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class AccountList(
@SerializedName("description") val description: String,
@SerializedName("favorite_count") val favoriteCount: Int,
@SerializedName("id") val id: Int,
@SerializedName("item_count") val itemCount: Int,
@SerializedName("iso_639_1") val languageCode: String,
@SerializedName("list_type") val listType: String, // media type
@SerializedName("name") val name: String
)

View File

@@ -0,0 +1,10 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class AccountListResponse(
@SerializedName("page") val page: Int,
@SerializedName("total_pages") val totalPages: Int,
@SerializedName("total_results") val totalResults: Int,
@SerializedName("results") val results: List<AccountList>
)

View File

@@ -0,0 +1,18 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
abstract class FavoriteMedia(
@SerializedName("poster_path") val posterPath: String?,
@SerializedName("popularity") val popularity: Float,
@SerializedName("id") val id: Int,
@SerializedName("backdrop_path") val backdropPath: String?,
@SerializedName("vote_average") val voteAverage: Float,
@SerializedName("overview") val overview: String,
@SerializedName("release_date", alternate = ["first_air_date"]) val releaseDate: String,
@SerializedName("genre_ids") val genreIds: List<Int>,
@SerializedName("original_language") val originalLanguage: String,
@SerializedName("vote_count") val voteCount: Int,
@SerializedName("title", alternate = ["name"]) val title: String,
@SerializedName("original_title", alternate = ["original_name"]) val originalTitle: String
)

View File

@@ -0,0 +1,10 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class FavoriteMediaResponse<T: FavoriteMedia>(
@SerializedName("page") val page: Int,
@SerializedName("results") val results: List<T>,
@SerializedName("total_pages") val totalPages: Int,
@SerializedName("total_results") val totalResults: Int
)

View File

@@ -0,0 +1,23 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class FavoriteMovie(
posterPath: String?,
popularity: Float,
id: Int,
backdropPath: String?,
voteAverage: Float,
overview: String,
releaseDate: String,
genreIds: List<Int>,
originalLanguage: String,
voteCount: Int,
title: String,
originalTitle: String,
@SerializedName("adult") val isAdult: Boolean,
@SerializedName("video") val isVideo: Boolean,
): FavoriteMedia(
posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate,
genreIds, originalLanguage, voteCount, title, originalTitle
)

View File

@@ -0,0 +1,22 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class FavoriteTvSeries(
posterPath: String?,
popularity: Float,
id: Int,
backdropPath: String?,
voteAverage: Float,
overview: String,
releaseDate: String,
genreIds: List<Int>,
originalLanguage: String,
voteCount: Int,
title: String,
originalTitle: String,
@SerializedName("origin_country") val originCountry: List<String>
): FavoriteMedia(
posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate,
genreIds, originalLanguage, voteCount, title, originalTitle
)

View File

@@ -2,7 +2,7 @@ package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
class RatingResponse( class StatusResponse(
@SerializedName("status_code") val statusCode: Int, @SerializedName("status_code") val statusCode: Int,
@SerializedName("status_message") val statusMessage: String @SerializedName("status_message") val statusMessage: String
) )

View File

@@ -0,0 +1,9 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class WatchlistBody(
@SerializedName("media_type") val mediaType: String, // media type
@SerializedName("media_id") val mediaId: Int,
@SerializedName("watchlist") val onWatchlist: Boolean
)

View File

@@ -0,0 +1,18 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
abstract class WatchlistMedia(
@SerializedName("poster_path") val posterPath: String?,
@SerializedName("popularity") val popularity: Float,
@SerializedName("id") val id: Int,
@SerializedName("backdrop_path") val backdropPath: String?,
@SerializedName("vote_average") val voteAverage: Float,
@SerializedName("overview") val overview: String,
@SerializedName("release_date", alternate = ["first_air_date"]) val releaseDate: String,
@SerializedName("genre_ids") val genreIds: List<Int>,
@SerializedName("original_language") val originalLanguage: String,
@SerializedName("vote_count") val voteCount: Int,
@SerializedName("title", alternate = ["name"]) val title: String,
@SerializedName("original_title", alternate = ["original_name"]) val originalTitle: String
)

View File

@@ -0,0 +1,23 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class WatchlistMovie(
posterPath: String?,
popularity: Float,
id: Int,
backdropPath: String?,
voteAverage: Float,
overview: String,
releaseDate: String,
genreIds: List<Int>,
originalLanguage: String,
voteCount: Int,
title: String,
originalTitle: String,
@SerializedName("adult") val isAdult: Boolean,
@SerializedName("video") val isVideo: Boolean,
): WatchlistMedia(
posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate,
genreIds, originalLanguage, voteCount, title, originalTitle
)

View File

@@ -0,0 +1,10 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class WatchlistResponse<T: WatchlistMedia>(
@SerializedName("page") val page: Int,
@SerializedName("results") val results: List<T>,
@SerializedName("total_pages") val totalPages: Int,
@SerializedName("total_results") val totalResults: Int
)

View File

@@ -0,0 +1,22 @@
package com.owenlejeune.tvtime.api.tmdb.model
import com.google.gson.annotations.SerializedName
class WatchlistTvSeries(
posterPath: String?,
popularity: Float,
id: Int,
backdropPath: String?,
voteAverage: Float,
overview: String,
releaseDate: String,
genreIds: List<Int>,
originalLanguage: String,
voteCount: Int,
title: String,
originalTitle: String,
@SerializedName("origin_country") val originCountry: List<String>
): WatchlistMedia(
posterPath, popularity, id, backdropPath, voteAverage, overview, releaseDate,
genreIds, originalLanguage, voteCount, title, originalTitle
)

View File

@@ -1,19 +1,26 @@
package com.owenlejeune.tvtime.ui.components package com.owenlejeune.tvtime.ui.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.AlertDialog import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.DropdownMenu import androidx.compose.material.DropdownMenu
import androidx.compose.material3.Divider
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
@Composable @Composable
@@ -36,7 +43,7 @@ fun TopAppBarDropdownMenu(
} }
DropdownMenu( DropdownMenu(
modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceVariant), modifier = Modifier.background(color = MaterialTheme.colorScheme.background),
expanded = expanded.value, expanded = expanded.value,
onDismissRequest = { expanded.value = false } onDismissRequest = { expanded.value = false }
) { ) {
@@ -44,6 +51,59 @@ fun TopAppBarDropdownMenu(
} }
} }
@Composable
fun CustomTopAppBarDropdownMenu(
icon: @Composable () -> Unit = {},
content: @Composable ColumnScope.(expanded: MutableState<Boolean>) -> Unit = {}
) {
val expanded = remember { mutableStateOf(false) }
Box(modifier = Modifier.wrapContentSize(Alignment.TopEnd)) {
IconButton(onClick = { expanded.value = true }) {
icon()
}
}
DropdownMenu(
expanded = expanded.value,
onDismissRequest = { expanded.value = false},
modifier = Modifier
.background(color = MaterialTheme.colorScheme.background)
.shadow(elevation = 0.dp),
offset = DpOffset(16.dp, 0.dp)
) {
content(this, expanded)
}
}
@Composable
fun CustomMenuItem(
text: String,
onClick: () -> Unit
) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(30.dp))
.fillMaxWidth()
.background(color = MaterialTheme.colorScheme.primary)
) {
Text(
text = text,
color = MaterialTheme.colorScheme.background,
modifier = Modifier
.padding(horizontal = 15.dp, vertical = 10.dp)
.clickable(onClick = onClick)
.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
}
@Composable
fun CustomMenuDivider() {
Divider(color = Color.Transparent, modifier = Modifier.padding(vertical = 2.dp))
}
@Composable @Composable
fun TopAppBarDialogMenu( fun TopAppBarDialogMenu(
icon: @Composable () -> Unit = {}, icon: @Composable () -> Unit = {},

View File

@@ -1,14 +1,11 @@
package com.owenlejeune.tvtime.ui.screens.tabs.bottom package com.owenlejeune.tvtime.ui.screens.tabs.bottom
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -31,9 +28,7 @@ import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.model.RatedMovie import com.owenlejeune.tvtime.api.tmdb.model.RatedMovie
import com.owenlejeune.tvtime.api.tmdb.model.RatedTopLevelMedia import com.owenlejeune.tvtime.api.tmdb.model.RatedTopLevelMedia
import com.owenlejeune.tvtime.api.tmdb.model.RatedTv import com.owenlejeune.tvtime.api.tmdb.model.RatedTv
import com.owenlejeune.tvtime.ui.components.RoundedLetterImage import com.owenlejeune.tvtime.ui.components.*
import com.owenlejeune.tvtime.ui.components.SignInDialog
import com.owenlejeune.tvtime.ui.components.TopAppBarDropdownMenu
import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem
import com.owenlejeune.tvtime.ui.navigation.ListFetchFun import com.owenlejeune.tvtime.ui.navigation.ListFetchFun
import com.owenlejeune.tvtime.ui.navigation.MainNavItem import com.owenlejeune.tvtime.ui.navigation.MainNavItem
@@ -175,7 +170,7 @@ private fun AccountDropdownMenu(
session: SessionManager.Session?, session: SessionManager.Session?,
lastSelectedOption: MutableState<String> lastSelectedOption: MutableState<String>
) { ) {
TopAppBarDropdownMenu( CustomTopAppBarDropdownMenu(
icon = { icon = {
when(session?.isAuthorized) { when(session?.isAuthorized) {
true -> { } true -> { }
@@ -198,14 +193,14 @@ private fun NoSessionMenuItems(
lastSelectedOption: MutableState<String> lastSelectedOption: MutableState<String>
) { ) {
val showSignInDialog = remember { mutableStateOf(false) } val showSignInDialog = remember { mutableStateOf(false) }
DropdownMenuItem( CustomMenuItem(
text = stringResource(id = R.string.action_sign_in),
onClick = { onClick = {
showSignInDialog.value = true showSignInDialog.value = true
},
modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceVariant)
) {
Text(text = stringResource(R.string.action_sign_in), color = MaterialTheme.colorScheme.onSurfaceVariant)
} }
)
CustomMenuDivider()
if (showSignInDialog.value) { if (showSignInDialog.value) {
SignInDialog(showDialog = showSignInDialog) { success -> SignInDialog(showDialog = showSignInDialog) { success ->
@@ -216,16 +211,13 @@ private fun NoSessionMenuItems(
} }
} }
Divider(color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(horizontal = 12.dp)) CustomMenuItem(
text = stringResource(R.string.action_sign_in_as_guest),
DropdownMenuItem(
onClick = { onClick = {
createGuestSession(lastSelectedOption) createGuestSession(lastSelectedOption)
expanded.value = false expanded.value = false
} }
) { )
Text(text = stringResource(R.string.action_sign_in_as_guest), color = MaterialTheme.colorScheme.onSurfaceVariant)
}
} }
@Composable @Composable
@@ -246,14 +238,10 @@ private fun GuestSessionMenuItems(
lastSelectedOption: MutableState<String> lastSelectedOption: MutableState<String>
) { ) {
val showSignInDialog = remember { mutableStateOf(false) } val showSignInDialog = remember { mutableStateOf(false) }
DropdownMenuItem( CustomMenuItem(
onClick = { text = stringResource(id = R.string.action_sign_in),
showSignInDialog.value = true onClick = { showSignInDialog.value = true }
}, )
modifier = Modifier.background(color = MaterialTheme.colorScheme.surfaceVariant)
) {
Text(text = stringResource(id = R.string.action_sign_in), color = MaterialTheme.colorScheme.onSurfaceVariant)
}
if (showSignInDialog.value) { if (showSignInDialog.value) {
SignInDialog(showDialog = showSignInDialog) { success -> SignInDialog(showDialog = showSignInDialog) { success ->
@@ -264,16 +252,15 @@ private fun GuestSessionMenuItems(
} }
} }
Divider(color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(horizontal = 12.dp)) CustomMenuDivider()
DropdownMenuItem( CustomMenuItem(
text = stringResource(id = R.string.action_sign_out),
onClick = { onClick = {
signOut(lastSelectedOption) signOut(lastSelectedOption)
expanded.value = false expanded.value = false
} }
) { )
Text(text = stringResource(id = R.string.action_sign_out), color = MaterialTheme.colorScheme.onSurfaceVariant)
}
} }
@Composable @Composable
@@ -287,14 +274,13 @@ private fun AuthorizedSessionMenuItems(
expanded: MutableState<Boolean>, expanded: MutableState<Boolean>,
lastSelectedOption: MutableState<String> lastSelectedOption: MutableState<String>
) { ) {
DropdownMenuItem( CustomMenuItem(
text = stringResource(id = R.string.action_sign_out),
onClick = { onClick = {
lastSelectedOption.value = ACCOUNT_SIGN_OUT lastSelectedOption.value = ACCOUNT_SIGN_OUT
expanded.value = false expanded.value = false
} }
) { )
Text(text = stringResource(id = R.string.action_sign_out), color = MaterialTheme.colorScheme.onSurfaceVariant)
}
} }
private fun createGuestSession(lastSelectedOption: MutableState<String>) { private fun createGuestSession(lastSelectedOption: MutableState<String>) {