-
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
#11: Version tracking implementation #12
Conversation
Cool UI, looks nice! Couple of minor comments regarding it though:
Superficial testing has not revealed any bugs, versions are displayed correctly and the number of created files is correct. We will review and comment the code itself in next couple of days. |
I think it will also be good to have an up button on the version list screen. |
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.
PR lacks formatting
We also need VersionStorage(val root: Path)
as a separate class, think of it like it's a wrapper over .ark/versions
folder. For an example you can look at PreviewStorage.
It should contain the following methods:
versions(id: ResourceId): List<ResourceId>
addVersion(id: ResourceId, newVersion: ResourceId)
removeVersion(id: ResourceId, removedVersion: ResourceId)
fun saveNote(note: TextNote?,rootResourceId: String? = null):Long { | ||
if (note != null) { | ||
val path = getPath() | ||
if (path != null) { | ||
Files.list(path) |
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.
Can note really be null?
Better to avoid nesting too much
if (path == null)
return CODES_CREATING_NOTE.NOTE_NOT_CREATED.errCode.toLong()
@Parcelize | ||
data class Version ( | ||
val content: Content, | ||
@IgnoredOnParcel | ||
val meta: VersionMeta? = null | ||
): Parcelable | ||
{ | ||
@Parcelize | ||
data class Content( | ||
val idList: List<String> | ||
): Parcelable | ||
} |
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.
Could it be just like that?
I think meta.id
should be included in this list too.
@Parcelize
data class Version (
val idList: List<String>
): Parcelable
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.
Sorry, do you mean that i should remove rootResourceId and have it as first item in idList?
I agree with all said here, im already working on it, will push changes shortly. Thank you. |
What you exactly mean by lacks formatting? I have noticed that i have used my old habit from java of using function setter/getter instead of default setter/getter, i guess thats what you mean? Thank you in advance for the review. |
I mean code formatting. The indentation is wrong, there are not enough spaces in some places. |
import java.nio.file.Path | ||
import kotlin.io.path.createDirectories | ||
|
||
typealias ResourceId = Long |
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.
ResourceId
from the library should be used to avoid type diverging in future: https://github.com/ARK-Builders/arklib-android/blob/3334850384f84fe8d5f0545a221cc1fbc14e722e/lib/src/main/java/space/taran/arklib/Lib.kt
import space.taran.arkmemo.data.repositories.ResourceId | ||
|
||
|
||
data class VersionMeta( |
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.
So just ResourceId
could be used, but that will be trivial refactoring and can be done later.
import java.nio.file.Path | ||
|
||
object ArkFiles { | ||
const val ARK_FOLDER = ".ark" |
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.
(Future work)
We need to extract this variable into the Android bindings, it is defined in the library: https://github.com/ARK-Builders/arklib/blob/fea125e8330243522183c187a467dc762dec1fb7/src/lib.rs#L22
path, | ||
JsonParser.parseNoteContentToJson(note.content), | ||
)!! | ||
if (newResourceId < 0) {//Error creating TextNote file: |
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.
(Future work)
Good point, but ideally id must always come from the lib and this way we could not worry about wrong values.
In Rust lib it is always unsigned (u64
): https://github.com/ARK-Builders/arklib/blob/main/src/id.rs#L51
In Android bindings it is plain Long, but we can rely on it to be unsigned: https://github.com/ARK-Builders/arklib-android/blob/3334850384f84fe8d5f0545a221cc1fbc14e722e/lib/src/main/java/space/taran/arklib/Lib.kt#L50
I see, you are using computeId
in other places, this is good. I think it can be used here too.
if (newResourceId < 0) {//Error creating TextNote file: | ||
return newResourceId | ||
} | ||
versionStorage.addVersion(newResourceId, rootResourceId) |
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.
In addVersion
definition, order of the arguments is different:
fun addVersion(id: ResourceId, newVersion: ResourceId?) {
getPath()?.resolve(ARK_VERSIONS_DIR)?.resolve("${version.meta?.rootResourceId}") | ||
removeFileFromMemory(notePath) | ||
removeFileFromMemory(versionPath) | ||
//we should delete all notes listed in version that don't have been forked? by now it just deletes the last note. |
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.
Good question. IMO we should delete all versions, if user initiates deletion from the main screen. When we'll have forks, we'll need to find where the fork occurs and delete only chain of versions down to the fork, e.g. if we have versions A -> B -> C ->D and B -> E, and the user deletes D from main screen, then we remove everything except A, B and E.
If the user delete a version from versions screen, then only particular version must be removed and it's parent and children must be attached (grandparent becomes just parent): with versions A -> B -> C, deletion of B must result in versions A -> C. When we'll have forks, all children should be lifted: with versions A -> B -> C and B -> D, deletion of B would result in A -> C and A -> D.
private const val DUMMY_FILENAME = "Note" | ||
const val NOTE_EXT = "note" | ||
private const val DUMMY_FILENAME = "Note" | ||
private const val ARK_VERSIONS_DIR = ".ark/versions" |
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.
Better to use arkFolder().arkVersions()
, instead of this line. Usages of ARK_VERSIONS_DIR
should use arkFolder().arkVersions()
like here
ARK-Memo/app/src/main/java/space/taran/arkmemo/data/repositories/VersionStorage.kt
Line 16 in 9ca0459
private val versionsDir = root.arkFolder().arkVersions() |
close() | ||
} | ||
val fileName = filePath.fileName.toString() | ||
val rootResourceId: ResourceId = fileName.toLong() |
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.
(Future work)
We have a method for creating filenames from ids:
https://github.com/ARK-Builders/arklib/blob/main/src/id.rs#L31
val bufferedReader = BufferedReader(fileReader) | ||
val jsonVersion = StringBuilder() | ||
with(bufferedReader) { | ||
forEachLine { |
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.
But can we have more than 1 line in the version file?
Am I right, the format is like this?
rootId, version1, version2 ... latestVersion
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.
Or if it's JSON, then something like this?
{ root: ResourceId, children: Array<ResourceId> }
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.
Its JSON, right now the format its that the rootId its the filename, and the versions are contained inside the file:
{ idList: [] }
I think we have talked about putting the rootId as the first item in the list, so i will stop using VersionMeta, then the rootId will just be the first item, and the filename also.
We have talked about that we should only write to file in version when there is more than one version, so i will apply this too, so then the notes from main branch will be compatible.
val noteFile = File(path.toFile(), "$ResourceId.$NOTE_EXT") | ||
val notePath = noteFile.toPath() | ||
try { | ||
val fileReader = FileReader(noteFile) |
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.
I think we should extract common code working with files. Would be easier to read.
val fileWriter = FileWriter(tempFile) | ||
val bufferedWriter = BufferedWriter(fileWriter) | ||
writeToFile(bufferedWriter) | ||
val id = computeId(Files.size(tempFile.toPath()), tempFile.toPath()) |
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.
(Future work)
Would be cool to count the size during writing and the just use it instead of querying filesystem for file size. For now it is not really necessary.
|
||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
val tempFile = File(file, "${DUMMY_FILENAME}.${NOTE_EXT}") |
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.
(Future work)
Probably something standard should be used for creating temporary file.
val bufferedReader = BufferedReader(fileReader) | ||
val jsonVersion = StringBuilder() | ||
with(bufferedReader) { | ||
forEachLine { |
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.
Seems that we have 4 instances of this code reading a file, we should have a method for this.
|
||
@Parcelize | ||
data class Version( | ||
val content: Content, |
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.
Actually, title
from content
is also metadata, it will be moved into MetadataStorage
in the future.
val selectedNote = notes!![bindingAdapterPosition] | ||
val selectedVersion = version!! | ||
val isReadOnly = | ||
bindingAdapterPosition != notes!!.size - 1//read only unless its last item. |
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.
(Minor) I would write it like bindingAdapterPosition < notes!!size
We'll continue this work in other PR. |
Im still working on the new UI for showing notes older versions. Tomorrow(Friday) or Saturday will try to add the rest.
I wait for feedback. Will try to also make code more clean.
Thank you in advance.