mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-11-28 19:55:53 +03:00
groups: handle s3 upload failures in collections
This commit is contained in:
parent
f0192cb66c
commit
836cdb2478
@ -10,7 +10,7 @@ export interface IuseStorage {
|
||||
upload: (file: File, bucket: string) => Promise<string>;
|
||||
uploadDefault: (file: File) => Promise<string>;
|
||||
uploading: boolean;
|
||||
promptUpload: () => Promise<string>;
|
||||
promptUpload: (onError?: (err: Error) => void) => Promise<string>;
|
||||
}
|
||||
|
||||
const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
||||
@ -85,7 +85,7 @@ const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
||||
}, [s3, upload]);
|
||||
|
||||
const promptUpload = useCallback(
|
||||
(): Promise<string> => {
|
||||
(onError?: (err: Error) => void): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fileSelector = document.createElement('input');
|
||||
fileSelector.setAttribute('type', 'file');
|
||||
@ -95,6 +95,9 @@ const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
|
||||
const files = fileSelector.files;
|
||||
if (!files || files.length <= 0) {
|
||||
reject();
|
||||
} else if (onError) {
|
||||
uploadDefault(files[0]).then(resolve).catch(err => onError(err));
|
||||
document.body.removeChild(fileSelector);
|
||||
} else {
|
||||
uploadDefault(files[0]).then(resolve);
|
||||
document.body.removeChild(fileSelector);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Box, LoadingSpinner, Action, Row } from '@tlon/indigo-react';
|
||||
import { Box, LoadingSpinner, Action, Row, Icon, Text } from '@tlon/indigo-react';
|
||||
|
||||
import { StatelessUrlInput } from '~/views/components/StatelessUrlInput';
|
||||
import SubmitDragger from '~/views/components/SubmitDragger';
|
||||
@ -21,6 +21,7 @@ export function LinkBlockInput(props: LinkBlockInputProps) {
|
||||
const [url, setUrl] = useState(props.url || '');
|
||||
const [valid, setValid] = useState(false);
|
||||
const [focussed, setFocussed] = useState(false);
|
||||
const [error, setError] = useState<string>('');
|
||||
|
||||
const addPost = useGraphState(selGraph);
|
||||
|
||||
@ -41,8 +42,13 @@ export function LinkBlockInput(props: LinkBlockInputProps) {
|
||||
setValid(URLparser.test(val) || Boolean(parsePermalink(val)));
|
||||
}, []);
|
||||
|
||||
const handleError = useCallback((err: Error) => {
|
||||
setError(err.message);
|
||||
}, []);
|
||||
|
||||
const { uploading, canUpload, promptUpload, drag } = useFileUpload({
|
||||
onSuccess: handleChange
|
||||
onSuccess: handleChange,
|
||||
onError: handleError
|
||||
});
|
||||
|
||||
const doPost = () => {
|
||||
@ -64,7 +70,7 @@ export function LinkBlockInput(props: LinkBlockInputProps) {
|
||||
};
|
||||
|
||||
const onKeyPress = useCallback(
|
||||
(e) => {
|
||||
(e: any) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
doPost();
|
||||
@ -89,22 +95,42 @@ export function LinkBlockInput(props: LinkBlockInputProps) {
|
||||
backgroundColor="washedGray"
|
||||
{...drag.bind}
|
||||
>
|
||||
{drag.dragging && <SubmitDragger />}
|
||||
{drag.dragging && canUpload && <SubmitDragger />}
|
||||
{uploading ? (
|
||||
<Box
|
||||
display="flex"
|
||||
width="100%"
|
||||
height="100%"
|
||||
position="absolute"
|
||||
left={0}
|
||||
right={0}
|
||||
bg="white"
|
||||
zIndex={9}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<LoadingSpinner />
|
||||
</Box>
|
||||
error != '' ? (
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
width="100%"
|
||||
height="100%"
|
||||
position="absolute"
|
||||
left={0}
|
||||
right={0}
|
||||
bg="white"
|
||||
zIndex={9}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Icon icon="ExclaimationMarkBold" size={32} />
|
||||
<Text bold>{error}</Text>
|
||||
<Text>Please check your S3 settings.</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
display="flex"
|
||||
width="100%"
|
||||
height="100%"
|
||||
position="absolute"
|
||||
left={0}
|
||||
right={0}
|
||||
bg="white"
|
||||
zIndex={9}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<LoadingSpinner />
|
||||
</Box>
|
||||
)
|
||||
) : (
|
||||
<StatelessUrlInput
|
||||
value={url}
|
||||
@ -114,6 +140,7 @@ export function LinkBlockInput(props: LinkBlockInputProps) {
|
||||
focussed={focussed}
|
||||
onBlur={onBlur}
|
||||
promptUpload={promptUpload}
|
||||
handleError={handleError}
|
||||
onKeyPress={onKeyPress}
|
||||
center
|
||||
/>
|
||||
@ -125,7 +152,11 @@ export function LinkBlockInput(props: LinkBlockInputProps) {
|
||||
p="2"
|
||||
justifyContent="row-end"
|
||||
>
|
||||
<Action onClick={doPost} disabled={!valid} backgroundColor="transparent">
|
||||
<Action
|
||||
onClick={doPost}
|
||||
disabled={!valid}
|
||||
backgroundColor="transparent"
|
||||
>
|
||||
Post
|
||||
</Action>
|
||||
</Row>
|
||||
|
@ -1,6 +1,13 @@
|
||||
import { BaseInput, Box, Button, LoadingSpinner } from '@tlon/indigo-react';
|
||||
import {
|
||||
BaseInput,
|
||||
Box,
|
||||
Button,
|
||||
Icon,
|
||||
LoadingSpinner,
|
||||
Text
|
||||
} from '@tlon/indigo-react';
|
||||
import { hasProvider } from 'oembed-parser';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { parsePermalink, permalinkToReference } from '~/logic/lib/permalinks';
|
||||
import { StatelessUrlInput } from '~/views/components/StatelessUrlInput';
|
||||
import SubmitDragger from '~/views/components/SubmitDragger';
|
||||
@ -22,15 +29,15 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
const [linkTitle, setLinkTitle] = useState('');
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const [linkValid, setLinkValid] = useState(false);
|
||||
const [error, setError] = useState<string>('');
|
||||
|
||||
const {
|
||||
canUpload,
|
||||
uploading,
|
||||
promptUpload,
|
||||
drag,
|
||||
onPaste
|
||||
} = useFileUpload({
|
||||
const handleError = useCallback((err: Error) => {
|
||||
setError(err.message);
|
||||
}, []);
|
||||
|
||||
const { canUpload, uploading, promptUpload, drag, onPaste } = useFileUpload({
|
||||
onSuccess: setLinkValue,
|
||||
onError: handleError,
|
||||
multiple: false
|
||||
});
|
||||
|
||||
@ -38,25 +45,21 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
const url = linkValue;
|
||||
const text = linkTitle ? linkTitle : linkValue;
|
||||
const contents = url.startsWith('web+urbitgraph:/')
|
||||
? [{ text }, permalinkToReference(parsePermalink(url)!)]
|
||||
: [{ text }, { url }];
|
||||
? [{ text }, permalinkToReference(parsePermalink(url)!)]
|
||||
: [{ text }, { url }];
|
||||
|
||||
setDisabled(true);
|
||||
const parentIndex = props.parentIndex || '';
|
||||
const post = createPost(window.ship, contents, parentIndex);
|
||||
|
||||
addPost(
|
||||
`~${props.ship}`,
|
||||
props.name,
|
||||
post
|
||||
);
|
||||
addPost(`~${props.ship}`, props.name, post);
|
||||
setDisabled(false);
|
||||
setLinkValue('');
|
||||
setLinkTitle('');
|
||||
setLinkValid(false);
|
||||
};
|
||||
|
||||
const validateLink = (link) => {
|
||||
const validateLink = link => {
|
||||
const URLparser = new RegExp(
|
||||
/((?:([\w\d\.-]+)\:\/\/?){1}(?:(www)\.?){0,1}(((?:[\w\d-]+\.)*)([\w\d-]+\.[\w\d]+))){1}(?:\:(\d+)){0,1}((\/(?:(?:[^\/\s\?]+\/)*))(?:([^\?\/\s#]+?(?:.[^\?\s]+){0,1}){0,1}(?:\?([^\s#]+)){0,1})){0,1}(?:#([^#\s]+)){0,1}/
|
||||
);
|
||||
@ -70,9 +73,9 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
setLinkValue(link);
|
||||
}
|
||||
}
|
||||
if(link.startsWith('web+urbitgraph://')) {
|
||||
if (link.startsWith('web+urbitgraph://')) {
|
||||
const permalink = parsePermalink(link);
|
||||
if(!permalink) {
|
||||
if (!permalink) {
|
||||
setLinkValid(false);
|
||||
return;
|
||||
}
|
||||
@ -82,21 +85,27 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
if (hasProvider(linkValue)) {
|
||||
fetch(`https://noembed.com/embed?url=${linkValue}`)
|
||||
.then(response => response.json())
|
||||
.then((result) => {
|
||||
.then(result => {
|
||||
if (result.title && !linkTitle) {
|
||||
setLinkTitle(result.title);
|
||||
}
|
||||
}).catch((error) => { /* noop*/ });
|
||||
})
|
||||
.catch(error => {
|
||||
/* noop*/
|
||||
});
|
||||
} else if (!linkTitle) {
|
||||
setLinkTitle(decodeURIComponent(link
|
||||
.split('/')
|
||||
.pop()
|
||||
.split('.')
|
||||
.slice(0, -1)
|
||||
.join('.')
|
||||
.replace('_', ' ')
|
||||
.replace(/\d{4}\.\d{1,2}\.\d{2}\.\.\d{2}\.\d{2}\.\d{2}-/, '')
|
||||
));
|
||||
setLinkTitle(
|
||||
decodeURIComponent(
|
||||
link
|
||||
.split('/')
|
||||
.pop()
|
||||
.split('.')
|
||||
.slice(0, -1)
|
||||
.join('.')
|
||||
.replace('_', ' ')
|
||||
.replace(/\d{4}\.\d{1,2}\.\d{2}\.\.\d{2}\.\d{2}\.\d{2}-/, '')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return link;
|
||||
@ -113,7 +122,7 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
|
||||
useEffect(onLinkChange, [linkValue]);
|
||||
|
||||
const onKeyPress = (e) => {
|
||||
const onKeyPress = e => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
doPost();
|
||||
@ -122,60 +131,86 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* @ts-ignore archaic event type mismatch */}
|
||||
{/* @ts-ignore archaic event type mismatch */}
|
||||
<Box
|
||||
flexShrink={0}
|
||||
position='relative'
|
||||
border='1px solid'
|
||||
position="relative"
|
||||
border="1px solid"
|
||||
borderColor={submitFocused ? 'black' : 'lightGray'}
|
||||
width='100%'
|
||||
width="100%"
|
||||
borderRadius={2}
|
||||
{...drag.bind}
|
||||
>
|
||||
{uploading && <Box
|
||||
display="flex"
|
||||
width="100%"
|
||||
height="100%"
|
||||
position="absolute"
|
||||
left={0}
|
||||
right={0}
|
||||
bg="white"
|
||||
zIndex={9}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<LoadingSpinner />
|
||||
</Box>}
|
||||
{drag.dragging && <SubmitDragger />}
|
||||
<StatelessUrlInput
|
||||
value={linkValue}
|
||||
promptUpload={promptUpload}
|
||||
canUpload={canUpload}
|
||||
onSubmit={doPost}
|
||||
onChange={setLinkValue}
|
||||
error={linkValid ? 'Invalid URL' : undefined}
|
||||
onKeyPress={onKeyPress}
|
||||
onPaste={onPaste}
|
||||
/>
|
||||
<BaseInput
|
||||
type="text"
|
||||
pl={2}
|
||||
backgroundColor="transparent"
|
||||
width="100%"
|
||||
color="black"
|
||||
fontSize={1}
|
||||
style={{
|
||||
resize: 'none',
|
||||
height: 40
|
||||
}}
|
||||
placeholder="Provide a title"
|
||||
onChange={e => setLinkTitle(e.target.value)}
|
||||
onBlur={() => setSubmitFocused(false)}
|
||||
onFocus={() => setSubmitFocused(true)}
|
||||
spellCheck="false"
|
||||
onKeyPress={onKeyPress}
|
||||
value={linkTitle}
|
||||
/>
|
||||
{uploading ? (
|
||||
error !== '' ? (
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
width="100%"
|
||||
height="100%"
|
||||
left={0}
|
||||
right={0}
|
||||
bg="white"
|
||||
zIndex={9}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
py={2}
|
||||
>
|
||||
<Icon icon="ExclaimationMarkBold" size={32} />
|
||||
<Text bold>{error}</Text>
|
||||
<Text>Please check your S3 settings.</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
display="flex"
|
||||
width="100%"
|
||||
height="100%"
|
||||
left={0}
|
||||
right={0}
|
||||
bg="white"
|
||||
zIndex={9}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
py={2}
|
||||
>
|
||||
<LoadingSpinner />
|
||||
</Box>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<StatelessUrlInput
|
||||
value={linkValue}
|
||||
promptUpload={promptUpload}
|
||||
canUpload={canUpload}
|
||||
onSubmit={doPost}
|
||||
onChange={setLinkValue}
|
||||
error={linkValid ? 'Invalid URL' : undefined}
|
||||
onKeyPress={onKeyPress}
|
||||
onPaste={onPaste}
|
||||
handleError={handleError}
|
||||
/>
|
||||
<BaseInput
|
||||
type="text"
|
||||
pl={2}
|
||||
backgroundColor="transparent"
|
||||
width="100%"
|
||||
color="black"
|
||||
fontSize={1}
|
||||
style={{
|
||||
resize: 'none',
|
||||
height: 40
|
||||
}}
|
||||
placeholder="Provide a title"
|
||||
onChange={e => setLinkTitle(e.target.value)}
|
||||
onBlur={() => setSubmitFocused(false)}
|
||||
onFocus={() => setSubmitFocused(true)}
|
||||
spellCheck="false"
|
||||
onKeyPress={onKeyPress}
|
||||
value={linkTitle}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{drag.dragging && <SubmitDragger />}
|
||||
</Box>
|
||||
<Box mt={2} mb={4}>
|
||||
<Button
|
||||
@ -183,7 +218,9 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
flexShrink={0}
|
||||
disabled={!linkValid || disabled}
|
||||
onClick={doPost}
|
||||
>Post link</Button>
|
||||
>
|
||||
Post link
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
@ -9,8 +9,9 @@ type StatelessUrlInputProps = PropFunc<typeof BaseInput> & {
|
||||
focussed?: boolean;
|
||||
disabled?: boolean;
|
||||
onChange?: (value: string) => void;
|
||||
promptUpload: () => Promise<string>;
|
||||
promptUpload: (onError: (err: Error) => void) => Promise<string>;
|
||||
canUpload: boolean;
|
||||
handleError: (err: Error) => void;
|
||||
center?: boolean;
|
||||
};
|
||||
|
||||
@ -22,6 +23,7 @@ export function StatelessUrlInput(props: StatelessUrlInputProps) {
|
||||
onChange = () => {},
|
||||
promptUpload,
|
||||
canUpload,
|
||||
handleError,
|
||||
center = false,
|
||||
...rest
|
||||
} = props;
|
||||
@ -53,7 +55,7 @@ export function StatelessUrlInput(props: StatelessUrlInputProps) {
|
||||
cursor="pointer"
|
||||
color="blue"
|
||||
style={{ pointerEvents: 'all' }}
|
||||
onClick={() => promptUpload().then(onChange)}
|
||||
onClick={() => promptUpload(handleError).then(onChange)}
|
||||
>
|
||||
upload
|
||||
</Text>{' '}
|
||||
|
Loading…
Reference in New Issue
Block a user