diff --git a/components/src/atoms/Toast/index.tsx b/components/src/atoms/Toast/index.tsx
index 04a202b69cf..e4a9af243ab 100644
--- a/components/src/atoms/Toast/index.tsx
+++ b/components/src/atoms/Toast/index.tsx
@@ -370,7 +370,7 @@ export function Toast(props: ToastProps): JSX.Element {
) : null}
{message}
- {linkText ? (
+ {linkText != null ? (
{
diff --git a/protocol-designer/src/ProtocolRoutes.tsx b/protocol-designer/src/ProtocolRoutes.tsx
index e2766b34c14..74f79c437b9 100644
--- a/protocol-designer/src/ProtocolRoutes.tsx
+++ b/protocol-designer/src/ProtocolRoutes.tsx
@@ -11,7 +11,6 @@ import {
Kitchen,
FileUploadMessagesModal,
LabwareUploadModal,
- AnnouncementModal,
} from './organisms'
import type { RouteProps } from './types'
@@ -63,7 +62,6 @@ export function ProtocolRoutes(): JSX.Element {
-
diff --git a/protocol-designer/src/assets/localization/en/shared.json b/protocol-designer/src/assets/localization/en/shared.json
index 737d92cc952..499bec09040 100644
--- a/protocol-designer/src/assets/localization/en/shared.json
+++ b/protocol-designer/src/assets/localization/en/shared.json
@@ -37,6 +37,7 @@
"labware_detail": "Labware detail",
"labware_name_conflict": "Duplicate labware name",
"labware": "Labware",
+ "learn_more": "Learn more about the recent changes in the {{version}} release.",
"left_right": "Left+Right",
"left": "Left",
"liquid": "Liquid",
@@ -118,6 +119,7 @@
"temperaturemoduletype": "Temperature Module",
"thermocyclermoduletype": "Thermocycler Module",
"trashBin": "Trash Bin",
+ "updated_protocol_designer": "We've updated Protocol Designer!",
"user_settings": "User settings",
"uses_standard_namespace": "Opentrons verified labware",
"version": "Version {{version}}",
diff --git a/protocol-designer/src/organisms/AnnouncementModal/announcements.tsx b/protocol-designer/src/organisms/AnnouncementModal/announcements.tsx
index 4115c55198d..38b16bf95aa 100644
--- a/protocol-designer/src/organisms/AnnouncementModal/announcements.tsx
+++ b/protocol-designer/src/organisms/AnnouncementModal/announcements.tsx
@@ -302,7 +302,7 @@ export const useAnnouncements = (): Announcement[] => {
),
},
{
- announcementKey: 'redesign9.0',
+ announcementKey: 'redesign8.2',
image: ,
heading: t('announcements.redesign.body1'),
message: (
diff --git a/protocol-designer/src/organisms/AnnouncementModal/index.tsx b/protocol-designer/src/organisms/AnnouncementModal/index.tsx
index 6eca54206ac..34a40249157 100644
--- a/protocol-designer/src/organisms/AnnouncementModal/index.tsx
+++ b/protocol-designer/src/organisms/AnnouncementModal/index.tsx
@@ -64,7 +64,7 @@ export const AnnouncementModal = (
justifyContent={JUSTIFY_CENTER}
gridGap={SPACING.spacing12}
>
- {image && image}
+ {image != null && image}
{message}
diff --git a/protocol-designer/src/pages/Landing/__tests__/Landing.test.tsx b/protocol-designer/src/pages/Landing/__tests__/Landing.test.tsx
index ad713cb02fb..315496d755e 100644
--- a/protocol-designer/src/pages/Landing/__tests__/Landing.test.tsx
+++ b/protocol-designer/src/pages/Landing/__tests__/Landing.test.tsx
@@ -6,11 +6,19 @@ import { renderWithProviders } from '../../../__testing-utils__'
import { loadProtocolFile } from '../../../load-file/actions'
import { getFileMetadata } from '../../../file-data/selectors'
import { toggleNewProtocolModal } from '../../../navigation/actions'
+import { useKitchen } from '../../../organisms/Kitchen/hooks'
+import { useAnnouncements } from '../../../organisms/AnnouncementModal/announcements'
import { Landing } from '../index'
vi.mock('../../../load-file/actions')
vi.mock('../../../file-data/selectors')
vi.mock('../../../navigation/actions')
+vi.mock('../../../organisms/AnnouncementModal/announcements')
+vi.mock('../../../organisms/Kitchen/hooks')
+
+const mockMakeSnackbar = vi.fn()
+const mockEatToast = vi.fn()
+const mockBakeToast = vi.fn()
const render = () => {
return renderWithProviders(
@@ -27,7 +35,14 @@ describe('Landing', () => {
beforeEach(() => {
vi.mocked(getFileMetadata).mockReturnValue({})
vi.mocked(loadProtocolFile).mockReturnValue(vi.fn())
+ vi.mocked(useAnnouncements).mockReturnValue({} as any)
+ vi.mocked(useKitchen).mockReturnValue({
+ makeSnackbar: mockMakeSnackbar,
+ bakeToast: mockBakeToast,
+ eatToast: mockEatToast,
+ })
})
+
it('renders the landing page image and text', () => {
render()
screen.getByLabelText('welcome image')
@@ -40,4 +55,9 @@ describe('Landing', () => {
screen.getByText('Edit existing protocol')
screen.getByRole('img', { name: 'welcome image' })
})
+
+ it('render toast when there is an announcement', () => {
+ render()
+ expect(mockBakeToast).toHaveBeenCalled()
+ })
})
diff --git a/protocol-designer/src/pages/Landing/index.tsx b/protocol-designer/src/pages/Landing/index.tsx
index 4c6206d3861..a4e0be187f5 100644
--- a/protocol-designer/src/pages/Landing/index.tsx
+++ b/protocol-designer/src/pages/Landing/index.tsx
@@ -1,4 +1,4 @@
-import * as React from 'react'
+import { useEffect, useState } from 'react'
import { NavLink, useNavigate } from 'react-router-dom'
import styled from 'styled-components'
import { useDispatch, useSelector } from 'react-redux'
@@ -9,6 +9,7 @@ import {
CURSOR_POINTER,
DIRECTION_COLUMN,
Flex,
+ INFO_TOAST,
JUSTIFY_CENTER,
LargeButton,
SPACING,
@@ -16,9 +17,13 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'
import { BUTTON_LINK_STYLE } from '../../atoms'
+import { AnnouncementModal } from '../../organisms'
import { actions as loadFileActions } from '../../load-file'
import { getFileMetadata } from '../../file-data/selectors'
import { toggleNewProtocolModal } from '../../navigation/actions'
+import { useKitchen } from '../../organisms/Kitchen/hooks'
+import { useAnnouncements } from '../../organisms/AnnouncementModal/announcements'
+import { getLocalStorageItem, localStorageAnnouncementKey } from '../../persist'
import welcomeImage from '../../assets/images/welcome_page.png'
import type { ThunkDispatch } from '../../types'
@@ -28,8 +33,42 @@ export function Landing(): JSX.Element {
const dispatch: ThunkDispatch = useDispatch()
const metadata = useSelector(getFileMetadata)
const navigate = useNavigate()
+ const [showAnnouncementModal, setShowAnnouncementModal] = useState(
+ false
+ )
+ const { bakeToast, eatToast } = useKitchen()
+ const announcements = useAnnouncements()
+ const lastAnnouncement = announcements[announcements.length - 1]
+ const announcementKey = lastAnnouncement
+ ? lastAnnouncement.announcementKey
+ : null
+ const showGateModal =
+ process.env.NODE_ENV === 'production' || process.env.OT_PD_SHOW_GATE
+ const userHasNotSeenAnnouncement =
+ getLocalStorageItem(localStorageAnnouncementKey) !== announcementKey &&
+ !showGateModal
+
+ useEffect(() => {
+ if (userHasNotSeenAnnouncement) {
+ const toastId = bakeToast(
+ t('learn_more', { version: process.env.OT_PD_VERSION }) as string,
+ INFO_TOAST,
+ {
+ heading: t('updated_protocol_designer'),
+ closeButton: true,
+ linkText: t('view_release_notes'),
+ onLinkClick: () => {
+ eatToast(toastId)
+ setShowAnnouncementModal(true)
+ },
+ disableTimeout: true,
+ justifyContent: JUSTIFY_CENTER,
+ }
+ )
+ }
+ }, [userHasNotSeenAnnouncement])
- React.useEffect(() => {
+ useEffect(() => {
if (metadata?.created != null) {
console.warn('protocol already exists, navigating to overview')
navigate('/overview')
@@ -43,57 +82,67 @@ export function Landing(): JSX.Element {
}
return (
-
-
-
+ {showAnnouncementModal ? (
+ {
+ setShowAnnouncementModal(false)
+ }}
/>
-
-
- {t('welcome')}
-
-
+
+
+
- {t('no-code-required')}
-
+
+ {t('welcome')}
+
+
+ {t('no-code-required')}
+
+
+
+ {
+ dispatch(toggleNewProtocolModal(true))
+ }}
+ buttonText={{t('create_a_protocol')}}
+ />
+
+
+
+
+ {t('edit_existing')}
+
+
+
+
-
- {
- dispatch(toggleNewProtocolModal(true))
- }}
- buttonText={{t('create_a_protocol')}}
- />
-
-
-
-
- {t('edit_existing')}
-
-
-
-
-
+ >
)
}