diff --git a/packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj b/packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj index b400015826..48d6bc6fa3 100644 --- a/packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj +++ b/packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 77; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ diff --git a/packages/frontend/apps/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/packages/frontend/apps/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 05d5ff6717..d6e049f0c0 100644 --- a/packages/frontend/apps/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/packages/frontend/apps/ios/App/App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apollographql/apollo-ios", "state" : { - "revision" : "9aa748d6f0526a744d49d59a2383dc7fdf9d645b", - "version" : "1.18.0" + "revision" : "4d0845f9f2901ed657d680c874ffc68d12704cd4", + "version" : "1.19.0" + } + }, + { + "identity" : "chidorimenu", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Lakr233/ChidoriMenu", + "state" : { + "revision" : "ccd37f01cd4dcf4fb0889f263573d90d5c16e59b", + "version" : "2.4.3" } }, { @@ -27,6 +36,15 @@ "version" : "0.15.3" } }, + { + "identity" : "swift-cmark", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-cmark", + "state" : { + "revision" : "3ccff77b2dc5b96b77db3da0d68d28068593fa53", + "version" : "0.5.0" + } + }, { "identity" : "swift-collections", "kind" : "remoteSourceControl", diff --git a/packages/frontend/apps/ios/App/App/AffineViewController+AIButton.swift b/packages/frontend/apps/ios/App/App/AffineViewController+AIButton.swift index 6ca5db3ade..e34d2da57b 100644 --- a/packages/frontend/apps/ios/App/App/AffineViewController+AIButton.swift +++ b/packages/frontend/apps/ios/App/App/AffineViewController+AIButton.swift @@ -1,13 +1,13 @@ // -// File.swift +// AffineViewController+AIButton.swift // App // // Created by 秋星桥 on 2025/1/8. // -import UIKit -import Intelligents import ChidoriMenu +import Intelligents +import UIKit extension AFFiNEViewController: IntelligentsButtonDelegate, IntelligentsFocusApertureViewDelegate { func onIntelligentsButtonTapped(_ button: IntelligentsButton) { @@ -19,35 +19,35 @@ extension AFFiNEViewController: IntelligentsButtonDelegate, IntelligentsFocusApe button.beginProgress() let group = DispatchGroup() - + group.enter() webView.evaluateScript(.getCurrentServerBaseUrl) { result in self.baseUrl = result as? String print("[*] setting baseUrl: \(self.baseUrl ?? "")") group.leave() } - + group.enter() webView.evaluateScript(.getCurrentDocId) { result in self.documentID = result as? String print("[*] setting documentID: \(self.documentID ?? "")") group.leave() } - + group.enter() webView.evaluateScript(.getCurrentWorkspaceId) { result in self.workspaceID = result as? String print("[*] setting workspaceID: \(self.workspaceID ?? "")") group.leave() } - + group.enter() webView.evaluateScript(.getCurrentDocContentInMarkdown) { input in self.documentContent = input as? String print("[*] setting documentContent: \(self.documentContent?.count ?? 0) chars") group.leave() } - + DispatchQueue.global().asyncAfter(deadline: .now()) { group.wait() DispatchQueue.main.async { @@ -102,7 +102,7 @@ extension AFFiNEViewController: IntelligentsButtonDelegate, IntelligentsFocusApe presentIntoCurrentContext(withTargetController: controller) }) } - view.present(menu: .init(children: actions)) { controller in + view.present(menu: .init(children: actions)) { controller in controller.overrideUserInterfaceStyle = .dark } controllerDidPresent: { _ in } case .summary: diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Package.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Package.swift index 65ee44e0ec..c87014c634 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Package.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Package.swift @@ -14,7 +14,7 @@ let package = Package( .library(name: "AffineGraphQL", targets: ["AffineGraphQL"]), ], dependencies: [ - .package(url: "https://github.com/apollographql/apollo-ios", exact: "1.19.0"), + .package(url: "https://github.com/apollographql/apollo-ios", exact: "1.18.0"), ], targets: [ .target( diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/ClaimAudioTranscriptionMutation.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/ClaimAudioTranscriptionMutation.graphql.swift index fa4a6b74fb..dd424668a0 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/ClaimAudioTranscriptionMutation.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/ClaimAudioTranscriptionMutation.graphql.swift @@ -7,7 +7,7 @@ public class ClaimAudioTranscriptionMutation: GraphQLMutation { public static let operationName: String = "claimAudioTranscription" public static let operationDocument: ApolloAPI.OperationDocument = .init( definition: .init( - #"mutation claimAudioTranscription($jobId: String!) { claimAudioTranscription(jobId: $jobId) { __typename id status transcription { __typename speaker start end transcription } summary } }"# + #"mutation claimAudioTranscription($jobId: String!) { claimAudioTranscription(jobId: $jobId) { __typename id status title summary transcription { __typename speaker start end transcription } } }"# )) public var jobId: String @@ -41,14 +41,16 @@ public class ClaimAudioTranscriptionMutation: GraphQLMutation { .field("__typename", String.self), .field("id", AffineGraphQL.ID.self), .field("status", GraphQLEnum.self), - .field("transcription", [Transcription]?.self), + .field("title", String?.self), .field("summary", String?.self), + .field("transcription", [Transcription]?.self), ] } public var id: AffineGraphQL.ID { __data["id"] } public var status: GraphQLEnum { __data["status"] } - public var transcription: [Transcription]? { __data["transcription"] } + public var title: String? { __data["title"] } public var summary: String? { __data["summary"] } + public var transcription: [Transcription]? { __data["transcription"] } /// ClaimAudioTranscription.Transcription /// diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateAppConfigMutation.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateAppConfigMutation.graphql.swift new file mode 100644 index 0000000000..6f0ee757b8 --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateAppConfigMutation.graphql.swift @@ -0,0 +1,33 @@ +// @generated +// This file was automatically generated and should not be edited. + +@_exported import ApolloAPI + +public class UpdateAppConfigMutation: GraphQLMutation { + public static let operationName: String = "updateAppConfig" + public static let operationDocument: ApolloAPI.OperationDocument = .init( + definition: .init( + #"mutation updateAppConfig($updates: [UpdateAppConfigInput!]!) { updateAppConfig(updates: $updates) }"# + )) + + public var updates: [UpdateAppConfigInput] + + public init(updates: [UpdateAppConfigInput]) { + self.updates = updates + } + + public var __variables: Variables? { ["updates": updates] } + + 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("updateAppConfig", AffineGraphQL.JSONObject.self, arguments: ["updates": .variable("updates")]), + ] } + + /// update app configuration + public var updateAppConfig: AffineGraphQL.JSONObject { __data["updateAppConfig"] } + } +} diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateServerRuntimeConfigsMutation.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateServerRuntimeConfigsMutation.graphql.swift deleted file mode 100644 index 7cdbba6134..0000000000 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateServerRuntimeConfigsMutation.graphql.swift +++ /dev/null @@ -1,51 +0,0 @@ -// @generated -// This file was automatically generated and should not be edited. - -@_exported import ApolloAPI - -public class UpdateServerRuntimeConfigsMutation: GraphQLMutation { - public static let operationName: String = "updateServerRuntimeConfigs" - public static let operationDocument: ApolloAPI.OperationDocument = .init( - definition: .init( - #"mutation updateServerRuntimeConfigs($updates: JSONObject!) { updateRuntimeConfigs(updates: $updates) { __typename key value } }"# - )) - - public var updates: JSONObject - - public init(updates: JSONObject) { - self.updates = updates - } - - public var __variables: Variables? { ["updates": updates] } - - 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("updateRuntimeConfigs", [UpdateRuntimeConfig].self, arguments: ["updates": .variable("updates")]), - ] } - - /// update multiple server runtime configurable settings - public var updateRuntimeConfigs: [UpdateRuntimeConfig] { __data["updateRuntimeConfigs"] } - - /// UpdateRuntimeConfig - /// - /// Parent Type: `ServerRuntimeConfigType` - public struct UpdateRuntimeConfig: AffineGraphQL.SelectionSet { - public let __data: DataDict - public init(_dataDict: DataDict) { __data = _dataDict } - - public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.ServerRuntimeConfigType } - public static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("key", String.self), - .field("value", AffineGraphQL.JSON.self), - ] } - - public var key: String { __data["key"] } - public var value: AffineGraphQL.JSON { __data["value"] } - } - } -} diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateUserSettingsMutation.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateUserSettingsMutation.graphql.swift index 6015ede2c7..de1e12e375 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateUserSettingsMutation.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Mutations/UpdateUserSettingsMutation.graphql.swift @@ -7,12 +7,12 @@ public class UpdateUserSettingsMutation: GraphQLMutation { public static let operationName: String = "updateUserSettings" public static let operationDocument: ApolloAPI.OperationDocument = .init( definition: .init( - #"mutation updateUserSettings($input: UpdateSettingsInput!) { updateSettings(input: $input) }"# + #"mutation updateUserSettings($input: UpdateUserSettingsInput!) { updateSettings(input: $input) }"# )) - public var input: UpdateSettingsInput + public var input: UpdateUserSettingsInput - public init(input: UpdateSettingsInput) { + public init(input: UpdateUserSettingsInput) { self.input = input } diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/AdminServerConfigQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/AdminServerConfigQuery.graphql.swift index 0daee55ffc..17cf92d93a 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/AdminServerConfigQuery.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/AdminServerConfigQuery.graphql.swift @@ -42,7 +42,7 @@ public class AdminServerConfigQuery: GraphQLQuery { .field("type", GraphQLEnum.self), .field("initialized", Bool.self), .field("credentialsRequirement", CredentialsRequirement.self), - .field("availableUpgrade", AvailableUpgrade.self), + .field("availableUpgrade", AvailableUpgrade?.self), .field("availableUserFeatures", [GraphQLEnum].self), ] } @@ -61,7 +61,7 @@ public class AdminServerConfigQuery: GraphQLQuery { /// credentials requirement public var credentialsRequirement: CredentialsRequirement { __data["credentialsRequirement"] } /// fetch latest available upgradable release of server - public var availableUpgrade: AvailableUpgrade { __data["availableUpgrade"] } + public var availableUpgrade: AvailableUpgrade? { __data["availableUpgrade"] } /// Features for user that can be configured public var availableUserFeatures: [GraphQLEnum] { __data["availableUserFeatures"] } diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/AppConfigQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/AppConfigQuery.graphql.swift new file mode 100644 index 0000000000..c2aad1881a --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/AppConfigQuery.graphql.swift @@ -0,0 +1,27 @@ +// @generated +// This file was automatically generated and should not be edited. + +@_exported import ApolloAPI + +public class AppConfigQuery: GraphQLQuery { + public static let operationName: String = "appConfig" + public static let operationDocument: ApolloAPI.OperationDocument = .init( + definition: .init( + #"query appConfig { appConfig }"# + )) + + public init() {} + + 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("appConfig", AffineGraphQL.JSONObject.self), + ] } + + /// get the whole app configuration + public var appConfig: AffineGraphQL.JSONObject { __data["appConfig"] } + } +} diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetAudioTranscriptionQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetAudioTranscriptionQuery.graphql.swift index b6f8523959..663da3f97d 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetAudioTranscriptionQuery.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetAudioTranscriptionQuery.graphql.swift @@ -7,23 +7,27 @@ public class GetAudioTranscriptionQuery: GraphQLQuery { public static let operationName: String = "getAudioTranscription" public static let operationDocument: ApolloAPI.OperationDocument = .init( definition: .init( - #"query getAudioTranscription($workspaceId: String!, $jobId: String!) { currentUser { __typename copilot(workspaceId: $workspaceId) { __typename audioTranscription(jobId: $jobId) { __typename id status transcription { __typename speaker start end transcription } summary } } } }"# + #"query getAudioTranscription($workspaceId: String!, $jobId: String, $blobId: String) { currentUser { __typename copilot(workspaceId: $workspaceId) { __typename audioTranscription(jobId: $jobId, blobId: $blobId) { __typename id status title summary transcription { __typename speaker start end transcription } } } } }"# )) public var workspaceId: String - public var jobId: String + public var jobId: GraphQLNullable + public var blobId: GraphQLNullable public init( workspaceId: String, - jobId: String + jobId: GraphQLNullable, + blobId: GraphQLNullable ) { self.workspaceId = workspaceId self.jobId = jobId + self.blobId = blobId } public var __variables: Variables? { [ "workspaceId": workspaceId, - "jobId": jobId + "jobId": jobId, + "blobId": blobId ] } public struct Data: AffineGraphQL.SelectionSet { @@ -63,10 +67,13 @@ public class GetAudioTranscriptionQuery: GraphQLQuery { public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.Copilot } public static var __selections: [ApolloAPI.Selection] { [ .field("__typename", String.self), - .field("audioTranscription", [AudioTranscription].self, arguments: ["jobId": .variable("jobId")]), + .field("audioTranscription", AudioTranscription?.self, arguments: [ + "jobId": .variable("jobId"), + "blobId": .variable("blobId") + ]), ] } - public var audioTranscription: [AudioTranscription] { __data["audioTranscription"] } + public var audioTranscription: AudioTranscription? { __data["audioTranscription"] } /// CurrentUser.Copilot.AudioTranscription /// @@ -80,14 +87,16 @@ public class GetAudioTranscriptionQuery: GraphQLQuery { .field("__typename", String.self), .field("id", AffineGraphQL.ID.self), .field("status", GraphQLEnum.self), - .field("transcription", [Transcription]?.self), + .field("title", String?.self), .field("summary", String?.self), + .field("transcription", [Transcription]?.self), ] } public var id: AffineGraphQL.ID { __data["id"] } public var status: GraphQLEnum { __data["status"] } - public var transcription: [Transcription]? { __data["transcription"] } + public var title: String? { __data["title"] } public var summary: String? { __data["summary"] } + public var transcription: [Transcription]? { __data["transcription"] } /// CurrentUser.Copilot.AudioTranscription.Transcription /// diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetInviteInfoQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetInviteInfoQuery.graphql.swift index 13da7e872a..bffd6ad5f8 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetInviteInfoQuery.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetInviteInfoQuery.graphql.swift @@ -7,7 +7,7 @@ public class GetInviteInfoQuery: GraphQLQuery { public static let operationName: String = "getInviteInfo" public static let operationDocument: ApolloAPI.OperationDocument = .init( definition: .init( - #"query getInviteInfo($inviteId: String!) { getInviteInfo(inviteId: $inviteId) { __typename workspace { __typename id name avatar } user { __typename id name avatarUrl } } }"# + #"query getInviteInfo($inviteId: String!) { getInviteInfo(inviteId: $inviteId) { __typename workspace { __typename id name avatar } user { __typename id name avatarUrl } status invitee { __typename id name email avatarUrl } } }"# )) public var inviteId: String @@ -42,12 +42,18 @@ public class GetInviteInfoQuery: GraphQLQuery { .field("__typename", String.self), .field("workspace", Workspace.self), .field("user", User.self), + .field("status", GraphQLEnum?.self), + .field("invitee", Invitee.self), ] } /// Workspace information public var workspace: Workspace { __data["workspace"] } /// User information public var user: User { __data["user"] } + /// Invitation status in workspace + public var status: GraphQLEnum? { __data["status"] } + /// Invitee information + public var invitee: Invitee { __data["invitee"] } /// GetInviteInfo.Workspace /// @@ -73,23 +79,43 @@ public class GetInviteInfoQuery: GraphQLQuery { /// GetInviteInfo.User /// - /// Parent Type: `UserType` + /// Parent Type: `WorkspaceUserType` public struct User: AffineGraphQL.SelectionSet { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } - public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.UserType } + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.WorkspaceUserType } public static var __selections: [ApolloAPI.Selection] { [ .field("__typename", String.self), - .field("id", AffineGraphQL.ID.self), + .field("id", String.self), .field("name", String.self), .field("avatarUrl", String?.self), ] } - public var id: AffineGraphQL.ID { __data["id"] } - /// User name + public var id: String { __data["id"] } public var name: String { __data["name"] } - /// User avatar url + public var avatarUrl: String? { __data["avatarUrl"] } + } + + /// GetInviteInfo.Invitee + /// + /// Parent Type: `WorkspaceUserType` + public struct Invitee: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.WorkspaceUserType } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("id", String.self), + .field("name", String.self), + .field("email", String.self), + .field("avatarUrl", String?.self), + ] } + + public var id: String { __data["id"] } + public var name: String { __data["name"] } + public var email: String { __data["email"] } public var avatarUrl: String? { __data["avatarUrl"] } } } diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetServerRuntimeConfigQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetServerRuntimeConfigQuery.graphql.swift deleted file mode 100644 index e3926e49a6..0000000000 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetServerRuntimeConfigQuery.graphql.swift +++ /dev/null @@ -1,55 +0,0 @@ -// @generated -// This file was automatically generated and should not be edited. - -@_exported import ApolloAPI - -public class GetServerRuntimeConfigQuery: GraphQLQuery { - public static let operationName: String = "getServerRuntimeConfig" - public static let operationDocument: ApolloAPI.OperationDocument = .init( - definition: .init( - #"query getServerRuntimeConfig { serverRuntimeConfig { __typename id module key description value type updatedAt } }"# - )) - - public init() {} - - 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("serverRuntimeConfig", [ServerRuntimeConfig].self), - ] } - - /// get all server runtime configurable settings - public var serverRuntimeConfig: [ServerRuntimeConfig] { __data["serverRuntimeConfig"] } - - /// ServerRuntimeConfig - /// - /// Parent Type: `ServerRuntimeConfigType` - public struct ServerRuntimeConfig: AffineGraphQL.SelectionSet { - public let __data: DataDict - public init(_dataDict: DataDict) { __data = _dataDict } - - public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.ServerRuntimeConfigType } - public static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("id", String.self), - .field("module", String.self), - .field("key", String.self), - .field("description", String.self), - .field("value", AffineGraphQL.JSON.self), - .field("type", GraphQLEnum.self), - .field("updatedAt", AffineGraphQL.DateTime.self), - ] } - - public var id: String { __data["id"] } - public var module: String { __data["module"] } - public var key: String { __data["key"] } - public var description: String { __data["description"] } - public var value: AffineGraphQL.JSON { __data["value"] } - public var type: GraphQLEnum { __data["type"] } - public var updatedAt: AffineGraphQL.DateTime { __data["updatedAt"] } - } - } -} diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetServerServiceConfigsQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetServerServiceConfigsQuery.graphql.swift deleted file mode 100644 index 1b7dc44a48..0000000000 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetServerServiceConfigsQuery.graphql.swift +++ /dev/null @@ -1,44 +0,0 @@ -// @generated -// This file was automatically generated and should not be edited. - -@_exported import ApolloAPI - -public class GetServerServiceConfigsQuery: GraphQLQuery { - public static let operationName: String = "getServerServiceConfigs" - public static let operationDocument: ApolloAPI.OperationDocument = .init( - definition: .init( - #"query getServerServiceConfigs { serverServiceConfigs { __typename name config } }"# - )) - - public init() {} - - 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("serverServiceConfigs", [ServerServiceConfig].self), - ] } - - public var serverServiceConfigs: [ServerServiceConfig] { __data["serverServiceConfigs"] } - - /// ServerServiceConfig - /// - /// Parent Type: `ServerServiceConfig` - public struct ServerServiceConfig: AffineGraphQL.SelectionSet { - public let __data: DataDict - public init(_dataDict: DataDict) { __data = _dataDict } - - public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.ServerServiceConfig } - public static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("name", String.self), - .field("config", AffineGraphQL.JSONObject.self), - ] } - - public var name: String { __data["name"] } - public var config: AffineGraphQL.JSONObject { __data["config"] } - } - } -} diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetUserByEmailQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetUserByEmailQuery.graphql.swift index 18e6d630df..d62e610938 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetUserByEmailQuery.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetUserByEmailQuery.graphql.swift @@ -7,7 +7,7 @@ public class GetUserByEmailQuery: GraphQLQuery { public static let operationName: String = "getUserByEmail" public static let operationDocument: ApolloAPI.OperationDocument = .init( definition: .init( - #"query getUserByEmail($email: String!) { userByEmail(email: $email) { __typename id name email features hasPassword emailVerified avatarUrl quota { __typename humanReadable { __typename blobLimit historyPeriod memberLimit name storageQuota } } } }"# + #"query getUserByEmail($email: String!) { userByEmail(email: $email) { __typename id name email features hasPassword emailVerified avatarUrl disabled } }"# )) public var email: String @@ -47,7 +47,7 @@ public class GetUserByEmailQuery: GraphQLQuery { .field("hasPassword", Bool?.self), .field("emailVerified", Bool.self), .field("avatarUrl", String?.self), - .field("quota", Quota.self), + .field("disabled", Bool.self), ] } public var id: AffineGraphQL.ID { __data["id"] } @@ -63,47 +63,8 @@ public class GetUserByEmailQuery: GraphQLQuery { public var emailVerified: Bool { __data["emailVerified"] } /// User avatar url public var avatarUrl: String? { __data["avatarUrl"] } - public var quota: Quota { __data["quota"] } - - /// UserByEmail.Quota - /// - /// Parent Type: `UserQuotaType` - public struct Quota: AffineGraphQL.SelectionSet { - public let __data: DataDict - public init(_dataDict: DataDict) { __data = _dataDict } - - public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.UserQuotaType } - public static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("humanReadable", HumanReadable.self), - ] } - - public var humanReadable: HumanReadable { __data["humanReadable"] } - - /// UserByEmail.Quota.HumanReadable - /// - /// Parent Type: `UserQuotaHumanReadableType` - public struct HumanReadable: AffineGraphQL.SelectionSet { - public let __data: DataDict - public init(_dataDict: DataDict) { __data = _dataDict } - - public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.UserQuotaHumanReadableType } - public static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("blobLimit", String.self), - .field("historyPeriod", String.self), - .field("memberLimit", String.self), - .field("name", String.self), - .field("storageQuota", String.self), - ] } - - public var blobLimit: String { __data["blobLimit"] } - public var historyPeriod: String { __data["historyPeriod"] } - public var memberLimit: String { __data["memberLimit"] } - public var name: String { __data["name"] } - public var storageQuota: String { __data["storageQuota"] } - } - } + /// User is disabled + public var disabled: Bool { __data["disabled"] } } } } diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetUserSettingsQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetUserSettingsQuery.graphql.swift index d26780348f..ac73d6aef6 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetUserSettingsQuery.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/GetUserSettingsQuery.graphql.swift @@ -42,12 +42,12 @@ public class GetUserSettingsQuery: GraphQLQuery { /// CurrentUser.Settings /// - /// Parent Type: `SettingsType` + /// Parent Type: `UserSettingsType` public struct Settings: AffineGraphQL.SelectionSet { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } - public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.SettingsType } + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.UserSettingsType } public static var __selections: [ApolloAPI.Selection] { [ .field("__typename", String.self), .field("receiveInvitationEmail", Bool.self), diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchContextQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchContextQuery.graphql.swift index 7fc9b4d924..396b7a487d 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchContextQuery.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchContextQuery.graphql.swift @@ -7,27 +7,31 @@ public class MatchContextQuery: GraphQLQuery { public static let operationName: String = "matchContext" public static let operationDocument: ApolloAPI.OperationDocument = .init( definition: .init( - #"query matchContext($contextId: String!, $content: String!, $limit: SafeInt) { currentUser { __typename copilot { __typename contexts(contextId: $contextId) { __typename matchContext(content: $content, limit: $limit) { __typename fileId chunk content distance } } } } }"# + #"query matchContext($contextId: String!, $content: String!, $limit: SafeInt, $threshold: Float) { currentUser { __typename copilot { __typename contexts(contextId: $contextId) { __typename matchFiles(content: $content, limit: $limit, threshold: $threshold) { __typename fileId chunk content distance } matchWorkspaceDocs(content: $content, limit: $limit, threshold: $threshold) { __typename docId chunk content distance } } } } }"# )) public var contextId: String public var content: String public var limit: GraphQLNullable + public var threshold: GraphQLNullable public init( contextId: String, content: String, - limit: GraphQLNullable + limit: GraphQLNullable, + threshold: GraphQLNullable ) { self.contextId = contextId self.content = content self.limit = limit + self.threshold = threshold } public var __variables: Variables? { [ "contextId": contextId, "content": content, - "limit": limit + "limit": limit, + "threshold": threshold ] } public struct Data: AffineGraphQL.SelectionSet { @@ -83,19 +87,27 @@ public class MatchContextQuery: GraphQLQuery { public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.CopilotContext } public static var __selections: [ApolloAPI.Selection] { [ .field("__typename", String.self), - .field("matchContext", [MatchContext].self, arguments: [ + .field("matchFiles", [MatchFile].self, arguments: [ "content": .variable("content"), - "limit": .variable("limit") + "limit": .variable("limit"), + "threshold": .variable("threshold") + ]), + .field("matchWorkspaceDocs", [MatchWorkspaceDoc].self, arguments: [ + "content": .variable("content"), + "limit": .variable("limit"), + "threshold": .variable("threshold") ]), ] } - /// match file context - public var matchContext: [MatchContext] { __data["matchContext"] } + /// match file in context + public var matchFiles: [MatchFile] { __data["matchFiles"] } + /// match workspace docs + public var matchWorkspaceDocs: [MatchWorkspaceDoc] { __data["matchWorkspaceDocs"] } - /// CurrentUser.Copilot.Context.MatchContext + /// CurrentUser.Copilot.Context.MatchFile /// /// Parent Type: `ContextMatchedFileChunk` - public struct MatchContext: AffineGraphQL.SelectionSet { + public struct MatchFile: AffineGraphQL.SelectionSet { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } @@ -113,6 +125,28 @@ public class MatchContextQuery: GraphQLQuery { public var content: String { __data["content"] } public var distance: Double? { __data["distance"] } } + + /// CurrentUser.Copilot.Context.MatchWorkspaceDoc + /// + /// Parent Type: `ContextMatchedDocChunk` + public struct MatchWorkspaceDoc: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.ContextMatchedDocChunk } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("docId", String.self), + .field("chunk", AffineGraphQL.SafeInt.self), + .field("content", String.self), + .field("distance", Double?.self), + ] } + + public var docId: String { __data["docId"] } + public var chunk: AffineGraphQL.SafeInt { __data["chunk"] } + public var content: String { __data["content"] } + public var distance: Double? { __data["distance"] } + } } } } diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchFilesQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchFilesQuery.graphql.swift new file mode 100644 index 0000000000..767f84c2bf --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchFilesQuery.graphql.swift @@ -0,0 +1,120 @@ +// @generated +// This file was automatically generated and should not be edited. + +@_exported import ApolloAPI + +public class MatchFilesQuery: GraphQLQuery { + public static let operationName: String = "matchFiles" + public static let operationDocument: ApolloAPI.OperationDocument = .init( + definition: .init( + #"query matchFiles($contextId: String!, $content: String!, $limit: SafeInt) { currentUser { __typename copilot { __typename contexts(contextId: $contextId) { __typename matchFiles(content: $content, limit: $limit) { __typename fileId chunk content distance } } } } }"# + )) + + public var contextId: String + public var content: String + public var limit: GraphQLNullable + + public init( + contextId: String, + content: String, + limit: GraphQLNullable + ) { + self.contextId = contextId + self.content = content + self.limit = limit + } + + public var __variables: Variables? { [ + "contextId": contextId, + "content": content, + "limit": limit + ] } + + 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("currentUser", CurrentUser?.self), + ] } + + /// Get current user + public var currentUser: CurrentUser? { __data["currentUser"] } + + /// CurrentUser + /// + /// Parent Type: `UserType` + public struct CurrentUser: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.UserType } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("copilot", Copilot.self), + ] } + + public var copilot: Copilot { __data["copilot"] } + + /// CurrentUser.Copilot + /// + /// Parent Type: `Copilot` + public struct Copilot: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.Copilot } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("contexts", [Context].self, arguments: ["contextId": .variable("contextId")]), + ] } + + /// Get the context list of a session + public var contexts: [Context] { __data["contexts"] } + + /// CurrentUser.Copilot.Context + /// + /// Parent Type: `CopilotContext` + public struct Context: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.CopilotContext } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("matchFiles", [MatchFile].self, arguments: [ + "content": .variable("content"), + "limit": .variable("limit") + ]), + ] } + + /// match file in context + public var matchFiles: [MatchFile] { __data["matchFiles"] } + + /// CurrentUser.Copilot.Context.MatchFile + /// + /// Parent Type: `ContextMatchedFileChunk` + public struct MatchFile: AffineGraphQL.SelectionSet { + public let __data: DataDict + public init(_dataDict: DataDict) { __data = _dataDict } + + public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.ContextMatchedFileChunk } + public static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("fileId", String.self), + .field("chunk", AffineGraphQL.SafeInt.self), + .field("content", String.self), + .field("distance", Double?.self), + ] } + + public var fileId: String { __data["fileId"] } + public var chunk: AffineGraphQL.SafeInt { __data["chunk"] } + public var content: String { __data["content"] } + public var distance: Double? { __data["distance"] } + } + } + } + } + } +} diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchWorkspaceContextQuery.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchWorkspaceDocsQuery.graphql.swift similarity index 82% rename from packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchWorkspaceContextQuery.graphql.swift rename to packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchWorkspaceDocsQuery.graphql.swift index 594cda71c2..07c4da7206 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchWorkspaceContextQuery.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Operations/Queries/MatchWorkspaceDocsQuery.graphql.swift @@ -3,11 +3,11 @@ @_exported import ApolloAPI -public class MatchWorkspaceContextQuery: GraphQLQuery { - public static let operationName: String = "matchWorkspaceContext" +public class MatchWorkspaceDocsQuery: GraphQLQuery { + public static let operationName: String = "matchWorkspaceDocs" public static let operationDocument: ApolloAPI.OperationDocument = .init( definition: .init( - #"query matchWorkspaceContext($contextId: String!, $content: String!, $limit: SafeInt) { currentUser { __typename copilot { __typename contexts(contextId: $contextId) { __typename matchWorkspaceContext(content: $content, limit: $limit) { __typename docId chunk content distance } } } } }"# + #"query matchWorkspaceDocs($contextId: String!, $content: String!, $limit: SafeInt) { currentUser { __typename copilot { __typename contexts(contextId: $contextId) { __typename matchWorkspaceDocs(content: $content, limit: $limit) { __typename docId chunk content distance } } } } }"# )) public var contextId: String @@ -83,19 +83,19 @@ public class MatchWorkspaceContextQuery: GraphQLQuery { public static var __parentType: any ApolloAPI.ParentType { AffineGraphQL.Objects.CopilotContext } public static var __selections: [ApolloAPI.Selection] { [ .field("__typename", String.self), - .field("matchWorkspaceContext", MatchWorkspaceContext.self, arguments: [ + .field("matchWorkspaceDocs", [MatchWorkspaceDoc].self, arguments: [ "content": .variable("content"), "limit": .variable("limit") ]), ] } - /// match workspace doc content - public var matchWorkspaceContext: MatchWorkspaceContext { __data["matchWorkspaceContext"] } + /// match workspace docs + public var matchWorkspaceDocs: [MatchWorkspaceDoc] { __data["matchWorkspaceDocs"] } - /// CurrentUser.Copilot.Context.MatchWorkspaceContext + /// CurrentUser.Copilot.Context.MatchWorkspaceDoc /// /// Parent Type: `ContextMatchedDocChunk` - public struct MatchWorkspaceContext: AffineGraphQL.SelectionSet { + public struct MatchWorkspaceDoc: AffineGraphQL.SelectionSet { public let __data: DataDict public init(_dataDict: DataDict) { __data = _dataDict } diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/CustomScalars/JSON.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/CustomScalars/JSON.swift index d987fea99f..44c4ff2ebe 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/CustomScalars/JSON.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/CustomScalars/JSON.swift @@ -7,5 +7,4 @@ import ApolloAPI -/// The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). -public typealias JSON = String +public typealias JSON = JSONObject diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/CustomScalars/JSONObject.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/CustomScalars/JSONObject.swift index b89e7a619a..bb6e37251e 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/CustomScalars/JSONObject.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/CustomScalars/JSONObject.swift @@ -7,5 +7,36 @@ import ApolloAPI -/// The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). -public typealias JSONObject = String +public typealias JSONObject = CustomJSON + +public enum CustomJSON: CustomScalarType, Hashable { + case dictionary([String: AnyHashable]) + case array([AnyHashable]) + + public init(_jsonValue value: JSONValue) throws { + if let dict = value as? [String: AnyHashable] { + self = .dictionary(dict) + } else if let array = value as? [AnyHashable] { + self = .array(array) + } else { + throw JSONDecodingError.couldNotConvert(value: value, to: CustomJSON.self) + } + } + + public var _jsonValue: JSONValue { + switch self { + case let .dictionary(json as AnyHashable), + let .array(json as AnyHashable): + json + } + } + + public static func == (lhs: CustomJSON, rhs: CustomJSON) -> Bool { + lhs._jsonValue == rhs._jsonValue + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(_jsonValue) + } +} + diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Enums/RuntimeConfigType.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Enums/RuntimeConfigType.graphql.swift deleted file mode 100644 index da834a184f..0000000000 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Enums/RuntimeConfigType.graphql.swift +++ /dev/null @@ -1,12 +0,0 @@ -// @generated -// This file was automatically generated and should not be edited. - -import ApolloAPI - -public enum RuntimeConfigType: String, EnumType { - case array = "Array" - case boolean = "Boolean" - case number = "Number" - case object = "Object" - case string = "String" -} diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateAppConfigInput.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateAppConfigInput.graphql.swift new file mode 100644 index 0000000000..d5eca85c71 --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateAppConfigInput.graphql.swift @@ -0,0 +1,39 @@ +// @generated +// This file was automatically generated and should not be edited. + +import ApolloAPI + +public struct UpdateAppConfigInput: InputObject { + public private(set) var __data: InputDict + + public init(_ data: InputDict) { + __data = data + } + + public init( + key: String, + module: String, + value: JSON + ) { + __data = InputDict([ + "key": key, + "module": module, + "value": value + ]) + } + + public var key: String { + get { __data["key"] } + set { __data["key"] = newValue } + } + + public var module: String { + get { __data["module"] } + set { __data["module"] = newValue } + } + + public var value: JSON { + get { __data["value"] } + set { __data["value"] = newValue } + } +} diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateSettingsInput.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateUserSettingsInput.graphql.swift similarity index 94% rename from packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateSettingsInput.graphql.swift rename to packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateUserSettingsInput.graphql.swift index 7d314f6f8c..13c9bdacd8 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateSettingsInput.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/InputObjects/UpdateUserSettingsInput.graphql.swift @@ -3,7 +3,7 @@ import ApolloAPI -public struct UpdateSettingsInput: InputObject { +public struct UpdateUserSettingsInput: InputObject { public private(set) var __data: InputDict public init(_ data: InputDict) { diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/ServerRuntimeConfigType.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/ServerRuntimeConfigType.graphql.swift deleted file mode 100644 index 900649a81c..0000000000 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/ServerRuntimeConfigType.graphql.swift +++ /dev/null @@ -1,12 +0,0 @@ -// @generated -// This file was automatically generated and should not be edited. - -import ApolloAPI - -public extension Objects { - static let ServerRuntimeConfigType = ApolloAPI.Object( - typename: "ServerRuntimeConfigType", - implementedInterfaces: [], - keyFields: nil - ) -} \ No newline at end of file diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/ServerServiceConfig.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/ServerServiceConfig.graphql.swift deleted file mode 100644 index 4740abe46e..0000000000 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/ServerServiceConfig.graphql.swift +++ /dev/null @@ -1,12 +0,0 @@ -// @generated -// This file was automatically generated and should not be edited. - -import ApolloAPI - -public extension Objects { - static let ServerServiceConfig = ApolloAPI.Object( - typename: "ServerServiceConfig", - implementedInterfaces: [], - keyFields: nil - ) -} \ No newline at end of file diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/SettingsType.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/UserSettingsType.graphql.swift similarity index 68% rename from packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/SettingsType.graphql.swift rename to packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/UserSettingsType.graphql.swift index b3d2036f38..49d3017d90 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/SettingsType.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/Objects/UserSettingsType.graphql.swift @@ -4,8 +4,8 @@ import ApolloAPI public extension Objects { - static let SettingsType = ApolloAPI.Object( - typename: "SettingsType", + static let UserSettingsType = ApolloAPI.Object( + typename: "UserSettingsType", implementedInterfaces: [], keyFields: nil ) diff --git a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/SchemaMetadata.graphql.swift b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/SchemaMetadata.graphql.swift index a2c7aba3a7..2bdd14acfb 100644 --- a/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/SchemaMetadata.graphql.swift +++ b/packages/frontend/apps/ios/App/Packages/AffineGraphQL/Sources/Schema/SchemaMetadata.graphql.swift @@ -65,9 +65,6 @@ public enum SchemaMetadata: ApolloAPI.SchemaMetadata { case "ReleaseVersionType": return AffineGraphQL.Objects.ReleaseVersionType case "RemoveAvatar": return AffineGraphQL.Objects.RemoveAvatar case "ServerConfigType": return AffineGraphQL.Objects.ServerConfigType - case "ServerRuntimeConfigType": return AffineGraphQL.Objects.ServerRuntimeConfigType - case "ServerServiceConfig": return AffineGraphQL.Objects.ServerServiceConfig - case "SettingsType": return AffineGraphQL.Objects.SettingsType case "SubscriptionPrice": return AffineGraphQL.Objects.SubscriptionPrice case "SubscriptionType": return AffineGraphQL.Objects.SubscriptionType case "TranscriptionItemType": return AffineGraphQL.Objects.TranscriptionItemType @@ -76,6 +73,7 @@ public enum SchemaMetadata: ApolloAPI.SchemaMetadata { case "UserQuotaHumanReadableType": return AffineGraphQL.Objects.UserQuotaHumanReadableType case "UserQuotaType": return AffineGraphQL.Objects.UserQuotaType case "UserQuotaUsageType": return AffineGraphQL.Objects.UserQuotaUsageType + case "UserSettingsType": return AffineGraphQL.Objects.UserSettingsType case "UserType": return AffineGraphQL.Objects.UserType case "WorkspacePageMeta": return AffineGraphQL.Objects.WorkspacePageMeta case "WorkspacePermissions": return AffineGraphQL.Objects.WorkspacePermissions diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Package.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Package.swift index 09d7df37e8..662afd82fe 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Package.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Package.swift @@ -16,7 +16,7 @@ let package = Package( dependencies: [ .package(path: "../AffineGraphQL"), .package(path: "../MarkdownView"), - .package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.19.0"), + .package(url: "https://github.com/apollographql/apollo-ios.git", from: "1.18.0"), .package(url: "https://github.com/LaunchDarkly/swift-eventsource.git", from: "3.3.0"), .package(url: "https://github.com/apple/swift-collections", from: "1.1.4"), .package(url: "https://github.com/Lakr233/ChidoriMenu", from: "2.4.3"), diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Backend/Constant/UnableTo.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Backend/Constant/UnableTo.swift new file mode 100644 index 0000000000..bb7c809436 --- /dev/null +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Backend/Constant/UnableTo.swift @@ -0,0 +1,45 @@ +// +// UnableTo.swift +// Intelligents +// +// Created by 秋星桥 on 4/1/25. +// + +import Foundation + +private let domain = "Intelligents" + +enum UnableTo { + static let identifyDocumentOrWorkspace = + NSError( + domain: domain, + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Unable to identify the document or workspace"] + ) + + static let createSession = NSError( + domain: domain, + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Unable to create a session"] + ) + + static let createMessage = NSError( + domain: domain, + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Unable to create a message"] + ) + + static let compressImage = NSError( + domain: domain, + code: -1, + userInfo: [ + NSLocalizedDescriptionKey: "Failed to compress image data", + ] + ) + + static let clearHistory = NSError( + domain: domain, + code: -1, + userInfo: [NSLocalizedDescriptionKey: "Unable to clear history"] + ) +} diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/InputEditView+Camera.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/InputEditView+Camera.swift index eff4e8002c..54babfff8c 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/InputEditView+Camera.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/InputEditView+Camera.swift @@ -25,9 +25,7 @@ extension InputEditView: UIImagePickerControllerDelegate, UINavigationController private func processJPEGImageData(_ image: UIImage) throws -> Data? { guard let data = image.jpegData(compressionQuality: 0.75) else { - throw NSError(domain: "", code: -1, userInfo: [ - NSLocalizedDescriptionKey: "Failed to compress image data", - ]) + throw UnableTo.compressImage } return data } diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/InputEditView.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/InputEditView.swift index d4c720a927..27fe21a14c 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/InputEditView.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/InputEditView.swift @@ -8,7 +8,7 @@ import Combine import UIKit -class InputEditView: UIView, UITextViewDelegate { +class InputEditView: UIView { let mainStack = UIStackView() let attachmentsEditor = AttachmentBannerView() let textEditor = PlainTextEditView() @@ -22,6 +22,8 @@ class InputEditView: UIView, UITextViewDelegate { } } + var submitAction: (() -> Void) = {} + init() { super.init(frame: .zero) @@ -38,7 +40,6 @@ class InputEditView: UIView, UITextViewDelegate { mainStack.bottomAnchor.constraint(equalTo: bottomAnchor), ].forEach { $0.isActive = true } - textEditor.delegate = self textEditor.heightAnchor.constraint(greaterThanOrEqualToConstant: 64).isActive = true [ @@ -89,6 +90,15 @@ class InputEditView: UIView, UITextViewDelegate { .store(in: &viewModel.cancellables) updateValues() + + textEditor.textDidChange = { [weak self] text in + self?.viewModel.text = text + self?.updatePlaceholderVisibility() + } + + textEditor.textDidReturn = { [weak self] in + self?.submitAction() + } } @available(*, unavailable) @@ -96,18 +106,6 @@ class InputEditView: UIView, UITextViewDelegate { fatalError() } - func textViewDidChange(_ textView: UITextView) { - viewModel.text = textView.text - } - - func textViewDidBeginEditing(_: UITextView) { - updatePlaceholderVisibility() - } - - func textViewDidEndEditing(_: UITextView) { - updatePlaceholderVisibility() - } - func updatePlaceholderVisibility() { let visible = viewModel.text.isEmpty && !textEditor.isFirstResponder UIView.animate(withDuration: 0.25) { diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/PlainTextEditView.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/PlainTextEditView.swift index c362f52dfa..181161a3b1 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/PlainTextEditView.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/InputEditView/PlainTextEditView.swift @@ -8,6 +8,9 @@ import UIKit class PlainTextEditView: UITextView, UITextViewDelegate { + var textDidChange: ((String) -> Void) = { _ in } + var textDidReturn: (() -> Void) = {} + init() { super.init(frame: .zero, textContainer: nil) @@ -34,4 +37,33 @@ class PlainTextEditView: UITextView, UITextViewDelegate { required init?(coder _: NSCoder) { fatalError() } + + func textViewDidChange(_ textView: UITextView) { + textDidChange(textView.text) + } + + func textViewDidBeginEditing(_ textView: UITextView) { + textDidChange(textView.text) + } + + func textViewDidEndEditing(_ textView: UITextView) { + textDidChange(textView.text) + } + + func textView(_: UITextView, editMenuForTextIn _: NSRange, suggestedActions: [UIMenuElement]) -> UIMenu? { + .init(children: suggestedActions + [ + UIAction(title: "Insert Newline") { [weak self] _ in + self?.insertText("\n") + }, + ]) + } + + func textView(_: UITextView, shouldChangeTextIn _: NSRange, replacementText text: String) -> Bool { + if text == "\n" { + textDidReturn() + return false + } else { + return true + } + } } diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController+Chat.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController+Chat.swift index 9982f14c90..c61f548568 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController+Chat.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController+Chat.swift @@ -15,7 +15,11 @@ extension IntelligentsChatController { beginProgress() chat_createSession { session in self.sessionID = session ?? "" - self.endProgress() + self.chat_retrieveHistories { + self.dispatchToMain { + self.endProgress() + } + } } onFailure: { error in self.presentError(error) { if let nav = self.navigationController { @@ -38,6 +42,85 @@ extension IntelligentsChatController { self.endProgress() } } + + func chat_clearHistory() { + beginProgress() + Intelligents.qlClient.perform(mutation: CleanupCopilotSessionMutation(input: .init( + docId: metadata[.documentID] ?? "", + sessionIds: [sessionID], + workspaceId: metadata[.workspaceID] ?? "" + ))) { result in + self.dispatchToMain { + self.endProgress() + if case let .success(value) = result, + let sessions = value.data?.cleanupCopilotSession, + sessions.contains(self.sessionID) + { + self.simpleChatContents.removeAll() + return + } + self.presentError(UnableTo.clearHistory) + } + } + } + + func chat_retrieveHistories(_ completion: @escaping () -> Void) { + Intelligents.qlClient.fetch(query: GetCopilotHistoriesQuery( + workspaceId: metadata[.workspaceID] ?? "", + docId: .init(stringLiteral: metadata[.documentID] ?? ""), + options: .some(.init( + action: false, + fork: false, + limit: .init(nilLiteral: ()), + messageOrder: .some(.case(.asc)), + sessionId: .init(stringLiteral: sessionID), + sessionOrder: .some(.case(.desc)), + skip: .init(nilLiteral: ()), + withPrompt: .init(booleanLiteral: false) + )) + )) { [weak self] result in + if let self, + case let .success(value) = result, + let object = value.data, + let currentUser = object.__data._data["currentUser"] as? DataDict, + let copilot = currentUser._data["copilot"] as? DataDict, + let histories = copilot._data["histories"] as? [DataDict], + let mostRecent = histories.first, + let messages = mostRecent._data["messages"] as? [DataDict], + !messages.isEmpty + { + print("[*] retrieved \(messages.count) messages") + tableView.scrollToBottomOnNextUpdate = true + tableView.alpha = 0 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.8) { + self.tableView.alpha = 1 + } + } + for message in messages { + guard let role = message._data["role"] as? String, + let content = message._data["content"] as? String + // TODO: ATTACHMENTS + else { continue } + switch role { + case "assistant": + simpleChatContents.updateValue( + .assistant(document: content), + forKey: UUID() + ) + case "user": + simpleChatContents.updateValue( + .user(document: content), + forKey: UUID() + ) + default: + assertionFailure() + } + } + } + completion() + } + } } private extension IntelligentsChatController { @@ -51,6 +134,7 @@ private extension IntelligentsChatController { func beginProgress() { dispatchToMain { [self] in + header.isUserInteractionEnabled = false inputBox.isUserInteractionEnabled = false progressView.isHidden = false progressView.alpha = 0 @@ -67,6 +151,7 @@ private extension IntelligentsChatController { UIView.animate(withDuration: 0.3) { self.inputBox.editor.alpha = 1 self.progressView.alpha = 0 + self.header.isUserInteractionEnabled = true } completion: { _ in self.inputBox.isUserInteractionEnabled = true self.progressView.stopAnimating() @@ -86,9 +171,44 @@ private extension IntelligentsChatController { } func chat_createSession( + forceCreateNewSession: Bool = false, onSuccess: @escaping (String?) -> Void, onFailure: @escaping (Error) -> Void ) { + if !forceCreateNewSession, + let doc = metadata[.documentID], + !doc.isEmpty + { + Intelligents.qlClient.fetch(query: GetCopilotSessionsQuery( + workspaceId: .init(stringLiteral: metadata[.workspaceID] ?? ""), + docId: .init(stringLiteral: doc), + options: .some(QueryChatSessionsInput(InputDict([ + "action": false, + ]))) + )) { result in + switch result { + case let .success(value): + if let result = value.data, + let currentUser = result.__data._data["currentUser"] as? DataDict, + let copilot = currentUser._data["copilot"] as? DataDict, + let sessions = copilot._data["sessions"] as? [DataDict], + let mostRecent = sessions.last, + let sessionID = mostRecent._data["id"] as? String + { + print("[*] using existing session", sessionID) + self.dispatchToMain { onSuccess(sessionID) } + return + } + self.chat_createSession( + forceCreateNewSession: true, + onSuccess: onSuccess, + onFailure: onFailure + ) + case let .failure(error): + self.dispatchToMain { onFailure(error) } + } + } + } Intelligents.qlClient.perform( mutation: CreateCopilotSessionMutation(options: .init( docId: metadata[.documentID] ?? "", @@ -103,13 +223,7 @@ private extension IntelligentsChatController { self.dispatchToMain { onSuccess(session) } } else { self.dispatchToMain { - onFailure( - NSError( - domain: "Intelligents", - code: 0, - userInfo: [NSLocalizedDescriptionKey: "No session created"] - ) - ) + onFailure(UnableTo.createSession) } } case let .failure(error): @@ -122,11 +236,15 @@ private extension IntelligentsChatController { let text = viewModel.text // let images = viewModel.attachments + let assistantContentID = UUID() dispatchToMain { let content = ChatContent.user(document: text) - let key = UUID() - self.simpleChatContents.updateValue(content, forKey: key) - self.tableView.scrollLastCellToTop() + self.simpleChatContents.updateValue(content, forKey: .init()) + self.simpleChatContents.updateValue( + .assistant(document: "..."), + forKey: assistantContentID + ) + self.tableView.scrollToBottomOnNextUpdate = true } let sem = DispatchSemaphore(value: 0) @@ -134,6 +252,12 @@ private extension IntelligentsChatController { Intelligents.qlClient.perform( mutation: CreateCopilotMessageMutation(options: .init( content: .init(stringLiteral: text), + params: .some(.dictionary([ + "docs": [ + "docId": metadata[.documentID] ?? "", + "docContent": metadata[.content] ?? "", + ], + ])), sessionId: sessionID )), queue: .global() @@ -143,13 +267,13 @@ private extension IntelligentsChatController { case let .success(value): if let messageID = value.data?.createCopilotMessage { print("[*] messageID", messageID) - self.chat_processWithMessageID(sessionID: sessionID, messageID: messageID) + self.chat_processWithMessageID( + sessionID: sessionID, + messageID: messageID, + cellID: assistantContentID + ) } else { - self.chat_onError(NSError( - domain: "Intelligents", - code: 0, - userInfo: [NSLocalizedDescriptionKey: "No message created"] - )) + self.chat_onError(UnableTo.createMessage) } case let .failure(error): self.chat_onError(error) @@ -159,7 +283,7 @@ private extension IntelligentsChatController { sem.wait() } - func chat_processWithMessageID(sessionID: String, messageID: String) { + func chat_processWithMessageID(sessionID: String, messageID: String, cellID: UUID) { let url = Constant.affineUpstreamURL .appendingPathComponent("api") .appendingPathComponent("copilot") @@ -171,19 +295,14 @@ private extension IntelligentsChatController { guard let url = comps?.url else { assertionFailure() - chat_onError(NSError( - domain: "Intelligents", - code: 0, - userInfo: [NSLocalizedDescriptionKey: "No message created"] - )) + chat_onError(UnableTo.createMessage) return } - let contentIdentifier = UUID() dispatchToMain { self.simpleChatContents.updateValue( .assistant(document: "..."), - forKey: contentIdentifier + forKey: cellID ) } @@ -207,7 +326,7 @@ private extension IntelligentsChatController { self.dispatchToMain { document += message.data let content = ChatContent.assistant(document: document) - self.simpleChatContents.updateValue(content, forKey: contentIdentifier) + self.simpleChatContents.updateValue(content, forKey: cellID) } } let eventSource = EventSource(config: .init(handler: eventHandler, url: url)) diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController+Header.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController+Header.swift index b6004f60a7..203ac3da78 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController+Header.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController+Header.swift @@ -28,6 +28,22 @@ extension IntelligentsChatController { fatalError() } + override var isUserInteractionEnabled: Bool { + didSet { updateAvailabilityStyles() } + } + + func updateAvailabilityStyles() { + if isUserInteractionEnabled { + backButton.isEnabled = true + dropMenu.isEnabled = true + moreMenu.isEnabled = true + } else { + backButton.isEnabled = false + dropMenu.isEnabled = false + moreMenu.isEnabled = false + } + } + @objc func navigateActionBack() { parentViewController?.dismissInContext() } diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController.swift index c707e7e645..4a991cbfa9 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/IntelligentsChatController.swift @@ -30,9 +30,7 @@ public class IntelligentsChatController: UIViewController { didSet { updateContentToPublisher() } } - var sessionID: String = "" { - didSet { print("[*] new sessionID: \(sessionID)") } - } + var sessionID: String = "" public enum MetadataKey: String { case documentID @@ -84,10 +82,19 @@ public class IntelligentsChatController: UIViewController { view.addSubview(progressView) setupLayout() + header.moreMenu.showsMenuAsPrimaryAction = true + header.moreMenu.menu = .init(children: [ + UIAction(title: "Clear History".localized(), image: UIImage(systemName: "eraser")) { [weak self] _ in + self?.chat_clearHistory() + }, + ]) + // TODO: IMPL + header.dropMenu.isHidden = true inputBox.editor.controlBanner.cameraButton.isHidden = true inputBox.editor.controlBanner.photoButton.isHidden = true + updateContentToPublisher() chat_onLoad() } @@ -126,6 +133,10 @@ public class IntelligentsChatController: UIViewController { action: #selector(chat_onSend), for: .touchUpInside ) + inputBox.editor.submitAction = { [weak self] in + guard let self else { return } + chat_onSend() + } progressView.hidesWhenStopped = true progressView.stopAnimating() diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/MessageListView/TableView/MessageListView+Update.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/MessageListView/TableView/MessageListView+Update.swift index e6b214bf73..315e8cb3de 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/MessageListView/TableView/MessageListView+Update.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/MessageListView/TableView/MessageListView+Update.swift @@ -28,7 +28,7 @@ extension MessageListView { .eraseToAnyPublisher() // after so, limit the refresh rate so we can handle them better - let updateQueue = DispatchQueue(label: "flowdown.message-list-update-queue", qos: .userInteractive) + let updateQueue = DispatchQueue(label: "affine.message-list-update-queue", qos: .userInteractive) let inQueuePublisher = publisher .throttle(for: .seconds(1 / 5), scheduler: updateQueue, latest: true) .eraseToAnyPublisher() @@ -49,7 +49,7 @@ extension MessageListView { private func pickupElementsPair() -> (oldValue: Elements, newValue: Elements)? { #if DEBUG // just make sure assert is not called in release mode - assert(!elementUpdateProcessLock.try(), "Should not call this method without lock") + assert(!elementUpdateProcessLock.try(), "should not call this method without lock") #endif guard let distributedPendingUpdateElements else { return nil } @@ -78,6 +78,11 @@ extension MessageListView { self.tableView.layoutIfNeeded() } tableView.contentOffset = contentOffset + + if scrollToBottomOnNextUpdate { + scrollToBottomOnNextUpdate = false + scrollToBottom(useTableViewAnimation: false) + } } func reconfigure(enforceReload: Bool) { @@ -122,20 +127,20 @@ extension MessageListView { perform(#selector(finishAutomaticScroll), with: nil, afterDelay: 0.5) } - func scrollLastCellToTop(useTableViewAnimation: Bool = false) { - guard elements.count > 1 else { return } - guard tableView.contentSize.height > tableView.frame.height else { return } - UIView.animate(withDuration: 0.35, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.8) { - self.tableView.scrollToRow( - at: IndexPath(row: self.elements.count - 1, section: 0), - at: .top, - animated: useTableViewAnimation - ) - } - NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(finishAutomaticScroll), object: nil) - isAutomaticScrollAnimating = true - perform(#selector(finishAutomaticScroll), with: nil, afterDelay: 0.5) - } +// func scrollLastCellToTop(useTableViewAnimation: Bool = false) { +// guard elements.count > 1 else { return } +// guard tableView.contentSize.height > tableView.frame.height else { return } +// UIView.animate(withDuration: 0.35, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.8) { +// self.tableView.scrollToRow( +// at: IndexPath(row: self.elements.count - 1, section: 0), +// at: .top, +// animated: useTableViewAnimation +// ) +// } +// NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(finishAutomaticScroll), object: nil) +// isAutomaticScrollAnimating = true +// perform(#selector(finishAutomaticScroll), with: nil, afterDelay: 0.5) +// } @objc private func finishAutomaticScroll() { isAutomaticScrollAnimating = false diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/MessageListView/TableView/MessageListView.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/MessageListView/TableView/MessageListView.swift index 47ba345258..045596f197 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/MessageListView/TableView/MessageListView.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsChatController/MessageListView/TableView/MessageListView.swift @@ -23,8 +23,9 @@ class MessageListView: UIView { let elementUpdateProcessLock = NSLock() var distributedPendingUpdateElements: Elements? = nil var isAutomaticScrollAnimating: Bool = false + var scrollToBottomOnNextUpdate = false - let footerView = UIView(frame: .init(x: 0, y: 0, width: 0, height: 500)) + let footerView = UIView(frame: .init(x: 0, y: 0, width: 0, height: 200)) init(dataPublisher: AnyPublisher<[Element], Never>) { super.init(frame: .zero) diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsEphemeralActionController/IntelligentsEphemeralActionController+API.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsEphemeralActionController/IntelligentsEphemeralActionController+API.swift index b52efb881b..fae017aff2 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsEphemeralActionController/IntelligentsEphemeralActionController+API.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsEphemeralActionController/IntelligentsEphemeralActionController+API.swift @@ -15,6 +15,8 @@ extension IntelligentsEphemeralActionController { chatTask?.stop() chatTask = nil copilotDocumentStorage = "" + sessionID = "" + messageID = "" chat_createSession( documentIdentifier: documentID, workspaceIdentifier: workspaceID @@ -35,18 +37,12 @@ extension IntelligentsEphemeralActionController { onFailure: @escaping (Error) -> Void ) { if documentIdentifier.isEmpty || workspaceIdentifier.isEmpty { - onFailure( - NSError( - domain: "Intelligents", - code: 0, - userInfo: [NSLocalizedDescriptionKey: "Unable to identify the document or workspace"] - ) - ) + onFailure(UnableTo.identifyDocumentOrWorkspace) } Intelligents.qlClient.perform( mutation: CreateCopilotSessionMutation(options: .init( docId: documentIdentifier, - promptName: ation.prompt.rawValue, + promptName: action.prompt.rawValue, workspaceId: workspaceIdentifier )), queue: .global() @@ -57,13 +53,7 @@ extension IntelligentsEphemeralActionController { DispatchQueue.main.async { onSuccess(session) } } else { DispatchQueue.main.async { - onFailure( - NSError( - domain: "Intelligents", - code: 0, - userInfo: [NSLocalizedDescriptionKey: "No session created"] - ) - ) + onFailure(UnableTo.createSession) } } case let .failure(error): @@ -73,9 +63,17 @@ extension IntelligentsEphemeralActionController { } func beginThisRound() { + let parms: [String: AnyHashable] = switch action { + case let .translate(lang): + ["language": lang.rawValue] + case .summarize: + [:] + } + let json = try! CustomJSON(_jsonValue: parms) Intelligents.qlClient.perform( mutation: CreateCopilotMessageMutation(options: .init( content: .init(stringLiteral: "\(documentContent)"), + params: .some(json), sessionId: sessionID )), queue: .global() @@ -83,8 +81,12 @@ extension IntelligentsEphemeralActionController { switch result { case let .success(value): if let messageID = value.data?.createCopilotMessage { - print("[*] messageID", messageID) + self.messageID = messageID self.chat_processWithMessageID(sessionID: self.sessionID, messageID: messageID) + } else { + self.presentError(UnableTo.createMessage) { + self.close() + } } case let .failure(error): self.presentError(error) { @@ -106,11 +108,7 @@ extension IntelligentsEphemeralActionController { guard let url = comps?.url else { assertionFailure() - presentError(NSError( - domain: "Intelligents", - code: 0, - userInfo: [NSLocalizedDescriptionKey: "No message created"] - )) + presentError(UnableTo.createMessage) return } diff --git a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsEphemeralActionController/IntelligentsEphemeralActionController.swift b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsEphemeralActionController/IntelligentsEphemeralActionController.swift index ae6fb81184..6794ffe43f 100644 --- a/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsEphemeralActionController/IntelligentsEphemeralActionController.swift +++ b/packages/frontend/apps/ios/App/Packages/Intelligents/Sources/Intelligents/Interface/IntelligentsEphemeralActionController/IntelligentsEphemeralActionController.swift @@ -11,7 +11,7 @@ import MarkdownView import UIKit public class IntelligentsEphemeralActionController: UIViewController { - let ation: EphemeralAction + let action: EphemeralAction let scrollView = UIScrollView() let stackView = UIStackView() @@ -28,7 +28,13 @@ public class IntelligentsEphemeralActionController: UIViewController { public var documentID: String = "" public var workspaceID: String = "" public var documentContent: String = "" - var sessionID: String = "" + public internal(set) var sessionID: String = "" { + didSet { print(#fileID, #function, sessionID) } + } + + public internal(set) var messageID: String = "" { + didSet { print(#fileID, #function, messageID) } + } var chatTask: EventSource? var copilotDocumentStorage: String = "" { @@ -39,7 +45,7 @@ public class IntelligentsEphemeralActionController: UIViewController { } public init(action: EphemeralAction) { - ation = action + self.action = action super.init(nibName: nil, bundle: nil) title = action.title } @@ -121,6 +127,10 @@ public class IntelligentsEphemeralActionController: UIViewController { actionBar.retryButton.action = { [weak self] in self?.beginAction() } + actionBar.continueToChat.action = { [weak self] in + guard let self else { return } + continueToChat() + } } func setupContentViews() { @@ -275,3 +285,13 @@ public class IntelligentsEphemeralActionController: UIViewController { ) { self.scrollView.setContentOffset(bottomOffset, animated: false) } } } + +extension IntelligentsEphemeralActionController { + func continueToChat() { + let chatController = IntelligentsChatController() + chatController.metadata[.documentID] = documentID + chatController.metadata[.workspaceID] = workspaceID + chatController.metadata[.content] = documentContent + navigationController?.pushViewController(chatController, animated: true) + } +} diff --git a/packages/frontend/apps/ios/App/Packages/MarkdownView/Example/Example/App.swift b/packages/frontend/apps/ios/App/Packages/MarkdownView/Example/Example/App.swift index 2116442981..c972267baa 100644 --- a/packages/frontend/apps/ios/App/Packages/MarkdownView/Example/Example/App.swift +++ b/packages/frontend/apps/ios/App/Packages/MarkdownView/Example/Example/App.swift @@ -9,62 +9,62 @@ import SwiftUI @main struct TheApp: App { - var body: some Scene { - WindowGroup { - NavigationView { - Content() - .navigationTitle("MarkdownView") - .navigationBarTitleDisplayMode(.inline) - } - .navigationViewStyle(.stack) - } + var body: some Scene { + WindowGroup { + NavigationView { + Content() + .navigationTitle("MarkdownView") + .navigationBarTitleDisplayMode(.inline) + } + .navigationViewStyle(.stack) } + } } import MarkdownParser import MarkdownView class ContentController: UIViewController { - let document = MarkdownParser().feed(testDocument) - let scrollView = UIScrollView() - let markdownView = MarkdownView(theme: .default) + let document = MarkdownParser().feed(testDocument) + let scrollView = UIScrollView() + let markdownView = MarkdownView(theme: .default) - override func viewDidLoad() { - super.viewDidLoad() - view.addSubview(scrollView) - scrollView.addSubview(markdownView) - } + override func viewDidLoad() { + super.viewDidLoad() + view.addSubview(scrollView) + scrollView.addSubview(markdownView) + } - override func viewWillLayoutSubviews() { - super.viewWillLayoutSubviews() - scrollView.frame = view.bounds - let width = view.bounds.width - 32 - let manifest = document.map { - let manifest = $0.manifest(theme: markdownView.theme) - manifest.setLayoutWidth(width) - manifest.layoutIfNeeded() - return manifest - } - markdownView.updateContentViews(manifest) - markdownView.frame = .init( - x: 16, - y: 16, - width: width, - height: markdownView.height - ) - scrollView.contentSize = .init( - width: width, - height: markdownView.height + 100 - ) + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + scrollView.frame = view.bounds + let width = view.bounds.width - 32 + let manifest = document.map { + let manifest = $0.manifest(theme: markdownView.theme) + manifest.setLayoutWidth(width) + manifest.layoutIfNeeded() + return manifest } + markdownView.updateContentViews(manifest) + markdownView.frame = .init( + x: 16, + y: 16, + width: width, + height: markdownView.height + ) + scrollView.contentSize = .init( + width: width, + height: markdownView.height + 100 + ) + } } struct Content: UIViewControllerRepresentable { - func makeUIViewController(context _: Context) -> ContentController { - ContentController() - } + func makeUIViewController(context _: Context) -> ContentController { + ContentController() + } - func updateUIViewController(_: ContentController, context _: Context) {} + func updateUIViewController(_: ContentController, context _: Context) {} } let testDocument = ###""" diff --git a/packages/frontend/apps/ios/App/Packages/MarkdownView/Sources/MarkdownView/MarkdownBlockView/Supplements/BlockView+DiffableUpdate.swift b/packages/frontend/apps/ios/App/Packages/MarkdownView/Sources/MarkdownView/MarkdownBlockView/Supplements/BlockView+DiffableUpdate.swift index def163aeb6..1720a51b7d 100644 --- a/packages/frontend/apps/ios/App/Packages/MarkdownView/Sources/MarkdownView/MarkdownBlockView/Supplements/BlockView+DiffableUpdate.swift +++ b/packages/frontend/apps/ios/App/Packages/MarkdownView/Sources/MarkdownView/MarkdownBlockView/Supplements/BlockView+DiffableUpdate.swift @@ -15,7 +15,7 @@ extension UIView { } var shouldRemovedIndices: [Int] = [] defer { - shouldRemovedIndices.sorted(by: >).forEach { index in + for index in shouldRemovedIndices.sorted(by: >) { blockViews.remove(at: index) } } diff --git a/packages/frontend/apps/ios/apollo-codegen-config.json b/packages/frontend/apps/ios/apollo-codegen-config.json index 4eb44fa05a..aede536b67 100644 --- a/packages/frontend/apps/ios/apollo-codegen-config.json +++ b/packages/frontend/apps/ios/apollo-codegen-config.json @@ -2,7 +2,12 @@ "schemaNamespace": "AffineGraphQL", "input": { "operationSearchPaths": ["../../../common/graphql/src/graphql/**/*.gql"], - "schemaSearchPaths": ["../../../backend/server/src/schema.gql"] + "schemaSearchPaths": ["../../../backend/server/src/schema.gql"], + "schemaCustomization": { + "customTypeNames": { + "JSON": "[String: Any]" + } + } }, "output": { "testMocks": {