Add app wide snackbar

This commit is contained in:
Ken Hibino 2020-12-10 07:06:54 -08:00
parent ce978f4516
commit d8c549adb2
4 changed files with 131 additions and 2 deletions

View File

@ -1,4 +1,5 @@
import React, { useState } from "react";
import { connect, ConnectedProps } from "react-redux";
import clsx from "clsx";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import { makeStyles } from "@material-ui/core/styles";
@ -7,12 +8,19 @@ import Drawer from "@material-ui/core/Drawer";
import Toolbar from "@material-ui/core/Toolbar";
import List from "@material-ui/core/List";
import Typography from "@material-ui/core/Typography";
import Snackbar from "@material-ui/core/Snackbar";
import SnackbarContent from "@material-ui/core/SnackbarContent";
import IconButton from "@material-ui/core/IconButton";
import Slide from "@material-ui/core/Slide";
import { TransitionProps } from "@material-ui/core/transitions";
import MenuIcon from "@material-ui/icons/Menu";
import BarChartIcon from "@material-ui/icons/BarChart";
import LayersIcon from "@material-ui/icons/Layers";
import SettingsIcon from "@material-ui/icons/Settings";
import CloseIcon from "@material-ui/icons/Close";
import { AppState } from "./store";
import { paths } from "./paths";
import { closeSnackbar } from "./actions/snackbarActions";
import ListItemLink from "./components/ListItemLink";
import SchedulersView from "./views/SchedulersView";
import DashboardView from "./views/DashboardView";
@ -71,6 +79,13 @@ const useStyles = makeStyles((theme) => ({
width: theme.spacing(9),
},
},
snackbar: {
background: theme.palette.grey["A400"],
color: "#ffffff",
},
snackbarCloseIcon: {
color: theme.palette.grey[400],
},
appBarSpacer: theme.mixins.toolbar,
mainContainer: {
display: "flex",
@ -96,7 +111,21 @@ const useStyles = makeStyles((theme) => ({
},
}));
function App() {
function mapStateToProps(state: AppState) {
return { snackbar: state.snackbar };
}
const mapDispatchToProps = {
closeSnackbar,
};
const connector = connect(mapStateToProps, mapDispatchToProps);
function SlideUpTransition(props: TransitionProps) {
return <Slide {...props} direction="up" />;
}
function App(props: ConnectedProps<typeof connector>) {
const classes = useStyles();
const [open, setOpen] = useState(true);
const toggleDrawer = () => {
@ -142,6 +171,31 @@ function App() {
}}
open={open}
>
<Snackbar
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
open={props.snackbar.isOpen}
autoHideDuration={6000}
onClose={props.closeSnackbar}
TransitionComponent={SlideUpTransition}
>
<SnackbarContent
message={props.snackbar.message}
className={classes.snackbar}
action={
<IconButton
size="small"
aria-label="close"
color="inherit"
onClick={props.closeSnackbar}
>
<CloseIcon
className={classes.snackbarCloseIcon}
fontSize="small"
/>
</IconButton>
}
/>
</Snackbar>
<div className={classes.appBarSpacer} />
<div className={classes.sidebarContainer}>
<List>
@ -191,4 +245,4 @@ function App() {
);
}
export default App;
export default connector(App);

View File

@ -0,0 +1,12 @@
export const CLOSE_SNACKBAR = "CLOSE_SNACKBAR";
interface CloseSnakbarAction {
type: typeof CLOSE_SNACKBAR;
}
// Union of all snackbar related action types
export type SnackbarActionTypes = CloseSnakbarAction;
export function closeSnackbar() {
return { type: CLOSE_SNACKBAR };
}

View File

@ -0,0 +1,61 @@
import {
CLOSE_SNACKBAR,
SnackbarActionTypes,
} from "../actions/snackbarActions";
import {
DELETE_DEAD_TASK_SUCCESS,
DELETE_RETRY_TASK_SUCCESS,
DELETE_SCHEDULED_TASK_SUCCESS,
TasksActionTypes,
} from "../actions/tasksActions";
interface SnackbarState {
isOpen: boolean;
message: string;
}
const initialState: SnackbarState = {
isOpen: false,
message: "",
};
function snackbarReducer(
state = initialState,
action: TasksActionTypes | SnackbarActionTypes
): SnackbarState {
switch (action.type) {
case CLOSE_SNACKBAR:
return {
// Note: We keep the message state unchanged for
// smoother transition animation.
...state,
isOpen: false,
};
case DELETE_SCHEDULED_TASK_SUCCESS:
return {
isOpen: true,
// TODO: show only task id
message: `Scheduled task ${action.taskKey} deleted`,
};
case DELETE_RETRY_TASK_SUCCESS:
return {
isOpen: true,
// TODO: show only task id
message: `Retry task ${action.taskKey} deleted`,
};
case DELETE_DEAD_TASK_SUCCESS:
return {
isOpen: true,
// TODO: show only task id
message: `Dead task ${action.taskKey} deleted`,
};
default:
return state;
}
}
export default snackbarReducer;

View File

@ -3,12 +3,14 @@ import settingsReducer from "./reducers/settingsReducer";
import queuesReducer from "./reducers/queuesReducer";
import tasksReducer from "./reducers/tasksReducer";
import schedulerEntriesReducer from "./reducers/schedulerEntriesReducer";
import snackbarReducer from "./reducers/snackbarReducer";
const rootReducer = combineReducers({
settings: settingsReducer,
queues: queuesReducer,
tasks: tasksReducer,
schedulerEntries: schedulerEntriesReducer,
snackbar: snackbarReducer,
});
// AppState is the top-level application state maintained by redux store.