Loading cardinal-android/app/src/main/java/earth/maps/cardinal/ui/saved/ManagePlacesScreen.kt +287 −298 Original line number Diff line number Diff line Loading @@ -102,15 +102,10 @@ fun ManagePlacesScreen( val clipboard by viewModel.clipboard.collectAsState(emptySet()) val selectedItems by viewModel.selectedItems.collectAsState() val isAllSelected by viewModel.isAllSelected.collectAsState(initial = false) val showDeleteConfirmation = remember { mutableStateOf(false) } val showCreateListDialog = remember { mutableStateOf(false) } val showEditDialog = remember { mutableStateOf(false) } val editingItem = remember { mutableStateOf<ListContent?>(null) } var newListName by remember { mutableStateOf("") } var showEmptyNameWarning by remember { mutableStateOf(false) } var editName by remember { mutableStateOf("") } var editDescription by remember { mutableStateOf("") } var editPinned by remember { mutableStateOf(false) } var showDeleteConfirmation by remember { mutableStateOf(false) } var showCreateListDialog by remember { mutableStateOf(false) } var showEditDialog by remember { mutableStateOf(false) } var editingItem by remember { mutableStateOf<ListContent?>(null) } var fabMenuExpanded by remember { mutableStateOf(false) } // Initialize the view model with the listId if provided Loading @@ -119,8 +114,7 @@ fun ManagePlacesScreen( } Scaffold( contentWindowInsets = WindowInsets.safeDrawing, topBar = { contentWindowInsets = WindowInsets.safeDrawing, topBar = { ManagePlacesTopBar( navController = navController, title = currentListName ?: stringResource(string.saved_places_title_case), Loading Loading @@ -157,8 +151,7 @@ fun ManagePlacesScreen( coroutineScope.launch { val place = viewModel.getSavedPlace(item.id) ?: return@launch NavigationUtils.navigate( navController, Screen.PlaceCard(place) navController, Screen.PlaceCard(place) ) } } Loading @@ -166,27 +159,20 @@ fun ManagePlacesScreen( is ListContentItem -> { currentListId?.let { currentListId -> NavigationUtils.navigate( navController, Screen.ManagePlaces( item.id, parents = parents.plus(currentListName ?: "") ), avoidCycles = false navController, Screen.ManagePlaces( item.id, parents = parents.plus(currentListName ?: "") ), avoidCycles = false ) } } } }, onEditClick = { editingItem.value = it; showEditDialog.value = true } ) onEditClick = { editingItem = it; showEditDialog = true }) } } val modifier = if (fabMenuExpanded) { Modifier .clickable( indication = null, interactionSource = null, onClick = { fabMenuExpanded = false }) Modifier.clickable( indication = null, interactionSource = null, onClick = { fabMenuExpanded = false }) } else { Modifier } Loading @@ -200,12 +186,9 @@ fun ManagePlacesScreen( modifier = Modifier.align(Alignment.BottomEnd), expanded = fabMenuExpanded, button = { ToggleFloatingActionButton( checked = fabMenuExpanded, onCheckedChange = { ToggleFloatingActionButton(checked = fabMenuExpanded, onCheckedChange = { fabMenuExpanded = it }, content = { }, content = { val close = painterResource(drawable.ic_close) val open = painterResource(drawable.ic_menu) val painter by remember { Loading @@ -218,126 +201,134 @@ fun ManagePlacesScreen( contentDescription = null, modifier = Modifier.animateIcon({ checkedProgress }), ) } ) } ) { FloatingActionButtonMenuItem( onClick = { showCreateListDialog.value = true }) }) { FloatingActionButtonMenuItem(onClick = { showCreateListDialog = true fabMenuExpanded = false }, text = { }, text = { Text( text = stringResource( string.new_list ) ) }, icon = { }, icon = { Icon( painter = painterResource(drawable.ic_new_list), contentDescription = null painter = painterResource(drawable.ic_new_list), contentDescription = null ) }) FloatingActionButtonMenuItem( onClick = { FloatingActionButtonMenuItem(onClick = { viewModel.cutSelected() fabMenuExpanded = false }, text = { }, text = { Text(text = stringResource(string.cut)) }, icon = { }, icon = { Icon( painter = painterResource(drawable.ic_content_cut), contentDescription = null ) } ) FloatingActionButtonMenuItem( onClick = { }) FloatingActionButtonMenuItem(onClick = { viewModel.pasteSelected() fabMenuExpanded = false }, text = { }, text = { Text(text = stringResource(string.paste)) }, icon = { }, icon = { Icon( painter = painterResource(drawable.ic_content_paste), contentDescription = null ) } ) FloatingActionButtonMenuItem( onClick = { showDeleteConfirmation.value = true }) FloatingActionButtonMenuItem(onClick = { showDeleteConfirmation = true fabMenuExpanded = false }, text = { }, text = { Text(text = stringResource(string.delete)) }, icon = { }, icon = { Icon( painter = painterResource(drawable.ic_delete), contentDescription = null painter = painterResource(drawable.ic_delete), contentDescription = null ) } ) FloatingActionButtonMenuItem( onClick = { }) FloatingActionButtonMenuItem(onClick = { if (isAllSelected) { viewModel.clearSelection() } else { viewModel.selectAll() } }, text = { }, text = { Text(text = stringResource(if (isAllSelected) string.deselect_all else string.select_all)) }, icon = { }, icon = { Icon( painter = painterResource(if (isAllSelected) drawable.ic_clear_selection else drawable.ic_select_all), contentDescription = null ) }) } ) } } if (showDeleteConfirmation) { DeleteConfirmationDialog( selectedItemsCount = selectedItems.size, onDelete = { viewModel.deleteSelected() }, onDismiss = { showCreateListDialog = false }) } if (showDeleteConfirmation.value) { if (showCreateListDialog) { CreateListDialog(onCreate = { viewModel.createNewListWithSelected(it) }, onDismiss = { showCreateListDialog = false }) } if (showEditDialog) { EditDialog(editingItem = editingItem, viewModel = viewModel, onDismiss = { showEditDialog = false }) } } @Composable private fun DeleteConfirmationDialog( selectedItemsCount: Int, onDelete: () -> Unit, onDismiss: () -> Unit, ) { AlertDialog( onDismissRequest = { showDeleteConfirmation.value = false }, onDismissRequest = { onDismiss() }, title = { Text(stringResource(string.confirm_delete)) }, text = { Text(stringResource(string.delete_confirmation_message, selectedItems.size)) }, text = { Text(stringResource(string.delete_confirmation_message, selectedItemsCount)) }, confirmButton = { Button(onClick = { viewModel.deleteSelected() showDeleteConfirmation.value = false onDelete() onDismiss() }) { Text(stringResource(string.delete)) } }, dismissButton = { Button(onClick = { showDeleteConfirmation.value = false }) { Button(onClick = { onDismiss() }) { Text(stringResource(string.cancel)) } } ) }) } if (showCreateListDialog.value) { AlertDialog( onDismissRequest = { showCreateListDialog.value = false @Composable private fun CreateListDialog( onCreate: (String) -> Unit, onDismiss: () -> Unit, ) { var newListName by remember { mutableStateOf("") } var showEmptyNameWarning by remember { mutableStateOf(false) } AlertDialog(onDismissRequest = { onDismiss() newListName = "" showEmptyNameWarning = false }, title = { Text(stringResource(string.add_new_list)) }, text = { }, title = { Text(stringResource(string.add_new_list)) }, text = { Column { OutlinedTextField( value = newListName, Loading @@ -357,35 +348,41 @@ fun ManagePlacesScreen( ) } } }, confirmButton = { }, confirmButton = { Button(onClick = { if (newListName.isBlank()) { showEmptyNameWarning = true } else { viewModel.createNewListWithSelected(newListName) showCreateListDialog.value = false onCreate(newListName) onDismiss() newListName = "" showEmptyNameWarning = false } }) { Text(stringResource(string.add_new_list)) } }, dismissButton = { }, dismissButton = { Button(onClick = { showCreateListDialog.value = false onDismiss() newListName = "" showEmptyNameWarning = false }) { Text(stringResource(string.cancel)) } } ) }) } if (showEditDialog.value) { editingItem.value?.let { item -> @Composable private fun EditDialog( editingItem: ListContent?, viewModel: ManagePlacesViewModel, onDismiss: () -> Unit, ) { editingItem?.let { item -> var editName by remember { mutableStateOf("") } var editDescription by remember { mutableStateOf("") } var editPinned by remember { mutableStateOf(false) } LaunchedEffect(item) { when (item) { is PlaceContent -> { Loading @@ -407,10 +404,7 @@ fun ManagePlacesScreen( is ListContentItem -> stringResource(string.edit_list) } AlertDialog( onDismissRequest = { showEditDialog.value = false }, title = { Text(title) }, text = { AlertDialog(onDismissRequest = { onDismiss() }, title = { Text(title) }, text = { Column { OutlinedTextField( value = editName, Loading @@ -427,61 +421,39 @@ fun ManagePlacesScreen( if (item is PlaceContent) { Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( checked = editPinned, onCheckedChange = { editPinned = it } ) checked = editPinned, onCheckedChange = { editPinned = it }) Text(stringResource(string.pin_place)) } } } }, confirmButton = { }, confirmButton = { Button(onClick = { val name = editName.ifBlank { null } val desc = editDescription // Allow blank descriptions. val desc = editDescription when (item) { is PlaceContent -> viewModel.updatePlace( item.id, name, desc, editPinned item.id, name, desc, editPinned ) is ListContentItem -> viewModel.updateList(item.id, name, desc) } showEditDialog.value = false onDismiss() }) { Text(stringResource(string.save)) } }, dismissButton = { Button(onClick = { showEditDialog.value = false }) { }, dismissButton = { Button(onClick = { onDismiss() }) { Text(stringResource(string.cancel)) } } ) } }) } } @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable private fun ManagePlacesTopBar( private fun Breadcrumbs( navController: NavController, title: String, breadcrumbNames: List<String>, ) { TopAppBar( title = { Column { Text( text = title, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, textAlign = TextAlign.Start, ) // Breadcrumbs if (breadcrumbNames.size > 1) { Row( modifier = Modifier .fillMaxWidth() Loading Loading @@ -513,14 +485,33 @@ private fun ManagePlacesTopBar( } } else { Modifier } ) }) } } } @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable private fun ManagePlacesTopBar( navController: NavController, title: String, breadcrumbNames: List<String>, ) { TopAppBar( title = { Column { Text( text = title, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, textAlign = TextAlign.Start, ) // Breadcrumbs if (breadcrumbNames.size > 1) { Breadcrumbs(navController, breadcrumbNames) } } ) }) } @Composable Loading Loading @@ -570,8 +561,7 @@ private fun ListContentGrid( if (shouldShowListsHeader) { item { SectionHeader( title = stringResource(string.saved_lists), modifier = Modifier.padding( title = stringResource(string.saved_lists), modifier = Modifier.padding( vertical = dimensionResource(dimen.padding_minor), horizontal = dimensionResource(dimen.padding) ) Loading Loading @@ -647,8 +637,7 @@ private fun ListContentGrid( @Composable private fun SectionHeader( title: String, modifier: Modifier = Modifier title: String, modifier: Modifier = Modifier ) { Text( text = title, Loading Loading
cardinal-android/app/src/main/java/earth/maps/cardinal/ui/saved/ManagePlacesScreen.kt +287 −298 Original line number Diff line number Diff line Loading @@ -102,15 +102,10 @@ fun ManagePlacesScreen( val clipboard by viewModel.clipboard.collectAsState(emptySet()) val selectedItems by viewModel.selectedItems.collectAsState() val isAllSelected by viewModel.isAllSelected.collectAsState(initial = false) val showDeleteConfirmation = remember { mutableStateOf(false) } val showCreateListDialog = remember { mutableStateOf(false) } val showEditDialog = remember { mutableStateOf(false) } val editingItem = remember { mutableStateOf<ListContent?>(null) } var newListName by remember { mutableStateOf("") } var showEmptyNameWarning by remember { mutableStateOf(false) } var editName by remember { mutableStateOf("") } var editDescription by remember { mutableStateOf("") } var editPinned by remember { mutableStateOf(false) } var showDeleteConfirmation by remember { mutableStateOf(false) } var showCreateListDialog by remember { mutableStateOf(false) } var showEditDialog by remember { mutableStateOf(false) } var editingItem by remember { mutableStateOf<ListContent?>(null) } var fabMenuExpanded by remember { mutableStateOf(false) } // Initialize the view model with the listId if provided Loading @@ -119,8 +114,7 @@ fun ManagePlacesScreen( } Scaffold( contentWindowInsets = WindowInsets.safeDrawing, topBar = { contentWindowInsets = WindowInsets.safeDrawing, topBar = { ManagePlacesTopBar( navController = navController, title = currentListName ?: stringResource(string.saved_places_title_case), Loading Loading @@ -157,8 +151,7 @@ fun ManagePlacesScreen( coroutineScope.launch { val place = viewModel.getSavedPlace(item.id) ?: return@launch NavigationUtils.navigate( navController, Screen.PlaceCard(place) navController, Screen.PlaceCard(place) ) } } Loading @@ -166,27 +159,20 @@ fun ManagePlacesScreen( is ListContentItem -> { currentListId?.let { currentListId -> NavigationUtils.navigate( navController, Screen.ManagePlaces( item.id, parents = parents.plus(currentListName ?: "") ), avoidCycles = false navController, Screen.ManagePlaces( item.id, parents = parents.plus(currentListName ?: "") ), avoidCycles = false ) } } } }, onEditClick = { editingItem.value = it; showEditDialog.value = true } ) onEditClick = { editingItem = it; showEditDialog = true }) } } val modifier = if (fabMenuExpanded) { Modifier .clickable( indication = null, interactionSource = null, onClick = { fabMenuExpanded = false }) Modifier.clickable( indication = null, interactionSource = null, onClick = { fabMenuExpanded = false }) } else { Modifier } Loading @@ -200,12 +186,9 @@ fun ManagePlacesScreen( modifier = Modifier.align(Alignment.BottomEnd), expanded = fabMenuExpanded, button = { ToggleFloatingActionButton( checked = fabMenuExpanded, onCheckedChange = { ToggleFloatingActionButton(checked = fabMenuExpanded, onCheckedChange = { fabMenuExpanded = it }, content = { }, content = { val close = painterResource(drawable.ic_close) val open = painterResource(drawable.ic_menu) val painter by remember { Loading @@ -218,126 +201,134 @@ fun ManagePlacesScreen( contentDescription = null, modifier = Modifier.animateIcon({ checkedProgress }), ) } ) } ) { FloatingActionButtonMenuItem( onClick = { showCreateListDialog.value = true }) }) { FloatingActionButtonMenuItem(onClick = { showCreateListDialog = true fabMenuExpanded = false }, text = { }, text = { Text( text = stringResource( string.new_list ) ) }, icon = { }, icon = { Icon( painter = painterResource(drawable.ic_new_list), contentDescription = null painter = painterResource(drawable.ic_new_list), contentDescription = null ) }) FloatingActionButtonMenuItem( onClick = { FloatingActionButtonMenuItem(onClick = { viewModel.cutSelected() fabMenuExpanded = false }, text = { }, text = { Text(text = stringResource(string.cut)) }, icon = { }, icon = { Icon( painter = painterResource(drawable.ic_content_cut), contentDescription = null ) } ) FloatingActionButtonMenuItem( onClick = { }) FloatingActionButtonMenuItem(onClick = { viewModel.pasteSelected() fabMenuExpanded = false }, text = { }, text = { Text(text = stringResource(string.paste)) }, icon = { }, icon = { Icon( painter = painterResource(drawable.ic_content_paste), contentDescription = null ) } ) FloatingActionButtonMenuItem( onClick = { showDeleteConfirmation.value = true }) FloatingActionButtonMenuItem(onClick = { showDeleteConfirmation = true fabMenuExpanded = false }, text = { }, text = { Text(text = stringResource(string.delete)) }, icon = { }, icon = { Icon( painter = painterResource(drawable.ic_delete), contentDescription = null painter = painterResource(drawable.ic_delete), contentDescription = null ) } ) FloatingActionButtonMenuItem( onClick = { }) FloatingActionButtonMenuItem(onClick = { if (isAllSelected) { viewModel.clearSelection() } else { viewModel.selectAll() } }, text = { }, text = { Text(text = stringResource(if (isAllSelected) string.deselect_all else string.select_all)) }, icon = { }, icon = { Icon( painter = painterResource(if (isAllSelected) drawable.ic_clear_selection else drawable.ic_select_all), contentDescription = null ) }) } ) } } if (showDeleteConfirmation) { DeleteConfirmationDialog( selectedItemsCount = selectedItems.size, onDelete = { viewModel.deleteSelected() }, onDismiss = { showCreateListDialog = false }) } if (showDeleteConfirmation.value) { if (showCreateListDialog) { CreateListDialog(onCreate = { viewModel.createNewListWithSelected(it) }, onDismiss = { showCreateListDialog = false }) } if (showEditDialog) { EditDialog(editingItem = editingItem, viewModel = viewModel, onDismiss = { showEditDialog = false }) } } @Composable private fun DeleteConfirmationDialog( selectedItemsCount: Int, onDelete: () -> Unit, onDismiss: () -> Unit, ) { AlertDialog( onDismissRequest = { showDeleteConfirmation.value = false }, onDismissRequest = { onDismiss() }, title = { Text(stringResource(string.confirm_delete)) }, text = { Text(stringResource(string.delete_confirmation_message, selectedItems.size)) }, text = { Text(stringResource(string.delete_confirmation_message, selectedItemsCount)) }, confirmButton = { Button(onClick = { viewModel.deleteSelected() showDeleteConfirmation.value = false onDelete() onDismiss() }) { Text(stringResource(string.delete)) } }, dismissButton = { Button(onClick = { showDeleteConfirmation.value = false }) { Button(onClick = { onDismiss() }) { Text(stringResource(string.cancel)) } } ) }) } if (showCreateListDialog.value) { AlertDialog( onDismissRequest = { showCreateListDialog.value = false @Composable private fun CreateListDialog( onCreate: (String) -> Unit, onDismiss: () -> Unit, ) { var newListName by remember { mutableStateOf("") } var showEmptyNameWarning by remember { mutableStateOf(false) } AlertDialog(onDismissRequest = { onDismiss() newListName = "" showEmptyNameWarning = false }, title = { Text(stringResource(string.add_new_list)) }, text = { }, title = { Text(stringResource(string.add_new_list)) }, text = { Column { OutlinedTextField( value = newListName, Loading @@ -357,35 +348,41 @@ fun ManagePlacesScreen( ) } } }, confirmButton = { }, confirmButton = { Button(onClick = { if (newListName.isBlank()) { showEmptyNameWarning = true } else { viewModel.createNewListWithSelected(newListName) showCreateListDialog.value = false onCreate(newListName) onDismiss() newListName = "" showEmptyNameWarning = false } }) { Text(stringResource(string.add_new_list)) } }, dismissButton = { }, dismissButton = { Button(onClick = { showCreateListDialog.value = false onDismiss() newListName = "" showEmptyNameWarning = false }) { Text(stringResource(string.cancel)) } } ) }) } if (showEditDialog.value) { editingItem.value?.let { item -> @Composable private fun EditDialog( editingItem: ListContent?, viewModel: ManagePlacesViewModel, onDismiss: () -> Unit, ) { editingItem?.let { item -> var editName by remember { mutableStateOf("") } var editDescription by remember { mutableStateOf("") } var editPinned by remember { mutableStateOf(false) } LaunchedEffect(item) { when (item) { is PlaceContent -> { Loading @@ -407,10 +404,7 @@ fun ManagePlacesScreen( is ListContentItem -> stringResource(string.edit_list) } AlertDialog( onDismissRequest = { showEditDialog.value = false }, title = { Text(title) }, text = { AlertDialog(onDismissRequest = { onDismiss() }, title = { Text(title) }, text = { Column { OutlinedTextField( value = editName, Loading @@ -427,61 +421,39 @@ fun ManagePlacesScreen( if (item is PlaceContent) { Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( checked = editPinned, onCheckedChange = { editPinned = it } ) checked = editPinned, onCheckedChange = { editPinned = it }) Text(stringResource(string.pin_place)) } } } }, confirmButton = { }, confirmButton = { Button(onClick = { val name = editName.ifBlank { null } val desc = editDescription // Allow blank descriptions. val desc = editDescription when (item) { is PlaceContent -> viewModel.updatePlace( item.id, name, desc, editPinned item.id, name, desc, editPinned ) is ListContentItem -> viewModel.updateList(item.id, name, desc) } showEditDialog.value = false onDismiss() }) { Text(stringResource(string.save)) } }, dismissButton = { Button(onClick = { showEditDialog.value = false }) { }, dismissButton = { Button(onClick = { onDismiss() }) { Text(stringResource(string.cancel)) } } ) } }) } } @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable private fun ManagePlacesTopBar( private fun Breadcrumbs( navController: NavController, title: String, breadcrumbNames: List<String>, ) { TopAppBar( title = { Column { Text( text = title, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, textAlign = TextAlign.Start, ) // Breadcrumbs if (breadcrumbNames.size > 1) { Row( modifier = Modifier .fillMaxWidth() Loading Loading @@ -513,14 +485,33 @@ private fun ManagePlacesTopBar( } } else { Modifier } ) }) } } } @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable private fun ManagePlacesTopBar( navController: NavController, title: String, breadcrumbNames: List<String>, ) { TopAppBar( title = { Column { Text( text = title, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, textAlign = TextAlign.Start, ) // Breadcrumbs if (breadcrumbNames.size > 1) { Breadcrumbs(navController, breadcrumbNames) } } ) }) } @Composable Loading Loading @@ -570,8 +561,7 @@ private fun ListContentGrid( if (shouldShowListsHeader) { item { SectionHeader( title = stringResource(string.saved_lists), modifier = Modifier.padding( title = stringResource(string.saved_lists), modifier = Modifier.padding( vertical = dimensionResource(dimen.padding_minor), horizontal = dimensionResource(dimen.padding) ) Loading Loading @@ -647,8 +637,7 @@ private fun ListContentGrid( @Composable private fun SectionHeader( title: String, modifier: Modifier = Modifier title: String, modifier: Modifier = Modifier ) { Text( text = title, Loading