Skip to content

Commit

Permalink
Added loading screen placeholder and removed old icons
Browse files Browse the repository at this point in the history
  • Loading branch information
PsychoSanchez committed Nov 9, 2024
1 parent 5e8d680 commit 3927f7b
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 103 deletions.
40 changes: 40 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
Expand Down
15 changes: 15 additions & 0 deletions src/popup/components/AppLoadingSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

import { Skeleton } from '@shared/ui/skeleton';

export const AppLoadingSkeleton = () => {
return (
<div className="flex flex-col gap-2 w-full p-2">
<Skeleton className="h-10 min-w-32 w-full" />
<Skeleton className="h-32 min-w-32 w-full" />
<Skeleton className="h-32 min-w-32 w-full" />
<Skeleton className="h-48 min-w-32 w-full" />
<Skeleton className="h-64 min-w-32 w-full" />
</div>
);
};
21 changes: 21 additions & 0 deletions src/popup/components/_stories/AppLoadingSkeleton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Meta, StoryObj } from '@storybook/react';
import React from 'react';

import { AppLoadingSkeleton } from '../AppLoadingSkeleton';

export default {
title: 'popup/components/AppLoadingSkeleton',
component: AppLoadingSkeleton,
parameters: {
layout: 'centered',
},
} satisfies Meta<typeof AppLoadingSkeleton>;

type Story = StoryObj<Meta<typeof AppLoadingSkeleton>>;

export const Default: Story = {
render: () => <AppLoadingSkeleton />,
parameters: {
layout: 'centered',
},
};
5 changes: 3 additions & 2 deletions src/popup/hooks/PopupContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DeepReadonly } from 'utility-types';
import { Preferences } from '@shared/db/types';
import { DEFAULT_PREFERENCES } from '@shared/preferences';

import { AppLoadingSkeleton } from '@popup/components/AppLoadingSkeleton';
import { getFilteredWebsiteTimeStoreSlice } from '@popup/services/time-store';

import { useActiveTabHostname } from './useActiveTab';
Expand All @@ -29,7 +30,7 @@ const PopupContext = React.createContext<PopupContextType>(DEFAULT_CONTEXT);
export const usePopupContext = () => React.useContext(PopupContext);

export const PopupContextProvider = ({ children }: React.PropsWithChildren) => {
const store = useTimeStore();
const [store, isLoaded] = useTimeStore();
const host = useActiveTabHostname();
const [settings, updateSettings] = useSettings();

Expand All @@ -51,7 +52,7 @@ export const PopupContextProvider = ({ children }: React.PropsWithChildren) => {
updateSettings,
}}
>
{children}
{isLoaded ? children : <AppLoadingSkeleton />}
</PopupContext.Provider>
);
};
12 changes: 11 additions & 1 deletion src/popup/hooks/useTimeStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ export { TimeStore };

export const useTimeStore = () => {
const [store, setStore] = React.useState<TimeStore>({});
const [isLoaded, setIsLoaded] = React.useState(false);

React.useEffect(() => {
const startDate = new Date();
Promise.all([getTotalActivity(), getActiveTabRecord()]).then(
([activity, activeRecord]) => {
if (activeRecord?.hostname) {
Expand All @@ -24,9 +26,17 @@ export const useTimeStore = () => {
}

setStore(activity);

const endDate = new Date();
// Should be at least 150ms to avoid flickering
const delay = Math.max(
150 - (endDate.getTime() - startDate.getTime()),
0,
);
setTimeout(() => setIsLoaded(true), delay);
},
);
}, []);

return store;
return [store, isLoaded] as const;
};
32 changes: 14 additions & 18 deletions src/popup/pages/preferences/components/ThemeSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Eclipse, Moon, Sun } from 'lucide-react';
import * as React from 'react';
import { twMerge } from 'tailwind-merge';

import { Button } from '@shared/blocks/Button';
import { Icon, IconType } from '@shared/blocks/Icon';
import { i18n } from '@shared/services/i18n';
import { ColorScheme, ThemeService } from '@shared/services/theme';
import { Button } from '@shared/ui/button';

export const ThemeSelector = () => {
const [theme, setTheme] = React.useState(() => ThemeService.getAppTheme());
Expand Down Expand Up @@ -35,30 +34,27 @@ export const ThemeSelector = () => {
<div className="flex flex-row rounded-lg border-2 border-solid border-neutral-300 dark:border-neutral-900 overflow-hidden">
<Button
onClick={handleAutoThemeSelect}
className={twMerge(
'flex-1 rounded-none',
theme === 'auto' && 'bg-neutral-300 dark:bg-neutral-900',
)}
className="flex-1"
variant={theme === 'auto' ? 'default' : 'secondary'}
>
<Icon type={IconType.Eclipse} /> {i18n('ThemeSelector_OptionAuto')}
<Eclipse size={16} className="mr-1" />
{i18n('ThemeSelector_OptionAuto')}
</Button>
<Button
onClick={handleDarkThemeSelect}
className={twMerge(
'flex-1 rounded-none border-l-2 border-r-2 border-solid border-neutral-300 dark:border-neutral-900',
theme === 'dark' && 'bg-neutral-300 dark:bg-neutral-900',
)}
className="flex-1"
variant={theme === 'dark' ? 'default' : 'secondary'}
>
<Icon type={IconType.Moon} /> {i18n('ThemeSelector_OptionDark')}
<Moon size={16} className="mr-1" />
{i18n('ThemeSelector_OptionDark')}
</Button>
<Button
onClick={handleLightThemeSelect}
className={twMerge(
'flex-1 rounded-none',
theme === 'light' && 'bg-neutral-300 dark:bg-neutral-900',
)}
className="flex-1"
variant={theme === 'light' ? 'default' : 'secondary'}
>
<Icon type={IconType.Sun} /> {i18n('ThemeSelector_OptionLight')}
<Sun size={16} className="mr-1" />
{i18n('ThemeSelector_OptionLight')}
</Button>
</div>
</div>
Expand Down
17 changes: 13 additions & 4 deletions src/shared/services/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,28 @@ export const i18n = <T extends keyof I18n>(
) => {
const values = placeholders[0];
if (chrome?.i18n?.getMessage) {
return chrome.i18n.getMessage(
message,
values ? Object.values(values) : undefined,
return (
chrome.i18n.getMessage(
message,
values ? Object.values(values) : undefined,
) ?? getMessageFromFallback(message, values)
);
}

return getMessageFromFallback(message, values);
};

function getMessageFromFallback<T extends keyof I18n>(
message: T,
values?: Record<string, string>,
) {
const messageTemplate = fallback[message].message;
if (!values) {
return messageTemplate;
}

return formatI18NMessage(messageTemplate, values);
};
}

export function formatI18NMessage(
message: string,
Expand Down
26 changes: 26 additions & 0 deletions src/shared/ui/progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as ProgressPrimitive from '@radix-ui/react-progress';
import * as React from 'react';

import { cn } from '@shared/utils';

const Progress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
'relative h-2 w-full overflow-hidden rounded-full bg-primary/20',
className,
)}
{...props}
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
));
Progress.displayName = ProgressPrimitive.Root.displayName;

export { Progress };
17 changes: 17 additions & 0 deletions src/shared/ui/skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';

import { cn } from '@shared/utils';

function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn('animate-pulse rounded-md bg-primary/10', className)}
{...props}
/>
);
}

export { Skeleton };
Loading

0 comments on commit 3927f7b

Please sign in to comment.