From 7069538056b9a609a0a94d2dc2ca1d56f80a6344 Mon Sep 17 00:00:00 2001 From: palumbon Date: Mon, 18 Apr 2022 18:20:53 +0200 Subject: [PATCH 1/3] Fix navigation: - Bye inner navigator - Fix problems navigation - Renames --- src/__tests__/Problems-test.tsx | 2 +- src/__tests__/TestItem-test.tsx | 3 +- src/__tests__/mocks/ProjectProviderMock.tsx | 4 +- .../entity-detail/ModuleDetails.tsx} | 49 +++++---- .../new-attribute-modal/NewAttributeModal.tsx | 43 ++++---- .../new-method-modal/NewMethodModal.tsx | 28 +++-- .../LiteralModal/LiteralInputModals.tsx | 2 +- .../expression-lists/messages-list.tsx | 4 +- src/components/problems/ProblemIcon.tsx | 2 +- .../problems/ProblemReporterButton.tsx | 2 +- src/components/problems/ProblemsModal.tsx | 2 +- src/components/projects/ProjectHeader.tsx | 37 +++---- src/components/tests/NewTestModal.tsx | 21 ++-- src/components/tests/Tests.tsx | 22 ++-- .../ui/Body/AssignmentFormModal.tsx | 2 +- src/components/ui/ExpressionInput.tsx | 6 +- src/components/ui/FormModal/FormModal.tsx | 20 ++-- src/context/EntityProvider.tsx | 58 ---------- src/context/ProjectProvider.tsx | 58 +++++++--- ...{NewMessageCall.tsx => ArgumentsMaker.tsx} | 10 +- src/pages/Editor.tsx | 56 ++++++++++ src/pages/EntityMemberDetail.tsx | 46 -------- src/pages/EntityStack.tsx | 103 ++++-------------- .../{ExpressionMaker => }/ExpressionMaker.tsx | 46 ++++---- src/pages/Home.tsx | 6 +- src/pages/ProjectNavigator.tsx | 28 ++++- src/pages/{ => tabs}/Describes.tsx | 8 +- .../Entities.tsx => tabs/Modules.tsx} | 2 +- src/utils/type-helpers.ts | 5 + src/utils/wollok-helpers.ts | 31 ++++-- 30 files changed, 334 insertions(+), 372 deletions(-) rename src/{pages/EntityDetails/EntityDetails.tsx => components/entity-detail/ModuleDetails.tsx} (60%) delete mode 100644 src/context/EntityProvider.tsx rename src/pages/{NewMessageCall.tsx => ArgumentsMaker.tsx} (86%) create mode 100644 src/pages/Editor.tsx delete mode 100644 src/pages/EntityMemberDetail.tsx rename src/pages/{ExpressionMaker => }/ExpressionMaker.tsx (72%) rename src/pages/{ => tabs}/Describes.tsx (73%) rename src/pages/{Entities/Entities.tsx => tabs/Modules.tsx} (97%) diff --git a/src/__tests__/Problems-test.tsx b/src/__tests__/Problems-test.tsx index 34ca60e..5e77c4f 100644 --- a/src/__tests__/Problems-test.tsx +++ b/src/__tests__/Problems-test.tsx @@ -1,6 +1,6 @@ import { render, RenderAPI } from '@testing-library/react-native' import React from 'react' -import { Node, Problem } from 'wollok-ts' +import { Node, Problem } from 'wollok-ts/dist/model' import { ProblemIcon } from '../components/problems/ProblemIcon' import { ProblemReporterButton } from '../components/problems/ProblemReporterButton' import { ProblemModal } from '../components/problems/ProblemsModal' diff --git a/src/__tests__/TestItem-test.tsx b/src/__tests__/TestItem-test.tsx index 30aea75..99a4453 100644 --- a/src/__tests__/TestItem-test.tsx +++ b/src/__tests__/TestItem-test.tsx @@ -2,8 +2,7 @@ import { fireEvent, render } from '@testing-library/react-native' import React from 'react' import { ActivityIndicator } from 'react-native-paper' import { act, ReactTestInstance } from 'react-test-renderer' -import { WollokException } from 'wollok-ts/dist/interpreter/runtimeModel' -import { Body, Test } from 'wollok-ts/dist/model' +import { Body, Test, WollokException } from 'wollok-ts' import TestItem from '../components/tests/TestItem' import { theme } from '../theme' import { TestRun } from '../utils/wollok-helpers' diff --git a/src/__tests__/mocks/ProjectProviderMock.tsx b/src/__tests__/mocks/ProjectProviderMock.tsx index 9a340c4..e2fd7e4 100644 --- a/src/__tests__/mocks/ProjectProviderMock.tsx +++ b/src/__tests__/mocks/ProjectProviderMock.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Environment, Problem } from 'wollok-ts' +import { Environment, Problem } from 'wollok-ts/dist/model' import { ProjectContext } from '../../context/ProjectProvider' import { ParentComponentProp } from '../../utils/type-helpers' @@ -11,6 +11,8 @@ export const initialContext = { actions: { addEntity: jest.fn(), addDescribe: jest.fn(), + addMember: jest.fn(), + changeMember: jest.fn(), rebuildEnvironment: jest.fn(), runTest: jest.fn(), execution: jest.fn(), diff --git a/src/pages/EntityDetails/EntityDetails.tsx b/src/components/entity-detail/ModuleDetails.tsx similarity index 60% rename from src/pages/EntityDetails/EntityDetails.tsx rename to src/components/entity-detail/ModuleDetails.tsx index 27c6d85..8ff4795 100644 --- a/src/pages/EntityDetails/EntityDetails.tsx +++ b/src/components/entity-detail/ModuleDetails.tsx @@ -3,22 +3,28 @@ import React, { useState } from 'react' import { ScrollView } from 'react-native-gesture-handler' import { List } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' -import { Field, is, Method } from 'wollok-ts/dist/model' -import { AccordionList } from '../../components/entity-detail/AccordionList' -import AttributeItemComponent from '../../components/entity-detail/AttributeItem/AttributeItem' -import NewAttributeModal from '../../components/entity-detail/new-attribute-modal/NewAttributeModal' -import NewMethodModal from '../../components/entity-detail/new-method-modal/NewMethodModal' -import MultiFabScreen from '../../components/FabScreens/MultiFabScreen' -import { ProblemReporterButton } from '../../components/problems/ProblemReporterButton' -import { useEntity } from '../../context/EntityProvider' +import { Describe, Field, is, Method, Module } from 'wollok-ts/dist/model' +import { AccordionList } from './AccordionList' +import AttributeItemComponent from './AttributeItem/AttributeItem' +import NewAttributeModal from './new-attribute-modal/NewAttributeModal' +import NewMethodModal from './new-method-modal/NewMethodModal' +import MultiFabScreen from '../FabScreens/MultiFabScreen' +import { ProblemReporterButton } from '../problems/ProblemReporterButton' +import { useProject } from '../../context/ProjectProvider' import { wTranslate } from '../../utils/translation-helpers' import { methodFQN, methodLabel } from '../../utils/wollok-helpers' -import { EntityMemberScreenNavigationProp } from '../EntityMemberDetail' +import { EditorScreenNavigationProp } from '../../pages/Editor' -export const EntityDetails = function () { +export type ModuleDetailsProps = { + module: Exclude +} + +export const ModuleDetails = function ({ module }: ModuleDetailsProps) { const [methodModalVisible, setMethodModalVisible] = useState(false) const [attributeModalVisible, setAttributeModalVisible] = useState(false) - const { entity } = useEntity() + const { + actions: { addMember }, + } = useProject() return ( title={wTranslate('entityDetails.attributes').toUpperCase()} - items={entity.members.filter(is('Field')) as Field[]} + items={module.members.filter(is('Field')) as Field[]} VisualItem={AttributeItem} initialExpanded={true} /> title={wTranslate('entityDetails.methods').toUpperCase()} - items={entity.members.filter(is('Method')) as Method[]} + items={module.members.filter(is('Method')) as Method[]} VisualItem={MethodItem} initialExpanded={true} /> @@ -53,10 +59,13 @@ export const EntityDetails = function () { ) @@ -67,18 +76,18 @@ function AttributeItem({ item: attribute }: { item: Field }) { } function MethodItem({ item: method }: { item: Method }) { - const navigator = useNavigation() + const navigator = useNavigation() + function gotoMethod() { + navigator.navigate('Editor', { + fqn: methodFQN(method), + }) + } return ( } - onPress={() => - navigator.navigate('EntityMemberDetails', { - entityMember: method, - fqn: methodFQN(method), - }) - } + onPress={gotoMethod} /> ) } diff --git a/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx b/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx index 416c883..698fe28 100644 --- a/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx +++ b/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx @@ -1,34 +1,30 @@ import React, { useState } from 'react' import { StyleSheet, View } from 'react-native' -import { Text, TextInput, withTheme } from 'react-native-paper' +import { Text, TextInput } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' import { Expression, Field } from 'wollok-ts/dist/model' -import { useEntity } from '../../../context/EntityProvider' -import { Theme } from '../../../theme' import { wTranslate } from '../../../utils/translation-helpers' +import { Visible } from '../../../utils/type-helpers' import CheckIcon from '../../ui/CheckIcon' import ExpressionInput from '../../ui/ExpressionInput' import FormModal from '../../ui/FormModal/FormModal' import { ATTRIBUTE_ICONS } from '../attribute-icons' -type Props = { - visible: boolean - setVisible: (visible: boolean) => void - theme: Theme +type AttributeFormModalProps = Visible & { + addNewField: (f: Field) => void + contextFQN: string } -const AttributeFormModal = (props: Props) => { - const { - actions: { addMember }, - entity, - } = useEntity() +const AttributeFormModal = ({ + visible, + setVisible, + addNewField, + contextFQN, +}: AttributeFormModalProps) => { const [name, setName] = useState('') const [isConstant, setConstant] = useState(false) const [isProperty, setProperty] = useState(false) const [initialValue, setInitialValue] = useState() - const { visible, setVisible } = props - - const styles = getStyles(props.theme) const checkboxes = [ { @@ -68,7 +64,7 @@ const AttributeFormModal = (props: Props) => { { } function newAttribute() { - addMember(new Field({ name, isConstant, isProperty, value: initialValue })) + addNewField( + new Field({ name, isConstant, isProperty, value: initialValue }), + ) } } -const getStyles = (_theme: Theme) => - StyleSheet.create({ - checkbox: { flexDirection: 'row', alignItems: 'center', marginVertical: 5 }, - constName: { fontSize: 16 }, - }) +const styles = StyleSheet.create({ + checkbox: { flexDirection: 'row', alignItems: 'center', marginVertical: 5 }, + constName: { fontSize: 16 }, +}) -export default withTheme(AttributeFormModal) +export default AttributeFormModal diff --git a/src/components/entity-detail/new-method-modal/NewMethodModal.tsx b/src/components/entity-detail/new-method-modal/NewMethodModal.tsx index febce88..b97cd7e 100644 --- a/src/components/entity-detail/new-method-modal/NewMethodModal.tsx +++ b/src/components/entity-detail/new-method-modal/NewMethodModal.tsx @@ -1,22 +1,20 @@ import React, { useState } from 'react' import { StyleSheet } from 'react-native' -import { Text, TextInput, withTheme } from 'react-native-paper' +import { Text, TextInput } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' import { Body, Method, Parameter } from 'wollok-ts/dist/model' -import { useEntity } from '../../../context/EntityProvider' -import { Theme } from '../../../theme' import { wTranslate } from '../../../utils/translation-helpers' +import { Visible } from '../../../utils/type-helpers' import FormModal from '../../ui/FormModal/FormModal' import ParameterInput from './ParameterInput' -const NewMethodModal = (props: { - visible: boolean - setVisible: (value: boolean) => void - theme: Theme -}) => { - const { - actions: { addMember }, - } = useEntity() +type NewMethodModalProps = Visible & { addNewMethod: (m: Method) => void } + +const NewMethodModal = ({ + visible, + setVisible, + addNewMethod, +}: NewMethodModalProps) => { const [name, setName] = useState('') const [parameters, setParameters] = useState([]) const [nextParameter, setNextParameter] = useState('') @@ -26,8 +24,8 @@ const NewMethodModal = (props: { title={wTranslate('entityDetails.methodModal.newMethod')} resetForm={reset} onSubmit={newMethod} - setVisible={props.setVisible} - visible={props.visible}> + setVisible={setVisible} + visible={visible}> () + const navigation = useNavigation() + const goToEntityDetails = (entity: Entity) => { navigation.navigate('EntityStack', { entityFQN: entity.fullyQualifiedName(), }) - - setShowProblems(false) } - const goToMethod = (method: Method) => { - navigation.navigate('EntityMemberDetails', { - entityMember: method, - fqn: methodFQN(method), + const goToEditor = (entityMember: EntityMemberWithBody) => { + navigation.navigate('Editor', { + fqn: entityMemberFQN(entityMember), }) - - setShowProblems(false) } - const goto = (n: Node): void => + const goto = (n: Node): void => { n.match({ - Method: goToMethod, + Method: goToEditor, + Test: goToEditor, Singleton: goToEntityDetails, + Describe: goToEntityDetails, Field: f => goToEntityDetails(f.parent), - Assignment: a => goto(a.parent), Body: b => goto(b.parent), + Sentence: e => goto(e.parent), Expression: e => goto(e.parent), - Test: t => goto(t.parent), - Describe: t => goToEntityDetails(t), }) + setShowProblems(false) + } + return ( void -}) { - const { - actions: { addMember }, - } = useEntity() +type NewTestModalProps = Pick & { + addNewTest: (t: Test) => void +} + +function NewTestModal({ visible, setVisible, addNewTest }: NewTestModalProps) { const [name, setName] = useState(initialName) return ( + setVisible={setVisible} + visible={visible}> () + const navigator = useNavigation() function navigateTo(test: Test) { - navigator.navigate('EntityMemberDetails', { - entityMember: test, + navigator.navigate('Editor', { fqn: test.fullyQualifiedName(), }) } @@ -63,6 +64,7 @@ export const Tests = function () { ) diff --git a/src/components/ui/Body/AssignmentFormModal.tsx b/src/components/ui/Body/AssignmentFormModal.tsx index cf1f4df..f541cf6 100644 --- a/src/components/ui/Body/AssignmentFormModal.tsx +++ b/src/components/ui/Body/AssignmentFormModal.tsx @@ -62,7 +62,7 @@ export function AssignmentFormModal({ diff --git a/src/components/ui/ExpressionInput.tsx b/src/components/ui/ExpressionInput.tsx index b665c1e..99eba36 100644 --- a/src/components/ui/ExpressionInput.tsx +++ b/src/components/ui/ExpressionInput.tsx @@ -3,7 +3,7 @@ import React from 'react' import { StyleSheet, View } from 'react-native' import { IconButton, Text, withTheme } from 'react-native-paper' import { Expression, Name } from 'wollok-ts/dist/model' -import { ExpressionMakerScreenProp } from '../../pages/ExpressionMaker/ExpressionMaker' +import { ExpressionMakerScreenProp } from '../../pages/ExpressionMaker' import { Theme } from '../../theme' import { ExpressionDisplay } from '../expressions/ExpressionDisplay' @@ -11,7 +11,7 @@ type Props = { value?: Expression setValue: (expression?: Expression) => void theme: Theme - fqn: Name + contextFQN: Name inputPlaceholder: string } @@ -21,7 +21,7 @@ const ExpressionInput = (props: Props) => { const goToExpressionMaker = () => { navigation.push('ExpressionMaker', { onSubmit: setValue, - contextFQN: props.fqn, + contextFQN: props.contextFQN, initialExpression: value, }) } diff --git a/src/components/ui/FormModal/FormModal.tsx b/src/components/ui/FormModal/FormModal.tsx index 52e24c0..e926404 100644 --- a/src/components/ui/FormModal/FormModal.tsx +++ b/src/components/ui/FormModal/FormModal.tsx @@ -9,18 +9,18 @@ import { } from 'react-native-paper' import { Theme } from '../../../theme' import { wTranslate } from '../../../utils/translation-helpers' -import { ParentComponentProp } from '../../../utils/type-helpers' +import { ParentComponentProp, Visible } from '../../../utils/type-helpers' import { stylesheet } from './styles' -export type FormModalProps = ParentComponentProp<{ - visible: boolean - setVisible: (value: boolean) => void - onSubmit: () => void - resetForm?: () => void - title?: string - valid?: boolean - theme: Theme -}> +export type FormModalProps = ParentComponentProp< + Visible & { + onSubmit: () => void + resetForm?: () => void + title?: string + valid?: boolean + theme: Theme + } +> function FormModal(props: FormModalProps) { const styles = stylesheet(props.theme) diff --git a/src/context/EntityProvider.tsx b/src/context/EntityProvider.tsx deleted file mode 100644 index 0596244..0000000 --- a/src/context/EntityProvider.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { createContext } from 'react' -import { Module } from 'wollok-ts/dist/model' -import { ParentComponentProp } from '../utils/type-helpers' -import { EntityMember } from '../utils/wollok-helpers' -import { createContextHook } from './create-context-hook' -import { useProject } from './ProjectProvider' - -export const EntityContext = createContext<{ - entity: Module - actions: Actions -} | null>(null) - -type Actions = { - addMember: (newMember: EntityMember) => void - changeMember: (oldMember: EntityMember, newMember: EntityMember) => void -} - -export function EntityProvider( - props: ParentComponentProp<{ - entity: Module - }>, -) { - const { children, entity } = props - const { - actions: { rebuildEnvironment }, - } = useProject() - - const addMember = (newMember: EntityMember) => { - rebuildEnvironment( - entity.copy({ - members: [...entity.members, newMember], - }) as Module, - ) - } - - const changeMember = (oldMember: EntityMember, newMember: EntityMember) => { - rebuildEnvironment( - entity.copy({ - members: [...entity.members.filter(m => m !== oldMember), newMember], - }) as Module, - ) - } - - const initialContext = { - entity: entity, - actions: { addMember, changeMember }, - } - return ( - - {children} - - ) -} - -export const useEntity = createContextHook(EntityContext, { - hookName: 'useEntity', - contextName: 'EntityProvider', -}) diff --git a/src/context/ProjectProvider.tsx b/src/context/ProjectProvider.tsx index 4ca618b..164a3ba 100644 --- a/src/context/ProjectProvider.tsx +++ b/src/context/ProjectProvider.tsx @@ -17,7 +17,12 @@ import { import validate from 'wollok-ts/dist/validator' import { saveProject } from '../services/persistance.service' import { ParentComponentProp } from '../utils/type-helpers' -import { executionFor, interpretTest, TestRun } from '../utils/wollok-helpers' +import { + EntityMember, + executionFor, + interpretTest, + TestRun, +} from '../utils/wollok-helpers' import { createContextHook } from './create-context-hook' export const mainPackageName = 'main' @@ -32,9 +37,13 @@ export const ProjectContext = createContext<{ } | null>(null) type Actions = { + rebuildEnvironment: (entity: Entity) => void addEntity: (module: Module) => void addDescribe: (test: Describe) => void - rebuildEnvironment: (entity: Entity) => void + addMember: (parent: Module) => (newMember: EntityMember) => void + changeMember: ( + parent: Module, + ) => (oldMember: EntityMember, newMember: EntityMember) => void runTest: (test: Test) => TestRun execution: (test: Test) => ExecutionDirector save: () => Promise @@ -74,28 +83,20 @@ export function ProjectProvider( return link([pack], base ?? project) } - function addEntity(newEntity: Module) { - rebuildEnvironment(newEntity) - } - - function addDescribe(newDescribe: Describe) { - rebuildEnvironment(newDescribe) - } - function rebuildEnvironment(entity: Entity) { const packageName = entity.is('Describe') ? testsPackageName : mainPackageName const newProject = buildEnvironment(packageName, [entity]) setProject(newProject) - setProblems(validateProject(newProject) as Problem[]) setChanged(true) + setProblems(validateProject(newProject) as Problem[]) } function validateProject(_project: Environment) { const targetPackages = [ - project.getNodeByFQN(mainPackageName), - project.getNodeByFQN(testsPackageName), + _project.getNodeByFQN(mainPackageName), + _project.getNodeByFQN(testsPackageName), ] const belongsToTargetProject = (p: Problem) => targetPackages.some(target => p.node.ancestors().includes(target)) @@ -105,6 +106,35 @@ export function ProjectProvider( /////////////////////////////////// BUILD ////////////////////////////////// + /////////////////////////////////// ENTITIES ////////////////////////////////// + + function addEntity(newEntity: Module) { + rebuildEnvironment(newEntity) + } + + function addDescribe(newDescribe: Describe) { + rebuildEnvironment(newDescribe) + } + + const addMember = (entity: Module) => (newMember: EntityMember) => { + rebuildEnvironment( + entity.copy({ + members: [...entity.members, newMember], + }) as Module, + ) + } + + const changeMember = + (entity: Module) => (oldMember: EntityMember, newMember: EntityMember) => { + rebuildEnvironment( + entity.copy({ + members: [...entity.members.filter(m => m !== oldMember), newMember], + }) as Module, + ) + } + + /////////////////////////////////// ENTITIES ////////////////////////////////// + /////////////////////////////////// EXECUTION ////////////////////////////////// function runTest(test: Test) { @@ -130,6 +160,8 @@ export function ProjectProvider( actions: { addEntity, addDescribe, + addMember, + changeMember, rebuildEnvironment, runTest, execution, diff --git a/src/pages/NewMessageCall.tsx b/src/pages/ArgumentsMaker.tsx similarity index 86% rename from src/pages/NewMessageCall.tsx rename to src/pages/ArgumentsMaker.tsx index dd7b9f2..93137fd 100644 --- a/src/pages/NewMessageCall.tsx +++ b/src/pages/ArgumentsMaker.tsx @@ -7,14 +7,15 @@ import { Expression, Send } from 'wollok-ts/dist/model' import ExpressionInput from '../components/ui/ExpressionInput' import { SubmitCheckButton } from '../components/ui/Header' import { wTranslate } from '../utils/translation-helpers' -import { EntityStackParamList } from './EntityStack' +import { methodLabel } from '../utils/wollok-helpers' +import { ProjectStackParamList } from './ProjectNavigator' -export function NewMessageCall({ +export function ArgumentsMaker({ route: { params: { method, receiver, contextFQN, onSubmit }, }, }: { - route: RouteProp + route: RouteProp }) { const [args, setArguments] = useState<(Expression | undefined)[]>( method.parameters.map(() => undefined), @@ -24,6 +25,7 @@ export function NewMessageCall({ React.useLayoutEffect(() => { navigation.setOptions({ + title: methodLabel(method), //TODO: Show receiver? headerRight: () => ( a === undefined)} @@ -53,7 +55,7 @@ export function NewMessageCall({ {_.name} setParameter(i, expression)} value={args[i]} inputPlaceholder={upperCaseFirst( diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx new file mode 100644 index 0000000..d9b67d8 --- /dev/null +++ b/src/pages/Editor.tsx @@ -0,0 +1,56 @@ +import { RouteProp, useNavigation } from '@react-navigation/native' +import { StackNavigationProp } from '@react-navigation/stack' +import React from 'react' +import { Body } from 'wollok-ts/dist/model' +import { BodyMaker } from '../components/ui/Body/BodyMaker' +import { useProject } from '../context/ProjectProvider' +import { + allScopedVariables, + entityMemberByFQN, + EntityMemberWithBody, +} from '../utils/wollok-helpers' +import { ProjectStackParamList } from './ProjectNavigator' + +export type EditorScreenNavigationProp = StackNavigationProp< + ProjectStackParamList, + 'Editor' +> + +type Route = RouteProp + +export const Editor = ({ + route: { + params: { fqn }, + }, +}: { + route: Route +}) => { + const { + project, + actions: { changeMember }, + } = useProject() + const entity = entityMemberByFQN(project, fqn) + const parent = entity.parent + + const navigation = useNavigation() + React.useLayoutEffect(() => { + navigation.setOptions({ + title: entity.name, + headerTitleAlign: 'center', + animationEnabled: false, + }) + }, [navigation, entity]) + + function setBody(body: Body) { + changeMember(parent)(entity, entity.copy({ body }) as EntityMemberWithBody) + } + + return ( + + ) +} diff --git a/src/pages/EntityMemberDetail.tsx b/src/pages/EntityMemberDetail.tsx deleted file mode 100644 index afb6a92..0000000 --- a/src/pages/EntityMemberDetail.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { RouteProp } from '@react-navigation/native' -import { StackNavigationProp } from '@react-navigation/stack' -import React from 'react' -import { Body } from 'wollok-ts/dist/model' -import { BodyMaker } from '../components/ui/Body/BodyMaker' -import { useEntity } from '../context/EntityProvider' -import { - allScopedVariables, - EntityMemberWithBody, -} from '../utils/wollok-helpers' -import { EntityStackParamList } from './EntityStack' - -export type EntityMemberScreenNavigationProp = StackNavigationProp< - EntityStackParamList, - 'EntityMemberDetails' -> - -type Route = RouteProp - -export const EntityMemberDetail = ({ - route: { - params: { entityMember, fqn }, - }, -}: { - route: Route -}) => { - const { - actions: { changeMember }, - } = useEntity() - - function setBody(body: Body) { - changeMember( - entityMember, - entityMember.copy({ body }) as EntityMemberWithBody, - ) - } - - return ( - - ) -} diff --git a/src/pages/EntityStack.tsx b/src/pages/EntityStack.tsx index 5799353..8f0cdb5 100644 --- a/src/pages/EntityStack.tsx +++ b/src/pages/EntityStack.tsx @@ -1,95 +1,30 @@ -import { RouteProp } from '@react-navigation/native' -import { createStackNavigator } from '@react-navigation/stack' +import { RouteProp, useNavigation } from '@react-navigation/native' import React from 'react' -import { Expression, Method, Module, Name, Send } from 'wollok-ts/dist/model' +import { Module } from 'wollok-ts/dist/model' +import { ModuleDetails } from '../components/entity-detail/ModuleDetails' import { Tests } from '../components/tests/Tests' -import { EntityProvider } from '../context/EntityProvider' import { useProject } from '../context/ProjectProvider' -import { wTranslate } from '../utils/translation-helpers' -import { - entityMemberLabel, - EntityMemberWithBody, -} from '../utils/wollok-helpers' -import { EntityDetails } from './EntityDetails/EntityDetails' -import { EntityMemberDetail } from './EntityMemberDetail' -import ExpressionMaker, { - ExpressionOnSubmit, -} from './ExpressionMaker/ExpressionMaker' -import { NewMessageCall } from './NewMessageCall' import { ProjectStackParamList } from './ProjectNavigator' export type EntityStackRoute = RouteProp -export type EntityStackParamList = { - EntityDetails: undefined - Tests: undefined - EntityMemberDetails: { - entityMember: EntityMemberWithBody - fqn: Name - } - ExpressionMaker: { - onSubmit: ExpressionOnSubmit - contextFQN: Name - initialExpression?: Expression - } - NewMessageSend: { - receiver: Expression - method: Method - contextFQN: Name - onSubmit: (s: Send) => void - } -} - -export default function (props: { route: EntityStackRoute }) { +function EntityStack(props: { route: EntityStackRoute }) { const { project } = useProject() - const Stack = createStackNavigator() const entity = project.getNodeByFQN(props.route.params.entityFQN) - return ( - - - {entity.is('Describe') ? ( - - ) : ( - - )} - ({ - title: entityMemberLabel(methodRoute.params.entityMember), - })} - /> - - ({ title: route.route.params.method.name })} - /> - - + + const navigation = useNavigation() + React.useLayoutEffect(() => { + navigation.setOptions({ + title: entity.name, + headerTitleAlign: 'center', + }) + }, [navigation, entity]) + + return entity.is('Describe') ? ( + + ) : ( + ) } + +export default EntityStack diff --git a/src/pages/ExpressionMaker/ExpressionMaker.tsx b/src/pages/ExpressionMaker.tsx similarity index 72% rename from src/pages/ExpressionMaker/ExpressionMaker.tsx rename to src/pages/ExpressionMaker.tsx index 33b120b..66e9e6c 100644 --- a/src/pages/ExpressionMaker/ExpressionMaker.tsx +++ b/src/pages/ExpressionMaker.tsx @@ -4,30 +4,25 @@ import React, { useState } from 'react' import { StyleSheet, View } from 'react-native' import { ScrollView } from 'react-native-gesture-handler' import { Button, List, TextInput } from 'react-native-paper' -import { Expression, Module } from 'wollok-ts/dist/model' -import { ListLiterals } from '../../components/expressions/expression-lists/literals-list' -import { ListMessages } from '../../components/expressions/expression-lists/messages-list' -import { ListSingletons } from '../../components/expressions/expression-lists/singletons-list' -import { ListVariables } from '../../components/expressions/expression-lists/variables-list' -import { ExpressionDisplay } from '../../components/expressions/ExpressionDisplay' -import { SubmitCheckButton } from '../../components/ui/Header' +import { Expression } from 'wollok-ts/dist/model' +import { ListLiterals } from '../components/expressions/expression-lists/literals-list' +import { ListMessages } from '../components/expressions/expression-lists/messages-list' +import { ListSingletons } from '../components/expressions/expression-lists/singletons-list' +import { ListVariables } from '../components/expressions/expression-lists/variables-list' +import { ExpressionDisplay } from '../components/expressions/ExpressionDisplay' +import { SubmitCheckButton } from '../components/ui/Header' import { Context, ExpressionContextProvider, useExpressionContext, -} from '../../context/ExpressionContextProvider' -import { useProject } from '../../context/ProjectProvider' -import { wTranslate } from '../../utils/translation-helpers' -import { isMethodFQN, methodByFQN } from '../../utils/wollok-helpers' -import { EntityStackParamList } from '../EntityStack' - -export type ExpressionMakerProp = RouteProp< - EntityStackParamList, - 'ExpressionMaker' -> +} from '../context/ExpressionContextProvider' +import { useProject } from '../context/ProjectProvider' +import { wTranslate } from '../utils/translation-helpers' +import { entityMemberByFQN } from '../utils/wollok-helpers' +import { ProjectStackParamList } from './ProjectNavigator' export type ExpressionMakerScreenProp = StackNavigationProp< - EntityStackParamList, + ProjectStackParamList, 'ExpressionMaker' > @@ -55,6 +50,9 @@ function ExpressionMaker(props: { const navigation = useNavigation() React.useLayoutEffect(() => { navigation.setOptions({ + title: wTranslate('expression.title'), + headerTitleAlign: 'center', + animationEnabled: false, headerRight: () => ( + const styles = StyleSheet.create({ view: { display: 'flex', maxHeight: '85%' }, }) @@ -124,12 +127,11 @@ export default function ({ params: { contextFQN, onSubmit, initialExpression }, }, }: { - route: RouteProp + route: ExpressionMakerRouteProp }) { const { project } = useProject() - const context: Context = isMethodFQN(contextFQN) - ? methodByFQN(project, contextFQN) - : project.getNodeByFQN(contextFQN) + const context: Context = entityMemberByFQN(project, contextFQN) + return ( }} /> void + } } const Stack = createStackNavigator() @@ -26,11 +43,10 @@ export function ProjectNavigator({ route }: { route: ProjectStackRoute }) { initialProject={route.params.project}> - + + + + ) diff --git a/src/pages/Describes.tsx b/src/pages/tabs/Describes.tsx similarity index 73% rename from src/pages/Describes.tsx rename to src/pages/tabs/Describes.tsx index 4493ce6..4100bee 100644 --- a/src/pages/Describes.tsx +++ b/src/pages/tabs/Describes.tsx @@ -1,10 +1,10 @@ import React, { useState } from 'react' import { ScrollView } from 'react-native-gesture-handler' import { Describe, Package } from 'wollok-ts/dist/model' -import FabAddScreen from '../components/FabScreens/FabAddScreen' -import NewDescribeModal from '../components/tests/NewDescribeModal' -import DescribeItem from '../components/tests/DescribeItem' -import { useProject } from '../context/ProjectProvider' +import FabAddScreen from '../../components/FabScreens/FabAddScreen' +import NewDescribeModal from '../../components/tests/NewDescribeModal' +import DescribeItem from '../../components/tests/DescribeItem' +import { useProject } from '../../context/ProjectProvider' export function Describes() { const { diff --git a/src/pages/Entities/Entities.tsx b/src/pages/tabs/Modules.tsx similarity index 97% rename from src/pages/Entities/Entities.tsx rename to src/pages/tabs/Modules.tsx index a9262a4..5c72f03 100644 --- a/src/pages/Entities/Entities.tsx +++ b/src/pages/tabs/Modules.tsx @@ -6,7 +6,7 @@ import NewEntityModal from '../../components/entities/NewEntityModal/NewEntityMo import FabAddScreen from '../../components/FabScreens/FabAddScreen' import { mainPackageName, useProject } from '../../context/ProjectProvider' -export function Entities() { +export function Modules() { const { project, actions: { addEntity }, diff --git a/src/utils/type-helpers.ts b/src/utils/type-helpers.ts index fadabfe..174fd7d 100644 --- a/src/utils/type-helpers.ts +++ b/src/utils/type-helpers.ts @@ -9,3 +9,8 @@ export type Maybe = T | undefined export type ParentComponentProp = T & { children: OneOrMany } + +export type Visible = { + visible: boolean + setVisible: (value: boolean) => void +} diff --git a/src/utils/wollok-helpers.ts b/src/utils/wollok-helpers.ts index d479504..3c3512e 100644 --- a/src/utils/wollok-helpers.ts +++ b/src/utils/wollok-helpers.ts @@ -56,14 +56,6 @@ export function methodLabel(method: Method): string { return `${method.name}(${method.parameters.map(_ => _.name).join(',')})` } -export function entityMemberLabel(node: EntityMemberWithBody): string { - return node.is('Method') ? methodLabel(node) : node.name -} - -export function entityMemberFQN(node: EntityMemberWithBody): string { - return node.is('Method') ? methodFQN(node) : node.fullyQualifiedName() -} - export function literalClassFQN(literal: Literal): Name { return `wollok.lang.${upperCaseFirst(typeof literal.value)}` } @@ -78,6 +70,8 @@ export function allScopedVariables( return [...fields, ...params, ...methodVars] } +// METHODS + export function methodFQN(method: Method) { return `${method.parent.fullyQualifiedName()}.${method.name}/${ method.parameters.length @@ -102,10 +96,31 @@ export function methodByFQN(environment: Environment, fqn: Name): Method { return entity.lookupMethod(methodName, Number.parseInt(methodArity, 10))! } +export function entityMemberLabel(node: EntityMemberWithBody): string { + return node.is('Method') ? methodLabel(node) : node.name +} + +export function entityMemberFQN(node: EntityMemberWithBody): string { + return node.is('Method') ? methodFQN(node) : node.fullyQualifiedName() +} + +export function entityMemberByFQN( + environment: Environment, + fqn: Name, +): EntityMemberWithBody { + return isMethodFQN(fqn) + ? methodByFQN(environment, fqn) + : environment.getNodeByFQN(fqn) +} + +// PROBLEMS + export function isError(problem: Problem): boolean { return problem.level === 'error' } +// TESTS + export type TestResult = 'Passed' | 'Failure' | 'Error' export type TestRun = { result: TestResult; exception?: WollokException } export function interpretTest(test: Test, environment: Environment): TestRun { From 647929e23680c5ba28da5817df927f5a5dbccd11 Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 24 Apr 2022 22:29:27 +0200 Subject: [PATCH 2/3] Rename: EntityStack -> EntityDetails --- src/components/entities/Entity/Entity.tsx | 2 +- src/components/projects/ProjectHeader.tsx | 2 +- src/components/tests/DescribeItem.tsx | 2 +- src/pages/{EntityStack.tsx => EntityDetails.tsx} | 9 ++++++--- src/pages/ProjectNavigator.tsx | 6 +++--- 5 files changed, 12 insertions(+), 9 deletions(-) rename src/pages/{EntityStack.tsx => EntityDetails.tsx} (81%) diff --git a/src/components/entities/Entity/Entity.tsx b/src/components/entities/Entity/Entity.tsx index 38f67af..8a8f7ab 100644 --- a/src/components/entities/Entity/Entity.tsx +++ b/src/components/entities/Entity/Entity.tsx @@ -17,7 +17,7 @@ function EntityComponent({ entity, theme }: EntityComponentProps) { const styles = stylesheet(theme) const navigation = useNavigation() const goToEntityDetails = () => { - navigation.navigate('EntityStack', { + navigation.navigate('EntityDetails', { entityFQN: entity.fullyQualifiedName(), }) } diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index 7ec5163..31570c4 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -27,7 +27,7 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { const navigation = useNavigation() const goToEntityDetails = (entity: Entity) => { - navigation.navigate('EntityStack', { + navigation.navigate('EntityDetails', { entityFQN: entity.fullyQualifiedName(), }) } diff --git a/src/components/tests/DescribeItem.tsx b/src/components/tests/DescribeItem.tsx index ba4133b..40afa3b 100644 --- a/src/components/tests/DescribeItem.tsx +++ b/src/components/tests/DescribeItem.tsx @@ -17,7 +17,7 @@ function DescribeItem({ describe, theme }: Props) { const styles = stylesheet(theme) const navigation = useNavigation() const goToEntityDetails = () => { - navigation.navigate('EntityStack', { + navigation.navigate('EntityDetails', { entityFQN: describe.fullyQualifiedName(), }) } diff --git a/src/pages/EntityStack.tsx b/src/pages/EntityDetails.tsx similarity index 81% rename from src/pages/EntityStack.tsx rename to src/pages/EntityDetails.tsx index 8f0cdb5..ef0fb15 100644 --- a/src/pages/EntityStack.tsx +++ b/src/pages/EntityDetails.tsx @@ -6,9 +6,12 @@ import { Tests } from '../components/tests/Tests' import { useProject } from '../context/ProjectProvider' import { ProjectStackParamList } from './ProjectNavigator' -export type EntityStackRoute = RouteProp +export type EntityDetailsRoute = RouteProp< + ProjectStackParamList, + 'EntityDetails' +> -function EntityStack(props: { route: EntityStackRoute }) { +function EntityDetails(props: { route: EntityDetailsRoute }) { const { project } = useProject() const entity = project.getNodeByFQN(props.route.params.entityFQN) @@ -27,4 +30,4 @@ function EntityStack(props: { route: EntityStackRoute }) { ) } -export default EntityStack +export default EntityDetails diff --git a/src/pages/ProjectNavigator.tsx b/src/pages/ProjectNavigator.tsx index e3f168d..0d08882 100644 --- a/src/pages/ProjectNavigator.tsx +++ b/src/pages/ProjectNavigator.tsx @@ -6,13 +6,13 @@ import { RootStackParamList } from '../App' import { ProjectProvider } from '../context/ProjectProvider' import { ArgumentsMaker } from './ArgumentsMaker' import { Editor } from './Editor' -import EntityStack from './EntityStack' +import EntityDetails from './EntityDetails' import ExpressionMaker, { ExpressionOnSubmit } from './ExpressionMaker' import { Home } from './Home' export type ProjectStackParamList = { Home: undefined - EntityStack: { entityFQN: Name } + EntityDetails: { entityFQN: Name } Editor: { fqn: Name } @@ -43,7 +43,7 @@ export function ProjectNavigator({ route }: { route: ProjectStackRoute }) { initialProject={route.params.project}> - + From a928462b6707df13612a24ff16e34ecc8757417e Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 24 Apr 2022 22:58:19 +0200 Subject: [PATCH 3/3] useNodeNavigation --- src/components/entities/Entity/Entity.tsx | 11 ++--- src/components/projects/ProjectHeader.tsx | 35 ++------------- src/components/tests/DescribeItem.tsx | 10 ++--- src/context/NodeNavigation.tsx | 52 +++++++++++++++++++++++ src/pages/ProjectNavigator.tsx | 35 +++++++++------ src/pages/SelectProject.tsx | 3 +- 6 files changed, 87 insertions(+), 59 deletions(-) create mode 100644 src/context/NodeNavigation.tsx diff --git a/src/components/entities/Entity/Entity.tsx b/src/components/entities/Entity/Entity.tsx index 8a8f7ab..52260a5 100644 --- a/src/components/entities/Entity/Entity.tsx +++ b/src/components/entities/Entity/Entity.tsx @@ -1,8 +1,7 @@ -import { useNavigation } from '@react-navigation/native' import React from 'react' import { List, withTheme } from 'react-native-paper' import { Module } from 'wollok-ts/dist/model' -import { HomeScreenNavigationProp } from '../../../pages/Home' +import { useNodeNavigation } from '../../../context/NodeNavigation' import { Theme } from '../../../theme' import { ProblemReporterButton } from '../../problems/ProblemReporterButton' import { EntityKindIcon } from '../EntityKindIcon' @@ -15,12 +14,8 @@ type EntityComponentProps = { function EntityComponent({ entity, theme }: EntityComponentProps) { const styles = stylesheet(theme) - const navigation = useNavigation() - const goToEntityDetails = () => { - navigation.navigate('EntityDetails', { - entityFQN: entity.fullyQualifiedName(), - }) - } + const { goToNode } = useNodeNavigation() + const goToEntityDetails = () => goToNode(entity) return ( () - - const goToEntityDetails = (entity: Entity) => { - navigation.navigate('EntityDetails', { - entityFQN: entity.fullyQualifiedName(), - }) - } - const goToEditor = (entityMember: EntityMemberWithBody) => { - navigation.navigate('Editor', { - fqn: entityMemberFQN(entityMember), - }) - } + const { goToNode } = useNodeNavigation() const goto = (n: Node): void => { - n.match({ - Method: goToEditor, - Test: goToEditor, - Singleton: goToEntityDetails, - Describe: goToEntityDetails, - Field: f => goToEntityDetails(f.parent), - Body: b => goto(b.parent), - Sentence: e => goto(e.parent), - Expression: e => goto(e.parent), - }) - + goToNode(n) setShowProblems(false) } diff --git a/src/components/tests/DescribeItem.tsx b/src/components/tests/DescribeItem.tsx index 40afa3b..d34e1ab 100644 --- a/src/components/tests/DescribeItem.tsx +++ b/src/components/tests/DescribeItem.tsx @@ -1,7 +1,7 @@ -import { useNavigation } from '@react-navigation/native' import React from 'react' import { List, withTheme } from 'react-native-paper' import { Describe } from 'wollok-ts/dist/model' +import { useNodeNavigation } from '../../context/NodeNavigation' import { Theme } from '../../theme' import { stylesheet } from '../entities/Entity/styles' import IconImage from '../ui/IconImage' @@ -15,12 +15,8 @@ type Props = { // TODO: Merge with Entity component function DescribeItem({ describe, theme }: Props) { const styles = stylesheet(theme) - const navigation = useNavigation() - const goToEntityDetails = () => { - navigation.navigate('EntityDetails', { - entityFQN: describe.fullyQualifiedName(), - }) - } + const { goToNode } = useNodeNavigation() + const goToEntityDetails = () => goToNode(describe) return ( void +} | null>(null) + +export function NodeNavigationProvider({ children }: ParentComponentProp) { + const navigation = useNavigation() + + const goToEntityDetails = (entity: Entity) => { + navigation.navigate('EntityDetails', { + entityFQN: entity.fullyQualifiedName(), + }) + } + const goToEditor = (entityMember: EntityMemberWithBody) => { + navigation.navigate('Editor', { + fqn: entityMemberFQN(entityMember), + }) + } + + const goToNode = (n: Node): void => { + n.match({ + Method: goToEditor, + Test: goToEditor, + Describe: goToEntityDetails, + Module: goToEntityDetails, + Field: f => goToEntityDetails(f.parent), + Body: b => goToNode(b.parent), + Sentence: e => goToNode(e.parent), + Expression: e => goToNode(e.parent), + }) + } + + const init = { goToNode } + + return ( + {children} + ) +} + +export const useNodeNavigation = createContextHook(ContextContext, { + contextName: 'NodeNavigationProvider', + hookName: 'useNodeNavigation', +}) diff --git a/src/pages/ProjectNavigator.tsx b/src/pages/ProjectNavigator.tsx index 0d08882..c46e715 100644 --- a/src/pages/ProjectNavigator.tsx +++ b/src/pages/ProjectNavigator.tsx @@ -1,8 +1,12 @@ import { RouteProp } from '@react-navigation/core' -import { createStackNavigator } from '@react-navigation/stack' +import { + createStackNavigator, + StackNavigationProp, +} from '@react-navigation/stack' import React from 'react' import { Expression, Method, Name, Send } from 'wollok-ts/dist/model' import { RootStackParamList } from '../App' +import { NodeNavigationProvider } from '../context/NodeNavigation' import { ProjectProvider } from '../context/ProjectProvider' import { ArgumentsMaker } from './ArgumentsMaker' import { Editor } from './Editor' @@ -36,19 +40,26 @@ export type ProjectStackRoute = RouteProp< 'ProjectNavigator' > +export type ProjectScreenNavigationProp = StackNavigationProp< + RootStackParamList, + 'ProjectNavigator' +> + export function ProjectNavigator({ route }: { route: ProjectStackRoute }) { return ( - - - - - - - - - + + + + + + + + + + + ) } diff --git a/src/pages/SelectProject.tsx b/src/pages/SelectProject.tsx index d3022d8..64fb4b5 100644 --- a/src/pages/SelectProject.tsx +++ b/src/pages/SelectProject.tsx @@ -13,12 +13,13 @@ import { saveProject, } from '../services/persistance.service' import { useTheme } from '../theme' +import { ProjectScreenNavigationProp } from './ProjectNavigator' export function SelectProject() { const [projects, setProjects] = useState([]) const [showNewProjectModal, setShowNewProjectModal] = useState(false) const focused = useIsFocused() - const navigation = useNavigation() + const navigation = useNavigation() const theme = useTheme()