Skip to content

Commit 6628a8d

Browse files
authored
Merge pull request #31 from Iconem/UP42_APOLLO_fixes
fix(ui): prevent loader from blocking treeview
2 parents 126cb1f + 4dc0693 commit 6628a8d

File tree

2 files changed

+145
-50
lines changed

2 files changed

+145
-50
lines changed

src/control-panel/control-panel.tsx

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ function ControlPanel(props: ControlPanelProps): React.ReactElement {
161161
})
162162
}
163163

164-
//get Bearer and datacollection for UP42
164+
// State management for satellite data and provider selection
165165
const [dataCollection, setDataCollection] = useLocalStorage(
166166
"dataCollection",
167167
[]
@@ -170,57 +170,108 @@ function ControlPanel(props: ControlPanelProps): React.ReactElement {
170170
'providersTreeviewDataSelection',
171171
[]
172172
)
173+
const [isUp42Loading, setIsUp42Loading] = React.useState(false);
173174

175+
// Load UP42 data on component mount
174176
React.useEffect(() => {
175177
(async () => {
178+
const lastFetched = localStorage.getItem('dataCollectionLastEdited');
179+
const today = new Date().toDateString();
180+
181+
if (lastFetched === today && dataCollection.length > 0) {
182+
console.log('UP42 data already fresh for today, skipping fetch');
183+
return;
184+
}
185+
176186
const { up42Email, up42Password } = apiKeys[Providers.UP42];
177-
const newToken = await getUp42Bearer(up42Email, up42Password);
187+
setIsUp42Loading(true);
188+
try {
189+
const newToken = await getUp42Bearer(up42Email, up42Password);
190+
const data = await getDataCollections(newToken, up42Email, up42Password, setters);
178191

179-
const data = await getDataCollections(newToken, up42Email, up42Password, setters);
180-
setDataCollection((prev) => {
181-
if (equal(prev, data)) {
182-
return prev;
183-
}
184-
return data;
185-
});
192+
// Only update if data actually changed
193+
setDataCollection((prev) => {
194+
if (equal(prev, data)) {
195+
return prev;
196+
}
197+
return data;
198+
});
199+
200+
localStorage.setItem('dataCollectionLastEdited', today);
201+
} catch (error) {
202+
console.error('UP42 CORS Error:', error);
203+
} finally {
204+
setIsUp42Loading(false);
205+
}
186206
})();
187207
}, [apiKeys[Providers.UP42].up42Email, apiKeys[Providers.UP42].up42Password]);
188208

209+
// Merge hardcoded providers with dynamic UP42 data
189210
const { providersDict, constellationDict } = React.useMemo(() => {
190-
if (dataCollection.length === 0) {
191-
return { providersDict: {}, constellationDict: {} };
192-
}
211+
let newProvidersDict = { ...initialProvidersDict };
212+
let newConstellationDict = { ...initialConstellationDict };
193213

194-
const up42Hosts = extractUp42HostsWithGsd(dataCollection);
214+
if (dataCollection.length > 0) {
215+
// Add UP42 providers once loaded
216+
const up42Hosts = extractUp42HostsWithGsd(dataCollection);
217+
newProvidersDict[Providers.UP42] = up42Hosts.map(host => host.title);
195218

196-
const newProvidersDict = {
197-
...initialProvidersDict,
198-
[Providers.UP42]: up42Hosts.map(host => host.title)
199-
};
200-
const newConstellationDict: Record<string, any> = {};
201-
up42Hosts.forEach(host => {
202-
newConstellationDict[host.title] = {
203-
satellites: host.satellites,
204-
gsd: host.gsd
205-
};
206-
});
219+
up42Hosts.forEach(host => {
220+
newConstellationDict[host.title] = {
221+
satellites: host.satellites,
222+
gsd: host.gsd
223+
};
224+
});
225+
} else {
226+
newProvidersDict[Providers.UP42] = []; // Empty while loading
227+
}
207228

208229
return { providersDict: newProvidersDict, constellationDict: newConstellationDict };
209230
}, [dataCollection]);
210231

232+
// Build tree structure with loading state
211233
const treeviewData = React.useMemo(
212-
() => (dataCollection.length ? buildTreeviewData(providersDict, constellationDict) : null),
213-
[dataCollection, providersDict, constellationDict]
234+
() => buildTreeviewData(providersDict, constellationDict, isUp42Loading),
235+
[providersDict, constellationDict, isUp42Loading]
214236
);
215237

216-
// Initialize selection when treeviewData is ready and selection is empty
238+
// Initial selection (hardcoded providers only)
217239
React.useEffect(() => {
218240
if (treeviewData && providersTreeviewDataSelection.length === 0) {
219241
const initialSelection = sourcesTreeviewInitialSelection(treeviewData)
220242
setProvidersTreeviewDataSelection(initialSelection)
221243
}
222244
}, [treeviewData])
223245

246+
// Add UP42 to selection once loaded
247+
React.useEffect(() => {
248+
if (!isUp42Loading && dataCollection.length > 0 && treeviewData) {
249+
const up42Provider = treeviewData.children?.find(
250+
provider => provider.id === `treeview-provider-${Providers.UP42}`
251+
);
252+
253+
if (up42Provider && !up42Provider.loading) {
254+
const up42ProviderId = up42Provider.id;
255+
const wasUp42Selected = providersTreeviewDataSelection.includes(up42ProviderId);
256+
257+
if (wasUp42Selected) {
258+
const up42SelectionIds = [
259+
up42Provider.id,
260+
...(up42Provider.children?.map(child => child.id) || [])
261+
];
262+
263+
setProvidersTreeviewDataSelection(prev => {
264+
265+
const withoutOldUp42 = prev.filter(id => !id.includes('treeview-constellation-UP42-'));
266+
const newSelection = [...withoutOldUp42, ...up42SelectionIds];
267+
return [...new Set(newSelection)];
268+
});
269+
}
270+
271+
}
272+
}
273+
}, [isUp42Loading])
274+
224275
const setters = {
225276
setSearchResults: props.setSearchResults,
226277
setSearchPromises,

src/control-panel/satellite-imagery-sources-treeview.tsx

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import * as React from 'react'
66
import { TreeView, TreeItem } from '@mui/lab'
7-
import { Typography, Collapse, Checkbox, FormControlLabel } from '@mui/material'
7+
import { Typography, Collapse, Checkbox, FormControlLabel, CircularProgress, Box } from '@mui/material'
88
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
99
import { faChevronDown, faChevronUp, faChevronRight, faSatellite } from '@fortawesome/free-solid-svg-icons'
1010
import { useLocalStorage } from '../utilities'
@@ -16,24 +16,56 @@ interface RenderTree {
1616
name: string
1717
children?: RenderTree[]
1818
disabled?: boolean
19+
loading?: boolean
1920
}
2021

2122
const treeviewRootId = 'treeview-provider-root'
2223

23-
function buildTreeviewData(providersDict, constellationDict): RenderTree {
24+
// function buildTreeviewData(providersDict, constellationDict): RenderTree {
25+
// return {
26+
// id: treeviewRootId,
27+
// name: 'Satellite Sources',
28+
// children: Object.keys(providersDict).map((providerKey: string) => ({
29+
// id: `treeview-provider-${providerKey}`,
30+
// name: providerKey,
31+
// children: providersDict[providerKey].map((constellationKey: string) => ({
32+
// id: `treeview-constellation-${providerKey}-${constellationKey}`,
33+
// name: `${constellationKey} - ${100 * (constellationDict[constellationKey]?.gsd ?? 0)}cm`,
34+
// disabled: !(providerKey === Providers.UP42),
35+
// })),
36+
// })),
37+
// }
38+
// }
39+
40+
function buildTreeviewData(providersDict, constellationDict, isUp42Loading = false): RenderTree {
2441
return {
2542
id: treeviewRootId,
2643
name: 'Satellite Sources',
27-
children: Object.keys(providersDict).map((providerKey: string) => ({
28-
id: `treeview-provider-${providerKey}`,
29-
name: providerKey,
30-
children: providersDict[providerKey].map((constellationKey: string) => ({
31-
id: `treeview-constellation-${providerKey}-${constellationKey}`,
32-
name: `${constellationKey} - ${100 * (constellationDict[constellationKey]?.gsd ?? 0)}cm`,
33-
disabled: !(providerKey === Providers.UP42),
34-
})),
35-
})),
36-
}
44+
children: Object.keys(providersDict).map((providerKey: string) => {
45+
46+
if (providerKey === Providers.UP42 && isUp42Loading) {
47+
return {
48+
id: `treeview-provider-${providerKey}`,
49+
name: `${providerKey} (Loading...)`,
50+
children: [{
51+
id: `treeview-constellation-${providerKey}-loading`,
52+
name: 'Loading...',
53+
loading: true,
54+
children: []
55+
}]
56+
};
57+
}
58+
return {
59+
id: `treeview-provider-${providerKey}`,
60+
name: providerKey,
61+
children: providersDict[providerKey].map((constellationKey: string) => ({
62+
id: `treeview-constellation-${providerKey}-${constellationKey}`,
63+
name: `${constellationKey} - ${100 * (constellationDict[constellationKey]?.gsd ?? 0)}cm`,
64+
// disabled: !(providerKey === Providers.UP42),
65+
})),
66+
};
67+
}),
68+
};
3769
}
3870

3971
const sourcesTreeviewInitialSelection = (treeviewData: RenderTree): string[] => {
@@ -47,6 +79,7 @@ const sourcesTreeviewInitialSelection = (treeviewData: RenderTree): string[] =>
4779
return result
4880
}
4981

82+
5083
RecursiveTreeView.propTypes = {
5184
providersTreeviewDataSelection: PropTypes.any,
5285
setProvidersTreeviewDataSelection: PropTypes.func,
@@ -124,10 +157,10 @@ function RecursiveTreeView(props): React.ReactElement {
124157
}
125158

126159
const renderTree = (nodes: RenderTree): React.ReactElement => {
160+
127161
const allSelectedChildren = parentMap[nodes.id]?.every((childNodeId: string) => selectedSet.has(childNodeId))
128162
const checked = selectedSet.has(nodes.id) || allSelectedChildren || false
129163
const indeterminate = parentMap[nodes.id]?.some((childNodeId: string) => selectedSet.has(childNodeId)) || false
130-
131164
if (allSelectedChildren && !selectedSet.has(nodes.id)) {
132165
props.setProvidersTreeviewDataSelection([...props.providersTreeviewDataSelection, nodes.id])
133166
}
@@ -147,7 +180,13 @@ function RecursiveTreeView(props): React.ReactElement {
147180
disabled={nodes.disabled}
148181
/>
149182
}
150-
label={<Typography variant="subtitle2">{nodes.name}</Typography>}
183+
// label={<Typography variant="subtitle2">{nodes.name}</Typography>}
184+
label={
185+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
186+
<Typography variant="subtitle2">{nodes.name}</Typography>
187+
{nodes.loading && <CircularProgress size={16} />}
188+
</Box>
189+
}
151190
key={nodes.id}
152191
/>
153192
}
@@ -184,16 +223,21 @@ function SatelliteImagerySourcesTreeview(props): React.ReactElement {
184223
&nbsp; Satellite Sources Selection &nbsp;
185224
{advancedSettingsCollapsed ? <FontAwesomeIcon icon={faChevronDown} /> : <FontAwesomeIcon icon={faChevronUp} />}
186225
</Typography>
226+
{/* <Collapse in={!advancedSettingsCollapsed} timeout="auto" unmountOnExit>
227+
228+
<RecursiveTreeView
229+
setProvidersTreeviewDataSelection={props.setProvidersTreeviewDataSelection}
230+
providersTreeviewDataSelection={props.providersTreeviewDataSelection}
231+
treeviewData={props.treeviewData}
232+
/>
233+
234+
</Collapse> */}
187235
<Collapse in={!advancedSettingsCollapsed} timeout="auto" unmountOnExit>
188-
{props.treeviewData ? (
189-
<RecursiveTreeView
190-
setProvidersTreeviewDataSelection={props.setProvidersTreeviewDataSelection}
191-
providersTreeviewDataSelection={props.providersTreeviewDataSelection}
192-
treeviewData={props.treeviewData}
193-
/>
194-
) : (
195-
<div>Loading...</div>
196-
)}
236+
<RecursiveTreeView
237+
setProvidersTreeviewDataSelection={props.setProvidersTreeviewDataSelection}
238+
providersTreeviewDataSelection={props.providersTreeviewDataSelection}
239+
treeviewData={props.treeviewData}
240+
/>
197241
</Collapse>
198242
</>
199243
)

0 commit comments

Comments
 (0)