Skip to content

Commit

Permalink
Merge pull request #327 from penge/export
Browse files Browse the repository at this point in the history
Export notes
  • Loading branch information
penge authored Oct 27, 2021
2 parents aced9c3 + 284fdc1 commit 89c3f35
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 42 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ It cannot see, access nor modify, any other files in your Google Drive.
7. Use [<ins>Context menu</ins>](#context-menu) to transfer selected text to My Notes on other computers.
8. Use [<ins>Custom theme</ins>](#custom-theme) to customize the look of My Notes in any way as needed.
9. Drag and Drop selected text onto a note's name in the Sidebar to insert the text into the note.
10. Drag and Drop a TXT file anywhere in the bottom part of the Sidebar (the area with 3 icons) to import the file as a new note.
10. Drag and Drop a TXT or HTML file anywhere in the bottom part of the Sidebar (the area with 3 icons) to import the file as a new note.
11. Drag the Sidebar line to resize the Sidebar, double-click on the Sidebar line to restore the original Sidebar width.

<br><br>
Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
},
"dependencies": {
"clsx": "^1.1.1",
"fflate": "^0.7.1",
"preact": "10.5.14"
}
}
7 changes: 6 additions & 1 deletion src/notes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { sendMessage } from "messages";
import notesHistory from "notes/history";
import keyboardShortcuts, { KeyboardShortcut } from "notes/keyboard-shortcuts";
import { Command, commands } from "notes/commands";
import { exportNote } from "notes/export";

const getFocusOverride = (): boolean => new URL(window.location.href).searchParams.get("focus") === "";
const getActiveFromUrl = (): string => new URL(window.location.href).searchParams.get("note") || ""; // Bookmark
Expand Down Expand Up @@ -628,10 +629,14 @@ const Notes = (): h.JSX.Element => {
});
},
locked: notesProps.notes[noteName].locked ?? false,
toggleLocked: (noteName) => {
onToggleLocked: (noteName) => {
setContextMenuProps(null);
tabId && notesRef.current && setLocked(noteName, !(notesProps.notes[noteName].locked ?? false), tabId, notesRef.current);
},
onExport: (noteName) => {
setContextMenuProps(null);
exportNote(noteName);
},
})}
onNewNote={() => onNewNote()}
sync={sync}
Expand Down
8 changes: 5 additions & 3 deletions src/notes/components/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ export interface ContextMenuProps {
onRename: (noteName: string) => void
onDelete: (noteName: string) => void
locked: boolean
toggleLocked: (noteName: string) => void
onToggleLocked: (noteName: string) => void
onExport: (noteName: string) => void
}

const ContextMenu = ({
noteName, x, y, onRename, onDelete, locked, toggleLocked,
noteName, x, y, onRename, onDelete, locked, onToggleLocked, onExport,
}: ContextMenuProps): h.JSX.Element => {
const [offsetHeight, setOffsetHeight] = useState<number>(0);
const ref = useRef<HTMLDivElement>(null);
Expand All @@ -40,7 +41,8 @@ const ContextMenu = ({
}}>
<div class={clsx("action", locked && "disabled")} onClick={() => !locked && onRename(noteName)}>Rename</div>
<div class={clsx("action", locked && "disabled")} onClick={() => !locked && onDelete(noteName)}>Delete</div>
<div class="action" onClick={() => toggleLocked(noteName)}>{locked ? "Unlock" : "Lock"}</div>
<div class="action" onClick={() => onToggleLocked(noteName)}>{locked ? "Unlock" : "Lock"}</div>
<div class="action" onClick={() => onExport(noteName)}>Export</div>
</div>
);
};
Expand Down
31 changes: 31 additions & 0 deletions src/notes/export/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { zipSync, Zippable } from "fflate";
import { NotesObject } from "shared/storage/schema";
import { downloadBlob } from "shared/download";

export const exportNote = (noteName: string): void => chrome.storage.local.get("notes", (local) => {
const { notes } = local as { notes: NotesObject };
if (!(noteName in notes)) {
return; // there is no note to export
}

const encoder = new TextEncoder();
const data = encoder.encode(notes[noteName].content);

downloadBlob(data, `${noteName}.html`, "text/html");
});

export const exportNotes = (): void => chrome.storage.local.get("notes", (local) => {
const { notes } = local as { notes: NotesObject };
if (!Object.keys(notes).length) {
return; // there are no notes to export
}

const encoder = new TextEncoder();
const data: Zippable = Object.keys(notes).reduce((acc, curr) => {
acc[`${curr}.html`] = encoder.encode(notes[curr].content);
return acc;
}, {} as Zippable);

const gzipped = zipSync(data, { level: 0 });
downloadBlob(gzipped, "notes.zip", "application/zip");
});
7 changes: 6 additions & 1 deletion src/notes/import/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { readFile } from "./read-file";

const SUPPORTED_FILE_TYPES = [
"text/plain",
"text/html",
];

export const importNoteFromTxtFile = (file: File, callback: () => void): void => {
if (!file.type.match("text/plain")) {
if (!SUPPORTED_FILE_TYPES.includes(file.type)) {
callback();
return;
}
Expand Down
12 changes: 12 additions & 0 deletions src/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import __Size from "options/Size";
import __Theme from "options/Theme";
import __KeyboardShortcuts from "options/KeyboardShortcuts";
import __Options from "options/Options";
import __Export from "options/Export";
import __Version from "options/Version";

import {
Os,
Storage,
NotesObject,
RegularFont,
GoogleFont,
Theme,
Expand All @@ -21,6 +23,7 @@ import { setTheme as setThemeCore } from "themes/set-theme";
const Options = (): h.JSX.Element => {
const [os, setOs] = useState<Os | undefined>(undefined);
const [version] = useState<string>(chrome.runtime.getManifest().version);
const [notesCount, setNotesCount] = useState<number>(0);
const [font, setFont] = useState<RegularFont | GoogleFont | undefined>(undefined);
const [size, setSize] = useState<number>(0);
const [theme, setTheme] = useState<Theme | undefined>(undefined);
Expand All @@ -34,6 +37,7 @@ const Options = (): h.JSX.Element => {
chrome.runtime.getPlatformInfo((platformInfo) => setOs(platformInfo.os === "mac" ? "mac" : "other"));

chrome.storage.local.get([
"notes",
"font",
"size",
"theme",
Expand All @@ -45,6 +49,7 @@ const Options = (): h.JSX.Element => {
], items => {
const local = items as Storage;

setNotesCount(Object.keys(local.notes).length);
setFont(local.font);
setSize(local.size);
setTheme(local.theme);
Expand All @@ -60,6 +65,12 @@ const Options = (): h.JSX.Element => {
return;
}

if (changes["notes"]) {
const newValue: NotesObject = changes["notes"].newValue;
const newNotesCount = Object.keys(newValue).length;
setNotesCount(newNotesCount);
}

if (changes["font"]) {
setFont(changes["font"].newValue);
}
Expand Down Expand Up @@ -116,6 +127,7 @@ const Options = (): h.JSX.Element => {
tab={tab}
tabSize={tabSize}
/>
<__Export canExport={notesCount > 0} />
<__Version version={version} />
</Fragment>
);
Expand Down
31 changes: 31 additions & 0 deletions src/options/Export.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { h, Fragment } from "preact";
import clsx from "clsx";
import { exportNotes } from "notes/export";

let locked = false;

interface ExportProps {
canExport: boolean
}

const Export = ({ canExport }: ExportProps): h.JSX.Element => (
<Fragment>
<h2>Export</h2>
<input
type="button"
class={clsx("bold", "button", !canExport && "disabled")}
value="Export all notes"
onClick={() => {
if (!canExport || locked) {
return;
}

locked = true;
exportNotes();
window.setTimeout(() => locked = false, 1000);
}}
/>
</Fragment>
);

export default Export;
4 changes: 2 additions & 2 deletions src/options/Font.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const Font = ({ font }: FontProps): h.JSX.Element => {
<input
type="text"
placeholder="Font Name (E.g. Roboto Mono)"
class="input"
value={googleFontName}
onInput={(event) => {
setGoogleFontName((event.target as HTMLInputElement).value);
Expand All @@ -96,8 +97,7 @@ const Font = ({ font }: FontProps): h.JSX.Element => {
/>
<input
type="submit"
id="submit"
class={clsx("bold", (googleFontName !== font?.name) && "active")}
class={clsx("bold", "button", (googleFontName === font?.name) && "disabled")}
value={googleSubmitButtonText}
onClick={() => {
const trimmedGoogleFontName = googleFontName.trim();
Expand Down
2 changes: 1 addition & 1 deletion src/options/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const Options = ({ sync, autoSync, tab, tabSize }: OptionsProps): h.JSX.Element
</div>
<div class={clsx("space-top", !tab && "disabled")}>
<label>Tab size:</label>
<select name="tab-size" value={tabSize} onChange={(event) => {
<select name="tab-size" class="select" value={tabSize} onChange={(event) => {
const newTabSize: number = parseInt((event.target as HTMLSelectElement).value);
chrome.storage.local.set({ tabSize: newTabSize });
}}>
Expand Down
17 changes: 17 additions & 0 deletions src/shared/download/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const downloadURL = (data: string, fileName: string): void => {
const a = document.createElement("a");
a.href = data;
a.download = fileName;
a.click();
a.remove();
};

export const downloadBlob = (data: Uint8Array, fileName: string, mimeType: string): void => {
const blob = new Blob([data], {
type: mimeType,
});

const url = window.URL.createObjectURL(blob);
downloadURL(url, fileName);
window.setTimeout(() => window.URL.revokeObjectURL(url), 2000);
};
66 changes: 33 additions & 33 deletions static/options.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,38 @@ input[type="radio"], input[type='checkbox'] {
margin: 0 10px 0 2px;
}

select { border-radius: 3px; }

.separator { padding: 0 12px; }
.bold { font-weight: bold; }
.space-top { margin-top: 1em; }
.space-left { margin-left: 1em; }

.disabled {
opacity: .3;
user-select: none;
pointer-events: none;
}

/* Inputs */

.button {
cursor: pointer;
}

.button, .input, .select {
max-width: 500px;
box-sizing: content-box;
outline: none;
border: 1px solid silver;
padding: 12px;
border-radius: 3px;
}

#dark .button,
#dark .input,
#dark .select {
border-color: transparent;
}

/* Selection */

.selection { padding: 10px 0; }
Expand Down Expand Up @@ -65,9 +90,6 @@ body#dark .comment {
.font-category { cursor: pointer; }
.font-category.active { text-decoration: underline; }

#submit { opacity: .3; }
#submit.active { opacity: 1; cursor: pointer; }

.font-area .selection {
font-size: 80%;
display: flex;
Expand All @@ -82,26 +104,14 @@ body#dark .comment {
#google-fonts-area ol { margin-top: 0; }
#google-fonts-area ol li { line-height: 2em; }

#google-fonts-area input, select {
max-width: 500px;
box-sizing: content-box;
outline: none;
border: 1px solid silver;
padding: 12px;
}

#dark #google-fonts-area input, select {
border-color: transparent;
#google-fonts-area .input {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}

#google-fonts-area input[type="text"] {
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}

#google-fonts-area input[type="submit"] {
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
#google-fonts-area .button {
border-top-left-radius: 0;
border-top-right-radius: 0;
}

/* Font size */
Expand Down Expand Up @@ -189,16 +199,6 @@ body#dark .comment {
word-break: break-all;
}

div.disabled {
opacity: .3;
user-select: none;
}

div.disabled input,
div.disabled select {
pointer-events: none;
}

/* Media Queries */

@media only screen and (max-width: 600px) {
Expand Down

0 comments on commit 89c3f35

Please sign in to comment.