Skip to content

Commit

Permalink
feat: Improve UI of disk analysis page and memoize components
Browse files Browse the repository at this point in the history
  • Loading branch information
pacholoamit committed Jun 19, 2024
1 parent 4225b7d commit 15e720a
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 76 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ All notable changes to this project will be documented in this file. This projec
- Implement logger for better debugging
- Implement better product analytics
- Implement better icons for the app
- Cleanup of Disk analysis code
- Improved UI of disk analysis page

## 0.9.9
- Fix issue where some popover components are transparent
Expand Down
4 changes: 2 additions & 2 deletions src/components/treemap-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Highcharts, { Point } from "highcharts";
import HighchartsReact from "highcharts-react-official";
import HighchartsTreemap from "highcharts/modules/treemap";
import DarkUnica from "highcharts/themes/dark-unica";
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
import React, { Dispatch, memo, SetStateAction, useEffect, useRef, useState } from "react";

import formatBytes from "@/features/metrics/utils/format-bytes";
import { useMantineTheme } from "@mantine/core";
Expand Down Expand Up @@ -100,4 +100,4 @@ const TreemapChart: React.FC<HighchartsReact.Props> = (props) => {
);
};

export default TreemapChart;
export default memo(TreemapChart);
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "@/features/metrics/styles/disk-treeview.css";

import { memo } from "react";
import { NodeRendererProps, Tree } from "react-arborist";

import formatBytes from "@/features/metrics/utils/format-bytes";
Expand Down Expand Up @@ -50,7 +51,7 @@ const DiskDirectoryTreeView: React.FC<DiskDirectoryTreeViewProps> = (props) => {
data={data}
openByDefault={false}
width={"100%"}
height={290}
height={350}
indent={24}
rowHeight={36}
overscanCount={1}
Expand All @@ -63,4 +64,4 @@ const DiskDirectoryTreeView: React.FC<DiskDirectoryTreeViewProps> = (props) => {
);
};

export default DiskDirectoryTreeView;
export default memo(DiskDirectoryTreeView);
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ import { IconAlertCircle, IconFolderOpen } from "@tabler/icons-react";

interface DiskInformationAnalyticsCardProps {
startDiskAnalysis: () => Promise<void>;
startDiskAnalysisTurbo: () => Promise<void>;
}

const DiskInformationAnalyticsCard = (props: DiskInformationAnalyticsCardProps) => {
const { startDiskAnalysis, startDiskAnalysisTurbo } = props;
const { startDiskAnalysis } = props;
const [opened, { close, open }] = useDisclosure(false);
const disk = useDisksStore.use.selectedDisk();

Expand Down Expand Up @@ -78,17 +77,17 @@ const DiskInformationAnalyticsCard = (props: DiskInformationAnalyticsCardProps)

return (
<Popover width={200} position="top" withArrow shadow="md" opened={opened}>
<Card height="350px">
<Card height="400px">
<Group position="apart">
<Title order={4}>Disk Information</Title>
<ActionIcon size={"sm"} variant="light" onClick={showDirectory}>
<IconFolderOpen stroke={1.5} />
</ActionIcon>
</Group>
<Space h={8} />
<Space h={16} />

<Stack spacing={"lg"}>
<Stack spacing={3}>
<Stack spacing={4}>
{data.map((d, i) => (
<Group key={i} position="apart">
<Text c="dimmed" size={"sm"}>
Expand All @@ -100,6 +99,7 @@ const DiskInformationAnalyticsCard = (props: DiskInformationAnalyticsCardProps)
</Group>
))}
</Stack>
<Space h={12} />
<DynamicProgress size={36} sections={sections} />
<Group>
<Button radius="md" variant="gradient" style={{ flex: 1 }} onClick={startDiskAnalysis}>
Expand All @@ -111,7 +111,7 @@ const DiskInformationAnalyticsCard = (props: DiskInformationAnalyticsCardProps)
style={{ flex: 1 }}
variant="gradient"
gradient={{ from: "orange", to: "red" }}
onClick={startDiskAnalysisTurbo}
onClick={startDiskAnalysis}
leftIcon={<IconAlertCircle />}
onMouseEnter={open}
onMouseLeave={close}
Expand Down
90 changes: 33 additions & 57 deletions src/features/metrics/pages/disk-analytics.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import DiskInformationAnalyticsCard from "@/features/metrics/components/disks/di
import useDisksStore from "@/features/metrics/stores/disk.store";
import formatBytes from "@/features/metrics/utils/format-bytes";
import { commands, DiskAnalysisProgress, DiskItem, streams } from "@/lib";
import logger from "@/lib/logger";
import notification from "@/utils/notification";
import { Box, Grid, LoadingOverlay, Progress, Stack, Text, Title, useMantineTheme } from "@mantine/core";
import { Box, Center, Grid, LoadingOverlay, Progress, Stack, Text, Title, useMantineTheme } from "@mantine/core";

interface AnalysisProgressIndicatorProps {
enableStatus?: boolean;
Expand All @@ -30,7 +31,7 @@ const AnalysisIndicator: React.FC<AnalysisProgressIndicatorProps> = (props) => {
<Stack align="center" justify="center" spacing="xs" pt={props.pt}>
<Title order={3}>{isScanningCompleted ? serializingProgress : scanningProgress}</Title>
<Box style={{ width: "300px" }}>
<Progress value={percentage} size={"lg"} />
<Progress value={percentage} size={"lg"} color="cyan" />
</Box>
</Stack>
);
Expand All @@ -56,11 +57,6 @@ const AnalysisIndicator: React.FC<AnalysisProgressIndicatorProps> = (props) => {

interface DiskAnalyticsPageProps {}

const MemoTreemapChart = React.memo(TreemapChart);

const MemoDiskDirectoryTreeView = React.memo(DiskDirectoryTreeView);

// TODO: Desperately needs refactoring
const DiskAnalyticsPage: React.FC<DiskAnalyticsPageProps> = () => {
const { id = "" } = useParams();
const disk = useDisksStore.use.selectedDisk();
Expand All @@ -81,29 +77,30 @@ const DiskAnalyticsPage: React.FC<DiskAnalyticsPageProps> = () => {
},
});

const setProgressAndFetchData = useCallback(
async (fetchData: () => Promise<DiskItem>) => {
if (!disk.mountPoint)
return notification.error({
title: "Oh no! An error",
message: "Error in retrieveing disk data",
});
streams.diskAnalysisProgress((stream) => setProgress(stream));
const rootFsTree = await fetchData();
setDiskAnalysis(rootFsTree.children as DiskItem[]);
},
[disk.mountPoint]
);
const startDiskAnalysis = useCallback(async () => {
try {
setIsLoading(true);

const populateFileExplorerTurbo = useCallback(
() => setProgressAndFetchData(() => commands.turboScan({ path: disk.mountPoint })),
[setProgressAndFetchData, disk.mountPoint]
);
const fs = await commands.scan((stream) => setProgress(stream), {
path: disk.mountPoint,
disk_name: disk.name,
is_turbo: true,
});

const populateFileExplorer = useCallback(
() => setProgressAndFetchData(() => commands.scan({ path: disk.mountPoint })),
[setProgressAndFetchData, disk.mountPoint]
);
setDiskAnalysis(fs.children as DiskItem[]);

await populateTreemap();
} catch (err) {
logger.error(err);

notification.error({
title: "Failed to start disk analysis",
message: "Please try again or restart the app",
});
} finally {
setIsLoading(false);
}
}, []);

const populateTreemap = useCallback(async () => {
if (disk.mountPoint) {
Expand Down Expand Up @@ -155,52 +152,31 @@ const DiskAnalyticsPage: React.FC<DiskAnalyticsPageProps> = () => {
}
}, [disk.mountPoint, colors.dark, setChartOptions]);

const startDiskAnalysisCommon = useCallback(
async (populateFn: () => Promise<void>) => {
setIsLoading(true);
await populateFn();
setIsLoading(false);
await populateTreemap();
},
[populateTreemap]
);

const startDiskAnalysisTurbo = useCallback(
() => startDiskAnalysisCommon(populateFileExplorerTurbo),
[startDiskAnalysisCommon, populateFileExplorerTurbo]
);

const startDiskAnalysis = useCallback(
() => startDiskAnalysisCommon(populateFileExplorer),
[startDiskAnalysisCommon, populateFileExplorer]
);

return (
<PageWrapper name={id}>
<Grid>
<Grid.Col md={12} lg={4} xl={3}>
<DiskInformationAnalyticsCard
startDiskAnalysis={startDiskAnalysis}
startDiskAnalysisTurbo={startDiskAnalysisTurbo}
/>
<DiskInformationAnalyticsCard startDiskAnalysis={startDiskAnalysis} />
</Grid.Col>
<Grid.Col md={12} lg={8} xl={9}>
<Card height="350px">
<Card height="400px">
<Title order={4}>File Explorer</Title>
{isDiskScanEmpty ? (
<AnalysisIndicator progress={progress} enableStatus={true} pt="86px" />
) : (
<MemoDiskDirectoryTreeView data={diskAnalysis} />
<DiskDirectoryTreeView data={diskAnalysis} />
)}
</Card>
</Grid.Col>
<Grid.Col xl={12}>
<Card height="560px">
<LoadingOverlay visible={isLoading} overlayBlur={3} />
<Card height="540px">
<Center>
<Title order={4}>Disk Usage</Title>
</Center>
{isDiskScanEmpty ? (
<AnalysisIndicator progress={progress} pt="188px" />
) : (
<MemoTreemapChart options={chartOptions} />
<TreemapChart options={chartOptions} />
)}
</Card>
</Grid.Col>
Expand Down
3 changes: 3 additions & 0 deletions src/lib/bindings/DiskScanInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type DiskScanInput = { path: string, is_turbo: boolean, disk_name: string, };
21 changes: 17 additions & 4 deletions src/lib/commands.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
import { invoke } from '@/lib/helpers';
import { Command, DiskItem, KillProcessOpts, KillProcessResult, ScanOpts } from '@/lib/types';
import { invoke } from "@/lib/helpers";
import {
Command,
DiskAnalysisProgress,
DiskItem,
DiskScanInput,
KillProcessOpts,
KillProcessResult,
ScanOpts,
ServerEvent,
} from "@/lib/types";
import { listen } from "@tauri-apps/api/event";

// TODO: Fix types
export const commands = {
killProcess: (opts: KillProcessOpts): Promise<KillProcessResult> => invoke(Command.KillProcess, opts as any),
showInFolder: (path: string): Promise<void> => invoke(Command.ShowInFolder, { path }),
turboScan: (path: ScanOpts): Promise<DiskItem> => invoke(Command.DiskTurboScan, path as any),
scan: (path: ScanOpts): Promise<DiskItem> => invoke(Command.DiskScan, path as any),
scan: (callback: (progress: DiskAnalysisProgress) => void, input: DiskScanInput): Promise<DiskItem> => {
listen<DiskAnalysisProgress>(ServerEvent.DiskAnalysisProgress, ({ payload }) => callback(payload));

return invoke(Command.DiskScan, { input });
},
disk_analysis_flattened: (path: ScanOpts): Promise<DiskItem[]> => invoke(Command.DiskAnalysisFlattened, path as any),
add_pachtop_exclusion: (): Promise<void> => invoke(Command.AddPachtopExclusion, {}),
};
4 changes: 0 additions & 4 deletions src/lib/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ export const streams = {
listen<Network[]>(ServerEvent.Networks, ({ payload }) => callback(payload));
},

diskAnalysisProgress: (callback: (data: DiskAnalysisProgress) => void) => {
listen<DiskAnalysisProgress>(ServerEvent.DiskAnalysisProgress, ({ payload }) => callback(payload));
},

window: {
willEnterFullScreen: (callback: () => void) => {
listen(ServerEvent.WindowWillEnterFullScreen, () => callback());
Expand Down
2 changes: 1 addition & 1 deletion src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export * from "@/lib/bindings/Timestamp";
export * from "@/lib/bindings/DiskItem";
export * from "@/lib/bindings/FileEntry";
export * from "@/lib/bindings/DiskAnalysisProgress";
export * from "@/lib/bindings/DiskScanInput";

export enum Command {
KillProcess = "kill_process",
ShowInFolder = "show_folder",
DiskTurboScan = "disk_turbo_scan",
DiskScan = "disk_scan",
DiskAnalysisFlattened = "disk_analysis_flattened",
AddPachtopExclusion = "add_pachtop_exclusion",
Expand Down

0 comments on commit 15e720a

Please sign in to comment.