[web] Compile time warning split test (#8813)

* Implement compile time warning split test

* Add i18n for compile time warning

* Memoize CompileTimeWarning component

* Reuse startFreeTrial() function from account-upgrade

* Improve usage of react bootstrap native properties

* Cleanup compile-time-warning

* Move all logic into CompileTimeWarning component

* Only show compile time warning to project owners

* Handle compile time warning in detached mode and while switching layout

GitOrigin-RevId: 4fc40acfc113f91c81a6744c14c0f8b3ef766e39
This commit is contained in:
Alexandre Bourdin 2022-07-13 10:33:47 +02:00 committed by Copybot
parent 7757d8d2ad
commit d501bc4820
9 changed files with 184 additions and 1 deletions

View file

@ -949,6 +949,25 @@ const ProjectController = {
} }
) )
}, },
compileTimeWarningAssignment: [
'user',
(results, cb) => {
if (results.user?.features?.compileTimeout <= 60) {
SplitTestHandler.getAssignment(
req,
res,
'compile-time-warning',
{},
() => {
// do not fail editor load if assignment fails
cb()
}
)
} else {
cb()
}
},
],
}, },
( (
err, err,

View file

@ -10,6 +10,7 @@
"also": "", "also": "",
"anyone_with_link_can_edit": "", "anyone_with_link_can_edit": "",
"anyone_with_link_can_view": "", "anyone_with_link_can_view": "",
"approaching_compile_timeout_limit_upgrade_for_more_compile_time": "<0></0>",
"are_you_still_at": "", "are_you_still_at": "",
"ask_proj_owner_to_upgrade_for_git_bridge": "", "ask_proj_owner_to_upgrade_for_git_bridge": "",
"ask_proj_owner_to_upgrade_for_longer_compiles": "", "ask_proj_owner_to_upgrade_for_longer_compiles": "",

View file

@ -0,0 +1,62 @@
import { memo, useCallback, useEffect } from 'react'
import { Button } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import * as eventTracking from '../../../infrastructure/event-tracking'
import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
import { startFreeTrial } from '../../../main/account-upgrade'
function CompileTimeWarning() {
const { t } = useTranslation()
const { showCompileTimeWarning, setShowCompileTimeWarning } =
useDetachCompileContext()
useEffect(() => {
if (showCompileTimeWarning) {
eventTracking.sendMB('compile-time-warning-displayed', {})
}
}, [showCompileTimeWarning])
const closeWarning = () => {
eventTracking.sendMB('compile-time-warning-dismissed', {})
setShowCompileTimeWarning(false)
}
const handleUpgradeClick = useCallback(event => {
event.preventDefault()
startFreeTrial('compile-time-warning')
}, [])
if (!showCompileTimeWarning) {
return null
}
return (
<div className="alert alert-success compile-time-warning" role="alert">
<Button
className="close"
data-dismiss="alert"
aria-label="Close"
onClick={closeWarning}
>
<span aria-hidden="true">&times;</span>
</Button>
<div>
<div className="warning-text">
<Trans
i18nKey="approaching_compile_timeout_limit_upgrade_for_more_compile_time"
// eslint-disable-next-line react/jsx-key
components={[<strong style={{ display: 'inline-block' }} />]}
/>
</div>
<div className="upgrade-prompt">
<Button bsStyle="primary" bsSize="sm" onClick={handleUpgradeClick}>
{t('upgrade')}
</Button>
</div>
</div>
</div>
)
}
export default memo(CompileTimeWarning)

View file

@ -1,5 +1,6 @@
import { memo, Suspense } from 'react' import { memo, Suspense } from 'react'
import classNames from 'classnames' import classNames from 'classnames'
import CompileTimeWarning from './compile-time-warning'
import PdfLogsViewer from './pdf-logs-viewer' import PdfLogsViewer from './pdf-logs-viewer'
import PdfViewer from './pdf-viewer' import PdfViewer from './pdf-viewer'
import LoadingSpinner from '../../../shared/components/loading-spinner' import LoadingSpinner from '../../../shared/components/loading-spinner'
@ -15,6 +16,7 @@ function PdfPreviewPane() {
return ( return (
<div className={classes}> <div className={classes}>
<PdfHybridPreviewToolbar /> <PdfHybridPreviewToolbar />
<CompileTimeWarning />
<Suspense fallback={<LoadingPreview />}> <Suspense fallback={<LoadingPreview />}>
<div className="pdf-viewer"> <div className="pdf-viewer">
<PdfViewer /> <PdfViewer />

View file

@ -48,11 +48,13 @@ export function DetachCompileProvider({ children }) {
setHasLintingError: _setHasLintingError, setHasLintingError: _setHasLintingError,
setHighlights: _setHighlights, setHighlights: _setHighlights,
setPosition: _setPosition, setPosition: _setPosition,
setShowCompileTimeWarning: _setShowCompileTimeWarning,
setShowLogs: _setShowLogs, setShowLogs: _setShowLogs,
toggleLogs: _toggleLogs, toggleLogs: _toggleLogs,
setStopOnFirstError: _setStopOnFirstError, setStopOnFirstError: _setStopOnFirstError,
setStopOnValidationError: _setStopOnValidationError, setStopOnValidationError: _setStopOnValidationError,
showLogs: _showLogs, showLogs: _showLogs,
showCompileTimeWarning: _showCompileTimeWarning,
showFasterCompilesFeedbackUI: _showFasterCompilesFeedbackUI, showFasterCompilesFeedbackUI: _showFasterCompilesFeedbackUI,
stopOnFirstError: _stopOnFirstError, stopOnFirstError: _stopOnFirstError,
stopOnValidationError: _stopOnValidationError, stopOnValidationError: _stopOnValidationError,
@ -185,6 +187,12 @@ export function DetachCompileProvider({ children }) {
'detacher', 'detacher',
'detached' 'detached'
) )
const [showCompileTimeWarning] = useDetachStateWatcher(
'showCompileTimeWarning',
_showCompileTimeWarning,
'detacher',
'detached'
)
const [showLogs] = useDetachStateWatcher( const [showLogs] = useDetachStateWatcher(
'showLogs', 'showLogs',
_showLogs, _showLogs,
@ -276,6 +284,12 @@ export function DetachCompileProvider({ children }) {
'detacher', 'detacher',
'detached' 'detached'
) )
const setShowCompileTimeWarning = useDetachAction(
'setShowCompileTimeWarning',
_setShowCompileTimeWarning,
'detached',
'detacher'
)
const setShowLogs = useDetachAction( const setShowLogs = useDetachAction(
'setShowLogs', 'setShowLogs',
_setShowLogs, _setShowLogs,
@ -376,11 +390,13 @@ export function DetachCompileProvider({ children }) {
setHasLintingError, setHasLintingError,
setHighlights, setHighlights,
setPosition, setPosition,
setShowCompileTimeWarning,
setShowLogs, setShowLogs,
toggleLogs, toggleLogs,
setStopOnFirstError, setStopOnFirstError,
setStopOnValidationError, setStopOnValidationError,
showLogs, showLogs,
showCompileTimeWarning,
showFasterCompilesFeedbackUI, showFasterCompilesFeedbackUI,
startCompile, startCompile,
stopCompile, stopCompile,
@ -425,10 +441,12 @@ export function DetachCompileProvider({ children }) {
setHasLintingError, setHasLintingError,
setHighlights, setHighlights,
setPosition, setPosition,
setShowCompileTimeWarning,
setShowLogs, setShowLogs,
toggleLogs, toggleLogs,
setStopOnFirstError, setStopOnFirstError,
setStopOnValidationError, setStopOnValidationError,
showCompileTimeWarning,
showLogs, showLogs,
showFasterCompilesFeedbackUI, showFasterCompilesFeedbackUI,
startCompile, startCompile,

View file

@ -27,6 +27,9 @@ import { useIdeContext } from './ide-context'
import { useProjectContext } from './project-context' import { useProjectContext } from './project-context'
import { useEditorContext } from './editor-context' import { useEditorContext } from './editor-context'
import { buildFileList } from '../../features/pdf-preview/util/file-list' import { buildFileList } from '../../features/pdf-preview/util/file-list'
import { useSplitTestContext } from './split-test-context'
const ONE_DAY = 24 * 60 * 60 * 24 * 1000
export const LocalCompileContext = createContext() export const LocalCompileContext = createContext()
@ -57,10 +60,12 @@ export const CompileContextPropTypes = {
setHasLintingError: PropTypes.func.isRequired, // only for storybook setHasLintingError: PropTypes.func.isRequired, // only for storybook
setHighlights: PropTypes.func.isRequired, setHighlights: PropTypes.func.isRequired,
setPosition: PropTypes.func.isRequired, setPosition: PropTypes.func.isRequired,
setShowCompileTimeWarning: PropTypes.func.isRequired,
setShowLogs: PropTypes.func.isRequired, setShowLogs: PropTypes.func.isRequired,
toggleLogs: PropTypes.func.isRequired, toggleLogs: PropTypes.func.isRequired,
setStopOnFirstError: PropTypes.func.isRequired, setStopOnFirstError: PropTypes.func.isRequired,
setStopOnValidationError: PropTypes.func.isRequired, setStopOnValidationError: PropTypes.func.isRequired,
showCompileTimeWarning: PropTypes.bool.isRequired,
showLogs: PropTypes.bool.isRequired, showLogs: PropTypes.bool.isRequired,
showFasterCompilesFeedbackUI: PropTypes.bool.isRequired, showFasterCompilesFeedbackUI: PropTypes.bool.isRequired,
stopOnFirstError: PropTypes.bool.isRequired, stopOnFirstError: PropTypes.bool.isRequired,
@ -82,9 +87,21 @@ export function LocalCompileProvider({ children }) {
const { _id: projectId, rootDocId } = useProjectContext() const { _id: projectId, rootDocId } = useProjectContext()
const { splitTestVariants } = useSplitTestContext()
// whether a compile is in progress // whether a compile is in progress
const [compiling, setCompiling] = useState(false) const [compiling, setCompiling] = useState(false)
// whether to show the compile time warning
const [showCompileTimeWarning, setShowCompileTimeWarning] = useState(false)
// the last time the compile time warning was displayed
const [lastDisplay, setLastDisplay] = usePersistedState(
'compile-time-warning-displayed-at',
0,
true
)
// the log entries parsed from the compile output log // the log entries parsed from the compile output log
const [logEntries, setLogEntries] = useScopeValueSetterOnly('pdf.logEntries') const [logEntries, setLogEntries] = useScopeValueSetterOnly('pdf.logEntries')
@ -264,6 +281,31 @@ export function LocalCompileProvider({ children }) {
} }
}, [compiledOnce, currentDoc, compiler]) }, [compiledOnce, currentDoc, compiler])
useEffect(() => {
const compileTimeWarningEnabled =
splitTestVariants['compile-time-warning'] === 'show-upgrade-prompt'
if (compileTimeWarningEnabled && compiling && isProjectOwner) {
const timeout = window.setTimeout(() => {
if (lastDisplay && Date.now() - lastDisplay < ONE_DAY) {
return
}
setShowCompileTimeWarning(true)
setLastDisplay(Date.now())
}, 30000)
return () => {
window.clearTimeout(timeout)
}
}
}, [
compiling,
isProjectOwner,
lastDisplay,
setLastDisplay,
splitTestVariants,
])
// handle the data returned from a compile request // handle the data returned from a compile request
// note: this should _only_ run when `data` changes, // note: this should _only_ run when `data` changes,
// the other dependencies must all be static // the other dependencies must all be static
@ -522,6 +564,8 @@ export function LocalCompileProvider({ children }) {
setHasLintingError, // only for stories setHasLintingError, // only for stories
setHighlights, setHighlights,
setPosition, setPosition,
showCompileTimeWarning,
setShowCompileTimeWarning,
setShowLogs, setShowLogs,
toggleLogs, toggleLogs,
setStopOnFirstError, setStopOnFirstError,
@ -570,8 +614,10 @@ export function LocalCompileProvider({ children }) {
setHasLintingError, // only for stories setHasLintingError, // only for stories
setHighlights, setHighlights,
setPosition, setPosition,
setShowCompileTimeWarning,
setStopOnFirstError, setStopOnFirstError,
setStopOnValidationError, setStopOnValidationError,
showCompileTimeWarning,
showLogs, showLogs,
showFasterCompilesFeedbackUI, showFasterCompilesFeedbackUI,
startCompile, startCompile,

View file

@ -743,3 +743,36 @@ CodeMirror
.editor-container { .editor-container {
overflow: visible !important; overflow: visible !important;
} }
.compile-time-warning {
position: absolute;
right: @margin-sm;
top: @margin-xl;
padding: @padding-sm;
background-color: @ol-green;
width: 400px;
z-index: @zindex-popover;
box-shadow: 5px 5px 6px rgba(0, 0, 0, 0.3);
.warning-text {
max-width: 300px;
padding-right: @alert-padding;
display: inline-block;
font-size: @font-size-small;
}
.upgrade-prompt {
display: inline-block;
position: absolute;
height: 100%;
top: @margin-lg;
right: 32px;
}
button.btn {
background-color: @ol-darker-green;
&.close {
background-color: @ol-green;
}
}
}

View file

@ -22,6 +22,7 @@
@ol-green: #138a07; @ol-green: #138a07;
@ol-type-green: #107206; @ol-type-green: #107206;
@ol-dark-green: #004a0e; @ol-dark-green: #004a0e;
@ol-darker-green: #083c03;
@ol-blue: #3e70bb; @ol-blue: #3e70bb;
@ol-dark-blue: #2857a1; @ol-dark-blue: #2857a1;
@ol-red: #c9453e; @ol-red: #c9453e;

View file

@ -1792,5 +1792,6 @@
"get_most_subscription_by_checking_premium_features": "Get the most out of your __appName__ subscription by checking out the list of <0>__appName__s premium features</0>.", "get_most_subscription_by_checking_premium_features": "Get the most out of your __appName__ subscription by checking out the list of <0>__appName__s premium features</0>.",
"would_you_like_to_see_a_university_subscription": "Would you like to see a university-wide __appName__ subscription at your university?", "would_you_like_to_see_a_university_subscription": "Would you like to see a university-wide __appName__ subscription at your university?",
"student_and_faculty_support_make_difference": "Student and faculty support make a difference! We can share this information with our contacts at your university when discussing an Overleaf institutional account.", "student_and_faculty_support_make_difference": "Student and faculty support make a difference! We can share this information with our contacts at your university when discussing an Overleaf institutional account.",
"show_your_support": "Show your support" "show_your_support": "Show your support",
"approaching_compile_timeout_limit_upgrade_for_more_compile_time": "You are approaching your compile timeout limit. Upgrade to <strong>Overleaf Premium</strong> for <0>4x more</0> compile time."
} }