Message.tsx

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