mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Implement test to reduce compile timeout to 20 seconds (#14705)
Compile timeout reduction to 20s for treatment users Co-authored-by: Rebeka <rebeka.dekany@overleaf.com> GitOrigin-RevId: 54f70fe4b1fc631cef966deb0c1d28c904dd3a44
This commit is contained in:
parent
d04a1d3767
commit
31cb9e336b
22 changed files with 838 additions and 8 deletions
|
@ -114,6 +114,15 @@ module.exports = CompileController = {
|
|||
stopOnFirstError,
|
||||
}
|
||||
|
||||
// temporary override to force the new compile timeout
|
||||
const forceNewCompileTimeout = req.query.force_new_compile_timeout
|
||||
if (
|
||||
forceNewCompileTimeout === 'active' ||
|
||||
forceNewCompileTimeout === 'changing'
|
||||
) {
|
||||
options.forceNewCompileTimeout = forceNewCompileTimeout
|
||||
}
|
||||
|
||||
if (req.body.rootDoc_id) {
|
||||
options.rootDoc_id = req.body.rootDoc_id
|
||||
} else if (
|
||||
|
|
|
@ -11,7 +11,11 @@ const { RateLimiter } = require('../../infrastructure/RateLimiter')
|
|||
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
|
||||
const { getAnalyticsIdFromMongoUser } = require('../Analytics/AnalyticsHelper')
|
||||
|
||||
const NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF = new Date('2023-09-18T11:00:00.000Z')
|
||||
|
||||
module.exports = CompileManager = {
|
||||
NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF,
|
||||
|
||||
compile(projectId, userId, options = {}, _callback) {
|
||||
const timer = new Metrics.Timer('editor.compile')
|
||||
const callback = function (...args) {
|
||||
|
@ -54,6 +58,16 @@ module.exports = CompileManager = {
|
|||
const value = limits[key]
|
||||
options[key] = value
|
||||
}
|
||||
if (options.timeout !== 20) {
|
||||
// temporary override to force the new compile timeout
|
||||
if (options.forceNewCompileTimeout === 'active') {
|
||||
options.timeout = 20
|
||||
} else if (
|
||||
options.forceNewCompileTimeout === 'changing'
|
||||
) {
|
||||
options.timeout = 60
|
||||
}
|
||||
}
|
||||
// Put a lower limit on autocompiles for free users, based on compileGroup
|
||||
CompileManager._checkCompileGroupAutoCompileLimit(
|
||||
options.isAutoCompile,
|
||||
|
@ -149,6 +163,7 @@ module.exports = CompileManager = {
|
|||
betaProgram: 1,
|
||||
features: 1,
|
||||
splitTests: 1,
|
||||
signUpDate: 1, // for compile-timeout-20s
|
||||
},
|
||||
function (err, owner) {
|
||||
if (err) {
|
||||
|
@ -176,7 +191,28 @@ module.exports = CompileManager = {
|
|||
limits.compileBackendClass = compileBackendClass
|
||||
limits.showFasterCompilesFeedbackUI =
|
||||
showFasterCompilesFeedbackUI
|
||||
callback(null, limits)
|
||||
if (compileBackendClass === 'n2d' && limits.timeout <= 60) {
|
||||
// project owners with faster compiles but with <= 60 compile timeout (default)
|
||||
// will have a 20s compile timeout
|
||||
// The compile-timeout-20s split test exists to enable a gradual rollout
|
||||
SplitTestHandler.getAssignmentForMongoUser(
|
||||
owner,
|
||||
'compile-timeout-20s',
|
||||
(err, assignment) => {
|
||||
if (err) return callback(err)
|
||||
if (assignment?.variant === '20s') {
|
||||
if (
|
||||
owner.signUpDate > NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF
|
||||
) {
|
||||
limits.timeout = 20
|
||||
}
|
||||
}
|
||||
callback(null, limits)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
callback(null, limits)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@ const Errors = require('../Errors/Errors')
|
|||
const DocstoreManager = require('../Docstore/DocstoreManager')
|
||||
const logger = require('@overleaf/logger')
|
||||
const { expressify } = require('../../util/promises')
|
||||
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
|
||||
const {
|
||||
NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF,
|
||||
} = require('../Compile/CompileManager')
|
||||
|
||||
module.exports = {
|
||||
joinProject: expressify(joinProject),
|
||||
|
@ -67,6 +71,30 @@ async function joinProject(req, res, next) {
|
|||
if (!project) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
// Compile timeout 20s test
|
||||
if (project.features?.compileTimeout <= 60) {
|
||||
const compileAssignment =
|
||||
await SplitTestHandler.promises.getAssignmentForMongoUser(
|
||||
project.owner._id,
|
||||
'compile-backend-class-n2d'
|
||||
)
|
||||
if (compileAssignment?.variant === 'n2d') {
|
||||
const timeoutAssignment =
|
||||
await SplitTestHandler.promises.getAssignmentForMongoUser(
|
||||
project.owner._id,
|
||||
'compile-timeout-20s'
|
||||
)
|
||||
if (timeoutAssignment?.variant === '20s') {
|
||||
if (project.owner.signUpDate > NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF) {
|
||||
// New users will see a 10s warning and compile fail at 20s
|
||||
project.showNewCompileTimeoutUI = 'active'
|
||||
} else {
|
||||
// Older users aren't limited to 20s, but will see a notice of upcoming changes if compile >20s
|
||||
project.showNewCompileTimeoutUI = 'changing'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hide sensitive data if the user is restricted
|
||||
if (isRestrictedUser) {
|
||||
project.owner = { _id: project.owner._id }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"1_4_width": "",
|
||||
"3_4_width": "",
|
||||
"a_custom_size_has_been_used_in_the_latex_code": "",
|
||||
"a_fatal_compile_error_that_completely_blocks_compilation": "",
|
||||
"a_file_with_that_name_already_exists_and_will_be_overriden": "",
|
||||
"a_more_comprehensive_list_of_keyboard_shortcuts": "",
|
||||
"about_to_archive_projects": "",
|
||||
|
@ -63,6 +64,7 @@
|
|||
"also": "",
|
||||
"an_email_has_already_been_sent_to": "",
|
||||
"an_error_occurred_when_verifying_the_coupon_code": "",
|
||||
"and_you_can_upgrade_for_plenty_more_compile_time": "",
|
||||
"anonymous": "",
|
||||
"anyone_with_link_can_edit": "",
|
||||
"anyone_with_link_can_view": "",
|
||||
|
@ -102,6 +104,7 @@
|
|||
"browser": "",
|
||||
"bulk_accept_confirm": "",
|
||||
"bulk_reject_confirm": "",
|
||||
"but_note_that_free_compile_timeout_limit_will_be_reduced_on_x_date": "",
|
||||
"by_subscribing_you_agree_to_our_terms_of_service": "",
|
||||
"can_edit": "",
|
||||
"can_link_institution_email_acct_to_institution_acct": "",
|
||||
|
@ -167,6 +170,7 @@
|
|||
"comment": "",
|
||||
"commit": "",
|
||||
"common": "",
|
||||
"common_causes_of_compile_timeouts_are": "",
|
||||
"commons_plan_tooltip": "",
|
||||
"compact": "",
|
||||
"company_name": "",
|
||||
|
@ -177,6 +181,7 @@
|
|||
"compile_larger_projects": "",
|
||||
"compile_mode": "",
|
||||
"compile_terminated_by_user": "",
|
||||
"compile_timeout_will_be_reduced_project_exceeds_limit_speed_up_compile": "",
|
||||
"compiler": "",
|
||||
"compiling": "",
|
||||
"configure_sso": "",
|
||||
|
@ -311,6 +316,7 @@
|
|||
"emails_and_affiliations_explanation": "",
|
||||
"emails_and_affiliations_title": "",
|
||||
"enable_managed_users": "",
|
||||
"enable_stop_on_first_error_under_recompile_dropdown_menu": "",
|
||||
"enabled_managed_users_set_up_sso": "",
|
||||
"enabling": "",
|
||||
"end_of_document": "",
|
||||
|
@ -549,6 +555,7 @@
|
|||
"labs_program_already_participating": "",
|
||||
"labs_program_benefits": "<0></0>",
|
||||
"labs_program_not_participating": "",
|
||||
"large_or_high-resolution_images_taking_too_long": "",
|
||||
"last_active": "",
|
||||
"last_active_description": "",
|
||||
"last_modified": "",
|
||||
|
@ -565,6 +572,7 @@
|
|||
"learn_more": "",
|
||||
"learn_more_about_link_sharing": "",
|
||||
"learn_more_about_managed_users": "",
|
||||
"learn_more_about_other_causes_of_compile_timeouts": "",
|
||||
"leave": "",
|
||||
"leave_any_group_subscriptions": "",
|
||||
"leave_group": "",
|
||||
|
@ -737,6 +745,7 @@
|
|||
"organize_projects": "",
|
||||
"other_logs_and_files": "",
|
||||
"other_output_files": "",
|
||||
"other_ways_to_prevent_compile_timeouts": "",
|
||||
"output_file": "",
|
||||
"overall_theme": "",
|
||||
"overleaf": "",
|
||||
|
@ -789,6 +798,7 @@
|
|||
"please_select_a_project": "",
|
||||
"please_select_an_output_file": "",
|
||||
"please_set_main_file": "",
|
||||
"plus_additional_collaborators_document_history_track_changes_and_more": "",
|
||||
"plus_more": "",
|
||||
"plus_upgraded_accounts_receive": "",
|
||||
"postal_code": "",
|
||||
|
@ -841,6 +851,8 @@
|
|||
"react_history_tutorial_content": "",
|
||||
"react_history_tutorial_title": "",
|
||||
"reactivate_subscription": "",
|
||||
"read_more_about_fix_prevent_timeout": "",
|
||||
"read_more_about_free_compile_timeouts_servers": "",
|
||||
"read_only": "",
|
||||
"read_only_token": "",
|
||||
"read_write_token": "",
|
||||
|
@ -1022,8 +1034,10 @@
|
|||
"sso_config_prop_help_user_id": "",
|
||||
"sso_explanation": "",
|
||||
"sso_link_error": "",
|
||||
"start_a_free_trial": "",
|
||||
"start_by_adding_your_email": "",
|
||||
"start_free_trial": "",
|
||||
"start_free_trial_without_exclamation": "",
|
||||
"stop_compile": "",
|
||||
"stop_on_first_error": "",
|
||||
"stop_on_first_error_enabled_description": "",
|
||||
|
@ -1065,6 +1079,8 @@
|
|||
"tc_switch_everyone_tip": "",
|
||||
"tc_switch_guests_tip": "",
|
||||
"tc_switch_user_tip": "",
|
||||
"tell_the_project_owner_and_ask_them_to_upgrade": "",
|
||||
"tell_the_project_owner_to_upgrade_plan_for_more_compile_time": "",
|
||||
"template_approved_by_publisher": "",
|
||||
"template_description": "",
|
||||
"template_title_taken_from_project_title": "",
|
||||
|
@ -1088,6 +1104,8 @@
|
|||
"this_could_be_because_we_cant_support_some_elements_of_the_table": "",
|
||||
"this_field_is_required": "",
|
||||
"this_grants_access_to_features_2": "",
|
||||
"this_project_compiled_but_soon_might_not": "",
|
||||
"this_project_exceeded_compile_timeout_limit_on_free_plan": "",
|
||||
"this_project_is_public": "",
|
||||
"this_project_is_public_read_only": "",
|
||||
"this_project_will_appear_in_your_dropbox_folder_at": "",
|
||||
|
@ -1112,6 +1130,7 @@
|
|||
"too_many_requests": "",
|
||||
"too_many_search_results": "",
|
||||
"too_recently_compiled": "",
|
||||
"took_a_while": "",
|
||||
"toolbar_add_comment": "",
|
||||
"toolbar_bullet_list": "",
|
||||
"toolbar_choose_section_heading_level": "",
|
||||
|
@ -1203,7 +1222,9 @@
|
|||
"updating": "",
|
||||
"upgrade": "",
|
||||
"upgrade_cc_btn": "",
|
||||
"upgrade_for_12x_more_compile_time": "",
|
||||
"upgrade_for_longer_compiles": "",
|
||||
"upgrade_for_plenty_more_compile_time": "",
|
||||
"upgrade_now": "",
|
||||
"upgrade_to_get_feature": "",
|
||||
"upgrade_to_track_changes": "",
|
||||
|
@ -1275,10 +1296,12 @@
|
|||
"you_have_added_x_of_group_size_y": "",
|
||||
"you_have_been_invited_to_transfer_management_of_your_account": "",
|
||||
"you_have_been_invited_to_transfer_management_of_your_account_to": "",
|
||||
"you_may_be_able_to_prevent_a_compile_timeout": "",
|
||||
"you_will_be_able_to_reassign_subscription": "",
|
||||
"youll_get_best_results_in_visual_but_can_be_used_in_source": "",
|
||||
"your_affiliation_is_confirmed": "",
|
||||
"your_browser_does_not_support_this_feature": "",
|
||||
"your_compile_timed_out": "",
|
||||
"your_git_access_info": "",
|
||||
"your_git_access_info_bullet_1": "",
|
||||
"your_git_access_info_bullet_2": "",
|
||||
|
@ -1290,6 +1313,9 @@
|
|||
"your_new_plan": "",
|
||||
"your_plan": "",
|
||||
"your_plan_is_changing_at_term_end": "",
|
||||
"your_project_compiled_but_soon_might_not": "",
|
||||
"your_project_exceeded_compile_timeout_limit_on_free_plan": "",
|
||||
"your_project_near_compile_timeout_limit": "",
|
||||
"your_projects": "",
|
||||
"your_subscription": "",
|
||||
"your_subscription_has_expired": "",
|
||||
|
|
|
@ -29,7 +29,7 @@ function CompileTimeWarning() {
|
|||
return
|
||||
}
|
||||
setDisplayStatus({ lastDisplayTime: Date.now(), dismissed: false })
|
||||
eventTracking.sendMB('compile-time-warning-displayed', {})
|
||||
eventTracking.sendMB('compile-time-warning-displayed', { time: 30 })
|
||||
}
|
||||
}, [showCompileTimeWarning, displayStatus, setDisplayStatus])
|
||||
|
||||
|
@ -40,6 +40,7 @@ function CompileTimeWarning() {
|
|||
const closeWarning = useCallback(() => {
|
||||
eventTracking.sendMB('compile-time-warning-dismissed', {
|
||||
'time-since-displayed': getTimeSinceDisplayed(),
|
||||
time: 30,
|
||||
})
|
||||
setShowCompileTimeWarning(false)
|
||||
setDisplayStatus(displayStatus => ({ ...displayStatus, dismissed: true }))
|
||||
|
@ -48,6 +49,7 @@ function CompileTimeWarning() {
|
|||
const handleUpgradeClick = useCallback(() => {
|
||||
eventTracking.sendMB('compile-time-warning-upgrade-click', {
|
||||
'time-since-displayed': getTimeSinceDisplayed(),
|
||||
time: 30,
|
||||
})
|
||||
setShowCompileTimeWarning(false)
|
||||
setDisplayStatus(displayStatus => ({ ...displayStatus, dismissed: true }))
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
import { memo, useCallback, useEffect, useState, useMemo } from 'react'
|
||||
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||
import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
|
||||
import usePersistedState from '../../../shared/hooks/use-persisted-state'
|
||||
import Notification from '../../../shared/components/notification'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
|
||||
|
||||
function CompileTimeoutMessages() {
|
||||
const {
|
||||
showNewCompileTimeoutUI,
|
||||
isProjectOwner,
|
||||
deliveryLatencies,
|
||||
compiling,
|
||||
showLogs,
|
||||
error,
|
||||
} = useDetachCompileContext()
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [showWarning, setShowWarning] = useState(false)
|
||||
const [showChangingSoon, setShowChangingSoon] = useState(false)
|
||||
const [dismissedUntilWarning, setDismissedUntilWarning] = usePersistedState<
|
||||
Date | undefined
|
||||
>(`has-dismissed-10s-compile-time-warning-until`)
|
||||
|
||||
const segmentation = useMemo(() => {
|
||||
return {
|
||||
newCompileTimeout: showNewCompileTimeoutUI || 'control',
|
||||
isProjectOwner,
|
||||
}
|
||||
}, [showNewCompileTimeoutUI, isProjectOwner])
|
||||
|
||||
const handleNewCompile = useCallback(
|
||||
compileTime => {
|
||||
setShowWarning(false)
|
||||
setShowChangingSoon(false)
|
||||
if (compileTime > 20000) {
|
||||
if (showNewCompileTimeoutUI === 'changing') {
|
||||
setShowChangingSoon(true)
|
||||
eventTracking.sendMB('compile-time-warning-displayed', {
|
||||
time: 20,
|
||||
...segmentation,
|
||||
})
|
||||
}
|
||||
} else if (compileTime > 10000) {
|
||||
setShowChangingSoon(false)
|
||||
if (
|
||||
(isProjectOwner && showNewCompileTimeoutUI === 'active') ||
|
||||
showNewCompileTimeoutUI === 'changing'
|
||||
) {
|
||||
if (
|
||||
!dismissedUntilWarning ||
|
||||
new Date(dismissedUntilWarning) < new Date()
|
||||
) {
|
||||
setShowWarning(true)
|
||||
eventTracking.sendMB('compile-time-warning-displayed', {
|
||||
time: 10,
|
||||
...segmentation,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
eventTracking.sendMB('compile-time-warning-would-display', {
|
||||
time: 10,
|
||||
...segmentation,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
isProjectOwner,
|
||||
showNewCompileTimeoutUI,
|
||||
dismissedUntilWarning,
|
||||
segmentation,
|
||||
]
|
||||
)
|
||||
|
||||
const handleDismissWarning = useCallback(() => {
|
||||
eventTracking.sendMB('compile-time-warning-dismissed', {
|
||||
time: 10,
|
||||
...segmentation,
|
||||
})
|
||||
setShowWarning(false)
|
||||
const until = new Date()
|
||||
until.setDate(until.getDate() + 1) // 1 day
|
||||
setDismissedUntilWarning(until)
|
||||
}, [setDismissedUntilWarning, segmentation])
|
||||
|
||||
const handleDismissChangingSoon = useCallback(() => {
|
||||
eventTracking.sendMB('compile-time-warning-dismissed', {
|
||||
time: 20,
|
||||
...segmentation,
|
||||
})
|
||||
}, [segmentation])
|
||||
|
||||
useEffect(() => {
|
||||
if (compiling || error || showLogs) return
|
||||
window.sl_console.log(
|
||||
`[compileTimeout] compiledTimeServerE2E ${
|
||||
deliveryLatencies.compileTimeServerE2E / 1000
|
||||
}s`
|
||||
)
|
||||
handleNewCompile(deliveryLatencies.compileTimeServerE2E)
|
||||
}, [compiling, error, showLogs, deliveryLatencies, handleNewCompile])
|
||||
|
||||
if (!window.ExposedSettings.enableSubscriptions) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (compiling || error || showLogs) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!showWarning && !showChangingSoon) {
|
||||
return null
|
||||
}
|
||||
|
||||
function sendInfoClickEvent() {
|
||||
eventTracking.sendMB('paywall-info-click', {
|
||||
'paywall-type': 'compile-time-warning',
|
||||
content: 'blog',
|
||||
})
|
||||
}
|
||||
|
||||
const compileTimeoutBlogLinks = [
|
||||
/* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
|
||||
<a
|
||||
aria-label={t('read_more_about_free_compile_timeouts_servers')}
|
||||
href="/blog/changes-to-free-compile-timeouts-and-servers"
|
||||
key="compileTimeoutBlogLink1"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
onClick={sendInfoClickEvent}
|
||||
/>,
|
||||
/* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
|
||||
<a
|
||||
aria-label={t('read_more_about_fix_prevent_timeout')}
|
||||
href="/learn/how-to/Fixing_and_preventing_compile_timeouts"
|
||||
key="compileTimeoutBlogLink2"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>,
|
||||
]
|
||||
|
||||
// if showWarning is true then the 10s warning is shown
|
||||
// and if showChangingSoon is true then the 20s-60s should show
|
||||
|
||||
return (
|
||||
<div>
|
||||
{showWarning && isProjectOwner && (
|
||||
<Notification
|
||||
action={
|
||||
<StartFreeTrialButton
|
||||
source="compile-time-warning-new-10s"
|
||||
buttonProps={{
|
||||
className: 'btn-secondary-compile-timeout-override',
|
||||
}}
|
||||
>
|
||||
{t('start_free_trial_without_exclamation')}
|
||||
</StartFreeTrialButton>
|
||||
}
|
||||
ariaLive="polite"
|
||||
content={
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
<Trans i18nKey="your_project_near_compile_timeout_limit" />
|
||||
</span>
|
||||
</div>
|
||||
{showNewCompileTimeoutUI === 'active' ? (
|
||||
<>
|
||||
<strong>
|
||||
<Trans i18nKey="upgrade_for_12x_more_compile_time" />
|
||||
</strong>
|
||||
{'. '}
|
||||
</>
|
||||
) : (
|
||||
<strong>
|
||||
<Trans i18nKey="upgrade_for_plenty_more_compile_time" />
|
||||
</strong>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
type="warning"
|
||||
title={t('took_a_while')}
|
||||
isActionBelowContent
|
||||
isDismissible
|
||||
onDismiss={handleDismissWarning}
|
||||
/>
|
||||
)}
|
||||
{showChangingSoon &&
|
||||
(isProjectOwner ? (
|
||||
<Notification
|
||||
action={
|
||||
<StartFreeTrialButton
|
||||
source="compile-time-warning-new-changing-soon"
|
||||
buttonProps={{
|
||||
className: 'btn-secondary-compile-timeout-override',
|
||||
}}
|
||||
>
|
||||
{t('start_free_trial_without_exclamation')}
|
||||
</StartFreeTrialButton>
|
||||
}
|
||||
ariaLive="polite"
|
||||
content={
|
||||
<div>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="compile_timeout_will_be_reduced_project_exceeds_limit_speed_up_compile"
|
||||
components={compileTimeoutBlogLinks}
|
||||
values={{ date: 'October 6 2023' }}
|
||||
/>{' '}
|
||||
<Trans i18nKey="and_you_can_upgrade_for_plenty_more_compile_time" />
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
title={t('your_project_compiled_but_soon_might_not')}
|
||||
type="warning"
|
||||
isActionBelowContent
|
||||
isDismissible
|
||||
onDismiss={handleDismissChangingSoon}
|
||||
/>
|
||||
) : (
|
||||
<Notification
|
||||
ariaLive="polite"
|
||||
content={
|
||||
<div>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="compile_timeout_will_be_reduced_project_exceeds_limit_speed_up_compile"
|
||||
components={compileTimeoutBlogLinks}
|
||||
values={{ date: 'October 6 2023' }}
|
||||
/>
|
||||
</p>
|
||||
<p className="row-spaced">
|
||||
<Trans i18nKey="tell_the_project_owner_to_upgrade_plan_for_more_compile_time" />
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
title={t('this_project_compiled_but_soon_might_not')}
|
||||
type="warning"
|
||||
isDismissible
|
||||
onDismiss={handleDismissChangingSoon}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(CompileTimeoutMessages)
|
|
@ -4,6 +4,7 @@ import classnames from 'classnames'
|
|||
import PdfValidationIssue from './pdf-validation-issue'
|
||||
import StopOnFirstErrorPrompt from './stop-on-first-error-prompt'
|
||||
import TimeoutUpgradePrompt from './timeout-upgrade-prompt'
|
||||
import TimeoutUpgradePromptNew from './timeout-upgrade-prompt-new'
|
||||
import PdfPreviewError from './pdf-preview-error'
|
||||
import PdfClearCacheButton from './pdf-clear-cache-button'
|
||||
import PdfDownloadFilesButton from './pdf-download-files-button'
|
||||
|
@ -23,6 +24,7 @@ function PdfLogsViewer() {
|
|||
validationIssues,
|
||||
showLogs,
|
||||
stoppedOnFirstError,
|
||||
showNewCompileTimeoutUI,
|
||||
} = useCompileContext()
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
@ -34,9 +36,14 @@ function PdfLogsViewer() {
|
|||
|
||||
{stoppedOnFirstError && <StopOnFirstErrorPrompt />}
|
||||
|
||||
{error && <PdfPreviewError error={error} />}
|
||||
|
||||
{error === 'timedout' && <TimeoutUpgradePrompt />}
|
||||
{showNewCompileTimeoutUI && error === 'timedout' ? (
|
||||
<TimeoutUpgradePromptNew />
|
||||
) : (
|
||||
<>
|
||||
{error && <PdfPreviewError error={error} />}
|
||||
{error === 'timedout' && <TimeoutUpgradePrompt />}
|
||||
</>
|
||||
)}
|
||||
|
||||
{validationIssues &&
|
||||
Object.entries(validationIssues).map(([name, issue]) => (
|
||||
|
|
|
@ -8,9 +8,10 @@ import { useDetachCompileContext as useCompileContext } from '../../../shared/co
|
|||
import FasterCompilesFeedback from './faster-compiles-feedback'
|
||||
import { PdfPreviewMessages } from './pdf-preview-messages'
|
||||
import CompileTimeWarning from './compile-time-warning'
|
||||
import CompileTimeoutMessages from './compile-timeout-messages'
|
||||
|
||||
function PdfPreviewPane() {
|
||||
const { pdfUrl } = useCompileContext()
|
||||
const { pdfUrl, showNewCompileTimeoutUI } = useCompileContext()
|
||||
const classes = classNames('pdf', 'full-size', {
|
||||
'pdf-empty': !pdfUrl,
|
||||
})
|
||||
|
@ -18,7 +19,11 @@ function PdfPreviewPane() {
|
|||
<div className={classes}>
|
||||
<PdfHybridPreviewToolbar />
|
||||
<PdfPreviewMessages>
|
||||
<CompileTimeWarning />
|
||||
{showNewCompileTimeoutUI ? (
|
||||
<CompileTimeoutMessages />
|
||||
) : (
|
||||
<CompileTimeWarning />
|
||||
)}
|
||||
</PdfPreviewMessages>
|
||||
<Suspense fallback={<LoadingPreview />}>
|
||||
<div className="pdf-viewer">
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
|
||||
import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
|
||||
import { memo, useCallback } from 'react'
|
||||
import PdfLogEntry from './pdf-log-entry'
|
||||
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||
|
||||
function TimeoutUpgradePromptNew() {
|
||||
const {
|
||||
startCompile,
|
||||
lastCompileOptions,
|
||||
setAnimateCompileDropdownArrow,
|
||||
showNewCompileTimeoutUI,
|
||||
isProjectOwner,
|
||||
} = useDetachCompileContext()
|
||||
|
||||
const { enableStopOnFirstError } = useStopOnFirstError({
|
||||
eventSource: 'timeout-new',
|
||||
})
|
||||
|
||||
const handleEnableStopOnFirstErrorClick = useCallback(() => {
|
||||
enableStopOnFirstError()
|
||||
startCompile({ stopOnFirstError: true })
|
||||
setAnimateCompileDropdownArrow(true)
|
||||
}, [enableStopOnFirstError, startCompile, setAnimateCompileDropdownArrow])
|
||||
|
||||
if (!window.ExposedSettings.enableSubscriptions) {
|
||||
return null
|
||||
}
|
||||
|
||||
const compileTimeChanging = showNewCompileTimeoutUI === 'changing'
|
||||
|
||||
return (
|
||||
<>
|
||||
<CompileTimeout
|
||||
compileTimeChanging={compileTimeChanging}
|
||||
isProjectOwner={isProjectOwner}
|
||||
/>
|
||||
<PreventTimeoutHelpMessage
|
||||
compileTimeChanging={compileTimeChanging}
|
||||
handleEnableStopOnFirstErrorClick={handleEnableStopOnFirstErrorClick}
|
||||
lastCompileOptions={lastCompileOptions}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
type CompileTimeoutProps = {
|
||||
compileTimeChanging?: boolean
|
||||
isProjectOwner: boolean
|
||||
}
|
||||
|
||||
function CompileTimeout({
|
||||
compileTimeChanging,
|
||||
isProjectOwner,
|
||||
}: CompileTimeoutProps) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<PdfLogEntry
|
||||
headerTitle={t('your_compile_timed_out')}
|
||||
formattedContent={
|
||||
<>
|
||||
<p>
|
||||
{isProjectOwner
|
||||
? t('your_project_exceeded_compile_timeout_limit_on_free_plan')
|
||||
: t('this_project_exceeded_compile_timeout_limit_on_free_plan')}
|
||||
</p>
|
||||
{isProjectOwner ? (
|
||||
<p>
|
||||
{compileTimeChanging ? (
|
||||
<>
|
||||
<strong>{t('upgrade_for_plenty_more_compile_time')}</strong>{' '}
|
||||
{t(
|
||||
'plus_additional_collaborators_document_history_track_changes_and_more'
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<strong>
|
||||
<Trans i18nKey="upgrade_for_12x_more_compile_time" />
|
||||
</strong>{' '}
|
||||
<Trans i18nKey="plus_additional_collaborators_document_history_track_changes_and_more" />
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
) : (
|
||||
<Trans
|
||||
i18nKey="tell_the_project_owner_and_ask_them_to_upgrade"
|
||||
components={[
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<strong />,
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isProjectOwner && (
|
||||
<p className="text-center">
|
||||
<StartFreeTrialButton
|
||||
source={
|
||||
compileTimeChanging
|
||||
? 'compile-timeout-new-changing'
|
||||
: 'compile-timeout-new-active'
|
||||
}
|
||||
buttonProps={{
|
||||
bsStyle: 'success',
|
||||
className: 'row-spaced-small',
|
||||
block: true,
|
||||
}}
|
||||
>
|
||||
{t('start_a_free_trial')}
|
||||
</StartFreeTrialButton>
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
entryAriaLabel={t('your_compile_timed_out')}
|
||||
level="error"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
type PreventTimeoutHelpMessageProps = {
|
||||
compileTimeChanging?: boolean
|
||||
lastCompileOptions: any
|
||||
handleEnableStopOnFirstErrorClick: () => void
|
||||
}
|
||||
|
||||
function PreventTimeoutHelpMessage({
|
||||
compileTimeChanging,
|
||||
lastCompileOptions,
|
||||
handleEnableStopOnFirstErrorClick,
|
||||
}: PreventTimeoutHelpMessageProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
function sendInfoClickEvent() {
|
||||
eventTracking.sendMB('paywall-info-click', {
|
||||
'paywall-type': 'compile-timeout',
|
||||
content: 'blog',
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<PdfLogEntry
|
||||
headerTitle={t('other_ways_to_prevent_compile_timeouts')}
|
||||
formattedContent={
|
||||
<>
|
||||
<p>
|
||||
{t('you_may_be_able_to_prevent_a_compile_timeout')}
|
||||
{compileTimeChanging && (
|
||||
<>
|
||||
{' '}
|
||||
<Trans
|
||||
i18nKey="but_note_that_free_compile_timeout_limit_will_be_reduced_on_x_date"
|
||||
components={[
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a
|
||||
aria-label={t(
|
||||
'read_more_about_free_compile_timeouts_servers'
|
||||
)}
|
||||
href="/blog/changes-to-free-compile-timeouts-and-servers"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
onClick={sendInfoClickEvent}
|
||||
/>,
|
||||
]}
|
||||
values={{ date: 'October 6 2023' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
<p>{t('common_causes_of_compile_timeouts_are')}:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<Trans
|
||||
i18nKey="large_or_high-resolution_images_taking_too_long"
|
||||
components={[
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a
|
||||
href="/learn/how-to/Optimising_very_large_image_files"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<Trans
|
||||
i18nKey="a_fatal_compile_error_that_completely_blocks_compilation"
|
||||
components={[
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a
|
||||
href="/learn/how-to/Fixing_and_preventing_compile_timeouts#Step_3:_Assess_your_project_for_time-consuming_tasks_and_fatal_errors"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
{!lastCompileOptions.stopOnFirstError && (
|
||||
<>
|
||||
{' '}
|
||||
<Trans
|
||||
i18nKey="enable_stop_on_first_error_under_recompile_dropdown_menu"
|
||||
components={[
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<Button
|
||||
bsSize="xs"
|
||||
bsStyle="info-ghost-inline"
|
||||
onClick={handleEnableStopOnFirstErrorClick}
|
||||
/>,
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<strong />,
|
||||
]}
|
||||
/>{' '}
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="learn_more_about_other_causes_of_compile_timeouts"
|
||||
components={[
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
|
||||
<a
|
||||
href="/learn/how-to/Fixing_and_preventing_compile_timeouts"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
entryAriaLabel={t('other_ways_to_prevent_compile_timeouts')}
|
||||
level="raw"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(TimeoutUpgradePromptNew)
|
|
@ -184,6 +184,14 @@ export default class DocumentCompiler {
|
|||
params.file_line_errors = 'true'
|
||||
}
|
||||
|
||||
// temporary override to force the new compile timeout
|
||||
const newCompileTimeoutOverride = new URLSearchParams(
|
||||
window.location.search
|
||||
).get('force_new_compile_timeout')
|
||||
if (newCompileTimeoutOverride) {
|
||||
params.set('force_new_compile_timeout', newCompileTimeoutOverride)
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ export function DetachCompileProvider({ children }) {
|
|||
fileList: _fileList,
|
||||
hasChanges: _hasChanges,
|
||||
highlights: _highlights,
|
||||
isProjectOwner: _isProjectOwner,
|
||||
lastCompileOptions: _lastCompileOptions,
|
||||
logEntries: _logEntries,
|
||||
logEntryAnnotations: _logEntryAnnotations,
|
||||
|
@ -55,6 +56,7 @@ export function DetachCompileProvider({ children }) {
|
|||
setStopOnValidationError: _setStopOnValidationError,
|
||||
showLogs: _showLogs,
|
||||
showCompileTimeWarning: _showCompileTimeWarning,
|
||||
showNewCompileTimeoutUI: _showNewCompileTimeoutUI,
|
||||
showFasterCompilesFeedbackUI: _showFasterCompilesFeedbackUI,
|
||||
stopOnFirstError: _stopOnFirstError,
|
||||
stopOnValidationError: _stopOnValidationError,
|
||||
|
@ -135,6 +137,12 @@ export function DetachCompileProvider({ children }) {
|
|||
'detacher',
|
||||
'detached'
|
||||
)
|
||||
const [isProjectOwner] = useDetachStateWatcher(
|
||||
'isProjectOwner',
|
||||
_isProjectOwner,
|
||||
'detacher',
|
||||
'detached'
|
||||
)
|
||||
const [lastCompileOptions] = useDetachStateWatcher(
|
||||
'lastCompileOptions',
|
||||
_lastCompileOptions,
|
||||
|
@ -189,6 +197,12 @@ export function DetachCompileProvider({ children }) {
|
|||
'detacher',
|
||||
'detached'
|
||||
)
|
||||
const [showNewCompileTimeoutUI] = useDetachStateWatcher(
|
||||
'showNewCompileTimeoutUI',
|
||||
_showNewCompileTimeoutUI,
|
||||
'detacher',
|
||||
'detached'
|
||||
)
|
||||
const [showFasterCompilesFeedbackUI] = useDetachStateWatcher(
|
||||
'showFasterCompilesFeedbackUI',
|
||||
_showFasterCompilesFeedbackUI,
|
||||
|
@ -384,6 +398,7 @@ export function DetachCompileProvider({ children }) {
|
|||
fileList,
|
||||
hasChanges,
|
||||
highlights,
|
||||
isProjectOwner,
|
||||
lastCompileOptions,
|
||||
logEntryAnnotations,
|
||||
logEntries,
|
||||
|
@ -409,6 +424,7 @@ export function DetachCompileProvider({ children }) {
|
|||
setStopOnValidationError,
|
||||
showLogs,
|
||||
showCompileTimeWarning,
|
||||
showNewCompileTimeoutUI,
|
||||
showFasterCompilesFeedbackUI,
|
||||
startCompile,
|
||||
stopCompile,
|
||||
|
@ -437,6 +453,7 @@ export function DetachCompileProvider({ children }) {
|
|||
fileList,
|
||||
hasChanges,
|
||||
highlights,
|
||||
isProjectOwner,
|
||||
lastCompileOptions,
|
||||
logEntryAnnotations,
|
||||
logEntries,
|
||||
|
@ -460,6 +477,7 @@ export function DetachCompileProvider({ children }) {
|
|||
setStopOnValidationError,
|
||||
showCompileTimeWarning,
|
||||
showLogs,
|
||||
showNewCompileTimeoutUI,
|
||||
showFasterCompilesFeedbackUI,
|
||||
startCompile,
|
||||
stopCompile,
|
||||
|
|
|
@ -66,6 +66,7 @@ export const CompileContextPropTypes = {
|
|||
setStopOnValidationError: PropTypes.func.isRequired,
|
||||
showCompileTimeWarning: PropTypes.bool.isRequired,
|
||||
showLogs: PropTypes.bool.isRequired,
|
||||
showNewCompileTimeoutUI: PropTypes.string,
|
||||
showFasterCompilesFeedbackUI: PropTypes.bool.isRequired,
|
||||
stopOnFirstError: PropTypes.bool.isRequired,
|
||||
stopOnValidationError: PropTypes.bool.isRequired,
|
||||
|
@ -84,7 +85,11 @@ export function LocalCompileProvider({ children }) {
|
|||
|
||||
const { hasPremiumCompile, isProjectOwner } = useEditorContext()
|
||||
|
||||
const { _id: projectId, rootDocId } = useProjectContext()
|
||||
const {
|
||||
_id: projectId,
|
||||
rootDocId,
|
||||
showNewCompileTimeoutUI,
|
||||
} = useProjectContext()
|
||||
|
||||
const { pdfPreviewOpen } = useLayoutContext()
|
||||
|
||||
|
@ -555,6 +560,7 @@ export function LocalCompileProvider({ children }) {
|
|||
fileList,
|
||||
hasChanges,
|
||||
highlights,
|
||||
isProjectOwner,
|
||||
lastCompileOptions,
|
||||
logEntryAnnotations,
|
||||
logEntries,
|
||||
|
@ -580,6 +586,7 @@ export function LocalCompileProvider({ children }) {
|
|||
setStopOnFirstError,
|
||||
setStopOnValidationError,
|
||||
showLogs,
|
||||
showNewCompileTimeoutUI,
|
||||
showFasterCompilesFeedbackUI,
|
||||
startCompile,
|
||||
stopCompile,
|
||||
|
@ -609,6 +616,7 @@ export function LocalCompileProvider({ children }) {
|
|||
fileList,
|
||||
hasChanges,
|
||||
highlights,
|
||||
isProjectOwner,
|
||||
lastCompileOptions,
|
||||
logEntries,
|
||||
logEntryAnnotations,
|
||||
|
@ -629,6 +637,7 @@ export function LocalCompileProvider({ children }) {
|
|||
setStopOnValidationError,
|
||||
showCompileTimeWarning,
|
||||
showLogs,
|
||||
showNewCompileTimeoutUI,
|
||||
showFasterCompilesFeedbackUI,
|
||||
startCompile,
|
||||
stopCompile,
|
||||
|
|
|
@ -33,6 +33,7 @@ export const projectShape = {
|
|||
_id: PropTypes.string.isRequired,
|
||||
email: PropTypes.string.isRequired,
|
||||
}),
|
||||
useNewCompileTimeoutUI: PropTypes.string,
|
||||
}
|
||||
|
||||
ProjectContext.Provider.propTypes = {
|
||||
|
@ -79,8 +80,20 @@ export function ProjectProvider({ children }) {
|
|||
features,
|
||||
publicAccesLevel: publicAccessLevel,
|
||||
owner,
|
||||
showNewCompileTimeoutUI,
|
||||
} = project || projectFallback
|
||||
|
||||
// temporary override for new compile timeout
|
||||
const forceNewCompileTimeout = new URLSearchParams(
|
||||
window.location.search
|
||||
).get('force_new_compile_timeout')
|
||||
const newCompileTimeoutOverride =
|
||||
forceNewCompileTimeout === 'active'
|
||||
? 'active'
|
||||
: forceNewCompileTimeout === 'changing'
|
||||
? 'changing'
|
||||
: undefined
|
||||
|
||||
const value = useMemo(() => {
|
||||
return {
|
||||
_id,
|
||||
|
@ -91,6 +104,8 @@ export function ProjectProvider({ children }) {
|
|||
features,
|
||||
publicAccessLevel,
|
||||
owner,
|
||||
showNewCompileTimeoutUI:
|
||||
newCompileTimeoutOverride || showNewCompileTimeoutUI,
|
||||
}
|
||||
}, [
|
||||
_id,
|
||||
|
@ -101,6 +116,8 @@ export function ProjectProvider({ children }) {
|
|||
features,
|
||||
publicAccessLevel,
|
||||
owner,
|
||||
showNewCompileTimeoutUI,
|
||||
newCompileTimeoutOverride,
|
||||
])
|
||||
|
||||
return (
|
||||
|
|
|
@ -756,6 +756,7 @@ CodeMirror
|
|||
.pdf-preview-messages {
|
||||
position: absolute;
|
||||
right: @margin-sm;
|
||||
left: @margin-sm;
|
||||
top: @margin-xl;
|
||||
z-index: @zindex-popover;
|
||||
}
|
||||
|
|
|
@ -589,3 +589,10 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-secondary-compile-timeout-override {
|
||||
color: #1b222c;
|
||||
background-color: #ffffff;
|
||||
border-color: #677283;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
|
|
@ -156,6 +156,35 @@
|
|||
background-color: @red-10;
|
||||
}
|
||||
}
|
||||
.btn-info-ghost when (@is-new-css = true) {
|
||||
.btn-borderless(@blue-50, @btn-info-ghost-bg, @blue-10);
|
||||
}
|
||||
// Info Ghost appear as info blue with no border
|
||||
.btn-info-ghost when (@is-new-css = false) {
|
||||
.button-variant(@btn-info-ghost-color; @btn-info-ghost-bg; @btn-info-ghost-border);
|
||||
// hover for ghost acts different from typical variants, as it's default state has no bg
|
||||
&:hover {
|
||||
background-color: @blue-10;
|
||||
}
|
||||
}
|
||||
// Inline button to fit text, without link styling.
|
||||
// TODO: generic class for other styles
|
||||
.btn-info-ghost-inline when (@is-new-css = true) {
|
||||
.btn-borderless(@blue-50, @btn-info-ghost-bg, @blue-10);
|
||||
padding: 0 !important;
|
||||
font-size: inherit !important;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
.btn-info-ghost-inline when (@is-new-css = false) {
|
||||
.button-variant(@btn-info-ghost-color; @btn-info-ghost-bg; @btn-info-ghost-border);
|
||||
// hover for ghost acts different from typical variants, as it's default state has no bg
|
||||
&:hover {
|
||||
background-color: @blue-10;
|
||||
}
|
||||
padding: 0 !important;
|
||||
font-size: inherit !important;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
.btn-danger-ghost when (@is-new-css = true) {
|
||||
.btn-borderless(@red-50, @btn-danger-ghost-bg, @red-10);
|
||||
}
|
||||
|
|
|
@ -241,6 +241,10 @@
|
|||
@btn-info-bg: @ol-blue;
|
||||
@btn-info-border: transparent;
|
||||
|
||||
@btn-info-ghost-color: @blue-50;
|
||||
@btn-info-ghost-bg: transparent;
|
||||
@btn-info-ghost-border: transparent;
|
||||
|
||||
@btn-warning-color: #fff;
|
||||
@btn-warning-bg: @orange;
|
||||
@btn-warning-border: transparent;
|
||||
|
|
|
@ -171,6 +171,10 @@
|
|||
@btn-info-bg: @blue;
|
||||
@btn-info-border: transparent;
|
||||
|
||||
@btn-info-ghost-color: @blue-50;
|
||||
@btn-info-ghost-bg: transparent;
|
||||
@btn-info-ghost-border: transparent;
|
||||
|
||||
@btn-warning-color: #fff;
|
||||
@btn-warning-bg: @orange;
|
||||
@btn-warning-border: transparent;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"Terms": "Terms",
|
||||
"Universities": "Universities",
|
||||
"a_custom_size_has_been_used_in_the_latex_code": "A custom size has been used in the LaTeX code.",
|
||||
"a_fatal_compile_error_that_completely_blocks_compilation": "A <0>fatal compile error</0> that completely blocks the compilation.",
|
||||
"a_file_with_that_name_already_exists_and_will_be_overriden": "A file with that name already exists. That file will be overwritten.",
|
||||
"a_more_comprehensive_list_of_keyboard_shortcuts": "A more comprehensive list of keyboard shortcuts can be found in <0>this __appName__ project template</0>",
|
||||
"about": "About",
|
||||
|
@ -100,6 +101,7 @@
|
|||
"an_email_has_already_been_sent_to": "An email has already been sent to <0>__email__</0>. Please wait and try again later.",
|
||||
"an_error_occurred_when_verifying_the_coupon_code": "An error occurred when verifying the coupon code",
|
||||
"and": "and",
|
||||
"and_you_can_upgrade_for_plenty_more_compile_time": "And you can <strong>upgrade to get plenty more compile time.</strong>",
|
||||
"annual": "Annual",
|
||||
"annual_billing_enabled": "Annual billing enabled",
|
||||
"anonymous": "Anonymous",
|
||||
|
@ -178,6 +180,7 @@
|
|||
"built_in": "Built-In",
|
||||
"bulk_accept_confirm": "Are you sure you want to accept the selected __nChanges__ changes?",
|
||||
"bulk_reject_confirm": "Are you sure you want to reject the selected __nChanges__ changes?",
|
||||
"but_note_that_free_compile_timeout_limit_will_be_reduced_on_x_date": "But note that the free compile timeout limit <0>will be reduced on __date__</0>.",
|
||||
"buy_now_no_exclamation_mark": "Buy now",
|
||||
"by": "by",
|
||||
"by_registering_you_agree_to_our_terms_of_service": "By registering, you agree to our <0>terms of service</0>.",
|
||||
|
@ -270,6 +273,7 @@
|
|||
"comment": "Comment",
|
||||
"commit": "Commit",
|
||||
"common": "Common",
|
||||
"common_causes_of_compile_timeouts_are": "Common causes of compile timeouts are",
|
||||
"commons_plan_tooltip": "You’re on the __plan__ plan because of your affiliation with __institution__. Click to find out how to make the most of your Overleaf premium features.",
|
||||
"compact": "Compact",
|
||||
"company_name": "Company Name",
|
||||
|
@ -284,6 +288,7 @@
|
|||
"compile_timeout": "Compile timeout (minutes)",
|
||||
"compile_timeout_short": "Compile timeout",
|
||||
"compile_timeout_short_info": "This is how much time you get to compile your project on the Overleaf servers. For short and simple projects, 1 minute should be enough, but you may need longer for complex or longer projects",
|
||||
"compile_timeout_will_be_reduced_project_exceeds_limit_speed_up_compile": "The compile timeout limit on our free plan <0>will be reduced on __date__</0> and this project currently exceeds the new limit. You may be able to fix issues to <1>speed up the compile</1>.",
|
||||
"compiler": "Compiler",
|
||||
"compiling": "Compiling",
|
||||
"complete": "Complete",
|
||||
|
@ -485,6 +490,7 @@
|
|||
"empty_zip_file": "Zip doesn’t contain any file",
|
||||
"en": "English",
|
||||
"enable_managed_users": "Enable Managed Users",
|
||||
"enable_stop_on_first_error_under_recompile_dropdown_menu": "Enable <0>“Stop on first error”</0> under the <1>Recompile</1> drop-down menu to help you find and fix errors right away.",
|
||||
"enabled_managed_users_set_up_sso": "You need to enable Managed Users to set up SSO.",
|
||||
"enabling": "Enabling",
|
||||
"end_of_document": "End of document",
|
||||
|
@ -886,6 +892,7 @@
|
|||
"labs_program_benefits": "__appName__ is always looking for new ways to help users work more quickly and effectively. By joining Overleaf Labs, you can participate in experiments that explore innovative ideas in the space of collaborative writing and publishing.",
|
||||
"labs_program_not_participating": "You are not enrolled in Labs",
|
||||
"language": "Language",
|
||||
"large_or_high-resolution_images_taking_too_long": "Large or high-resolution images taking too long to process. You may be able to <0>optimize them</0>.",
|
||||
"last_active": "Last Active",
|
||||
"last_active_description": "Last time a project was opened.",
|
||||
"last_modified": "Last Modified",
|
||||
|
@ -915,6 +922,7 @@
|
|||
"learn_more_about_emails": "<0>Learn more</0> about managing your __appName__ emails.",
|
||||
"learn_more_about_link_sharing": "Learn more about Link Sharing",
|
||||
"learn_more_about_managed_users": "Learn more about Managed Users.",
|
||||
"learn_more_about_other_causes_of_compile_timeouts": "<0>Learn more</0> about other causes of compile timeouts and how to fix them.",
|
||||
"learn_more_lowercase": "learn more",
|
||||
"leave": "Leave",
|
||||
"leave_any_group_subscriptions": "Leave any group subscriptions other than the one that will be managing your account. <0>Leave them from the Subscription page.</0>",
|
||||
|
@ -1186,6 +1194,7 @@
|
|||
"other_logs_and_files": "Other logs and files",
|
||||
"other_output_files": "Download other output files",
|
||||
"other_sessions": "Other Sessions",
|
||||
"other_ways_to_prevent_compile_timeouts": "Other ways to prevent compile timeouts",
|
||||
"our_values": "Our values",
|
||||
"output_file": "Output file",
|
||||
"over": "over",
|
||||
|
@ -1269,6 +1278,7 @@
|
|||
"please_select_an_output_file": "Please Select an Output File",
|
||||
"please_set_a_password": "Please set a password",
|
||||
"please_set_main_file": "Please choose the main file for this project in the project menu. ",
|
||||
"plus_additional_collaborators_document_history_track_changes_and_more": "(plus additional collaborators, document history, track changes, and more).",
|
||||
"plus_more": "plus more",
|
||||
"plus_upgraded_accounts_receive": "Plus with an upgraded account you get",
|
||||
"popular_tags": "Popular Tags",
|
||||
|
@ -1342,6 +1352,8 @@
|
|||
"react_history_tutorial_content": "To compare a range of versions, use the <0></0> on the versions you want at the start and end of the range. To add a label or to download a version use the options in the three-dot menu. <1>Learn more about using Overleaf History.</1>",
|
||||
"react_history_tutorial_title": "History actions have a new home",
|
||||
"reactivate_subscription": "Reactivate your subscription",
|
||||
"read_more_about_fix_prevent_timeout": "Read more about how to fix and prevent timeouts",
|
||||
"read_more_about_free_compile_timeouts_servers": "Read more about changes to free compile timeouts and servers",
|
||||
"read_only": "Read Only",
|
||||
"read_only_token": "Read-Only Token",
|
||||
"read_write_token": "Read-Write Token",
|
||||
|
@ -1612,8 +1624,10 @@
|
|||
"sso_not_linked": "You have not linked your account to __provider__. Please log in to your account another way and link your __provider__ account via your account settings.",
|
||||
"sso_user_denied_access": "Cannot log in because __appName__ was not granted access to your __provider__ account. Please try again.",
|
||||
"standard": "Standard",
|
||||
"start_a_free_trial": "Start a free trial",
|
||||
"start_by_adding_your_email": "Start by adding your email address.",
|
||||
"start_free_trial": "Start Free Trial!",
|
||||
"start_free_trial_without_exclamation": "Start Free Trial",
|
||||
"start_using_latex_now": "start using LaTeX right now",
|
||||
"start_using_sl_now": "Start using __appName__ now",
|
||||
"state": "State",
|
||||
|
@ -1674,6 +1688,8 @@
|
|||
"tc_switch_everyone_tip": "Toggle track-changes for everyone",
|
||||
"tc_switch_guests_tip": "Toggle track-changes for all link-sharing guests",
|
||||
"tc_switch_user_tip": "Toggle track-changes for this user",
|
||||
"tell_the_project_owner_and_ask_them_to_upgrade": "<0>Tell the project owner</0> and ask them to upgrade their Overleaf plan if you need more compile time.",
|
||||
"tell_the_project_owner_to_upgrade_plan_for_more_compile_time": "<strong>Tell the project owner</strong> and ask them to upgrade their Overleaf plan if you need more compile time.",
|
||||
"template": "Template",
|
||||
"template_approved_by_publisher": "This template has been approved by the publisher",
|
||||
"template_description": "Template Description",
|
||||
|
@ -1722,6 +1738,8 @@
|
|||
"this_field_is_required": "This field is required",
|
||||
"this_grants_access_to_features_2": "This grants you access to <0>__appName__</0> <0>__featureType__</0> features.",
|
||||
"this_is_your_template": "This is your template from your project",
|
||||
"this_project_compiled_but_soon_might_not": "This project compiled, but soon it might not",
|
||||
"this_project_exceeded_compile_timeout_limit_on_free_plan": "This project exceeded the compile timeout limit on our free plan.",
|
||||
"this_project_is_public": "This project is public and can be edited by anyone with the URL.",
|
||||
"this_project_is_public_read_only": "This project is public and can be viewed but not edited by anyone with the URL",
|
||||
"this_project_will_appear_in_your_dropbox_folder_at": "This project will appear in your Dropbox folder at ",
|
||||
|
@ -1751,6 +1769,7 @@
|
|||
"too_many_requests": "Too many requests were received in a short space of time. Please wait for a few moments and try again.",
|
||||
"too_many_search_results": "There are more than 100 results. Please refine your search.",
|
||||
"too_recently_compiled": "This project was compiled very recently, so this compile has been skipped.",
|
||||
"took_a_while": "That took a while...",
|
||||
"toolbar_add_comment": "Add Comment",
|
||||
"toolbar_bullet_list": "Bullet List",
|
||||
"toolbar_choose_section_heading_level": "Choose section heading level",
|
||||
|
@ -1867,7 +1886,9 @@
|
|||
"updating_site": "Updating Site",
|
||||
"upgrade": "Upgrade",
|
||||
"upgrade_cc_btn": "Upgrade now, pay after 7 days",
|
||||
"upgrade_for_12x_more_compile_time": "Upgrade to get 12x more compile time",
|
||||
"upgrade_for_longer_compiles": "Upgrade to increase your timeout limit.",
|
||||
"upgrade_for_plenty_more_compile_time": "Upgrade to get plenty more compile time.",
|
||||
"upgrade_now": "Upgrade Now",
|
||||
"upgrade_to_get_feature": "Upgrade to get __feature__, plus:",
|
||||
"upgrade_to_track_changes": "Upgrade to Track Changes",
|
||||
|
@ -1977,6 +1998,7 @@
|
|||
"you_have_been_invited_to_transfer_management_of_your_account_to": "You have been invited to transfer management of your account to __groupName__.",
|
||||
"you_introed_high_number": " You’ve introduced <0>__numberOfPeople__</0> people to __appName__. Good job!",
|
||||
"you_introed_small_number": " You’ve introduced <0>__numberOfPeople__</0> person to __appName__. Good job, but can you get some more?",
|
||||
"you_may_be_able_to_prevent_a_compile_timeout": "You may be able to prevent a compile timeout using the following tips.",
|
||||
"you_not_introed_anyone_to_sl": "You’ve not introduced anyone to __appName__ yet. Get sharing!",
|
||||
"you_plus_1": "You + 1",
|
||||
"you_plus_10": "You + 10",
|
||||
|
@ -1987,6 +2009,7 @@
|
|||
"your_account_is_managed_by_admin_cant_join_additional_group": "Your __appName__ account is managed by your current group admin (__admin__). This means you can’t join additional group subscriptions. <0>Read more about Managed Users.</0>",
|
||||
"your_affiliation_is_confirmed": "Your <0>__institutionName__</0> affiliation is confirmed.",
|
||||
"your_browser_does_not_support_this_feature": "Sorry, your browser doesn’t support this feature. Please update your browser to its latest version.",
|
||||
"your_compile_timed_out": "Your compile timed out",
|
||||
"your_git_access_info": "Your Git authentication tokens should be entered whenever you’re prompted for a password.",
|
||||
"your_git_access_info_bullet_1": "You can have up to 10 tokens.",
|
||||
"your_git_access_info_bullet_2": "If you reach the maximum limit, you’ll need to delete a token before you can generate a new one.",
|
||||
|
@ -1999,6 +2022,9 @@
|
|||
"your_password_has_been_successfully_changed": "Your password has been successfully changed",
|
||||
"your_plan": "Your plan",
|
||||
"your_plan_is_changing_at_term_end": "Your plan is changing to <0>__pendingPlanName__</0> at the end of the current billing period.",
|
||||
"your_project_compiled_but_soon_might_not": "Your project compiled, but soon it might not",
|
||||
"your_project_exceeded_compile_timeout_limit_on_free_plan": "Your project exceeded the compile timeout limit on our free plan.",
|
||||
"your_project_near_compile_timeout_limit": "Your project is near the compile timeout limit for our free plan.",
|
||||
"your_projects": "Your Projects",
|
||||
"your_sessions": "Your Sessions",
|
||||
"your_subscription": "Your Subscription",
|
||||
|
|
|
@ -215,6 +215,7 @@ describe('CompileManager', function () {
|
|||
betaProgram: 1,
|
||||
features: 1,
|
||||
splitTests: 1,
|
||||
signUpDate: 1,
|
||||
})
|
||||
.should.equal(true)
|
||||
})
|
||||
|
@ -232,6 +233,96 @@ describe('CompileManager', function () {
|
|||
})
|
||||
})
|
||||
|
||||
describe('getProjectCompileLimits with reduced compile timeout', function () {
|
||||
beforeEach(function () {
|
||||
this.getAssignmentForMongoUser.callsFake((user, test, cb) => {
|
||||
if (test === 'compile-backend-class-n2d') {
|
||||
cb(null, { variant: 'n2d' })
|
||||
}
|
||||
if (test === 'compile-timeout-20s') {
|
||||
cb(null, { variant: '20s' })
|
||||
}
|
||||
})
|
||||
this.features = {
|
||||
compileTimeout: (this.timeout = 60),
|
||||
compileGroup: (this.group = 'standard'),
|
||||
}
|
||||
this.ProjectGetter.getProject = sinon
|
||||
.stub()
|
||||
.callsArgWith(
|
||||
2,
|
||||
null,
|
||||
(this.project = { owner_ref: (this.owner_id = 'owner-id-123') })
|
||||
)
|
||||
this.UserGetter.getUser = sinon
|
||||
.stub()
|
||||
.callsArgWith(
|
||||
2,
|
||||
null,
|
||||
(this.user = { features: this.features, analyticsId: 'abc' })
|
||||
)
|
||||
this.CompileManager.getProjectCompileLimits(
|
||||
this.project_id,
|
||||
this.callback
|
||||
)
|
||||
})
|
||||
|
||||
describe('user is in the n2d group and compile-timeout-20s split test variant', function () {
|
||||
describe('user has a timeout of more than 60s', function () {
|
||||
beforeEach(function () {
|
||||
this.features.compileTimeout = 120
|
||||
})
|
||||
it('should keep the users compile timeout', function () {
|
||||
this.CompileManager.getProjectCompileLimits(
|
||||
this.project_id,
|
||||
this.callback
|
||||
)
|
||||
this.callback
|
||||
.calledWith(null, sinon.match({ timeout: 120 }))
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
describe('user registered before the cut off date', function () {
|
||||
beforeEach(function () {
|
||||
this.features.compileTimeout = 60
|
||||
const signUpDate = new Date(
|
||||
this.CompileManager.NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF
|
||||
)
|
||||
signUpDate.setDate(signUpDate.getDate() - 1)
|
||||
this.user.signUpDate = signUpDate
|
||||
})
|
||||
it('should keep the users compile timeout', function () {
|
||||
this.CompileManager.getProjectCompileLimits(
|
||||
this.project_id,
|
||||
this.callback
|
||||
)
|
||||
this.callback
|
||||
.calledWith(null, sinon.match({ timeout: 60 }))
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
describe('user registered after the cut off date', function () {
|
||||
beforeEach(function () {
|
||||
this.timeout = 60
|
||||
const signUpDate = new Date(
|
||||
this.CompileManager.NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF
|
||||
)
|
||||
signUpDate.setDate(signUpDate.getDate() + 1)
|
||||
this.user.signUpDate = signUpDate
|
||||
})
|
||||
it('should reduce compile timeout to 20s', function () {
|
||||
this.CompileManager.getProjectCompileLimits(
|
||||
this.project_id,
|
||||
this.callback
|
||||
)
|
||||
this.callback
|
||||
.calledWith(null, sinon.match({ timeout: 20 }))
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('compileBackendClass', function () {
|
||||
beforeEach(function () {
|
||||
this.features = {
|
||||
|
|
|
@ -131,6 +131,13 @@ describe('EditorHttpController', function () {
|
|||
notFound: sinon.stub(),
|
||||
unprocessableEntity: sinon.stub(),
|
||||
}
|
||||
this.SplitTestHandler = {
|
||||
promises: {
|
||||
getAssignmentForMongoUser: sinon
|
||||
.stub()
|
||||
.resolves({ variant: 'default' }),
|
||||
},
|
||||
}
|
||||
this.EditorHttpController = SandboxedModule.require(MODULE_PATH, {
|
||||
requires: {
|
||||
'../Project/ProjectDeleter': this.ProjectDeleter,
|
||||
|
@ -150,6 +157,8 @@ describe('EditorHttpController', function () {
|
|||
this.ProjectEntityUpdateHandler,
|
||||
'../Docstore/DocstoreManager': this.DocstoreManager,
|
||||
'../Errors/HttpErrorHandler': this.HttpErrorHandler,
|
||||
'../SplitTests/SplitTestHandler': this.SplitTestHandler,
|
||||
'../Compile/CompileManager': {},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
|
|
@ -8,6 +8,9 @@ declare global {
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
interface Window {
|
||||
csrfToken: string
|
||||
sl_console: {
|
||||
log: (message: string) => void
|
||||
}
|
||||
sl_debugging: boolean
|
||||
user: User
|
||||
user_id?: string
|
||||
|
|
Loading…
Reference in a new issue