繁体   English   中英

全局应用内通知

[英]Global in-app notifications

我希望使用一个应用程序内通知系统,也就是一种更具吸引力并且在您的面部表情中使用较少的警报,以便让用户知道正在执行的操作,尤其是例如在检测到条形码但需要发送该条形码时条形码发送到服务器,用户需要等待。

我找到了这个库,并试图实现它。 但是当我使用React Navigation并希望在应用程序的最顶部渲染项目时,它会被React Native标头截断

是否可以有一个我可以在需要全局通知时创建和引用的函数,并且该函数将在最顶层呈现,我认为它需要在此处呈现:

import React from 'react';
import { createBottomTabNavigator,createStackNavigator } from 'react-navigation';
import SearchTab from './components/Tabs/SearchTab';
import HomeTab from './components/Tabs/HomeTab';
import ScannerTab from './components/Tabs/ScannerTab';
import SettingsTab from './components/Tabs/SettingsTab';
import Ionicons from 'react-native-vector-icons/Ionicons';
import StockModal from './components/Modals/StockModal';

const MainStack = createBottomTabNavigator(
    {
        Home: HomeTab,
        Search: SearchTab,
        Scanner: ScannerTab,
        Settings: SettingsTab,
        //Todo: Total overlay modals HERE
    },
    {
        navigationOptions: ({ navigation }) => ({
            tabBarIcon: ({ focused, tintColor }) => {
                const { routeName } = navigation.state;
                let iconName;

                if (routeName === 'Home') {
                    iconName = `ios-information-circle${focused ? '' : '-outline'}`;
                } else if (routeName === 'Settings') {
                    iconName = `ios-options${focused ? '' : '-outline'}`;
                }else if (routeName === 'Scanner') {
                    iconName = `ios-barcode${focused ? '' : '-outline'}`;
                }else if (routeName === 'Search') {
                    iconName = `ios-search${focused ? '' : '-outline'}`;
                }
                return <Ionicons name={iconName} size={25} color={tintColor} />;
            },
        }),
        tabBarOptions: {
            activeTintColor: 'tomato',
            inactiveTintColor: 'gray',
        },
    }
);

export default RootStack = createStackNavigator(
    {
        Main: {
            screen: MainStack,
        },
        QuickStockScreen: {
            screen: StockModal,
        },
    },
    {
        mode: 'modal',
        headerMode: 'none',
    }
);

但是,即使有可能,我也不知道如何构建一个通知显示的功能。 我想到了React Redux,但我不希望仅为一项功能实现如此繁琐的系统,这是我在创建其应用程序时决定考虑的事情。

有问题的通知系统(可惜的不是非常清晰的文档或示例) https://www.npmjs.com/package/react-native-in-app-notification

这是我正在使用的导航库: https : //reactnavigation.org/

您需要的是与导航级别相同的组件(以便可以在其上方显示)。 在多个项目中,我使用react-native-root-siblings来这样做。 它允许您在应用程序上以及导航上添加UI。

一个例子,我是怎么做的。 深色层和底部的框是“兄弟姐妹”组件的一部分。 https://gyazo.com/7ad3fc3fea767ea84243aaa493294670

兄弟姐妹的用法类似于React-Native的Alert,因此是一个函数(非常有用!)

messageMenu.js

import React, { Component } from 'react';
import RootSiblings from 'react-native-root-siblings';
import MessageMenuContainer from './MessageMenuContainer';

export default class Dialog extends Component {
  static show = (props) => new RootSiblings(<MessageMenuContainer {...props} />);

  static update = (menu, props) => {
    if (menu instanceof RootSiblings) {
      menu.update(<MessageMenuContainer {...props} />);
    } else {
      console.warn(`Dialog.update expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof menu}\` instead.`);
    }
  }

  static close = (menu) => {
    if (menu instanceof RootSiblings) {
      menu.destroy();
    } else {
      console.warn(`Dialog.destroy expected a \`RootSiblings\` instance as argument.\nBut got \`${typeof menu}\` instead.`);
    }
  }

  render() {
    return null;
  }
}

export {
  RootSiblings as Manager,
};

其中MessageMenuContainer是要在顶部呈现的组件。

使用根兄弟姐妹的组件:

 import React from 'react';
import PropTypes from 'prop-types';
import I18n from 'react-native-i18n';
import { BackHandler, Keyboard, Platform, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import DraftMenu from './messageMenu'; //HERE IS THE IMPORT YOU WANT

import { Metrics, Colors, Fonts } from '../../main/themes';

class DraftBackButton extends React.Component {

  state = {
    draftMenu: undefined,
  }

  componentDidMount() {
    BackHandler.addEventListener('hardwareBackPress', this.handleBackAndroid);
  }
  componentWillUnmount() {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackAndroid);
  }

  handleBackAndroid = () => {
    this.handleBack();
    return true;
  }

  handleBack = async () => {
    Keyboard.dismiss();
    await this.openDraftMenu();
  }

  openDraftMenu = async () => {
    if (this.state.draftMenu) {
      await DraftMenu.update(this.state.draftMenu, this.draftMenuProps());
    } else {
      const draftMenu = await DraftMenu.show(this.draftMenuProps());
      this.setState({ draftMenu: draftMenu });
    }
  }

  draftMenuProps = () => ({
    options: [
      { title: I18n.t('message.deleteDraft'), onPress: this.deleteDraft, icon: 'trash' },
      { title: I18n.t('message.saveDraft'), onPress: this.saveOrUpdateDraft, icon: 'documents' },
      { title: I18n.t('cancel'), icon: 'close', style: { backgroundColor: Colors.tertiaryBackground } },
    ],
    destroyMenuComponent: async () => {
      DraftMenu.close(this.state.draftMenu);
      await this.setState({ draftMenu: undefined });
    },
    withIcon: true,
  })

  saveOrUpdateDraft = async () => {
   // SAVE OR UPDATE DRAFT. NOT IMPORTANT
  }

  saveDraft = async () => {
    // SAVING THE DRAFT
  }

  updateDraft = async () => {
   // UPDATING THE DRAFT
  }

  deleteDraft = async () => {
    // DELETING THE DRAFT
  }

  render() {
    return (
      <TouchableOpacity
        hitSlop={Metrics.touchable.largeHitSlop}
        onPress={() => {
          this.handleBack();
        }}
      >
        <Text>BUTTON</Text>
      </TouchableOpacity>
    );
  }
}

DraftBackButton.propTypes = {
  // ALL THE PROPTYPES
};

function mapStateToProps(state, ownProps) {
 //
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators({ fetchMessages }, dispatch),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(DraftBackButton);

这个lib最好的地方是,您可以在应用程序中的任何位置调用.show ,它将在最顶部呈现! 希望这就是您要寻找的!

编辑:我更新了如何使用根兄弟姐妹的示例。

这是我的MessageContainer的内容,它将显示在所有内容的顶部

    import React from 'react';
    import PropTypes from 'prop-types';
    import { Animated, Dimensions, InteractionManager, StyleSheet, TouchableOpacity, View } from 'react-native';
    import MessageMenuItem from './MessageMenuItem';
    import { Colors } from '../../../main/themes';

    const { width, height } = Dimensions.get('window');

    const OPTION_HEIGHT = 55;
    const OVERLAY_OPACITY = 0.5;

    export default class DraftMenuContainer extends React.Component {

      constructor(props) {
        super(props);

        this.state = {
          animatedHeight: new Animated.Value(0),
          animatedOpacity: new Animated.Value(0),
          menuHeight: props.options.length * OPTION_HEIGHT,
        };
      }

      componentDidMount() {
        this.onOpen();
      }

// Using Animated from react-native to make the animation (fade in/out of the dark layer and the dimensions of the actual content)

      onOpen = async () => {
        await this.state.animatedHeight.setValue(0);
        await this.state.animatedOpacity.setValue(0);

        Animated.parallel([
          Animated.timing(this.state.animatedHeight, { toValue: this.state.menuHeight, duration: 200 }),
          Animated.timing(this.state.animatedOpacity, { toValue: OVERLAY_OPACITY, duration: 200 }),
        ]).start();
      }

      onClose = async () => {
        await this.state.animatedHeight.setValue(this.state.menuHeight);
        await this.state.animatedOpacity.setValue(OVERLAY_OPACITY);

        Animated.parallel([
          Animated.timing(this.state.animatedHeight, { toValue: 0, duration: 200 }),
          Animated.timing(this.state.animatedOpacity, { toValue: 0, duration: 200 }),
        ]).start(() => this.props.destroyMenuComponent()); // HERE IS IMPORTANT. Once you're done with the component, you need to destroy it. To do so, you need to set a props 'destroyMenuComponent' which is set at the creation of the initial view. See the other code what it actually do
      }

      render() {
        return (
          <View style={styles.menu}>
            <Animated.View style={[styles.backgroundOverlay, { opacity: this.state.animatedOpacity }]}>
              <TouchableOpacity
                activeOpacity={1}
                onPress={() => this.onClose()}
                style={{ flex: 1 }}
              />
            </Animated.View>

            <Animated.View style={[styles.container, { height: this.state.animatedHeight }]}>
              {this.props.options.map((option, index) => (
                <MessageMenuItem
                  height={OPTION_HEIGHT}
                  icon={option.icon}
                  key={index}
                  onPress={async () => {
                    await this.onClose();
                    InteractionManager.runAfterInteractions(() => {
                      if (option.onPress) {
                        option.onPress();
                      }
                    });
                  }}
                  style={option.style}
                  title={option.title}
                  withIcon={this.props.withIcon}
                />
              ))}
            </Animated.View>
          </View>
        );
      }
    }

    DraftMenuContainer.propTypes = {
      destroyMenuComponent: PropTypes.func.isRequired,
      withIcon: PropTypes.bool,
      options: PropTypes.arrayOf(PropTypes.shape({
        icon: PropTypes.string.isRequired,
        onPress: PropTypes.func,
        title: PropTypes.string.isRequired,
      })),
    };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM