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
 73type HistBtnProps = {
 74  bugId: string;
 75  commentId: string;
 76};
 77function HistoryMenuToggleButton({ bugId, commentId }: HistBtnProps) {
 78  const classes = useStyles();
 79  const [open, setOpen] = React.useState(false);
 80
 81  const handleClickOpen = () => {
 82    setOpen(true);
 83  };
 84
 85  const handleClose = () => {
 86    setOpen(false);
 87  };
 88
 89  return (
 90    <div>
 91      <IconButton
 92        aria-label="more"
 93        aria-controls="long-menu"
 94        aria-haspopup="true"
 95        onClick={handleClickOpen}
 96        className={classes.headerActions}
 97      >
 98        <HistoryIcon />
 99      </IconButton>
100      {
101        // Render CustomizedDialogs on open to prevent fetching the history
102        // before opening the history menu.
103        open && (
104          <MessageHistoryDialog
105            bugId={bugId}
106            commentId={commentId}
107            open={open}
108            onClose={handleClose}
109          />
110        )
111      }
112    </div>
113  );
114}
115
116type Props = {
117  bug: BugFragment;
118  op: AddCommentFragment | CreateFragment;
119};
120function Message({ bug, op }: Props) {
121  const classes = useStyles();
122  const [editMode, switchToEditMode] = useState(false);
123  const [comment, setComment] = useState(op);
124
125  const editComment = (id: String) => {
126    switchToEditMode(true);
127  };
128
129  function readMessageView() {
130    return (
131      <Paper elevation={1} className={classes.bubble}>
132        <header className={classes.header}>
133          <div className={classes.title}>
134            <Author className={classes.author} author={comment.author} />
135            <span> commented </span>
136            <Date date={comment.createdAt} />
137          </div>
138          {comment.edited && (
139            <HistoryMenuToggleButton bugId={bug.id} commentId={comment.id} />
140          )}
141          <IfLoggedIn>
142            {() => (
143              <Tooltip title="Edit Message" placement="top" arrow={true}>
144                <IconButton
145                  disableRipple
146                  className={classes.headerActions}
147                  aria-label="edit message"
148                  onClick={() => editComment(comment.id)}
149                >
150                  <EditIcon />
151                </IconButton>
152              </Tooltip>
153            )}
154          </IfLoggedIn>
155        </header>
156        <section className={classes.body}>
157          <Content markdown={comment.message} />
158        </section>
159      </Paper>
160    );
161  }
162
163  function editMessageView() {
164    const cancelEdition = () => {
165      switchToEditMode(false);
166    };
167
168    const onPostSubmit = (comment: AddCommentFragment | CreateFragment) => {
169      setComment(comment);
170      switchToEditMode(false);
171    };
172
173    return (
174      <div className={classes.bubble}>
175        <EditCommentForm
176          bug={bug}
177          onCancel={cancelEdition}
178          onPostSubmit={onPostSubmit}
179          comment={comment}
180        />
181      </div>
182    );
183  }
184
185  return (
186    <article className={classes.container}>
187      <Avatar author={comment.author} className={classes.avatar} />
188      {editMode ? editMessageView() : readMessageView()}
189    </article>
190  );
191}
192
193export default Message;