From c4bcc38c53ace988cce4709beb869de7ce7aea8c Mon Sep 17 00:00:00 2001 From: Romain LE JEUNE Date: Wed, 26 Jul 2023 17:09:16 +0200 Subject: [PATCH] feat(GODT-2793): Feed the bug report body with the answered questions. --- .../bridge-gui/bridge-gui/QMLBackend.cpp | 2 +- .../bridge-gui/bridge-gui/QMLBackend.h | 2 +- .../bridge-gui/qml/BugQuestionView.qml | 13 +++---- .../bridge-gui/qml/BugReportFlow.qml | 3 +- .../bridge-gui/qml/BugReportView.qml | 36 ++++++------------- .../bridge-gui/qml/QuestionItem.qml | 29 +++++++++++++-- .../qml/Resources/bug_report_flow.json | 6 ++-- .../bridgepp/Test/TestBugReportFlow.cpp | 4 +-- .../bridgepp/BugReportFlow/BugReportFlow.cpp | 17 +++++---- 9 files changed, 61 insertions(+), 51 deletions(-) diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp index 86c6e07a..073d5704 100644 --- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp +++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.cpp @@ -247,7 +247,7 @@ void QMLBackend::setQuestionAnswer(quint8 questionId, QString const &answer) { /// \param[in] categoryId The id of the question set. /// \return concatenate answers for set of questions. //**************************************************************************************************************************************************** -QString QMLBackend::collectAnswer(quint8 categoryId) const { +QString QMLBackend::collectAnswers(quint8 categoryId) const { return reportFlow_.collectAnswers(categoryId); } diff --git a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h index dac6b18a..83f70e35 100644 --- a/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h +++ b/internal/frontend/bridge-gui/bridge-gui/QMLBackend.h @@ -60,7 +60,7 @@ public: // member functions. Q_INVOKABLE bool areSameFileOrFolder(QUrl const &lhs, QUrl const &rhs) const; ///< Check if two local URL point to the same file. Q_INVOKABLE QVariantList getQuestionSet(quint8 categoryId) const; ///< Retrieve the set of question for a given bug category. Q_INVOKABLE void setQuestionAnswer(quint8 questionId, QString const &answer); ///< Feed an answer for a given question. - Q_INVOKABLE QString collectAnswer(quint8 categoryId) const; ///< Collect answer for a given set of questions. + Q_INVOKABLE QString collectAnswers(quint8 categoryId) const; ///< Collect answer for a given set of questions. public: // Qt/QML properties. Note that the NOTIFY-er signal is required even for read-only properties (QML warning otherwise) Q_PROPERTY(bool showOnStartup READ showOnStartup NOTIFY showOnStartupChanged) diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/BugQuestionView.qml b/internal/frontend/bridge-gui/bridge-gui/qml/BugQuestionView.qml index 1c8c4895..ffac25ae 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/BugQuestionView.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/BugQuestionView.qml @@ -24,9 +24,6 @@ SettingsView { signal questionAnswered - function setDefaultValue() { - } - function setCategoryId(catId) { root.categoryId = catId; } @@ -38,11 +35,6 @@ SettingsView { onCategoryIdChanged: { root.questionSet = Backend.getQuestionSet(root.categoryId) - root.setDefaultValue(); - } - - onVisibleChanged: { - root.setDefaultValue(); } Label { @@ -67,7 +59,12 @@ SettingsView { tips: root.questions[modelData].tips ? root.questions[modelData].tips : "" label: root.questions[modelData].label ? root.questions[modelData].label : "" type: root.questions[modelData].type + mandatory: root.questions[modelData].mandatory ? root.questions[modelData].mandatory : false answerList: root.questions[modelData].answerList ? root.questions[modelData].answerList : [] + + onAnswerChanged:{ + Backend.setQuestionAnswer(modelData, answer); + } } } // fill height so the footer label will always be attached to the bottom diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/BugReportFlow.qml b/internal/frontend/bridge-gui/bridge-gui/qml/BugReportFlow.qml index 57207055..b309178f 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/BugReportFlow.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/BugReportFlow.qml @@ -35,10 +35,11 @@ Item { bugReportFlow.currentIndex = 0; } function showBugQuestion() { - bugReportFlow.currentIndex = 1; bugQuestion.setCategoryId(root.categoryId); + bugReportFlow.currentIndex = 1; } function showBugReport() { + bugReport.setCategoryId(root.categoryId); bugReportFlow.currentIndex = 2; } diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/BugReportView.qml b/internal/frontend/bridge-gui/bridge-gui/qml/BugReportView.qml index dbeaeabf..0434d43f 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/BugReportView.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/BugReportView.qml @@ -19,6 +19,7 @@ SettingsView { id: root property var selectedAddress + property var categoryId:-1 signal bugReportWasSent @@ -26,15 +27,18 @@ SettingsView { const reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/; return reEmail.test(text); } + + function setCategoryId(catId) { + root.categoryId = catId; + } + function setDefaultValue() { - description.text = ""; + description.text = Backend.collectAnswers(root.categoryId); address.text = root.selectedAddress; emailClient.text = Backend.currentEmailClient; includeLogs.checked = true; } - function setDescription(message) { - description.text = message; - } + function submit() { sendButton.loading = true; Backend.reportBug(description.text, address.text, emailClient.text, includeLogs.checked); @@ -54,39 +58,19 @@ SettingsView { TextArea { id: description - property int _maxLength: 800 - property int _minLength: 150 - KeyNavigation.priority: KeyNavigation.BeforeItem KeyNavigation.tab: address Layout.fillHeight: true Layout.fillWidth: true Layout.minimumHeight: heightForLinesVisible(4) colorScheme: root.colorScheme - hint: description.text.length + "/" + _maxLength // set implicitHeight to explicit height because se don't // want TextArea implicitHeight (which is height of all text) // to be considered in SettingsView internal scroll view implicitHeight: height - label: qsTr("Description") - placeholderText: qsTr("Tell us what went wrong or isn't working (min. %1 characters).").arg(_minLength) - validator: function (text) { - if (description.text.length < description._minLength) { - return qsTr("Enter a problem description (min. %1 characters).").arg(_minLength); - } - if (description.text.length > description._maxLength) { - return qsTr("Enter a problem description (max. %1 characters).").arg(_maxLength); - } - return; - } - - onTextChanged: { - // Rise max length error immediately while typing - if (description.text.length > description._maxLength) { - validate(); - } - } + label: qsTr("Your answers") + readOnly : true } TextField { id: address diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/QuestionItem.qml b/internal/frontend/bridge-gui/bridge-gui/qml/QuestionItem.qml index 92a7d114..38d48435 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/QuestionItem.qml +++ b/internal/frontend/bridge-gui/bridge-gui/qml/QuestionItem.qml @@ -31,6 +31,7 @@ Item { property string text: "" property string tips: "" property string label: "" + property bool mandatory: false property var type: QuestionItem.InputType.TextInput property var answerList: ListModel{} @@ -67,8 +68,30 @@ Item { Layout.minimumHeight: root.type === QuestionItem.InputType.TextInput ? heightForLinesVisible(2) : 0 colorScheme: root.colorScheme + property int _maxLength: 400 + property int _minLength: 10 + label: qsTr(root.label) - placeholderText: qsTr(root.tips) + hint: mandatory ? textInput.text.length + "/" + _maxLength : "" + placeholderText: mandatory ? qsTr("%1... (min. %2 characters)").arg(root.text).arg(_minLength) : "" + + validator: function (text) { + if (!mandatory) + return; + if (textInput.text.length < textInput._minLength) { + return qsTr("min. %1 characters").arg(_minLength); + } + if (textInput.text.length > textInput._maxLength) { + return qsTr("max. %1 characters").arg(_maxLength); + } + return; + } + onTextChanged: { + // Rise max length error immediately while typing if mandatory field + if (mandatory && textInput.text.length > textInput._maxLength) { + validate(); + } + } visible: root.type === QuestionItem.InputType.TextInput } @@ -96,10 +119,10 @@ Item { var str = ""; for (var i = 0; i < buttons.length; ++i) { if (buttons[i].checked) { - str += buttons[i].text + " "; + str += buttons[i].text + ", "; } } - return str; + return str.slice(0, -2); } } Repeater { diff --git a/internal/frontend/bridge-gui/bridge-gui/qml/Resources/bug_report_flow.json b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/bug_report_flow.json index b42694a8..ce9d2d8c 100644 --- a/internal/frontend/bridge-gui/bridge-gui/qml/Resources/bug_report_flow.json +++ b/internal/frontend/bridge-gui/bridge-gui/qml/Resources/bug_report_flow.json @@ -35,13 +35,15 @@ "id": 0, "text": "What happened?", "tips": "Expected behavior", - "type": 1 + "type": 1, + "mandatory": true }, { "id": 1, "text": "What did you want or expect to happen?", "tips": "Result", - "type": 1 + "type": 1, + "mandatory": true }, { "id": 2, diff --git a/internal/frontend/bridge-gui/bridgepp/Test/TestBugReportFlow.cpp b/internal/frontend/bridge-gui/bridgepp/Test/TestBugReportFlow.cpp index eaaf9b73..ef5116af 100644 --- a/internal/frontend/bridge-gui/bridgepp/Test/TestBugReportFlow.cpp +++ b/internal/frontend/bridge-gui/bridgepp/Test/TestBugReportFlow.cpp @@ -129,10 +129,10 @@ TEST_F(BugReportFlowFixture, validFile) { EXPECT_TRUE(flow_.setAnswer(0, "pwet")); EXPECT_FALSE(flow_.setAnswer(1, "pwet")); qDebug() << flow_.collectAnswers(0); - EXPECT_EQ(flow_.collectAnswers(0), " - What happened? pwet\n\r"); + EXPECT_EQ(flow_.collectAnswers(0), "Category: I can't receive mail\n\r - What happened?\n\rpwet\n\r"); EXPECT_EQ(flow_.collectAnswers(1), ""); flow_.clearAnswers(); - EXPECT_EQ(flow_.collectAnswers(0), " - What happened? \n\r"); + EXPECT_EQ(flow_.collectAnswers(0), "Category: I can't receive mail\n\r"); } diff --git a/internal/frontend/bridge-gui/bridgepp/bridgepp/BugReportFlow/BugReportFlow.cpp b/internal/frontend/bridge-gui/bridgepp/bridgepp/BugReportFlow/BugReportFlow.cpp index 7deebefb..105b3542 100644 --- a/internal/frontend/bridge-gui/bridgepp/bridgepp/BugReportFlow/BugReportFlow.cpp +++ b/internal/frontend/bridge-gui/bridgepp/bridgepp/BugReportFlow/BugReportFlow.cpp @@ -97,14 +97,17 @@ bool BugReportFlow::setAnswer(quint8 questionId, QString const &answer) { //**************************************************************************************************************************************************** QString BugReportFlow::collectAnswers(quint8 categoryId) const { QString answers; + if (categoryId > categories_.count() - 1) + return answers; + answers += "Category: " + categories_[categoryId] + "\n\r"; QVariantList sets = this->questionSet(categoryId); - foreach(const QVariant& var, sets) { - answers += " - "; - answers += questions_[var.toInt()].toMap()["text"].toString(); - answers += " "; - answers += answers_[var.toInt()]; - answers += "\n\r"; + for (QVariant const &var: sets) { + const QString& answer = answers_[var.toInt()]; + if (answer.isEmpty()) + continue; + answers += " - " + questions_[var.toInt()].toMap()["text"].toString() + "\n\r"; + answers += answer + "\n\r"; } return answers; } @@ -129,7 +132,7 @@ bool BugReportFlow::parseFile() { QJsonObject data = getJsonDataObj(getJsonRootObj()); QJsonArray categoriesJson = data.value("categories").toArray(); - foreach (const QJsonValue & v, categoriesJson) { + for (const QJsonValueRef &v : categoriesJson) { categories_.append(v.toObject()["name"].toString()); questionsSet_.append(v.toObject()["questions"].toArray().toVariantList()); }