Skip to content

Commit

Permalink
feat: add w3up-launch announcement banner (when env.NEXT_PUBLIC_W3UP_…
Browse files Browse the repository at this point in the history
…LAUNCH_SUNSET_ANNOUNCEMENT_START is set) (#2319)

Motivation:
* https://github.com/web3-storage/secrets/issues/20

Tactics:
* there is a new env var
`NEXT_PUBLIC_W3UP_LAUNCH_SUNSET_ANNOUNCEMENT_START` that is an ISO8601
datetime of when the announcement should appear. If unset, the
announcement will never appear. My intention is to set it only on the
staging environment for acceptance testing. Once we're happy on staging,
we can enable it on production by setting this var to whatever datetime
we want it to appear.

Scope:
* when enabled at after the appropriate time, the banner should appear
on the following routes
  * /
  * /account
  * /account/payment
  * /docs
* the github actions `website` workflow now uses github environments.
This is because I want to use them to configure the staging environment
to have a particular value for
`NEXT_PUBLIC_W3UP_LAUNCH_SUNSET_ANNOUNCEMENT_START`
  • Loading branch information
gobengo authored Nov 8, 2023
1 parent 5096645 commit f3d5b97
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ jobs:
core.setFailed('e2e tests did not succeed')
preview:
name: Preview
environment:
name: ${{ (github.ref_name == 'main') && 'staging' || format('preview-{0}', github.ref_name) }}
url: ${{ (github.ref_name == 'main') && 'https://staging.web3.storage/' || steps.cloudflare_url.outputs.stdout }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/website/components/messagebanner/messagebanner.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@
}
}

.message-banner-underline-links *:link {
text-decoration: underline;
}

.message-banner-close-button {
position: absolute;
display: flex;
Expand Down
59 changes: 59 additions & 0 deletions packages/website/components/page-banner/page-banner-portal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @fileoverview component that contains a 'page banner', i.e. a banner across the whole
* web page that shows a prominent message to the end-user.
*/

import clsx from 'clsx';
import * as React from 'react';
import * as ReactDOM from 'react-dom';

export const defaultPortalElementId = 'page-banner-portal';
export const defaultContentsClassName = 'page-banner-contents';
export const defaultPortalQuerySelector = `#${defaultPortalElementId} .${defaultContentsClassName}`;

/**
* wrap children in stiles like a .message-banner (see ../messagebanner/messagebanner.scss)
*/
export function MessageBannerStyled({ children }) {
return (
<div className={clsx('message-banner-wrapper')}>
<div className="message-banner-container" style={{ padding: '0.5em' }}>
<div className={clsx('message-banner-content', 'message-banner-underline-links', 'mb-reduced-fontsize')}>
{children}
</div>
</div>
</div>
);
}

/**
* component for an element across the top of page that can host banner messages on a per-page basis.
* Other components will use ReactDOM.createPortal() to render into this even though it isn't a child of
* that component (since it needs to be atop the page).
*/
export const PageBannerPortal = ({ id, contentsRef, contentsClassName = defaultContentsClassName }) => {
return (
<div id={id}>
<div className={contentsClassName} ref={contentsRef}></div>
</div>
);
};

/**
* React Context used for passing around the HTMLElement for the PageBannerPortal
* so that the PageBanner component can pass it to React.createPortal.
*/
export const PageBannerPortalContainerContext = React.createContext(undefined);

/**
* render children into a PageBannerPortal at the top of the page
*/
export const PageBanner = ({ children }) => {
const container = React.useContext(PageBannerPortalContainerContext);
const bannerChild = (
<div>
<MessageBannerStyled>{children}</MessageBannerStyled>
</div>
);
return <>{container && children && ReactDOM.createPortal(bannerChild, container)}</>;
};
59 changes: 59 additions & 0 deletions packages/website/components/w3up-launch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from 'react';

const sunsetStartEnv = process.env.NEXT_PUBLIC_W3UP_LAUNCH_SUNSET_START ?? '2023-01-09T00:00:00Z';
const sunsetStartDate = new Date(Date.parse(sunsetStartEnv));

/**
* If this isn't set, no announcements will appear
*/
const sunsetAnnouncementStartEnv = process.env.NEXT_PUBLIC_W3UP_LAUNCH_SUNSET_ANNOUNCEMENT_START;

/**
* after this datetime, show announcements that web3.storage is sunset
* and end-users should switch to w3up/console.web3.storage
*/
const sunsetAnnouncementStartDate = sunsetAnnouncementStartEnv
? new Date(Date.parse(sunsetAnnouncementStartEnv))
: undefined;

/**
* Return whether sunset announcements related to w3up-launch should be shown.
* An announcement date must be explicitly configured via env var, and now must be after that date.
* @param {Date} at - time at which to return whether to show the announcement
* @param {Date|undefined} [announcementStartDate] - when to begin showing announcements.
* If not provided, always return false.
*/
export const shouldShowSunsetAnnouncement = (at = new Date(), announcementStartDate = sunsetAnnouncementStartDate) => {
return announcementStartDate && at > announcementStartDate;
};

export const w3upLaunchConfig = {
type: 'W3upLaunchConfig',
stages: {
sunsetAnnouncement: {
start: sunsetAnnouncementStartDate,
},
sunset: {
start: sunsetStartDate,
},
},
shouldShowSunsetAnnouncement: shouldShowSunsetAnnouncement(),
};

/**
* copy for banner message across top of some web3.storage pages when w3up ships
*/
export const W3upMigrationRecommendationCopy = () => {
const createNewAccountHref = 'https://console.web3.storage/?intent=create-account';
const learnWhatsNewHref = 'https://console.web3.storage/?intent=learn-new-web3storage-experience';
const sunsetDateFormatter = new Intl.DateTimeFormat(undefined, { dateStyle: 'long' });
return (
<>
This web3.storage product will sunset on {sunsetDateFormatter.format(sunsetStartDate)}. We recommend migrating
your usage of web3.storage to the new web3.storage.
<br />
<a href={createNewAccountHref}>Click here to create a new account</a> and&nbsp;
<a href={learnWhatsNewHref}>here to read about what’s awesome</a> about the new web3.storage experience.
</>
);
};
14 changes: 10 additions & 4 deletions packages/website/modules/docs-theme/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect } from 'react';
import { MDXProvider } from '@mdx-js/react';

import Head from 'next/head';
import { useRouter } from 'next/router';
import hljs from 'highlight.js/lib/core';
Expand All @@ -9,10 +8,13 @@ import powershell from 'highlight.js/lib/languages/powershell';
import bash from 'highlight.js/lib/languages/bash';
import go from 'highlight.js/lib/languages/go';
import json from 'highlight.js/lib/languages/json';

import Sidebar from './sidebar/sidebar';
import Feedback from './feedback/feedback';
import Toc from './toc/toc';
import DocsPagination from './docspagination/docspagination';
import { W3upMigrationRecommendationCopy, shouldShowSunsetAnnouncement } from '../../components/w3up-launch.js';
import * as PageBannerPortal from '../../components/page-banner/page-banner-portal.js';

hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('powershell', powershell);
Expand All @@ -27,7 +29,7 @@ export default function Docs(props) {
useEffect(() => {
const pres = document.querySelectorAll('pre');

pres.forEach((pre) => {
pres.forEach(pre => {
const code = pre.firstElementChild?.innerHTML;
if (code) {
const button = document.createElement('button');
Expand All @@ -39,7 +41,7 @@ export default function Docs(props) {
button.classList.add('code-copy-button');
document.body.appendChild(textarea);

button.addEventListener('click', (event) => {
button.addEventListener('click', event => {
if (!navigator.clipboard) {
textarea.focus({ preventScroll: true });
textarea.select();
Expand All @@ -63,7 +65,6 @@ export default function Docs(props) {

pre.appendChild(button);
}

});

hljs.highlightAll();
Expand Down Expand Up @@ -91,6 +92,11 @@ export default function Docs(props) {
return function Layout({ children }) {
return (
<>
{shouldShowSunsetAnnouncement() && (
<PageBannerPortal.PageBanner>
<W3upMigrationRecommendationCopy />
</PageBannerPortal.PageBanner>
)}
{sharedHead}
<div className="docs-container">
<Sidebar openMenu={null} />
Expand Down
35 changes: 28 additions & 7 deletions packages/website/pages/_app.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import '../styles/global.scss';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import clsx from 'clsx';
import Script from 'next/script';

import Metadata from 'components/general/metadata';
import RestrictedRoute from 'components/general/restrictedRoute';
import AppProviders from 'components/general/appProviders';
import {
PageBannerPortal,
defaultPortalElementId,
PageBannerPortalContainerContext,
} from '../components/page-banner/page-banner-portal.js';
import MessageBanner from '../components/messagebanner/messagebanner.js';
import Navigation from '../components/navigation/navigation.js';
import Footer from '../components/footer/footer.js';
Expand All @@ -19,6 +24,7 @@ const App = ({ Component, pageProps }) => {
const productRoutes = ['/login', '/account', '/account/payment', '/tokens', '/callback'];
const productApp = productRoutes.includes(pathname);
const pageClass = pathname.includes('docs') ? 'docs-site' : productApp ? 'product-app' : 'marketing-site';
const [pageBannerPortalEl, setPageBannerPortalEl] = useState();

useEffect(() => {
document.querySelector('body')?.classList.add(pageClass);
Expand All @@ -41,13 +47,28 @@ const App = ({ Component, pageProps }) => {
referrerPolicy="no-referrer-when-downgrade"
/>
</noscript>
<div id="master-container" className={clsx(pageClass)}>
{productApp && <div className="corkscrew-background"></div>}
<MessageBanner />
<Navigation isProductApp={productApp} breadcrumbs={pageProps.breadcrumbs} />
<Component {...pageProps} />
<Footer isProductApp={productApp} />

<div
style={{
// needed so this banner is above .corkscrew-background
zIndex: 1,
// must be positioned for zIndex to take effect
position: 'relative',
}}
>
<PageBannerPortal id={defaultPortalElementId} contentsRef={setPageBannerPortalEl}></PageBannerPortal>
</div>

<PageBannerPortalContainerContext.Provider value={pageBannerPortalEl}>
<div id="master-container" className={clsx(pageClass)} style={{ zIndex: 0 }}>
{productApp && <div className="corkscrew-background"></div>}

<MessageBanner />
<Navigation isProductApp={productApp} breadcrumbs={pageProps.breadcrumbs} />
<Component {...pageProps} />
<Footer isProductApp={productApp} />
</div>
</PageBannerPortalContainerContext.Provider>
</RestrictedRoute>
</AppProviders>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/website/pages/_document.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Document, { Html, Head, Main, NextScript } from 'next/document';

import { w3upLaunchConfig } from '../components/w3up-launch.js';

class MyDocument extends Document {
/**
* @param {import("next/document").DocumentContext} ctx
Expand Down Expand Up @@ -28,6 +30,11 @@ class MyDocument extends Document {
<Main />
<NextScript />
<div id="modal-root"></div>
{/* add this for debuggability of launch announcements that only appear when configured */}
<script
type="application/json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(w3upLaunchConfig) }}
></script>
</body>
</Html>
);
Expand Down
7 changes: 7 additions & 0 deletions packages/website/pages/account/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import FileUploader from '../../components/account/fileUploader/fileUploader';
import GradientBackground from '../../components/gradientbackground/gradientbackground.js';
import AppData from '../../content/pages/app/account.json';
import GeneralPageData from '../../content/pages/general.json';
import { W3upMigrationRecommendationCopy, shouldShowSunsetAnnouncement } from '../../components/w3up-launch.js';
import * as PageBannerPortal from '../../components/page-banner/page-banner-portal.js';

export const CTACardTypes = {
API_TOKENS: 'API_TOKENS',
Expand Down Expand Up @@ -79,6 +81,11 @@ const Account = () => {

return (
<>
{shouldShowSunsetAnnouncement() && (
<PageBannerPortal.PageBanner>
<W3upMigrationRecommendationCopy />
</PageBannerPortal.PageBanner>
)}
<div className="page-container account-container">
<h1 className="table-heading">{dashboard.heading}</h1>
<div className="account-content">
Expand Down
8 changes: 7 additions & 1 deletion packages/website/pages/account/payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { plans, freePlan } from '../../components/contexts/plansContext';
import { userBillingSettings } from '../../lib/api';
import GeneralPageData from '../../content/pages/general.json';
import constants from '../../lib/constants.js';
import { W3upMigrationRecommendationCopy, shouldShowSunsetAnnouncement } from '../../components/w3up-launch.js';
import * as PageBannerPortal from '../../components/page-banner/page-banner-portal.js';

/**
* @typedef {import('../../components/contexts/plansContext').Plan} Plan
Expand Down Expand Up @@ -123,9 +125,13 @@ const PaymentSettingsPage = props => {
const savedPaymentMethod = useMemo(() => {
return paymentSettings?.paymentMethod;
}, [paymentSettings]);

return (
<>
{shouldShowSunsetAnnouncement() && (
<PageBannerPortal.PageBanner>
<W3upMigrationRecommendationCopy />
</PageBannerPortal.PageBanner>
)}
<>
<div className="page-container billing-container">
<div className="">
Expand Down
8 changes: 8 additions & 0 deletions packages/website/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import IndexPageData from '../content/pages/index.json';
import Scroll2Top from '../components/scroll2top/scroll2top.js';
import BlockBuilder from '../components/blockbuilder/blockbuilder.js';
import { initFloaterAnimations } from '../lib/floater-animations.js';
import { W3upMigrationRecommendationCopy, shouldShowSunsetAnnouncement } from '../components/w3up-launch.js';
import * as PageBannerPortal from '../components/page-banner/page-banner-portal.js';

export default function Home() {
const sections = IndexPageData.page_content;
Expand All @@ -23,6 +25,12 @@ export default function Home() {

return (
<>
{shouldShowSunsetAnnouncement() && (
<PageBannerPortal.PageBanner>
<W3upMigrationRecommendationCopy />
</PageBannerPortal.PageBanner>
)}

<main className="page page-index">
{sections.map((section, index) => (
<BlockBuilder id={`section_${index + 1}`} key={`section_${index + 1}`} subsections={section} />
Expand Down

0 comments on commit f3d5b97

Please sign in to comment.