From c722dcbc8ddf1cb4a034be6d081c668daf4574e9 Mon Sep 17 00:00:00 2001 From: M Fahru Date: Tue, 28 May 2024 09:48:29 -0700 Subject: [PATCH] Merge pull request #18419 from overleaf/mf-plans-page-permanent-url [web] Create permanent URL for all plans and switch state in the plans page GitOrigin-RevId: 5fa0af0e33868f8fa4f6a74627bedb4c22705cb0 --- .../layout/fat-footer-website-redesign.pug | 5 +- services/web/app/views/layout/fat-footer.pug | 5 +- .../subscription/plans-v2/plans-v2-hash.js | 47 ++++++++++ .../subscription/plans-v2/plans-v2-main.js | 89 ++++++++++++++----- 4 files changed, 120 insertions(+), 26 deletions(-) create mode 100644 services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-hash.js diff --git a/services/web/app/views/layout/fat-footer-website-redesign.pug b/services/web/app/views/layout/fat-footer-website-redesign.pug index 0c845f153f..5758bf2dbf 100644 --- a/services/web/app/views/layout/fat-footer-website-redesign.pug +++ b/services/web/app/views/layout/fat-footer-website-redesign.pug @@ -49,7 +49,10 @@ footer.fat-footer.hidden-print.website-redesign-fat-footer li a(href="/for/universities") #{translate('for_universities')} li - a(href="/user/subscription/plans?itm_referrer=footer-for-students#view=student") #{translate('for_students')} + a( + data-ol-for-students-link + href="/user/subscription/plans?itm_referrer=footer-for-students#student-annual" + ) #{translate('for_students')} .footer-section h2.footer-section-heading #{translate('get_involved')} diff --git a/services/web/app/views/layout/fat-footer.pug b/services/web/app/views/layout/fat-footer.pug index 1dc02a5056..f4c32d3bb7 100644 --- a/services/web/app/views/layout/fat-footer.pug +++ b/services/web/app/views/layout/fat-footer.pug @@ -49,7 +49,10 @@ footer.fat-footer.hidden-print li a(href="/for/universities") #{translate('for_universities')} li - a(href="/user/subscription/plans?itm_referrer=footer-for-students#view=student") #{translate('for_students')} + a( + data-ol-for-students-link + href="/user/subscription/plans?itm_referrer=footer-for-students#student-annual" + ) #{translate('for_students')} .footer-section h2.footer-section-heading #{translate('get_involved')} diff --git a/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-hash.js b/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-hash.js new file mode 100644 index 0000000000..cfc6add521 --- /dev/null +++ b/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-hash.js @@ -0,0 +1,47 @@ +export function getViewInfoFromHash() { + const hash = window.location.hash.substring(1) + + switch (hash) { + case 'individual-monthly': + return ['individual', 'monthly'] + case 'individual-annual': + return ['individual', 'annual'] + case 'group': + return ['group', 'annual'] + case 'student-monthly': + return ['student', 'monthly'] + case 'student-annual': + return ['student', 'annual'] + default: + return ['individual', 'monthly'] + } +} + +/** + * + * @param {individual | group | student} viewTab + * @param {monthly | annual} period + */ +export function setHashFromViewTab(viewTab, period) { + const newHash = viewTab === 'group' ? 'group' : `${viewTab}-${period}` + if (window.location.hash.substring(1)) { + window.location.hash = newHash + } +} + +// this is only for the students link in footer +export function handleForStudentsLinkInFooter() { + const links = document.querySelectorAll('[data-ol-for-students-link]') + + links.forEach(function (link) { + link.addEventListener('click', function () { + if (window.location.pathname === '/user/subscription/plans') { + // reload location with the correct hash + const newURL = + '/user/subscription/plans?itm_referrer=footer-for-students#student-annual' + history.replaceState(null, '', newURL) + location.reload() + } + }) + }) +} diff --git a/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-main.js b/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-main.js index aac0a52db6..ad0ae6a2ca 100644 --- a/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-main.js +++ b/services/web/frontend/js/pages/user/subscription/plans-v2/plans-v2-main.js @@ -13,6 +13,11 @@ import { updateMainGroupPlanPricing, } from './plans-v2-group-plan' import { setUpGroupSubscriptionButtonAction } from './plans-v2-subscription-button' +import { + getViewInfoFromHash, + handleForStudentsLinkInFooter, + setHashFromViewTab, +} from './plans-v2-hash' import getMeta from '../../../../utils/meta' const currentCurrencyCode = getMeta('ol-recommendedCurrency') @@ -116,6 +121,9 @@ function selectTab(viewTab) { updateMonthlyAnnualSwitchValue(viewTab) toggleUniversityInfo(viewTab) + + // update the hash to reflect the current view when switching individual, group, or student tabs + setHashFromViewTab(viewTab, currentMonthlyAnnualSwitchValue) } function updateMonthlyAnnualSwitchValue(viewTab) { @@ -190,19 +198,22 @@ function toggleUniversityInfo(viewTab) { el.hidden = viewTab !== 'student' } -function selectViewFromHash() { +// This is the old scheme for hashing redirection +// This is deprecated and should be removed in the future +// This is only used for backward compatibility +function selectViewFromHashDeprecated() { try { const params = new URLSearchParams(window.location.hash.substring(1)) const view = params.get('view') if (view) { // View params are expected to be of the format e.g. individual or individual-monthly - const [tab, cadence] = view.split('-') + const [tab, period] = view.split('-') // make sure the selected view is valid if (document.querySelector(`[data-ol-plans-v2-view-tab="${tab}"]`)) { selectTab(tab) - if (['monthly', 'annual'].includes(cadence)) { - currentMonthlyAnnualSwitchValue = cadence + if (['monthly', 'annual'].includes(period)) { + currentMonthlyAnnualSwitchValue = period } else { // set annual as the default currentMonthlyAnnualSwitchValue = 'annual' @@ -210,24 +221,8 @@ function selectViewFromHash() { updateMonthlyAnnualSwitchValue(tab) - // clear the hash so it doesn't persist when switching plans - const currentURL = window.location.pathname + window.location.search - history.replaceState('', document.title, currentURL) - - // Add a small delay since it seems the scroll won't behave correctly on this scenario: - // 1. Open plans page - // 2. Click on "Group Plans" - // 3. Scroll down to footer - // 4. Click "For students" link - // - // I assume this is happening because the `selectTab` function above is doing a lot - // of computation to change the view and it somehow prevents the `window.scrollTo` command - // to behave correctly. - const SCROLL_TO_TOP_DELAY = 50 - - window.setTimeout(() => { - window.scrollTo({ top: 0, behavior: 'smooth' }) - }, SCROLL_TO_TOP_DELAY) + // change the hash with the new scheme + setHashFromViewTab(tab, currentMonthlyAnnualSwitchValue) } } } catch { @@ -235,6 +230,35 @@ function selectViewFromHash() { } } +function selectViewAndPeriodFromHash() { + const [viewTab, period] = getViewInfoFromHash() + + // the sequence of these three lines is important + // because `currentMonthlyAnnualSwitchValue` is mutable. + // `selectTab` and `updateMonthlyAnnualSwitchValue` depend on the value of `currentMonthlyAnnualSwitchValue` + // to determine the UI state + currentMonthlyAnnualSwitchValue = period + selectTab(viewTab) + updateMonthlyAnnualSwitchValue(viewTab) + + // handle the case where user access plans page while still on the plans page + // current example would the the "For students" link on the footer + const SCROLL_TO_TOP_DELAY = 50 + window.setTimeout(() => { + window.scrollTo({ top: 0, behavior: 'smooth' }) + }, SCROLL_TO_TOP_DELAY) +} + +// call the function to select the view and period from the hash value +// this is called once when the page is loaded +if (window.location.hash) { + if (window.location.hash.includes('view')) { + selectViewFromHashDeprecated() + } else { + selectViewAndPeriodFromHash() + } +} + document .querySelector('[data-ol-plans-v2-m-a-switch]') .addEventListener('click', () => { @@ -249,6 +273,15 @@ document } switchMonthlyAnnual(currentMonthlyAnnualSwitchValue) + + // update the hash to reflect the current view when pressing the monthly-annual switch + const DEFAULT_VIEW_TAB = 'individual' + const viewTab = + document + .querySelector('[data-ol-plans-v2-m-a-switch-container]') + .getAttribute('data-ol-current-view') ?? DEFAULT_VIEW_TAB + + setHashFromViewTab(viewTab, currentMonthlyAnnualSwitchValue) }) document @@ -261,6 +294,14 @@ setUpMonthlyAnnualSwitching() setUpGroupSubscriptionButtonAction() setUpStickyHeaderObserver() updateLinkTargets() +handleForStudentsLinkInFooter() -selectViewFromHash() -window.addEventListener('hashchange', selectViewFromHash) +window.addEventListener('hashchange', () => { + if (window.location.hash) { + if (window.location.hash.includes('view')) { + selectViewFromHashDeprecated() + } else { + selectViewAndPeriodFromHash() + } + } +})