Move
This commit is contained in:
@@ -9,7 +9,7 @@ import DialogTitleWithCloseButton from "@ente/shared/components/DialogBox/TitleW
|
||||
import { Box, Button, Dialog, DialogContent, Typography } from "@mui/material";
|
||||
import { t } from "i18next";
|
||||
import { useContext } from "react";
|
||||
import billingService from "services/billingService";
|
||||
import billingService from "services/plan";
|
||||
|
||||
export function MemberSubscriptionManage({ open, userDetails, onClose }) {
|
||||
const { setDialogMessage } = useContext(AppContext);
|
||||
|
||||
@@ -63,11 +63,8 @@ import React, {
|
||||
useState,
|
||||
} from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import { redirectToCustomerPortal } from "services/billingService";
|
||||
import { getUncategorizedCollection } from "services/collectionService";
|
||||
import exportService from "services/export";
|
||||
import { getUserDetailsV2 } from "services/userService";
|
||||
import { UserDetails } from "types/user";
|
||||
import {
|
||||
hasAddOnBonus,
|
||||
hasExceededStorageQuota,
|
||||
@@ -77,7 +74,10 @@ import {
|
||||
isSubscriptionCancelled,
|
||||
isSubscriptionPastDue,
|
||||
isSubscriptionStripe,
|
||||
} from "utils/billing";
|
||||
redirectToCustomerPortal,
|
||||
} from "services/plan";
|
||||
import { getUserDetailsV2 } from "services/userService";
|
||||
import { UserDetails } from "types/user";
|
||||
import { testUpload } from "../../../tests/upload.test";
|
||||
import { MemberSubscriptionManage } from "../MemberSubscriptionManage";
|
||||
import { Preferences } from "./Preferences";
|
||||
|
||||
@@ -23,8 +23,8 @@ import { t } from "i18next";
|
||||
import isElectron from "is-electron";
|
||||
import { GalleryContext } from "pages/gallery";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { redirectToCustomerPortal } from "services/billingService";
|
||||
import { getLatestCollections } from "services/collectionService";
|
||||
import { redirectToCustomerPortal } from "services/plan";
|
||||
import {
|
||||
getPublicCollectionUID,
|
||||
getPublicCollectionUploaderName,
|
||||
|
||||
@@ -35,28 +35,21 @@ import Typography from "@mui/material/Typography";
|
||||
import { t } from "i18next";
|
||||
import { useContext, useEffect, useMemo, useState } from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import type {
|
||||
Plan,
|
||||
PlanPeriod,
|
||||
PlansData,
|
||||
Subscription,
|
||||
} from "services/billingService";
|
||||
import type { Plan, PlanPeriod, PlansData, Subscription } from "services/plan";
|
||||
import billingService, {
|
||||
getPlansData,
|
||||
redirectToCustomerPortal,
|
||||
redirectToPaymentsApp,
|
||||
} from "services/billingService";
|
||||
import { getFamilyPortalRedirectURL } from "services/userService";
|
||||
import { SetLoading } from "types/gallery";
|
||||
import { BonusData, UserDetails } from "types/user";
|
||||
import {
|
||||
hasAddOnBonus,
|
||||
isSubscriptionActive,
|
||||
isSubscriptionActiveFree,
|
||||
isSubscriptionActivePaid,
|
||||
isSubscriptionCancelled,
|
||||
isSubscriptionStripe,
|
||||
} from "utils/billing";
|
||||
redirectToCustomerPortal,
|
||||
redirectToPaymentsApp,
|
||||
} from "services/plan";
|
||||
import { getFamilyPortalRedirectURL } from "services/userService";
|
||||
import { SetLoading } from "types/gallery";
|
||||
import { BonusData, UserDetails } from "types/user";
|
||||
|
||||
interface PlanSelectorProps {
|
||||
modalView: boolean;
|
||||
|
||||
@@ -106,9 +106,6 @@ import { t } from "i18next";
|
||||
import { useRouter, type NextRouter } from "next/router";
|
||||
import { createContext, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import billingService, {
|
||||
redirectToCustomerPortal,
|
||||
} from "services/billingService";
|
||||
import {
|
||||
constructEmailList,
|
||||
constructUserIDToEmailMap,
|
||||
@@ -118,6 +115,7 @@ import {
|
||||
getAllLocalCollections,
|
||||
} from "services/collectionService";
|
||||
import { syncFiles } from "services/fileService";
|
||||
import billingService, { redirectToCustomerPortal } from "services/plan";
|
||||
import { preFileInfoSync, sync } from "services/sync";
|
||||
import { syncTrash } from "services/trashService";
|
||||
import uploadManager from "services/upload/uploadManager";
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { authenticatedRequestHeaders, ensureOk } from "@/base/http";
|
||||
import log from "@/base/log";
|
||||
import { apiURL, paymentsAppOrigin } from "@/base/origins";
|
||||
import {
|
||||
getTotalFamilyUsage,
|
||||
isPartOfFamily,
|
||||
} from "@/new/photos/services/user";
|
||||
import { nullToUndefined } from "@/utils/transform";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import {
|
||||
@@ -10,6 +14,7 @@ import {
|
||||
} from "@ente/shared/storage/localStorage";
|
||||
import { getToken } from "@ente/shared/storage/localStorage/helpers";
|
||||
import isElectron from "is-electron";
|
||||
import { BonusData, UserDetails } from "types/user";
|
||||
import { z } from "zod";
|
||||
|
||||
const PlanPeriod = z.enum(["month", "year"]);
|
||||
@@ -223,3 +228,71 @@ export const redirectToCustomerPortal = async () => {
|
||||
.parse(await res.json()).data;
|
||||
window.location.href = data.url;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the given {@link Subscription} has not expired.
|
||||
*/
|
||||
export const isSubscriptionActive = (subscription: Subscription) =>
|
||||
subscription && subscription.expiryTime > Date.now() * 1000;
|
||||
|
||||
/**
|
||||
* Return true if the given active {@link Subscription} is for a paid plan.
|
||||
*/
|
||||
export const isSubscriptionActivePaid = (subscription: Subscription) =>
|
||||
subscription &&
|
||||
isSubscriptionActive(subscription) &&
|
||||
subscription.productID != "free";
|
||||
|
||||
/**
|
||||
* Return true if the given active {@link Subscription} is for a free plan.
|
||||
*/
|
||||
export const isSubscriptionActiveFree = (subscription: Subscription) =>
|
||||
subscription &&
|
||||
isSubscriptionActive(subscription) &&
|
||||
subscription.productID == "free";
|
||||
|
||||
/**
|
||||
* Return true if the given {@link Subscription} is using Stripe.
|
||||
*/
|
||||
export const isSubscriptionStripe = (subscription: Subscription) =>
|
||||
subscription && subscription.paymentProvider == "stripe";
|
||||
|
||||
/**
|
||||
* Return true if the given {@link Subscription} has the cancelled attribute.
|
||||
*/
|
||||
export const isSubscriptionCancelled = (subscription: Subscription) =>
|
||||
subscription && subscription.attributes.isCancelled;
|
||||
|
||||
export function isSubscriptionPastDue(subscription: Subscription) {
|
||||
const thirtyDaysMicroseconds = 30 * 24 * 60 * 60 * 1000 * 1000;
|
||||
const currentTime = Date.now() * 1000;
|
||||
return (
|
||||
!isSubscriptionCancelled(subscription) &&
|
||||
subscription.expiryTime < currentTime &&
|
||||
subscription.expiryTime >= currentTime - thirtyDaysMicroseconds
|
||||
);
|
||||
}
|
||||
|
||||
// Checks if the bonus data contain any bonus whose type starts with 'ADD_ON'
|
||||
export function hasAddOnBonus(bonusData?: BonusData) {
|
||||
return (
|
||||
bonusData &&
|
||||
bonusData.storageBonuses &&
|
||||
bonusData.storageBonuses.length > 0 &&
|
||||
bonusData.storageBonuses.some((bonus) =>
|
||||
bonus.type.startsWith("ADD_ON"),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function hasExceededStorageQuota(userDetails: UserDetails) {
|
||||
const bonusStorage = userDetails.storageBonus ?? 0;
|
||||
if (isPartOfFamily(userDetails.familyData)) {
|
||||
const usage = getTotalFamilyUsage(userDetails.familyData);
|
||||
return usage > userDetails.familyData.storage + bonusStorage;
|
||||
} else {
|
||||
return (
|
||||
userDetails.usage > userDetails.subscription.storage + bonusStorage
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FamilyData } from "@/new/photos/services/user";
|
||||
import { Subscription } from "services/billingService";
|
||||
import { Subscription } from "services/plan";
|
||||
|
||||
export interface Bonus {
|
||||
storage: number;
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import {
|
||||
getTotalFamilyUsage,
|
||||
isPartOfFamily,
|
||||
} from "@/new/photos/services/user";
|
||||
import { Subscription } from "services/billingService";
|
||||
import { BonusData, UserDetails } from "types/user";
|
||||
|
||||
/**
|
||||
* Return true if the given {@link Subscription} has not expired.
|
||||
*/
|
||||
export const isSubscriptionActive = (subscription: Subscription) =>
|
||||
subscription && subscription.expiryTime > Date.now() * 1000;
|
||||
|
||||
/**
|
||||
* Return true if the given active {@link Subscription} is for a paid plan.
|
||||
*/
|
||||
export const isSubscriptionActivePaid = (subscription: Subscription) =>
|
||||
subscription &&
|
||||
isSubscriptionActive(subscription) &&
|
||||
subscription.productID != "free";
|
||||
|
||||
/**
|
||||
* Return true if the given active {@link Subscription} is for a free plan.
|
||||
*/
|
||||
export const isSubscriptionActiveFree = (subscription: Subscription) =>
|
||||
subscription &&
|
||||
isSubscriptionActive(subscription) &&
|
||||
subscription.productID == "free";
|
||||
|
||||
/**
|
||||
* Return true if the given {@link Subscription} is using Stripe.
|
||||
*/
|
||||
export const isSubscriptionStripe = (subscription: Subscription) =>
|
||||
subscription && subscription.paymentProvider == "stripe";
|
||||
|
||||
/**
|
||||
* Return true if the given {@link Subscription} has the cancelled attribute.
|
||||
*/
|
||||
export const isSubscriptionCancelled = (subscription: Subscription) =>
|
||||
subscription && subscription.attributes.isCancelled;
|
||||
|
||||
export function isSubscriptionPastDue(subscription: Subscription) {
|
||||
const thirtyDaysMicroseconds = 30 * 24 * 60 * 60 * 1000 * 1000;
|
||||
const currentTime = Date.now() * 1000;
|
||||
return (
|
||||
!isSubscriptionCancelled(subscription) &&
|
||||
subscription.expiryTime < currentTime &&
|
||||
subscription.expiryTime >= currentTime - thirtyDaysMicroseconds
|
||||
);
|
||||
}
|
||||
|
||||
// Checks if the bonus data contain any bonus whose type starts with 'ADD_ON'
|
||||
export function hasAddOnBonus(bonusData?: BonusData) {
|
||||
return (
|
||||
bonusData &&
|
||||
bonusData.storageBonuses &&
|
||||
bonusData.storageBonuses.length > 0 &&
|
||||
bonusData.storageBonuses.some((bonus) =>
|
||||
bonus.type.startsWith("ADD_ON"),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function hasExceededStorageQuota(userDetails: UserDetails) {
|
||||
const bonusStorage = userDetails.storageBonus ?? 0;
|
||||
if (isPartOfFamily(userDetails.familyData)) {
|
||||
const usage = getTotalFamilyUsage(userDetails.familyData);
|
||||
return usage > userDetails.familyData.storage + bonusStorage;
|
||||
} else {
|
||||
return (
|
||||
userDetails.usage > userDetails.subscription.storage + bonusStorage
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { DialogBoxAttributes } from "@ente/shared/components/DialogBox/types";
|
||||
import InfoOutlined from "@mui/icons-material/InfoRounded";
|
||||
import { t } from "i18next";
|
||||
import { Trans } from "react-i18next";
|
||||
import { Subscription } from "services/billingService";
|
||||
import { Subscription } from "services/plan";
|
||||
|
||||
export const getTrashFilesMessage = (
|
||||
deleteFileHelper,
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
"**/*.js",
|
||||
"../../packages/shared/themes/mui-theme.d.ts",
|
||||
"../../packages/base/global-electron.d.ts"
|
||||
],
|
||||
, "../../packages/new/photos/services/plan.ts" ],
|
||||
"exclude": ["node_modules", "out", ".next", "thirdparty"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user