Skip to content

Commit 4b65dcf

Browse files
committed
2 parents 78e8e1b + 1553972 commit 4b65dcf

File tree

9 files changed

+147
-21
lines changed

9 files changed

+147
-21
lines changed

README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ I know what you might be thinking (*jeez, another toast library?*). Trust me her
2020
- **Customizable** (custom styles, dimensions, duration, and even create your own component to be used in the toast)
2121
- Add support for **promises** <-- Really! Call `toast.promise(my_promise)` and watch react-native-toast work its magic, automatically updating the toast with a custom message on success -- or an error message on reject.
2222
- Runs on **web**
23+
- Support for native modals
24+
- Callbacks for onPress, onShow, and onHide
2325

2426
# Getting Started
2527

@@ -173,6 +175,49 @@ When a toast is pressed, this callback will fire, returning the toast object tha
173175
onToastPress?: (toast: T) => void;
174176
```
175177

178+
#### providerKey (`string`) _<font size = 2>(optional)</font>_
179+
Provide the Toasts component with a providerKey to conditionally render toasts in a component. Useful for rendering toasts in native modals.
180+
```js
181+
// Component in native modal
182+
<Toasts providerKey="MODAL::1" />
183+
184+
//...
185+
// Root component
186+
<Toasts /> //has default providerKey of DEFAULT
187+
188+
//...
189+
// Call toast in root modal
190+
191+
const id = toast("Hello from root modal") //default providerKey of DEFAULT
192+
193+
// Native modal becomes visible
194+
const id = toast("Hello from native modal", {providerKey: "MODAL::1"})
195+
//Now, toast is shown only in native modal
196+
```
197+
198+
If you want to persist toasts across components (i.e. when you open/close a modal and want to keep the same toasts), your toasts should be assigned a providerKey of "PERSISTS".
199+
200+
```js
201+
toast("Message...", {providerKey: "PERSISTS"})
202+
```
203+
204+
Or, if you cannot do so, you can update each toast manually.
205+
206+
```js
207+
const { toasts } = useToasterStore(); //Note, no provider key passed in
208+
209+
useEffect(() => {
210+
toasts.forEach((t) => {
211+
toast(t.message, {
212+
...t,
213+
providerKey: isModalVisible ? 'MODAL::1' : 'DEFAULT', //Switch provider key here
214+
});
215+
});
216+
}, [isModalVisible]);
217+
```
218+
219+
220+
176221
### Example
177222
```
178223
<Toasts
@@ -449,4 +494,3 @@ Made with [create-react-native-library](https://github.com/callstack/react-nativ
449494
- Add unit tests for Components and hooks
450495
- Allow theming in `<Toasts />`
451496
- Queue manager
452-
- Explore native modal fixes

example/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"react-dom": "18.2.0",
1919
"react-native": "0.71.8",
2020
"react-native-gesture-handler": "~2.9.0",
21+
"react-native-modal": "^13.0.1",
2122
"react-native-reanimated": "~2.14.4",
2223
"react-native-safe-area-context": "4.5.0",
2324
"react-native-web": "~0.18.11",

example/src/App.tsx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as React from 'react';
2-
import { FunctionComponent, useState } from 'react';
2+
import { FunctionComponent, useCallback, useState } from 'react';
33
import { SafeAreaProvider } from 'react-native-safe-area-context';
44
import {
5+
Button,
56
Pressable,
67
ScrollView,
78
Switch,
@@ -11,6 +12,7 @@ import {
1112
useWindowDimensions,
1213
View,
1314
} from 'react-native';
15+
import Modal from 'react-native-modal';
1416

1517
import { Toasts } from '../../src/components/Toasts';
1618
import { toast } from '../../src/headless';
@@ -21,6 +23,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
2123
export default function App() {
2224
const { width: screenWidth } = useWindowDimensions();
2325
const isSystemDarkMode = useColorScheme() === 'dark';
26+
const [isModalVisible, setModalVisible] = useState<boolean>(false);
2427

2528
const [isDarkMode, setIsUserDarkMode] = useState(isSystemDarkMode);
2629
const [position, setPosition] = useState(ToastPosition.TOP);
@@ -30,6 +33,10 @@ export default function App() {
3033
);
3134
const [duration, setDuration] = useState(4000);
3235

36+
const toggleModal = useCallback(() => {
37+
setModalVisible(!isModalVisible);
38+
}, [isModalVisible, setModalVisible]);
39+
3340
return (
3441
<SafeAreaProvider>
3542
<GestureHandlerRootView style={{ flex: 1 }}>
@@ -92,6 +99,7 @@ export default function App() {
9299
duration,
93100
height,
94101
width,
102+
providerKey: 'PERSISTS',
95103
});
96104
}}
97105
>
@@ -175,7 +183,57 @@ export default function App() {
175183
Promise Toast
176184
</Text>
177185
</Pressable>
186+
<Button title={'Toggle Modal'} onPress={toggleModal} />
178187
</ScrollView>
188+
<Modal style={{ padding: 0, margin: 0 }} isVisible={isModalVisible}>
189+
<View
190+
style={{
191+
flex: 1,
192+
alignItems: 'center',
193+
justifyContent: 'center',
194+
backgroundColor: !isDarkMode ? colors.textDark : colors.textLight,
195+
}}
196+
>
197+
<Button title={'Toggle Modal'} onPress={toggleModal} />
198+
199+
<Pressable
200+
style={{ marginTop: 64 }}
201+
onPress={() => {
202+
toast(Math.floor(Math.random() * 1000).toString(), {
203+
position,
204+
duration,
205+
height,
206+
width,
207+
providerKey: 'PERSISTS',
208+
});
209+
}}
210+
>
211+
<Text
212+
style={{
213+
fontSize: 16,
214+
fontWeight: 'bold',
215+
color: isDarkMode ? colors.textDark : colors.textLight,
216+
}}
217+
>
218+
Normal Toast
219+
</Text>
220+
</Pressable>
221+
222+
<Toasts
223+
providerKey={'MODAL::1'}
224+
onToastShow={(t) => {
225+
console.log('SHOW: ', t);
226+
}}
227+
onToastHide={(t) => {
228+
console.log('HIDE: ', t);
229+
}}
230+
onToastPress={(t) => {
231+
console.log('PRESS: ', t);
232+
}}
233+
overrideDarkMode={isDarkMode}
234+
/>
235+
</View>
236+
</Modal>
179237
<Toasts
180238
onToastShow={(t) => {
181239
console.log('SHOW: ', t);

example/yarn.lock

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7280,7 +7280,7 @@ prompts@^2.3.2, prompts@^2.4.0:
72807280
kleur "^3.0.3"
72817281
sisteransi "^1.0.5"
72827282

7283-
prop-types@*, prop-types@^15.7.2:
7283+
prop-types@*, prop-types@^15.6.2, prop-types@^15.7.2:
72847284
version "15.8.1"
72857285
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
72867286
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -7405,6 +7405,13 @@ react-is@^17.0.1:
74057405
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
74067406
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
74077407

7408+
7409+
version "1.3.3"
7410+
resolved "https://registry.yarnpkg.com/react-native-animatable/-/react-native-animatable-1.3.3.tgz#a13a4af8258e3bb14d0a9d839917e9bb9274ec8a"
7411+
integrity sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==
7412+
dependencies:
7413+
prop-types "^15.7.2"
7414+
74087415
react-native-codegen@^0.71.5:
74097416
version "0.71.5"
74107417
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.71.5.tgz#454a42a891cd4ca5fc436440d301044dc1349c14"
@@ -7431,6 +7438,14 @@ react-native-gradle-plugin@^0.71.18:
74317438
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.19.tgz#3379e28341fcd189bc1f4691cefc84c1a4d7d232"
74327439
integrity sha512-1dVk9NwhoyKHCSxcrM6vY6cxmojeATsBobDicX0ZKr7DgUF2cBQRTKsimQFvzH8XhOVXyH8p4HyDSZNIFI8OlQ==
74337440

7441+
react-native-modal@^13.0.1:
7442+
version "13.0.1"
7443+
resolved "https://registry.yarnpkg.com/react-native-modal/-/react-native-modal-13.0.1.tgz#691f1e646abb96fa82c1788bf18a16d585da37cd"
7444+
integrity sha512-UB+mjmUtf+miaG/sDhOikRfBOv0gJdBU2ZE1HtFWp6UixW9jCk/bhGdHUgmZljbPpp0RaO/6YiMmQSSK3kkMaw==
7445+
dependencies:
7446+
prop-types "^15.6.2"
7447+
react-native-animatable "1.3.3"
7448+
74347449
react-native-reanimated@~2.14.4:
74357450
version "2.14.4"
74367451
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.14.4.tgz#3fa3da4e7b99f5dfb28f86bcf24d9d1024d38836"

src/components/Toast.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,7 @@ export const Toast: FC<Props> = ({
241241
style={[
242242
{
243243
height: toastHeight,
244-
backgroundColor: isDarkMode
245-
? colors.backgroundDark
246-
: colors.backgroundLight,
247244
width: toastWidth,
248-
borderRadius: 8,
249245
flexDirection: 'row',
250246
alignItems: 'center',
251247
paddingVertical: 12,

src/components/Toasts.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Props = {
1616
onToastShow?: (toast: T) => void;
1717
onToastHide?: (toast: T) => void;
1818
onToastPress?: (toast: T) => void;
19+
providerKey?: string;
1920
};
2021

2122
export const Toasts: FunctionComponent<Props> = ({
@@ -24,8 +25,9 @@ export const Toasts: FunctionComponent<Props> = ({
2425
onToastHide,
2526
onToastPress,
2627
onToastShow,
28+
providerKey = 'DEFAULT',
2729
}) => {
28-
const { toasts, handlers } = useToaster();
30+
const { toasts, handlers } = useToaster({ providerKey });
2931
const { startPause, endPause } = handlers;
3032
const insets = useSafeAreaInsets();
3133

src/core/store.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -187,19 +187,26 @@ export const useStore = (toastOptions: DefaultToastOptions = {}): State => {
187187
};
188188
}, [state]);
189189

190-
const mergedToasts = state.toasts.map((t) => ({
191-
...toastOptions,
192-
...toastOptions[t.type],
193-
...t,
194-
duration:
195-
t.duration ||
196-
toastOptions[t.type]?.duration ||
197-
toastOptions?.duration ||
198-
defaultTimeouts[t.type],
199-
styles: {
200-
...toastOptions.styles,
201-
},
202-
}));
190+
const mergedToasts = state.toasts
191+
.filter(
192+
(t) =>
193+
toastOptions?.providerKey === undefined ||
194+
t.providerKey === toastOptions?.providerKey ||
195+
t.providerKey === 'PERSISTS'
196+
)
197+
.map((t) => ({
198+
...toastOptions,
199+
...toastOptions[t.type],
200+
...t,
201+
duration:
202+
t.duration ||
203+
toastOptions[t.type]?.duration ||
204+
toastOptions?.duration ||
205+
defaultTimeouts[t.type],
206+
styles: {
207+
...toastOptions.styles,
208+
},
209+
}));
203210

204211
return {
205212
...state,

src/core/toast.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const createToast = (
2626
message,
2727
pauseDuration: 0,
2828
position: ToastPosition.TOP,
29+
providerKey: 'DEFAULT',
2930
...opts,
3031
id: opts?.id || genId(),
3132
});

src/core/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export interface Toast {
4848
indicator?: ViewStyle;
4949
};
5050
customToast?: (toast: Toast) => JSX.Element;
51+
providerKey: string;
5152
}
5253

5354
export type ToastOptions = Partial<
@@ -62,6 +63,7 @@ export type ToastOptions = Partial<
6263
| 'width'
6364
| 'customToast'
6465
| 'disableShadow'
66+
| 'providerKey'
6567
>
6668
>;
6769

0 commit comments

Comments
 (0)