Skip to content

Instantly share code, notes, and snippets.

@trevorblades
Created May 2, 2020 20:15
Show Gist options
  • Save trevorblades/dbfacf81d10cf20be4be291049ed20dd to your computer and use it in GitHub Desktop.
Save trevorblades/dbfacf81d10cf20be4be291049ed20dd to your computer and use it in GitHub Desktop.
GitHub-style image uploads
import PropTypes from 'prop-types';
import React, {useState} from 'react';
import {BLOCK_FRAGMENT, CREATE_SIGNED_URL, uploadFile} from '../utils';
import {Box, Button, Flex, Input, Stack, Text, Textarea} from '@chakra-ui/core';
import {gql, useMutation} from '@apollo/client';
const CREATE_BLOCK = gql`
mutation CreateBlock($input: CreateBlockInput!) {
createBlock(input: $input) {
...BlockFragment
projectId
}
}
${BLOCK_FRAGMENT}
`;
const UPDATE_BLOCK = gql`
mutation UpdateBlock($input: UpdateBlockInput!) {
updateBlock(input: $input) {
...BlockFragment
}
}
${BLOCK_FRAGMENT}
`;
function NotesInput(props) {
const [value, setValue] = useState(props.defaultValue);
const [loading, setLoading] = useState(false);
const [createSignedUrl] = useMutation(CREATE_SIGNED_URL);
async function handleDrop(event) {
event.preventDefault();
const item = event.dataTransfer.items[0];
if (item && item.kind === 'file' && /image\/\w+/.test(item.type)) {
setLoading(true);
const file = item.getAsFile();
const {selectionStart} = event.target;
const placeholder = `![Uploading ${file.name}...]()`;
setValue(prevValue =>
[
prevValue.slice(0, selectionStart),
placeholder,
prevValue.slice(selectionStart)
].join('\n')
);
const {data} = await createSignedUrl({
variables: {
input: {
type: file.type,
name: file.name
}
}
});
const key = await uploadFile(file, data.createSignedUrl);
const filename = file.name
.split('.')
.slice(0, -1)
.join('.');
setValue(prevValue =>
prevValue.replace(placeholder, `![${filename}](${key})`)
);
setLoading(false);
}
}
return (
<Textarea
mb="2"
height="320px"
resize="none"
placeholder="Notes"
name="description"
size="lg"
onDrop={handleDrop}
isDisabled={loading}
value={value}
onChange={event => setValue(event.target.value)}
/>
);
}
NotesInput.propTypes = {
defaultValue: PropTypes.string.isRequired
};
export default function BlockForm(props) {
const [mutate, {loading, error}] = useMutation(
props.block.id ? UPDATE_BLOCK : CREATE_BLOCK,
props.mutationOptions
);
function handleSubmit(event) {
event.preventDefault();
const {name, description} = event.target;
const {id, start, end} = props.block;
const input = {
id,
start,
end,
name: name.value,
description: description.value
};
if (!input.id) {
input.projectId = props.projectId;
}
mutate({variables: {input}});
}
return (
<Flex direction="column" p="4" as="form" onSubmit={handleSubmit}>
<Stack spacing="4">
{error && <Text color="red.500">{error.message}</Text>}
<Input
autoFocus
isRequired
placeholder="Block name"
size="lg"
name="name"
defaultValue={props.block.name}
/>
<Box>
<NotesInput defaultValue={props.block.description} />
<Text fontSize="sm" color="gray.500">
You can use _markdown_ to format your notes. Drag/drop images on the
text field to upload them.
</Text>
</Box>
</Stack>
<Stack mt="6" ml="auto" isInline>
<Button variant="ghost" onClick={props.onCancelClick}>
Cancel
</Button>
<Button
ref={props.submitRef}
isLoading={loading}
variantColor="pink"
type="submit"
>
Save block
</Button>
</Stack>
</Flex>
);
}
BlockForm.propTypes = {
block: PropTypes.object.isRequired,
onCancelClick: PropTypes.func.isRequired,
submitRef: PropTypes.object.isRequired,
projectId: PropTypes.string.isRequired,
mutationOptions: PropTypes.object.isRequired
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment