Skip to content

Commit d3a06e1

Browse files
committed
Add progress bar to top of page for nicer loading indicator styles
1 parent 708c15e commit d3a06e1

File tree

12 files changed

+133
-34
lines changed

12 files changed

+133
-34
lines changed

resources/scripts/api/http.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import axios, { AxiosInstance } from 'axios';
2+
import { store } from '@/state';
23

34
const http: AxiosInstance = axios.create({
5+
timeout: 20000,
46
headers: {
57
'X-Requested-With': 'XMLHttpRequest',
68
'Accept': 'application/json',
@@ -9,6 +11,18 @@ const http: AxiosInstance = axios.create({
911
},
1012
});
1113

14+
http.interceptors.request.use(req => {
15+
store.getActions().progress.startContinuous();
16+
17+
return req;
18+
});
19+
20+
http.interceptors.response.use(resp => {
21+
store.getActions().progress.setComplete();
22+
23+
return resp;
24+
});
25+
1226
// If we have a phpdebugbar instance registered at this point in time go
1327
// ahead and route the response data through to it so things show up.
1428
// @ts-ignore

resources/scripts/components/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import AuthenticationRouter from '@/routers/AuthenticationRouter';
99
import { Provider } from 'react-redux';
1010
import { SiteSettings } from '@/state/settings';
1111
import { DefaultTheme, ThemeProvider } from 'styled-components';
12+
import ProgressBar from '@/components/elements/ProgressBar';
1213

1314
interface ExtendedWindow extends Window {
1415
SiteConfiguration?: SiteSettings;
@@ -57,6 +58,7 @@ const App = () => {
5758
<ThemeProvider theme={theme}>
5859
<StoreProvider store={store}>
5960
<Provider store={store}>
61+
<ProgressBar/>
6062
<div className={'mx-auto w-auto'}>
6163
<BrowserRouter basename={'/'} key={'root-router'}>
6264
<Switch>

resources/scripts/components/elements/ListRefreshIndicator.tsx

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React, { useEffect, useRef, useState } from 'react';
2+
import styled from 'styled-components';
3+
import { useStoreActions, useStoreState } from 'easy-peasy';
4+
import { randomInt } from '@/helpers';
5+
import { CSSTransition } from 'react-transition-group';
6+
7+
const BarFill = styled.div`
8+
${tw`h-full bg-cyan-400`};
9+
transition: 250ms ease-in-out;
10+
box-shadow: 0 -2px 10px 2px hsl(178, 78%, 57%);
11+
`;
12+
13+
export default () => {
14+
const interval = useRef<number>(null);
15+
const timeout = useRef<number>(null);
16+
const [ visible, setVisible ] = useState(false);
17+
const progress = useStoreState(state => state.progress.progress);
18+
const continuous = useStoreState(state => state.progress.continuous);
19+
const setProgress = useStoreActions(actions => actions.progress.setProgress);
20+
21+
useEffect(() => {
22+
return () => {
23+
timeout.current && clearTimeout(timeout.current);
24+
interval.current && clearInterval(interval.current);
25+
};
26+
}, []);
27+
28+
useEffect(() => {
29+
setVisible((progress || 0) > 0);
30+
31+
if (progress === 100) {
32+
// @ts-ignore
33+
timeout.current = setTimeout(() => setProgress(undefined), 500);
34+
}
35+
}, [ progress ]);
36+
37+
useEffect(() => {
38+
if (!continuous) {
39+
interval.current && clearInterval(interval.current);
40+
return;
41+
}
42+
43+
if (!progress || progress === 0) {
44+
setProgress(randomInt(20, 30));
45+
}
46+
}, [ continuous ]);
47+
48+
useEffect(() => {
49+
if (continuous) {
50+
interval.current && clearInterval(interval.current);
51+
if ((progress || 0) >= 90) {
52+
setProgress(90);
53+
} else {
54+
// @ts-ignore
55+
interval.current = setTimeout(() => setProgress(progress + randomInt(1, 5)), 500);
56+
}
57+
}
58+
}, [ progress, continuous ]);
59+
60+
return (
61+
<div className={'w-full fixed'} style={{ height: '2px' }}>
62+
<CSSTransition
63+
timeout={250}
64+
appear={true}
65+
in={visible}
66+
unmountOnExit={true}
67+
classNames={'fade'}
68+
>
69+
<BarFill style={{ width: progress === undefined ? '100%' : `${progress}%` }}/>
70+
</CSSTransition>
71+
</div>
72+
);
73+
};

resources/scripts/components/server/backups/BackupContainer.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useState } from 'react';
22
import Spinner from '@/components/elements/Spinner';
3-
import getServerBackups, { ServerBackup } from '@/api/server/backups/getServerBackups';
3+
import getServerBackups from '@/api/server/backups/getServerBackups';
44
import useServer from '@/plugins/useServer';
55
import useFlash from '@/plugins/useFlash';
66
import { httpErrorToHuman } from '@/api/http';
@@ -9,7 +9,6 @@ import CreateBackupButton from '@/components/server/backups/CreateBackupButton';
99
import FlashMessageRender from '@/components/FlashMessageRender';
1010
import BackupRow from '@/components/server/backups/BackupRow';
1111
import { ServerContext } from '@/state/server';
12-
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
1312

1413
export default () => {
1514
const { uuid } = useServer();
@@ -36,7 +35,6 @@ export default () => {
3635

3736
return (
3837
<div className={'mt-10 mb-6'}>
39-
<ListRefreshIndicator visible={loading}/>
4038
<FlashMessageRender byKey={'backups'} className={'mb-4'}/>
4139
{!backups.length ?
4240
<p className="text-center text-sm text-neutral-400">

resources/scripts/components/server/databases/DatabasesContainer.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import CreateDatabaseButton from '@/components/server/databases/CreateDatabaseBu
1010
import Can from '@/components/elements/Can';
1111
import useFlash from '@/plugins/useFlash';
1212
import useServer from '@/plugins/useServer';
13-
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
1413

1514
export default () => {
1615
const { uuid, featureLimits } = useServer();
@@ -41,7 +40,6 @@ export default () => {
4140
:
4241
<CSSTransition classNames={'fade'} timeout={250}>
4342
<>
44-
<ListRefreshIndicator visible={loading}/>
4543
{databases.length > 0 ?
4644
databases.map((database, index) => (
4745
<DatabaseRow

resources/scripts/components/server/schedules/ScheduleContainer.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
import React, { useEffect, useMemo, useState } from 'react';
2-
import getServerSchedules, { Schedule } from '@/api/server/schedules/getServerSchedules';
1+
import React, { useEffect, useState } from 'react';
2+
import getServerSchedules from '@/api/server/schedules/getServerSchedules';
33
import { ServerContext } from '@/state/server';
44
import Spinner from '@/components/elements/Spinner';
55
import { RouteComponentProps } from 'react-router-dom';
66
import FlashMessageRender from '@/components/FlashMessageRender';
77
import ScheduleRow from '@/components/server/schedules/ScheduleRow';
88
import { httpErrorToHuman } from '@/api/http';
9-
import { Actions, useStoreActions } from 'easy-peasy';
10-
import { ApplicationStore } from '@/state';
119
import EditScheduleModal from '@/components/server/schedules/EditScheduleModal';
1210
import Can from '@/components/elements/Can';
1311
import useServer from '@/plugins/useServer';
1412
import useFlash from '@/plugins/useFlash';
15-
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
1613

1714
export default ({ match, history }: RouteComponentProps) => {
1815
const { uuid } = useServer();
@@ -37,7 +34,6 @@ export default ({ match, history }: RouteComponentProps) => {
3734
return (
3835
<div className={'my-10 mb-6'}>
3936
<FlashMessageRender byKey={'schedules'} className={'mb-4'}/>
40-
<ListRefreshIndicator visible={loading}/>
4137
{(!schedules.length && loading) ?
4238
<Spinner size={'large'} centered={true}/>
4339
:

resources/scripts/components/server/users/UsersContainer.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import FlashMessageRender from '@/components/FlashMessageRender';
99
import getServerSubusers from '@/api/server/users/getServerSubusers';
1010
import { httpErrorToHuman } from '@/api/http';
1111
import Can from '@/components/elements/Can';
12-
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
1312

1413
export default () => {
1514
const [ loading, setLoading ] = useState(true);
@@ -48,7 +47,6 @@ export default () => {
4847

4948
return (
5049
<div className={'mt-10 mb-6'}>
51-
<ListRefreshIndicator visible={loading}/>
5250
<FlashMessageRender byKey={'users'} className={'mb-4'}/>
5351
{!subusers.length ?
5452
<p className={'text-center text-sm text-neutral-400'}>

resources/scripts/easy-peasy.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// noinspection ES6UnusedImports
2-
import EasyPeasy from 'easy-peasy';
2+
import EasyPeasy, { Actions, State } from 'easy-peasy';
33
import { ApplicationStore } from '@/state';
44

55
declare module 'easy-peasy' {
66
export function useStoreState<Result>(
7-
mapState: (state: ApplicationStore) => Result,
7+
mapState: (state: State<ApplicationStore>) => Result,
8+
): Result;
9+
10+
export function useStoreActions<Result>(
11+
mapActions: (actions: Actions<ApplicationStore>) => Result,
812
): Result;
913
}

resources/scripts/helpers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ export function bytesToHuman (bytes: number): string {
99
}
1010

1111
export const bytesToMegabytes = (bytes: number) => Math.floor(bytes / 1000 / 1000);
12+
13+
export const randomInt = (low: number, high: number) => Math.floor(Math.random() * (high - low) + low);

0 commit comments

Comments
 (0)