mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-17 06:16:59 +08:00
feat: fix several view model issue (#13388)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Error messages in chat cells are now clearly displayed with improved formatting and dynamic height adjustment for better readability. * Introduced the ability to remove specific chat cell view models from a session. * **Bug Fixes** * Enhanced error handling to automatically remove invalid chat cell view models when a message creation fails. * **Other Improvements** * Improved internal logic for handling message attachments and added more detailed debug logging for the copilot response lifecycle. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -160,6 +160,7 @@ private extension ChatManager {
|
||||
viewModelId: UUID
|
||||
) {
|
||||
assert(!Thread.isMainThread)
|
||||
print("[+] starting copilot response for session: \(sessionId)")
|
||||
|
||||
let messageParameters: [String: AnyHashable] = [
|
||||
// packages/frontend/core/src/blocksuite/ai/provider/setup-provider.tsx
|
||||
@@ -167,11 +168,11 @@ private extension ChatManager {
|
||||
"files": [String](), // attachment in context, keep nil for now
|
||||
"searchMode": editorData.isSearchEnabled ? "MUST" : "AUTO",
|
||||
]
|
||||
let hasMultipleAttachmentBlobs = [
|
||||
let attachmentCount = [
|
||||
editorData.fileAttachments.count,
|
||||
editorData.documentAttachments.count,
|
||||
].reduce(0, +) > 1
|
||||
let attachmentFieldName = hasMultipleAttachmentBlobs ? "options.blobs" : "options.blob"
|
||||
].reduce(0, +)
|
||||
let attachmentFieldName = attachmentCount > 1 && attachmentCount != 0 ? "options.blobs" : "options.blob"
|
||||
let uploadableAttachments: [GraphQLFile] = [
|
||||
editorData.fileAttachments.map { file -> GraphQLFile in
|
||||
.init(fieldName: attachmentFieldName, originalName: file.name, data: file.data ?? .init())
|
||||
@@ -183,8 +184,8 @@ private extension ChatManager {
|
||||
assert(uploadableAttachments.allSatisfy { !($0.data?.isEmpty ?? true) })
|
||||
guard let input = try? CreateChatMessageInput(
|
||||
attachments: [],
|
||||
blob: hasMultipleAttachmentBlobs ? .none : "",
|
||||
blobs: hasMultipleAttachmentBlobs ? .some([]) : .none,
|
||||
blob: .none,
|
||||
blobs: attachmentCount > 1 && attachmentCount != 0 ? .some([]) : .none,
|
||||
content: .some(contextSnippet.isEmpty ? editorData.text : "\(contextSnippet)\n\(editorData.text)"),
|
||||
params: .some(AffineGraphQL.JSON(_jsonValue: messageParameters)),
|
||||
sessionId: sessionId
|
||||
@@ -195,11 +196,13 @@ private extension ChatManager {
|
||||
}
|
||||
let mutation = CreateCopilotMessageMutation(options: input)
|
||||
QLService.shared.client.upload(operation: mutation, files: uploadableAttachments) { result in
|
||||
print("[*] createCopilotMessage result: \(result)")
|
||||
DispatchQueue.main.async {
|
||||
switch result {
|
||||
case let .success(graphQLResult):
|
||||
guard let messageIdentifier = graphQLResult.data?.createCopilotMessage else {
|
||||
self.report(sessionId, ChatError.invalidResponse)
|
||||
self.delete(sessionId: sessionId, vmId: viewModelId)
|
||||
return
|
||||
}
|
||||
self.startStreamingResponse(
|
||||
@@ -217,6 +220,7 @@ private extension ChatManager {
|
||||
|
||||
private extension ChatManager {
|
||||
func startStreamingResponse(sessionId: String, messageId: String, applyingTo vmId: UUID) {
|
||||
print("[+] starting streaming response for session: \(sessionId), message: \(messageId)")
|
||||
let base = IntelligentContext.shared.webViewMetadata[.currentServerBaseUrl] as? String
|
||||
guard let base, let url = URL(string: base) else {
|
||||
report(sessionId, ChatError.invalidServerConfiguration)
|
||||
|
||||
@@ -68,6 +68,10 @@ public class ChatManager: ObservableObject, @unchecked Sendable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func delete(sessionId: String, vmId: UUID) {
|
||||
with(sessionId: sessionId) { $0.removeValue(forKey: vmId) }
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func append(sessionId: String, _ viewModel: any ChatCellViewModel) -> UUID {
|
||||
|
||||
@@ -11,24 +11,61 @@ import Then
|
||||
import UIKit
|
||||
|
||||
class ErrorCell: ChatBaseCell {
|
||||
let label = UILabel()
|
||||
|
||||
override func prepareContentView(inside contentView: UIView) {
|
||||
super.prepareContentView(inside: contentView)
|
||||
contentView.addSubview(label)
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
label.attributedText = nil
|
||||
}
|
||||
|
||||
override func layoutContentView(bounds: CGRect) {
|
||||
super.layoutContentView(bounds: bounds)
|
||||
let width = bounds.width * 0.8
|
||||
label.frame = .init(
|
||||
x: (bounds.width - width) / 2,
|
||||
y: 0,
|
||||
width: width,
|
||||
height: bounds.height
|
||||
)
|
||||
}
|
||||
|
||||
override func configure(with viewModel: any ChatCellViewModel) {
|
||||
super.configure(with: viewModel)
|
||||
guard let vm = viewModel as? ErrorCellViewModel else {
|
||||
assertionFailure("Invalid view model type")
|
||||
return
|
||||
}
|
||||
label.attributedText = Self.attributeText(for: vm.errorMessage)
|
||||
}
|
||||
|
||||
static func attributeText(for text: String) -> NSAttributedString {
|
||||
return .init(string: text, attributes: [
|
||||
.font: UIFont.preferredFont(forTextStyle: .footnote),
|
||||
.foregroundColor: UIColor.affineTextSecondary,
|
||||
.paragraphStyle: NSMutableParagraphStyle().then {
|
||||
$0.lineBreakMode = .byWordWrapping
|
||||
$0.alignment = .center
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
override class func heightForContent(
|
||||
for viewModel: any ChatCellViewModel,
|
||||
width: CGFloat
|
||||
) -> CGFloat {
|
||||
_ = viewModel
|
||||
_ = width
|
||||
return 0
|
||||
let vm = viewModel as! ErrorCellViewModel
|
||||
let text = Self.attributeText(for: vm.errorMessage)
|
||||
let boundingRect = text.boundingRect(
|
||||
with: CGSize(width: width * 0.8, height: .greatestFiniteMagnitude),
|
||||
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
||||
context: nil
|
||||
)
|
||||
let boundingSize = boundingRect.size
|
||||
return ceil(boundingSize.height)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user