feat: remove state preservation

This commit is contained in:
2025-09-19 21:12:15 +02:00
parent 262b03413a
commit 0aca182de9
6 changed files with 38 additions and 46 deletions

View File

@@ -33,13 +33,13 @@ spore-ui/
├── public/ # Frontend files
│ ├── index.html # Main HTML page
│ ├── styles.css # All CSS styles
│ ├── framework.js # Enhanced component framework with state preservation
│ ├── framework.js # Enhanced component framework
│ ├── components.js # UI components with partial update support
│ ├── view-models.js # Data models with UI state management
│ ├── app.js # Main application logic
│ └── test-state-preservation.html # Test interface for state preservation
│ └── test-interface.html # Test interface
├── docs/
│ └── STATE_PRESERVATION.md # Detailed documentation of state preservation system
│ └── FRAMEWORK_README.md # Framework documentation
└── README.md # This file
```
@@ -48,7 +48,7 @@ spore-ui/
1. **Install dependencies**: `npm install`
2. **Start the server**: `npm start`
3. **Open in browser**: `http://localhost:3001`
4. **Test state preservation**: `http://localhost:3001/test-state-preservation.html`
4. **Test interface**: `http://localhost:3001/test-interface.html`
## API Endpoints
@@ -62,7 +62,7 @@ spore-ui/
- **Backend**: Express.js, Node.js
- **Frontend**: Vanilla JavaScript, CSS3, HTML5
- **Framework**: Custom component-based architecture with state preservation
- **Framework**: Custom component-based architecture
- **API**: SPORE Embedded System API
- **Design**: Glassmorphism, CSS Grid, Flexbox

View File

@@ -90,7 +90,7 @@ document.addEventListener('DOMContentLoaded', async function() {
});
})();
// Set up periodic updates with state preservation
// Set up periodic updates
function setupPeriodicUpdates() {
// Auto-refresh cluster members every 30 seconds using smart update
setInterval(() => {
@@ -99,7 +99,7 @@ function setupPeriodicUpdates() {
// Use smart update if available, otherwise fall back to regular update
if (viewModel.smartUpdate && typeof viewModel.smartUpdate === 'function') {
logger.debug('App: Performing smart update to preserve UI state...');
logger.debug('App: Performing smart update...');
viewModel.smartUpdate();
} else if (viewModel.updateClusterMembers && typeof viewModel.updateClusterMembers === 'function') {
logger.debug('App: Performing regular update...');

View File

@@ -1,4 +1,4 @@
// Cluster Members Component with enhanced state preservation
// Cluster Members Component
class ClusterMembersComponent extends Component {
constructor(container, viewModel, eventBus) {
super(container, viewModel, eventBus);
@@ -142,7 +142,7 @@ class ClusterMembersComponent extends Component {
logger.debug('ClusterMembersComponent: View model listeners set up');
}
// Handle members update with state preservation
// Handle members update
handleMembersUpdate(newMembers, previousMembers) {
logger.debug('ClusterMembersComponent: Members updated:', { newMembers, previousMembers });
@@ -166,9 +166,9 @@ class ClusterMembersComponent extends Component {
return;
}
if (this.shouldPreserveState(newMembers, previousMembers)) {
// Perform partial update to preserve UI state
logger.debug('ClusterMembersComponent: Preserving state, performing partial update');
if (this.shouldSkipFullRender(newMembers, previousMembers)) {
// Perform partial update
logger.debug('ClusterMembersComponent: Skipping full render, performing partial update');
this.updateMembersPartially(newMembers, previousMembers);
} else {
// Full re-render if structure changed significantly
@@ -242,8 +242,8 @@ class ClusterMembersComponent extends Component {
}
}
// Check if we should preserve UI state during update
shouldPreserveState(newMembers, previousMembers) {
// Check if we should skip full re-render during update
shouldSkipFullRender(newMembers, previousMembers) {
if (!previousMembers || !Array.isArray(previousMembers)) return false;
if (!Array.isArray(newMembers)) return false;
@@ -254,7 +254,7 @@ class ClusterMembersComponent extends Component {
const newIps = new Set(newMembers.map(m => m.ip));
const prevIps = new Set(previousMembers.map(m => m.ip));
// If IPs are the same, we can preserve state
// If IPs are the same, we can skip full re-render
return newIps.size === prevIps.size &&
[...newIps].every(ip => prevIps.has(ip));
}
@@ -269,9 +269,9 @@ class ClusterMembersComponent extends Component {
return false;
}
// Update members partially to preserve UI state
// Update members partially
updateMembersPartially(newMembers, previousMembers) {
logger.debug('ClusterMembersComponent: Performing partial update to preserve UI state');
logger.debug('ClusterMembersComponent: Performing partial update');
// Build previous map by IP for stable diffs
const prevByIp = new Map((previousMembers || []).map(m => [m.ip, m]));

View File

@@ -1,4 +1,4 @@
// Node Details Component with enhanced state preservation
// Node Details Component
class NodeDetailsComponent extends Component {
constructor(container, viewModel, eventBus) {
super(container, viewModel, eventBus);
@@ -15,14 +15,14 @@ class NodeDetailsComponent extends Component {
this.subscribeToProperty('monitoringResources', this.handleMonitoringResourcesUpdate.bind(this));
}
// Handle node status update with state preservation
// Handle node status update
handleNodeStatusUpdate(newStatus, previousStatus) {
if (newStatus && !this.viewModel.get('isLoading')) {
this.renderNodeDetails(newStatus, this.viewModel.get('tasks'), this.viewModel.get('endpoints'), this.viewModel.get('monitoringResources'));
}
}
// Handle tasks update with state preservation
// Handle tasks update
handleTasksUpdate(newTasks, previousTasks) {
const nodeStatus = this.viewModel.get('nodeStatus');
if (nodeStatus && !this.viewModel.get('isLoading')) {
@@ -51,7 +51,7 @@ class NodeDetailsComponent extends Component {
this.updateActiveTab(newTab, previousTab);
}
// Handle endpoints update with state preservation
// Handle endpoints update
handleEndpointsUpdate(newEndpoints, previousEndpoints) {
const nodeStatus = this.viewModel.get('nodeStatus');
const tasks = this.viewModel.get('tasks');
@@ -60,7 +60,7 @@ class NodeDetailsComponent extends Component {
}
}
// Handle monitoring resources update with state preservation
// Handle monitoring resources update
handleMonitoringResourcesUpdate(newResources, previousResources) {
const nodeStatus = this.viewModel.get('nodeStatus');
const tasks = this.viewModel.get('tasks');

View File

@@ -225,10 +225,7 @@ class ViewModel {
// Batch update with change detection
batchUpdate(updates, options = {}) {
const { preserveUIState = true, notifyChanges = true } = options;
// Optionally preserve UI state snapshot
const currentUIState = preserveUIState ? new Map(this._uiState) : null;
const { notifyChanges = true } = options;
// Track which keys actually change and what the previous values were
const changedKeys = [];
@@ -245,11 +242,6 @@ class ViewModel {
}
});
// Restore UI state if requested
if (preserveUIState && currentUIState) {
this._uiState = currentUIState;
}
// Notify listeners for changed keys
if (notifyChanges) {
changedKeys.forEach(key => {
@@ -259,7 +251,7 @@ class ViewModel {
}
}
// Base Component class with enhanced state preservation
// Base Component class
class Component {
constructor(container, viewModel, eventBus) {
this.container = container;

View File

@@ -1,6 +1,6 @@
// View Models for SPORE UI Components
// Cluster View Model with enhanced state preservation
// Cluster View Model
class ClusterViewModel extends ViewModel {
constructor() {
super();
@@ -23,7 +23,7 @@ class ClusterViewModel extends ViewModel {
}, 100);
}
// Update cluster members with state preservation
// Update cluster members
async updateClusterMembers() {
try {
logger.debug('ClusterViewModel: updateClusterMembers called');
@@ -45,12 +45,12 @@ class ClusterViewModel extends ViewModel {
? members.filter(m => m && m.status && m.status.toUpperCase() === 'ACTIVE').length
: 0;
// Use batch update to preserve UI state
// Use batch update
this.batchUpdate({
members: members,
lastUpdateTime: new Date().toISOString(),
onlineNodes: onlineNodes
}, { preserveUIState: true });
});
// Restore expanded cards and active tabs
this.set('expandedCards', currentExpandedCards);
@@ -69,12 +69,12 @@ class ClusterViewModel extends ViewModel {
}
}
// Update primary node display with state preservation
// Update primary node display
async updatePrimaryNodeDisplay() {
try {
const discoveryInfo = await window.apiClient.getDiscoveryInfo();
// Use batch update to preserve UI state
// Use batch update
const updates = {};
if (discoveryInfo.primaryNode) {
@@ -91,7 +91,7 @@ class ClusterViewModel extends ViewModel {
updates.totalNodes = 0;
}
this.batchUpdate(updates, { preserveUIState: true });
this.batchUpdate(updates);
} catch (error) {
console.error('Failed to fetch discovery info:', error);
@@ -208,7 +208,7 @@ class ClusterViewModel extends ViewModel {
}
}
// Node Details View Model with enhanced state preservation
// Node Details View Model
class NodeDetailsViewModel extends ViewModel {
constructor() {
super();
@@ -225,7 +225,7 @@ class NodeDetailsViewModel extends ViewModel {
});
}
// Load node details with state preservation
// Load node details
async loadNodeDetails(ip) {
try {
// Store current UI state
@@ -237,10 +237,10 @@ class NodeDetailsViewModel extends ViewModel {
const nodeStatus = await window.apiClient.getNodeStatus(ip);
// Use batch update to preserve UI state
// Use batch update
this.batchUpdate({
nodeStatus: nodeStatus
}, { preserveUIState: true });
});
// Restore active tab
this.set('activeTab', currentActiveTab);
@@ -262,7 +262,7 @@ class NodeDetailsViewModel extends ViewModel {
}
}
// Load tasks data with state preservation
// Load tasks data
async loadTasksData() {
try {
const ip = this.get('nodeIp');
@@ -276,7 +276,7 @@ class NodeDetailsViewModel extends ViewModel {
}
}
// Load endpoints data with state preservation
// Load endpoints data
async loadEndpointsData() {
try {
const ip = this.get('nodeIp');
@@ -290,7 +290,7 @@ class NodeDetailsViewModel extends ViewModel {
}
}
// Load monitoring resources data with state preservation
// Load monitoring resources data
async loadMonitoringResources() {
try {
const ip = this.get('nodeIp');