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