From 9ca22733adef707d704a2edb7bfe4d9980d15f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADtor=20Vasconcellos?= Date: Tue, 10 Oct 2023 21:52:21 -0300 Subject: [PATCH] [ENG-1086, ENG-1087] Add error and loading indicator to thumbnail (#1448) * Add onLoad and onError listeners to FileThumb - Show toast when Original file fails loading in QuickPreview - Formatting * Fix close toast not working when QuickPreview was open * Fix panic when serving unsupported files * Formatting & lint --- apps/desktop/src/index.html | 22 +- core/src/custom_uri/mod.rs | 2 +- core/src/custom_uri/utils.rs | 11 +- .../$libraryId/Explorer/FilePath/Thumb.tsx | 41 +++- .../app/$libraryId/Explorer/OptionsPanel.tsx | 10 +- .../Explorer/QuickPreview/index.tsx | 57 +++++- .../app/$libraryId/Explorer/View/GridView.tsx | 2 +- .../app/$libraryId/Explorer/useExplorer.ts | 2 +- .../Layout/Sidebar/FeedbackDialog.tsx | 2 +- .../library/locations/AddLocationDialog.tsx | 2 +- packages/client/src/utils/explorerItem.ts | 2 +- packages/ui/src/Toast.tsx | 193 +++++++++--------- 12 files changed, 218 insertions(+), 128 deletions(-) diff --git a/apps/desktop/src/index.html b/apps/desktop/src/index.html index 9e256ff06..c0f509c3f 100644 --- a/apps/desktop/src/index.html +++ b/apps/desktop/src/index.html @@ -1,16 +1,14 @@ + + + + + Spacedrive + - - - - - Spacedrive - - - -
- - - + +
+ + diff --git a/core/src/custom_uri/mod.rs b/core/src/custom_uri/mod.rs index 55c34c753..605ec9a36 100644 --- a/core/src/custom_uri/mod.rs +++ b/core/src/custom_uri/mod.rs @@ -475,8 +475,8 @@ async fn infer_the_mime_type( "txt" => "text/plain", _ => { if charset.is_empty() { - todo!("file ext='{ext}' mime_type='{mime_type}' is not supported"); // "TODO: This filetype is not supported because of the missing mime type!", + return Err(not_implemented(())); }; mime_type } diff --git a/core/src/custom_uri/utils.rs b/core/src/custom_uri/utils.rs index 6b5401c11..0b73719d5 100644 --- a/core/src/custom_uri/utils.rs +++ b/core/src/custom_uri/utils.rs @@ -31,7 +31,7 @@ pub(crate) fn not_found(err: impl Debug) -> http::Response { #[track_caller] pub(crate) fn internal_server_error(err: impl Debug) -> http::Response { debug!( - "500 - Internal Server Error at {}: {err:?}", + "500: Internal Server Error at {}: {err:?}", Location::caller() ); @@ -40,6 +40,15 @@ pub(crate) fn internal_server_error(err: impl Debug) -> http::Response .body(body::boxed(Full::from(""))) } +#[track_caller] +pub(crate) fn not_implemented(err: impl Debug) -> http::Response { + debug!("501: Not Implemented at {}: {err:?}", Location::caller()); + + InfallibleResponse::builder() + .status(StatusCode::NOT_IMPLEMENTED) + .body(body::boxed(Full::from(""))) +} + pub(crate) async fn cors_middleware(req: Request, next: Next) -> Response { if req.method() == Method::OPTIONS { return Response::builder() diff --git a/interface/app/$libraryId/Explorer/FilePath/Thumb.tsx b/interface/app/$libraryId/Explorer/FilePath/Thumb.tsx index 7384f4b1a..9a2f561e0 100644 --- a/interface/app/$libraryId/Explorer/FilePath/Thumb.tsx +++ b/interface/app/$libraryId/Explorer/FilePath/Thumb.tsx @@ -2,6 +2,7 @@ import { getIcon, getIconByName, iconNames } from '@sd/assets/util'; import clsx from 'clsx'; import { memo, + SyntheticEvent, useCallback, useEffect, useLayoutEffect, @@ -37,6 +38,8 @@ export interface ThumbProps { size?: number; cover?: boolean; frame?: boolean; + onLoad?: (state: ThumbType) => void; + onError?: (state: ThumbType, error: Error) => void; blackBars?: boolean; blackBarsSize?: number; extension?: boolean; @@ -69,16 +72,31 @@ export const FileThumb = memo((props: ThumbProps) => { isDark ? classes.checkers : classes.checkersLight ); - const onLoad = useCallback(() => setLoaded(true), []); + const _onLoad = props.onLoad; + const onLoad = useCallback(() => { + setLoaded(true); + _onLoad?.(thumbType); + }, [_onLoad, thumbType]); - const onError = useCallback(() => { - setLoaded(false); - setThumbType((prevThumbType) => - prevThumbType === ThumbType.Original && itemData.hasLocalThumbnail - ? ThumbType.Thumbnail - : ThumbType.Icon - ); - }, [itemData.hasLocalThumbnail]); + const _onError = props.onError; + const onError = useCallback( + (event: ErrorEvent | SyntheticEvent) => { + setLoaded(false); + setThumbType((prevThumbType) => + prevThumbType === ThumbType.Original && itemData.hasLocalThumbnail + ? ThumbType.Thumbnail + : ThumbType.Icon + ); + + const rawError = + ('error' in event && event.error) || + ('message' in event && event.message) || + 'Filetype is not supported yet'; + + _onError?.(thumbType, rawError instanceof Error ? rawError : new Error(rawError)); + }, + [_onError, thumbType, itemData.hasLocalThumbnail] + ); // useLayoutEffect is required to ensure the thumbType is always updated before the onError listener can execute, // thus avoiding improper thumb types changes @@ -251,6 +269,11 @@ export const FileThumb = memo((props: ThumbProps) => { )} ); + default: + setThumbType(ThumbType.Thumbnail); + return null; + case 'Image': + // Fallthrough as image and thumbnail use the same component } } diff --git a/interface/app/$libraryId/Explorer/OptionsPanel.tsx b/interface/app/$libraryId/Explorer/OptionsPanel.tsx index dc3035dfd..06e37ec16 100644 --- a/interface/app/$libraryId/Explorer/OptionsPanel.tsx +++ b/interface/app/$libraryId/Explorer/OptionsPanel.tsx @@ -177,13 +177,13 @@ export default () => {