Show download queue size in nav bar

This commit is contained in:
schroda
2025-05-24 17:10:29 +02:00
parent 2d34f67df4
commit 86578593ca
6 changed files with 66 additions and 18 deletions

View File

@@ -164,6 +164,10 @@
"failed_to_retry": "Could not retry failed download." "failed_to_retry": "Could not retry failed download."
} }
}, },
"info": {
"paused": "Paused — $t(download.queue.info.remaining)",
"remaining": "{{count}} remaining"
},
"label": { "label": {
"delete_all": "Delete all", "delete_all": "Delete all",
"no_downloads": "No downloads" "no_downloads": "No downloads"
@@ -455,6 +459,7 @@
"sort": "Sort", "sort": "Sort",
"standard": "Standard", "standard": "Standard",
"started": "Started", "started": "Started",
"stopped": "Stopped",
"success": "Success", "success": "Success",
"system": "System", "system": "System",
"type": "Type", "type": "Type",

View File

@@ -19,8 +19,11 @@ import GetAppOutlinedIcon from '@mui/icons-material/GetAppOutlined';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import SettingsIcon from '@mui/icons-material/Settings'; import SettingsIcon from '@mui/icons-material/Settings';
import InfoIcon from '@mui/icons-material/Info'; import InfoIcon from '@mui/icons-material/Info';
import { useTranslation } from 'react-i18next';
import { NavbarItem, NavBarItemMoreGroup } from '@/modules/navigation-bar/NavigationBar.types.ts'; import { NavbarItem, NavBarItemMoreGroup } from '@/modules/navigation-bar/NavigationBar.types.ts';
import { AppRoutes } from '@/modules/core/AppRoute.constants.ts'; import { AppRoutes } from '@/modules/core/AppRoute.constants.ts';
import { requestManager } from '@/lib/requests/RequestManager.ts';
import { DownloaderState } from '@/lib/graphql/generated/graphql.ts';
type RestrictedNavBarItem<Show extends NavbarItem['show']> = Omit<NavbarItem, 'show'> & { show: Show }; type RestrictedNavBarItem<Show extends NavbarItem['show']> = Omit<NavbarItem, 'show'> & { show: Show };
@@ -67,6 +70,19 @@ const NAVIGATION_BAR_DESKTOP_ITEMS = [
IconComponent: GetAppOutlinedIcon, IconComponent: GetAppOutlinedIcon,
show: 'desktop', show: 'desktop',
moreGroup: NavBarItemMoreGroup.HIDDEN_ITEM, moreGroup: NavBarItemMoreGroup.HIDDEN_ITEM,
useBadge: () => {
const { t } = useTranslation();
const { data } = requestManager.useGetDownloadStatus();
const downloadStatus = data?.downloadStatus;
const isPaused = downloadStatus?.state === DownloaderState.Stopped;
const count = downloadStatus?.queue.length ?? 0;
return {
count,
title: t(isPaused ? 'download.queue.info.paused' : 'download.queue.info.remaining', { count }),
};
},
}, },
{ {
path: AppRoutes.settings.path, path: AppRoutes.settings.path,

View File

@@ -30,6 +30,7 @@ export interface NavbarItem {
IconComponent: OverridableComponent<SvgIconTypeMap<{}, 'svg'>>; IconComponent: OverridableComponent<SvgIconTypeMap<{}, 'svg'>>;
show: 'mobile' | 'desktop' | 'both'; show: 'mobile' | 'desktop' | 'both';
moreGroup: NavBarItemMoreGroup; moreGroup: NavBarItemMoreGroup;
useBadge?: () => { count: number; title: string };
} }
export type NavbarContextType = { export type NavbarContextType = {

View File

@@ -20,6 +20,8 @@ import { styled, useTheme } from '@mui/material/styles';
import { useCallback, useMemo, useRef } from 'react'; import { useCallback, useMemo, useRef } from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import ListItem from '@mui/material/ListItem'; import ListItem from '@mui/material/ListItem';
import Badge from '@mui/material/Badge';
import Typography from '@mui/material/Typography';
import { CustomTooltip } from '@/modules/core/components/CustomTooltip.tsx'; import { CustomTooltip } from '@/modules/core/components/CustomTooltip.tsx';
import { ListItemLink } from '@/modules/core/components/lists/ListItemLink.tsx'; import { ListItemLink } from '@/modules/core/components/lists/ListItemLink.tsx';
import { useGetOptionForDirection } from '@/modules/theme/services/ThemeCreator.ts'; import { useGetOptionForDirection } from '@/modules/theme/services/ThemeCreator.ts';
@@ -37,11 +39,12 @@ const DrawerHeader = styled('div')(({ theme }) => ({
...theme.mixins.toolbar, ...theme.mixins.toolbar,
})); }));
const NavigationBarItem = ({ path, title, IconComponent, SelectedIconComponent }: NavbarItem) => { const NavigationBarItem = ({ path, title, IconComponent, SelectedIconComponent, useBadge }: NavbarItem) => {
const { t } = useTranslation(); const { t } = useTranslation();
const location = useLocation(); const location = useLocation();
const { isCollapsed } = useNavBarContext(); const { isCollapsed } = useNavBarContext();
const theme = useTheme(); const theme = useTheme();
const badgeInfo = useBadge?.();
const isActive = location.pathname.startsWith(path); const isActive = location.pathname.startsWith(path);
const Icon = isActive ? SelectedIconComponent : IconComponent; const Icon = isActive ? SelectedIconComponent : IconComponent;
@@ -56,9 +59,19 @@ const NavigationBarItem = ({ path, title, IconComponent, SelectedIconComponent }
return ( return (
<ListItemLink selected={!isCollapsed && isActive} sx={{ p: 0, m: 0 }} to={path}> <ListItemLink selected={!isCollapsed && isActive} sx={{ p: 0, m: 0 }} to={path}>
<CustomTooltip title={t(title)} placement="right"> <CustomTooltip
title={
<>
{t(title)}
<br />
{badgeInfo?.title}
</>
}
placement="right"
>
<ListItem sx={listItemProps}> <ListItem sx={listItemProps}>
<ListItemIcon sx={listItemIconProps}> <ListItemIcon sx={listItemIconProps}>
<Badge badgeContent={badgeInfo?.count} color="primary">
<Icon <Icon
sx={{ sx={{
color: isActive ? 'primary.dark' : undefined, color: isActive ? 'primary.dark' : undefined,
@@ -67,7 +80,9 @@ const NavigationBarItem = ({ path, title, IconComponent, SelectedIconComponent }
}), }),
}} }}
/> />
</Badge>
</ListItemIcon> </ListItemIcon>
<ListItemText <ListItemText
primary={ primary={
<TypographyMaxLines <TypographyMaxLines
@@ -83,7 +98,14 @@ const NavigationBarItem = ({ path, title, IconComponent, SelectedIconComponent }
{t(title)} {t(title)}
</TypographyMaxLines> </TypographyMaxLines>
} }
sx={{ maxWidth: '100%', m: 0 }} secondary={
!isCollapsed && (
<Typography variant="caption" color="textSecondary">
{badgeInfo?.title}
</Typography>
)
}
sx={{ maxWidth: '100%', m: 0, display: 'flex', flexDirection: 'column' }}
/> />
</ListItem> </ListItem>
</CustomTooltip> </CustomTooltip>

View File

@@ -10,9 +10,10 @@ import BottomNavigation from '@mui/material/BottomNavigation';
import BottomNavigationAction from '@mui/material/BottomNavigationAction'; import BottomNavigationAction from '@mui/material/BottomNavigationAction';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper';
import { useCallback, useLayoutEffect, useRef, useState } from 'react'; import { CSSProperties, useCallback, useLayoutEffect, useRef, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import { CSSObject, useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import Badge from '@mui/material/Badge';
import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx'; import { useResizeObserver } from '@/modules/core/hooks/useResizeObserver.tsx';
import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx'; import { useNavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
import { NavbarItem } from '@/modules/navigation-bar/NavigationBar.types.ts'; import { NavbarItem } from '@/modules/navigation-bar/NavigationBar.types.ts';
@@ -52,7 +53,7 @@ export const MobileBottomBar = ({ navBarItems }: { navBarItems: NavbarItem[] })
style={{ style={{
...(theme.applyStyles('dark', { ...(theme.applyStyles('dark', {
'--Paper-overlay': 'unset', '--Paper-overlay': 'unset',
}) as Omit<CSSObject, 'accentColorsd'>), }) as CSSProperties),
}} }}
elevation={3} elevation={3}
> >
@@ -64,12 +65,15 @@ export const MobileBottomBar = ({ navBarItems }: { navBarItems: NavbarItem[] })
navigate(newValue); navigate(newValue);
}} }}
> >
{navBarItems.map(({ path, title, IconComponent, SelectedIconComponent }) => ( {navBarItems.map(({ path, title, IconComponent, SelectedIconComponent, useBadge }) => (
<BottomNavigationAction <BottomNavigationAction
key={path}
value={path} value={path}
label={t(title)} label={t(title)}
icon={selectedNavBarItem === path ? <SelectedIconComponent /> : <IconComponent />} icon={
<Badge key={path} badgeContent={useBadge?.().count} color="primary">
{selectedNavBarItem === path ? <SelectedIconComponent /> : <IconComponent />}
</Badge>
}
/> />
))} ))}
</BottomNavigation> </BottomNavigation>

View File

@@ -68,7 +68,7 @@ export const More = () => {
<ListItemIcon> <ListItemIcon>
<item.IconComponent /> <item.IconComponent />
</ListItemIcon> </ListItemIcon>
<ListItemText primary={t(item.title)} /> <ListItemText primary={t(item.title)} secondary={item.useBadge?.().title} />
</ListItemLink> </ListItemLink>
))} ))}
{index !== list.length - 1 && <Divider />} {index !== list.length - 1 && <Divider />}