This commit is contained in:
Manav Rathi
2024-11-07 13:35:40 +05:30
parent 3c5e20dd50
commit 32f53eb9f3
5 changed files with 69 additions and 77 deletions

View File

@@ -238,20 +238,19 @@ const SubscriptionStatus: React.FC<SubscriptionStatusProps> = ({
const handleClick = useMemo(() => {
const eventHandler: MouseEventHandler<HTMLSpanElement> = (e) => {
e.stopPropagation();
if (userDetails) {
if (isSubscriptionActive(userDetails.subscription)) {
if (hasExceededStorageQuota(userDetails)) {
showPlanSelectorModal();
}
if (isSubscriptionActive(userDetails.subscription)) {
if (hasExceededStorageQuota(userDetails)) {
showPlanSelectorModal();
}
} else {
if (
isSubscriptionStripe(userDetails.subscription) &&
isSubscriptionPastDue(userDetails.subscription)
) {
redirectToCustomerPortal();
} else {
if (
isSubscriptionStripe(userDetails.subscription) &&
isSubscriptionPastDue(userDetails.subscription)
) {
redirectToCustomerPortal();
} else {
showPlanSelectorModal();
}
showPlanSelectorModal();
}
}
};

View File

@@ -1,4 +1,5 @@
import { genericRetriableErrorDialogAttributes } from "@/base/components/utils/dialog";
import type { ModalVisibilityProps } from "@/base/components/utils/modal";
import log from "@/base/log";
import { useUserDetailsSnapshot } from "@/new/photos/components/utils/use-snapshot";
import type {
@@ -13,9 +14,10 @@ import {
cancelStripeSubscription,
getPlansData,
isSubscriptionActive,
isSubscriptionActiveFree,
isSubscriptionActivePaid,
isSubscriptionCancelled,
isSubscriptionForPlan,
isSubscriptionFree,
isSubscriptionStripe,
planUsage,
redirectToCustomerPortal,
@@ -55,24 +57,24 @@ import { Trans } from "react-i18next";
import { getFamilyPortalRedirectURL } from "services/userService";
import { SetLoading } from "types/gallery";
interface PlanSelectorProps {
modalView: boolean;
closeModal: any;
type PlanSelectorProps = ModalVisibilityProps & {
setLoading: SetLoading;
}
};
function PlanSelector(props: PlanSelectorProps) {
const PlanSelector: React.FC<PlanSelectorProps> = ({
open,
onClose,
setLoading,
}: PlanSelectorProps) => {
const fullScreen = useMediaQuery(useTheme().breakpoints.down("sm"));
if (!props.modalView) {
if (!open) {
return <></>;
}
return (
<Dialog
{...{ fullScreen }}
open={props.modalView}
onClose={props.closeModal}
{...{ open, onClose, fullScreen }}
PaperProps={{
sx: (theme) => ({
width: { sm: "391px" },
@@ -81,23 +83,20 @@ function PlanSelector(props: PlanSelectorProps) {
}),
}}
>
<PlanSelectorCard
closeModal={props.closeModal}
setLoading={props.setLoading}
/>
<PlanSelectorCard {...{ onClose, setLoading }} />
</Dialog>
);
}
};
export default PlanSelector;
interface PlanSelectorCardProps {
closeModal: any;
onClose: () => void;
setLoading: SetLoading;
}
const PlanSelectorCard: React.FC<PlanSelectorCardProps> = ({
closeModal,
onClose,
setLoading,
}) => {
const { showMiniDialog, setDialogMessage } = useContext(AppContext);
@@ -126,16 +125,11 @@ const PlanSelectorCard: React.FC<PlanSelectorCardProps> = ({
setLoading(true);
const plansData = await getPlansData();
const { plans } = plansData;
if (isSubscriptionActive(subscription)) {
const planNotListed =
plans.filter((plan) =>
isUserSubscribedPlan(plan, subscription),
).length === 0;
if (
subscription &&
!isSubscriptionActiveFree(subscription) &&
planNotListed
) {
if (subscription && isSubscriptionActive(subscription)) {
const activePlan = plans.find((plan) =>
isSubscriptionForPlan(subscription, plan),
);
if (!isSubscriptionFree(subscription) && !activePlan) {
plans.push({
id: subscription.productID,
storage: subscription.storage,
@@ -150,7 +144,7 @@ const PlanSelectorCard: React.FC<PlanSelectorCardProps> = ({
setPlansData(plansData);
} catch (e) {
log.error("plan selector modal open failed", e);
closeModal();
onClose();
showMiniDialog(genericRetriableErrorDialogAttributes());
} finally {
setLoading(false);
@@ -197,7 +191,7 @@ const PlanSelectorCard: React.FC<PlanSelectorCardProps> = ({
});
} finally {
setLoading(false);
closeModal();
onClose();
}
},
},
@@ -234,7 +228,7 @@ const PlanSelectorCard: React.FC<PlanSelectorCardProps> = ({
const commonCardData = {
subscription,
addOnBonuses,
closeModal,
closeModal: onClose,
planPeriod,
togglePeriod,
setLoading,
@@ -247,7 +241,7 @@ const PlanSelectorCard: React.FC<PlanSelectorCardProps> = ({
onPlanSelect={onPlanSelect}
subscription={subscription}
hasAddOnBonus={addOnBonuses.length > 0}
closeModal={closeModal}
closeModal={onClose}
/>
);
@@ -525,15 +519,6 @@ const Plans = ({
);
};
function isUserSubscribedPlan(plan: Plan, subscription: Subscription) {
return (
isSubscriptionActive(subscription) &&
(plan.stripeID === subscription.productID ||
plan.iosID === subscription.productID ||
plan.androidID === subscription.productID)
);
}
const isPopularPlan = (plan: Plan) =>
plan.storage === 100 * 1024 * 1024 * 1024; /* 100 GB */

View File

@@ -50,7 +50,7 @@ import {
import type { SearchOption } from "@/new/photos/services/search/types";
import { initSettings } from "@/new/photos/services/settings";
import {
initUserDetails,
initUserDetailsOrTriggerSync,
userDetailsSnapshot,
} from "@/new/photos/services/user";
import { useAppContext } from "@/new/photos/types/context";
@@ -186,7 +186,6 @@ export default function Gallery() {
collectionID: 0,
context: { mode: "albums", collectionID: ALL_SECTION },
});
const [planModalView, setPlanModalView] = useState(false);
const [blockingLoad, setBlockingLoad] = useState(false);
const [collectionNamerAttributes, setCollectionNamerAttributes] =
useState<CollectionNamerAttributes>(null);
@@ -248,8 +247,6 @@ export default function Gallery() {
const [fixCreationTimeAttributes, setFixCreationTimeAttributes] =
useState<FixCreationTimeAttributes>(null);
const showPlanSelectorModal = () => setPlanModalView(true);
const [uploadTypeSelectorView, setUploadTypeSelectorView] = useState(false);
const [uploadTypeSelectorIntent, setUploadTypeSelectorIntent] =
useState<UploadTypeSelectorIntent>("upload");
@@ -296,6 +293,8 @@ export default function Gallery() {
const [collectionSelectorAttributes, setCollectionSelectorAttributes] =
useState<CollectionSelectorAttributes | undefined>();
const { show: showPlanSelector, props: planSelectorVisibilityProps } =
useModalVisibility();
const { show: showWhatsNew, props: whatsNewVisibilityProps } =
useModalVisibility();
@@ -353,13 +352,13 @@ export default function Gallery() {
return;
}
initSettings();
await initUserDetails();
await initUserDetailsOrTriggerSync();
await downloadManager.init(token);
setupSelectAllKeyBoardShortcutHandler();
dispatch({ type: "showAll" });
setIsFirstLoad(isFirstLogin());
if (justSignedUp()) {
setPlanModalView(true);
showPlanSelector();
}
setIsFirstLogin(false);
const user = getData(LS_KEYS.USER);
@@ -500,7 +499,7 @@ export default function Gallery() {
openCollectionSelector ||
collectionNamerView ||
fixCreationTimeView ||
planModalView ||
planSelectorVisibilityProps.open ||
exportModalView ||
authenticateUserModalView ||
isPhotoSwipeOpen ||
@@ -886,7 +885,7 @@ export default function Gallery() {
<GalleryContext.Provider
value={{
...defaultGalleryContext,
showPlanSelectorModal,
showPlanSelectorModal: showPlanSelector,
setActiveCollectionID: handleSetActiveCollectionID,
onShowCollection: (id) =>
dispatch({
@@ -929,8 +928,7 @@ export default function Gallery() {
</CenteredFlex>
)}
<PlanSelector
modalView={planModalView}
closeModal={() => setPlanModalView(false)}
{...planSelectorVisibilityProps}
setLoading={setBlockingLoad}
/>
<CollectionNamer

View File

@@ -285,29 +285,34 @@ export const redirectToCustomerPortal = async () => {
* Return true if the given {@link Subscription} has not expired.
*/
export const isSubscriptionActive = (subscription: Subscription) =>
subscription && subscription.expiryTime > Date.now() * 1000;
subscription.expiryTime > Date.now() * 1000;
/**
* Return true if the given active {@link Subscription} is for a paid plan.
* Return true if the given {@link Subscription} is active and for a paid plan.
*/
export const isSubscriptionActivePaid = (subscription: Subscription) =>
subscription &&
isSubscriptionActive(subscription) &&
subscription.productID != "free";
isSubscriptionActive(subscription) && subscription.productID != "free";
/**
* Return true if the given active {@link Subscription} is for a free plan.
* Return true if the given {@link Subscription} is for a free plan.
*/
export const isSubscriptionActiveFree = (subscription: Subscription) =>
subscription &&
isSubscriptionActive(subscription) &&
export const isSubscriptionFree = (subscription: Subscription) =>
subscription.productID == "free";
/**
* Return true if the given {@link Subscription} is active and for the given
* {@link Plan}.
*/
export const isSubscriptionForPlan = (subscription: Subscription, plan: Plan) =>
plan.stripeID === subscription.productID ||
plan.iosID === subscription.productID ||
plan.androidID === subscription.productID;
/**
* Return true if the given {@link Subscription} is using Stripe.
*/
export const isSubscriptionStripe = (subscription: Subscription) =>
subscription && subscription.paymentProvider == "stripe";
subscription.paymentProvider == "stripe";
/**
* Return true if the given {@link Subscription} has the cancelled attribute.

View File

@@ -48,14 +48,19 @@ export const logoutUserDetails = () => {
};
/**
* Read in the locally persisted settings into memory, but otherwise do not
* initate any network requests to fetch the latest values.
* Read in the locally persisted settings into memory, otherwise initate a
* network requests to fetch the latest values (but don't wait for it to
* complete).
*
* This assumes that the user is already logged in.
*/
export const initUserDetails = async () => {
export const initUserDetailsOrTriggerSync = async () => {
const saved = await getKV("userDetails");
if (saved) setUserDetailsSnapshot(UserDetails.parse(saved));
if (saved) {
setUserDetailsSnapshot(UserDetails.parse(saved));
} else {
void syncUserDetails();
}
};
/**