diff --git a/Makefile b/Makefile index 3120f24..3bc6190 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,9 @@ install-extension: build-extension ## Install the extension update-extension: build-extension ## Update the extension docker extension update $(IMAGE):$(TAG) +install-dev: + cd ui/ && npm install + debug: ## Start the extension in debug mode docker extension dev debug $(IMAGE) diff --git a/ui/src/components/Header/Controller.tsx b/ui/src/components/Header/Controller.tsx index 09afea4..f338cab 100644 --- a/ui/src/components/Header/Controller.tsx +++ b/ui/src/components/Header/Controller.tsx @@ -14,6 +14,8 @@ import { PRO_IMAGE, COMMUNITY_IMAGE, FLAGS_AS_STRING, + WIN_OS, + MAC_OS, } from '../../constants'; import { LongMenu } from './Menu'; import { DockerContainer, DockerImage } from '../../types'; @@ -52,10 +54,10 @@ export const Controller = (): ReactElement => { if (!hasSkippedConfiguration) { switch (ddClient.host.platform) { - case 'win32': + case WIN_OS: location = `\\\\wsl$\\${os}\\home\\${user}\\.cache\\localstack\\volume`; break; - case 'darwin': + case MAC_OS: location = `/Users/${user}/Library/Caches/localstack/volume`; break; default: diff --git a/ui/src/components/Views/Configs/SettingsForm.tsx b/ui/src/components/Views/Configs/SettingsForm.tsx index 47f756b..6c82d25 100644 --- a/ui/src/components/Views/Configs/SettingsForm.tsx +++ b/ui/src/components/Views/Configs/SettingsForm.tsx @@ -1,47 +1,24 @@ -import { ExecResult } from '@docker/extension-api-client-types/dist/v1'; -import { - Box, - Button, - CircularProgress, - Dialog, - DialogContent, - Divider, - FormControl, - MenuItem, - Paper, - Select, - Step, - StepLabel, - Stepper, - Typography, -} from '@mui/material'; -import React, { ReactElement, useEffect, useState } from 'react'; -import { - getOSsFromBinary, - getUsersFromBinaryUnix, - getUsersFromBinaryWindows, - useDDClient, - useMountPoint, -} from '../../../services'; -import { ConfirmableButton } from '../../Feedback'; - -const ShrinkedCircularProgress = (): ReactElement => ; +import { Dialog, DialogContent, Step, StepLabel, Stepper } from '@mui/material'; +import React, { ReactElement, useState } from 'react'; +import { useDDClient } from '../../../services'; +import { StepZero, StepOne, StepTwo } from './Stepper'; interface MountPointFormProps { initialState: number; } -export const SettingsForm = ({ initialState }: MountPointFormProps): ReactElement => { - - const [userState, setUserState] = useState({ loading: false, selectedUser: '', users: [] }); - const [osState, setOsState] = useState({ loading: false, selectedOS: '', OSs: [] }); - const [triggerUserCheck, setTriggerUserCheck] = useState(false); +export const SettingsForm = ({ + initialState, +}: MountPointFormProps): ReactElement => { const [activeStep, setActiveStep] = useState(initialState); - const { setMountPointData, user, os } = useMountPoint(); const { client: ddClient } = useDDClient(); - const steps = ['Enable Docker Desktop option', 'Launching pro container', 'Set mount point']; + const steps = [ + 'Enable Docker Desktop option', + 'Launching pro container', + 'Set mount point', + ]; const handleNext = () => { if (activeStep !== steps.length - 1) { @@ -53,98 +30,15 @@ export const SettingsForm = ({ initialState }: MountPointFormProps): ReactElemen setActiveStep(activeStep - 1); }; - const getMountPointPath = (): string => { - if (ddClient.host.platform === 'darwin') { - return `/Users/${userState.selectedUser || 'loading...'}/Library/Caches/localstack/volume`; - } - return `/home/${userState.selectedUser || 'loading...'}/.cache/localstack/volume`; - }; - - const checkWindowsDistro = async () => { - setOsState({ ...osState, loading: true }); - - const res = await ddClient.extension.host?.cli.exec('checkWSLOS.cmd', []); - - const foundOSs = getOSsFromBinary(res.stdout); - - setOsState({ loading: false, selectedOS: os || foundOSs[0], OSs: foundOSs }); - setTriggerUserCheck(true); - }; - - const checkUser = async () => { - setUserState({ ...userState, loading: true }); - - let res: ExecResult; - let foundUsers = []; - if (ddClient.host.platform === 'win32') { - res = await ddClient.extension.host?.cli.exec('checkUser.cmd', [osState.selectedOS]); - foundUsers = getUsersFromBinaryWindows(res.stdout); - } else { - res = await ddClient.extension.host?.cli.exec('checkUser.sh', []); - foundUsers = getUsersFromBinaryUnix(res.stdout); - } - - if (res.stderr || !res.stdout) { - ddClient.desktopUI.toast.error(`Error while locating users: ${res.stderr} using /tmp as mount point`); - closeWithoutSetting(); - } - - setUserState({ loading: false, selectedUser: user || foundUsers[0], users: foundUsers }); - }; - - const locateMountPoint = async () => { - if (ddClient.host.platform === 'win32') { - checkWindowsDistro(); - } else { - checkUser(); - } - }; - - useEffect(() => { - const execChecks = async () => { - if (userState.users.length === 0 - || (ddClient.host.platform === 'win32' && osState.OSs.length === 0)) { - locateMountPoint(); - } - }; - - execChecks(); - }, []); - - useEffect(() => { - if (osState.selectedOS) { - checkUser(); - } - }, [triggerUserCheck]); - - const saveAndClose = () => { - setMountPointData({ - user: userState.selectedUser, - os: osState.selectedOS, - showForm: false, - showSetupWarning: false, - hasSkippedConfiguration: false, - }); - }; - - const closeWithoutSetting = () => { - setMountPointData({ - user: userState.selectedUser, - os: osState.selectedOS, - showForm: false, - showSetupWarning: false, - hasSkippedConfiguration: true, - }); - }; - const onClose = () => { ddClient.desktopUI.toast.warning('Complete the setup first'); }; - const handleOsChange = (target: string) => { - setOsState({ ...osState, selectedOS: target }); - checkUser(); - }; + const stepsList = [ + , + , + , + ]; return ( @@ -154,130 +48,10 @@ export const SettingsForm = ({ initialState }: MountPointFormProps): ReactElemen {label} - ), - )} + ))} - - {activeStep === 0 && - <> - - Make sure to have the option "Show Docker Extensions system containers" enabled. - To enable it visit your settings: - -
    -
  • Navigate to Settings
  • -
  • Select the Extensions tab
  • -
  • Next to Show Docker Extensions system containers, select the checkbox
  • -
  • In the bottom-right corner, select Apply & Restart
  • -
- - } - { - activeStep === 1 && - - In order to start the Pro container, add a configuration with the variable LOCALSTACK_AUTH_TOKEN - set to your auth token and select that configuration in the top right corner. - API Keys are also supported, but will be deprecated in the future. - - } - {activeStep === 2 && - <> - - Default mount point settings -
- {ddClient.host.platform === 'win32' && - <> - - WSL distro - - - Select in which WSL distro you want to mount the container - - {osState.loading ? - - : - - - } - - } - <> - - User - - - Select under which user you want to mount the container - - {userState.loading || osState.loading ? - - : - - - } - -
- {`The LocalStack container will be mounted under ${getMountPointPath()}`} - - *You can still change this by overriding the LOCALSTACK_VOLUME_DIR environment variable - - } -
- - - - { - activeStep === steps.length - 1 ? - <> - - Close - - - - : - - } - - + {stepsList[activeStep]} -
+ ); }; diff --git a/ui/src/components/Views/Configs/Stepper/StepOne.tsx b/ui/src/components/Views/Configs/Stepper/StepOne.tsx new file mode 100644 index 0000000..fed9715 --- /dev/null +++ b/ui/src/components/Views/Configs/Stepper/StepOne.tsx @@ -0,0 +1,20 @@ +import { Typography } from '@mui/material'; +import React, { ReactElement } from 'react'; +import { StepTemplate, StepTemplateProps } from './StepTemplate'; + +type Props = Pick; + +export const StepOne = ({ handleBack, handleNext }: Props): ReactElement => ( + + + In order to start the Pro container, add a configuration with the variable + LOCALSTACK_AUTH_TOKEN set to your auth token and select that configuration + in the top right corner. API Keys are also supported, but will be + deprecated in the future. + + +); diff --git a/ui/src/components/Views/Configs/Stepper/StepTemplate.tsx b/ui/src/components/Views/Configs/Stepper/StepTemplate.tsx new file mode 100644 index 0000000..229b771 --- /dev/null +++ b/ui/src/components/Views/Configs/Stepper/StepTemplate.tsx @@ -0,0 +1,34 @@ +import { Box, Button } from '@mui/material'; +import React, { ReactElement } from 'react'; + +export type StepTemplateProps = { + children: ReactElement; + isFirstElement: boolean; + handleBack?: () => void; + handleNext?: () => void; + FinishActions?: ReactElement; +}; + +export const StepTemplate = ({ + children, + isFirstElement, + handleNext, + handleBack, + FinishActions, +}: StepTemplateProps): ReactElement => ( + <> + {children} + + + + {FinishActions ? ( + { FinishActions } + ) : ( + + )} + + + +); diff --git a/ui/src/components/Views/Configs/Stepper/StepTwo.tsx b/ui/src/components/Views/Configs/Stepper/StepTwo.tsx new file mode 100644 index 0000000..0c2ed76 --- /dev/null +++ b/ui/src/components/Views/Configs/Stepper/StepTwo.tsx @@ -0,0 +1,251 @@ +import { + Button, + CircularProgress, + Divider, + FormControl, + MenuItem, + Paper, + Select, + Typography, +} from '@mui/material'; +import React, { ReactElement, useEffect, useState } from 'react'; +import { ExecResult } from '@docker/extension-api-client-types/dist/v1'; +import { MAC_OS, WIN_OS } from '../../../../constants'; +import { + getOSsFromBinary, + getUsersFromBinaryUnix, + getUsersFromBinaryWindows, + useDDClient, + useMountPoint, +} from '../../../../services'; +import { StepTemplate, StepTemplateProps } from './StepTemplate'; +import { ConfirmableButton } from '../../../Feedback'; + +const ShrunkCircularProgress = (): ReactElement => ( + +); + +type Props = Pick; +export const StepTwo = ({ handleBack }: Props): ReactElement => { + const [userState, setUserState] = useState({ + loading: false, + selectedUser: '', + users: [], + }); + const [osState, setOsState] = useState({ + loading: false, + selectedOS: '', + OSs: [], + }); + const [triggerUserCheck, setTriggerUserCheck] = useState(false); + + const { client: ddClient } = useDDClient(); + const { setMountPointData, user, os } = useMountPoint(); + + const saveAndClose = () => { + setMountPointData({ + user: userState.selectedUser, + os: osState.selectedOS, + showForm: false, + showSetupWarning: false, + hasSkippedConfiguration: false, + }); + }; + + const closeWithoutSetting = () => { + setMountPointData({ + user: userState.selectedUser, + os: osState.selectedOS, + showForm: false, + showSetupWarning: false, + hasSkippedConfiguration: true, + }); + }; + const getMountPointPath = (): string => { + if (ddClient.host.platform === MAC_OS) { + return `/Users/${ + userState.selectedUser || 'loading...' + }/Library/Caches/localstack/volume`; + } + return `/home/${ + userState.selectedUser || 'loading...' + }/.cache/localstack/volume`; + }; + + const checkUser = async () => { + setUserState({ ...userState, loading: true }); + + let res: ExecResult; + let foundUsers = []; + if (ddClient.host.platform === WIN_OS) { + res = await ddClient.extension.host?.cli.exec('checkUser.cmd', [ + osState.selectedOS, + ]); + foundUsers = getUsersFromBinaryWindows(res.stdout); + } else { + res = await ddClient.extension.host?.cli.exec('checkUser.sh', []); + foundUsers = getUsersFromBinaryUnix(res.stdout); + } + + if (res.stderr || !res.stdout) { + ddClient.desktopUI.toast.error( + `Error while locating users: ${res.stderr} using /tmp as mount point`, + ); + closeWithoutSetting(); + } + + setUserState({ + loading: false, + selectedUser: user || foundUsers[0], + users: foundUsers, + }); + }; + + const checkWindowsDistro = async () => { + setOsState({ ...osState, loading: true }); + + const res = await ddClient.extension.host?.cli.exec('checkWSLOS.cmd', []); + + const foundOSs = getOSsFromBinary(res.stdout); + + setOsState({ + loading: false, + selectedOS: os || foundOSs[0], + OSs: foundOSs, + }); + setTriggerUserCheck(true); + }; + + const locateMountPoint = async () => { + if (ddClient.host.platform === WIN_OS) { + checkWindowsDistro(); + } else { + checkUser(); + } + }; + useEffect(() => { + const execChecks = async () => { + if ( + userState.users.length === 0 || + (ddClient.host.platform === WIN_OS && osState.OSs.length === 0) + ) { + locateMountPoint(); + } + }; + + execChecks(); + }, []); + + useEffect(() => { + if (osState.selectedOS) { + checkUser(); + } + }, [triggerUserCheck]); + + const handleOsChange = (target: string) => { + setOsState({ ...osState, selectedOS: target }); + checkUser(); + }; + + return ( + + + Close + + + + } + > + <> + Default mount point settings +
+ + {ddClient.host.platform === WIN_OS && ( + <> + WSL distro + + Select in which WSL distro you want to mount the container + + {osState.loading ? ( + + ) : ( + + + + )} + + + )} + <> + User + + Select under which user you want to mount the container + + {userState.loading || osState.loading ? ( + + ) : ( + + + + )} + + +
+ + {`The LocalStack container will be mounted under ${getMountPointPath()}`} + + + *You can still change this by overriding the LOCALSTACK_VOLUME_DIR + environment variable + + +
+ ); +}; diff --git a/ui/src/components/Views/Configs/Stepper/StepZero.tsx b/ui/src/components/Views/Configs/Stepper/StepZero.tsx new file mode 100644 index 0000000..393c87f --- /dev/null +++ b/ui/src/components/Views/Configs/Stepper/StepZero.tsx @@ -0,0 +1,24 @@ +import { Typography } from '@mui/material'; +import React, { ReactElement } from 'react'; +import { StepTemplate, StepTemplateProps } from './StepTemplate'; + +type Props = Pick; + +export const StepZero = ({ handleNext }: Props): ReactElement => ( + + <> + + Make sure to have the option "Show Docker Extensions system + containers" enabled. To enable it visit your settings: + +
    +
  • Navigate to Settings
  • +
  • Select the Extensions tab
  • +
  • + Next to Show Docker Extensions system containers, select the checkbox +
  • +
  • In the bottom-right corner, select Apply & Restart
  • +
+ +
+); diff --git a/ui/src/components/Views/Configs/Stepper/index.ts b/ui/src/components/Views/Configs/Stepper/index.ts new file mode 100644 index 0000000..254519c --- /dev/null +++ b/ui/src/components/Views/Configs/Stepper/index.ts @@ -0,0 +1,3 @@ +export * from './StepOne'; +export * from './StepTwo'; +export * from './StepZero'; diff --git a/ui/src/constants/common.ts b/ui/src/constants/common.ts index 2d44d1c..4e36850 100644 --- a/ui/src/constants/common.ts +++ b/ui/src/constants/common.ts @@ -2,3 +2,5 @@ export const DEFAULT_CONFIGURATION_ID = '00000000-0000-0000-0000-000000000000'; export const CORS_ALLOW_DEFAULT = 'http://localhost:3000'; export const COMMUNITY_IMAGE = 'localstack/localstack'; export const PRO_IMAGE = 'localstack/localstack-pro'; +export const WIN_OS = 'win32'; +export const MAC_OS = 'darwin'; diff --git a/ui/src/services/hooks/utils.ts b/ui/src/services/hooks/utils.ts index cc22d13..a5e2cef 100644 --- a/ui/src/services/hooks/utils.ts +++ b/ui/src/services/hooks/utils.ts @@ -1,6 +1,7 @@ import { DockerDesktopClient } from '@docker/extension-api-client-types/dist/v1'; import { useContext } from 'react'; import { GlobalDDContext } from '../context/GlobalDDContext'; +import { MAC_OS, WIN_OS } from '../../constants'; export interface useDDClientReturn { @@ -23,9 +24,9 @@ export const useDDClient = (): useDDClientReturn => { } let os = ''; - if (client.host.platform === 'darwin' || client.host.platform === 'linux') { + if (client.host.platform === MAC_OS || client.host.platform === 'linux') { os = client.host.platform; - } else if (client.host.platform === 'win32' && architecture === 'amd') { + } else if (client.host.platform === WIN_OS && architecture === 'amd') { os = 'windows'; } else { client.desktopUI.toast.error(