• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

EcrituresNumeriques / stylo / 14472219229

15 Apr 2025 02:37PM UTC coverage: 33.505% (+2.1%) from 31.388%
14472219229

push

github

web-flow
chore: utilise SWR pour gérer les versions d'un article (#1370)

515 of 777 branches covered (66.28%)

Branch coverage included in aggregate %.

227 of 953 new or added lines in 22 files covered. (23.82%)

107 existing lines in 6 files now uncovered.

5015 of 15728 relevant lines covered (31.89%)

2.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/front/src/components/collaborative/CollaborativeTextEditor.jsx
1
import Editor from '@monaco-editor/react'
×
NEW
2
import clsx from 'clsx'
×
3
import throttle from 'lodash.throttle'
×
NEW
4
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
×
5
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
×
6
import { MonacoBinding } from 'y-monaco'
×
NEW
7
import { useArticleVersion } from '../../hooks/article.js'
×
NEW
8
import { useCollaboration } from '../../hooks/collaboration.js'
×
NEW
9
import Alert from '../molecules/Alert.jsx'
×
10

11
import Loading from '../molecules/Loading.jsx'
×
12
import defaultEditorOptions from '../Write/providers/monaco/options.js'
×
13
import CollaborativeEditorStatus from './CollaborativeEditorStatus.jsx'
×
14
import CollaborativeEditorWebSocketStatus from './CollaborativeEditorWebSocketStatus.jsx'
×
15

16
import styles from './CollaborativeTextEditor.module.scss'
×
17

18
/**
19
 * @param {object} props
20
 * @param {string} props.articleId
21
 * @param {string|undefined} props.versionId
22
 * @returns {Element}
23
 */
NEW
24
export default function CollaborativeTextEditor({ articleId, versionId }) {
×
NEW
25
  const { yText, awareness, websocketStatus, dynamicStyles } = useCollaboration(
×
NEW
26
    { articleId, versionId }
×
27
  )
×
NEW
28
  const { version, error, isLoading } = useArticleVersion({ versionId })
×
29
  const dispatch = useDispatch()
×
30
  const editorRef = useRef(null)
×
31
  const editorCursorPosition = useSelector(
×
32
    (state) => state.editorCursorPosition,
×
33
    shallowEqual
×
34
  )
×
35

NEW
36
  const hasVersion = useMemo(() => !!versionId, [versionId])
×
37

38
  const options = useMemo(
×
39
    () => ({
×
40
      ...defaultEditorOptions,
×
NEW
41
      contextmenu: hasVersion ? false : websocketStatus === 'connected',
×
NEW
42
      readOnly: hasVersion ? true : websocketStatus !== 'connected',
×
43
    }),
×
NEW
44
    [websocketStatus, hasVersion]
×
45
  )
×
46

47
  const handleUpdateArticleStructureAndStats = throttle(
×
48
    ({ text }) => {
×
49
      dispatch({ type: 'UPDATE_ARTICLE_STATS', md: text })
×
50
      dispatch({ type: 'UPDATE_ARTICLE_STRUCTURE', md: text })
×
51
    },
×
52
    250,
×
53
    { leading: false, trailing: true }
×
54
  )
×
55

NEW
56
  const handleCollaborativeEditorDidMount = useCallback(
×
57
    (editor) => {
×
58
      editorRef.current = editor
×
NEW
59
      if (yText && awareness) {
×
NEW
60
        new MonacoBinding(
×
NEW
61
          yText,
×
NEW
62
          editor.getModel(),
×
NEW
63
          new Set([editor]),
×
NEW
64
          awareness
×
NEW
65
        )
×
NEW
66
      }
×
67
    },
×
68
    [yText, awareness]
×
69
  )
×
70

NEW
71
  const handleEditorDidMount = useCallback((editor) => {
×
NEW
72
    editorRef.current = editor
×
NEW
73
  }, [])
×
74

NEW
75
  let timeoutId
×
76
  useEffect(() => {
×
NEW
77
    if (yText) {
×
NEW
78
      yText.observe(function () {
×
NEW
79
        dispatch({
×
NEW
80
          type: 'UPDATE_ARTICLE_WORKING_COPY_STATUS',
×
NEW
81
          status: 'syncing',
×
NEW
82
        })
×
NEW
83
        if (timeoutId) {
×
NEW
84
          clearTimeout(timeoutId)
×
NEW
85
        }
×
NEW
86
        timeoutId = setTimeout(() => {
×
NEW
87
          dispatch({
×
NEW
88
            type: 'UPDATE_ARTICLE_WORKING_COPY_STATUS',
×
NEW
89
            status: 'synced',
×
NEW
90
          })
×
NEW
91
        }, 4000)
×
92

NEW
93
        handleUpdateArticleStructureAndStats({ text: yText.toString() })
×
NEW
94
      })
×
95
    }
×
NEW
96
  }, [yText])
×
97

NEW
98
  useEffect(() => {
×
NEW
99
    if (version) {
×
NEW
100
      dispatch({ type: 'UPDATE_ARTICLE_STATS', md: version.md })
×
NEW
101
      dispatch({ type: 'UPDATE_ARTICLE_STRUCTURE', md: version.md })
×
102
    }
×
NEW
103
  }, [version])
×
104

105
  useEffect(() => {
×
106
    const line = editorCursorPosition.lineNumber
×
107
    const editor = editorRef.current
×
108
    editor?.focus()
×
109
    const endOfLineColumn = editor?.getModel()?.getLineMaxColumn(line + 1)
×
110
    editor?.setPosition({ lineNumber: line + 1, column: endOfLineColumn })
×
111
    editor?.revealLineNearTop(line + 1, 1) // smooth
×
112
  }, [editorRef, editorCursorPosition])
×
113

NEW
114
  if (!yText && !version) {
×
NEW
115
    return <Loading />
×
NEW
116
  }
×
117

NEW
118
  if (isLoading) {
×
119
    return <Loading />
×
120
  }
×
121

NEW
122
  if (error) {
×
NEW
123
    return <Alert message={error.message} />
×
NEW
124
  }
×
125

126
  return (
×
127
    <>
×
128
      <style>{dynamicStyles}</style>
×
NEW
129
      <CollaborativeEditorStatus versionId={versionId} />
×
130
      <div className={styles.inlineStatus}>
×
131
        <CollaborativeEditorWebSocketStatus status={websocketStatus} />
×
132
      </div>
×
NEW
133
      {version && (
×
NEW
134
        <Editor
×
NEW
135
          width={'100%'}
×
NEW
136
          height={'auto'}
×
NEW
137
          value={version.md}
×
NEW
138
          options={options}
×
NEW
139
          className={styles.editor}
×
NEW
140
          defaultLanguage="markdown"
×
NEW
141
          onMount={handleEditorDidMount}
×
NEW
142
        />
×
143
      )}
NEW
144
      <div
×
NEW
145
        className={clsx(styles.collaborativeEditor, versionId && styles.hidden)}
×
146
      >
NEW
147
        <Editor
×
NEW
148
          width={'100%'}
×
NEW
149
          height={'auto'}
×
NEW
150
          options={options}
×
NEW
151
          className={styles.editor}
×
NEW
152
          defaultLanguage="markdown"
×
NEW
153
          onMount={handleCollaborativeEditorDidMount}
×
NEW
154
        />
×
NEW
155
      </div>
×
UNCOV
156
    </>
×
157
  )
158
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc