Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better preview and saving on sputlit #85

Merged
merged 3 commits into from
Jun 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 12 additions & 93 deletions apps/extension/src/Components/Content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,137 +4,56 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'

import { getMexHTMLDeserializer } from '../../Utils/deserialize'
import { Editor } from '../Editor'
import { useSputlitContext, VisualState } from '../../Hooks/useSputlitContext'
import { useSputlitContext } from '../../Hooks/useSputlitContext'
import Results from '../Results'
import { StyledContent } from './styled'
import { useAuthStore } from '../../Hooks/useAuth'
import toast from 'react-hot-toast'
import {
CaptureType,
createNodeWithUid,
defaultContent,
extractMetadata,
generateNodeId,
getNewDraftKey,
mog,
QuickLinkType,
SEPARATOR
} from '@mexit/core'
import { CategoryType, NodeEditorContent, NodeMetadata } from '@mexit/core'
import { CategoryType, createNodeWithUid, defaultContent, getNewDraftKey, QuickLinkType } from '@mexit/core'
import { NodeEditorContent } from '@mexit/core'
import { useEditorContext } from '../../Hooks/useEditorContext'
import { useSnippets } from '../../Hooks/useSnippets'
import { useContentStore } from '../../Stores/useContentStore'
import useDataStore from '../../Stores/useDataStore'
import { useSaveChanges } from '../../Hooks/useSaveChanges'

export default function Content() {
const { selection, setVisualState, searchResults, activeIndex } = useSputlitContext()
const { node, nodeContent, setNodeContent, previewMode, setNode } = useEditorContext()
const { selection, searchResults, activeIndex } = useSputlitContext()
const { node, setNodeContent, previewMode, setNode } = useEditorContext()
const { saveIt } = useSaveChanges()

const { setContent, setMetadata, getContent } = useContentStore()
const { getContent } = useContentStore()
const editor = usePlateEditorRef(node.nodeid)
const getSnippet = useSnippets().getSnippet

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

const workspaceDetails = useAuthStore((store) => store.workspaceDetails)

const ilinks = useDataStore((store) => store.ilinks)

useEffect(() => {
const content = getMexHTMLDeserializer(selection?.html, editor)

if (selection?.range && content && selection?.url && previewMode) {
setNodeContent([{ children: content }])
contentRef.current = content
deserializedContentRef.current = content
}
}, [editor]) // eslint-disable-line

// useEffect(() => {
// console.log('NODE CHANGED: ', node)
// }, [node])

useEffect(() => {
setNode(createNodeWithUid(getNewDraftKey()))
}, [])
}, [editor])

const onChangeSave = (val: any[]) => {
if (val) {
setNodeContent(val)
contentRef.current = val
}
}

useEffect(() => {
const handleSave = (saveAndExit = false) => {
const metadata = {
saveableRange: selection?.range,
sourceUrl: selection?.range && window.location.href
}

const splitPath = node.path.split(SEPARATOR)
const request = {
type: 'CAPTURE_HANDLER',
subType: 'BULK_CREATE_NODE',
data: {
id: node.nodeid,
content: contentRef.current,
title: splitPath.slice(-1)[0],
type: CaptureType.DRAFT,
workspaceID: workspaceDetails.id,
metadata: metadata
}
}

if (splitPath.length > 1) {
const parent = splitPath.slice(0, -1).join(SEPARATOR)
const parentID = ilinks.find((i) => i.path === parent)
request.data['referenceID'] = parentID.nodeid
}

// console.log('Sending: ', node, request)

setContent(node.nodeid, contentRef.current)
chrome.runtime.sendMessage(request, (response) => {
const { message, error } = response
console.log('Response aaya: ', response)
if (error) {
if (error === 'Not Authenticated') {
toast.error('Not Authenticated. Please login on Mexit webapp.')
} else {
toast.error('An Error Occured. Please try again.')
}
} else {
setMetadata(message.id, extractMetadata(message.data[0]))
toast.success('Saved to Cloud')
if (saveAndExit) {
setTimeout(() => {
setVisualState(VisualState.hidden)
}, 2000)
}
}
})
}

const handleSaveKeydown = (event: KeyboardEvent) => {
if (event.key === 's' && event.metaKey) {
event.preventDefault()
handleSave(true)
} else if (event.key === 'Enter' && event.metaKey) {
if (event.key === 's' && event.metaKey && !previewMode) {
event.preventDefault()
handleSave(true)
saveIt(true, true)
}
}

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

return () => {
// handleSave()
document.getElementById('mexit')!.removeEventListener('keydown', handleSaveKeydown)
}
}, [node, ilinks])
}, [node, previewMode])

useEffect(() => {
const item = searchResults[activeIndex]
Expand Down
2 changes: 1 addition & 1 deletion apps/extension/src/Components/Editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export const Editor: React.FC<EditorProps> = ({ readOnly, onChange }) => {
}, 1000)

return (
<EditorWrapper style={springProps} onClick={() => setPreviewMode(false)} onBlur={() => setPreviewMode(true)}>
<EditorWrapper style={springProps} onClick={() => setPreviewMode(false)}>
<EditorStyles>
<MexEditor
comboboxConfig={comboboxConfig}
Expand Down
15 changes: 12 additions & 3 deletions apps/extension/src/Components/InternalEvents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import LinkedInBadge from './LinkedInBadge'
import { getHtmlString } from './Source'
import { MEXIT_FRONTEND_URL_BASE } from '@mexit/core'
import { useContentStore } from '../Stores/useContentStore'
import { useEditorContext } from '../Hooks/useEditorContext'
import { useSaveChanges } from '../Hooks/useSaveChanges'

export function InternalEvents() {
useToggleHandler()
Expand All @@ -32,6 +34,8 @@ const highlighter = new Highlighter()
*/
function useToggleHandler() {
const { visualState, setVisualState, setSelection, setTooltipState } = useSputlitContext()
const { previewMode, setPreviewMode } = useEditorContext()
const { saveIt } = useSaveChanges()

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

function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') {
setVisualState(VisualState.hidden)
setTooltipState({ visualState: VisualState.hidden })
if (previewMode) {
setVisualState(VisualState.hidden)
setTooltipState({ visualState: VisualState.hidden })
} else {
setPreviewMode(true)
saveIt(false, true)
}
}
}

Expand All @@ -74,7 +83,7 @@ function useToggleHandler() {
chrome.runtime.onMessage.removeListener(messageHandler)
window.removeEventListener('keydown', handleKeyDown)
}
}, [visualState])
}, [visualState, previewMode])
}

function dibbaToggle() {
Expand Down
13 changes: 10 additions & 3 deletions apps/extension/src/Components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import {
import { useTheme } from 'styled-components'
import { useSearchProps } from '../../Hooks/useSearchProps'
import { useEditorContext } from '../../Hooks/useEditorContext'
import { useSaveChanges } from '../../Hooks/useSaveChanges'

const Search = () => {
const { input, setInput, setSearch, isLoading } = useSputlitContext()
const { previewMode } = useEditorContext()
const { previewMode, setPreviewMode } = useEditorContext()
const { icon, placeholder } = useSearchProps()
const { saveIt } = useSaveChanges()
const theme = useTheme()

const getQuery = (value: string): Search => {
Expand Down Expand Up @@ -63,12 +65,17 @@ const Search = () => {
handleSearchInput(query)
}

// TODO: it would be good to have the ability to go back after selected a search type action
const onBackClick = () => {
if (!previewMode) {
setPreviewMode(true)
saveIt(false, true)
}
}

return (
<StyledSearch>
{/* {activeItem?.type === ActionType.SEARCH && <QuerySearch>{activeItem.title} | </QuerySearch>} */}
<CenterIcon id="wd-mex-search-left-icon" cursor={false}>
<CenterIcon id="wd-mex-search-left-icon" cursor={!previewMode} onClick={onBackClick}>
<Icon color={theme.colors.primary} height={24} width={24} icon={icon} />
</CenterIcon>
<StyledInput
Expand Down
39 changes: 25 additions & 14 deletions apps/extension/src/Hooks/useActionExecutor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import { ActionType, CategoryType, ILink, MexitAction, parseSnippet, QuickLinkType, SEPARATOR } from '@mexit/core'
import {
ActionType,
CategoryType,
createNodeWithUid,
getNewDraftKey,
getUntitledDraftKey,
ILink,
MexitAction,
parseSnippet,
QuickLinkType,
SEPARATOR
} from '@mexit/core'
import { copyToClipboard } from '@mexit/shared'
import toast from 'react-hot-toast'
import Action from '../Components/Action'
Expand All @@ -12,30 +23,31 @@ import { useSputlitContext, VisualState } from './useSputlitContext'
export function useActionExecutor() {
const { setVisualState, search, activeItem, setActiveItem, setSearch, setInput, setSearchResults } =
useSputlitContext()
const { node, setNodeContent, setPreviewMode, setNode } = useEditorContext()
const { setNodeContent, setPreviewMode, setNode } = useEditorContext()
const workspaceDetails = useAuthStore((store) => store.workspaceDetails)
const { getSnippet } = useSnippets()
const { ilinks } = useDataStore()

function execute(item: MexitAction) {
switch (item.category) {
case QuickLinkType.backlink: {
let existingNode
let nodePath = node.path
if (!item?.extras?.new) {
existingNode = ilinks.find((i) => i.nodeid === item.id)
nodePath = existingNode.path
let node: ILink
const val = search.type === CategoryType.backlink ? search.value.slice(2) : search.value
const nodeValue = val || getNewDraftKey()

if (item?.extras?.new) {
node = createNodeWithUid(nodeValue)
} else {
nodePath = search.value.startsWith('[[') ? search.value.substring(2) : search.value || node.path
node = ilinks.find((i) => i.nodeid === item.id)
}

console.log('nodePat', nodePath)
setNode({
id: (existingNode || node).nodeid,
title: nodePath.split(SEPARATOR).slice(-1)[0],
path: nodePath,
nodeid: (existingNode || node).nodeid
id: node.nodeid,
title: node.path.split(SEPARATOR).slice(-1)[0],
path: node.path,
nodeid: node.nodeid
})

setPreviewMode(false)
setInput('')
break
Expand All @@ -56,7 +68,6 @@ export function useActionExecutor() {
setVisualState(VisualState.hidden)
break
case ActionType.SEARCH: {
console.log('activeItem', activeItem, item)
// Ignore the case for search type action when it is the generic search action
// As it is not a two step action
if (activeItem?.title !== item?.title && item?.id !== '0') {
Expand Down
80 changes: 80 additions & 0 deletions apps/extension/src/Hooks/useSaveChanges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { getPlateId, platesStore } from '@udecode/plate'
import { CaptureType, extractMetadata, mog, NodeProperties, SEPARATOR } from '@mexit/core'
import { useContentStore } from '../Stores/useContentStore'
import useDataStore from '../Stores/useDataStore'
import { useSputlitContext, VisualState } from './useSputlitContext'
import toast from 'react-hot-toast'
import { useAuthStore } from './useAuth'
import { useEditorContext } from './useEditorContext'

export function useSaveChanges() {
const workspaceDetails = useAuthStore((store) => store.workspaceDetails)
const { node } = useEditorContext()
const ilinks = useDataStore((state) => state.ilinks)
const { selection, setVisualState } = useSputlitContext()
const { setContent, setMetadata } = useContentStore()

const saveIt = (saveAndExit = false, notification = false) => {
const state = platesStore.get.state()

// Editor Id is different from nodeId
const editorId = getPlateId()
const editorState = state[editorId].get.value()

// mog('editorState', { editorState })

const metadata = {
saveableRange: selection?.range,
sourceUrl: selection?.range && window.location.href
}

const splitPath = node.path.split(SEPARATOR)
const request = {
type: 'CAPTURE_HANDLER',
subType: 'BULK_CREATE_NODE',
data: {
id: node.nodeid,
content: editorState,
title: splitPath.slice(-1)[0],
type: CaptureType.DRAFT,
workspaceID: workspaceDetails.id,
metadata: metadata
}
}

if (splitPath.length > 1) {
const parent = splitPath.slice(0, -1).join(SEPARATOR)
const parentID = ilinks.find((i) => i.path === parent)
request.data['referenceID'] = parentID.nodeid
}

// console.log('Sending: ', node, request)

setContent(node.nodeid, editorState)
if (notification) {
toast.success('Saved')
}

chrome.runtime.sendMessage(request, (response) => {
const { message, error } = response

if (error && notification) {
toast.error('An Error Occured. Please try again.')
} else {
setMetadata(message.id, extractMetadata(message.data[0]))

if (notification) {
toast.success('Saved to Cloud')
}

if (saveAndExit) {
setVisualState(VisualState.hidden)
}
}
})
}

return {
saveIt
}
}
Loading