diff --git a/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/billing/index.tsx b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/billing/index.tsx
index 4ba34cfeab..2eeb8ccb49 100644
--- a/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/billing/index.tsx
+++ b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/billing/index.tsx
@@ -8,8 +8,10 @@ import {
import { getUpgradeQuestionnaireLink } from '@affine/core/components/hooks/affine/use-subscription-notify';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { useMutation } from '@affine/core/components/hooks/use-mutation';
+import { useWorkspace } from '@affine/core/components/hooks/use-workspace';
import {
AuthService,
+ SubscriptionService,
WorkspaceInvoicesService,
WorkspaceSubscriptionService,
} from '@affine/core/modules/cloud';
@@ -27,7 +29,7 @@ import {
FrameworkScope,
useLiveData,
useService,
- WorkspaceService,
+ type WorkspaceMetadata,
} from '@toeverything/infra';
import { cssVar } from '@toeverything/theme';
import { useCallback, useEffect, useMemo, useState } from 'react';
@@ -38,18 +40,29 @@ import {
} from '../../general-setting/plans/actions';
import * as styles from './styles.css';
-export const WorkspaceSettingBilling = () => {
+export const WorkspaceSettingBilling = ({
+ workspaceMetadata,
+}: {
+ workspaceMetadata: WorkspaceMetadata;
+}) => {
+ // useWorkspace hook is a vary heavy operation here, but we need syncing name and avatar changes here,
+ // we don't have a better way to do this now
+ const workspace = useWorkspace(workspaceMetadata);
+
const t = useI18n();
- const workspace = useService(WorkspaceService).workspace;
- const subscriptionService = useService(WorkspaceSubscriptionService);
- const subscription = useLiveData(
- subscriptionService.subscription.subscription$
+
+ const subscriptionService = workspace?.scope.get(
+ WorkspaceSubscriptionService
);
- const title = useLiveData(workspace.name$) || 'untitled';
+ const subscription = useLiveData(
+ subscriptionService?.subscription.subscription$
+ );
+
+ useEffect(() => {
+ subscriptionService?.subscription.revalidate();
+ }, [subscriptionService]);
if (workspace === null) {
- console.log('workspace is null', title);
-
return null;
}
@@ -83,17 +96,20 @@ export const WorkspaceSettingBilling = () => {
const TeamCard = () => {
const t = useI18n();
- const subscriptionService = useService(WorkspaceSubscriptionService);
+ const workspaceSubscriptionService = useService(WorkspaceSubscriptionService);
+ const subscriptionService = useService(SubscriptionService);
+
const teamSubscription = useLiveData(
- subscriptionService.subscription.subscription$
+ workspaceSubscriptionService.subscription.subscription$
);
const teamPrices = useLiveData(subscriptionService.prices.teamPrice$);
const [openCancelModal, setOpenCancelModal] = useState(false);
+
useEffect(() => {
- subscriptionService.subscription.revalidate();
- subscriptionService.prices.revalidate();
- }, [subscriptionService]);
+ workspaceSubscriptionService.subscription.revalidate();
+ }, [workspaceSubscriptionService.subscription]);
+
const expiration = teamSubscription?.end;
const nextBillingDate = teamSubscription?.nextBillAt;
const recurring = teamSubscription?.recurring;
diff --git a/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/index.tsx b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/index.tsx
index 56ff2a7a5e..79f5334d3e 100644
--- a/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/index.tsx
+++ b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/index.tsx
@@ -31,7 +31,7 @@ export const WorkspaceSetting = ({
);
case 'workspace:billing':
- return ;
+ return ;
}
return null;
};
diff --git a/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/new-workspace-setting-detail/members/cloud-members-panel.tsx b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/new-workspace-setting-detail/members/cloud-members-panel.tsx
index 7eeb0d61b2..05b4026d59 100644
--- a/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/new-workspace-setting-detail/members/cloud-members-panel.tsx
+++ b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/new-workspace-setting-detail/members/cloud-members-panel.tsx
@@ -1,7 +1,5 @@
import { Button, Loading, notify } from '@affine/component';
import {
- InviteModal,
- type InviteModalProps,
InviteTeamMemberModal,
type InviteTeamMemberModalProps,
MemberLimitModal,
@@ -98,25 +96,6 @@ export const CloudWorkspaceMembersPanel = ({
return success;
}, [permissionService.permission]);
- const onInviteConfirm = useCallback(
- async ({ email, permission }) => {
- setIsMutating(true);
- const success = await permissionService.permission.inviteMember(
- email,
- permission,
- true
- );
- if (success) {
- notify.success({
- title: t['Invitation sent'](),
- message: t['Invitation sent hint'](),
- });
- setOpen(false);
- }
- setIsMutating(false);
- },
- [permissionService.permission, t]
- );
const onInviteBatchConfirm = useCallback<
InviteTeamMemberModalProps['onConfirm']
>(
@@ -208,18 +187,7 @@ export const CloudWorkspaceMembersPanel = ({
{isOwner ? (
<>
- {isTeam ? (
- }
- />
- ) : isLimited ? (
+ {isLimited && !isTeam ? (
) : (
- }
/>
)}
>
diff --git a/packages/frontend/core/src/modules/cloud/entities/workspace-invoices.ts b/packages/frontend/core/src/modules/cloud/entities/workspace-invoices.ts
index ba6d8fe6dc..ab85f68245 100644
--- a/packages/frontend/core/src/modules/cloud/entities/workspace-invoices.ts
+++ b/packages/frontend/core/src/modules/cloud/entities/workspace-invoices.ts
@@ -14,7 +14,8 @@ import {
import { EMPTY, map, mergeMap } from 'rxjs';
import { isBackendError, isNetworkError } from '../error';
-import type { InvoicesStore } from '../stores/invoices';
+import type { WorkspaceServerService } from '../services/workspace-server';
+import { InvoicesStore } from '../stores/invoices';
export type Invoice = NonNullable<
InvoicesQuery['currentUser']
@@ -22,12 +23,14 @@ export type Invoice = NonNullable<
export class WorkspaceInvoices extends Entity {
constructor(
- private readonly store: InvoicesStore,
- private readonly workspaceService: WorkspaceService
+ private readonly workspaceService: WorkspaceService,
+ private readonly workspaceServerService: WorkspaceServerService
) {
super();
}
+ store = this.workspaceServerService.server?.scope.get(InvoicesStore);
+
pageNum$ = new LiveData(0);
invoiceCount$ = new LiveData(undefined);
pageInvoices$ = new LiveData(undefined);
@@ -43,6 +46,9 @@ export class WorkspaceInvoices extends Entity {
(a, b) => a === b,
pageNum => {
return fromPromise(async signal => {
+ if (!this.store) {
+ throw new Error('No invoices store');
+ }
return this.store.fetchWorkspaceInvoices(
pageNum * this.PAGE_SIZE,
this.PAGE_SIZE,
diff --git a/packages/frontend/core/src/modules/cloud/entities/workspace-subscription.ts b/packages/frontend/core/src/modules/cloud/entities/workspace-subscription.ts
index e9f6a74825..7d86210328 100644
--- a/packages/frontend/core/src/modules/cloud/entities/workspace-subscription.ts
+++ b/packages/frontend/core/src/modules/cloud/entities/workspace-subscription.ts
@@ -15,8 +15,8 @@ import {
import { EMPTY, mergeMap } from 'rxjs';
import { isBackendError, isNetworkError } from '../error';
-import type { ServerService } from '../services/server';
-import type { SubscriptionStore } from '../stores/subscription';
+import type { WorkspaceServerService } from '../services/workspace-server';
+import { SubscriptionStore } from '../stores/subscription';
export type SubscriptionType = NonNullable<
SubscriptionQuery['currentUser']
@@ -33,18 +33,25 @@ export class WorkspaceSubscription extends Entity {
constructor(
private readonly workspaceService: WorkspaceService,
- private readonly serverService: ServerService,
- private readonly store: SubscriptionStore
+ private readonly workspaceServerService: WorkspaceServerService
) {
super();
}
+ server = this.workspaceServerService.server;
+ store = this.workspaceServerService.server?.scope.get(SubscriptionStore);
async resumeSubscription(idempotencyKey: string, plan?: SubscriptionPlan) {
+ if (!this.store) {
+ throw new Error('Subscription store not available');
+ }
await this.store.mutateResumeSubscription(idempotencyKey, plan);
await this.waitForRevalidation();
}
async cancelSubscription(idempotencyKey: string, plan?: SubscriptionPlan) {
+ if (!this.store) {
+ throw new Error('Subscription store not available');
+ }
await this.store.mutateCancelSubscription(idempotencyKey, plan);
await this.waitForRevalidation();
}
@@ -54,6 +61,9 @@ export class WorkspaceSubscription extends Entity {
recurring: SubscriptionRecurring,
plan?: SubscriptionPlan
) {
+ if (!this.store) {
+ throw new Error('Subscription store not available');
+ }
await this.store.setSubscriptionRecurring(idempotencyKey, recurring, plan);
await this.waitForRevalidation();
}
@@ -70,11 +80,11 @@ export class WorkspaceSubscription extends Entity {
exhaustMapWithTrailing(() => {
return fromPromise(async signal => {
const currentWorkspaceId = this.workspaceService.workspace.id;
- if (!currentWorkspaceId) {
+ if (!currentWorkspaceId || !this.server) {
return undefined; // no subscription if no user
}
- const serverConfig =
- await this.serverService.server.features$.waitForNonNull(signal);
+
+ const serverConfig = await this.server.features$.waitForNonNull(signal);
if (!serverConfig.payment) {
// No payment feature, no subscription
@@ -83,6 +93,12 @@ export class WorkspaceSubscription extends Entity {
subscription: null,
};
}
+ if (!this.store) {
+ return {
+ workspaceId: currentWorkspaceId,
+ subscription: null,
+ };
+ }
const { workspaceId, subscription } =
await this.store.fetchWorkspaceSubscriptions(
currentWorkspaceId,
@@ -101,7 +117,7 @@ export class WorkspaceSubscription extends Entity {
when: isBackendError,
}),
mergeMap(data => {
- if (data && data.subscription && data.workspaceId) {
+ if (data && data.subscription && data.workspaceId && this.store) {
this.store.setCachedWorkspaceSubscription(
data.workspaceId,
data.subscription
diff --git a/packages/frontend/core/src/modules/cloud/index.ts b/packages/frontend/core/src/modules/cloud/index.ts
index 852fbeb75c..ad32ebc1f9 100644
--- a/packages/frontend/core/src/modules/cloud/index.ts
+++ b/packages/frontend/core/src/modules/cloud/index.ts
@@ -149,16 +149,7 @@ export function configureCloudModule(framework: Framework) {
.store(UserFeatureStore, [GraphQLService])
.service(InvoicesService)
.store(InvoicesStore, [GraphQLService])
- .entity(Invoices, [InvoicesStore])
- .scope(WorkspaceScope)
- .service(WorkspaceSubscriptionService, [SubscriptionStore])
- .entity(WorkspaceSubscription, [
- WorkspaceService,
- ServerService,
- SubscriptionStore,
- ])
- .service(WorkspaceInvoicesService)
- .entity(WorkspaceInvoices, [InvoicesStore, WorkspaceService]);
+ .entity(Invoices, [InvoicesStore]);
framework
.scope(WorkspaceScope)
@@ -167,4 +158,10 @@ export function configureCloudModule(framework: Framework) {
.service(CloudDocMetaService)
.entity(CloudDocMeta, [CloudDocMetaStore, DocService, GlobalCache])
.store(CloudDocMetaStore, [WorkspaceServerService]);
+ framework
+ .scope(WorkspaceScope)
+ .service(WorkspaceSubscriptionService, [WorkspaceServerService])
+ .entity(WorkspaceSubscription, [WorkspaceService, WorkspaceServerService])
+ .service(WorkspaceInvoicesService)
+ .entity(WorkspaceInvoices, [WorkspaceService, WorkspaceServerService]);
}
diff --git a/packages/frontend/core/src/modules/cloud/services/workspace-subscription.ts b/packages/frontend/core/src/modules/cloud/services/workspace-subscription.ts
index c9850da2c5..b247f1bdfb 100644
--- a/packages/frontend/core/src/modules/cloud/services/workspace-subscription.ts
+++ b/packages/frontend/core/src/modules/cloud/services/workspace-subscription.ts
@@ -1,19 +1,22 @@
import { type CreateCheckoutSessionInput } from '@affine/graphql';
import { Service } from '@toeverything/infra';
-import { SubscriptionPrices } from '../entities/subscription-prices';
import { WorkspaceSubscription } from '../entities/workspace-subscription';
-import type { SubscriptionStore } from '../stores/subscription';
+import { SubscriptionStore } from '../stores/subscription';
+import type { WorkspaceServerService } from './workspace-server';
export class WorkspaceSubscriptionService extends Service {
subscription = this.framework.createEntity(WorkspaceSubscription);
- prices = this.framework.createEntity(SubscriptionPrices);
- constructor(private readonly store: SubscriptionStore) {
+ constructor(private readonly workspaceServerService: WorkspaceServerService) {
super();
}
+ store = this.workspaceServerService.server?.scope.get(SubscriptionStore);
async createCheckoutSession(input: CreateCheckoutSessionInput) {
+ if (!this.store) {
+ throw new Error('No subscription store');
+ }
return await this.store.createCheckoutSession(input);
}
}