mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #4715 from overleaf/ab-project-members-modal-split-test
Share modal split test GitOrigin-RevId: 274450564e1cbfc3ba3ec7c2ca60dfeda552a536
This commit is contained in:
parent
60d1483150
commit
7e6839b0af
14 changed files with 340 additions and 94 deletions
|
@ -38,6 +38,7 @@ const UserController = require('../User/UserController')
|
||||||
const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
||||||
const Modules = require('../../infrastructure/Modules')
|
const Modules = require('../../infrastructure/Modules')
|
||||||
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
|
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
|
||||||
|
const SplitTestV2Handler = require('../SplitTests/SplitTestV2Handler')
|
||||||
const { getNewLogsUIVariantForUser } = require('../Helpers/NewLogsUI')
|
const { getNewLogsUIVariantForUser } = require('../Helpers/NewLogsUI')
|
||||||
|
|
||||||
const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => {
|
const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => {
|
||||||
|
@ -725,23 +726,36 @@ const ProjectController = {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
sharingModalSplitTest(cb) {
|
||||||
|
SplitTestV2Handler.assignInLocalsContext(
|
||||||
|
res,
|
||||||
|
userId,
|
||||||
|
'project-share-modal-paywall',
|
||||||
|
err => {
|
||||||
|
cb(err, null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
(err, results) => {
|
(
|
||||||
|
err,
|
||||||
|
{
|
||||||
|
project,
|
||||||
|
user,
|
||||||
|
subscription,
|
||||||
|
isTokenMember,
|
||||||
|
brandVariation,
|
||||||
|
pdfCachingFeatureFlag,
|
||||||
|
}
|
||||||
|
) => {
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
OError.tag(err, 'error getting details for project page')
|
OError.tag(err, 'error getting details for project page')
|
||||||
return next(err)
|
return next(err)
|
||||||
}
|
}
|
||||||
const { project } = results
|
|
||||||
const { user } = results
|
|
||||||
const { subscription } = results
|
|
||||||
const { brandVariation } = results
|
|
||||||
const { pdfCachingFeatureFlag } = results
|
|
||||||
|
|
||||||
const anonRequestToken = TokenAccessHandler.getRequestToken(
|
const anonRequestToken = TokenAccessHandler.getRequestToken(
|
||||||
req,
|
req,
|
||||||
projectId
|
projectId
|
||||||
)
|
)
|
||||||
const { isTokenMember } = results
|
|
||||||
const allowedImageNames = ProjectHelper.getAllowedImagesForUser(
|
const allowedImageNames = ProjectHelper.getAllowedImagesForUser(
|
||||||
sessionUser
|
sessionUser
|
||||||
)
|
)
|
||||||
|
|
|
@ -81,6 +81,14 @@ async function getAssignment(userId, splitTestName, options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function assignInLocalsContext(res, userId, splitTestName, options) {
|
||||||
|
const assignment = await getAssignment(userId, splitTestName, options)
|
||||||
|
if (!res.locals.splitTestVariants) {
|
||||||
|
res.locals.splitTestVariants = {}
|
||||||
|
}
|
||||||
|
res.locals.splitTestVariants[splitTestName] = assignment.variant
|
||||||
|
}
|
||||||
|
|
||||||
async function _getAssignmentMetadata(userId, splitTest) {
|
async function _getAssignmentMetadata(userId, splitTest) {
|
||||||
const currentVersion = splitTest.getCurrentVersion()
|
const currentVersion = splitTest.getCurrentVersion()
|
||||||
const phase = currentVersion.phase
|
const phase = currentVersion.phase
|
||||||
|
@ -171,7 +179,9 @@ async function _getUser(id) {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getAssignment: callbackify(getAssignment),
|
getAssignment: callbackify(getAssignment),
|
||||||
|
assignInLocalsContext: callbackify(assignInLocalsContext),
|
||||||
promises: {
|
promises: {
|
||||||
getAssignment,
|
getAssignment,
|
||||||
|
assignInLocalsContext,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ html(
|
||||||
})
|
})
|
||||||
//- Expose some settings globally to the frontend
|
//- Expose some settings globally to the frontend
|
||||||
meta(name="ol-ExposedSettings" data-type="json" content=ExposedSettings)
|
meta(name="ol-ExposedSettings" data-type="json" content=ExposedSettings)
|
||||||
|
meta(name="ol-splitTestVariants" data-type="json" content=splitTestVariants || {})
|
||||||
|
|
||||||
if (typeof(settings.algolia) != "undefined")
|
if (typeof(settings.algolia) != "undefined")
|
||||||
meta(name="ol-algolia" data-type="json" content={
|
meta(name="ol-algolia" data-type="json" content={
|
||||||
|
|
|
@ -199,6 +199,7 @@
|
||||||
"navigate_log_source": "",
|
"navigate_log_source": "",
|
||||||
"navigation": "",
|
"navigation": "",
|
||||||
"need_to_upgrade_for_more_collabs": "",
|
"need_to_upgrade_for_more_collabs": "",
|
||||||
|
"need_to_upgrade_for_more_collabs_variant": "",
|
||||||
"new_file": "",
|
"new_file": "",
|
||||||
"new_folder": "",
|
"new_folder": "",
|
||||||
"new_name": "",
|
"new_name": "",
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import Icon from '../../../shared/components/icon'
|
||||||
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
export default function AddCollaboratorsUpgradeContentDefault() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-center">
|
||||||
|
<Trans i18nKey="need_to_upgrade_for_more_collabs" />. {t('also')}:
|
||||||
|
</p>
|
||||||
|
<ul className="list-unstyled">
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="unlimited_projects" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans
|
||||||
|
i18nKey="collabs_per_proj"
|
||||||
|
values={{ collabcount: 'Multiple' }}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="full_doc_history" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="sync_to_dropbox" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="sync_to_github" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="compile_larger_projects" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import Icon from '../../../shared/components/icon'
|
||||||
|
import { Trans } from 'react-i18next'
|
||||||
|
|
||||||
|
export default function AddCollaboratorsUpgradeContentVariant() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-xs-10 col-xs-offset-1">
|
||||||
|
<p className="text-center">
|
||||||
|
<Trans i18nKey="need_to_upgrade_for_more_collabs_variant" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<ul className="list-unstyled col-xs-7">
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="unlimited_projects" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans
|
||||||
|
i18nKey="collabs_per_proj"
|
||||||
|
values={{ collabcount: 'Multiple' }}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="full_doc_history" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul className="list-unstyled col-xs-5">
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="sync_to_dropbox" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="sync_to_github" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Icon type="check" />
|
||||||
|
|
||||||
|
<Trans i18nKey="compile_larger_projects" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,63 +1,34 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
import { Button } from 'react-bootstrap'
|
import { Button } from 'react-bootstrap'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { useUserContext } from '../../../shared/context/user-context'
|
import { useUserContext } from '../../../shared/context/user-context'
|
||||||
|
|
||||||
import Icon from '../../../shared/components/icon'
|
|
||||||
import { upgradePlan } from '../../../main/account-upgrade'
|
import { upgradePlan } from '../../../main/account-upgrade'
|
||||||
import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
|
import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
|
||||||
|
import AddCollaboratorsUpgradeContentDefault from './add-collaborators-upgrade-content-default'
|
||||||
|
import AddCollaboratorsUpgradeContentVariant from './add-collaborators-upgrade-content-variant'
|
||||||
|
import { useSplitTestContext } from '../../../shared/context/split-test-context'
|
||||||
|
|
||||||
export default function AddCollaboratorsUpgrade() {
|
export default function AddCollaboratorsUpgrade() {
|
||||||
const { t } = useTranslation()
|
|
||||||
const user = useUserContext({
|
const user = useUserContext({
|
||||||
allowedFreeTrial: PropTypes.bool,
|
allowedFreeTrial: PropTypes.bool,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [startedFreeTrial, setStartedFreeTrial] = useState(false)
|
const [startedFreeTrial, setStartedFreeTrial] = useState(false)
|
||||||
|
const { splitTestVariants } = useSplitTestContext({
|
||||||
|
splitTestVariants: PropTypes.object,
|
||||||
|
})
|
||||||
|
const variant = splitTestVariants['project-share-modal-paywall']
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="add-collaborators-upgrade">
|
<div className={variant === 'default' ? 'add-collaborators-upgrade' : ''}>
|
||||||
<p className="text-center">
|
{!variant || variant === 'default' ? (
|
||||||
<Trans i18nKey="need_to_upgrade_for_more_collabs" />. {t('also')}:
|
<AddCollaboratorsUpgradeContentDefault />
|
||||||
</p>
|
) : (
|
||||||
|
<AddCollaboratorsUpgradeContentVariant />
|
||||||
<ul className="list-unstyled">
|
)}
|
||||||
<li>
|
|
||||||
<Icon type="check" />
|
|
||||||
|
|
||||||
<Trans i18nKey="unlimited_projects" />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Icon type="check" />
|
|
||||||
|
|
||||||
<Trans
|
|
||||||
i18nKey="collabs_per_proj"
|
|
||||||
values={{ collabcount: 'Multiple' }}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Icon type="check" />
|
|
||||||
|
|
||||||
<Trans i18nKey="full_doc_history" />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Icon type="check" />
|
|
||||||
|
|
||||||
<Trans i18nKey="sync_to_dropbox" />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Icon type="check" />
|
|
||||||
|
|
||||||
<Trans i18nKey="sync_to_github" />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Icon type="check" />
|
|
||||||
|
|
||||||
<Trans i18nKey="compile_larger_projects" />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p className="text-center row-spaced-thin">
|
<p className="text-center row-spaced-thin">
|
||||||
{user.allowedFreeTrial ? (
|
{user.allowedFreeTrial ? (
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { useShareProjectContext } from './share-project-modal'
|
||||||
import { setProjectAccessLevel } from '../utils/api'
|
import { setProjectAccessLevel } from '../utils/api'
|
||||||
import CopyLink from '../../../shared/components/copy-link'
|
import CopyLink from '../../../shared/components/copy-link'
|
||||||
import { useProjectContext } from '../../../shared/context/project-context'
|
import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
|
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||||
|
|
||||||
export default function LinkSharing() {
|
export default function LinkSharing() {
|
||||||
const [inflight, setInflight] = useState(false)
|
const [inflight, setInflight] = useState(false)
|
||||||
|
@ -75,7 +76,10 @@ function PrivateSharing({ setAccessLevel, inflight }) {
|
||||||
type="button"
|
type="button"
|
||||||
bsStyle="link"
|
bsStyle="link"
|
||||||
className="btn-inline-link"
|
className="btn-inline-link"
|
||||||
onClick={() => setAccessLevel('tokenBased')}
|
onClick={() => {
|
||||||
|
setAccessLevel('tokenBased')
|
||||||
|
eventTracking.sendMB('link-sharing-click')
|
||||||
|
}}
|
||||||
disabled={inflight}
|
disabled={inflight}
|
||||||
>
|
>
|
||||||
<Trans i18nKey="turn_on_link_sharing" />
|
<Trans i18nKey="turn_on_link_sharing" />
|
||||||
|
|
|
@ -7,35 +7,116 @@ import ViewMember from './view-member'
|
||||||
import OwnerInfo from './owner-info'
|
import OwnerInfo from './owner-info'
|
||||||
import SendInvitesNotice from './send-invites-notice'
|
import SendInvitesNotice from './send-invites-notice'
|
||||||
import { useProjectContext } from '../../../shared/context/project-context'
|
import { useProjectContext } from '../../../shared/context/project-context'
|
||||||
|
import { useSplitTestContext } from '../../../shared/context/split-test-context'
|
||||||
|
import { Row } from 'react-bootstrap'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import RecaptchaConditions from '../../../shared/components/recaptcha-conditions'
|
import RecaptchaConditions from '../../../shared/components/recaptcha-conditions'
|
||||||
|
|
||||||
export default function ShareModalBody() {
|
export default function ShareModalBody() {
|
||||||
const { isAdmin } = useShareProjectContext()
|
const { isAdmin } = useShareProjectContext()
|
||||||
|
const { splitTestVariants } = useSplitTestContext({
|
||||||
|
splitTestVariants: PropTypes.object,
|
||||||
|
})
|
||||||
|
|
||||||
const project = useProjectContext()
|
const project = useProjectContext()
|
||||||
|
|
||||||
return (
|
switch (splitTestVariants['project-share-modal-paywall']) {
|
||||||
<>
|
case 'new-copy-top':
|
||||||
{isAdmin && <LinkSharing />}
|
return (
|
||||||
|
<>
|
||||||
|
{isAdmin ? (
|
||||||
|
<>
|
||||||
|
<SendInvites />
|
||||||
|
<Row className="public-access-level" />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<SendInvitesNotice />
|
||||||
|
)}
|
||||||
|
|
||||||
<OwnerInfo />
|
<OwnerInfo />
|
||||||
|
|
||||||
{project.members.map(member =>
|
{project.members.map(member =>
|
||||||
isAdmin ? (
|
isAdmin ? (
|
||||||
<EditMember key={member._id} member={member} />
|
<EditMember key={member._id} member={member} />
|
||||||
) : (
|
) : (
|
||||||
<ViewMember key={member._id} member={member} />
|
<ViewMember key={member._id} member={member} />
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{project.invites.map(invite => (
|
{project.invites.map(invite => (
|
||||||
<Invite key={invite._id} invite={invite} isAdmin={isAdmin} />
|
<Invite key={invite._id} invite={invite} isAdmin={isAdmin} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{isAdmin ? <SendInvites /> : <SendInvitesNotice />}
|
{isAdmin && (
|
||||||
{!window.ExposedSettings.recaptchaDisabled?.invite && (
|
<>
|
||||||
<RecaptchaConditions />
|
<br />
|
||||||
)}
|
<LinkSharing />
|
||||||
</>
|
</>
|
||||||
)
|
)}
|
||||||
|
|
||||||
|
{!window.ExposedSettings.recaptchaDisabled?.invite && (
|
||||||
|
<RecaptchaConditions />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case 'new-copy-middle':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<OwnerInfo />
|
||||||
|
|
||||||
|
{project.members.map(member =>
|
||||||
|
isAdmin ? (
|
||||||
|
<EditMember key={member._id} member={member} />
|
||||||
|
) : (
|
||||||
|
<ViewMember key={member._id} member={member} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
|
{project.invites.map(invite => (
|
||||||
|
<Invite key={invite._id} invite={invite} isAdmin={isAdmin} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
{isAdmin ? <SendInvites /> : <SendInvitesNotice />}
|
||||||
|
|
||||||
|
{isAdmin && (
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
<LinkSharing />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!window.ExposedSettings.recaptchaDisabled?.invite && (
|
||||||
|
<RecaptchaConditions />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case 'new-copy-bottom':
|
||||||
|
case 'default':
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isAdmin && <LinkSharing />}
|
||||||
|
|
||||||
|
<OwnerInfo />
|
||||||
|
|
||||||
|
{project.members.map(member =>
|
||||||
|
isAdmin ? (
|
||||||
|
<EditMember key={member._id} member={member} />
|
||||||
|
) : (
|
||||||
|
<ViewMember key={member._id} member={member} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
|
{project.invites.map(invite => (
|
||||||
|
<Invite key={invite._id} invite={invite} isAdmin={isAdmin} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
{isAdmin ? <SendInvites /> : <SendInvitesNotice />}
|
||||||
|
|
||||||
|
{!window.ExposedSettings.recaptchaDisabled?.invite && (
|
||||||
|
<RecaptchaConditions />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,22 +8,25 @@ import { CompileProvider } from './compile-context'
|
||||||
import { LayoutProvider } from './layout-context'
|
import { LayoutProvider } from './layout-context'
|
||||||
import { ChatProvider } from '../../features/chat/context/chat-context'
|
import { ChatProvider } from '../../features/chat/context/chat-context'
|
||||||
import { ProjectProvider } from './project-context'
|
import { ProjectProvider } from './project-context'
|
||||||
|
import { SplitTestProvider } from './split-test-context'
|
||||||
|
|
||||||
export function ContextRoot({ children, ide, settings }) {
|
export function ContextRoot({ children, ide, settings }) {
|
||||||
return (
|
return (
|
||||||
<IdeProvider ide={ide}>
|
<SplitTestProvider>
|
||||||
<UserProvider>
|
<IdeProvider ide={ide}>
|
||||||
<ProjectProvider>
|
<UserProvider>
|
||||||
<EditorProvider settings={settings}>
|
<ProjectProvider>
|
||||||
<CompileProvider>
|
<EditorProvider settings={settings}>
|
||||||
<LayoutProvider>
|
<CompileProvider>
|
||||||
<ChatProvider>{children}</ChatProvider>
|
<LayoutProvider>
|
||||||
</LayoutProvider>
|
<ChatProvider>{children}</ChatProvider>
|
||||||
</CompileProvider>
|
</LayoutProvider>
|
||||||
</EditorProvider>
|
</CompileProvider>
|
||||||
</ProjectProvider>
|
</EditorProvider>
|
||||||
</UserProvider>
|
</ProjectProvider>
|
||||||
</IdeProvider>
|
</UserProvider>
|
||||||
|
</IdeProvider>
|
||||||
|
</SplitTestProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { createContext, useContext, useMemo } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import getMeta from '../../utils/meta'
|
||||||
|
|
||||||
|
export const SplitTestContext = createContext()
|
||||||
|
|
||||||
|
SplitTestContext.Provider.propTypes = {
|
||||||
|
value: PropTypes.shape({
|
||||||
|
splitTestVariants: PropTypes.object.isRequired,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SplitTestProvider({ children }) {
|
||||||
|
const value = useMemo(
|
||||||
|
() => ({
|
||||||
|
splitTestVariants: getMeta('ol-splitTestVariants') || {},
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SplitTestContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</SplitTestContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitTestProvider.propTypes = {
|
||||||
|
children: PropTypes.any,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSplitTestContext(propTypes) {
|
||||||
|
const context = useContext(SplitTestContext)
|
||||||
|
|
||||||
|
PropTypes.checkPropTypes(
|
||||||
|
propTypes,
|
||||||
|
context,
|
||||||
|
'data',
|
||||||
|
'SplitTestContext.Provider'
|
||||||
|
)
|
||||||
|
|
||||||
|
return context
|
||||||
|
}
|
|
@ -981,6 +981,7 @@
|
||||||
"share_with_your_collabs": "Share with your collaborators",
|
"share_with_your_collabs": "Share with your collaborators",
|
||||||
"share": "Share",
|
"share": "Share",
|
||||||
"need_to_upgrade_for_more_collabs": "You need to upgrade your account to add more collaborators",
|
"need_to_upgrade_for_more_collabs": "You need to upgrade your account to add more collaborators",
|
||||||
|
"need_to_upgrade_for_more_collabs_variant": "You have reached the maximum number of collaborators. Upgrade your account to add more.",
|
||||||
"make_project_public": "Make project public",
|
"make_project_public": "Make project public",
|
||||||
"make_project_public_consequences": "If you make your project public then anyone with the URL will be able to access it.",
|
"make_project_public_consequences": "If you make your project public then anyone with the URL will be able to access it.",
|
||||||
"allow_public_editing": "Allow public editing",
|
"allow_public_editing": "Allow public editing",
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { ChatProvider } from '../../../frontend/js/features/chat/context/chat-co
|
||||||
import { IdeProvider } from '../../../frontend/js/shared/context/ide-context'
|
import { IdeProvider } from '../../../frontend/js/shared/context/ide-context'
|
||||||
import { get } from 'lodash'
|
import { get } from 'lodash'
|
||||||
import { ProjectProvider } from '../../../frontend/js/shared/context/project-context'
|
import { ProjectProvider } from '../../../frontend/js/shared/context/project-context'
|
||||||
|
import { SplitTestProvider } from '../../../frontend/js/shared/context/split-test-context'
|
||||||
|
|
||||||
export function EditorProviders({
|
export function EditorProviders({
|
||||||
user = { id: '123abd' },
|
user = { id: '123abd' },
|
||||||
|
@ -53,15 +54,17 @@ export function EditorProviders({
|
||||||
window._ide = { $scope, socket }
|
window._ide = { $scope, socket }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IdeProvider ide={window._ide}>
|
<SplitTestProvider>
|
||||||
<UserProvider>
|
<IdeProvider ide={window._ide}>
|
||||||
<ProjectProvider>
|
<UserProvider>
|
||||||
<EditorProvider settings={{}}>
|
<ProjectProvider>
|
||||||
<LayoutProvider>{children}</LayoutProvider>
|
<EditorProvider settings={{}}>
|
||||||
</EditorProvider>
|
<LayoutProvider>{children}</LayoutProvider>
|
||||||
</ProjectProvider>
|
</EditorProvider>
|
||||||
</UserProvider>
|
</ProjectProvider>
|
||||||
</IdeProvider>
|
</UserProvider>
|
||||||
|
</IdeProvider>
|
||||||
|
</SplitTestProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,14 @@ describe('ProjectController', function () {
|
||||||
},
|
},
|
||||||
getTestSegmentation: sinon.stub().yields(null, { enabled: false }),
|
getTestSegmentation: sinon.stub().yields(null, { enabled: false }),
|
||||||
}
|
}
|
||||||
|
this.SplitTestV2Handler = {
|
||||||
|
promises: {
|
||||||
|
getAssignment: sinon.stub().resolves({ active: false }),
|
||||||
|
assignInLocalsContext: sinon.stub().resolves(),
|
||||||
|
},
|
||||||
|
getAssignment: sinon.stub().yields(null, { active: false }),
|
||||||
|
assignInLocalsContext: sinon.stub().yields(null),
|
||||||
|
}
|
||||||
|
|
||||||
this.ProjectController = SandboxedModule.require(MODULE_PATH, {
|
this.ProjectController = SandboxedModule.require(MODULE_PATH, {
|
||||||
requires: {
|
requires: {
|
||||||
|
@ -137,6 +145,7 @@ describe('ProjectController', function () {
|
||||||
'@overleaf/settings': this.settings,
|
'@overleaf/settings': this.settings,
|
||||||
'@overleaf/metrics': this.Metrics,
|
'@overleaf/metrics': this.Metrics,
|
||||||
'../SplitTests/SplitTestHandler': this.SplitTestHandler,
|
'../SplitTests/SplitTestHandler': this.SplitTestHandler,
|
||||||
|
'../SplitTests/SplitTestV2Handler': this.SplitTestV2Handler,
|
||||||
'./ProjectDeleter': this.ProjectDeleter,
|
'./ProjectDeleter': this.ProjectDeleter,
|
||||||
'./ProjectDuplicator': this.ProjectDuplicator,
|
'./ProjectDuplicator': this.ProjectDuplicator,
|
||||||
'./ProjectCreationHandler': this.ProjectCreationHandler,
|
'./ProjectCreationHandler': this.ProjectCreationHandler,
|
||||||
|
|
Loading…
Reference in a new issue