PROJECT: Notably


Overview

Notably is for those who prefer to use a desktop app for managing notes. More importantly, Notably is optimized for those who prefer to work with a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, Notably can get your notes taken down faster than traditional GUI apps.

Summary of contributions

  • Major enhancement: added a tree data structure

    • What it does: allows the user to store their notes in a hierarchical form, similar to that of a file-folder structure in an Operating System

    • Justification: This feature allows for a more organised storage and access of notes. The design is based on the practice of users organising their physical notes in sections and divisions for clarity. A note (or referred to internally as a Block) also doubles as a folder, allowing for a recursive data structure using only notes. As a result, this design has an added advantage of allowing for efficient navigation with the use of Paths.

    • Highlights: This enhancement is a complete rewrite of the data model while maintaining some design considerations from the previous iteration AB3. It required a lot of careful consideration about its design since it is the core feature of Notably. Also, the data structure would have to be observable to work seamlessly with the UI’s sidebar TreeView. It was time consuming to experiment with a few design options before settling on wrapping JavaFX’s TreeItem objects.

    • Credits:

  • Minor enhancement:

    • Refactored and created Block class with Title and Body classes

    • Updated the Storage to store and access JSON data which supports nested Blocks as a result of the new Tree data structure

    • Create sample Block data in the form of a first-time user tutorial walkthrough

  • Code contributed: [Functional code] [Test code]

  • Other contributions:

    • Project management:

      • Set up GitHub repository

      • Managed tags v1.1 and v1.3 (2 tags) on GitHub

    • Enhancements to existing features:

      • Updated UserPrefs

    • Documentation:

      • Improvements to UG command information: 306

      • Added section on tree data structure implementation and other improvements in DG

    • Community:

      • PRs reviewed (with non-trivial review comments): #175, #429, #443

      • Reported bugs and suggestions for other teams in the class (examples: 1, 2)

Contributions to the User Guide

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

Create a new note: new

If you want to add a new note (to the path of the currently open note), use the new command and specify the TITLE of the note.

Format: new -t TITLE [-o]

  • Don’t forget the -t flag!

  • The TITLE for the note MUST be specified.

  • The TITLE for the note can only contain alphabets, numbers, symbols (except / and -) and whitespaces.

  • The TITLE for the note cannot begin with whitespaces or the period . characer.

  • Duplicate notes are not allowed under the same note.

    • This is explained here

  • Use the optional -o flag to immediately open the note after creating it.

  • Leaving out the -o open flag can be useful if you want to create multiple notes quickly for future use, but you don’t need to edit them right away!

  • Want to save some time? Use the n shorthand instead of new

    • n -t TITLE [-o]

Creating duplicate notes

Creating duplicate notes are not allowed as mentioned 3.3. What are considered duplicate notes?

  • Two notes are considered duplicates IF their TITLEs are the same while ignore their case. For example:

    • Hello, hello, HELLO and HeLLo are considered duplicates

    • hello world!, helloworld! and helloworld@ are not considered duplicates, because whitespaces and allowed symbols are not ignored

  • Two notes are considered duplicate IF they have the same TITLE under the same note. This can be seen in the following diagrams below:

    PathDuplicates1
    Figure 1. INVALID as there are duplicate notes under /Workspace/CS2103
    PathDuplicates2
    Figure 2. VALID as they are not both directly under /Workspace/CS2103
    PathDuplicates3
    Figure 3. VALID as one exists in /Workspace/CS2103 and the other in /Workspace/CS2101

Example: Creating new notes

  1. Let’s first create a note (with a TITLE), and open it immediately after. Type this command:

    new -t Notably -o
  2. Due to the -o flag, the currently open note is now Notably (as seen in the sidebar). Let’s add a new note to the currently opened note, (Step 2) with a TITLE as follows:

    new -t CS2103T
    • Observe that the (Absolute) Path to the currently open note is now changed from /Workspace in step 1 to /Workspace/Notably in step 2.

      • Not sure where to see this path? Check it out here

      • Not sure what Absolute Paths are or why the new note is created in this way? Check it out here

  3. The new note CS2103T has been created successfully!

    • Observe that the note CS2103T was not opened immediately as seen in step 3

      • This is because the -o open flag was not specified, so the note CS2103T was created without opening it immediately.

New1
Figure 4. Step 1 - Create a new note with the title "Notably" (with the -o open flag)
New2
Figure 5. Step 2 - Create a new note with the title "CS2103T" (without the -o open flag)
New3
Figure 6. Step 3 - Final state after creating both notes

Delete a note: delete

If you no longer need a note, or if you have accidentally created a wrong note, don’t worry! You can always delete that note.

Format: delete [-t] AbsolutePath/RelativePath

  • Requires prior understanding of AbsolutePath and RelativePath. You can refer here for an explanation

  • Deleting a note will also delete its child notes. Be very careful! You can refer to the example below for an illustration

  • Deleting the root note (i.e. delete /) is prohibited.

  • You can delete the currently open note using the command delete .

  • Want to save some time? Drop the optional -t flag and use the d shorthand instead of delete

    • d AbsolutePath/RelativePath

Example: Deleting a note under the currently open note

  1. Let’s delete the Notably note using this command:

    delete -t Notably
    • The currently open note is Workspace

    • The above example uses RelativePath. You can achieve the same result as above by using an AbsolutePath instead by executing delete -t /Workspace/Notably.

      • Not sure about AbsolutePath and RelativePath? You can refer here for a clearer explanation

  2. The note with the title Notably is deleted successfully since it exists!

    • Another Note, CS2103 and ST2334 are immediately deleted as well since they are notes under Notably

    Delete1
    Figure 7. Step 1 - Key in the example command
    Delete2
    Figure 8. Final state after deletion

Deleting the currently open note

  1. In this example, Notably is the currently open note. Let’s delete it using the command:

    delete .
    • The . in the command is a RelativePath that points to the path of the currently open note

  2. Notably note is deleted and the currently open note is now Workspace

    • This will change the currently open note to the deleted note’s parent note

    Delete3
    Figure 9. Step 1 - Key in the example command
    Delete2
    Figure 10. Final state after deletion

Contributions to the Developer Guide

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

Model

ModelClassDiagram
Figure 11. Structure of the Model Component

API : Model.java

The Model,

  • stores and manipulates the BlockTree data that represents a tree of Blocks, through BlockModel

  • stores and manipulates a list of suggestions based on the user’s input, through SuggestionModel

  • stores the current state of the View, through ViewStateModel

    • stores the command input given by the user, through CommandInputModel

    • stores the state of the help modal being open, through HelpFlagModel

    • stores the state of the current block’s edit modal being open, through BlockEditFlagModel

  • stores UserPref data that represents the user’s preferences, through UserPrefModel

BlockModel component

BlockModelClassDiagram
Figure 12. Structure of the Model Component

The BlockModel

  • stores and directly manipulates the BlockTree

    • contains a single BlockTreeItem as the root; the tree is built by adding chilren BlockTreeItems to the root

      • each BlockTreeItem stores the reference to its parent and children BlockTreeItems, and its own content, through TreeItem<Block>

        • stores its content, through Block

          • contains the Title and Body content

Storage

StorageClassDiagram
Figure 13. Structure of the Storage Component

API : Storage.java

The Storage component,

  • can save UserPref objects in JSON format and read it back.

  • can save Notably’s BlockModel data in JSON format and read it back.

    • stores the BlockTree and also the path of the last opened Block

Tree Data Structure

Notably aims to provide end user a neat and well-organized workspace to store their notes. This is done by creating a tree structure; allowing users to create folder-like paths to organize their notes and group them into categories to their own liking.

Rationale

While this can be done with a linear data structure (a simple list), a linear list of notes would require more work to establish the relationship between groups of notes. A tree data structure supports this better, giving a clearer distinction while also establishing a form of hierarchy (as seen in the design example below).

On top of that, observability must be ensured so that the UI can update with any changes that happen on the tree (and its nodes) and also the data within each node.

TreeDataStructureDesign
Figure 14. Tree Data Structure Design Example

Current Implementation

A custom tree data structure that supports observability has been implemented. As seen here, the BlockModel is the entry of point of manipulating the data tree. The tree (referred to as BlockTree) is made up of tree nodes (referred to as BlockTreeItem). The tree is observable such that if any change occurs on any of the tree’s nodes, the change event will bubble upwards to the root node. Hence, the root node serves as the entry point for the BlockTree.

To achieve this design, a BlockTreeItem needs to contain 3 primary components:

  • an Observable reference to its parent

  • an ObservableList of its children

  • User’s note data (referred to as Block data) consisting of:

    • Title of the note

    • Body content of the note (optional)

After multiple designs, the current implementation now has BlockTreeItem using an underlying TreeItem<Block> to handle the general behaviour of a tree node.

When manipulating the BlockTree, the execution of any operation is always split in this order:

  1. Get the currentlyOpenPath from the BlockModel

  2. BlockModel carries out the command required based on that currentlyOpenPath

An example of an operation is new -t CS2103T. To execute this, the following sequence occurs:

  1. NewCommandParser creates the Block that has the title 'CS2103T' and a default empty body.

    • The Title and Body objects are created as well

  2. NewCommand then calls the Model and in turn, the BlockModel to add this Block to the BlockTree

  3. BlockModel first obtains the currentlyOpenPath to execute the operation on, i.e in this case, to add the new Block on the path

  4. BlockModel calls BlockTree to add the Block to the AbsolutePath obtained from BlockModel

  5. BlockTree creates a BlockTreeItem using the Block parsed earlier.

  6. Subsequently, the underlying TreeItem<Block> is created and the BlockTreeItem is then added to the BlockTree.

Below is a sequence diagram that demonstrates this example

TreeDataStructureSequenceDiagram
Figure 15. Tree Data Structure SequenceDiagram

Design Considerations

Aspect: Using JavaFX’s TreeItem<T> vs implementing a BlockTreeItem from scratch

Current choice: Using JavaFX’s TreeItem<T>

Pros:

  • seamless integration with JavaFX’s TreeView which is used in Notably’s sidebar to show the notes in a traditional file-browser-like manner

  • TreeItem<T> has the requirements of BlockTreeItem’s design already implemented to a usable extent

  • conveniently handles underlying event handling required for BlockTree to be observable

Cons:

  • Implementation still requires wrapping and unwrapping of underlying TreeItem<T> to work with TreeView

Aspect: BlockTreeItem vs Folders to represent path structure

Current choice: BlockTreeItem

Pros:

  • No need for an additional class. Having a separate folder object would also require a separate UI View since folders should not contain any block data.

Cons:

  • Somewhat unconventional design. User might be unfamiliar with the intention on first use, without proper explanation

Aspect: Root should also be a BlockTreeItem

Pros:

  • Seamless transition to JSON storage

Cons:

  • Need to add constraint to ensure that the root BlockTreeItem does not contain any Body and is also unmodifiable