From 8edf6d82533516efd722e4ac9f1d55177c612cbd Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 20:36:18 +0530 Subject: [PATCH 01/14] Remove unnecessary cast --- web/apps/photos/src/components/Badge.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/apps/photos/src/components/Badge.tsx b/web/apps/photos/src/components/Badge.tsx index a3aca884a1..0e5da17d9d 100644 --- a/web/apps/photos/src/components/Badge.tsx +++ b/web/apps/photos/src/components/Badge.tsx @@ -1,5 +1,4 @@ import { Box, styled } from "@mui/material"; -import { CSSProperties } from "@mui/material/styles/createTypography"; export const Badge = styled(Box)(({ theme }) => ({ borderRadius: theme.shape.borderRadius, @@ -8,5 +7,5 @@ export const Badge = styled(Box)(({ theme }) => ({ backdropFilter: `blur(${theme.colors.blur.muted})`, color: theme.colors.white.base, textTransform: "uppercase", - ...(theme.typography.tiny as CSSProperties), + ...theme.typography.mini, })); From f15729d73f65c5a1cfdd481183d87d8970fdfe1e Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 20:37:14 +0530 Subject: [PATCH 02/14] Inline --- web/apps/photos/src/components/Badge.tsx | 11 ----------- .../pages/gallery/PlanSelector/plans/planRow.tsx | 11 ++++++++++- 2 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 web/apps/photos/src/components/Badge.tsx diff --git a/web/apps/photos/src/components/Badge.tsx b/web/apps/photos/src/components/Badge.tsx deleted file mode 100644 index 0e5da17d9d..0000000000 --- a/web/apps/photos/src/components/Badge.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Box, styled } from "@mui/material"; - -export const Badge = styled(Box)(({ theme }) => ({ - borderRadius: theme.shape.borderRadius, - padding: "2px 4px", - backgroundColor: theme.colors.black.muted, - backdropFilter: `blur(${theme.colors.blur.muted})`, - color: theme.colors.white.base, - textTransform: "uppercase", - ...theme.typography.mini, -})); diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planRow.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planRow.tsx index 9701baf01a..d0bbb87460 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planRow.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planRow.tsx @@ -3,7 +3,6 @@ import { FlexWrapper, FluidContainer } from "@ente/shared/components/Container"; import ArrowForward from "@mui/icons-material/ArrowForward"; import Done from "@mui/icons-material/Done"; import { Box, Button, ButtonProps, Typography, styled } from "@mui/material"; -import { Badge } from "components/Badge"; import { PLAN_PERIOD } from "constants/gallery"; import { t } from "i18next"; import { Plan, Subscription } from "types/billing"; @@ -101,3 +100,13 @@ export function PlanRow({ ); } + +const Badge = styled(Box)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + padding: "2px 4px", + backgroundColor: theme.colors.black.muted, + backdropFilter: `blur(${theme.colors.blur.muted})`, + color: theme.colors.white.base, + textTransform: "uppercase", + ...theme.typography.mini, +})); From 1c4a6ca8b142007826e40f7cba15248ee8472584 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 20:39:28 +0530 Subject: [PATCH 03/14] Remove unused --- .../gallery/PlanSelector/plans/planTile.tsx | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planTile.tsx diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planTile.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planTile.tsx deleted file mode 100644 index de39335284..0000000000 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planTile.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { styled } from "@mui/material"; - -const PlanTile = styled("div")<{ current: boolean }>(({ theme, current }) => ({ - padding: theme.spacing(3), - border: `1px solid ${theme.palette.divider}`, - - "&:hover": { - backgroundColor: " rgba(40, 214, 101, 0.11)", - cursor: "pointer", - }, - ...(current && { - borderColor: theme.colors.accent.A500, - cursor: "not-allowed", - "&:hover": { backgroundColor: "transparent" }, - }), - width: " 260px", - "@media (min-width: 1152px)": { - "&:first-of-type": { - borderTopLeftRadius: "8px", - }, - - "&:last-of-type": { - borderTopRightRadius: "8px", - }, - }, - "@media (max-width: 1151px) and (min-width:551px)": { - "&:first-of-type": { - borderTopLeftRadius: "8px", - }, - - "&:nth-of-type(2)": { - borderTopRightRadius: "8px", - }, - }, - "@media (max-width: 551px)": { - "&:first-of-type": { - borderTopLeftRadius: "8px", - borderTopRightRadius: "8px", - }, - }, -})); - -export default PlanTile; From 0a03df924242f7be6c286c953bdf0e1fafb4c828 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 20:50:03 +0530 Subject: [PATCH 04/14] Inline --- .../pages/gallery/PlanSelector/card.tsx | 43 +++++++++++++++++-- .../gallery/PlanSelector/plans/BfAddOnRow.tsx | 42 ------------------ 2 files changed, 40 insertions(+), 45 deletions(-) delete mode 100644 web/apps/photos/src/components/pages/gallery/PlanSelector/plans/BfAddOnRow.tsx diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx index cb2e08368d..c722534cfe 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx @@ -1,8 +1,8 @@ import log from "@/base/log"; -import { bytesInGB } from "@/new/photos/utils/units"; +import { bytesInGB, formattedStorageByteSize } from "@/new/photos/utils/units"; import { SpaceBetweenFlex } from "@ente/shared/components/Container"; import Close from "@mui/icons-material/Close"; -import { IconButton, Link, Stack } from "@mui/material"; +import { IconButton, Link, Stack, styled } from "@mui/material"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import { PLAN_PERIOD } from "constants/gallery"; @@ -32,7 +32,6 @@ import { getTotalFamilyUsage, isPartOfFamily } from "utils/user/family"; import { ManageSubscription } from "./manageSubscription"; import { PeriodToggler } from "./periodToggler"; import Plans from "./plans"; -import { BFAddOnRow } from "./plans/BfAddOnRow"; interface Props { closeModal: any; @@ -353,3 +352,41 @@ function PaidSubscriptionPlanSelectorCard({ ); } + +function BFAddOnRow({ bonusData, closeModal }) { + return ( + <> + {bonusData.storageBonuses.map((bonus) => { + if (bonus.type.startsWith("ADD_ON")) { + return ( + + + + + + + + ); + } + return null; + })} + + ); +} + +const AddOnRowContainer = styled(SpaceBetweenFlex)(({ theme }) => ({ + // gap: theme.spacing(1.5), + padding: theme.spacing(1, 0), + cursor: "pointer", + "&:hover .endIcon": { + backgroundColor: "rgba(255,255,255,0.08)", + }, +})); diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/BfAddOnRow.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/BfAddOnRow.tsx deleted file mode 100644 index 6484ae2156..0000000000 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/BfAddOnRow.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { SpaceBetweenFlex } from "@ente/shared/components/Container"; -import { Box, styled, Typography } from "@mui/material"; - -import { formattedStorageByteSize } from "@/new/photos/utils/units"; -import { Trans } from "react-i18next"; - -const RowContainer = styled(SpaceBetweenFlex)(({ theme }) => ({ - // gap: theme.spacing(1.5), - padding: theme.spacing(1, 0), - cursor: "pointer", - "&:hover .endIcon": { - backgroundColor: "rgba(255,255,255,0.08)", - }, -})); -export function BFAddOnRow({ bonusData, closeModal }) { - return ( - <> - {bonusData.storageBonuses.map((bonus) => { - if (bonus.type.startsWith("ADD_ON")) { - return ( - - - - - - - - ); - } - return null; - })} - - ); -} From cd73a736f4facb3afb29237c3ec474e54679f499 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 20:51:44 +0530 Subject: [PATCH 05/14] Remove unused --- .../gallery/PlanSelector/plans/button.tsx | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 web/apps/photos/src/components/pages/gallery/PlanSelector/plans/button.tsx diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/button.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/button.tsx deleted file mode 100644 index f6ac71ac10..0000000000 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/button.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { SpaceBetweenFlex } from "@ente/shared/components/Container"; -import ChevronRight from "@mui/icons-material/ChevronRight"; -import Done from "@mui/icons-material/Done"; -import { Box, Button } from "@mui/material"; -import { t } from "i18next"; -export function PlanIconButton({ - current, - onClick, -}: { - current: boolean; - onClick: () => void; -}) { - return ( - - {current ? ( - - ) : ( - - )} - - ); -} - -function CurrentPlanTileButton() { - return ( - - ); -} - -function NormalPlanTileButton({ onClick }) { - return ( - - ); -} From 7e5577ecd7430ac738af0dfcfbe3d95739be7e81 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 20:52:45 +0530 Subject: [PATCH 06/14] Inline --- .../PlanSelector/manageSubscription/button.tsx | 11 ----------- .../gallery/PlanSelector/manageSubscription/index.tsx | 11 +++++++++-- 2 files changed, 9 insertions(+), 13 deletions(-) delete mode 100644 web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/button.tsx diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/button.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/button.tsx deleted file mode 100644 index f1aca561ff..0000000000 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/button.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { FluidContainer } from "@ente/shared/components/Container"; -import ChevronRight from "@mui/icons-material/ChevronRight"; -import { Button, ButtonProps } from "@mui/material"; - -const ManageSubscriptionButton = ({ children, ...props }: ButtonProps) => ( - -); - -export default ManageSubscriptionButton; diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx index dde76f89bd..ef3dcfd521 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx @@ -1,4 +1,6 @@ -import { Stack } from "@mui/material"; +import { FluidContainer } from "@ente/shared/components/Container"; +import ChevronRight from "@mui/icons-material/ChevronRight"; +import { Button, ButtonProps, Stack } from "@mui/material"; import { t } from "i18next"; import { AppContext } from "pages/_app"; import { useContext } from "react"; @@ -15,7 +17,6 @@ import { manageFamilyMethod, updatePaymentMethod, } from "utils/billing"; -import ManageSubscriptionButton from "./button"; interface Iprops { subscription: Subscription; @@ -135,3 +136,9 @@ function StripeSubscriptionOptions({ ); } + +const ManageSubscriptionButton = ({ children, ...props }: ButtonProps) => ( + +); From ad0b8ae0179cfbd847e54df0a06a09187e319a3b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 20:55:52 +0530 Subject: [PATCH 07/14] Inline --- .../pages/gallery/PlanSelector/card.tsx | 148 +++++++++++++++++- .../PlanSelector/manageSubscription/index.tsx | 144 ----------------- 2 files changed, 144 insertions(+), 148 deletions(-) diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx index c722534cfe..4fac1e131e 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx @@ -1,8 +1,19 @@ import log from "@/base/log"; import { bytesInGB, formattedStorageByteSize } from "@/new/photos/utils/units"; -import { SpaceBetweenFlex } from "@ente/shared/components/Container"; +import { + FluidContainer, + SpaceBetweenFlex, +} from "@ente/shared/components/Container"; +import ChevronRight from "@mui/icons-material/ChevronRight"; import Close from "@mui/icons-material/Close"; -import { IconButton, Link, Stack, styled } from "@mui/material"; +import { + Button, + ButtonProps, + IconButton, + Link, + Stack, + styled, +} from "@mui/material"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import { PLAN_PERIOD } from "constants/gallery"; @@ -12,9 +23,12 @@ import { GalleryContext } from "pages/gallery"; import { useContext, useEffect, useMemo, useState } from "react"; import { Trans } from "react-i18next"; import billingService, { type PlansResponse } from "services/billingService"; -import { Plan } from "types/billing"; +import { Plan, Subscription } from "types/billing"; import { SetLoading } from "types/gallery"; +import { BonusData } from "types/user"; import { + activateSubscription, + cancelSubscription, getLocalUserSubscription, hasAddOnBonus, hasMobileSubscription, @@ -24,12 +38,13 @@ import { isSubscriptionActive, isSubscriptionCancelled, isUserSubscribedPlan, + manageFamilyMethod, planForSubscription, + updatePaymentMethod, updateSubscription, } from "utils/billing"; import { getLocalUserDetails } from "utils/user"; import { getTotalFamilyUsage, isPartOfFamily } from "utils/user/family"; -import { ManageSubscription } from "./manageSubscription"; import { PeriodToggler } from "./periodToggler"; import Plans from "./plans"; @@ -390,3 +405,128 @@ const AddOnRowContainer = styled(SpaceBetweenFlex)(({ theme }) => ({ backgroundColor: "rgba(255,255,255,0.08)", }, })); + +interface ManageSubscriptionProps { + subscription: Subscription; + bonusData?: BonusData; + closeModal: () => void; + setLoading: SetLoading; +} + +function ManageSubscription({ + subscription, + bonusData, + closeModal, + setLoading, +}: ManageSubscriptionProps) { + const appContext = useContext(AppContext); + const openFamilyPortal = () => + manageFamilyMethod(appContext.setDialogMessage, setLoading); + + return ( + + {hasStripeSubscription(subscription) && ( + + )} + + {t("MANAGE_FAMILY_PORTAL")} + + + ); +} + +function StripeSubscriptionOptions({ + subscription, + bonusData, + setLoading, + closeModal, +}: ManageSubscriptionProps) { + const appContext = useContext(AppContext); + + const confirmReactivation = () => + appContext.setDialogMessage({ + title: t("REACTIVATE_SUBSCRIPTION"), + content: t("REACTIVATE_SUBSCRIPTION_MESSAGE", { + date: subscription.expiryTime, + }), + proceed: { + text: t("REACTIVATE_SUBSCRIPTION"), + action: activateSubscription.bind( + null, + appContext.setDialogMessage, + closeModal, + setLoading, + ), + variant: "accent", + }, + close: { + text: t("cancel"), + }, + }); + const confirmCancel = () => + appContext.setDialogMessage({ + title: t("CANCEL_SUBSCRIPTION"), + content: hasAddOnBonus(bonusData) ? ( + + ) : ( + + ), + proceed: { + text: t("CANCEL_SUBSCRIPTION"), + action: cancelSubscription.bind( + null, + appContext.setDialogMessage, + closeModal, + setLoading, + ), + variant: "critical", + }, + close: { + text: t("NEVERMIND"), + }, + }); + const openManagementPortal = updatePaymentMethod.bind( + null, + appContext.setDialogMessage, + setLoading, + ); + return ( + <> + {isSubscriptionCancelled(subscription) ? ( + + {t("REACTIVATE_SUBSCRIPTION")} + + ) : ( + + {t("CANCEL_SUBSCRIPTION")} + + )} + + {t("MANAGEMENT_PORTAL")} + + + ); +} + +const ManageSubscriptionButton = ({ children, ...props }: ButtonProps) => ( + +); diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx index ef3dcfd521..e69de29bb2 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx @@ -1,144 +0,0 @@ -import { FluidContainer } from "@ente/shared/components/Container"; -import ChevronRight from "@mui/icons-material/ChevronRight"; -import { Button, ButtonProps, Stack } from "@mui/material"; -import { t } from "i18next"; -import { AppContext } from "pages/_app"; -import { useContext } from "react"; -import { Trans } from "react-i18next"; -import { Subscription } from "types/billing"; -import { SetLoading } from "types/gallery"; -import { BonusData } from "types/user"; -import { - activateSubscription, - cancelSubscription, - hasAddOnBonus, - hasStripeSubscription, - isSubscriptionCancelled, - manageFamilyMethod, - updatePaymentMethod, -} from "utils/billing"; - -interface Iprops { - subscription: Subscription; - bonusData?: BonusData; - closeModal: () => void; - setLoading: SetLoading; -} - -export function ManageSubscription({ - subscription, - bonusData, - closeModal, - setLoading, -}: Iprops) { - const appContext = useContext(AppContext); - const openFamilyPortal = () => - manageFamilyMethod(appContext.setDialogMessage, setLoading); - - return ( - - {hasStripeSubscription(subscription) && ( - - )} - - {t("MANAGE_FAMILY_PORTAL")} - - - ); -} - -function StripeSubscriptionOptions({ - subscription, - bonusData, - setLoading, - closeModal, -}: Iprops) { - const appContext = useContext(AppContext); - - const confirmReactivation = () => - appContext.setDialogMessage({ - title: t("REACTIVATE_SUBSCRIPTION"), - content: t("REACTIVATE_SUBSCRIPTION_MESSAGE", { - date: subscription.expiryTime, - }), - proceed: { - text: t("REACTIVATE_SUBSCRIPTION"), - action: activateSubscription.bind( - null, - appContext.setDialogMessage, - closeModal, - setLoading, - ), - variant: "accent", - }, - close: { - text: t("cancel"), - }, - }); - const confirmCancel = () => - appContext.setDialogMessage({ - title: t("CANCEL_SUBSCRIPTION"), - content: hasAddOnBonus(bonusData) ? ( - - ) : ( - - ), - proceed: { - text: t("CANCEL_SUBSCRIPTION"), - action: cancelSubscription.bind( - null, - appContext.setDialogMessage, - closeModal, - setLoading, - ), - variant: "critical", - }, - close: { - text: t("NEVERMIND"), - }, - }); - const openManagementPortal = updatePaymentMethod.bind( - null, - appContext.setDialogMessage, - setLoading, - ); - return ( - <> - {isSubscriptionCancelled(subscription) ? ( - - {t("REACTIVATE_SUBSCRIPTION")} - - ) : ( - - {t("CANCEL_SUBSCRIPTION")} - - )} - - {t("MANAGEMENT_PORTAL")} - - - ); -} - -const ManageSubscriptionButton = ({ children, ...props }: ButtonProps) => ( - -); From cfe2e96227ada5e8af21f8dc3630a97fbd612c0e Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 20:59:26 +0530 Subject: [PATCH 08/14] Inline --- .../pages/gallery/PlanSelector/card.tsx | 45 ++++++++++++++++++- .../gallery/PlanSelector/periodToggler.tsx | 45 ------------------- 2 files changed, 44 insertions(+), 46 deletions(-) delete mode 100644 web/apps/photos/src/components/pages/gallery/PlanSelector/periodToggler.tsx diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx index 4fac1e131e..cd4016501b 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/card.tsx @@ -13,6 +13,8 @@ import { Link, Stack, styled, + ToggleButton, + ToggleButtonGroup, } from "@mui/material"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; @@ -45,7 +47,6 @@ import { } from "utils/billing"; import { getLocalUserDetails } from "utils/user"; import { getTotalFamilyUsage, isPartOfFamily } from "utils/user/family"; -import { PeriodToggler } from "./periodToggler"; import Plans from "./plans"; interface Props { @@ -368,6 +369,48 @@ function PaidSubscriptionPlanSelectorCard({ ); } +function PeriodToggler({ planPeriod, togglePeriod }) { + const handleChange = (_, newPlanPeriod: PLAN_PERIOD) => { + if (newPlanPeriod !== null && newPlanPeriod !== planPeriod) { + togglePeriod(); + } + }; + + return ( + + + {t("MONTHLY")} + + + {t("YEARLY")} + + + ); +} + +const CustomToggleButton = styled(ToggleButton)(({ theme }) => ({ + textTransform: "none", + padding: "12px 16px", + borderRadius: "4px", + backgroundColor: theme.colors.fill.faint, + border: `1px solid transparent`, + color: theme.colors.text.faint, + "&.Mui-selected": { + backgroundColor: theme.colors.accent.A500, + color: theme.colors.text.base, + }, + "&.Mui-selected:hover": { + backgroundColor: theme.colors.accent.A500, + color: theme.colors.text.base, + }, + width: "97.433px", +})); + function BFAddOnRow({ bonusData, closeModal }) { return ( <> diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/periodToggler.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/periodToggler.tsx deleted file mode 100644 index 1faf74b343..0000000000 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/periodToggler.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { styled, ToggleButton, ToggleButtonGroup } from "@mui/material"; -import { PLAN_PERIOD } from "constants/gallery"; -import { t } from "i18next"; - -const CustomToggleButton = styled(ToggleButton)(({ theme }) => ({ - textTransform: "none", - padding: "12px 16px", - borderRadius: "4px", - backgroundColor: theme.colors.fill.faint, - border: `1px solid transparent`, - color: theme.colors.text.faint, - "&.Mui-selected": { - backgroundColor: theme.colors.accent.A500, - color: theme.colors.text.base, - }, - "&.Mui-selected:hover": { - backgroundColor: theme.colors.accent.A500, - color: theme.colors.text.base, - }, - width: "97.433px", -})); - -export function PeriodToggler({ planPeriod, togglePeriod }) { - const handleChange = (_, newPlanPeriod: PLAN_PERIOD) => { - if (newPlanPeriod !== null && newPlanPeriod !== planPeriod) { - togglePeriod(); - } - }; - - return ( - - - {t("MONTHLY")} - - - {t("YEARLY")} - - - ); -} From d14b18867aa993605caeab048f66942b371c5538 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 21:00:16 +0530 Subject: [PATCH 09/14] Remove unused --- .../pages/gallery/PlanSelector/manageSubscription/index.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/manageSubscription/index.tsx deleted file mode 100644 index e69de29bb2..0000000000 From ab652ee3fee488b3e9ae9bf2373bbe4e8637481f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 21:05:04 +0530 Subject: [PATCH 10/14] Inline --- .../gallery/PlanSelector/plans/index.tsx | 123 +++++++++++++++++- .../gallery/PlanSelector/plans/planRow.tsx | 112 ---------------- 2 files changed, 119 insertions(+), 116 deletions(-) delete mode 100644 web/apps/photos/src/components/pages/gallery/PlanSelector/plans/planRow.tsx diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/index.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/index.tsx index 3489d3f03d..a3db22f1c0 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/index.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/plans/index.tsx @@ -1,7 +1,20 @@ -import { formattedStorageByteSize } from "@/new/photos/utils/units"; -import { SpaceBetweenFlex } from "@ente/shared/components/Container"; +import { bytesInGB, formattedStorageByteSize } from "@/new/photos/utils/units"; +import { + FlexWrapper, + FluidContainer, + SpaceBetweenFlex, +} from "@ente/shared/components/Container"; import ArrowForward from "@mui/icons-material/ArrowForward"; -import { Box, IconButton, Stack, Typography, styled } from "@mui/material"; +import Done from "@mui/icons-material/Done"; +import { + Box, + Button, + ButtonProps, + IconButton, + Stack, + Typography, + styled, +} from "@mui/material"; import { PLAN_PERIOD } from "constants/gallery"; import { t } from "i18next"; import type { PlansResponse } from "services/billingService"; @@ -13,7 +26,6 @@ import { isPopularPlan, isUserSubscribedPlan, } from "utils/billing"; -import { PlanRow } from "./planRow"; interface Iprops { plansResponse: PlansResponse | undefined; @@ -61,6 +73,109 @@ const Plans = ({ export default Plans; +interface PlanRowProps { + plan: Plan; + subscription: Subscription; + onPlanSelect: (plan: Plan) => void; + disabled: boolean; + popular: boolean; +} + +function PlanRow({ + plan, + subscription, + onPlanSelect, + disabled, + popular, +}: PlanRowProps) { + const handleClick = () => { + !isUserSubscribedPlan(plan, subscription) && onPlanSelect(plan); + }; + + const PlanButton = disabled ? DisabledPlanButton : ActivePlanButton; + + return ( + + + + {bytesInGB(plan.storage)} + + + + {t("storage_unit.gb")} + + {popular && !hasPaidSubscription(subscription) && ( + {t("POPULAR")} + )} + + + + + + + {plan.price}{" "} + {" "} + + {`/ ${ + plan.period === PLAN_PERIOD.MONTH + ? t("MONTH_SHORT") + : t("YEAR") + }`} + + + + + + ); +} + +const PlanRowContainer = styled(FlexWrapper)(() => ({ + background: + "linear-gradient(268.22deg, rgba(256, 256, 256, 0.08) -3.72%, rgba(256, 256, 256, 0) 85.73%)", +})); + +const TopAlignedFluidContainer = styled(FluidContainer)` + align-items: flex-start; +`; + +const DisabledPlanButton = styled((props: ButtonProps) => ( + -); diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/index.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/index.tsx index d58c455158..ed3d34d3a0 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/index.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/index.tsx @@ -1,6 +1,56 @@ -import { Dialog, useMediaQuery, useTheme } from "@mui/material"; +import log from "@/base/log"; +import { bytesInGB, formattedStorageByteSize } from "@/new/photos/utils/units"; +import { + FluidContainer, + SpaceBetweenFlex, +} from "@ente/shared/components/Container"; +import ChevronRight from "@mui/icons-material/ChevronRight"; +import Close from "@mui/icons-material/Close"; +import { + Button, + ButtonProps, + Dialog, + IconButton, + Link, + Stack, + styled, + ToggleButton, + ToggleButtonGroup, + useMediaQuery, + useTheme, +} from "@mui/material"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import { PLAN_PERIOD } from "constants/gallery"; +import { t } from "i18next"; +import { AppContext } from "pages/_app"; +import { GalleryContext } from "pages/gallery"; +import { useContext, useEffect, useMemo, useState } from "react"; +import { Trans } from "react-i18next"; +import billingService, { type PlansResponse } from "services/billingService"; +import { Plan, Subscription } from "types/billing"; import { SetLoading } from "types/gallery"; -import PlanSelectorCard from "./card"; +import { BonusData } from "types/user"; +import { + activateSubscription, + cancelSubscription, + getLocalUserSubscription, + hasAddOnBonus, + hasMobileSubscription, + hasPaidSubscription, + hasStripeSubscription, + isOnFreePlan, + isSubscriptionActive, + isSubscriptionCancelled, + isUserSubscribedPlan, + manageFamilyMethod, + planForSubscription, + updatePaymentMethod, + updateSubscription, +} from "utils/billing"; +import { getLocalUserDetails } from "utils/user"; +import { getTotalFamilyUsage, isPartOfFamily } from "utils/user/family"; +import Plans from "./plans"; interface PlanSelectorProps { modalView: boolean; @@ -37,3 +87,526 @@ function PlanSelector(props: PlanSelectorProps) { } export default PlanSelector; + +interface PlanSelectorCardProps { + closeModal: any; + setLoading: SetLoading; +} + +function PlanSelectorCard(props: PlanSelectorCardProps) { + const subscription = useMemo(() => getLocalUserSubscription(), []); + const [plansResponse, setPlansResponse] = useState< + PlansResponse | undefined + >(); + + const [planPeriod, setPlanPeriod] = useState( + subscription?.period || PLAN_PERIOD.MONTH, + ); + const galleryContext = useContext(GalleryContext); + const appContext = useContext(AppContext); + const bonusData = useMemo(() => { + const userDetails = getLocalUserDetails(); + if (!userDetails) { + return null; + } + return userDetails.bonusData; + }, []); + + const usage = useMemo(() => { + const userDetails = getLocalUserDetails(); + if (!userDetails) { + return 0; + } + return isPartOfFamily(userDetails.familyData) + ? getTotalFamilyUsage(userDetails.familyData) + : userDetails.usage; + }, []); + + const togglePeriod = () => { + setPlanPeriod((prevPeriod) => + prevPeriod === PLAN_PERIOD.MONTH + ? PLAN_PERIOD.YEAR + : PLAN_PERIOD.MONTH, + ); + }; + function onReopenClick() { + appContext.closeMessageDialog(); + galleryContext.showPlanSelectorModal(); + } + useEffect(() => { + const main = async () => { + try { + props.setLoading(true); + const response = await billingService.getPlans(); + const { plans } = response; + if (isSubscriptionActive(subscription)) { + const planNotListed = + plans.filter((plan) => + isUserSubscribedPlan(plan, subscription), + ).length === 0; + if ( + subscription && + !isOnFreePlan(subscription) && + planNotListed + ) { + plans.push(planForSubscription(subscription)); + } + } + setPlansResponse(response); + } catch (e) { + log.error("plan selector modal open failed", e); + props.closeModal(); + appContext.setDialogMessage({ + title: t("OPEN_PLAN_SELECTOR_MODAL_FAILED"), + content: t("UNKNOWN_ERROR"), + close: { text: t("CLOSE"), variant: "secondary" }, + proceed: { + text: t("REOPEN_PLAN_SELECTOR_MODAL"), + variant: "accent", + action: onReopenClick, + }, + }); + } finally { + props.setLoading(false); + } + }; + main(); + }, []); + + async function onPlanSelect(plan: Plan) { + if ( + !hasPaidSubscription(subscription) && + !isSubscriptionCancelled(subscription) + ) { + try { + props.setLoading(true); + await billingService.buySubscription(plan.stripeID); + } catch (e) { + props.setLoading(false); + appContext.setDialogMessage({ + title: t("ERROR"), + content: t("SUBSCRIPTION_PURCHASE_FAILED"), + close: { variant: "critical" }, + }); + } + } else if (hasStripeSubscription(subscription)) { + appContext.setDialogMessage({ + title: t("update_subscription_title"), + content: t("UPDATE_SUBSCRIPTION_MESSAGE"), + proceed: { + text: t("UPDATE_SUBSCRIPTION"), + action: updateSubscription.bind( + null, + plan, + appContext.setDialogMessage, + props.setLoading, + props.closeModal, + ), + variant: "accent", + }, + close: { text: t("cancel") }, + }); + } else if (hasMobileSubscription(subscription)) { + appContext.setDialogMessage({ + title: t("CANCEL_SUBSCRIPTION_ON_MOBILE"), + content: t("CANCEL_SUBSCRIPTION_ON_MOBILE_MESSAGE"), + close: { variant: "secondary" }, + }); + } else { + appContext.setDialogMessage({ + title: t("MANAGE_PLAN"), + content: ( + , + }} + values={{ emailID: "support@ente.io" }} + /> + ), + close: { variant: "secondary" }, + }); + } + } + + const { closeModal, setLoading } = props; + + const commonCardData = { + subscription, + bonusData, + closeModal, + planPeriod, + togglePeriod, + setLoading, + }; + + const plansList = ( + + ); + + return ( + <> + + {hasPaidSubscription(subscription) ? ( + + {plansList} + + ) : ( + + {plansList} + + )} + + + ); +} + +function FreeSubscriptionPlanSelectorCard({ + children, + subscription, + bonusData, + closeModal, + setLoading, + planPeriod, + togglePeriod, +}) { + return ( + <> + + {t("CHOOSE_PLAN")} + + + + + + + + {t("TWO_MONTHS_FREE")} + + + {children} + {hasAddOnBonus(bonusData) && ( + + )} + {hasAddOnBonus(bonusData) && ( + + )} + + + + ); +} + +function PaidSubscriptionPlanSelectorCard({ + children, + subscription, + bonusData, + closeModal, + usage, + planPeriod, + togglePeriod, + setLoading, +}) { + return ( + <> + + + + + {t("SUBSCRIPTION")} + + + {bytesInGB(subscription.storage, 2)}{" "} + {t("storage_unit.gb")} + + + + + + + + + + + + + + + + `1px solid ${theme.palette.divider}`} + p={1.5} + borderRadius={(theme) => `${theme.shape.borderRadius}px`} + > + + + + {t("TWO_MONTHS_FREE")} + + + {children} + + + + + {!isSubscriptionCancelled(subscription) + ? t("subscription_status_renewal_active", { + date: subscription.expiryTime, + }) + : t("subscription_status_renewal_cancelled", { + date: subscription.expiryTime, + })} + + {hasAddOnBonus(bonusData) && ( + + )} + + + + + + ); +} + +function PeriodToggler({ planPeriod, togglePeriod }) { + const handleChange = (_, newPlanPeriod: PLAN_PERIOD) => { + if (newPlanPeriod !== null && newPlanPeriod !== planPeriod) { + togglePeriod(); + } + }; + + return ( + + + {t("MONTHLY")} + + + {t("YEARLY")} + + + ); +} + +const CustomToggleButton = styled(ToggleButton)(({ theme }) => ({ + textTransform: "none", + padding: "12px 16px", + borderRadius: "4px", + backgroundColor: theme.colors.fill.faint, + border: `1px solid transparent`, + color: theme.colors.text.faint, + "&.Mui-selected": { + backgroundColor: theme.colors.accent.A500, + color: theme.colors.text.base, + }, + "&.Mui-selected:hover": { + backgroundColor: theme.colors.accent.A500, + color: theme.colors.text.base, + }, + width: "97.433px", +})); + +function BFAddOnRow({ bonusData, closeModal }) { + return ( + <> + {bonusData.storageBonuses.map((bonus) => { + if (bonus.type.startsWith("ADD_ON")) { + return ( + + + + + + + + ); + } + return null; + })} + + ); +} + +const AddOnRowContainer = styled(SpaceBetweenFlex)(({ theme }) => ({ + // gap: theme.spacing(1.5), + padding: theme.spacing(1, 0), + cursor: "pointer", + "&:hover .endIcon": { + backgroundColor: "rgba(255,255,255,0.08)", + }, +})); + +interface ManageSubscriptionProps { + subscription: Subscription; + bonusData?: BonusData; + closeModal: () => void; + setLoading: SetLoading; +} + +function ManageSubscription({ + subscription, + bonusData, + closeModal, + setLoading, +}: ManageSubscriptionProps) { + const appContext = useContext(AppContext); + const openFamilyPortal = () => + manageFamilyMethod(appContext.setDialogMessage, setLoading); + + return ( + + {hasStripeSubscription(subscription) && ( + + )} + + {t("MANAGE_FAMILY_PORTAL")} + + + ); +} + +function StripeSubscriptionOptions({ + subscription, + bonusData, + setLoading, + closeModal, +}: ManageSubscriptionProps) { + const appContext = useContext(AppContext); + + const confirmReactivation = () => + appContext.setDialogMessage({ + title: t("REACTIVATE_SUBSCRIPTION"), + content: t("REACTIVATE_SUBSCRIPTION_MESSAGE", { + date: subscription.expiryTime, + }), + proceed: { + text: t("REACTIVATE_SUBSCRIPTION"), + action: activateSubscription.bind( + null, + appContext.setDialogMessage, + closeModal, + setLoading, + ), + variant: "accent", + }, + close: { + text: t("cancel"), + }, + }); + const confirmCancel = () => + appContext.setDialogMessage({ + title: t("CANCEL_SUBSCRIPTION"), + content: hasAddOnBonus(bonusData) ? ( + + ) : ( + + ), + proceed: { + text: t("CANCEL_SUBSCRIPTION"), + action: cancelSubscription.bind( + null, + appContext.setDialogMessage, + closeModal, + setLoading, + ), + variant: "critical", + }, + close: { + text: t("NEVERMIND"), + }, + }); + const openManagementPortal = updatePaymentMethod.bind( + null, + appContext.setDialogMessage, + setLoading, + ); + return ( + <> + {isSubscriptionCancelled(subscription) ? ( + + {t("REACTIVATE_SUBSCRIPTION")} + + ) : ( + + {t("CANCEL_SUBSCRIPTION")} + + )} + + {t("MANAGEMENT_PORTAL")} + + + ); +} + +const ManageSubscriptionButton = ({ children, ...props }: ButtonProps) => ( + +); From 7c71277759be6667efd95eee76f40045a06f06b4 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 1 Aug 2024 21:10:54 +0530 Subject: [PATCH 13/14] Inline --- .../pages/gallery/PlanSelector/index.tsx | 184 ++++++++++++++- .../gallery/PlanSelector/plans/index.tsx | 209 ------------------ 2 files changed, 183 insertions(+), 210 deletions(-) delete mode 100644 web/apps/photos/src/components/pages/gallery/PlanSelector/plans/index.tsx diff --git a/web/apps/photos/src/components/pages/gallery/PlanSelector/index.tsx b/web/apps/photos/src/components/pages/gallery/PlanSelector/index.tsx index ed3d34d3a0..3879d3667a 100644 --- a/web/apps/photos/src/components/pages/gallery/PlanSelector/index.tsx +++ b/web/apps/photos/src/components/pages/gallery/PlanSelector/index.tsx @@ -1,11 +1,14 @@ import log from "@/base/log"; import { bytesInGB, formattedStorageByteSize } from "@/new/photos/utils/units"; import { + FlexWrapper, FluidContainer, SpaceBetweenFlex, } from "@ente/shared/components/Container"; +import ArrowForward from "@mui/icons-material/ArrowForward"; import ChevronRight from "@mui/icons-material/ChevronRight"; import Close from "@mui/icons-material/Close"; +import Done from "@mui/icons-material/Done"; import { Button, ButtonProps, @@ -40,6 +43,7 @@ import { hasPaidSubscription, hasStripeSubscription, isOnFreePlan, + isPopularPlan, isSubscriptionActive, isSubscriptionCancelled, isUserSubscribedPlan, @@ -50,7 +54,6 @@ import { } from "utils/billing"; import { getLocalUserDetails } from "utils/user"; import { getTotalFamilyUsage, isPartOfFamily } from "utils/user/family"; -import Plans from "./plans"; interface PlanSelectorProps { modalView: boolean; @@ -448,6 +451,185 @@ const CustomToggleButton = styled(ToggleButton)(({ theme }) => ({ width: "97.433px", })); +interface PlansProps { + plansResponse: PlansResponse | undefined; + planPeriod: PLAN_PERIOD; + subscription: Subscription; + bonusData?: BonusData; + onPlanSelect: (plan: Plan) => void; + closeModal: () => void; +} + +const Plans = ({ + plansResponse, + planPeriod, + subscription, + bonusData, + onPlanSelect, + closeModal, +}: PlansProps) => { + const { freePlan, plans } = plansResponse ?? {}; + return ( + + {plans + ?.filter((plan) => plan.period === planPeriod) + ?.map((plan) => ( + + ))} + {!hasPaidSubscription(subscription) && + !hasAddOnBonus(bonusData) && + freePlan && ( + + )} + + ); +}; + +interface PlanRowProps { + plan: Plan; + subscription: Subscription; + onPlanSelect: (plan: Plan) => void; + disabled: boolean; + popular: boolean; +} + +function PlanRow({ + plan, + subscription, + onPlanSelect, + disabled, + popular, +}: PlanRowProps) { + const handleClick = () => { + !isUserSubscribedPlan(plan, subscription) && onPlanSelect(plan); + }; + + const PlanButton = disabled ? DisabledPlanButton : ActivePlanButton; + + return ( + + + + {bytesInGB(plan.storage)} + + + + {t("storage_unit.gb")} + + {popular && !hasPaidSubscription(subscription) && ( + {t("POPULAR")} + )} + + + + + + + {plan.price}{" "} + {" "} + + {`/ ${ + plan.period === PLAN_PERIOD.MONTH + ? t("MONTH_SHORT") + : t("YEAR") + }`} + + + + + + ); +} + +const PlanRowContainer = styled(FlexWrapper)(() => ({ + background: + "linear-gradient(268.22deg, rgba(256, 256, 256, 0.08) -3.72%, rgba(256, 256, 256, 0) 85.73%)", +})); + +const TopAlignedFluidContainer = styled(FluidContainer)` + align-items: flex-start; +`; + +const DisabledPlanButton = styled((props: ButtonProps) => ( +