• 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/Write/WorkingVersion.jsx
1
import React, { useEffect, useMemo, useState } from 'react'
×
2
import {
×
3
  AlertCircle,
4
  AlignLeft,
5
  Check,
6
  Edit3,
7
  Eye,
8
  Loader,
9
  Printer,
10
} from 'lucide-react'
11
import { useTranslation } from 'react-i18next'
×
12
import { shallowEqual, useSelector } from 'react-redux'
×
13
import { Link } from 'react-router-dom'
×
14
import { toBibtex } from '../../helpers/bibtex.js'
×
15
import { useModal } from '../../hooks/modal.js'
×
16
import Button from '../Button'
×
17
import buttonStyles from '../button.module.scss'
×
18
import Export from '../Export'
×
19
import Modal from '../Modal'
×
20
import TimeAgo from '../TimeAgo.jsx'
×
21

22
import styles from './workingVersion.module.scss'
×
23

24
const ONE_MINUTE = 60000
×
25

26
export function ArticleVersion({ version }) {
×
27
  const { t } = useTranslation()
×
28

29
  return (
×
30
    <span>
×
31
      {!version && <span>{t('workingVersion.spanWorkingCopy.text')}</span>}
×
32
      {version && version.message && (
×
33
        <span>
×
34
          <span className={styles.versionLabel}>{version.message}</span>
×
35
          <span className={styles.versionNumber}>
×
36
            v{version.major}.{version.minor}
×
37
          </span>
×
38
        </span>
×
39
      )}
40
      {version && !version.message && (
×
41
        <span>
×
42
          <span className={styles.versionNumber}>
×
43
            v{version.major}.{version.minor}
×
44
          </span>
×
45
        </span>
×
46
      )}
47
    </span>
×
48
  )
49
}
×
50

51
export function ArticleSaveState({ state, updatedAt, stateMessage }) {
×
52
  const [lastRefreshedAt, setLastRefresh] = useState(Date.now())
×
53
  const { t } = useTranslation()
×
54

55
  const stateUiProps = {
×
56
    saved: {
×
57
      text: t('workingVersion.stateUiProps.savedText'),
×
58
      icon: <Check />,
×
59
      style: styles.savedIndicator,
×
60
    },
×
61
    saving: {
×
62
      text: t('workingVersion.stateUiProps.savingText'),
×
63
      icon: <Loader />,
×
64
      style: styles.savingIndicator,
×
65
    },
×
66
    saveFailure: {
×
67
      text: t('workingVersion.stateUiProps.saveErrorText'),
×
68
      icon: <AlertCircle />,
×
69
      style: styles.failureIndicator,
×
70
    },
×
71
  }
×
72
  const stateUi = stateUiProps[state]
×
73

74
  const isoString = useMemo(
×
75
    () => new Date(updatedAt).toISOString(),
×
76
    [updatedAt, lastRefreshedAt]
×
77
  )
×
78

79
  useEffect(() => {
×
80
    const timer = setTimeout(
×
81
      () => setLastRefresh(Date.now() * 1000),
×
82
      ONE_MINUTE
×
83
    )
×
84
    return () => clearTimeout(timer)
×
85
  }, [])
×
86

87
  return (
×
88
    <>
×
89
      <span className={stateUi.style}>
×
90
        {state !== 'saved' && stateUi.icon}
×
91
        {state !== 'saveFailure' && stateUi.text}
×
92
        {state === 'saveFailure' && (
×
93
          <span>
×
94
            <strong>{stateUi.text}</strong>
×
95
            {stateMessage}
×
96
          </span>
×
97
        )}
98
      </span>
×
99

NEW
100
      {state === 'saved' && isoString && <TimeAgo date={isoString} />}
×
101
    </>
×
102
  )
103
}
×
104

105
export default function WorkingVersion({
×
106
  articleInfos,
×
107
  live,
×
108
  selectedVersion,
×
109
  mode,
×
110
}) {
×
111
  const workingArticle = useSelector(
×
112
    (state) => state.workingArticle,
×
113
    shallowEqual
×
114
  )
×
115
  const { t } = useTranslation()
×
116
  const exportModal = useModal()
×
117
  const previewUrl = selectedVersion
×
118
    ? `/article/${articleInfos._id}/version/${selectedVersion}/preview`
×
119
    : `/article/${articleInfos._id}/preview`
×
120
  const articleOwnerAndContributors = [
×
121
    articleInfos.owner.displayName,
×
122
    ...articleInfos.contributors.map(
×
123
      (contributor) => contributor.user.displayName
×
124
    ),
×
125
  ]
×
126

127
  return (
×
128
    <>
×
129
      <section className={styles.section}>
×
130
        <header className={styles.header}>
×
131
          <h1 className={styles.title}>
×
132
            <AlignLeft />
×
133
            {articleInfos.title}
×
134
          </h1>
×
135
        </header>
×
136
        <Modal
×
137
          {...exportModal.bindings}
×
138
          title={
×
139
            <>
×
140
              <Printer /> Export
×
141
            </>
×
142
          }
143
        >
144
          <Export
×
145
            articleVersionId={selectedVersion}
×
146
            articleId={articleInfos._id}
×
147
            bib={toBibtex(workingArticle.bibliography.entries.slice(0, 3))}
×
148
            name={articleInfos.title}
×
149
            onCancel={() => exportModal.close()}
×
150
          />
×
151
        </Modal>
×
152
        <ul className={styles.actions}>
×
153
          {articleInfos.preview.stylesheet && (
×
154
            <>
×
155
              <li>
×
156
                <Link
×
157
                  to={`/article/${articleInfos._id}`}
×
158
                  className={
×
159
                    mode === 'write'
×
160
                      ? buttonStyles.primaryDisabled
×
161
                      : buttonStyles.secondary
×
162
                  }
163
                  title="Edit article"
×
164
                >
165
                  <Edit3 /> Edit
×
166
                </Link>
×
167
              </li>
×
168
              <li>
×
169
                <Link
×
170
                  to={`/article/${articleInfos._id}/preview`}
×
171
                  className={
×
172
                    mode === 'preview'
×
173
                      ? buttonStyles.primaryDisabled
×
174
                      : buttonStyles.secondary
×
175
                  }
176
                  title="Preview article"
×
177
                >
178
                  <Eye />
×
179
                  {articleInfos.preview.stylesheet ? (
×
180
                    'Paged.js'
×
181
                  ) : (
182
                    <abbr title="HyperText Markup Language">HTML</abbr>
×
183
                  )}
×
184
                  &nbsp;Preview
185
                </Link>
×
186
              </li>
×
187
            </>
×
188
          )}
189
          <li>
×
190
            <Button
×
191
              icon
×
192
              title={t('write.title.buttonExport')}
×
193
              onClick={() => exportModal.show()}
×
194
            >
195
              <Printer />
×
196
            </Button>
×
197
          </li>
×
198
          <li>
×
199
            <Link
×
200
              to={previewUrl}
×
201
              title={t('write.title.buttonPreview')}
×
202
              target="_blank"
×
203
              rel="noopener noreferrer"
×
204
              className={buttonStyles.icon}
×
205
            >
206
              <Eye />
×
207
            </Link>
×
208
          </li>
×
209
        </ul>
×
210
      </section>
×
211
      <section>
×
212
        <div className={styles.meta}>
×
213
          <ul className={styles.byLine}>
×
214
            <li className={styles.owners}>
×
215
              {t('workingVersion.by.text')}{' '}
×
216
              {articleOwnerAndContributors.join(', ')}
×
217
            </li>
×
218
            <li className={styles.version}>
×
219
              <ArticleVersion version={live.version} />
×
220
            </li>
×
221
            {!live.version && (
×
222
              <li className={styles.lastSaved}>
×
223
                <ArticleSaveState
×
224
                  state={workingArticle.state}
×
225
                  updatedAt={workingArticle.updatedAt}
×
226
                  stateMessage={workingArticle.stateMessage}
×
227
                />
×
228
              </li>
×
229
            )}
230
          </ul>
×
231
        </div>
×
232
      </section>
×
233
    </>
×
234
  )
235
}
×
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