From 076ee7b5675870ebd55718591503ba35baff2a69 Mon Sep 17 00:00:00 2001 From: hegocre <15657088+hegocre@users.noreply.github.com> Date: Sat, 7 Sep 2024 01:06:44 +0200 Subject: [PATCH 1/3] Improve bottom sheet animations --- .../ui/components/NCPApp.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPApp.kt b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPApp.kt index 0031d5a2..f23779fa 100644 --- a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPApp.kt +++ b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPApp.kt @@ -73,7 +73,8 @@ fun NextcloudPasswordsApp( backstackEntry.value?.destination?.route ) - val modalSheetState = rememberModalBottomSheetState() + var openBottomSheet by rememberSaveable { mutableStateOf(false) } + val modalSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val needsMasterPassword by passwordsViewModel.needsMasterPassword.collectAsState() val masterPasswordInvalid by passwordsViewModel.masterPasswordInvalid.collectAsState() @@ -223,9 +224,7 @@ fun NextcloudPasswordsApp( openPasswordDetails = { password -> passwordsViewModel.setVisiblePassword(password) keyboardController?.hide() - coroutineScope.launch { - modalSheetState.show() - } + openBottomSheet = true }, replyAutofill = replyAutofill, searchVisibility = searchExpanded, @@ -279,14 +278,11 @@ fun NextcloudPasswordsApp( ) } - if (modalSheetState.isVisible) { + if (openBottomSheet) { ModalBottomSheet( - onDismissRequest = { - coroutineScope.launch { - modalSheetState.hide() - } - }, - contentWindowInsets = { WindowInsets.navigationBars } + onDismissRequest = { openBottomSheet = false }, + contentWindowInsets = { WindowInsets.navigationBars }, + sheetState = modalSheetState ) { PasswordItem( password = passwordsViewModel.visiblePassword.value, @@ -294,6 +290,10 @@ fun NextcloudPasswordsApp( { coroutineScope.launch { modalSheetState.hide() + }.invokeOnCompletion { + if (!modalSheetState.isVisible) { + openBottomSheet = false + } } navController.navigate("${NCPScreen.PasswordEdit.name}/${passwordsViewModel.visiblePassword.value?.id ?: "none"}") } From 850a91ac69be07b7e734f7eebb6dcbdc0ed65e79 Mon Sep 17 00:00:00 2001 From: hegocre <15657088+hegocre@users.noreply.github.com> Date: Sat, 7 Sep 2024 01:06:51 +0200 Subject: [PATCH 2/3] Make notes text selectable --- .../nextcloudpasswords/ui/components/PasswordItem.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt index 6f161763..ff9f87a5 100644 --- a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt +++ b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.appendInlineContent +import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Star @@ -475,7 +476,11 @@ fun PasswordMarkdownField( color = MaterialTheme.colorScheme.onSurfaceVariant ) } - MDDocument(root) + SelectionContainer { + Column { + MDDocument(root) + } + } } } From c8c88bebb28bb38384c161e26a7aedf24de25589 Mon Sep 17 00:00:00 2001 From: hegocre <15657088+hegocre@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:16:10 +0200 Subject: [PATCH 3/3] Display folder path under password label on details view --- .../ui/components/NCPApp.kt | 8 +-- .../ui/components/NCPNavHost.kt | 16 ++++- .../ui/components/PasswordItem.kt | 63 +++++++++++++++++-- .../ui/viewmodels/PasswordsViewModel.kt | 6 +- 4 files changed, 78 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPApp.kt b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPApp.kt index f23779fa..7b1dcae1 100644 --- a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPApp.kt +++ b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPApp.kt @@ -221,8 +221,8 @@ fun NextcloudPasswordsApp( searchQuery = searchQuery, isAutofillRequest = isAutofillRequest, modalSheetState = modalSheetState, - openPasswordDetails = { password -> - passwordsViewModel.setVisiblePassword(password) + openPasswordDetails = { password, folderPath -> + passwordsViewModel.setVisiblePassword(password, folderPath) keyboardController?.hide() openBottomSheet = true }, @@ -285,7 +285,7 @@ fun NextcloudPasswordsApp( sheetState = modalSheetState ) { PasswordItem( - password = passwordsViewModel.visiblePassword.value, + passwordInfo = passwordsViewModel.visiblePassword.value, onEditPassword = if (sessionOpen) { { coroutineScope.launch { @@ -295,7 +295,7 @@ fun NextcloudPasswordsApp( openBottomSheet = false } } - navController.navigate("${NCPScreen.PasswordEdit.name}/${passwordsViewModel.visiblePassword.value?.id ?: "none"}") + navController.navigate("${NCPScreen.PasswordEdit.name}/${passwordsViewModel.visiblePassword.value?.first?.id ?: "none"}") } } else null, modifier = Modifier.padding(bottom = 16.dp) diff --git a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPNavHost.kt b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPNavHost.kt index 6c352705..27112b87 100644 --- a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPNavHost.kt +++ b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/NCPNavHost.kt @@ -66,7 +66,7 @@ fun NCPNavHost( modifier: Modifier = Modifier, searchQuery: String = "", isAutofillRequest: Boolean, - openPasswordDetails: (Password) -> Unit, + openPasswordDetails: (Password, List) -> Unit, replyAutofill: ((String, String, String) -> Unit)? = null, modalSheetState: SheetState? = null, searchVisibility: Boolean? = null, @@ -101,11 +101,23 @@ fun NCPNavHost( } ?: emptyList()) } + val baseFolderName = stringResource(R.string.top_level_folder_name) val onPasswordClick: (Password) -> Unit = { password -> if (isAutofillRequest && replyAutofill != null) { replyAutofill(password.label, password.username, password.password) } else { - openPasswordDetails(password) + val folderPath = mutableListOf() + var nextFolderUuid = password.folder + while (nextFolderUuid != FoldersApi.DEFAULT_FOLDER_UUID) { + val nextFolder = + foldersDecryptionState.decryptedList?.find { it.id == nextFolderUuid } + nextFolder?.label?.let { + folderPath.add(it) + } + nextFolderUuid = nextFolder?.parent ?: FoldersApi.DEFAULT_FOLDER_UUID + } + folderPath.add(baseFolderName) + openPasswordDetails(password, folderPath.toList()) } } diff --git a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt index ff9f87a5..d984818b 100644 --- a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt +++ b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/components/PasswordItem.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.text.appendInlineContent import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Folder import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.twotone.AccountCircle import androidx.compose.material.icons.twotone.AlternateEmail @@ -55,6 +56,7 @@ import com.hegocre.nextcloudpasswords.R import com.hegocre.nextcloudpasswords.data.password.CustomField import com.hegocre.nextcloudpasswords.data.password.Password import com.hegocre.nextcloudpasswords.ui.components.markdown.MDDocument +import com.hegocre.nextcloudpasswords.ui.theme.ContentAlpha import com.hegocre.nextcloudpasswords.ui.theme.NextcloudPasswordsTheme import com.hegocre.nextcloudpasswords.ui.theme.favoriteColor import com.hegocre.nextcloudpasswords.utils.copyToClipboard @@ -64,12 +66,16 @@ import org.commonmark.parser.Parser @Composable fun PasswordItem( - password: Password?, + passwordInfo: Pair>?, modifier: Modifier = Modifier, onEditPassword: (() -> Unit)? = null, ) { - password?.let { pass -> - PasswordItemContent(password = pass, onEditPassword = onEditPassword, modifier = modifier) + passwordInfo?.let { pass -> + PasswordItemContent( + passwordInfo = pass, + onEditPassword = onEditPassword, + modifier = modifier + ) } ?: Text( text = stringResource(R.string.password), style = MaterialTheme.typography.headlineMedium, @@ -79,7 +85,7 @@ fun PasswordItem( @Composable fun PasswordItemContent( - password: Password, + passwordInfo: Pair>, onEditPassword: (() -> Unit)?, modifier: Modifier = Modifier ) { @@ -88,6 +94,18 @@ fun PasswordItemContent( val uriHandler = LocalUriHandler.current + val password = passwordInfo.first + val folderPath = remember { + buildAnnotatedString { + appendInlineContent("folder") + append(" ") + passwordInfo.second.reversed().forEachIndexed { index, folderName -> + if (index != 0) append(" /") + append(" $folderName") + } + } + } + val customFields by remember { derivedStateOf { if (password.customFields.isNotBlank()) { @@ -101,7 +119,7 @@ fun PasswordItemContent( Column(modifier = modifier) { Row( modifier = Modifier - .padding(bottom = 8.dp) + .padding(bottom = 4.dp) .padding(horizontal = 16.dp), verticalAlignment = CenterVertically ) { @@ -151,6 +169,37 @@ fun PasswordItemContent( } } LazyColumn { + item(key = "${password.id}_path") { + val folderInlineContent = mapOf( + Pair( + "folder", + InlineTextContent( + placeholder = Placeholder( + width = LocalTextStyle.current.fontSize, + height = LocalTextStyle.current.fontSize, + placeholderVerticalAlign = PlaceholderVerticalAlign.Center + ) + ) { + Icon( + imageVector = Icons.Default.Folder, + contentDescription = stringResource( + id = R.string.folder + ), + tint = MaterialTheme.colorScheme.onSurface + .copy(alpha = ContentAlpha.medium) + ) + } + ) + ) + Text( + text = folderPath, + inlineContent = folderInlineContent, + modifier = Modifier + .padding(bottom = 16.dp) + .padding(horizontal = 16.dp) + ) + } + if (password.username.isNotBlank()) { item(key = "${password.id}_username") { val usernameLabel = stringResource(id = R.string.password_attr_username) @@ -490,7 +539,8 @@ fun PasswordItemPreview() { NextcloudPasswordsTheme { Surface { PasswordItem( - password = Password( + passwordInfo = Pair( + Password( id = "", label = "Nextcloud with a really long label", username = "john_doe", @@ -518,6 +568,7 @@ fun PasswordItemPreview() { edited = 0, created = 0, updated = 0 + ), listOf("Second", "Home") ), onEditPassword = {}, modifier = Modifier.padding(bottom = 16.dp) diff --git a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/viewmodels/PasswordsViewModel.kt b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/viewmodels/PasswordsViewModel.kt index 75e6b784..64a270ea 100644 --- a/app/src/main/java/com/hegocre/nextcloudpasswords/ui/viewmodels/PasswordsViewModel.kt +++ b/app/src/main/java/com/hegocre/nextcloudpasswords/ui/viewmodels/PasswordsViewModel.kt @@ -104,7 +104,7 @@ class PasswordsViewModel(application: Application) : AndroidViewModel(applicatio val folders: LiveData> get() = FolderController.getInstance(getApplication()).getFolders() - var visiblePassword = mutableStateOf(null) + var visiblePassword = mutableStateOf>?>(null) private set var visibleFolder = mutableStateOf(null) private set @@ -208,8 +208,8 @@ class PasswordsViewModel(application: Application) : AndroidViewModel(applicatio } } - fun setVisiblePassword(password: Password) { - visiblePassword.value = password + fun setVisiblePassword(password: Password, folderPath: List) { + visiblePassword.value = Pair(password, folderPath) } fun setVisibleFolder(folder: Folder?) {