mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #18175 from overleaf/jdt-new-bib-file-prompt
[Web] Add opportunistic prompts for third party references GitOrigin-RevId: d794df16781d0db707423f23ab12f40a13604907
This commit is contained in:
parent
e3ff70e896
commit
81903bb79d
11 changed files with 90 additions and 24 deletions
|
@ -607,6 +607,13 @@ const ProjectController = {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
tprPromptAssignment(cb) {
|
||||||
|
if (anonymous) {
|
||||||
|
cb()
|
||||||
|
} else {
|
||||||
|
SplitTestHandler.getAssignment(req, res, 'bib-file-tpr-prompt', cb)
|
||||||
|
}
|
||||||
|
},
|
||||||
compileLogEventsAssignment(cb) {
|
compileLogEventsAssignment(cb) {
|
||||||
SplitTestHandler.getAssignment(req, res, 'compile-log-events', cb)
|
SplitTestHandler.getAssignment(req, res, 'compile-log-events', cb)
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,6 +7,7 @@ const VALID_KEYS = [
|
||||||
'table-generator-promotion',
|
'table-generator-promotion',
|
||||||
'writefull-integration',
|
'writefull-integration',
|
||||||
'writefull-oauth-promotion',
|
'writefull-oauth-promotion',
|
||||||
|
'bib-file-tpr-prompt',
|
||||||
]
|
]
|
||||||
|
|
||||||
async function completeTutorial(req, res, next) {
|
async function completeTutorial(req, res, next) {
|
||||||
|
|
|
@ -899,6 +899,7 @@ module.exports = {
|
||||||
tprFileViewRefreshError: [],
|
tprFileViewRefreshError: [],
|
||||||
tprFileViewRefreshButton: [],
|
tprFileViewRefreshButton: [],
|
||||||
tprFileViewNotOriginalImporter: [],
|
tprFileViewNotOriginalImporter: [],
|
||||||
|
newFilePromotions: [],
|
||||||
contactUsModal: [],
|
contactUsModal: [],
|
||||||
editorToolbarButtons: [],
|
editorToolbarButtons: [],
|
||||||
sourceEditorExtensions: [],
|
sourceEditorExtensions: [],
|
||||||
|
@ -907,7 +908,7 @@ module.exports = {
|
||||||
sourceEditorCompletionSources: [],
|
sourceEditorCompletionSources: [],
|
||||||
sourceEditorSymbolPalette: [],
|
sourceEditorSymbolPalette: [],
|
||||||
sourceEditorToolbarComponents: [],
|
sourceEditorToolbarComponents: [],
|
||||||
writefullEditorPromotion: [],
|
editorPromotions: [],
|
||||||
langFeedbackLinkingWidgets: [],
|
langFeedbackLinkingWidgets: [],
|
||||||
integrationLinkingWidgets: [],
|
integrationLinkingWidgets: [],
|
||||||
referenceLinkingWidgets: [],
|
referenceLinkingWidgets: [],
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
"add_your_first_group_member_now": "",
|
"add_your_first_group_member_now": "",
|
||||||
"added_by_on": "",
|
"added_by_on": "",
|
||||||
"adding": "",
|
"adding": "",
|
||||||
|
"adding_a_bibliography": "",
|
||||||
"additional_certificate": "",
|
"additional_certificate": "",
|
||||||
"additional_licenses": "",
|
"additional_licenses": "",
|
||||||
"address_line_1": "",
|
"address_line_1": "",
|
||||||
|
@ -106,6 +107,7 @@
|
||||||
"beta_program_already_participating": "",
|
"beta_program_already_participating": "",
|
||||||
"beta_program_benefits": "",
|
"beta_program_benefits": "",
|
||||||
"beta_program_not_participating": "",
|
"beta_program_not_participating": "",
|
||||||
|
"better_bibliographies": "",
|
||||||
"binary_history_error": "",
|
"binary_history_error": "",
|
||||||
"blank_project": "",
|
"blank_project": "",
|
||||||
"blocked_filename": "",
|
"blocked_filename": "",
|
||||||
|
@ -333,6 +335,7 @@
|
||||||
"duplicate_file": "",
|
"duplicate_file": "",
|
||||||
"duplicate_projects": "",
|
"duplicate_projects": "",
|
||||||
"each_user_will_have_access_to": "",
|
"each_user_will_have_access_to": "",
|
||||||
|
"easily_import_and_sync_your_references": "",
|
||||||
"easily_manage_your_project_files_everywhere": "",
|
"easily_manage_your_project_files_everywhere": "",
|
||||||
"edit": "",
|
"edit": "",
|
||||||
"edit_dictionary": "",
|
"edit_dictionary": "",
|
||||||
|
|
|
@ -5,12 +5,14 @@ import { useFileTreeCreateName } from '../../../contexts/file-tree-create-name'
|
||||||
import { useFileTreeCreateForm } from '../../../contexts/file-tree-create-form'
|
import { useFileTreeCreateForm } from '../../../contexts/file-tree-create-form'
|
||||||
import * as eventTracking from '../../../../../infrastructure/event-tracking'
|
import * as eventTracking from '../../../../../infrastructure/event-tracking'
|
||||||
import ErrorMessage from '../error-message'
|
import ErrorMessage from '../error-message'
|
||||||
|
import importOverleafModules from '../../../../../../macros/import-overleaf-module.macro'
|
||||||
|
|
||||||
|
const newFilePromotionComponents = importOverleafModules('newFilePromotions')
|
||||||
|
|
||||||
export default function FileTreeCreateNewDoc() {
|
export default function FileTreeCreateNewDoc() {
|
||||||
const { name, validName } = useFileTreeCreateName()
|
const { name, validName } = useFileTreeCreateName()
|
||||||
const { setValid } = useFileTreeCreateForm()
|
const { setValid } = useFileTreeCreateForm()
|
||||||
const { error, finishCreatingDoc, inFlight } = useFileTreeActionable()
|
const { error, finishCreatingDoc, inFlight } = useFileTreeActionable()
|
||||||
|
|
||||||
// form validation: name is valid
|
// form validation: name is valid
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setValid(validName)
|
setValid(validName)
|
||||||
|
@ -33,8 +35,12 @@ export default function FileTreeCreateNewDoc() {
|
||||||
return (
|
return (
|
||||||
<form noValidate id="create-file" onSubmit={handleSubmit}>
|
<form noValidate id="create-file" onSubmit={handleSubmit}>
|
||||||
<FileTreeCreateNameInput focusName error={error} inFlight={inFlight} />
|
<FileTreeCreateNameInput focusName error={error} inFlight={inFlight} />
|
||||||
|
|
||||||
{error && <ErrorMessage error={error} />}
|
{error && <ErrorMessage error={error} />}
|
||||||
|
{newFilePromotionComponents.map(
|
||||||
|
({ import: { default: Component }, path }) => (
|
||||||
|
<Component key={path} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,7 @@ import { ErrorBoundaryFallback } from '../../../shared/components/error-boundary
|
||||||
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
||||||
import GrammarlyAdvert from './grammarly-advert'
|
import GrammarlyAdvert from './grammarly-advert'
|
||||||
|
|
||||||
const writefullPromotion = importOverleafModules(
|
const editorPromotions = importOverleafModules('editorPromotions') as {
|
||||||
'writefullEditorPromotion'
|
|
||||||
) as {
|
|
||||||
import: { default: ElementType }
|
import: { default: ElementType }
|
||||||
path: string
|
path: string
|
||||||
}[]
|
}[]
|
||||||
|
@ -20,7 +18,7 @@ const CodeMirrorEditor = lazy(
|
||||||
function SourceEditor() {
|
function SourceEditor() {
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<FullSizeLoadingSpinner delay={500} />}>
|
<Suspense fallback={<FullSizeLoadingSpinner delay={500} />}>
|
||||||
{writefullPromotion.map(({ import: { default: Component }, path }) => (
|
{editorPromotions.map(({ import: { default: Component }, path }) => (
|
||||||
<Component key={path} />
|
<Component key={path} />
|
||||||
))}
|
))}
|
||||||
<GrammarlyAdvert />
|
<GrammarlyAdvert />
|
||||||
|
|
|
@ -50,26 +50,35 @@ export const useTutorial = (
|
||||||
}, [completeTutorial])
|
}, [completeTutorial])
|
||||||
|
|
||||||
// try to show the popup if we don't already have one showing, returns true if it can show, false if it can't
|
// try to show the popup if we don't already have one showing, returns true if it can show, false if it can't
|
||||||
const tryShowingPopup = useCallback(() => {
|
const tryShowingPopup = useCallback(
|
||||||
if (currentPopup === null) {
|
(eventName: string = 'promo-prompt') => {
|
||||||
|
if (currentPopup === null) {
|
||||||
|
setCurrentPopup(tutorialKey)
|
||||||
|
setShowPopup(true)
|
||||||
|
eventTracking.sendMB(eventName, eventData)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
[currentPopup, setCurrentPopup, tutorialKey, eventData]
|
||||||
|
)
|
||||||
|
|
||||||
|
const clearPopup = useCallback(() => {
|
||||||
|
// popups should only clear themselves, in cases they need to cleanup or shouldnt show anymore
|
||||||
|
// allow forcing the clear if needed, eg: higher prio alert needs to show
|
||||||
|
if (currentPopup === tutorialKey) {
|
||||||
|
setCurrentPopup(null)
|
||||||
|
setShowPopup(false)
|
||||||
|
}
|
||||||
|
}, [setCurrentPopup, setShowPopup, currentPopup, tutorialKey])
|
||||||
|
|
||||||
|
const clearAndShow = useCallback(
|
||||||
|
(eventName: string = 'promo-prompt') => {
|
||||||
setCurrentPopup(tutorialKey)
|
setCurrentPopup(tutorialKey)
|
||||||
setShowPopup(true)
|
setShowPopup(true)
|
||||||
eventTracking.sendMB('promo-prompt', eventData)
|
eventTracking.sendMB(eventName, eventData)
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}, [currentPopup, setCurrentPopup, tutorialKey, eventData])
|
|
||||||
|
|
||||||
const clearPopup = useCallback(
|
|
||||||
(force: boolean = false) => {
|
|
||||||
// popups should only clear themselves, in cases they need to cleanup or shouldnt show anymore
|
|
||||||
// allow forcing the clear if needed, eg: higher prio alert needs to show
|
|
||||||
if (force || currentPopup === tutorialKey) {
|
|
||||||
setCurrentPopup(null)
|
|
||||||
setShowPopup(false)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[setCurrentPopup, setShowPopup, currentPopup, tutorialKey]
|
[setCurrentPopup, setShowPopup, tutorialKey, eventData]
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -78,6 +87,7 @@ export const useTutorial = (
|
||||||
maybeLater,
|
maybeLater,
|
||||||
tryShowingPopup,
|
tryShowingPopup,
|
||||||
clearPopup,
|
clearPopup,
|
||||||
|
clearAndShow,
|
||||||
showPopup,
|
showPopup,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,3 +154,4 @@
|
||||||
@import 'modules/group-settings.less';
|
@import 'modules/group-settings.less';
|
||||||
@import 'modules/overleaf-integration.less';
|
@import 'modules/overleaf-integration.less';
|
||||||
@import 'modules/writefull.less';
|
@import 'modules/writefull.less';
|
||||||
|
@import 'modules/third-party-references.less';
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* Animation keyframes */
|
||||||
|
@keyframes slide-in {
|
||||||
|
0% {
|
||||||
|
transform: translateY(25%);
|
||||||
|
opacity: 0;
|
||||||
|
bottom: 12px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-slide-in {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease; /* Smooth opacity transition */
|
||||||
|
animation: slide-in 0.3s ease-in-out forwards;
|
||||||
|
animation-delay: 300ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tpr-editor-prompt-container {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: @popover-dark-max-width;
|
||||||
|
}
|
|
@ -77,6 +77,7 @@
|
||||||
"added": "added",
|
"added": "added",
|
||||||
"added_by_on": "Added by __name__ on __date__",
|
"added_by_on": "Added by __name__ on __date__",
|
||||||
"adding": "Adding",
|
"adding": "Adding",
|
||||||
|
"adding_a_bibliography": "Adding a bibliography?",
|
||||||
"additional_certificate": "Additional certificate",
|
"additional_certificate": "Additional certificate",
|
||||||
"additional_licenses": "Your subscription includes <0>__additionalLicenses__</0> additional license(s) for a total of <1>__totalLicenses__</1> licenses.",
|
"additional_licenses": "Your subscription includes <0>__additionalLicenses__</0> additional license(s) for a total of <1>__totalLicenses__</1> licenses.",
|
||||||
"address": "Address",
|
"address": "Address",
|
||||||
|
@ -163,6 +164,7 @@
|
||||||
"beta_program_not_participating": "You are not enrolled in the Beta Program",
|
"beta_program_not_participating": "You are not enrolled in the Beta Program",
|
||||||
"beta_program_opt_in_action": "Opt-In to Beta Program",
|
"beta_program_opt_in_action": "Opt-In to Beta Program",
|
||||||
"beta_program_opt_out_action": "Opt-Out of Beta Program",
|
"beta_program_opt_out_action": "Opt-Out of Beta Program",
|
||||||
|
"better_bibliographies": "Better bibliographies",
|
||||||
"bibliographies": "Bibliographies",
|
"bibliographies": "Bibliographies",
|
||||||
"binary_history_error": "Preview not available for this file type",
|
"binary_history_error": "Preview not available for this file type",
|
||||||
"blank_project": "Blank Project",
|
"blank_project": "Blank Project",
|
||||||
|
@ -469,6 +471,7 @@
|
||||||
"duplicate_file": "Duplicate File",
|
"duplicate_file": "Duplicate File",
|
||||||
"duplicate_projects": "This user has projects with duplicate names",
|
"duplicate_projects": "This user has projects with duplicate names",
|
||||||
"each_user_will_have_access_to": "Each user will have access to",
|
"each_user_will_have_access_to": "Each user will have access to",
|
||||||
|
"easily_import_and_sync_your_references": "Easily import and sync your references from Zotero or Mendeley when you upgrade your Overleaf plan.",
|
||||||
"easily_manage_your_project_files_everywhere": "Easily manage your project files, everywhere",
|
"easily_manage_your_project_files_everywhere": "Easily manage your project files, everywhere",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"edit_dictionary": "Edit Dictionary",
|
"edit_dictionary": "Edit Dictionary",
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="128" height="128" rx="64" fill="#EAF6EF"/>
|
||||||
|
<path d="M53.2105 83.8968L90.4835 78.0935C90.9022 80.8507 91.1684 82.9139 91.1491 84.5801C91.1262 86.5638 90.693 87.6799 89.898 88.5506C89.1027 89.4213 87.9009 90.0955 85.532 90.7306C83.0934 91.3844 79.8067 91.9004 75.0935 92.6342L61.9594 94.6792C57.2461 95.413 53.9583 95.9207 51.4364 96.0392C48.9865 96.1543 47.6366 95.8775 46.6143 95.2898C45.592 94.7021 44.8401 93.7705 44.2151 91.8876C44.1682 91.7461 44.1224 91.6008 44.0777 91.4517C43.8245 90.607 43.6979 90.1844 43.9145 89.108C44.1312 88.032 44.2689 87.8469 44.5445 87.4767C45.3616 86.3791 46.5753 85.4718 48.0462 84.9076C48.878 84.5886 49.9984 84.3969 53.2105 83.8968Z" fill="#1E6B41"/>
|
||||||
|
<path opacity="0.5" d="M38.1644 39.8876C38.9276 38.8337 40.1059 38.0406 42.4554 37.3415C44.8739 36.6218 48.1477 36.1067 52.8428 35.3757L65.9266 33.3386C70.6217 32.6076 73.8972 32.103 76.4201 32.0533C78.8709 32.005 80.2344 32.4023 81.2818 33.1743C82.3292 33.9463 83.119 35.1361 83.8186 37.5062C84.5386 39.9461 85.0581 43.2475 85.7953 47.9823L90.4835 78.0935L53.2105 83.8968C49.9983 84.3969 48.878 84.5886 48.0462 84.9076C46.5753 85.4718 45.3616 86.3791 44.5445 87.4767C44.2689 87.8469 44.1312 88.032 43.9145 89.1081C43.7902 89.7255 43.7789 90.1281 43.8397 90.5181L38.3667 55.3668C37.6295 50.632 37.1206 47.329 37.0648 44.7857C37.0106 42.315 37.4013 40.9414 38.1644 39.8876Z" fill="#53B57F"/>
|
||||||
|
<path d="M47.612 51.3709C47.4208 50.143 48.2612 48.9927 49.4891 48.8015L73.2033 45.1092C74.4311 44.9181 75.5815 45.7585 75.7727 46.9863C75.9639 48.2141 75.1234 49.3645 73.8956 49.5557L50.1814 53.2479C48.9535 53.4391 47.8032 52.5987 47.612 51.3709Z" fill="#195936"/>
|
||||||
|
<path d="M51.1044 59.1765C49.8766 59.3677 49.0362 60.5181 49.2274 61.7459C49.4185 62.9737 50.5689 63.8141 51.7967 63.6229L66.6182 61.3153C67.846 61.1241 68.6864 59.9737 68.4952 58.7459C68.3041 57.5181 67.1537 56.6777 65.9259 56.8688L51.1044 59.1765Z" fill="#195936"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
Loading…
Reference in a new issue