vis
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user