Skip to content

Commit

Permalink
Merge pull request #136 from penge/next
Browse files Browse the repository at this point in the history
Improve navigation between notes and link modal
  • Loading branch information
penge authored Oct 11, 2020
2 parents 6e2b942 + bbce3f2 commit 809c741
Show file tree
Hide file tree
Showing 16 changed files with 152 additions and 53 deletions.
8 changes: 8 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
52 changes: 42 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ My Notes needs to be open in the second computer and same Google Account needs t

## Google Drive Sync

Google Drive Sync (see Options) saves your notes to Google Drive and synchronizes the changes between your local My Notes and your Google Drive
every time you hit the Sync button (bottom left corner).
Google Drive Sync (see Options) saves your notes to your personal Google Drive and synchronizes the changes between your local My Notes and your Google Drive every time you hit the Sync button (bottom left corner).

Benefits:

Expand All @@ -88,18 +87,23 @@ Benefits:

### Location

Notes are uploaded to your Google Drive to the folder **My Notes**. This folder is created automatically.
If the folder exists from a previous installation, notes are downloaded and uploaded.
Notes are uploaded to your Google Drive to the folder "My Notes". This folder is created automatically.
If the folder exists from a previous installation, notes are downloaded and uploaded and the synchronization continues.

### Synchronization

Notes are synchronized every time you hit the Sync button (bottom left corner).
Synchronization works in both ways — to Google Drive, from Google Drive.

Notes are synchronized every time you hit the Sync button.
While the synchronization is in progress, Sync button will spin.
Sync button tooltip shows the time of the most recent synchronization.

### Access

My Notes has only access to the folder **My Notes**.
It cannot see any other files in your Google Drive.
My Notes has only access to the folder "My Notes" and only to the files it created in this folder.
It cannot see, access or modify any other files in your Google Drive.

Google Drive Sync works only if the extension is installed from Web Store.

<br><br>

Expand Down Expand Up @@ -148,6 +152,7 @@ tests/ # Entrypoint for tests
# - Prints "Assertion failed: console.assert" if any assertion failed
.editorconfig # To enforce same editor configuration
.eslintrc # To enforce code quality and same coding style
.gitignore # To exclude any generated files (only .DS_Store at this point)
Expand All @@ -172,6 +177,25 @@ README.md

<br><br>

## Browser support

My Notes has full support in Google Chrome only. Although it may be possible to install it in other browsers, the support is not complete.

Support for other Chromium-based browsers will be added if possible.

<br><br>

## Security and Privacy

My Notes doesn't collect any personal information or data.
All your notes are stored locally in your browser.
If you use Google Drive Sync, My Notes can backup the notes to your personal Google Drive.

To provide Google Drive functionality, My Notes has an application in Google Cloud.
The sole purpose of this application is to authenticate you securely towards the Google Drive and to allow the synchronization of notes.

<br><br>

## Permissions

My Notes has the permissions listed in `manifest.json`.
Expand All @@ -181,12 +205,20 @@ My Notes has the permissions listed in `manifest.json`.
- `"storage"` — used to save the notes and options to Chrome Storage
- `"contextMenus"` — used to create My Notes Context menu

Required permissions are shown to the user before installing the extension, and are needed at all times to provide the basic functionality.

**Optional:**

- `"tabs"` — used by **Open My Notes in every New Tab** (see **Options**)
- `"identity"` — used by **Enable Google Drive Sync** (see **Options**)
- `"tabs"` — needed for "Open My Notes in every New Tab" (see Options)
- `"identity"` — needed for "Enable Google Drive Sync" (see Options)

Optional permissions are needed only to provide additional functionality that can be enabled via a checkbox in Options.

User has the choice to either approve or deny the permissions.

`"tabs"` is a more powerful permission — the displayed warning may contain an unrelated message — `It can: Read your Browsing history`.
`"tabs"` is a more powerful permission and browsers usually display a generic, in this case unrelated warning as `It can: Read your Browsing history`.
My Notes doesn't read your browsing history. The permission is only needed to enable "Open My Notes in every New Tab".
To read more about the warnings, see the [offical documentation](https://developer.chrome.com/extensions/permission_warnings).

<br><br>

Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "My Notes",
"description": "Simple and fast note-taking.",
"version": "3.4.1",
"version": "3.4.2",
"homepage_url": "https://github.com/penge/my-notes",
"icons": { "128": "images/icon128.png" },
"options_page": "options.html",
Expand Down
8 changes: 6 additions & 2 deletions notes.css
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ body.with-sidebar { left: 25%; }
body:not(.with-sidebar) { left: 0 !important; }

#sidebar-notes {
margin-right: 1px; /* Drag */
padding: .5em;
overflow-y: auto;
flex-grow: 1;
Expand Down Expand Up @@ -220,7 +219,7 @@ body.resizing-sidebar-locked-max { cursor: w-resize; }
font-weight: bold;
}

body.with-command #content a {
body.with-control #content a {
cursor: pointer;
}

Expand Down Expand Up @@ -367,6 +366,11 @@ body.with-modal #toolbar {
color: var(--modal-button-text-color);
}

#modal .modal-description {
font-size: .75em;
margin-top: 1.5em;
}

body.with-overlay #modal {
border: none !important;
}
Expand Down
6 changes: 6 additions & 0 deletions notes.html
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,12 @@
<input type="button" value="Cancel" id="cancel" />
<input type="submit" value="Confirm" id="confirm" class="bold" />
</div>
<!-- Place for modal description -->
</div>

<div class="modal-description" id="insert-link-modal-description">
To open the inserted link, hold down the <span class="hotkey">Ctrl</span> key.<br><br>
Link can start with <span class="hotlink">http</span>, <span class="hotlink">https</span>, or <span class="hotlink">chrome-extension</span> if referencing other note.
</div>
</template>

Expand Down
26 changes: 20 additions & 6 deletions notes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global chrome, window, document, Set, localStorage, setTimeout */
/* global chrome, window, document, Set, localStorage */

// Setting application state and view updates are done via a Proxy
import state from "./notes/state/index.js";
Expand All @@ -12,6 +12,8 @@ import sidebar from "./notes/sidebar.js";
import { newNoteModal } from "./notes/modals.js";
import contextMenu from "./notes/context-menu.js";

import notesHistory from "./notes/history.js";

let tabId; // important so can update the content in other tabs (except the tab that has made the changes)
chrome.tabs.getCurrent((tab) => {
// set as a "string" to quickly compare with localStorage.getItem("notesChangedBy")
Expand Down Expand Up @@ -77,7 +79,7 @@ chrome.storage.local.get([
const {
notification,
font, size, sidebar, sidebarWidth, toolbar, theme, customTheme,
notes, active,
notes, active : lastActive,
focus, tab,
sync } = local;

Expand All @@ -93,7 +95,12 @@ chrome.storage.local.get([

// Notes
state.notes = notes;
state.active = (active in notes) ? active : null;
const activeFromUrl = window.location.search.startsWith("?") && window.location.search.substring(1); // Bookmark
const activeCandidate = activeFromUrl || lastActive || "Clipboard";
state.active = (activeCandidate in notes) ? activeCandidate : null;
if (state.active && !activeFromUrl) {
notesHistory.replace(activeCandidate);
}

// Options
state.focus = focus;
Expand Down Expand Up @@ -148,10 +155,11 @@ chrome.storage.onChanged.addListener((changes, areaName) => {
const newNotes = changes["notes"].newValue;
state.notes = newNotes;

// Autoactivate the created note
// Auto-activate the created note
const newActive = changes["active"] && changes["active"].newValue;
if (newActive && newActive in newNotes) {
state.active = newActive;
notesHistory.push(newActive);
return;
}

Expand All @@ -169,6 +177,7 @@ chrome.storage.onChanged.addListener((changes, areaName) => {
) {
const newActive = state.active;
state.active = newActive;
notesHistory.replace(newActive);
return;
}

Expand All @@ -182,6 +191,7 @@ chrome.storage.onChanged.addListener((changes, areaName) => {
const newSet = new Set(Object.keys(newNotes));
if (oldSet.size > newSet.size) {
state.active = "Clipboard";
notesHistory.replace("Clipboard");
return;
}

Expand All @@ -191,6 +201,7 @@ chrome.storage.onChanged.addListener((changes, areaName) => {
if (diff.size === 1) {
const newActive = diff.values().next().value;
state.active = newActive;
notesHistory.replace(newActive);
}
}
}
Expand All @@ -213,7 +224,7 @@ chrome.storage.onChanged.addListener((changes, areaName) => {
});

const openLink = (event) => {
if (!document.body.classList.contains("with-command")) {
if (!document.body.classList.contains("with-control")) {
return;
}

Expand All @@ -226,9 +237,12 @@ content.addEventListener("click", openLink); // Chrome OS, Windows
content.addEventListener("contextmenu", openLink); // OSX would open Context menu when holding Ctrl

window.addEventListener("blur", () => {
document.body.classList.remove("with-command");
document.body.classList.remove("with-control");
});

// History
notesHistory.attach(state);

// Notes are saved every 1 second by "saveNotesDebounce()"
// When the window is closed sooner, save the notes immediately, if changed
window.addEventListener("beforeunload", () => {
Expand Down
5 changes: 5 additions & 0 deletions notes/context-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import range from "./range.js";

import { contextMenu, renameAction, deleteAction } from "./view/elements.js";
import { renameNoteModal, deleteNoteModal } from "./modals.js";
import { isReserved } from "./reserved.js";

const hide = () => {
if (!document.body.classList.contains("with-context-menu")) {
Expand All @@ -15,6 +16,10 @@ const hide = () => {
};

const show = (x, y, noteName) => {
if (isReserved(noteName)) {
return;
}

range.save();

contextMenu.style.left = x + "px";
Expand Down
32 changes: 32 additions & 0 deletions notes/history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* global window */

// Use replace when a note is renamed or deleted
const replace = (noteName) =>
window.history.replaceState({ noteName }, noteName, `?${noteName}`);

// Use push when a note is created
const push = (noteName) =>
window.history.pushState({ noteName }, noteName, `?${noteName}`);

let attached = false;
const attach = (state) => {
if (attached) {
return;
}

window.onpopstate = (e) => {
const noteName = e.state && e.state.noteName;
if (noteName && noteName in state.notes) {
state.active = noteName;
}
};

attached = true;
};

export default {
replace,
push,

attach,
};
18 changes: 3 additions & 15 deletions notes/hotkeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const toggleFocus = () => {

const keydown = (state) => document.addEventListener("keydown", (event) => {
if (event.ctrlKey) {
document.body.classList.add("with-command");
document.body.classList.add("with-control");
const hoveredNote = document.querySelector(".note.over");
hoveredNote && state.activateNote(hoveredNote.innerText);
}
Expand All @@ -32,7 +32,7 @@ const keydown = (state) => document.addEventListener("keydown", (event) => {
// Look for #confirm in #modal
const confirm = document.getElementById("confirm");
if (confirm) {
// if modal is open and thefore can be confirmed and closed with Enter,
// if modal is open and therefore can be confirmed and closed with Enter,
// prevent default behaviour of Enter
event.preventDefault();
confirm.click();
Expand Down Expand Up @@ -63,18 +63,6 @@ const keydown = (state) => document.addEventListener("keydown", (event) => {
return;
}

if ((event.metaKey || event.ctrlKey) && event.key === "[") {
event.preventDefault();
state.previousNote();
return;
}

if ((event.metaKey || event.ctrlKey) && event.key === "]") {
event.preventDefault();
state.nextNote();
return;
}

if ((event.metaKey || event.ctrlKey) && event.shiftKey && (event.key === "F" || event.key === "f")) {
event.preventDefault();
state.active && toggleFocus(); // toggle focus only when a note is open
Expand All @@ -89,7 +77,7 @@ const keydown = (state) => document.addEventListener("keydown", (event) => {
});

const keyup = () => document.addEventListener("keyup", () => {
document.body.classList.remove("with-command");
document.body.classList.remove("with-control");
});

const register = (state) => {
Expand Down
13 changes: 10 additions & 3 deletions notes/modals.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,22 @@ const removeModal = (onRemove) => {
return true;
};

const createModal = ({ clazz, overlay, captionValue, inputValue, cancelValue, confirmValue, validate, onConfirm, onRemove }) => {
const createModal = ({ clazz, overlay, captionValue, inputValue, cancelValue, confirmValue, descriptionId, validate, onConfirm, onRemove }) => {
removeModal();
range.save();

const node = modalTemplate.content.cloneNode(true);
const modal = node.children[0];
const modal = node.getElementById("modal");

if (clazz) {
modal.className = clazz;
}

if (descriptionId) {
const modalDescription = node.getElementById(descriptionId);
modal.append(modalDescription);
}

const caption = node.getElementById("caption"); if (captionValue) { caption.innerHTML = captionValue; } else { caption.className = "hide"; }
const input = node.getElementById("input"); if (typeof inputValue === "string") { input.value = inputValue; } else { input.className = "hide"; }
const cancel = node.getElementById("cancel"); if (cancelValue) { cancel.value = cancelValue; }
Expand Down Expand Up @@ -60,7 +66,7 @@ const createModal = ({ clazz, overlay, captionValue, inputValue, cancelValue, co

document.body.classList.add("with-modal");
if (overlay) { document.body.classList.add("with-overlay", overlay); }
document.body.prepend(node);
document.body.prepend(modal);

input.focus();
input.onblur = () => {
Expand All @@ -85,6 +91,7 @@ export const insertLinkModal = (onConfirm) => {
captionValue: "Link URL",
inputValue: "",
confirmValue: "Insert",
descriptionId: "insert-link-modal-description",
validate: (inputValue) => inputValue.length > 0,
onConfirm,
});
Expand Down
Loading

0 comments on commit 809c741

Please sign in to comment.