Message.tsx

  1import { useState } from 'react';
  2import * as React from 'react';
  3
  4import IconButton from '@material-ui/core/IconButton';
  5import Paper from '@material-ui/core/Paper';
  6import Tooltip from '@material-ui/core/Tooltip/Tooltip';
  7import { makeStyles } from '@material-ui/core/styles';
  8import EditIcon from '@material-ui/icons/Edit';
  9import HistoryIcon from '@material-ui/icons/History';
 10
 11import Author, { Avatar } from 'src/components/Author';
 12import Content from 'src/components/Content';
 13import Date from 'src/components/Date';
 14import IfLoggedIn from 'src/components/IfLoggedIn/IfLoggedIn';
 15
 16import { BugFragment } from './Bug.generated';
 17import EditCommentForm from './EditCommentForm';
 18import { AddCommentFragment } from './MessageCommentFragment.generated';
 19import { CreateFragment } from './MessageCreateFragment.generated';
 20import MessageHistoryDialog from './MessageHistoryDialog';
 21
 22const useStyles = makeStyles((theme) => ({
 23  author: {
 24    fontWeight: 'bold',
 25    color: theme.palette.info.contrastText,
 26  },
 27  container: {
 28    display: 'flex',
 29  },
 30  avatar: {
 31    marginTop: 2,
 32  },
 33  bubble: {
 34    flex: 1,
 35    marginLeft: theme.spacing(1),
 36    minWidth: 0,
 37  },
 38  header: {
 39    ...theme.typography.body1,
 40    padding: '0.5rem 1rem',
 41    borderBottom: `1px solid ${theme.palette.divider}`,
 42    display: 'flex',
 43    borderTopRightRadius: theme.shape.borderRadius,
 44    borderTopLeftRadius: theme.shape.borderRadius,
 45    backgroundColor: theme.palette.info.main,
 46    color: theme.palette.info.contrastText,
 47  },
 48  title: {
 49    flex: 1,
 50  },
 51  tag: {
 52    ...theme.typography.button,
 53    color: '#888',
 54    border: '#ddd solid 1px',
 55    padding: '0 0.5rem',
 56    fontSize: '0.75rem',
 57    borderRadius: 2,
 58    marginLeft: '0.5rem',
 59  },
 60  body: {
 61    overflow: 'auto',
 62    ...theme.typography.body2,
 63    paddingLeft: theme.spacing(1),
 64    paddingRight: theme.spacing(1),
 65  },
 66  headerActions: {
 67    color: theme.palette.info.contrastText,
 68    padding: '0rem',
 69    marginLeft: theme.spacing(1),
 70    fontSize: '0.75rem',
 71    '&:hover': {
 72      backgroundColor: 'inherit',
 73    },
 74  },
 75}));
 76
 77type HistBtnProps = {
 78  bugId: string;
 79  commentId: string;
 80};
 81function HistoryMenuToggleButton({ bugId, commentId }: HistBtnProps) {
 82  const classes = useStyles();
 83  const [open, setOpen] = React.useState(false);
 84
 85  const handleClickOpen = () => {
 86    setOpen(true);
 87  };
 88
 89  const handleClose = () => {
 90    setOpen(false);
 91  };
 92
 93  return (
 94    <div>
 95      <IconButton
 96        aria-label="more"
 97        aria-controls="long-menu"
 98        aria-haspopup="true"
 99        onClick={handleClickOpen}
100        className={classes.headerActions}
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                >
154                  <EditIcon />
155                </IconButton>
156              </Tooltip>
157            )}
158          </IfLoggedIn>
159        </header>
160        <section className={classes.body}>
161          {comment.message !== '' ? (
162            <Content markdown={comment.message} />
163          ) : (
164            <Content markdown="*No description provided.*" />
165          )}
166        </section>
167      </Paper>
168    );
169  }
170
171  function editMessageView() {
172    const cancelEdition = () => {
173      switchToEditMode(false);
174    };
175
176    const onPostSubmit = (comment: AddCommentFragment | CreateFragment) => {
177      setComment(comment);
178      switchToEditMode(false);
179    };
180
181    return (
182      <div className={classes.bubble}>
183        <EditCommentForm
184          bug={bug}
185          onCancel={cancelEdition}
186          onPostSubmit={onPostSubmit}
187          comment={comment}
188        />
189      </div>
190    );
191  }
192
193  return (
194    <article className={classes.container}>
195      <Avatar author={comment.author} className={classes.avatar} />
196      {editMode ? editMessageView() : readMessageView()}
197    </article>
198  );
199}
200
201export default Message;