Adding a support submission button on CLI to assist in support (#3206)
* Adding a support submission button on CLI to assist in identifying issues * Fixes following rebase * Rename build_api to BuildApi.js10.9-maintenance
parent
1344baa59c
commit
902f979878
|
@ -77,6 +77,9 @@
|
||||||
"cancel": {
|
"cancel": {
|
||||||
"message": "Cancel"
|
"message": "Cancel"
|
||||||
},
|
},
|
||||||
|
"submit": {
|
||||||
|
"message": "Submit"
|
||||||
|
},
|
||||||
"autoConnectEnabled": {
|
"autoConnectEnabled": {
|
||||||
"message": "Auto-Connect: Enabled - Configurator automatically tries to connect when new port is detected"
|
"message": "Auto-Connect: Enabled - Configurator automatically tries to connect when new port is detected"
|
||||||
},
|
},
|
||||||
|
@ -533,15 +536,24 @@
|
||||||
"message" : "Configurator: <strong>{{configuratorVersion}}</strong>",
|
"message" : "Configurator: <strong>{{configuratorVersion}}</strong>",
|
||||||
"description": "Message that appears in the GUI log panel indicating Configurator version"
|
"description": "Message that appears in the GUI log panel indicating Configurator version"
|
||||||
},
|
},
|
||||||
"buildServerLoaded": {
|
"buildServerSuccess": {
|
||||||
"message" : "Loaded builds information for $1 from build server."
|
"message" : "Success: $1"
|
||||||
},
|
},
|
||||||
"buildServerLoadFailed": {
|
"buildServerFailure": {
|
||||||
"message" : "<b>Build server query for $1 releases failed, using cached information. Reason: <code>$2</code></b>"
|
"message" : "<b>Build server failure: $1 <code>$2</code></b>"
|
||||||
},
|
},
|
||||||
"buildServerUsingCached": {
|
"buildServerUsingCached": {
|
||||||
"message" : "Using cached builds information for $1."
|
"message" : "Using cached builds information for $1."
|
||||||
},
|
},
|
||||||
|
"buildServerSupportRequestSubmission": {
|
||||||
|
"message": "<br>*** Support data submitted *** <br>Id: $1<br><br><br># copy ID and provide to the betaflight team."
|
||||||
|
},
|
||||||
|
"supportWarningDialogTitle": {
|
||||||
|
"message": "Confirm Data Submission"
|
||||||
|
},
|
||||||
|
"supportWarningDialogText": {
|
||||||
|
"message": "Please confirm data submission to the Betaflight team.<br><br>This process will run some commands and submit the output to the build server.<br><br>You will then be provided a unique identifier for your data submission.<br><br>Please ensure you provide this unique identifier to the Betaflight team when using Discord or opening an issues on Github."
|
||||||
|
},
|
||||||
"releaseCheckLoaded": {
|
"releaseCheckLoaded": {
|
||||||
"message" : "Loaded release information for $1 from GitHub."
|
"message" : "Loaded release information for $1 from GitHub."
|
||||||
},
|
},
|
||||||
|
@ -2885,6 +2897,9 @@
|
||||||
"cliLoadFromFileBtn": {
|
"cliLoadFromFileBtn": {
|
||||||
"message": "Load from file"
|
"message": "Load from file"
|
||||||
},
|
},
|
||||||
|
"cliSupportRequestBtn": {
|
||||||
|
"message": "Submit Support Data"
|
||||||
|
},
|
||||||
"cliConfirmSnippetDialogTitle": {
|
"cliConfirmSnippetDialogTitle": {
|
||||||
"message": "Loaded file <strong>{{fileName}}</strong>. Review the loaded commands"
|
"message": "Loaded file <strong>{{fileName}}</strong>. Review the loaded commands"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,10 +2,10 @@ import GUI from "./gui";
|
||||||
import { i18n } from "./localization";
|
import { i18n } from "./localization";
|
||||||
import { get as getStorage, set as setStorage } from "./SessionStorage";
|
import { get as getStorage, set as setStorage } from "./SessionStorage";
|
||||||
|
|
||||||
export default class ReleaseLoader {
|
export default class BuildApi {
|
||||||
|
|
||||||
constructor (url) {
|
constructor () {
|
||||||
this._url = url;
|
this._url = 'https://build.betaflight.com';
|
||||||
this._cacheExpirationPeriod = 3600 * 1000;
|
this._cacheExpirationPeriod = 3600 * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,6 @@ export default class ReleaseLoader {
|
||||||
|
|
||||||
if (!cachedData || !cachedLastUpdate || dataTimestamp - cachedLastUpdate > this._cacheExpirationPeriod) {
|
if (!cachedData || !cachedLastUpdate || dataTimestamp - cachedLastUpdate > this._cacheExpirationPeriod) {
|
||||||
$.get(url, function (info) {
|
$.get(url, function (info) {
|
||||||
GUI.log(i18n.getMessage('buildServerLoaded', [url]));
|
|
||||||
|
|
||||||
// cache loaded info
|
// cache loaded info
|
||||||
const object = {};
|
const object = {};
|
||||||
object[dataTag] = info;
|
object[dataTag] = info;
|
||||||
|
@ -38,7 +36,7 @@ export default class ReleaseLoader {
|
||||||
setStorage(object);
|
setStorage(object);
|
||||||
onSuccess(info);
|
onSuccess(info);
|
||||||
}).fail(xhr => {
|
}).fail(xhr => {
|
||||||
GUI.log(i18n.getMessage('buildServerLoadFailed', [url, `HTTP ${xhr.status}`]));
|
GUI.log(i18n.getMessage('buildServerFailure', [url, `HTTP ${xhr.status}`]));
|
||||||
if (onFailure !== undefined) {
|
if (onFailure !== undefined) {
|
||||||
onFailure();
|
onFailure();
|
||||||
} else {
|
} else {
|
||||||
|
@ -75,7 +73,41 @@ export default class ReleaseLoader {
|
||||||
GUI.log(i18n.getMessage('buildServerLoaded', [path]));
|
GUI.log(i18n.getMessage('buildServerLoaded', [path]));
|
||||||
onSuccess(data);
|
onSuccess(data);
|
||||||
}).fail(xhr => {
|
}).fail(xhr => {
|
||||||
GUI.log(i18n.getMessage('buildServerLoadFailed', [path, `HTTP ${xhr.status}`]));
|
GUI.log(i18n.getMessage('buildServerFailure', [path, `HTTP ${xhr.status}`]));
|
||||||
|
if (onFailure !== undefined) {
|
||||||
|
onFailure();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getSupportCommands(onSuccess, onFailure) {
|
||||||
|
|
||||||
|
const url = `${this._url}/api/support/commands`;
|
||||||
|
$.get(url, function (data) {
|
||||||
|
onSuccess(data);
|
||||||
|
}).fail(xhr => {
|
||||||
|
GUI.log(i18n.getMessage('buildServerFailure', [url, `HTTP ${xhr.status}`]));
|
||||||
|
if (onFailure !== undefined) {
|
||||||
|
onFailure();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
submitSupportData(data, onSuccess, onFailure) {
|
||||||
|
|
||||||
|
const url = `${this._url}/api/support`;
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
type: "POST",
|
||||||
|
data: data,
|
||||||
|
contentType: "text/plain",
|
||||||
|
dataType: "text",
|
||||||
|
|
||||||
|
success: function(response) {
|
||||||
|
onSuccess(response);
|
||||||
|
},
|
||||||
|
}).fail(xhr => {
|
||||||
|
GUI.log(i18n.getMessage('buildServerFailure', [`HTTP ${xhr.status}`]));
|
||||||
if (onFailure !== undefined) {
|
if (onFailure !== undefined) {
|
||||||
onFailure();
|
onFailure();
|
||||||
}
|
}
|
||||||
|
@ -92,11 +124,11 @@ export default class ReleaseLoader {
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
|
|
||||||
success: function(data) {
|
success: function(response) {
|
||||||
onSuccess(data);
|
onSuccess(response);
|
||||||
},
|
},
|
||||||
}).fail(xhr => {
|
}).fail(xhr => {
|
||||||
GUI.log(i18n.getMessage('buildServerLoadFailed', [url, `HTTP ${xhr.status}`]));
|
GUI.log(i18n.getMessage('buildServerFailure', [url, `HTTP ${xhr.status}`]));
|
||||||
if (onFailure !== undefined) {
|
if (onFailure !== undefined) {
|
||||||
onFailure();
|
onFailure();
|
||||||
}
|
}
|
||||||
|
@ -107,10 +139,10 @@ export default class ReleaseLoader {
|
||||||
|
|
||||||
const url = `${this._url}/api/builds/${key}/status`;
|
const url = `${this._url}/api/builds/${key}/status`;
|
||||||
$.get(url, function (data) {
|
$.get(url, function (data) {
|
||||||
GUI.log(i18n.getMessage('buildServerLoaded', [url]));
|
GUI.log(i18n.getMessage('buildServerSuccess', [url]));
|
||||||
onSuccess(data);
|
onSuccess(data);
|
||||||
}).fail(xhr => {
|
}).fail(xhr => {
|
||||||
GUI.log(i18n.getMessage('buildServerLoadFailed', [url, `HTTP ${xhr.status}`]));
|
GUI.log(i18n.getMessage('buildServerFailure', [url, `HTTP ${xhr.status}`]));
|
||||||
if (onFailure !== undefined) {
|
if (onFailure !== undefined) {
|
||||||
onFailure();
|
onFailure();
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { i18n } from "../localization";
|
import { i18n } from "../localization";
|
||||||
import Clipboard from "../Clipboard";
|
import Clipboard from "../Clipboard";
|
||||||
import GUI from '../gui';
|
import GUI from '../gui';
|
||||||
|
import BuildApi from '../BuildApi';
|
||||||
|
|
||||||
const cli = {
|
const cli = {
|
||||||
lineDelayMs: 15,
|
lineDelayMs: 15,
|
||||||
|
@ -13,6 +14,7 @@ const cli = {
|
||||||
copyButton: null,
|
copyButton: null,
|
||||||
windowWrapper: null,
|
windowWrapper: null,
|
||||||
},
|
},
|
||||||
|
lastArrival: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
function removePromptHash(promptText) {
|
function removePromptHash(promptText) {
|
||||||
|
@ -89,11 +91,16 @@ cli.initialize = function (callback) {
|
||||||
|
|
||||||
const enterKeyCode = 13;
|
const enterKeyCode = 13;
|
||||||
|
|
||||||
|
function clearHistory() {
|
||||||
|
self.outputHistory = "";
|
||||||
|
self.GUI.windowWrapper.empty();
|
||||||
|
}
|
||||||
|
|
||||||
function executeCommands(outString) {
|
function executeCommands(outString) {
|
||||||
self.history.add(outString.trim());
|
self.history.add(outString.trim());
|
||||||
|
|
||||||
const outputArray = outString.split("\n");
|
const outputArray = outString.split("\n");
|
||||||
Promise.reduce(outputArray, function(delay, line, index) {
|
return Promise.reduce(outputArray, function(delay, line, index) {
|
||||||
return new Promise(function (resolve) {
|
return new Promise(function (resolve) {
|
||||||
GUI.timeout_add('CLI_send_slowly', function () {
|
GUI.timeout_add('CLI_send_slowly', function () {
|
||||||
let processingDelay = self.lineDelayMs;
|
let processingDelay = self.lineDelayMs;
|
||||||
|
@ -181,8 +188,7 @@ cli.initialize = function (callback) {
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.tab-cli .clear').click(function() {
|
$('.tab-cli .clear').click(function() {
|
||||||
self.outputHistory = "";
|
clearHistory();
|
||||||
self.GUI.windowWrapper.empty();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Clipboard.available) {
|
if (Clipboard.available) {
|
||||||
|
@ -247,6 +253,38 @@ cli.initialize = function (callback) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.tab-cli .support').click(function() {
|
||||||
|
|
||||||
|
function submitSupportData() {
|
||||||
|
clearHistory();
|
||||||
|
const api = new BuildApi();
|
||||||
|
api.getSupportCommands((commands) => {
|
||||||
|
executeCommands(commands.join('\n')).then(() => {
|
||||||
|
const delay = setInterval(() => {
|
||||||
|
const time = new Date().getTime();
|
||||||
|
if (self.lastArrival < time - 250) {
|
||||||
|
clearInterval(delay);
|
||||||
|
const text = self.outputHistory;
|
||||||
|
api.submitSupportData(text, (key) => {
|
||||||
|
writeToOutput(i18n.getMessage('buildServerSupportRequestSubmission', [key]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogSettings = {
|
||||||
|
title: i18n.getMessage("supportWarningDialogTitle"),
|
||||||
|
text: i18n.getMessage("supportWarningDialogText"),
|
||||||
|
buttonYesText: i18n.getMessage("submit"),
|
||||||
|
buttonNoText: i18n.getMessage("cancel"),
|
||||||
|
buttonYesCallback: submitSupportData,
|
||||||
|
};
|
||||||
|
|
||||||
|
GUI.showYesNoDialog(dialogSettings);
|
||||||
|
});
|
||||||
|
|
||||||
// Tab key detection must be on keydown,
|
// Tab key detection must be on keydown,
|
||||||
// `keypress`/`keyup` happens too late, as `textarea` will have already lost focus.
|
// `keypress`/`keyup` happens too late, as `textarea` will have already lost focus.
|
||||||
textarea.keydown(function (event) {
|
textarea.keydown(function (event) {
|
||||||
|
@ -464,6 +502,8 @@ cli.read = function (readInfo) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.lastArrival = new Date().getTime();
|
||||||
|
|
||||||
if (!CONFIGURATOR.cliValid && validateText.indexOf('CLI') !== -1) {
|
if (!CONFIGURATOR.cliValid && validateText.indexOf('CLI') !== -1) {
|
||||||
GUI.log(i18n.getMessage('cliEnter'));
|
GUI.log(i18n.getMessage('cliEnter'));
|
||||||
CONFIGURATOR.cliValid = true;
|
CONFIGURATOR.cliValid = true;
|
||||||
|
|
|
@ -2,11 +2,11 @@ import { i18n } from '../localization';
|
||||||
import GUI from '../gui';
|
import GUI from '../gui';
|
||||||
import { get as getConfig, set as setConfig } from '../ConfigStorage';
|
import { get as getConfig, set as setConfig } from '../ConfigStorage';
|
||||||
import { get as getStorage, set as setStorage } from '../SessionStorage';
|
import { get as getStorage, set as setStorage } from '../SessionStorage';
|
||||||
import ReleaseLoader from '../release_loader';
|
import BuildApi from '../BuildApi';
|
||||||
|
|
||||||
const firmware_flasher = {
|
const firmware_flasher = {
|
||||||
targets: null,
|
targets: null,
|
||||||
releaseLoader: new ReleaseLoader('https://build.betaflight.com'),
|
releaseLoader: new BuildApi(),
|
||||||
localFirmwareLoaded: false,
|
localFirmwareLoaded: false,
|
||||||
selectedBoard: undefined,
|
selectedBoard: undefined,
|
||||||
boardNeedsVerification: false,
|
boardNeedsVerification: false,
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
<div class="btn save_btn">
|
<div class="btn save_btn">
|
||||||
<a class="copy" href="#" i18n="cliCopyToClipboardBtn"></a>
|
<a class="copy" href="#" i18n="cliCopyToClipboardBtn"></a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="btn save_btn">
|
||||||
|
<a class="support" href="#" i18n="cliSupportRequestBtn"></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="toolbar_expand_btn" nbrow="2">
|
<div class="toolbar_expand_btn" nbrow="2">
|
||||||
|
|
Loading…
Reference in New Issue