Эх сурвалжийг харах

Merge pull request #13801 from overleaf/ii-review-panel-migration-upgrade-track-changes-modal

[web] Create upgrade track changes modal

GitOrigin-RevId: 704ddef862ea9a2f8ca2bf63880da6e88f783724
ilkin-overleaf 3 жил өмнө
parent
commit
dbef201e3f

+ 7 - 0
services/web/frontend/extracted-translations.json

@@ -16,6 +16,7 @@
   "accept": "",
   "accept": "",
   "accept_all": "",
   "accept_all": "",
   "accept_invitation": "",
   "accept_invitation": "",
+  "accept_or_reject_each_changes_individually": "",
   "accepted_invite": "",
   "accepted_invite": "",
   "account_has_been_link_to_institution_account": "",
   "account_has_been_link_to_institution_account": "",
   "account_has_past_due_invoice_change_plan_warning": "",
   "account_has_past_due_invoice_change_plan_warning": "",
@@ -215,6 +216,7 @@
   "deleted_by_on": "",
   "deleted_by_on": "",
   "deleting": "",
   "deleting": "",
   "demonstrating_git_integration": "",
   "demonstrating_git_integration": "",
+  "demonstrating_track_changes_feature": "",
   "department": "",
   "department": "",
   "descending": "",
   "descending": "",
   "description": "",
   "description": "",
@@ -719,6 +721,7 @@
   "percent_discount_for_groups": "",
   "percent_discount_for_groups": "",
   "plan": "",
   "plan": "",
   "plan_tooltip": "",
   "plan_tooltip": "",
+  "please_ask_the_project_owner_to_upgrade_to_track_changes": "",
   "please_change_primary_to_remove": "",
   "please_change_primary_to_remove": "",
   "please_check_your_inbox": "",
   "please_check_your_inbox": "",
   "please_check_your_inbox_to_confirm": "",
   "please_check_your_inbox_to_confirm": "",
@@ -844,6 +847,7 @@
   "reverse_x_sort_order": "",
   "reverse_x_sort_order": "",
   "revert_pending_plan_change": "",
   "revert_pending_plan_change": "",
   "review": "",
   "review": "",
+  "review_your_peers_work": "",
   "revoke": "",
   "revoke": "",
   "revoke_invite": "",
   "revoke_invite": "",
   "rich_text_is_only_available_for_tex_files": "",
   "rich_text_is_only_available_for_tex_files": "",
@@ -879,6 +883,7 @@
   "search_whole_word": "",
   "search_whole_word": "",
   "search_within_selection": "",
   "search_within_selection": "",
   "security": "",
   "security": "",
+  "see_changes_in_your_documents_live": "",
   "select_a_file": "",
   "select_a_file": "",
   "select_a_file_figure_modal": "",
   "select_a_file_figure_modal": "",
   "select_a_payment_method": "",
   "select_a_payment_method": "",
@@ -1053,6 +1058,7 @@
   "total_per_year": "",
   "total_per_year": "",
   "total_with_subtotal_and_tax": "",
   "total_with_subtotal_and_tax": "",
   "total_words": "",
   "total_words": "",
+  "track_any_change_in_real_time": "",
   "track_changes": "",
   "track_changes": "",
   "track_changes_for_everyone": "",
   "track_changes_for_everyone": "",
   "track_changes_for_x": "",
   "track_changes_for_x": "",
@@ -1119,6 +1125,7 @@
   "upgrade_for_longer_compiles": "",
   "upgrade_for_longer_compiles": "",
   "upgrade_now": "",
   "upgrade_now": "",
   "upgrade_to_get_feature": "",
   "upgrade_to_get_feature": "",
+  "upgrade_to_track_changes": "",
   "upload": "",
   "upload": "",
   "upload_from_computer": "",
   "upload_from_computer": "",
   "upload_project": "",
   "upload_project": "",

+ 25 - 3
services/web/frontend/js/features/source-editor/components/review-panel/toolbar/toggle-menu.tsx

@@ -1,15 +1,24 @@
-import { useRef } from 'react'
+import { useState, useRef } from 'react'
 import { Trans, useTranslation } from 'react-i18next'
 import { Trans, useTranslation } from 'react-i18next'
 import Tooltip from '../../../../../shared/components/tooltip'
 import Tooltip from '../../../../../shared/components/tooltip'
 import Icon from '../../../../../shared/components/icon'
 import Icon from '../../../../../shared/components/icon'
 import TrackChangesToggle from './track-changes-toggle'
 import TrackChangesToggle from './track-changes-toggle'
+import UpgradeTrackChangesModal from '../upgrade-track-changes-modal'
 import { useProjectContext } from '../../../../../shared/context/project-context'
 import { useProjectContext } from '../../../../../shared/context/project-context'
 import {
 import {
   useReviewPanelUpdaterFnsContext,
   useReviewPanelUpdaterFnsContext,
   useReviewPanelValueContext,
   useReviewPanelValueContext,
 } from '../../../context/review-panel/review-panel-context'
 } from '../../../context/review-panel/review-panel-context'
-import classnames from 'classnames'
 import useCollapseHeight from '../hooks/use-collapse-height'
 import useCollapseHeight from '../hooks/use-collapse-height'
+import { send, sendMB } from '../../../../../infrastructure/event-tracking'
+import classnames from 'classnames'
+
+const sendAnalytics = () => {
+  send('subscription-funnel', 'editor-click-feature', 'real-time-track-changes')
+  sendMB('paywall-prompt', {
+    'paywall-type': 'track-changes',
+  })
+}
 
 
 function ToggleMenu() {
 function ToggleMenu() {
   const { t } = useTranslation()
   const { t } = useTranslation()
@@ -29,9 +38,20 @@ function ToggleMenu() {
     formattedProjectMembers,
     formattedProjectMembers,
   } = useReviewPanelValueContext()
   } = useReviewPanelValueContext()
 
 
+  const [showModal, setShowModal] = useState(false)
+
   const containerRef = useRef<HTMLUListElement | null>(null)
   const containerRef = useRef<HTMLUListElement | null>(null)
   useCollapseHeight(containerRef, shouldCollapse)
   useCollapseHeight(containerRef, shouldCollapse)
 
 
+  const handleToggleFullTCStateCollapse = () => {
+    if (project.features.trackChanges) {
+      setShouldCollapse(value => !value)
+    } else {
+      sendAnalytics()
+      setShowModal(true)
+    }
+  }
+
   return (
   return (
     <>
     <>
       <span className="review-panel-toolbar-label">
       <span className="review-panel-toolbar-label">
@@ -43,7 +63,7 @@ function ToggleMenu() {
 
 
         <button
         <button
           className="review-panel-toolbar-collapse-button"
           className="review-panel-toolbar-collapse-button"
-          onClick={() => setShouldCollapse(value => !value)}
+          onClick={handleToggleFullTCStateCollapse}
         >
         >
           {wantTrackChanges ? (
           {wantTrackChanges ? (
             // eslint-disable-next-line react/jsx-key
             // eslint-disable-next-line react/jsx-key
@@ -164,6 +184,8 @@ function ToggleMenu() {
           />
           />
         </li>
         </li>
       </ul>
       </ul>
+
+      <UpgradeTrackChangesModal show={showModal} setShow={setShowModal} />
     </>
     </>
   )
   )
 }
 }

+ 105 - 0
services/web/frontend/js/features/source-editor/components/review-panel/upgrade-track-changes-modal.tsx

@@ -0,0 +1,105 @@
+import { useTranslation } from 'react-i18next'
+import { Row, Col, Button, Modal } from 'react-bootstrap'
+import AccessibleModal from '../../../../shared/components/accessible-modal'
+import Icon from '../../../../shared/components/icon'
+import { useProjectContext } from '../../../../shared/context/project-context'
+import { useUserContext } from '../../../../shared/context/user-context'
+import { startFreeTrial, upgradePlan } from '../../../../main/account-upgrade'
+
+type UpgradeTrackChangesModalProps = {
+  show: boolean
+  setShow: React.Dispatch<React.SetStateAction<boolean>>
+}
+
+function UpgradeTrackChangesModal({
+  show,
+  setShow,
+}: UpgradeTrackChangesModalProps) {
+  const { t } = useTranslation()
+  const project = useProjectContext()
+  const user = useUserContext()
+
+  return (
+    <AccessibleModal show={show} onHide={() => setShow(false)}>
+      <Modal.Header closeButton>
+        <h3>{t('upgrade_to_track_changes')}</h3>
+      </Modal.Header>
+      <Modal.Body>
+        <div className="teaser-video-container">
+          {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
+          <video className="teaser-video" autoPlay loop>
+            <source
+              src="/img/teasers/track-changes/teaser-track-changes.mp4"
+              type="video/mp4"
+            />
+            <img
+              src="/img/teasers/track-changes/teaser-track-changes.gif"
+              alt={t('demonstrating_track_changes_feature')}
+            />
+          </video>
+        </div>
+        <h4 className="teaser-title">
+          {t('see_changes_in_your_documents_live')}
+        </h4>
+        <p className="small">{t('refresh_page_after_starting_free_trial')}</p>
+        <Row>
+          <Col md={10} mdOffset={1}>
+            <ul className="list-unstyled">
+              {[
+                t('track_any_change_in_real_time'),
+                t('review_your_peers_work'),
+                t('accept_or_reject_each_changes_individually'),
+              ].map(translation => (
+                <li key={translation}>
+                  <Icon type="check" /> {translation}
+                </li>
+              ))}
+            </ul>
+          </Col>
+        </Row>
+        {project.owner && (
+          <Row className="text-center">
+            {project.owner._id === user.id ? (
+              user.allowedFreeTrial ? (
+                <Button
+                  bsStyle={null}
+                  className="btn-primary"
+                  onClick={() => startFreeTrial('track-changes')}
+                >
+                  {t('try_it_for_free')}
+                </Button>
+              ) : (
+                <Button
+                  bsStyle={null}
+                  className="btn-primary"
+                  onClick={() => upgradePlan('project-sharing')}
+                >
+                  {t('upgrade')}
+                </Button>
+              )
+            ) : (
+              <p>
+                <strong>
+                  {t(
+                    'please_ask_the_project_owner_to_upgrade_to_track_changes'
+                  )}
+                </strong>
+              </p>
+            )}
+          </Row>
+        )}
+      </Modal.Body>
+      <Modal.Footer>
+        <Button
+          bsStyle={null}
+          className="btn-secondary"
+          onClick={() => setShow(false)}
+        >
+          {t('close')}
+        </Button>
+      </Modal.Footer>
+    </AccessibleModal>
+  )
+}
+
+export default UpgradeTrackChangesModal

+ 1 - 1
services/web/frontend/js/features/source-editor/context/review-panel/hooks/use-angular-review-panel-state.ts

@@ -1,8 +1,8 @@
 import { useState, useMemo, useCallback } from 'react'
 import { useState, useMemo, useCallback } from 'react'
 import useScopeValue from '../../../../../shared/hooks/use-scope-value'
 import useScopeValue from '../../../../../shared/hooks/use-scope-value'
 import useScopeEventEmitter from '../../../../../shared/hooks/use-scope-event-emitter'
 import useScopeEventEmitter from '../../../../../shared/hooks/use-scope-event-emitter'
-import { ReviewPanelState } from '../types/review-panel-state'
 import { sendMB } from '../../../../../infrastructure/event-tracking'
 import { sendMB } from '../../../../../infrastructure/event-tracking'
+import { ReviewPanelState } from '../types/review-panel-state'
 import * as ReviewPanel from '../types/review-panel-state'
 import * as ReviewPanel from '../types/review-panel-state'
 import { SubView } from '../../../../../../../types/review-panel/review-panel'
 import { SubView } from '../../../../../../../types/review-panel/review-panel'
 import { ReviewPanelCommentEntry } from '../../../../../../../types/review-panel/entry'
 import { ReviewPanelCommentEntry } from '../../../../../../../types/review-panel/entry'

+ 1 - 0
services/web/locales/en.json

@@ -346,6 +346,7 @@
   "deleted_files": "Deleted Files",
   "deleted_files": "Deleted Files",
   "deleting": "Deleting",
   "deleting": "Deleting",
   "demonstrating_git_integration": "Demonstrating Git integration",
   "demonstrating_git_integration": "Demonstrating Git integration",
+  "demonstrating_track_changes_feature": "Demonstrating Track Changes feature",
   "department": "Department",
   "department": "Department",
   "descending": "Descending",
   "descending": "Descending",
   "description": "Description",
   "description": "Description",

+ 17 - 0
services/web/test/frontend/features/review-panel/review-panel.spec.tsx

@@ -264,4 +264,21 @@ describe('<ReviewPanel />', function () {
     // eslint-disable-next-line mocha/no-skipped-tests
     // eslint-disable-next-line mocha/no-skipped-tests
     it.skip('add comment', function () {})
     it.skip('add comment', function () {})
   })
   })
+
+  describe('upgrade track changes', function () {
+    // eslint-disable-next-line mocha/no-skipped-tests
+    it.skip('renders modal', function () {})
+
+    // eslint-disable-next-line mocha/no-skipped-tests
+    it.skip('closes modal', function () {})
+
+    // eslint-disable-next-line mocha/no-skipped-tests
+    it.skip('opens subscription page after clicking on `upgrade`', function () {})
+
+    // eslint-disable-next-line mocha/no-skipped-tests
+    it.skip('opens subscription page after clicking on `try it for free`', function () {})
+
+    // eslint-disable-next-line mocha/no-skipped-tests
+    it.skip('shows `ask project owner to upgrade` message', function () {})
+  })
 })
 })