From 7e24e3103358fe2b9520cddd6d5ede904c5c7caa Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:45:13 +0200 Subject: [PATCH 1/3] optimize database creation --- .../github/wiiznokes/gitnote/data/room/Dao.kt | 63 ++++++++++++++----- .../gitnote/data/room/RepoDatabase.kt | 23 +++---- 2 files changed, 59 insertions(+), 27 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 09f275f2..416aa219 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 @@ -4,8 +4,10 @@ import android.util.Log import androidx.paging.PagingSource import androidx.room.Dao import androidx.room.Delete +import androidx.room.Insert import androidx.room.Query import androidx.room.RawQuery +import androidx.room.Transaction import androidx.room.Upsert import androidx.sqlite.db.SimpleSQLiteQuery import androidx.sqlite.db.SupportSQLiteQuery @@ -28,8 +30,7 @@ private const val LIMIT_FILE_SIZE_DB = 2 * 1024 * 1024 @Dao interface RepoDatabaseDao { - // todo: use @Transaction - // todo: don't clear the all database each time + @Transaction suspend fun clearAndInit( rootPath: String, timestamps: HashMap, @@ -42,11 +43,21 @@ interface RepoDatabaseDao { val rootFolder = NoteFolder.new( relativePath = "", ) - insertNoteFolder(rootFolder) + + val notes = mutableListOf() + val folders = mutableListOf() + + folders.add(rootFolder) val rootLength = rootFs.path.length + 1 - suspend fun initRec(folder: NodeFs.Folder) { + var folderCount = 0 + + val stack = ArrayDeque() + stack.add(rootFs) + + while (stack.isNotEmpty()) { + val folder = stack.removeLast() folder.forEachNodeFs { nodeFs -> @@ -69,11 +80,16 @@ interface RepoDatabaseDao { val relativePath = nodeFs.path.substring(startIndex = rootLength) val note = Note.new( relativePath = relativePath, - lastModifiedTimeMillis = timestamps.get(relativePath) + lastModifiedTimeMillis = timestamps[relativePath] ?: nodeFs.lastModifiedTime().toMillis(), content = nodeFs.readText(), ) - insertNote(note) + + notes.add(note) + if (notes.size >= 1000) { + insertNotes(notes) + notes.clear() + } //Log.d(TAG, "add note: $note") } @@ -85,15 +101,26 @@ interface RepoDatabaseDao { relativePath = nodeFs.path.substring(startIndex = rootLength), ) //Log.d(TAG, "add noteFolder: $noteFolder") - insertNoteFolder(noteFolder) - progressCb?.invoke(Progress.GeneratingDatabase(noteFolder.relativePath)) - initRec(nodeFs) + folders.add(noteFolder) + + if (folders.size >= 1000) { + insertFolders(folders) + folders.clear() + } + + if (folderCount % 20 == 0) { + progressCb?.invoke(Progress.GeneratingDatabase(noteFolder.relativePath)) + } + folderCount += 1 + + stack.addLast(nodeFs) } } } } - initRec(rootFs) + insertNotes(notes) + insertFolders(folders) } @@ -270,9 +297,13 @@ interface RepoDatabaseDao { } - @Upsert + @Insert suspend fun insertNoteFolder(noteFolder: NoteFolder) + + @Insert + suspend fun insertFolders(folders: List) + /** * Delete all notes inside the note folder, and the note folder */ @@ -297,18 +328,22 @@ interface RepoDatabaseDao { @Upsert suspend fun insertNote(note: Note) + @Upsert + suspend fun insertNotes(notes: List) + @Delete suspend fun removeNote(note: Note) @Query("DELETE FROM NoteFolders") - fun removeAllNoteFolder() + suspend fun removeAllNoteFolder() @Query("DELETE FROM Notes") - fun removeAllNote() + suspend fun removeAllNote() - fun clearDatabase() { + suspend fun clearDatabase() { removeAllNoteFolder() removeAllNote() + } } diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/data/room/RepoDatabase.kt b/app/src/main/java/io/github/wiiznokes/gitnote/data/room/RepoDatabase.kt index aabe62cf..4a29e894 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/data/room/RepoDatabase.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/data/room/RepoDatabase.kt @@ -52,21 +52,18 @@ abstract class RepoDatabase : RoomDatabase() { } fun buildFactory(path: String): SupportSQLiteOpenHelper.Factory { - return object : SupportSQLiteOpenHelper.Factory { - override fun create(configuration: SupportSQLiteOpenHelper.Configuration): SupportSQLiteOpenHelper { - val config = SQLiteDatabaseConfiguration( - path, - SQLiteDatabase.OPEN_CREATE or SQLiteDatabase.OPEN_READWRITE - ) - - config.functions.add(SQLiteFunction("rank", 1, Rank)) - config.functions.add(SQLiteFunction("parentPath", 1, ParentPath)) - config.functions.add(SQLiteFunction("fullName", 1, FullName)) + return SupportSQLiteOpenHelper.Factory { configuration -> + val config = SQLiteDatabaseConfiguration( + path, + SQLiteDatabase.OPEN_CREATE or SQLiteDatabase.OPEN_READWRITE + ) - val options = RequerySQLiteOpenHelperFactory.ConfigurationOptions { config } - return RequerySQLiteOpenHelperFactory(listOf(options)).create(configuration) - } + config.functions.add(SQLiteFunction("rank", 1, Rank)) + config.functions.add(SQLiteFunction("parentPath", 1, ParentPath)) + config.functions.add(SQLiteFunction("fullName", 1, FullName)) + val options = RequerySQLiteOpenHelperFactory.ConfigurationOptions { config } + RequerySQLiteOpenHelperFactory(listOf(options)).create(configuration) } } From 93d84d82d1f3032e7ee253db7639200b38a7642e Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Wed, 5 Aug 2026 13:01:22 +0200 Subject: [PATCH 2/3] fix some lint --- .../wiiznokes/gitnote/provider/GitHub.kt | 4 +- .../gitnote/ui/component/CustomDropDown.kt | 2 +- .../ui/component/RequestConfirmationDialog.kt | 3 +- .../gitnote/ui/screen/app/grid/TopGrid.kt | 3 +- .../gitnote/ui/screen/setup/SetupNav.kt | 4 +- .../ui/screen/setup/remote/EnterUrlScreen.kt | 3 +- .../wiiznokes/gitnote/ui/utils/Fuzzy.kt | 62 ------------------- .../gitnote/ui/utils/rememberWindowInfo.kt | 23 ++++--- .../ui/viewmodel/edit/MarkdownSmartEditor.kt | 4 +- app/src/main/res/values/strings.xml | 7 ++- 10 files changed, 32 insertions(+), 83 deletions(-) delete mode 100644 app/src/main/java/io/github/wiiznokes/gitnote/ui/utils/Fuzzy.kt diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/provider/GitHub.kt b/app/src/main/java/io/github/wiiznokes/gitnote/provider/GitHub.kt index ecbc04df..de97fa1b 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/provider/GitHub.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/provider/GitHub.kt @@ -19,7 +19,7 @@ class GithubProvider : Provider { private val clientId = "Ov23li8EPatIAsWPt9QT" // storing this secret in the repo is "ok" - // the only risk is github app reputation and quotas + // the only risk is GitHub app reputation and quotas // it would require a server to not store it here private val clientSecret = "12f3f4742855deaafb45e798bcc635608b9d6fe6" @@ -111,7 +111,7 @@ class GithubProvider : Provider { if (responseCode !in 200..299) { val error = connection.errorStream?.bufferedReader()?.use { it.readText() } - Exception("Failed to create repo: HTTP $responseCode $error") + throw Exception("Failed to create repo: HTTP $responseCode $error") } } 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 12ae7db0..d625ee52 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 @@ -21,7 +21,7 @@ import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp -private val TAG = "CustomDropDown" +private const val TAG = "CustomDropDown" data class CustomDropDownModel( val text: String, diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/RequestConfirmationDialog.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/RequestConfirmationDialog.kt index cc7d5eab..f49569c7 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/RequestConfirmationDialog.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/RequestConfirmationDialog.kt @@ -109,8 +109,7 @@ private fun DialogPreview() { expanded = remember { mutableStateOf(true) }, - text = "Do you wanna have sex ? kvfeznfezlzelfnklze" + - "geg g ggre gegljerngljerngljrnljrengljneglnerlgnelrnglerngenrgne lnglng lenrgln elgrne lgn lgknergner ngegn lekgner nengk enrgl e" + text = "I am a description" ) { } 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 7e76e026..6174baee 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 @@ -83,6 +83,7 @@ import io.github.wiiznokes.gitnote.ui.model.NoteViewType import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlin.math.roundToInt +import kotlin.time.Duration.Companion.milliseconds private const val TAG = "TopGridScreen" @@ -445,7 +446,7 @@ private fun SyncStateIcon( if (state is Ok) { LaunchedEffect(visible) { - delay(1000) + delay(1000.milliseconds) visible = false onConsumeOkSyncState() tooltipState.dismiss() diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/SetupNav.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/SetupNav.kt index f607f274..2f632cbb 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/SetupNav.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/SetupNav.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.res.stringResource import androidx.lifecycle.viewmodel.compose.viewModel import dev.olshevski.navigation.reimagined.AnimatedNavHost import dev.olshevski.navigation.reimagined.NavAction @@ -15,6 +16,7 @@ import dev.olshevski.navigation.reimagined.navigate import dev.olshevski.navigation.reimagined.pop import dev.olshevski.navigation.reimagined.popUpTo import dev.olshevski.navigation.reimagined.rememberNavController +import io.github.wiiznokes.gitnote.R import io.github.wiiznokes.gitnote.ui.component.RequestConfirmationDialog import io.github.wiiznokes.gitnote.ui.destination.NewRepoMethod import io.github.wiiznokes.gitnote.ui.destination.SetupDestination @@ -96,7 +98,7 @@ fun SetupNav( RequestConfirmationDialog( expanded = requestConfirmationDialogExpanded, - text = "WARNING: Are you sure you want to continue? The directory will be deleted.", + text = stringResource(R.string.warning_folder_deletion), onConfirmation = { pendingStorageConfig?.let { storageConfig -> when (setupDestination.newRepoMethod) { diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/remote/EnterUrlScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/remote/EnterUrlScreen.kt index 2f34781e..0a671bf8 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/remote/EnterUrlScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/setup/remote/EnterUrlScreen.kt @@ -1,5 +1,6 @@ package io.github.wiiznokes.gitnote.ui.screen.setup.remote +import android.os.Build import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Arrangement @@ -134,7 +135,7 @@ fun EnterUrlScreen( onClick = { val urlText = url.value.text scope.launch { - if (NetworkPermissionHelper.requiresLocalNetworkPermission(urlText) && !NetworkPermissionHelper.isPermissionGranted( + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN && NetworkPermissionHelper.requiresLocalNetworkPermission(urlText) && !NetworkPermissionHelper.isPermissionGranted( context ) ) { diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/utils/Fuzzy.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/utils/Fuzzy.kt deleted file mode 100644 index 89ee347a..00000000 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/utils/Fuzzy.kt +++ /dev/null @@ -1,62 +0,0 @@ -package io.github.wiiznokes.gitnote.ui.utils - -import io.github.wiiznokes.gitnote.data.room.Note -import java.util.Vector - -private const val TAG = "Fuzzy" - -// todo: highlight matching part (maybe by return List> to avoid wrapping the note struct -fun fuzzySort( - query: String, - notes: List -): List { - - val names = notes.map { it.nameWithoutExtension() } - val contents = notes.map { it.content } - - - val v1 = Vector() - val v2 = Vector() - - val done = BooleanArray(notes.size) { false } - - - for ((pos, name) in names.withIndex()) { - - if (name.contains(query, ignoreCase = true)) { - done[pos] = true - - if (name.startsWith(query)) { - v1.add(pos) - } else { - v2.add(pos) - } - } - - } - - for ((pos, name) in contents.withIndex()) { - if (done[pos]) { - continue - } - if (name.contains(query, ignoreCase = true)) { - - v2.add(pos) - } - - } - - val v1n = v1.map { pos -> - notes[pos] - } - - val v2n = v2.map { pos -> - notes[pos] - } - - val res = v1n + v2n - - return res -} - - diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/utils/rememberWindowInfo.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/utils/rememberWindowInfo.kt index 3320613d..0cbe55cc 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/utils/rememberWindowInfo.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/utils/rememberWindowInfo.kt @@ -1,29 +1,36 @@ package io.github.wiiznokes.gitnote.ui.utils import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @Composable fun rememberWindowInfo(): WindowInfo { - val configuration = LocalConfiguration.current + val windowInfo = LocalWindowInfo.current + val size = windowInfo.containerSize + + val widthDp = with(LocalDensity.current) { size.width.toDp() } + val heightDp = with(LocalDensity.current) { size.height.toDp() } + return WindowInfo( screenWidthInfo = when { - configuration.screenWidthDp < 600 -> WindowInfo.WindowType.Compact - configuration.screenWidthDp < 840 -> WindowInfo.WindowType.Medium + widthDp < 600.dp -> WindowInfo.WindowType.Compact + widthDp < 840.dp -> WindowInfo.WindowType.Medium else -> WindowInfo.WindowType.Expanded }, screenHeightInfo = when { - configuration.screenHeightDp < 480 -> WindowInfo.WindowType.Compact - configuration.screenHeightDp < 900 -> WindowInfo.WindowType.Medium + heightDp < 480.dp -> WindowInfo.WindowType.Compact + heightDp < 900.dp -> WindowInfo.WindowType.Medium else -> WindowInfo.WindowType.Expanded }, - screenWidth = configuration.screenWidthDp.dp, - screenHeight = configuration.screenHeightDp.dp + screenWidth = widthDp, + screenHeight = heightDp ) } + data class WindowInfo( val screenWidthInfo: WindowType, val screenHeightInfo: WindowType, diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/edit/MarkdownSmartEditor.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/edit/MarkdownSmartEditor.kt index 05030369..3c461b61 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/edit/MarkdownSmartEditor.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/edit/MarkdownSmartEditor.kt @@ -395,8 +395,8 @@ fun onLink(v: TextFieldValue): TextFieldValue { fun Int.max(b: Int): Int = max(this, b) /** - * @param f1: this callback will be called on each line selected. Return true if you want to short circuit. - * @param f2: this callback is also called on each line selected, after f1. Return the modified line + * @param f1 this callback will be called on each line selected. Return true if you want to short circuit. + * @param f2 this callback is also called on each line selected, after f1. Return the modified line */ private fun multiLinePrefixModifier( v: TextFieldValue, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9c1113c9..5f422a3c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,7 +81,7 @@ The application needs this permission to access the storage The application needs this permission to access repositories on your local network Use device storage - Requires permission to access the full device storage. You will be able to access the repository on your computer afterwards. + Requires permission to access the full device storage. You will be able to access the repository on your computer afterward. Create repository in this folder Open this repository Clone repository in this folder @@ -132,11 +132,11 @@ Create repository Next SSH keys - In order to access this repository, this public key must be copied as a deploy key + In order to access this repository, this public key must be copied as a deployment key Copy the key Copy Key Regenerate Key - Open webpage, and paste the deploy key. Make sure it is given Write Access + Open webpage, and paste the deployment key. Make sure it is given Write Access Open deploy key webpage Add the public key to the git provider Try Cloning… @@ -167,5 +167,6 @@ Cancel Go back Are you sure you want to quit ? You will lose all changes. + WARNING: Are you sure you want to continue? The directory will be deleted. From 7c7ea54ccb838defa39590afdb6064c815686a16 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Wed, 5 Aug 2026 13:03:07 +0200 Subject: [PATCH 3/3] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d22e4b7..fb986535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed -- Improve timestamp algorithme. O(n \* m) to O(n) +- Improve timestamp algorithm. O(n \* m) to O(n) +- Improve performance of the database creation. Use transaction and batch insert ## [26.08]