From 88f693214077313baf3875d2149532bc55f091c6 Mon Sep 17 00:00:00 2001 From: Owen LeJeune Date: Mon, 5 Jun 2023 10:59:42 -0400 Subject: [PATCH] move account and sign in/out options to overlay menu --- .../com/owenlejeune/tvtime/MainActivity.kt | 129 +++--- .../owenlejeune/tvtime/ui/components/Menus.kt | 375 ++++++++++++------ .../tvtime/ui/components/Posters.kt | 11 +- .../tvtime/ui/components/Widgets.kt | 107 ++++- .../tvtime/ui/navigation/BottomNavItem.kt | 39 +- .../tvtime/ui/navigation/MainNavItem.kt | 2 + .../tvtime/ui/navigation/Routes.kt | 18 +- .../main/{AccountTab.kt => AccountView.kt} | 204 ++++------ .../tvtime/ui/screens/main/PeopleTab.kt | 12 +- .../tvtime/ui/screens/main/SettingsTab.kt | 2 +- .../tvtime/ui/screens/main/WebLinkView.kt | 12 +- .../com/owenlejeune/tvtime/ui/theme/Theme.kt | 8 +- .../tvtime/ui/views/HomeTabRecyclerAdapter.kt | 2 +- .../tvtime/utils/SessionManager.kt | 13 +- app/src/main/res/values/strings.xml | 1 + 15 files changed, 572 insertions(+), 363 deletions(-) rename app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/{AccountTab.kt => AccountView.kt} (69%) diff --git a/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt b/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt index 065c074..ff6b035 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/MainActivity.kt @@ -1,20 +1,18 @@ package com.owenlejeune.tvtime import android.os.Bundle -import android.util.Log import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent import androidx.compose.animation.rememberSplineBasedDecay import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Scaffold -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager @@ -23,7 +21,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController -import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -31,11 +28,17 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument +import androidx.navigation.navDeepLink +import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.kieronquinn.monetcompat.app.MonetCompatActivity import com.owenlejeune.tvtime.extensions.WindowSizeClass import com.owenlejeune.tvtime.extensions.navigateInBottomBar import com.owenlejeune.tvtime.extensions.rememberWindowSizeClass import com.owenlejeune.tvtime.preferences.AppPreferences +import com.owenlejeune.tvtime.ui.components.AccountIcon +import com.owenlejeune.tvtime.ui.components.ProfileMenuContainer +import com.owenlejeune.tvtime.ui.components.ProfileMenuDefaults +import com.owenlejeune.tvtime.ui.components.ProfileMenuOverlay import com.owenlejeune.tvtime.ui.navigation.BottomNavItem import com.owenlejeune.tvtime.ui.navigation.MainNavGraph import com.owenlejeune.tvtime.ui.navigation.MainNavItem @@ -89,8 +92,6 @@ class MainActivity : MonetCompatActivity() { preferences: AppPreferences = get(AppPreferences::class.java) ) { val navController = rememberNavController() - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentRoute = navBackStackEntry?.destination?.route val decayAnimationSpec = rememberSplineBasedDecay() val topAppBarScrollState = rememberTopAppBarScrollState() @@ -102,38 +103,58 @@ class MainActivity : MonetCompatActivity() { val appBarActions = remember { mutableStateOf<@Composable RowScope.() -> Unit>({}) } val fab = remember { mutableStateOf<@Composable () -> Unit>({}) } - Scaffold ( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - topBar = { - if (windowSize != WindowSizeClass.Expanded) { - TopBar( + val showProfileMenuOverlay = remember { mutableStateOf(false) } + val navigationIcon = @Composable { + AccountIcon( + modifier = Modifier.padding(horizontal = 12.dp), + size = 32.dp, + onClick = { showProfileMenuOverlay.value = true } + ) + } + + val defaultNavBarColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.08f).compositeOver(background = MaterialTheme.colorScheme.surface) + + ProfileMenuContainer( + appNavController = appNavController, + visible = showProfileMenuOverlay.value, + onDismissRequest = { showProfileMenuOverlay.value = false }, + colors = ProfileMenuDefaults.systemBarColors(navBarColor = defaultNavBarColor) + ) { + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + if (windowSize != WindowSizeClass.Expanded) { + TopBar( + appNavController = appNavController, + title = appBarTitle.value, + scrollBehavior = scrollBehavior, + appBarActions = appBarActions, + navigationIcon = navigationIcon + ) + } + }, + floatingActionButton = { + fab.value() + }, + bottomBar = { + if (windowSize != WindowSizeClass.Expanded) { + BottomNavBar(navController = navController) + } + } + ) { innerPadding -> + Box(modifier = Modifier.padding(innerPadding)) { + MainContent( + windowSize = windowSize, appNavController = appNavController, - title = appBarTitle.value, - scrollBehavior = scrollBehavior, - appBarActions = appBarActions + navController = navController, + fab = fab, + appBarTitle = appBarTitle, + appBarActions = appBarActions, + topBarScrollBehaviour = scrollBehavior, + mainNavStartRoute = mainNavStartRoute, + navigationIcon = navigationIcon ) } - }, - floatingActionButton = { - fab.value() - }, - bottomBar = { - if (windowSize != WindowSizeClass.Expanded) { - BottomNavBar(navController = navController) - } - } - ) { innerPadding -> - Box(modifier = Modifier.padding(innerPadding)) { - MainContent( - windowSize = windowSize, - appNavController = appNavController, - navController = navController, - fab = fab, - appBarTitle = appBarTitle, - appBarActions = appBarActions, - topBarScrollBehaviour = scrollBehavior, - mainNavStartRoute = mainNavStartRoute - ) } } } @@ -143,17 +164,9 @@ class MainActivity : MonetCompatActivity() { appNavController: NavHostController, title: @Composable () -> Unit, scrollBehavior: TopAppBarScrollBehavior, - appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}) + appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}), + navigationIcon: @Composable () -> Unit = {} ) { - val defaultAppBarActions: @Composable RowScope.() -> Unit = { - IconButton( - onClick = { - appNavController.navigate(MainNavItem.SettingsView.route) - } - ) { - Icon(imageVector = Icons.Filled.Settings, contentDescription = stringResource(id = R.string.nav_settings_title)) - } - } LargeTopAppBar( title = title, scrollBehavior = scrollBehavior, @@ -162,9 +175,9 @@ class MainActivity : MonetCompatActivity() { scrolledContainerColor = MaterialTheme.colorScheme.background ), actions = { - defaultAppBarActions() appBarActions.value(this) - } + }, + navigationIcon = navigationIcon ) } @@ -208,6 +221,7 @@ class MainActivity : MonetCompatActivity() { topBarScrollBehaviour: TopAppBarScrollBehavior, appBarTitle: MutableState<@Composable () -> Unit>, appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}), + navigationIcon: @Composable () -> Unit = {}, mainNavStartRoute: String = BottomNavItem.SortedItems[0].route ) { if (windowSize == WindowSizeClass.Expanded) { @@ -218,7 +232,8 @@ class MainActivity : MonetCompatActivity() { appBarTitle = appBarTitle, appBarActions = appBarActions, topBarScrollBehaviour = topBarScrollBehaviour, - mainNavStartRoute = mainNavStartRoute + mainNavStartRoute = mainNavStartRoute, + navigationIcon = navigationIcon ) } else { SingleColumnMainContent( @@ -259,6 +274,7 @@ class MainActivity : MonetCompatActivity() { topBarScrollBehaviour: TopAppBarScrollBehavior, appBarTitle: MutableState<@Composable () -> Unit>, appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}), + navigationIcon: @Composable () -> Unit = {}, mainNavStartRoute: String = BottomNavItem.SortedItems[0].route, preferences: AppPreferences = get(AppPreferences::class.java) ) { @@ -291,7 +307,8 @@ class MainActivity : MonetCompatActivity() { appNavController = appNavController, title = appBarTitle.value, scrollBehavior = topBarScrollBehaviour, - appBarActions = appBarActions + appBarActions = appBarActions, + navigationIcon = navigationIcon ) MainMediaView( appNavController = appNavController, @@ -435,6 +452,18 @@ class MainActivity : MonetCompatActivity() { WebLinkView(url = url, appNavController = appNavController) } } + composable( + route = MainNavItem.AccountView.route, + deepLinks = listOf( + navDeepLink { uriPattern = "app://tvtime.auth.{${NavConstants.ACCOUNT_KEY}}" } + ) + ) { + val deepLink = it.arguments?.getString(NavConstants.ACCOUNT_KEY) + AccountView( + appNavController = appNavController, + doSignInPartTwo = deepLink == NavConstants.AUTH_REDIRECT_PAGE + ) + } } } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Menus.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Menus.kt index a2d023b..3affe8d 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Menus.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Menus.kt @@ -1,141 +1,282 @@ package com.owenlejeune.tvtime.ui.components +import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.DropdownMenu -import androidx.compose.material3.Divider -import androidx.compose.material3.IconButton +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Login +import androidx.compose.material.icons.outlined.Logout +import androidx.compose.material.icons.outlined.Person +import androidx.compose.material.icons.outlined.PersonAdd +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment 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.graphics.compositeOver +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.owenlejeune.tvtime.BuildConfig +import com.owenlejeune.tvtime.R +import com.owenlejeune.tvtime.ui.navigation.MainNavItem +import com.owenlejeune.tvtime.utils.SessionManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +private const val ALPHA = 0.7f + +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun TopAppBarDropdownMenu( - icon: @Composable () -> Unit = {}, - content: @Composable ColumnScope.(expanded: MutableState) -> Unit = {} +fun ProfileMenuOverlay( + appNavController: NavController, + visible: Boolean, + onDismissRequest: () -> Unit ) { - val expanded = remember { mutableStateOf(false) } - - Box( - modifier = Modifier.wrapContentSize(Alignment.TopEnd) - ) { - IconButton( - onClick = { - expanded.value = true - } - ) { - icon() - } - } - - DropdownMenu( - modifier = Modifier.background(color = MaterialTheme.colorScheme.background), - expanded = expanded.value, - onDismissRequest = { expanded.value = false } - ) { - content(this, expanded) - } -} - -@Composable -fun CustomTopAppBarDropdownMenu( - icon: @Composable () -> Unit = {}, - content: @Composable ColumnScope.(expanded: MutableState) -> Unit = {} -) { - val expanded = remember { mutableStateOf(false) } - - Box(modifier = Modifier.wrapContentSize(Alignment.TopEnd).padding(end = 12.dp)) { - 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, + if (visible) { + val context = LocalContext.current + Box( 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 -fun TopAppBarDialogMenu( - icon: @Composable () -> Unit = {}, - content: @Composable (showing: MutableState) -> Unit = {} -) { - val expanded = remember { mutableStateOf(false) } - - Box( - modifier = Modifier.wrapContentSize(Alignment.TopEnd) - ) { - IconButton( - onClick = { - expanded.value = true - } + .fillMaxSize() + .background(color = Color.Black.copy(alpha = ALPHA)) + .clickable(onClick = onDismissRequest) ) { - icon() + Card( + modifier = Modifier + .align(Alignment.TopCenter) + .padding(vertical = 100.dp, horizontal = 12.dp) + .fillMaxWidth() + .wrapContentHeight(), + colors = CardDefaults.cardColors( + containerColor = Color.Black.copy(alpha = 0.6f).compositeOver(MaterialTheme.colorScheme.primaryContainer), + contentColor = Color.White.copy(alpha = 0.8f).compositeOver(MaterialTheme.colorScheme.onPrimaryContainer) + ) + ) { + Column( + modifier = Modifier.padding(vertical = 12.dp) + ) { + val currentSessionState = remember { SessionManager.currentSession } + val currentSession = currentSessionState.value + + currentSession?.let { + ProfileMenuItem { + AccountIcon( + size = 48.dp, + enabled = false + ) + Text( + text = currentSession.accountDetails.value?.name ?: "", + fontSize = 20.sp, + fontWeight = FontWeight.Bold + ) + } + + MenuDivider() + + ProfileMenuItem( + onClick = { + Toast.makeText(context, "Under construction", Toast.LENGTH_SHORT).show() + } + ) { + Icon( + imageVector = Icons.Outlined.PersonAdd, + contentDescription = null + ) + Text( + text = stringResource(id = R.string.account_add), + fontSize = 16.sp + ) + } + } ?: run { + ProfileMenuItem { + Text( + text = stringResource(id = R.string.account_not_logged_in), + fontSize = 16.sp + ) + } + } + + MenuDivider() + + currentSession?.let { + ProfileMenuItem( + onClick = { + onDismissRequest() + appNavController.navigate(MainNavItem.AccountView.route) + } + ) { + Icon( + imageVector = Icons.Outlined.Person, + contentDescription = null + ) + Text( + text = stringResource(id = R.string.nav_account_title), + fontSize = 16.sp + ) + } + } + + ProfileMenuItem( + onClick = { + onDismissRequest() + appNavController.navigate(MainNavItem.SettingsView.route) + } + ) { + Icon( + imageVector = Icons.Outlined.Settings, + contentDescription = null + ) + Text( + text = stringResource(id = R.string.nav_settings_title), + fontSize = 16.sp + ) + } + + MenuDivider() + + ProfileMenuItem( + onClick = { + onDismissRequest() + CoroutineScope(Dispatchers.IO).launch { + if (currentSession != null) { + SessionManager.clearSession() + } else { + SessionManager.signInPart1(context) { + appNavController.navigate( + MainNavItem.WebLinkView.route.plus("/$it") + ) + } + } + } + } + ) { + if (currentSession != null) { + Icon( + imageVector = Icons.Outlined.Logout, + contentDescription = null + ) + Text( + text = stringResource(id = R.string.action_sign_out), + fontSize = 16.sp + ) + } else { + Icon( + imageVector = Icons.Outlined.Login, + contentDescription = null + ) + Text( + text = stringResource(id = R.string.action_sign_in), + fontSize = 16.sp + ) + } + } + + MenuDivider() + + Text( + text = "${stringResource(id = R.string.app_name)} v${BuildConfig.VERSION_NAME}", + fontSize = 10.sp, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + } + } } } +} - if (expanded.value) { - Dialog( - onDismissRequest = { expanded.value = false }, - content = { content(expanded) } +@Composable +private fun MenuDivider() { + MyDivider(modifier = Modifier.padding(vertical = 9.dp)) +} + +@Composable +private fun ProfileMenuItem( + onClick: (() -> Unit)? = null, + content: @Composable RowScope.() -> Unit +) { + Box( + modifier = Modifier + .clickable( + enabled = onClick != null, + onClick = onClick ?: {} + ) + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(18.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .padding(horizontal = 24.dp, vertical = 9.dp) + .fillMaxWidth(), + content = content + ) + } +} + +class ProfileMenuColors internal constructor(val statusBarColor: Color, val navBarColor: Color) + +object ProfileMenuDefaults { + + @Composable + fun systemBarColors( + statusBarColor: Color = MaterialTheme.colorScheme.background, + navBarColor: Color = MaterialTheme.colorScheme.background + ) = ProfileMenuColors(statusBarColor, navBarColor) +} + +@Composable +fun ProfileMenuContainer( + colors: ProfileMenuColors = ProfileMenuDefaults.systemBarColors(), + appNavController: NavController, + visible: Boolean, + onDismissRequest: () -> Unit, + content: @Composable BoxScope.() -> Unit +) { + val statusBarColor = if (visible) { + Color.Black.copy(alpha = ALPHA).compositeOver(background = colors.statusBarColor) + } else { + colors.statusBarColor + } + val navBarColor = if (visible) { + Color.Black.copy(alpha = ALPHA).compositeOver(background = colors.navBarColor) + } else { + colors.navBarColor + } + + val systemUiController = rememberSystemUiController() + systemUiController.setStatusBarColor(statusBarColor) + systemUiController.setNavigationBarColor(navBarColor) + + Box { + content() + + ProfileMenuOverlay( + visible = visible, + onDismissRequest = onDismissRequest, + appNavController = appNavController ) -// AlertDialog( -// modifier = Modifier -// .fillMaxWidth() -// .wrapContentHeight(), -// backgroundColor = MaterialTheme.colorScheme.background, -// onDismissRequest = { expanded.value = false }, -// text = { content(expanded) }, -// buttons = {} -// ) } } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt index 8d0f184..6a2173a 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Posters.kt @@ -247,13 +247,16 @@ fun PosterItem( colors = CardDefaults.cardColors(containerColor = Color.Transparent) ) { var backgroundColor by remember { mutableStateOf(Color.Gray) } + val m = if (backgroundColor == Color.Transparent) { + Modifier.wrapContentHeight() + } else { + Modifier.height(POSTER_HEIGHT) + } Box( - modifier = Modifier + modifier = m .width(width = width) - .height(height = POSTER_HEIGHT) .background(color = backgroundColor) .clip(RoundedCornerShape(5.dp)) - .onGloballyPositioned { sizeImage = it.size } ) { var bgIcon by remember { mutableStateOf(placeholder) } Icon( @@ -286,7 +289,7 @@ fun PosterItem( }, model = url, contentDescription = title, - contentScale = ContentScale.FillWidth, + contentScale = ContentScale.FillBounds, onSuccess = { backgroundColor = Color.Transparent } ) diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Widgets.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Widgets.kt index f5b036e..2734ed9 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/components/Widgets.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/components/Widgets.kt @@ -17,12 +17,14 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.ArrowDropUp import androidx.compose.material.icons.filled.Error import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff +import androidx.compose.material.icons.outlined.AccountCircle import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable @@ -65,9 +67,11 @@ import coil.compose.rememberAsyncImagePainter import com.google.accompanist.flowlayout.FlowRow import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails +import com.owenlejeune.tvtime.extensions.unlessEmpty import com.owenlejeune.tvtime.preferences.AppPreferences import com.owenlejeune.tvtime.ui.navigation.MainNavItem import com.owenlejeune.tvtime.ui.screens.main.MediaViewType +import com.owenlejeune.tvtime.utils.SessionManager import com.owenlejeune.tvtime.utils.TmdbUtils import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -696,34 +700,90 @@ fun AvatarImage( contentDescription = "" ) } else { - val text = if (author.name.isNotEmpty()) author.name[0] else author.username[0] - RoundedLetterImage( - size = size, - character = text + val name = author.name.unlessEmpty(author.username) + UserInitials(size = size, name = name) + } +} + +@Composable +fun UserInitials( + size: Dp, + name: String, + fontSize: TextUnit = 16.sp +) { + val sanitizedName = name.replace("\\s+".toRegex(), " ") + val userName = if(sanitizedName.contains(" ")) { + sanitizedName.split(" ")[0][0].toString() + sanitizedName.split(" ")[1][0].toString() + } else { + if (sanitizedName.length < 3) name else { sanitizedName.substring(0, 2) } + } + Box( + modifier = Modifier + .clip(CircleShape) + .size(size) + .background(color = MaterialTheme.colorScheme.secondary) + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = userName.uppercase(), + textAlign = TextAlign.Center, + style = TextStyle( + color = MaterialTheme.colorScheme.background, + fontSize = fontSize + ) ) } } @Composable -fun RoundedLetterImage( - size: Dp, - character: Char, - modifier: Modifier = Modifier +fun AccountIcon( + modifier: Modifier = Modifier, + size: Dp = 60.dp, + onClick: () -> Unit = {}, + enabled: Boolean = true ) { - Box( - modifier = modifier - .clip(CircleShape) - .size(size) - .background(color = MaterialTheme.colorScheme.tertiary) - ) { - Text( - modifier = Modifier - .align(Alignment.Center), - text = character.uppercase(), - color = MaterialTheme.colorScheme.onTertiary, - textAlign = TextAlign.Center, - style = MaterialTheme.typography.titleLarge + val accountDetails = SessionManager.currentSession.value?.accountDetails?.value + val avatarUrl = accountDetails?.let { + when { + accountDetails.avatar.tmdb?.avatarPath?.isNotEmpty() == true -> { + TmdbUtils.getAccountAvatarUrl(accountDetails) + } + accountDetails.avatar.gravatar?.isDefault() == false -> { + TmdbUtils.getAccountGravatarUrl(accountDetails) + } + else -> null + } + } + + Box(modifier = modifier + .clip(CircleShape) + .clickable( + enabled = enabled, + onClick = onClick ) + ) { + if (accountDetails == null) { + Icon( + imageVector = Icons.Outlined.AccountCircle, + contentDescription = null, + modifier = Modifier.size(size), + tint = MaterialTheme.colorScheme.secondary + ) + } else if (avatarUrl == null) { + val name = accountDetails.name.ifEmpty { accountDetails.username } + UserInitials(size = size, name = name) + } else { + Box(modifier = Modifier.size(size)) { + AsyncImage( + model = avatarUrl, + contentDescription = "", + modifier = Modifier + .size(60.dp) + .clip(CircleShape), + contentScale = ContentScale.Fit + ) + } + } } } @@ -1035,4 +1095,9 @@ fun SearchBar( ) } ) +} + +@Composable +fun MyDivider(modifier: Modifier = Modifier) { + Divider(thickness = 0.5.dp, modifier = modifier, color = MaterialTheme.colorScheme.secondaryContainer) } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/BottomNavItem.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/BottomNavItem.kt index 473ef5c..cd1b037 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/BottomNavItem.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/BottomNavItem.kt @@ -3,6 +3,7 @@ package com.owenlejeune.tvtime.ui.navigation import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.preferences.AppPreferences import com.owenlejeune.tvtime.utils.ResourceUtils +import com.owenlejeune.tvtime.utils.SessionManager import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -41,9 +42,39 @@ sealed class BottomNavItem( } } - object Movies: BottomNavItem(R.string.nav_movies_title, R.drawable.ic_movie, "movies_route", { it.moviesTabPosition }, { p, i -> p.moviesTabPosition = i } ) - object TV: BottomNavItem(R.string.nav_tv_title, R.drawable.ic_tv, "tv_route", { it.tvTabPosition }, { p, i -> p.tvTabPosition = i } ) - object Account: BottomNavItem(R.string.nav_account_title, R.drawable.ic_person, "account_route", { it.accountTabPosition }, { p, i -> p.accountTabPosition = i } ) - object People: BottomNavItem(R.string.nav_people_title, R.drawable.ic_face, "people_route", { it.peopleTabPosition }, { p, i -> p.peopleTabPosition = i } ) + object Movies: BottomNavItem( + R.string.nav_movies_title, + R.drawable.ic_movie, + "movies_route", + { it.moviesTabPosition }, + { p, i -> p.moviesTabPosition = i } + ) + object TV: BottomNavItem( + R.string.nav_tv_title, + R.drawable.ic_tv, + "tv_route", + { it.tvTabPosition }, + { p, i -> p.tvTabPosition = i } + ) + object Account: BottomNavItem( + R.string.nav_account_title, + R.drawable.ic_person, + "account_route", + { +// if (SessionManager.currentSession.value?.isAuthorized == true) { +// it.accountTabPosition +// } else { + -2 +// } + }, + { p, i -> p.accountTabPosition = i } + ) + object People: BottomNavItem( + R.string.nav_people_title, + R.drawable.ic_face, + "people_route", + { it.peopleTabPosition }, + { p, i -> p.peopleTabPosition = i } + ) } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/MainNavItem.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/MainNavItem.kt index a732585..3f12562 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/MainNavItem.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/MainNavItem.kt @@ -12,4 +12,6 @@ sealed class MainNavItem(val route: String) { object SearchView: MainNavItem("search_route") object WebLinkView: MainNavItem("web_link_route") + object AccountView: MainNavItem("account_route") + } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt index 617854b..f486f78 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/navigation/Routes.kt @@ -1,16 +1,13 @@ package com.owenlejeune.tvtime.ui.navigation -import android.util.Log import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.RowScope import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.navigation.NavHostController -import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.navArgument import androidx.navigation.navDeepLink import com.owenlejeune.tvtime.ui.screens.main.MediaViewType import com.owenlejeune.tvtime.ui.screens.main.* @@ -45,19 +42,8 @@ fun MainNavGraph( fab = fab ) } - composable( - route = BottomNavItem.Account.route, - deepLinks = listOf( - navDeepLink { uriPattern = "app://tvtime.auth.{${NavConstants.ACCOUNT_KEY}}" } - ) - ) { - val deepLink = it.arguments?.getString(NavConstants.ACCOUNT_KEY) - AccountTab( - appBarTitle = appBarTitle, - appNavController = appNavController, - appBarActions = appBarActions, - doSignInPartTwo = deepLink == NavConstants.AUTH_REDIRECT_PAGE - ) + composable(route = BottomNavItem.Account.route) { + AccountViewContent(appNavController = appNavController) fab.value = {} } composable(BottomNavItem.People.route) { diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountTab.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountView.kt similarity index 69% rename from app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountTab.kt rename to app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountView.kt index 937ac9a..57a19a7 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountTab.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/AccountView.kt @@ -1,29 +1,25 @@ package com.owenlejeune.tvtime.ui.screens.main +import androidx.compose.animation.rememberSplineBasedDecay import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AccountCircle +import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController import androidx.paging.compose.collectAsLazyPagingItems -import coil.compose.AsyncImage import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.PagerState @@ -31,63 +27,104 @@ import com.google.accompanist.pager.rememberPagerState import com.owenlejeune.tvtime.R import com.owenlejeune.tvtime.api.tmdb.api.v3.model.* import com.owenlejeune.tvtime.api.tmdb.api.v4.model.V4AccountList +import com.owenlejeune.tvtime.api.tmdb.viewmodel.RecommendedMediaViewModel import com.owenlejeune.tvtime.extensions.unlessEmpty +import com.owenlejeune.tvtime.ui.components.AccountIcon import com.owenlejeune.tvtime.ui.components.PagingPosterGrid -import com.owenlejeune.tvtime.ui.components.RoundedLetterImage +import com.owenlejeune.tvtime.ui.components.ProfileMenuContainer +import com.owenlejeune.tvtime.ui.components.ProfileMenuOverlay import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem import com.owenlejeune.tvtime.ui.navigation.ListFetchFun import com.owenlejeune.tvtime.ui.navigation.MainNavItem import com.owenlejeune.tvtime.ui.screens.main.tabs.top.ScrollableTabs -import com.owenlejeune.tvtime.api.tmdb.viewmodel.RecommendedMediaViewModel import com.owenlejeune.tvtime.utils.SessionManager import com.owenlejeune.tvtime.utils.TmdbUtils -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlin.reflect.KClass -@OptIn(ExperimentalPagerApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun AccountTab( +fun AccountView( appNavController: NavHostController, - appBarTitle: MutableState<@Composable () -> Unit>, - appBarActions: MutableState<@Composable (RowScope.() -> Unit)> = mutableStateOf({}), doSignInPartTwo: Boolean = false ) { val currentSessionState = remember { SessionManager.currentSession } val currentSession = currentSessionState.value + val showProfileMenuOverlay = remember { mutableStateOf(false) } + + ProfileMenuContainer( + appNavController = appNavController, + visible = showProfileMenuOverlay.value, + onDismissRequest = { showProfileMenuOverlay.value = false } + ) { + val decayAnimationSpec = rememberSplineBasedDecay() + val topAppBarScrollState = rememberTopAppBarScrollState() + val scrollBehavior = remember(decayAnimationSpec) { + TopAppBarDefaults.exitUntilCollapsedScrollBehavior(decayAnimationSpec, topAppBarScrollState) + } + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + LargeTopAppBar( + scrollBehavior = scrollBehavior, + navigationIcon = { + IconButton( + onClick = { appNavController.popBackStack() } + ) { + Icon( + Icons.Filled.ArrowBack, + contentDescription = null + ) + } + }, + title = { + if (currentSession?.isAuthorized == false) { + Text(text = stringResource(id = R.string.account_not_logged_in)) + } else { + val accountDetails = remember { currentSession!!.accountDetails } + Text(text = getAccountName(accountDetails.value)) + } + }, + colors = TopAppBarDefaults.largeTopAppBarColors(scrolledContainerColor = MaterialTheme.colorScheme.background), + actions = { + AccountIcon( + modifier = Modifier.padding(start = 12.dp), + size = 32.dp, + onClick = { showProfileMenuOverlay.value = true } + ) + Spacer(modifier = Modifier.width(8.dp)) + } + ) + } + ) { + Box(modifier = Modifier.padding(it)) { + AccountViewContent(appNavController = appNavController, doSignInPartTwo = doSignInPartTwo) + } + } + } +} + +@OptIn(ExperimentalPagerApi::class) +@Composable +fun AccountViewContent( + appNavController: NavHostController, + doSignInPartTwo: Boolean = false +) { + val currentSessionState = remember { SessionManager.currentSession } + val currentSession = currentSessionState.value val scope = rememberCoroutineScope() - if (currentSession?.isAuthorized == false) { - appBarTitle.value = { Text(text = stringResource(id = R.string.account_not_logged_in)) } - if (doSignInPartTwo) { - AccountLoadingView() - LaunchedEffect(Unit) { - scope.launch { - SessionManager.singInPart2() - } + if (currentSession?.isAuthorized != true && doSignInPartTwo) { + AccountLoadingView() + LaunchedEffect(Unit) { + scope.launch { + SessionManager.signInPart2() } } } else { - if (currentSession?.isAuthorized == true) { - val accountDetails = remember { currentSession.accountDetails } - appBarTitle.value = { Text(text = stringResource(id = R.string.account_header_title_formatted, getAccountName(accountDetails.value))) } - } else { - appBarTitle.value = { Text(text = stringResource(id = R.string.account_not_logged_in)) } - } - - appBarActions.value = { - AccountDropdownMenu( - session = currentSession, - appNavController = appNavController - ) - } - currentSession?.let { Column { - AuthorizedSessionIcon() - val tabs = AccountTabNavItem.AuthorizedItems val pagerState = rememberPagerState() ScrollableTabs(tabs = tabs, pagerState = pagerState) @@ -288,95 +325,6 @@ private fun MediaItemRow( ) } -@Composable -private fun AccountDropdownMenu(session: SessionManager.Session?, appNavController: NavHostController) { - val expanded = remember { mutableStateOf(false) } - - IconButton( - onClick = { expanded.value = true } - ) { - Icon(imageVector = Icons.Filled.AccountCircle, contentDescription = stringResource(id = R.string.nav_account_title)) - } - - DropdownMenu( - expanded = expanded.value, - onDismissRequest = { expanded.value = false } - ) { - if(session?.isAuthorized == true) { - AuthorizedSessionMenuItems(expanded = expanded) - } else { - NoSessionMenuItems(expanded = expanded, appNavController = appNavController) - } - } -} - -@Composable -private fun AuthorizedSessionMenuItems(expanded: MutableState) { - DropdownMenuItem( - text = { Text(text = stringResource(id = R.string.action_sign_out)) }, - onClick = { - signOut() - expanded.value = false - } - ) -} - -@Composable -private fun NoSessionMenuItems(expanded: MutableState, appNavController: NavHostController) { - val context = LocalContext.current - DropdownMenuItem( - text = { Text(text = stringResource(id = R.string.action_sign_in)) }, - onClick = { - CoroutineScope(Dispatchers.IO).launch { - SessionManager.signInPart1(context) { - appNavController.navigate(MainNavItem.WebLinkView.route.plus("/$it")) - } - } - expanded.value = false - } - ) -} - -@Composable -private fun AuthorizedSessionIcon() { - val accountDetails = SessionManager.currentSession.value?.accountDetails?.value - val avatarUrl = accountDetails?.let { - when { - accountDetails.avatar.tmdb?.avatarPath?.isNotEmpty() == true -> { - TmdbUtils.getAccountAvatarUrl(accountDetails) - } - accountDetails.avatar.gravatar?.isDefault() == false -> { - TmdbUtils.getAccountGravatarUrl(accountDetails) - } - else -> null - } - } - - Box(modifier = Modifier.padding(start = 12.dp)) { - if (accountDetails == null || avatarUrl == null) { - val accLetter = (accountDetails?.name?.ifEmpty { accountDetails.username } ?: " ")[0] - RoundedLetterImage(size = 60.dp, character = accLetter) - } else { - Box(modifier = Modifier.size(60.dp)) { - AsyncImage( - model = avatarUrl, - contentDescription = "", - modifier = Modifier - .size(60.dp) - .clip(CircleShape), - contentScale = ContentScale.Fit - ) - } - } - } -} - -private fun signOut() { - CoroutineScope(Dispatchers.IO).launch { - SessionManager.clearSession() - } -} - @OptIn(ExperimentalPagerApi::class) @Composable diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/PeopleTab.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/PeopleTab.kt index f7027ba..1b45b5a 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/PeopleTab.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/PeopleTab.kt @@ -2,7 +2,7 @@ package com.owenlejeune.tvtime.ui.screens.main import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding -import androidx.compose.material.Text +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.ui.Modifier @@ -22,7 +22,7 @@ fun PeopleTab( appNavController: NavHostController, fab: MutableState<@Composable () -> Unit> ) { - val titleText = stringResource(id = R.string.nav_people_title) + val titleText = stringResource(id = R.string.popular_today_header) appBarTitle.value = { Text(text = titleText) } Column { @@ -38,10 +38,10 @@ fun PeopleTab( PagingPeoplePosterGrid( lazyPagingItems = peopleList, header = { - Text( - text = stringResource(R.string.popular_today_header), - modifier = Modifier.padding(start = 8.dp) - ) +// Text( +// text = stringResource(R.string.popular_today_header), +// modifier = Modifier.padding(start = 8.dp) +// ) }, onClick = { id -> appNavController.navigate( diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/SettingsTab.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/SettingsTab.kt index 2d00f4f..beab561 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/SettingsTab.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/SettingsTab.kt @@ -382,7 +382,7 @@ private fun HomeScreenPreferences( SwitchPreference( titleText = stringResource(R.string.preference_show_poster_titles_title), subtitleText = stringResource(R.string.preference_show_poster_titles_subtitle), - checkState = showTabLabels.value, + checkState = showPosterTitles.value, onCheckedChange = { isChecked -> showPosterTitles.value = isChecked preferences.showPosterTitles = isChecked diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/WebLinkView.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/WebLinkView.kt index b448e83..b94146f 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/WebLinkView.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/screens/main/WebLinkView.kt @@ -22,6 +22,7 @@ import androidx.navigation.NavController import com.google.accompanist.web.AccompanistWebViewClient import com.google.accompanist.web.WebView import com.google.accompanist.web.rememberWebViewState +import com.owenlejeune.tvtime.utils.SessionManager import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -37,7 +38,12 @@ fun WebLinkView( title = {}, navigationIcon = { IconButton( - onClick = { appNavController.popBackStack() } + onClick = { + if (url.contains("auth")) { + SessionManager.cancelSignIn() + } + appNavController.popBackStack() + } ) { Icon( imageVector = Icons.Filled.Close, @@ -47,8 +53,8 @@ fun WebLinkView( } ) } - ) { - Box(modifier = Modifier.padding(it)) { + ) { paddingValues -> + Box(modifier = Modifier.padding(paddingValues)) { val webViewState = rememberWebViewState(url = url) WebView( state = webViewState, diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/theme/Theme.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/theme/Theme.kt index 320d7e1..79ab7f8 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/theme/Theme.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/theme/Theme.kt @@ -115,13 +115,7 @@ fun TVTimeTheme( content = { val systemUiController = rememberSystemUiController() systemUiController.setStatusBarColor(color = androidx.compose.material3.MaterialTheme.colorScheme.background) - systemUiController.setNavigationBarColor( - color = androidx.compose.material3.MaterialTheme.colorScheme.primary.copy( - alpha = 0.08f - ).compositeOver( - background = androidx.compose.material3.MaterialTheme.colorScheme.surface - ) - ) + systemUiController.setNavigationBarColor(color = androidx.compose.material3.MaterialTheme.colorScheme.background) content() } diff --git a/app/src/main/java/com/owenlejeune/tvtime/ui/views/HomeTabRecyclerAdapter.kt b/app/src/main/java/com/owenlejeune/tvtime/ui/views/HomeTabRecyclerAdapter.kt index c2cac2c..77bdedb 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/ui/views/HomeTabRecyclerAdapter.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/ui/views/HomeTabRecyclerAdapter.kt @@ -33,7 +33,7 @@ class HomeTabRecyclerAdapter: RecyclerView.Adapter -1 }.sortedBy { it.order } - val hiddenPages = BottomNavItem.Items.filter { it.order < 0 } + val hiddenPages = BottomNavItem.Items.filter { it.order == -1 } pages = ArrayList().apply { addAll(visiblePages) add(null) 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 e3fc3b1..c6d4af4 100644 --- a/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt +++ b/app/src/main/java/com/owenlejeune/tvtime/utils/SessionManager.kt @@ -1,8 +1,6 @@ package com.owenlejeune.tvtime.utils import android.content.Context -import android.content.Intent -import android.net.Uri import android.widget.Toast import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf @@ -18,7 +16,6 @@ import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthDeleteBody import com.owenlejeune.tvtime.api.tmdb.api.v4.model.AuthRequestBody import com.owenlejeune.tvtime.api.tmdb.api.v4.model.V4AccountList import com.owenlejeune.tvtime.preferences.AppPreferences -import com.owenlejeune.tvtime.ui.navigation.AccountTabNavItem import com.owenlejeune.tvtime.ui.screens.main.MediaViewType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -59,6 +56,12 @@ object SessionManager: KoinComponent { } } + fun cancelSignIn() { + if (currentSession.value is InProgressSession) { + currentSession.value = null + } + } + suspend fun initialize() { preferences.authorizedSessionValues?.let { values -> val session = AuthorizedSession( @@ -79,17 +82,17 @@ object SessionManager: KoinComponent { val requestTokenResponse = service.createRequestToken(AuthRequestBody(redirect = "app://tvtime.auth.return")) if (requestTokenResponse.isSuccessful) { requestTokenResponse.body()?.let { ctr -> - currentSession.value = InProgressSession(ctr.requestToken) val url = context.getString(R.string.tmdb_auth_url, ctr.requestToken) val encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8.toString()) withContext(Dispatchers.Main) { onRedirect(encodedUrl) } + currentSession.value = InProgressSession(ctr.requestToken) } } } - suspend fun singInPart2( + suspend fun signInPart2( context: Context = get(Context::class.java) ) { if (currentSession.value is InProgressSession) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 511c72d..4f5d8df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,6 +114,7 @@ Hello, %1$s! Guest Not logged in + Add Account No rated content Rating: %1$d