Download tune and logs (#857)

This commit is contained in:
Piotr Rogowski 2022-10-28 22:17:40 +02:00 committed by GitHub
parent 2fc6398b1e
commit 49d659f817
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 30 deletions

View File

@ -1,6 +1,7 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"cSpell.words": [
"hypertuner",
"kbar",
"pocketbase",
"prefs",

View File

@ -1,7 +1,11 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
/* eslint-disable jsx-a11y/anchor-has-content */
import {
useCallback,
useEffect,
useMemo,
useRef,
} from 'react';
import {
useLocation,
@ -31,10 +35,7 @@ import {
LoginOutlined,
LineChartOutlined,
SlidersOutlined,
FileExcelOutlined,
FileTextOutlined,
FileZipOutlined,
SaveOutlined,
DesktopOutlined,
DownOutlined,
SearchOutlined,
@ -44,6 +45,8 @@ import {
LogoutOutlined,
InfoCircleOutlined,
CarOutlined,
FileTextOutlined,
FileExcelOutlined,
} from '@ant-design/icons';
import { useKBar } from 'kbar';
import store from '../store';
@ -53,10 +56,17 @@ import { Routes } from '../routes';
import { useAuth } from '../contexts/AuthContext';
import { logOutSuccessful } from '../pages/auth/notifications';
import { TuneDataState } from '../types/state';
import { removeFilenameSuffix } from '../pocketbase';
import useServerStorage from '../hooks/useServerStorage';
const { Header } = Layout;
const { useBreakpoint } = Grid;
const { SubMenu } = Menu;
const logsExtensionsIcons: { [key: string]: any } = {
'mlg': <FileZipOutlined />,
'msl': <FileTextOutlined />,
'csv': <FileExcelOutlined />,
};
const TopBar = ({
tuneData,
@ -77,6 +87,8 @@ const TopBar = ({
const tabMatch = useMatch(`${Routes.TUNE_TAB}/*`);
const uploadMatch = useMatch(Routes.UPLOAD);
const hubMatch = useMatch(Routes.HUB);
const { downloadFile } = useServerStorage();
const downloadAnchorRef = useRef<HTMLAnchorElement | null>(null);
const logoutClick = useCallback(() => {
logout();
logOutSuccessful();
@ -92,6 +104,54 @@ const TopBar = ({
}
}, []);
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,
];
useEffect(() => {
document.addEventListener('keydown', handleGlobalKeyboard);
@ -212,31 +272,9 @@ const TopBar = ({
{lg && 'Upload'}
</Button>
</Link>
<Dropdown
{tuneData?.tuneId && <Dropdown
overlay={
<Menu disabled>
<SubMenu key="tune-sub" title="Tune" icon={<SlidersOutlined />}>
<Menu.Item key="download" icon={<SaveOutlined />}>
<a href="/tunes/202103.msq" target="__blank" rel="noopener noreferrer">
Download
</a>
</Menu.Item>
<Menu.Item key="open" disabled icon={<DesktopOutlined />}>
Open in app
</Menu.Item>
</SubMenu>
<SubMenu key="logs-sub" title="Logs" icon={<LineChartOutlined />}>
<Menu.Item key="mlg" disabled icon={<FileZipOutlined />}>
MLG
</Menu.Item>
<Menu.Item key="msl" disabled icon={<FileTextOutlined />}>
MSL
</Menu.Item>
<Menu.Item key="csv" disabled icon={<FileExcelOutlined />}>
CSV
</Menu.Item>
</SubMenu>
</Menu>
<Menu triggerSubMenuAction="click" items={downloadItems} />
}
placement="bottom"
trigger={['click']}
@ -244,7 +282,7 @@ const TopBar = ({
<Button icon={<CloudDownloadOutlined />}>
{downloadButton}
</Button>
</Dropdown>
</Dropdown>}
<Dropdown
overlay={<Menu items={userMenuItems} />}
placement="bottomRight"
@ -254,6 +292,8 @@ const TopBar = ({
{sm && <DownOutlined />}
</Button>
</Dropdown>
{/* dummy anchor for file download */}
<a ref={downloadAnchorRef} style={{ display: 'none' }} />
</Space>
</Col>
</Row>

View File

@ -1,12 +1,16 @@
import Pako from 'pako';
import * as Sentry from '@sentry/browser';
import { API_URL } from '../pocketbase';
import {
API_URL,
removeFilenameSuffix,
} from '../pocketbase';
import { Collections } from '../@types/pocketbase-types';
import useDb from './useDb';
import {
fetchWithProgress,
OnProgress,
} from '../utils/http';
import { downloading } from '../pages/auth/notifications';
const useServerStorage = () => {
const { getIni } = useDb();
@ -43,10 +47,28 @@ const useServerStorage = () => {
signal,
).then((response) => response);
const downloadFile = async (recordId: string, filename: string, anchorRef: HTMLAnchorElement) => {
downloading();
const response = await fetch(buildFileUrl(Collections.Tunes, recordId, filename));
const data = Pako.inflate(new Uint8Array(await response.arrayBuffer()));
const url = window.URL.createObjectURL(new Blob([data]));
// eslint-disable-next-line no-param-reassign
anchorRef.href = url;
// eslint-disable-next-line no-param-reassign
anchorRef.target = '_blank';
// eslint-disable-next-line no-param-reassign
anchorRef.download = removeFilenameSuffix(filename);
anchorRef.click();
window.URL.revokeObjectURL(url);
};
return {
fetchTuneFile: (recordId: string, filename: string): Promise<ArrayBuffer> => fetchTuneFile(recordId, filename),
fetchINIFile: (signature: string): Promise<ArrayBuffer> => fetchINIFile(signature),
fetchLogFileWithProgress: (recordId: string, filename: string, onProgress?: OnProgress, signal?: AbortSignal): Promise<ArrayBuffer> => fetchLogFileWithProgress(recordId, filename, onProgress, signal),
downloadFile: (recordId: string, filename: string, anchorRef: HTMLAnchorElement): Promise<void> => downloadFile(recordId, filename, anchorRef),
};
};

View File

@ -147,6 +147,12 @@ const signatureNotSupportedWarning = (message: string) => notification.warning({
...baseOptions,
});
const downloading = () => notification.success({
message: 'Downloading...',
...baseOptions,
duration: 1,
});
export {
error,
emailNotVerified,
@ -172,4 +178,5 @@ export {
iniLoadingError,
tuneParsingError,
signatureNotSupportedWarning,
downloading,
};