diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
index 3ad69b9d53..ec7f62f6cc 100644
--- a/.github/workflows/website.yml
+++ b/.github/workflows/website.yml
@@ -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
diff --git a/package-lock.json b/package-lock.json
index 2b07b21f47..d4b5bdfc97 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23830,7 +23830,7 @@
},
"packages/api": {
"name": "@web3-storage/api",
- "version": "7.18.1",
+ "version": "7.20.0",
"license": "(Apache-2.0 OR MIT)",
"dependencies": {
"@aws-sdk/client-s3": "^3.53.1",
@@ -26650,7 +26650,7 @@
},
"packages/website": {
"name": "@web3-storage/website",
- "version": "2.36.1",
+ "version": "2.36.3",
"dependencies": {
"@docsearch/react": "^3.0.0",
"@fortawesome/free-brands-svg-icons": "^6.1.2",
diff --git a/packages/website/components/messagebanner/messagebanner.scss b/packages/website/components/messagebanner/messagebanner.scss
index 487642bbe1..43341872c0 100644
--- a/packages/website/components/messagebanner/messagebanner.scss
+++ b/packages/website/components/messagebanner/messagebanner.scss
@@ -75,6 +75,10 @@
}
}
+.message-banner-underline-links *:link {
+ text-decoration: underline;
+}
+
.message-banner-close-button {
position: absolute;
display: flex;
diff --git a/packages/website/components/page-banner/page-banner-portal.js b/packages/website/components/page-banner/page-banner-portal.js
new file mode 100644
index 0000000000..d94e6aa1de
--- /dev/null
+++ b/packages/website/components/page-banner/page-banner-portal.js
@@ -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 (
+
+ );
+}
+
+/**
+ * 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 (
+
+ );
+};
+
+/**
+ * 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 = (
+
+ {children}
+
+ );
+ return <>{container && children && ReactDOM.createPortal(bannerChild, container)}>;
+};
diff --git a/packages/website/components/w3up-launch.js b/packages/website/components/w3up-launch.js
new file mode 100644
index 0000000000..d07d21c8b3
--- /dev/null
+++ b/packages/website/components/w3up-launch.js
@@ -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.
+
+ Click here to create a new account and
+ here to read about what’s awesome about the new web3.storage experience.
+ >
+ );
+};
diff --git a/packages/website/modules/docs-theme/index.js b/packages/website/modules/docs-theme/index.js
index 9de049f56c..3e3ca087e4 100644
--- a/packages/website/modules/docs-theme/index.js
+++ b/packages/website/modules/docs-theme/index.js
@@ -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';
@@ -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);
@@ -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');
@@ -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();
@@ -63,7 +65,6 @@ export default function Docs(props) {
pre.appendChild(button);
}
-
});
hljs.highlightAll();
@@ -91,6 +92,11 @@ export default function Docs(props) {
return function Layout({ children }) {
return (
<>
+ {shouldShowSunsetAnnouncement() && (
+
+
+
+ )}
{sharedHead}
diff --git a/packages/website/pages/_app.js b/packages/website/pages/_app.js
index 80da76b4d1..1fae4f0d74 100644
--- a/packages/website/pages/_app.js
+++ b/packages/website/pages/_app.js
@@ -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';
@@ -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);
@@ -41,13 +47,28 @@ const App = ({ Component, pageProps }) => {
referrerPolicy="no-referrer-when-downgrade"
/>
-
- {productApp &&
}
-
-
-
-
+
+
+
+
+
+ {productApp &&
}
+
+
+
+
+
+
+
);
diff --git a/packages/website/pages/_document.js b/packages/website/pages/_document.js
index 9d0ac92569..42b2a93952 100644
--- a/packages/website/pages/_document.js
+++ b/packages/website/pages/_document.js
@@ -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
@@ -28,6 +30,11 @@ class MyDocument extends Document {
+ {/* add this for debuggability of launch announcements that only appear when configured */}
+