diff --git a/src/App.tsx b/src/App.tsx index 6539682b..8671cc74 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,6 @@ import { Button, ConfigProvider, Flex, - Input, message, Popconfirm, Space, @@ -16,7 +15,6 @@ import { theme, Upload } from 'antd'; -import type { InputRef } from 'antd'; import type { TreeDataNode, TreeProps } from 'antd'; import type { UploadProps } from 'antd'; import { @@ -55,8 +53,6 @@ import * as editor from './editor/editor'; import { extendedPythonGenerator } from './editor/extended_python_generator'; import { createGeneratorContext, GeneratorContext } from './editor/generator_context'; -import * as toolboxItems from './toolbox/items'; -import * as toolbox from './toolbox/toolbox'; //import { testAllBlocksInToolbox } from './toolbox/toolbox_tests'; import ToolboxSettingsModal from './toolbox/settings'; @@ -173,6 +169,9 @@ const App: React.FC = () => { }, [storage]); const fetchMostRecentModulePath = async () => { + if (!storage) { + return; + } try { const value = await storage.fetchEntry('mostRecentModulePath', ''); setMostRecentModulePath(value); @@ -183,6 +182,9 @@ const App: React.FC = () => { }; const initializeShownPythonToolboxCategories = async () => { + if (!storage) { + return; + } try { const value = await storage.fetchEntry('shownPythonToolboxCategories', '[]'); const shownCategories: string[] = JSON.parse(value); @@ -206,6 +208,10 @@ const App: React.FC = () => { const fetchListOfModules = async (): Promise => { return new Promise(async (resolve, reject) => { + if (!storage) { + reject(new Error()); + return; + } try { const array = await storage.listModules(); setModules(array) @@ -348,7 +354,7 @@ const App: React.FC = () => { } if (currentModule && blocklyComponent.current && generatorContext.current) { const blocklyWorkspace = blocklyComponent.current.getBlocklyWorkspace(); - setGeneratedCode(extendedPythonGenerator.workspaceToCode( + setGeneratedCode(extendedPythonGenerator.mrcWorkspaceToCode( blocklyWorkspace, generatorContext.current)); } else { setGeneratedCode(''); @@ -462,6 +468,9 @@ const App: React.FC = () => { }; const handleNewProjectNameOk = async (newProjectClassName: string) => { + if (!storage || !currentModule) { + return; + } const newProjectName = commonStorage.classNameToModuleName(newProjectClassName); const newProjectPath = commonStorage.makeProjectPath(newProjectName); if (newProjectNameModalPurpose === PURPOSE_NEW_PROJECT) { @@ -553,7 +562,10 @@ const App: React.FC = () => { }; const handleNewModuleNameOk = async (newModuleClassName: string) => { - const newModuleName = commonStorage.classNameToModuleName(newModuleClassName); + if (!storage || !currentModule) { + return; + } + const newModuleName = commonStorage.classNameToModuleName(newModuleClassName); const newModulePath = commonStorage.makeModulePath(currentModule.projectName, newModuleName); if (newModuleNameModalPurpose === PURPOSE_NEW_MECHANISM) { const mechanismContent = commonStorage.newMechanismContent( @@ -612,26 +624,30 @@ const App: React.FC = () => { }; const handleSaveClicked = async () => { - saveBlocks((success) => {}); + saveBlocks(); }; - const saveBlocks = async (): boolean => { - if (blocksEditor.current && currentModulePath) { + const saveBlocks = async (): Promise => { + return new Promise(async (resolve, reject) => { + if (!blocksEditor.current || !currentModulePath) { + reject(new Error()); + return; + } try { await blocksEditor.current.saveBlocks(); messageApi.open({ type: 'success', content: 'Save completed successfully.', }); - return true; - } catch (e: Error) { + resolve(true); + } catch (e) { console.log('Failed to save the blocks. Caught the following error...'); console.log(e); setAlertErrorMessage('Failed to save the blocks.'); setAlertErrorVisible(true); + reject(new Error('Failed to save the blocks.')); } - } - return false; + }); }; const handleRenameClicked = () => { @@ -715,7 +731,7 @@ const App: React.FC = () => { afterPopconfirmOk.current = () => { setOpenPopconfirm(false); checkIfBlocksWereModified(async () => { - if (!currentModule) { + if (!storage || !currentModule) { return; } if (currentModule.moduleType == commonStorage.MODULE_TYPE_PROJECT) { @@ -779,15 +795,20 @@ const App: React.FC = () => { } return isBlocks || Upload.LIST_IGNORE; }, - onChange: (info) => { - }, customRequest: ({ file, onSuccess, onError }) => { + if (!onSuccess || !onError) { + return; + } + const fileObject = file as File; const reader = new FileReader(); reader.onload = async (event) => { - const dataUrl = event.target.result; - const uploadProjectName = commonStorage.makeUploadProjectName(file.name, getProjectNames()); + const dataUrl = event.target?.result; + if (!storage || !dataUrl) { + return; + } + const uploadProjectName = commonStorage.makeUploadProjectName(fileObject.name, getProjectNames()); try { - await storage.uploadProject(uploadProjectName, dataUrl); + await storage.uploadProject(uploadProjectName, dataUrl as string); onSuccess('Upload successful'); await fetchListOfModules(); const uploadProjectPath = commonStorage.makeProjectPath(uploadProjectName); @@ -795,7 +816,7 @@ const App: React.FC = () => { } catch (e) { console.log('Failed to upload the project. Caught the following error...'); console.log(e); - onError('Failed to upload the project.'); + onError(new Error('Failed to upload the project.')); setAlertErrorMessage('Failed to upload the project'); setAlertErrorVisible(true); } @@ -803,16 +824,19 @@ const App: React.FC = () => { reader.onerror = (error) => { console.log('Failed to upload the project. reader.onerror called with the following error...'); console.log(error); - onError('Failed to upload the project.'); + onError(new Error('Failed to upload the project.')); setAlertErrorMessage('Failed to upload the project'); setAlertErrorVisible(true); }; - reader.readAsDataURL(file); + reader.readAsDataURL(fileObject); }, }; const handleDownloadClicked = () => { checkIfBlocksWereModified(async () => { + if (!storage || !currentModule) { + return; + } try { const url = await storage.downloadProject(currentModule.projectName); const link = document.createElement('a'); @@ -833,13 +857,16 @@ const App: React.FC = () => { }; const handleToolboxSettingsOk = async (updatedShownCategories: Set) => { + if (!storage) { + return; + } setShownPythonToolboxCategories(updatedShownCategories); const array = Array.from(updatedShownCategories); array.sort(); storage.saveEntry('shownPythonToolboxCategories', JSON.stringify(array)); }; - const handleModuleSelected: TreeProps['onSelect'] = (a: React.Key[], e) => { + const handleModuleSelected: TreeProps['onSelect'] = (a: React.Key[]) => { if (a.length === 1) { checkIfBlocksWereModified(() => { setTreeSelectedKey(a[0]); diff --git a/src/blocks/mrc_call_python_function.ts b/src/blocks/mrc_call_python_function.ts index 185c4f9a..3d7b3fba 100644 --- a/src/blocks/mrc_call_python_function.ts +++ b/src/blocks/mrc_call_python_function.ts @@ -21,7 +21,7 @@ import * as Blockly from 'blockly'; -import { Order, PythonGenerator } from 'blockly/python'; +import { Order } from 'blockly/python'; import * as pythonUtils from './utils/generated/python'; import { createFieldNonEditableText } from '../fields/FieldNonEditableText'; @@ -58,7 +58,7 @@ interface CallPythonFunctionMixin extends CallPythonFunctionMixinType { mrcImportModule: string, mrcActualFunctionName: string, mrcExportedFunction: boolean, - renameMethod(this: CallPythonFunctionBlock, oldName: string, newName: string): void; + renameMethod(this: CallPythonFunctionBlock, newName: string): void; mutateMethod(this: CallPythonFunctionBlock, defBlockExtraState: ClassMethodDefExtraState): void; } type CallPythonFunctionMixinType = typeof CALL_PYTHON_FUNCTION; @@ -139,7 +139,7 @@ const CALL_PYTHON_FUNCTION = { break; } default: - throw new Error('mrcFunctionKind has unexpected value: ' + mrcFunctionKind) + throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind) } const funcTooltip = this.mrcTooltip; if (funcTooltip) { @@ -261,7 +261,7 @@ const CALL_PYTHON_FUNCTION = { break; } default: - throw new Error('mrcFunctionKind has unexpected value: ' + mrcFunctionKind) + throw new Error('mrcFunctionKind has unexpected value: ' + this.mrcFunctionKind) } } @@ -305,7 +305,7 @@ const CALL_PYTHON_FUNCTION = { defBlockExtraState.params.forEach((param) => { this.mrcArgs.push({ 'name': param.name, - 'type': param.type, + 'type': param.type ?? '', }); }); this.updateBlock_(); @@ -345,7 +345,6 @@ export const pythonFromBlock = function( break; } case FunctionKind.CONSTRUCTOR: { - const callPythonFunctionBlock = block as CallPythonFunctionBlock; const className = block.getFieldValue(pythonUtils.FIELD_MODULE_OR_CLASS_NAME); code = className; break; @@ -367,7 +366,7 @@ export const pythonFromBlock = function( break; } default: - throw new Error('mrcFunctionKind has unexpected value: ' + mrcFunctionKind) + throw new Error('mrcFunctionKind has unexpected value: ' + callPythonFunctionBlock.mrcFunctionKind) } code += '(' + generateCodeForArguments(callPythonFunctionBlock, generator, argStartIndex) + ')'; if (block.outputConnection) { diff --git a/src/blocks/mrc_class_method_def.ts b/src/blocks/mrc_class_method_def.ts index 22c71943..e1a43d08 100644 --- a/src/blocks/mrc_class_method_def.ts +++ b/src/blocks/mrc_class_method_def.ts @@ -33,19 +33,12 @@ export const BLOCK_NAME = 'mrc_class_method_def'; const MUTATOR_BLOCK_NAME = 'methods_mutatorarg'; const PARAM_CONTAINER_BLOCK_NAME = 'method_param_container'; -class MethodInput extends Blockly.inputs.Input { - readonly type = Blockly.inputs.inputTypes.CUSTOM; - constructor(name: string, block: Blockly.Block) { - super(name, block); - } -} - export type Parameter = { name: string, type?: string, }; -type ClassMethodDefBlock = Blockly.Block & ClassMethodDefMixin & Blockly.BlockSvg; +export type ClassMethodDefBlock = Blockly.Block & ClassMethodDefMixin & Blockly.BlockSvg; interface ClassMethodDefMixin extends ClassMethodDefMixinType { mrcCanChangeSignature: boolean, mrcCanBeCalledWithinClass: boolean, @@ -57,7 +50,7 @@ interface ClassMethodDefMixin extends ClassMethodDefMixinType { type ClassMethodDefMixinType = typeof CLASS_METHOD_DEF; /** Extra state for serialising call_python_* blocks. */ -type ClassMethodDefExtraState = { +export type ClassMethodDefExtraState = { /** * Can change name and parameters and return type */ @@ -178,7 +171,7 @@ const CLASS_METHOD_DEF = { let paramBlock = containerBlock.getInputTargetBlock('STACK'); while (paramBlock && !paramBlock.isInsertionMarker()) { - let param : Parameter = { + const param : Parameter = { name : paramBlock.getFieldValue('NAME'), type : '' } @@ -192,13 +185,13 @@ const CLASS_METHOD_DEF = { decompose: function (this: ClassMethodDefBlock, workspace: Blockly.Workspace) { // This is a special sub-block that only gets created in the mutator UI. // It acts as our "top block" - let topBlock = workspace.newBlock(PARAM_CONTAINER_BLOCK_NAME); + const topBlock = workspace.newBlock(PARAM_CONTAINER_BLOCK_NAME); (topBlock as Blockly.BlockSvg).initSvg(); // Then we add one sub-block for each item in the list. - var connection = topBlock!.getInput('STACK')!.connection; + let connection = topBlock!.getInput('STACK')!.connection; - for (var i = 0; i < this.mrcParameters.length; i++) { + for (let i = 0; i < this.mrcParameters.length; i++) { let itemBlock = workspace.newBlock(MUTATOR_BLOCK_NAME); (itemBlock as Blockly.BlockSvg).initSvg(); itemBlock.setFieldValue(this.mrcParameters[i].name, 'NAME') @@ -237,7 +230,7 @@ const CLASS_METHOD_DEF = { const legalName = findLegalMethodName(name, this); const oldName = nameField.getValue(); - if (oldName !== name && oldName !== legalName) { + if (oldName && oldName !== name && oldName !== legalName) { // Rename any callers. renameMethodCallers(this.workspace, oldName, legalName); } @@ -282,7 +275,7 @@ function findLegalMethodName(name: string, block: ClassMethodDefBlock): string { * @returns True if the name is used, otherwise return false. */ function isMethodNameUsed( - name: string, workspace: Workspace, opt_exclude?: Block): boolean { + name: string, workspace: Blockly.Workspace, opt_exclude?: Blockly.Block): boolean { const nameLowerCase = name.toLowerCase(); for (const block of workspace.getBlocksByType('mrc_class_method_def')) { if (block === opt_exclude) { @@ -291,8 +284,9 @@ function isMethodNameUsed( if (nameLowerCase === block.getFieldValue('NAME').toLowerCase()) { return true; } - if (block.mrcPythonMethodName && - nameLowerCase === block.mrcPythonMethodName.toLowerCase()) { + const classMethodDefBlock = block as ClassMethodDefBlock; + if (classMethodDefBlock.mrcPythonMethodName && + nameLowerCase === classMethodDefBlock.mrcPythonMethodName.toLowerCase()) { return true; } } @@ -315,11 +309,10 @@ interface MethodMutatorArgMixin extends MethodMutatorArgMixinType{ type MethodMutatorArgMixinType = typeof METHODS_MUTATORARG; function setName(block: Blockly.BlockSvg){ - let parentBlock = ChangeFramework.getParentOfType(block, PARAM_CONTAINER_BLOCK_NAME); + const parentBlock = ChangeFramework.getParentOfType(block, PARAM_CONTAINER_BLOCK_NAME); if (parentBlock) { - let done = false; - let variableBlocks = parentBlock!.getDescendants(true) - let otherNames: string[] = [] + const variableBlocks = parentBlock!.getDescendants(true) + const otherNames: string[] = [] variableBlocks?.forEach(function (variableBlock) { if (variableBlock != block) { otherNames.push(variableBlock.getFieldValue('NAME')); @@ -465,7 +458,7 @@ export const pythonFromBlock = function ( xfix2 = xfix1; } if(block.mrcPythonMethodName == '__init__'){ - branch = generator.defineClassVariables(block.workspace) + branch; + branch = generator.defineClassVariables() + branch; } if (returnValue) { returnValue = generator.INDENT + 'return ' + returnValue + '\n'; diff --git a/src/blocks/mrc_get_python_enum_value.ts b/src/blocks/mrc_get_python_enum_value.ts index 9dfa26d1..69fd8c06 100644 --- a/src/blocks/mrc_get_python_enum_value.ts +++ b/src/blocks/mrc_get_python_enum_value.ts @@ -21,7 +21,7 @@ import * as Blockly from 'blockly'; -import { Order, PythonGenerator } from 'blockly/python'; +import { Order } from 'blockly/python'; import * as pythonUtils from './utils/generated/python'; import { createFieldDropdown } from '../fields/FieldDropdown'; diff --git a/src/blocks/mrc_get_python_variable.ts b/src/blocks/mrc_get_python_variable.ts index 6f631feb..359e6e38 100644 --- a/src/blocks/mrc_get_python_variable.ts +++ b/src/blocks/mrc_get_python_variable.ts @@ -21,7 +21,7 @@ import * as Blockly from 'blockly'; -import { Order, PythonGenerator } from 'blockly/python'; +import { Order } from 'blockly/python'; import * as pythonUtils from './utils/generated/python'; import { createFieldDropdown } from '../fields/FieldDropdown'; diff --git a/src/blocks/mrc_mechanism.ts b/src/blocks/mrc_mechanism.ts index 2187a5aa..56d8c6ad 100644 --- a/src/blocks/mrc_mechanism.ts +++ b/src/blocks/mrc_mechanism.ts @@ -20,12 +20,12 @@ * @author alan@porpoiseful.com (Alan Smith) */ import * as Blockly from 'blockly'; -import { Order, PythonGenerator } from 'blockly/python'; +import { Order } from 'blockly/python'; import { MRC_STYLE_FUNCTIONS } from '../themes/styles' import { createFieldNonEditableText } from '../fields/FieldNonEditableText'; import { ExtendedPythonGenerator } from '../editor/extended_python_generator'; -import { getAllowedTypesForSetCheck, getOutputCheck } from './utils/python'; +import { getAllowedTypesForSetCheck } from './utils/python'; export const BLOCK_NAME = 'mrc_mechanism'; export const OUTPUT_NAME = 'mrc_mechansim'; diff --git a/src/blocks/mrc_misc_comment.ts b/src/blocks/mrc_misc_comment.ts index fe480ce2..4d84d3aa 100644 --- a/src/blocks/mrc_misc_comment.ts +++ b/src/blocks/mrc_misc_comment.ts @@ -42,7 +42,7 @@ export const setup = function() { export const pythonFromBlock = function( block: Blockly.Block, - generator: PythonGenerator, + _: PythonGenerator, ) { return '# ' + block.getFieldValue('COMMENT') + '\n'; }; diff --git a/src/blocks/mrc_set_python_variable.ts b/src/blocks/mrc_set_python_variable.ts index 16724d38..87124f04 100644 --- a/src/blocks/mrc_set_python_variable.ts +++ b/src/blocks/mrc_set_python_variable.ts @@ -21,12 +21,12 @@ import * as Blockly from 'blockly'; -import { Order, PythonGenerator } from 'blockly/python'; +import { Order } from 'blockly/python'; import * as pythonUtils from './utils/generated/python'; import { createFieldDropdown } from '../fields/FieldDropdown'; import { createFieldNonEditableText } from '../fields/FieldNonEditableText'; -import { getAllowedTypesForSetCheck, getOutputCheck } from './utils/python'; +import { getAllowedTypesForSetCheck } from './utils/python'; import { ExtendedPythonGenerator } from '../editor/extended_python_generator'; import { MRC_STYLE_VARIABLES } from '../themes/styles'; diff --git a/src/editor/editor.ts b/src/editor/editor.ts index 2d9ca141..6cc9994b 100644 --- a/src/editor/editor.ts +++ b/src/editor/editor.ts @@ -211,18 +211,24 @@ export class Editor { } private getModuleContent(): string { - const pythonCode = extendedPythonGenerator.workspaceToCode(this.blocklyWorkspace, this.generatorContext); + if (!this.currentModule) { + throw new Error('getModuleContent: this.currentModule is null.'); + } + const pythonCode = extendedPythonGenerator.mrcWorkspaceToCode( + this.blocklyWorkspace, this.generatorContext); const exportedBlocks = JSON.stringify(this.generatorContext.getExportedBlocks()); - const blocksContent = JSON.stringify(Blockly.serialization.workspaces.save(this.blocklyWorkspace)); - return commonStorage.makeModuleContent(this.currentModule, pythonCode, exportedBlocks, blocksContent); + const blocksContent = JSON.stringify( + Blockly.serialization.workspaces.save(this.blocklyWorkspace)); + return commonStorage.makeModuleContent( + this.currentModule, pythonCode, exportedBlocks, blocksContent); } - public async saveBlocks(): void { + public async saveBlocks() { const moduleContent = this.getModuleContent(); try { await this.storage.saveModule(this.modulePath, moduleContent); this.moduleContent = moduleContent; - } catch (e: Error) { + } catch (e) { throw e; } } diff --git a/src/editor/extended_python_generator.ts b/src/editor/extended_python_generator.ts index daedb203..3780a7bc 100644 --- a/src/editor/extended_python_generator.ts +++ b/src/editor/extended_python_generator.ts @@ -59,23 +59,23 @@ export class ExtendedPythonGenerator extends PythonGenerator { * This is called from the python generator for the mrc_class_method_def for the * init method */ - defineClassVariables(workspace: Blockly.Workspace) : string{ + defineClassVariables() : string { let variableDefinitions = ''; - if (this.context?.getHasMechanisms()){ + if (this.context?.getHasMechanisms()) { variableDefinitions += this.INDENT + "self.mechanisms = []\n"; } return variableDefinitions; } - getVariableName(nameOrId : string) : string{ - const varName = super.getVariableName(name); + getVariableName(nameOrId: string): string { + const varName = super.getVariableName(nameOrId); return "self." + varName; } setHasMechanism() : void{ this.context?.setHasMechanism(); } - workspaceToCode(workspace: Blockly.Workspace, context: GeneratorContext): string { + mrcWorkspaceToCode(workspace: Blockly.Workspace, context: GeneratorContext): string { this.workspace = workspace; this.context = context; this.context.clear(); @@ -102,7 +102,7 @@ export class ExtendedPythonGenerator extends PythonGenerator { } finish(code: string): string { - if (this.context) { + if (this.context && this.workspace) { const className = this.context.getClassName(); const classParent = this.context.getClassParent(); this.addImport(classParent); diff --git a/src/editor/generator_context.ts b/src/editor/generator_context.ts index 6501a4d7..4532e2cb 100644 --- a/src/editor/generator_context.ts +++ b/src/editor/generator_context.ts @@ -55,26 +55,20 @@ export class GeneratorContext { } getClassName(): string { + if (!this.module) { + throw new Error('getClassName: this.module is null.'); + } if (this.module.moduleType === commonStorage.MODULE_TYPE_PROJECT) { return 'Robot'; } - // TODO(lizlooney): className should be a field in commonStorage.Module. - // Until that happens, we'll figure it out now from the module name. - - let className = ''; - let nextCharUpper = true; - for (let i = 0; i < this.module.moduleName.length; i++) { - const char = this.module.moduleName.charAt(i); - if (char !== '_') { - className += nextCharUpper ? char.toUpperCase() : char; - } - nextCharUpper = (char === '_'); - } - return className; + return this.module.className; } getClassParent(): string { + if (!this.module) { + throw new Error('getClassParent: this.module is null.'); + } if (this.module.moduleType === commonStorage.MODULE_TYPE_PROJECT) { return 'RobotBase'; } @@ -91,7 +85,7 @@ export class GeneratorContext { this.exportedBlocks.length = 0; } - setExportedBlocks(exportedBlocks: Blocks[]) { + setExportedBlocks(exportedBlocks: Block[]) { this.exportedBlocks.length = 0; this.exportedBlocks.push(...exportedBlocks); } diff --git a/src/modal/NewModuleNameModal.tsx b/src/modal/NewModuleNameModal.tsx index a4875faa..3b28bb33 100644 --- a/src/modal/NewModuleNameModal.tsx +++ b/src/modal/NewModuleNameModal.tsx @@ -83,7 +83,7 @@ export const NewModuleNameModal: React.FC = ({ title, e } }; - const onChange = (e) => { + const onChange = (e: React.ChangeEvent) => { setValue(commonStorage.onChangeClassName(e.target.value)); }; diff --git a/src/modal/NewProjectNameModal.tsx b/src/modal/NewProjectNameModal.tsx index 63335d07..3ee3f0dc 100644 --- a/src/modal/NewProjectNameModal.tsx +++ b/src/modal/NewProjectNameModal.tsx @@ -76,7 +76,7 @@ export const NewProjectNameModal: React.FC = ({ title, } }; - const onChange = (e) => { + const onChange = (e: React.ChangeEvent) => { setValue(commonStorage.onChangeClassName(e.target.value)); }; diff --git a/src/storage/client_side_storage.ts b/src/storage/client_side_storage.ts index 5a976462..d24a189a 100644 --- a/src/storage/client_side_storage.ts +++ b/src/storage/client_side_storage.ts @@ -28,15 +28,15 @@ const DATABASE_NAME = 'systemcore-blocks-interface'; export async function openClientSideStorage(): Promise { return new Promise((resolve, reject) => { const openRequest = window.indexedDB.open(DATABASE_NAME, 1); - openRequest.onerror = (event: Event) => { + openRequest.onerror = () => { console.log('IndexedDB open request failed. openRequest.error is...'); console.log(openRequest.error); reject(new Error('IndexedDB open request failed.')); }; - openRequest.onupgradeneeded = (event: Event) => { + openRequest.onupgradeneeded = () => { const db = openRequest.result; - var stores = db.objectStoreNames; + const stores = db.objectStoreNames; if (!stores.contains('entries')) { // Create an object store for key/value entries. @@ -48,9 +48,9 @@ export async function openClientSideStorage(): Promise { db.createObjectStore('modules', { keyPath: 'path' }); } }; - openRequest.onsuccess = (event: Event) => { + openRequest.onsuccess = () => { const db = openRequest.result; - resolve(new ClientSideStorage(db)); + resolve(ClientSideStorage.create(db)); }; }); } @@ -58,6 +58,10 @@ export async function openClientSideStorage(): Promise { class ClientSideStorage implements commonStorage.Storage { db: IDBDatabase; + static create(db: IDBDatabase) { + return new ClientSideStorage(db) + } + private constructor(db: IDBDatabase) { this.db = db; } @@ -65,7 +69,7 @@ class ClientSideStorage implements commonStorage.Storage { async saveEntry(entryKey: string, entryValue: string): Promise { return new Promise((resolve, reject) => { const transaction = this.db.transaction(['entries'], 'readwrite'); - transaction.oncomplete = (event: Event) => { + transaction.oncomplete = () => { resolve(); }; transaction.onabort = () => { @@ -74,12 +78,12 @@ class ClientSideStorage implements commonStorage.Storage { }; const entriesObjectStore = transaction.objectStore('entries'); const getRequest = entriesObjectStore.get(entryKey); - getRequest.onerror = (event: Event) => { + getRequest.onerror = () => { console.log('IndexedDB get request failed. getRequest.error is...'); console.log(getRequest.error); throw new Error('IndexedDB get request failed.'); }; - getRequest.onsuccess = (event: Event) => { + getRequest.onsuccess = () => { let value; if (getRequest.result === undefined) { value = Object.create(null); @@ -89,7 +93,7 @@ class ClientSideStorage implements commonStorage.Storage { } value.value = entryValue; const putRequest = entriesObjectStore.put(value); - putRequest.onerror = (event: Event) => { + putRequest.onerror = () => { console.log('IndexedDB put request failed. putRequest.error is...'); console.log(putRequest.error); throw new Error('IndexedDB put request failed.'); @@ -102,12 +106,12 @@ class ClientSideStorage implements commonStorage.Storage { return new Promise((resolve, reject) => { const getRequest = this.db.transaction(['entries'], 'readonly') .objectStore('entries').get(entryKey); - getRequest.onerror = (event: Event) => { + getRequest.onerror = () => { console.log('IndexedDB get request failed. getRequest.error is...'); console.log(getRequest.error); reject(new Error('IndexedDB get request failed.')); }; - getRequest.onsuccess = (event: Event) => { + getRequest.onsuccess = () => { const value = (getRequest.result === undefined) ? defaultValue : getRequest.result.value; resolve(value); }; @@ -124,12 +128,12 @@ class ClientSideStorage implements commonStorage.Storage { const openCursorRequest = this.db.transaction(['modules'], 'readonly') .objectStore('modules') .openCursor(); - openCursorRequest.onerror = (event: Event) => { + openCursorRequest.onerror = () => { console.log('IndexedDB openCursor request failed. openCursorRequest.error is...'); console.log(openCursorRequest.error); reject(new Error('IndexedDB openCursor request failed.')); }; - openCursorRequest.onsuccess = (event: Event) => { + openCursorRequest.onsuccess = () => { const cursor = openCursorRequest.result; if (cursor) { const value = cursor.value; @@ -214,12 +218,12 @@ class ClientSideStorage implements commonStorage.Storage { return new Promise((resolve, reject) => { const getRequest = this.db.transaction(['modules'], 'readonly') .objectStore('modules').get(modulePath); - getRequest.onerror = (event: Event) => { + getRequest.onerror = () => { console.log('IndexedDB get request failed. getRequest.error is...'); console.log(getRequest.error); reject(new Error('IndexedDB get request failed.')); }; - getRequest.onsuccess = (event: Event) => { + getRequest.onsuccess = () => { if (getRequest.result === undefined) { // Module does not exist. reject(new Error('IndexedDB get request succeeded, but the module does not exist.')); @@ -244,7 +248,7 @@ class ClientSideStorage implements commonStorage.Storage { // When saving an existing module, the moduleType must be falsy. return new Promise((resolve, reject) => { const transaction = this.db.transaction(['modules'], 'readwrite'); - transaction.oncomplete = (event: Event) => { + transaction.oncomplete = () => { resolve(); }; transaction.onabort = () => { @@ -253,12 +257,12 @@ class ClientSideStorage implements commonStorage.Storage { }; const modulesObjectStore = transaction.objectStore('modules'); const getRequest = modulesObjectStore.get(modulePath); - getRequest.onerror = (event: Event) => { + getRequest.onerror = () => { console.log('IndexedDB get request failed. getRequest.error is...'); console.log(getRequest.error); throw new Error('IndexedDB get request failed.'); }; - getRequest.onsuccess = (event: Event) => { + getRequest.onsuccess = () => { let value; if (getRequest.result === undefined) { // The module does not exist. @@ -286,7 +290,7 @@ class ClientSideStorage implements commonStorage.Storage { value.content = moduleContent; value.dateModifiedMillis = Date.now(); const putRequest = modulesObjectStore.put(value); - putRequest.onerror = (event: Event) => { + putRequest.onerror = () => { console.log('IndexedDB put request failed. putRequest.error is...'); console.log(putRequest.error); throw new Error('IndexedDB put request failed.'); @@ -297,12 +301,8 @@ class ClientSideStorage implements commonStorage.Storage { private async _renameOrCopyProject(oldProjectName: string, newProjectName: string, copy: boolean): Promise { return new Promise((resolve, reject) => { - const errorMessage = copy - ? 'Copy Project failed.' - : 'Rename Project failed.' - const transaction = this.db.transaction(['modules'], 'readwrite'); - transaction.oncomplete = (event: Event) => { + transaction.oncomplete = () => { resolve(); }; transaction.onabort = () => { @@ -313,12 +313,12 @@ class ClientSideStorage implements commonStorage.Storage { // First get the list of modules in the project. const oldToNewModulePaths: {[key: string]: string} = {}; const openCursorRequest = modulesObjectStore.openCursor(); - openCursorRequest.onerror = (event: Event) => { + openCursorRequest.onerror = () => { console.log('IndexedDB openCursor request failed. openCursorRequest.error is...'); console.log(openCursorRequest.error); throw new Error('IndexedDB openCursor request failed.'); }; - openCursorRequest.onsuccess = (event: Event) => { + openCursorRequest.onsuccess = () => { const cursor = openCursorRequest.result; if (cursor) { const value = cursor.value; @@ -339,12 +339,12 @@ class ClientSideStorage implements commonStorage.Storage { // Now rename the project for each of the modules. Object.entries(oldToNewModulePaths).forEach(([oldModulePath, newModulePath]) => { const getRequest = modulesObjectStore.get(oldModulePath); - getRequest.onerror = (event: Event) => { + getRequest.onerror = () => { console.log('IndexedDB get request failed. getRequest.error is...'); console.log(getRequest.error); throw new Error('IndexedDB get request failed.'); }; - getRequest.onsuccess = (event: Event) => { + getRequest.onsuccess = () => { if (getRequest.result === undefined) { console.log('IndexedDB get request succeeded, but the module does not exist.'); throw new Error('IndexedDB get request succeeded, but the module does not exist.'); @@ -353,21 +353,19 @@ class ClientSideStorage implements commonStorage.Storage { value.path = newModulePath; value.dateModifiedMillis = Date.now(); const putRequest = modulesObjectStore.put(value); - putRequest.onerror = (event: Event) => { + putRequest.onerror = () => { console.log('IndexedDB put request failed. putRequest.error is...'); console.log(putRequest.error); throw new Error('IndexedDB put request failed.'); }; - putRequest.onsuccess = (event: Event) => { + putRequest.onsuccess = () => { if (!copy) { const deleteRequest = modulesObjectStore.delete(oldModulePath); - deleteRequest.onerror = (event: Event) => { + deleteRequest.onerror = () => { console.log('IndexedDB delete request failed. deleteRequest.error is...'); console.log(deleteRequest.error); throw new Error('IndexedDB delete request failed.'); }; - deleteRequest.onsuccess = (event: Event) => { - }; } }; }; @@ -400,12 +398,8 @@ class ClientSideStorage implements commonStorage.Storage { } return new Promise((resolve, reject) => { - const errorMessage = copy - ? 'Copy module failed.' - : 'Rename module failed.' - const transaction = this.db.transaction(['modules'], 'readwrite'); - transaction.oncomplete = (event: Event) => { + transaction.oncomplete = () => { resolve(); }; transaction.onabort = () => { @@ -416,12 +410,12 @@ class ClientSideStorage implements commonStorage.Storage { const oldModulePath = commonStorage.makeModulePath(projectName, oldModuleName); const newModulePath = commonStorage.makeModulePath(projectName, newModuleName); const getRequest = modulesObjectStore.get(oldModulePath); - getRequest.onerror = (event: Event) => { + getRequest.onerror = () => { console.log('IndexedDB get request failed. getRequest.error is...'); console.log(getRequest.error); throw new Error('IndexedDB get request failed.'); }; - getRequest.onsuccess = (event: Event) => { + getRequest.onsuccess = () => { if (getRequest.result === undefined) { console.log('IndexedDB get request succeeded, but the module does not exist.'); throw new Error('IndexedDB get request succeeded, but the module does not exist.'); @@ -431,20 +425,20 @@ class ClientSideStorage implements commonStorage.Storage { value.path = newModulePath; value.dateModifiedMillis = Date.now(); const putRequest = modulesObjectStore.put(value); - putRequest.onerror = (event: Event) => { + putRequest.onerror = () => { console.log('IndexedDB put request failed. putRequest.error is...'); console.log(putRequest.error); throw new Error('IndexedDB put request failed.'); }; - putRequest.onsuccess = (event: Event) => { + putRequest.onsuccess = () => { if (!copy) { const deleteRequest = modulesObjectStore.delete(oldModulePath); - deleteRequest.onerror = (event: Event) => { + deleteRequest.onerror = () => { console.log('IndexedDB delete request failed. deleteRequest.error is...'); console.log(deleteRequest.error); throw new Error('IndexedDB delete request failed.'); }; - deleteRequest.onsuccess = (event: Event) => { + deleteRequest.onsuccess = () => { }; } }; @@ -455,7 +449,7 @@ class ClientSideStorage implements commonStorage.Storage { private async _deleteProject(projectName: string): Promise { return new Promise((resolve, reject) => { const transaction = this.db.transaction(['modules'], 'readwrite'); - transaction.oncomplete = (event: Event) => { + transaction.oncomplete = () => { resolve(); }; transaction.onabort = () => { @@ -466,12 +460,12 @@ class ClientSideStorage implements commonStorage.Storage { // First get the list of modulePaths in the project. const modulePaths: string[] = []; const openCursorRequest = modulesObjectStore.openCursor(); - openCursorRequest.onerror = (event: Event) => { + openCursorRequest.onerror = () => { console.log('IndexedDB openCursor request failed. openCursorRequest.error is...'); console.log(openCursorRequest.error); throw new Error('IndexedDB openCursor request failed.'); }; - openCursorRequest.onsuccess = (event: Event) => { + openCursorRequest.onsuccess = () => { const cursor = openCursorRequest.result; if (cursor) { const value = cursor.value; @@ -484,12 +478,12 @@ class ClientSideStorage implements commonStorage.Storage { // Now delete each of the modules. modulePaths.forEach((modulePath) => { const deleteRequest = modulesObjectStore.delete(modulePath); - deleteRequest.onerror = (event: Event) => { + deleteRequest.onerror = () => { console.log('IndexedDB delete request failed. deleteRequest.error is...'); console.log(deleteRequest.error); throw new Error('IndexedDB delete request failed.'); }; - deleteRequest.onsuccess = (event: Event) => { + deleteRequest.onsuccess = () => { }; }); } @@ -505,7 +499,7 @@ class ClientSideStorage implements commonStorage.Storage { return new Promise((resolve, reject) => { const transaction = this.db.transaction(['modules'], 'readwrite'); - transaction.oncomplete = (event: Event) => { + transaction.oncomplete = () => { resolve(); }; transaction.onabort = () => { @@ -514,12 +508,12 @@ class ClientSideStorage implements commonStorage.Storage { }; const modulesObjectStore = transaction.objectStore('modules'); const deleteRequest = modulesObjectStore.delete(modulePath); - deleteRequest.onerror = (event: Event) => { + deleteRequest.onerror = () => { console.log('IndexedDB delete request failed. deleteRequest.error is...'); console.log(deleteRequest.error); throw new Error('IndexedDB delete request failed.'); }; - deleteRequest.onsuccess = (event: Event) => { + deleteRequest.onsuccess = () => { }; }); } @@ -531,12 +525,12 @@ class ClientSideStorage implements commonStorage.Storage { const openCursorRequest = this.db.transaction(['modules'], 'readonly') .objectStore('modules') .openCursor(); - openCursorRequest.onerror = (event: Event) => { + openCursorRequest.onerror = () => { console.log('IndexedDB openCursor request failed. openCursorRequest.error is...'); console.log(openCursorRequest.error); reject(new Error('IndexedDB openCursor request failed.')); }; - openCursorRequest.onsuccess = async (event: Event) => { + openCursorRequest.onsuccess = async () => { const cursor = openCursorRequest.result; if (cursor) { const value = cursor.value; @@ -571,7 +565,7 @@ class ClientSideStorage implements commonStorage.Storage { // Save each module. const transaction = this.db.transaction(['modules'], 'readwrite'); - transaction.oncomplete = (event: Event) => { + transaction.oncomplete = () => { resolve(); }; transaction.onabort = () => { @@ -585,12 +579,12 @@ class ClientSideStorage implements commonStorage.Storage { const moduleContent = moduleContents[moduleName]; const modulePath = commonStorage.makeModulePath(projectName, moduleName); const getRequest = modulesObjectStore.get(modulePath); - getRequest.onerror = (event: Event) => { + getRequest.onerror = () => { console.log('IndexedDB get request failed. getRequest.error is...'); console.log(getRequest.error); throw new Error('IndexedDB get request failed.'); }; - getRequest.onsuccess = (event: Event) => { + getRequest.onsuccess = () => { if (getRequest.result !== undefined) { // The module already exists. That is not expected! console.log('IndexedDB get request succeeded, but the module already exists.'); @@ -602,7 +596,7 @@ class ClientSideStorage implements commonStorage.Storage { value.content = moduleContent; value.dateModifiedMillis = Date.now(); const putRequest = modulesObjectStore.put(value); - putRequest.onerror = (event: Event) => { + putRequest.onerror = () => { console.log('IndexedDB put request failed. putRequest.error is...'); console.log(putRequest.error); throw new Error('IndexedDB put request failed.'); diff --git a/src/storage/common_storage.ts b/src/storage/common_storage.ts index fa24bba8..f3acdfda 100644 --- a/src/storage/common_storage.ts +++ b/src/storage/common_storage.ts @@ -29,7 +29,7 @@ import startingMechanismBlocks from '../modules/mechanism_start.json'; import startingRobotBlocks from '../modules/robot_start.json'; import {extendedPythonGenerator} from '../editor/extended_python_generator'; -import { createGeneratorContext, GeneratorContext } from '../editor/generator_context'; +import { createGeneratorContext } from '../editor/generator_context'; // Types, constants, and functions related to modules, regardless of where the modules are stored. @@ -69,17 +69,17 @@ const NUMBER_OF_PARTS = 3; export const UPLOAD_DOWNLOAD_FILE_EXTENSION = '.blocks'; export interface Storage { - async saveEntry(entryKey: string, entryValue: string): Promise; - async fetchEntry(entryKey: string, defaultValue: string): Promise; - async listModules(): Promise; - async fetchModuleContent(modulePath: string): Promise; - async createModule(moduleType: string, modulePath: string, moduleContent: string): Promise; - async saveModule(modulePath: string, moduleContent: string): Promise; - async renameModule(moduleType: string, projectName: string, oldModuleName: string, newModuleName: string): Promise; - async copyModule(moduleType: string, projectName: string, oldModuleName: string, newModuleName: string): Promise; - async deleteModule(moduleType: string, modulePath: string): Promise; - async downloadProject(projectName: string): Promise; - async uploadProject(projectName: string, blobUrl: string): Promise; + saveEntry(entryKey: string, entryValue: string): Promise; + fetchEntry(entryKey: string, defaultValue: string): Promise; + listModules(): Promise; + fetchModuleContent(modulePath: string): Promise; + createModule(moduleType: string, modulePath: string, moduleContent: string): Promise; + saveModule(modulePath: string, moduleContent: string): Promise; + renameModule(moduleType: string, projectName: string, oldModuleName: string, newModuleName: string): Promise; + copyModule(moduleType: string, projectName: string, oldModuleName: string, newModuleName: string): Promise; + deleteModule(moduleType: string, modulePath: string): Promise; + downloadProject(projectName: string): Promise; + uploadProject(projectName: string, blobUrl: string): Promise; } /** @@ -151,7 +151,7 @@ export function isValidClassName(name: string): boolean { /** * Returns the module name for the given class name. */ -export function classNameToModuleName(className: string): boolean { +export function classNameToModuleName(className: string): string { let moduleName = ''; for (let i = 0; i < className.length; i++) { const char = className.charAt(i); @@ -170,7 +170,7 @@ export function classNameToModuleName(className: string): boolean { /** * Returns the class name for the given module name. */ -export function moduleNameToClassName(moduleName: string): boolean { +export function moduleNameToClassName(moduleName: string): string { let className = ''; let nextCharUpper = true; for (let i = 0; i < moduleName.length; i++) { @@ -241,7 +241,7 @@ function startingBlocksToModuleContent( const generatorContext = createGeneratorContext(); generatorContext.setModule(module); - const pythonCode = extendedPythonGenerator.workspaceToCode( + const pythonCode = extendedPythonGenerator.mrcWorkspaceToCode( headlessBlocklyWorkspace, generatorContext); const exportedBlocks = JSON.stringify(generatorContext.getExportedBlocks()); const blocksContent = JSON.stringify( @@ -319,7 +319,7 @@ export function makeModuleContent(module: Module, pythonCode: string, exportedBl '"""\n'); } -function getParts(moduleContent: string): string { +function getParts(moduleContent: string): string[] { // The last line is """. const lastChars = '\n"""\n'; if (!moduleContent.endsWith(lastChars) || moduleContent.length <= lastChars.length) { @@ -341,7 +341,10 @@ function getParts(moduleContent: string): string { // Ignore the first (index 0) element of the split array, which is the python // code. while (split.length > 1 && parts.length < NUMBER_OF_PARTS) { - parts.push(split.pop().trim()); + const s = split.pop(); + if (s) { + parts.push(s.trim()); + } } if (parts.length < 2) { throw new Error('Unable to parse the module content.'); @@ -391,7 +394,7 @@ export function extractExportedBlocks(moduleName: string, moduleContent: string) /** * Extract the moduleType from the given module content. */ -export function extractModuleType(moduleName: string, moduleContent: string): Block[] { +export function extractModuleType(moduleContent: string): string { const parts = getParts(moduleContent); let moduleType = parts[PARTS_INDEX_MODULE_TYPE]; if (moduleType.startsWith(MARKER_MODULE_TYPE)) { @@ -404,7 +407,7 @@ export function extractModuleType(moduleName: string, moduleContent: string): Bl * Produce the blob for downloading a project. */ export async function produceDownloadProjectBlob( - projectName: string, moduleContents: {[key: string]: string}): string { + projectName: string, moduleContents: {[key: string]: string}): Promise { const zip = new JSZip(); for (let moduleName in moduleContents) { const moduleContent = moduleContents[moduleName]; @@ -457,7 +460,7 @@ export function makeUploadProjectName( let name = preferredName; // No suffix. let suffix = 0; while (true) { - var nameClash = false; + let nameClash = false; for (const existingProjectName of existingProjectNames) { if (name == existingProjectName) { nameClash = true; @@ -477,7 +480,7 @@ export function makeUploadProjectName( */ export async function processUploadedBlob( projectName: string, blobUrl: string) - : [{[key: string]: string}, {[key: string]: string}] { + : Promise<[{[key: string]: string}, {[key: string]: string}]> { const prefix = 'data:application/octet-stream;base64,'; if (!blobUrl.startsWith(prefix)) { throw new Error('blobUrl does not have the expected prefix.'); @@ -485,9 +488,10 @@ export async function processUploadedBlob( const data = blobUrl.substring(prefix.length); const zip = await JSZip.loadAsync(data, {base64: true}); - const promises = {}; + const promises: {[key: string]: Promise} = {}; zip.forEach((moduleName, zipEntry) => { promises[moduleName] = zipEntry.async('text'); + console.log("HeyLiz - promises[moduleName] is ..."); console.log(promises[moduleName]); }); // Wait for all promises to resolve. @@ -551,7 +555,7 @@ export function _processUploadedModule( const generatorContext = createGeneratorContext(); generatorContext.setModule(module); - const pythonCode = extendedPythonGenerator.workspaceToCode( + const pythonCode = extendedPythonGenerator.mrcWorkspaceToCode( headlessBlocklyWorkspace, generatorContext); const exportedBlocks = JSON.stringify(generatorContext.getExportedBlocks()); const moduleContent = makeModuleContent( diff --git a/src/toolbox/items.ts b/src/toolbox/items.ts index 3fefc2eb..47c3a4d8 100644 --- a/src/toolbox/items.ts +++ b/src/toolbox/items.ts @@ -31,6 +31,15 @@ export class Sep extends Item { } } +export class Label extends Item { + text: string; + + constructor(text: string) { + super('label'); + this.text = text; + } +} + export class Block extends Item { /** The block type. */ type: string; @@ -53,7 +62,7 @@ export class Block extends Item { } } -export type ContentsType = Sep | Block | Category; +export type ContentsType = Sep | Label | Block | Category; export class Category extends Item { /** The category name. */ diff --git a/src/toolbox/mechanism_class_methods.ts b/src/toolbox/mechanism_class_methods.ts index 46146fbf..f556da93 100644 --- a/src/toolbox/mechanism_class_methods.ts +++ b/src/toolbox/mechanism_class_methods.ts @@ -19,7 +19,7 @@ * @author lizlooney@google.com (Liz Looney) */ -import * as toolboxItems from '../toolbox/items'; +import * as toolboxItems from './items'; /** diff --git a/src/toolbox/methods_category.ts b/src/toolbox/methods_category.ts index 4dec7fb3..d41ced35 100644 --- a/src/toolbox/methods_category.ts +++ b/src/toolbox/methods_category.ts @@ -21,11 +21,13 @@ import * as Blockly from 'blockly/core'; +import * as toolboxItems from './items'; import * as commonStorage from '../storage/common_storage'; import { MRC_CATEGORY_STYLE_METHODS } from '../themes/styles' import { mechanism_class_blocks } from './mechanism_class_methods'; import { opmode_class_blocks } from './opmode_class_methods'; import { robot_class_blocks } from './robot_class_methods'; +import { ClassMethodDefBlock } from '../blocks/mrc_class_method_def' const CUSTOM_CATEGORY_METHODS = 'METHODS'; @@ -48,11 +50,8 @@ export class MethodsCategory { this.currentModule = currentModule; } - public methodsFlyout(workspace: Blockly.WorkspaceSvg): ToolboxInfo { - const toolboxInfo = { - contents: [ - ], - } + public methodsFlyout(workspace: Blockly.WorkspaceSvg) { + const contents: toolboxItems.ContentsType[] = []; // Add blocks for defining any methods that can be defined in the current // module. For example, if the current module is an OpMode, add blocks to @@ -61,7 +60,8 @@ export class MethodsCategory { // Collect the method names for mrc_class_method_def blocks that are // already in the blockly workspace. const methodNamesAlreadyUsed: string[] = []; - workspace.getBlocksByType('mrc_class_method_def', false).forEach((classMethodDefBlock) => { + workspace.getBlocksByType('mrc_class_method_def', false).forEach((block) => { + const classMethodDefBlock = block as ClassMethodDefBlock; if (!classMethodDefBlock.mrcCanChangeSignature) { methodNamesAlreadyUsed.push(classMethodDefBlock.getFieldValue('NAME')); } @@ -71,22 +71,22 @@ export class MethodsCategory { // Add the methods for a Project (Robot). this.addClassBlocksForCurrentModule( 'More Robot Methods', robot_class_blocks, - methodNamesAlreadyUsed, toolboxInfo.contents); + methodNamesAlreadyUsed, contents); } else if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_MECHANISM) { // Add the methods for a Mechanism. this.addClassBlocksForCurrentModule( 'More Mechanism Methods', mechanism_class_blocks, - methodNamesAlreadyUsed, toolboxInfo.contents); + methodNamesAlreadyUsed, contents); } else if (this.currentModule.moduleType == commonStorage.MODULE_TYPE_OPMODE) { // Add the methods for an OpMode. this.addClassBlocksForCurrentModule( 'More OpMode Methods', opmode_class_blocks, - methodNamesAlreadyUsed, toolboxInfo.contents); + methodNamesAlreadyUsed, contents); } } // Add a block that lets the user define a new method. - toolboxInfo.contents.push( + contents.push( { kind: 'label', text: 'Custom Methods', @@ -108,52 +108,61 @@ export class MethodsCategory { // For each mrc_class_method_def block in the blockly workspace, check if it // can be called from within the class, and if so, add a // mrc_call_python_function block. - workspace.getBlocksByType('mrc_class_method_def', false).forEach((classMethodDefBlock) => { + workspace.getBlocksByType('mrc_class_method_def', false).forEach((block) => { + const classMethodDefBlock = block as ClassMethodDefBlock; if (classMethodDefBlock.mrcCanBeCalledWithinClass) { - const callPythonFunctionBlock = { + const callPythonFunctionBlock: toolboxItems.Block = { kind: 'block', type: 'mrc_call_python_function', - importModule: '', extraState: { functionKind: 'instance_within', returnType: classMethodDefBlock.mrcReturnType, args: [], + importModule: '', }, fields: { FUNC: classMethodDefBlock.getFieldValue('NAME'), }, }; classMethodDefBlock.mrcParameters.forEach((param) => { - callPythonFunctionBlock.extraState.args.push( - { - name: param.name, - type: param.type, - }); + if (callPythonFunctionBlock.extraState) { + callPythonFunctionBlock.extraState.args.push( + { + name: param.name, + type: param.type ?? '', + }); + } }); - toolboxInfo.contents.push(callPythonFunctionBlock); + contents.push(callPythonFunctionBlock); } }); + const toolboxInfo = { + contents: contents, + }; + return toolboxInfo; } private addClassBlocksForCurrentModule( label: string, class_blocks: toolboxItems.Block[], - methodNamesAlreadyUsed: string[], contents: []) { + methodNamesAlreadyUsed: string[], contents: toolboxItems.ContentsType[]) { let labelAdded = false; class_blocks.forEach((blockInfo) => { - const methodName = blockInfo.fields['NAME']; - if (!methodNamesAlreadyUsed.includes(methodName)) { - if (!labelAdded) { - contents.push( - { - kind: 'label', - text: label, - }, - ); - labelAdded = true; + if (blockInfo.fields) { + const methodName = blockInfo.fields['NAME']; + if (!methodNamesAlreadyUsed.includes(methodName)) { + if (!labelAdded) { + contents.push( + { + kind: 'label', + text: label, + }, + ); + labelAdded = true; + } + contents.push(blockInfo); } - contents.push(blockInfo); } }); } diff --git a/src/toolbox/opmode_class_methods.ts b/src/toolbox/opmode_class_methods.ts index 195b7d21..068578f5 100644 --- a/src/toolbox/opmode_class_methods.ts +++ b/src/toolbox/opmode_class_methods.ts @@ -19,7 +19,7 @@ * @author lizlooney@google.com (Liz Looney) */ -import * as toolboxItems from '../toolbox/items'; +import * as toolboxItems from './items'; /** diff --git a/src/toolbox/robot_class_methods.ts b/src/toolbox/robot_class_methods.ts index 9b080fb1..d2b0d413 100644 --- a/src/toolbox/robot_class_methods.ts +++ b/src/toolbox/robot_class_methods.ts @@ -19,7 +19,7 @@ * @author lizlooney@google.com (Liz Looney) */ -import * as toolboxItems from '../toolbox/items'; +import * as toolboxItems from './items'; /** diff --git a/src/toolbox/settings.tsx b/src/toolbox/settings.tsx index 4c4c6b15..676a4d6e 100644 --- a/src/toolbox/settings.tsx +++ b/src/toolbox/settings.tsx @@ -3,7 +3,6 @@ import { Button, Flex, Modal, Splitter, Tree, Typography } from 'antd'; import type { TreeDataNode, TreeProps } from 'antd'; import * as toolboxItems from './items'; -import * as toolbox from './toolbox'; import * as generatedToolbox from './generated/toolbox'; @@ -177,7 +176,7 @@ const ToolboxSettingsModal: React.FC = ({ isOpen, sho } }; - const handleModuleTreeSelected: TreeProps['onSelect'] = (a: React.Key[], e) => { + const handleModuleTreeSelected: TreeProps['onSelect'] = (a: React.Key[]) => { if (a.length === 1) { setModuleTreeSelectedKey(a[0]); } diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index 30281c88..9f261611 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -20,7 +20,7 @@ */ import * as generatedToolbox from './generated/toolbox'; -import * as toolboxItems from '../toolbox/items'; +import * as toolboxItems from './items'; import {category as logicCategory} from './logic_category'; import {category as loopCategory} from './loop_category'; import {category as mathCategory} from './math_category'; diff --git a/src/toolbox/toolbox_tests.ts b/src/toolbox/toolbox_tests.ts index 8ff9ca0c..5aa00f0d 100644 --- a/src/toolbox/toolbox_tests.ts +++ b/src/toolbox/toolbox_tests.ts @@ -21,7 +21,7 @@ import * as Blockly from 'blockly/core'; import { pythonGenerator } from 'blockly/python'; -import * as toolboxItems from '../toolbox/items'; +import * as toolboxItems from './items'; // Tests