Skip to content

Commit 2eb6ab4

Browse files
committed
Store backups in server state
1 parent f9878d8 commit 2eb6ab4

File tree

6 files changed

+75
-23
lines changed

6 files changed

+75
-23
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import Spinner from '@/components/elements/Spinner';
3+
import { CSSTransition } from 'react-transition-group';
4+
5+
interface Props {
6+
visible: boolean;
7+
children?: React.ReactChild;
8+
}
9+
10+
const ListRefreshIndicator = ({ visible, children }: Props) => (
11+
<CSSTransition timeout={250} in={visible} appear={true} unmountOnExit={true} classNames={'fade'}>
12+
<div className={'flex items-center mb-2'}>
13+
<Spinner size={'tiny'}/>
14+
<p className={'ml-2 text-sm text-neutral-400'}>{children || 'Refreshing listing...'}</p>
15+
</div>
16+
</CSSTransition>
17+
);
18+
19+
export default ListRefreshIndicator;

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,35 @@ import Can from '@/components/elements/Can';
88
import CreateBackupButton from '@/components/server/backups/CreateBackupButton';
99
import FlashMessageRender from '@/components/FlashMessageRender';
1010
import BackupRow from '@/components/server/backups/BackupRow';
11+
import { ServerContext } from '@/state/server';
12+
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
1113

1214
export default () => {
1315
const { uuid } = useServer();
1416
const { addError, clearFlashes } = useFlash();
1517
const [ loading, setLoading ] = useState(true);
16-
const [ backups, setBackups ] = useState<ServerBackup[]>([]);
18+
19+
const backups = ServerContext.useStoreState(state => state.backups.data);
20+
const setBackups = ServerContext.useStoreActions(actions => actions.backups.setBackups);
1721

1822
useEffect(() => {
1923
clearFlashes('backups');
2024
getServerBackups(uuid)
21-
.then(data => {
22-
setBackups(data.items);
23-
})
25+
.then(data => setBackups(data.items))
2426
.catch(error => {
2527
console.error(error);
2628
addError({ key: 'backups', message: httpErrorToHuman(error) });
2729
})
2830
.then(() => setLoading(false));
2931
}, []);
3032

31-
if (loading) {
33+
if (backups.length === 0 && loading) {
3234
return <Spinner size={'large'} centered={true}/>;
3335
}
3436

3537
return (
3638
<div className={'mt-10 mb-6'}>
39+
<ListRefreshIndicator visible={loading}/>
3740
<FlashMessageRender byKey={'backups'} className={'mb-4'}/>
3841
{!backups.length ?
3942
<p className="text-center text-sm text-neutral-400">
@@ -44,18 +47,13 @@ export default () => {
4447
{backups.map((backup, index) => <BackupRow
4548
key={backup.uuid}
4649
backup={backup}
47-
onBackupUpdated={data => setBackups(
48-
s => ([ ...s.map(b => b.uuid === data.uuid ? data : b) ]),
49-
)}
5050
className={index !== (backups.length - 1) ? 'mb-2' : undefined}
5151
/>)}
5252
</div>
5353
}
5454
<Can action={'backup.create'}>
5555
<div className={'mt-6 flex justify-end'}>
56-
<CreateBackupButton
57-
onBackupGenerated={backup => setBackups(s => [ ...s, backup ])}
58-
/>
56+
<CreateBackupButton/>
5957
</div>
6058
</Can>
6159
</div>

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
1515
import useFlash from '@/plugins/useFlash';
1616
import { httpErrorToHuman } from '@/api/http';
1717
import useWebsocketEvent from '@/plugins/useWebsocketEvent';
18+
import { ServerContext } from '@/state/server';
1819

1920
interface Props {
2021
backup: ServerBackup;
21-
onBackupUpdated: (backup: ServerBackup) => void;
2222
className?: string;
2323
}
2424

@@ -34,16 +34,18 @@ const DownloadModal = ({ checksum, ...props }: RequiredModalProps & { checksum:
3434
</Modal>
3535
);
3636

37-
export default ({ backup, onBackupUpdated, className }: Props) => {
37+
export default ({ backup, className }: Props) => {
3838
const { uuid } = useServer();
3939
const { addError, clearFlashes } = useFlash();
4040
const [ loading, setLoading ] = useState(false);
4141
const [ visible, setVisible ] = useState(false);
4242

43+
const appendBackup = ServerContext.useStoreActions(actions => actions.backups.appendBackup);
44+
4345
useWebsocketEvent(`backup completed:${backup.uuid}`, data => {
4446
try {
4547
const parsed = JSON.parse(data);
46-
onBackupUpdated({
48+
appendBackup({
4749
...backup,
4850
sha256Hash: parsed.sha256_hash || '',
4951
bytes: parsed.file_size || 0,

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,13 @@ import useServer from '@/plugins/useServer';
99
import createServerBackup from '@/api/server/backups/createServerBackup';
1010
import { httpErrorToHuman } from '@/api/http';
1111
import FlashMessageRender from '@/components/FlashMessageRender';
12-
import { ServerBackup } from '@/api/server/backups/getServerBackups';
12+
import { ServerContext } from '@/state/server';
1313

1414
interface Values {
1515
name: string;
1616
ignored: string;
1717
}
1818

19-
interface Props {
20-
onBackupGenerated: (backup: ServerBackup) => void;
21-
}
22-
2319
const ModalContent = ({ ...props }: RequiredModalProps) => {
2420
const { isSubmitting } = useFormikContext<Values>();
2521

@@ -66,20 +62,22 @@ const ModalContent = ({ ...props }: RequiredModalProps) => {
6662
);
6763
};
6864

69-
export default ({ onBackupGenerated }: Props) => {
65+
export default () => {
7066
const { uuid } = useServer();
7167
const { addError, clearFlashes } = useFlash();
7268
const [ visible, setVisible ] = useState(false);
7369

70+
const appendBackup = ServerContext.useStoreActions(actions => actions.backups.appendBackup);
71+
7472
useEffect(() => {
7573
clearFlashes('backups:create');
76-
}, [visible]);
74+
}, [ visible ]);
7775

7876
const submit = ({ name, ignored }: Values, { setSubmitting }: FormikHelpers<Values>) => {
79-
clearFlashes('backups:create')
77+
clearFlashes('backups:create');
8078
createServerBackup(uuid, name, ignored)
8179
.then(backup => {
82-
onBackupGenerated(backup);
80+
appendBackup(backup);
8381
setVisible(false);
8482
})
8583
.catch(error => {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ServerBackup } from '@/api/server/backups/getServerBackups';
2+
import { action, Action } from 'easy-peasy';
3+
4+
export interface ServerBackupStore {
5+
data: ServerBackup[];
6+
setBackups: Action<ServerBackupStore, ServerBackup[]>;
7+
appendBackup: Action<ServerBackupStore, ServerBackup>;
8+
removeBackup: Action<ServerBackupStore, string>;
9+
}
10+
11+
const backups: ServerBackupStore = {
12+
data: [],
13+
14+
setBackups: action((state, payload) => {
15+
state.data = payload;
16+
}),
17+
18+
appendBackup: action((state, payload) => {
19+
if (state.data.find(backup => backup.uuid === payload.uuid)) {
20+
state.data = state.data.map(backup => backup.uuid === payload.uuid ? payload : backup);
21+
} else {
22+
state.data = [ ...state.data, payload ];
23+
}
24+
}),
25+
26+
removeBackup: action((state, payload) => {
27+
state.data = [ ...state.data.filter(backup => backup.uuid !== payload) ];
28+
}),
29+
};
30+
31+
export default backups;

resources/scripts/state/server/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ServerDatabase } from '@/api/server/getServerDatabases';
55
import files, { ServerFileStore } from '@/state/server/files';
66
import subusers, { ServerSubuserStore } from '@/state/server/subusers';
77
import { composeWithDevTools } from 'redux-devtools-extension';
8+
import backups, { ServerBackupStore } from '@/state/server/backups';
89

910
export type ServerStatus = 'offline' | 'starting' | 'stopping' | 'running';
1011

@@ -73,6 +74,7 @@ export interface ServerStore {
7374
subusers: ServerSubuserStore;
7475
databases: ServerDatabaseStore;
7576
files: ServerFileStore;
77+
backups: ServerBackupStore;
7678
socket: SocketStore;
7779
status: ServerStatusStore;
7880
clearServerState: Action<ServerStore>;
@@ -85,13 +87,15 @@ export const ServerContext = createContextStore<ServerStore>({
8587
databases,
8688
files,
8789
subusers,
90+
backups,
8891
clearServerState: action(state => {
8992
state.server.data = undefined;
9093
state.server.permissions = [];
9194
state.databases.items = [];
9295
state.subusers.data = [];
9396
state.files.directory = '/';
9497
state.files.contents = [];
98+
state.backups.backups = [];
9599

96100
if (state.socket.instance) {
97101
state.socket.instance.removeAllListeners();

0 commit comments

Comments
 (0)