Skip to content

Instantly share code, notes, and snippets.

@painedpineapple
Last active March 5, 2020 23:18
Show Gist options
  • Save painedpineapple/c57c3776f4eca315baef07b01f184114 to your computer and use it in GitHub Desktop.
Save painedpineapple/c57c3776f4eca315baef07b01f184114 to your computer and use it in GitHub Desktop.
/**
* This is deprecated. I recommend using https://gist.github.com/thislogancall/8d13eac0cc9a9435921817be071650a4
*/
open Utils;
[%raw {|
require("react-notifications-component/dist/theme.css")
|}];
type transition = {
duration: int,
timingFunction: string,
delay: int,
};
type touchSlidingExitTransition = {
swipe: transition,
fade: transition,
};
type dismiss;
[@bs.obj]
external dismiss:
(
~duration: int=?,
~pauseOnHover: bool=?,
~onScreen: bool=?,
~waitForAnimation: bool=?,
~click: bool=?,
~touch: bool=?,
~showIcon: bool=?,
unit
) =>
dismiss =
"";
type notification;
[@bs.obj]
external notification:
(
~title: string,
~message: React.element,
~_type: [@bs.string] [ | `success | `danger | `info | `default | `warning],
~insert: [@bs.string] [ | `top | `bottom]=?,
~container: [@bs.string] [
| [@bs.as "top-left"] `topLeft
| [@bs.as "top-right"] `topRight
| [@bs.as "top-center"] `topCenter
| [@bs.as "bottom-left"] `bottomLeft
| [@bs.as "bottom-right"] `bottomRight
| [@bs.as "bottom-center"] `bottomCenter
],
~dismiss: dismiss=?,
~animationIn: array(string)=?,
~animationOut: array(string)=?,
~slidingEnter: transition=?,
~slidingExit: transition=?,
~touchRevert: transition=?,
~touchSlidingExit: touchSlidingExitTransition=?,
~width: int=?,
unit
) =>
notification =
"";
type contentProps = {id: string};
[@bs.obj]
external customNotification:
(
~content: (~props: contentProps) => React.element=?,
~insert: [@bs.string] [ | `top | `bottom]=?,
~container: [@bs.string] [
| [@bs.as "top-left"] `topLeft
| [@bs.as "top-right"] `topRight
| [@bs.as "top-center"] `topCenter
| [@bs.as "bottom-left"] `bottomLeft
| [@bs.as "bottom-right"] `bottomRight
| [@bs.as "bottom-center"] `bottomCenter
],
~dismiss: dismiss=?,
~animationIn: array(string)=?,
~animationOut: array(string)=?,
~slidingEnter: transition=?,
~slidingExit: transition=?,
~touchRevert: transition=?,
~touchSlidingExit: touchSlidingExitTransition=?,
~width: int=?,
unit
) =>
notification =
"";
[@bs.module "react-notifications-component"] [@react.component]
external make:
(
~isMobile: bool=?,
~breakpoint: int=?,
~types: array('t)=?,
~className: string=?,
~id: string=?
) =>
React.element =
"default";
type store;
[@bs.module "react-notifications-component"] external store: store = "store";
[@bs.module "react-notifications-component"] [@bs.scope "store"]
external add: notification => string = "addNotification";
[@bs.module "react-notifications-component"] [@bs.scope "store"]
external remove: string => unit = "removeNotification";
module Style = {
open Css;
let root =
style([
selector(".notification-item", [boxShadow(`none)]),
selector(
".notification-loader",
[
backgroundColor(`transparent)->important,
selector(
".notification-content",
[padding(0->px), height(64->px), width(64->px)],
),
],
),
selector(
".notification-item > div:not(.notification-loader)",
[
boxShadow(
Shadow.box(~x=1->px, ~y=3->px, ~blur=4->px, rgba(0, 0, 0, 0.2)),
),
],
),
selector(
".notification-content .notification-title",
[
fontWeight(`bold),
textTransform(`uppercase),
marginTop(0->rem_of_px),
marginBottom(10->rem_of_px),
],
),
selector(
".notification-success",
[
borderLeftColor(Color.green3),
selector(".timer-filler", [backgroundColor(Color.green3)]),
selector(
"&, .notification-close, .timer",
[backgroundColor(Color.greenLight)],
),
selector(
"&, .notification-title, .notification-message, .notification-close::after",
[color(Color.green3)],
),
],
),
selector(
".notification-danger",
[
borderLeftColor(Color.red),
selector(".timer-filler", [backgroundColor(Color.red)]),
selector(
"&, .notification-close, .timer",
[backgroundColor(Color.redLight)],
),
selector(
"&, .notification-title, .notification-message, .notification-close::after",
[color(Color.red)],
),
],
),
selector(
".notification-info, .notification-loader",
[
borderLeftColor(Color.blue2),
selector(".timer-filler", [backgroundColor(Color.blue)]),
selector(
"&, .notification-close, .timer",
[backgroundColor(Color.blueLight)],
),
selector(
"&, .notification-title, .notification-message, .notification-close::after",
[color(Color.blue)],
),
],
),
selector(
".notification-default",
[
borderLeftColor(Color.gray2),
selector(
"&, .notification-close, .timer",
[backgroundColor(Color.gray)],
),
],
),
selector(
".notification-warning",
[
borderLeftColor(Color.black2),
selector(".timer-filler", [backgroundColor(Color.black2)]),
selector(
"&, .notification-close, .timer",
[backgroundColor(Color.yellowLight)],
),
selector(
"&, .notification-title, .notification-message, .notification-close::after",
[color(Color.black2)],
),
],
),
]);
let loaderContainer =
style([
`flex->display,
alignItems(`center),
paddingBottom(15->px),
// transforms([translateX((-43)->px)]),
selector(
".inner-container",
[
backgroundColor(Color.blue)->important,
position(`relative),
margin(0->px)->important,
// bottom((-116)->px),
transforms([scale(0.75, 0.75)]),
],
),
]);
};
let duration = 5000;
module Notification = {
type _type = [ | `success | `danger | `info | `default | `warning | `loader];
/**If a delay is provided then that notification will not start showing until the delay is over. This is useful if you have a `loading notification that you want to delay or _not_ show (it won't show if there is a delay and then the `.make` method is called again on the) */
[@react.component]
let make =
(
~message,
~title,
~_type: _type,
~id="",
~duration=duration,
~handleRemove as customRemove=?,
~showTimer=true,
~delay=0,
) => {
let (playState, setPlayState) = React.useState(() => `running);
let (isReady, setIsReady) = React.useState(() => false);
let (elHeight, setElHeight) = React.useState(() => 0);
let heightRef = React.useRef(Js.Nullable.null);
let timestamp =
React.useMemo2(
() => {
let date = Js.Date.make();
date->Js.Date.setMilliseconds(
date->Js.Date.getMilliseconds
+. duration->float_of_int
+. delay->float_of_int,
);
},
(duration, delay),
);
let handleRemove =
React.useCallback2(
() => {
customRemove->(
fun
| Some(customRemove) => customRemove()
| None => id->remove
)
},
(customRemove, id),
);
let timer =
ReactTimerHook.useTimer({
expiryTimestamp: timestamp,
onExpire: handleRemove,
});
React.useEffect5(
() => {
let timeoutId =
Js.Global.setTimeout(
() => {
timer.start();
heightRef
->React.Ref.current
->Js.Nullable.toOption
->(
fun
| Some(el) =>
setElHeight(_ => el->Document.domElToDomEl_##offsetHeight)
| None => ()
);
setIsReady(_ => true);
},
delay,
);
Some(
() => {
timeoutId->Js.Global.clearTimeout;
id->remove;
},
);
},
(delay, heightRef, setIsReady, setElHeight, id),
);
<div
className=Css.(
merge([
style([100.->pct->width, elHeight->Utils.rem_of_px->height]),
style(isReady ? [] : [opacity(0.)]),
"notification-"
++ (
switch (_type) {
| `success => "success"
| `danger => "danger"
| `loader => "loader"
| `info => "info"
| `default => "default"
| `warning => "warning"
}
),
])
)
onMouseEnter={_ =>
showTimer
? {
timer.pause();
setPlayState(_ => `paused);
}
: ()
}
onMouseLeave={_ =>
showTimer
? {
timer.resume();
setPlayState(_ => `running);
}
: ()
}>
<div
className="notification-content"
ref={heightRef->ReactDOMRe.Ref.domRef}>
{switch (_type) {
| `loader => message
| _ =>
<>
<div
className="notification-close"
onClick={_ => handleRemove()}
/>
<div className="notification-title"> title </div>
<div className="notification-message"> <div> message </div> </div>
{showTimer
? <div className="timer">
<div
className=Css.(
merge([
"timer-filler",
style([
/**Animation already exists within "react-notifications-component/dist/theme.css" */
unsafe("animationName", "timer"),
animationDuration(duration),
animationTimingFunction(`linear),
animationFillMode(`forwards),
animationPlayState(playState),
]),
])
)
/>
</div>
: React.null}
</>
}}
</div>
</div>;
};
};
let transition = {duration: 300, timingFunction: "ease-in-out", delay: 0};
let dismiss =
dismiss(
~duration,
~onScreen=true,
~pauseOnHover=true,
~click=false,
~touch=false,
~showIcon=false,
(),
);
type notificationHookType = [
| `success
| `error
| `info
| `loader
| `warning
];
type notificationHook = {
id: string,
make: (~content: React.element=?, notificationHookType) => unit,
remove: unit => unit,
isActive: bool,
};
let useNotification = (~useLogout, ()) => {
let logout = useLogout();
let (id, setId) = React.useState(() => "");
let previousId = Hooks.usePrevious(id);
React.useEffect2(
() => {
if (previousId != "" && previousId != id) {
previousId->remove;
};
None;
},
(id, previousId),
);
let remove = () => {
id->remove;
setId(_ => "");
};
let make = (~content=React.null, notificationHookType) => {
let newId =
customNotification(
~container=
switch (notificationHookType) {
| `loader => `bottomLeft
| _ => `bottomRight
},
~slidingEnter=transition,
~slidingExit=transition,
~dismiss,
~animationIn=[|"animated", "fadeIn"|],
~animationOut=[|"animated", "fadeOut"|],
~content=
(~props) =>
<Notification
id={props.id}
handleRemove=remove
delay={
switch (notificationHookType) {
| `loader => 1000
| _ => 0
}
}
message={
switch (notificationHookType) {
| `error =>
<div>
<div> content </div>
<div>
{str("Our development team has been notified.")}
<ButtonGroup
className=Css.(
style([marginTop(px(10)), marginBottom(px(10))])
)
renderLeft={<SentryReportForm message="Send Report" />}
renderRight={
<Button
color=`red
horz=`sm
vert=`sm
onClick={_ => logout()}>
{str("Restart")}
</Button>
}
/>
</div>
</div>
| `info => <div> content </div>
| `success => <div> content </div>
| `loader =>
<Loaders.Spinner
color=Color.white
className=Style.loaderContainer
/>
| `warning => <div> content </div>
}
}
title={
(
switch (notificationHookType) {
| `error => "Error"
| `info => "Information"
| `success => "Success"
| `loader => "Loading"
| `warning => "Warning"
}
)
->str
}
_type={
switch (notificationHookType) {
| `error => `danger
| `info => `info
| `success => `success
| `loader => `loader
| `warning => `warning
}
}
/>,
(),
)
->add;
setId(_ => newId);
};
// let make = (~content as _c=React.null, _stuff) => Js.log("Make..");
React.useMemo1(() => {make, remove, id, isActive: id != ""}, [|id|]);
};
/** EXAMPLE USAGE:
* DO NOT use an instance of useNotification as a hook dependency because it is likely rerender each time. It does this b/c each time you call the `make` method the notification `id` changes and thus the instance changes.
*
let submitNotification = AppStoreHooks.useNotification();
let handleSubmit = () => {
submitNotification.make(`loader);
data->someAsyncRequest->(
fun
| Error(message) =>
submitNotification.make(
"Sorry, there was a problem updating your project"->str,
`error,
);
| Ok(updatedProject) =>
submitNotification.make(
"Your project was successfully updated!"->str,
`success,
)
}
*/;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment