diff --git a/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js b/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js index 95aaf1c294a..5b20a1d70ae 100644 --- a/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js +++ b/packages/react-native-codegen/src/generators/components/GeneratePropsJavaDelegate.js @@ -11,6 +11,7 @@ 'use strict'; import type { + CommandTypeShape, ComponentShape, PropTypeShape, SchemaType, @@ -26,10 +27,20 @@ package com.facebook.react.viewmanagers; ::_IMPORTS_:: public class ::_CLASSNAME_:: { + ::_METHODS_:: +} +`; + +const propSetterTemplate = ` public void setProperty(::_INTERFACE_CLASSNAME_:: viewManager, T view, String propName, Object value) { ::_PROP_CASES_:: } -} +`; + +const commandsTemplate = ` + public void receiveCommand(::_INTERFACE_CLASSNAME_:: viewManager, T view, String commandName, ReadableArray args) { + ::_COMMAND_CASES_:: + } `; function getJavaValueForProp( @@ -105,6 +116,52 @@ function generatePropCasesString( }`; } +function getCommandArgJavaType(param) { + switch (param.typeAnnotation.type) { + case 'BooleanTypeAnnotation': + return 'getBoolean'; + case 'Int32TypeAnnotation': + return 'getInt'; + default: + (param.typeAnnotation.type: empty); + throw new Error('Receieved invalid typeAnnotation'); + } +} + +function getCommandArguments(command: CommandTypeShape): string { + const commandArgs = command.typeAnnotation.params + .map((param, index) => { + const commandArgJavaType = getCommandArgJavaType(param); + + return `args.${commandArgJavaType}(${index})`; + }) + .join(', '); + + return `view, ${commandArgs}`; +} + +function generateCommandCasesString( + component: ComponentShape, + componentName: string, +) { + if (component.commands.length === 0) { + return null; + } + + const commandMethods = component.commands + .map(command => { + return `case "${command.name}": + viewManager.${toSafeJavaString( + command.name, + false, + )}(${getCommandArguments(command)}); + break;`; + }) + .join('\n' + ' '); + + return commandMethods; +} + function getClassExtendString(component): string { const extendString = component.extendsProps .map(extendProps => { @@ -127,6 +184,28 @@ function getClassExtendString(component): string { return extendString; } +function getDelegateImports(component) { + const imports = getImports(component); + // The delegate needs ReadableArray for commands always. + // The interface doesn't always need it + if (component.commands.length > 0) { + imports.add('import com.facebook.react.bridge.ReadableArray;'); + } + + return imports; +} + +function generateMethods(propsString, commandsString): string { + return [ + propSetterTemplate.trim().replace('::_PROP_CASES_::', propsString), + commandsString != null + ? commandsTemplate.trim().replace('::_COMMAND_CASES_::', commandsString) + : '', + ] + .join('\n\n ') + .trimRight(); +} + module.exports = { generate(libraryName: string, schema: SchemaType): FilesOutput { const files = new Map(); @@ -143,8 +222,12 @@ module.exports = { const interfaceClassName = `${componentName}ViewManagerInterface`; const fileName = `${className}.java`; - const imports = getImports(component); + const imports = getDelegateImports(component); const propsString = generatePropCasesString(component, componentName); + const commandsString = generateCommandCasesString( + component, + componentName, + ); const extendString = getClassExtendString(component); const replacedTemplate = template @@ -155,9 +238,13 @@ module.exports = { .join('\n'), ) .replace(/::_CLASSNAME_::/g, className) - .replace(/::_INTERFACE_CLASSNAME_::/g, interfaceClassName) .replace('::_EXTEND_CLASSES_::', extendString) - .replace('::_PROP_CASES_::', propsString); + .replace('::_PROP_CASES_::', propsString) + .replace( + '::_METHODS_::', + generateMethods(propsString, commandsString), + ) + .replace(/::_INTERFACE_CLASSNAME_::/g, interfaceClassName); files.set(fileName, replacedTemplate); }); diff --git a/packages/react-native-codegen/src/generators/components/GeneratePropsJavaInterface.js b/packages/react-native-codegen/src/generators/components/GeneratePropsJavaInterface.js index a13a091b3d6..ce9aa7306be 100644 --- a/packages/react-native-codegen/src/generators/components/GeneratePropsJavaInterface.js +++ b/packages/react-native-codegen/src/generators/components/GeneratePropsJavaInterface.js @@ -124,11 +124,9 @@ function generateCommandsString( ) { return component.commands .map(command => { - const safeJavaName = toSafeJavaString(command.name); - const lowerJavaName = - safeJavaName[0].toLowerCase() + safeJavaName.slice(1); + const safeJavaName = toSafeJavaString(command.name, false); - return `void ${lowerJavaName}(${getCommandArguments( + return `void ${safeJavaName}(${getCommandArguments( command, componentName, )});`; diff --git a/packages/react-native-codegen/src/generators/components/JavaHelpers.js b/packages/react-native-codegen/src/generators/components/JavaHelpers.js index 7413fe4de15..61bcb365f86 100644 --- a/packages/react-native-codegen/src/generators/components/JavaHelpers.js +++ b/packages/react-native-codegen/src/generators/components/JavaHelpers.js @@ -16,11 +16,17 @@ function upperCaseFirst(inString: string): string { return inString[0].toUpperCase() + inString.slice(1); } -function toSafeJavaString(input: string): string { - return input - .split('-') - .map(upperCaseFirst) - .join(''); +function toSafeJavaString( + input: string, + shouldUpperCaseFirst?: boolean, +): string { + const parts = input.split('-'); + + if (shouldUpperCaseFirst === false) { + return parts.join(''); + } + + return parts.map(upperCaseFirst).join(''); } function getImports(component: ComponentShape): Set { diff --git a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap index f04f2cf7844..b9003c2f4ec 100644 --- a/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap +++ b/packages/react-native-codegen/src/generators/components/__tests__/__snapshots__/GeneratePropsJavaDelegate-test.js.snap @@ -88,11 +88,21 @@ Map { package com.facebook.react.viewmanagers; import android.view.View; +import com.facebook.react.bridge.ReadableArray; public class CommandNativeComponentViewManagerDelegate { public void setProperty(CommandNativeComponentViewManagerInterface viewManager, T view, String propName, Object value) { // No props } + + public void receiveCommand(CommandNativeComponentInterface viewManager, T view, String commandName, ReadableArray args) { + case \\"hotspotUpdate\\": + viewManager.hotspotUpdate(view, args.getInt(0), args.getInt(1)); + break; + case \\"scrollTo\\": + viewManager.scrollTo(view, args.getInt(0), args.getBoolean(1)); + break; + } } ", } @@ -104,6 +114,7 @@ Map { package com.facebook.react.viewmanagers; import android.view.View; +import com.facebook.react.bridge.ReadableArray; public class CommandNativeComponentViewManagerDelegate { public void setProperty(CommandNativeComponentViewManagerInterface viewManager, T view, String propName, Object value) { @@ -113,6 +124,12 @@ public class CommandNativeComponentViewManagerDelegate { break; } } + + public void receiveCommand(CommandNativeComponentInterface viewManager, T view, String commandName, ReadableArray args) { + case \\"hotspotUpdate\\": + viewManager.hotspotUpdate(view, args.getInt(0), args.getInt(1)); + break; + } } ", }