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();