// @flow import * as React from 'react'; import { Avatar, Button, Card, Paragraph, Portal, withTheme, } from 'react-native-paper'; import * as Animatable from 'react-native-animatable'; import { BackHandler, Dimensions, ScrollView, TouchableWithoutFeedback, View, } from 'react-native'; import Mascot from './Mascot'; import type {CustomThemeType} from '../../managers/ThemeManager'; import SpeechArrow from './SpeechArrow'; import AsyncStorageManager from '../../managers/AsyncStorageManager'; type PropsType = { theme: CustomThemeType, icon: string, title: string, message: string, buttons: { action: { message: string, icon: string | null, color: string | null, onPress?: () => void, }, cancel: { message: string, icon: string | null, color: string | null, onPress?: () => void, }, }, emotion: number, visible?: boolean, prefKey?: string, }; type StateType = { shouldRenderDialog: boolean, // Used to stop rendering after hide animation dialogVisible: boolean, }; /** * Component used to display a popup with the mascot. */ class MascotPopup extends React.Component { static defaultProps = { visible: null, prefKey: null, }; mascotSize: number; windowWidth: number; windowHeight: number; constructor(props: PropsType) { super(props); this.windowWidth = Dimensions.get('window').width; this.windowHeight = Dimensions.get('window').height; this.mascotSize = Dimensions.get('window').height / 6; if (props.visible != null) { this.state = { shouldRenderDialog: props.visible, dialogVisible: props.visible, }; } else if (props.prefKey != null) { const visible = AsyncStorageManager.getBool(props.prefKey); this.state = { shouldRenderDialog: visible, dialogVisible: visible, }; } else { this.state = { shouldRenderDialog: false, dialogVisible: false, }; } } componentDidMount(): * { BackHandler.addEventListener( 'hardwareBackPress', this.onBackButtonPressAndroid, ); } shouldComponentUpdate(nextProps: PropsType, nextState: StateType): boolean { const {props, state} = this; if (nextProps.visible) { this.state.shouldRenderDialog = true; this.state.dialogVisible = true; } else if ( nextProps.visible !== props.visible || (!nextState.dialogVisible && nextState.dialogVisible !== state.dialogVisible) ) { this.state.dialogVisible = false; setTimeout(this.onAnimationEnd, 300); } return true; } onAnimationEnd = () => { this.setState({ shouldRenderDialog: false, }); }; onBackButtonPressAndroid = (): boolean => { const {state, props} = this; if (state.dialogVisible) { const {cancel} = props.buttons; const {action} = props.buttons; if (cancel != null) this.onDismiss(cancel.onPress); else this.onDismiss(action.onPress); return true; } return false; }; getSpeechBubble(): React.Node { const {state, props} = this; return ( ( ) : null } /> {props.message} {this.getButtons()} ); } getMascot(): React.Node { const {props, state} = this; return ( ); } getButtons(): React.Node { const {props} = this; const {action} = props.buttons; const {cancel} = props.buttons; return ( {action != null ? ( ) : null} {cancel != null ? ( ) : null} ); } getBackground(): React.Node { const {props, state} = this; return ( { this.onDismiss(props.buttons.cancel.onPress); }}> ); } onDismiss = (callback?: () => void) => { const {prefKey} = this.props; if (prefKey != null) { AsyncStorageManager.set(prefKey, false); this.setState({dialogVisible: false}); } if (callback != null) callback(); }; render(): React.Node { const {shouldRenderDialog} = this.state; if (shouldRenderDialog) { return ( {this.getBackground()} {this.getMascot()} {this.getSpeechBubble()} ); } return null; } } export default withTheme(MascotPopup);