feat: request apply subscription mutation (#13844)

This commit is contained in:
Lakr
2025-10-31 22:53:30 +08:00
committed by GitHub
parent 8535b3dc41
commit f3bb2be5ef
4 changed files with 117 additions and 3 deletions

View File

@@ -0,0 +1,68 @@
// @generated
// This file was automatically generated and should not be edited.
@_exported import ApolloAPI
public class RequestApplySubscriptionMutation: GraphQLMutation {
public static let operationName: String = "requestApplySubscription"
public static let operationDocument: ApolloAPI.OperationDocument = .init(
definition: .init(
#"mutation requestApplySubscription($transactionId: String!) { requestApplySubscription(transactionId: $transactionId) { __typename id status plan recurring start end nextBillAt canceledAt variant } }"#
))
public var transactionId: String
public init(transactionId: String) {
self.transactionId = transactionId
}
public var __variables: Variables? { ["transactionId": transactionId] }
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("requestApplySubscription", [RequestApplySubscription].self, arguments: ["transactionId": .variable("transactionId")]),
] }
/// Request to apply the subscription in advance
public var requestApplySubscription: [RequestApplySubscription] { __data["requestApplySubscription"] }
/// RequestApplySubscription
///
/// Parent Type: `SubscriptionType`
public struct RequestApplySubscription: 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<AffineGraphQL.SubscriptionStatus>.self),
.field("plan", GraphQLEnum<AffineGraphQL.SubscriptionPlan>.self),
.field("recurring", GraphQLEnum<AffineGraphQL.SubscriptionRecurring>.self),
.field("start", AffineGraphQL.DateTime.self),
.field("end", AffineGraphQL.DateTime?.self),
.field("nextBillAt", AffineGraphQL.DateTime?.self),
.field("canceledAt", AffineGraphQL.DateTime?.self),
.field("variant", GraphQLEnum<AffineGraphQL.SubscriptionVariant>?.self),
] }
@available(*, deprecated, message: "removed")
public var id: String? { __data["id"] }
public var status: GraphQLEnum<AffineGraphQL.SubscriptionStatus> { __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<AffineGraphQL.SubscriptionPlan> { __data["plan"] }
public var recurring: GraphQLEnum<AffineGraphQL.SubscriptionRecurring> { __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<AffineGraphQL.SubscriptionVariant>? { __data["variant"] }
}
}
}

View File

@@ -71,6 +71,7 @@ struct PurchaseFooterView: View {
.foregroundStyle(AffineColors.textSecondary.color)
.opacity(viewModel.products.isEmpty ? 0 : 1)
.disabled(isPurchased)
.disabled(viewModel.updating)
Text("The Monthly and Annual plans renew automatically, but youre free to cancel at any time if its not right for you.")
.font(.system(size: 12))

View File

@@ -31,8 +31,18 @@ extension ViewModel {
switch result {
case .pending:
break
case let .success(transaction):
print("purchase success", transaction)
case let .success(verificationResult):
switch verificationResult {
case .verified(let transaction):
print("purchase success", transaction)
try await self.applySubscription(transactionID: .init(transaction.id))
case .unverified:
#if DEBUG
break
#else
throw NSError() // should not happening
#endif
}
shouldDismiss = true
case .userCancelled:
break
@@ -93,6 +103,21 @@ extension ViewModel {
associatedController?.dismiss(animated: true)
}
func applySubscription(transactionID: String) async throws {
print(#function, transactionID)
if let context = associatedWebContext {
_ = try await context.callAsyncJavaScript(
"return await window.requestApplySubscription('\(transactionID)');",
contentWorld: .page
)
print("requestApplySubscription success")
} else {
assertionFailure()
throw NSError()
}
}
}
nonisolated extension ViewModel {

View File

@@ -39,7 +39,10 @@ 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 {
refreshSubscriptionMutation,
requestApplySubscriptionMutation,
} from '@affine/graphql';
import { I18n } from '@affine/i18n';
import { StoreManagerClient } from '@affine/nbstore/worker/client';
import { Container } from '@blocksuite/affine/global/di';
@@ -372,6 +375,23 @@ const frameworkProvider = framework.provider();
const subscriptionService = currentServer.scope.get(SubscriptionService);
subscriptionService.subscription.revalidate();
};
(window as any).requestApplySubscription = async (transactionId: string) => {
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: requestApplySubscriptionMutation,
variables: { transactionId },
})
.catch(console.error);
const subscriptionService = currentServer.scope.get(SubscriptionService);
subscriptionService.subscription.revalidate();
};
// setup application lifecycle events, and emit application start event
window.addEventListener('focus', () => {