diff --git a/README.md b/README.md index 4edd57f..c3da221 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ _Supported Android versions: 11 to 14_ -Android note which integrate Git. You can use this app with other desktop editors. +Android note app which integrate Git. You can use this app with other desktop editors. ## Why diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/MainActivity.kt b/app/src/main/java/io/github/wiiznokes/gitnote/MainActivity.kt index 250375c..a14b4a4 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/MainActivity.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/MainActivity.kt @@ -17,6 +17,7 @@ import dev.olshevski.navigation.reimagined.rememberNavController import io.github.wiiznokes.gitnote.MyApp.Companion.appModule import io.github.wiiznokes.gitnote.ui.destination.AppDestination import io.github.wiiznokes.gitnote.ui.destination.Destination +import io.github.wiiznokes.gitnote.ui.destination.EditParams import io.github.wiiznokes.gitnote.ui.destination.InitDestination import io.github.wiiznokes.gitnote.ui.screen.app.AppScreen import io.github.wiiznokes.gitnote.ui.screen.init.InitScreen @@ -59,7 +60,7 @@ class MainActivity : ComponentActivity() { if (isEditUnsaved()) { Log.d(TAG, "launch as EDIT_IS_UNSAVED") Destination.App( - AppDestination.EditSaved + AppDestination.Edit(EditParams.Saved) ) } else { Destination.App( 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 531587d..6e58f3c 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 @@ -83,9 +83,25 @@ interface RepoDatabaseDao { @Upsert suspend fun insertNoteFolder(noteFolder: NoteFolder) + /** + * Delete all notes inside the note folder, and the note folder + */ + suspend fun deleteNoteFolder(noteFolder: NoteFolder) { + internalDeleteNotesIn(noteFolder.relativePath) + internalDeleteNoteFolder(noteFolder) + } + + /** + * Private + */ + @Query("DELETE FROM Notes WHERE relativePath LIKE :relativePath || '%'") + suspend fun internalDeleteNotesIn(relativePath: String) - // todo - // suspend fun removeNoteFolder(noteFolder: NoteFolder) + /** + * Private + */ + @Delete + suspend fun internalDeleteNoteFolder(noteFolder: NoteFolder) @Upsert suspend fun insertNote(note: Note) @@ -93,10 +109,6 @@ interface RepoDatabaseDao { @Delete suspend fun removeNote(note: Note) - @Query("DELETE FROM Notes WHERE relativePath = :relativePath") - suspend fun removeNote(relativePath: String): Int - - @Query("DELETE FROM NoteFolders") fun removeAllNoteFolder() diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Schema.kt b/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Schema.kt index 51412cd..f635370 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Schema.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/data/room/Schema.kt @@ -3,6 +3,7 @@ package io.github.wiiznokes.gitnote.data.room import android.os.Parcelable import androidx.room.Entity import io.github.wiiznokes.gitnote.BuildConfig +import io.github.wiiznokes.gitnote.data.platform.NodeFs import io.github.wiiznokes.gitnote.data.removeFirstAndLastSlash import io.github.wiiznokes.gitnote.data.requireNotEndOrStartWithSlash import io.github.wiiznokes.gitnote.ui.model.FileExtension @@ -50,6 +51,17 @@ data class NoteFolder( if (relativePath == "") return null return relativePath.substringBeforeLast("/", missingDelimiterValue = "") } + + fun toFolderFs(rootPath: String): NodeFs.Folder { + return NodeFs.Folder.fromPath(rootPath, relativePath) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + return id == (other as Note).id + } + override fun hashCode(): Int = id } @Entity( @@ -113,4 +125,15 @@ data class Note( requireNotEndOrStartWithSlash(nameWithoutExtension()) } } + + fun toFileFs(rootPath: String): NodeFs.File { + return NodeFs.File.fromPath(rootPath, relativePath) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + return id == (other as Note).id + } + override fun hashCode(): Int = id } \ No newline at end of file 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 6489a98..86c0464 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 @@ -4,7 +4,6 @@ import android.util.Log 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.platform.NodeFs import io.github.wiiznokes.gitnote.data.room.Note import io.github.wiiznokes.gitnote.data.room.NoteFolder import io.github.wiiznokes.gitnote.data.room.RepoDatabase @@ -113,7 +112,7 @@ class StorageManager { dao.insertNote(new) val rootPath = prefs.repoPath() - val previousFile = NodeFs.File.fromPath(rootPath, previous.relativePath) + val previousFile = previous.toFileFs(rootPath) previousFile.delete().onFailure { val message = @@ -122,8 +121,7 @@ class StorageManager { uiHelper.makeToast(message) } - val newFile = NodeFs.File.fromPath(rootPath, new.relativePath) - + val newFile = new.toFileFs(rootPath) newFile.create().onFailure { val message = uiHelper.getString(R.string.error_create_file, it.message) Log.e(TAG, message) @@ -150,8 +148,7 @@ class StorageManager { update { dao.insertNote(note) - val rootPath = prefs.repoPath() - val file = NodeFs.File.fromPath(rootPath, note.relativePath) + val file = note.toFileFs(prefs.repoPath()) file.create().onFailure { val message = uiHelper.getString(R.string.error_create_file, it.message) @@ -175,8 +172,7 @@ class StorageManager { update { dao.removeNote(note) - val rootPath = prefs.repoPath() - val file = NodeFs.File.fromPath(rootPath, note.relativePath) + val file = note.toFileFs(prefs.repoPath()) file.delete().onFailure { val message = uiHelper.getString(R.string.error_delete_file, file.path, it.message) Log.e(TAG, message) @@ -186,24 +182,20 @@ class StorageManager { } } - suspend fun deleteNotes(noteRelativePaths: List): Result = locker.withLock { - Log.d(TAG, "deleteNotes: ${noteRelativePaths.size}") + suspend fun deleteNotes(notes: List): Result = locker.withLock { + Log.d(TAG, "deleteNotes: ${notes.size}") update { - // optimization because we only see the db state on screen - noteRelativePaths.forEach { noteRelativePath -> - val removedNoteCount = dao.removeNote(noteRelativePath) - if (removedNoteCount != 1) { - Log.e(TAG, "removed note count was != 1 from the doa: $removedNoteCount") - } + notes.forEach { note -> + dao.removeNote(note) } - val rootPath = prefs.repoPath() - noteRelativePaths.forEach { noteRelativePath -> + val repoPath = prefs.repoPath() + notes.forEach { note -> - Log.d(TAG, "deleting $noteRelativePath") - val file = NodeFs.File.fromPath(rootPath, noteRelativePath) + Log.d(TAG, "deleting $note") + val file = note.toFileFs(repoPath) file.delete().onFailure { val message = @@ -222,9 +214,7 @@ class StorageManager { update { dao.insertNoteFolder(noteFolder) - val rootPath = prefs.repoPath() - val folder = NodeFs.Folder.fromPath(rootPath, noteFolder.relativePath) - + val folder = noteFolder.toFolderFs(prefs.repoPath()) folder.create().onFailure { val message = uiHelper.getString(R.string.error_create_folder, it.message) Log.e(TAG, message) @@ -235,6 +225,23 @@ class StorageManager { } } + suspend fun deleteNoteFolder(noteFolder: NoteFolder): Result = locker.withLock { + Log.d(TAG, "deleteNoteFolder: $noteFolder") + + update { + dao.deleteNoteFolder(noteFolder) + + val folder = noteFolder.toFolderFs(prefs.repoPath()) + folder.delete().onFailure { + val msg = uiHelper.getString(R.string.error_delete_folder, it.message) + Log.e(TAG, msg) + uiHelper.makeToast(msg) + } + + success(Unit) + } + } + suspend fun closeRepo() = locker.withLock { gitManager.closeRepo() dao.clearDatabase() diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/CustomDropDown.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/CustomDropDown.kt index f73f0ac..b52fde0 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/CustomDropDown.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/CustomDropDown.kt @@ -1,10 +1,6 @@ package io.github.wiiznokes.gitnote.ui.component -import android.util.Log import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem @@ -14,23 +10,20 @@ import androidx.compose.material3.Text import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.DpOffset -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.core.util.TypedValueCompat.pxToDp private val TAG = "CustomDropDown" + data class CustomDropDownModel( val text: String, val onClick: () -> Unit diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/destination/AppDestination.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/destination/AppDestination.kt index c5b4e8d..17a3194 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/destination/AppDestination.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/destination/AppDestination.kt @@ -11,13 +11,19 @@ sealed interface AppDestination : Parcelable { data object Grid : AppDestination @Parcelize - data class Edit(val note: Note, val editType: EditType) : AppDestination - - @Parcelize - data object EditSaved : AppDestination + data class Edit(val params: EditParams) : AppDestination @Parcelize data class Settings(val settingsDestination: SettingsDestination) : AppDestination } +@Parcelize +sealed class EditParams : Parcelable { + data object Saved : EditParams() + + data class Idle( + val note: Note, + val editType: EditType + ) : EditParams() +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/destination/InitDestination.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/destination/InitDestination.kt index aa0be2f..b906c96 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/destination/InitDestination.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/destination/InitDestination.kt @@ -32,7 +32,7 @@ enum class NewRepoSource { fun getExplorerTitle(): String { val context = MyApp.appModule.context - + return when (this) { Create -> context.getString(R.string.create_repo_explorer) Open -> context.getString(R.string.open_repo_explorer) diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/AppScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/AppScreen.kt index 68d070d..1050aa0 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/AppScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/AppScreen.kt @@ -11,6 +11,7 @@ import dev.olshevski.navigation.reimagined.navigate import dev.olshevski.navigation.reimagined.pop import dev.olshevski.navigation.reimagined.rememberNavController import io.github.wiiznokes.gitnote.ui.destination.AppDestination +import io.github.wiiznokes.gitnote.ui.destination.EditParams import io.github.wiiznokes.gitnote.ui.destination.SettingsDestination import io.github.wiiznokes.gitnote.ui.screen.app.grid.GridScreen import io.github.wiiznokes.gitnote.ui.screen.settings.SettingsScreen @@ -47,17 +48,20 @@ fun AppScreen( ) }, onEditClick = { note, editType -> - navController.navigate(AppDestination.Edit(note, editType)) + navController.navigate(AppDestination.Edit(EditParams.Idle(note, editType))) }, onStorageFailure = onStorageFailure ) } is AppDestination.Edit -> EditScreen( - initialNote = it.note, - initialEditType = it.editType, + editParams = it.params, onFinished = { navController.pop() + + if (it.params == EditParams.Saved) { + navController.navigate(AppDestination.Grid) + } } ) @@ -66,15 +70,6 @@ fun AppScreen( destination = it.settingsDestination, onStorageFailure = onStorageFailure ) - - AppDestination.EditSaved -> EditScreen( - initialNote = null, - initialEditType = null, - onFinished = { - navController.pop() - navController.navigate(AppDestination.Grid) - } - ) } } } @@ -98,7 +93,6 @@ private object AppNavTransitionSpec : NavTransitionSpec { } is AppDestination.Settings -> slide(backWard = true) - AppDestination.EditSaved -> crossFade() } } } 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 60fad4a..a990b22 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 @@ -51,6 +51,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import io.github.wiiznokes.gitnote.R +import io.github.wiiznokes.gitnote.data.room.NoteFolder import io.github.wiiznokes.gitnote.ui.component.CustomDropDown import io.github.wiiznokes.gitnote.ui.component.CustomDropDownModel import io.github.wiiznokes.gitnote.ui.component.GetStringDialog @@ -65,10 +66,8 @@ import kotlinx.coroutines.launch private const val TAG = "DrawerScreen" data class DrawerFolderModel( - val relativePath: String, - val fullName: String, val noteCount: Int, - val id: Int + val noteFolder: NoteFolder, ) @@ -139,7 +138,7 @@ fun DrawerScreen( ) { items(currentNoteFolders, - key = { it.id }) { noteFolder -> + key = { it.noteFolder.id }) { drawerNoteFolder -> Box { val dropDownExpanded = remember { mutableStateOf(false) @@ -149,8 +148,6 @@ fun DrawerScreen( mutableStateOf(Offset.Zero) } - // todo: impl options - /* // need this box for clickPosition Box { CustomDropDown( @@ -160,7 +157,7 @@ fun DrawerScreen( CustomDropDownModel( text = stringResource(R.string.delete_this_folder), onClick = { - vm.deleteFolder(noteFolder.relativePath) + vm.deleteFolder(drawerNoteFolder.noteFolder) } ), ), @@ -168,8 +165,6 @@ fun DrawerScreen( ) } - */ - CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { Row( @@ -180,7 +175,7 @@ fun DrawerScreen( dropDownExpanded.value = true }, onClick = { - vm.openFolder(noteFolder.relativePath) + vm.openFolder(drawerNoteFolder.noteFolder.relativePath) } ) .pointerInteropFilter { @@ -194,7 +189,7 @@ fun DrawerScreen( CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) { Text( - text = noteFolder.noteCount.toString(), + text = drawerNoteFolder.noteCount.toString(), modifier = Modifier .padding(LocalSpaces.current.smallPadding) ) @@ -215,7 +210,7 @@ fun DrawerScreen( SimpleSpacer(width = LocalSpaces.current.smallPadding) Text( - text = noteFolder.fullName, + text = drawerNoteFolder.noteFolder.fullName(), maxLines = 1, overflow = TextOverflow.Ellipsis, ) diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/EditScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/EditScreen.kt index bdf563f..345279e 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/EditScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/EditScreen.kt @@ -42,31 +42,29 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import io.github.wiiznokes.gitnote.R -import io.github.wiiznokes.gitnote.data.room.Note import io.github.wiiznokes.gitnote.ui.component.CustomDropDown import io.github.wiiznokes.gitnote.ui.component.CustomDropDownModel import io.github.wiiznokes.gitnote.ui.component.SimpleIcon +import io.github.wiiznokes.gitnote.ui.destination.EditParams import io.github.wiiznokes.gitnote.ui.model.EditType import io.github.wiiznokes.gitnote.ui.model.FileExtension -import io.github.wiiznokes.gitnote.ui.viewmodel.delegateEditVM +import io.github.wiiznokes.gitnote.ui.viewmodel.newEditViewModel private const val TAG = "EditScreen" - -// todo: maybe use fragment to reuse the note of gridview -// like in google keep, this will potentially allow a nice transition -// from a note of the grid to edit +/** + * initialEditType and initialNote equal null + */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun EditScreen( - initialEditType: EditType?, - initialNote: Note?, + editParams: EditParams, onFinished: () -> Unit, ) { - val vm = delegateEditVM(initialEditType, initialNote) + val vm = newEditViewModel(editParams) val nameFocusRequester = remember { FocusRequester() } val textFocusRequester = remember { FocusRequester() } 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 907e4b7..402c06d 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 @@ -118,7 +118,7 @@ fun GridScreen( GridViewTop( vm = vm, offset = offset, - selectedNotes = selectedNotes, + selectedNotesNumber = selectedNotes.size, maxOffset = maxOffset, drawerState = drawerState, onSettingsClick = onSettingsClick, @@ -166,7 +166,7 @@ private fun GridView( maxOffset: Float, offset: MutableFloatState, onEditClick: (Note, EditType) -> Unit, - selectedNotes: List, + selectedNotes: List, fabExpanded: MutableState, ) { val gridNotes by vm.gridNotes.collectAsState() @@ -179,6 +179,7 @@ private fun GridView( var lastQuery: String = rememberSaveable { query.value } if (lastQuery != query.value) { + @Suppress("UNUSED_VALUE") lastQuery = query.value LaunchedEffect(null) { gridState.animateScrollToItem(index = 0) @@ -264,8 +265,7 @@ private fun GridView( gridNote.note, EditType.Update ) } else { - vm.selectNote( - gridNote.note.relativePath, add = !gridNote.selected + vm.selectNote(gridNote.note, add = !gridNote.selected ) } }) @@ -289,7 +289,7 @@ private fun GridView( if (selectedNotes.isEmpty()) CustomDropDownModel(text = stringResource( R.string.select_multiple_notes ), onClick = { - vm.selectNote(gridNote.note.relativePath, true) + vm.selectNote(gridNote.note, true) }) else null, ), clickPosition = clickPosition diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/TopGrid.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/TopGrid.kt index 5e4763f..f73b902 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/TopGrid.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/TopGrid.kt @@ -17,11 +17,8 @@ import androidx.compose.material.icons.rounded.Close import androidx.compose.material.icons.rounded.Menu import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material3.DrawerState -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuDefaults import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextFieldDefaults @@ -48,6 +45,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import io.github.wiiznokes.gitnote.R +import io.github.wiiznokes.gitnote.data.room.Note import io.github.wiiznokes.gitnote.ui.component.CustomDropDown import io.github.wiiznokes.gitnote.ui.component.CustomDropDownModel import io.github.wiiznokes.gitnote.ui.component.SimpleIcon @@ -62,7 +60,7 @@ private const val TAG = "TopGridScreen" fun GridViewTop( vm: GridViewModel, offset: MutableFloatState, - selectedNotes: List, + selectedNotesNumber: Int, maxOffset: MutableFloatState, drawerState: DrawerState, onSettingsClick: () -> Unit, @@ -72,7 +70,7 @@ fun GridViewTop( val statusBarHeight = 0.dp AnimatedContent( - targetState = selectedNotes.isEmpty(), + targetState = selectedNotesNumber == 0, label = "", ) { shouldShowSearchBar -> if (shouldShowSearchBar) { @@ -90,7 +88,7 @@ fun GridViewTop( statusBarHeight = statusBarHeight, topBarHeight = topBarHeight, vm = vm, - selectedNotes = selectedNotes + selectedNotesNumber = selectedNotesNumber ) } } @@ -228,7 +226,7 @@ private fun SelectableTopBar( statusBarHeight: Dp, topBarHeight: Dp, vm: GridViewModel, - selectedNotes: List + selectedNotesNumber: Int ) { Row( modifier = Modifier @@ -255,7 +253,7 @@ private fun SelectableTopBar( } Text( - text = selectedNotes.size.toString(), + text = selectedNotesNumber.toString(), color = MaterialTheme.colorScheme.onSurface ) } @@ -281,7 +279,10 @@ private fun SelectableTopBar( expanded = expanded, options = listOf( CustomDropDownModel( - text = pluralStringResource(R.plurals.delete_selected_notes, selectedNotes.size), + text = pluralStringResource( + R.plurals.delete_selected_notes, + selectedNotesNumber + ), onClick = { vm.deleteSelectedNotes() } ) ) diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/EditViewModel.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/EditViewModel.kt index c191992..985c991 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/EditViewModel.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/EditViewModel.kt @@ -11,11 +11,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewmodel.compose.viewModel import io.github.wiiznokes.gitnote.MyApp import io.github.wiiznokes.gitnote.R -import io.github.wiiznokes.gitnote.data.platform.NodeFs import io.github.wiiznokes.gitnote.data.room.Note import io.github.wiiznokes.gitnote.helper.NameValidation import io.github.wiiznokes.gitnote.helper.UiHelper import io.github.wiiznokes.gitnote.manager.StorageManager +import io.github.wiiznokes.gitnote.ui.destination.EditParams import io.github.wiiznokes.gitnote.ui.model.EditType import io.github.wiiznokes.gitnote.ui.model.FileExtension import kotlinx.coroutines.CoroutineScope @@ -154,15 +154,19 @@ class EditViewModel() : ViewModel() { val relativePath = "$parentPath/$name.${fileExtension.text}" - prefs.repoPathBlocking().let { rootPath -> - val previousFile = NodeFs.File.fromPath(rootPath, previousNote.relativePath) + val newNote = Note.new( + relativePath = relativePath, + content = content, + id = id + ) + prefs.repoPathBlocking().let { repoPath -> + val previousFile = previousNote.toFileFs(repoPath) if (!previousFile.exist()) { Log.w(TAG, "previous file ${previousFile.path} does not exist") } - val newFile = NodeFs.File.fromPath(rootPath, relativePath) - + val newFile = newNote.toFileFs(repoPath) if (newFile.path != previousFile.path) { if (newFile.exist()) { uiHelper.makeToast(uiHelper.getString(R.string.error_file_already_exist)) @@ -171,12 +175,6 @@ class EditViewModel() : ViewModel() { } } - val newNote = Note.new( - relativePath = relativePath, - content = content, - id = id - ) - CoroutineScope(Dispatchers.IO).launch { storageManager.updateNote( @@ -216,19 +214,17 @@ class EditViewModel() : ViewModel() { val relativePath = "$parentPath/$name.${fileExtension.text}" - prefs.repoPathBlocking().let { rootPath -> - if (NodeFs.File.fromPath(rootPath, relativePath).exist()) { - uiHelper.makeToast(uiHelper.getString(R.string.error_file_already_exist)) - return failure(EditException(EditExceptionType.NoteAlreadyExist)) - } - } - val note = Note.new( relativePath = relativePath, content = content, id = id, ) + if (note.toFileFs(prefs.repoPathBlocking()).exist()) { + uiHelper.makeToast(uiHelper.getString(R.string.error_file_already_exist)) + return failure(EditException(EditExceptionType.NoteAlreadyExist)) + } + CoroutineScope(Dispatchers.IO).launch { storageManager.createNote(note).onFailure { uiHelper.makeToast(it.message) @@ -280,25 +276,28 @@ fun isEditUnsaved(): Boolean { } @Composable -fun delegateEditVM(editType: EditType?, previousNote: Note?): EditViewModel { - return if (editType != null && previousNote != null) { - viewModel( - factory = viewModelFactory { - EditViewModel(editType, previousNote) - } - ) - } else { - viewModel( +fun newEditViewModel(editParams: EditParams): EditViewModel { + + return when (editParams) { + is EditParams.Idle -> viewModel( factory = viewModelFactory { - EditViewModel( - editType = readObj(EDIT_EDIT_TYPE), - previousNote = readObj(EDIT_PREVIOUS_NOTE), - name = readObj(EDIT_NAME), - content = readObj(EDIT_CONTENT), - fileExtension = readObj(EDIT_FILE_EXTENSION), - ) + EditViewModel(editParams.editType, editParams.note) } ) + + EditParams.Saved -> { + viewModel( + factory = viewModelFactory { + EditViewModel( + editType = readObj(EDIT_EDIT_TYPE), + previousNote = readObj(EDIT_PREVIOUS_NOTE), + name = readObj(EDIT_NAME), + content = readObj(EDIT_CONTENT), + fileExtension = readObj(EDIT_FILE_EXTENSION), + ) + } + ) + } } } 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 1d25b68..34d577d 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 @@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope 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.platform.NodeFs import io.github.wiiznokes.gitnote.data.room.Note import io.github.wiiznokes.gitnote.data.room.NoteFolder import io.github.wiiznokes.gitnote.data.room.RepoDatabase @@ -65,8 +64,9 @@ class GridViewModel : ViewModel() { get() = _currentNoteFolderRelativePath.asStateFlow() - private val _selectedNotes: MutableStateFlow> = MutableStateFlow(emptyList()) - val selectedNotes: StateFlow> + private val _selectedNotes: MutableStateFlow> = MutableStateFlow(emptyList()) + + val selectedNotes: StateFlow> get() = _selectedNotes.asStateFlow() @@ -82,11 +82,8 @@ class GridViewModel : ViewModel() { CoroutineScope(Dispatchers.IO).launch { allNotes.collect { allNotes -> - // Log.d(TAG, "filter selected note, depend on allNotes") selectedNotes.value.filter { selectedNote -> - allNotes.contains { note -> - note.relativePath == selectedNote - } + allNotes.contains(selectedNote) }.let { newSelectedNotes -> _selectedNotes.emit(newSelectedNotes) } @@ -129,19 +126,17 @@ class GridViewModel : ViewModel() { val relativePath = "$relativeParentPath/$name" - prefs.repoPathBlocking().let { rootPath -> - if (NodeFs.Folder.fromPath(rootPath, relativePath).exist()) { - uiHelper.makeToast(uiHelper.getString(R.string.error_folder_already_exist)) - return false - } + 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 = NoteFolder.new( - relativePath = relativePath - ) - ) + storageManager.createNoteFolder(noteFolder) } return true @@ -151,11 +146,11 @@ class GridViewModel : ViewModel() { /** * @param add true if the note must be selected, false otherwise */ - fun selectNote(relativePath: String, add: Boolean) = viewModelScope.launch { + fun selectNote(note: Note, add: Boolean) = viewModelScope.launch { if (add) { - selectedNotes.value.plus(relativePath) + selectedNotes.value.plus(note) } else { - selectedNotes.value.minus(relativePath) + selectedNotes.value.minus(note) }.let { _selectedNotes.emit(it) } @@ -170,20 +165,28 @@ class GridViewModel : ViewModel() { val currentSelectedNotes = selectedNotes.value unselectAllNotes() storageManager.deleteNotes(currentSelectedNotes) - uiHelper.makeToast(uiHelper.getQuantityString(R.plurals.success_notes_delete, currentSelectedNotes.size)) + uiHelper.makeToast( + uiHelper.getQuantityString( + R.plurals.success_notes_delete, + currentSelectedNotes.size + ) + ) } } fun deleteNote(note: Note) { - selectNote(note.relativePath, false) + selectNote(note, false) CoroutineScope(Dispatchers.IO).launch { storageManager.deleteNote(note) uiHelper.makeToast(uiHelper.getQuantityString(R.plurals.success_notes_delete, 1)) } } - fun deleteFolder(relativePath: String) { - // todo + fun deleteFolder(noteFolder: NoteFolder) { + CoroutineScope(Dispatchers.IO).launch { + storageManager.deleteNoteFolder(noteFolder) + uiHelper.makeToast(uiHelper.getQuantityString(R.plurals.success_noteFolders_delete, 1)) + } } @@ -253,7 +256,7 @@ class GridViewModel : ViewModel() { } else { name }, - selected = selectedNotes.contains(note.relativePath), + selected = selectedNotes.contains(note), note = note ) } @@ -272,12 +275,10 @@ class GridViewModel : ViewModel() { }.combine(notes) { folders, notes -> folders.map { folder -> DrawerFolderModel( - relativePath = folder.relativePath, - fullName = folder.fullName(), noteCount = notes.count { it.parentPath().startsWith(folder.relativePath) }, - id = folder.id + noteFolder = folder ) } }.stateIn( diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f9ffdc5..06de7d9 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -115,5 +115,10 @@ Note supprimée avec succès Notes supprimées avec succès + Impossible de supprimer le dossier: %1$s + + Dossier supprimé avec succès + Dossiers supprimés avec succès + \ 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 a64861b..2044f0a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -94,6 +94,7 @@ The folder is not empty Note successfully updated Note successfully created + Can\'t delete folder: %1$s Delete selected note @@ -105,4 +106,9 @@ Notes successfully deleted + + Folder successfully deleted + Folders successfully deleted + + \ No newline at end of file