Skip to content

Commit fd2c495

Browse files
better preview and saving on sputlit (#85)
#85 * removed mouse blur for preview; added two step escape for false preview; Signed-off-by: Sahil Shubham <[email protected]> * better saving mechanism on sputlit; saves on preview change; Signed-off-by: Sahil Shubham <[email protected]> * better node id setting; Signed-off-by: Sahil Shubham <[email protected]>
1 parent 190ccbd commit fd2c495

File tree

7 files changed

+142
-116
lines changed

7 files changed

+142
-116
lines changed

apps/extension/src/Components/Content/index.tsx

+12-93
Original file line numberDiff line numberDiff line change
@@ -4,137 +4,56 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'
44

55
import { getMexHTMLDeserializer } from '../../Utils/deserialize'
66
import { Editor } from '../Editor'
7-
import { useSputlitContext, VisualState } from '../../Hooks/useSputlitContext'
7+
import { useSputlitContext } from '../../Hooks/useSputlitContext'
88
import Results from '../Results'
99
import { StyledContent } from './styled'
10-
import { useAuthStore } from '../../Hooks/useAuth'
11-
import toast from 'react-hot-toast'
12-
import {
13-
CaptureType,
14-
createNodeWithUid,
15-
defaultContent,
16-
extractMetadata,
17-
generateNodeId,
18-
getNewDraftKey,
19-
mog,
20-
QuickLinkType,
21-
SEPARATOR
22-
} from '@mexit/core'
23-
import { CategoryType, NodeEditorContent, NodeMetadata } from '@mexit/core'
10+
import { CategoryType, createNodeWithUid, defaultContent, getNewDraftKey, QuickLinkType } from '@mexit/core'
11+
import { NodeEditorContent } from '@mexit/core'
2412
import { useEditorContext } from '../../Hooks/useEditorContext'
2513
import { useSnippets } from '../../Hooks/useSnippets'
2614
import { useContentStore } from '../../Stores/useContentStore'
27-
import useDataStore from '../../Stores/useDataStore'
15+
import { useSaveChanges } from '../../Hooks/useSaveChanges'
2816

2917
export default function Content() {
30-
const { selection, setVisualState, searchResults, activeIndex } = useSputlitContext()
31-
const { node, nodeContent, setNodeContent, previewMode, setNode } = useEditorContext()
18+
const { selection, searchResults, activeIndex } = useSputlitContext()
19+
const { node, setNodeContent, previewMode, setNode } = useEditorContext()
20+
const { saveIt } = useSaveChanges()
3221

33-
const { setContent, setMetadata, getContent } = useContentStore()
22+
const { getContent } = useContentStore()
3423
const editor = usePlateEditorRef(node.nodeid)
3524
const getSnippet = useSnippets().getSnippet
3625

37-
// Ref so that the function contains the newest value without re-renders
38-
const contentRef = useRef<NodeEditorContent>(nodeContent)
3926
const deserializedContentRef = useRef<NodeEditorContent>()
4027

41-
const workspaceDetails = useAuthStore((store) => store.workspaceDetails)
42-
43-
const ilinks = useDataStore((store) => store.ilinks)
44-
4528
useEffect(() => {
4629
const content = getMexHTMLDeserializer(selection?.html, editor)
4730

4831
if (selection?.range && content && selection?.url && previewMode) {
4932
setNodeContent([{ children: content }])
50-
contentRef.current = content
5133
deserializedContentRef.current = content
5234
}
53-
}, [editor]) // eslint-disable-line
54-
55-
// useEffect(() => {
56-
// console.log('NODE CHANGED: ', node)
57-
// }, [node])
58-
59-
useEffect(() => {
60-
setNode(createNodeWithUid(getNewDraftKey()))
61-
}, [])
35+
}, [editor])
6236

6337
const onChangeSave = (val: any[]) => {
6438
if (val) {
6539
setNodeContent(val)
66-
contentRef.current = val
6740
}
6841
}
6942

7043
useEffect(() => {
71-
const handleSave = (saveAndExit = false) => {
72-
const metadata = {
73-
saveableRange: selection?.range,
74-
sourceUrl: selection?.range && window.location.href
75-
}
76-
77-
const splitPath = node.path.split(SEPARATOR)
78-
const request = {
79-
type: 'CAPTURE_HANDLER',
80-
subType: 'BULK_CREATE_NODE',
81-
data: {
82-
id: node.nodeid,
83-
content: contentRef.current,
84-
title: splitPath.slice(-1)[0],
85-
type: CaptureType.DRAFT,
86-
workspaceID: workspaceDetails.id,
87-
metadata: metadata
88-
}
89-
}
90-
91-
if (splitPath.length > 1) {
92-
const parent = splitPath.slice(0, -1).join(SEPARATOR)
93-
const parentID = ilinks.find((i) => i.path === parent)
94-
request.data['referenceID'] = parentID.nodeid
95-
}
96-
97-
// console.log('Sending: ', node, request)
98-
99-
setContent(node.nodeid, contentRef.current)
100-
chrome.runtime.sendMessage(request, (response) => {
101-
const { message, error } = response
102-
console.log('Response aaya: ', response)
103-
if (error) {
104-
if (error === 'Not Authenticated') {
105-
toast.error('Not Authenticated. Please login on Mexit webapp.')
106-
} else {
107-
toast.error('An Error Occured. Please try again.')
108-
}
109-
} else {
110-
setMetadata(message.id, extractMetadata(message.data[0]))
111-
toast.success('Saved to Cloud')
112-
if (saveAndExit) {
113-
setTimeout(() => {
114-
setVisualState(VisualState.hidden)
115-
}, 2000)
116-
}
117-
}
118-
})
119-
}
120-
12144
const handleSaveKeydown = (event: KeyboardEvent) => {
122-
if (event.key === 's' && event.metaKey) {
123-
event.preventDefault()
124-
handleSave(true)
125-
} else if (event.key === 'Enter' && event.metaKey) {
45+
if (event.key === 's' && event.metaKey && !previewMode) {
12646
event.preventDefault()
127-
handleSave(true)
47+
saveIt(true, true)
12848
}
12949
}
13050

13151
document.getElementById('mexit')!.addEventListener('keydown', handleSaveKeydown)
13252

13353
return () => {
134-
// handleSave()
13554
document.getElementById('mexit')!.removeEventListener('keydown', handleSaveKeydown)
13655
}
137-
}, [node, ilinks])
56+
}, [node, previewMode])
13857

13958
useEffect(() => {
14059
const item = searchResults[activeIndex]

apps/extension/src/Components/Editor/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export const Editor: React.FC<EditorProps> = ({ readOnly, onChange }) => {
149149
}, 1000)
150150

151151
return (
152-
<EditorWrapper style={springProps} onClick={() => setPreviewMode(false)} onBlur={() => setPreviewMode(true)}>
152+
<EditorWrapper style={springProps} onClick={() => setPreviewMode(false)}>
153153
<EditorStyles>
154154
<MexEditor
155155
comboboxConfig={comboboxConfig}

apps/extension/src/Components/InternalEvents.tsx

+12-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import LinkedInBadge from './LinkedInBadge'
1313
import { getHtmlString } from './Source'
1414
import { MEXIT_FRONTEND_URL_BASE } from '@mexit/core'
1515
import { useContentStore } from '../Stores/useContentStore'
16+
import { useEditorContext } from '../Hooks/useEditorContext'
17+
import { useSaveChanges } from '../Hooks/useSaveChanges'
1618

1719
export function InternalEvents() {
1820
useToggleHandler()
@@ -32,6 +34,8 @@ const highlighter = new Highlighter()
3234
*/
3335
function useToggleHandler() {
3436
const { visualState, setVisualState, setSelection, setTooltipState } = useSputlitContext()
37+
const { previewMode, setPreviewMode } = useEditorContext()
38+
const { saveIt } = useSaveChanges()
3539

3640
useEffect(() => {
3741
function messageHandler(request: any, sender: chrome.runtime.MessageSender, sendResponse: (response: any) => void) {
@@ -59,8 +63,13 @@ function useToggleHandler() {
5963

6064
function handleKeyDown(event: KeyboardEvent) {
6165
if (event.key === 'Escape') {
62-
setVisualState(VisualState.hidden)
63-
setTooltipState({ visualState: VisualState.hidden })
66+
if (previewMode) {
67+
setVisualState(VisualState.hidden)
68+
setTooltipState({ visualState: VisualState.hidden })
69+
} else {
70+
setPreviewMode(true)
71+
saveIt(false, true)
72+
}
6473
}
6574
}
6675

@@ -74,7 +83,7 @@ function useToggleHandler() {
7483
chrome.runtime.onMessage.removeListener(messageHandler)
7584
window.removeEventListener('keydown', handleKeyDown)
7685
}
77-
}, [visualState])
86+
}, [visualState, previewMode])
7887
}
7988

8089
function dibbaToggle() {

apps/extension/src/Components/Search/index.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ import {
1717
import { useTheme } from 'styled-components'
1818
import { useSearchProps } from '../../Hooks/useSearchProps'
1919
import { useEditorContext } from '../../Hooks/useEditorContext'
20+
import { useSaveChanges } from '../../Hooks/useSaveChanges'
2021

2122
const Search = () => {
2223
const { input, setInput, setSearch, isLoading } = useSputlitContext()
23-
const { previewMode } = useEditorContext()
24+
const { previewMode, setPreviewMode } = useEditorContext()
2425
const { icon, placeholder } = useSearchProps()
26+
const { saveIt } = useSaveChanges()
2527
const theme = useTheme()
2628

2729
const getQuery = (value: string): Search => {
@@ -63,12 +65,17 @@ const Search = () => {
6365
handleSearchInput(query)
6466
}
6567

66-
// TODO: it would be good to have the ability to go back after selected a search type action
68+
const onBackClick = () => {
69+
if (!previewMode) {
70+
setPreviewMode(true)
71+
saveIt(false, true)
72+
}
73+
}
6774

6875
return (
6976
<StyledSearch>
7077
{/* {activeItem?.type === ActionType.SEARCH && <QuerySearch>{activeItem.title} | </QuerySearch>} */}
71-
<CenterIcon id="wd-mex-search-left-icon" cursor={false}>
78+
<CenterIcon id="wd-mex-search-left-icon" cursor={!previewMode} onClick={onBackClick}>
7279
<Icon color={theme.colors.primary} height={24} width={24} icon={icon} />
7380
</CenterIcon>
7481
<StyledInput

apps/extension/src/Hooks/useActionExecutor.ts

+25-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
import { ActionType, CategoryType, ILink, MexitAction, parseSnippet, QuickLinkType, SEPARATOR } from '@mexit/core'
1+
import {
2+
ActionType,
3+
CategoryType,
4+
createNodeWithUid,
5+
getNewDraftKey,
6+
getUntitledDraftKey,
7+
ILink,
8+
MexitAction,
9+
parseSnippet,
10+
QuickLinkType,
11+
SEPARATOR
12+
} from '@mexit/core'
213
import { copyToClipboard } from '@mexit/shared'
314
import toast from 'react-hot-toast'
415
import Action from '../Components/Action'
@@ -12,30 +23,31 @@ import { useSputlitContext, VisualState } from './useSputlitContext'
1223
export function useActionExecutor() {
1324
const { setVisualState, search, activeItem, setActiveItem, setSearch, setInput, setSearchResults } =
1425
useSputlitContext()
15-
const { node, setNodeContent, setPreviewMode, setNode } = useEditorContext()
26+
const { setNodeContent, setPreviewMode, setNode } = useEditorContext()
1627
const workspaceDetails = useAuthStore((store) => store.workspaceDetails)
1728
const { getSnippet } = useSnippets()
1829
const { ilinks } = useDataStore()
1930

2031
function execute(item: MexitAction) {
2132
switch (item.category) {
2233
case QuickLinkType.backlink: {
23-
let existingNode
24-
let nodePath = node.path
25-
if (!item?.extras?.new) {
26-
existingNode = ilinks.find((i) => i.nodeid === item.id)
27-
nodePath = existingNode.path
34+
let node: ILink
35+
const val = search.type === CategoryType.backlink ? search.value.slice(2) : search.value
36+
const nodeValue = val || getNewDraftKey()
37+
38+
if (item?.extras?.new) {
39+
node = createNodeWithUid(nodeValue)
2840
} else {
29-
nodePath = search.value.startsWith('[[') ? search.value.substring(2) : search.value || node.path
41+
node = ilinks.find((i) => i.nodeid === item.id)
3042
}
3143

32-
console.log('nodePat', nodePath)
3344
setNode({
34-
id: (existingNode || node).nodeid,
35-
title: nodePath.split(SEPARATOR).slice(-1)[0],
36-
path: nodePath,
37-
nodeid: (existingNode || node).nodeid
45+
id: node.nodeid,
46+
title: node.path.split(SEPARATOR).slice(-1)[0],
47+
path: node.path,
48+
nodeid: node.nodeid
3849
})
50+
3951
setPreviewMode(false)
4052
setInput('')
4153
break
@@ -56,7 +68,6 @@ export function useActionExecutor() {
5668
setVisualState(VisualState.hidden)
5769
break
5870
case ActionType.SEARCH: {
59-
console.log('activeItem', activeItem, item)
6071
// Ignore the case for search type action when it is the generic search action
6172
// As it is not a two step action
6273
if (activeItem?.title !== item?.title && item?.id !== '0') {
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { getPlateId, platesStore } from '@udecode/plate'
2+
import { CaptureType, extractMetadata, mog, NodeProperties, SEPARATOR } from '@mexit/core'
3+
import { useContentStore } from '../Stores/useContentStore'
4+
import useDataStore from '../Stores/useDataStore'
5+
import { useSputlitContext, VisualState } from './useSputlitContext'
6+
import toast from 'react-hot-toast'
7+
import { useAuthStore } from './useAuth'
8+
import { useEditorContext } from './useEditorContext'
9+
10+
export function useSaveChanges() {
11+
const workspaceDetails = useAuthStore((store) => store.workspaceDetails)
12+
const { node } = useEditorContext()
13+
const ilinks = useDataStore((state) => state.ilinks)
14+
const { selection, setVisualState } = useSputlitContext()
15+
const { setContent, setMetadata } = useContentStore()
16+
17+
const saveIt = (saveAndExit = false, notification = false) => {
18+
const state = platesStore.get.state()
19+
20+
// Editor Id is different from nodeId
21+
const editorId = getPlateId()
22+
const editorState = state[editorId].get.value()
23+
24+
// mog('editorState', { editorState })
25+
26+
const metadata = {
27+
saveableRange: selection?.range,
28+
sourceUrl: selection?.range && window.location.href
29+
}
30+
31+
const splitPath = node.path.split(SEPARATOR)
32+
const request = {
33+
type: 'CAPTURE_HANDLER',
34+
subType: 'BULK_CREATE_NODE',
35+
data: {
36+
id: node.nodeid,
37+
content: editorState,
38+
title: splitPath.slice(-1)[0],
39+
type: CaptureType.DRAFT,
40+
workspaceID: workspaceDetails.id,
41+
metadata: metadata
42+
}
43+
}
44+
45+
if (splitPath.length > 1) {
46+
const parent = splitPath.slice(0, -1).join(SEPARATOR)
47+
const parentID = ilinks.find((i) => i.path === parent)
48+
request.data['referenceID'] = parentID.nodeid
49+
}
50+
51+
// console.log('Sending: ', node, request)
52+
53+
setContent(node.nodeid, editorState)
54+
if (notification) {
55+
toast.success('Saved')
56+
}
57+
58+
chrome.runtime.sendMessage(request, (response) => {
59+
const { message, error } = response
60+
61+
if (error && notification) {
62+
toast.error('An Error Occured. Please try again.')
63+
} else {
64+
setMetadata(message.id, extractMetadata(message.data[0]))
65+
66+
if (notification) {
67+
toast.success('Saved to Cloud')
68+
}
69+
70+
if (saveAndExit) {
71+
setVisualState(VisualState.hidden)
72+
}
73+
}
74+
})
75+
}
76+
77+
return {
78+
saveIt
79+
}
80+
}

0 commit comments

Comments
 (0)