mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(console): use useSWRImmutable
to avoid redundant subscrtipion data fetching (#4272)
This commit is contained in:
parent
8cba35d116
commit
56b0a2cd18
2 changed files with 45 additions and 55 deletions
|
@ -1,7 +1,9 @@
|
||||||
import { useContext, useEffect, useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import ReactModal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
|
import useSWRImmutable from 'swr/immutable';
|
||||||
|
|
||||||
|
import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
|
||||||
import PlanUsage from '@/components/PlanUsage';
|
import PlanUsage from '@/components/PlanUsage';
|
||||||
import { contactEmailLink } from '@/consts';
|
import { contactEmailLink } from '@/consts';
|
||||||
import { subscriptionPage } from '@/consts/pages';
|
import { subscriptionPage } from '@/consts/pages';
|
||||||
|
@ -10,9 +12,7 @@ import Button from '@/ds-components/Button';
|
||||||
import FormField from '@/ds-components/FormField';
|
import FormField from '@/ds-components/FormField';
|
||||||
import InlineNotification from '@/ds-components/InlineNotification';
|
import InlineNotification from '@/ds-components/InlineNotification';
|
||||||
import ModalLayout from '@/ds-components/ModalLayout';
|
import ModalLayout from '@/ds-components/ModalLayout';
|
||||||
import useSubscription from '@/hooks/use-subscription';
|
import useSubscriptionPlans from '@/hooks/use-subscription-plans';
|
||||||
import useSubscriptionPlan from '@/hooks/use-subscription-plan';
|
|
||||||
import useSubscriptionUsage from '@/hooks/use-subscription-usage';
|
|
||||||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||||
import * as modalStyles from '@/scss/modal.module.scss';
|
import * as modalStyles from '@/scss/modal.module.scss';
|
||||||
|
|
||||||
|
@ -22,55 +22,52 @@ import * as styles from './index.module.scss';
|
||||||
|
|
||||||
function MauExceededModal() {
|
function MauExceededModal() {
|
||||||
const { currentTenantId } = useContext(TenantsContext);
|
const { currentTenantId } = useContext(TenantsContext);
|
||||||
|
const cloudApi = useCloudApi();
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
const { navigate } = useTenantPathname();
|
const { navigate } = useTenantPathname();
|
||||||
const [hasClosed, setHasClosed] = useState(false);
|
const [hasClosed, setHasClosed] = useState(false);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setHasClosed(true);
|
setHasClosed(true);
|
||||||
setIsOpen(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data: currentUsage, error: fetchUsageError } = useSubscriptionUsage(currentTenantId);
|
const { data: subscriptionPlans } = useSubscriptionPlans();
|
||||||
const { data: currentSubscription, error: fetchSubscriptionError } =
|
|
||||||
useSubscription(currentTenantId);
|
|
||||||
const { data: currentPlan, error: fetchCurrentPlanError } = useSubscriptionPlan(currentTenantId);
|
|
||||||
|
|
||||||
const isLoadingUsage = !currentUsage && !fetchUsageError;
|
const { data: currentSubscription } = useSWRImmutable(
|
||||||
const isLoadingSubscription = !currentSubscription && !fetchSubscriptionError;
|
`/api/tenants/${currentTenantId}/subscription`,
|
||||||
const isLoadingCurrentPlan = !currentPlan && !fetchCurrentPlanError;
|
async () =>
|
||||||
|
cloudApi.get('/api/tenants/:tenantId/subscription', { params: { tenantId: currentTenantId } })
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const { data: currentUsage } = useSWRImmutable(
|
||||||
if (!currentUsage || !currentPlan || hasClosed) {
|
`/api/tenants/${currentTenantId}/usage`,
|
||||||
return;
|
async () =>
|
||||||
}
|
cloudApi.get('/api/tenants/:tenantId/usage', { params: { tenantId: currentTenantId } })
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const currentPlan =
|
||||||
quota: { mauLimit },
|
currentSubscription &&
|
||||||
} = currentPlan;
|
subscriptionPlans?.find((plan) => plan.id === currentSubscription.planId);
|
||||||
|
|
||||||
if (mauLimit === null) {
|
if (!currentPlan || !currentUsage || hasClosed) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentUsage.activeUsers >= mauLimit) {
|
|
||||||
setIsOpen(true);
|
|
||||||
}
|
|
||||||
}, [currentPlan, currentUsage, hasClosed]);
|
|
||||||
|
|
||||||
if (isLoadingUsage || isLoadingSubscription || isLoadingCurrentPlan) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentUsage || !currentSubscription || !currentPlan) {
|
const {
|
||||||
|
quota: { mauLimit },
|
||||||
|
name: planName,
|
||||||
|
} = currentPlan;
|
||||||
|
|
||||||
|
const isMauExceeded = mauLimit !== null && currentUsage.activeUsers >= mauLimit;
|
||||||
|
|
||||||
|
if (!isMauExceeded) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactModal
|
<ReactModal
|
||||||
|
isOpen
|
||||||
shouldCloseOnEsc
|
shouldCloseOnEsc
|
||||||
isOpen={isOpen}
|
|
||||||
className={modalStyles.content}
|
className={modalStyles.content}
|
||||||
overlayClassName={modalStyles.overlay}
|
overlayClassName={modalStyles.overlay}
|
||||||
onRequestClose={handleCloseModal}
|
onRequestClose={handleCloseModal}
|
||||||
|
@ -97,7 +94,7 @@ function MauExceededModal() {
|
||||||
<InlineNotification severity="error">
|
<InlineNotification severity="error">
|
||||||
<Trans
|
<Trans
|
||||||
components={{
|
components={{
|
||||||
planName: <PlanName name={currentPlan.name} />,
|
planName: <PlanName name={planName} />,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('upsell.mau_exceeded_modal.notification')}
|
{t('upsell.mau_exceeded_modal.notification')}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { conditional } from '@silverhand/essentials';
|
import { conditional } from '@silverhand/essentials';
|
||||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
import { useContext, useMemo, useState } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import ReactModal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
|
import useSWRImmutable from 'swr/immutable';
|
||||||
|
|
||||||
|
import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
|
||||||
import { contactEmailLink } from '@/consts';
|
import { contactEmailLink } from '@/consts';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||||
import Button from '@/ds-components/Button';
|
import Button from '@/ds-components/Button';
|
||||||
import FormField from '@/ds-components/FormField';
|
import FormField from '@/ds-components/FormField';
|
||||||
import InlineNotification from '@/ds-components/InlineNotification';
|
import InlineNotification from '@/ds-components/InlineNotification';
|
||||||
import ModalLayout from '@/ds-components/ModalLayout';
|
import ModalLayout from '@/ds-components/ModalLayout';
|
||||||
import useInvoices from '@/hooks/use-invoices';
|
|
||||||
import useSubscribe from '@/hooks/use-subscribe';
|
import useSubscribe from '@/hooks/use-subscribe';
|
||||||
import * as modalStyles from '@/scss/modal.module.scss';
|
import * as modalStyles from '@/scss/modal.module.scss';
|
||||||
import { getLatestUnpaidInvoice } from '@/utils/subscription';
|
import { getLatestUnpaidInvoice } from '@/utils/subscription';
|
||||||
|
@ -21,41 +22,33 @@ import * as styles from './index.module.scss';
|
||||||
function PaymentOverdueModal() {
|
function PaymentOverdueModal() {
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||||
const { currentTenant, currentTenantId } = useContext(TenantsContext);
|
const { currentTenant, currentTenantId } = useContext(TenantsContext);
|
||||||
const { data: invoices, error } = useInvoices(currentTenantId);
|
const cloudApi = useCloudApi();
|
||||||
|
const { data: invoicesResponse } = useSWRImmutable(
|
||||||
|
`/api/tenants/${currentTenantId}/invoices`,
|
||||||
|
async () =>
|
||||||
|
cloudApi.get('/api/tenants/:tenantId/invoices', { params: { tenantId: currentTenantId } })
|
||||||
|
);
|
||||||
const { visitManagePaymentPage } = useSubscribe();
|
const { visitManagePaymentPage } = useSubscribe();
|
||||||
const [isActionLoading, setIsActionLoading] = useState(false);
|
const [isActionLoading, setIsActionLoading] = useState(false);
|
||||||
const isLoadingInvoices = !invoices && !error;
|
|
||||||
|
|
||||||
const latestUnpaidInvoice = useMemo(() => {
|
const latestUnpaidInvoice = useMemo(
|
||||||
return conditional(invoices && getLatestUnpaidInvoice(invoices));
|
() => conditional(invoicesResponse && getLatestUnpaidInvoice(invoicesResponse.invoices)),
|
||||||
}, [invoices]);
|
[invoicesResponse]
|
||||||
|
);
|
||||||
|
|
||||||
const [hasClosed, setHasClosed] = useState(false);
|
const [hasClosed, setHasClosed] = useState(false);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setHasClosed(true);
|
setHasClosed(true);
|
||||||
setIsOpen(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
if (!invoicesResponse || !latestUnpaidInvoice || hasClosed) {
|
||||||
if (isLoadingInvoices || hasClosed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (latestUnpaidInvoice) {
|
|
||||||
setIsOpen(true);
|
|
||||||
}
|
|
||||||
}, [hasClosed, isLoadingInvoices, latestUnpaidInvoice]);
|
|
||||||
|
|
||||||
if (isLoadingInvoices || !latestUnpaidInvoice || hasClosed) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactModal
|
<ReactModal
|
||||||
|
isOpen
|
||||||
shouldCloseOnEsc
|
shouldCloseOnEsc
|
||||||
isOpen={isOpen}
|
|
||||||
className={modalStyles.content}
|
className={modalStyles.content}
|
||||||
overlayClassName={modalStyles.overlay}
|
overlayClassName={modalStyles.overlay}
|
||||||
onRequestClose={handleCloseModal}
|
onRequestClose={handleCloseModal}
|
||||||
|
|
Loading…
Reference in a new issue