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) .foregroundStyle(AffineColors.textSecondary.color)
.opacity(viewModel.products.isEmpty ? 0 : 1) .opacity(viewModel.products.isEmpty ? 0 : 1)
.disabled(isPurchased) .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.") 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)) .font(.system(size: 12))

View File

@@ -31,8 +31,18 @@ extension ViewModel {
switch result { switch result {
case .pending: case .pending:
break break
case let .success(transaction): case let .success(verificationResult):
print("purchase success", transaction) 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 shouldDismiss = true
case .userCancelled: case .userCancelled:
break break
@@ -93,6 +103,21 @@ extension ViewModel {
associatedController?.dismiss(animated: true) 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 { nonisolated extension ViewModel {

View File

@@ -39,7 +39,10 @@ import {
} from '@affine/core/modules/workspace'; } from '@affine/core/modules/workspace';
import { configureBrowserWorkspaceFlavours } from '@affine/core/modules/workspace-engine'; import { configureBrowserWorkspaceFlavours } from '@affine/core/modules/workspace-engine';
import { getWorkerUrl } from '@affine/env/worker'; import { getWorkerUrl } from '@affine/env/worker';
import { refreshSubscriptionMutation } from '@affine/graphql'; import {
refreshSubscriptionMutation,
requestApplySubscriptionMutation,
} from '@affine/graphql';
import { I18n } from '@affine/i18n'; import { I18n } from '@affine/i18n';
import { StoreManagerClient } from '@affine/nbstore/worker/client'; import { StoreManagerClient } from '@affine/nbstore/worker/client';
import { Container } from '@blocksuite/affine/global/di'; import { Container } from '@blocksuite/affine/global/di';
@@ -372,6 +375,23 @@ const frameworkProvider = framework.provider();
const subscriptionService = currentServer.scope.get(SubscriptionService); const subscriptionService = currentServer.scope.get(SubscriptionService);
subscriptionService.subscription.revalidate(); 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 // setup application lifecycle events, and emit application start event
window.addEventListener('focus', () => { window.addEventListener('focus', () => {