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

EcrituresNumeriques / stylo / 12925854411

23 Jan 2025 09:11AM UTC coverage: 25.831% (-4.7%) from 30.523%
12925854411

push

github

web-flow
Merge pull request #1192 from EcrituresNumeriques/feat/vite6

322 of 518 branches covered (62.16%)

Branch coverage included in aggregate %.

3448 of 14077 relevant lines covered (24.49%)

1.66 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, useCallback } from 'react'
×
2
import { shallowEqual, useSelector } from 'react-redux'
×
3
import { Link } from 'react-router-dom'
×
4
import {
×
5
  AlertCircle,
6
  AlignLeft,
7
  Check,
8
  Edit3,
9
  Eye,
10
  Loader,
11
  Printer,
12
} from 'react-feather'
13
import { useTranslation } from 'react-i18next'
×
14
import TimeAgo from '../TimeAgo.jsx'
×
15

16
import styles from './workingVersion.module.scss'
×
17
import buttonStyles from '../button.module.scss'
×
18
import Button from '../Button'
×
19
import Modal from '../Modal'
×
20
import Export from '../Export'
×
21

22
const ONE_MINUTE = 60000
×
23

24
export function ArticleVersion({ version }) {
×
25
  const { t } = useTranslation()
×
26

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

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

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

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

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

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

98
      {state === 'saved' && <TimeAgo date={isoString} />}
×
99
    </>
×
100
  )
101
}
×
102

103
export default function WorkingVersion({
×
104
  articleInfos,
×
105
  live,
×
106
  selectedVersion,
×
107
  mode,
×
108
}) {
×
109
  const [exporting, setExporting] = useState(false)
×
110
  const workingArticle = useSelector(
×
111
    (state) => state.workingArticle,
×
112
    shallowEqual
×
113
  )
×
114
  const cancelExport = useCallback(() => setExporting(false), [])
×
115
  const openExport = useCallback(() => setExporting(true), [])
×
116
  const { t } = useTranslation()
×
117

118
  const previewUrl = selectedVersion
×
119
    ? `/article/${articleInfos._id}/version/${selectedVersion}/preview`
×
120
    : `/article/${articleInfos._id}/preview`
×
121
  const articleOwnerAndContributors = [
×
122
    articleInfos.owner.displayName,
×
123
    ...articleInfos.contributors.map(
×
124
      (contributor) => contributor.user.displayName
×
125
    ),
×
126
  ]
×
127

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