From dbbd0fcf05c7f7c4b30f060d3f9d271fb2b31615 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Thu, 30 Oct 2025 23:11:19 +0100 Subject: [PATCH 1/3] remove vm in DrawerScreen --- .../gitnote/ui/screen/app/DrawerScreen.kt | 39 ++++++++++--------- .../gitnote/ui/screen/app/grid/GridScreen.kt | 7 +++- .../gitnote/ui/viewmodel/GridViewModel.kt | 1 + 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt index 960ae700..ee98ed41 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt @@ -33,8 +33,6 @@ import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -60,7 +58,6 @@ import io.github.wiiznokes.gitnote.ui.component.SimpleIcon import io.github.wiiznokes.gitnote.ui.component.SimpleSpacer import io.github.wiiznokes.gitnote.ui.theme.IconDefaultSize import io.github.wiiznokes.gitnote.ui.theme.LocalSpaces -import io.github.wiiznokes.gitnote.ui.viewmodel.GridViewModel import kotlinx.coroutines.launch @@ -75,11 +72,13 @@ data class DrawerFolderModel( @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) @Composable fun DrawerScreen( - vm: GridViewModel, - drawerState: DrawerState + drawerState: DrawerState, + currentNoteFolderRelativePath: String, + drawerFolders: List, + openFolder: (String) -> Unit, + deleteFolder: (NoteFolder) -> Unit, + createNoteFolder: (relativeParentPath: String, name: String) -> Boolean, ) { - val currentNoteFolderRelativePath by vm.currentNoteFolderRelativePath.collectAsState() - val currentNoteFolders by vm.drawerFolders.collectAsState() fun getParent(path: String) = path.substringBeforeLast( delimiter = "/", @@ -91,15 +90,16 @@ fun DrawerScreen( if (currentNoteFolderRelativePath.isEmpty()) { scope.launch { drawerState.close() } } else { - vm.openFolder(getParent(currentNoteFolderRelativePath)) + openFolder(getParent(currentNoteFolderRelativePath)) } } Scaffold( topBar = { RowNFoldersNavigation( - vm = vm, - currentPath = currentNoteFolderRelativePath + currentPath = currentNoteFolderRelativePath, + openFolder = openFolder, + createNoteFolder = createNoteFolder ) }, floatingActionButton = { @@ -112,7 +112,7 @@ fun DrawerScreen( containerColor = MaterialTheme.colorScheme.secondary, shape = RoundedCornerShape(20.dp), onClick = { - vm.openFolder(getParent(currentNoteFolderRelativePath)) + openFolder(getParent(currentNoteFolderRelativePath)) } ) { SimpleIcon( @@ -134,7 +134,7 @@ fun DrawerScreen( ) { items( - currentNoteFolders, + drawerFolders, key = { it.noteFolder.id }) { drawerNoteFolder -> Box { val dropDownExpanded = remember { @@ -154,7 +154,7 @@ fun DrawerScreen( CustomDropDownModel( text = stringResource(R.string.delete_this_folder), onClick = { - vm.deleteFolder(drawerNoteFolder.noteFolder) + deleteFolder(drawerNoteFolder.noteFolder) } ), ), @@ -172,7 +172,7 @@ fun DrawerScreen( dropDownExpanded.value = true }, onClick = { - vm.openFolder(drawerNoteFolder.noteFolder.relativePath) + openFolder(drawerNoteFolder.noteFolder.relativePath) } ) .pointerInteropFilter { @@ -227,8 +227,9 @@ fun DrawerScreen( @OptIn(ExperimentalMaterial3Api::class) @Composable fun RowNFoldersNavigation( - vm: GridViewModel, - currentPath: String + currentPath: String, + openFolder: (String) -> Unit, + createNoteFolder: (relativeParentPath: String, name: String) -> Boolean, ) { val containers = if (currentPath.isEmpty()) emptyList() else currentPath.split('/') @@ -242,7 +243,7 @@ fun RowNFoldersNavigation( navigationIcon = { IconButton( onClick = { - vm.openFolder("") + openFolder("") } ) { Icon( @@ -278,7 +279,7 @@ fun RowNFoldersNavigation( folder } } - vm.openFolder(path) + openFolder(path) }, text = item, maxLines = 1, @@ -308,7 +309,7 @@ fun RowNFoldersNavigation( actionText = stringResource(R.string.create_new_folder), unExpandedOnValidation = false ) { - if (vm.createNoteFolder(currentPath, it)) { + if (createNoteFolder(currentPath, it)) { showCreateNewFolder.value = false } } diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/GridScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/GridScreen.kt index a29fa8ca..6da28701 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/GridScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/GridScreen.kt @@ -93,7 +93,12 @@ fun GridScreen( ModalNavigationDrawer(drawerState = drawerState, drawerContent = { ModalDrawerSheet { DrawerScreen( - vm = vm, drawerState = drawerState + drawerState = drawerState, + currentNoteFolderRelativePath = vm.currentNoteFolderRelativePath.collectAsState().value, + drawerFolders = vm.drawerFolders.collectAsState().value, + openFolder = vm::openFolder, + deleteFolder = vm::deleteFolder, + createNoteFolder = vm::createNoteFolder, ) } }) { diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt index b596daab..3f972eb3 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt @@ -226,6 +226,7 @@ class GridViewModel : ViewModel() { CoroutineScope(Dispatchers.IO), SharingStarted.WhileSubscribed(5000), PagingData.empty() ) + // todo: use pager @OptIn(ExperimentalCoroutinesApi::class) val drawerFolders = combine( currentNoteFolderRelativePath, From ff3b4f8d9862233edb63dc5afea9a6125f6ac169 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Fri, 31 Oct 2025 00:47:42 +0100 Subject: [PATCH 2/3] add dialog --- .../wiiznokes/gitnote/data/AppPreferences.kt | 2 +- .../github/wiiznokes/gitnote/data/room/Dao.kt | 19 ++ .../gitnote/ui/component/BaseDialog.kt | 9 +- .../gitnote/ui/component/PickFolderDialog.kt | 278 ++++++++++++++++++ .../gitnote/ui/screen/app/DrawerScreen.kt | 10 +- .../gitnote/ui/screen/app/edit/BottomBar.kt | 6 + .../ui/screen/settings/SettingsScreen.kt | 17 ++ .../gitnote/ui/viewmodel/GridViewModel.kt | 9 +- .../github/wiiznokes/gitnote/utils/utils.kt | 5 + app/src/main/res/values/strings.xml | 3 + 10 files changed, 347 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/io/github/wiiznokes/gitnote/ui/component/PickFolderDialog.kt diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/data/AppPreferences.kt b/app/src/main/java/io/github/wiiznokes/gitnote/data/AppPreferences.kt index f7a19d31..91469d99 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/data/AppPreferences.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/data/AppPreferences.kt @@ -108,7 +108,7 @@ class AppPreferences( val provider = enumPreference("provider", ProviderType.GitHub) - + val defaultPathForNewNote = stringPreference("defaultPathForNewNote", "") val sortOrder = enumPreference("sortOrder", SortOrder.MostRecent) val sortOrderFolder = enumPreference("sortOrderFolder", SortOrder.AZ) diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Dao.kt b/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Dao.kt index f5c6207d..61b79bbf 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Dao.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Dao.kt @@ -222,6 +222,25 @@ interface RepoDatabaseDao { return this.gridDrawerFoldersRaw(query) } + @RawQuery(observedEntities = [Note::class, NoteFolder::class]) + fun noteFoldersRaw(query: SupportSQLiteQuery): Flow> + + // todo: use pages + fun noteFolders( + currentNoteFolderRelativePath: String, + ): Flow> { + + val sql = """ + SELECT relativePath, id, fullName(relativePath) as folderName + FROM NoteFolders + WHERE parentPath(relativePath) = ? + ORDER BY folderName ASC + """.trimIndent() + + val query = SimpleSQLiteQuery(sql, arrayOf(currentNoteFolderRelativePath)) + return this.noteFoldersRaw(query) + } + data class Testing( val relativePath: String, val id: Int, diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/BaseDialog.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/BaseDialog.kt index d3c1c3a7..58771be9 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/BaseDialog.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/BaseDialog.kt @@ -14,10 +14,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog +import io.github.wiiznokes.gitnote.ui.utils.conditional @Composable fun BaseDialog( expanded: MutableState, + modifier: Modifier = Modifier, + verticalScrollEnabled: Boolean = true, dialogContent: @Composable ColumnScope.(MutableState) -> Unit ) { if (expanded.value) { @@ -27,9 +30,11 @@ fun BaseDialog( } ) { Surface( - modifier = Modifier + modifier = modifier .fillMaxWidth() - .verticalScroll(rememberScrollState()), + .conditional(verticalScrollEnabled) { + verticalScroll(rememberScrollState()) + }, shape = MaterialTheme.shapes.medium, color = MaterialTheme.colorScheme.surface, contentColor = MaterialTheme.colorScheme.onSurface diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/PickFolderDialog.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/PickFolderDialog.kt new file mode 100644 index 00000000..6f8bbf77 --- /dev/null +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/PickFolderDialog.kt @@ -0,0 +1,278 @@ +package io.github.wiiznokes.gitnote.ui.component + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.Button +import androidx.compose.material3.Checkbox +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Folder +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.LayoutDirection +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.compose.viewModel +import io.github.wiiznokes.gitnote.MyApp +import io.github.wiiznokes.gitnote.R +import io.github.wiiznokes.gitnote.data.AppPreferences +import io.github.wiiznokes.gitnote.data.room.NoteFolder +import io.github.wiiznokes.gitnote.data.room.RepoDatabase +import io.github.wiiznokes.gitnote.helper.NameValidation +import io.github.wiiznokes.gitnote.manager.StorageManager +import io.github.wiiznokes.gitnote.ui.screen.app.RowNFoldersNavigation +import io.github.wiiznokes.gitnote.ui.theme.IconDefaultSize +import io.github.wiiznokes.gitnote.ui.theme.LocalSpaces +import io.github.wiiznokes.gitnote.utils.getParentPath +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + + +private const val TAG = "PickFolder" + +class PickFolderVm: ViewModel() { + + val prefs: AppPreferences = MyApp.appModule.appPreferences + private val db: RepoDatabase = MyApp.appModule.repoDatabase + private val dao = db.repoDatabaseDao + val uiHelper = MyApp.appModule.uiHelper + private val storageManager: StorageManager = MyApp.appModule.storageManager + + + private val _currentNoteFolderRelativePath = MutableStateFlow("") + + val currentNoteFolderRelativePath: StateFlow + get() = _currentNoteFolderRelativePath.asStateFlow() + + private val _selected: MutableStateFlow = MutableStateFlow(null) + + val selected: StateFlow + get() = _selected.asStateFlow() + + fun select(value: String?) { + viewModelScope.launch { + _selected.emit(value) + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + val noteFolders = currentNoteFolderRelativePath.flatMapLatest { currentNoteFolderRelativePath -> + dao.noteFolders(currentNoteFolderRelativePath) + }.stateIn( + CoroutineScope(Dispatchers.IO), SharingStarted.WhileSubscribed(5000), emptyList() + ) + + fun openFolder(relativePath: String) { + viewModelScope.launch { + _currentNoteFolderRelativePath.emit(relativePath) + } + } + + fun createNoteFolder(relativeParentPath: String, name: String): Boolean { + if (!NameValidation.check(name)) { + uiHelper.makeToast(uiHelper.getString(R.string.error_invalid_name)) + return false + } + + val relativePath = "$relativeParentPath/$name" + + val noteFolder = NoteFolder.new( + relativePath = relativePath + ) + + if (noteFolder.toFolderFs(prefs.repoPathBlocking()).exist()) { + uiHelper.makeToast(uiHelper.getString(R.string.error_folder_already_exist)) + return false + } + + CoroutineScope(Dispatchers.IO).launch { + storageManager.createNoteFolder(noteFolder) + } + + return true + } + +} + +@Composable +fun PickFolderDialog( + expanded: MutableState, + onSelectedFolder: (String) -> Unit, +) { + + if (expanded.value) { + val vm: PickFolderVm = viewModel() + + PickFolderDialogInternal( + expanded = expanded, + onSelectedFolder = onSelectedFolder, + selected = vm.selected.collectAsState().value, + select = vm::select, + currentNoteFolderRelativePath = vm.currentNoteFolderRelativePath.collectAsState().value, + noteFolders = vm.noteFolders.collectAsState().value, + openFolder = vm::openFolder, + createNoteFolder = vm::createNoteFolder + ) + } +} +@Composable +private fun PickFolderDialogInternal( + expanded: MutableState, + selected: String?, + select: (String?) -> Unit, + currentNoteFolderRelativePath: String, + noteFolders: List, + openFolder: (String) -> Unit, + createNoteFolder: (relativeParentPath: String, name: String) -> Boolean, + onSelectedFolder: (String) -> Unit, +) { + + BackHandler(enabled = expanded.value) { + if (currentNoteFolderRelativePath.isEmpty()) { + expanded.value = false + select(null) + } else { + openFolder(getParentPath(currentNoteFolderRelativePath)) + select(null) + } + } + + BaseDialog( + expanded = expanded, + modifier = Modifier + .fillMaxHeight(0.8f), + verticalScrollEnabled = false + ) { + + RowNFoldersNavigation( + currentPath = currentNoteFolderRelativePath, + openFolder = openFolder, + createNoteFolder = createNoteFolder + ) + + val listState = rememberLazyListState() + + LazyColumn( + modifier = Modifier + .weight(1f), + state = listState + ) { + + items( + noteFolders, + key = { it.id }) { noteFolder -> + + CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { + + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + openFolder(noteFolder.relativePath) + select(null) + }, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + + CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) { + + Checkbox( + modifier = Modifier + .padding(LocalSpaces.current.smallPadding), + checked = selected == noteFolder.relativePath, + onCheckedChange = { + if (it) + select(noteFolder.relativePath) + else + select(null) + } + ) + + Row( + modifier = Modifier + .padding(LocalSpaces.current.smallPadding), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically + ) { + SimpleIcon( + modifier = Modifier + .size(IconDefaultSize), + imageVector = Icons.Rounded.Folder + ) + + SimpleSpacer(width = LocalSpaces.current.smallPadding) + + Text( + text = noteFolder.fullName(), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } + } + } + } + } + + Button( + enabled = selected != null, + onClick = { + selected?.let { + onSelectedFolder(selected) + expanded.value = false + } + } + ) { + + Text(stringResource(R.string.pick_folder)) + } + } +} + + +@Composable +@Preview +private fun PickFolderDialogPreview() { + PickFolderDialogInternal( + expanded = remember { mutableStateOf(true) }, + selected = null, + select = {}, + currentNoteFolderRelativePath = "", + noteFolders = listOf(), + openFolder = {}, + createNoteFolder = { _,_ -> + true + }, + onSelectedFolder = {}, + ) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt index ee98ed41..c4d3b4d7 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt @@ -58,6 +58,7 @@ import io.github.wiiznokes.gitnote.ui.component.SimpleIcon import io.github.wiiznokes.gitnote.ui.component.SimpleSpacer import io.github.wiiznokes.gitnote.ui.theme.IconDefaultSize import io.github.wiiznokes.gitnote.ui.theme.LocalSpaces +import io.github.wiiznokes.gitnote.utils.getParentPath import kotlinx.coroutines.launch @@ -80,17 +81,14 @@ fun DrawerScreen( createNoteFolder: (relativeParentPath: String, name: String) -> Boolean, ) { - fun getParent(path: String) = path.substringBeforeLast( - delimiter = "/", - missingDelimiterValue = "" - ) + val scope = rememberCoroutineScope() BackHandler(enabled = drawerState.isOpen) { if (currentNoteFolderRelativePath.isEmpty()) { scope.launch { drawerState.close() } } else { - openFolder(getParent(currentNoteFolderRelativePath)) + openFolder(getParentPath(currentNoteFolderRelativePath)) } } @@ -112,7 +110,7 @@ fun DrawerScreen( containerColor = MaterialTheme.colorScheme.secondary, shape = RoundedCornerShape(20.dp), onClick = { - openFolder(getParent(currentNoteFolderRelativePath)) + openFolder(getParentPath(currentNoteFolderRelativePath)) } ) { SimpleIcon( diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/edit/BottomBar.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/edit/BottomBar.kt index 5a8f2e2d..6f4776a8 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/edit/BottomBar.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/edit/BottomBar.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.github.wiiznokes.gitnote.R import io.github.wiiznokes.gitnote.ui.viewmodel.edit.TextVM +import io.github.wiiznokes.gitnote.utils.getParentPath val bottomBarHeight = 50.dp @@ -90,6 +91,11 @@ fun DefaultRow( .padding(10.dp), text = stringResource(R.string.extension, vm.previousNote.fileExtension().text) ) + Text( + modifier = Modifier + .padding(10.dp), + text = stringResource(R.string.parent, getParentPath(vm.previousNote.relativePath)) + ) } } diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/settings/SettingsScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/settings/SettingsScreen.kt index b7d4ae28..c1bad130 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/settings/SettingsScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/settings/SettingsScreen.kt @@ -28,6 +28,7 @@ import io.github.wiiznokes.gitnote.R import io.github.wiiznokes.gitnote.ui.component.AppPage import io.github.wiiznokes.gitnote.ui.component.DefaultSettingsRow import io.github.wiiznokes.gitnote.ui.component.MultipleChoiceSettings +import io.github.wiiznokes.gitnote.ui.component.PickFolderDialog import io.github.wiiznokes.gitnote.ui.component.RequestConfirmationDialog import io.github.wiiznokes.gitnote.ui.component.SettingsSection import io.github.wiiznokes.gitnote.ui.component.SimpleIcon @@ -143,6 +144,22 @@ fun SettingsScreen( } ) + + val defaultPathForNewNote by vm.prefs.defaultPathForNewNote.getAsState() + val pickFolderDialogExpanded = rememberSaveable { mutableStateOf(false) } + DefaultSettingsRow( + title = stringResource(R.string.defaultPathForNewNote), + subTitle = "Only when located in the root folder.\nCurrent value: \"$defaultPathForNewNote\"", + onClick = { pickFolderDialogExpanded.value = true } + ) + + PickFolderDialog( + expanded = pickFolderDialogExpanded, + onSelectedFolder = { + vm.update { vm.prefs.defaultPathForNewNote.update(it) } + } + ) + /* DefaultSettingsRow( title = stringResource(R.string.folder_filters), diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt index 3f972eb3..a2205fc1 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt @@ -166,7 +166,6 @@ class GridViewModel : ViewModel() { } fun deleteNote(note: Note) { - selectNote(note, false) CoroutineScope(Dispatchers.IO).launch { storageManager.deleteNote(note) } @@ -190,8 +189,14 @@ class GridViewModel : ViewModel() { val defaultExtension = FileExtension.match(prefs.defaultExtension.getBlocking()) val defaultFullName = "$defaultName.${defaultExtension.text}" + val currentNoteFolderRelativePath = currentNoteFolderRelativePath.value + + val parent = if (currentNoteFolderRelativePath == "") { + prefs.defaultPathForNewNote.getBlocking() + } else currentNoteFolderRelativePath + return Note.new( - relativePath = "${currentNoteFolderRelativePath.value}/$defaultFullName", + relativePath = "$parent/$defaultFullName", ) } diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/utils/utils.kt b/app/src/main/java/io/github/wiiznokes/gitnote/utils/utils.kt index 39fdab58..d0faa9b9 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/utils/utils.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/utils/utils.kt @@ -89,3 +89,8 @@ inline fun Flow.mapAndCombine(crossinline transform: suspend (value: T transform { value -> return@transform emit(Pair(value, transform(value))) } + +fun getParentPath(path: String) = path.substringBeforeLast( + delimiter = "/", + missingDelimiterValue = "" +) \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ce8e62b4..c185c487 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,6 +19,7 @@ Dynamic colors Sort Order Sort Order folder + Default path for new notes Minimal width of a note Show long notes entirely Remember last opened folder @@ -148,5 +149,7 @@ Loading… Logs Extension: %1$s + Parent: %1$s + Pick this folder \ No newline at end of file From 8955c32614f8950b1326d47253be3c2d3b00f158 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Fri, 31 Oct 2025 00:48:43 +0100 Subject: [PATCH 3/3] fmt --- .../github/wiiznokes/gitnote/data/room/Dao.kt | 6 +++- .../gitnote/manager/StorageManager.kt | 14 +++++--- .../gitnote/ui/component/PickFolderDialog.kt | 13 ++++---- .../gitnote/ui/screen/app/DrawerScreen.kt | 1 - .../gitnote/ui/screen/app/edit/BottomBar.kt | 5 ++- .../ui/screen/setup/remote/PickRepoScreen.kt | 1 - .../gitnote/ui/viewmodel/SetupViewModel.kt | 32 ++++++++++--------- 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Dao.kt b/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Dao.kt index 61b79bbf..9c82b3fa 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Dao.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Dao.kt @@ -30,7 +30,11 @@ interface RepoDatabaseDao { // todo: use @Transaction // todo: don't clear the all database each time - suspend fun clearAndInit(rootPath: String, timestamps: HashMap, progressCb: ((Progress) -> Unit)? = null) { + suspend fun clearAndInit( + rootPath: String, + timestamps: HashMap, + progressCb: ((Progress) -> Unit)? = null + ) { Log.d(TAG, "clearAndInit") clearDatabase() diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/manager/StorageManager.kt b/app/src/main/java/io/github/wiiznokes/gitnote/manager/StorageManager.kt index 95bd3a59..fbe4b2f7 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/manager/StorageManager.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/manager/StorageManager.kt @@ -32,9 +32,9 @@ sealed interface SyncState { } sealed class Progress { - data object Timestamps: Progress() + data object Timestamps : Progress() - data class GeneratingDatabase(val path: String): Progress() + data class GeneratingDatabase(val path: String) : Progress() } class StorageManager { @@ -101,7 +101,10 @@ class StorageManager { * The caller must ensure that all files has been committed * to keep the database in sync with the remote repo */ - private suspend fun updateDatabaseWithoutLocker(force: Boolean = false, progressCb: ((Progress) -> Unit)? = null): Result { + private suspend fun updateDatabaseWithoutLocker( + force: Boolean = false, + progressCb: ((Progress) -> Unit)? = null + ): Result { val fsCommit = gitManager.lastCommit() val databaseCommit = prefs.databaseCommit.get() @@ -127,7 +130,10 @@ class StorageManager { /** * See the documentation of [updateDatabaseWithoutLocker] */ - suspend fun updateDatabase(force: Boolean = false, progressCb: ((Progress) -> Unit)? = null): Result = locker.withLock { + suspend fun updateDatabase( + force: Boolean = false, + progressCb: ((Progress) -> Unit)? = null + ): Result = locker.withLock { updateDatabaseWithoutLocker(force, progressCb) } diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/PickFolderDialog.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/PickFolderDialog.kt index 6f8bbf77..656fc0d6 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/PickFolderDialog.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/PickFolderDialog.kt @@ -6,16 +6,15 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.Button -import androidx.compose.material3.Checkbox import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Folder +import androidx.compose.material3.Button +import androidx.compose.material3.Checkbox import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -51,7 +50,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -59,7 +57,7 @@ import kotlinx.coroutines.launch private const val TAG = "PickFolder" -class PickFolderVm: ViewModel() { +class PickFolderVm : ViewModel() { val prefs: AppPreferences = MyApp.appModule.appPreferences private val db: RepoDatabase = MyApp.appModule.repoDatabase @@ -130,7 +128,7 @@ fun PickFolderDialog( ) { if (expanded.value) { - val vm: PickFolderVm = viewModel() + val vm: PickFolderVm = viewModel() PickFolderDialogInternal( expanded = expanded, @@ -144,6 +142,7 @@ fun PickFolderDialog( ) } } + @Composable private fun PickFolderDialogInternal( expanded: MutableState, @@ -270,7 +269,7 @@ private fun PickFolderDialogPreview() { currentNoteFolderRelativePath = "", noteFolders = listOf(), openFolder = {}, - createNoteFolder = { _,_ -> + createNoteFolder = { _, _ -> true }, onSelectedFolder = {}, diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt index c4d3b4d7..e95d9246 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/DrawerScreen.kt @@ -82,7 +82,6 @@ fun DrawerScreen( ) { - val scope = rememberCoroutineScope() BackHandler(enabled = drawerState.isOpen) { if (currentNoteFolderRelativePath.isEmpty()) { diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/edit/BottomBar.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/edit/BottomBar.kt index 6f4776a8..e76ccc3d 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/edit/BottomBar.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/edit/BottomBar.kt @@ -94,7 +94,10 @@ fun DefaultRow( Text( modifier = Modifier .padding(10.dp), - text = stringResource(R.string.parent, getParentPath(vm.previousNote.relativePath)) + text = stringResource( + R.string.parent, + getParentPath(vm.previousNote.relativePath) + ) ) } } diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/remote/PickRepoScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/remote/PickRepoScreen.kt index 8813761b..211068b4 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/remote/PickRepoScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/remote/PickRepoScreen.kt @@ -2,7 +2,6 @@ package io.github.wiiznokes.gitnote.ui.screen.setup.remote import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/SetupViewModel.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/SetupViewModel.kt index f4f16d8a..73194a77 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/SetupViewModel.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/SetupViewModel.kt @@ -235,10 +235,12 @@ class SetupViewModel(val authFlow: SharedFlow) : ViewModel(), SetupViewM storageManager.updateDatabase( progressCb = { viewModelScope.launch { - _initState.emit(when (it) { - is Progress.GeneratingDatabase -> InitState.GeneratingDatabase(it.path) - Progress.Timestamps -> InitState.CalculatingTimestamps - }) + _initState.emit( + when (it) { + is Progress.GeneratingDatabase -> InitState.GeneratingDatabase(it.path) + Progress.Timestamps -> InitState.CalculatingTimestamps + } + ) } } ) @@ -387,22 +389,22 @@ class SetupViewModel(val authFlow: SharedFlow) : ViewModel(), SetupViewM sealed class InitState { - data object Idle: InitState() - data class Error(val message: String? = null): InitState() + data object Idle : InitState() + data class Error(val message: String? = null) : InitState() - data object GettingAccessToken: InitState() - data object FetchingRepos: InitState() - data object GettingUserInfo: InitState() + data object GettingAccessToken : InitState() + data object FetchingRepos : InitState() + data object GettingUserInfo : InitState() - data object AuthentificationSuccess: InitState() + data object AuthentificationSuccess : InitState() - data object CreatingRemoteRepo: InitState() - data object AddingDeployKey: InitState() + data object CreatingRemoteRepo : InitState() + data object AddingDeployKey : InitState() - data class Cloning(val percent: Int): InitState() + data class Cloning(val percent: Int) : InitState() - data object CalculatingTimestamps: InitState() - data class GeneratingDatabase(val path: String): InitState() + data object CalculatingTimestamps : InitState() + data class GeneratingDatabase(val path: String) : InitState() fun message(): String {