Skip to content

Instantly share code, notes, and snippets.

@trevorblades
Created October 31, 2019 22:29
Show Gist options
  • Select an option

  • Save trevorblades/efdd42fdfcf98ddb639cffe4fce323b0 to your computer and use it in GitHub Desktop.

Select an option

Save trevorblades/efdd42fdfcf98ddb639cffe4fce323b0 to your computer and use it in GitHub Desktop.
Apollo mutation update option
import PropTypes from 'prop-types';
import React, {Fragment, useRef, useState} from 'react';
import gql from 'graphql-tag';
import useClickAway from 'react-use/lib/useClickAway';
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
IconButton
} from '@material-ui/core';
import {FiEdit, FiMoreHorizontal, FiTrash2} from 'react-icons/fi';
import {GET_PROJECT} from '../../utils/queries';
import {formatGraphQLError} from '../../utils';
import {useMutation} from '@apollo/react-hooks';
const DELETE_BLOCK = gql`
mutation DeleteBlock($id: ID!) {
deleteBlock(id: $id) {
id
projectId
}
}
`;
function updateProject(cache, {data}) {
const queryOptions = {
query: GET_PROJECT,
variables: {
id: data.deleteBlock.projectId
}
};
const {project} = cache.readQuery(queryOptions);
cache.writeQuery({
...queryOptions,
data: {
project: {
...project,
blocks: project.blocks.filter(block => block.id !== data.deleteBlock.id)
}
}
});
}
export default function BlockActions(props) {
const wrapperRef = useRef(null);
const [open, setOpen] = useState(false);
const [dialogOpen, setDialogOpen] = useState(false);
const [deleteBlock, {loading, error}] = useMutation(DELETE_BLOCK, {
variables: {
id: props.block.id
},
update: updateProject
});
function openDialog() {
setDialogOpen(true);
}
function closeDialog() {
setDialogOpen(false);
}
useClickAway(wrapperRef, () => setOpen(false));
function handleEditClick() {
props.startEditing(props.block);
}
return (
<Fragment>
<Box ml={1.5} color="grey.600" ref={wrapperRef}>
{open ? (
<Fragment>
<IconButton color="inherit" onClick={handleEditClick}>
<FiEdit size={20} />
</IconButton>
<IconButton color="inherit" onClick={openDialog}>
<FiTrash2 size={20} />
</IconButton>
</Fragment>
) : (
<IconButton onClick={() => setOpen(true)}>
<FiMoreHorizontal size={20} />
</IconButton>
)}
</Box>
<Dialog maxWidth="xs" open={dialogOpen} onClose={closeDialog} fullWidth>
<DialogTitle>Are you sure?</DialogTitle>
<DialogContent>
{error && (
<DialogContentText color="error">
{formatGraphQLError(error)}
</DialogContentText>
)}
<DialogContentText>
<strong>{props.block.name}</strong> will be permanently deleted.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={closeDialog}>Cancel</Button>
<Button disabled={loading} color="secondary" onClick={deleteBlock}>
Yes, delete it
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
}
BlockActions.propTypes = {
block: PropTypes.object.isRequired,
startEditing: PropTypes.func.isRequired
};
import DescriptionField from './description-field';
import PropTypes from 'prop-types';
import React from 'react';
import gql from 'graphql-tag';
import {BLOCK_FRAGMENT, GET_PROJECT} from '../../utils/queries';
import {Box, Button, TextField, Typography} from '@material-ui/core';
import {formatGraphQLError} from '../../utils';
import {useMutation} from '@apollo/react-hooks';
const CREATE_BLOCK = gql`
mutation CreateBlock(
$name: String!
$description: String
$start: Float!
$end: Float!
$projectId: String!
) {
createBlock(
name: $name
description: $description
start: $start
end: $end
projectId: $projectId
) {
...BlockFragment
}
}
${BLOCK_FRAGMENT}
`;
export default function BlockForm(props) {
const {id, start, end, projectId} = props.block;
const [mutate, {loading, error}] = useMutation(
CREATE_BLOCK,
{
variables: {
id,
start,
end,
projectId
},
onCompleted(data) {
props.setSelectedBlock(id ? data.updateBlock : data.createBlock);
props.setStatus(null);
},
update(cache, {data}) {
const queryOptions = {
query: GET_PROJECT,
variables: {
id: projectId
}
};
const {project} = cache.readQuery(queryOptions);
cache.writeQuery({
...queryOptions,
data: {
project: {
...project,
blocks: [...project.blocks, data.createBlock]
}
}
});
}
}
);
function handleKeyDown(event) {
if (event.key === 'Enter' && event.metaKey) {
event.preventDefault();
props.submitRef.current.click();
}
}
function handleSubmit(event) {
event.preventDefault();
const {name, description} = event.target;
mutate({
variables: {
name: name.value.trim(),
description: description.value.trim()
}
});
}
return (
<Box p={4} component="form" onSubmit={handleSubmit}>
{error && (
<Typography gutterBottom color="error">
{formatGraphQLError(error)}
</Typography>
)}
<TextField
fullWidth
disabled={loading}
margin="normal"
variant="outlined"
name="name"
required
defaultValue={props.block.name}
label="Block name"
/>
<DescriptionField
disabled={loading}
inputProps={{onKeyDown: handleKeyDown}}
defaultValue={props.block.description}
/>
<Box mt={2} display="flex" justifyContent="flex-end">
<Box mr={1}>
<Button size="large" onClick={props.onCancel}>
Cancel
</Button>
</Box>
<Button
size="large"
variant="contained"
color="secondary"
disabled={loading}
type="submit"
ref={props.submitRef}
>
Save block
</Button>
</Box>
</Box>
);
}
BlockForm.propTypes = {
block: PropTypes.object.isRequired,
onCancel: PropTypes.func.isRequired,
setSelectedBlock: PropTypes.func.isRequired,
setStatus: PropTypes.func.isRequired,
submitRef: PropTypes.object
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment