Show toast on upload error (#2959)

A little toast for when image uploads fail. Solves #2944

![Kapture 2024-02-27 at 09 27
12](https://github.com/tldraw/tldraw/assets/1242537/9e285622-8015-41fa-bc3d-92dccfaa7ba9)

### Change Type

- [x] `patch` — Bug fix


### Release Notes

- Adds a quick toast to show when image uploads fail.
This commit is contained in:
David Sheldrick 2024-02-27 14:14:42 +00:00 committed by GitHub
parent 2a8ae6188e
commit 4639436aad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 45 additions and 12 deletions

View file

@ -107,6 +107,8 @@
"action.zoom-to-100": "Zoom to 100%", "action.zoom-to-100": "Zoom to 100%",
"action.zoom-to-fit": "Zoom to fit", "action.zoom-to-fit": "Zoom to fit",
"action.zoom-to-selection": "Zoom to selection", "action.zoom-to-selection": "Zoom to selection",
"assets.files.upload-failed": "Upload failed",
"assets.url.failed": "Couldn't load URL preview",
"color-style.black": "Black", "color-style.black": "Black",
"color-style.blue": "Blue", "color-style.blue": "Blue",
"color-style.green": "Green", "color-style.green": "Green",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -33,7 +33,9 @@ import { registerDefaultSideEffects } from './defaultSideEffects'
import { defaultTools } from './defaultTools' import { defaultTools } from './defaultTools'
import { TldrawUi, TldrawUiProps } from './ui/TldrawUi' import { TldrawUi, TldrawUiProps } from './ui/TldrawUi'
import { TLUiComponents, useTldrawUiComponents } from './ui/context/components' import { TLUiComponents, useTldrawUiComponents } from './ui/context/components'
import { useToasts } from './ui/context/toasts'
import { usePreloadAssets } from './ui/hooks/usePreloadAssets' import { usePreloadAssets } from './ui/hooks/usePreloadAssets'
import { useTranslation } from './ui/hooks/useTranslation/useTranslation'
import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls' import { useDefaultEditorAssetsWithOverrides } from './utils/static-assets/assetUrls'
/**@public */ /**@public */
@ -158,6 +160,8 @@ function InsideOfEditorAndUiContext({
onMount, onMount,
}: Partial<TLExternalContentProps & { onMount: TLOnMountHandler }>) { }: Partial<TLExternalContentProps & { onMount: TLOnMountHandler }>) {
const editor = useEditor() const editor = useEditor()
const toasts = useToasts()
const msg = useTranslation()
const onMountEvent = useEvent((editor: Editor) => { const onMountEvent = useEvent((editor: Editor) => {
const unsubs: (void | (() => void) | undefined)[] = [] const unsubs: (void | (() => void) | undefined)[] = []
@ -165,12 +169,19 @@ function InsideOfEditorAndUiContext({
unsubs.push(...registerDefaultSideEffects(editor)) unsubs.push(...registerDefaultSideEffects(editor))
// for content handling, first we register the default handlers... // for content handling, first we register the default handlers...
registerDefaultExternalContentHandlers(editor, { registerDefaultExternalContentHandlers(
maxImageDimension, editor,
maxAssetSize, {
acceptedImageMimeTypes, maxImageDimension,
acceptedVideoMimeTypes, maxAssetSize,
}) acceptedImageMimeTypes,
acceptedVideoMimeTypes,
},
{
toasts,
msg,
}
)
// ...then we run the onMount prop, which may override the above // ...then we run the onMount prop, which may override the above
unsubs.push(onMount?.(editor)) unsubs.push(onMount?.(editor))

View file

@ -19,6 +19,8 @@ import {
getHashForString, getHashForString,
} from '@tldraw/editor' } from '@tldraw/editor'
import { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from './shapes/shared/default-shape-constants' import { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from './shapes/shared/default-shape-constants'
import { TLUiToastsContextType } from './ui/context/toasts'
import { useTranslation } from './ui/hooks/useTranslation/useTranslation'
import { containBoxSize, downsizeImage, isGifAnimated } from './utils/assets/assets' import { containBoxSize, downsizeImage, isGifAnimated } from './utils/assets/assets'
import { getEmbedInfo } from './utils/embeds/embeds' import { getEmbedInfo } from './utils/embeds/embeds'
import { cleanupText, isRightToLeftLanguage, truncateStringWithEllipsis } from './utils/text/text' import { cleanupText, isRightToLeftLanguage, truncateStringWithEllipsis } from './utils/text/text'
@ -42,7 +44,8 @@ export function registerDefaultExternalContentHandlers(
maxAssetSize, maxAssetSize,
acceptedImageMimeTypes, acceptedImageMimeTypes,
acceptedVideoMimeTypes, acceptedVideoMimeTypes,
}: TLExternalContentProps }: TLExternalContentProps,
{ toasts, msg }: { toasts: TLUiToastsContextType; msg: ReturnType<typeof useTranslation> }
) { ) {
// files -> asset // files -> asset
editor.registerExternalAssetHandler('file', async ({ file: _file }) => { editor.registerExternalAssetHandler('file', async ({ file: _file }) => {
@ -122,6 +125,9 @@ export function registerDefaultExternalContentHandlers(
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)
toasts.addToast({
title: msg('assets.url.failed'),
})
meta = { image: '', title: truncateStringWithEllipsis(url, 32), description: '' } meta = { image: '', title: truncateStringWithEllipsis(url, 32), description: '' }
} }
@ -241,6 +247,9 @@ export function registerDefaultExternalContentHandlers(
assets[i] = asset assets[i] = asset
} catch (error) { } catch (error) {
toasts.addToast({
title: msg('assets.files.upload-failed'),
})
console.error(error) console.error(error)
return null return null
} }
@ -352,9 +361,16 @@ export function registerDefaultExternalContentHandlers(
let shouldAlsoCreateAsset = false let shouldAlsoCreateAsset = false
if (!asset) { if (!asset) {
shouldAlsoCreateAsset = true shouldAlsoCreateAsset = true
const bookmarkAsset = await editor.getAssetForExternalContent({ type: 'url', url }) try {
if (!bookmarkAsset) throw Error('Could not create an asset') const bookmarkAsset = await editor.getAssetForExternalContent({ type: 'url', url })
asset = bookmarkAsset if (!bookmarkAsset) throw Error('Could not create an asset')
asset = bookmarkAsset
} catch (e) {
toasts.addToast({
title: msg('assets.url.failed'),
})
return
}
} }
editor.batch(() => { editor.batch(() => {

View file

@ -111,6 +111,8 @@ export type TLUiTranslationKey =
| 'action.zoom-to-100' | 'action.zoom-to-100'
| 'action.zoom-to-fit' | 'action.zoom-to-fit'
| 'action.zoom-to-selection' | 'action.zoom-to-selection'
| 'assets.files.upload-failed'
| 'assets.url.failed'
| 'color-style.black' | 'color-style.black'
| 'color-style.blue' | 'color-style.blue'
| 'color-style.green' | 'color-style.green'

View file

@ -111,6 +111,8 @@ export const DEFAULT_TRANSLATION = {
'action.zoom-to-100': 'Zoom to 100%', 'action.zoom-to-100': 'Zoom to 100%',
'action.zoom-to-fit': 'Zoom to fit', 'action.zoom-to-fit': 'Zoom to fit',
'action.zoom-to-selection': 'Zoom to selection', 'action.zoom-to-selection': 'Zoom to selection',
'assets.files.upload-failed': 'Upload failed',
'assets.url.failed': "Couldn't load URL preview",
'color-style.black': 'Black', 'color-style.black': 'Black',
'color-style.blue': 'Blue', 'color-style.blue': 'Blue',
'color-style.green': 'Green', 'color-style.green': 'Green',