ReserveDialogs.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import * as React from 'react';
  2. import {useState} from 'react';
  3. import Button from '@mui/material/Button';
  4. import TextField from '@mui/material/TextField';
  5. import Dialog from '@mui/material/Dialog';
  6. import DialogContent from '@mui/material/DialogContent';
  7. import DialogContentText from '@mui/material/DialogContentText';
  8. import DialogTitle from '@mui/material/DialogTitle';
  9. import {Alert, FormControl, Select, useMediaQuery} from "@mui/material";
  10. import theme from "./theme";
  11. import {validTopic} from "../app/utils";
  12. import DialogFooter from "./DialogFooter";
  13. import {useTranslation} from "react-i18next";
  14. import session from "../app/Session";
  15. import routes from "./routes";
  16. import accountApi, {Permission} from "../app/AccountApi";
  17. import ReserveTopicSelect from "./ReserveTopicSelect";
  18. import MenuItem from "@mui/material/MenuItem";
  19. import ListItemIcon from "@mui/material/ListItemIcon";
  20. import ListItemText from "@mui/material/ListItemText";
  21. import {Check, DeleteForever} from "@mui/icons-material";
  22. import {TopicReservedError, UnauthorizedError} from "../app/errors";
  23. export const ReserveAddDialog = (props) => {
  24. const { t } = useTranslation();
  25. const [error, setError] = useState("");
  26. const [topic, setTopic] = useState(props.topic || "");
  27. const [everyone, setEveryone] = useState(Permission.DENY_ALL);
  28. const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
  29. const allowTopicEdit = !props.topic;
  30. const alreadyReserved = props.reservations.filter(r => r.topic === topic).length > 0;
  31. const submitButtonEnabled = validTopic(topic) && !alreadyReserved;
  32. const handleSubmit = async () => {
  33. try {
  34. await accountApi.upsertReservation(topic, everyone);
  35. console.debug(`[ReserveAddDialog] Added reservation for topic ${t}: ${everyone}`);
  36. } catch (e) {
  37. console.log(`[ReserveAddDialog] Error adding topic reservation.`, e);
  38. if (e instanceof UnauthorizedError) {
  39. session.resetAndRedirect(routes.login);
  40. } else if (e instanceof TopicReservedError) {
  41. setError(t("subscribe_dialog_error_topic_already_reserved"));
  42. return;
  43. } else {
  44. setError(e.message);
  45. return;
  46. }
  47. }
  48. props.onClose();
  49. };
  50. return (
  51. <Dialog open={props.open} onClose={props.onClose} maxWidth="sm" fullWidth fullScreen={fullScreen}>
  52. <DialogTitle>{t("prefs_reservations_dialog_title_add")}</DialogTitle>
  53. <DialogContent>
  54. <DialogContentText>
  55. {t("prefs_reservations_dialog_description")}
  56. </DialogContentText>
  57. {allowTopicEdit && <TextField
  58. autoFocus
  59. margin="dense"
  60. id="topic"
  61. label={t("prefs_reservations_dialog_topic_label")}
  62. aria-label={t("prefs_reservations_dialog_topic_label")}
  63. value={topic}
  64. onChange={ev => setTopic(ev.target.value)}
  65. type="url"
  66. fullWidth
  67. variant="standard"
  68. />}
  69. <ReserveTopicSelect
  70. value={everyone}
  71. onChange={setEveryone}
  72. sx={{mt: 1}}
  73. />
  74. </DialogContent>
  75. <DialogFooter status={error}>
  76. <Button onClick={props.onClose}>{t("common_cancel")}</Button>
  77. <Button onClick={handleSubmit} disabled={!submitButtonEnabled}>{t("common_add")}</Button>
  78. </DialogFooter>
  79. </Dialog>
  80. );
  81. };
  82. export const ReserveEditDialog = (props) => {
  83. const { t } = useTranslation();
  84. const [error, setError] = useState("");
  85. const [everyone, setEveryone] = useState(props.reservation?.everyone || Permission.DENY_ALL);
  86. const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
  87. const handleSubmit = async () => {
  88. try {
  89. await accountApi.upsertReservation(props.reservation.topic, everyone);
  90. console.debug(`[ReserveEditDialog] Updated reservation for topic ${t}: ${everyone}`);
  91. } catch (e) {
  92. console.log(`[ReserveEditDialog] Error updating topic reservation.`, e);
  93. if (e instanceof UnauthorizedError) {
  94. session.resetAndRedirect(routes.login);
  95. } else {
  96. setError(e.message);
  97. return;
  98. }
  99. }
  100. props.onClose();
  101. };
  102. return (
  103. <Dialog open={props.open} onClose={props.onClose} maxWidth="sm" fullWidth fullScreen={fullScreen}>
  104. <DialogTitle>{t("prefs_reservations_dialog_title_edit")}</DialogTitle>
  105. <DialogContent>
  106. <DialogContentText>
  107. {t("prefs_reservations_dialog_description")}
  108. </DialogContentText>
  109. <ReserveTopicSelect
  110. value={everyone}
  111. onChange={setEveryone}
  112. sx={{mt: 1}}
  113. />
  114. </DialogContent>
  115. <DialogFooter status={error}>
  116. <Button onClick={props.onClose}>{t("common_cancel")}</Button>
  117. <Button onClick={handleSubmit}>{t("common_save")}</Button>
  118. </DialogFooter>
  119. </Dialog>
  120. );
  121. };
  122. export const ReserveDeleteDialog = (props) => {
  123. const { t } = useTranslation();
  124. const [error, setError] = useState("");
  125. const [deleteMessages, setDeleteMessages] = useState(false);
  126. const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
  127. const handleSubmit = async () => {
  128. try {
  129. await accountApi.deleteReservation(props.topic, deleteMessages);
  130. console.debug(`[ReserveDeleteDialog] Deleted reservation for topic ${props.topic}`);
  131. } catch (e) {
  132. console.log(`[ReserveDeleteDialog] Error deleting topic reservation.`, e);
  133. if (e instanceof UnauthorizedError) {
  134. session.resetAndRedirect(routes.login);
  135. } else {
  136. setError(e.message);
  137. return;
  138. }
  139. }
  140. props.onClose();
  141. };
  142. return (
  143. <Dialog open={props.open} onClose={props.onClose} maxWidth="sm" fullWidth fullScreen={fullScreen}>
  144. <DialogTitle>{t("prefs_reservations_dialog_title_delete")}</DialogTitle>
  145. <DialogContent>
  146. <DialogContentText>
  147. {t("reservation_delete_dialog_description")}
  148. </DialogContentText>
  149. <FormControl fullWidth variant="standard">
  150. <Select
  151. value={deleteMessages}
  152. onChange={(ev) => setDeleteMessages(ev.target.value)}
  153. sx={{
  154. "& .MuiSelect-select": {
  155. display: 'flex',
  156. alignItems: 'center',
  157. paddingTop: "4px",
  158. paddingBottom: "4px",
  159. }
  160. }}
  161. >
  162. <MenuItem value={false}>
  163. <ListItemIcon><Check/></ListItemIcon>
  164. <ListItemText primary={t("reservation_delete_dialog_action_keep_title")}/>
  165. </MenuItem>
  166. <MenuItem value={true}>
  167. <ListItemIcon><DeleteForever/></ListItemIcon>
  168. <ListItemText primary={t("reservation_delete_dialog_action_delete_title")}/>
  169. </MenuItem>
  170. </Select>
  171. </FormControl>
  172. {!deleteMessages &&
  173. <Alert severity="info" sx={{ mt: 1 }}>
  174. {t("reservation_delete_dialog_action_keep_description")}
  175. </Alert>
  176. }
  177. {deleteMessages &&
  178. <Alert severity="warning" sx={{ mt: 1 }}>
  179. {t("reservation_delete_dialog_action_delete_description")}
  180. </Alert>
  181. }
  182. </DialogContent>
  183. <DialogFooter status={error}>
  184. <Button onClick={props.onClose}>{t("common_cancel")}</Button>
  185. <Button onClick={handleSubmit} color="error">{t("reservation_delete_dialog_submit_button")}</Button>
  186. </DialogFooter>
  187. </Dialog>
  188. );
  189. };