Created
October 31, 2019 22:29
-
-
Save trevorblades/efdd42fdfcf98ddb639cffe4fce323b0 to your computer and use it in GitHub Desktop.
Apollo mutation update option
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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