client: Detect legacy CoreInfo, request on show
authorShane Synan <digitalcircuit36939@gmail.com>
Mon, 18 Jun 2018 21:16:37 +0000 (16:16 -0500)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 19 Jun 2018 00:02:15 +0000 (02:02 +0200)
Add feature flag 'SyncedCoreInfo' to signify the core supports
signals for the CoreInfo object.

Detect cores without support for 'SyncedCoreInfo', and poll for core
information on every CoreInfoDlg show.  This avoids regressing
functionality for older cores.

Synchronize the CoreInfo object before connecting, ensuring it's
ready to receive parameters on first connect, mimicking backlog
requester.  This avoids a warning about "No registered receiver"
for the CoreInfo handler.

Reset CoreInfo object on disconnect, update CoreInfoDlg with
"Disconnected from core" message.  This reduces confusion about the
state of the core; before, it would simply stop updating.

Remove several nullpointer checks, CoreInfo now should never be null.

Add Client::coreInfoResynchronized() signal for CoreInfo
resynchronization, allowing for periodic updates on legacy cores.

src/client/client.cpp
src/client/client.h
src/common/coreinfo.cpp
src/common/coreinfo.h
src/common/quassel.h
src/qtui/coreinfodlg.cpp
src/qtui/coreinfodlg.h

index 6cd94c1..32f599d 100644 (file)
@@ -101,7 +101,7 @@ Client::Client(QObject *parent)
     _backlogManager(new ClientBacklogManager(this)),
     _bufferViewManager(0),
     _bufferViewOverlay(new BufferViewOverlay(this)),
-    _coreInfo(nullptr),
+    _coreInfo(new CoreInfo(this)),
     _dccConfig(0),
     _ircListHelper(new ClientIrcListHelper(this)),
     _inputHandler(0),
@@ -175,6 +175,9 @@ void Client::init()
 
     coreAccountModel()->load();
 
+    // Attach CoreInfo
+    p->synchronize(coreInfo());
+
     connect(coreConnection(), SIGNAL(stateChanged(CoreConnection::ConnectionState)), SLOT(connectionStateChanged(CoreConnection::ConnectionState)));
     coreConnection()->init();
 }
@@ -407,11 +410,6 @@ void Client::setSyncedToCore()
     SignalProxy *p = signalProxy();
     p->synchronize(bufferSyncer());
 
-    // create CoreInfo
-    Q_ASSERT(!_coreInfo);
-    _coreInfo = new CoreInfo(this);
-    p->synchronize(coreInfo());
-
     // create a new BufferViewManager
     Q_ASSERT(!_bufferViewManager);
     _bufferViewManager = new ClientBufferViewManager(p, this);
@@ -486,6 +484,27 @@ void Client::requestInitialBacklog()
 }
 
 
+void Client::requestLegacyCoreInfo()
+{
+    // On older cores, the CoreInfo object was only synchronized on demand.  Synchronize now if
+    // needed.
+    if (isConnected() && !isCoreFeatureEnabled(Quassel::Feature::SyncedCoreInfo)) {
+        // Delete the existing core info object (it will always exist as client is single-threaded)
+        _coreInfo->deleteLater();
+        // No need to set to null when creating new one immediately after
+
+        // Create a fresh, unsynchronized CoreInfo object, emulating legacy behavior of CoreInfo not
+        // persisting
+        _coreInfo = new CoreInfo(this);
+        // Synchronize the new object
+        signalProxy()->synchronize(_coreInfo);
+
+        // Let others know signal handlers have been reset
+        emit coreInfoResynchronized();
+    }
+}
+
+
 void Client::disconnectFromCore()
 {
     if (!coreConnection()->isConnected())
@@ -512,10 +531,7 @@ void Client::setDisconnectedFromCore()
         _bufferSyncer = 0;
     }
 
-    if (_coreInfo) {
-        _coreInfo->deleteLater();
-        _coreInfo = nullptr;
-    }
+    _coreInfo->reset();
 
     if (_bufferViewManager) {
         _bufferViewManager->deleteLater();
@@ -711,6 +727,12 @@ void Client::markBufferAsRead(BufferId id)
 }
 
 
+void Client::refreshLegacyCoreInfo()
+{
+    instance()->requestLegacyCoreInfo();
+}
+
+
 void Client::changePassword(const QString &oldPassword, const QString &newPassword) {
     CoreAccount account = currentCoreAccount();
     account.setPassword(newPassword);
index 3676e29..f8baa98 100644 (file)
@@ -149,6 +149,17 @@ public:
     static void mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2);
     static void purgeKnownBufferIds();
 
+    /**
+     * Requests client to resynchronize the CoreInfo object for legacy (pre-0.13) cores
+     *
+     * This provides compatibility with updating core information for legacy cores, and can be
+     * removed after protocol break.
+     *
+     * NOTE: On legacy (pre-0.13) cores, any existing connected signals will be destroyed and must
+     * be re-added after calling this, in addition to checking for existing data in coreInfo().
+     */
+    static void refreshLegacyCoreInfo();
+
     static void changePassword(const QString &oldPassword, const QString &newPassword);
     static void kickClient(int peerId);
 
@@ -178,6 +189,17 @@ signals:
     void disconnected();
     void coreConnectionStateChanged(bool);
 
+    /**
+     * Signals that core information has been resynchronized, removing existing signal handlers
+     *
+     * Whenever this is emitted, one should re-add any handlers for CoreInfo::coreDataChanged() and
+     * apply any existing information in the coreInfo() object.
+     *
+     * Only emitted on legacy (pre-0.13) cores.  Generally, one should use the
+     * CoreInfo::coreDataChanged() signal too.
+     */
+    void coreInfoResynchronized();
+
     //! The identity with the given ID has been newly created in core and client.
     /** \param id The ID of the newly created identity.
      */
@@ -258,6 +280,17 @@ private:
 
     void requestInitialBacklog();
 
+    /**
+     * Deletes and resynchronizes the CoreInfo object for legacy (pre-0.13) cores
+     *
+     * This provides compatibility with updating core information for legacy cores, and can be
+     * removed after protocol break.
+     *
+     * NOTE: On legacy (pre-0.13) cores, any existing connected signals will be destroyed and must
+     * be re-added after calling this, in addition to checking for existing data in coreInfo().
+     */
+    void requestLegacyCoreInfo();
+
     static void addNetwork(Network *);
 
     static QPointer<Client> instanceptr;
index e4019c7..cfa86ab 100644 (file)
@@ -41,3 +41,11 @@ void CoreInfo::setConnectedClientData(const int peerCount, const QVariantList pe
     _coreData["sessionConnectedClientData"] = peerData;
     setCoreData(_coreData);
 }
+
+void CoreInfo::reset()
+{
+    // Clear any stored data
+    _coreData.clear();
+    // Propagate changes to listeners
+    emit coreDataChanged(_coreData);
+}
index 26879d4..284336c 100644 (file)
@@ -39,7 +39,15 @@ public:
 
     void setConnectedClientData(int, QVariantList);
 
+    /**
+     * Reset the core info state, clearing anything saved
+     */
+    void reset();
+
 signals:
+    /**
+     * Signals that core information has changed
+     */
     void coreDataChanged(QVariantMap);
 
 public slots:
index 50ea82a..952f8fa 100644 (file)
@@ -138,6 +138,7 @@ public:
         EcdsaCertfpKeys,          ///< ECDSA keys for CertFP in identities
 #endif
         LongMessageId,            ///< 64-bit IDs for messages
+        SyncedCoreInfo,           ///< CoreInfo dynamically updated using signals
     };
     Q_ENUMS(Feature)
 
index e80450f..063e7a4 100644 (file)
 
 CoreInfoDlg::CoreInfoDlg(QWidget *parent) : QDialog(parent) {
     ui.setupUi(this);
-    CoreInfo *coreInfo = Client::coreInfo();
-    connect(coreInfo, SIGNAL(coreDataChanged(const QVariantMap &)), this, SLOT(coreInfoChanged(const QVariantMap &)));
 
-    coreInfoChanged(coreInfo->coreData());
+    // Listen for resynchronization events (pre-0.13 cores only)
+    connect(Client::instance(), SIGNAL(coreInfoResynchronized()),
+            this, SLOT(coreInfoResynchronized()));
+
+    // Update legacy core info for Quassel cores earlier than 0.13.  This does nothing on modern
+    // cores.
+    Client::refreshLegacyCoreInfo();
+
+    // Display existing core info, set up signal handlers
+    coreInfoResynchronized();
 
     // Warning icon
     ui.coreUnsupportedIcon->setPixmap(icon::get("dialog-warning").pixmap(16));
@@ -41,10 +48,40 @@ CoreInfoDlg::CoreInfoDlg(QWidget *parent) : QDialog(parent) {
 }
 
 
+void CoreInfoDlg::coreInfoResynchronized() {
+    // CoreInfo object has been recreated, or this is the first time the dialog's been shown
+
+    CoreInfo *coreInfo = Client::coreInfo();
+    // Listen for changes to core information
+    connect(coreInfo, SIGNAL(coreDataChanged(const QVariantMap &)),
+            this, SLOT(coreInfoChanged(const QVariantMap &)));
+
+    // Update with any known core information set before connecting the signal.  This is needed for
+    // both modern (0.13+) and legacy cores.
+    coreInfoChanged(coreInfo->coreData());
+}
+
+
 void CoreInfoDlg::coreInfoChanged(const QVariantMap &coreInfo) {
-    ui.labelCoreVersion->setText(coreInfo["quasselVersion"].toString());
-    ui.labelCoreVersionDate->setText(coreInfo["quasselBuildDate"].toString()); // "BuildDate" for compatibility
-    ui.labelClientCount->setNum(coreInfo["sessionConnectedClients"].toInt());
+    if(coreInfo.isEmpty()) {
+        // We're missing data for some reason
+        if (Client::isConnected()) {
+            // Core info is entirely empty despite being connected.  Something's probably wrong.
+            ui.labelCoreVersion->setText(tr("Unknown"));
+            ui.labelCoreVersionDate->setText(tr("Unknown"));
+        } else {
+            // We're disconnected.  Mark as such.
+            ui.labelCoreVersion->setText(tr("Disconnected from core"));
+            ui.labelCoreVersionDate->setText(tr("Not available"));
+        }
+        ui.labelClientCount->setNum(0);
+        // Don't return, allow the code below to remove any existing session widgets
+    } else {
+        ui.labelCoreVersion->setText(coreInfo["quasselVersion"].toString());
+        // "BuildDate" for compatibility
+        ui.labelCoreVersionDate->setText(coreInfo["quasselBuildDate"].toString());
+        ui.labelClientCount->setNum(coreInfo["sessionConnectedClients"].toInt());
+    }
 
     auto coreSessionSupported = false;
     auto ids = _widgets.keys();
@@ -80,14 +117,26 @@ void CoreInfoDlg::coreInfoChanged(const QVariantMap &coreInfo) {
 
     ui.coreSessionScrollArea->setVisible(coreSessionSupported);
 
-    // Hide the information bar when core sessions are supported
-    ui.coreUnsupportedWidget->setVisible(!coreSessionSupported);
+    // Hide the information bar when core sessions are supported or when disconnected
+    ui.coreUnsupportedWidget->setVisible(
+                !(coreSessionSupported || Client::isConnected() == false));
+
+    // Update uptime for immediate display, don't wait for the timer
+    updateUptime();
 }
 
 
 void CoreInfoDlg::updateUptime() {
     CoreInfo *coreInfo = Client::coreInfo();
-    if (coreInfo) {
+
+    if (!Client::isConnected()) {
+        // Not connected, don't bother trying to calculate the uptime
+        ui.labelUptime->setText(tr("Not available"));
+    } else if (coreInfo->coreData().isEmpty()) {
+        // Core info is entirely empty despite being connected.  Something's probably wrong.
+        ui.labelUptime->setText(tr("Unknown"));
+    } else {
+        // Connected, format the uptime for display
         QDateTime startTime = coreInfo->at("startTime").toDateTime();
 
         int64_t uptime = startTime.secsTo(QDateTime::currentDateTime().toUTC());
index 15140f7..0636534 100644 (file)
@@ -39,6 +39,13 @@ protected:
     void timerEvent(QTimerEvent *) override { updateUptime(); }
 
 private slots:
+    /**
+     * Handler for recreation of CoreInfo object, including first-time setup
+     *
+     * Applies existing CoreInfo information to the dialog, too.
+     */
+    void coreInfoResynchronized();
+
     void on_closeButton_clicked() { reject(); }
     void updateUptime();
     void disconnectClicked(int peerId);