|
|
@@ -7,7 +7,7 @@ import generateId from '../utilities/Id' |
|
|
|
import Link from 'next/link' |
|
|
|
import { formatDate } from '../utilities/Date' |
|
|
|
import { useRouter } from 'next/router' |
|
|
|
import { Trash2, FilePlus, FolderPlus } from 'react-feather' |
|
|
|
import { Trash2, FilePlus, FolderPlus, FileText, GitBranch, User } from 'react-feather' |
|
|
|
|
|
|
|
const Navbar = styled('aside')({ |
|
|
|
width: 360, |
|
|
@@ -22,6 +22,43 @@ const Navbar = styled('aside')({ |
|
|
|
}, |
|
|
|
}) |
|
|
|
|
|
|
|
const PrimaryNavItems = styled('nav')({ |
|
|
|
height: '100%', |
|
|
|
display: 'flex', |
|
|
|
flexDirection: 'column', |
|
|
|
justifyContent: 'space-between', |
|
|
|
alignItems: 'stretch', |
|
|
|
'@media (min-width: 1080px)': { |
|
|
|
width: '4rem', |
|
|
|
}, |
|
|
|
}) |
|
|
|
|
|
|
|
const SecondaryNavItems = styled('nav')({ |
|
|
|
height: '100%', |
|
|
|
position: 'relative', |
|
|
|
'::before': { |
|
|
|
content: "''", |
|
|
|
display: 'block', |
|
|
|
top: 0, |
|
|
|
left: 0, |
|
|
|
width: '100%', |
|
|
|
height: '100%', |
|
|
|
position: 'absolute', |
|
|
|
zIndex: -1, |
|
|
|
backgroundColor: 'white', |
|
|
|
opacity: 0.5, |
|
|
|
}, |
|
|
|
'@media (min-width: 1080px)': { |
|
|
|
flex: 'auto', |
|
|
|
}, |
|
|
|
}) |
|
|
|
|
|
|
|
const SecondaryNavItemsOverflow = styled('div')({ |
|
|
|
overflow: 'auto', |
|
|
|
width: '100%', |
|
|
|
height: '100%', |
|
|
|
}) |
|
|
|
|
|
|
|
const Main = styled('main')({ |
|
|
|
margin: '2rem 0', |
|
|
|
'@media (min-width: 1080px)': { |
|
|
@@ -40,11 +77,17 @@ const Container = styled('div')({ |
|
|
|
}, |
|
|
|
}) |
|
|
|
|
|
|
|
const NavbarContainer = styled('span')({ |
|
|
|
const NavbarItems = styled('div')({ |
|
|
|
display: 'flex', |
|
|
|
width: '100%', |
|
|
|
height: '100%', |
|
|
|
}) |
|
|
|
|
|
|
|
const NavbarContainer = styled('div')({ |
|
|
|
display: 'block', |
|
|
|
width: '100%', |
|
|
|
height: '100%', |
|
|
|
margin: '0 0 0 auto', |
|
|
|
padding: '0 1rem', |
|
|
|
boxSizing: 'border-box', |
|
|
|
maxWidth: 360, |
|
|
|
}) |
|
|
@@ -59,7 +102,6 @@ const TitleInput = styled('input')({ |
|
|
|
fontSize: '3rem', |
|
|
|
fontWeight: 'bold', |
|
|
|
outline: 0, |
|
|
|
marginBottom: '2rem', |
|
|
|
}) |
|
|
|
|
|
|
|
const NoteLink = styled('a')({ |
|
|
@@ -127,6 +169,30 @@ const BinIcon = styled(Trash2)({ |
|
|
|
marginRight: '0.5rem', |
|
|
|
}) |
|
|
|
|
|
|
|
const NavbarItemContent = styled('span')({ |
|
|
|
padding: '0 1rem', |
|
|
|
boxSizing: 'border-box', |
|
|
|
}) |
|
|
|
|
|
|
|
const PrimaryNavItem = styled('a')({ |
|
|
|
width: '4rem', |
|
|
|
height: '4rem', |
|
|
|
display: 'grid', |
|
|
|
placeContent: 'center', |
|
|
|
color: 'inherit', |
|
|
|
}) |
|
|
|
|
|
|
|
const PostMeta = styled('small')({ |
|
|
|
opacity: 0.5, |
|
|
|
height: '1.25rem', |
|
|
|
display: 'block', |
|
|
|
lineHeight: 1.25, |
|
|
|
}) |
|
|
|
|
|
|
|
const PostPrimary = styled('div')({ |
|
|
|
marginBottom: '2rem', |
|
|
|
}) |
|
|
|
|
|
|
|
type NoteInstance = { id: string, title: string, content?: object, updatedAt: string, } |
|
|
|
|
|
|
|
const Notes = ({ id: idProp }) => { |
|
|
@@ -145,7 +211,7 @@ const Notes = ({ id: idProp }) => { |
|
|
|
timeoutRef.current = window.setTimeout(async () => { |
|
|
|
const newNote = await Storage.saveNote({ |
|
|
|
...stateRef.current, |
|
|
|
updatedAt: new Date().toISOString(), |
|
|
|
updatedAt: (stateRef.current.updatedAt = new Date().toISOString()), |
|
|
|
}) |
|
|
|
if (router.query.id !== id) { |
|
|
|
await router.push(`/notes/${id}`, undefined, { shallow: true }) |
|
|
@@ -174,6 +240,14 @@ const Notes = ({ id: idProp }) => { |
|
|
|
autoSave() |
|
|
|
} |
|
|
|
|
|
|
|
const deleteNote = note => async () => { |
|
|
|
setNotes(notes.filter(n => n.id !== note.id)) |
|
|
|
const result = await Storage.deleteNote(note) |
|
|
|
if (!result) { |
|
|
|
setNotes(notes) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
React.useEffect(() => { |
|
|
|
const loadNotes = async () => { |
|
|
|
const theNotes = await Storage.loadNotes() |
|
|
@@ -210,121 +284,168 @@ const Notes = ({ id: idProp }) => { |
|
|
|
<link rel="icon" href="/favicon.ico" /> |
|
|
|
</Head> |
|
|
|
<Navbar> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/profile', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarContainer> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<NewFolderIcon /> |
|
|
|
<NoteLinkTitle> |
|
|
|
Personal |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
</NavbarContainer> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/folders/new', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarContainer> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<NewFolderIcon /> |
|
|
|
<NoteLinkTitle> |
|
|
|
Create Folder |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
</NavbarContainer> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/notes', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarContainer> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<NewIcon /> |
|
|
|
<NoteLinkTitle> |
|
|
|
Create Note |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
</NavbarContainer> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
{ |
|
|
|
Array.isArray(notes!) |
|
|
|
&& notes.map(n => ( |
|
|
|
<LinkContainer |
|
|
|
key={n.id} |
|
|
|
> |
|
|
|
{ |
|
|
|
n.id === id |
|
|
|
&& ( |
|
|
|
<NoteLinkBackground /> |
|
|
|
) |
|
|
|
} |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/notes/[id]', |
|
|
|
query: { id: n.id }, |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarContainer> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<NoteLinkTitle |
|
|
|
style={{ opacity: n.title.length > 0 ? 1 : 0.5, }} |
|
|
|
<NavbarContainer> |
|
|
|
<NavbarItems> |
|
|
|
<PrimaryNavItems> |
|
|
|
<div> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/notes', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<PrimaryNavItem> |
|
|
|
<FileText /> |
|
|
|
</PrimaryNavItem> |
|
|
|
</Link> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/graph', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<PrimaryNavItem> |
|
|
|
<GitBranch /> |
|
|
|
</PrimaryNavItem> |
|
|
|
</Link> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/me', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<PrimaryNavItem> |
|
|
|
<User /> |
|
|
|
</PrimaryNavItem> |
|
|
|
</Link> |
|
|
|
</div> |
|
|
|
</PrimaryNavItems> |
|
|
|
<SecondaryNavItems> |
|
|
|
<SecondaryNavItemsOverflow> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/profile', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarItemContent> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<NewFolderIcon /> |
|
|
|
<NoteLinkTitle> |
|
|
|
Personal |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
</NavbarItemContent> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/folders/new', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarItemContent> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<NewFolderIcon /> |
|
|
|
<NoteLinkTitle> |
|
|
|
Create Folder |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
</NavbarItemContent> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/notes', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarItemContent> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<NewIcon /> |
|
|
|
<NoteLinkTitle> |
|
|
|
Create Note |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
</NavbarItemContent> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
{ |
|
|
|
Array.isArray(notes!) |
|
|
|
&& notes.map(n => ( |
|
|
|
<LinkContainer |
|
|
|
key={n.id} |
|
|
|
> |
|
|
|
{ |
|
|
|
n.id === id |
|
|
|
&& ( |
|
|
|
<NoteLinkBackground /> |
|
|
|
) |
|
|
|
} |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/notes/[id]', |
|
|
|
query: { id: n.id }, |
|
|
|
}} |
|
|
|
replace |
|
|
|
passHref |
|
|
|
> |
|
|
|
{n.title.length > 0 ? n.title : '(untitled)'} |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
{' '} |
|
|
|
<small> |
|
|
|
<time |
|
|
|
dateTime={new Date(n.updatedAt).toISOString()} |
|
|
|
> |
|
|
|
Last updated {formatDate(new Date(n.updatedAt))} |
|
|
|
</time> |
|
|
|
</small> |
|
|
|
</NavbarContainer> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
<NoteActions> |
|
|
|
<NoteAction> |
|
|
|
<Trash2 /> |
|
|
|
</NoteAction> |
|
|
|
</NoteActions> |
|
|
|
</LinkContainer> |
|
|
|
)) |
|
|
|
} |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/bin', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarContainer> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<BinIcon /> |
|
|
|
<NoteLinkTitle> |
|
|
|
View Binned Notes |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
</NavbarContainer> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
<NoteLink> |
|
|
|
<NavbarItemContent> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<NoteLinkTitle |
|
|
|
style={{ opacity: n.title.length > 0 ? 1 : 0.5, }} |
|
|
|
> |
|
|
|
{n.title.length > 0 ? n.title : '(untitled)'} |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
{' '} |
|
|
|
<PostMeta> |
|
|
|
<time |
|
|
|
dateTime={new Date(n.updatedAt).toISOString()} |
|
|
|
> |
|
|
|
Last updated {formatDate(new Date(n.updatedAt))} |
|
|
|
</time> |
|
|
|
</PostMeta> |
|
|
|
</NavbarItemContent> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
<NoteActions> |
|
|
|
<NoteAction |
|
|
|
onClick={deleteNote(n)} |
|
|
|
> |
|
|
|
<Trash2 /> |
|
|
|
</NoteAction> |
|
|
|
</NoteActions> |
|
|
|
</LinkContainer> |
|
|
|
)) |
|
|
|
} |
|
|
|
<Link |
|
|
|
href={{ |
|
|
|
pathname: '/bin', |
|
|
|
}} |
|
|
|
passHref |
|
|
|
> |
|
|
|
<NoteLink> |
|
|
|
<NavbarItemContent> |
|
|
|
<NoteLinkPrimary> |
|
|
|
<BinIcon /> |
|
|
|
<NoteLinkTitle> |
|
|
|
View Binned Notes |
|
|
|
</NoteLinkTitle> |
|
|
|
</NoteLinkPrimary> |
|
|
|
</NavbarItemContent> |
|
|
|
</NoteLink> |
|
|
|
</Link> |
|
|
|
</SecondaryNavItemsOverflow> |
|
|
|
</SecondaryNavItems> |
|
|
|
</NavbarItems> |
|
|
|
</NavbarContainer> |
|
|
|
</Navbar> |
|
|
|
<Main> |
|
|
|
<Container> |
|
|
@@ -332,11 +453,26 @@ const Notes = ({ id: idProp }) => { |
|
|
|
Array.isArray(notes!) |
|
|
|
&& ( |
|
|
|
<React.Fragment> |
|
|
|
<TitleInput |
|
|
|
placeholder="Title" |
|
|
|
value={title} |
|
|
|
onChange={handleTitleChange} |
|
|
|
/> |
|
|
|
<PostPrimary> |
|
|
|
<TitleInput |
|
|
|
placeholder="Title" |
|
|
|
value={title} |
|
|
|
onChange={handleTitleChange} |
|
|
|
/> |
|
|
|
<PostMeta> |
|
|
|
{ |
|
|
|
stateRef.current.updatedAt |
|
|
|
&& router.query.id |
|
|
|
&& ( |
|
|
|
<time |
|
|
|
dateTime={new Date(stateRef.current.updatedAt).toISOString()} |
|
|
|
> |
|
|
|
Last updated {formatDate(new Date(stateRef.current.updatedAt))} |
|
|
|
</time> |
|
|
|
) |
|
|
|
} |
|
|
|
</PostMeta> |
|
|
|
</PostPrimary> |
|
|
|
<Editor |
|
|
|
autoFocus={false} |
|
|
|
key={id} |
|
|
|