mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
Merge pull request #19206 from overleaf/tm-link-sharing-changes-events
Implement link sharing warning events by reusing the StartFreeTrial paywall button component from the original modal GitOrigin-RevId: 9c16407ad8a7b5afc9b5b13be1491ef903ae74a3
This commit is contained in:
parent
a419fdf1a8
commit
566466185b
7 changed files with 124 additions and 27 deletions
|
@ -44,6 +44,7 @@ const TutorialHandler = require('../Tutorial/TutorialHandler')
|
|||
const UserUpdater = require('../User/UserUpdater')
|
||||
const { checkUserPermissions } =
|
||||
require('../Authorization/PermissionsManager').promises
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
|
||||
/**
|
||||
* @typedef {import("./types").GetProjectsRequest} GetProjectsRequest
|
||||
|
@ -400,6 +401,8 @@ const _ProjectController = {
|
|||
brandVariationId: 1,
|
||||
overleaf: 1,
|
||||
tokens: 1,
|
||||
tokenAccessReadAndWrite_refs: 1, // used for link sharing analytics
|
||||
collaberator_refs: 1, // used for link sharing analytics
|
||||
}),
|
||||
userIsMemberOfGroupSubscription: sessionUser
|
||||
? LimitationsManager.promises.userIsMemberOfGroupSubscription(
|
||||
|
@ -546,12 +549,24 @@ const _ProjectController = {
|
|||
logger.error({ err }, 'failed to update split test info in session')
|
||||
)
|
||||
if (userId) {
|
||||
const ownerFeatures = await UserGetter.promises.getUserFeatures(
|
||||
project.owner_ref
|
||||
)
|
||||
const projectOpenedSegmentation = {
|
||||
projectId: project._id,
|
||||
// temporary link sharing segmentation:
|
||||
linkSharingWarning: linkSharingChanges?.variant,
|
||||
namedEditors: project.collaberator_refs?.length || 0,
|
||||
tokenEditors: project.tokenAccessReadAndWrite_refs?.length || 0,
|
||||
planLimit: ownerFeatures?.collaborators || 0,
|
||||
}
|
||||
projectOpenedSegmentation.exceedAtLimit =
|
||||
projectOpenedSegmentation.namedEditors >=
|
||||
projectOpenedSegmentation.planLimit
|
||||
AnalyticsManager.recordEventForUserInBackground(
|
||||
userId,
|
||||
'project-opened',
|
||||
{
|
||||
projectId: project._id,
|
||||
}
|
||||
projectOpenedSegmentation
|
||||
)
|
||||
User.updateOne(
|
||||
{ _id: new ObjectId(userId) },
|
||||
|
@ -619,12 +634,6 @@ const _ProjectController = {
|
|||
? 'project/ide-react-detached'
|
||||
: 'project/ide-react'
|
||||
|
||||
const assignLink = await SplitTestHandler.promises.getAssignmentForUser(
|
||||
project.owner_ref,
|
||||
'link-sharing-warning'
|
||||
)
|
||||
const linkSharingWarning = assignLink.variant === 'active'
|
||||
|
||||
res.render(template, {
|
||||
title: project.name,
|
||||
priority_title: true,
|
||||
|
@ -698,7 +707,7 @@ const _ProjectController = {
|
|||
optionalPersonalAccessToken,
|
||||
hasTrackChangesFeature: Features.hasFeature('track-changes'),
|
||||
projectTags,
|
||||
linkSharingWarning,
|
||||
linkSharingWarning: linkSharingChanges.variant === 'active',
|
||||
})
|
||||
timer.done()
|
||||
} catch (err) {
|
||||
|
|
|
@ -4,14 +4,19 @@ import Notification from '@/shared/components/notification'
|
|||
import { upgradePlan } from '../../../../main/account-upgrade'
|
||||
import { linkSharingEnforcementDate } from '../../utils/link-sharing'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
|
||||
|
||||
export default function AddCollaboratorsUpgrade() {
|
||||
const { t } = useTranslation()
|
||||
const { features } = useProjectContext()
|
||||
const user = useUserContext()
|
||||
|
||||
return (
|
||||
<div className="add-collaborators-upgrade">
|
||||
<Notification
|
||||
isActionBelowContent
|
||||
type="warning"
|
||||
title={t('editor_limit_exceeded_in_this_project')}
|
||||
content={
|
||||
|
@ -26,20 +31,41 @@ export default function AddCollaboratorsUpgrade() {
|
|||
}
|
||||
action={
|
||||
<div className="upgrade-actions">
|
||||
{user.allowedFreeTrial ? (
|
||||
<StartFreeTrialButton
|
||||
buttonProps={{ variant: 'secondary', size: 'small' }}
|
||||
source="project-sharing"
|
||||
variant="exceeds"
|
||||
>
|
||||
{t('upgrade')}
|
||||
</StartFreeTrialButton>
|
||||
) : (
|
||||
<Button
|
||||
bsSize="sm"
|
||||
className="btn-secondary"
|
||||
onClick={() => {
|
||||
upgradePlan('project-sharing')
|
||||
}}
|
||||
>
|
||||
{t('upgrade')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
bsSize="sm"
|
||||
className="btn-secondary"
|
||||
onClick={() => upgradePlan('project-sharing')}
|
||||
>
|
||||
{t('upgrade')}
|
||||
</Button>
|
||||
<a
|
||||
href="https://www.overleaf.com/blog/changes-to-project-sharing"
|
||||
bsSize="sm"
|
||||
className="btn-link"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={() => {
|
||||
sendMB('paywall-info-click', {
|
||||
'paywall-type': 'project-sharing',
|
||||
content: 'blog',
|
||||
variant: 'exceeds',
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('read_more')}
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -3,10 +3,13 @@ import { useTranslation } from 'react-i18next'
|
|||
import Notification from '@/shared/components/notification'
|
||||
import { upgradePlan } from '@/main/account-upgrade'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import { useUserContext } from '@/shared/context/user-context'
|
||||
import StartFreeTrialButton from '@/shared/components/start-free-trial-button'
|
||||
|
||||
export default function CollaboratorsLimitUpgrade() {
|
||||
const { t } = useTranslation()
|
||||
const { features } = useProjectContext()
|
||||
const user = useUserContext()
|
||||
|
||||
return (
|
||||
<div className="invite-warning">
|
||||
|
@ -22,13 +25,25 @@ export default function CollaboratorsLimitUpgrade() {
|
|||
</p>
|
||||
}
|
||||
action={
|
||||
<Button
|
||||
bsSize="sm"
|
||||
className="btn-secondary"
|
||||
onClick={() => upgradePlan('project-sharing')}
|
||||
>
|
||||
{t('upgrade')}
|
||||
</Button>
|
||||
user.allowedFreeTrial ? (
|
||||
<StartFreeTrialButton
|
||||
buttonProps={{ variant: 'secondary', size: 'small' }}
|
||||
source="project-sharing"
|
||||
variant="limit"
|
||||
>
|
||||
{t('upgrade')}
|
||||
</StartFreeTrialButton>
|
||||
) : (
|
||||
<Button
|
||||
bsSize="sm"
|
||||
className="btn-secondary"
|
||||
onClick={() => {
|
||||
upgradePlan('project-sharing')
|
||||
}}
|
||||
>
|
||||
{t('upgrade')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Button, Modal } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { linkSharingEnforcementDate } from '../../utils/link-sharing'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
|
||||
type EditorOverLimitModalContentProps = {
|
||||
handleHide: () => void
|
||||
|
@ -32,10 +33,25 @@ export default function EditorOverLimitModalContent({
|
|||
href="/blog/changes-to-project-sharing"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={() => {
|
||||
sendMB('notification-click', {
|
||||
name: 'link-sharing-collaborator-limit',
|
||||
button: 'learn',
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('learn_more')}
|
||||
</Button>
|
||||
<Button className="btn-primary" onClick={handleHide}>
|
||||
<Button
|
||||
className="btn-primary"
|
||||
onClick={() => {
|
||||
sendMB('notification-click', {
|
||||
name: 'link-sharing-collaborator-limit',
|
||||
button: 'ok',
|
||||
})
|
||||
handleHide()
|
||||
}}
|
||||
>
|
||||
{t('ok')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
|
|
|
@ -4,6 +4,7 @@ import EditorOverLimitModalContent from './editor-over-limit-modal-content'
|
|||
import customLocalStorage from '@/infrastructure/local-storage'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import { useEditorContext } from '@/shared/context/editor-context'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
|
||||
const EditorOverLimitModal = () => {
|
||||
const [show, setShow] = useState(false)
|
||||
|
@ -46,6 +47,9 @@ const EditorOverLimitModal = () => {
|
|||
) {
|
||||
setShow(true)
|
||||
customLocalStorage.setItem(localStorageKey, Date.now())
|
||||
sendMB('notification-prompt', {
|
||||
name: 'link-sharing-collaborator-limit',
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [features, isProjectOwner, members, permissionsLevel, projectId])
|
||||
|
@ -54,7 +58,12 @@ const EditorOverLimitModal = () => {
|
|||
<AccessibleModal
|
||||
animation
|
||||
show={show}
|
||||
onHide={handleHide}
|
||||
onHide={() => {
|
||||
sendMB('notification-dismiss', {
|
||||
name: 'link-sharing-collaborator-limit',
|
||||
})
|
||||
handleHide()
|
||||
}}
|
||||
id="editor-over-limit-modal"
|
||||
>
|
||||
<EditorOverLimitModalContent handleHide={handleHide} />
|
||||
|
|
|
@ -8,6 +8,7 @@ import { postJSON } from '@/infrastructure/fetch-json'
|
|||
import { debugConsole } from '@/utils/debugging'
|
||||
import useAsync from '@/shared/hooks/use-async'
|
||||
import Notification from '@/shared/components/notification'
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
|
||||
function SharingUpdatesRoot() {
|
||||
const { isReady } = useWaitForI18n()
|
||||
|
@ -16,6 +17,10 @@ function SharingUpdatesRoot() {
|
|||
const projectId = getMeta('ol-project_id')
|
||||
|
||||
const joinProject = useCallback(() => {
|
||||
sendMB('notification-click', {
|
||||
name: 'link-sharing-collaborator',
|
||||
button: 'ok',
|
||||
})
|
||||
runAsync(postJSON(`/project/${projectId}/sharing-updates/join`))
|
||||
.then(() => {
|
||||
location.assign(`/project/${projectId}`)
|
||||
|
@ -24,6 +29,10 @@ function SharingUpdatesRoot() {
|
|||
}, [runAsync, projectId])
|
||||
|
||||
const viewProject = useCallback(() => {
|
||||
sendMB('notification-click', {
|
||||
name: 'link-sharing-collaborator',
|
||||
button: 'anonymous',
|
||||
})
|
||||
runAsync(postJSON(`/project/${projectId}/sharing-updates/view`))
|
||||
.then(() => {
|
||||
location.assign(`/project/${projectId}`)
|
||||
|
@ -32,6 +41,10 @@ function SharingUpdatesRoot() {
|
|||
}, [runAsync, projectId])
|
||||
|
||||
const leaveProject = useCallback(() => {
|
||||
sendMB('notification-click', {
|
||||
name: 'link-sharing-collaborator',
|
||||
button: 'leave',
|
||||
})
|
||||
runAsync(postJSON(`/project/${projectId}/leave`))
|
||||
.then(() => {
|
||||
location.assign('/project')
|
||||
|
@ -67,6 +80,12 @@ function SharingUpdatesRoot() {
|
|||
href="/blog/changes-to-project-sharing"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
onClick={() => {
|
||||
sendMB('notification-click', {
|
||||
name: 'link-sharing-collaborator',
|
||||
button: 'learn',
|
||||
})
|
||||
}}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
|
|
|
@ -135,6 +135,9 @@ describe('ProjectController', function () {
|
|||
this.UserGetter = {
|
||||
getUserFullEmails: sinon.stub().yields(null, []),
|
||||
getUser: sinon.stub().resolves({ lastLoginIp: '192.170.18.2' }),
|
||||
promises: {
|
||||
getUserFeatures: sinon.stub().resolves(null, { collaborators: 1 }),
|
||||
},
|
||||
}
|
||||
this.Features = {
|
||||
hasFeature: sinon.stub(),
|
||||
|
|
Loading…
Reference in a new issue