mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 09:21:24 +08:00
chore: added mime-type in gql (#13414)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -9,9 +9,9 @@ import Intelligents
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
extension AFFiNEViewController: IntelligentsButtonDelegate {
|
extension AFFiNEViewController: IntelligentsButtonDelegate {
|
||||||
func onIntelligentsButtonTapped(_ button: IntelligentsButton) {
|
func onIntelligentsButtonTapped(_: IntelligentsButton) {
|
||||||
// if it shows up then we are ready to go
|
// if it shows up then we are ready to go
|
||||||
let controller = IntelligentsController()
|
let controller = IntelligentsController()
|
||||||
self.present(controller, animated: true)
|
present(controller, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+74
@@ -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"] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
-3
@@ -12,6 +12,7 @@ import EventSource
|
|||||||
import Foundation
|
import Foundation
|
||||||
import MarkdownParser
|
import MarkdownParser
|
||||||
import MarkdownView
|
import MarkdownView
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
private let loadingIndicator = " ●"
|
private let loadingIndicator = " ●"
|
||||||
|
|
||||||
@@ -175,16 +176,26 @@ private extension ChatManager {
|
|||||||
let attachmentFieldName = attachmentCount > 1 && attachmentCount != 0 ? "options.blobs" : "options.blob"
|
let attachmentFieldName = attachmentCount > 1 && attachmentCount != 0 ? "options.blobs" : "options.blob"
|
||||||
let uploadableAttachments: [GraphQLFile] = [
|
let uploadableAttachments: [GraphQLFile] = [
|
||||||
editorData.fileAttachments.map { file -> GraphQLFile in
|
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
|
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)
|
].flatMap(\.self)
|
||||||
assert(uploadableAttachments.allSatisfy { !($0.data?.isEmpty ?? true) })
|
assert(uploadableAttachments.allSatisfy { !($0.data?.isEmpty ?? true) })
|
||||||
guard let input = try? CreateChatMessageInput(
|
guard let input = try? CreateChatMessageInput(
|
||||||
attachments: [],
|
attachments: [],
|
||||||
blob: .none,
|
blob: attachmentCount == 1 ? "" : .none,
|
||||||
blobs: attachmentCount > 1 && attachmentCount != 0 ? .some([]) : .none,
|
blobs: attachmentCount > 1 && attachmentCount != 0 ? .some([]) : .none,
|
||||||
content: .some(contextSnippet.isEmpty ? editorData.text : "\(contextSnippet)\n\(editorData.text)"),
|
content: .some(contextSnippet.isEmpty ? editorData.text : "\(contextSnippet)\n\(editorData.text)"),
|
||||||
params: .some(AffineGraphQL.JSON(_jsonValue: messageParameters)),
|
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 {
|
private extension ChatManager {
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ public class ChatManager: ObservableObject, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func delete(sessionId: String, vmId: UUID) {
|
public func delete(sessionId: String, vmId: UUID) {
|
||||||
with(sessionId: sessionId) { $0.removeValue(forKey: vmId) }
|
with(sessionId: sessionId) { $0.removeValue(forKey: vmId) }
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import Foundation
|
|||||||
|
|
||||||
extension QLService {
|
extension QLService {
|
||||||
final class URLSessionCookieClient: URLSessionClient {
|
final class URLSessionCookieClient: URLSessionClient {
|
||||||
public init() {
|
init() {
|
||||||
super.init()
|
super.init()
|
||||||
session.configuration.httpCookieStorage = .init()
|
session.configuration.httpCookieStorage = .init()
|
||||||
HTTPCookieStorage.shared.cookies?.forEach { cookie in
|
HTTPCookieStorage.shared.cookies?.forEach { cookie in
|
||||||
|
|||||||
+5
-5
@@ -12,7 +12,7 @@ import UIKit
|
|||||||
|
|
||||||
class ErrorCell: ChatBaseCell {
|
class ErrorCell: ChatBaseCell {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
|
|
||||||
override func prepareContentView(inside contentView: UIView) {
|
override func prepareContentView(inside contentView: UIView) {
|
||||||
super.prepareContentView(inside: contentView)
|
super.prepareContentView(inside: contentView)
|
||||||
contentView.addSubview(label)
|
contentView.addSubview(label)
|
||||||
@@ -33,7 +33,7 @@ class ErrorCell: ChatBaseCell {
|
|||||||
height: bounds.height
|
height: bounds.height
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func configure(with viewModel: any ChatCellViewModel) {
|
override func configure(with viewModel: any ChatCellViewModel) {
|
||||||
super.configure(with: viewModel)
|
super.configure(with: viewModel)
|
||||||
guard let vm = viewModel as? ErrorCellViewModel else {
|
guard let vm = viewModel as? ErrorCellViewModel else {
|
||||||
@@ -44,16 +44,16 @@ class ErrorCell: ChatBaseCell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func attributeText(for text: String) -> NSAttributedString {
|
static func attributeText(for text: String) -> NSAttributedString {
|
||||||
return .init(string: text, attributes: [
|
.init(string: text, attributes: [
|
||||||
.font: UIFont.preferredFont(forTextStyle: .footnote),
|
.font: UIFont.preferredFont(forTextStyle: .footnote),
|
||||||
.foregroundColor: UIColor.affineTextSecondary,
|
.foregroundColor: UIColor.affineTextSecondary,
|
||||||
.paragraphStyle: NSMutableParagraphStyle().then {
|
.paragraphStyle: NSMutableParagraphStyle().then {
|
||||||
$0.lineBreakMode = .byWordWrapping
|
$0.lineBreakMode = .byWordWrapping
|
||||||
$0.alignment = .center
|
$0.alignment = .center
|
||||||
}
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
override class func heightForContent(
|
override class func heightForContent(
|
||||||
for viewModel: any ChatCellViewModel,
|
for viewModel: any ChatCellViewModel,
|
||||||
width: CGFloat
|
width: CGFloat
|
||||||
|
|||||||
+5
-5
@@ -6,7 +6,7 @@ import UIKit
|
|||||||
class InputBox: UIView {
|
class InputBox: UIView {
|
||||||
weak var delegate: InputBoxDelegate?
|
weak var delegate: InputBoxDelegate?
|
||||||
|
|
||||||
public let viewModel = InputBoxViewModel()
|
let viewModel = InputBoxViewModel()
|
||||||
var cancellables = Set<AnyCancellable>()
|
var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
lazy var containerView = UIView().then {
|
lazy var containerView = UIView().then {
|
||||||
@@ -257,7 +257,7 @@ class InputBox: UIView {
|
|||||||
|
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
|
|
||||||
public func addImageAttachment(_ image: UIImage) {
|
func addImageAttachment(_ image: UIImage) {
|
||||||
let attachment = ImageAttachment(image: image)
|
let attachment = ImageAttachment(image: image)
|
||||||
|
|
||||||
performWithAnimation { [self] in
|
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
|
// check less then 15mb
|
||||||
let fileSizeLimit: Int64 = 15 * 1024 * 1024 // 15 MB
|
let fileSizeLimit: Int64 = 15 * 1024 * 1024 // 15 MB
|
||||||
let fileAttributes = try FileManager.default.attributesOfItem(atPath: url.path)
|
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
|
performWithAnimation { [self] in
|
||||||
viewModel.addDocumentAttachment(documentAttachment)
|
viewModel.addDocumentAttachment(documentAttachment)
|
||||||
layoutIfNeeded()
|
layoutIfNeeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var inputBoxData: InputBoxData {
|
var inputBoxData: InputBoxData {
|
||||||
viewModel.prepareSendData()
|
viewModel.prepareSendData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ public class IntelligentsButton: UIView {
|
|||||||
|
|
||||||
lazy var activityIndicator = UIActivityIndicatorView()
|
lazy var activityIndicator = UIActivityIndicatorView()
|
||||||
|
|
||||||
public weak var delegate: (any IntelligentsButtonDelegate)? = nil {
|
public weak var delegate: (any IntelligentsButtonDelegate)? {
|
||||||
didSet { assert(Thread.isMainThread) }
|
didSet { assert(Thread.isMainThread) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ class DarkActionButton: UIView {
|
|||||||
|
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
let iconView = UIImageView()
|
let iconView = UIImageView()
|
||||||
var action: (() -> Void)? = nil
|
var action: (() -> Void)?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|||||||
Reference in New Issue
Block a user