Browse Source

frontend: Add LogUploadDialog class

PatTheMav 7 months ago
parent
commit
e4e3035661

+ 27 - 0
frontend/OBSApp.cpp

@@ -1213,6 +1213,33 @@ const char *OBSApp::GetLastCrashLog() const
 	return lastCrashLogFile.c_str();
 }
 
+void OBSApp::uploadLastAppLog() const
+{
+	return;
+}
+
+void OBSApp::uploadCurrentAppLog() const
+{
+	return;
+}
+
+void OBSApp::uploadLastCrashLog()
+{
+	return;
+}
+
+OBS::LogFileState OBSApp::getLogFileState(OBS::LogFileType type) const
+{
+	switch (type) {
+	case OBS::LogFileType::CrashLog:
+	case OBS::LogFileType::CurrentAppLog:
+	case OBS::LogFileType::LastAppLog:
+		return OBS::LogFileState::New;
+	default:
+		return OBS::LogFileState::NoState;
+	}
+}
+
 bool OBSApp::TranslateString(const char *lookupVal, const char **out) const
 {
 	for (obs_frontend_translate_ui_cb cb : translatorHooks) {

+ 13 - 0
frontend/OBSApp.hpp

@@ -41,6 +41,11 @@ Q_DECLARE_METATYPE(VoidFunc)
 class QFileSystemWatcher;
 class QSocketNotifier;
 
+namespace OBS {
+enum class LogFileType { NoType, CurrentAppLog, LastAppLog, CrashLog };
+enum class LogFileState { NoState, New, Uploaded };
+} // namespace OBS
+
 struct UpdateBranch {
 	QString name;
 	QString display_name;
@@ -153,6 +158,11 @@ public:
 	const char *GetCurrentLog() const;
 
 	const char *GetLastCrashLog() const;
+	void uploadLastAppLog() const;
+	void uploadCurrentAppLog() const;
+	void uploadLastCrashLog();
+
+	OBS::LogFileState getLogFileState(OBS::LogFileType type) const;
 
 	std::string GetVersionString(bool platform = true) const;
 	bool IsPortableMode();
@@ -195,6 +205,9 @@ public slots:
 
 signals:
 	void StyleChanged();
+
+	void logUploadFinished(OBS::LogFileType, const QString &fileUrl);
+	void logUploadFailed(OBS::LogFileType, const QString &errorMessage);
 };
 
 int GetAppConfigPath(char *path, size_t size, const char *name);

+ 2 - 0
frontend/cmake/ui-dialogs.cmake

@@ -7,6 +7,8 @@ target_link_libraries(obs-studio PRIVATE OBS::properties-view)
 target_sources(
   obs-studio
   PRIVATE
+    dialogs/LogUploadDialog.cpp
+    dialogs/LogUploadDialog.hpp
     dialogs/NameDialog.cpp
     dialogs/NameDialog.hpp
     dialogs/OAuthLogin.cpp

+ 1 - 0
frontend/cmake/ui-qt.cmake

@@ -31,6 +31,7 @@ target_sources(
     forms/AutoConfigVideoPage.ui
     forms/ColorSelect.ui
     forms/obs.qrc
+    forms/LogUploadDialog.ui
     forms/OBSAbout.ui
     forms/OBSAdvAudio.ui
     forms/OBSBasic.ui

+ 13 - 0
frontend/data/locale/en-US.ini

@@ -468,6 +468,19 @@ LogReturnDialog.CopyURL="Copy URL"
 LogReturnDialog.AnalyzeURL="Analyze"
 LogReturnDialog.ErrorUploadingLog="Error uploading log file"
 
+# Log Upload Dialog for Application- and Crash-Logs
+LogUploadDialog.Title="OBS Studio Log File Upload"
+LogUploadDialog.Labels.PrivacyNotice="Please read the <a href='https://obsproject.com/privacy-policy'>Privacy Policy</a> and its section regarding file uploads before uploading any files."
+LogUploadDialog.Labels.Progress="Log upload in progress - please wait..."
+LogUploadDialog.Labels.Description.AppLog="Your log file has been uploaded. You can now share the URL for debugging or support purposes."
+LogUploadDialog.Labels.Description.CrashLog="Your crash report has been uploaded. You can now share the URL for debugging purposes."
+LogUploadDialog.Buttons.ConfirmUpload="Continue Upload..."
+LogUploadDialog.Buttons.CopyURL="Copy Log URL"
+LogUploadDialog.Buttons.AnalyzeURL="Analyze Log File"
+LogUploadDialog.Buttons.RetryButton="Retry Upload..."
+LogUploadDialog.Errors.Template="An error occurred while trying to upload the file:\n\n%1"
+LogUploadDialog.Errors.NoLogFile="No file to upload found or file was empty."
+
 # remux dialog
 Remux.SourceFile="OBS Recording"
 Remux.TargetFile="Target File"

+ 151 - 0
frontend/dialogs/LogUploadDialog.cpp

@@ -0,0 +1,151 @@
+/******************************************************************************
+    Copyright (C) 2023 by Lain Bailey <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "LogUploadDialog.hpp"
+
+#include <OBSApp.hpp>
+
+#include <QClipboard>
+#include <QDesktopServices>
+#include <QTimer>
+#include <QUrlQuery>
+
+#include "moc_LogUploadDialog.cpp"
+
+struct DialogPage {
+	static constexpr int Start = 0;
+	static constexpr int Success = 1;
+	static constexpr int Error = 2;
+};
+
+namespace OBS {
+LogUploadDialog::LogUploadDialog(QWidget *parent, LogFileType uploadType)
+	: QDialog(parent),
+	  ui(new Ui::LogUploadDialog),
+	  uploadType_(uploadType)
+{
+	uploadStatusTimer_ = std::make_unique<QTimer>(this);
+
+	setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+	ui->setupUi(this);
+
+	ui->stackedWidget->setCurrentIndex(DialogPage::Start);
+
+	ui->privacyNotice->setText(
+		QTStr("LogUploadDialog.Labels.PrivacyNotice").arg(QTStr("LogUploadDialog.Buttons.ConfirmUpload")));
+
+	if (uploadType_ == LogFileType::CrashLog) {
+		ui->analyzeURL->hide();
+		ui->description->setText(Str("LogUploadDialog.Labels.Description.CrashLog"));
+	}
+
+	connect(ui->confirmUploadButton, &QPushButton::clicked, this, &LogUploadDialog::startLogUpload);
+	connect(ui->retryButton, &QPushButton::clicked, this, &LogUploadDialog::startLogUpload);
+	connect(ui->copyURL, &QPushButton::clicked, this, &LogUploadDialog::copyToClipBoard);
+	connect(ui->analyzeURL, &QPushButton::clicked, this, &LogUploadDialog::openAnalyzeURL);
+	connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::reject);
+	connect(ui->retryCloseButton, &QPushButton::clicked, this, &QDialog::reject);
+
+	OBSApp *app = App();
+	connect(app, &OBSApp::logUploadFinished, this, &LogUploadDialog::handleUploadSuccess);
+	connect(app, &OBSApp::logUploadFailed, this, &LogUploadDialog::handleUploadFailure);
+
+	installEventFilter(CreateShortcutFilter());
+
+	LogFileState uploadState = app->getLogFileState(uploadType);
+	switch (uploadState) {
+	case LogFileState::Uploaded:
+		startLogUpload();
+		break;
+	default:
+		break;
+	}
+}
+
+void LogUploadDialog::startLogUpload()
+{
+	if (uploadType_ == LogFileType::NoType) {
+		return;
+	}
+
+	ui->confirmUploadButton->setEnabled(false);
+
+	connect(uploadStatusTimer_.get(), &QTimer::timeout, this, [this]() {
+		ui->uploadProgress->setText(Str("LogUploadDialog.Labels.Progress"));
+		setCursor(Qt::WaitCursor);
+	});
+
+	uploadStatusTimer_->setSingleShot(true);
+	uploadStatusTimer_->start(1000);
+
+	OBSApp *app = App();
+
+	switch (uploadType_) {
+	case LogFileType::CrashLog:
+		app->uploadLastCrashLog();
+		break;
+	case LogFileType::CurrentAppLog:
+		app->uploadCurrentAppLog();
+		break;
+	case LogFileType::LastAppLog:
+		app->uploadLastAppLog();
+		break;
+	default:
+		break;
+	}
+}
+
+void LogUploadDialog::handleUploadSuccess(LogFileType, const QString &fileURL)
+{
+	uploadStatusTimer_->stop();
+	unsetCursor();
+	ui->confirmUploadButton->setEnabled(true);
+	ui->uploadProgress->setText("");
+
+	ui->urlEdit->setText(fileURL);
+	ui->stackedWidget->setCurrentIndex(DialogPage::Success);
+}
+
+void LogUploadDialog::handleUploadFailure(LogFileType, const QString &errorMessage)
+{
+	uploadStatusTimer_->stop();
+	unsetCursor();
+	ui->confirmUploadButton->setEnabled(true);
+	ui->uploadProgress->setText("");
+
+	QString errorDescription = QTStr("LogUploadDialog.Errors.Template").arg(errorMessage);
+	ui->uploadErrorMessage->setText(errorDescription);
+	ui->stackedWidget->setCurrentIndex(DialogPage::Error);
+}
+
+void LogUploadDialog::copyToClipBoard() const
+{
+	QClipboard *clipboard = QApplication::clipboard();
+	clipboard->setText(ui->urlEdit->text());
+}
+
+void LogUploadDialog::openAnalyzeURL() const
+{
+	QUrlQuery queryParameters;
+	queryParameters.addQueryItem("log_url", QUrl::toPercentEncoding(ui->urlEdit->text()));
+	QUrl analyzerUrl = QUrl("https://obsproject.com/tools/analyzer", QUrl::TolerantMode);
+
+	analyzerUrl.setQuery(queryParameters);
+
+	QDesktopServices::openUrl(analyzerUrl);
+}
+} // namespace OBS

+ 52 - 0
frontend/dialogs/LogUploadDialog.hpp

@@ -0,0 +1,52 @@
+/******************************************************************************
+    Copyright (C) 2023 by Lain Bailey <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#pragma once
+
+#include "ui_LogUploadDialog.h"
+
+#include <QDialog>
+
+#include <filesystem>
+
+class QTimer;
+
+namespace OBS {
+
+enum class LogFileType;
+
+class LogUploadDialog : public QDialog {
+	Q_OBJECT
+
+private:
+	std::unique_ptr<Ui::LogUploadDialog> ui;
+	std::unique_ptr<QTimer> uploadStatusTimer_;
+
+	LogFileType uploadType_;
+
+public:
+	LogUploadDialog(QWidget *parent, LogFileType uploadType);
+
+private slots:
+	void startLogUpload();
+	void handleUploadSuccess(LogFileType uploadType, const QString &fileURL);
+	void handleUploadFailure(LogFileType uploadType, const QString &errorMessage);
+
+	void copyToClipBoard() const;
+	void openAnalyzeURL() const;
+};
+} // namespace OBS

+ 306 - 0
frontend/forms/LogUploadDialog.ui

@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LogUploadDialog</class>
+ <widget class="QDialog" name="LogUploadDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>600</width>
+    <height>140</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>LogUploadDialog.Title</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_LogUploadDialog">
+   <property name="sizeConstraint">
+    <enum>QLayout::SetFixedSize</enum>
+   </property>
+   <item>
+    <widget class="QStackedWidget" name="stackedWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="confirmUploadPage">
+      <layout class="QVBoxLayout" name="verticalLayout_confirmUploadPage">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_privacyNotice">
+         <item>
+          <widget class="QLabel" name="privacyNotice">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>LogUploadDialog.Labels.PrivacyNotice</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+           <property name="openExternalLinks">
+            <bool>true</bool>
+           </property>
+           <property name="textInteractionFlags">
+            <set>Qt::TextBrowserInteraction</set>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_uploadProgress">
+         <property name="leftMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="uploadProgress">
+           <property name="enabled">
+            <bool>true</bool>
+           </property>
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <spacer name="horizontalSpacer">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QPushButton" name="confirmUploadButton">
+           <property name="text">
+            <string>LogUploadDialog.Buttons.ConfirmUpload</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="closeButton">
+           <property name="text">
+            <string>Cancel</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="uploadSucceededPage">
+      <layout class="QVBoxLayout" name="verticalLayout_uploadSucceededPage">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_4">
+         <property name="bottomMargin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QLabel" name="description">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="text">
+            <string>LogUploadDialog.Labels.Description.AppLog</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+           </property>
+           <property name="wordWrap">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout">
+         <item>
+          <widget class="QLineEdit" name="urlEdit">
+           <property name="text">
+            <string notr="true"/>
+           </property>
+           <property name="readOnly">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="copyURL">
+           <property name="text">
+            <string>LogUploadDialog.Buttons.CopyURL</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="analyzeURL">
+           <property name="text">
+            <string>LogUploadDialog.Buttons.AnalyzeURL</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <widget class="QDialogButtonBox" name="buttonBox">
+         <property name="standardButtons">
+          <set>QDialogButtonBox::Close</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="uploadFailedPage">
+      <layout class="QVBoxLayout" name="verticalLayout_uploadFailedPage">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QLabel" name="uploadErrorMessage">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string>LogUploadDialog.Labels.UploadFailMessage</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_5">
+         <item>
+          <spacer name="horizontalSpacer_2">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>10</width>
+             <height>2</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QPushButton" name="retryButton">
+           <property name="text">
+            <string>LogUploadDialog.Buttons.RetryButton</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="retryCloseButton">
+           <property name="text">
+            <string>Cancel</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>LogUploadDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>LogUploadDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>