Skip to content

Commit

Permalink
Add initial open note-link functionality (BoostIO#313)
Browse files Browse the repository at this point in the history
Add copy note link menu option (NoteItem.tsx)
Update db API for creating a note link (createstore, FSNoteDb, PouchNoteDb)
Add codemirror hyperlink addon
Add codemirror hyperlink addon css style (CodeEditor.tsx)
Initialize hyperlink addon (CodeMirror.ts)
Add event handling of editor ctrl+click link with ipc (App.tsx)
Add regexes for note link shortId (MarkdownPreviewer.tsx)
Add handling of rendering and clicking of note link in md preview
  • Loading branch information
Komediruzecki committed Nov 18, 2020
1 parent 37d5c7c commit 3f65a15
Show file tree
Hide file tree
Showing 14 changed files with 414 additions and 11 deletions.
50 changes: 47 additions & 3 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import styled from '../lib/styled'
import { useEffectOnce } from 'react-use'
import AppNavigator from './organisms/AppNavigator'
import { useRouter } from '../lib/router'
import { values } from '../lib/db/utils'
import { useActiveStorageId } from '../lib/routeParams'
import { prependNoteIdPrefix, values } from '../lib/db/utils'
import { localLiteStorage } from 'ltstrg'
import { defaultStorageCreatedKey } from '../lib/localStorageKeys'
import {
Expand All @@ -32,7 +33,7 @@ import { generateId } from '../lib/string'
import FSNoteDb from '../lib/db/FSNoteDb'
import path from 'path'
import { useGeneralStatus } from '../lib/generalStatus'
import { getFolderItemId } from '../lib/nav'
import { getFolderItemId, getNoteFullItemId } from '../lib/nav'
import AppModeModal from './organisms/AppModeModal'
import { useBoostNoteProtocol } from '../lib/protocol'
import { useBoostHub, getBoostHubTeamIconUrl } from '../lib/boosthub'
Expand All @@ -57,6 +58,8 @@ import {
featureBoostHubIntro,
} from '../lib/checkedFeatures'
import BoostHubIntroModal from '../components/organisms/BoostHubIntroModal'
import { IpcRendererEvent } from 'electron/renderer'
import { useToast } from '../lib/toast'

const LoadingText = styled.div`
margin: 30px;
Expand Down Expand Up @@ -93,16 +96,24 @@ Please check out.
`

const App = () => {
const { initialize, queueSyncingAllStorage, createStorage } = useDb()
const {
initialize,
queueSyncingAllStorage,
createStorage,
getNotePathname,
} = useDb()
const { replace, push } = useRouter()
const activeStorageId = useActiveStorageId()
const [initialized, setInitialized] = useState(false)
const { addSideNavOpenedItem, setGeneralStatus } = useGeneralStatus()

const {
togglePreferencesModal,
preferences,
setPreferences,
} = usePreferences()
const { messageBox } = useDialog()
const { pushMessage } = useToast()
const { fetchDesktopGlobalData } = useBoostHub()

useEffectOnce(() => {
Expand Down Expand Up @@ -212,6 +223,39 @@ const App = () => {
run()
})

useEffect(() => {
const noteLinkNavigateEventHandler = (
_: IpcRendererEvent,
noteHref: string
) => {
const noteId = Array.isArray(noteHref) ? noteHref[0] : noteHref
if (!activeStorageId) {
pushMessage({
title: 'Invalid navigation!',
description: 'Cannot open note link without storage information.',
})
} else {
getNotePathname(activeStorageId, prependNoteIdPrefix(noteId)).then(
(pathname) => {
if (pathname) {
replace(getNoteFullItemId(activeStorageId, pathname, noteId))
} else {
pushMessage({
title: 'Note link invalid!',
description:
'The note link you are trying to open is invalid or from another storage.',
})
}
}
)
}
}
addIpcListener('note:navigate', noteLinkNavigateEventHandler)
return () => {
removeIpcListener('note:navigate', noteLinkNavigateEventHandler)
}
}, [activeStorageId, getNotePathname, pushMessage, replace])

useEffect(() => {
addIpcListener('preferences', togglePreferencesModal)
return () => {
Expand Down
10 changes: 10 additions & 0 deletions src/components/atoms/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ const StyledContainer = styled.div`
.CodeMirror {
font-family: inherit;
}
.CodeMirror-hyperlink {
cursor: pointer;
}
.CodeMirror-hover {
padding: 2px 4px 0 4px;
position: absolute;
z-index: 99;
}
`

const defaultCodeMirrorOptions: CodeMirror.EditorConfiguration = {
Expand Down
72 changes: 69 additions & 3 deletions src/components/atoms/MarkdownPreviewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ import 'katex/dist/katex.min.css'
import MarkdownCheckbox from './markdown/MarkdownCheckbox'
import AttachmentImage from './markdown/AttachmentImage'
import CodeFence from './markdown/CodeFence'
import { useActiveStorageId } from '../../lib/routeParams'
import { useRouter } from '../../lib/router'
import { useDb } from '../../lib/db'
import { prependNoteIdPrefix } from '../../lib/db/utils'
import { getNoteFullItemId } from '../../lib/nav'
import { useToast } from '../../lib/toast'

const schema = mergeDeepRight(gh, {
attributes: {
Expand Down Expand Up @@ -171,12 +177,55 @@ const MarkdownPreviewer = ({
attachmentMap = {},
updateContent,
}: MarkdownPreviewerProps) => {
const { replace } = useRouter()
const [rendering, setRendering] = useState(false)
const previousContentRef = useRef('')
const previousThemeRef = useRef<string | undefined>('')
const [renderedContent, setRenderedContent] = useState<React.ReactNode>([])

const checkboxIndexRef = useRef<number>(0)
const regexIsNoteShortIdLink = /[a-zA-Z0-9_\-]{7,14}/
const regexIsNoteShortIdLinkWithPrefix = /\((:note:)([a-zA-Z0-9_\-]{7,14})\)/g
const { getNotePathname } = useDb()
const activeStorageId = useActiveStorageId()
const { pushMessage } = useToast()

const isNoteLink = useCallback(
(href) => {
return href.match(regexIsNoteShortIdLink) !== null
},
[regexIsNoteShortIdLink]
)
const navigate = useCallback(
(noteId) => {
if (!activeStorageId) {
pushMessage({
title: 'Invalid navigation!',
description: 'Cannot open note link without storage information.',
})
} else {
getNotePathname(activeStorageId, prependNoteIdPrefix(noteId)).then(
(pathname) => {
if (pathname) {
replace(getNoteFullItemId(activeStorageId, pathname, noteId))
} else {
pushMessage({
title: 'Note link invalid!',
description:
'The note link you are trying to open is invalid or from another storage.',
})
}
}
)
}
},
[
getNoteFullItemId,
prependNoteIdPrefix,
activeStorageId,
getNotePathname,
replace,
]
)

const markdownProcessor = useMemo(() => {
return unified()
Expand Down Expand Up @@ -211,7 +260,14 @@ const MarkdownPreviewer = ({
href={href}
onClick={(event) => {
event.preventDefault()
openNew(href)
if (href) {
// See if link is to note
if (isNoteLink(href)) {
navigate(href)
} else {
openNew(href)
}
}
}}
>
{children}
Expand Down Expand Up @@ -244,7 +300,17 @@ const MarkdownPreviewer = ({

console.time('render')
checkboxIndexRef.current = 0
const result = await markdownProcessor.process(content)

// Remove note link prefixes when rendering links
let noteLinkContent = content
if (regexIsNoteShortIdLinkWithPrefix.test(content)) {
noteLinkContent = content.replace(
regexIsNoteShortIdLinkWithPrefix,
'($2)'
)
}

const result = await markdownProcessor.process(noteLinkContent)
console.timeEnd('render')

setRendering(false)
Expand Down
26 changes: 26 additions & 0 deletions src/components/molecules/NoteItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { GeneralNoteListViewOptions } from '../../lib/preferences'
import { useGeneralStatus } from '../../lib/generalStatus'
import { bookmarkItemId } from '../../lib/nav'
import { openContextMenu } from '../../lib/electronOnly'
import copy from 'copy-to-clipboard'
import { useToast } from '../../lib/toast'

const Container = styled.button`
margin: 0;
Expand Down Expand Up @@ -119,6 +121,7 @@ const NoteItem = ({
const href = `${basePathname}/${note._id}`
const {
createNote,
copyNoteLink,
trashNote,
purgeNote,
untrashNote,
Expand All @@ -129,6 +132,7 @@ const NoteItem = ({
const { addSideNavOpenedItem } = useGeneralStatus()

const { messageBox } = useDialog()
const { pushMessage } = useToast()
const { t } = useTranslation()

const openUntrashedNoteContextMenu = useCallback(
Expand All @@ -151,6 +155,28 @@ const NoteItem = ({
})
},
},
{
type: 'normal',
label: 'Copy note link',
click: async () => {
const noteLink = await copyNoteLink(storageId, note._id, {
title: note.title,
content: note.content,
folderPathname: note.folderPathname,
tags: note.tags,
data: note.data,
})
if (noteLink) {
copy(noteLink)
} else {
pushMessage({
title: 'Note Link Error',
description:
'An error occurred while attempting to create a note link',
})
}
},
},
{ type: 'separator' },
{
type: 'normal',
Expand Down
5 changes: 5 additions & 0 deletions src/lib/CodeMirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import 'codemirror/keymap/emacs'
import 'codemirror/keymap/vim'
import 'codemirror-abap'

// Custom addons
import {initHyperlink} from './addons/hyperlink'

const dispatchModeLoad = debounce(() => {
window.dispatchEvent(new CustomEvent('codemirror-mode-load'))
}, 300)
Expand Down Expand Up @@ -53,6 +56,8 @@ function loadMode(_CodeMirror: any) {
}
}

// Initialize custom addons
initHyperlink(CodeMirror)
loadMode(CodeMirror)

export default CodeMirror
Expand Down
Loading

0 comments on commit 3f65a15

Please sign in to comment.