rn-toastify 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.MD CHANGED
@@ -5,188 +5,194 @@
5
5
  ## Demo
6
6
 
7
7
  Animated toast message component for React Native.
8
- ![Demo GIF](https://github.com/muku534/react-native-toast/blob/master/docs/Toast.gif)
8
+ # rn-toastify (react-native-toast)
9
9
 
10
+ Animated, customizable toast notifications for React Native with a simple imperative API, responsive animations, and built-in light/dark theme support.
10
11
 
12
+ Demo GIF: https://github.com/muku534/react-native-toast/blob/master/docs/Toast.gif
11
13
 
12
- ## Features
13
-
14
- - 🚀 Imperative API
15
- - 🎨 Customizable layouts
16
- - 🔧 Flexible config
17
- - 📅 Promise Handling
18
- - 📍 Position Control
19
-
14
+ ## Key features
20
15
 
16
+ - Single-root container (mount once at App entry)
17
+ - Imperative hook API (useToast) — call from anywhere in your component tree
18
+ - System-aware light/dark theme with optional forced theme
19
+ - Promise helper (loading → success/error)
20
+ - Custom content and emoji toasts
21
21
 
22
22
  ## Installation
23
23
 
24
24
  ```bash
25
25
  npm install rn-toastify
26
26
  ```
27
-
28
- ## Usage
29
27
 
30
- To integrate the toast notifications into your application, follow these steps:
28
+ Or with yarn:
29
+
30
+ ```bash
31
+ yarn add rn-toastify
32
+ ```
33
+
34
+ ## Quick start (recommended)
35
+
36
+ 1. Mount a single `ToastContainer` at the top of your app (App.js). This container listens to the package singleton manager and renders toasts from anywhere in the app.
37
+
38
+ App.js
39
+
40
+ ```javascript
41
+ import React from 'react';
42
+ import { NavigationContainer } from '@react-navigation/native';
43
+ import MainNavigator from './src/navigation';
44
+ import useToast, { ToastContainer } from 'rn-toastify';
45
+
46
+ export default function App() {
47
+ return (
48
+ <>
49
+ <NavigationContainer>
50
+ <MainNavigator />
51
+ </NavigationContainer>
31
52
 
32
- - Import and Setup
53
+ {/* Mount once at root. Optionally force theme: <ToastContainer theme="dark" /> */}
54
+ <ToastContainer />
55
+ </>
56
+ );
57
+ }
58
+ ```
33
59
 
34
- Start by importing the necessary components and hooks from the library:
60
+ 2. Use the `useToast()` hook anywhere in your app (inside React function components):
35
61
 
36
62
  ```javascript
63
+ import React from 'react';
64
+ import { View, Button } from 'react-native';
37
65
  import useToast from 'rn-toastify';
38
- import ToastContainer from 'rn-toastify';
39
66
 
40
- const AppContent = () => {
41
- // Toast functions here
67
+ export default function HomeScreen() {
68
+ const { success, error, promise, custom, emoji } = useToast();
42
69
 
43
70
  return (
44
- <View style={styles.container}>
45
- {/* Buttons to trigger toasts */}
46
- <ToastContainer />
71
+ <View>
72
+ <Button title="Success" onPress={() => success('Saved successfully')} />
73
+ <Button title="Error" onPress={() => error('Failed to save', { position: 'top' })} />
47
74
  </View>
48
75
  );
49
- };
76
+ }
77
+ ```
50
78
 
51
- export default AppContent;
79
+ ## API (summary)
52
80
 
53
- ```
81
+ `useToast()` returns an object with convenience methods:
54
82
 
55
- - Implementing Toasts
83
+ - `show(content, options)` — show a raw element or text (content can be a React element)
84
+ - `success(messageOrElement, options)`
85
+ - `error(messageOrElement, options)`
86
+ - `custom(element, options)`
87
+ - `emoji(message, emojiChar, options)`
88
+ - `promise(promise, { loading, success, error }, options)`
56
89
 
57
- The useToast hook provides access to different types of toasts. Below are examples of how to implement each toast type:
90
+ ### Options (common)
58
91
 
59
- - Success Toast
60
- ```javascript
61
- import useToast from 'rn-toastify';
92
+ - `duration`: number (milliseconds). Use `Infinity` for persistent toasts.
93
+ - `position`: `'top' | 'bottom' | 'center'`
94
+ - `style`: object — custom style to apply to the toast wrapper
62
95
 
96
+ ## Examples
97
+
98
+ Success toast
99
+
100
+ ```javascript
63
101
  const { success } = useToast();
102
+ success('Operation successful', { duration: 1500, position: 'top' });
103
+ ```
64
104
 
65
- const handleSuccessToast = () => {
66
- success('Operation was successful!', { duration: 1500, position: 'top' });
67
- };
105
+ Promise helper
68
106
 
107
+ ```javascript
108
+ const { promise } = useToast();
109
+
110
+ const myPromise = fetch('/api/save');
69
111
 
112
+ promise(myPromise, {
113
+ loading: 'Saving…',
114
+ success: 'Saved!',
115
+ error: 'Save failed',
116
+ });
70
117
  ```
71
- - Error Toast
118
+
119
+ Custom content
120
+
72
121
  ```javascript
73
- import useToast from 'rn-toastify';
122
+ const { custom } = useToast();
123
+ custom(<MyCustomView />, { duration: 2000 });
124
+ ```
74
125
 
75
- const { error } = useToast();
126
+ Emoji toast
76
127
 
77
- const handleErrorToast = () => {
78
- error('Something went wrong!', { duration: 1500, position: 'top' });
79
- };
128
+ ```javascript
129
+ const { emoji } = useToast();
130
+ emoji('Great job!', '👍', { duration: 1300 });
131
+ ```
132
+
133
+ ## Theme
80
134
 
135
+ - By default `ToastContainer` detects the system color scheme (via `Appearance`) and passes a `theme` prop to visual toast components.
136
+ - To force a theme, mount the container with `theme`:
81
137
 
138
+ ```jsx
139
+ <ToastContainer theme="dark" />
82
140
  ```
83
141
 
84
- - Promise Toast
85
- ```javascript
86
- import useToast from 'rn-toastify';
142
+ If the system color scheme is unavailable, the toast theme defaults to `light`.
87
143
 
88
- const { promise } = useToast();
144
+ ## Compatibility & installation notes
89
145
 
90
- const handlePromiseToast = () => {
91
- const myPromise = new Promise((resolve, reject) => {
92
- setTimeout(() => {
93
- resolve();
94
- // reject();
95
- }, 1500);
96
- });
97
-
98
- promise(myPromise, {
99
- loading: 'Loading...',
100
- success: 'Promise resolved!',
101
- error: 'Promise rejected!',
102
- });
103
- };
146
+ - Peer dependencies: `react`, `react-native`, `react-native-reanimated` (v2+), `lottie-react-native` (if using animations). Install and link those in your app. See `package.json`.
147
+ - Reanimated setup: add `react-native-reanimated/plugin` to `babel.config.js` as the last plugin.
148
+ - iOS: run `cd ios && pod install` on macOS after installing native dependencies.
104
149
 
150
+ ## Import styles
105
151
 
106
- ```
152
+ This package exports a default `useToast` hook and named exports. Both of the following work:
107
153
 
108
- - Custom Toast
109
154
  ```javascript
110
- import React from 'react';
111
- import { View, Text, Image, StyleSheet } from 'react-native';
112
- import useToast from 'rn-toastify';
155
+ // ES default + named
156
+ import useToast, { ToastContainer } from 'rn-toastify';
113
157
 
114
- const { custom } = useToast();
158
+ // CommonJS
159
+ const rnToastify = require('rn-toastify');
160
+ const useToast = rnToastify.useToast || rnToastify.default;
161
+ const { ToastContainer } = rnToastify;
162
+ ```
115
163
 
116
- const handleCustomToast = () => {
117
- custom(
118
- <View style={styles.customContent}>
119
- <Image
120
- style={styles.image}
121
- source={{
122
- uri: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixqx=6GHAjsWpt9&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.2&w=160&h=160&q=80',
123
- }}
124
- />
125
- <View style={styles.textContainer}>
126
- <Text style={styles.customText}>Emilia Gates</Text>
127
- <Text style={styles.customSubText}>Sure! 8:30pm works great!</Text>
128
- </View>
129
- </View>,
130
- { duration: 1500, position: 'top' }
131
- );
132
- };
164
+ ## Troubleshooting
133
165
 
134
- const styles = StyleSheet.create({
135
- customContent: {
136
- flexDirection: 'row',
137
- alignItems: 'center',
138
- },
139
- image: {
140
- width: 50,
141
- height: 50,
142
- borderRadius: 25,
143
- },
144
- textContainer: {
145
- marginLeft: 10,
146
- },
147
- customText: {
148
- fontWeight: 'bold',
149
- },
150
- customSubText: {
151
- color: 'gray',
152
- },
153
- });
166
+ - Crash on app start / `_rnToastify.default is not a function`:
167
+ - Clear Metro cache and rebuild: `npx react-native start --reset-cache` and rebuild the app.
168
+ - Ensure your app resolves the package's `index.js` correctly (this package exports a CommonJS-friendly entry to avoid interop issues).
154
169
 
155
- ```
170
+ - Reanimated-related errors:
171
+ - Add the Reanimated babel plugin and rebuild the app. See Reanimated installation docs.
156
172
 
157
- - Emoji Toast
158
- ```javascript
159
- import useToast from 'rn-toastify';
173
+ - Lottie errors on iOS:
174
+ - Run `cd ios && pod install` and rebuild from Xcode.
160
175
 
161
- const { emoji } = useToast();
176
+ - Toasts not showing:
177
+ - Make sure `ToastContainer` is mounted once at the app root.
178
+ - Ensure native peer dependencies are installed and app rebuilt.
162
179
 
163
- const handleEmojiToast = () => {
164
- emoji('Great job!', '👍', { duration: 1500, position: 'top' });
165
- };
180
+ ## Publishing & versioning
166
181
 
167
- ```
182
+ When you're ready to release this change to npm:
168
183
 
169
- - Full Example
184
+ 1. Update the package version in `package.json` (or run `npm version patch|minor|major`).
185
+ 2. Run tests and build (if any): `npm test`.
186
+ 3. Pack and inspect: `npm pack`.
187
+ 4. Publish: `npm publish --access public` (make sure you're logged in via `npm login`).
170
188
 
171
- Here's a complete example to demonstrate how all the toast types can be implemented within a single component:
189
+ ## Contributing
172
190
 
173
- ```javascript
174
- import React from 'react';
175
- import { StyleSheet, View, Button } from 'react-native';
176
- import useToast from 'rn-toastify';
177
- import ToastContainer from 'rn-toastify';
191
+ PRs welcome. Please open an issue if you find bugs or want features like scoped containers, configurable color tokens, or example apps.
178
192
 
179
- const AppContent = () => {
180
- const { success, error, promise, custom, emoji } = useToast();
193
+ ## License
181
194
 
182
- return (
183
- <View style={styles.container}>
184
- <Button title="Show Success Toast" onPress={() => success('Operation was successful!', { duration: 1500, position: 'top' })} />
185
- <Button title="Show Error Toast" onPress={() => error('Something went wrong!', { duration: 1500, position: 'top' })} />
186
- <Button title="Show Promise Toast" onPress={() => {
187
- const myPromise = new Promise((resolve) => setTimeout(resolve, 1500));
188
- promise(myPromise, { loading: 'Loading...', success: 'Promise resolved!', error: 'Promise rejected!' });
189
- }} />
195
+ MIT
190
196
  <Button title="Show Custom Toast" onPress={() => custom(
191
197
  <View style={styles.customContent}>
192
198
  <Text>Custom Toast Content</Text>
@@ -216,6 +222,11 @@ export default AppContent;
216
222
  ```
217
223
  This example integrates multiple toast types and demonstrates how to trigger each one. It also includes the necessary ToastContainer to display the toasts.
218
224
 
225
+ Theme and single-root notes
226
+ - Mount `ToastContainer` once at the app root (recommended). The container detects the system color scheme by default and passes a `theme` prop to all toast components.
227
+ - To force a theme, pass `theme="dark"` or `theme="light"` to `<ToastContainer />`.
228
+ - If the system color scheme is unavailable, the toast theme defaults to `light`.
229
+
219
230
 
220
231
  ## API Reference
221
232
 
package/docs/Toast.gif CHANGED
Binary file
package/index.js CHANGED
@@ -1,8 +1,31 @@
1
- export { default as toast } from './src/Toast';
2
- export { default as useToast } from './src/hooks/useToast';
3
- export { default as ToastContainer } from './src/context/ToastContainer';
4
- export { SuccessToast } from './src/components/SuccessToast';
5
- export { CustomeToast } from './src/components/CustomeToast';
6
- export { EmojiToast } from './src/components/EmojiToast';
7
- export { ErrorToast } from './src/components/ErrorToast';
8
- export { LoadingToast } from './src/components/LoadingToast';
1
+ // CommonJS-friendly entry to avoid default/import interop issues with Metro in some RN versions
2
+ // We attempt to require modules and use .default if present (handles transpiled ESM)
3
+ const _useToastMod = require('./src/hooks/useToast');
4
+ const _toastMod = require('./src/Toast');
5
+ const _containerMod = require('./src/context/ToastContainer');
6
+ const _successMod = require('./src/components/SuccessToast');
7
+ const _customeMod = require('./src/components/CustomeToast');
8
+ const _emojiMod = require('./src/components/EmojiToast');
9
+ const _errorMod = require('./src/components/ErrorToast');
10
+ const _loadingMod = require('./src/components/LoadingToast');
11
+
12
+ const useToast = (_useToastMod && _useToastMod.__esModule) ? _useToastMod.default : _useToastMod;
13
+ const Toast = (_toastMod && _toastMod.__esModule) ? _toastMod.default : _toastMod;
14
+ const ToastContainer = (_containerMod && _containerMod.__esModule) ? _containerMod.default : _containerMod;
15
+ const SuccessToast = (_successMod && _successMod.__esModule) ? _successMod.default : _successMod;
16
+ const CustomeToast = (_customeMod && _customeMod.__esModule) ? _customeMod.default : _customeMod;
17
+ const EmojiToast = (_emojiMod && _emojiMod.__esModule) ? _emojiMod.default : _emojiMod;
18
+ const ErrorToast = (_errorMod && _errorMod.__esModule) ? _errorMod.default : _errorMod;
19
+ const LoadingToast = (_loadingMod && _loadingMod.__esModule) ? _loadingMod.default : _loadingMod;
20
+
21
+ // Export for both require() and import syntax
22
+ module.exports = useToast;
23
+ module.exports.default = useToast;
24
+ module.exports.useToast = useToast;
25
+ module.exports.Toast = Toast;
26
+ module.exports.ToastContainer = ToastContainer;
27
+ module.exports.SuccessToast = SuccessToast;
28
+ module.exports.CustomeToast = CustomeToast;
29
+ module.exports.EmojiToast = EmojiToast;
30
+ module.exports.ErrorToast = ErrorToast;
31
+ module.exports.LoadingToast = LoadingToast;
package/package.json CHANGED
@@ -1,44 +1,44 @@
1
- {
2
- "name": "rn-toastify",
3
- "version": "1.0.8",
4
- "description": "A customizable and performant toast notification library for React Native, featuring smooth animations, swipe gestures, and flexible styling options.",
5
- "main": "index.js",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/muku534/react-native-toast"
9
- },
10
- "homepage": "https://github.com/muku534/react-native-toast#readme",
11
- "scripts": {
12
- "test": "jest"
13
- },
14
- "keywords": [
15
- "react-native",
16
- "toast",
17
- "react-native-toastify",
18
- "react-native-toast",
19
- "react-native-toast-message",
20
- "rn-toastify",
21
- "alert",
22
- "notification",
23
- "library"
24
- ],
25
- "author": "Mukesh Prajapati",
26
- "license": "MIT",
27
- "peerDependencies": {
28
- "react": ">=16.8.0 <19.0.0",
29
- "react-native": ">=0.60.0 <1.0.0",
30
- "react-native-reanimated": ">=2.0.0 <4.0.0",
31
- "lottie-react-native": ">=3.0.0 <7.0.0"
32
- },
33
- "dependencies": {
34
- "events": "^3.3.0"
35
- },
36
- "devDependencies": {
37
- "@testing-library/jest-native": "^5.4.3",
38
- "@testing-library/react-native": "^12.5.2",
39
- "@types/jest": "^28.1.0",
40
- "@types/react-native": "^0.67.3",
41
- "jest": "^28.1.0",
42
- "react-test-renderer": "^17.0.2"
43
- }
1
+ {
2
+ "name": "rn-toastify",
3
+ "version": "1.0.9",
4
+ "description": "A customizable and performant toast notification library for React Native, featuring smooth animations, swipe gestures, and flexible styling options.",
5
+ "main": "index.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/muku534/react-native-toast"
9
+ },
10
+ "homepage": "https://github.com/muku534/react-native-toast#readme",
11
+ "scripts": {
12
+ "test": "jest"
13
+ },
14
+ "keywords": [
15
+ "react-native",
16
+ "toast",
17
+ "react-native-toastify",
18
+ "react-native-toast",
19
+ "react-native-toast-message",
20
+ "rn-toastify",
21
+ "alert",
22
+ "notification",
23
+ "library"
24
+ ],
25
+ "author": "Mukesh Prajapati",
26
+ "license": "MIT",
27
+ "peerDependencies": {
28
+ "react": ">=16.8.0 <19.0.0",
29
+ "react-native": ">=0.60.0 <1.0.0",
30
+ "react-native-reanimated": ">=2.0.0 <5.0.0",
31
+ "lottie-react-native": ">=3.0.0 <9.0.0"
32
+ },
33
+ "dependencies": {
34
+ "events": "^3.3.0"
35
+ },
36
+ "devDependencies": {
37
+ "@testing-library/jest-native": "^5.4.3",
38
+ "@testing-library/react-native": "^12.5.2",
39
+ "@types/jest": "^28.1.0",
40
+ "@types/react-native": "^0.67.3",
41
+ "jest": "^28.1.0",
42
+ "react-test-renderer": "^17.0.2"
43
+ }
44
44
  }
package/src/Toast.js CHANGED
@@ -9,7 +9,7 @@ import Animated, {
9
9
  Easing,
10
10
  } from 'react-native-reanimated';
11
11
 
12
- const Toast = ({ visible, duration, position, children, onHide, style }) => {
12
+ const Toast = ({ visible, duration, position, children, onHide, style, theme }) => {
13
13
  const opacity = useSharedValue(0);
14
14
  const translateY = useSharedValue(position === 'top' ? -50 : 50);
15
15
  const translateX = useSharedValue(0);
@@ -77,12 +77,20 @@ const Toast = ({ visible, duration, position, children, onHide, style }) => {
77
77
 
78
78
  if (!visible) return null;
79
79
 
80
+ // If children is a React element, inject theme prop; otherwise render as-is.
81
+ const renderContent = () => {
82
+ if (React.isValidElement(children)) {
83
+ return React.cloneElement(children, { theme });
84
+ }
85
+ return children;
86
+ };
87
+
80
88
  return (
81
89
  <Animated.View
82
90
  {...panResponder.panHandlers}
83
91
  style={[styles.container, animatedStyle, positionStyle, style]}
84
92
  >
85
- {children}
93
+ {renderContent()}
86
94
  </Animated.View>
87
95
  );
88
96
  };
@@ -5,9 +5,11 @@ import {
5
5
  widthPercentageToDP as wp,
6
6
  } from '../utils/Pixel/Index';
7
7
 
8
- const CustomToast = ({ content }) => {
8
+ const CustomToast = ({ content, theme = 'light' }) => {
9
+ const isDark = theme === 'dark';
10
+ const bg = isDark ? '#111827' : '#FFFFFF';
9
11
  return (
10
- <View style={styles.customToast}>
12
+ <View style={[styles.customToast, { backgroundColor: bg }]}>
11
13
  {content}
12
14
  </View>
13
15
  );
@@ -5,10 +5,14 @@ import {
5
5
  widthPercentageToDP as wp,
6
6
  } from '../utils/Pixel/Index';
7
7
 
8
- const EmojiToast = ({ message, emoji }) => {
8
+ const EmojiToast = ({ message, emoji, theme = 'light' }) => {
9
+ const isDark = theme === 'dark';
10
+ const bg = isDark ? '#111827' : '#F7F7FC';
11
+ const textColor = isDark ? '#F3F4F6' : 'black';
12
+
9
13
  return (
10
- <View style={styles.emojiToast}>
11
- <Text style={styles.text}>{emoji} {message}</Text>
14
+ <View style={[styles.emojiToast, { backgroundColor: bg }]}>
15
+ <Text style={[styles.text, { color: textColor }]}>{emoji} {message}</Text>
12
16
  </View>
13
17
  );
14
18
  }
@@ -7,9 +7,13 @@ import {
7
7
  widthPercentageToDP as wp,
8
8
  } from '../utils/Pixel/Index';
9
9
 
10
- const ErrorToast = ({ message }) => {
10
+ const ErrorToast = ({ message, theme = 'light' }) => {
11
+ const isDark = theme === 'dark';
12
+ const containerBg = isDark ? '#111827' : '#F7F7FC';
13
+ const textColor = isDark ? '#FEE2E2' : '#991B1B';
14
+
11
15
  return (
12
- <View style={styles.container}>
16
+ <View style={[styles.container, { backgroundColor: containerBg }]}>
13
17
  <LottieView
14
18
  source={require('../../assets/animated_Icon/ErrorAnimation.json')} // Replace with your success Lottie animation path
15
19
  autoPlay
@@ -17,7 +21,7 @@ const ErrorToast = ({ message }) => {
17
21
  speed={1.5}
18
22
  style={styles.lottie}
19
23
  />
20
- <Text style={styles.text}>{message}</Text>
24
+ <Text style={[styles.text, { color: textColor }]}>{message}</Text>
21
25
  </View>
22
26
  );
23
27
  }
@@ -6,10 +6,14 @@ import {
6
6
  widthPercentageToDP as wp,
7
7
  } from '../utils/Pixel/Index';
8
8
 
9
- const LoadingToast = ({ message }) => {
9
+ const LoadingToast = ({ message, theme = 'light' }) => {
10
+ const isDark = theme === 'dark';
11
+ const bg = isDark ? '#111827' : '#FFFFFF';
12
+ const indicatorColor = isDark ? '#9CA3AF' : '#6B7280';
13
+
10
14
  return (
11
- <View style={styles.toast}>
12
- <ActivityIndicator size="large" color={'#a9a9a9'} />
15
+ <View style={[styles.toast, { backgroundColor: bg }]}>
16
+ <ActivityIndicator size="small" color={indicatorColor} />
13
17
  {/** <Text style={styles.text}>{message}</Text> */}
14
18
  </View>
15
19
  );
@@ -6,9 +6,13 @@ import {
6
6
  widthPercentageToDP as wp,
7
7
  } from '../utils/Pixel/Index';
8
8
 
9
- const SuccessToast = ({ message }) => {
9
+ const SuccessToast = ({ message, theme = 'light' }) => {
10
+ const isDark = theme === 'dark';
11
+ const containerBg = isDark ? '#111827' : '#F7F7FC';
12
+ const textColor = isDark ? '#F3F4F6' : '#064E3B';
13
+
10
14
  return (
11
- <View style={styles.container}>
15
+ <View style={[styles.container, { backgroundColor: containerBg }]}>
12
16
  <LottieView
13
17
  source={require('../../assets/animated_Icon/SuccessAnimation.json')} // Replace with your success Lottie animation path
14
18
  autoPlay
@@ -16,7 +20,7 @@ const SuccessToast = ({ message }) => {
16
20
  style={styles.lottie}
17
21
  speed={1.2}
18
22
  />
19
- <Text style={styles.text}>{message}</Text>
23
+ <Text style={[styles.text, { color: textColor }]}>{message}</Text>
20
24
  </View>
21
25
  );
22
26
  };
@@ -27,8 +31,8 @@ const styles = StyleSheet.create({
27
31
  height: hp(6.8),
28
32
  paddingHorizontal: wp(4),
29
33
  borderRadius: wp(4),
30
- // backgroundColor: '#d2f7d2',
31
- backgroundColor: '#F7F7FC',
34
+ // backgroundColor: '#d2f7d2',
35
+ backgroundColor: '#F7F7FC',
32
36
  alignItems: 'center',
33
37
  flexDirection: 'row',
34
38
  },
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useState } from 'react';
2
- import { View, StyleSheet } from 'react-native';
2
+ import { View, StyleSheet, Appearance } from 'react-native';
3
3
  import Toast from '../Toast';
4
4
  import toastManagerInstance from './ToastManager';
5
5
  import {
@@ -7,8 +7,9 @@ import {
7
7
  widthPercentageToDP as wp,
8
8
  } from '../utils/Pixel/Index';
9
9
 
10
- const ToastContainer = () => {
10
+ const ToastContainer = ({ theme: forcedTheme } = {}) => {
11
11
  const [toasts, setToasts] = useState([]);
12
+ const [theme, setTheme] = useState(forcedTheme ?? (Appearance.getColorScheme() || 'light'));
12
13
 
13
14
  useEffect(() => {
14
15
  const handleShow = (toast) => {
@@ -22,28 +23,37 @@ const ToastContainer = () => {
22
23
  toastManagerInstance.on('show', handleShow);
23
24
  toastManagerInstance.on('remove', handleRemove);
24
25
 
26
+ let appearanceSub;
27
+ if (!forcedTheme && Appearance && Appearance.addChangeListener) {
28
+ appearanceSub = Appearance.addChangeListener(({ colorScheme }) => {
29
+ setTheme(colorScheme ?? 'light');
30
+ });
31
+ }
32
+
25
33
  return () => {
26
34
  toastManagerInstance.off('show', handleShow);
27
35
  toastManagerInstance.off('remove', handleRemove);
36
+ if (appearanceSub && appearanceSub.remove) appearanceSub.remove();
28
37
  };
29
38
  }, []);
30
39
 
31
40
  // Separate toasts by position
32
- const topToasts = toasts.filter(toast => toast.options.position === 'top');
33
- const centerToasts = toasts.filter(toast => toast.options.position === 'center');
34
- const bottomToasts = toasts.filter(toast => toast.options.position === 'bottom');
41
+ const topToasts = toasts.filter(toast => toast?.options?.position === 'top');
42
+ const centerToasts = toasts.filter(toast => toast?.options?.position === 'center');
43
+ const bottomToasts = toasts.filter(toast => toast?.options?.position === 'bottom');
35
44
 
36
45
  return (
37
46
  <>
38
47
  {/* Top positioned toasts */}
39
- <View style={styles.topContainer}>
48
+ <View style={styles.topContainer} pointerEvents="box-none">
40
49
  {topToasts.map((toast, index) => (
41
50
  <Toast
42
51
  key={toast.id}
43
52
  visible={true}
44
- duration={toast.options.duration}
53
+ duration={toast?.options?.duration}
45
54
  position="top"
46
- style={[toast.options.style, { top: hp(0.1) + index * 60 }]} // Adjust spacing between top toasts
55
+ theme={theme}
56
+ style={[toast?.options?.style || {}, { top: hp(0.1) + index * 60 }]} // Adjust spacing between top toasts
47
57
  onHide={() => toastManagerInstance.remove(toast.id)}
48
58
  >
49
59
  {toast.content}
@@ -52,14 +62,15 @@ const ToastContainer = () => {
52
62
  </View>
53
63
 
54
64
  {/* Center positioned toasts */}
55
- <View style={styles.centerContainer}>
65
+ <View style={styles.centerContainer} pointerEvents="box-none">
56
66
  {centerToasts.map((toast, index) => (
57
67
  <Toast
58
68
  key={toast.id}
59
69
  visible={true}
60
- duration={toast.options.duration}
70
+ duration={toast?.options?.duration}
61
71
  position="center"
62
- style={[toast.options.style, { marginTop: index * 60 }]} // Adjust spacing between center toasts
72
+ theme={theme}
73
+ style={[toast?.options?.style || {}, { marginTop: index * 60 }]} // Adjust spacing between center toasts
63
74
  onHide={() => toastManagerInstance.remove(toast.id)}
64
75
  >
65
76
  {toast.content}
@@ -68,14 +79,15 @@ const ToastContainer = () => {
68
79
  </View>
69
80
 
70
81
  {/* Bottom positioned toasts */}
71
- <View style={styles.bottomContainer}>
82
+ <View style={styles.bottomContainer} pointerEvents="box-none">
72
83
  {bottomToasts.map((toast, index) => (
73
84
  <Toast
74
85
  key={toast.id}
75
86
  visible={true}
76
- duration={toast.options.duration}
87
+ duration={toast?.options?.duration}
77
88
  position="bottom"
78
- style={[toast.options.style, { bottom: hp(2) + index * 60 }]} // Adjust spacing between bottom toasts
89
+ theme={theme}
90
+ style={[toast?.options?.style || {}, { bottom: hp(2) + index * 60 }]} // Adjust spacing between bottom toasts
79
91
  onHide={() => toastManagerInstance.remove(toast.id)}
80
92
  >
81
93
  {toast.content}
@@ -1,14 +1,16 @@
1
1
  import { EventEmitter } from 'events';
2
2
 
3
3
  class ToastManager extends EventEmitter {
4
- show(content, options = {}) {
5
- const id = Date.now().toString();
4
+ // show accepts an optional id so callers (like promise) can control the id lifecycle
5
+ show(content, options = {}, id = null) {
6
+ const _id = id ?? Date.now().toString();
6
7
  const defaultOptions = {
7
8
  duration: 1000,
8
9
  position: 'bottom',
9
10
  style: {},
10
11
  };
11
- this.emit('show', { id, content, options: { ...defaultOptions, ...options } });
12
+ this.emit('show', { id: _id, content, options: { ...defaultOptions, ...options } });
13
+ return _id;
12
14
  }
13
15
 
14
16
  remove(id) {
@@ -17,7 +19,8 @@ class ToastManager extends EventEmitter {
17
19
 
18
20
  async promise(promise, { loading, success, error }) {
19
21
  const id = Date.now().toString();
20
- this.show(loading, { duration: Infinity, position: 'top' });
22
+ // show the loading toast using the same id so we can remove it later
23
+ this.show(loading, { duration: Infinity, position: 'top' }, id);
21
24
  try {
22
25
  await promise;
23
26
  this.remove(id);