Merge pull request #18910 from overleaf/rh-editor-warning-over-limit

[web] Show warning modal to editors opening a project over collaborator limit

GitOrigin-RevId: d9868c021d0aaf04bffd8afbd8c1c96fbf548755
This commit is contained in:
roo hutton 2024-06-27 12:53:29 +01:00 committed by Copybot
parent cbda6b0bcc
commit d16fee1afe
9 changed files with 167 additions and 11 deletions

View file

@ -307,6 +307,7 @@
"discount_of": "",
"dismiss_error_popup": "",
"display_deleted_user": "",
"do_you_need_edit_access": "",
"do_you_want_to_change_your_primary_email_address_to": "",
"do_you_want_to_overwrite_it": "",
"do_you_want_to_overwrite_it_plural": "",
@ -451,6 +452,7 @@
"free_plan_label": "",
"free_plan_tooltip": "",
"from_another_project": "",
"from_enforcement_date": "",
"from_external_url": "",
"from_project_files": "",
"from_provider": "",
@ -710,6 +712,7 @@
"license_for_educational_purposes": "",
"limited_offer": "",
"limited_to_n_editors_per_project": "",
"limited_to_n_editors_per_project_plural": "",
"line_height": "",
"line_width_is_the_width_of_the_line_in_the_current_environment": "",
"link": "",
@ -1388,6 +1391,7 @@
"this_grants_access_to_features_2": "",
"this_is_a_labs_experiment": "",
"this_project_exceeded_compile_timeout_limit_on_free_plan": "",
"this_project_has_more_than_max_collabs": "",
"this_project_is_public": "",
"this_project_is_public_read_only": "",
"this_project_will_appear_in_your_dropbox_folder_at": "",
@ -1406,6 +1410,7 @@
"to_fix_this_you_can": "",
"to_fix_this_you_can_ask_the_github_repository_owner": "",
"to_insert_or_move_a_caption_make_sure_tabular_is_directly_within_table": "",
"to_keep_edit_access": "",
"to_modify_your_subscription_go_to": "",
"to_use_text_wrapping_in_your_table_make_sure_you_include_the_array_package": "",
"toggle_compile_options_menu": "",
@ -1646,6 +1651,7 @@
"you_can_now_enable_sso": "",
"you_can_now_log_in_sso": "",
"you_can_only_add_n_people_to_edit_a_project": "",
"you_can_only_add_n_people_to_edit_a_project_plural": "",
"you_can_request_a_maximum_of_limit_fixes_per_day": "",
"you_cant_add_or_change_password_due_to_sso": "",
"you_cant_join_this_group_subscription": "",
@ -1678,6 +1684,7 @@
"your_plan": "",
"your_plan_is_changing_at_term_end": "",
"your_plan_is_limited_to_n_editors": "",
"your_plan_is_limited_to_n_editors_plural": "",
"your_project_exceeded_compile_timeout_limit_on_free_plan": "",
"your_project_near_compile_timeout_limit": "",
"your_projects": "",

View file

@ -5,6 +5,8 @@ import * as eventTracking from '@/infrastructure/event-tracking'
import EditorNavigationToolbarRoot from '@/features/editor-navigation-toolbar/components/editor-navigation-toolbar-root'
import NewShareProjectModal from '@/features/share-project-modal/components/restricted-link-sharing/share-project-modal'
import ShareProjectModal from '@/features/share-project-modal/components/share-project-modal'
import EditorOverLimitModal from '@/features/share-project-modal/components/restricted-link-sharing/editor-over-limit-modal'
import getMeta from '@/utils/meta'
function EditorNavigationToolbar() {
@ -31,10 +33,13 @@ function EditorNavigationToolbar() {
openShareProjectModal={handleOpenShareModal}
/>
{showNewShareModal ? (
<NewShareProjectModal
show={showShareModal}
handleHide={handleHideShareModal}
/>
<>
<EditorOverLimitModal />
<NewShareProjectModal
show={showShareModal}
handleHide={handleHideShareModal}
/>
</>
) : (
<ShareProjectModal
show={showShareModal}

View file

@ -2,16 +2,28 @@ import { useTranslation } from 'react-i18next'
import { Button } from 'react-bootstrap'
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'
export default function AddCollaboratorsUpgrade() {
const { t } = useTranslation()
const { features } = useProjectContext()
return (
<div className="add-collaborators-upgrade">
<Notification
type="warning"
title={t('editor_limit_exceeded_in_this_project')}
content={<p>{t('your_plan_is_limited_to_n_editors')}</p>}
content={
<p>
{t('your_plan_is_limited_to_n_editors', {
count: features.collaborators,
})}{' '}
{t('from_enforcement_date', {
enforcementDate: linkSharingEnforcementDate,
})}
</p>
}
action={
<div className="upgrade-actions">
<Button

View file

@ -2,9 +2,11 @@ import { Button } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import Notification from '@/shared/components/notification'
import { upgradePlan } from '@/main/account-upgrade'
import { useProjectContext } from '@/shared/context/project-context'
export default function CollaboratorsLimitUpgrade() {
const { t } = useTranslation()
const { features } = useProjectContext()
return (
<div>
@ -12,7 +14,13 @@ export default function CollaboratorsLimitUpgrade() {
type="info"
customIcon={<div />}
title={t('upgrade_to_add_more_editors')}
content={<p>{t('you_can_only_add_n_people_to_edit_a_project')}</p>}
content={
<p>
{t('you_can_only_add_n_people_to_edit_a_project', {
count: features.collaborators,
})}
</p>
}
action={
<Button
bsSize="sm"

View file

@ -11,6 +11,7 @@ import { sendMB } from '@/infrastructure/event-tracking'
import { Select } from '@/shared/components/select'
import type { ProjectContextMember } from '@/shared/context/types/project-context'
import { PermissionsLevel } from '@/features/ide-react/types/permissions'
import { linkSharingEnforcementDate } from '../../utils/link-sharing'
type PermissionsOption = PermissionsLevel | 'removeAccess'
@ -129,7 +130,7 @@ export default function EditMember({
{shouldWarnMember() && (
<div className="subtitle">
{t('will_lose_edit_access_on_date', {
date: '[date]',
date: linkSharingEnforcementDate,
})}
</div>
)}
@ -180,6 +181,7 @@ function SelectPrivilege({
canAddCollaborators,
}: SelectPrivilegeProps) {
const { t } = useTranslation()
const { features } = useProjectContext()
const privileges = useMemo(
(): Privilege[] => [
@ -195,7 +197,7 @@ function SelectPrivilege({
return !canAddCollaborators &&
privilege === 'readAndWrite' &&
value !== 'readAndWrite'
? t('limited_to_n_editors_per_project')
? t('limited_to_n_editors_per_project', { count: features.collaborators })
: ''
}

View file

@ -0,0 +1,44 @@
import { Button, Modal } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { linkSharingEnforcementDate } from '../../utils/link-sharing'
type EditorOverLimitModalContentProps = {
handleHide: () => void
}
export default function EditorOverLimitModalContent({
handleHide,
}: EditorOverLimitModalContentProps) {
const { t } = useTranslation()
return (
<>
<Modal.Header closeButton>
<Modal.Title>{t('do_you_need_edit_access')}</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
{t('this_project_has_more_than_max_collabs', {
linkSharingDate: linkSharingEnforcementDate,
})}
</p>
<p>{t('to_keep_edit_access')}</p>
</Modal.Body>
<Modal.Footer>
<Button
bsStyle={null}
className="btn-secondary"
href="/blog/changes-to-project-sharing"
target="_blank"
rel="noreferrer"
>
{t('learn_more')}
</Button>
<Button className="btn-primary" onClick={handleHide}>
{t('ok')}
</Button>
</Modal.Footer>
</>
)
}

View file

@ -0,0 +1,65 @@
import { useEffect, useState } from 'react'
import AccessibleModal from '@/shared/components/accessible-modal'
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'
const EditorOverLimitModal = () => {
const [show, setShow] = useState(false)
const { isProjectOwner, permissionsLevel } = useEditorContext()
const { members, features, _id: projectId } = useProjectContext()
const handleHide = () => {
setShow(false)
}
// split test: link-sharing-warning
// show the over-limit warning if user
// is editor on a project over
// collaborator limit (once every 24 hours)
useEffect(() => {
const showModalCooldownHours = 24
const hasExceededCollaboratorLimit = () => {
if (isProjectOwner || !features || permissionsLevel === 'readOnly') {
return false
}
if (features.collaborators === -1) {
return false
}
return (
members.filter(member => member.privileges === 'readAndWrite').length >
(features.collaborators ?? 1)
)
}
if (hasExceededCollaboratorLimit()) {
const localStorageKey = `last-shown-need-edit-modal.${projectId}`
const lastShownNeedEditModalTime =
customLocalStorage.getItem(localStorageKey)
if (
!lastShownNeedEditModalTime ||
lastShownNeedEditModalTime + showModalCooldownHours * 60 * 60 * 1000 <
Date.now()
) {
setShow(true)
customLocalStorage.setItem(localStorageKey, Date.now())
}
}
}, [features, isProjectOwner, members, permissionsLevel, projectId])
return show ? (
<AccessibleModal
animation
show={show}
onHide={handleHide}
id="editor-over-limit-modal"
>
<EditorOverLimitModalContent handleHide={handleHide} />
</AccessibleModal>
) : null
}
export default EditorOverLimitModal

View file

@ -0,0 +1,6 @@
import { formatDate } from '@/utils/dates'
export const linkSharingEnforcementDate = formatDate(
new Date(2024, 6, 29),
'MMMM Do'
) // July 29th 2024

View file

@ -435,6 +435,7 @@
"display_deleted_user": "Display deleted users",
"do_not_have_acct_or_do_not_want_to_link": "If you dont have an <b>__appName__</b> account, or if you dont want to link to your <b>__institutionName__</b> account, please click <b>__clickText__</b>.",
"do_not_link_accounts": "Dont link accounts",
"do_you_need_edit_access": "Do you need edit access?",
"do_you_want_to_change_your_primary_email_address_to": "Do you want to change your primary email address to <b>__email__</b>?",
"do_you_want_to_overwrite_it": "Do you want to overwrite it?",
"do_you_want_to_overwrite_it_plural": "Do you want to overwrite them?",
@ -676,6 +677,7 @@
"free_plan_tooltip": "Click to find out how you could benefit from Overleaf premium features.",
"frequently_asked_questions": "frequently asked questions",
"from_another_project": "From another project",
"from_enforcement_date": "From __enforcementDate__ any additional editors on this project will be made viewers.",
"from_external_url": "From external URL",
"from_project_files": "From project files",
"from_provider": "From __provider__",
@ -1040,7 +1042,8 @@
"license": "License",
"license_for_educational_purposes": "This license is for educational purposes (applies to students or faculty using __appName__ for teaching)",
"limited_offer": "Limited offer",
"limited_to_n_editors_per_project": "Limited to n editors per project",
"limited_to_n_editors_per_project": "Limited to __count__ editor per project",
"limited_to_n_editors_per_project_plural": "Limited to __count__ editors per project",
"line_height": "Line Height",
"line_width_is_the_width_of_the_line_in_the_current_environment": "Line width is the width of the line in the current environment. e.g. a full page width in single-column layout or half a page width in a two-column layout.",
"link": "Link",
@ -1987,6 +1990,7 @@
"this_is_a_labs_experiment": "This is a Labs experiment",
"this_is_your_template": "This is your template from your project",
"this_project_exceeded_compile_timeout_limit_on_free_plan": "This project exceeded the compile timeout limit on our free plan.",
"this_project_has_more_than_max_collabs": "This project has more than the maximum number of collaborators allowed on the project owners Overleaf plan. This means you could lose edit access from __linkSharingDate__.",
"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 ",
@ -2009,6 +2013,7 @@
"to_fix_this_you_can": "To fix this, you can:",
"to_fix_this_you_can_ask_the_github_repository_owner": "To fix this, you can ask the GitHub repository owner (<0>__repoOwnerEmail__</0>) to renew their __appName__ subscription and reconnect the project.",
"to_insert_or_move_a_caption_make_sure_tabular_is_directly_within_table": "To insert or move a caption, make sure \\begin{tabular} is directly within a table environment",
"to_keep_edit_access": "To keep edit access, ask the project owner to upgrade their plan or reduce the number of people with edit access.",
"to_many_login_requests_2_mins": "This account has had too many login requests. Please wait 2 minutes before trying to log in again",
"to_modify_your_subscription_go_to": "To modify your subscription go to",
"to_use_text_wrapping_in_your_table_make_sure_you_include_the_array_package": "<0>Please note:</0> To use text wrapping in your table, make sure you include the <1>array</1> package in your document preamble:",
@ -2297,7 +2302,8 @@
"you_can_also_choose_to_view_anonymously_or_leave_the_project": "You can also choose to <0>view anonymously</0> (you will lose edit access) or <1>leave the project</1>.",
"you_can_now_enable_sso": "You can now enable SSO on your Group settings page.",
"you_can_now_log_in_sso": "You can now log in through your institution and if eligible you will receive <0>__appName__ Professional features</0>.",
"you_can_only_add_n_people_to_edit_a_project": "You can only add X people to edit a project with you on your current plan. Upgrade to add more.",
"you_can_only_add_n_people_to_edit_a_project": "You can only add __count__ person to edit a project with you on your current plan. Upgrade to add more.",
"you_can_only_add_n_people_to_edit_a_project_plural": "You can only add __count__ people to edit a project with you on your current plan. Upgrade to add more.",
"you_can_opt_in_and_out_of_the_program_at_any_time_on_this_page": "You can <0>opt in and out</0> of the program at any time on this page",
"you_can_request_a_maximum_of_limit_fixes_per_day": "You can request a maximum of __limit__ fixes per day. Please try again tomorrow.",
"you_cant_add_or_change_password_due_to_sso": "You cant add or change your password because your group or organization uses <0>single sign-on (SSO)</0>.",
@ -2339,7 +2345,8 @@
"your_password_was_detected": "Your password is on a <0>public list of known compromised passwords</0>. Keep your account safe by changing your password now.",
"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_plan_is_limited_to_n_editors": "Your plan allows [n] collaborators with edit access and unlimited viewers. From [date] any additional editors on this project will be made viewers.",
"your_plan_is_limited_to_n_editors": "Your plan allows __count__ collaborator with edit access and unlimited viewers.",
"your_plan_is_limited_to_n_editors_plural": "Your plan allows __count__ collaborators with edit access and unlimited viewers.",
"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",