Implement grammarly warning alert on cm6 users

GitOrigin-RevId: ed272bbc385faa69811ec1891075906cdca1c984
This commit is contained in:
M Fahru 2022-11-15 13:10:05 -05:00 committed by Copybot
parent 87b844aa15
commit d70e0b1c0e
6 changed files with 290 additions and 0 deletions

View file

@ -65,6 +65,8 @@ block content
span.sr-only #{translate("close")}
.system-message-content(ng-bind-html="htmlContent")
grammarly-warning()
include ./editor/main
script(type="text/ng-template", id="genericMessageModalTemplate")

View file

@ -0,0 +1,65 @@
import { useCallback, useEffect, useState } from 'react'
import { Button } from 'react-bootstrap'
import customLocalStorage from '../../../infrastructure/local-storage'
import useScopeValue from '../../../shared/hooks/use-scope-value'
import grammarlyExtensionPresent from '../../../shared/utils/grammarly'
import getMeta from '../../../utils/meta'
export default function GrammarlyWarning() {
const [show, setShow] = useState(false)
const [newSourceEditor] = useScopeValue('editor.newSourceEditor')
const [showRichText] = useScopeValue('editor.showRichText')
const grammarly = grammarlyExtensionPresent()
const hasDismissedGrammarlyWarning = customLocalStorage.getItem(
'editor.has_dismissed_grammarly_warning'
)
useEffect(() => {
const showGrammarlyWarning =
!hasDismissedGrammarlyWarning &&
grammarly &&
newSourceEditor &&
!showRichText
if (showGrammarlyWarning) {
setShow(true)
}
}, [grammarly, hasDismissedGrammarlyWarning, newSourceEditor, showRichText])
const handleClose = useCallback(() => {
setShow(false)
customLocalStorage.setItem('editor.has_dismissed_grammarly_warning', true)
}, [])
if (!getMeta('ol-showNewSourceEditorOption')) {
return null
}
if (!show) {
return null
}
return (
<div className="alert alert-info grammarly-warning" role="alert">
<Button
className="close"
data-dismiss="alert"
aria-label="Close"
onClick={handleClose}
>
<span aria-hidden="true">&times;</span>
</Button>
<div className="warning-content">
A browser extension, for example Grammarly, may be slowing down
Overleaf.{' '}
<a
className="warning-link"
href="/learn/how-to/Use_Grammarly_with_Overleaf#Performance_issues"
target="_blank"
>
Find out how to avoid this
</a>
</div>
</div>
)
}

View file

@ -0,0 +1,9 @@
import App from '../../../base'
import { react2angular } from 'react2angular'
import { rootContext } from '../../../shared/context/root-context'
import GrammarlyWarning from '../components/grammarly-warning'
App.component(
'grammarlyWarning',
react2angular(rootContext.use(GrammarlyWarning))
)

View file

@ -65,6 +65,7 @@ import './features/pdf-preview/controllers/pdf-preview-controller'
import './features/share-project-modal/controllers/react-share-project-modal-controller'
import './features/source-editor/controllers/editor-switch-controller'
import './features/source-editor/controllers/cm6-switch-away-survey-controller'
import './features/source-editor/controllers/grammarly-warning-controller'
import { cleanupServiceWorker } from './utils/service-worker-cleanup'
import { reportCM6Perf } from './infrastructure/cm6-performance'

View file

@ -827,3 +827,32 @@ CodeMirror
font-size: @font-size-small;
}
}
.grammarly-warning {
width: 500px;
&.alert.alert-info {
padding: @padding-sm;
}
.btn.close {
background-color: transparent;
color: @white;
opacity: 1;
}
.warning-content {
padding-right: @alert-padding;
font-size: @font-size-small;
margin-right: 32px;
.warning-link {
font-weight: 700;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}

View file

@ -0,0 +1,184 @@
import sinon from 'sinon'
import fetchMock from 'fetch-mock'
import { expect } from 'chai'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import { renderWithEditorContext } from '../../../helpers/render-with-context'
import GrammarlyWarning from '../../../../../frontend/js/features/source-editor/components/grammarly-warning'
import * as grammarlyModule from '../../../../../frontend/js/shared/utils/grammarly'
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
describe('<GrammarlyWarning />', function () {
let grammarlyStub
before(function () {
window.metaAttributesCache = new Map()
})
beforeEach(function () {
grammarlyStub = sinon.stub(grammarlyModule, 'default')
})
afterEach(function () {
window.metaAttributesCache = new Map()
grammarlyStub.restore()
fetchMock.reset()
localStorage.clear()
})
it('shows warning when grammarly is available', async function () {
grammarlyStub.returns(true)
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
renderWithEditorContext(<GrammarlyWarning />, {
scope: {
editor: {
newSourceEditor: true,
},
},
})
await screen.findByText(
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
)
await screen.findByRole('button', { name: 'Close' })
await screen.findByRole('link', { name: 'Find out how to avoid this' })
})
it('does not show warning when grammarly is not available', async function () {
grammarlyStub.returns(false)
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
renderWithEditorContext(<GrammarlyWarning />, {
scope: {
editor: {
newSourceEditor: true,
},
},
})
await waitFor(() => {
expect(
screen.queryByText(
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
)
).to.not.exist
})
})
it('does not show warning when user has dismissed the warning', async function () {
grammarlyStub.returns(true)
localStorage.setItem('editor.has_dismissed_grammarly_warning', true)
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
renderWithEditorContext(<GrammarlyWarning />, {
scope: {
editor: {
newSourceEditor: true,
},
},
})
await waitFor(() => {
expect(
screen.queryByText(
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
)
).to.not.exist
})
})
it('does not show warning when user does not have CM6', async function () {
grammarlyStub.returns(true)
window.metaAttributesCache.set('ol-showNewSourceEditorOption', false)
renderWithEditorContext(<GrammarlyWarning />)
await waitFor(() => {
expect(
screen.queryByText(
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
)
).to.not.exist
})
})
it('does not show warning when user have ace as their preference', async function () {
grammarlyStub.returns(true)
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
renderWithEditorContext(<GrammarlyWarning />, {
scope: {
editor: {
newSourceEditor: false,
},
},
})
await waitFor(() => {
expect(
screen.queryByText(
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
)
).to.not.exist
})
})
it('does not show warning when user have rich text as their preference', async function () {
grammarlyStub.returns(true)
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
renderWithEditorContext(<GrammarlyWarning />, {
scope: {
editor: {
newSourceEditor: true,
showRichText: true,
},
},
})
await waitFor(() => {
expect(
screen.queryByText(
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
)
).to.not.exist
})
})
it('hides warning if close button is pressed', async function () {
grammarlyStub.returns(true)
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
renderWithEditorContext(<GrammarlyWarning />, {
scope: {
editor: {
newSourceEditor: true,
},
},
})
const warningText =
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
await screen.findByText(warningText)
const hasDismissedGrammarlyWarning = localStorage.getItem(
'editor.has_dismissed_grammarly_warning'
)
expect(hasDismissedGrammarlyWarning).to.equal(null)
const closeButton = screen.getByRole('button', { name: 'Close' })
fireEvent.click(closeButton)
expect(screen.queryByText(warningText)).to.not.exist
await waitFor(() => {
const hasDismissedGrammarlyWarning = localStorage.getItem(
'editor.has_dismissed_grammarly_warning'
)
expect(hasDismissedGrammarlyWarning).to.equal(true)
})
})
})