From b8198f20c36ea29f7cc24b986c2d8e5819bac2e2 Mon Sep 17 00:00:00 2001
From: HifiExperiments <thingsandstuffblog@gmail.com>
Date: Mon, 21 Oct 2024 16:05:26 -0700
Subject: [PATCH] warning when avatar bookmarks JSON is malformed

---
 interface/src/Application.cpp | 34 +++++++++++++++++++++++-----------
 interface/src/Bookmarks.cpp   | 15 +++++++++------
 interface/src/Bookmarks.h     |  6 +++++-
 3 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 538b17f3a6..f77ee8a617 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1741,17 +1741,19 @@ void Application::idle() {
 #endif
 
 #ifdef Q_OS_WIN
-    // If tracing is enabled then monitor the CPU in a separate thread
-    static std::once_flag once;
-    std::call_once(once, [&] {
-        if (trace_app().isDebugEnabled()) {
-            QThread* cpuMonitorThread = new QThread(qApp);
-            cpuMonitorThread->setObjectName("cpuMonitorThread");
-            QObject::connect(cpuMonitorThread, &QThread::started, [this] { setupCpuMonitorThread(); });
-            QObject::connect(qApp, &QCoreApplication::aboutToQuit, cpuMonitorThread, &QThread::quit);
-            cpuMonitorThread->start();
-        }
-    });
+    {
+        // If tracing is enabled then monitor the CPU in a separate thread
+        static std::once_flag once;
+        std::call_once(once, [&] {
+            if (trace_app().isDebugEnabled()) {
+                QThread* cpuMonitorThread = new QThread(qApp);
+                cpuMonitorThread->setObjectName("cpuMonitorThread");
+                QObject::connect(cpuMonitorThread, &QThread::started, [this] { setupCpuMonitorThread(); });
+                QObject::connect(qApp, &QCoreApplication::aboutToQuit, cpuMonitorThread, &QThread::quit);
+                cpuMonitorThread->start();
+            }
+        });
+    }
 #endif
 
     auto displayPlugin = getActiveDisplayPlugin();
@@ -1880,6 +1882,16 @@ void Application::idle() {
     _overlayConductor.update(secondsSinceLastUpdate);
 
     _gameLoopCounter.increment();
+
+    {
+        static std::once_flag once;
+        std::call_once(once, [] {
+            const QString& bookmarksError = DependencyManager::get<AvatarBookmarks>()->getBookmarkError();
+            if (!bookmarksError.isEmpty()) {
+                OffscreenUi::asyncWarning("Avatar Bookmarks Error", "JSON parse error: " + bookmarksError, QMessageBox::Ok, QMessageBox::Ok);
+            }
+        });
+    }
 }
 
 void Application::update(float deltaTime) {
diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp
index 1ead451cd0..077a933a13 100644
--- a/interface/src/Bookmarks.cpp
+++ b/interface/src/Bookmarks.cpp
@@ -4,6 +4,7 @@
 //
 //  Created by David Rowe on 13 Jan 2015.
 //  Copyright 2015 High Fidelity, Inc.
+//  Copyright 2024 Overte e.V.
 //
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -24,10 +25,6 @@
 #include "Menu.h"
 #include "InterfaceLogging.h"
 
-Bookmarks::Bookmarks() :
-_isMenuSorted(false)
-{
-}
 void Bookmarks::deleteBookmark() {
     QStringList bookmarkList;
     QList<QAction*> menuItems = _bookmarksMenu->actions();
@@ -154,8 +151,14 @@ void Bookmarks::readFromFile() {
     }
 
     QByteArray data = loadFile.readAll();
-    QJsonDocument json(QJsonDocument::fromJson(data));
-    _bookmarks = json.object().toVariantMap();
+    QJsonParseError error;
+    QJsonDocument json = QJsonDocument::fromJson(data, &error);
+
+    if (json.isNull()) {
+        _bookmarkError = error.errorString();
+    } else {
+        _bookmarks = json.object().toVariantMap();
+    }
 }
 
 void Bookmarks::persistToFile() {
diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h
index f0d2a421fa..8d8aa9d778 100644
--- a/interface/src/Bookmarks.h
+++ b/interface/src/Bookmarks.h
@@ -4,6 +4,7 @@
 //
 //  Created by David Rowe on 13 Jan 2015.
 //  Copyright 2015 High Fidelity, Inc.
+//  Copyright 2024 Overte e.V.
 //
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -25,12 +26,14 @@ class Bookmarks: public QObject {
     Q_OBJECT
 
 public:
-    Bookmarks();
+    Bookmarks() : _isMenuSorted(false) {}
 
     virtual void setupMenus(Menu* menubar, MenuWrapper* menu) = 0;
     void insert(const QString& name, const QVariant& address);  // Overwrites any existing entry with same name.
     QString addressForBookmark(const QString& name) const;
 
+    const QString& getBookmarkError() const { return _bookmarkError; }
+
 protected:
     void deleteBookmark(const QString& bookmarkName);
 
@@ -45,6 +48,7 @@ protected:
     void remove(const QString& name);
 
     QVariantMap _bookmarks;  // { name: url, ... }
+    QString _bookmarkError;
     QPointer<MenuWrapper> _bookmarksMenu;
     QPointer<QAction> _deleteBookmarksAction;
     QString _bookmarksFilename;