Skip to content

Commit c602312

Browse files
CopilotAQSAMA
andcommitted
Fix folder system: sync virtual↔real dirs, expand button, drag-and-drop file move
Co-authored-by: AQSAMA <90539519+AQSAMA@users.noreply.github.com>
1 parent a7e433d commit c602312

5 files changed

Lines changed: 267 additions & 35 deletions

File tree

app/src/main/java/com/aqsama/neomarkor/data/repository/FolderRepositoryImpl.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class FolderRepositoryImpl(
2626
name: String,
2727
colorArgb: Int,
2828
parentId: String?,
29+
uriString: String?,
2930
): String {
3031
val existing = prefs.observeFolderMetadataMap().first()
3132
val siblings = existing.values.filter { it.parentId == parentId }
@@ -35,6 +36,7 @@ class FolderRepositoryImpl(
3536
colorArgb = colorArgb,
3637
parentId = parentId,
3738
orderIndex = nextIndex,
39+
uriString = uriString,
3840
)
3941
prefs.saveFolderMetadata(metadata)
4042
return metadata.id

app/src/main/java/com/aqsama/neomarkor/domain/model/FolderMetadata.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ data class FolderMetadata(
2525
val colorArgb: Int = FOLDER_PRESET_COLORS[0],
2626
val parentId: String? = null,
2727
val orderIndex: Int = 0,
28+
/** URI of the linked real filesystem directory, if any. */
29+
val uriString: String? = null,
2830
)

app/src/main/java/com/aqsama/neomarkor/domain/repository/FolderRepository.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,15 @@ interface FolderRepository {
1616

1717
/**
1818
* Creates a new folder with the given name and color, under [parentId] (null = root).
19+
* Optionally links the folder to a real filesystem directory via [uriString].
1920
* Returns the new folder's id.
2021
*/
21-
suspend fun createFolder(name: String, colorArgb: Int, parentId: String? = null): String
22+
suspend fun createFolder(
23+
name: String,
24+
colorArgb: Int,
25+
parentId: String? = null,
26+
uriString: String? = null,
27+
): String
2228

2329
/** Updates an existing folder's name and/or color. Null parameters are left unchanged. */
2430
suspend fun updateFolder(

app/src/main/java/com/aqsama/neomarkor/presentation/viewmodel/FolderViewModel.kt

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.aqsama.neomarkor.presentation.viewmodel
33
import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import com.aqsama.neomarkor.domain.model.FolderMetadata
6+
import com.aqsama.neomarkor.domain.model.FOLDER_PRESET_COLORS
67
import com.aqsama.neomarkor.domain.repository.FolderRepository
78
import com.aqsama.neomarkor.domain.repository.FileRepository
89
import com.aqsama.neomarkor.domain.model.FileNode
@@ -88,6 +89,16 @@ class FolderViewModel(
8889
)
8990
}.collect { state -> _uiState.value = state }
9091
}
92+
93+
// Auto-import real filesystem directories as virtual folders
94+
viewModelScope.launch {
95+
combine(
96+
fileRepository.observeFileTree(),
97+
folderRepository.observeFolders(),
98+
) { tree, folders -> Pair(tree, folders) }.collect { (tree, folders) ->
99+
syncFilesystemDirectories(tree, folders)
100+
}
101+
}
91102
}
92103

93104
fun toggleExpanded(id: String) {
@@ -117,7 +128,14 @@ class FolderViewModel(
117128

118129
fun createFolder(name: String, colorArgb: Int, parentId: String? = null) {
119130
viewModelScope.launch {
120-
folderRepository.createFolder(name, colorArgb, parentId)
131+
// Find the parent folder's filesystem URI so we create the directory in the right place
132+
val parentDirUri = if (parentId != null) {
133+
_uiState.value.folders.find { it.id == parentId }?.uriString
134+
} else null
135+
// Create the real filesystem directory first
136+
val realUri = fileRepository.createFolder(name, parentDirUri)
137+
// Register as a virtual folder linked to the real directory
138+
folderRepository.createFolder(name, colorArgb, parentId, realUri)
121139
}
122140
}
123141

@@ -185,4 +203,60 @@ class FolderViewModel(
185203

186204
private fun flattenNotes(nodes: List<FileNode>): List<FileNode> =
187205
nodes.flatMap { n -> if (n.isDirectory) flattenNotes(n.children) else listOf(n) }
206+
207+
/**
208+
* Imports filesystem directories into the virtual folder list so that real folders in the
209+
* chosen root directory appear automatically in the app's folder browser.
210+
* Handles nested directories by processing them in BFS order within a single coroutine.
211+
*/
212+
private fun syncFilesystemDirectories(
213+
fileTree: List<FileNode>,
214+
existingFolders: List<FolderMetadata>,
215+
) {
216+
val existingUris = existingFolders.mapNotNull { it.uriString }.toSet()
217+
// Collect new directories in parent-first order so children can resolve parent IDs
218+
val toCreate = collectDirectoriesToSync(fileTree, existingUris)
219+
if (toCreate.isEmpty()) return
220+
221+
// Build a URI→id map seeded from already-known folders
222+
val knownUriToId = existingFolders
223+
.mapNotNull { f -> f.uriString?.let { it to f.id } }
224+
.toMap()
225+
.toMutableMap()
226+
227+
// Single coroutine to avoid concurrent DataStore reads/writes
228+
viewModelScope.launch {
229+
toCreate.forEach { (dir, parentUri) ->
230+
val parentId = parentUri?.let { knownUriToId[it] }
231+
val newId = folderRepository.createFolder(
232+
name = dir.name,
233+
colorArgb = FOLDER_PRESET_COLORS[0],
234+
parentId = parentId,
235+
uriString = dir.uriString,
236+
)
237+
// Track so subsequent children can resolve this folder as their parent
238+
knownUriToId[dir.uriString] = newId
239+
}
240+
}
241+
}
242+
243+
/**
244+
* Returns a flat list of (directory, parentUri) pairs for all directories not yet tracked,
245+
* preserving parent-before-child ordering so IDs can be resolved incrementally.
246+
*/
247+
private fun collectDirectoriesToSync(
248+
nodes: List<FileNode>,
249+
existingUris: Set<String>,
250+
parentUri: String? = null,
251+
): List<Pair<FileNode, String?>> {
252+
val result = mutableListOf<Pair<FileNode, String?>>()
253+
nodes.filter { it.isDirectory }.forEach { dir ->
254+
if (dir.uriString !in existingUris) {
255+
result.add(dir to parentUri)
256+
}
257+
// Always recurse so we find new children under already-tracked parents
258+
result.addAll(collectDirectoriesToSync(dir.children, existingUris, dir.uriString))
259+
}
260+
return result
261+
}
188262
}

0 commit comments

Comments
 (0)