Spaces:
Runtime error
Runtime error
File size: 5,693 Bytes
4c36de1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | /**
* Background Data Service
* Handles preemptive loading of tree data for better performance
*/
export class BackgroundDataService {
constructor() {
this.treeDataCache = null;
this.isLoading = false;
this.loadPromise = null;
this.authToken = null;
this.batchSize = 1000;
}
setAuthToken(token) {
this.authToken = token;
}
async authenticatedFetch(url, options = {}) {
if (!this.authToken) {
throw new Error('No auth token available');
}
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.authToken}`,
...options.headers
};
const response = await fetch(url, {
...options,
headers
});
if (response.status === 401) {
// Token expired or invalid
localStorage.removeItem('auth_token');
localStorage.removeItem('user_info');
window.location.href = '/login';
return null;
}
return response;
}
/**
* Start preemptive loading of all tree data
*/
async startPreemptiveLoading() {
if (this.isLoading || this.treeDataCache) {
return this.loadPromise;
}
console.log('π Starting preemptive tree data loading...');
this.isLoading = true;
this.loadPromise = this.loadAllTreesInBackground();
return this.loadPromise;
}
/**
* Load all trees in background with batching
*/
async loadAllTreesInBackground() {
try {
let allTrees = [];
let offset = 0;
let hasMoreTrees = true;
console.log('π¦ Loading trees in background batches...');
while (hasMoreTrees && allTrees.length < 3000) { // Safety limit
console.log(`π₯ Background batch: offset=${offset}, limit=${this.batchSize}`);
const response = await this.authenticatedFetch(`/api/trees?limit=${this.batchSize}&offset=${offset}`);
if (!response) {
console.error('β Failed to fetch background batch');
break;
}
const batchTrees = await response.json();
console.log(`β
Background batch loaded: ${batchTrees.length} trees`);
if (batchTrees.length === 0) {
hasMoreTrees = false;
break;
}
allTrees = allTrees.concat(batchTrees);
offset += this.batchSize;
// If we got less than the batch size, we've reached the end
if (batchTrees.length < this.batchSize) {
hasMoreTrees = false;
}
// Small delay to prevent overwhelming the server
await this.delay(100);
}
// Filter out problematic trees
const filteredTrees = allTrees.filter(tree => {
// Exclude tree ID 18
if (tree.id === 18) {
console.log('π Background: Excluding tree ID 18 from cache');
return false;
}
return true;
});
this.treeDataCache = filteredTrees;
this.isLoading = false;
console.log(`π Background loading complete: ${filteredTrees.length} trees cached`);
// Dispatch custom event to notify that data is ready
window.dispatchEvent(new CustomEvent('treeDataReady', {
detail: { count: filteredTrees.length }
}));
return filteredTrees;
} catch (error) {
console.error('β Background tree loading failed:', error);
this.isLoading = false;
throw error;
}
}
/**
* Get cached tree data (returns immediately if available)
*/
getCachedTreeData() {
return this.treeDataCache;
}
/**
* Check if data is ready
*/
isDataReady() {
return this.treeDataCache !== null;
}
/**
* Get tree data - uses cache if available, otherwise loads
*/
async getTreeData() {
if (this.treeDataCache) {
console.log('β‘ Using cached tree data');
return this.treeDataCache;
}
if (this.isLoading) {
console.log('β³ Waiting for background loading to complete...');
return await this.loadPromise;
}
console.log('π Cache miss, loading trees now...');
return await this.startPreemptiveLoading();
}
/**
* Clear cached data (useful for refresh scenarios)
*/
clearCache() {
this.treeDataCache = null;
this.isLoading = false;
this.loadPromise = null;
console.log('ποΈ Tree data cache cleared');
}
/**
* Simple delay utility
*/
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Get loading status for UI feedback
*/
getLoadingStatus() {
return {
isLoading: this.isLoading,
isReady: this.isDataReady(),
cacheSize: this.treeDataCache ? this.treeDataCache.length : 0
};
}
}
// Export singleton instance
export const backgroundDataService = new BackgroundDataService(); |