mirror of https://github.com/coder/coder.git
114 lines
2.8 KiB
TypeScript
114 lines
2.8 KiB
TypeScript
import type { Interpolation, Theme } from "@emotion/react";
|
|
import { type FC, useState } from "react";
|
|
import { useCustomEvent } from "hooks/events";
|
|
import { ErrorIcon } from "../Icons/ErrorIcon";
|
|
import { EnterpriseSnackbar } from "./EnterpriseSnackbar";
|
|
import {
|
|
type AdditionalMessage,
|
|
isNotificationList,
|
|
isNotificationText,
|
|
isNotificationTextPrefixed,
|
|
MsgType,
|
|
type NotificationMsg,
|
|
SnackbarEventType,
|
|
} from "./utils";
|
|
|
|
const variantFromMsgType = (type: MsgType) => {
|
|
if (type === MsgType.Error) {
|
|
return "error";
|
|
} else if (type === MsgType.Success) {
|
|
return "success";
|
|
} else {
|
|
return "info";
|
|
}
|
|
};
|
|
|
|
export const GlobalSnackbar: FC = () => {
|
|
const [notificationMsg, setNotificationMsg] = useState<NotificationMsg>();
|
|
useCustomEvent<NotificationMsg>(SnackbarEventType, (event) => {
|
|
setNotificationMsg(event.detail);
|
|
});
|
|
|
|
const hasNotification = notificationMsg !== undefined;
|
|
if (!hasNotification) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<EnterpriseSnackbar
|
|
key={notificationMsg.msg}
|
|
open={hasNotification}
|
|
variant={variantFromMsgType(notificationMsg.msgType)}
|
|
onClose={() => setNotificationMsg(undefined)}
|
|
autoHideDuration={
|
|
notificationMsg.msgType === MsgType.Error ? 22000 : 6000
|
|
}
|
|
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
|
message={
|
|
<div css={{ display: "flex" }}>
|
|
{notificationMsg.msgType === MsgType.Error && (
|
|
<ErrorIcon css={styles.errorIcon} />
|
|
)}
|
|
|
|
<div css={{ maxWidth: 670 }}>
|
|
<span css={styles.messageTitle}>{notificationMsg.msg}</span>
|
|
|
|
{notificationMsg.additionalMsgs &&
|
|
notificationMsg.additionalMsgs.map((msg, index) => (
|
|
<AdditionalMessageDisplay key={index} message={msg} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
}
|
|
/>
|
|
);
|
|
};
|
|
|
|
interface AdditionalMessageDisplayProps {
|
|
message: AdditionalMessage;
|
|
}
|
|
|
|
const AdditionalMessageDisplay: FC<AdditionalMessageDisplayProps> = ({
|
|
message,
|
|
}) => {
|
|
if (isNotificationText(message)) {
|
|
return <span css={styles.messageSubtitle}>{message}</span>;
|
|
}
|
|
|
|
if (isNotificationTextPrefixed(message)) {
|
|
return (
|
|
<span css={styles.messageSubtitle}>
|
|
<strong>{message.prefix}:</strong> {message.text}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
if (isNotificationList(message)) {
|
|
return (
|
|
<ul css={{ paddingLeft: 0 }}>
|
|
{message.map((item, idx) => (
|
|
<li key={idx}>
|
|
<span css={styles.messageSubtitle}>{item}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const styles = {
|
|
messageTitle: {
|
|
fontSize: 14,
|
|
fontWeight: 600,
|
|
},
|
|
messageSubtitle: {
|
|
marginTop: 12,
|
|
},
|
|
errorIcon: (theme) => ({
|
|
color: theme.palette.error.contrastText,
|
|
marginRight: 16,
|
|
}),
|
|
} satisfies Record<string, Interpolation<Theme>>;
|