mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-28 19:41:33 +02:00
Merge pull request #29034 from overleaf/as-groups-enterprise-banner
[web] Persist whether user has dismissed Groups and Enterprise banner on server-side GitOrigin-RevId: a95060cc0fa772652299802ec467be61b09f5a1f
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// @ts-check
|
||||
import _ from 'lodash'
|
||||
import moment from 'moment'
|
||||
|
||||
import Metrics from '@overleaf/metrics'
|
||||
import Settings from '@overleaf/settings'
|
||||
@@ -414,11 +415,15 @@ async function projectListPage(req, res, next) {
|
||||
|
||||
const { showUSGovBanner, usGovBannerVariant } = usGovBanner
|
||||
|
||||
const isUser30DaysOld = moment.utc().diff(user.signUpDate, 'days') > 30
|
||||
|
||||
const showGroupsAndEnterpriseBanner =
|
||||
Features.hasFeature('saas') &&
|
||||
!showUSGovBanner &&
|
||||
!userIsMemberOfGroupSubscription &&
|
||||
!hasPaidAffiliation
|
||||
!hasPaidAffiliation &&
|
||||
!inactiveTutorials.includes('groups-enterprise-banner-repeat') &&
|
||||
isUser30DaysOld
|
||||
|
||||
const groupsAndEnterpriseBannerVariant =
|
||||
showGroupsAndEnterpriseBanner &&
|
||||
@@ -564,6 +569,7 @@ async function projectListPage(req, res, next) {
|
||||
primaryOccupation,
|
||||
role,
|
||||
usedLatex,
|
||||
inactiveTutorials,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ const VALID_KEYS = [
|
||||
'ide-redesign-beta-intro',
|
||||
'ide-redesign-labs-user-beta-promo',
|
||||
'rolling-compile-image-changed',
|
||||
'groups-enterprise-banner',
|
||||
'groups-enterprise-banner-repeat',
|
||||
]
|
||||
|
||||
async function completeTutorial(req, res, next) {
|
||||
|
||||
@@ -89,6 +89,7 @@ block append meta
|
||||
data-type='string'
|
||||
content=usGovBannerVariant
|
||||
)
|
||||
meta(name='ol-inactiveTutorials' data-type='json' content=inactiveTutorials)
|
||||
|
||||
block content
|
||||
#project-list-root
|
||||
|
||||
@@ -10,6 +10,9 @@ import {
|
||||
GroupsAndEnterpriseBannerVariants,
|
||||
} from '../../../../../../types/project/dashboard/notification'
|
||||
import OLButton from '@/shared/components/ol/ol-button'
|
||||
import { postJSON } from '@/infrastructure/fetch-json'
|
||||
import moment from 'moment'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
|
||||
type urlForVariantsType = {
|
||||
[key in GroupsAndEnterpriseBannerVariant]: string // eslint-disable-line no-unused-vars
|
||||
@@ -20,6 +23,9 @@ const urlForVariants: urlForVariantsType = {
|
||||
FOMO: '/for/contact-sales-4',
|
||||
}
|
||||
|
||||
const INITIAL_TUTORIAL_KEY = 'groups-enterprise-banner'
|
||||
const REPEAT_TUTORIAL_KEY = 'groups-enterprise-banner-repeat'
|
||||
|
||||
let viewEventSent = false
|
||||
|
||||
export default function GroupsAndEnterpriseBanner() {
|
||||
@@ -32,23 +38,35 @@ export default function GroupsAndEnterpriseBanner() {
|
||||
const groupsAndEnterpriseBannerVariant = getMeta(
|
||||
'ol-groupsAndEnterpriseBannerVariant'
|
||||
)
|
||||
const inactiveTutorials = getMeta('ol-inactiveTutorials')
|
||||
|
||||
const hasDismissedGroupsAndEnterpriseBanner = hasRecentlyDismissedBanner()
|
||||
|
||||
const locallyDismissedBanner = hasLocallyDismissedBanner()
|
||||
const contactSalesUrl = urlForVariants[groupsAndEnterpriseBannerVariant]
|
||||
|
||||
const shouldRenderBanner =
|
||||
showGroupsAndEnterpriseBanner &&
|
||||
totalProjectsCount !== 0 &&
|
||||
!hasDismissedGroupsAndEnterpriseBanner &&
|
||||
!inactiveTutorials.includes(REPEAT_TUTORIAL_KEY) &&
|
||||
!locallyDismissedBanner &&
|
||||
isVariantValid(groupsAndEnterpriseBannerVariant)
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
customLocalStorage.setItem(
|
||||
'has_dismissed_groups_and_enterprise_banner',
|
||||
new Date()
|
||||
)
|
||||
}, [])
|
||||
const handleClose = useCallback(async () => {
|
||||
if (!inactiveTutorials.includes(INITIAL_TUTORIAL_KEY)) {
|
||||
await postJSON(`/tutorial/${REPEAT_TUTORIAL_KEY}/postpone`, {
|
||||
body: {
|
||||
postponedUntil: moment().add(60, 'days').toISOString(),
|
||||
},
|
||||
}).catch(debugConsole.error)
|
||||
|
||||
await postJSON(`/tutorial/${INITIAL_TUTORIAL_KEY}/complete`).catch(
|
||||
debugConsole.error
|
||||
)
|
||||
} else {
|
||||
await postJSON(`/tutorial/${REPEAT_TUTORIAL_KEY}/complete`).catch(
|
||||
debugConsole.error
|
||||
)
|
||||
}
|
||||
}, [inactiveTutorials])
|
||||
|
||||
const handleClickContact = useCallback(() => {
|
||||
eventTracking.sendMB('groups-and-enterprise-banner-click', {
|
||||
@@ -67,6 +85,16 @@ export default function GroupsAndEnterpriseBanner() {
|
||||
}
|
||||
}, [shouldRenderBanner, groupsAndEnterpriseBannerVariant])
|
||||
|
||||
useEffect(() => {
|
||||
// Persist local dismissal status from previous banner versions to the backend
|
||||
if (
|
||||
locallyDismissedBanner &&
|
||||
!inactiveTutorials.includes(REPEAT_TUTORIAL_KEY)
|
||||
) {
|
||||
handleClose()
|
||||
}
|
||||
}, [handleClose, locallyDismissedBanner, inactiveTutorials])
|
||||
|
||||
if (!shouldRenderBanner) {
|
||||
return null
|
||||
}
|
||||
@@ -120,7 +148,7 @@ function BannerContent({
|
||||
}
|
||||
}
|
||||
|
||||
function hasRecentlyDismissedBanner() {
|
||||
function hasLocallyDismissedBanner() {
|
||||
const dismissed = customLocalStorage.getItem(
|
||||
'has_dismissed_groups_and_enterprise_banner'
|
||||
)
|
||||
|
||||
@@ -949,6 +949,8 @@ describe('<UserNotifications />', function () {
|
||||
'ol-groupsAndEnterpriseBannerVariant',
|
||||
'on-premise'
|
||||
)
|
||||
|
||||
window.metaAttributesCache.set('ol-inactiveTutorials', '[]')
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
@@ -974,9 +976,9 @@ describe('<UserNotifications />', function () {
|
||||
await screen.findByRole('link', { name: 'Contact sales' })
|
||||
})
|
||||
|
||||
it('shows the banner for users that have dismissed the banner more than 30 days ago', async function () {
|
||||
it('does not show the banner for users that have dismissed the banner within the last 30 days and before server-side state', async function () {
|
||||
const dismissed = new Date()
|
||||
dismissed.setDate(dismissed.getDate() - 31) // 31 days
|
||||
dismissed.setDate(dismissed.getDate() - 29) // 29 days
|
||||
window.metaAttributesCache.set('ol-showGroupsAndEnterpriseBanner', true)
|
||||
localStorage.setItem(
|
||||
'has_dismissed_groups_and_enterprise_banner',
|
||||
@@ -986,17 +988,28 @@ describe('<UserNotifications />', function () {
|
||||
renderWithinProjectListProvider(GroupsAndEnterpriseBanner)
|
||||
await fetchMock.callHistory.flush(true)
|
||||
|
||||
await screen.findByRole('link', { name: 'Contact sales' })
|
||||
expect(screen.queryByRole('link', { name: 'Contact sales' })).to.be.null
|
||||
})
|
||||
|
||||
it('does not show the banner for users that have dismissed the banner within the last 30 days', async function () {
|
||||
const dismissed = new Date()
|
||||
dismissed.setDate(dismissed.getDate() - 29) // 29 days
|
||||
window.metaAttributesCache.set('ol-showGroupsAndEnterpriseBanner', true)
|
||||
localStorage.setItem(
|
||||
'has_dismissed_groups_and_enterprise_banner',
|
||||
dismissed
|
||||
it('shows the banner for users who have not dismissed the repeat appearance', async function () {
|
||||
window.metaAttributesCache.set(
|
||||
'ol-inactiveTutorials',
|
||||
'["groups-enterprise-banner"]'
|
||||
)
|
||||
window.metaAttributesCache.set('ol-showGroupsAndEnterpriseBanner', true)
|
||||
|
||||
renderWithinProjectListProvider(GroupsAndEnterpriseBanner)
|
||||
await fetchMock.callHistory.flush(true)
|
||||
|
||||
expect(screen.queryByRole('link', { name: 'Contact sales' })).to.be.null
|
||||
})
|
||||
|
||||
it('does not show the banner for users with both inactive tutorials', async function () {
|
||||
window.metaAttributesCache.set(
|
||||
'ol-inactiveTutorials',
|
||||
'["groups-enterprise-banner", "groups-enterprise-banner-repeat"]'
|
||||
)
|
||||
window.metaAttributesCache.set('ol-showGroupsAndEnterpriseBanner', true)
|
||||
|
||||
renderWithinProjectListProvider(GroupsAndEnterpriseBanner)
|
||||
await fetchMock.callHistory.flush(true)
|
||||
|
||||
Reference in New Issue
Block a user