Skip to content

Commit 5a3e824

Browse files
committed
Merge remote-tracking branch 'origin/main' into pr_try_steps_block
2 parents 4afcbff + a8cd0e5 commit 5a3e824

File tree

5 files changed

+86
-71
lines changed

5 files changed

+86
-71
lines changed

src/App.tsx

Lines changed: 70 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -528,61 +528,77 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
528528
}
529529
}, [shownPythonToolboxCategories]);
530530

531-
// Fetch modules when project changes.
531+
// Fetch any unfetched modules when project changes.
532532
React.useEffect(() => {
533-
if (project && storage) {
534-
const fetchModules = async () => {
535-
const promises: {[modulePath: string]: Promise<string>} = {}; // value is promise of module content.
536-
promises[project.robot.modulePath] = storage.fetchFileContentText(project.robot.modulePath);
537-
project.mechanisms.forEach(mechanism => {
538-
promises[mechanism.modulePath] = storage.fetchFileContentText(mechanism.modulePath);
539-
});
540-
project.opModes.forEach(opmode => {
541-
promises[opmode.modulePath] = storage.fetchFileContentText(opmode.modulePath);
542-
});
543-
const updatedModulePathToContentText: {[modulePath: string]: string} = {}; // value is module content text
544-
await Promise.all(
545-
Object.entries(promises).map(async ([modulePath, promise]) => {
546-
updatedModulePathToContentText[modulePath] = await promise;
547-
})
548-
);
549-
const oldModulePathToContentText = modulePathToContentText;
550-
setModulePathToContentText(updatedModulePathToContentText);
551-
552-
// Remove any deleted modules from modulePaths, modulePathToBlocklyComponent, and
553-
// modulePathToEditor. Update currentModule if the current module was deleted.
554-
for (const modulePath in oldModulePathToContentText) {
555-
if (modulePath in updatedModulePathToContentText) {
556-
continue;
557-
}
558-
if (currentModule && currentModule.modulePath === modulePath) {
559-
setCurrentModule(project.robot);
560-
setActiveTab(project.robot.modulePath);
561-
}
562-
const indexToRemove: number = modulePaths.current.indexOf(modulePath);
563-
if (indexToRemove !== -1) {
564-
modulePaths.current.splice(indexToRemove, 1);
565-
}
566-
if (modulePath in modulePathToBlocklyComponent.current) {
567-
delete modulePathToBlocklyComponent.current[modulePath];
568-
}
569-
if (modulePath in modulePathToEditor.current) {
570-
const editor = modulePathToEditor.current[modulePath];
571-
editor.abandon();
572-
delete modulePathToEditor.current[modulePath];
573-
}
574-
}
575-
};
576-
fetchModules();
577-
}
533+
fetchModules();
578534
}, [project]);
579535

536+
const fetchModules = async () => {
537+
if (!project || !storage) {
538+
return;
539+
}
540+
const oldModulePathToContentText = modulePathToContentText;
541+
const promises: {[modulePath: string]: Promise<string>} = {}; // value is promise of module content.
542+
const updatedModulePathToContentText: {[modulePath: string]: string} = {}; // value is module content text
543+
if (project.robot.modulePath in modulePathToContentText) {
544+
updatedModulePathToContentText[project.robot.modulePath] = modulePathToContentText[project.robot.modulePath];
545+
} else {
546+
promises[project.robot.modulePath] = storage.fetchFileContentText(project.robot.modulePath);
547+
}
548+
project.mechanisms.forEach(mechanism => {
549+
if (mechanism.modulePath in modulePathToContentText) {
550+
updatedModulePathToContentText[mechanism.modulePath] = modulePathToContentText[mechanism.modulePath];
551+
} else {
552+
promises[mechanism.modulePath] = storage.fetchFileContentText(mechanism.modulePath);
553+
}
554+
});
555+
project.opModes.forEach(opmode => {
556+
if (opmode.modulePath in modulePathToContentText) {
557+
updatedModulePathToContentText[opmode.modulePath] = modulePathToContentText[opmode.modulePath];
558+
} else {
559+
promises[opmode.modulePath] = storage.fetchFileContentText(opmode.modulePath);
560+
}
561+
});
562+
if (Object.keys(promises).length) {
563+
await Promise.all(
564+
Object.entries(promises).map(async ([modulePath, promise]) => {
565+
updatedModulePathToContentText[modulePath] = await promise;
566+
})
567+
);
568+
setModulePathToContentText(updatedModulePathToContentText);
569+
}
570+
571+
// Remove any deleted modules from modulePaths, modulePathToBlocklyComponent, and
572+
// modulePathToEditor. Update currentModule if the current module was deleted.
573+
for (const modulePath in oldModulePathToContentText) {
574+
if (modulePath in updatedModulePathToContentText) {
575+
continue;
576+
}
577+
if (currentModule && currentModule.modulePath === modulePath) {
578+
setCurrentModule(project.robot);
579+
setActiveTab(project.robot.modulePath);
580+
}
581+
const indexToRemove: number = modulePaths.current.indexOf(modulePath);
582+
if (indexToRemove !== -1) {
583+
modulePaths.current.splice(indexToRemove, 1);
584+
}
585+
if (modulePath in modulePathToBlocklyComponent.current) {
586+
delete modulePathToBlocklyComponent.current[modulePath];
587+
}
588+
if (modulePath in modulePathToEditor.current) {
589+
const editor = modulePathToEditor.current[modulePath];
590+
editor.abandon();
591+
delete modulePathToEditor.current[modulePath];
592+
}
593+
}
594+
};
595+
580596
// Load saved tabs when project changes
581597
React.useEffect(() => {
582598
const loadSavedTabs = async () => {
583599
if (project && !isLoading) {
584600
setIsLoadingTabs(true);
585-
601+
586602
// Add a small delay to ensure UserSettingsProvider context is updated
587603
await new Promise(resolve => setTimeout(resolve, 0));
588604

@@ -705,7 +721,7 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
705721
}
706722
return tab;
707723
});
708-
724+
709725
// Only update if something actually changed
710726
const titlesChanged = updatedTabs.some((tab, index) => tab.title !== tabItems[index]?.title);
711727
if (titlesChanged) {
@@ -734,6 +750,10 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
734750
return () => clearTimeout(timeoutId);
735751
}, [tabItems, project?.projectName, isLoadingTabs]);
736752

753+
const onProjectChanged = async (): Promise<void> => {
754+
await fetchModules();
755+
};
756+
737757
const { Sider, Content } = Antd.Layout;
738758

739759
return (
@@ -766,6 +786,7 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
766786
gotoTab={setActiveTab}
767787
project={project}
768788
setProject={setProject}
789+
onProjectChanged={onProjectChanged}
769790
openWPIToolboxSettings={() => setToolboxSettingsModalIsOpen(true)}
770791
theme={theme}
771792
setTheme={setTheme}
@@ -784,7 +805,7 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
784805
currentModule={currentModule}
785806
setCurrentModule={changeModule}
786807
project={project}
787-
setProject={setProject}
808+
onProjectChanged={onProjectChanged}
788809
storage={storage}
789810
/>
790811
<div style={{ display: 'flex', height: FULL_HEIGHT }}>

src/reactComponents/AddTabDialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ interface AddTabDialogProps {
4141
onOk: (newTab: TabItem) => void;
4242
onCancel: () => void;
4343
project: storageProject.Project | null;
44-
setProject: (project: storageProject.Project | null) => void;
44+
onProjectChanged: () => Promise<void>;
4545
currentTabs: TabItem[];
4646
storage: commonStorage.Storage | null;
4747
}
@@ -111,6 +111,7 @@ export default function AddTabDialog(props: AddTabDialogProps) {
111111

112112
await storageProject.addModuleToProject(
113113
props.storage, props.project, moduleType, newClassName);
114+
await props.onProjectChanged();
114115

115116
const newModule = storageProject.findModuleByClassName(props.project, newClassName);
116117
if (newModule) {

src/reactComponents/FileManageModal.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ interface FileManageModalProps {
4040
isOpen: boolean;
4141
onClose: () => void;
4242
project: storageProject.Project | null;
43-
setProject: (project: storageProject.Project | null) => void;
43+
onProjectChanged: () => Promise<void>;
4444
gotoTab: (path: string) => void;
4545
setAlertErrorMessage: (message: string) => void;
4646
storage: commonStorage.Storage | null;
@@ -64,11 +64,6 @@ export default function FileManageModal(props: FileManageModalProps) {
6464
const [name, setName] = React.useState('');
6565
const [copyModalOpen, setCopyModalOpen] = React.useState(false);
6666

67-
const triggerProjectUpdate = (): void => {
68-
if (props.project) {
69-
props.setProject({...props.project});
70-
}
71-
}
7267
React.useEffect(() => {
7368
if (!props.project || props.tabType === null) {
7469
setModules([]);
@@ -109,6 +104,7 @@ export default function FileManageModal(props: FileManageModalProps) {
109104
newClassName,
110105
origModule.path
111106
);
107+
await props.onProjectChanged();
112108

113109
const newModules = modules.map((module) => {
114110
if (module.path === origModule.path) {
@@ -118,7 +114,6 @@ export default function FileManageModal(props: FileManageModalProps) {
118114
});
119115

120116
setModules(newModules);
121-
triggerProjectUpdate();
122117

123118
// Close the rename modal first
124119
setRenameModalOpen(false);
@@ -147,6 +142,7 @@ export default function FileManageModal(props: FileManageModalProps) {
147142
newClassName,
148143
origModule.path
149144
);
145+
await props.onProjectChanged();
150146

151147
const originalModule = modules.find((module) => module.path === origModule.path);
152148
if (!originalModule) {
@@ -165,7 +161,6 @@ export default function FileManageModal(props: FileManageModalProps) {
165161
newModules.push(newModule);
166162

167163
setModules(newModules);
168-
triggerProjectUpdate();
169164

170165
// Close the copy modal first
171166
setCopyModalOpen(false);
@@ -197,6 +192,7 @@ export default function FileManageModal(props: FileManageModalProps) {
197192
moduleType,
198193
newClassName
199194
);
195+
await props.onProjectChanged();
200196

201197
const newModule = storageProject.findModuleByClassName(props.project, newClassName);
202198
if (newModule) {
@@ -212,7 +208,7 @@ export default function FileManageModal(props: FileManageModalProps) {
212208
if(newModule){
213209
props.gotoTab(newModule.modulePath);
214210
}
215-
triggerProjectUpdate();
211+
216212
props.onClose();
217213
};
218214

@@ -227,7 +223,7 @@ export default function FileManageModal(props: FileManageModalProps) {
227223
props.project,
228224
record.path
229225
);
230-
triggerProjectUpdate();
226+
await props.onProjectChanged();
231227
}
232228
};
233229

src/reactComponents/Menu.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export interface MenuProps {
6060
gotoTab: (tabKey: string) => void;
6161
project: storageProject.Project | null;
6262
setProject: (project: storageProject.Project | null) => void;
63+
onProjectChanged: () => Promise<void>;
6364
openWPIToolboxSettings: () => void;
6465
theme: string;
6566
setTheme: (theme: string) => void;
@@ -429,7 +430,7 @@ export function Component(props: MenuProps): React.JSX.Element {
429430
project={props.project}
430431
storage={props.storage}
431432
tabType={tabType}
432-
setProject={props.setProject}
433+
onProjectChanged={props.onProjectChanged}
433434
setAlertErrorMessage={props.setAlertErrorMessage}
434435
gotoTab={props.gotoTab}
435436
/>

src/reactComponents/Tabs.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export interface TabsProps {
4848
setTabList: (items: TabItem[]) => void;
4949
activeTab: string;
5050
project: storageProject.Project | null;
51-
setProject: (project: storageProject.Project | null) => void;
51+
onProjectChanged: () => Promise<void>;
5252
setAlertErrorMessage: (message: string) => void;
5353
currentModule: storageModule.Module | null;
5454
setCurrentModule: (module: storageModule.Module | null) => void;
@@ -76,10 +76,6 @@ export function Component(props: TabsProps): React.JSX.Element {
7676
const [copyModalOpen, setCopyModalOpen] = React.useState(false);
7777
const [currentTab, setCurrentTab] = React.useState<TabItem | null>(null);
7878

79-
const triggerProjectUpdate = (): void => {
80-
props.setProject(structuredClone(props.project));
81-
}
82-
8379
/** Handles tab change and updates current module. */
8480
const handleTabChange = (key: string): void => {
8581
if (props.project) {
@@ -154,7 +150,7 @@ export function Component(props: TabsProps): React.JSX.Element {
154150
const handleAddTabOk = (newTab: TabItem): void => {
155151
props.setTabList([...props.tabList, newTab]);
156152

157-
setActiveKey(newTab.key);
153+
handleTabChange(newTab.key);
158154
setAddTabDialogOpen(false);
159155
};
160156

@@ -173,6 +169,7 @@ export function Component(props: TabsProps): React.JSX.Element {
173169
newClassName,
174170
oldModulePath,
175171
);
172+
await props.onProjectChanged();
176173

177174
const newTabs = props.tabList.map((tab) => {
178175
if (tab.key === key) {
@@ -183,7 +180,6 @@ export function Component(props: TabsProps): React.JSX.Element {
183180

184181
props.setTabList(newTabs);
185182
setActiveKey(newModulePath);
186-
triggerProjectUpdate();
187183
} catch (error) {
188184
console.error('Error renaming module:', error);
189185
props.setAlertErrorMessage(t('FAILED_TO_RENAME_MODULE'));
@@ -207,6 +203,7 @@ export function Component(props: TabsProps): React.JSX.Element {
207203
newClassName,
208204
oldModulePath,
209205
);
206+
await props.onProjectChanged();
210207

211208
const newTabs = [...props.tabList];
212209
const originalTab = props.tabList.find((tab) => tab.key === key);
@@ -220,7 +217,6 @@ export function Component(props: TabsProps): React.JSX.Element {
220217
newTabs.push({ key: newModulePath, title: newClassName, type: originalTab.type });
221218
props.setTabList(newTabs);
222219
setActiveKey(newModulePath);
223-
triggerProjectUpdate();
224220
} catch (error) {
225221
console.error('Error copying module:', error);
226222
props.setAlertErrorMessage(t('FAILED_TO_COPY_MODULE'));
@@ -271,7 +267,7 @@ export function Component(props: TabsProps): React.JSX.Element {
271267

272268
if (props.storage && props.project) {
273269
await storageProject.removeModuleFromProject(props.storage, props.project, tab.key);
274-
triggerProjectUpdate();
270+
await props.onProjectChanged();
275271
}
276272

277273
if (newTabs.length > 0) {
@@ -360,7 +356,7 @@ export function Component(props: TabsProps): React.JSX.Element {
360356
onCancel={() => setAddTabDialogOpen(false)}
361357
onOk={handleAddTabOk}
362358
project={props.project}
363-
setProject={props.setProject}
359+
onProjectChanged={props.onProjectChanged}
364360
currentTabs={props.tabList}
365361
storage={props.storage}
366362
/>

0 commit comments

Comments
 (0)