-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#8: Separate user-defined properties into dedicated storage #18
Changes from 10 commits
d886729
c79892a
c8da6dc
184e9bd
4673959
c514149
03acd4d
f74f9bc
8013a8b
f7e0861
be5852b
6053864
b374c36
27ee2ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package space.taran.arkmemo.data.repositories | ||
|
||
import android.util.Log | ||
import dev.arkbuilders.arklib.computeId | ||
import dev.arkbuilders.arklib.data.index.RootIndex | ||
import dev.arkbuilders.arklib.user.properties.Properties | ||
import dev.arkbuilders.arklib.user.properties.PropertiesStorage | ||
import dev.arkbuilders.arklib.user.properties.PropertiesStorageRepo | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.coroutines.job | ||
import kotlinx.coroutines.withContext | ||
import space.taran.arkmemo.data.ResourceMeta | ||
import space.taran.arkmemo.models.TextNote | ||
import java.nio.file.Files | ||
import java.nio.file.Path | ||
import javax.inject.Inject | ||
import javax.inject.Singleton | ||
import kotlin.io.path.deleteIfExists | ||
import kotlin.io.path.exists | ||
import kotlin.io.path.extension | ||
import kotlin.io.path.fileSize | ||
import kotlin.io.path.forEachLine | ||
import kotlin.io.path.getLastModifiedTime | ||
import kotlin.io.path.name | ||
import kotlin.io.path.writeLines | ||
|
||
@Singleton | ||
class TextNotesRepo @Inject constructor() { | ||
|
||
private val _textNotes = MutableStateFlow(listOf<TextNote>()) | ||
val textNotes: StateFlow<List<TextNote>> = _textNotes | ||
|
||
private val iODispatcher = Dispatchers.IO | ||
|
||
private lateinit var propertiesStorage: PropertiesStorage | ||
private lateinit var propertiesStorageRepo: PropertiesStorageRepo | ||
|
||
private lateinit var root: Path | ||
|
||
suspend fun init(root: Path, scope: CoroutineScope) { | ||
this.root = root | ||
propertiesStorageRepo = PropertiesStorageRepo(scope) | ||
propertiesStorage = propertiesStorageRepo.provide(RootIndex.provide(root)) | ||
} | ||
|
||
suspend fun save(note: TextNote?) { | ||
if (note != null) { | ||
add(note) | ||
write(note) | ||
} | ||
} | ||
shubertm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
suspend fun delete(note: TextNote) = withContext(iODispatcher) { | ||
val path = root.resolve("${note.meta?.name}") | ||
remove(note) | ||
delete(path) | ||
propertiesStorage.remove(note.meta?.id!!) | ||
propertiesStorage.persist() | ||
Log.d("text-repo", "${note.meta?.name!!} has been deleted") | ||
} | ||
|
||
suspend fun read() = withContext(Dispatchers.IO) { | ||
val notes = mutableListOf<TextNote>() | ||
Files.list(root).forEach { path -> | ||
if (path.fileName.extension == NOTE_EXT) { | ||
try { | ||
val data = StringBuilder() | ||
path.forEachLine { | ||
data.appendLine(it) | ||
} | ||
val size = path.fileSize() | ||
val id = computeId(size, path) | ||
val meta = ResourceMeta( | ||
id, | ||
path.fileName.name, | ||
path.extension, | ||
path.getLastModifiedTime(), | ||
size | ||
) | ||
val titles = propertiesStorage.getProperties(id).titles | ||
val content = TextNote.Content(titles.elementAt(0), data.toString()) | ||
val note = TextNote(content, meta) | ||
notes.add(note) | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exceptions can occur here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initial implementation was throwing |
||
} | ||
} | ||
Log.d("text-repo", "${notes.size} text note resources found") | ||
_textNotes.value = notes | ||
} | ||
|
||
private suspend fun write(note: TextNote) = withContext(Dispatchers.IO) { | ||
shubertm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
val path = root.resolve("${DUMMY_FILENAME}.${NOTE_EXT}") | ||
if (!path.exists()) { | ||
try { | ||
val lines = note.content.data.split(NEWLINE) | ||
path.writeLines(lines) | ||
val size = path.fileSize() | ||
val id = computeId(size, path) | ||
val properties = Properties(setOf(note.content.title), setOf()) | ||
|
||
Log.d("text-repo", "initial resource name ${path.name}") | ||
|
||
propertiesStorage.setProperties(id, properties) | ||
propertiesStorage.persist() | ||
|
||
val newPath = root.resolve("$id.$NOTE_EXT") | ||
if (!newPath.exists()) { | ||
if (path.toFile().renameTo(newPath.toFile())) { | ||
kirillt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
note.meta = ResourceMeta( | ||
id, | ||
newPath.fileName.name, | ||
newPath.extension, | ||
newPath.getLastModifiedTime(), | ||
size | ||
) | ||
Log.d("notes-repo", "resource renamed to ${newPath.name} successfully") | ||
} else delete(path) | ||
} | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can any exceptions really occur here? If @hieuwu Please correct me if I'm wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The path.deleteIfExists() inside |
||
} | ||
this.coroutineContext.job | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we return an job object when we don't do anything with it? Using an Unit function is good enough in this case There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was using it locally, just forgot to remove it :) |
||
} | ||
|
||
private fun delete(path: Path) { | ||
path.deleteIfExists() | ||
} | ||
|
||
private fun add(note: TextNote) { | ||
val notes = textNotes.value.toMutableList() | ||
notes.add(note) | ||
_textNotes.value = notes | ||
} | ||
|
||
private fun remove(note: TextNote) { | ||
val notes = textNotes.value.toMutableList() | ||
notes.remove(note) | ||
_textNotes.value = notes | ||
} | ||
} | ||
|
||
private const val NOTE_EXT = "note" | ||
private const val DUMMY_FILENAME = "Note" | ||
private const val NEWLINE = "\n" | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do not keep state in repository layer. Repsitory's responsibility is containing logic to access to which data source. Please move the state to ViewModel and perform update the state itself there