From 0baaed6cdf705058638d39089730f2ea86d5d591 Mon Sep 17 00:00:00 2001 From: Jimmy Domagala-Tang Date: Mon, 27 Jan 2025 09:39:49 -0500 Subject: [PATCH] Merge pull request #22994 from overleaf/jdt-diasble-assist Add a toggle to hide all AI features on a users account GitOrigin-RevId: 978b02609c40f3975daa267aa6c10ac49e13d6f3 --- .../src/Features/Project/ProjectController.js | 9 ++++-- .../src/Features/User/UserPagesController.mjs | 3 ++ services/web/app/src/models/User.js | 4 ++- .../web/frontend/extracted-translations.json | 11 ++++++-- .../settings/components/linking-section.tsx | 2 +- .../components/linking/enable-widget.tsx | 12 ++++++-- .../stylesheets/app/account-settings.less | 6 ++++ .../bootstrap-5/pages/account-settings.scss | 6 ++++ services/web/locales/en.json | 11 ++++++-- .../scripts/back_fill_hiding_ai_features.mjs | 28 +++++++++++++++++++ services/web/types/user.ts | 3 ++ 11 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 services/web/scripts/back_fill_hiding_ai_features.mjs diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index e69d5d7a66..27f60dade8 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -364,7 +364,7 @@ const _ProjectController = { user: (async () => { const user = await User.findById( userId, - 'email first_name last_name referal_id signUpDate featureSwitches features featuresEpoch refProviders alphaProgram betaProgram isAdmin ace labsProgram completedTutorials writefull' + 'email first_name last_name referal_id signUpDate featureSwitches features featuresEpoch refProviders alphaProgram betaProgram isAdmin ace labsProgram completedTutorials writefull aiErrorAssistant' ).exec() // Handle case of deleted user if (!user) { @@ -675,11 +675,14 @@ const _ProjectController = { subscription && !subscription.recurlySubscription_id const hasManuallyCollectedSubscription = subscription?.collectionMethod === 'manual' + const cannotPurchaseAddons = + hasNonRecurlySubscription || hasManuallyCollectedSubscription + const assistantDisabled = user.aiErrorAssistant?.enabled === false // the assistant has been manually disabled by the user const canUseErrorAssistant = user.features?.aiErrorAssistant || (splitTestAssignments['ai-add-on']?.variant === 'enabled' && - !hasNonRecurlySubscription && - !hasManuallyCollectedSubscription) + !cannotPurchaseAddons && + !assistantDisabled) let featureUsage = {} diff --git a/services/web/app/src/Features/User/UserPagesController.mjs b/services/web/app/src/Features/User/UserPagesController.mjs index 3c9e747d3b..55bbd136b9 100644 --- a/services/web/app/src/Features/User/UserPagesController.mjs +++ b/services/web/app/src/Features/User/UserPagesController.mjs @@ -153,6 +153,9 @@ async function settingsPage(req, res) { writefull: { enabled: Boolean(user.writefull?.enabled), }, + aiErrorAssistant: { + enabled: Boolean(user.aiErrorAssistant?.enabled), + }, }, hasPassword: !!user.hashedPassword, shouldAllowEditingDetails, diff --git a/services/web/app/src/models/User.js b/services/web/app/src/models/User.js index c3bca59add..7f6081efe2 100644 --- a/services/web/app/src/models/User.js +++ b/services/web/app/src/models/User.js @@ -169,7 +169,6 @@ const UserSchema = new Schema( zotero: { type: Boolean }, referencesSearch: { type: Boolean }, symbolPalette: { type: Boolean }, - compileAssistant: { type: Boolean }, }, }, ], @@ -196,6 +195,9 @@ const UserSchema = new Schema( enabled: { type: Boolean, default: null }, autoCreatedAccount: { type: Boolean, default: false }, }, + aiErrorAssistant: { + enabled: { type: Boolean, default: true }, + }, alphaProgram: { type: Boolean, default: false }, // experimental features betaProgram: { type: Boolean, default: false }, labsProgram: { type: Boolean, default: false }, diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 22cd554481..f595664679 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -18,6 +18,7 @@ "a_new_reference_was_added_from_provider": "", "a_new_reference_was_added_to_file": "", "a_new_reference_was_added_to_file_from_provider": "", + "about_error_assist": "", "about_to_archive_projects": "", "about_to_delete_cert": "", "about_to_delete_projects": "", @@ -29,6 +30,7 @@ "about_to_leave_project": "", "about_to_leave_projects": "", "about_to_trash_projects": "", + "about_writefull": "", "accept": "", "accept_all": "", "accept_and_continue": "", @@ -101,7 +103,10 @@ "aggregate_changed": "", "aggregate_to": "", "agree_with_the_terms": "", + "ai_assistance_to_help_you": "", + "ai_based_language_tools": "", "ai_can_make_mistakes": "", + "ai_features": "", "ai_feedback_do_you_have_any_thoughts_or_suggestions": "", "ai_feedback_tell_us_what_was_wrong_so_we_can_improve": "", "ai_feedback_the_answer_was_too_long": "", @@ -371,6 +376,7 @@ "dictionary": "", "did_you_know_institution_providing_professional": "", "disable": "", + "disable_ai_features": "", "disable_equation_preview": "", "disable_equation_preview_confirm": "", "disable_equation_preview_enable": "", @@ -464,6 +470,7 @@ "emails_and_affiliations_explanation": "", "emails_and_affiliations_title": "", "empty": "", + "enable_ai_features": "", "enable_managed_users": "", "enable_single_sign_on": "", "enable_sso": "", @@ -478,6 +485,7 @@ "enter_the_number_of_users_youd_like_to_add_to_see_the_cost_breakdown": "", "equation_preview": "", "error": "", + "error_assist": "", "error_opening_document": "", "error_opening_document_detail": "", "error_performing_request": "", @@ -808,7 +816,6 @@ "labels_help_you_to_easily_reference_your_figures": "", "labels_help_you_to_reference_your_tables": "", "language": "", - "language_feedback": "", "large_or_high-resolution_images_taking_too_long": "", "last_active": "", "last_active_description": "", @@ -1920,10 +1927,8 @@ "work_with_non_overleaf_users": "", "work_with_other_github_users": "", "writefull": "", - "writefull_learn_more": "", "writefull_loading_error_body": "", "writefull_loading_error_title": "", - "writefull_settings_description": "", "x_changes_in": "", "x_changes_in_plural": "", "x_libraries_accessed_in_this_project": "", diff --git a/services/web/frontend/js/features/settings/components/linking-section.tsx b/services/web/frontend/js/features/settings/components/linking-section.tsx index 38d2dd8e0e..69d2dc268e 100644 --- a/services/web/frontend/js/features/settings/components/linking-section.tsx +++ b/services/web/frontend/js/features/settings/components/linking-section.tsx @@ -91,7 +91,7 @@ function LinkingSection() { {haslangFeedbackLinkingWidgets ? ( <>

- {t('language_feedback')} + {t('ai_features')}

{langFeedbackLinkingWidgets.map( diff --git a/services/web/frontend/js/features/settings/components/linking/enable-widget.tsx b/services/web/frontend/js/features/settings/components/linking/enable-widget.tsx index 23ad1f0ad6..2568b520fb 100644 --- a/services/web/frontend/js/features/settings/components/linking/enable-widget.tsx +++ b/services/web/frontend/js/features/settings/components/linking/enable-widget.tsx @@ -80,16 +80,22 @@ type ActionButtonProps = { handleUnlinkClick: () => void handleLinkClick: () => void disabled?: boolean + linkText?: string + unlinkText?: string } -function ActionButton({ +export function ActionButton({ linked, handleUnlinkClick, handleLinkClick, hasFeature, disabled, + linkText, + unlinkText, }: ActionButtonProps) { const { t } = useTranslation() + const linkingText = linkText || t('turn_on') + const unlinkingText = unlinkText || t('turn_off') if (!hasFeature) { return ( - {t('turn_off')} + {unlinkingText} ) } else { @@ -117,7 +123,7 @@ function ActionButton({ disabled={disabled} onClick={handleLinkClick} > - {t('turn_on')} + {linkingText} ) } diff --git a/services/web/frontend/stylesheets/app/account-settings.less b/services/web/frontend/stylesheets/app/account-settings.less index 6c19429ca8..c85fe6b18e 100644 --- a/services/web/frontend/stylesheets/app/account-settings.less +++ b/services/web/frontend/stylesheets/app/account-settings.less @@ -173,6 +173,12 @@ tbody > tr.affiliations-table-warning-row > td { height: 40px; } + .dual-logo { + display: flex; + justify-content: space-evenly; + height: 100%; + } + .description-container { flex-grow: 1; } diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/account-settings.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/account-settings.scss index 611c663e45..3b6f0d63f2 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/account-settings.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/account-settings.scss @@ -100,6 +100,12 @@ flex-grow: 1; } + .dual-logo { + display: flex; + justify-content: space-evenly; + height: 100%; + } + .title-row { display: flex; align-items: center; diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 07683aaf56..b0470edaa4 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -21,6 +21,7 @@ "a_new_reference_was_added_to_file": "A new reference was added to <0>__filePath__", "a_new_reference_was_added_to_file_from_provider": "A new reference was added to <0>__filePath__ from __provider__", "about": "About", + "about_error_assist": "About Error Assist", "about_to_archive_projects": "You are about to archive the following projects:", "about_to_delete_cert": "You are about to delete the following certificate:", "about_to_delete_projects": "You are about to delete the following projects:", @@ -32,6 +33,7 @@ "about_to_leave_project": "You are about to leave this project.", "about_to_leave_projects": "You are about to leave the following projects:", "about_to_trash_projects": "You are about to trash the following projects:", + "about_writefull": "About Writefull", "abstract": "Abstract", "accept": "Accept", "accept_all": "Accept all", @@ -120,7 +122,10 @@ "aggregate_changed": "Changed", "aggregate_to": "to", "agree_with_the_terms": "I agree with the Overleaf terms", + "ai_assistance_to_help_you": "AI assistance to help you fix LaTeX errors", + "ai_based_language_tools": "AI-based language tools tailored to research writing", "ai_can_make_mistakes": "AI can make mistakes. Review fixes before you apply them.", + "ai_features": "AI features", "ai_feedback_do_you_have_any_thoughts_or_suggestions": "Do you have any thoughts or suggestions for improving this feature?", "ai_feedback_tell_us_what_was_wrong_so_we_can_improve": "Tell us what was wrong so we can improve.", "ai_feedback_the_answer_was_too_long": "The answer was too long", @@ -490,6 +495,7 @@ "dictionary": "Dictionary", "did_you_know_institution_providing_professional": "Did you know that __institutionName__ is providing <0>free __appName__ Professional features to everyone at __institutionName__?", "disable": "Disable", + "disable_ai_features": "Disable AI features", "disable_equation_preview": "Disable equation preview", "disable_equation_preview_confirm": "This will disable equation preview for you in all projects.", "disable_equation_preview_enable": "You can enable it again from the Menu.", @@ -610,6 +616,7 @@ "empty": "Empty", "empty_zip_file": "Zip doesn’t contain any file", "en": "English", + "enable_ai_features": "Enable AI features", "enable_managed_users": "Enable Managed Users", "enable_single_sign_on": "Enable single sign-on", "enable_sso": "Enable SSO", @@ -627,6 +634,7 @@ "enter_your_new_password": "Enter your new password", "equation_preview": "Equation preview", "error": "Error", + "error_assist": "Error Assist", "error_opening_document": "Error opening document", "error_opening_document_detail": "Sorry, something went wrong opening this document. Please try again.", "error_performing_request": "An error has occurred while performing your request.", @@ -1075,7 +1083,6 @@ "labels_help_you_to_reference_your_tables": "Labels help you to reference your tables throughout your document easily. To reference a table within the text, reference the label using the <0>\\ref{...} command. This makes it easy to reference tables without manually remembering the table numbering. <1>Read about labels and cross-references.", "labs_program_benefits": "By signing up for Overleaf Labs you can get your hands on in-development features and try them out as much as you like. All we ask in return is your honest feedback to help us develop and improve. It’s important to note that features available in this program are still being tested and actively developed. This means they could change, be removed, or become part of a premium plan.", "language": "Language", - "language_feedback": "Language Feedback", "large_or_high-resolution_images_taking_too_long": "Large or high-resolution images taking too long to process. You may be able to <0>optimize them.", "last_active": "Last Active", "last_active_description": "Last time a project was opened.", @@ -2469,10 +2476,8 @@ "work_with_other_github_users": "Work with other GitHub users", "write_and_collaborate_faster_with_features_like": "Write and collaborate faster with features like:", "writefull": "Writefull", - "writefull_learn_more": "Learn more about Writefull for Overleaf", "writefull_loading_error_body": "Try refreshing the page. If this doesn’t work, try disabling any active browser extensions to check they aren’t blocking Writefull from loading.", "writefull_loading_error_title": "Writefull didn’t load correctly", - "writefull_settings_description": "Get free AI-based language feedback specifically tailored for research writing with Writefull for Overleaf.", "x_changes_in": "__count__ change in", "x_changes_in_plural": "__count__ changes in", "x_libraries_accessed_in_this_project": "__provider__ libraries accessed in this project", diff --git a/services/web/scripts/back_fill_hiding_ai_features.mjs b/services/web/scripts/back_fill_hiding_ai_features.mjs new file mode 100644 index 0000000000..d9c73c7d38 --- /dev/null +++ b/services/web/scripts/back_fill_hiding_ai_features.mjs @@ -0,0 +1,28 @@ +import { db } from '../app/src/infrastructure/mongodb.js' +import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js' + +async function main() { + // update all applicable user models + await batchedUpdate( + db.users, + { + 'writefull.enabled': false, + }, + { + $set: { + 'aiErrorAssistant.enabled': false, + }, + } + ) + console.log('completed syncing writefull state with error assist') +} + +export default main + +try { + await main() + process.exit(0) +} catch (error) { + console.error({ error }) + process.exit(1) +} diff --git a/services/web/types/user.ts b/services/web/types/user.ts index 2ea85225e6..6a433d6c77 100644 --- a/services/web/types/user.ts +++ b/services/web/types/user.ts @@ -54,6 +54,9 @@ export type User = { autoCreatedAccount: boolean firstAutoLoad: boolean } + aiErrorAssistant?: { + enabled: boolean + } featureUsage?: FeatureUsage }