hyper-tuner-cloud/src/components/TopBar.tsx

305 lines
8.9 KiB
TypeScript
Raw Normal View History

2022-10-28 13:17:40 -07:00
/* eslint-disable jsx-a11y/anchor-is-valid */
/* eslint-disable jsx-a11y/anchor-has-content */
import {
useCallback,
useEffect,
2022-02-07 13:12:26 -08:00
useMemo,
2022-10-28 13:17:40 -07:00
useRef,
} from 'react';
2021-03-22 14:29:03 -07:00
import {
useLocation,
2022-01-25 12:18:04 -08:00
useNavigate,
2022-01-09 14:33:38 -08:00
Link,
generatePath,
2022-01-25 12:18:04 -08:00
useMatch,
2022-01-09 14:33:38 -08:00
} from 'react-router-dom';
2021-03-22 14:29:03 -07:00
import {
Layout,
Space,
Button,
Row,
Col,
Tooltip,
Grid,
Menu,
Dropdown,
Typography,
Radio,
} from 'antd';
import {
UserOutlined,
CloudUploadOutlined,
CloudDownloadOutlined,
SettingOutlined,
LoginOutlined,
LineChartOutlined,
SlidersOutlined,
FileZipOutlined,
DesktopOutlined,
DownOutlined,
SearchOutlined,
2021-03-22 15:24:39 -07:00
ToolOutlined,
FundOutlined,
2021-12-25 14:32:07 -08:00
UserAddOutlined,
LogoutOutlined,
2022-01-16 09:32:55 -08:00
InfoCircleOutlined,
2022-01-24 13:59:21 -08:00
CarOutlined,
2022-10-28 13:17:40 -07:00
FileTextOutlined,
FileExcelOutlined,
2021-03-22 14:29:03 -07:00
} from '@ant-design/icons';
import { useKBar } from 'kbar';
2021-03-22 14:29:03 -07:00
import store from '../store';
import { isMac } from '../utils/env';
import { isToggleSidebar } from '../utils/keyboard/shortcuts';
2021-03-22 14:29:03 -07:00
import { Routes } from '../routes';
2021-12-25 14:32:07 -08:00
import { useAuth } from '../contexts/AuthContext';
2022-10-17 13:28:33 -07:00
import { logOutSuccessful } from '../pages/auth/notifications';
2022-10-19 14:24:45 -07:00
import { TuneDataState } from '../types/state';
2022-10-28 13:17:40 -07:00
import { removeFilenameSuffix } from '../pocketbase';
import useServerStorage from '../hooks/useServerStorage';
2021-03-22 14:29:03 -07:00
const { Header } = Layout;
const { useBreakpoint } = Grid;
2022-10-28 13:17:40 -07:00
const logsExtensionsIcons: { [key: string]: any } = {
'mlg': <FileZipOutlined />,
'msl': <FileTextOutlined />,
'csv': <FileExcelOutlined />,
};
2021-03-22 14:29:03 -07:00
2022-10-19 14:24:45 -07:00
const TopBar = ({
tuneData,
}: {
tuneData: TuneDataState | null;
}) => {
2022-07-17 13:15:06 -07:00
const { xs, sm, lg } = useBreakpoint();
2021-03-22 14:29:03 -07:00
const { pathname } = useLocation();
2021-12-25 14:32:07 -08:00
const { currentUser, logout } = useAuth();
2022-01-25 12:18:04 -08:00
const navigate = useNavigate();
const { query } = useKBar();
2022-10-19 14:24:45 -07:00
const buildTuneUrl = useCallback((route: string) => tuneData?.tuneId ? generatePath(route, { tuneId: tuneData.tuneId }) : null, [tuneData?.tuneId]);
2022-02-13 14:14:28 -08:00
const hubPathMatch = useMatch(Routes.HUB);
2022-01-25 12:18:04 -08:00
const tuneRootMatch = useMatch(`${Routes.TUNE_ROOT}/*`);
const tuneTuneMatch = useMatch(`${Routes.TUNE_TUNE}/*`);
2022-10-18 12:40:53 -07:00
const tuneLogMatch = useMatch(`${Routes.TUNE_LOGS}/*`);
2022-10-19 05:47:47 -07:00
const toothLogMatch = useMatch(`${Routes.TUNE_DIAGNOSE}/*`);
2022-01-25 12:18:04 -08:00
const tabMatch = useMatch(`${Routes.TUNE_TAB}/*`);
const uploadMatch = useMatch(Routes.UPLOAD);
2022-02-13 14:14:28 -08:00
const hubMatch = useMatch(Routes.HUB);
2022-10-28 13:17:40 -07:00
const { downloadFile } = useServerStorage();
const downloadAnchorRef = useRef<HTMLAnchorElement | null>(null);
2022-10-17 13:28:33 -07:00
const logoutClick = useCallback(() => {
logout();
logOutSuccessful();
navigate(Routes.HUB);
2022-02-13 14:14:28 -08:00
}, [logout, navigate]);
2021-03-22 14:29:03 -07:00
const toggleCommandPalette = useCallback(() => query.toggle(), [query]);
const handleGlobalKeyboard = useCallback((e: KeyboardEvent) => {
2021-03-22 14:29:03 -07:00
if (isToggleSidebar(e)) {
e.preventDefault();
store.dispatch({ type: 'ui/toggleSidebar' });
}
}, []);
2022-10-28 13:17:40 -07:00
const downloadLogsItems = {
label: 'Logs',
icon: <LineChartOutlined />,
key: 'logs',
children: (tuneData?.logFiles || []).map((filename) => ({
key: filename,
label: removeFilenameSuffix(filename),
icon: logsExtensionsIcons[filename.slice(-3)],
onClick: () => downloadFile(tuneData!.id, filename, downloadAnchorRef.current!),
})),
};
const downloadToothLogsItems = {
label: 'Tooth logs',
icon: <SettingOutlined />,
key: 'toothLogs',
children: (tuneData?.toothLogFiles || []).map((filename) => ({
key: filename,
label: removeFilenameSuffix(filename),
icon: logsExtensionsIcons[filename.slice(-3)],
onClick: () => downloadFile(tuneData!.id, filename, downloadAnchorRef.current!),
})),
};
const downloadItems = [
{
label: 'Tune',
icon: <SlidersOutlined />,
key: 'tune',
children: [
{
label: 'Download',
icon: <FileTextOutlined />,
key: 'download',
onClick: () => downloadFile(tuneData!.id, tuneData!.tuneFile, downloadAnchorRef.current!),
},
{
label: 'Open in app',
icon: <DesktopOutlined />,
key: 'open',
onClick: () => window.open(`hypertuner://hypertuner.cloud/t/${tuneData!.tuneId}`, '_blank'),
},
],
},
(tuneData?.logFiles || []).length > 0 ? { ...downloadLogsItems } : null,
(tuneData?.toothLogFiles || []).length > 0 ? { ...downloadToothLogsItems } : null,
];
2021-03-22 14:29:03 -07:00
useEffect(() => {
document.addEventListener('keydown', handleGlobalKeyboard);
return () => document.removeEventListener('keydown', handleGlobalKeyboard);
}, [currentUser, handleGlobalKeyboard]);
2021-03-22 14:29:03 -07:00
2022-02-07 13:12:26 -08:00
const tabs = useMemo(() => (
2022-02-13 14:14:28 -08:00
<Col span={16} md={16} sm={16}>
2022-01-09 14:33:38 -08:00
<Radio.Group
key={pathname}
2022-10-19 05:47:47 -07:00
defaultValue={tuneLogMatch?.pathnameBase || toothLogMatch?.pathnameBase || tuneTuneMatch?.pathnameBase || tabMatch?.pathname || tuneRootMatch?.pathname || hubPathMatch?.pathname}
2022-01-09 14:33:38 -08:00
optionType="button"
buttonStyle="solid"
2022-01-25 12:18:04 -08:00
onChange={(e) => navigate(e.target.value)}
2022-01-09 14:33:38 -08:00
>
2022-02-13 14:14:28 -08:00
<Radio.Button value={buildTuneUrl(Routes.HUB)}>
2022-01-24 13:59:21 -08:00
<Space>
<CarOutlined />
{lg && 'Hub'}
</Space>
</Radio.Button>
2022-01-16 09:32:55 -08:00
<Radio.Button value={buildTuneUrl(Routes.TUNE_ROOT)}>
<Space>
<InfoCircleOutlined />
2022-01-24 13:59:21 -08:00
{lg && 'Info'}
2022-01-16 09:32:55 -08:00
</Space>
</Radio.Button>
2022-01-09 14:33:38 -08:00
<Radio.Button value={buildTuneUrl(Routes.TUNE_TUNE)}>
<Space>
<ToolOutlined />
2022-01-24 13:59:21 -08:00
{lg && 'Tune'}
2022-01-09 14:33:38 -08:00
</Space>
</Radio.Button>
2022-10-19 14:24:45 -07:00
<Radio.Button value={buildTuneUrl(Routes.TUNE_LOGS)} disabled={(tuneData?.logFiles || []).length === 0}>
2022-01-09 14:33:38 -08:00
<Space>
<FundOutlined />
2022-01-24 13:59:21 -08:00
{lg && 'Logs'}
2022-01-09 14:33:38 -08:00
</Space>
</Radio.Button>
2022-10-19 14:24:45 -07:00
<Radio.Button value={buildTuneUrl(Routes.TUNE_DIAGNOSE)} disabled={(tuneData?.toothLogFiles || []).length === 0}>
2022-01-09 14:33:38 -08:00
<Space>
<SettingOutlined />
2022-01-24 13:59:21 -08:00
{lg && 'Diagnose'}
2022-01-09 14:33:38 -08:00
</Space>
</Radio.Button>
</Radio.Group>
</Col>
2022-10-19 14:24:45 -07:00
), [pathname, tuneLogMatch?.pathnameBase, toothLogMatch?.pathnameBase, tuneTuneMatch?.pathnameBase, tabMatch?.pathname, tuneRootMatch?.pathname, hubPathMatch?.pathname, buildTuneUrl, lg, tuneData?.logFiles, tuneData?.toothLogFiles, navigate]);
2022-01-09 14:33:38 -08:00
2022-10-19 14:24:45 -07:00
const rightMenuColProps = tuneData?.tuneId ? {
2022-02-07 13:12:26 -08:00
span: 8,
md: 8,
sm: 8,
} : {
span: 14,
md: 10,
sm: 8,
};
2022-02-07 13:12:26 -08:00
const downloadButton = useMemo(() => {
const list = [];
if (lg) {
2022-02-07 13:31:26 -08:00
list.push(<span key="download-text">Download</span>);
2022-02-07 13:12:26 -08:00
}
if (sm) {
2022-02-07 13:31:26 -08:00
list.push(<DownOutlined key="download-icon" />);
2022-02-07 13:12:26 -08:00
}
return list.length ? list : null;
}, [lg, sm]);
2022-07-17 11:55:10 -07:00
const userMenuItems = useMemo(() => currentUser ? [{
key: 'profile',
icon: <UserOutlined />,
label: 'Profile',
onClick: () => navigate(Routes.PROFILE),
}, {
key: 'logout',
icon: <LogoutOutlined />,
label: 'Logout',
onClick: logoutClick,
}] : [{
key: 'login',
icon: <LoginOutlined />,
label: 'Login',
onClick: () => navigate(Routes.LOGIN),
}, {
key: 'sign-up',
icon: <UserAddOutlined />,
label: 'Sign Up',
onClick: () => navigate(Routes.SIGN_UP),
}], [currentUser, logoutClick, navigate]);
2021-03-22 14:29:03 -07:00
return (
2022-10-17 13:28:33 -07:00
<Header className="app-top-bar" style={xs ? { padding: '0 5px' } : {}}>
2021-03-22 14:29:03 -07:00
<Row>
2022-10-19 14:24:45 -07:00
{tuneData?.tuneId ? tabs : (
2022-02-13 14:14:28 -08:00
<Col span={10} md={14} sm={16}>
<Link to={Routes.HUB}>
<Button icon={<CarOutlined />} type={hubMatch ? 'primary' : 'default'}>Hub</Button>
</Link>
</Col>
)}
<Col {...rightMenuColProps} style={{ textAlign: 'right' }}>
<Space>
{sm && <Tooltip title={
<>
<Typography.Text keyboard>{isMac ? '⌘' : 'CTRL'}</Typography.Text>
<Typography.Text keyboard>K</Typography.Text>
</>
2021-03-22 14:29:03 -07:00
}>
<Button icon={<SearchOutlined />} onClick={toggleCommandPalette} />
2022-01-16 09:53:36 -08:00
</Tooltip>}
2022-01-02 13:25:52 -08:00
<Link to={Routes.UPLOAD}>
2022-01-25 12:18:04 -08:00
<Button icon={<CloudUploadOutlined />} type={uploadMatch ? 'primary' : 'default'}>
2022-01-02 13:25:52 -08:00
{lg && 'Upload'}
</Button>
</Link>
2022-10-28 13:17:40 -07:00
{tuneData?.tuneId && <Dropdown
2022-01-02 13:25:52 -08:00
overlay={
2022-10-28 13:17:40 -07:00
<Menu triggerSubMenuAction="click" items={downloadItems} />
2022-01-02 13:25:52 -08:00
}
placement="bottom"
2021-03-23 12:36:06 -07:00
trigger={['click']}
2021-03-22 14:29:03 -07:00
>
2022-01-02 13:25:52 -08:00
<Button icon={<CloudDownloadOutlined />}>
2022-02-07 13:12:26 -08:00
{downloadButton}
2021-03-22 14:29:03 -07:00
</Button>
2022-10-28 13:17:40 -07:00
</Dropdown>}
2021-03-22 14:29:03 -07:00
<Dropdown
2022-07-17 11:55:10 -07:00
overlay={<Menu items={userMenuItems} />}
placement="bottomRight"
2021-03-23 12:36:06 -07:00
trigger={['click']}
2021-03-22 14:29:03 -07:00
>
<Button icon={<UserOutlined />}>
2022-01-16 09:53:36 -08:00
{sm && <DownOutlined />}
2021-03-22 14:29:03 -07:00
</Button>
</Dropdown>
2022-10-28 13:17:40 -07:00
{/* dummy anchor for file download */}
<a ref={downloadAnchorRef} style={{ display: 'none' }} />
2021-03-22 14:29:03 -07:00
</Space>
</Col>
</Row>
</Header>
);
};
export default TopBar;