From 4b3ebd899bdc59b0f946f15270251a4b7d40b357 Mon Sep 17 00:00:00 2001 From: EYHN Date: Tue, 30 Sep 2025 11:12:51 +0800 Subject: [PATCH] feat(ios): update js subscription api (#13678) ## Summary by CodeRabbit - New Features - Added on-demand subscription refresh and state retrieval in the iOS app, enabling up-to-date subscription status and billing information. - Exposed lightweight runtime APIs to check and update subscription state for improved account visibility. - Chores - Integrated shared GraphQL package and project references to support subscription operations. - Updated workspace configuration to include the common GraphQL module for the iOS app. --- .../RefreshSubscriptionMutation.graphql.swift | 62 +++++++++++++++++++ packages/frontend/apps/ios/package.json | 1 + packages/frontend/apps/ios/src/app.tsx | 33 ++++++++++ packages/frontend/apps/ios/tsconfig.json | 1 + tools/utils/src/workspace.gen.ts | 1 + yarn.lock | 1 + 6 files changed, 99 insertions(+) create mode 100644 packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/RefreshSubscriptionMutation.graphql.swift diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/RefreshSubscriptionMutation.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/RefreshSubscriptionMutation.graphql.swift new file mode 100644 index 0000000000..5947b323ee --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/RefreshSubscriptionMutation.graphql.swift @@ -0,0 +1,62 @@ +// @generated +// This file was automatically generated and should not be edited. + +@_exported import ApolloAPI + +public class RefreshSubscriptionMutation: GraphQLMutation { + public static let operationName: String = "refreshSubscription" + public static let operationDocument: ApolloAPI.OperationDocument = .init( + definition: .init( + #"mutation refreshSubscription { refreshUserSubscriptions { __typename id status plan recurring start end nextBillAt canceledAt variant } }"# + )) + + public init() {} + + public struct Data: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.Mutation } + public static var __selections: [ApolloAPI.Selection] { [ + .field("refreshUserSubscriptions", [RefreshUserSubscription].self), + ] } + + /// Refresh current user subscriptions and return latest. + public var refreshUserSubscriptions: [RefreshUserSubscription] { __data["refreshUserSubscriptions"] } + + /// RefreshUserSubscription + /// + /// Parent Type: `SubscriptionType` + public struct RefreshUserSubscription: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.SubscriptionType } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("id", String?.self), + .field("status", GraphQLEnum.self), + .field("plan", GraphQLEnum.self), + .field("recurring", GraphQLEnum.self), + .field("start", AffineGraphQL.DateTime.self), + .field("end", AffineGraphQL.DateTime?.self), + .field("nextBillAt", AffineGraphQL.DateTime?.self), + .field("canceledAt", AffineGraphQL.DateTime?.self), + .field("variant", GraphQLEnum?.self), + ] } + + @available(*, deprecated, message: "removed") + public var id: String? { __data["id"] } + public var status: GraphQLEnum { __data["status"] } + /// The 'Free' plan just exists to be a placeholder and for the type convenience of frontend. + /// There won't actually be a subscription with plan 'Free' + public var plan: GraphQLEnum { __data["plan"] } + public var recurring: GraphQLEnum { __data["recurring"] } + public var start: AffineGraphQL.DateTime { __data["start"] } + public var end: AffineGraphQL.DateTime? { __data["end"] } + public var nextBillAt: AffineGraphQL.DateTime? { __data["nextBillAt"] } + public var canceledAt: AffineGraphQL.DateTime? { __data["canceledAt"] } + public var variant: GraphQLEnum? { __data["variant"] } + } + } +} diff --git a/packages/frontend/apps/ios/package.json b/packages/frontend/apps/ios/package.json index 30d40b8628..f96e1d33b4 100644 --- a/packages/frontend/apps/ios/package.json +++ b/packages/frontend/apps/ios/package.json @@ -17,6 +17,7 @@ "@affine/component": "workspace:*", "@affine/core": "workspace:*", "@affine/env": "workspace:*", + "@affine/graphql": "workspace:*", "@affine/i18n": "workspace:*", "@affine/nbstore": "workspace:*", "@blocksuite/affine": "workspace:*", diff --git a/packages/frontend/apps/ios/src/app.tsx b/packages/frontend/apps/ios/src/app.tsx index 825404201d..f648ba3137 100644 --- a/packages/frontend/apps/ios/src/app.tsx +++ b/packages/frontend/apps/ios/src/app.tsx @@ -14,6 +14,7 @@ import { ServerScope, ServerService, ServersService, + SubscriptionService, ValidatorProvider, } from '@affine/core/modules/cloud'; import { DocsService } from '@affine/core/modules/doc'; @@ -38,6 +39,7 @@ import { } from '@affine/core/modules/workspace'; import { configureBrowserWorkspaceFlavours } from '@affine/core/modules/workspace-engine'; import { getWorkerUrl } from '@affine/env/worker'; +import { refreshSubscriptionMutation } from '@affine/graphql'; import { I18n } from '@affine/i18n'; import { StoreManagerClient } from '@affine/nbstore/worker/client'; import { Container } from '@blocksuite/affine/global/di'; @@ -328,6 +330,37 @@ const frameworkProvider = framework.provider(); workspaceRef?.dispose(); } }; +(window as any).getSubscriptionState = async () => { + const globalContextService = frameworkProvider.get(GlobalContextService); + const currentServerId = globalContextService.globalContext.serverId.get(); + const serversService = frameworkProvider.get(ServersService); + const defaultServerService = frameworkProvider.get(DefaultServerService); + const currentServer = + (currentServerId ? serversService.server$(currentServerId).value : null) ?? + defaultServerService.server; + const subscriptionService = currentServer.scope.get(SubscriptionService); + await subscriptionService.subscription.waitForRevalidation(); + return { + pro: subscriptionService.subscription.pro$.value, + ai: subscriptionService.subscription.ai$.value, + }; +}; +(window as any).updateSubscriptionState = async () => { + const globalContextService = frameworkProvider.get(GlobalContextService); + const currentServerId = globalContextService.globalContext.serverId.get(); + const serversService = frameworkProvider.get(ServersService); + const defaultServerService = frameworkProvider.get(DefaultServerService); + const currentServer = + (currentServerId ? serversService.server$(currentServerId).value : null) ?? + defaultServerService.server; + await currentServer + .gql({ + query: refreshSubscriptionMutation, + }) + .catch(console.error); + const subscriptionService = currentServer.scope.get(SubscriptionService); + subscriptionService.subscription.revalidate(); +}; // setup application lifecycle events, and emit application start event window.addEventListener('focus', () => { diff --git a/packages/frontend/apps/ios/tsconfig.json b/packages/frontend/apps/ios/tsconfig.json index 240cd51ff3..9c4002e3d2 100644 --- a/packages/frontend/apps/ios/tsconfig.json +++ b/packages/frontend/apps/ios/tsconfig.json @@ -10,6 +10,7 @@ { "path": "../../component" }, { "path": "../../core" }, { "path": "../../../common/env" }, + { "path": "../../../common/graphql" }, { "path": "../../i18n" }, { "path": "../../../common/nbstore" }, { "path": "../../../../blocksuite/affine/all" }, diff --git a/tools/utils/src/workspace.gen.ts b/tools/utils/src/workspace.gen.ts index 0c7f4cb52a..34ce1e32fb 100644 --- a/tools/utils/src/workspace.gen.ts +++ b/tools/utils/src/workspace.gen.ts @@ -1287,6 +1287,7 @@ export const PackageList = [ 'packages/frontend/component', 'packages/frontend/core', 'packages/common/env', + 'packages/common/graphql', 'packages/frontend/i18n', 'packages/common/nbstore', 'blocksuite/affine/all', diff --git a/yarn.lock b/yarn.lock index d6d9f69f09..85087babb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -682,6 +682,7 @@ __metadata: "@affine/component": "workspace:*" "@affine/core": "workspace:*" "@affine/env": "workspace:*" + "@affine/graphql": "workspace:*" "@affine/i18n": "workspace:*" "@affine/native": "workspace:*" "@affine/nbstore": "workspace:*"