Skip to content

Commit

Permalink
feat(core): prevent invite members when team plan is canceled
Browse files Browse the repository at this point in the history
  • Loading branch information
JimmFly committed Jan 24, 2025
1 parent 0e8a1b3 commit d449b09
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { notify } from '@affine/component';
import { useDowngradeNotify } from '@affine/core/components/affine/subscription-landing/notify';
import { getDowngradeQuestionnaireLink } from '@affine/core/components/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { SubscriptionPlan } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { useLiveData, useService } from '@toeverything/infra';
import { nanoid } from 'nanoid';
Expand Down Expand Up @@ -231,6 +233,7 @@ export const TeamResumeAction = ({
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
const [isMutating, setIsMutating] = useState(false);
const subscription = useService(WorkspaceSubscriptionService).subscription;
const t = useI18n();

const resume = useAsyncCallback(async () => {
try {
Expand All @@ -243,10 +246,14 @@ export const TeamResumeAction = ({
// refresh idempotency key
setIdempotencyKey(nanoid());
onOpenChange(false);
notify.success({
title: t['com.affine.payment.resume.success.title'](),
message: t['com.affine.payment.resume.success.team.message'](),
});
} finally {
setIsMutating(false);
}
}, [subscription, idempotencyKey, onOpenChange]);
}, [subscription, idempotencyKey, onOpenChange, t]);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, Loading, notify } from '@affine/component';
import { Button, Loading, notify, useConfirmModal } from '@affine/component';
import {
InviteTeamMemberModal,
type InviteTeamMemberModalProps,
Expand All @@ -7,7 +7,11 @@ import {
import { SettingRow } from '@affine/component/setting-components';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { Upload } from '@affine/core/components/pure/file-upload';
import { ServerService, SubscriptionService } from '@affine/core/modules/cloud';
import {
ServerService,
SubscriptionService,
WorkspaceSubscriptionService,
} from '@affine/core/modules/cloud';
import {
WorkspaceMembersService,
WorkspacePermissionService,
Expand All @@ -17,11 +21,12 @@ import { WorkspaceShareSettingService } from '@affine/core/modules/share-setting
import { copyTextToClipboard } from '@affine/core/utils/clipboard';
import { emailRegex } from '@affine/core/utils/email-regex';
import type { WorkspaceInviteLinkExpireTime } from '@affine/graphql';
import { UserFriendlyError } from '@affine/graphql';
import { SubscriptionPlan, UserFriendlyError } from '@affine/graphql';
import { useI18n } from '@affine/i18n';
import { track } from '@affine/track';
import { ExportIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
import { nanoid } from 'nanoid';
import { useCallback, useEffect, useMemo, useState } from 'react';

import type { SettingState } from '../../types';
Expand Down Expand Up @@ -51,6 +56,8 @@ export const CloudWorkspaceMembersPanel = ({
isTeam?: boolean;
}) => {
const workspaceShareSettingService = useService(WorkspaceShareSettingService);
const subscription = useService(WorkspaceSubscriptionService).subscription;
const workspaceSubscription = useLiveData(subscription.subscription$);
const inviteLink = useLiveData(
workspaceShareSettingService.sharePreview.inviteLink$
);
Expand Down Expand Up @@ -89,9 +96,77 @@ export const CloudWorkspaceMembersPanel = ({
const [openMemberLimit, setOpenMemberLimit] = useState(false);
const [isMutating, setIsMutating] = useState(false);

const { openConfirmModal, closeConfirmModal } = useConfirmModal();
const goToTeamBilling = useCallback(() => {
onChangeSettingState({
activeTab: 'workspace:billing',
});
}, [onChangeSettingState]);
const [idempotencyKey, setIdempotencyKey] = useState(nanoid());
const resume = useAsyncCallback(async () => {
try {
setIsMutating(true);
await subscription.resumeSubscription(
idempotencyKey,
SubscriptionPlan.Team
);
await subscription.waitForRevalidation();
// refresh idempotency key
setIdempotencyKey(nanoid());
closeConfirmModal();
notify.success({
title: t['com.affine.payment.resume.success.title'](),
message: t['com.affine.payment.resume.success.team.message'](),
});
} catch (err) {
const error = UserFriendlyError.fromAnyError(err);
notify.error({
title: error.name,
message: error.message,
});
} finally {
setIsMutating(false);
}
}, [subscription, idempotencyKey, closeConfirmModal, t]);
const openInviteModal = useCallback(() => {
if (isTeam && workspaceSubscription?.canceledAt) {
openConfirmModal({
title: t['com.affine.payment.member.team.retry-payment.title'](),
description:
t[
`com.affine.payment.member.team.disabled-subscription.${isOwner ? 'owner' : 'admin'}.description`
](),
confirmText:
t[
isOwner
? 'com.affine.payment.member.team.disabled-subscription.resume-subscription'
: 'Got it'
](),
cancelText: t['Cancel'](),
cancelButtonOptions: {
style: {
visibility: isOwner ? 'visible' : 'hidden',
},
},
onConfirm: isOwner ? resume : undefined,
confirmButtonOptions: {
variant: 'primary',
loading: isMutating,
},
});

return;
}
setOpenInvite(true);
}, []);
}, [
isMutating,
isOwner,
isTeam,
openConfirmModal,
resume,
t,
workspaceSubscription?.canceledAt,
]);

const onGenerateInviteLink = useCallback(
async (expireTime: WorkspaceInviteLinkExpireTime) => {
Expand Down Expand Up @@ -170,12 +245,6 @@ export const CloudWorkspaceMembersPanel = ({
});
}, [onChangeSettingState]);

const goToTeamBilling = useCallback(() => {
onChangeSettingState({
activeTab: 'workspace:billing',
});
}, [onChangeSettingState]);

const desc = useMemo(() => {
if (!workspaceQuota) return null;

Expand Down
5 changes: 5 additions & 0 deletions packages/frontend/i18n/src/resources/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,9 @@
"com.affine.payment.member.team.retry-payment.owner.description": "The payment for adding new team members has failed. To add more seats, please update your payment method and process unpaid invoices.",
"com.affine.payment.member.team.retry-payment.admin.description": "The payment for adding new team members has failed. Please contact your workspace owner to update the payment method and process unpaid invoices.",
"com.affine.payment.member.team.retry-payment.update-payment": "Update Payment",
"com.affine.payment.member.team.disabled-subscription.owner.description": "Subscription has been disabled for your team workspace. To add more seats, you'll need to resume subscription first.",
"com.affine.payment.member.team.disabled-subscription.admin.description": "Your team workspace has subscription disabled, which prevents adding more seats. Please contact your workspace owner to enable subscription.",
"com.affine.payment.member.team.disabled-subscription.resume-subscription": "Resume Subscription",
"com.affine.payment.member.team.revoke.notify.title": "Invitation Revoked",
"com.affine.payment.member.team.revoke.notify.message": "You have canceled the invitation for {{name}}",
"com.affine.payment.member.team.approve.notify.title": "Request approved",
Expand Down Expand Up @@ -1016,6 +1019,8 @@
"com.affine.payment.recurring-monthly": "monthly",
"com.affine.payment.recurring-yearly": "annually",
"com.affine.payment.resume": "Resume",
"com.affine.payment.resume.success.title": "Subscription Resumed",
"com.affine.payment.resume.success.team.message": "Your team workspace subscription has been enabled successfully. Changes will take effect immediately.",
"com.affine.payment.resume-renewal": "Resume auto-renewal",
"com.affine.payment.see-all-plans": "See all plans",
"com.affine.payment.sign-up-free": "Sign up free",
Expand Down

0 comments on commit d449b09

Please sign in to comment.