collabmode-onoff

This commit is contained in:
Untone 2024-10-11 04:55:18 +03:00
parent 0615476dd9
commit 6bd219a2e4
3 changed files with 78 additions and 57 deletions

View File

@ -32,7 +32,6 @@ export type EditorComponentProps = {
shoutId: number shoutId: number
initialContent?: string initialContent?: string
onChange: (text: string) => void onChange: (text: string) => void
disableCollaboration?: boolean
} }
const yDocs: Record<string, Doc> = {} const yDocs: Record<string, Doc> = {}
@ -45,7 +44,7 @@ export const EditorComponent = (props: EditorComponentProps) => {
const [isCommonMarkup, setIsCommonMarkup] = createSignal(false) const [isCommonMarkup, setIsCommonMarkup] = createSignal(false)
const [shouldShowTextBubbleMenu, setShouldShowTextBubbleMenu] = createSignal(false) const [shouldShowTextBubbleMenu, setShouldShowTextBubbleMenu] = createSignal(false)
const { showSnackbar } = useSnackbar() const { showSnackbar } = useSnackbar()
const { countWords, setEditing } = useEditorContext() const { countWords, setEditing, isCollabMode } = useEditorContext()
const [editorOptions, setEditorOptions] = createSignal<Partial<EditorOptions>>({}) const [editorOptions, setEditorOptions] = createSignal<Partial<EditorOptions>>({})
const [editorElRef, setEditorElRef] = createSignal<HTMLElement | undefined>() const [editorElRef, setEditorElRef] = createSignal<HTMLElement | undefined>()
const [textBubbleMenuRef, setTextBubbleMenuRef] = createSignal<HTMLDivElement | undefined>() const [textBubbleMenuRef, setTextBubbleMenuRef] = createSignal<HTMLDivElement | undefined>()
@ -154,6 +153,7 @@ export const EditorComponent = (props: EditorComponentProps) => {
} }
console.log(options) console.log(options)
setEditorOptions(() => options) setEditorOptions(() => options)
return options
} }
// stage 1: create editor options when got author profile // stage 1: create editor options when got author profile
@ -166,19 +166,6 @@ export const EditorComponent = (props: EditorComponentProps) => {
}) })
) )
// Перенос всех эффектов, зависящих от editor, внутрь onMount
onMount(() => {
console.log('Editor component mounted')
editorElRef()?.addEventListener('focus', handleFocus)
requireAuthentication(() => {
setTimeout(() => {
setupEditor()
createEditorInstance(editorOptions())
initializeMenus()
}, 1200)
}, 'edit')
})
const isFigcaptionActive = createEditorTransaction(editor as Accessor<Editor | undefined>, (e) => const isFigcaptionActive = createEditorTransaction(editor as Accessor<Editor | undefined>, (e) =>
e?.isActive('figcaption') e?.isActive('figcaption')
) )
@ -276,38 +263,39 @@ export const EditorComponent = (props: EditorComponentProps) => {
return return
} }
try { setEditorOptions((prev: Partial<EditorOptions>) => {
const docName = `shout-${props.shoutId}` const extensions = [...(prev.extensions || [])]
const token = session()?.access_token || ''
const profile = author()
if (!(token && profile)) { try {
throw new Error('Missing authentication data') if (!isCollabMode()) {
} // Remove collaboration extensions and return
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(`HocuspocusProvider установлен для ${docName}`)
}
setEditorOptions((prev: Partial<EditorOptions>) => {
const extensions = [...(prev.extensions || [])]
if (props.disableCollaboration) {
// Remove collaboration extensions if they exist
const filteredExtensions = extensions.filter( const filteredExtensions = extensions.filter(
(ext) => ext.name !== 'collaboration' && ext.name !== 'collaborationCursor' (ext) => ext.name !== 'collaboration' && ext.name !== 'collaborationCursor'
) )
return { ...prev, extensions: filteredExtensions } return { ...prev, extensions: filteredExtensions }
} }
const docName = `shout-${props.shoutId}`
const token = session()?.access_token || ''
const profile = author()
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(`HocuspocusProvider установлен для ${docName}`)
}
extensions.push( extensions.push(
Collaboration.configure({ document: yDocs[docName] }), Collaboration.configure({ document: yDocs[docName] }),
CollaborationCursor.configure({ CollaborationCursor.configure({
@ -315,13 +303,14 @@ export const EditorComponent = (props: EditorComponentProps) => {
user: { name: profile.name, color: uniqolor(profile.slug).color } user: { name: profile.name, color: uniqolor(profile.slug).color }
}) })
) )
console.log('collab extensions added:', extensions)
return { ...prev, extensions } } catch (error) {
}) console.error('Error initializing collaboration:', error)
} catch (error) { showSnackbar({ body: t('Failed to initialize collaboration') })
console.error('Error initializing collaboration:', error) }
showSnackbar({ body: t('Failed to initialize collaboration') }) console.log('collab extensions added:', extensions)
} return { ...prev, extensions }
})
} }
const handleFocus = (event: FocusEvent) => { const handleFocus = (event: FocusEvent) => {
@ -332,13 +321,23 @@ export const EditorComponent = (props: EditorComponentProps) => {
} }
} }
// Инициализируем коллаборацию если необходимо onMount(() => {
console.log('Editor component mounted')
editorElRef()?.addEventListener('focus', handleFocus)
requireAuthentication(() => {
setTimeout(() => {
const opts = setupEditor()
createEditorInstance(opts)
initializeMenus()
}, 120)
}, 'edit')
})
// collab mode on/off
createEffect( createEffect(
on( on(
() => props.disableCollaboration, isCollabMode,
() => { (x) => !x && initializeCollaboration(),
initializeCollaboration()
},
{ defer: true } { defer: true }
) )
) )

View File

@ -11,6 +11,7 @@ import { useLocalize } from '~/context/localize'
import { useUI } from '~/context/ui' import { useUI } from '~/context/ui'
import { useEscKeyDownHandler } from '~/lib/useEscKeyDownHandler' import { useEscKeyDownHandler } from '~/lib/useEscKeyDownHandler'
import { useOutsideClickHandler } from '~/lib/useOutsideClickHandler' import { useOutsideClickHandler } from '~/lib/useOutsideClickHandler'
import styles from './Panel.module.scss' import styles from './Panel.module.scss'
const typograf = new Typograf({ locale: ['ru', 'en-US'] }) const typograf = new Typograf({ locale: ['ru', 'en-US'] })
@ -23,6 +24,7 @@ export const Panel = (props: Props) => {
const { t } = useLocalize() const { t } = useLocalize()
const { showModal } = useUI() const { showModal } = useUI()
const { const {
setIsCollabMode,
isEditorPanelVisible, isEditorPanelVisible,
wordCounter, wordCounter,
form, form,
@ -32,13 +34,12 @@ export const Panel = (props: Props) => {
publishShout, publishShout,
editing: editor editing: editor
} = useEditorContext() } = useEditorContext()
const [containerRef, setAsideContainerRef] = createSignal<HTMLElement | undefined>()
let containerRef: HTMLElement | undefined
const [isShortcutsVisible, setIsShortcutsVisible] = createSignal(false) const [isShortcutsVisible, setIsShortcutsVisible] = createSignal(false)
const [isTypographyFixed, setIsTypographyFixed] = createSignal(false) const [isTypographyFixed, setIsTypographyFixed] = createSignal(false)
useOutsideClickHandler({ useOutsideClickHandler({
containerRef, containerRef: containerRef(),
predicate: () => isEditorPanelVisible(), predicate: () => isEditorPanelVisible(),
handler: () => toggleEditorPanel() handler: () => toggleEditorPanel()
}) })
@ -67,7 +68,7 @@ export const Panel = (props: Props) => {
return ( return (
<aside <aside
ref={(el) => (containerRef = el)} ref={setAsideContainerRef}
class={clsx('col-md-6', styles.Panel, { [styles.hidden]: !isEditorPanelVisible() })} class={clsx('col-md-6', styles.Panel, { [styles.hidden]: !isEditorPanelVisible() })}
> >
<Button <Button
@ -96,6 +97,13 @@ export const Panel = (props: Props) => {
{t('Invite co-authors')} {t('Invite co-authors')}
</span> </span>
</p> </p>
{/* TODO: <Show when={coauthorsCount() > 0}> */}
<p>
<span class={styles.link} onClick={() => setIsCollabMode((x) => !x)}>
{t('Collaborative mode')}
</span>
</p>
{/*</Show> */}
<p> <p>
<A <A
class={styles.link} class={styles.link}

View File

@ -10,6 +10,7 @@ import { ToolbarControl } from './ToolbarControl'
import { Popover } from '~/components/_shared/Popover/Popover' import { Popover } from '~/components/_shared/Popover/Popover'
import styles from './FullBubbleMenu.module.scss' import styles from './FullBubbleMenu.module.scss'
import { useEditorContext } from '~/context/editor'
type FullBubbleMenuProps = { type FullBubbleMenuProps = {
editor: () => Editor | undefined editor: () => Editor | undefined
@ -20,6 +21,7 @@ type FullBubbleMenuProps = {
export const FullBubbleMenu = (props: FullBubbleMenuProps) => { export const FullBubbleMenu = (props: FullBubbleMenuProps) => {
const { t } = useLocalize() const { t } = useLocalize()
const { isCollabMode, setIsCollabMode } = useEditorContext()
// SIGNALS // SIGNALS
const [textSizeBubbleOpen, setTextSizeBubbleOpen] = createSignal(false) const [textSizeBubbleOpen, setTextSizeBubbleOpen] = createSignal(false)
@ -259,6 +261,18 @@ export const FullBubbleMenu = (props: FullBubbleMenuProps) => {
<div style={{ width: '5px' }} /> <div style={{ width: '5px' }} />
<ListDropdown /> <ListDropdown />
<div class={styles.dropDownHolder}>
<Popover content={t('Collaborative mode')}>
{(triggerRef: (el: HTMLButtonElement) => void) => (
<button ref={triggerRef} type="button" class={styles.actionButton}
onClick={() => setIsCollabMode(x => !x)}
>
<Icon name={`comment${isCollabMode() ? '-hover' : ''}`} />
</button>
)}
</Popover>
</div>
</Show> </Show>
</> </>
) )