feat: vibe chat
This commit is contained in:
parent
0dc032ee9e
commit
fa565bc052
@ -79,18 +79,15 @@ extension MainViewController: InputBoxDelegate {
|
||||
|
||||
Task { @MainActor in
|
||||
do {
|
||||
// Ensure we have a current session or create one
|
||||
let chatManager = ChatManager.shared
|
||||
|
||||
if let currentSession = chatManager.currentSession {
|
||||
// Send message to existing session
|
||||
try await chatManager.sendMessage(
|
||||
content: inputData.text,
|
||||
attachments: [], // TODO: Handle attachments
|
||||
sessionId: currentSession.id
|
||||
)
|
||||
} else {
|
||||
// Create new session first
|
||||
guard let workspaceId = IntelligentContext.shared.webViewMetadata[.currentWorkspaceId] as? String,
|
||||
!workspaceId.isEmpty
|
||||
else {
|
||||
@ -100,7 +97,6 @@ extension MainViewController: InputBoxDelegate {
|
||||
|
||||
let session = try await chatManager.createSession(workspaceId: workspaceId)
|
||||
|
||||
// Send message to new session
|
||||
try await chatManager.sendMessage(
|
||||
content: inputData.text,
|
||||
attachments: [], // TODO: Handle attachments
|
||||
@ -108,7 +104,6 @@ extension MainViewController: InputBoxDelegate {
|
||||
)
|
||||
}
|
||||
|
||||
// Clear input after successful send
|
||||
inputBox.text = ""
|
||||
inputBox.viewModel.clearAllAttachments()
|
||||
|
||||
|
@ -10,6 +10,27 @@ class MainViewController: UIViewController {
|
||||
$0.delegate = self
|
||||
}
|
||||
|
||||
lazy var tableView = UITableView().then {
|
||||
$0.backgroundColor = .clear
|
||||
$0.separatorStyle = .none
|
||||
$0.delegate = self
|
||||
$0.dataSource = self
|
||||
$0.register(ChatCell.self, forCellReuseIdentifier: "ChatCell")
|
||||
$0.keyboardDismissMode = .interactive
|
||||
$0.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
|
||||
lazy var emptyStateView = UIView().then {
|
||||
$0.isHidden = true
|
||||
}
|
||||
|
||||
lazy var emptyStateLabel = UILabel().then {
|
||||
$0.text = "Start a conversation..."
|
||||
$0.font = .systemFont(ofSize: 18, weight: .medium)
|
||||
$0.textColor = .systemGray
|
||||
$0.textAlignment = .center
|
||||
}
|
||||
|
||||
lazy var inputBox = InputBox().then {
|
||||
$0.delegate = self
|
||||
}
|
||||
@ -28,8 +49,10 @@ class MainViewController: UIViewController {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var messages: [ChatMessage] = []
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private let intelligentContext = IntelligentContext.shared
|
||||
private let chatManager = ChatManager.shared
|
||||
var terminateEditGesture: UITapGestureRecognizer!
|
||||
|
||||
// MARK: - Lifecycle
|
||||
@ -38,21 +61,46 @@ class MainViewController: UIViewController {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .affineLayerBackgroundPrimary
|
||||
|
||||
let inputBox = InputBox().then {
|
||||
$0.delegate = self
|
||||
}
|
||||
self.inputBox = inputBox
|
||||
setupUI()
|
||||
setupBindings()
|
||||
|
||||
view.isUserInteractionEnabled = true
|
||||
terminateEditGesture = UITapGestureRecognizer(target: self, action: #selector(terminateEditing))
|
||||
view.addGestureRecognizer(terminateEditGesture)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
private func setupUI() {
|
||||
view.addSubview(headerView)
|
||||
view.addSubview(tableView)
|
||||
view.addSubview(emptyStateView)
|
||||
view.addSubview(inputBox)
|
||||
view.addSubview(documentPickerHideDetector)
|
||||
view.addSubview(documentPickerView)
|
||||
|
||||
emptyStateView.addSubview(emptyStateLabel)
|
||||
|
||||
headerView.snp.makeConstraints { make in
|
||||
make.top.equalTo(view.safeAreaLayoutGuide)
|
||||
make.leading.trailing.equalToSuperview()
|
||||
}
|
||||
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.top.equalTo(headerView.snp.bottom)
|
||||
make.leading.trailing.equalToSuperview()
|
||||
make.bottom.equalTo(inputBox.snp.top)
|
||||
}
|
||||
|
||||
emptyStateView.snp.makeConstraints { make in
|
||||
make.center.equalTo(tableView)
|
||||
make.width.lessThanOrEqualTo(tableView).inset(32)
|
||||
}
|
||||
|
||||
emptyStateLabel.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
|
||||
inputBox.snp.makeConstraints { make in
|
||||
make.leading.trailing.equalToSuperview()
|
||||
make.bottom.equalTo(view.keyboardLayoutGuide.snp.top)
|
||||
@ -67,10 +115,24 @@ class MainViewController: UIViewController {
|
||||
make.leading.trailing.equalToSuperview()
|
||||
make.height.equalTo(500)
|
||||
}
|
||||
}
|
||||
|
||||
view.isUserInteractionEnabled = true
|
||||
terminateEditGesture = UITapGestureRecognizer(target: self, action: #selector(terminateEditing))
|
||||
view.addGestureRecognizer(terminateEditGesture)
|
||||
private func setupBindings() {
|
||||
chatManager.$currentSession
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] session in
|
||||
self?.updateMessages(for: session?.id)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
chatManager.$messages
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
if let sessionId = self?.chatManager.currentSession?.id {
|
||||
self?.updateMessages(for: sessionId)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
@ -90,4 +152,66 @@ class MainViewController: UIViewController {
|
||||
@objc func terminateEditing() {
|
||||
view.endEditing(true)
|
||||
}
|
||||
|
||||
// MARK: - Chat Methods
|
||||
|
||||
private func updateMessages(for sessionId: String?) {
|
||||
guard let sessionId else {
|
||||
messages = []
|
||||
updateEmptyState()
|
||||
tableView.reloadData()
|
||||
return
|
||||
}
|
||||
|
||||
messages = chatManager.messages[sessionId] ?? []
|
||||
updateEmptyState()
|
||||
tableView.reloadData()
|
||||
|
||||
if !messages.isEmpty {
|
||||
let indexPath = IndexPath(row: messages.count - 1, section: 0)
|
||||
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateEmptyState() {
|
||||
emptyStateView.isHidden = !messages.isEmpty
|
||||
tableView.isHidden = messages.isEmpty
|
||||
}
|
||||
|
||||
// MARK: - Internal Methods for Preview/Testing
|
||||
|
||||
#if DEBUG
|
||||
func setMessagesForPreview(_ previewMessages: [ChatMessage]) {
|
||||
messages = previewMessages
|
||||
updateEmptyState()
|
||||
tableView.reloadData()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
extension MainViewController: UITableViewDataSource {
|
||||
func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
|
||||
messages.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatCell", for: indexPath) as! ChatCell
|
||||
let message = messages[indexPath.row]
|
||||
cell.configure(with: message)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
extension MainViewController: UITableViewDelegate {
|
||||
func tableView(_: UITableView, heightForRowAt _: IndexPath) -> CGFloat {
|
||||
UITableView.automaticDimension
|
||||
}
|
||||
|
||||
func tableView(_: UITableView, estimatedHeightForRowAt _: IndexPath) -> CGFloat {
|
||||
60
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,155 @@
|
||||
//
|
||||
// ChatCell.swift
|
||||
// Intelligents
|
||||
//
|
||||
// Created by 秋星桥 on 6/26/25.
|
||||
//
|
||||
|
||||
import SnapKit
|
||||
import Then
|
||||
import UIKit
|
||||
|
||||
class ChatCell: UITableViewCell {
|
||||
// MARK: - UI Components
|
||||
|
||||
private lazy var avatarImageView = UIImageView().then {
|
||||
$0.contentMode = .scaleAspectFit
|
||||
$0.layer.cornerRadius = 16
|
||||
$0.layer.cornerCurve = .continuous
|
||||
$0.clipsToBounds = true
|
||||
$0.backgroundColor = .systemGray5
|
||||
}
|
||||
|
||||
private lazy var messageContainerView = UIView().then {
|
||||
$0.layer.cornerRadius = 12
|
||||
$0.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
private lazy var messageLabel = UILabel().then {
|
||||
$0.numberOfLines = 0
|
||||
$0.font = .systemFont(ofSize: 16)
|
||||
$0.textColor = .label
|
||||
}
|
||||
|
||||
private lazy var timestampLabel = UILabel().then {
|
||||
$0.font = .systemFont(ofSize: 12)
|
||||
$0.textColor = .systemGray
|
||||
$0.textAlignment = .right
|
||||
}
|
||||
|
||||
private lazy var stackView = UIStackView().then {
|
||||
$0.axis = .horizontal
|
||||
$0.spacing = 12
|
||||
$0.alignment = .top
|
||||
}
|
||||
|
||||
private lazy var messageStackView = UIStackView().then {
|
||||
$0.axis = .vertical
|
||||
$0.spacing = 4
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var message: ChatMessage?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
setupUI()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
private func setupUI() {
|
||||
backgroundColor = .clear
|
||||
selectionStyle = .none
|
||||
|
||||
contentView.addSubview(stackView)
|
||||
|
||||
messageStackView.addArrangedSubview(messageContainerView)
|
||||
messageStackView.addArrangedSubview(timestampLabel)
|
||||
|
||||
messageContainerView.addSubview(messageLabel)
|
||||
|
||||
stackView.addArrangedSubview(avatarImageView)
|
||||
stackView.addArrangedSubview(messageStackView)
|
||||
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(16)
|
||||
}
|
||||
|
||||
avatarImageView.snp.makeConstraints { make in
|
||||
make.size.equalTo(32)
|
||||
}
|
||||
|
||||
messageLabel.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(12)
|
||||
}
|
||||
|
||||
messageStackView.snp.makeConstraints { make in
|
||||
make.width.lessThanOrEqualTo(250)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
func configure(with message: ChatMessage) {
|
||||
self.message = message
|
||||
|
||||
messageLabel.text = message.content
|
||||
|
||||
if let createdDate = message.createdDate {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .none
|
||||
formatter.timeStyle = .short
|
||||
timestampLabel.text = formatter.string(from: createdDate)
|
||||
} else {
|
||||
timestampLabel.text = ""
|
||||
}
|
||||
|
||||
switch message.role {
|
||||
case .user:
|
||||
configureUserMessage()
|
||||
case .assistant:
|
||||
configureAssistantMessage()
|
||||
case .system:
|
||||
configureSystemMessage()
|
||||
}
|
||||
}
|
||||
|
||||
private func configureUserMessage() {
|
||||
// User message - align to right
|
||||
stackView.semanticContentAttribute = .forceRightToLeft
|
||||
messageContainerView.backgroundColor = .systemBlue
|
||||
messageLabel.textColor = .white
|
||||
avatarImageView.image = UIImage(systemName: "person.circle.fill")
|
||||
avatarImageView.tintColor = .systemBlue
|
||||
timestampLabel.textAlignment = .left
|
||||
}
|
||||
|
||||
private func configureAssistantMessage() {
|
||||
// Assistant message - align to left
|
||||
stackView.semanticContentAttribute = .forceLeftToRight
|
||||
messageContainerView.backgroundColor = .systemGray6
|
||||
messageLabel.textColor = .label
|
||||
avatarImageView.image = UIImage(systemName: "brain.head.profile")
|
||||
avatarImageView.tintColor = .systemPurple
|
||||
timestampLabel.textAlignment = .right
|
||||
}
|
||||
|
||||
private func configureSystemMessage() {
|
||||
// System message - center aligned
|
||||
stackView.semanticContentAttribute = .forceLeftToRight
|
||||
messageContainerView.backgroundColor = .systemYellow.withAlphaComponent(0.3)
|
||||
messageLabel.textColor = .label
|
||||
avatarImageView.image = UIImage(systemName: "gear")
|
||||
avatarImageView.tintColor = .systemOrange
|
||||
timestampLabel.textAlignment = .center
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
//
|
||||
// ChatCell.swift
|
||||
// Intelligents
|
||||
//
|
||||
// Created by 秋星桥 on 6/26/25.
|
||||
//
|
||||
|
||||
import SnapKit
|
||||
import Then
|
||||
import UIKit
|
||||
|
||||
extension ChatListView {
|
||||
class ChatCell: UITableViewCell {
|
||||
// MARK: - UI Components
|
||||
|
||||
private lazy var avatarImageView = UIImageView().then {
|
||||
$0.contentMode = .scaleAspectFit
|
||||
$0.layer.cornerRadius = 16
|
||||
$0.layer.cornerCurve = .continuous
|
||||
$0.clipsToBounds = true
|
||||
$0.backgroundColor = .systemGray5
|
||||
}
|
||||
|
||||
private lazy var messageContainerView = UIView().then {
|
||||
$0.layer.cornerRadius = 12
|
||||
$0.layer.cornerCurve = .continuous
|
||||
}
|
||||
|
||||
private lazy var messageLabel = UILabel().then {
|
||||
$0.numberOfLines = 0
|
||||
$0.font = .systemFont(ofSize: 16)
|
||||
$0.textColor = .label
|
||||
}
|
||||
|
||||
private lazy var timestampLabel = UILabel().then {
|
||||
$0.font = .systemFont(ofSize: 12)
|
||||
$0.textColor = .systemGray
|
||||
$0.textAlignment = .right
|
||||
}
|
||||
|
||||
private lazy var stackView = UIStackView().then {
|
||||
$0.axis = .horizontal
|
||||
$0.spacing = 12
|
||||
$0.alignment = .top
|
||||
}
|
||||
|
||||
private lazy var messageStackView = UIStackView().then {
|
||||
$0.axis = .vertical
|
||||
$0.spacing = 4
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var message: ChatMessage?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
setupUI()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
private func setupUI() {
|
||||
backgroundColor = .clear
|
||||
selectionStyle = .none
|
||||
|
||||
contentView.addSubview(stackView)
|
||||
|
||||
messageStackView.addArrangedSubview(messageContainerView)
|
||||
messageStackView.addArrangedSubview(timestampLabel)
|
||||
|
||||
messageContainerView.addSubview(messageLabel)
|
||||
|
||||
stackView.addArrangedSubview(avatarImageView)
|
||||
stackView.addArrangedSubview(messageStackView)
|
||||
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(16)
|
||||
}
|
||||
|
||||
avatarImageView.snp.makeConstraints { make in
|
||||
make.size.equalTo(32)
|
||||
}
|
||||
|
||||
messageLabel.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(12)
|
||||
}
|
||||
|
||||
messageStackView.snp.makeConstraints { make in
|
||||
make.width.lessThanOrEqualTo(250)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
func configure(with message: ChatMessage) {
|
||||
self.message = message
|
||||
|
||||
messageLabel.text = message.content
|
||||
|
||||
if let createdDate = message.createdDate {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .none
|
||||
formatter.timeStyle = .short
|
||||
timestampLabel.text = formatter.string(from: createdDate)
|
||||
} else {
|
||||
timestampLabel.text = ""
|
||||
}
|
||||
|
||||
switch message.role {
|
||||
case .user:
|
||||
configureUserMessage()
|
||||
case .assistant:
|
||||
configureAssistantMessage()
|
||||
case .system:
|
||||
configureSystemMessage()
|
||||
}
|
||||
}
|
||||
|
||||
private func configureUserMessage() {
|
||||
// User message - align to right
|
||||
stackView.semanticContentAttribute = .forceRightToLeft
|
||||
messageContainerView.backgroundColor = .systemBlue
|
||||
messageLabel.textColor = .white
|
||||
avatarImageView.image = UIImage(systemName: "person.circle.fill")
|
||||
avatarImageView.tintColor = .systemBlue
|
||||
timestampLabel.textAlignment = .left
|
||||
}
|
||||
|
||||
private func configureAssistantMessage() {
|
||||
// Assistant message - align to left
|
||||
stackView.semanticContentAttribute = .forceLeftToRight
|
||||
messageContainerView.backgroundColor = .systemGray6
|
||||
messageLabel.textColor = .label
|
||||
avatarImageView.image = UIImage(systemName: "brain.head.profile")
|
||||
avatarImageView.tintColor = .systemPurple
|
||||
timestampLabel.textAlignment = .right
|
||||
}
|
||||
|
||||
private func configureSystemMessage() {
|
||||
// System message - center aligned
|
||||
stackView.semanticContentAttribute = .forceLeftToRight
|
||||
messageContainerView.backgroundColor = .systemYellow.withAlphaComponent(0.3)
|
||||
messageLabel.textColor = .label
|
||||
avatarImageView.image = UIImage(systemName: "gear")
|
||||
avatarImageView.tintColor = .systemOrange
|
||||
timestampLabel.textAlignment = .center
|
||||
}
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
//
|
||||
// ChatListView.swift
|
||||
// Intelligents
|
||||
//
|
||||
// Created by 秋星桥 on 6/26/25.
|
||||
//
|
||||
|
||||
import AffineGraphQL
|
||||
import Combine
|
||||
import SnapKit
|
||||
import Then
|
||||
import UIKit
|
||||
|
||||
class ChatListView: UIView {
|
||||
// MARK: - UI Components
|
||||
|
||||
lazy var tableView = UITableView().then {
|
||||
$0.backgroundColor = .clear
|
||||
$0.separatorStyle = .none
|
||||
$0.delegate = self
|
||||
$0.dataSource = self
|
||||
$0.register(ChatCell.self, forCellReuseIdentifier: "ChatCell")
|
||||
$0.keyboardDismissMode = .interactive
|
||||
$0.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
|
||||
lazy var emptyStateView = UIView().then {
|
||||
$0.isHidden = true
|
||||
}
|
||||
|
||||
lazy var emptyStateLabel = UILabel().then {
|
||||
$0.text = "Start a conversation..."
|
||||
$0.font = .systemFont(ofSize: 18, weight: .medium)
|
||||
$0.textColor = .systemGray
|
||||
$0.textAlignment = .center
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private var messages: [ChatMessage] = []
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private let chatManager = ChatManager.shared
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupUI()
|
||||
setupBindings()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
private func setupUI() {
|
||||
backgroundColor = .affineLayerBackgroundPrimary
|
||||
|
||||
addSubview(tableView)
|
||||
addSubview(emptyStateView)
|
||||
emptyStateView.addSubview(emptyStateLabel)
|
||||
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
|
||||
emptyStateView.snp.makeConstraints { make in
|
||||
make.center.equalToSuperview()
|
||||
make.width.lessThanOrEqualToSuperview().inset(32)
|
||||
}
|
||||
|
||||
emptyStateLabel.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
private func setupBindings() {
|
||||
// Listen to current session changes
|
||||
chatManager.$currentSession
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] session in
|
||||
self?.updateMessages(for: session?.id)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
// Listen to messages changes
|
||||
chatManager.$messages
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in
|
||||
if let sessionId = self?.chatManager.currentSession?.id {
|
||||
self?.updateMessages(for: sessionId)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func updateMessages(for sessionId: String?) {
|
||||
guard let sessionId else {
|
||||
messages = []
|
||||
updateEmptyState()
|
||||
tableView.reloadData()
|
||||
return
|
||||
}
|
||||
|
||||
messages = chatManager.messages[sessionId] ?? []
|
||||
updateEmptyState()
|
||||
tableView.reloadData()
|
||||
|
||||
// Scroll to bottom for new messages
|
||||
if !messages.isEmpty {
|
||||
let indexPath = IndexPath(row: messages.count - 1, section: 0)
|
||||
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateEmptyState() {
|
||||
emptyStateView.isHidden = !messages.isEmpty
|
||||
tableView.isHidden = messages.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
extension ChatListView: UITableViewDataSource {
|
||||
func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
|
||||
messages.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatCell", for: indexPath) as! ChatCell
|
||||
let message = messages[indexPath.row]
|
||||
cell.configure(with: message)
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
extension ChatListView: UITableViewDelegate {
|
||||
func tableView(_: UITableView, heightForRowAt _: IndexPath) -> CGFloat {
|
||||
UITableView.automaticDimension
|
||||
}
|
||||
|
||||
func tableView(_: UITableView, estimatedHeightForRowAt _: IndexPath) -> CGFloat {
|
||||
60
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ public class InputBoxViewModel: ObservableObject {
|
||||
.assign(to: \.canSend, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
public func clearAllAttachments() {
|
||||
imageAttachments.removeAll()
|
||||
fileAttachments.removeAll()
|
||||
|
Loading…
x
Reference in New Issue
Block a user