├── .eslintrc
├── .gitignore
├── .npmignore
├── LICENSE
├── MessageBar.js
├── README.md
├── custom-messages.gif
├── default-messages.gif
├── demo
├── .babelrc
├── .buckconfig
├── .expo
│ └── packager-info.json
├── .gitattributes
├── .gitignore
├── .watchmanconfig
├── App.js
├── android
│ ├── app
│ │ ├── BUCK
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── messagesexample
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── MainApplication.java
│ │ │ └── res
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── keystores
│ │ ├── BUCK
│ │ └── debug.keystore.properties
│ └── settings.gradle
├── app.json
├── index.js
├── ios
│ ├── MessagesExample-tvOS
│ │ └── Info.plist
│ ├── MessagesExample-tvOSTests
│ │ └── Info.plist
│ ├── MessagesExample.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ ├── MessagesExample-tvOS.xcscheme
│ │ │ └── MessagesExample.xcscheme
│ ├── MessagesExample
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Base.lproj
│ │ │ └── LaunchScreen.xib
│ │ ├── Images.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ └── main.m
│ └── MessagesExampleTests
│ │ ├── Info.plist
│ │ └── MessagesExampleTests.m
├── package-lock.json
├── package.json
└── rn-cli-config.js
├── index.js
├── messageManager.js
├── package-lock.json
└── package.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "qlean-react-native",
3 | "rules": {
4 | "react/prop-types": 0,
5 | "react-native/no-inline-styles": 0,
6 | "react-native/no-color-literals": 0
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | demo
2 | *.gif
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-present, Qlean
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MessageBar.js:
--------------------------------------------------------------------------------
1 | // Peer dependecies
2 | /* eslint-disable import/no-unresolved, import/extensions */
3 | import React, { Component } from 'react';
4 | import { View, Text, Animated, PanResponder, StyleSheet } from 'react-native';
5 | /* eslint-enable import/no-unresolved, import/extensions */
6 |
7 | import messageManager from './messageManager';
8 |
9 | const MIN_SWIPE_DISTANCE = 20;
10 | const MIN_SWIPE_VELOCITY = 0.15;
11 |
12 | const styles = StyleSheet.create({
13 | root: {
14 | position: 'absolute',
15 | top: 0,
16 | left: 0,
17 | right: 0,
18 | },
19 | message: {
20 | paddingVertical: 15,
21 | paddingHorizontal: 20,
22 | justifyContent: 'center',
23 | backgroundColor: 'grey',
24 | },
25 | messageText: {
26 | color: 'white',
27 | },
28 | });
29 |
30 | function Message({ message }) {
31 | return (
32 |
33 | {message}
34 |
35 | );
36 | }
37 |
38 | export default class MessageBar extends Component {
39 | constructor(props) {
40 | super(props);
41 | this.state = {
42 | isVisibleAnimValue: new Animated.Value(0),
43 | isAnimatingHide: false,
44 | message: null,
45 | config: {},
46 | };
47 | }
48 | componentWillMount() {
49 | this.panResponder = PanResponder.create({
50 | onMoveShouldSetPanResponder: (e, gesture) => (
51 | this.getConfig().closeOnSwipe
52 | && gesture.dy < -MIN_SWIPE_DISTANCE
53 | && gesture.vy < -MIN_SWIPE_VELOCITY
54 | ),
55 | onPanResponderMove: (e, gesture) => {
56 | if (!this.state.isAnimatingHide) {
57 | this.hideMessage(this.state.message);
58 | }
59 | },
60 | onShouldBlockNativeResponder: () => true,
61 | });
62 | }
63 | componentDidMount() {
64 | messageManager.registerMessageBar(this);
65 | }
66 | componentWillUnmount() {
67 | messageManager.unregisterMessageBar();
68 | }
69 | getConfig() {
70 | return Object.assign({}, this.props, this.state.config);
71 | }
72 | pushMessage(message, config) {
73 | this.setState({ message, config }, () => this.showMessage(message));
74 | }
75 | showMessage(message) {
76 | const { duration, showAnimationDuration } = this.getConfig();
77 | this.state.isVisibleAnimValue.setValue(0);
78 | this.setState({ isAnimatingHide: false });
79 | Animated.timing(
80 | this.state.isVisibleAnimValue,
81 | { toValue: 1, duration: showAnimationDuration, useNativeDriver: true },
82 | ).start(() => setTimeout(() => this.hideMessage(message), duration));
83 | }
84 | hideMessage(message) {
85 | if (message === this.state.message) {
86 | const { hideAnimationDuration } = this.getConfig();
87 | this.setState({ isAnimatingHide: true });
88 | Animated.timing(
89 | this.state.isVisibleAnimValue,
90 | { toValue: 0, duration: hideAnimationDuration, useNativeDriver: true },
91 | ).start(() => {
92 | if (message === this.state.message) {
93 | this.setState({ message: null, config: {}, isAnimatingHide: false });
94 | }
95 | });
96 | }
97 | }
98 | render() {
99 | const { messageComponent: MessageComponent, slideAnimationOffset } = this.getConfig();
100 | const translateY = this.state.isVisibleAnimValue.interpolate({
101 | inputRange: [0, 1],
102 | outputRange: [-slideAnimationOffset, 0],
103 | });
104 | const opacity = this.state.isVisibleAnimValue.interpolate({
105 | inputRange: [0, 1],
106 | outputRange: [0, 1],
107 | });
108 | return (
109 |
116 |
117 | {this.state.message && (
118 | this.hideMessage(this.state.message)}
121 | />
122 | )
123 | }
124 |
125 |
126 | );
127 | }
128 | }
129 |
130 | MessageBar.defaultProps = {
131 | messageComponent: Message,
132 | duration: 1000,
133 | slideAnimationOffset: 40,
134 | showAnimationDuration: 255,
135 | hideAnimationDuration: 255,
136 | closeOnSwipe: true,
137 | };
138 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-messages
2 |
3 | React Native notification-like messages
4 |
5 | 
6 |
7 | ## Features
8 | - Customizable message component
9 | - Fast native driver animations
10 | - Auto-hide current message to display a next one
11 | - Robust implementation, used in production code
12 |
13 | ## Installation
14 | ```
15 | $ npm install react-native-messages
16 | ```
17 |
18 | ## Usage
19 | 1. Add `` to the top of your view hierarchy, as a last component. If you are using `react-navigation`, root navigator should be in place of ``. Root view should have `flex: 1`:
20 | ```jsx
21 | import { MessageBar } from 'react-native-messages';
22 |
23 |
24 |
25 |
26 |
27 | ```
28 |
29 | 2. Call `showMessage` in any other component:
30 | ```jsx
31 | import { showMessage } from 'react-native-messages';
32 |
33 |