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

Merge pull request #31737 from overleaf/mg-shared-ai-consent

Migrate Writefull AI consent to shared tutorial system

GitOrigin-RevId: c9298a177c9f1aa1a941c96599d6d854591f8a76
Malik Glossop 4 сар өмнө
parent
commit
a598c552a5

+ 1 - 5
services/web/frontend/extracted-translations.json

@@ -219,7 +219,6 @@
   "buy_licenses": "",
   "buy_more_licenses": "",
   "buy_now_no_exclamation_mark": "",
-  "by_continuing_you_agree_to_use_this_feature_in_line_with_your_institutions_policies": "",
   "by_email": "",
   "by_sharing_link": "",
   "by_subscribing_you_agree_to_our_terms_of_service": "",
@@ -284,7 +283,6 @@
   "choose_how_you_search_your_references": "",
   "choose_which_experiments": "",
   "citation": "",
-  "citation_checks_are_powered_by_trusted_sources_from_dimensions": "",
   "cite_a_reference_from_your_reference_manager_to_automatically_add_it_to_your_bib_file": "",
   "cite_directly_or_import_references": "",
   "cite_faster": "",
@@ -756,6 +754,7 @@
   "github_workflow_files_delete_github_repo": "",
   "github_workflow_files_error": "",
   "github_workflow_files_error_non_owner": "",
+  "give-ai-consent": "",
   "give_feedback": "",
   "go_next_page": "",
   "go_page": "",
@@ -1839,7 +1838,6 @@
   "suggestion_applied": "",
   "suggests_code_completions_while_typing": "",
   "support_for_your_browser_is_ending_soon": "",
-  "supporting_your_research_respecting_your_privacy": "",
   "supports_up_to_x_licenses": "",
   "sure_you_want_to_cancel_plan_change": "",
   "sure_you_want_to_change_plan": "",
@@ -2318,7 +2316,6 @@
   "you_have_x_licenses_and_your_plan_supports_up_to_y": "",
   "you_have_x_licenses_on_your_subscription": "",
   "you_need_to_configure_your_sso_settings": "",
-  "you_remain_in_control_please_review_all_suggestions_carefully": "",
   "you_unpaused_your_subscription": "",
   "you_will_be_able_to_reassign_subscription": "",
   "youll_get_best_results_in_visual_but_can_be_used_in_source": "",
@@ -2355,7 +2352,6 @@
   "your_plan_is_limited_to_n_editors": "",
   "your_plan_is_limited_to_n_editors_plural": "",
   "your_premium_plan_is_paused": "",
-  "your_project_content_is_never_used_for_model_training": "",
   "your_project_exceeded_collaborator_limit": "",
   "your_project_exceeded_compile_timeout_limit_on_free_plan": "",
   "your_project_near_compile_timeout_limit": "",

+ 59 - 0
services/web/frontend/js/shared/components/ai-consent-prompt-message.tsx

@@ -0,0 +1,59 @@
+import React from 'react'
+
+interface ConsentPromptMessageBodyProps {
+  className?: string
+  listClassName?: string
+}
+
+interface ConsentPromptMessageHeaderProps {
+  className?: string
+}
+
+export const AiConsentPromptMessageHeader = ({
+  className,
+}: ConsentPromptMessageHeaderProps) => {
+  return (
+    <div className={className}>
+      Supporting your research and protecting your privacy
+    </div>
+  )
+}
+
+export const AiConsentPromptMessageBody = ({
+  className,
+  listClassName,
+}: ConsentPromptMessageBodyProps) => {
+  return (
+    <div className={className}>
+      <ul className={listClassName}>
+        <li>
+          AI features in Overleaf, like this one, can help you with writing your
+          document and fixing errors.
+        </li>
+        <li>
+          As some AI features are powered by third parties, your project
+          content/data will be sent to those third parties in order to provide
+          those features to you. However, your project content/data will not be
+          used for model training. There are more details about how we use your
+          data in{' '}
+          <a
+            href="https://docs.overleaf.com/integrations-and-add-ons/ai-features#data-privacy-and-responsible-use"
+            target="_blank"
+            rel="noreferrer"
+          >
+            our docs
+          </a>
+          .
+        </li>
+        <li>
+          Like any AI, AI features in Overleaf can make mistakes, so please
+          review all suggestions carefully before accepting them.
+        </li>
+      </ul>
+      <p>
+        By using these features, you approve the use and sharing of your data in
+        this way.
+      </p>
+    </div>
+  )
+}

+ 33 - 0
services/web/frontend/js/shared/hooks/use-ai-consent.ts

@@ -0,0 +1,33 @@
+import { useCallback, useMemo, useState } from 'react'
+import useTutorial from '@/shared/hooks/promotions/use-tutorial'
+
+const AI_CONSENT_TUTORIAL_KEY = 'workbench-consent-release'
+const eventData = { name: AI_CONSENT_TUTORIAL_KEY }
+
+export { AI_CONSENT_TUTORIAL_KEY }
+
+export default function useAiConsent() {
+  const { completeTutorial, checkCompletion } = useTutorial(
+    AI_CONSENT_TUTORIAL_KEY,
+    eventData
+  )
+
+  const hasGivenAiConsent = useMemo(() => checkCompletion(), [checkCompletion])
+  const [consentError, setConsentError] = useState(false)
+
+  const giveAiConsent = useCallback(async () => {
+    setConsentError(false)
+    try {
+      await completeTutorial(
+        { event: 'promo-click', action: 'complete' },
+        { failSilently: false }
+      )
+      return true
+    } catch {
+      setConsentError(true)
+      return false
+    }
+  }, [completeTutorial])
+
+  return { hasGivenAiConsent, giveAiConsent, consentError }
+}

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

@@ -277,7 +277,6 @@
   "buy_more_licenses": "Buy more licenses",
   "buy_now_no_exclamation_mark": "Buy now",
   "by": "by",
-  "by_continuing_you_agree_to_use_this_feature_in_line_with_your_institutions_policies": "By continuing, you agree to use this feature in line with your institution’s policies.",
   "by_email": "By email",
   "by_joining_labs": "By joining Labs, you agree to receive occasional emails and updates from Overleaf—for example, to request your feedback. You also agree to our <0>terms of service</0> and <1>privacy notice</1>.",
   "by_registering_you_agree_to_our_terms_of_service": "By registering, you agree to our <0>terms of service</0> and <1>privacy notice</1>.",
@@ -356,7 +355,6 @@
   "choose_which_experiments": "Choose which experiments you’d like to try.",
   "choose_your_plan": "Choose your plan",
   "citation": "Citation",
-  "citation_checks_are_powered_by_trusted_sources_from_dimensions": "Citation checks are powered by trusted sources from Dimensions.",
   "cite_a_reference_from_your_reference_manager_to_automatically_add_it_to_your_bib_file": "Cite a reference from your reference manager to automatically add it to your .bib file.",
   "cite_directly_or_import_references": "Cite directly or import references",
   "cite_faster": "Cite faster",
@@ -963,6 +961,7 @@
   "github_workflow_files_delete_github_repo": "The repository has been created on GitHub but linking was unsuccessful. You will have to delete GitHub repository or choose a new name.",
   "github_workflow_files_error": "The __appName__ GitHub sync service couldn’t sync GitHub Workflow files (in .github/workflows/). Please authorize __appName__ to edit your GitHub workflow files and try again.",
   "github_workflow_files_error_non_owner": "The __appName__ GitHub sync service couldn’t sync GitHub Workflow files (in .github/workflows/). Please ensure the project owner has authorized __appName__ to edit this project’s GitHub workflow files and try again.",
+  "give-ai-consent": "Give AI consent",
   "give_feedback": "Give feedback",
   "global": "global",
   "go_back_and_link_accts": "<a href=\"__link__\">Go back</a> and link your accounts",
@@ -2343,7 +2342,6 @@
   "suggests_code_completions_while_typing": "Suggests code completions while typing",
   "support": "Support",
   "support_for_your_browser_is_ending_soon": "Support for your browser is ending soon",
-  "supporting_your_research_respecting_your_privacy": "Supporting your research, respecting your privacy",
   "supports_up_to_x_licenses": "Supports up to <0>__count__ licenses</0>",
   "sure_you_want_to_cancel_plan_change": "Are you sure you want to revert your scheduled plan change? You will remain subscribed to the <0>__planName__</0> plan.",
   "sure_you_want_to_change_plan": "Are you sure you want to change plan to <0>__planName__</0>?",
@@ -2891,7 +2889,6 @@
   "you_have_x_licenses_and_your_plan_supports_up_to_y": "You have allocated __addedUsersSize__ licenses and your plan supports up to __groupSize__.",
   "you_have_x_licenses_on_your_subscription": "You have __groupSize__ licenses on your subscription.",
   "you_need_to_configure_your_sso_settings": "You need to configure and test your SSO settings before enabling SSO",
-  "you_remain_in_control_please_review_all_suggestions_carefully": "You remain in control, so please review all suggestions carefully before accepting them.",
   "you_unpaused_your_subscription": "You unpaused your subscription.",
   "you_will_be_able_to_contact_us_any_time_to_share_your_feedback": "<0>You will be able to contact us</0> any time to share your feedback",
   "you_will_be_able_to_reassign_subscription": "You will be able to reassign their subscription membership to another person in your organization",
@@ -2932,7 +2929,6 @@
   "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_premium_plan_is_paused": "Your Premium plan is <0>paused</0>.",
-  "your_project_content_is_never_used_for_model_training": "Your project content is never used for model training. There are more details about how we use your data in <0>our docs</0>.",
   "your_project_exceeded_collaborator_limit": "Your project exceeded the collaborator limit and access levels were changed. Select a new access level for your collaborators, or upgrade to add more editors or reviewers.",
   "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.",

+ 4 - 2
services/web/test/frontend/features/workbench/chat.spec.tsx

@@ -1,4 +1,4 @@
-import { consentTutorialKey } from '@modules/workbench/frontend/js/components/chat'
+import { AI_CONSENT_TUTORIAL_KEY } from '@/shared/hooks/use-ai-consent'
 import Workbench from '@modules/workbench/frontend/js/components/workbench'
 import { EditorProviders } from '../../helpers/editor-providers'
 import { EditorView } from '@codemirror/view'
@@ -13,7 +13,9 @@ describe('Workbench', { scrollBehavior: false }, function () {
       win.metaAttributesCache.set('ol-splitTestVariants', {
         'ai-workbench-release': 'enabled',
       })
-      win.metaAttributesCache.set('ol-inactiveTutorials', [consentTutorialKey])
+      win.metaAttributesCache.set('ol-inactiveTutorials', [
+        AI_CONSENT_TUTORIAL_KEY,
+      ])
     })
   })
 

+ 0 - 20
services/web/test/frontend/helpers/editor-providers.tsx

@@ -55,7 +55,6 @@ import { DetachCompileContext } from '@/shared/context/detach-compile-context'
 import { type CompileContext } from '@/shared/context/local-compile-context'
 import { EditorContext } from '@/shared/context/editor-context'
 import { Cobranding } from '@ol-types/cobranding'
-import { TutorialContext } from '@/shared/context/tutorial-context'
 import { EDITOR_SESSION_ID } from '@/features/pdf-preview/util/metrics'
 
 // these constants can be imported in tests instead of
@@ -311,25 +310,6 @@ export function makeEditorProvider({
   return EditorProvider
 }
 
-export const makeTutorialProvider = (opts?: {
-  inactiveTutorials: string[]
-}) => {
-  const TutorialProvider: FC<PropsWithChildren> = ({ children }) => {
-    const value = {
-      deactivateTutorial: () => {},
-      inactiveTutorials: opts?.inactiveTutorials ?? [],
-      currentPopup: null,
-      setCurrentPopup: () => {},
-    }
-    return (
-      <TutorialContext.Provider value={value}>
-        {children}
-      </TutorialContext.Provider>
-    )
-  }
-  return TutorialProvider
-}
-
 const makeReferencesProvider = () => {
   const ReferencesProvider: FC<PropsWithChildren> = ({ children }) => {
     return (

+ 32 - 0
services/web/test/frontend/helpers/make-tutorial-provider.tsx

@@ -0,0 +1,32 @@
+import React, {
+  type FC,
+  type PropsWithChildren,
+  useCallback,
+  useState,
+} from 'react'
+import { TutorialContext } from '@/shared/context/tutorial-context'
+
+export const makeTutorialProvider = (opts?: {
+  inactiveTutorials: string[]
+}) => {
+  const TutorialProvider: FC<PropsWithChildren> = ({ children }) => {
+    const [inactiveTutorials, setInactiveTutorials] = useState<string[]>(
+      opts?.inactiveTutorials ?? []
+    )
+    const deactivateTutorial = useCallback((key: string) => {
+      setInactiveTutorials(prev => (prev.includes(key) ? prev : [...prev, key]))
+    }, [])
+    const value = {
+      deactivateTutorial,
+      inactiveTutorials,
+      currentPopup: null,
+      setCurrentPopup: () => {},
+    }
+    return (
+      <TutorialContext.Provider value={value}>
+        {children}
+      </TutorialContext.Provider>
+    )
+  }
+  return TutorialProvider
+}

+ 2 - 4
services/web/test/frontend/shared/hooks/use-tutorial.spec.tsx

@@ -1,9 +1,7 @@
 import useTutorial from '@/shared/hooks/promotions/use-tutorial'
 import { useEffect, useState } from 'react'
-import {
-  EditorProviders,
-  makeTutorialProvider,
-} from '../../helpers/editor-providers'
+import { EditorProviders } from '../../helpers/editor-providers'
+import { makeTutorialProvider } from '../../helpers/make-tutorial-provider'
 
 const TutorialTester = ({
   tutorial,