Message.tsx

  1import EditIcon from '@mui/icons-material/Edit';
  2import HistoryIcon from '@mui/icons-material/History';
  3import IconButton from '@mui/material/IconButton';
  4import Paper from '@mui/material/Paper';
  5import Tooltip from '@mui/material/Tooltip/Tooltip';
  6import makeStyles from '@mui/styles/makeStyles';
  7import * as React from 'react';
  8import { useState } from 'react';
  9
 10import Author, { Avatar } from 'src/components/Author';
 11import Content from 'src/components/Content';
 12import Date from 'src/components/Date';
 13import IfLoggedIn from 'src/components/IfLoggedIn/IfLoggedIn';
 14
 15import { BugFragment } from './Bug.generated';
 16import EditCommentForm from './EditCommentForm';
 17import { AddCommentFragment } from './MessageCommentFragment.generated';
 18import { CreateFragment } from './MessageCreateFragment.generated';
 19import MessageHistoryDialog from './MessageHistoryDialog';
 20
 21const useStyles = makeStyles((theme) => ({
 22  author: {
 23    fontWeight: 'bold',
 24    color: theme.palette.info.contrastText,
 25  },
 26  container: {
 27    display: 'flex',
 28  },
 29  avatar: {
 30    marginTop: 2,
 31  },
 32  bubble: {
 33    flex: 1,
 34    marginLeft: theme.spacing(1),
 35    minWidth: 0,
 36  },
 37  header: {
 38    ...theme.typography.body1,
 39    padding: '0.5rem 1rem',
 40    borderBottom: `1px solid ${theme.palette.divider}`,
 41    display: 'flex',
 42    borderTopRightRadius: theme.shape.borderRadius,
 43    borderTopLeftRadius: theme.shape.borderRadius,
 44    backgroundColor: theme.palette.info.main,
 45    color: theme.palette.info.contrastText,
 46  },
 47  title: {
 48    flex: 1,
 49  },
 50  tag: {
 51    ...theme.typography.button,
 52    color: '#888',
 53    border: '#ddd solid 1px',
 54    padding: '0 0.5rem',
 55    fontSize: '0.75rem',
 56    borderRadius: 2,
 57    marginLeft: '0.5rem',
 58  },
 59  body: {
 60    overflow: 'auto',
 61    ...theme.typography.body2,
 62    paddingLeft: theme.spacing(1),
 63    paddingRight: theme.spacing(1),
 64  },
 65  headerActions: {
 66    color: theme.palette.info.contrastText,
 67    padding: '0rem',
 68    marginLeft: theme.spacing(1),
 69    fontSize: '0.75rem',
 70    '&:hover': {
 71      backgroundColor: 'inherit',
 72    },
 73  },
 74}));
 75
 76type HistBtnProps = {
 77  bugId: string;
 78  commentId: string;
 79};
 80function HistoryMenuToggleButton({ bugId, commentId }: HistBtnProps) {
 81  const classes = useStyles();
 82  const [open, setOpen] = React.useState(false);
 83
 84  const handleClickOpen = () => {
 85    setOpen(true);
 86  };
 87
 88  const handleClose = () => {
 89    setOpen(false);
 90  };
 91
 92  return (
 93    <div>
 94      <IconButton
 95        aria-label="more"
 96        aria-controls="long-menu"
 97        aria-haspopup="true"
 98        onClick={handleClickOpen}
 99        className={classes.headerActions}
100        size="large"
101      >
102        <HistoryIcon />
103      </IconButton>
104      {
105        // Render CustomizedDialogs on open to prevent fetching the history
106        // before opening the history menu.
107        open && (
108          <MessageHistoryDialog
109            bugId={bugId}
110            commentId={commentId}
111            open={open}
112            onClose={handleClose}
113          />
114        )
115      }
116    </div>
117  );
118}
119
120type Props = {
121  bug: BugFragment;
122  op: AddCommentFragment | CreateFragment;
123};
124function Message({ bug, op }: Props) {
125  const classes = useStyles();
126  const [editMode, switchToEditMode] = useState(false);
127  const [comment, setComment] = useState(op);
128
129  const editComment = (id: String) => {
130    switchToEditMode(true);
131  };
132
133  function readMessageView() {
134    return (
135      <Paper elevation={1} className={classes.bubble}>
136        <header className={classes.header}>
137          <div className={classes.title}>
138            <Author className={classes.author} author={comment.author} />
139            <span> commented </span>
140            <Date date={comment.createdAt} />
141          </div>
142          {comment.edited && (
143            <HistoryMenuToggleButton bugId={bug.id} commentId={comment.id} />
144          )}
145          <IfLoggedIn>
146            {() => (
147              <Tooltip title="Edit Message" placement="top" arrow={true}>
148                <IconButton
149                  disableRipple
150                  className={classes.headerActions}
151                  aria-label="edit message"
152                  onClick={() => editComment(comment.id)}
153                  size="large"
154                >
155                  <EditIcon />
156                </IconButton>
157              </Tooltip>
158            )}
159          </IfLoggedIn>
160        </header>
161        <section className={classes.body}>
162          {comment.message !== '' ? (
163            <Content markdown={comment.message} />
164          ) : (
165            <Content markdown="*No description provided.*" />
166          )}
167        </section>
168      </Paper>
169    );
170  }
171
172  function editMessageView() {
173    const cancelEdition = () => {
174      switchToEditMode(false);
175    };
176
177    const onPostSubmit = (comment: AddCommentFragment | CreateFragment) => {
178      setComment(comment);
179      switchToEditMode(false);
180    };
181
182    return (
183      <div className={classes.bubble}>
184        <EditCommentForm
185          bug={bug}
186          onCancel={cancelEdition}
187          onPostSubmit={onPostSubmit}
188          comment={comment}
189        />
190      </div>
191    );
192  }
193
194  return (
195    <article className={classes.container}>
196      <Avatar author={comment.author} className={classes.avatar} />
197      {editMode ? editMessageView() : readMessageView()}
198    </article>
199  );
200}
201
202export default Message;