Handle long extra messages in EmptyView
This commit is contained in:
@@ -60,7 +60,7 @@ export function Browse() {
|
|||||||
<Tab value={Tabs.MIGRATE} sx={{ textTransform: 'none' }} label={t('migrate.title')} />
|
<Tab value={Tabs.MIGRATE} sx={{ textTransform: 'none' }} label={t('migrate.title')} />
|
||||||
</TabsMenu>
|
</TabsMenu>
|
||||||
<TabPanel index={Tabs.SOURCE} currentIndex={tabName}>
|
<TabPanel index={Tabs.SOURCE} currentIndex={tabName}>
|
||||||
<Sources />
|
<Sources tabsMenuHeight={tabsMenuHeight} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel index={Tabs.EXTENSIONS} currentIndex={tabName}>
|
<TabPanel index={Tabs.EXTENSIONS} currentIndex={tabName}>
|
||||||
<Extensions tabsMenuHeight={tabsMenuHeight} />
|
<Extensions tabsMenuHeight={tabsMenuHeight} />
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx';
|
|||||||
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
|
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
|
||||||
import { shouldForwardProp } from '@/modules/core/utils/ShouldForwardProp.ts';
|
import { shouldForwardProp } from '@/modules/core/utils/ShouldForwardProp.ts';
|
||||||
import { useChapterOptions } from '@/modules/chapter/hooks/useChapterOptions.tsx';
|
import { useChapterOptions } from '@/modules/chapter/hooks/useChapterOptions.tsx';
|
||||||
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
|
|
||||||
type ChapterListHeaderProps = {
|
type ChapterListHeaderProps = {
|
||||||
scrollbarWidth: number;
|
scrollbarWidth: number;
|
||||||
@@ -175,7 +176,7 @@ export const ChapterList = ({
|
|||||||
<Stack sx={{ justifyContent: 'center', position: 'relative', flexGrow: 1 }}>
|
<Stack sx={{ justifyContent: 'center', position: 'relative', flexGrow: 1 }}>
|
||||||
<EmptyViewAbsoluteCentered
|
<EmptyViewAbsoluteCentered
|
||||||
message={t('global.error.label.failed_to_load_data')}
|
message={t('global.error.label.failed_to_load_data')}
|
||||||
messageExtra={error.message}
|
messageExtra={getErrorMessage(error)}
|
||||||
retry={() => refetch().catch(defaultPromiseErrorHandler('ChapterList::refetch'))}
|
retry={() => refetch().catch(defaultPromiseErrorHandler('ChapterList::refetch'))}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ export interface EmptyViewProps {
|
|||||||
messageExtra?: JSX.Element | string;
|
messageExtra?: JSX.Element | string;
|
||||||
retry?: () => void;
|
retry?: () => void;
|
||||||
noFaces?: boolean;
|
noFaces?: boolean;
|
||||||
|
topOffset?: number;
|
||||||
sx?: SxProps<Theme>;
|
sx?: SxProps<Theme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function EmptyView({ message, messageExtra, retry, noFaces, sx }: EmptyViewProps) {
|
export function EmptyView({ message, messageExtra, retry, noFaces, topOffset = 0, sx }: EmptyViewProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const errorFace = useMemo(() => getRandomErrorFace(), []);
|
const errorFace = useMemo(() => getRandomErrorFace(), []);
|
||||||
@@ -41,7 +42,9 @@ export function EmptyView({ message, messageExtra, retry, noFaces, sx }: EmptyVi
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
height: '100%',
|
minWidth: '100%',
|
||||||
|
minHeight: `calc(100% - ${topOffset}px)`,
|
||||||
|
mt: `${topOffset}px`,
|
||||||
...sx,
|
...sx,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -51,7 +54,9 @@ export function EmptyView({ message, messageExtra, retry, noFaces, sx }: EmptyVi
|
|||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<Typography variant="h5">{message}</Typography>
|
<Typography variant="h5">{message}</Typography>
|
||||||
{messageExtra}
|
<Typography variant="body1" sx={{ wordBreak: 'break-word' }}>
|
||||||
|
{messageExtra}
|
||||||
|
</Typography>
|
||||||
{retry && <Button onClick={retry}>{t('global.button.retry')}</Button>}
|
{retry && <Button onClick={retry}>{t('global.button.retry')}</Button>}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,10 +14,8 @@ export function EmptyViewAbsoluteCentered({ sx, ...emptyViewProps }: EmptyViewPr
|
|||||||
{...emptyViewProps}
|
{...emptyViewProps}
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
height: undefined,
|
top: 0,
|
||||||
top: '50%',
|
left: 0,
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
...sx,
|
...sx,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ export function Extensions({ tabsMenuHeight }: { tabsMenuHeight: number }) {
|
|||||||
fetchExtensions().catch(defaultPromiseErrorHandler('Extensions::refetchExtensions'));
|
fetchExtensions().catch(defaultPromiseErrorHandler('Extensions::refetchExtensions'));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
topOffset={tabsMenuHeight}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ export const MangaGrid: React.FC<IMangaGridProps> = ({
|
|||||||
isLoading,
|
isLoading,
|
||||||
message,
|
message,
|
||||||
messageExtra,
|
messageExtra,
|
||||||
|
topOffset,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
loadMore,
|
loadMore,
|
||||||
gridLayout,
|
gridLayout,
|
||||||
@@ -348,6 +349,7 @@ export const MangaGrid: React.FC<IMangaGridProps> = ({
|
|||||||
message={message ?? t('manga.error.label.no_mangas_found')}
|
message={message ?? t('manga.error.label.no_mangas_found')}
|
||||||
messageExtra={messageExtra}
|
messageExtra={messageExtra}
|
||||||
retry={retry}
|
retry={retry}
|
||||||
|
topOffset={topOffset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ export const Migration = ({ tabsMenuHeight }: { tabsMenuHeight: number }) => {
|
|||||||
message={t('global.error.label.failed_to_load_data')}
|
message={t('global.error.label.failed_to_load_data')}
|
||||||
messageExtra={error.message}
|
messageExtra={error.message}
|
||||||
retry={() => refetch().catch(defaultPromiseErrorHandler('Migration::refetch'))}
|
retry={() => refetch().catch(defaultPromiseErrorHandler('Migration::refetch'))}
|
||||||
|
topOffset={tabsMenuHeight}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import { MangaIdInfo } from '@/modules/manga/Manga.types.ts';
|
|||||||
import { GridLayout } from '@/modules/core/Core.types.ts';
|
import { GridLayout } from '@/modules/core/Core.types.ts';
|
||||||
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
|
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
|
import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx';
|
||||||
|
|
||||||
const DEFAULT_SOURCE: Pick<SourceType, 'id'> = { id: '-1' };
|
const DEFAULT_SOURCE: Pick<SourceType, 'id'> = { id: '-1' };
|
||||||
|
|
||||||
@@ -249,6 +250,16 @@ export function SourceMangas() {
|
|||||||
const currentQuery = useRef(query);
|
const currentQuery = useRef(query);
|
||||||
const currentAbortRequest = useRef<(reason: any) => void>(() => {});
|
const currentAbortRequest = useRef<(reason: any) => void>(() => {});
|
||||||
|
|
||||||
|
const contentTypeMenuRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [contentTypeMenuHeight, setContentTypeMenuHeight] = useState(0);
|
||||||
|
useResizeObserver(
|
||||||
|
contentTypeMenuRef,
|
||||||
|
useCallback(
|
||||||
|
() => setContentTypeMenuHeight(contentTypeMenuRef.current?.clientHeight ?? 0),
|
||||||
|
[contentTypeMenuRef],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const didSearchChange = currentQuery.current !== query;
|
const didSearchChange = currentQuery.current !== query;
|
||||||
if (didSearchChange && contentType === SourceContentType.SEARCH) {
|
if (didSearchChange && contentType === SourceContentType.SEARCH) {
|
||||||
currentQuery.current = query;
|
currentQuery.current = query;
|
||||||
@@ -428,9 +439,11 @@ export function SourceMangas() {
|
|||||||
};
|
};
|
||||||
}, [t, source]);
|
}, [t, source]);
|
||||||
|
|
||||||
|
const EmptyViewComponent = mangas.length ? EmptyView : EmptyViewAbsoluteCentered;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledGridWrapper>
|
<StyledGridWrapper>
|
||||||
<ContentTypeMenu sx={{ top: `${appBarHeight}px` }}>
|
<ContentTypeMenu ref={contentTypeMenuRef} sx={{ top: `${appBarHeight}px` }}>
|
||||||
<ContentTypeButton
|
<ContentTypeButton
|
||||||
variant={contentType === SourceContentType.POPULAR ? 'contained' : 'outlined'}
|
variant={contentType === SourceContentType.POPULAR ? 'contained' : 'outlined'}
|
||||||
startIcon={<FavoriteIcon />}
|
startIcon={<FavoriteIcon />}
|
||||||
@@ -466,6 +479,7 @@ export function SourceMangas() {
|
|||||||
loadMore={loadMore}
|
loadMore={loadMore}
|
||||||
message={message}
|
message={message}
|
||||||
messageExtra={messageExtra}
|
messageExtra={messageExtra}
|
||||||
|
topOffset={contentTypeMenuHeight}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
gridLayout={sourceGridLayout}
|
gridLayout={sourceGridLayout}
|
||||||
mode="source"
|
mode="source"
|
||||||
@@ -473,18 +487,12 @@ export function SourceMangas() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{error && !mangas.length && (
|
{error && (
|
||||||
<EmptyViewAbsoluteCentered
|
<EmptyViewComponent
|
||||||
message={t('global.error.label.failed_to_load_data')}
|
message={t('global.error.label.failed_to_load_data')}
|
||||||
messageExtra={error.message}
|
messageExtra={getErrorMessage(error)}
|
||||||
retry={() => loadPage(lastPageNum).catch(defaultPromiseErrorHandler('SourceMangas::refetch'))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{error && !!mangas.length && (
|
|
||||||
<EmptyView
|
|
||||||
message={t('global.error.label.failed_to_load_data')}
|
|
||||||
messageExtra={error.message}
|
|
||||||
retry={() => loadPage(lastPageNum).catch(defaultPromiseErrorHandler('SourceMangas::refetch'))}
|
retry={() => loadPage(lastPageNum).catch(defaultPromiseErrorHandler('SourceMangas::refetch'))}
|
||||||
|
topOffset={contentTypeMenuHeight}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ function groupByLang<Source extends Pick<SourceType, 'id' | 'name' | 'lang'>>(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Sources() {
|
export function Sources({ tabsMenuHeight }: { tabsMenuHeight: number }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { setAction } = useContext(NavBarContext);
|
const { setAction } = useContext(NavBarContext);
|
||||||
|
|
||||||
@@ -138,12 +138,15 @@ export function Sources() {
|
|||||||
message={t('global.error.label.failed_to_load_data')}
|
message={t('global.error.label.failed_to_load_data')}
|
||||||
messageExtra={error.message}
|
messageExtra={error.message}
|
||||||
retry={() => refetch().catch(defaultPromiseErrorHandler('Sources::refetch'))}
|
retry={() => refetch().catch(defaultPromiseErrorHandler('Sources::refetch'))}
|
||||||
|
topOffset={tabsMenuHeight}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sources?.length === 0) {
|
if (sources?.length === 0) {
|
||||||
return <EmptyViewAbsoluteCentered message={t('source.error.label.no_sources_found')} />;
|
return (
|
||||||
|
<EmptyViewAbsoluteCentered message={t('source.error.label.no_sources_found')} topOffset={tabsMenuHeight} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user