From 67579f4259eb23b1fb3b37dd754a287ef4e113be Mon Sep 17 00:00:00 2001 From: Nick Diehl <47604184+ncdiehl11@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:38:54 -0500 Subject: [PATCH] feat(app): add privacy policy acknowledgement screen on ODD (#14435) * feat(app): add privacy policy acknowledgement screen to new 7.2 bootup on ODD closes RAUT-818 --- app/src/App/OnDeviceDisplayApp.tsx | 7 ++ .../privacy_policy_qrcode.png | Bin 0 -> 10003 bytes .../localization/en/device_settings.json | 3 + .../NameRobot/ConfirmRobotName.tsx | 2 +- app/src/pages/ConnectViaEthernet/index.tsx | 2 +- app/src/pages/ConnectViaUSB/index.tsx | 2 +- .../__tests__/ConnectViaWifi.test.tsx | 2 +- app/src/pages/ConnectViaWifi/index.tsx | 2 +- app/src/pages/EmergencyStop/index.tsx | 15 ++- .../NameRobot/__tests__/NameRobot.test.tsx | 7 -- app/src/pages/NameRobot/index.tsx | 18 +-- app/src/pages/NetworkSetupMenu/index.tsx | 2 +- app/src/pages/PrivacyPolicy/index.tsx | 106 ++++++++++++++++++ .../RobotDashboard/AnalyticsOptInModal.tsx | 91 --------------- app/src/pages/RobotDashboard/WelcomeModal.tsx | 13 ++- .../__tests__/AnalyticsOptInModal.test.tsx | 86 -------------- .../__tests__/WelcomeModal.test.tsx | 2 - app/src/pages/RobotDashboard/index.tsx | 28 ++--- 18 files changed, 161 insertions(+), 227 deletions(-) create mode 100644 app/src/assets/images/on-device-display/privacy_policy_qrcode.png create mode 100644 app/src/pages/PrivacyPolicy/index.tsx delete mode 100644 app/src/pages/RobotDashboard/AnalyticsOptInModal.tsx delete mode 100644 app/src/pages/RobotDashboard/__tests__/AnalyticsOptInModal.test.tsx diff --git a/app/src/App/OnDeviceDisplayApp.tsx b/app/src/App/OnDeviceDisplayApp.tsx index d28f82b3b36..95a2abd1afd 100644 --- a/app/src/App/OnDeviceDisplayApp.tsx +++ b/app/src/App/OnDeviceDisplayApp.tsx @@ -27,6 +27,7 @@ import { ConnectViaWifi } from '../pages/ConnectViaWifi' import { EmergencyStop } from '../pages/EmergencyStop' import { NameRobot } from '../pages/NameRobot' import { NetworkSetupMenu } from '../pages/NetworkSetupMenu' +import { PrivacyPolicy } from '../pages/PrivacyPolicy' import { ProtocolSetup } from '../pages/ProtocolSetup' import { RobotDashboard } from '../pages/RobotDashboard' import { RobotSettingsDashboard } from '../pages/RobotSettingsDashboard' @@ -195,6 +196,12 @@ export const onDeviceDisplayRoutes: RouteProps[] = [ name: 'Emergency Stop', path: '/emergency-stop', }, + { + Component: PrivacyPolicy, + exact: true, + name: 'Privacy Policy', + path: '/privacy-policy', + }, { Component: DeckConfigurationEditor, exact: true, diff --git a/app/src/assets/images/on-device-display/privacy_policy_qrcode.png b/app/src/assets/images/on-device-display/privacy_policy_qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..3de0cbe215865cb3a2307d2ad958b661b826e9a5 GIT binary patch literal 10003 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 the robot to move accurately and precisely, you need to calibrate it. Pipette and gripper calibration is an automated process that uses a calibration probe or pin.After calibration is complete, you can save the calibration data to your computer as a JSON file.", "about_calibration_title": "About Calibration", + "acknowledge_privacy_policy": "Acknowledge Privacy Policy", "advanced": "Advanced", + "agree": "I agree", "alpha_description": "Warning: alpha releases are feature-complete but may contain significant bugs.", "alternative_security_types": "Alternative security types", "alternative_security_types_description": "The Opentrons App supports connecting Flex to various enterprise access points. Connect via USB and finish setup in the app.", @@ -206,6 +208,7 @@ "pipette_offset_calibration_recommended": "Pipette Offset calibration recommended", "pipette_offset_calibrations_history": "See all Pipette Offset Calibration history", "pipette_offset_calibrations_title": "Pipette Offset Calibrations", + "privacy_policy_description": "By proceeding you are agreeing to share robot usage data. Opentrons uses this data to improve our products and services.To read more about our data collection policies, visit our Privacy Policy.", "problem_during_update": "This update is taking longer than usual.", "proceed_without_updating": "Proceed without update", "protocol_run_history": "Protocol run History", diff --git a/app/src/organisms/OnDeviceDisplay/NameRobot/ConfirmRobotName.tsx b/app/src/organisms/OnDeviceDisplay/NameRobot/ConfirmRobotName.tsx index b8a7123603f..be5fe3530b7 100644 --- a/app/src/organisms/OnDeviceDisplay/NameRobot/ConfirmRobotName.tsx +++ b/app/src/organisms/OnDeviceDisplay/NameRobot/ConfirmRobotName.tsx @@ -34,7 +34,7 @@ export function ConfirmRobotName({ } return ( <> - + - + - + { render() screen.getByTestId('StepMeter_StepMeterContainer') const bar = screen.getByTestId('StepMeter_StepMeterBar') - expect(bar).toHaveStyle('width: 33.33333333333333%') + expect(bar).toHaveStyle('width: 20%') }) it('should render Searching for networks', () => { diff --git a/app/src/pages/ConnectViaWifi/index.tsx b/app/src/pages/ConnectViaWifi/index.tsx index fb3fcc98077..97792806512 100644 --- a/app/src/pages/ConnectViaWifi/index.tsx +++ b/app/src/pages/ConnectViaWifi/index.tsx @@ -110,7 +110,7 @@ export function ConnectViaWifi(): JSX.Element { return ( <> - + disengaged @@ -38,7 +45,7 @@ export function EmergencyStop(): JSX.Element { return ( <> - + history.push('/robot-settings/rename-robot')} + onClick={() => { + seenOptIn && optedIn + ? history.push('/robot-settings/rename-robot') + : history.push('/privacy-policy') + }} /> diff --git a/app/src/pages/NameRobot/__tests__/NameRobot.test.tsx b/app/src/pages/NameRobot/__tests__/NameRobot.test.tsx index b07def9bebb..e58ffcd56d7 100644 --- a/app/src/pages/NameRobot/__tests__/NameRobot.test.tsx +++ b/app/src/pages/NameRobot/__tests__/NameRobot.test.tsx @@ -150,11 +150,4 @@ describe('NameRobot', () => { screen.getByText('Enter up to 17 characters (letters and numbers only)') screen.getByText('Confirm') }) - - it('should call a mock function when tapping back button', () => { - mockuseIsUnboxingFlowOngoing.mockReturnValue(false) - render() - fireEvent.click(screen.getByTestId('name_back_button')) - expect(mockPush).toHaveBeenCalledWith('/robot-settings') - }) }) diff --git a/app/src/pages/NameRobot/index.tsx b/app/src/pages/NameRobot/index.tsx index 2b65755b8fc..a8d4a0ebdba 100644 --- a/app/src/pages/NameRobot/index.tsx +++ b/app/src/pages/NameRobot/index.tsx @@ -19,7 +19,6 @@ import { COLORS, TYPOGRAPHY, Icon, - Btn, } from '@opentrons/components' import { useUpdateRobotNameMutation } from '@opentrons/react-api-client' @@ -153,7 +152,7 @@ export function NameRobot(): JSX.Element { ) : ( <> {isUnboxingFlowOngoing ? ( - + ) : null} - - { - if (isUnboxingFlowOngoing) { - history.push('/emergency-stop') - } else { - history.push('/robot-settings') - } - }} - > - - - + {isUnboxingFlowOngoing diff --git a/app/src/pages/NetworkSetupMenu/index.tsx b/app/src/pages/NetworkSetupMenu/index.tsx index fe245bf22f5..6d668131862 100644 --- a/app/src/pages/NetworkSetupMenu/index.tsx +++ b/app/src/pages/NetworkSetupMenu/index.tsx @@ -44,7 +44,7 @@ export function NetworkSetupMenu(): JSX.Element { return ( <> - + () + const isUnboxingFlowOngoing = useIsUnboxingFlowOngoing() + const seenOptedIn = useSelector(getAnalyticsOptInSeen) + const optedIn = useSelector(getAnalyticsOptedIn) + + const handleAgree = (): void => { + dispatch(setAnalyticsOptInSeen()) + dispatch(toggleAnalyticsOptedIn()) + } + + if (seenOptedIn && optedIn) { + if (isUnboxingFlowOngoing) { + history.push('/robot-settings/rename-robot') + } else { + history.push('/dashboard') + } + } + + return ( + <> + {isUnboxingFlowOngoing ? ( + + ) : null} + + + + {t('acknowledge_privacy_policy')} + + + + + }} + /> + + {PRIVACY_POLICY_URL} + + + + {IMG_ALT} + + + + + + + + ) +} diff --git a/app/src/pages/RobotDashboard/AnalyticsOptInModal.tsx b/app/src/pages/RobotDashboard/AnalyticsOptInModal.tsx deleted file mode 100644 index ae3d6a112f5..00000000000 --- a/app/src/pages/RobotDashboard/AnalyticsOptInModal.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import * as React from 'react' -import { useTranslation } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' - -import { - Flex, - COLORS, - DIRECTION_COLUMN, - DIRECTION_ROW, - SPACING, -} from '@opentrons/components' - -import { SmallButton } from '../../atoms/buttons' -import { StyledText } from '../../atoms/text' -import { Modal } from '../../molecules/Modal' -import { updateConfigValue } from '../../redux/config' -import { getLocalRobot } from '../../redux/discovery' -import { updateSetting } from '../../redux/robot-settings' - -import type { Dispatch } from '../../redux/types' - -export const ROBOT_ANALYTICS_SETTING_ID = 'disableLogAggregation' - -interface AnalyticsOptInModalProps { - setShowAnalyticsOptInModal: (showAnalyticsOptInModal: boolean) => void -} - -export function AnalyticsOptInModal({ - setShowAnalyticsOptInModal, -}: AnalyticsOptInModalProps): JSX.Element { - const { t } = useTranslation(['app_settings', 'shared']) - const dispatch = useDispatch() - - const localRobot = useSelector(getLocalRobot) - const robotName = localRobot?.name != null ? localRobot.name : 'no name' - - const handleCloseModal = (): void => { - dispatch( - updateConfigValue( - 'onDeviceDisplaySettings.unfinishedUnboxingFlowRoute', - null - ) - ) - setShowAnalyticsOptInModal(false) - } - - const handleOptIn = (): void => { - dispatch(updateSetting(robotName, ROBOT_ANALYTICS_SETTING_ID, false)) - dispatch(updateConfigValue('analytics.optedIn', true)) - handleCloseModal() - } - - const handleOptOut = (): void => { - dispatch(updateSetting(robotName, ROBOT_ANALYTICS_SETTING_ID, true)) - dispatch(updateConfigValue('analytics.optedIn', false)) - handleCloseModal() - } - - return ( - - - - - {t('opt_in_description')} - - - - - - - - - ) -} diff --git a/app/src/pages/RobotDashboard/WelcomeModal.tsx b/app/src/pages/RobotDashboard/WelcomeModal.tsx index 23777645a81..32ba09d7ec0 100644 --- a/app/src/pages/RobotDashboard/WelcomeModal.tsx +++ b/app/src/pages/RobotDashboard/WelcomeModal.tsx @@ -1,5 +1,7 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' +import { useDispatch } from 'react-redux' +import { updateConfigValue } from '../../redux/config' import { COLORS, @@ -18,19 +20,19 @@ import { Modal } from '../../molecules/Modal' import welcomeModalImage from '../../assets/images/on-device-display/welcome_dashboard_modal.png' import type { SetStatusBarCreateCommand } from '@opentrons/shared-data' +import type { Dispatch } from '../../redux/types' interface WelcomeModalProps { - setShowAnalyticsOptInModal: (showAnalyticsOptInModal: boolean) => void setShowWelcomeModal: (showWelcomeModal: boolean) => void } export function WelcomeModal({ - setShowAnalyticsOptInModal, setShowWelcomeModal, }: WelcomeModalProps): JSX.Element { const { t } = useTranslation(['device_details', 'shared']) const { createLiveCommand } = useCreateLiveCommandMutation() + const dispatch = useDispatch() const animationCommand: SetStatusBarCreateCommand = { commandType: 'setStatusBar', params: { animation: 'disco' }, @@ -46,8 +48,13 @@ export function WelcomeModal({ } const handleCloseModal = (): void => { + dispatch( + updateConfigValue( + 'onDeviceDisplaySettings.unfinishedUnboxingFlowRoute', + null + ) + ) setShowWelcomeModal(false) - setShowAnalyticsOptInModal(true) } React.useEffect(startDiscoAnimation, []) diff --git a/app/src/pages/RobotDashboard/__tests__/AnalyticsOptInModal.test.tsx b/app/src/pages/RobotDashboard/__tests__/AnalyticsOptInModal.test.tsx deleted file mode 100644 index 09e521b43da..00000000000 --- a/app/src/pages/RobotDashboard/__tests__/AnalyticsOptInModal.test.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import * as React from 'react' -import { fireEvent } from '@testing-library/react' - -import { renderWithProviders } from '@opentrons/components' - -import { i18n } from '../../../i18n' -import { updateConfigValue } from '../../../redux/config' -import { getLocalRobot } from '../../../redux/discovery' -import { updateSetting } from '../../../redux/robot-settings' -import { AnalyticsOptInModal } from '../AnalyticsOptInModal' - -import type { DiscoveredRobot } from '../../../redux/discovery/types' - -jest.mock('../../../redux/config') -jest.mock('../../../redux/discovery') -jest.mock('../../../redux/robot-settings') - -const mockUpdateConfigValue = updateConfigValue as jest.MockedFunction< - typeof updateConfigValue -> -const mockGetLocalRobot = getLocalRobot as jest.MockedFunction< - typeof getLocalRobot -> -const mockUpdateSetting = updateSetting as jest.MockedFunction< - typeof updateSetting -> - -const render = (props: React.ComponentProps) => { - return renderWithProviders(, { - i18nInstance: i18n, - }) -} - -describe('AnalyticsOptInModal', () => { - let props: React.ComponentProps - - beforeEach(() => { - props = { - setShowAnalyticsOptInModal: jest.fn(), - } - mockGetLocalRobot.mockReturnValue({ name: 'Otie' } as DiscoveredRobot) - }) - - it('should render text and button', () => { - const [{ getByText }] = render(props) - - getByText('Want to help out Opentrons?') - getByText( - 'Automatically send us anonymous diagnostics and usage data. We only use this information to improve our products.' - ) - getByText('Opt out') - getByText('Opt in') - }) - - it('should call a mock function when tapping opt out button', () => { - const [{ getByText }] = render(props) - fireEvent.click(getByText('Opt out')) - - expect(mockUpdateConfigValue).toHaveBeenCalledWith( - 'analytics.optedIn', - false - ) - expect(mockUpdateSetting).toHaveBeenCalledWith( - 'Otie', - 'disableLogAggregation', - true - ) - expect(props.setShowAnalyticsOptInModal).toHaveBeenCalled() - }) - - it('should call a mock function when tapping out in button', () => { - const [{ getByText }] = render(props) - fireEvent.click(getByText('Opt in')) - - expect(mockUpdateConfigValue).toHaveBeenCalledWith( - 'analytics.optedIn', - true - ) - expect(mockUpdateSetting).toHaveBeenCalledWith( - 'Otie', - 'disableLogAggregation', - true - ) - expect(props.setShowAnalyticsOptInModal).toHaveBeenCalled() - }) -}) diff --git a/app/src/pages/RobotDashboard/__tests__/WelcomeModal.test.tsx b/app/src/pages/RobotDashboard/__tests__/WelcomeModal.test.tsx index 77ca462c490..a035398aa18 100644 --- a/app/src/pages/RobotDashboard/__tests__/WelcomeModal.test.tsx +++ b/app/src/pages/RobotDashboard/__tests__/WelcomeModal.test.tsx @@ -33,7 +33,6 @@ describe('WelcomeModal', () => { mockCreateLiveCommand = jest.fn() mockCreateLiveCommand.mockResolvedValue(null) props = { - setShowAnalyticsOptInModal: jest.fn(), setShowWelcomeModal: mockFunc, } mockUseCreateLiveCommandMutation.mockReturnValue({ @@ -66,6 +65,5 @@ describe('WelcomeModal', () => { const [{ getByText }] = render(props) fireEvent.click(getByText('Next')) expect(props.setShowWelcomeModal).toHaveBeenCalled() - expect(props.setShowAnalyticsOptInModal).toHaveBeenCalled() }) }) diff --git a/app/src/pages/RobotDashboard/index.tsx b/app/src/pages/RobotDashboard/index.tsx index 3913147db07..346129757c8 100644 --- a/app/src/pages/RobotDashboard/index.tsx +++ b/app/src/pages/RobotDashboard/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' +import { useHistory } from 'react-router-dom' import { COLORS, @@ -18,7 +19,10 @@ import { RecentRunProtocolCarousel, } from '../../organisms/OnDeviceDisplay/RobotDashboard' import { getOnDeviceDisplaySettings } from '../../redux/config' -import { AnalyticsOptInModal } from './AnalyticsOptInModal' +import { + getAnalyticsOptInSeen, + getAnalyticsOptedIn, +} from '../../redux/analytics' import { WelcomeModal } from './WelcomeModal' import { RunData } from '@opentrons/api-client' import { ServerInitializing } from '../../organisms/OnDeviceDisplay/RobotDashboard/ServerInitializing' @@ -39,10 +43,14 @@ export function RobotDashboard(): JSX.Element { const [showWelcomeModal, setShowWelcomeModal] = React.useState( unfinishedUnboxingFlowRoute !== null ) - const [ - showAnalyticsOptInModal, - setShowAnalyticsOptInModal, - ] = React.useState(false) + + const seen = useSelector(getAnalyticsOptInSeen) + const hasOptedIn = useSelector(getAnalyticsOptedIn) + const history = useHistory() + + if (!seen || !hasOptedIn) { + history.push('/privacy-policy') + } const recentRunsOfUniqueProtocols = (allRunsQueryData?.data ?? []) .reverse() // newest runs first @@ -89,15 +97,7 @@ export function RobotDashboard(): JSX.Element { gridGap={SPACING.spacing16} > {showWelcomeModal ? ( - - ) : null} - {showAnalyticsOptInModal ? ( - + ) : null} {contents}