Refactor the Calibration History modal

This commit is contained in:
Mathias Malmqvist
2025-11-21 23:56:32 +01:00
parent dda8fc2ff1
commit 66b7107167
3 changed files with 94 additions and 79 deletions

View File

@@ -208,7 +208,7 @@
3. <span class="ds-i18n">Finetune stick calibration</span>
</button>
<hr>
<button id="savechanges" type="button" class="btn btn-success ds-btn ds-i18n" onclick="flash_all_changes()" id="resetBtn">Save changes permanently</button>
<button id="savechanges" type="button" class="btn btn-success ds-btn ds-i18n" onclick="flash_all_changes()">Save changes permanently</button>
<button id="restore-calibration-btn" type="button" class="btn btn-secondary ds-btn ds-i18n" onclick="openCalibrationHistoryModal()">Restore calibration</button>
<button type="button" class="btn btn-danger ds-btn ds-i18n" onclick="reboot_controller()" id="resetBtn">Reboot controller</button>

View File

@@ -14,8 +14,8 @@ import {
isQuickTestVisible,
quicktest_handle_controller_input
} from './modals/quick-test-modal.js';
import { show_calibration_history_modal } from './modals/calibration-history-modal.js';
import { FinetuneHistory } from './finetune-history.js';
import { CalibrationHistoryModal } from './modals/calibration-history-modal.js';
// Application State - manages app-wide state and UI
const app = {
@@ -110,7 +110,6 @@ function gboot() {
await loadAllTemplates();
CalibrationHistoryModal.init();
initAnalyticsApi(app); // init just with gu for now
lang_init(app, handleLanguageChange, show_welcome_modal);
show_welcome_modal();
@@ -338,7 +337,7 @@ async function continue_connection({data, device}) {
show_popup(`<p>${
l("It appears the latest joystick calibration has not been saved.")
}</p><p>${
l("You should save your changes, or reboot the controller if you don't want to keep them.")
l("You should save your changes, or reboot the controller to revert back to the previous state.")
}</p>`, true);
}
@@ -1144,16 +1143,6 @@ window.ds5_finetune = () => ds5_finetune(
(success) => success && switchToRangeMode()
);
window.apply_finetune_revert = async (finetuneData) => {
if (!controller || !controller.isConnected()) {
throw new Error('Controller not connected');
}
if (!Array.isArray(finetuneData) || finetuneData.length !== 12) {
throw new Error('Invalid finetune data');
}
await controller.writeFinetuneData(finetuneData);
};
window.openCalibrationHistoryModal = async () => {
let currentFinetuneData = null;
let controllerSerialNumber = null;
@@ -1161,7 +1150,6 @@ window.openCalibrationHistoryModal = async () => {
if (controller && typeof controller.getInMemoryModuleData === 'function') {
currentFinetuneData = await controller.getInMemoryModuleData('finetune');
}
// Get serial number from device info
if (controller && typeof controller.getDeviceInfo === 'function') {
const info = await controller.getDeviceInfo();
const serialNumberItem = info?.infoItems?.find(item => item.key === l("Serial Number"));
@@ -1170,7 +1158,10 @@ window.openCalibrationHistoryModal = async () => {
} catch (error) {
console.warn('Could not retrieve current finetune data or serial number:', error);
}
window.show_calibration_history_modal(currentFinetuneData, controllerSerialNumber);
await show_calibration_history_modal(controller, currentFinetuneData, controllerSerialNumber, (success, message) => {
if(!message) return;
success ? infoAlert(message) : errorAlert(message);
});
};
window.flash_all_changes = flash_all_changes;

View File

@@ -2,31 +2,46 @@
import { FinetuneHistory } from '../finetune-history.js';
import { formatLocalizedDate } from '../utils.js';
import { l } from '../translations.js';
export class CalibrationHistoryModal {
static modalElement = null;
static bootstrapModal = null;
static currentFinetuneData = null;
static currentControllerSerialNumber = null;
constructor(controllerInstance = null, doneCallback = null) {
this.modalElement = null;
this.bootstrapModal = null;
this.currentFinetuneData = null;
this.currentControllerSerialNumber = null;
this.controller = controllerInstance;
this.doneCallback = doneCallback;
static init() {
this._boundModalHidden = () => {
destroyCurrentInstance();
};
this._initEventListeners();
}
_initEventListeners() {
this.modalElement = document.getElementById('calibrationHistoryModal');
if (this.modalElement) {
this.bootstrapModal = new bootstrap.Modal(this.modalElement);
this.modalElement.addEventListener('hidden.bs.modal', this._boundModalHidden);
}
}
static async show(currentFinetuneData = null, controllerSerialNumber = null) {
if (!this.bootstrapModal) {
this.init();
removeEventListeners() {
if (this.modalElement) {
this.modalElement.removeEventListener('hidden.bs.modal', this._boundModalHidden);
}
}
async open(currentFinetuneData = null, controllerSerialNumber = null) {
this.currentFinetuneData = currentFinetuneData;
this.currentControllerSerialNumber = controllerSerialNumber;
await this._populateHistory();
this.bootstrapModal.show();
}
static hide() {
close() {
if (this.bootstrapModal) {
this.bootstrapModal.hide();
}
@@ -36,12 +51,12 @@ export class CalibrationHistoryModal {
* Populate the history list
* @private
*/
static async _populateHistory() {
async _populateHistory() {
const history = FinetuneHistory.getAll(this.currentControllerSerialNumber);
const container = document.getElementById('historyListContainer');
if (!history || history.length === 0) {
container.innerHTML = '<p class="text-muted ds-i18n">No saved calibration settings found.</p>';
container.innerHTML = `<p class="text-muted ds-i18n">${l('No saved calibrations found')}.</p>`;
document.getElementById('clearAllBtn').style.display = 'none';
return;
}
@@ -59,13 +74,13 @@ export class CalibrationHistoryModal {
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<h6 class="mb-1">${date}</h6>
<p class="mb-0 small">Values: ${entry.data.join(', ')}</p>
<p class="mb-0 small">${l('Values')}: ${entry.data.join(', ')}</p>
</div>
<div class="btn-group-sm" role="group">
${isCurrent ?
`<button type="button" class="btn btn-sm btn-success ds-i18n" disabled>Current</button>` :
`<button type="button" class="btn btn-sm btn-primary ds-i18n" onclick="calibration_history_revert('${entry.id}')">Revert</button>
<button type="button" class="btn btn-sm btn-outline-danger ds-i18n" onclick="calibration_history_delete('${entry.id}')">Delete</button>`
`<button type="button" class="btn btn-sm btn-success ds-i18n" disabled>${l('Current')}</button>` :
`<button type="button" class="btn btn-sm btn-primary ds-i18n" onclick="calibration_history_restore('${entry.id}')">${l('Restore')}</button>
<button type="button" class="btn btn-sm btn-outline-danger ds-i18n" onclick="calibration_history_delete('${entry.id}')">${l('Delete')}</button>`
}
</div>
</div>
@@ -81,7 +96,7 @@ export class CalibrationHistoryModal {
* Compare two data arrays for equality
* @private
*/
static _dataEquals(data1, data2) {
_dataEquals(data1, data2) {
if (!Array.isArray(data1) || !Array.isArray(data2)) {
return false;
}
@@ -92,66 +107,45 @@ export class CalibrationHistoryModal {
}
/**
* Escape HTML special characters
* Apply finetune calibration to the controller
* @param {Array} finetuneData - The finetune data to apply
* @private
*/
static _escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
async _applyCalibration(finetuneData) {
if (!this.controller || !this.controller.isConnected()) {
throw new Error('Controller not connected');
}
if (!Array.isArray(finetuneData) || finetuneData.length !== 12) {
throw new Error('Invalid finetune data');
}
await this.controller.writeFinetuneData(finetuneData);
this.controller.setHasChangesToWrite(true);
}
/**
* Revert to a saved calibration
* Restore a saved calibration
* @param {string} entryId - The ID of the entry to revert to
*/
static revertTo(entryId) {
async restoreCalibration(entryId) {
const entry = FinetuneHistory.getById(entryId, this.currentControllerSerialNumber);
if (!entry) {
alert('Calibration settings not found.');
return;
}
if (!entry) throw new Error('Calibration settings not found.');
// Export revert function to window for onclick handlers
window.calibration_history_pending_revert_id = entryId;
window.calibration_history_pending_revert_data = entry.data;
// Show confirmation dialog
const confirmMsg = `Revert to this version?\n\nThis will restore the stored finetune settings.`;
if (confirm(confirmMsg)) {
this._executeRevert(entryId, entry.data);
}
}
/**
* Execute the revert operation
* @private
*/
static _executeRevert(entryId, finetuneData) {
// Call the revert function exposed in core.js
if (typeof window.apply_finetune_revert === 'function') {
window.apply_finetune_revert(finetuneData).then(() => {
this.hide();
alert('Calibration reverted successfully. Remember to save changes permanently.');
}).catch(err => {
alert('Failed to revert calibration: ' + err.message);
});
} else {
alert('Controller not ready. Please try again.');
}
await this._applyCalibration(entry.data);
this.close();
this.doneCallback(true, l('The calibration was restored successfully! Remember to save the changes in order not to loose them when the controller is rebooted.'));
}
/**
* Delete a saved entry
* @param {string} entryId - The ID of the entry to delete
*/
static async delete(entryId) {
async delete(entryId) {
const entry = FinetuneHistory.getById(entryId, this.currentControllerSerialNumber);
if (!entry) {
return;
}
if (confirm(`Delete this calibration entry?`)) {
if (confirm(l(`Delete this calibration entry?`))) {
FinetuneHistory.delete(entryId, this.currentControllerSerialNumber);
await this._populateHistory();
}
@@ -160,16 +154,46 @@ export class CalibrationHistoryModal {
/**
* Clear all saved entries
*/
static async clearAll() {
if (confirm('Delete all calibration history for this controller? This cannot be undone.')) {
async clearAll() {
if (confirm(l('Delete all calibration history for this controller? This cannot be undone.'))) {
FinetuneHistory.clearAll(this.currentControllerSerialNumber);
await this._populateHistory();
}
}
}
// Export functions to window for onclick handlers
window.calibration_history_revert = (entryId) => CalibrationHistoryModal.revertTo(entryId);
window.calibration_history_delete = (entryId) => CalibrationHistoryModal.delete(entryId);
window.calibration_history_clear_all = () => CalibrationHistoryModal.clearAll();
window.show_calibration_history_modal = (currentFinetuneData = null, controllerSerialNumber = null) => CalibrationHistoryModal.show(currentFinetuneData, controllerSerialNumber);
let currentCalibrationHistoryInstance = null;
function destroyCurrentInstance() {
if (currentCalibrationHistoryInstance) {
currentCalibrationHistoryInstance.removeEventListeners();
currentCalibrationHistoryInstance = null;
}
}
export async function show_calibration_history_modal(controllerInstance = null, currentFinetuneData = null, controllerSerialNumber = null, doneCallback = null) {
destroyCurrentInstance();
currentCalibrationHistoryInstance = new CalibrationHistoryModal(controllerInstance, doneCallback);
await currentCalibrationHistoryInstance.open(currentFinetuneData, controllerSerialNumber);
}
window.calibration_history_restore = (entryId) => {
if (currentCalibrationHistoryInstance) {
currentCalibrationHistoryInstance.restoreCalibration(entryId);
}
};
window.calibration_history_delete = (entryId) => {
if (currentCalibrationHistoryInstance) {
currentCalibrationHistoryInstance.delete(entryId);
}
};
window.calibration_history_clear_all = () => {
if (currentCalibrationHistoryInstance) {
currentCalibrationHistoryInstance.clearAll();
}
};
window.show_calibration_history_modal = show_calibration_history_modal;