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

Alpha next #44

Merged
merged 13 commits into from
Apr 29, 2020
Merged
Prev Previous commit
Next Next commit
feat: add header to loading and error screens
  • Loading branch information
Xiphe committed Apr 28, 2020
commit dd41bae1be63d8a0ca7ee9e1b5c3becd8a314a5f
30 changes: 15 additions & 15 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import './theme.scss';
import React, { Suspense, useState } from 'react';
import { useInit, INIT_EMPTY } from './lib';
import { Loading } from './components';
import { ErrorBoundary, Startup } from './components';
import styles from './App.module.scss';

const Budget = React.lazy(() => import('./views/Budget'));
Expand All @@ -13,21 +13,21 @@ export default function App() {
const [init, setInitialState] = useInit();
return (
<div className={styles.app}>
<Suspense fallback={<Loading />}>
{!init ? (
<Loading />
) : init instanceof Error ? (
<p>Error: {init.message}</p>
) : init === INIT_EMPTY ? (
welcome ? (
<Welcome onCreate={() => setWelcome(false)} />
<ErrorBoundary error={init instanceof Error ? init : undefined}>
<Suspense fallback={<Startup />}>
{!init ? (
<Startup />
) : init instanceof Error ? null : init === INIT_EMPTY ? (
welcome ? (
<Welcome onCreate={() => setWelcome(false)} />
) : (
<NewBudget onCreate={setInitialState} />
)
) : (
<NewBudget onCreate={setInitialState} />
)
) : (
<Budget init={init} />
)}
</Suspense>
<Budget init={init} />
)}
</Suspense>
</ErrorBoundary>
</div>
);
}
3 changes: 3 additions & 0 deletions src/components/Content/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import classNames from 'classnames';
import styles from './Content.module.scss';

type Props = {
className?: string;
header: ReactNode;
children: ReactNode;
padding?: boolean;
Expand All @@ -14,13 +15,15 @@ export default function Content({
children,
padding,
background,
className,
flex,
}: Props) {
return (
<>
{header}
<div
className={classNames(
className,
styles.content,
padding && styles.padding,
flex && styles.flex,
Expand Down
55 changes: 55 additions & 0 deletions src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { ReactNode } from 'react';
import Content from './Content';
import Header, { HeaderSpacer } from './Header';
import LoadingError from './LoadingError';

type ErrorWithRetry = Error & {
retry?: null | (() => void);
};

export function FullScreenError({ error }: { error: ErrorWithRetry }) {
return (
<Content
background
padding
header={
<Header center>
<HeaderSpacer />
Error
<HeaderSpacer />
</Header>
}
>
<h3>An Error Ocurred</h3>
<LoadingError message={error.message} retry={error.retry || undefined} />
</Content>
);
}

export default class ErrorBoundary extends React.Component<
{ children: ReactNode; error?: Error },
{ error: Error | null }
> {
constructor(props: any) {
super(props);
this.state = { error: null };
}

static getDerivedStateFromError(error: Error) {
return { error };
}

componentDidCatch() {
// You can also log the error to an error reporting service
// logErrorToMyService(error, errorInfo);
}

render() {
const error = this.state.error || this.props.error;
if (error) {
return <FullScreenError error={error} />;
}

return this.props.children;
}
}
3 changes: 3 additions & 0 deletions src/components/Header/Header.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
display: flex;
align-items: center;
}
.trafficLightCounterSpacer {
padding-right: 78px;
}
.trafficLightSpacer {
margin-left: 78px;
}
Expand Down
15 changes: 11 additions & 4 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@ import styles from './Header.module.scss';

type Props = {
className?: string;
center?: boolean;
children?: ReactNode;
};
export default function Header({ children, className }: Props) {
export default function Header({ children, className, center }: Props) {
return (
<div className={classNames(className, styles.header)}>
<div className={styles.trafficLightSpacer}></div>
<div
className={classNames(
className,
styles.header,
center && styles.trafficLightCounterSpacer,
)}
>
<div className={styles.trafficLightSpacer} />
{children}
</div>
);
}
export function HeaderSpacer() {
return <div className={styles.spacer}></div>;
return <div className={styles.spacer} />;
}
5 changes: 0 additions & 5 deletions src/components/Loading.tsx

This file was deleted.

62 changes: 62 additions & 0 deletions src/components/Loading/Loading.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* from https://loading.io/css/ */
.lds {
display: inline-block;
position: relative;
width: 80px;
height: 13px;
}
.lds div {
position: absolute;
width: 13px;
height: 13px;
border-radius: 50%;
background: var(--main-color);
opacity: 1;
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
.lds div:nth-child(1) {
left: 8px;
animation: lds1 0.6s infinite;
}
.lds div:nth-child(2) {
left: 8px;
animation: lds2 0.6s infinite;
}
.lds div:nth-child(3) {
left: 32px;
animation: lds2 0.6s infinite;
}
.lds div:nth-child(4) {
left: 56px;
animation: lds3 0.6s infinite;
}
@keyframes lds1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes lds3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes lds2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(24px, 0);
}
}

.center {
display: inline-block;
top: calc(50% - 7px);
left: calc(50% - 40px);
}
17 changes: 17 additions & 0 deletions src/components/Loading/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import classNames from 'classnames';
import styles from './Loading.module.scss';

type Props = {
center?: boolean;
};
export default function Loading({ center }: Props) {
return (
<div className={classNames(styles.lds, center && styles.center)}>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
);
}
1 change: 1 addition & 0 deletions src/components/Loading/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Loading';
14 changes: 14 additions & 0 deletions src/components/Startup/Startup.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.startup {
background-image: url(/images/jouwen-wang-UChknR2z5EM-unsplash.jpg);
background-size: cover;
background-position: center;
}

.creds {
font-size: 0.8rem;
margin: 0;
position: fixed;
bottom: 0.7em;
right: 1em;
opacity: 0.5;
}
23 changes: 23 additions & 0 deletions src/components/Startup/Startup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import Content from '../Content';
import Header, { HeaderSpacer } from '../Header';
import Loading from '../Loading';
import styles from './Startup.module.scss';

export default function Startup() {
return (
<Content
className={styles.startup}
background
header={
<Header center>
<HeaderSpacer />
BudgetBudget
<HeaderSpacer />
</Header>
}
>
<Loading center />
</Content>
);
}
1 change: 1 addition & 0 deletions src/components/Startup/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Startup';
2 changes: 2 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export { default as LoadingError } from './LoadingError';
export { default as Header, HeaderSpacer } from './Header';
export { default as Content } from './Content';
export { default as Sidebar } from './Sidebar';
export { default as ErrorBoundary, FullScreenError } from './ErrorBoundary';
export { default as Startup } from './Startup';
export { default as Select } from './Select';
export { default as Row } from './Row';
export { default as Button } from './Button';
Expand Down
21 changes: 9 additions & 12 deletions src/views/Budget/Budget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import React, {
import isSameMonth from 'date-fns/isSameMonth';
import differenceInCalendarMonths from 'date-fns/differenceInCalendarMonths';
import { BudgetState, Action, useBudgetData } from '../../budget';
import { Content, Loading, InfiniteSlider, ScrollTo } from '../../components';
import {
Content,
FullScreenError,
InfiniteSlider,
ScrollTo,
Startup,
} from '../../components';
import { HeaderHeightProvider, VisibleMothContextProvider } from '../../lib';
import Month from '../Month';
import BudgetHeader from './Header';
Expand Down Expand Up @@ -71,20 +77,11 @@ export default function Budget({ state, dispatch }: Props) {
}, [extendFuture]);

if (error) {
return (
<div>
<p>Error: {error.message}</p>
{retry && <button onClick={retry}>retry</button>}
</div>
);
return <FullScreenError error={Object.assign(error, { retry }) as any} />;
}

if (loading) {
return (
<>
<Loading />
</>
);
return <Startup />;
}

return (
Expand Down
6 changes: 3 additions & 3 deletions src/views/Budget/Entry.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { lazy } from 'react';
import { Loading } from '../../components';
import { Startup } from '../../components';
import useBudgetState, { BudgetState } from '../../budget';
import { withShowSettingsProvider, useShowSettings } from '../../lib';
import Settings from '../Settings';
Expand All @@ -14,10 +14,10 @@ export default withShowSettingsProvider(({ init }: Props) => {
const showSettings = useShowSettings();
const { error, state, dispatch } = useBudgetState(init);
if (error) {
return <p>Error: {error.message}</p>;
throw error;
}
if (state === null) {
return <Loading />;
return <Startup />;
}
if (showSettings) {
return <Settings state={state} dispatch={dispatch} />;
Expand Down
2 changes: 1 addition & 1 deletion src/views/Settings/Categories/Categories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function CategorySettings(props: Omit<Props, 'categories'>) {
const [categories, _, retry] = useCategories(currency);

if (categories === null) {
return <Loading />;
return <Loading center />;
}

if (categories instanceof Error) {
Expand Down