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