diff --git a/packages/dashboard-v2/gatsby-browser.js b/packages/dashboard-v2/gatsby-browser.js
index 030fcadd..927fd206 100644
--- a/packages/dashboard-v2/gatsby-browser.js
+++ b/packages/dashboard-v2/gatsby-browser.js
@@ -1,5 +1,7 @@
import * as React from "react";
import { SWRConfig } from "swr";
+import { Elements } from "@stripe/react-stripe-js";
+import { loadStripe } from "@stripe/stripe-js";
import "@fontsource/sora/300.css"; // light
import "@fontsource/sora/400.css"; // normal
import "@fontsource/sora/500.css"; // medium
@@ -11,15 +13,19 @@ import swrConfig from "./src/lib/swrConfig";
import { MODAL_ROOT_ID } from "./src/components/Modal";
import { PortalSettingsProvider } from "./src/contexts/portal-settings";
+const stripePromise = loadStripe(process.env.GATSBY_STRIPE_PUBLISHABLE_KEY);
+
export function wrapPageElement({ element, props }) {
const Layout = element.type.Layout ?? React.Fragment;
return (
-
- {element}
-
-
+
+
+ {element}
+
+
+
);
diff --git a/packages/dashboard-v2/gatsby-ssr.js b/packages/dashboard-v2/gatsby-ssr.js
index 030fcadd..927fd206 100644
--- a/packages/dashboard-v2/gatsby-ssr.js
+++ b/packages/dashboard-v2/gatsby-ssr.js
@@ -1,5 +1,7 @@
import * as React from "react";
import { SWRConfig } from "swr";
+import { Elements } from "@stripe/react-stripe-js";
+import { loadStripe } from "@stripe/stripe-js";
import "@fontsource/sora/300.css"; // light
import "@fontsource/sora/400.css"; // normal
import "@fontsource/sora/500.css"; // medium
@@ -11,15 +13,19 @@ import swrConfig from "./src/lib/swrConfig";
import { MODAL_ROOT_ID } from "./src/components/Modal";
import { PortalSettingsProvider } from "./src/contexts/portal-settings";
+const stripePromise = loadStripe(process.env.GATSBY_STRIPE_PUBLISHABLE_KEY);
+
export function wrapPageElement({ element, props }) {
const Layout = element.type.Layout ?? React.Fragment;
return (
-
- {element}
-
-
+
+
+ {element}
+
+
+
);
diff --git a/packages/dashboard-v2/package.json b/packages/dashboard-v2/package.json
index 2e17c56b..53edfa12 100644
--- a/packages/dashboard-v2/package.json
+++ b/packages/dashboard-v2/package.json
@@ -22,6 +22,8 @@
"dependencies": {
"@fontsource/sora": "^4.5.3",
"@fontsource/source-sans-pro": "^4.5.3",
+ "@stripe/react-stripe-js": "^1.7.1",
+ "@stripe/stripe-js": "^1.27.0",
"classnames": "^2.3.1",
"copy-text-to-clipboard": "^3.0.1",
"dayjs": "^1.10.8",
diff --git a/packages/dashboard-v2/src/contexts/plans/PlansProvider.js b/packages/dashboard-v2/src/contexts/plans/PlansProvider.js
index 7c6579ad..906bd7f4 100644
--- a/packages/dashboard-v2/src/contexts/plans/PlansProvider.js
+++ b/packages/dashboard-v2/src/contexts/plans/PlansProvider.js
@@ -19,14 +19,16 @@ const aggregatePlansAndLimits = (plans, limits, { includeFreePlan }) => {
// Decorate each plan with its corresponding limits data, if available.
if (limits?.length) {
- return limits.map((limitsDescriptor, index) => {
- const asssociatedPlan = sortedPlans.find((plan) => plan.tier === index) || {};
+ return limits
+ .map((limitsDescriptor, index) => {
+ const asssociatedPlan = sortedPlans.find((plan) => plan.tier === index) || {};
- return {
- ...asssociatedPlan,
- limits: limitsDescriptor || null,
- };
- });
+ return {
+ ...asssociatedPlan,
+ limits: limitsDescriptor || null,
+ };
+ })
+ .slice(includeFreePlan ? 1 : 2);
}
// If we don't have the limits data yet, set just return the plans.
diff --git a/packages/dashboard-v2/src/pages/upgrade.js b/packages/dashboard-v2/src/pages/upgrade.js
index f3a531da..61973ec0 100644
--- a/packages/dashboard-v2/src/pages/upgrade.js
+++ b/packages/dashboard-v2/src/pages/upgrade.js
@@ -1,5 +1,7 @@
import * as React from "react";
import styled from "styled-components";
+import { useStripe } from "@stripe/react-stripe-js";
+import cn from "classnames";
import { useUser } from "../contexts/user";
import { PlansProvider } from "../contexts/plans/PlansProvider";
@@ -13,7 +15,9 @@ import { usePortalSettings } from "../contexts/portal-settings";
import { Alert } from "../components/Alert";
import HighlightedLink from "../components/HighlightedLink";
import { Metadata } from "../components/Metadata";
+import accountsService from "../services/accountsService";
import humanBytes from "../lib/humanBytes";
+import { Modal } from "../components/Modal";
const PAID_PORTAL_BREAKPOINTS = [
{
@@ -77,6 +81,11 @@ const PlansSlider = () => {
const { user, error: userError } = useUser();
const { plans, loading, activePlan, error: plansError } = useActivePlan(user);
const { settings } = usePortalSettings();
+ const [showPaymentError, setShowPaymentError] = React.useState(true);
+ const stripe = useStripe();
+ // This will be the base plan that we compare upload/download speeds against.
+ // On will either be the user's active plan or lowest of available tiers.
+ const basePlan = activePlan || plans[0];
if (userError || plansError) {
return (
@@ -87,6 +96,22 @@ const PlansSlider = () => {
);
}
+ const handleSubscribe = async (selectedPlan) => {
+ try {
+ const { sessionId } = await accountsService
+ .post("stripe/checkout", {
+ json: {
+ price: selectedPlan.stripe,
+ },
+ })
+ .json();
+ await stripe.redirectToCheckout({ sessionId });
+ } catch (error) {
+ console.log(error);
+ setShowPaymentError(true);
+ }
+ };
+
return (
@@ -108,40 +133,90 @@ const PlansSlider = () => {
{
const isHigherThanCurrent = plan.tier > activePlan?.tier;
+ const isCurrentPlanPaid = activePlan?.tier > 1;
const isCurrent = plan.tier === activePlan?.tier;
+ const isLower = plan.tier < activePlan?.tier;
+ const speed = plan.limits.uploadBandwidth;
+ const currentSpeed = basePlan?.limits?.uploadBandwidth;
+ const speedChange = speed > currentSpeed ? speed / currentSpeed : currentSpeed / speed;
+ const hasActivePlan = Boolean(activePlan);
return (
-
+
+ {isCurrent && (
+
+
+ Current plan
+
+
+ )}
{plan.name}
{plan.description}
-
+ {(!hasActivePlan || isHigherThanCurrent) &&
+ (isCurrentPlanPaid ? (
+
+ ) : (
+
+ ))}
+ {isCurrent && }
+ {isLower && (
+
+ )}
{plan.limits && (
- Pin up to {storage(plan.limits.storageLimit)} of censorship-resistant storage
+ Pin up to {storage(plan.limits.storageLimit)} on decentralized storage
Support for up to {localizedNumber(plan.limits.maxNumberUploads)} files
- {bandwidth(plan.limits.uploadBandwidth)} upload bandwidth
- {bandwidth(plan.limits.downloadBandwidth)} download bandwidth
+
+ {speed === currentSpeed
+ ? `${bandwidth(plan.limits.uploadBandwidth)} upload and ${bandwidth(
+ plan.limits.downloadBandwidth
+ )} download`
+ : `${speedChange}X ${
+ speed > currentSpeed ? "faster" : "slower"
+ } upload and download speeds (${bandwidth(plan.limits.uploadBandwidth)} and ${bandwidth(
+ plan.limits.downloadBandwidth
+ )})`}
+
+
+ {plan.limits.maxUploadSize === plan.limits.storageLimit
+ ? "No limit to file upload size"
+ : `Upload files up to ${storage(plan.limits.maxUploadSize)}`}
+
)}
);
})}
breakpoints={settings.isSubscriptionRequired ? PAID_PORTAL_BREAKPOINTS : FREE_PORTAL_BREAKPOINTS}
- className="px-8 sm:px-4 md:px-0 lg:px-0"
+ className="px-8 sm:px-4 md:px-0 lg:px-0 mt-10"
/>
)}
+ {showPaymentError && (
+ setShowPaymentError(false)}>
+ Oops! 😔
+ There was an error contacting our payments provider
+ Please try again later
+
+ )}
);
};
diff --git a/packages/dashboard-v2/yarn.lock b/packages/dashboard-v2/yarn.lock
index bf3123ca..ae43c2db 100644
--- a/packages/dashboard-v2/yarn.lock
+++ b/packages/dashboard-v2/yarn.lock
@@ -3107,6 +3107,18 @@
resolve-from "^5.0.0"
store2 "^2.12.0"
+"@stripe/react-stripe-js@^1.7.1":
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-1.7.1.tgz#6e1db8f4a0eaf2193b153173d4aa7c38b681310d"
+ integrity sha512-GiUPoMo0xVvmpRD6JR9JAhAZ0W3ZpnYZNi0KE+91+tzrSFVpChKZbeSsJ5InlZhHFk9NckJCt1wOYBTqNsvt3A==
+ dependencies:
+ prop-types "^15.7.2"
+
+"@stripe/stripe-js@^1.27.0":
+ version "1.27.0"
+ resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.27.0.tgz#ab0c82fa89fd40260de4414f69868b769e810550"
+ integrity sha512-SEiybUBu+tlsFKuzdFFydxxjkbrdzHo0tz/naYC5Dt9or/Ux2gcKJBPYQ4RmqQCNHFxgyNj6UYsclywwhe2inQ==
+
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"