#include "chat_widget.h" #include #include #include #include #include #include #include #include #include namespace scar { ChatWidget::ChatWidget(QWidget* parent) : QWidget(parent), isTyping_(false) { setupDatabase(); setupUI(); // Typing indicator timer typingTimer_ = new QTimer(this); typingTimer_->setSingleShot(true); connect(typingTimer_, &QTimer::timeout, [this]() { if (isTyping_) { isTyping_ = false; emit typingIndicator(false); } }); } ChatWidget::~ChatWidget() { if (db_.isOpen()) { db_.close(); } } void ChatWidget::setupUI() { auto* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); // Chat display chatDisplay_ = new QTextEdit(this); chatDisplay_->setReadOnly(true); chatDisplay_->setStyleSheet("QTextEdit { border: none; }"); layout->addWidget(chatDisplay_); // Input area auto* inputLayout = new QHBoxLayout(); messageInput_ = new QLineEdit(this); messageInput_->setPlaceholderText("Type a message..."); connect(messageInput_, &QLineEdit::returnPressed, this, &ChatWidget::onSendClicked); connect(messageInput_, &QLineEdit::textChanged, this, &ChatWidget::onTextChanged); inputLayout->addWidget(messageInput_); sendButton_ = new QPushButton("Send", this); connect(sendButton_, &QPushButton::clicked, this, &ChatWidget::onSendClicked); inputLayout->addWidget(sendButton_); layout->addLayout(inputLayout); } void ChatWidget::setupDatabase() { QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QDir().mkpath(dataDir); db_ = QSqlDatabase::addDatabase("QSQLITE", "chat_history"); db_.setDatabaseName(dataDir + "/chat_history.db"); if (!db_.open()) { std::cerr << "Failed to open chat history database" << std::endl; return; } QSqlQuery query(db_); query.exec(R"( CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, sender TEXT NOT NULL, content TEXT NOT NULL, timestamp INTEGER NOT NULL ) )"); } void ChatWidget::onSendClicked() { QString message = messageInput_->text().trimmed(); if (message.isEmpty()) return; emit messageSent(message); addMessage("You", message, true); messageInput_->clear(); } void ChatWidget::onTextChanged() { if (!messageInput_->text().isEmpty() && !isTyping_) { isTyping_ = true; emit typingIndicator(true); } // Reset typing timer typingTimer_->start(2000); // Stop typing indicator after 2 seconds } void ChatWidget::addMessage(const QString& sender, const QString& content, bool isOwnMessage) { QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss"); QString color = isOwnMessage ? "#5865F2" : "#43B581"; // Blue for own, green for others QString html = QString("

" "%2 " "%3
" "%4

") .arg(color, sender, timestamp, content); chatDisplay_->append(html); // Save to database if (!isOwnMessage) { saveMessage(sender, content); } } void ChatWidget::clear() { chatDisplay_->clear(); } void ChatWidget::loadHistory() { if (!db_.isOpen()) return; QSqlQuery query(db_); query.prepare("SELECT sender, content FROM messages ORDER BY timestamp DESC LIMIT 100"); if (query.exec()) { QStringList messages; while (query.next()) { messages.prepend(query.value(0).toString() + ": " + query.value(1).toString()); } for (const QString& msg : messages) { int colonPos = msg.indexOf(": "); if (colonPos > 0) { QString sender = msg.left(colonPos); QString content = msg.mid(colonPos + 2); addMessage(sender, content, false); } } } } void ChatWidget::saveMessage(const QString& sender, const QString& content) { if (!db_.isOpen()) return; QSqlQuery query(db_); query.prepare("INSERT INTO messages (sender, content, timestamp) VALUES (?, ?, ?)"); query.addBindValue(sender); query.addBindValue(content); query.addBindValue(QDateTime::currentSecsSinceEpoch()); query.exec(); } } // namespace scar