Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: avoid hardcoding entry when initially selecting entry id #20

Merged
merged 4 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 8 additions & 22 deletions webui/src/app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Slot } from 'expo-router';
import { useColorScheme } from 'nativewind';
import { useEffect } from 'react';

import { ModuleFilterProvider } from '~/providers/modules';
import { QueryProvider } from '~/providers/query';
import { StatsEntryProvider } from '~/providers/stats';
import { ThemeProvider } from '~/providers/theme';

// Import the Expo-required radix styles
import '@radix-ui/colors/green.css';
Expand All @@ -31,28 +30,15 @@ import '~/styles-expo.css';
import '~/styles.css';

export default function RootLayout() {
useWorkaroundForThemeClass();

return (
<QueryProvider>
<StatsEntryProvider>
<ModuleFilterProvider>
<Slot />
</ModuleFilterProvider>
</StatsEntryProvider>
<ThemeProvider>
<StatsEntryProvider>
<ModuleFilterProvider>
<Slot />
</ModuleFilterProvider>
</StatsEntryProvider>
</ThemeProvider>
</QueryProvider>
);
}

function useWorkaroundForThemeClass() {
const { colorScheme } = useColorScheme();

useEffect(() => {
if (document.body) {
document.body.classList.remove('light-theme', 'dark-theme');
if (colorScheme) {
document.body.className = `${colorScheme}-theme`;
}
}
}, [colorScheme]);
}
6 changes: 3 additions & 3 deletions webui/src/components/forms/StatsEntrySelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ export function StatsEntrySelect() {
side="bottom"
collisionPadding={{ left: 16, right: 16 }}
className={cn(
'flex min-w-[220px] flex-col gap-0.5 rounded-md border border-default bg-default p-1 shadow-md'
// 'will-change-[opacity,transform]',
// 'data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=top]:animate-slideDownAndFade'
'flex min-w-[220px] flex-col gap-0.5 rounded-md border border-default bg-default p-1 shadow-md',
'transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-200 data-[state=open]:duration-300',
'data-[state=closed]:fade-out data-[state=closed]:slide-out-to-top-1/3 data-[state=open]:fade-in data-[state=open]:slide-in-from-top-1/3'
)}
>
<Select.ScrollUpButton className="SelectScrollButton">
Expand Down
11 changes: 10 additions & 1 deletion webui/src/components/graphs/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ export const Graph = forwardRef<ReactECharts, ComponentProps<typeof ReactECharts
);
});

function useDynamicHeight<T extends HTMLElement>(ref: RefObject<T>, initialHeight = 300) {
let lastKnownHeight = 300;

function useDynamicHeight<T extends HTMLElement>(
ref: RefObject<T>,
initialHeight = lastKnownHeight
) {
const [size, setSize] = useState({ height: initialHeight, width: 0 });

useEffect(() => {
lastKnownHeight = size.height;
}, [size.height]);

useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
Expand Down
42 changes: 38 additions & 4 deletions webui/src/providers/stats.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { type PropsWithChildren, createContext, useContext, useMemo, useState } from 'react';

import { Spinner } from '~/ui/Spinner';
import { fetchApi } from '~/utils/api';
import { type PartialStatsEntry } from '~core/data/types';

Expand All @@ -24,18 +25,51 @@ export const useStatsEntryContext = () => useContext(statsEntryContext);

export function StatsEntryProvider({ children }: PropsWithChildren) {
const entries = useStatsEntriesData();
const [entryId, setEntryId] = useState('2');
const [entryId, setEntryId] = useState<string>();
const entryIdOrFirstEntry = entryId ?? entries.data?.[0]?.id;

const entry = useMemo(
() => entries.data?.find((entry) => entry.id === entryId),
[entries, entryId]
() => entries.data?.find((entry) => entry.id === entryIdOrFirstEntry),
[entries, entryIdOrFirstEntry]
);

function entryFilePath(absolutePath: string) {
return entry?.projectRoot ? absolutePath.replace(entry.projectRoot + '/', '') : absolutePath;
}

// TODO: add better UX for loading
if (entries.isFetching && !entries.data?.length) {
return (
<div className="flex flex-1 justify-center items-center">
<Spinner />
</div>
);
}

// TODO: add better UX for empty state
if (entries.isFetched && !entries.data?.length) {
return (
<div className="flex flex-1 justify-center items-center">
<h2 className="text-lg font-bold m-4">No stats found.</h2>
<p>Open your app in the browser, or device, to collect the stats.</p>
</div>
);
}

// TODO: add better UX for error state
if (!entryIdOrFirstEntry) {
return (
<div className="flex flex-1 justify-center items-center">
<h2 className="text-lg font-bold m-4">Unable to load stats.</h2>
<p>Make sure you configured Expo Atlas properly.</p>
</div>
);
}

return (
<statsEntryContext.Provider value={{ entryId, setEntryId, entries, entry, entryFilePath }}>
<statsEntryContext.Provider
value={{ entryId: entryIdOrFirstEntry, setEntryId, entries, entry, entryFilePath }}
>
{children}
</statsEntryContext.Provider>
);
Expand Down
21 changes: 21 additions & 0 deletions webui/src/providers/theme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useColorScheme } from 'nativewind';
import { useEffect, type PropsWithChildren } from 'react';

export function ThemeProvider({ children }: PropsWithChildren) {
useWorkaroundForThemeClass();

return children;
}

function useWorkaroundForThemeClass() {
const { colorScheme } = useColorScheme();

useEffect(() => {
if (document.body) {
document.body.classList.remove('light-theme', 'dark-theme');
if (colorScheme) {
document.body.className = `${colorScheme}-theme`;
}
}
}, [colorScheme]);
}
8 changes: 8 additions & 0 deletions webui/src/ui/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import cn from 'classnames';
// @ts-expect-error
import LoaderIcon from 'lucide-react/dist/esm/icons/loader-2';
import { type ComponentProps } from 'react';

export function Spinner({ className, ...props }: ComponentProps<typeof LoaderIcon>) {
return <LoaderIcon className={cn('animate-spin', className)} {...props} />;
}