/* eslint-disable max-classes-per-file */ import React, { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' import { ActivityIndicator, Keyboard, StyleSheet, StyleProp, TouchableHighlight, View, ViewStyle, } from 'react-native' import { events, bindingTypes, actionTypes } from '@adalo/constants' // utils import { getBindingValue } from 'utils/dependencies' import { executeAction, actionContextTypes } from 'utils/actions' import { evaluateCondition } from 'utils/conditions' // types import { IAction, IObject, IReduxState } from 'interfaces' export interface ActionWrapperProps { object: IObject bindingData?: {} bindingComparison?: string hitSlop?: number style?: StyleProp getBinding?: () => null getParams?: () => object dispatch?: () => void disabled?: boolean } export interface ActionWrapperState { active?: boolean } class ActionWrapper extends Component< T, ActionWrapperState > { static contextTypes = { ...actionContextTypes, } state = { active: false, } getVisible = () => { const { object, bindingData, bindingComparison } = this.props const binding = object.dataBinding if (!binding) { return !object.attributes?.hidden } if ( binding && binding.source && binding.bindingType === bindingTypes.VISIBILITY ) { if (bindingData === undefined) { return false } return evaluateCondition( bindingData, binding.comparator, binding.source.dataType, bindingComparison ) } return !object.attributes?.hidden } getAffectLayout() { const { object } = this.props const binding = object.dataBinding if ( binding && binding.options && binding.bindingType === bindingTypes.VISIBILITY ) { return !!binding.options.affectLayout } return false } getLink() { const { object } = this.props return object.link } hasLink() { const link = this.getLink() return !!link && Object.keys(link).length > 0 } getActions(): object { const { object } = this.props return object.actions || {} } hasActions() { const actions = this.getActions() return Object.keys(actions).length > 0 } handlePress = () => { const { getBindings, navigate } = this.context const { active } = this.state const { dispatch, disabled } = this.props if (active || disabled) { return } const link = this.getLink() const actions = this.getActions() const actionPromises: Promise[] = [] this.setState({ active: true }) let hasLinkAction = false Object.keys(actions) .filter(id => actions[id].eventType === events.TAP) .forEach(id => { const linkActions = actions[id].actions.filter((a: IAction) => { return a.actionType === actionTypes.NAVIGATE }) if (linkActions.length > 0) { hasLinkAction = true } actionPromises.push( executeAction(actions[id], { ...this.context, dispatch }) ) }) if (actionPromises.length > 0) { Promise.all(actionPromises).then(() => { const { setDirty } = this.context setDirty() this.setState({ active: false }) if (hasLinkAction) { Keyboard.dismiss() } }) } if (link) { const params = getBindings?.() ?? {} navigate({ ...link, params }) } } render() { const { active } = this.state const { children, style, hitSlop = 10 } = this.props const visible = this.getVisible() const affectLayout = this.getAffectLayout() if (!visible) { if (affectLayout) { return null } } const hasAction = this.hasActions() || this.hasLink() const opacity = active ? 0.4 : 1 if (hasAction && visible) { return ( {children} {active ? ( ) : null} ) } const wrapperOpacity = !visible ? { opacity: 0 } : {} return ( {children} ) } } const mapStateToProps = (state: IReduxState, props: ActionWrapperProps) => { const { object, getBinding, getParams } = props let bindingComparison if (object.dataBinding && object.dataBinding.comparison) { bindingComparison = getBindingValue(object.dataBinding.comparison, { state, getParams, getBinding, }) } return { bindingComparison, } } const ConnectedActionWrapper: any = connect(mapStateToProps)( ActionWrapper as any ) export default class WrappedActionWrapper< T extends ActionWrapperProps > extends Component { static contextTypes = { getBinding: PropTypes.func, getParams: PropTypes.func, } render() { const { getBinding, getParams } = this.context return ( ) } } const styles = StyleSheet.create({ loaderWrapper: { position: 'absolute', left: 0, top: 0, right: 0, bottom: 0, alignItems: 'center', justifyContent: 'center', }, })