flexier parsing
This commit is contained in:
@@ -111,7 +111,7 @@ const IndividualSubscriptionCardContent: React.FC<
|
||||
IndividualSubscriptionCardContentProps
|
||||
> = ({ userDetails }) => {
|
||||
const totalStorage =
|
||||
userDetails.subscription.storage + (userDetails.storageBonus ?? 0);
|
||||
userDetails.subscription.storage + userDetails.storageBonus;
|
||||
return (
|
||||
<>
|
||||
<StorageSection storage={totalStorage} usage={userDetails.usage} />
|
||||
@@ -230,7 +230,7 @@ const FamilySubscriptionCardContent: React.FC<
|
||||
}
|
||||
}, [userDetails]);
|
||||
const totalStorage =
|
||||
userDetails.familyData.storage + (userDetails.storageBonus ?? 0);
|
||||
userDetails.familyData.storage + userDetails.storageBonus;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -105,8 +105,8 @@ const PlanSelectorCard: React.FC<PlanSelectorCardProps> = ({
|
||||
const userDetails = useUserDetailsSnapshot();
|
||||
|
||||
const [plansData, setPlansData] = useState<PlansData | undefined>();
|
||||
const [planPeriod, setPlanPeriod] = useState<PlanPeriod>(
|
||||
userDetails?.subscription?.period || "month",
|
||||
const [planPeriod, setPlanPeriod] = useState<PlanPeriod | undefined>(
|
||||
userDetails?.subscription?.period,
|
||||
);
|
||||
|
||||
const usage = userDetails ? planUsage(userDetails) : 0;
|
||||
@@ -443,9 +443,7 @@ function PaidSubscriptionPlanSelectorCard({
|
||||
|
||||
function PeriodToggler({ planPeriod, togglePeriod }) {
|
||||
const handleChange = (_, newPlanPeriod: PlanPeriod) => {
|
||||
if (newPlanPeriod !== null && newPlanPeriod !== planPeriod) {
|
||||
togglePeriod();
|
||||
}
|
||||
if (newPlanPeriod !== planPeriod) togglePeriod();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,28 +10,40 @@ import { z } from "zod";
|
||||
import type { UserDetails } from "./user";
|
||||
import { syncUserDetails, userDetailsSnapshot } from "./user";
|
||||
|
||||
const PlanPeriod = z.enum(["month", "year"]);
|
||||
|
||||
/**
|
||||
* Validity of the plan.
|
||||
*/
|
||||
export type PlanPeriod = z.infer<typeof PlanPeriod>;
|
||||
export type PlanPeriod = "month" | "year";
|
||||
|
||||
export const Subscription = z.object({
|
||||
/**
|
||||
* Store-specific ID of the product ("plan") that the user has subscribed
|
||||
* to. e.g. if the user has subscribed to a plan using Stripe, then this
|
||||
* will be the stripeID of the corresponding {@link Plan}.
|
||||
*
|
||||
* For free plans, the productID will be the constant "free".
|
||||
*/
|
||||
productID: z.string(),
|
||||
/**
|
||||
* Storage (in bytes) that the user can use.
|
||||
*/
|
||||
storage: z.number(),
|
||||
/**
|
||||
* Epoch microseconds indicating the time until which the user's
|
||||
* subscription is valid.
|
||||
*/
|
||||
expiryTime: z.number(),
|
||||
paymentProvider: z.string(),
|
||||
price: z.string(),
|
||||
period: z
|
||||
.string()
|
||||
.transform((p) => (p == "month" || p == "year" ? p : undefined)),
|
||||
attributes: z
|
||||
.object({
|
||||
isCancelled: z.boolean().nullish().transform(nullToUndefined),
|
||||
})
|
||||
.nullish()
|
||||
.transform(nullToUndefined),
|
||||
price: z.string(),
|
||||
// TODO: We get back subscriptions without a period on cancel / reactivate.
|
||||
// Handle them better, or remove this TODO.
|
||||
period: z.enum(["month", "year", ""]).transform((s) => (s ? s : "month")),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -121,7 +133,9 @@ const Plan = z.object({
|
||||
stripeID: z.string().nullish().transform(nullToUndefined),
|
||||
storage: z.number(),
|
||||
price: z.string(),
|
||||
period: PlanPeriod,
|
||||
period: z
|
||||
.string()
|
||||
.transform((p) => (p == "month" || p == "year" ? p : undefined)),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -131,7 +145,7 @@ export type Plan = z.infer<typeof Plan>;
|
||||
|
||||
const PlansData = z.object({
|
||||
freePlan: z.object({
|
||||
/* Number of bytes available in the free plan */
|
||||
/* Number of bytes available in the free plan. */
|
||||
storage: z.number(),
|
||||
}),
|
||||
plans: z.array(Plan),
|
||||
@@ -399,9 +413,8 @@ export const hasExceededStorageQuota = (userDetails: UserDetails) => {
|
||||
storage = userDetails.familyData?.storage ?? 0;
|
||||
} else {
|
||||
usage = userDetails.usage;
|
||||
storage = userDetails.subscription?.storage ?? 0;
|
||||
storage = userDetails.subscription.storage;
|
||||
}
|
||||
|
||||
const bonusStorage = userDetails.storageBonus ?? 0;
|
||||
return usage > storage + bonusStorage;
|
||||
return usage > storage + userDetails.storageBonus;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { authenticatedRequestHeaders, ensureOk } from "@/base/http";
|
||||
import { getKV, setKV } from "@/base/kv";
|
||||
import { apiURL } from "@/base/origins";
|
||||
import { nullishToZero, nullToUndefined } from "@/utils/transform";
|
||||
import { getData, LS_KEYS, setLSUser } from "@ente/shared/storage/localStorage";
|
||||
import { z } from "zod";
|
||||
import { FamilyData, Subscription, BonusData } from "./plan";
|
||||
import { BonusData, FamilyData, Subscription } from "./plan";
|
||||
|
||||
/**
|
||||
* Zod schema for {@link UserDetails}
|
||||
@@ -11,11 +12,11 @@ import { FamilyData, Subscription, BonusData } from "./plan";
|
||||
const UserDetails = z.object({
|
||||
email: z.string(),
|
||||
usage: z.number(),
|
||||
fileCount: z.number().optional(),
|
||||
fileCount: z.number().nullish().transform(nullishToZero),
|
||||
subscription: Subscription,
|
||||
familyData: FamilyData.optional(),
|
||||
storageBonus: z.number().optional(),
|
||||
bonusData: BonusData.optional(),
|
||||
familyData: FamilyData.nullish().transform(nullToUndefined),
|
||||
storageBonus: z.number().nullish().transform(nullishToZero),
|
||||
bonusData: BonusData.nullish().transform(nullToUndefined),
|
||||
});
|
||||
|
||||
export type UserDetails = z.infer<typeof UserDetails>;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/** Convert `null` to `undefined`, passthrough everything else unchanged. */
|
||||
/**
|
||||
* Convert `null` to `undefined`, passthrough everything else unchanged.
|
||||
*/
|
||||
export const nullToUndefined = <T>(v: T | null | undefined): T | undefined =>
|
||||
v === null ? undefined : v;
|
||||
|
||||
/**
|
||||
* Convert `null` and `undefined` to `0`, passthrough everything else unchanged.
|
||||
*/
|
||||
export const nullishToZero = (v: number | null | undefined) => v ?? 0;
|
||||
|
||||
Reference in New Issue
Block a user