|
| 1 | + |
| 2 | +import { |
| 3 | + Box, |
| 4 | + Button, |
| 5 | + CircularProgress, |
| 6 | + Dialog, |
| 7 | + DialogActions, |
| 8 | + DialogContent, |
| 9 | + FormControl, |
| 10 | + MenuItem, |
| 11 | + Select, |
| 12 | + Typography, |
| 13 | +} from '@mui/material'; |
| 14 | +import React, { ReactElement, useEffect, useState } from 'react'; |
| 15 | +import { useDDClient, useMountPoint } from '../../services/hooks'; |
| 16 | +import { DockerImage } from '../../types'; |
| 17 | +import { DownloadProgress } from '../DownloadProgress/DownloadProgress'; |
| 18 | + |
| 19 | + |
| 20 | +export const OnBoarding = (): ReactElement => { |
| 21 | + const { setMountPointUser } = useMountPoint(); |
| 22 | + const ddClient = useDDClient(); |
| 23 | + const [userState, setUserState] = useState({ loading: false, selectedUser: '', users: [] }); |
| 24 | + const [hasLocalImage, setHasLocalImage] = useState({ checking: true, isPresent: false }); |
| 25 | + const [isPullingImage, setIsPullingImage] = useState(false); |
| 26 | + const [triggerUseEffect, setTriggerUseEffect] = useState(false); |
| 27 | + |
| 28 | + const checkHomeDir = async () => { |
| 29 | + setUserState({ loading: true, selectedUser: userState.selectedUser, users: userState.users }); |
| 30 | + const path = ddClient.host.platform === 'darwin' ? 'Users' : 'home'; |
| 31 | + const res = await ddClient.docker.cli.exec('run', |
| 32 | + ['--rm', '--entrypoint=', '-v', `/${path}:/users`, 'localstack/localstack', 'ls', '/users']); |
| 33 | + |
| 34 | + if (res.stderr !== '' || res.stdout === '') { |
| 35 | + ddClient.desktopUI.toast.error(`Error while locating users: ${res.stderr}\n using /tmp as mount point`); |
| 36 | + setUserState({ loading: false, selectedUser: 'tmp', users: ['tmp'] }); |
| 37 | + setMountPointUser('tmp'); |
| 38 | + } |
| 39 | + const foundUsers = res.stdout.split('\n'); |
| 40 | + foundUsers.pop(); // remove last '' element |
| 41 | + setUserState({ loading: false, selectedUser: foundUsers[0], users: foundUsers }); |
| 42 | + }; |
| 43 | + |
| 44 | + const checkLocalImage = async () => { |
| 45 | + setHasLocalImage({ checking: true, isPresent: hasLocalImage.isPresent }); |
| 46 | + const images = await ddClient.docker.listImages() as [DockerImage]; |
| 47 | + const isPresent = images.filter(image => image.RepoTags?.at(0).split(':').at(0) === 'localstack/localstack'); |
| 48 | + setHasLocalImage({ checking: false, isPresent: isPresent.length > 0 }); |
| 49 | + return isPresent.length > 0; |
| 50 | + }; |
| 51 | + |
| 52 | + useEffect(() => { |
| 53 | + const execChecks = async () => { |
| 54 | + if (userState.users.length === 0) { |
| 55 | + const isImagePresent = await checkLocalImage(); |
| 56 | + if (isImagePresent) { |
| 57 | + checkHomeDir(); |
| 58 | + } else { |
| 59 | + setIsPullingImage(true); |
| 60 | + } |
| 61 | + } |
| 62 | + }; |
| 63 | + |
| 64 | + execChecks(); |
| 65 | + }, [triggerUseEffect]); |
| 66 | + |
| 67 | + const onClose = () => { |
| 68 | + setMountPointUser(userState.selectedUser); |
| 69 | + }; |
| 70 | + |
| 71 | + return ( |
| 72 | + <Dialog open onClose={onClose}> |
| 73 | + <DialogContent> |
| 74 | + <Box > |
| 75 | + <Box marginBottom={5} display="flex" gap={5} alignItems="center"> |
| 76 | + {hasLocalImage.checking && |
| 77 | + <Typography> |
| 78 | + Checking for local LocalStack image |
| 79 | + </Typography> |
| 80 | + } |
| 81 | + {userState.loading && |
| 82 | + <Typography> |
| 83 | + Checking for users |
| 84 | + </Typography> |
| 85 | + } |
| 86 | + {isPullingImage && |
| 87 | + <> |
| 88 | + <Typography> |
| 89 | + Pulling localstack/localstack:latest... Please do not exit this view |
| 90 | + </Typography> |
| 91 | + <DownloadProgress callback={() => { |
| 92 | + setIsPullingImage(false); |
| 93 | + setTriggerUseEffect(!triggerUseEffect); |
| 94 | + }} /> |
| 95 | + </> |
| 96 | + } |
| 97 | + { |
| 98 | + (hasLocalImage.checking || userState.loading) && <CircularProgress /> |
| 99 | + } |
| 100 | + { |
| 101 | + userState.users.length > 0 && |
| 102 | + <FormControl sx={{ minWidth: 120 }} size="small" variant='outlined'> |
| 103 | + <Select |
| 104 | + value={userState.selectedUser || userState.users[0]} |
| 105 | + onChange={({ target }) => |
| 106 | + setUserState({ loading: userState.loading, selectedUser: target.value, users: userState.users })} |
| 107 | + > |
| 108 | + {userState.users.map(user => ( |
| 109 | + <MenuItem key={user} value={user}>{user}</MenuItem> |
| 110 | + ))} |
| 111 | + </Select> |
| 112 | + </FormControl> |
| 113 | + } |
| 114 | + </Box> |
| 115 | + <Typography variant='h3' gutterBottom> |
| 116 | + Select where LocalStack will be mounted |
| 117 | + </Typography> |
| 118 | + <Typography variant='subtitle2'> |
| 119 | + {`For MacOS users it will be under /Users/${userState.selectedUser || 'loading...'}/.localstack-volume`} |
| 120 | + </Typography> |
| 121 | + <Typography variant='subtitle2' gutterBottom> |
| 122 | + {`For Linux/Windows users it will be under \ |
| 123 | + /home/${userState.selectedUser || 'loading...'}/.localstack-volume`} |
| 124 | + </Typography> |
| 125 | + </Box> |
| 126 | + </DialogContent> |
| 127 | + <DialogActions> |
| 128 | + <Button |
| 129 | + onClick={onClose} |
| 130 | + disabled={!userState.selectedUser || userState.selectedUser === ''} |
| 131 | + > |
| 132 | + Confirm |
| 133 | + </Button> |
| 134 | + </DialogActions> |
| 135 | + </Dialog > |
| 136 | + ); |
| 137 | +}; |
0 commit comments