mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 04:36:43 +00:00
feat(GODT-2793): Feed the bug report body with the answered questions.
This commit is contained in:
committed by
Romain Le Jeune
parent
2c2f816f3a
commit
c4bcc38c53
@ -247,7 +247,7 @@ void QMLBackend::setQuestionAnswer(quint8 questionId, QString const &answer) {
|
|||||||
/// \param[in] categoryId The id of the question set.
|
/// \param[in] categoryId The id of the question set.
|
||||||
/// \return concatenate answers for set of questions.
|
/// \return concatenate answers for set of questions.
|
||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
QString QMLBackend::collectAnswer(quint8 categoryId) const {
|
QString QMLBackend::collectAnswers(quint8 categoryId) const {
|
||||||
return reportFlow_.collectAnswers(categoryId);
|
return reportFlow_.collectAnswers(categoryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 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 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 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)
|
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)
|
Q_PROPERTY(bool showOnStartup READ showOnStartup NOTIFY showOnStartupChanged)
|
||||||
|
|||||||
@ -24,9 +24,6 @@ SettingsView {
|
|||||||
|
|
||||||
signal questionAnswered
|
signal questionAnswered
|
||||||
|
|
||||||
function setDefaultValue() {
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCategoryId(catId) {
|
function setCategoryId(catId) {
|
||||||
root.categoryId = catId;
|
root.categoryId = catId;
|
||||||
}
|
}
|
||||||
@ -38,11 +35,6 @@ SettingsView {
|
|||||||
|
|
||||||
onCategoryIdChanged: {
|
onCategoryIdChanged: {
|
||||||
root.questionSet = Backend.getQuestionSet(root.categoryId)
|
root.questionSet = Backend.getQuestionSet(root.categoryId)
|
||||||
root.setDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
root.setDefaultValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@ -67,7 +59,12 @@ SettingsView {
|
|||||||
tips: root.questions[modelData].tips ? root.questions[modelData].tips : ""
|
tips: root.questions[modelData].tips ? root.questions[modelData].tips : ""
|
||||||
label: root.questions[modelData].label ? root.questions[modelData].label : ""
|
label: root.questions[modelData].label ? root.questions[modelData].label : ""
|
||||||
type: root.questions[modelData].type
|
type: root.questions[modelData].type
|
||||||
|
mandatory: root.questions[modelData].mandatory ? root.questions[modelData].mandatory : false
|
||||||
answerList: root.questions[modelData].answerList ? root.questions[modelData].answerList : []
|
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
|
// fill height so the footer label will always be attached to the bottom
|
||||||
|
|||||||
@ -35,10 +35,11 @@ Item {
|
|||||||
bugReportFlow.currentIndex = 0;
|
bugReportFlow.currentIndex = 0;
|
||||||
}
|
}
|
||||||
function showBugQuestion() {
|
function showBugQuestion() {
|
||||||
bugReportFlow.currentIndex = 1;
|
|
||||||
bugQuestion.setCategoryId(root.categoryId);
|
bugQuestion.setCategoryId(root.categoryId);
|
||||||
|
bugReportFlow.currentIndex = 1;
|
||||||
}
|
}
|
||||||
function showBugReport() {
|
function showBugReport() {
|
||||||
|
bugReport.setCategoryId(root.categoryId);
|
||||||
bugReportFlow.currentIndex = 2;
|
bugReportFlow.currentIndex = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ SettingsView {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var selectedAddress
|
property var selectedAddress
|
||||||
|
property var categoryId:-1
|
||||||
|
|
||||||
signal bugReportWasSent
|
signal bugReportWasSent
|
||||||
|
|
||||||
@ -26,15 +27,18 @@ SettingsView {
|
|||||||
const reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/;
|
const reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/;
|
||||||
return reEmail.test(text);
|
return reEmail.test(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCategoryId(catId) {
|
||||||
|
root.categoryId = catId;
|
||||||
|
}
|
||||||
|
|
||||||
function setDefaultValue() {
|
function setDefaultValue() {
|
||||||
description.text = "";
|
description.text = Backend.collectAnswers(root.categoryId);
|
||||||
address.text = root.selectedAddress;
|
address.text = root.selectedAddress;
|
||||||
emailClient.text = Backend.currentEmailClient;
|
emailClient.text = Backend.currentEmailClient;
|
||||||
includeLogs.checked = true;
|
includeLogs.checked = true;
|
||||||
}
|
}
|
||||||
function setDescription(message) {
|
|
||||||
description.text = message;
|
|
||||||
}
|
|
||||||
function submit() {
|
function submit() {
|
||||||
sendButton.loading = true;
|
sendButton.loading = true;
|
||||||
Backend.reportBug(description.text, address.text, emailClient.text, includeLogs.checked);
|
Backend.reportBug(description.text, address.text, emailClient.text, includeLogs.checked);
|
||||||
@ -54,39 +58,19 @@ SettingsView {
|
|||||||
TextArea {
|
TextArea {
|
||||||
id: description
|
id: description
|
||||||
|
|
||||||
property int _maxLength: 800
|
|
||||||
property int _minLength: 150
|
|
||||||
|
|
||||||
KeyNavigation.priority: KeyNavigation.BeforeItem
|
KeyNavigation.priority: KeyNavigation.BeforeItem
|
||||||
KeyNavigation.tab: address
|
KeyNavigation.tab: address
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumHeight: heightForLinesVisible(4)
|
Layout.minimumHeight: heightForLinesVisible(4)
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
hint: description.text.length + "/" + _maxLength
|
|
||||||
|
|
||||||
// set implicitHeight to explicit height because se don't
|
// set implicitHeight to explicit height because se don't
|
||||||
// want TextArea implicitHeight (which is height of all text)
|
// want TextArea implicitHeight (which is height of all text)
|
||||||
// to be considered in SettingsView internal scroll view
|
// to be considered in SettingsView internal scroll view
|
||||||
implicitHeight: height
|
implicitHeight: height
|
||||||
label: qsTr("Description")
|
label: qsTr("Your answers")
|
||||||
placeholderText: qsTr("Tell us what went wrong or isn't working (min. %1 characters).").arg(_minLength)
|
readOnly : true
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: address
|
id: address
|
||||||
|
|||||||
@ -31,6 +31,7 @@ Item {
|
|||||||
property string text: ""
|
property string text: ""
|
||||||
property string tips: ""
|
property string tips: ""
|
||||||
property string label: ""
|
property string label: ""
|
||||||
|
property bool mandatory: false
|
||||||
property var type: QuestionItem.InputType.TextInput
|
property var type: QuestionItem.InputType.TextInput
|
||||||
property var answerList: ListModel{}
|
property var answerList: ListModel{}
|
||||||
|
|
||||||
@ -67,8 +68,30 @@ Item {
|
|||||||
Layout.minimumHeight: root.type === QuestionItem.InputType.TextInput ? heightForLinesVisible(2) : 0
|
Layout.minimumHeight: root.type === QuestionItem.InputType.TextInput ? heightForLinesVisible(2) : 0
|
||||||
colorScheme: root.colorScheme
|
colorScheme: root.colorScheme
|
||||||
|
|
||||||
|
property int _maxLength: 400
|
||||||
|
property int _minLength: 10
|
||||||
|
|
||||||
label: qsTr(root.label)
|
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
|
visible: root.type === QuestionItem.InputType.TextInput
|
||||||
}
|
}
|
||||||
@ -96,10 +119,10 @@ Item {
|
|||||||
var str = "";
|
var str = "";
|
||||||
for (var i = 0; i < buttons.length; ++i) {
|
for (var i = 0; i < buttons.length; ++i) {
|
||||||
if (buttons[i].checked) {
|
if (buttons[i].checked) {
|
||||||
str += buttons[i].text + " ";
|
str += buttons[i].text + ", ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return str;
|
return str.slice(0, -2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Repeater {
|
Repeater {
|
||||||
|
|||||||
@ -35,13 +35,15 @@
|
|||||||
"id": 0,
|
"id": 0,
|
||||||
"text": "What happened?",
|
"text": "What happened?",
|
||||||
"tips": "Expected behavior",
|
"tips": "Expected behavior",
|
||||||
"type": 1
|
"type": 1,
|
||||||
|
"mandatory": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"text": "What did you want or expect to happen?",
|
"text": "What did you want or expect to happen?",
|
||||||
"tips": "Result",
|
"tips": "Result",
|
||||||
"type": 1
|
"type": 1,
|
||||||
|
"mandatory": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
|
|||||||
@ -129,10 +129,10 @@ TEST_F(BugReportFlowFixture, validFile) {
|
|||||||
EXPECT_TRUE(flow_.setAnswer(0, "pwet"));
|
EXPECT_TRUE(flow_.setAnswer(0, "pwet"));
|
||||||
EXPECT_FALSE(flow_.setAnswer(1, "pwet"));
|
EXPECT_FALSE(flow_.setAnswer(1, "pwet"));
|
||||||
qDebug() << flow_.collectAnswers(0);
|
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), "");
|
EXPECT_EQ(flow_.collectAnswers(1), "");
|
||||||
flow_.clearAnswers();
|
flow_.clearAnswers();
|
||||||
EXPECT_EQ(flow_.collectAnswers(0), " - What happened? \n\r");
|
EXPECT_EQ(flow_.collectAnswers(0), "Category: I can't receive mail\n\r");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -97,14 +97,17 @@ bool BugReportFlow::setAnswer(quint8 questionId, QString const &answer) {
|
|||||||
//****************************************************************************************************************************************************
|
//****************************************************************************************************************************************************
|
||||||
QString BugReportFlow::collectAnswers(quint8 categoryId) const {
|
QString BugReportFlow::collectAnswers(quint8 categoryId) const {
|
||||||
QString answers;
|
QString answers;
|
||||||
|
if (categoryId > categories_.count() - 1)
|
||||||
|
return answers;
|
||||||
|
|
||||||
|
answers += "Category: " + categories_[categoryId] + "\n\r";
|
||||||
QVariantList sets = this->questionSet(categoryId);
|
QVariantList sets = this->questionSet(categoryId);
|
||||||
foreach(const QVariant& var, sets) {
|
for (QVariant const &var: sets) {
|
||||||
answers += " - ";
|
const QString& answer = answers_[var.toInt()];
|
||||||
answers += questions_[var.toInt()].toMap()["text"].toString();
|
if (answer.isEmpty())
|
||||||
answers += " ";
|
continue;
|
||||||
answers += answers_[var.toInt()];
|
answers += " - " + questions_[var.toInt()].toMap()["text"].toString() + "\n\r";
|
||||||
answers += "\n\r";
|
answers += answer + "\n\r";
|
||||||
}
|
}
|
||||||
return answers;
|
return answers;
|
||||||
}
|
}
|
||||||
@ -129,7 +132,7 @@ bool BugReportFlow::parseFile() {
|
|||||||
QJsonObject data = getJsonDataObj(getJsonRootObj());
|
QJsonObject data = getJsonDataObj(getJsonRootObj());
|
||||||
|
|
||||||
QJsonArray categoriesJson = data.value("categories").toArray();
|
QJsonArray categoriesJson = data.value("categories").toArray();
|
||||||
foreach (const QJsonValue & v, categoriesJson) {
|
for (const QJsonValueRef &v : categoriesJson) {
|
||||||
categories_.append(v.toObject()["name"].toString());
|
categories_.append(v.toObject()["name"].toString());
|
||||||
questionsSet_.append(v.toObject()["questions"].toArray().toVariantList());
|
questionsSet_.append(v.toObject()["questions"].toArray().toVariantList());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user