From 046fd54add7b1b89161330710e81b46a78e05ac0 Mon Sep 17 00:00:00 2001 From: jacobrein Date: Mon, 25 Nov 2024 09:26:45 -0700 Subject: [PATCH] - Feat: Enhance Custom Lists with Export and Action Items This commit introduces several enhancements to custom lists: - **Export Functionality:** Adds an option to export a custom list as a JSON file. This is accessible via an info sheet displayed when clicking an info icon next to the list. - **Action Items:** Replaces the previous single card for "Remove Items" with a row of action items within the info sheet. This now includes "Export List" and "Remove Items", with "Delete List" added for non-default lists. - **Info Sheet Enhancements:** The info sheet now presents actions in a more organized and visually appealing manner. - **Icon Update:** Replaces the three-dot menu icon with an info icon, offering a clearer indication of the sheet's purpose. These changes improve the functionality and usability of custom lists, providing users with greater control and flexibility. --- .../uiviews/lists/OtakuCustomListScreen.kt | 138 ++++++++++-------- 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/lists/OtakuCustomListScreen.kt b/UIViews/src/main/java/com/programmersbox/uiviews/lists/OtakuCustomListScreen.kt index 5a8e1a1af..dfa3a2508 100644 --- a/UIViews/src/main/java/com/programmersbox/uiviews/lists/OtakuCustomListScreen.kt +++ b/UIViews/src/main/java/com/programmersbox/uiviews/lists/OtakuCustomListScreen.kt @@ -19,6 +19,9 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets @@ -39,7 +42,8 @@ import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Circle import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.filled.ImportExport +import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.RemoveCircle import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Share @@ -48,11 +52,10 @@ import androidx.compose.material3.BottomAppBar import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card +import androidx.compose.material3.CardColors import androidx.compose.material3.CardDefaults import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.Checkbox -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -264,7 +267,8 @@ fun OtakuCustomListScreen( .uuid .toString() .let { navController.navigate(Screen.CustomListScreen.DeleteFromList(it)) } - } + }, + onExportAction = { pickDocumentLauncher.launch("${it.item.name}.json") } ) } } @@ -347,50 +351,32 @@ fun OtakuCustomListScreen( Text("(${customItem?.list.orEmpty().size})") - var showMenu by remember { mutableStateOf(false) } - - DropdownMenu( - expanded = showMenu, - onDismissRequest = { showMenu = false } - ) { - DropdownMenuItem( - text = { Text(stringResource(R.string.export_list)) }, - onClick = { - showMenu = false - pickDocumentLauncher.launch("${customItem?.item?.name}.json") - } - ) - - DropdownMenuItem( - text = { Text(stringResource(R.string.edit_import_list)) }, - onClick = { - showMenu = false - showInfoSheet = true - } - ) - } - AnimatedVisibility(!searchBarActive) { - IconButton( - onClick = { - shareItem.launchCatching( - Intent.createChooser( - Intent(Intent.ACTION_SEND).apply { - type = "text/plain" - putExtra( - Intent.EXTRA_TEXT, - customItem?.list.orEmpty().joinToString("\n") { "${it.title} - ${it.url}" } - ) - putExtra(Intent.EXTRA_TITLE, customItem?.item?.name.orEmpty()) - }, - context.getString(R.string.share_item, customItem?.item?.name.orEmpty()) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + IconButton( + onClick = { + shareItem.launchCatching( + Intent.createChooser( + Intent(Intent.ACTION_SEND).apply { + type = "text/plain" + putExtra( + Intent.EXTRA_TEXT, + customItem?.list.orEmpty().joinToString("\n") { "${it.title} - ${it.url}" } + ) + putExtra(Intent.EXTRA_TITLE, customItem?.item?.name.orEmpty()) + }, + context.getString(R.string.share_item, customItem?.item?.name.orEmpty()) + ) ) - ) - } - ) { Icon(Icons.Default.Share, null) } - } - AnimatedVisibility(!searchBarActive) { - IconButton(onClick = { showMenu = true }) { Icon(Icons.Default.MoreVert, null) } + } + ) { Icon(Icons.Default.Share, null) } + + IconButton( + onClick = { showInfoSheet = true } + ) { Icon(Icons.Default.Info, null) } + } } } }, @@ -747,7 +733,7 @@ private fun CustomListScreenPreview() { //TODO: Add a bottom sheet for some info about the list. // This includes being able to maybe changing the cover and anything else -@OptIn(ExperimentalMaterial3Api::class, ExperimentalGlideComposeApi::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalGlideComposeApi::class, ExperimentalLayoutApi::class) @Composable private fun InfoSheet( customItem: CustomList, @@ -759,6 +745,7 @@ private fun InfoSheet( logo: AppLogo, onDeleteListAction: () -> Unit, onRemoveItemsAction: () -> Unit, + onExportAction: () -> Unit, ) { val scope = rememberCoroutineScope() val context = LocalContext.current @@ -924,11 +911,24 @@ private fun InfoSheet( HorizontalDivider() - Row( + FlowRow( horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.fillMaxWidth() ) { - Card( + ActionItem( + onClick = { + scope.launch { sheetState.hide() } + .invokeOnCompletion { + onDismiss() + onExportAction() + } + } + ) { + Icon(Icons.Default.ImportExport, null) + Text(stringResource(R.string.export_list)) + } + + ActionItem( onClick = { scope.launch { sheetState.hide() } .invokeOnCompletion { @@ -941,17 +941,12 @@ private fun InfoSheet( contentColor = MaterialTheme.colorScheme.error, ) ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(8.dp) - ) { - Icon(Icons.Default.RemoveCircle, null) - Text(stringResource(R.string.remove_items)) - } + Icon(Icons.Default.RemoveCircle, null) + Text(stringResource(R.string.remove_items)) } if (customItem.item.uuid != OtakuApp.forLaterUuid) { - Card( + ActionItem( onClick = { scope.launch { sheetState.hide() } .invokeOnCompletion { @@ -964,16 +959,31 @@ private fun InfoSheet( contentColor = MaterialTheme.colorScheme.onErrorContainer, ), ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(8.dp) - ) { - Icon(Icons.Default.Delete, null) - Text(stringResource(R.string.delete_list_title)) - } + Icon(Icons.Default.Delete, null) + Text(stringResource(R.string.delete_list_title)) } } } } } +} + +@Composable +private fun ActionItem( + modifier: Modifier = Modifier, + onClick: () -> Unit, + colors: CardColors = CardDefaults.cardColors(), + content: @Composable ColumnScope.() -> Unit, +) { + Card( + onClick = onClick, + colors = colors, + modifier = modifier + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(8.dp), + content = content + ) + } } \ No newline at end of file