scar-chat7/client/ui/chat_widget.cpp

162 lines
4.6 KiB
C++
Raw Normal View History

2025-12-07 12:00:44 -07:00
#include "chat_widget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QSqlQuery>
#include <QSqlError>
#include <QTimer>
#include <QDateTime>
#include <QStandardPaths>
#include <QDir>
#include <iostream>
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("<p style='margin: 5px 0;'>"
"<span style='color: %1; font-weight: bold;'>%2</span> "
"<span style='color: #888; font-size: 10px;'>%3</span><br>"
"%4</p>")
.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