mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-14 20:40:17 -05:00
Merge pull request #21107 from overleaf/jdt-error-assistant-freemium
Convert Ai Error Assistant to a Freemium Model GitOrigin-RevId: 348c19262928d7dde8236baf37663c85d91f101a
This commit is contained in:
parent
745695650c
commit
811e935ced
6 changed files with 84 additions and 8 deletions
|
@ -348,6 +348,9 @@ const _ProjectController = {
|
||||||
'write-and-cite-ars',
|
'write-and-cite-ars',
|
||||||
'default-visual-for-beginners',
|
'default-visual-for-beginners',
|
||||||
'hotjar',
|
'hotjar',
|
||||||
|
'spell-check-client',
|
||||||
|
'spell-check-no-server',
|
||||||
|
'ai-add-on',
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
|
|
||||||
const getUserValues = async userId =>
|
const getUserValues = async userId =>
|
||||||
|
@ -646,6 +649,21 @@ const _ProjectController = {
|
||||||
aiFeaturesAllowed = false
|
aiFeaturesAllowed = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const canUseErrorAssistant =
|
||||||
|
user.features?.aiErrorAssistant ||
|
||||||
|
splitTestAssignments['ai-add-on']?.variant === 'enabled'
|
||||||
|
|
||||||
|
let featureUsage = {}
|
||||||
|
|
||||||
|
if (Features.hasFeature('saas')) {
|
||||||
|
const usagesLeft = await Modules.promises.hooks.fire(
|
||||||
|
'remainingFeatureAllocation',
|
||||||
|
userId
|
||||||
|
)
|
||||||
|
usagesLeft?.forEach(usage => {
|
||||||
|
featureUsage = { ...featureUsage, ...usage }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// check if a user has never tried writefull before (writefull.enabled will be null)
|
// check if a user has never tried writefull before (writefull.enabled will be null)
|
||||||
// if they previously accepted writefull, or are have been already assigned to a trial, user.writefull will be true,
|
// if they previously accepted writefull, or are have been already assigned to a trial, user.writefull will be true,
|
||||||
|
@ -713,6 +731,7 @@ const _ProjectController = {
|
||||||
allowedFreeTrial,
|
allowedFreeTrial,
|
||||||
featureSwitches: user.featureSwitches,
|
featureSwitches: user.featureSwitches,
|
||||||
features: user.features,
|
features: user.features,
|
||||||
|
featureUsage,
|
||||||
refProviders: _.mapValues(user.refProviders, Boolean),
|
refProviders: _.mapValues(user.refProviders, Boolean),
|
||||||
writefull: {
|
writefull: {
|
||||||
enabled: Boolean(user.writefull?.enabled && aiFeaturesAllowed),
|
enabled: Boolean(user.writefull?.enabled && aiFeaturesAllowed),
|
||||||
|
@ -766,8 +785,7 @@ const _ProjectController = {
|
||||||
showSymbolPalette,
|
showSymbolPalette,
|
||||||
symbolPaletteAvailable: Features.hasFeature('symbol-palette'),
|
symbolPaletteAvailable: Features.hasFeature('symbol-palette'),
|
||||||
userRestrictions: Array.from(req.userRestrictions || []),
|
userRestrictions: Array.from(req.userRestrictions || []),
|
||||||
showAiErrorAssistant:
|
showAiErrorAssistant: aiFeaturesAllowed && canUseErrorAssistant,
|
||||||
aiFeaturesAllowed && user.features?.aiErrorAssistant,
|
|
||||||
detachRole,
|
detachRole,
|
||||||
metadata: { viewport: false },
|
metadata: { viewport: false },
|
||||||
showUpgradePrompt,
|
showUpgradePrompt,
|
||||||
|
|
|
@ -152,6 +152,8 @@
|
||||||
"browser": "",
|
"browser": "",
|
||||||
"bulk_accept_confirm": "",
|
"bulk_accept_confirm": "",
|
||||||
"bulk_reject_confirm": "",
|
"bulk_reject_confirm": "",
|
||||||
|
"buy_error_assistant_for": "",
|
||||||
|
"buy_now_no_exclamation_mark": "",
|
||||||
"buy_overleaf_assist": "",
|
"buy_overleaf_assist": "",
|
||||||
"by_subscribing_you_agree_to_our_terms_of_service": "",
|
"by_subscribing_you_agree_to_our_terms_of_service": "",
|
||||||
"can_edit": "",
|
"can_edit": "",
|
||||||
|
@ -483,6 +485,7 @@
|
||||||
"first_name": "",
|
"first_name": "",
|
||||||
"fit_to_height": "",
|
"fit_to_height": "",
|
||||||
"fit_to_width": "",
|
"fit_to_width": "",
|
||||||
|
"fix_latex_errors_fast_and_perfect_your_projects": "",
|
||||||
"fixed_width": "",
|
"fixed_width": "",
|
||||||
"fixed_width_wrap_text": "",
|
"fixed_width_wrap_text": "",
|
||||||
"fold_line": "",
|
"fold_line": "",
|
||||||
|
@ -525,6 +528,7 @@
|
||||||
"get_discounted_plan": "",
|
"get_discounted_plan": "",
|
||||||
"get_dropbox_sync": "",
|
"get_dropbox_sync": "",
|
||||||
"get_early_access_to_ai": "",
|
"get_early_access_to_ai": "",
|
||||||
|
"get_error_assistant": "",
|
||||||
"get_exclusive_access_to_labs": "",
|
"get_exclusive_access_to_labs": "",
|
||||||
"get_full_project_history": "",
|
"get_full_project_history": "",
|
||||||
"get_git_integration": "",
|
"get_git_integration": "",
|
||||||
|
@ -763,6 +767,7 @@
|
||||||
"last_used": "",
|
"last_used": "",
|
||||||
"latam_discount_modal_info": "",
|
"latam_discount_modal_info": "",
|
||||||
"latam_discount_modal_title": "",
|
"latam_discount_modal_title": "",
|
||||||
|
"latex_error_fixing_with_a_little_help": "",
|
||||||
"latex_in_thirty_minutes": "",
|
"latex_in_thirty_minutes": "",
|
||||||
"latex_places_figures_according_to_a_special_algorithm": "",
|
"latex_places_figures_according_to_a_special_algorithm": "",
|
||||||
"latex_places_tables_according_to_a_special_algorithm": "",
|
"latex_places_tables_according_to_a_special_algorithm": "",
|
||||||
|
@ -1068,6 +1073,7 @@
|
||||||
"premium_feature": "",
|
"premium_feature": "",
|
||||||
"premium_features": "",
|
"premium_features": "",
|
||||||
"premium_plan_label": "",
|
"premium_plan_label": "",
|
||||||
|
"premium_suggestion_available": "",
|
||||||
"presentation_mode": "",
|
"presentation_mode": "",
|
||||||
"press_and_awards": "",
|
"press_and_awards": "",
|
||||||
"previous_page": "",
|
"previous_page": "",
|
||||||
|
|
|
@ -31,11 +31,24 @@ export const useLogEvents = (setShowLogs: (show: boolean) => void) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (suggestFix) {
|
if (suggestFix) {
|
||||||
element
|
// if they are paywalled, click that instead
|
||||||
.querySelector<HTMLButtonElement>(
|
const paywall = document.querySelector<HTMLButtonElement>(
|
||||||
'button[data-action="suggest-fix"]'
|
'button[data-action="assistant-paywall-show"]'
|
||||||
)
|
)
|
||||||
?.click()
|
|
||||||
|
if (paywall) {
|
||||||
|
paywall.scrollIntoView({
|
||||||
|
block: 'start',
|
||||||
|
inline: 'nearest',
|
||||||
|
})
|
||||||
|
paywall.click()
|
||||||
|
} else {
|
||||||
|
element
|
||||||
|
.querySelector<HTMLButtonElement>(
|
||||||
|
'button[data-action="suggest-fix"]'
|
||||||
|
)
|
||||||
|
?.click()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -49,13 +49,17 @@ export const EditorContext = createContext<
|
||||||
currentPopup: string | null
|
currentPopup: string | null
|
||||||
setCurrentPopup: Dispatch<SetStateAction<string | null>>
|
setCurrentPopup: Dispatch<SetStateAction<string | null>>
|
||||||
setOutOfSync: (value: boolean) => void
|
setOutOfSync: (value: boolean) => void
|
||||||
|
hasPremiumSuggestion: boolean
|
||||||
|
setHasPremiumSuggestion: (value: boolean) => void
|
||||||
|
setPremiumSuggestionResetDate: (date: Date) => void
|
||||||
|
premiumSuggestionResetDate: Date
|
||||||
}
|
}
|
||||||
| undefined
|
| undefined
|
||||||
>(undefined)
|
>(undefined)
|
||||||
|
|
||||||
export const EditorProvider: FC = ({ children }) => {
|
export const EditorProvider: FC = ({ children }) => {
|
||||||
const ide = useIdeContext()
|
const ide = useIdeContext()
|
||||||
const { id: userId } = useUserContext()
|
const { id: userId, featureUsage } = useUserContext()
|
||||||
const { role } = useDetachContext()
|
const { role } = useDetachContext()
|
||||||
const { showGenericMessageModal } = useModalsContext()
|
const { showGenericMessageModal } = useModalsContext()
|
||||||
|
|
||||||
|
@ -91,6 +95,20 @@ export const EditorProvider: FC = ({ children }) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
const [currentPopup, setCurrentPopup] = useState<string | null>(null)
|
const [currentPopup, setCurrentPopup] = useState<string | null>(null)
|
||||||
|
const [hasPremiumSuggestion, setHasPremiumSuggestion] = useState<boolean>(
|
||||||
|
() => {
|
||||||
|
return Boolean(
|
||||||
|
featureUsage?.aiErrorAssistant &&
|
||||||
|
featureUsage?.aiErrorAssistant.remainingUsage > 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const [premiumSuggestionResetDate, setPremiumSuggestionResetDate] =
|
||||||
|
useState<Date>(() => {
|
||||||
|
return featureUsage?.aiErrorAssistant.resetDate
|
||||||
|
? new Date(featureUsage.aiErrorAssistant.resetDate)
|
||||||
|
: new Date()
|
||||||
|
})
|
||||||
|
|
||||||
const isPendingEditor = useMemo(
|
const isPendingEditor = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -183,6 +201,10 @@ export const EditorProvider: FC = ({ children }) => {
|
||||||
currentPopup,
|
currentPopup,
|
||||||
setCurrentPopup,
|
setCurrentPopup,
|
||||||
setOutOfSync,
|
setOutOfSync,
|
||||||
|
hasPremiumSuggestion,
|
||||||
|
setHasPremiumSuggestion,
|
||||||
|
premiumSuggestionResetDate,
|
||||||
|
setPremiumSuggestionResetDate,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
cobranding,
|
cobranding,
|
||||||
|
@ -203,6 +225,10 @@ export const EditorProvider: FC = ({ children }) => {
|
||||||
setCurrentPopup,
|
setCurrentPopup,
|
||||||
outOfSync,
|
outOfSync,
|
||||||
setOutOfSync,
|
setOutOfSync,
|
||||||
|
hasPremiumSuggestion,
|
||||||
|
setHasPremiumSuggestion,
|
||||||
|
premiumSuggestionResetDate,
|
||||||
|
setPremiumSuggestionResetDate,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -208,6 +208,7 @@
|
||||||
"built_in": "Built-In",
|
"built_in": "Built-In",
|
||||||
"bulk_accept_confirm": "Are you sure you want to accept the selected __nChanges__ changes?",
|
"bulk_accept_confirm": "Are you sure you want to accept the selected __nChanges__ changes?",
|
||||||
"bulk_reject_confirm": "Are you sure you want to reject the selected __nChanges__ changes?",
|
"bulk_reject_confirm": "Are you sure you want to reject the selected __nChanges__ changes?",
|
||||||
|
"buy_error_assistant_for": "<strong>Buy Error Assist for $__aiAddonCost__ per year</strong>",
|
||||||
"buy_now_no_exclamation_mark": "Buy now",
|
"buy_now_no_exclamation_mark": "Buy now",
|
||||||
"buy_overleaf_assist": "Buy Overleaf Assist",
|
"buy_overleaf_assist": "Buy Overleaf Assist",
|
||||||
"by": "by",
|
"by": "by",
|
||||||
|
@ -690,6 +691,7 @@
|
||||||
"first_name_sentence_case": "First name",
|
"first_name_sentence_case": "First name",
|
||||||
"fit_to_height": "Fit to height",
|
"fit_to_height": "Fit to height",
|
||||||
"fit_to_width": "Fit to width",
|
"fit_to_width": "Fit to width",
|
||||||
|
"fix_latex_errors_fast_and_perfect_your_projects": "Fix LaTeX errors fast and perfect your projects with the AI-powered Error Assist.",
|
||||||
"fixed_width": "Fixed width",
|
"fixed_width": "Fixed width",
|
||||||
"fixed_width_wrap_text": "Fixed width, wrap text",
|
"fixed_width_wrap_text": "Fixed width, wrap text",
|
||||||
"flexible_plans_for_everyone": "Flexible plans for everyone—from individual students and researchers, to large businesses and universities.",
|
"flexible_plans_for_everyone": "Flexible plans for everyone—from individual students and researchers, to large businesses and universities.",
|
||||||
|
@ -762,6 +764,7 @@
|
||||||
"get_discounted_plan": "Get discounted plan",
|
"get_discounted_plan": "Get discounted plan",
|
||||||
"get_dropbox_sync": "Get Dropbox Sync",
|
"get_dropbox_sync": "Get Dropbox Sync",
|
||||||
"get_early_access_to_ai": "Get early access to the new AI Error Assistant in Overleaf Labs",
|
"get_early_access_to_ai": "Get early access to the new AI Error Assistant in Overleaf Labs",
|
||||||
|
"get_error_assistant": "Get Error Assistant",
|
||||||
"get_exclusive_access_to_labs": "Get exclusive access to early-stage experiments when you join Overleaf Labs. All we ask in return is your honest feedback to help us develop and improve.",
|
"get_exclusive_access_to_labs": "Get exclusive access to early-stage experiments when you join Overleaf Labs. All we ask in return is your honest feedback to help us develop and improve.",
|
||||||
"get_full_project_history": "Get full project history",
|
"get_full_project_history": "Get full project history",
|
||||||
"get_git_integration": "Get Git integration",
|
"get_git_integration": "Get Git integration",
|
||||||
|
@ -1089,6 +1092,7 @@
|
||||||
"latam_discount_offer_plans_page_banner": "__flag__ We’ve applied a __discount__ discount to premium plans on this page for our users in __country__. Check out the new lower prices (in __currency__).",
|
"latam_discount_offer_plans_page_banner": "__flag__ We’ve applied a __discount__ discount to premium plans on this page for our users in __country__. Check out the new lower prices (in __currency__).",
|
||||||
"latex_articles_page_summary": "Papers, presentations, reports and more, written in LaTeX and published by our community. Search or browse below.",
|
"latex_articles_page_summary": "Papers, presentations, reports and more, written in LaTeX and published by our community. Search or browse below.",
|
||||||
"latex_articles_page_title": "Articles - Papers, Presentations, Reports and more",
|
"latex_articles_page_title": "Articles - Papers, Presentations, Reports and more",
|
||||||
|
"latex_error_fixing_with_a_little_help": "LaTeX error fixing with a little help from AI",
|
||||||
"latex_examples": "LaTeX examples",
|
"latex_examples": "LaTeX examples",
|
||||||
"latex_examples_page_summary": "Examples of powerful LaTeX packages and techniques in use — a great way to learn LaTeX by example. Search or browse below.",
|
"latex_examples_page_summary": "Examples of powerful LaTeX packages and techniques in use — a great way to learn LaTeX by example. Search or browse below.",
|
||||||
"latex_examples_page_title": "Examples - Equations, Formatting, TikZ, Packages and More",
|
"latex_examples_page_title": "Examples - Equations, Formatting, TikZ, Packages and More",
|
||||||
|
@ -1549,6 +1553,7 @@
|
||||||
"premium_feature": "Premium feature",
|
"premium_feature": "Premium feature",
|
||||||
"premium_features": "Premium features",
|
"premium_features": "Premium features",
|
||||||
"premium_plan_label": "You’re using <b>Overleaf Premium</b>",
|
"premium_plan_label": "You’re using <b>Overleaf Premium</b>",
|
||||||
|
"premium_suggestion_available": "Premium suggestion available",
|
||||||
"presentation": "Presentation",
|
"presentation": "Presentation",
|
||||||
"presentation_mode": "Presentation mode",
|
"presentation_mode": "Presentation mode",
|
||||||
"press_and_awards": "Press & awards",
|
"press_and_awards": "Press & awards",
|
||||||
|
|
|
@ -25,6 +25,13 @@ export type Features = {
|
||||||
zotero?: boolean
|
zotero?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FeatureUsage = {
|
||||||
|
[feature: string]: {
|
||||||
|
remainingUsage: number
|
||||||
|
resetDate: string // date string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
id: UserId | null
|
id: UserId | null
|
||||||
isAdmin?: boolean
|
isAdmin?: boolean
|
||||||
|
@ -44,6 +51,7 @@ export type User = {
|
||||||
autoCreatedAccount: boolean
|
autoCreatedAccount: boolean
|
||||||
firstAutoLoad: boolean
|
firstAutoLoad: boolean
|
||||||
}
|
}
|
||||||
|
featureUsage?: FeatureUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MongoUser = Pick<User, Exclude<keyof User, 'id'>> & { _id: string }
|
export type MongoUser = Pick<User, Exclude<keyof User, 'id'>> & { _id: string }
|
||||||
|
|
Loading…
Reference in a new issue