From 7f1dddf2d2fbe4a4a08affb1a91a3377c7e147cc Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Tue, 19 Oct 2021 20:12:13 +0530 Subject: [PATCH] feat: Add note attributes to notes options menu (#681) --- .../javascripts/components/NotesOptions.tsx | 103 ++++++++++++++++-- app/assets/stylesheets/_sn.scss | 4 + 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/components/NotesOptions.tsx b/app/assets/javascripts/components/NotesOptions.tsx index 3346ff1e4..d95548af5 100644 --- a/app/assets/javascripts/components/NotesOptions.tsx +++ b/app/assets/javascripts/components/NotesOptions.tsx @@ -2,7 +2,7 @@ import { AppState } from '@/ui_models/app_state'; import { Icon } from './Icon'; import { Switch } from './Switch'; import { observer } from 'mobx-react-lite'; -import { useRef, useState, useEffect } from 'preact/hooks'; +import { useRef, useState, useEffect, useMemo } from 'preact/hooks'; import { Disclosure, DisclosureButton, @@ -11,6 +11,7 @@ import { import { SNNote } from '@standardnotes/snjs/dist/@types'; import { WebApplication } from '@/ui_models/application'; import { KeyboardModifier } from '@/services/ioService'; +import { FunctionComponent } from 'preact'; type Props = { application: WebApplication; @@ -20,9 +21,9 @@ type Props = { }; type DeletePermanentlyButtonProps = { - closeOnBlur: Props["closeOnBlur"]; + closeOnBlur: Props['closeOnBlur']; onClick: () => void; -} +}; const DeletePermanentlyButton = ({ closeOnBlur, @@ -34,6 +35,87 @@ const DeletePermanentlyButton = ({ ); +const countNoteAttributes = (text: string) => { + try { + JSON.parse(text); + return { + characters: 'N/A', + words: 'N/A', + paragraphs: 'N/A', + }; + } catch { + const characters = text.length; + const words = text.match(/[\w’'-]+\b/g)?.length; + const paragraphs = text.replace(/\n$/gm, '').split(/\n/).length; + + return { + characters, + words, + paragraphs, + }; + } +}; + +const calculateReadTime = (words: number) => { + const timeToRead = Math.round(words / 200); + if (timeToRead === 0) { + return '< 1 minute'; + } else { + return `${timeToRead} ${timeToRead > 1 ? 'minutes' : 'minute'}`; + } +}; + +const formatDate = (date: Date | undefined) => { + if (!date) return; + return `${date.toDateString()} ${date.toLocaleTimeString()}`; +}; + +const NoteAttributes: FunctionComponent<{ note: SNNote }> = ({ note }) => { + const { words, characters, paragraphs } = useMemo( + () => countNoteAttributes(note.text), + [note.text] + ); + + const readTime = useMemo( + () => (typeof words === 'number' ? calculateReadTime(words) : 'N/A'), + [words] + ); + + const dateLastModified = useMemo( + () => formatDate(note.serverUpdatedAt), + [note.serverUpdatedAt] + ); + + const dateCreated = useMemo( + () => formatDate(note.created_at), + [note.created_at] + ); + + return ( +
+ {typeof words === 'number' ? ( + <> +
+ {words} words · {characters} characters · {paragraphs} paragraphs +
+
+ Read time: {readTime} +
+ + ) : null} +
+ Last modified: {dateLastModified} +
+
+ Created: {dateCreated} +
+
+ Note ID: {note.uuid} +
+
+ ); +}; + export const NotesOptions = observer( ({ application, appState, closeOnBlur, onSubmenuChange }: Props) => { const [tagsMenuOpen, setTagsMenuOpen] = useState(false); @@ -45,8 +127,9 @@ export const NotesOptions = observer( top: 0, right: 0, }); - const [tagsMenuMaxHeight, setTagsMenuMaxHeight] = - useState('auto'); + const [tagsMenuMaxHeight, setTagsMenuMaxHeight] = useState( + 'auto' + ); const [altKeyDown, setAltKeyDown] = useState(false); const toggleOn = (condition: (note: SNNote) => boolean) => { @@ -86,7 +169,7 @@ export const NotesOptions = observer( }, onKeyUp: () => { setAltKeyDown(false); - } + }, }); return () => { @@ -163,7 +246,7 @@ export const NotesOptions = observer( Protect -
+
{appState.tags.tagsCount > 0 && ( )} + {notes.length === 1 ? ( + <> +
+ + + ) : null} ); } diff --git a/app/assets/stylesheets/_sn.scss b/app/assets/stylesheets/_sn.scss index 4638703f0..dd594c2f1 100644 --- a/app/assets/stylesheets/_sn.scss +++ b/app/assets/stylesheets/_sn.scss @@ -274,6 +274,10 @@ min-width: 3.75rem; } +.min-h-1px { + min-height: 1px; +} + .min-h-1 { min-height: 0.25rem; }