From d398aa9a71f86d385108337fb820662996fc0aa4 Mon Sep 17 00:00:00 2001 From: Lakr <25259084+Lakr233@users.noreply.github.com> Date: Tue, 5 Aug 2025 16:10:40 +0800 Subject: [PATCH] chore: added mime-type in gql (#13414) ## Summary by CodeRabbit * **New Features** * Improved file and image attachment handling by including MIME type information for uploads. * Added a new query to fetch document summaries by workspace and document IDs. * **Refactor** * Minor adjustments to method signatures and property initializations to streamline code and maintain consistency. * Updated access levels for certain properties and methods to internal, enhancing encapsulation. * **Style** * Formatting and whitespace clean-up for improved code readability. No changes to user-facing functionality or behavior. --- .../App/AffineViewController+AIButton.swift | 4 +- .../Queries/GetDocSummaryQuery.graphql.swift | 74 +++++++++++++++++++ .../ChatManager/ChatManager+Stream.swift | 31 +++++++- .../ChatManager/ChatManager.swift | 2 +- .../QLService+URLSessionCookieClient.swift | 2 +- .../Interface/View/ChatCell/ErrorCell.swift | 10 +-- .../Interface/View/InputBox/InputBox.swift | 10 +-- .../IntelligentsButton.swift | 2 +- .../SupplementView/DarkActionButton.swift | 2 +- 9 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetDocSummaryQuery.graphql.swift diff --git a/packages/frontend/apps/ios/App/App/AffineViewController+AIButton.swift b/packages/frontend/apps/ios/App/App/AffineViewController+AIButton.swift index c26f57391..ec156f6b2 100644 --- a/packages/frontend/apps/ios/App/App/AffineViewController+AIButton.swift +++ b/packages/frontend/apps/ios/App/App/AffineViewController+AIButton.swift @@ -9,9 +9,9 @@ import Intelligents import UIKit extension AFFiNEViewController: IntelligentsButtonDelegate { - func onIntelligentsButtonTapped(_ button: IntelligentsButton) { + func onIntelligentsButtonTapped(_: IntelligentsButton) { // if it shows up then we are ready to go let controller = IntelligentsController() - self.present(controller, animated: true) + present(controller, animated: true) } } diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetDocSummaryQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetDocSummaryQuery.graphql.swift new file mode 100644 index 000000000..56b95a120 --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetDocSummaryQuery.graphql.swift @@ -0,0 +1,74 @@ +// @generated +// This file was automatically generated and should not be edited. + +@_exported import ApolloAPI + +public class GetDocSummaryQuery: GraphQLQuery { + public static let operationName: String = "getDocSummary" + public static let operationDocument: ApolloAPI.OperationDocument = .init( + definition: .init( + #"query getDocSummary($workspaceId: String!, $docId: String!) { workspace(id: $workspaceId) { __typename doc(docId: $docId) { __typename summary } } }"# + )) + + public var workspaceId: String + public var docId: String + + public init( + workspaceId: String, + docId: String + ) { + self.workspaceId = workspaceId + self.docId = docId + } + + public var __variables: Variables? { [ + "workspaceId": workspaceId, + "docId": docId + ] } + + public struct Data: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.Query } + public static var __selections: [ApolloAPI.Selection] { [ + .field("workspace", Workspace.self, arguments: ["id": .variable("workspaceId")]), + ] } + + /// Get workspace by id + public var workspace: Workspace { __data["workspace"] } + + /// Workspace + /// + /// Parent Type: `WorkspaceType` + public struct Workspace: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.WorkspaceType } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("doc", Doc.self, arguments: ["docId": .variable("docId")]), + ] } + + /// Get get with given id + public var doc: Doc { __data["doc"] } + + /// Workspace.Doc + /// + /// Parent Type: `DocType` + public struct Doc: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.DocType } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("summary", String?.self), + ] } + + public var summary: String? { __data["summary"] } + } + } + } +} diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager+Stream.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager+Stream.swift index 2d453d5e7..464a234b2 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager+Stream.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager+Stream.swift @@ -12,6 +12,7 @@ import EventSource import Foundation import MarkdownParser import MarkdownView +import UniformTypeIdentifiers private let loadingIndicator = " ●" @@ -175,16 +176,26 @@ private extension ChatManager { 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()) + .init( + fieldName: attachmentFieldName, + originalName: file.name, + mimeType: mimeType(text: file.name), + data: file.data ?? .init() + ) }, editorData.imageAttachments.map { image -> GraphQLFile in - .init(fieldName: attachmentFieldName, originalName: "image.jpg", data: image.imageData) + .init( + fieldName: attachmentFieldName, + originalName: "image.jpg", + mimeType: mimeType(pathExtension: "jpg"), + data: image.imageData + ) }, ].flatMap(\.self) assert(uploadableAttachments.allSatisfy { !($0.data?.isEmpty ?? true) }) guard let input = try? CreateChatMessageInput( attachments: [], - blob: .none, + blob: attachmentCount == 1 ? "" : .none, blobs: attachmentCount > 1 && attachmentCount != 0 ? .some([]) : .none, content: .some(contextSnippet.isEmpty ? editorData.text : "\(contextSnippet)\n\(editorData.text)"), params: .some(AffineGraphQL.JSON(_jsonValue: messageParameters)), @@ -216,6 +227,20 @@ private extension ChatManager { } } } + + private func pathExtension(for text: String) -> String { + (text as NSString).pathExtension + } + + private func mimeType(pathExtension: String) -> String { + let type = UTType(filenameExtension: pathExtension) ?? .data + return type.preferredMIMEType ?? "application/octet-stream" + } + + private func mimeType(text: String) -> String { + let pathExt = pathExtension(for: text) + return mimeType(pathExtension: pathExt) + } } private extension ChatManager { diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager.swift index 7b1d640f5..04bedcf74 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/ChatManager/ChatManager.swift @@ -68,7 +68,7 @@ public class ChatManager: ObservableObject, @unchecked Sendable { } } } - + public func delete(sessionId: String, vmId: UUID) { with(sessionId: sessionId) { $0.removeValue(forKey: vmId) } } diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/IntelligentContext/QLService+URLSessionCookieClient.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/IntelligentContext/QLService+URLSessionCookieClient.swift index 483a051d8..0c4e32f10 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/IntelligentContext/QLService+URLSessionCookieClient.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/IntelligentContext/QLService+URLSessionCookieClient.swift @@ -10,7 +10,7 @@ import Foundation extension QLService { final class URLSessionCookieClient: URLSessionClient { - public init() { + init() { super.init() session.configuration.httpCookieStorage = .init() HTTPCookieStorage.shared.cookies?.forEach { cookie in diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/ChatCell/ErrorCell.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/ChatCell/ErrorCell.swift index f84b2856c..4e4129673 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/ChatCell/ErrorCell.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/ChatCell/ErrorCell.swift @@ -12,7 +12,7 @@ import UIKit class ErrorCell: ChatBaseCell { let label = UILabel() - + override func prepareContentView(inside contentView: UIView) { super.prepareContentView(inside: contentView) contentView.addSubview(label) @@ -33,7 +33,7 @@ class ErrorCell: ChatBaseCell { height: bounds.height ) } - + override func configure(with viewModel: any ChatCellViewModel) { super.configure(with: viewModel) guard let vm = viewModel as? ErrorCellViewModel else { @@ -44,16 +44,16 @@ class ErrorCell: ChatBaseCell { } static func attributeText(for text: String) -> NSAttributedString { - return .init(string: text, attributes: [ + .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 diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.swift index eeea5641c..b3cf398ef 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/InputBox/InputBox.swift @@ -6,7 +6,7 @@ import UIKit class InputBox: UIView { weak var delegate: InputBoxDelegate? - public let viewModel = InputBoxViewModel() + let viewModel = InputBoxViewModel() var cancellables = Set() lazy var containerView = UIView().then { @@ -257,7 +257,7 @@ class InputBox: UIView { // MARK: - Public Methods - public func addImageAttachment(_ image: UIImage) { + func addImageAttachment(_ image: UIImage) { let attachment = ImageAttachment(image: image) performWithAnimation { [self] in @@ -266,7 +266,7 @@ class InputBox: UIView { } } - public func addFileAttachment(_ url: URL) throws { + func addFileAttachment(_ url: URL) throws { // check less then 15mb let fileSizeLimit: Int64 = 15 * 1024 * 1024 // 15 MB let fileAttributes = try FileManager.default.attributesOfItem(atPath: url.path) @@ -292,14 +292,14 @@ class InputBox: UIView { } } - public func addDocumentAttachment(_ documentAttachment: DocumentAttachment) { + func addDocumentAttachment(_ documentAttachment: DocumentAttachment) { performWithAnimation { [self] in viewModel.addDocumentAttachment(documentAttachment) layoutIfNeeded() } } - public var inputBoxData: InputBoxData { + var inputBoxData: InputBoxData { viewModel.prepareSendData() } } diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/IntelligentsButton/IntelligentsButton.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/IntelligentsButton/IntelligentsButton.swift index a633a5c12..9782528b3 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/IntelligentsButton/IntelligentsButton.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/IntelligentsButton/IntelligentsButton.swift @@ -26,7 +26,7 @@ public class IntelligentsButton: UIView { lazy var activityIndicator = UIActivityIndicatorView() - public weak var delegate: (any IntelligentsButtonDelegate)? = nil { + public weak var delegate: (any IntelligentsButtonDelegate)? { didSet { assert(Thread.isMainThread) } } diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/SupplementView/DarkActionButton.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/SupplementView/DarkActionButton.swift index 258e0cc9e..6e729f17d 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/SupplementView/DarkActionButton.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/View/SupplementView/DarkActionButton.swift @@ -20,7 +20,7 @@ class DarkActionButton: UIView { let titleLabel = UILabel() let iconView = UIImageView() - var action: (() -> Void)? = nil + var action: (() -> Void)? override init(frame: CGRect) { super.init(frame: frame)