diff --git a/gpt4all-chat/CHANGELOG.md b/gpt4all-chat/CHANGELOG.md index 07039076..5f542766 100644 --- a/gpt4all-chat/CHANGELOG.md +++ b/gpt4all-chat/CHANGELOG.md @@ -6,11 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] -## Fixed +### Fixed - Fix the timeout error in code interpreter ([#3369](https://github.com/nomic-ai/gpt4all/pull/3369)) - Fix code interpreter console.log not accepting multiple arguments ([#3371](https://github.com/nomic-ai/gpt4all/pull/3371)) - Remove 'X is defined' checks from templates as they work incorrectly with Jinja2Cpp ([#3372](https://github.com/nomic-ai/gpt4all/pull/3372)) - Jinja2Cpp: Add 'if' requirement for 'else' parsing to fix crash ([#3373](https://github.com/nomic-ai/gpt4all/pull/3373)) +- Save chats on quit, even if the window isn't closed first ([#3387](https://github.com/nomic-ai/gpt4all/pull/3387)) ## [3.6.1] - 2024-12-20 diff --git a/gpt4all-chat/main.qml b/gpt4all-chat/main.qml index 2dc4410c..5b60d36a 100644 --- a/gpt4all-chat/main.qml +++ b/gpt4all-chat/main.qml @@ -54,7 +54,7 @@ Window { systemTrayIcon.shouldClose = true; window.shouldClose = true; savingPopup.open(); - ChatListModel.saveChats(); + ChatListModel.saveChatsForQuit(); } } } @@ -231,8 +231,8 @@ Window { window.shouldClose = true; savingPopup.open(); - ChatListModel.saveChats(); - close.accepted = false + ChatListModel.saveChatsForQuit(); + close.accepted = false; } Connections { diff --git a/gpt4all-chat/src/chatlistmodel.cpp b/gpt4all-chat/src/chatlistmodel.cpp index fd9d4509..85cb44d5 100644 --- a/gpt4all-chat/src/chatlistmodel.cpp +++ b/gpt4all-chat/src/chatlistmodel.cpp @@ -52,9 +52,11 @@ void ChatListModel::loadChats() connect(thread, &ChatsRestoreThread::finished, thread, &QObject::deleteLater); thread->start(); - ChatSaver *saver = new ChatSaver; - connect(this, &ChatListModel::requestSaveChats, saver, &ChatSaver::saveChats, Qt::QueuedConnection); - connect(saver, &ChatSaver::saveChatsFinished, this, &ChatListModel::saveChatsFinished, Qt::QueuedConnection); + m_chatSaver = std::make_unique(); + connect(this, &ChatListModel::requestSaveChats, m_chatSaver.get(), &ChatSaver::saveChats, Qt::QueuedConnection); + connect(m_chatSaver.get(), &ChatSaver::saveChatsFinished, this, &ChatListModel::saveChatsFinished, Qt::QueuedConnection); + // save chats on application quit + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &ChatListModel::saveChatsSync); connect(MySettings::globalInstance(), &MySettings::serverChatChanged, this, &ChatListModel::handleServerEnabledChanged); } @@ -78,16 +80,24 @@ ChatSaver::ChatSaver() m_thread.start(); } +ChatSaver::~ChatSaver() +{ + m_thread.quit(); + m_thread.wait(); +} + +QVector ChatListModel::getChatsToSave() const +{ + QVector toSave; + for (auto *chat : m_chats) + if (chat != m_serverChat && !chat->isNewChat()) + toSave << chat; + return toSave; +} + void ChatListModel::saveChats() { - QVector toSave; - for (Chat *chat : m_chats) { - if (chat == m_serverChat) - continue; - if (chat->isNewChat()) - continue; - toSave.append(chat); - } + auto toSave = getChatsToSave(); if (toSave.isEmpty()) { emit saveChatsFinished(); return; @@ -96,8 +106,24 @@ void ChatListModel::saveChats() emit requestSaveChats(toSave); } +void ChatListModel::saveChatsForQuit() +{ + saveChats(); + m_startedFinalSave = true; +} + +void ChatListModel::saveChatsSync() +{ + auto toSave = getChatsToSave(); + if (!m_startedFinalSave && !toSave.isEmpty()) + m_chatSaver->saveChats(toSave); +} + void ChatSaver::saveChats(const QVector &chats) { + // we can be called from the main thread instead of a worker thread at quit time, so take a lock + QMutexLocker locker(&m_mutex); + QElapsedTimer timer; timer.start(); const QString savePath = MySettings::globalInstance()->modelPath(); diff --git a/gpt4all-chat/src/chatlistmodel.h b/gpt4all-chat/src/chatlistmodel.h index 4fe1c374..0c405b15 100644 --- a/gpt4all-chat/src/chatlistmodel.h +++ b/gpt4all-chat/src/chatlistmodel.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,9 @@ #include #include +#include + + class ChatsRestoreThread : public QThread { Q_OBJECT @@ -33,6 +37,7 @@ class ChatSaver : public QObject Q_OBJECT public: explicit ChatSaver(); + ~ChatSaver() override; Q_SIGNALS: void saveChatsFinished(); @@ -42,6 +47,7 @@ public Q_SLOTS: private: QThread m_thread; + QMutex m_mutex; }; class ChatListModel : public QAbstractListModel @@ -228,6 +234,7 @@ public: void removeChatFile(Chat *chat) const; Q_INVOKABLE void saveChats(); + Q_INVOKABLE void saveChatsForQuit(); void restoreChat(Chat *chat); void chatsRestoredFinished(); @@ -244,6 +251,9 @@ protected: bool eventFilter(QObject *obj, QEvent *ev) override; private Q_SLOTS: + // Used with QCoreApplication::aboutToQuit. Does not require an event loop. + void saveChatsSync(); + void newChatCountChanged() { Q_ASSERT(m_newChat && m_newChat->chatModel()->count()); @@ -274,11 +284,16 @@ private Q_SLOTS: } } +private: + QVector getChatsToSave() const; + private: Chat* m_newChat = nullptr; Chat* m_serverChat = nullptr; Chat* m_currentChat = nullptr; QList m_chats; + std::unique_ptr m_chatSaver; + bool m_startedFinalSave = false; private: explicit ChatListModel();