Skip to content

Commit

Permalink
DownloadImageModal: Add Releases dropdown for preloading
Browse files Browse the repository at this point in the history
Change-type: minor
  • Loading branch information
myarmolinsky committed Feb 2, 2023
1 parent 57e0ef3 commit 4ffe224
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 32 deletions.
3 changes: 3 additions & 0 deletions src/hooks/useTranslation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const translationMap = {
'warnings.some_fields_are_invalid': 'Some fields are invalid',
'warnings.image_deployed_to_docker':
'This image is deployed to docker so you can only download its config',
'warnings.option_not_available_while_preloading':
'This option is not available while "Preload with release" is selected',

'errors.no_tags_for_selected_itemtype':
'The selected {{itemType}} has no tags',
Expand All @@ -59,6 +61,7 @@ const translationMap = {

// DownloadImageModal
'placeholders.select_device_type': 'Select device type',
'placeholders.preload_release': 'Preload release',
'placeholders.select_os_type_status': 'Select OS type',
'placeholders.choose_device_type': 'Choose a device type...',
'placeholders.select_os_type': 'Select OS type...',
Expand Down
94 changes: 74 additions & 20 deletions src/unstable-temp/DownloadImageModal/DownloadImageModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import uniq from 'lodash/uniq';
import {
OsVersionsByDeviceType,
Application,
Release,
DeviceType,
OsTypesEnum,
} from './models';
Expand All @@ -22,11 +23,13 @@ import { Spinner } from '../../components/Spinner';
import { Alert } from '../../components/Alert';
import { Modal } from '../../components/Modal';
import { Img } from '../../components/Img';
import { Checkbox } from '../../components/Checkbox';
import { useTranslation } from '../../hooks/useTranslation';
import { FALLBACK_LOGO_UNKNOWN_DEVICE, stripVersionBuild } from './utils';
import { OsConfiguration } from './OsConfiguration';
import { FormModel } from './FormModel';
import { DownloadImageLabel, FormModel } from './FormModel';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { ReleaseSelector } from './ReleaseSelector';

export const DeviceLogo = styled(Img)<{ small?: boolean }>`
// To prevent Save Image dialog
Expand Down Expand Up @@ -81,7 +84,6 @@ export interface DownloadOptions extends DownloadOptionsBase, FormModel {}

export interface UnstableTempDownloadImageModalProps {
application: Application;
releaseId?: number;
compatibleDeviceTypes: DeviceType[] | null;
initialDeviceType?: DeviceType;
initialOsVersions?: OsVersionsByDeviceType;
Expand Down Expand Up @@ -116,7 +118,6 @@ export interface UnstableTempDownloadImageModalProps {
export const UnstableTempDownloadImageModal = ({
downloadUrl,
application,
releaseId,
compatibleDeviceTypes,
initialDeviceType,
initialOsVersions,
Expand Down Expand Up @@ -144,6 +145,22 @@ export const UnstableTempDownloadImageModal = ({
const [osTypes, setOsTypes] = React.useState<string[]>(
getUniqueOsTypes(osVersions, deviceType?.slug),
);
const defaultReleaseRawVersion =
application.should_be_running__release?.raw_version ??
application.owns__release[0]?.raw_version;
const defaultRelease: Release = {
raw_version: `Fleet's current release (${
!!defaultReleaseRawVersion ? `v${defaultReleaseRawVersion}` : 'none'
})`,
id:
application.should_be_running__release?.id ??
application.owns__release[0]?.id ??
undefined,
};
const [selectedRelease, setSelectedRelease] =
React.useState<Release>(defaultRelease);
const [preloadWithRelease, setPreloadWithRelease] = React.useState(false);
const [flashSelected, setFlashSelected] = React.useState(true);

const [deviceTypeHasEsr, setDeviceTypeHasEsr] = React.useState<
Dictionary<boolean>
Expand Down Expand Up @@ -255,11 +272,15 @@ export const UnstableTempDownloadImageModal = ({
setIsDownloadingConfig={setIsDownloadingConfig}
deviceType={deviceType}
appId={application.id}
releaseId={releaseId}
releaseId={
preloadWithRelease ? selectedRelease?.id : undefined
}
preloading={preloadWithRelease}
downloadUrl={downloadUrl}
rawVersion={rawVersion}
developmentMode={developmentMode}
modalActions={modalActions}
setFlashSelected={setFlashSelected}
authToken={authToken}
{...(downloadConfig && {
downloadConfig: ({
Expand All @@ -277,22 +298,55 @@ export const UnstableTempDownloadImageModal = ({
getDownloadSize(deviceType, rawVersion),
})}
configurationComponent={
<OsConfiguration
compatibleDeviceTypes={compatibleDeviceTypes}
selectedDeviceType={deviceType}
selectedOsType={osType}
deviceTypeOsVersions={osVersions}
osTypes={osTypes}
isInitialDefault={isInitialDefault}
onSelectedDeviceTypeChange={setDeviceType}
onSelectedVersionChange={setRawVersion}
onSelectedDevelopmentMode={setDevelopmentMode}
onSelectedOsTypeChange={setOsType}
hasEsrVersions={
deviceTypeHasEsr[deviceType.slug] ?? false
}
docsIcon={docsIcon}
/>
<>
<OsConfiguration
compatibleDeviceTypes={compatibleDeviceTypes}
selectedDeviceType={deviceType}
selectedOsType={osType}
deviceTypeOsVersions={osVersions}
osTypes={osTypes}
isInitialDefault={isInitialDefault}
onSelectedDeviceTypeChange={setDeviceType}
onSelectedVersionChange={setRawVersion}
onSelectedDevelopmentMode={setDevelopmentMode}
onSelectedOsTypeChange={setOsType}
hasEsrVersions={
deviceTypeHasEsr[deviceType.slug] ?? false
}
docsIcon={docsIcon}
/>
<Box mb={3}>
<DownloadImageLabel>
{t('placeholders.preload_release')}
</DownloadImageLabel>
<Flex alignItems="center" mx={-2}>
<Box flex={3} mx={2}>
<ReleaseSelector
application={application}
selectedRelease={selectedRelease}
setSelectedRelease={setSelectedRelease}
defaultRelease={defaultRelease}
disabled={!preloadWithRelease || !flashSelected}
/>
</Box>
<Box flex={2} mx={2}>
<Checkbox
checked={preloadWithRelease}
label="Preload with release"
onChange={() =>
setPreloadWithRelease(!preloadWithRelease)
}
disabled={!flashSelected}
tooltip={
!flashSelected
? 'To enable this option, select "Flash" as the download method'
: ''
}
/>
</Box>
</Flex>
</Box>
</>
}
/>
)}
Expand Down
20 changes: 19 additions & 1 deletion src/unstable-temp/DownloadImageModal/ImageForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ interface ImageFormProps {
downloadUrl: string;
appId: number;
releaseId?: number;
preloading: boolean;
rawVersion: string | null;
developmentMode: boolean;
deviceType: DeviceType;
Expand All @@ -168,13 +169,15 @@ interface ImageFormProps {
version: string | null,
) => Promise<string | undefined>;
modalActions?: ModalAction[];
setFlashSelected: (selected: boolean) => void;
configurationComponent: React.ReactNode;
}

export const ImageForm = ({
downloadUrl,
appId,
releaseId,
preloading,
rawVersion,
developmentMode,
deviceType,
Expand All @@ -184,6 +187,7 @@ export const ImageForm = ({
downloadConfig,
getDownloadSize,
modalActions,
setFlashSelected,
configurationComponent,
}: ImageFormProps) => {
const { t } = useTranslation();
Expand Down Expand Up @@ -236,9 +240,11 @@ export const ImageForm = ({
formElement?.current?.submit();
},
icon: <FontAwesomeIcon icon={faDownload} />,
disabled: hasDockerImageDownload,
disabled: hasDockerImageDownload || preloading,
tooltip: hasDockerImageDownload
? t('warnings.image_deployed_to_docker')
: preloading
? t('warnings.option_not_available_while_preloading')
: '',
label: `${t('actions.download_balenaos')} ${
rawVersion && downloadSize ? ` (~${downloadSize})` : ''
Expand All @@ -258,6 +264,10 @@ export const ImageForm = ({
}
startDownload(true);
},
disabled: preloading,
tooltip: preloading
? t('warnings.option_not_available_while_preloading')
: '',
icon: <FontAwesomeIcon icon={faDownload} />,
label: t('actions.download_configuration_file_only'),
});
Expand All @@ -267,6 +277,14 @@ export const ImageForm = ({
actions.find((a) => !a.disabled)?.label || actions[0].label,
);

React.useEffect(() => {
if (selectedActionLabel === 'Flash') {
setFlashSelected(true);
} else {
setFlashSelected(false);
}
}, [selectedActionLabel]);

const startDownload = (downloadConfigOnly: boolean) => {
if (typeof onDownloadStart === 'function') {
onDownloadStart(downloadConfigOnly, {
Expand Down
12 changes: 1 addition & 11 deletions src/unstable-temp/DownloadImageModal/OsConfiguration.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react';
import styled from 'styled-components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
Expand All @@ -26,16 +25,7 @@ import { useTranslation } from '../../hooks/useTranslation';
import { getOsVariantDisplayText } from './utils';
import { useTheme } from '../../hooks/useTheme';
import { Theme } from '~/theme';

export const DownloadImageLabel = styled.label`
display: flex;
flex-direction: row;
align-items: center;
height: 21px;
font-size: 12px;
margin-bottom: 8px;
font-weight: normal;
`;
import { DownloadImageLabel } from './FormModel';

interface OsConfigurationProps {
deviceTypeOsVersions: OsVersionsByDeviceType;
Expand Down
84 changes: 84 additions & 0 deletions src/unstable-temp/DownloadImageModal/ReleaseSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import * as React from 'react';
import { Flex } from '../../components/Flex';
import { Select } from '../../components/Select';
import { Txt } from '../../components/Txt';
import { Application, Release } from './models';

interface ReleaseOptionProps {
release?: Release;
isSelected?: boolean;
}

const ReleaseOption = ({ release, isSelected }: ReleaseOptionProps) => {
const typeDisplayName = release?.raw_version;

return (
<Flex style={{ height: 36 }} py={2} pl={3} width="100%" alignItems="center">
<Txt ml={2} bold={isSelected}>
{!isNaN(parseInt(typeDisplayName?.charAt(0) ?? '', 10)) ? 'v' : ''}
{typeDisplayName}
</Txt>
</Flex>
);
};

interface ReleaseSelectorProps {
application: Application;
selectedRelease: Release;
setSelectedRelease: (release: Release) => void;
defaultRelease: Release;
disabled: boolean;
}

const ReleaseSelectorBase = ({
application,
selectedRelease,
setSelectedRelease,
defaultRelease,
disabled,
}: ReleaseSelectorProps) => {
const releasesWithDefault = [defaultRelease].concat(
application.owns__release,
);
const [query, setQuery] = React.useState('');
const [filteredReleases, setFilteredReleases] =
React.useState<Release[]>(releasesWithDefault);

React.useEffect(() => {
const filtered = releasesWithDefault.filter(
(r) => r.raw_version.indexOf(query.toLowerCase()) >= 0,
);

setFilteredReleases(filtered);
}, [application, query]);

return (
<Select<Release>
options={filteredReleases}
valueKey="id"
labelKey="raw_version"
value={selectedRelease}
valueLabel={<ReleaseOption release={selectedRelease} />}
onChange={({ option }) => {
setSelectedRelease(option);
setQuery('');
}}
onSearch={setQuery}
disabled={disabled}
tooltip={
disabled
? 'To enable this dropdown and select a release to preload onto your device, check the "Preload with release" checkbox.'
: ''
}
>
{(option) => (
<ReleaseOption
release={option}
isSelected={option.id === selectedRelease.id}
/>
)}
</Select>
);
};

export const ReleaseSelector = React.memo(ReleaseSelectorBase);
7 changes: 7 additions & 0 deletions src/unstable-temp/DownloadImageModal/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ export interface Application {
app_name: string;
slug: string;
uuid: string;
owns__release: Release[];
should_be_running__release: Release | null;
}

export interface Release {
id: number;
raw_version: string;
}

export interface DeviceTypeInstructions {
Expand Down

0 comments on commit 4ffe224

Please sign in to comment.