react-native-toast-hybrid
Version:
A toast that can be used for react-native, while available for native android, ios.
172 lines (143 loc) • 3.88 kB
text/typescript
import { useRef, useEffect } from 'react';
import { BackHandler } from 'react-native';
import ToastHybrid from './NativeToast';
export interface ToastConfig {
backgroundColor?: string
tintColor?: string
fontSize?: number
cornerRadius?: number
duration?: number
graceTime?: number
minShowTime?: number
loadingText?: string
}
let defaultDuration = 2000;
export default class Toast {
private static instances = new Set<Toast>();
static config(options: ToastConfig = {}) {
defaultDuration = options.duration || defaultDuration;
ToastHybrid.config(options);
}
static text(text: string, duration = defaultDuration) {
new Toast().text(text, duration);
}
static info(text: string, duration = defaultDuration) {
new Toast().info(text, duration);
}
static done(text: string, duration = defaultDuration) {
new Toast().done(text, duration);
}
static error(text: string, duration = defaultDuration) {
new Toast().error(text, duration);
}
static loading(text?: string) {
return new Toast().loading(text);
}
static hideAll() {
Array.from(Toast.instances).forEach(t => t.hide());
}
private underlying: Promise<number> | null = null;
constructor() {
Toast.instances.add(this);
}
private ensure(): Promise<number> {
if (this.underlying !== null) {
return this.underlying;
}
const underlying = ToastHybrid.createToast();
return underlying;
}
private closed = false;
private timer: number | null = null;
loading(text?: string) {
if (!this.closed) {
this.clearTimeout();
this.underlying = this.ensure();
this.underlying.then(key => {
this.clearTimeout();
ToastHybrid.loading(key, text);
});
}
return this;
}
private clearTimeout() {
if (this.timer !== null) {
clearTimeout(this.timer);
this.timer = null;
}
}
text(text: string, duration = defaultDuration) {
return this.show(ToastHybrid.text, text, duration);
}
private show(fn: (key: number, text: string) => void, text: string, duration: number) {
if (duration === 0) {
if (this.underlying === null) {
return this;
}
this.hide();
return this;
}
if (!this.closed) {
this.clearTimeout();
this.underlying = this.ensure();
this.underlying.then(key => {
if (!this.closed) {
fn(key, text);
this.clearTimeout();
this.timer = <any>setTimeout(() => this.hide(), duration);
} else {
this.hide();
}
});
}
return this;
}
info(text: string, duration = defaultDuration) {
return this.show(ToastHybrid.info, text, duration);
}
done(text: string, duration = defaultDuration) {
return this.show(ToastHybrid.done, text, duration);
}
error(text: string, duration = defaultDuration) {
return this.show(ToastHybrid.error, text, duration);
}
hide() {
Toast.instances.delete(this);
this.clearTimeout();
if (this.underlying !== null) {
this.underlying.then(key => {
ToastHybrid.hide(key);
});
this.underlying = null;
}
}
private shutdown() {
this.closed = true;
this.hide();
}
}
export function useToast() {
const toastRef = useRef(new Toast());
useEffect(() => {
const toast = toastRef.current
;(toast as any).closed = false;
return () => {
(toast as any).shutdown();
};
}, []);
useEffect(() => {
function handleHardwareBack() {
const toast = toastRef.current;
if ((toast as any).underlying !== null) {
toast.hide();
return true;
}
return false;
}
const subscription = BackHandler.addEventListener('hardwareBackPress', handleHardwareBack);
return () => {
subscription.remove();
};
}, []);
return toastRef.current;
}