mirror of
https://github.com/MichaelMure/git-bug.git
synced 2025-01-06 01:44:27 +03:00
Use dialog with accordion for message history menu
This commit is contained in:
parent
d453abdeed
commit
155b37e81f
@ -1,67 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
|
||||||
import Menu from '@material-ui/core/Menu';
|
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
|
||||||
|
|
||||||
import Date from 'src/components/Date';
|
|
||||||
|
|
||||||
import { AddCommentFragment } from './MessageCommentFragment.generated';
|
|
||||||
import { CreateFragment } from './MessageCreateFragment.generated';
|
|
||||||
import { useMessageEditHistoryQuery } from './MessageEditHistory.generated';
|
|
||||||
|
|
||||||
const ITEM_HEIGHT = 48;
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
anchor: null | HTMLElement;
|
|
||||||
bugId: string;
|
|
||||||
commentId: string;
|
|
||||||
onClose: () => void;
|
|
||||||
};
|
|
||||||
function EditHistoryMenu({ anchor, bugId, commentId, onClose }: Props) {
|
|
||||||
const open = Boolean(anchor);
|
|
||||||
|
|
||||||
const { loading, error, data } = useMessageEditHistoryQuery({
|
|
||||||
variables: { bugIdPrefix: bugId },
|
|
||||||
});
|
|
||||||
if (loading) return <CircularProgress />;
|
|
||||||
if (error) return <p>Error: {error}</p>;
|
|
||||||
|
|
||||||
const comments = data?.repository?.bug?.timeline.comments as (
|
|
||||||
| AddCommentFragment
|
|
||||||
| CreateFragment
|
|
||||||
)[];
|
|
||||||
// NOTE Searching for the changed comment could be dropped if GraphQL get
|
|
||||||
// filter by id argument for timelineitems
|
|
||||||
const comment = comments.find((elem) => elem.id === commentId);
|
|
||||||
const history = comment?.history;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Menu
|
|
||||||
id="long-menu"
|
|
||||||
anchorEl={anchor}
|
|
||||||
keepMounted
|
|
||||||
open={open}
|
|
||||||
onClose={onClose}
|
|
||||||
PaperProps={{
|
|
||||||
style: {
|
|
||||||
maxHeight: ITEM_HEIGHT * 4.5,
|
|
||||||
width: '20ch',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem key={0} disabled>
|
|
||||||
Edited {history?.length} times.
|
|
||||||
</MenuItem>
|
|
||||||
{history?.map((edit, index) => (
|
|
||||||
<MenuItem key={index} onClick={onClose}>
|
|
||||||
<Date date={edit.date} />
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EditHistoryMenu;
|
|
@ -14,9 +14,9 @@ import IfLoggedIn from 'src/components/IfLoggedIn/IfLoggedIn';
|
|||||||
|
|
||||||
import { BugFragment } from './Bug.generated';
|
import { BugFragment } from './Bug.generated';
|
||||||
import EditCommentForm from './EditCommentForm';
|
import EditCommentForm from './EditCommentForm';
|
||||||
import EditHistoryMenu from './EditHistoryMenu';
|
|
||||||
import { AddCommentFragment } from './MessageCommentFragment.generated';
|
import { AddCommentFragment } from './MessageCommentFragment.generated';
|
||||||
import { CreateFragment } from './MessageCreateFragment.generated';
|
import { CreateFragment } from './MessageCreateFragment.generated';
|
||||||
|
import MessageHistoryDialog from './MessageHistoryDialog';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
author: {
|
author: {
|
||||||
@ -70,10 +70,6 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//TODO move button out of this component and let only menu as component with
|
|
||||||
//query. Then the query won't execute unless button click renders menu with
|
|
||||||
//query.
|
|
||||||
//TODO Fix display of load button spinner.
|
|
||||||
//TODO Move this button and menu in separate component directory
|
//TODO Move this button and menu in separate component directory
|
||||||
//TODO fix failing pipeline due to eslint error
|
//TODO fix failing pipeline due to eslint error
|
||||||
type HistBtnProps = {
|
type HistBtnProps = {
|
||||||
@ -82,14 +78,14 @@ type HistBtnProps = {
|
|||||||
};
|
};
|
||||||
function HistoryMenuToggleButton({ bugId, commentId }: HistBtnProps) {
|
function HistoryMenuToggleButton({ bugId, commentId }: HistBtnProps) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
const handleClickOpen = () => {
|
||||||
setAnchorEl(event.currentTarget);
|
setOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setAnchorEl(null);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -98,19 +94,23 @@ function HistoryMenuToggleButton({ bugId, commentId }: HistBtnProps) {
|
|||||||
aria-label="more"
|
aria-label="more"
|
||||||
aria-controls="long-menu"
|
aria-controls="long-menu"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
onClick={handleClick}
|
onClick={handleClickOpen}
|
||||||
className={classes.headerActions}
|
className={classes.headerActions}
|
||||||
>
|
>
|
||||||
<HistoryIcon />
|
<HistoryIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{anchorEl && (
|
{
|
||||||
<EditHistoryMenu
|
// Render CustomizedDialogs on open to prevent fetching the history
|
||||||
|
// before opening the history menu.
|
||||||
|
open && (
|
||||||
|
<MessageHistoryDialog
|
||||||
bugId={bugId}
|
bugId={bugId}
|
||||||
commentId={commentId}
|
commentId={commentId}
|
||||||
anchor={anchorEl}
|
open={open}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
/>
|
/>
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
215
webui/src/pages/bug/MessageHistoryDialog.tsx
Normal file
215
webui/src/pages/bug/MessageHistoryDialog.tsx
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import React from 'react';
|
||||||
|
import Moment from 'react-moment';
|
||||||
|
|
||||||
|
import MuiAccordion from '@material-ui/core/Accordion';
|
||||||
|
import MuiAccordionDetails from '@material-ui/core/AccordionDetails';
|
||||||
|
import MuiAccordionSummary from '@material-ui/core/AccordionSummary';
|
||||||
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||||
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
|
import MuiDialogContent from '@material-ui/core/DialogContent';
|
||||||
|
import MuiDialogTitle from '@material-ui/core/DialogTitle';
|
||||||
|
import Grid from '@material-ui/core/Grid';
|
||||||
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
import Tooltip from '@material-ui/core/Tooltip/Tooltip';
|
||||||
|
import Typography from '@material-ui/core/Typography';
|
||||||
|
import {
|
||||||
|
createStyles,
|
||||||
|
Theme,
|
||||||
|
withStyles,
|
||||||
|
WithStyles,
|
||||||
|
} from '@material-ui/core/styles';
|
||||||
|
import CloseIcon from '@material-ui/icons/Close';
|
||||||
|
|
||||||
|
import { AddCommentFragment } from './MessageCommentFragment.generated';
|
||||||
|
import { CreateFragment } from './MessageCreateFragment.generated';
|
||||||
|
import { useMessageEditHistoryQuery } from './MessageEditHistory.generated';
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
root: {
|
||||||
|
margin: 0,
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
},
|
||||||
|
closeButton: {
|
||||||
|
position: 'absolute',
|
||||||
|
right: theme.spacing(1),
|
||||||
|
top: theme.spacing(1),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface DialogTitleProps extends WithStyles<typeof styles> {
|
||||||
|
id: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DialogTitle = withStyles(styles)((props: DialogTitleProps) => {
|
||||||
|
const { children, classes, onClose, ...other } = props;
|
||||||
|
return (
|
||||||
|
<MuiDialogTitle disableTypography className={classes.root} {...other}>
|
||||||
|
<Typography variant="h6">{children}</Typography>
|
||||||
|
{onClose ? (
|
||||||
|
<IconButton
|
||||||
|
aria-label="close"
|
||||||
|
className={classes.closeButton}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
) : null}
|
||||||
|
</MuiDialogTitle>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const DialogContent = withStyles((theme: Theme) => ({
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}))(MuiDialogContent);
|
||||||
|
|
||||||
|
const Accordion = withStyles({
|
||||||
|
root: {
|
||||||
|
border: '1px solid rgba(0, 0, 0, .125)',
|
||||||
|
boxShadow: 'none',
|
||||||
|
'&:not(:last-child)': {
|
||||||
|
borderBottom: 0,
|
||||||
|
},
|
||||||
|
'&:before': {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
'&$expanded': {
|
||||||
|
margin: 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expanded: {},
|
||||||
|
})(MuiAccordion);
|
||||||
|
|
||||||
|
const AccordionSummary = withStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
backgroundColor: theme.palette.primary.light,
|
||||||
|
borderBottomWidth: '1px',
|
||||||
|
borderBottomStyle: 'solid',
|
||||||
|
borderBottomColor: theme.palette.divider,
|
||||||
|
marginBottom: -1,
|
||||||
|
minHeight: 56,
|
||||||
|
'&$expanded': {
|
||||||
|
minHeight: 56,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
'&$expanded': {
|
||||||
|
margin: '12px 0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expanded: {},
|
||||||
|
}))(MuiAccordionSummary);
|
||||||
|
|
||||||
|
const AccordionDetails = withStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}))(MuiAccordionDetails);
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
bugId: string;
|
||||||
|
commentId: string;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
function MessageHistoryDialog({ bugId, commentId, open, onClose }: Props) {
|
||||||
|
const [expanded, setExpanded] = React.useState<string | false>('panel0');
|
||||||
|
|
||||||
|
const { loading, error, data } = useMessageEditHistoryQuery({
|
||||||
|
variables: { bugIdPrefix: bugId },
|
||||||
|
});
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
onClose={onClose}
|
||||||
|
aria-labelledby="customized-dialog-title"
|
||||||
|
open={open}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
|
>
|
||||||
|
<DialogTitle id="customized-dialog-title" onClose={onClose}>
|
||||||
|
Loading...
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<Grid container justify="center">
|
||||||
|
<CircularProgress />
|
||||||
|
</Grid>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
onClose={onClose}
|
||||||
|
aria-labelledby="customized-dialog-title"
|
||||||
|
open={open}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
|
>
|
||||||
|
<DialogTitle id="customized-dialog-title" onClose={onClose}>
|
||||||
|
Something went wrong...
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<p>Error: {error}</p>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const comments = data?.repository?.bug?.timeline.comments as (
|
||||||
|
| AddCommentFragment
|
||||||
|
| CreateFragment
|
||||||
|
)[];
|
||||||
|
// NOTE Searching for the changed comment could be dropped if GraphQL get
|
||||||
|
// filter by id argument for timelineitems
|
||||||
|
const comment = comments.find((elem) => elem.id === commentId);
|
||||||
|
const history = comment?.history;
|
||||||
|
|
||||||
|
const handleChange = (panel: string) => (
|
||||||
|
event: React.ChangeEvent<{}>,
|
||||||
|
newExpanded: boolean
|
||||||
|
) => {
|
||||||
|
setExpanded(newExpanded ? panel : false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
onClose={onClose}
|
||||||
|
aria-labelledby="customized-dialog-title"
|
||||||
|
open={open}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="md"
|
||||||
|
>
|
||||||
|
<DialogTitle id="customized-dialog-title" onClose={onClose}>
|
||||||
|
Edited {history?.length} times.
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
{history?.map((edit, index) => (
|
||||||
|
<Accordion
|
||||||
|
square
|
||||||
|
expanded={expanded === 'panel' + index}
|
||||||
|
onChange={handleChange('panel' + index)}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
aria-controls="panel1d-content"
|
||||||
|
id="panel1d-header"
|
||||||
|
>
|
||||||
|
<Tooltip title={moment(edit.date).format('LLLL')}>
|
||||||
|
<Moment date={edit.date} format="on ll" />
|
||||||
|
</Tooltip>
|
||||||
|
</AccordionSummary>
|
||||||
|
<AccordionDetails>{edit.message}</AccordionDetails>
|
||||||
|
</Accordion>
|
||||||
|
))}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MessageHistoryDialog;
|
Loading…
Reference in New Issue
Block a user