Clevernote is an Evernote clone created with React, Redux, Rails, and PostgreSQL. Users can create notes, save them to notebooks, and organize them with tags.
- Secure user authentication
- Create and edit notes in a rich text editor
- Save and organize notes to notebooks
- Add note tags
- Filter notes by notebook and tag
- React (front-end library)
- AJAX
- Ruby on Rails
- PostgreSQL (database)
The frontend of this project focused on using reusable React components. By sketching out the component hierarchy during the design phase, I was able to easily plan the architecture of UI components.
Clevernote features a rich text editor where users can edit and save notes.
I chose to integrate an existing rich text editor library called Quill. By carefully reading the docs, reading the examples, and experimenting, I implemented a full-featured editor with a style that matches the original Evernote editor.
Autosaving a note was a simple addition that makes a big difference in a user's experience. I implemented autosave using a simple timer that saves the note after a user finishes writing:
if (this.autosaveTimeoutId) {
clearTimeout(this.autosaveTimeoutId);
}
this.autosaveTimeoutId = setTimeout(this.saveNote.bind(this), 500);
}
By using a reusable component for showing a list of notes, a user can easily search for a set of notes with a given tag. Tag search is seamless and intuitive.
Implementing permanent URLs for a searched tag was a challenge, so that a user could return to a search at a later time by visiting the same URL. After some thought, I wrote a simple URL router that searches the current URL for keywords, and dispatches UI actions when the URL changes to a new keyword. When a UI action is received, the UI updates to the current tag search.
These routes were implemented using React Router, a popular library for managing the URL and history of the browser.
<Switch>
<Route exact path="/app/notes" component={AllNotesPage} />
<Route path="/app/notes/:noteId" component={AllNotesPage} />
<Route exact path="/app/notebooks" component={NotebooksIndexContainer} />
<Route
path="/app/notebooks/:notebookId/notes/:noteId"
component={NotebookNotesPage}
/>
<Route path="/app/notebooks/:notebookId" component={NotebookNotesPage} />
<Route exact path="/app/tags" component={TagsIndexContainer} />
<Route path="/app/tags/:tagId/notes/:noteId" component={TaggedNotesPage} />
<Route path="/app/tags/:tagId/" component={TaggedNotesPage} />
<Redirect to="/app/notes" />
</Switch>