Skip to content

Commit

Permalink
feat(api-client,app,react-api-client): upload splash logo from deskto…
Browse files Browse the repository at this point in the history
…p app (#14941)

adds the upload input component, api-client, and react-api-client
functions needed to upload a splash logo from the factory mode slideout

closes PLAT-283
  • Loading branch information
brenthagen authored Apr 22, 2024
1 parent 737c58c commit 433ef44
Show file tree
Hide file tree
Showing 14 changed files with 397 additions and 36 deletions.
2 changes: 2 additions & 0 deletions api-client/src/robot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export { acknowledgeEstopDisengage } from './acknowledgeEstopDisengage'
export { getLights } from './getLights'
export { setLights } from './setLights'
export { getRobotSettings } from './getRobotSettings'
export { updateRobotSetting } from './updateRobotSetting'

export type {
DoorStatus,
Expand All @@ -15,4 +16,5 @@ export type {
RobotSettingsField,
RobotSettingsResponse,
SetLightsData,
UpdateRobotSettingRequest,
} from './types'
5 changes: 5 additions & 0 deletions api-client/src/robot/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export interface RobotSettingsField {

export type RobotSettings = RobotSettingsField[]

export interface UpdateRobotSettingRequest {
id: string
value: boolean | null
}

export interface RobotSettingsResponse {
settings: RobotSettings
links?: { restart?: string }
Expand Down
18 changes: 18 additions & 0 deletions api-client/src/robot/updateRobotSetting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { POST, request } from '../request'

import type { ResponsePromise } from '../request'
import type { HostConfig } from '../types'
import type { RobotSettingsResponse, UpdateRobotSettingRequest } from './types'

export function updateRobotSetting(
config: HostConfig,
id: string,
value: boolean
): ResponsePromise<RobotSettingsResponse> {
return request<RobotSettingsResponse, UpdateRobotSettingRequest>(
POST,
'/settings',
{ id, value },
config
)
}
24 changes: 24 additions & 0 deletions api-client/src/system/createSplash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { POST, request } from '../request'
import type { ResponsePromise } from '../request'
import type { HostConfig } from '../types'

export function createSplash(
config: HostConfig,
file: File
): ResponsePromise<void> {
// sanitize file name to ensure no spaces
const renamedFile = new File([file], file.name.replace(' ', '_'), {
type: 'image/png',
})

const formData = new FormData()
formData.append('file', renamedFile)

// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
return request<void, FormData>(
POST,
'/system/oem_mode/upload_splash',
formData,
config
)
}
1 change: 1 addition & 0 deletions api-client/src/system/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { createAuthorization } from './createAuthorization'
export { createRegistration } from './createRegistration'
export { createSplash } from './createSplash'
export { getConnections } from './getConnections'
export * from './types'
4 changes: 4 additions & 0 deletions app/src/assets/localization/en/device_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"check_for_updates": "Check for updates",
"checking_for_updates": "Checking for updates",
"choose": "Choose...",
"choose_file": "Choose file",
"choose_network_type": "Choose network type",
"choose_reset_settings": "Choose reset settings",
"clear_all_data": "Clear all data",
Expand Down Expand Up @@ -293,6 +294,9 @@
"update_robot_software": "Update robot software manually with a local file (.zip)",
"updating": "Updating",
"update_requires_restarting_robot": "Updating the robot software requires restarting the robot",
"upload_custom_logo_description": "Upload a logo for the robot to display during boot up. If no file is uploaded, we will display an anonymous logo.",
"upload_custom_logo_dimensions": "The logo must fit within dimensions 1024 x 600 and be a PNG file (.png).",
"upload_custom_logo": "Upload custom logo",
"usage_settings": "Usage Settings",
"usb": "USB",
"usb_to_ethernet_description": "Looking for USB-to-Ethernet Adapter info?",
Expand Down
60 changes: 60 additions & 0 deletions app/src/molecules/FileUpload/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as React from 'react'
import { css } from 'styled-components'

import {
ALIGN_CENTER,
BORDERS,
Btn,
COLORS,
DIRECTION_COLUMN,
Flex,
Icon,
JUSTIFY_SPACE_BETWEEN,
SPACING,
StyledText,
} from '@opentrons/components'

const FILE_UPLOAD_STYLE = css`
&:hover > svg {
background: ${COLORS.black90}${COLORS.opacity20HexCode};
}
&:active > svg {
background: ${COLORS.black90}${COLORS.opacity20HexCode}};
}
`

interface FileUploadProps {
file: File
fileError: string | null
handleClick: () => unknown
}

export function FileUpload({
file,
fileError,
handleClick,
}: FileUploadProps): JSX.Element {
return (
<Flex flexDirection={DIRECTION_COLUMN} gridGap={SPACING.spacing4}>
<Btn onClick={handleClick} aria-label="remove_file">
<Flex
alignItems={ALIGN_CENTER}
backgroundColor={fileError == null ? COLORS.grey20 : COLORS.red30}
borderRadius={BORDERS.borderRadius4}
height={SPACING.spacing44}
justifyContent={JUSTIFY_SPACE_BETWEEN}
padding={SPACING.spacing8}
css={FILE_UPLOAD_STYLE}
>
<StyledText as="p">{file.name}</StyledText>
<Icon name="close" size="1.5rem" borderRadius="50%" />
</Flex>
</Btn>
{fileError != null ? (
<StyledText as="label" color={COLORS.red50}>
{fileError}
</StyledText>
) : null}
</Flex>
)
}
34 changes: 22 additions & 12 deletions app/src/molecules/UploadInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,19 @@ const StyledInput = styled.input`
export interface UploadInputProps {
onUpload: (file: File) => unknown
onClick?: () => void
uploadButtonText?: string
uploadText?: string | JSX.Element
dragAndDropText?: string | JSX.Element
}

export function UploadInput(props: UploadInputProps): JSX.Element | null {
const {
dragAndDropText,
onClick,
onUpload,
uploadButtonText,
uploadText,
} = props
const { t } = useTranslation('protocol_info')

const fileInput = React.useRef<HTMLInputElement>(null)
Expand All @@ -60,7 +68,7 @@ export function UploadInput(props: UploadInputProps): JSX.Element | null {
const handleDrop: React.DragEventHandler<HTMLLabelElement> = e => {
e.preventDefault()
e.stopPropagation()
Array.from(e.dataTransfer.files).forEach(f => props.onUpload(f))
Array.from(e.dataTransfer.files).forEach(f => onUpload(f))
setIsFileOverDropZone(false)
}
const handleDragEnter: React.DragEventHandler<HTMLLabelElement> = e => {
Expand All @@ -81,11 +89,11 @@ export function UploadInput(props: UploadInputProps): JSX.Element | null {
}

const handleClick: React.MouseEventHandler<HTMLButtonElement> = _event => {
props.onClick != null ? props.onClick() : fileInput.current?.click()
onClick != null ? onClick() : fileInput.current?.click()
}

const onChange: React.ChangeEventHandler<HTMLInputElement> = event => {
;[...(event.target.files ?? [])].forEach(f => props.onUpload(f))
;[...(event.target.files ?? [])].forEach(f => onUpload(f))
if ('value' in event.currentTarget) event.currentTarget.value = ''
}

Expand All @@ -97,18 +105,20 @@ export function UploadInput(props: UploadInputProps): JSX.Element | null {
alignItems={ALIGN_CENTER}
gridGap={SPACING.spacing24}
>
<StyledText
as="p"
textAlign={TYPOGRAPHY.textAlignCenter}
marginTop={SPACING.spacing16}
>
{props.uploadText}
</StyledText>
{uploadText != null ? (
<StyledText
as="p"
textAlign={TYPOGRAPHY.textAlignCenter}
marginTop={SPACING.spacing16}
>
{uploadText}
</StyledText>
) : null}
<PrimaryButton
onClick={handleClick}
id="UploadInput_protocolUploadButton"
>
{t('upload')}
{uploadButtonText ?? t('upload')}
</PrimaryButton>

<StyledLabel
Expand All @@ -127,7 +137,7 @@ export function UploadInput(props: UploadInputProps): JSX.Element | null {
name="upload"
marginBottom={SPACING.spacing24}
/>
{props.dragAndDropText}
{dragAndDropText}
<StyledInput
id="file_input"
data-testid="file_input"
Expand Down
Loading

0 comments on commit 433ef44

Please sign in to comment.