Some checks failed
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 4s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Analyze (javascript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
137 lines
4.1 KiB
Swift
137 lines
4.1 KiB
Swift
//
|
|
// AffineViewController+AIButton.swift
|
|
// App
|
|
//
|
|
// Created by 秋星桥 on 2025/1/8.
|
|
//
|
|
|
|
import Intelligents
|
|
import UIKit
|
|
import WebKit
|
|
|
|
extension AFFiNEViewController: IntelligentsButtonDelegate {
|
|
private static let aiConsentKey = "com.affine.intelligents.userConsented"
|
|
|
|
private var hasUserConsented: Bool {
|
|
UserDefaults.standard.bool(forKey: Self.aiConsentKey)
|
|
}
|
|
|
|
func onIntelligentsButtonTapped(_ button: IntelligentsButton) {
|
|
button.beginProgress()
|
|
Task { @MainActor [weak self, weak button] in
|
|
guard let self else {
|
|
button?.stopProgress()
|
|
return
|
|
}
|
|
await self.handleIntelligentsButtonTapped(tappedButton: button)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private func handleIntelligentsButtonTapped(tappedButton: IntelligentsButton?) async {
|
|
guard let webView else {
|
|
tappedButton?.stopProgress()
|
|
return
|
|
}
|
|
|
|
do {
|
|
let isSignedIn = try await PaywallAuthGuard.ensureSignedIn(using: webView)
|
|
guard isSignedIn else {
|
|
tappedButton?.stopProgress()
|
|
dismissIntelligentsButton()
|
|
return
|
|
}
|
|
|
|
if try await PaywallAuthGuard.hasAISubscription(in: webView) {
|
|
tappedButton?.stopProgress()
|
|
continueToIntelligentsController()
|
|
return
|
|
}
|
|
|
|
tappedButton?.stopProgress()
|
|
presentAIPaywall(bindWebView: webView)
|
|
} catch {
|
|
tappedButton?.stopProgress()
|
|
showAIErrorAlert(error)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private func continueToIntelligentsController() {
|
|
if hasUserConsented {
|
|
prepareAndPresentIntelligentsController()
|
|
return
|
|
}
|
|
showAIConsentAlert { [weak self] in
|
|
self?.prepareAndPresentIntelligentsController()
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private func prepareAndPresentIntelligentsController() {
|
|
intelligentsButton?.beginProgress()
|
|
IntelligentContext.shared.webView = webView
|
|
IntelligentContext.shared.preparePresent { [weak self] result in
|
|
DispatchQueue.main.async {
|
|
guard let self else { return }
|
|
self.intelligentsButton?.stopProgress()
|
|
switch result {
|
|
case .success:
|
|
self.presentIntelligentsController()
|
|
case let .failure(error):
|
|
self.showAIErrorAlert(error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private func presentIntelligentsController() {
|
|
let controller = IntelligentsController()
|
|
present(controller, animated: true)
|
|
}
|
|
|
|
@MainActor
|
|
private func presentAIPaywall(bindWebView webView: WKWebView) {
|
|
guard presentedViewController == nil else { return }
|
|
|
|
let paywallController = AppPaywallViewController()
|
|
paywallController.initialPlan = .ai
|
|
paywallController.bindWebView = webView
|
|
paywallController.modalPresentationStyle = .fullScreen
|
|
paywallController.modalTransitionStyle = .coverVertical
|
|
paywallController.onPurchaseCompleted = { [weak self] in
|
|
Task { @MainActor in
|
|
self?.continueToIntelligentsController()
|
|
}
|
|
}
|
|
present(paywallController, animated: true)
|
|
}
|
|
|
|
@MainActor
|
|
private func showAIConsentAlert(onContinue: @escaping () -> Void) {
|
|
let alert = UIAlertController(
|
|
title: "AI Feature Data Usage",
|
|
message: "To provide AI-powered features, your input (such as document content and conversation messages) will be sent to our third-party AI service providers (Google, Anthropic, or OpenAI, based on your choice) for processing. This data is used solely to generate responses and is not used for any other purpose.\n\nBy continuing, you agree to share this data with these AI services.",
|
|
preferredStyle: .alert
|
|
)
|
|
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
|
alert.addAction(UIAlertAction(title: "Agree & Continue", style: .default) { _ in
|
|
UserDefaults.standard.set(true, forKey: Self.aiConsentKey)
|
|
onContinue()
|
|
})
|
|
present(alert, animated: true)
|
|
}
|
|
|
|
@MainActor
|
|
private func showAIErrorAlert(_ error: Error) {
|
|
let alert = UIAlertController(
|
|
title: "Unable to open AFFiNE AI",
|
|
message: error.localizedDescription,
|
|
preferredStyle: .alert
|
|
)
|
|
alert.addAction(UIAlertAction(title: "OK", style: .cancel))
|
|
present(alert, animated: true)
|
|
}
|
|
}
|