collab-toggle
This commit is contained in:
parent
83c660235a
commit
5c7f810c5f
|
@ -1,9 +1,14 @@
|
||||||
|
import { HocuspocusProvider } from '@hocuspocus/provider'
|
||||||
import { useMatch, useNavigate } from '@solidjs/router'
|
import { useMatch, useNavigate } from '@solidjs/router'
|
||||||
import { Editor } from '@tiptap/core'
|
import { Editor } from '@tiptap/core'
|
||||||
|
import Collaboration from '@tiptap/extension-collaboration'
|
||||||
|
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
|
||||||
import type { JSX } from 'solid-js'
|
import type { JSX } from 'solid-js'
|
||||||
import { Accessor, createContext, createSignal, onCleanup, useContext } from 'solid-js'
|
import { Accessor, createContext, createEffect, createSignal, on, onCleanup, useContext } from 'solid-js'
|
||||||
import { SetStoreFunction, createStore } from 'solid-js/store'
|
import { SetStoreFunction, createStore } from 'solid-js/store'
|
||||||
import { debounce } from 'throttle-debounce'
|
import { debounce } from 'throttle-debounce'
|
||||||
|
import uniqolor from 'uniqolor'
|
||||||
|
import { Doc } from 'yjs'
|
||||||
import { useSnackbar } from '~/context/ui'
|
import { useSnackbar } from '~/context/ui'
|
||||||
import deleteShoutQuery from '~/graphql/mutation/core/article-delete'
|
import deleteShoutQuery from '~/graphql/mutation/core/article-delete'
|
||||||
import updateShoutQuery from '~/graphql/mutation/core/article-update'
|
import updateShoutQuery from '~/graphql/mutation/core/article-update'
|
||||||
|
@ -14,6 +19,8 @@ import { useLocalize } from './localize'
|
||||||
import { useSession } from './session'
|
import { useSession } from './session'
|
||||||
|
|
||||||
export const AUTO_SAVE_DELAY = 3000
|
export const AUTO_SAVE_DELAY = 3000
|
||||||
|
const yDocs: Record<string, Doc> = {}
|
||||||
|
const providers: Record<string, HocuspocusProvider> = {}
|
||||||
|
|
||||||
export type WordCounter = {
|
export type WordCounter = {
|
||||||
characters: number
|
characters: number
|
||||||
|
@ -98,7 +105,7 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const matchEdit = useMatch(() => '/edit')
|
const matchEdit = useMatch(() => '/edit')
|
||||||
const matchEditSettings = useMatch(() => '/editSettings')
|
const matchEditSettings = useMatch(() => '/editSettings')
|
||||||
const { client } = useSession()
|
const { client, session } = useSession()
|
||||||
const { addFeed } = useFeed()
|
const { addFeed } = useFeed()
|
||||||
const snackbar = useSnackbar()
|
const snackbar = useSnackbar()
|
||||||
const [isEditorPanelVisible, setIsEditorPanelVisible] = createSignal<boolean>(false)
|
const [isEditorPanelVisible, setIsEditorPanelVisible] = createSignal<boolean>(false)
|
||||||
|
@ -251,7 +258,7 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[publishShoutById]', error)
|
console.error('[publishShoutById]', error)
|
||||||
snackbar?.showSnackbar({ type: 'error', body: localize?.t('Error') })
|
snackbar?.showSnackbar({ type: 'error', body: localize?.t('Error') || '' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,6 +285,63 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
|
||||||
})
|
})
|
||||||
onCleanup(debouncedAutoSave.cancel)
|
onCleanup(debouncedAutoSave.cancel)
|
||||||
|
|
||||||
|
createEffect(
|
||||||
|
on(
|
||||||
|
isCollabMode,
|
||||||
|
(x?: boolean) => () => {
|
||||||
|
const editorInstance = editing()
|
||||||
|
if (!editorInstance) return
|
||||||
|
try {
|
||||||
|
const docName = `shout-${form.shoutId}`
|
||||||
|
const token = session()?.access_token || ''
|
||||||
|
const profile = session()?.user?.app_data?.profile
|
||||||
|
|
||||||
|
if (!(token && profile)) {
|
||||||
|
throw new Error('Missing authentication data')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!yDocs[docName]) {
|
||||||
|
yDocs[docName] = new Doc()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!providers[docName]) {
|
||||||
|
providers[docName] = new HocuspocusProvider({
|
||||||
|
url: 'wss://hocuspocus.discours.io',
|
||||||
|
name: docName,
|
||||||
|
document: yDocs[docName],
|
||||||
|
token
|
||||||
|
})
|
||||||
|
console.log(`[collab mode] HocuspocusProvider connected for ${docName}`)
|
||||||
|
}
|
||||||
|
if (x) {
|
||||||
|
const newExtensions = [
|
||||||
|
Collaboration.configure({ document: yDocs[docName] }),
|
||||||
|
CollaborationCursor.configure({
|
||||||
|
provider: providers[docName],
|
||||||
|
user: { name: profile.name, color: uniqolor(profile.slug).color }
|
||||||
|
})
|
||||||
|
]
|
||||||
|
const extensions = editing()?.options.extensions.concat(newExtensions)
|
||||||
|
editorInstance.setOptions({ ...editorInstance.options, extensions })
|
||||||
|
providers[docName].connect()
|
||||||
|
} else if (editorInstance) {
|
||||||
|
providers[docName].disconnect()
|
||||||
|
const updatedExtensions = editorInstance.options.extensions.filter(
|
||||||
|
(ext) => ext.name !== 'collaboration' && ext.name !== 'collaborationCursor'
|
||||||
|
)
|
||||||
|
editorInstance.setOptions({
|
||||||
|
...editorInstance.options,
|
||||||
|
extensions: updatedExtensions
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[collab mode] error', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ defer: true }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
const handleInputChange = (key: keyof ShoutForm, value: string) => {
|
const handleInputChange = (key: keyof ShoutForm, value: string) => {
|
||||||
console.log(`[handleInputChange] ${key}: ${value}`)
|
console.log(`[handleInputChange] ${key}: ${value}`)
|
||||||
setForm(key, value)
|
setForm(key, value)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user