Skip to content
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

Closed
wants to merge 4 commits into from

Conversation

AgCaliva
Copy link

@AgCaliva AgCaliva commented Dec 9, 2022

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.

@kirillt
Copy link
Member

kirillt commented Dec 17, 2022

Cool UI, looks nice! Couple of minor comments regarding it though:

  • Would be good to add some text labels like "history of changes" for the list of versions.
  • When user opens a version, probably better to make it on gray background to show that it is in read-only mode.
  • Existing notes created with main version are not displayed.

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.

@shubertm
Copy link
Member

I think it will also be good to have an up button on the version list screen.
And i think making the latest version of a note editable from versions screen may improve UX.

Copy link
Member

@mdrlzy mdrlzy left a 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)

Comment on lines 26 to 30
fun saveNote(note: TextNote?,rootResourceId: String? = null):Long {
if (note != null) {
val path = getPath()
if (path != null) {
Files.list(path)
Copy link
Member

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()

Comment on lines 8 to 19
@Parcelize
data class Version (
val content: Content,
@IgnoredOnParcel
val meta: VersionMeta? = null
): Parcelable
{
@Parcelize
data class Content(
val idList: List<String>
): Parcelable
}
Copy link
Member

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

Copy link
Author

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?

app/src/main/java/space/taran/arkmemo/data/VersionMeta.kt Outdated Show resolved Hide resolved
@AgCaliva
Copy link
Author

I agree with all said here, im already working on it, will push changes shortly. Thank you.

@AgCaliva
Copy link
Author

PR lacks formatting

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.

@mdrlzy
Copy link
Member

mdrlzy commented Dec 21, 2022

What you exactly mean by lacks formatting?

I mean code formatting. The indentation is wrong, there are not enough spaces in some places.
Just use Ctrl+Alt+L when editing
https://blog.jetbrains.com/idea/2020/06/code-formatting/

@kirillt kirillt temporarily deployed to Development December 22, 2022 01:11 — with GitHub Actions Inactive
import java.nio.file.Path
import kotlin.io.path.createDirectories

typealias ResourceId = Long
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import space.taran.arkmemo.data.repositories.ResourceId


data class VersionMeta(
Copy link
Member

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"
Copy link
Member

@kirillt kirillt Dec 22, 2022

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:
Copy link
Member

@kirillt kirillt Dec 22, 2022

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)
Copy link
Member

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.
Copy link
Member

@kirillt kirillt Dec 22, 2022

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"
Copy link
Member

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

private val versionsDir = root.arkFolder().arkVersions()

close()
}
val fileName = filePath.fileName.toString()
val rootResourceId: ResourceId = fileName.toLong()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

val bufferedReader = BufferedReader(fileReader)
val jsonVersion = StringBuilder()
with(bufferedReader) {
forEachLine {
Copy link
Member

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

Copy link
Member

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> }

Copy link
Author

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)
Copy link
Member

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())
Copy link
Member

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}")
Copy link
Member

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 {
Copy link
Member

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,
Copy link
Member

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.
Copy link
Member

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

@kirillt
Copy link
Member

kirillt commented Jan 16, 2023

We'll continue this work in other PR.

@kirillt kirillt closed this Jan 16, 2023
@kirillt kirillt changed the title Version tracking implementation #11: Version tracking implementation May 27, 2023
@kirillt kirillt mentioned this pull request Sep 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants