diff --git a/package.json b/package.json index 980f6082..b967676e 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "next": "13.4.12", "next-auth": "^4.22.1", "nextjs-toploader": "^1.4.2", - "novel": "0.5.0", + "novel": "1.0.2", "qs": "^6.13.0", "rc-pagination": "^3.3.1", "react": "18.2.0", @@ -100,6 +100,7 @@ "survey-react-ui": "^1.9.90", "swr": "^2.2.4", "tabulator-tables": "^5.5.2", + "tiptap-markdown": "0.8.10", "turndown": "^7.2.0", "use-debounce": "^9.0.4", "validator": "^13.9.0", diff --git a/src/domain/profile/repository.js b/src/domain/profile/repository.js index 5859d6b2..4f117cee 100644 --- a/src/domain/profile/repository.js +++ b/src/domain/profile/repository.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import { unwrapBlockData, wrapBlockData } from '@/components/block-editor/helper'; +import { unwrapBlockData, wrapBlockData } from '@/components/control/block-editor/helper'; import httpClient from '@/utils/http'; async function fetchWeb3BioProfile(address) { diff --git a/src/domain/profile/views/team-profile/CustomContent.js b/src/domain/profile/views/team-profile/CustomContent.js index 856820d0..2c2cc062 100644 --- a/src/domain/profile/views/team-profile/CustomContent.js +++ b/src/domain/profile/views/team-profile/CustomContent.js @@ -17,7 +17,26 @@ import clsx from 'clsx'; import { useState } from 'react'; -import BlockEditor, { getInitialBlockData, isBlockDataValid } from '@/components/block-editor'; +import BlockEditor, { setUploadHandler, getInitialBlockData, isBlockDataValid } from '@/components/control/block-editor'; + +import { upload } from '#/services/common'; + +setUploadHandler(async file => { + const formData = new FormData(); + + formData.append('file', file, file.name); + formData.append('intent', 'course_series'); + + const { code, data, message } = await upload({ file: formData }); + + return { + success: code === 200, + data: data?.user_upload_path ? `https://file-cdn.openbuild.xyz${data.user_upload_path}` : '', + code, + message, + extra: {}, + }; +}); function CustomContent({ className, data, onChange, editable }) { const [content, setContent] = useState(data); diff --git a/src/domain/profile/views/team-profile/TeamProfile.js b/src/domain/profile/views/team-profile/TeamProfile.js index c142811e..19ba8cd5 100644 --- a/src/domain/profile/views/team-profile/TeamProfile.js +++ b/src/domain/profile/views/team-profile/TeamProfile.js @@ -17,7 +17,7 @@ import { useState } from 'react'; import { useDebouncedCallback } from 'use-debounce'; -import { isBlockDataValid } from '@/components/block-editor'; +import { isBlockDataValid } from '@/components/control/block-editor/helper'; import useAppConfig from '@/hooks/useAppConfig'; import useMounted from '@/hooks/useMounted'; diff --git a/src/shared/components/block-editor/BlockEditor.js b/src/shared/components/control/block-editor/BlockEditor.js similarity index 95% rename from src/shared/components/block-editor/BlockEditor.js rename to src/shared/components/control/block-editor/BlockEditor.js index 78f20b45..aa3c21af 100644 --- a/src/shared/components/block-editor/BlockEditor.js +++ b/src/shared/components/control/block-editor/BlockEditor.js @@ -20,11 +20,12 @@ import clsx from 'clsx'; import { EditorRoot, EditorContent, EditorCommand, EditorCommandEmpty, EditorCommandList, EditorCommandItem, + ImageResizer, handleCommandNavigation, } from 'novel'; -import { ImageResizer, handleCommandNavigation } from 'novel/extensions'; -import { isFunction } from '../../utils'; +import { isFunction } from '../../../utils'; import BlockEditorBubble from './bubble'; +import DragHandle from './DragHandle'; import { defaultExtensions } from './extensions'; import { isBlockDataValid } from './helper'; import { slashCommand, suggestionItems } from './slash'; @@ -56,6 +57,7 @@ function BlockEditor({ className, data, onChange, editable = false }) { onUpdate={handleUpdate} slotAfter={} > + No results diff --git a/src/shared/components/control/block-editor/DragHandle.js b/src/shared/components/control/block-editor/DragHandle.js new file mode 100644 index 00000000..cb443c6a --- /dev/null +++ b/src/shared/components/control/block-editor/DragHandle.js @@ -0,0 +1,50 @@ +/** + * Copyright 2024 OpenBuild + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// import { useEditor } from 'novel'; + +// function insertNewBlockBelow(editor) { +// return editor.chain() +// .focus() +// .command(({ tr }) => { +// const { $from } = tr.selection; +// const insertPos = $from.after($from.depth); +// tr.insert(insertPos, editor.schema.nodes.paragraph.create()); +// return true; +// }) +// .run(); +// } + +// function deleteBlock(editor) { +// return editor.chain() +// .focus() +// .command(({ tr }) => { +// const { $from } = tr.selection; +// tr.delete($from.before($from.depth), $from.after($from.depth)); +// return true; +// }) +// .run(); +// } + +function DragHandle() { + // const { editor } = useEditor(); + + return ( +
+ ); +} + +export default DragHandle; diff --git a/src/shared/components/block-editor/bubble/Button.js b/src/shared/components/control/block-editor/bubble/Button.js similarity index 100% rename from src/shared/components/block-editor/bubble/Button.js rename to src/shared/components/control/block-editor/bubble/Button.js diff --git a/src/shared/components/block-editor/bubble/ColorSelector.js b/src/shared/components/control/block-editor/bubble/ColorSelector.js similarity index 100% rename from src/shared/components/block-editor/bubble/ColorSelector.js rename to src/shared/components/control/block-editor/bubble/ColorSelector.js diff --git a/src/shared/components/block-editor/bubble/LinkSelector.js b/src/shared/components/control/block-editor/bubble/LinkSelector.js similarity index 100% rename from src/shared/components/block-editor/bubble/LinkSelector.js rename to src/shared/components/control/block-editor/bubble/LinkSelector.js diff --git a/src/shared/components/block-editor/bubble/MathSelector.js b/src/shared/components/control/block-editor/bubble/MathSelector.js similarity index 100% rename from src/shared/components/block-editor/bubble/MathSelector.js rename to src/shared/components/control/block-editor/bubble/MathSelector.js diff --git a/src/shared/components/block-editor/bubble/NodeSelector.js b/src/shared/components/control/block-editor/bubble/NodeSelector.js similarity index 100% rename from src/shared/components/block-editor/bubble/NodeSelector.js rename to src/shared/components/control/block-editor/bubble/NodeSelector.js diff --git a/src/shared/components/block-editor/bubble/Popover.js b/src/shared/components/control/block-editor/bubble/Popover.js similarity index 100% rename from src/shared/components/block-editor/bubble/Popover.js rename to src/shared/components/control/block-editor/bubble/Popover.js diff --git a/src/shared/components/block-editor/bubble/Separator.js b/src/shared/components/control/block-editor/bubble/Separator.js similarity index 100% rename from src/shared/components/block-editor/bubble/Separator.js rename to src/shared/components/control/block-editor/bubble/Separator.js diff --git a/src/shared/components/block-editor/bubble/TextButtons.js b/src/shared/components/control/block-editor/bubble/TextButtons.js similarity index 100% rename from src/shared/components/block-editor/bubble/TextButtons.js rename to src/shared/components/control/block-editor/bubble/TextButtons.js diff --git a/src/shared/components/block-editor/bubble/helper.js b/src/shared/components/control/block-editor/bubble/helper.js similarity index 100% rename from src/shared/components/block-editor/bubble/helper.js rename to src/shared/components/control/block-editor/bubble/helper.js diff --git a/src/shared/components/block-editor/bubble/index.js b/src/shared/components/control/block-editor/bubble/index.js similarity index 100% rename from src/shared/components/block-editor/bubble/index.js rename to src/shared/components/control/block-editor/bubble/index.js diff --git a/src/shared/components/block-editor/extensions.js b/src/shared/components/control/block-editor/extensions.js similarity index 94% rename from src/shared/components/block-editor/extensions.js rename to src/shared/components/control/block-editor/extensions.js index 4e59b2a2..1fdb9af9 100644 --- a/src/shared/components/block-editor/extensions.js +++ b/src/shared/components/control/block-editor/extensions.js @@ -26,7 +26,6 @@ import { GlobalDragHandle, HighlightExtension, HorizontalRule, - MarkdownExtension, Placeholder, StarterKit, TaskItem, @@ -39,8 +38,9 @@ import { UpdatedImage, Youtube, Mathematics, -} from 'novel/extensions'; -import { UploadImagesPlugin } from 'novel/plugins'; + UploadImagesPlugin, +} from 'novel'; +import { Markdown } from 'tiptap-markdown'; //TODO I am using cx here to get tailwind autocomplete working, idk if someone else can write a regex to just capture the class key in objects const aiHighlight = AIHighlight; @@ -164,7 +164,7 @@ const mathematics = Mathematics.configure({ const characterCount = CharacterCount.configure(); -const markdownExtension = MarkdownExtension.configure({ +const markdownExtension = Markdown.configure({ html: true, tightLists: true, tightListClass: 'tight', @@ -175,6 +175,10 @@ const markdownExtension = MarkdownExtension.configure({ transformCopiedText: false, }); +const dragHandle = GlobalDragHandle.configure({ + dragHandleSelector: '.BlockEditor-dragHandle', +}); + const defaultExtensions = [ starterKit, placeholder, @@ -196,7 +200,7 @@ const defaultExtensions = [ TextStyle, Color, CustomKeymap, - GlobalDragHandle, + dragHandle, ColumnsExtension, ]; diff --git a/src/shared/components/block-editor/helper.js b/src/shared/components/control/block-editor/helper.js similarity index 72% rename from src/shared/components/block-editor/helper.js rename to src/shared/components/control/block-editor/helper.js index 706931de..093a1dab 100644 --- a/src/shared/components/block-editor/helper.js +++ b/src/shared/components/control/block-editor/helper.js @@ -14,7 +14,21 @@ * limitations under the License. */ -import { isPlainObject } from '../../utils'; +import { isFunction, isPlainObject } from '../../../utils'; + +let uploadHandler; + +function setUploadHandler(handler) { + if (!isFunction(handler)) { + return; + } + + uploadHandler = handler; +} + +function getUploadHandler() { + return uploadHandler; +} const BLOCK_DATA_SPEC_VERSION = '0.0.1'; @@ -34,4 +48,7 @@ function wrapBlockData(data) { return { version: BLOCK_DATA_SPEC_VERSION, data }; } -export { getInitialBlockData, isBlockDataValid, wrapBlockData, unwrapBlockData }; +export { + getUploadHandler, setUploadHandler, + getInitialBlockData, isBlockDataValid, wrapBlockData, unwrapBlockData, +}; diff --git a/src/shared/components/block-editor/index.js b/src/shared/components/control/block-editor/index.js similarity index 100% rename from src/shared/components/block-editor/index.js rename to src/shared/components/control/block-editor/index.js diff --git a/src/shared/components/block-editor/slash.js b/src/shared/components/control/block-editor/slash.js similarity index 98% rename from src/shared/components/block-editor/slash.js rename to src/shared/components/control/block-editor/slash.js index a5fb929e..b8ce4e09 100644 --- a/src/shared/components/block-editor/slash.js +++ b/src/shared/components/control/block-editor/slash.js @@ -31,7 +31,9 @@ import { Columns3, Columns4, } from 'lucide-react'; -import { createSuggestionItems, Command, renderItems } from 'novel/extensions'; +import { createSuggestionItems, Command, renderItems } from 'novel'; + +import uploadFn from './upload'; function createColumnItems() { return [ @@ -141,8 +143,7 @@ const suggestionItems = createSuggestionItems([ if (input.files?.length) { const file = input.files[0]; const pos = editor.view.state.selection.from; - console.log(file, editor.view, pos); - alert('Isn\'t implemented yet.'); + uploadFn(file, editor.view, pos); } }; input.click(); diff --git a/src/shared/components/control/block-editor/upload.js b/src/shared/components/control/block-editor/upload.js new file mode 100644 index 00000000..ac5332b6 --- /dev/null +++ b/src/shared/components/control/block-editor/upload.js @@ -0,0 +1,65 @@ +/** + * Copyright 2024 OpenBuild + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createImageUpload } from 'novel'; +import { toast } from 'react-toastify'; + +import { isFunction } from '../../../utils'; +import { getUploadHandler } from './helper'; + +function onUpload(file) { + const uploadHandler = getUploadHandler(); + + if (!isFunction(uploadHandler)) { + return Promise.reject('Handler for uploading images isn\'t specified.'); + } + + const req = uploadHandler(file); + + return new Promise((resolve, reject) => { + req + .then(res => { + if (res.success) { + resolve(res.data); + } else { + throw new Error('Error uploading image. Please try again.'); + } + }) + .catch(err => { + reject(err); + return e.message; + }); + }); +} + +const uploadFn = createImageUpload({ + onUpload, + validateFn: file => { + if (!file.type.includes('image/')) { + toast.error('File type not supported.'); + return false; + } + + if (file.size / 1024 / 1024 > 20) { + toast.error('File size too big (max 20MB).'); + return false; + } + + return true; + }, +}); + +export default uploadFn; diff --git a/src/shared/components/control/headlessui.js b/src/shared/components/control/headlessui.js index a75a13dd..56945c1a 100644 --- a/src/shared/components/control/headlessui.js +++ b/src/shared/components/control/headlessui.js @@ -14,4 +14,4 @@ * limitations under the License. */ -export { Disclosure, Popover, Dialog, Listbox, Transition, Switch } from '@headlessui/react'; +export { Disclosure, Popover, Dialog, Listbox, Transition, Switch, Menu } from '@headlessui/react';