From 2f052bf3a327501e7fef1e2c884e5d4b41d4eaf0 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 21:26:52 +0900 Subject: [PATCH 01/11] Add navigation --- .../AccessCodeDetails/AccessCodeDetails.tsx | 26 +++++- .../ui/AccessCodeDetails/AccessCodeDevice.tsx | 18 +++- .../ui/AccessCodeTable/AccessCodeTable.tsx | 26 +++++- src/lib/ui/DeviceDetails/DeviceDetails.tsx | 30 ++++--- .../ui/DeviceTable/DeviceTable.stories.tsx | 8 +- src/lib/ui/DeviceTable/DeviceTable.tsx | 36 +++++++- src/lib/ui/Table/TableRow.tsx | 10 ++- src/lib/ui/layout/ContentHeader.tsx | 11 ++- src/styles/_access-code-details.scss | 3 +- src/styles/_access-code-table.scss | 6 ++ src/styles/_device-details.scss | 2 +- src/styles/_device-table.scss | 83 ++++++++++--------- src/styles/_layout.scss | 2 +- src/styles/_tables.scss | 1 - 14 files changed, 189 insertions(+), 73 deletions(-) diff --git a/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx b/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx index 0a06fe2ea..595fc3a09 100644 --- a/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx +++ b/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx @@ -1,21 +1,38 @@ import { DateTime } from 'luxon' -import type { AccessCode } from 'seamapi' +import { useState } from 'react' +import type { AccessCode, CommonDeviceProperties, Device } from 'seamapi' import AccessCodeDevice from 'lib/ui/AccessCodeDetails/AccessCodeDevice.js' +import { DeviceDetails } from 'lib/ui/DeviceDetails/DeviceDetails.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' export interface AccessCodeDetailsProps { accessCode: AccessCode + onBack?: () => void } export default function AccessCodeDetails({ accessCode, + onBack, }: AccessCodeDetailsProps) { const name = accessCode.name ?? t.fallbackName + const [selectedDevice, selectDevice] = + useState | null>(null) + + if (selectedDevice) { + return ( + { + selectDevice(null) + }} + /> + ) + } return (
- +
{t.accessCode} @@ -25,7 +42,10 @@ export default function AccessCodeDetails({
- +
diff --git a/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx b/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx index 59ca15ec5..a3c682c43 100644 --- a/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx +++ b/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx @@ -1,3 +1,5 @@ +import type { CommonDeviceProperties, Device } from 'seamapi' + import { isLockDevice } from 'lib/seam/devices/types.js' import { useFakeDevice } from 'lib/seam/devices/use-device.js' import { Button } from 'lib/ui/Button.js' @@ -5,7 +7,13 @@ import { DeviceImage } from 'lib/ui/device/DeviceImage.js' import { getDeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js' import { TextButton } from 'lib/ui/TextButton.js' -export default function AccessCodeDevice({ deviceId }: { deviceId: string }) { +export default function AccessCodeDevice({ + deviceId, + onSelectDevice, +}: { + deviceId: string + onSelectDevice: (device: Device) => void +}) { // TODO: Replace with `useDevice()` once ready const { isLoading, device } = useFakeDevice({ device_id: deviceId }) @@ -23,7 +31,13 @@ export default function AccessCodeDevice({ deviceId }: { deviceId: string }) {
{model}
- {t.deviceDetails} + { + onSelectDevice(device) + }} + > + {t.deviceDetails} +
diff --git a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx index f427b86c2..1f92f9b36 100644 --- a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +++ b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx @@ -1,5 +1,9 @@ +import { useState } from 'react' +import type { AccessCode } from 'seamapi' + import { AccessCodeKeyIcon } from 'lib/icons/AccessCodeKey.js' import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js' +import AccessCodeDetails from 'lib/ui/AccessCodeDetails/AccessCodeDetails.js' import { CodeDetails } from 'lib/ui/AccessCodeTable/CodeDetails.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' import { TableBody } from 'lib/ui/Table/TableBody.js' @@ -19,6 +23,21 @@ export function AccessCodeTable(props: { }) const { onBack } = props + const [selectedAccessCode, selectAccessCode] = useState( + null + ) + + if (selectedAccessCode) { + return ( + { + selectAccessCode(null) + }} + /> + ) + } + if (!accessCodes) return <>{null} return ( @@ -31,7 +50,12 @@ export function AccessCodeTable(props: { {accessCodes.map((code) => ( - + { + selectAccessCode(code) + }} + >
diff --git a/src/lib/ui/DeviceDetails/DeviceDetails.tsx b/src/lib/ui/DeviceDetails/DeviceDetails.tsx index bf464d3f9..dbdf7f6d6 100644 --- a/src/lib/ui/DeviceDetails/DeviceDetails.tsx +++ b/src/lib/ui/DeviceDetails/DeviceDetails.tsx @@ -1,6 +1,7 @@ -import { type LockDevice } from 'seamapi' +import { type CommonDeviceProperties, type Device } from 'seamapi' import { ChevronRightIcon } from 'lib/icons/ChevronRight.js' +import { isLockDevice } from 'lib/seam/devices/types.js' import { AccessCodeTable } from 'lib/ui/AccessCodeTable/AccessCodeTable.js' import { Button } from 'lib/ui/Button.js' import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js' @@ -11,16 +12,12 @@ import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' import useToggle from 'lib/use-toggle.js' export interface DeviceDetailsProps { - device: LockDevice + device: Device + onBack?: () => void } -export function DeviceDetails(props: DeviceDetailsProps): JSX.Element { - const { device } = props - - const lockStatus = device.properties.locked ? t.locked : t.unlocked - - const accessCodeLength = - device.properties?.schlage_metadata?.access_code_length +export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null { + const { device, onBack } = props const [showingAccessCodes, toggleAccessCodes] = useToggle() @@ -30,9 +27,18 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element { ) } + if (!isLockDevice(device)) { + return null + } + + const lockStatus = device.properties.locked ? t.locked : t.unlocked + + const accessCodeLength = + device.properties?.schlage_metadata?.access_code_length + return (
- +
@@ -57,7 +63,9 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element { className='seam-content seam-access-codes' onClick={toggleAccessCodes} > - 49 {t.accessCodes} + + {accessCodeLength} {t.accessCodes} +
diff --git a/src/lib/ui/DeviceTable/DeviceTable.stories.tsx b/src/lib/ui/DeviceTable/DeviceTable.stories.tsx index 33ea0c9fe..783563e9a 100644 --- a/src/lib/ui/DeviceTable/DeviceTable.stories.tsx +++ b/src/lib/ui/DeviceTable/DeviceTable.stories.tsx @@ -17,13 +17,15 @@ export default meta type Story = StoryObj -export const Content: Story = {} +export const Content: Story = { + render: ({ onBack, ...otherProps }) => , +} export const InsideModal: Story = { render: InsideModalComponent, } -function InsideModalComponent(props: DeviceTableProps) { +function InsideModalComponent({ onBack, ...otherProps }: DeviceTableProps) { const [open, toggleOpen] = useToggle() // Wrap modal/dialog contents in `seam-components` class // to apply styles when rendered in a portal, @@ -33,7 +35,7 @@ function InsideModalComponent(props: DeviceTableProps) {
- +
| null>(null) + + if (selectedDevice) { + return ( + { + selectDevice(null) + }} + /> + ) + } + if (isLoading) return

...

if (isError) return

{error?.message}

if (devices == null) return null @@ -43,22 +62,31 @@ export function DeviceTable({ onBack, ...props }: DeviceTableProps) { {devices.map((device) => ( - + { + selectDevice(device) + }} + /> ))}
) } -function DeviceRow(props: { device: UseDevicesData[number] }) { - const { device } = props +function DeviceRow(props: { + device: UseDevicesData[number] + onClick: () => void +}) { + const { device, onClick } = props if (!isLockDevice(device)) return null const deviceModel = getDeviceModel(device) ?? t.unknownLock return ( - + diff --git a/src/lib/ui/Table/TableRow.tsx b/src/lib/ui/Table/TableRow.tsx index b1aa6e64a..125f80376 100644 --- a/src/lib/ui/Table/TableRow.tsx +++ b/src/lib/ui/Table/TableRow.tsx @@ -1,5 +1,11 @@ import type { DivProps } from 'lib/ui/types.js' -export function TableRow(props: DivProps): JSX.Element { - return
{props.children}
+export function TableRow( + props: DivProps & { onClick?: () => void } +): JSX.Element { + return ( +
+ {props.children} +
+ ) } diff --git a/src/lib/ui/layout/ContentHeader.tsx b/src/lib/ui/layout/ContentHeader.tsx index db94e5cc9..e4d657de4 100644 --- a/src/lib/ui/layout/ContentHeader.tsx +++ b/src/lib/ui/layout/ContentHeader.tsx @@ -3,11 +3,16 @@ import { ArrowBackIcon } from 'lib/icons/ArrowBack.js' export function ContentHeader(props: { title?: string onBack?: () => void -}): JSX.Element { +}): JSX.Element | null { + const { title, onBack } = props + if (!title && !onBack) { + return null + } + return (
- - {props.title} + + {title}
) } diff --git a/src/styles/_access-code-details.scss b/src/styles/_access-code-details.scss index a5094ae0e..058663577 100644 --- a/src/styles/_access-code-details.scss +++ b/src/styles/_access-code-details.scss @@ -2,12 +2,12 @@ @mixin all { .seam-access-code-details { - padding: 0 24px 24px; > .seam-summary { background: colors.$bg-a; border-radius: 16px; margin-bottom: 16px; + margin: 0 24px 16px; > .seam-top { padding: 24px 16px; @@ -81,6 +81,7 @@ font-size: 16px; line-height: 134%; border-bottom: 1px solid rgb(0 0 0 / 12%); + padding: 0 24px; .seam-row { padding: 16px; diff --git a/src/styles/_access-code-table.scss b/src/styles/_access-code-table.scss index 78ad9af53..75ac640d8 100644 --- a/src/styles/_access-code-table.scss +++ b/src/styles/_access-code-table.scss @@ -2,6 +2,11 @@ @mixin all { .seam-access-code-table { + + .seam-table-row { + + cursor: pointer; + .seam-icon-cell { flex: 0; display: flex; @@ -43,4 +48,5 @@ justify-content: center; } } + } } diff --git a/src/styles/_device-details.scss b/src/styles/_device-details.scss index 06dec397b..a0a6bdc5a 100644 --- a/src/styles/_device-details.scss +++ b/src/styles/_device-details.scss @@ -2,12 +2,12 @@ @mixin all { .seam-device-details { - padding: 0 24px 24px; .seam-body { display: flex; flex-direction: column; gap: 16px; + margin: 0 24px 24px; .seam-box { border: 1px solid colors.$text-gray-3; diff --git a/src/styles/_device-table.scss b/src/styles/_device-table.scss index c235cc0e2..c54220a96 100644 --- a/src/styles/_device-table.scss +++ b/src/styles/_device-table.scss @@ -2,54 +2,57 @@ @mixin all { .seam-device-table { - .seam-image-cell { - width: 64px; - height: 64px; - padding: 4px; - - img { - width: 100%; + .seam-table-row { + cursor: pointer; + .seam-image-cell { + width: 64px; + height: 64px; + padding: 4px; + + img { + width: 100%; + } } - } - .seam-body-cell { - flex: 1; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 2px; - - .seam-bottom { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - - .seam-device-model { - font-weight: 400; - font-size: 14px; - line-height: 134%; - color: colors.$text-gray-1; - flex: 0 1 190px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } + .seam-body-cell { + flex: 1; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 2px; - .seam-device-statuses { + .seam-bottom { display: flex; + justify-content: space-between; align-items: center; - color: colors.$text-gray-1; - font-size: 14px; - line-height: 134%; + width: 100%; + + .seam-device-model { + font-weight: 400; + font-size: 14px; + line-height: 134%; + color: colors.$text-gray-1; + flex: 0 1 190px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } - > div { + .seam-device-statuses { display: flex; align-items: center; - margin-right: 16px; - - svg { - scale: 0.75; + color: colors.$text-gray-1; + font-size: 14px; + line-height: 134%; + + > div { + display: flex; + align-items: center; + margin-right: 16px; + + svg { + scale: 0.75; + } } } } diff --git a/src/styles/_layout.scss b/src/styles/_layout.scss index f06b1bd4d..d7fa5c1c2 100644 --- a/src/styles/_layout.scss +++ b/src/styles/_layout.scss @@ -12,7 +12,7 @@ .seam-back-icon { position: absolute; top: 50%; - transform: translate(0, -25%); + transform: translate(4px, -25%); left: 0; cursor: pointer; } diff --git a/src/styles/_tables.scss b/src/styles/_tables.scss index 154cd2a60..546f91d48 100644 --- a/src/styles/_tables.scss +++ b/src/styles/_tables.scss @@ -5,7 +5,6 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 16px; height: 56px; padding: 0 16px; } From 959ad843f3759568e82d0eed55bbbe198e1d45e7 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 21:34:46 +0900 Subject: [PATCH 02/11] Fix access code length --- src/lib/ui/AccessCodeTable/AccessCodeTable.tsx | 2 +- src/lib/ui/DeviceDetails/DeviceDetails.tsx | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx index 1f92f9b36..ab1d7eaf8 100644 --- a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +++ b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx @@ -30,7 +30,7 @@ export function AccessCodeTable(props: { if (selectedAccessCode) { return ( { selectAccessCode(null) }} diff --git a/src/lib/ui/DeviceDetails/DeviceDetails.tsx b/src/lib/ui/DeviceDetails/DeviceDetails.tsx index dbdf7f6d6..9441e7abe 100644 --- a/src/lib/ui/DeviceDetails/DeviceDetails.tsx +++ b/src/lib/ui/DeviceDetails/DeviceDetails.tsx @@ -1,5 +1,7 @@ import { type CommonDeviceProperties, type Device } from 'seamapi' +import { useAccessCodes } from 'lib/index.js' + import { ChevronRightIcon } from 'lib/icons/ChevronRight.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { AccessCodeTable } from 'lib/ui/AccessCodeTable/AccessCodeTable.js' @@ -21,6 +23,14 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null { const [showingAccessCodes, toggleAccessCodes] = useToggle() + const { isLoading, accessCodes } = useAccessCodes({ + device_id: device.device_id, + }) + + if (isLoading) { + return null + } + if (showingAccessCodes) { return ( @@ -33,6 +43,8 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null { const lockStatus = device.properties.locked ? t.locked : t.unlocked + const accessCodeCount = accessCodes?.length + const accessCodeLength = device.properties?.schlage_metadata?.access_code_length @@ -64,7 +76,7 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null { onClick={toggleAccessCodes} > - {accessCodeLength} {t.accessCodes} + {accessCodeCount} {t.accessCodes}
From 8138b4566c6b0424e269862cf2a034a70f1937bc Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 21:43:54 +0900 Subject: [PATCH 03/11] Run format --- src/styles/_access-code-details.scss | 2 - src/styles/_access-code-table.scss | 66 ++++++++++++++-------------- src/styles/_device-details.scss | 1 - src/styles/_device-table.scss | 1 + 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/styles/_access-code-details.scss b/src/styles/_access-code-details.scss index 058663577..c9cdda80d 100644 --- a/src/styles/_access-code-details.scss +++ b/src/styles/_access-code-details.scss @@ -2,11 +2,9 @@ @mixin all { .seam-access-code-details { - > .seam-summary { background: colors.$bg-a; border-radius: 16px; - margin-bottom: 16px; margin: 0 24px 16px; > .seam-top { diff --git a/src/styles/_access-code-table.scss b/src/styles/_access-code-table.scss index 75ac640d8..54323afed 100644 --- a/src/styles/_access-code-table.scss +++ b/src/styles/_access-code-table.scss @@ -2,51 +2,49 @@ @mixin all { .seam-access-code-table { - .seam-table-row { - cursor: pointer; - .seam-icon-cell { - flex: 0; - display: flex; - align-items: center; - justify-content: center; - - > div { - width: 40px; - height: 40px; + .seam-icon-cell { + flex: 0; display: flex; align-items: center; justify-content: center; - } - } - - .seam-name-cell { - flex: 1; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 2px; - .seam-code-details { - font-size: 14px; - line-height: 134%; - color: colors.$text-gray-1; + > div { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + } + } - .seam-dot-divider { - margin: 0 4px; - color: colors.$text-gray-3; + .seam-name-cell { + flex: 1; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 2px; + + .seam-code-details { + font-size: 14px; + line-height: 134%; + color: colors.$text-gray-1; + + .seam-dot-divider { + margin: 0 4px; + color: colors.$text-gray-3; + } } } - } - .seam-action-cell { - margin-right: 12px; - display: flex; - align-items: center; - justify-content: center; + .seam-action-cell { + margin-right: 12px; + display: flex; + align-items: center; + justify-content: center; + } } } - } } diff --git a/src/styles/_device-details.scss b/src/styles/_device-details.scss index a0a6bdc5a..a113a51d4 100644 --- a/src/styles/_device-details.scss +++ b/src/styles/_device-details.scss @@ -2,7 +2,6 @@ @mixin all { .seam-device-details { - .seam-body { display: flex; flex-direction: column; diff --git a/src/styles/_device-table.scss b/src/styles/_device-table.scss index c54220a96..4a34b36a9 100644 --- a/src/styles/_device-table.scss +++ b/src/styles/_device-table.scss @@ -4,6 +4,7 @@ .seam-device-table { .seam-table-row { cursor: pointer; + .seam-image-cell { width: 64px; height: 64px; From 1c39118fb2ee539b037e98fe4988b62b2e6a42c2 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 22:20:31 +0900 Subject: [PATCH 04/11] use --- src/lib/NavigationProvider.tsx | 115 ++++++++++++++++++ src/lib/seam/access-codes/use-access-code.ts | 18 +++ .../AccessCodeDetails.stories.tsx | 16 +-- .../AccessCodeDetails/AccessCodeDetails.tsx | 38 +++--- .../ui/AccessCodeDetails/AccessCodeDevice.tsx | 14 +-- .../ui/AccessCodeTable/AccessCodeTable.tsx | 33 ++--- .../AccessCodeTableContent.tsx | 5 +- .../DeviceDetails/DeviceDetails.stories.tsx | 2 +- src/lib/ui/DeviceDetails/DeviceDetails.tsx | 38 +++--- .../ui/DeviceTable/DeviceTable.stories.tsx | 31 +++++ src/lib/ui/DeviceTable/DeviceTable.tsx | 25 +--- src/lib/ui/layout/ContentHeader.tsx | 14 +-- 12 files changed, 223 insertions(+), 126 deletions(-) create mode 100644 src/lib/NavigationProvider.tsx diff --git a/src/lib/NavigationProvider.tsx b/src/lib/NavigationProvider.tsx new file mode 100644 index 000000000..d74b12c93 --- /dev/null +++ b/src/lib/NavigationProvider.tsx @@ -0,0 +1,115 @@ +import React, { useState } from 'react' + +import { AccessCodeTable, DeviceDetails, DeviceTable } from 'lib/index.js' + +import AccessCodeDetails from 'lib/ui/AccessCodeDetails/AccessCodeDetails.js' + +interface DeviceTableView { + name: 'device_table' +} + +interface DeviceDetailView { + name: 'device_detail' + deviceId: string +} + +interface AccessCodeTableView { + name: 'access_code_table' + deviceId: string +} + +interface AccessCodeDetailView { + name: 'access_code_detail' + accessCodeId: string +} + +type View = + | DeviceTableView + | DeviceDetailView + | AccessCodeTableView + | AccessCodeDetailView + +export interface NavigationStackProps { + children: JSX.Element +} + +interface NavigationContextProps { + show: (view: View) => void + goBack?: () => void +} + +const NavigationContext = React.createContext< + NavigationContextProps | undefined +>(undefined) + +export default function NavigationProvider({ children }: NavigationStackProps) { + const [views, setViews] = useState([]) + + const pushView = (view: View) => { + setViews((current) => [view, ...current]) + } + + const popView = () => { + setViews((current) => { + if (current.length === 0) { + return current + } + + const [_showing, ...others] = current + return others + }) + } + + const hasViews = views.length > 0 + + return ( + + + + ) +} + +function Content({ + views, + rootView, +}: { + views: View[] + rootView: JSX.Element +}) { + const firstView = views[0] + if (firstView != null) { + return + } + + return rootView +} + +export function useNavigation(): NavigationContextProps { + const context = React.useContext(NavigationContext) + if (context === undefined) { + return { + show: () => {}, + goBack: undefined, + } + } + + return context +} + +function Subview({ view }: { view: View }) { + switch (view.name) { + case 'device_detail': + return + case 'access_code_detail': + return + case 'device_table': + return + case 'access_code_table': + return + } +} diff --git a/src/lib/seam/access-codes/use-access-code.ts b/src/lib/seam/access-codes/use-access-code.ts index 1112473ac..bbce6d538 100644 --- a/src/lib/seam/access-codes/use-access-code.ts +++ b/src/lib/seam/access-codes/use-access-code.ts @@ -1,5 +1,7 @@ import { useQuery } from '@tanstack/react-query' +import { DateTime } from 'luxon' import type { AccessCode, AccessCodeGetRequest } from 'seamapi' +import { v4 as uuid } from 'uuid' import { useSeamClient } from 'lib/seam/use-seam-client.js' @@ -22,3 +24,19 @@ export function useAccessCode(params: UseAccessCodeParams) { }, }) } + +export function useFakeAccessCode(_params: UseAccessCodeParams) { + const accessCode: AccessCode = { + access_code_id: uuid(), + type: 'time_bound', + starts_at: DateTime.now().minus({ days: 2 }).toISO() ?? '', + ends_at: DateTime.now().plus({ days: 1 }).toISO() ?? '', + code: '1234', + created_at: DateTime.now().toISO() ?? '', + device_id: 'some_device_id', + status: 'set', + name: 'Guest - Kranz', + } + + return { isLoading: false, data: accessCode } +} diff --git a/src/lib/ui/AccessCodeDetails/AccessCodeDetails.stories.tsx b/src/lib/ui/AccessCodeDetails/AccessCodeDetails.stories.tsx index 94fb4c6f7..5ee3a759f 100644 --- a/src/lib/ui/AccessCodeDetails/AccessCodeDetails.stories.tsx +++ b/src/lib/ui/AccessCodeDetails/AccessCodeDetails.stories.tsx @@ -1,6 +1,5 @@ import { Button, Dialog } from '@mui/material' import type { Meta, StoryObj } from '@storybook/react' -import { DateTime } from 'luxon' import { v4 as uuid } from 'uuid' import AccessCodeDetails, { @@ -8,25 +7,12 @@ import AccessCodeDetails, { } from 'lib/ui/AccessCodeDetails/AccessCodeDetails.js' import useToggle from 'lib/use-toggle.js' -/** - * These stories showcase the device manager. - */ const meta: Meta = { title: 'Example/AccessCodeDetails', component: AccessCodeDetails, tags: ['autodocs'], args: { - accessCode: { - access_code_id: uuid(), - type: 'time_bound', - starts_at: DateTime.now().minus({ days: 2 }).toISO() ?? '', - ends_at: DateTime.now().plus({ days: 1 }).toISO() ?? '', - code: '1234', - created_at: DateTime.now().toISO() ?? '', - device_id: 'some_device_id', - status: 'set', - name: 'Guest - Kranz', - }, + accessCodeId: uuid(), }, } diff --git a/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx b/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx index 595fc3a09..aa7449e47 100644 --- a/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx +++ b/src/lib/ui/AccessCodeDetails/AccessCodeDetails.tsx @@ -1,38 +1,31 @@ import { DateTime } from 'luxon' -import { useState } from 'react' -import type { AccessCode, CommonDeviceProperties, Device } from 'seamapi' +import type { AccessCode } from 'seamapi' + +import { useFakeAccessCode } from 'lib/index.js' import AccessCodeDevice from 'lib/ui/AccessCodeDetails/AccessCodeDevice.js' -import { DeviceDetails } from 'lib/ui/DeviceDetails/DeviceDetails.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' export interface AccessCodeDetailsProps { - accessCode: AccessCode - onBack?: () => void + accessCodeId: string } export default function AccessCodeDetails({ - accessCode, - onBack, + accessCodeId, }: AccessCodeDetailsProps) { - const name = accessCode.name ?? t.fallbackName - const [selectedDevice, selectDevice] = - useState | null>(null) + const { isLoading, data: accessCode } = useFakeAccessCode({ + access_code_id: accessCodeId, + }) - if (selectedDevice) { - return ( - { - selectDevice(null) - }} - /> - ) + if (isLoading || !accessCode) { + return null } + const name = accessCode.name ?? t.fallbackName + return (
- +
{t.accessCode} @@ -42,10 +35,7 @@ export default function AccessCodeDetails({
- +
diff --git a/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx b/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx index a3c682c43..4716ad72a 100644 --- a/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx +++ b/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx @@ -1,5 +1,4 @@ -import type { CommonDeviceProperties, Device } from 'seamapi' - +import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { useFakeDevice } from 'lib/seam/devices/use-device.js' import { Button } from 'lib/ui/Button.js' @@ -7,15 +6,10 @@ import { DeviceImage } from 'lib/ui/device/DeviceImage.js' import { getDeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js' import { TextButton } from 'lib/ui/TextButton.js' -export default function AccessCodeDevice({ - deviceId, - onSelectDevice, -}: { - deviceId: string - onSelectDevice: (device: Device) => void -}) { +export default function AccessCodeDevice({ deviceId }: { deviceId: string }) { // TODO: Replace with `useDevice()` once ready const { isLoading, device } = useFakeDevice({ device_id: deviceId }) + const { show } = useNavigation() // TODO: Do we want to return a skeleton loader here instead? if (isLoading || !device || !isLockDevice(device)) { @@ -33,7 +27,7 @@ export default function AccessCodeDevice({
{model}
{ - onSelectDevice(device) + show({ name: 'device_detail', deviceId: device.device_id }) }} > {t.deviceDetails} diff --git a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx index ab1d7eaf8..c140469f1 100644 --- a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +++ b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx @@ -1,9 +1,6 @@ -import { useState } from 'react' -import type { AccessCode } from 'seamapi' - import { AccessCodeKeyIcon } from 'lib/icons/AccessCodeKey.js' +import { useNavigation } from 'lib/NavigationProvider.js' import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js' -import AccessCodeDetails from 'lib/ui/AccessCodeDetails/AccessCodeDetails.js' import { CodeDetails } from 'lib/ui/AccessCodeTable/CodeDetails.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' import { TableBody } from 'lib/ui/Table/TableBody.js' @@ -14,35 +11,18 @@ import { TableTitle } from 'lib/ui/Table/TableTitle.js' import { Caption } from 'lib/ui/typography/Caption.js' import { Title } from 'lib/ui/typography/Title.js' -export function AccessCodeTable(props: { - deviceId: string - onBack?: () => void -}): JSX.Element { +export function AccessCodeTable(props: { deviceId: string }): JSX.Element { const { accessCodes } = useAccessCodes({ device_id: props.deviceId, }) - const { onBack } = props - - const [selectedAccessCode, selectAccessCode] = useState( - null - ) - if (selectedAccessCode) { - return ( - { - selectAccessCode(null) - }} - /> - ) - } + const { show } = useNavigation() if (!accessCodes) return <>{null} return (
- + {t.accessCodes} ({accessCodes.length}) @@ -53,7 +33,10 @@ export function AccessCodeTable(props: { { - selectAccessCode(code) + show({ + name: 'access_code_detail', + accessCodeId: code.access_code_id, + }) }} > diff --git a/src/lib/ui/AccessCodeTable/AccessCodeTableContent.tsx b/src/lib/ui/AccessCodeTable/AccessCodeTableContent.tsx index 124501f6b..4cd58805f 100644 --- a/src/lib/ui/AccessCodeTable/AccessCodeTableContent.tsx +++ b/src/lib/ui/AccessCodeTable/AccessCodeTableContent.tsx @@ -13,17 +13,16 @@ import { Title } from 'lib/ui/typography/Title.js' export interface AccessCodeTableContentProps { accessCodes: AccessCode[] - onBack?: () => void } export function AccessCodeTableContent( props: AccessCodeTableContentProps ): JSX.Element { - const { accessCodes, onBack } = props + const { accessCodes } = props return (
- + {t.accessCodes} ({accessCodes.length}) diff --git a/src/lib/ui/DeviceDetails/DeviceDetails.stories.tsx b/src/lib/ui/DeviceDetails/DeviceDetails.stories.tsx index c65577744..8926e721a 100644 --- a/src/lib/ui/DeviceDetails/DeviceDetails.stories.tsx +++ b/src/lib/ui/DeviceDetails/DeviceDetails.stories.tsx @@ -40,7 +40,7 @@ const meta: Meta = { title: 'Example/DeviceDetails', component: DeviceDetails, args: { - device: fakeDevice, + deviceId: fakeDevice.device_id, }, tags: ['autodocs'], } diff --git a/src/lib/ui/DeviceDetails/DeviceDetails.tsx b/src/lib/ui/DeviceDetails/DeviceDetails.tsx index 9441e7abe..c3bebd7d7 100644 --- a/src/lib/ui/DeviceDetails/DeviceDetails.tsx +++ b/src/lib/ui/DeviceDetails/DeviceDetails.tsx @@ -1,40 +1,36 @@ -import { type CommonDeviceProperties, type Device } from 'seamapi' - -import { useAccessCodes } from 'lib/index.js' +import { useAccessCodes, useFakeDevice } from 'lib/index.js' import { ChevronRightIcon } from 'lib/icons/ChevronRight.js' +import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' -import { AccessCodeTable } from 'lib/ui/AccessCodeTable/AccessCodeTable.js' import { Button } from 'lib/ui/Button.js' import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js' import { DeviceImage } from 'lib/ui/device/DeviceImage.js' import { OnlineStatus } from 'lib/ui/device/OnlineStatus.js' import { DeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' -import useToggle from 'lib/use-toggle.js' export interface DeviceDetailsProps { - device: Device - onBack?: () => void + deviceId: string } export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null { - const { device, onBack } = props + const { deviceId } = props - const [showingAccessCodes, toggleAccessCodes] = useToggle() + const { isLoading: isLoadingDevice, device } = useFakeDevice({ + device_id: deviceId, + }) - const { isLoading, accessCodes } = useAccessCodes({ - device_id: device.device_id, + const { isLoading: isLoadingAccessCodes, accessCodes } = useAccessCodes({ + device_id: deviceId, }) - if (isLoading) { - return null - } + const isLoading = isLoadingDevice || isLoadingAccessCodes + + const { show } = useNavigation() - if (showingAccessCodes) { - return ( - - ) + if (isLoading || !device) { + return null } if (!isLockDevice(device)) { @@ -50,7 +46,7 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null { return (
- +
@@ -73,7 +69,9 @@ export function DeviceDetails(props: DeviceDetailsProps): JSX.Element | null {
{ + show({ name: 'access_code_table', deviceId: device.device_id }) + }} > {accessCodeCount} {t.accessCodes} diff --git a/src/lib/ui/DeviceTable/DeviceTable.stories.tsx b/src/lib/ui/DeviceTable/DeviceTable.stories.tsx index 783563e9a..eab2ff6ea 100644 --- a/src/lib/ui/DeviceTable/DeviceTable.stories.tsx +++ b/src/lib/ui/DeviceTable/DeviceTable.stories.tsx @@ -1,6 +1,7 @@ import { Button, Dialog, DialogActions } from '@mui/material' import type { Meta, StoryObj } from '@storybook/react' +import NavigationProvider from 'lib/NavigationProvider.js' import { DeviceTable, type DeviceTableProps, @@ -48,3 +49,33 @@ function InsideModalComponent({ onBack, ...otherProps }: DeviceTableProps) { ) } + +export const WithNavigation: Story = { + render: WithNavigationComponent, +} + +function WithNavigationComponent(props: DeviceTableProps) { + const [open, toggleOpen] = useToggle() + // Wrap modal/dialog contents in `seam-components` class + // to apply styles when rendered in a portal, + // which is the default MUI behavior. + return ( + <> + + +
+ + + +
+ + + +
+ + ) +} diff --git a/src/lib/ui/DeviceTable/DeviceTable.tsx b/src/lib/ui/DeviceTable/DeviceTable.tsx index 0e3ef140e..6375cfe96 100644 --- a/src/lib/ui/DeviceTable/DeviceTable.tsx +++ b/src/lib/ui/DeviceTable/DeviceTable.tsx @@ -1,8 +1,4 @@ -import { useState } from 'react' -import type { CommonDeviceProperties, Device } from 'seamapi' - -import { DeviceDetails } from 'lib/index.js' - +import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { useDevices, @@ -31,20 +27,7 @@ interface Props { export function DeviceTable({ onBack, ...props }: DeviceTableProps) { const { devices, isLoading, isError, error } = useDevices(props) - - const [selectedDevice, selectDevice] = - useState | null>(null) - - if (selectedDevice) { - return ( - { - selectDevice(null) - }} - /> - ) - } + const { show } = useNavigation() if (isLoading) return

...

if (isError) return

{error?.message}

@@ -54,7 +37,7 @@ export function DeviceTable({ onBack, ...props }: DeviceTableProps) { return (
- + {t.devices} ({deviceCount}) @@ -66,7 +49,7 @@ export function DeviceTable({ onBack, ...props }: DeviceTableProps) { device={device} key={device.device_id} onClick={() => { - selectDevice(device) + show({ name: 'device_detail', deviceId: device.device_id }) }} /> ))} diff --git a/src/lib/ui/layout/ContentHeader.tsx b/src/lib/ui/layout/ContentHeader.tsx index e4d657de4..0693848d8 100644 --- a/src/lib/ui/layout/ContentHeader.tsx +++ b/src/lib/ui/layout/ContentHeader.tsx @@ -1,17 +1,17 @@ import { ArrowBackIcon } from 'lib/icons/ArrowBack.js' +import { useNavigation } from 'lib/NavigationProvider.js' -export function ContentHeader(props: { - title?: string - onBack?: () => void -}): JSX.Element | null { - const { title, onBack } = props - if (!title && !onBack) { +export function ContentHeader(props: { title?: string }): JSX.Element | null { + const { title } = props + + const { goBack } = useNavigation() + if (!title && !goBack) { return null } return (
- + {goBack && } {title}
) From ff1dd7a67e2cc0a5145ca9503abe847ce9f561d9 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 22:27:07 +0900 Subject: [PATCH 05/11] Separate out use-navigation --- src/lib/NavigationProvider.tsx | 16 ++-------------- src/lib/use-navigation.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 src/lib/use-navigation.ts diff --git a/src/lib/NavigationProvider.tsx b/src/lib/NavigationProvider.tsx index d74b12c93..7473e14fe 100644 --- a/src/lib/NavigationProvider.tsx +++ b/src/lib/NavigationProvider.tsx @@ -33,12 +33,12 @@ export interface NavigationStackProps { children: JSX.Element } -interface NavigationContextProps { +export interface NavigationContextProps { show: (view: View) => void goBack?: () => void } -const NavigationContext = React.createContext< +export const NavigationContext = React.createContext< NavigationContextProps | undefined >(undefined) @@ -89,18 +89,6 @@ function Content({ return rootView } -export function useNavigation(): NavigationContextProps { - const context = React.useContext(NavigationContext) - if (context === undefined) { - return { - show: () => {}, - goBack: undefined, - } - } - - return context -} - function Subview({ view }: { view: View }) { switch (view.name) { case 'device_detail': diff --git a/src/lib/use-navigation.ts b/src/lib/use-navigation.ts new file mode 100644 index 000000000..22f62ad66 --- /dev/null +++ b/src/lib/use-navigation.ts @@ -0,0 +1,18 @@ +import React from 'react' + +import { + NavigationContext, + type NavigationContextProps, +} from 'lib/NavigationProvider.js' + +export function useNavigation(): NavigationContextProps { + const context = React.useContext(NavigationContext) + if (context === undefined) { + return { + show: () => {}, + goBack: undefined, + } + } + + return context +} From f2b19b9d189d4f583aad5bdd1bac570842006c37 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 22:28:40 +0900 Subject: [PATCH 06/11] Update useNavigation calls --- src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx | 2 +- src/lib/ui/AccessCodeTable/AccessCodeTable.tsx | 2 +- src/lib/ui/DeviceDetails/DeviceDetails.tsx | 2 +- src/lib/ui/DeviceTable/DeviceTable.tsx | 2 +- src/lib/ui/layout/ContentHeader.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx b/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx index 4716ad72a..cdafdbec5 100644 --- a/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx +++ b/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx @@ -1,10 +1,10 @@ -import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { useFakeDevice } from 'lib/seam/devices/use-device.js' import { Button } from 'lib/ui/Button.js' import { DeviceImage } from 'lib/ui/device/DeviceImage.js' import { getDeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js' import { TextButton } from 'lib/ui/TextButton.js' +import { useNavigation } from 'lib/use-navigation.js' export default function AccessCodeDevice({ deviceId }: { deviceId: string }) { // TODO: Replace with `useDevice()` once ready diff --git a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx index c140469f1..e97d6426e 100644 --- a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +++ b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx @@ -1,5 +1,4 @@ import { AccessCodeKeyIcon } from 'lib/icons/AccessCodeKey.js' -import { useNavigation } from 'lib/NavigationProvider.js' import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js' import { CodeDetails } from 'lib/ui/AccessCodeTable/CodeDetails.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' @@ -10,6 +9,7 @@ import { TableRow } from 'lib/ui/Table/TableRow.js' import { TableTitle } from 'lib/ui/Table/TableTitle.js' import { Caption } from 'lib/ui/typography/Caption.js' import { Title } from 'lib/ui/typography/Title.js' +import { useNavigation } from 'lib/use-navigation.js' export function AccessCodeTable(props: { deviceId: string }): JSX.Element { const { accessCodes } = useAccessCodes({ diff --git a/src/lib/ui/DeviceDetails/DeviceDetails.tsx b/src/lib/ui/DeviceDetails/DeviceDetails.tsx index c3bebd7d7..2260d5771 100644 --- a/src/lib/ui/DeviceDetails/DeviceDetails.tsx +++ b/src/lib/ui/DeviceDetails/DeviceDetails.tsx @@ -1,7 +1,6 @@ import { useAccessCodes, useFakeDevice } from 'lib/index.js' import { ChevronRightIcon } from 'lib/icons/ChevronRight.js' -import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { Button } from 'lib/ui/Button.js' import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js' @@ -9,6 +8,7 @@ import { DeviceImage } from 'lib/ui/device/DeviceImage.js' import { OnlineStatus } from 'lib/ui/device/OnlineStatus.js' import { DeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' +import { useNavigation } from 'lib/use-navigation.js' export interface DeviceDetailsProps { deviceId: string diff --git a/src/lib/ui/DeviceTable/DeviceTable.tsx b/src/lib/ui/DeviceTable/DeviceTable.tsx index 6375cfe96..d8625fc38 100644 --- a/src/lib/ui/DeviceTable/DeviceTable.tsx +++ b/src/lib/ui/DeviceTable/DeviceTable.tsx @@ -1,4 +1,3 @@ -import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { useDevices, @@ -18,6 +17,7 @@ import { TableRow } from 'lib/ui/Table/TableRow.js' import { TableTitle } from 'lib/ui/Table/TableTitle.js' import { Caption } from 'lib/ui/typography/Caption.js' import { Title } from 'lib/ui/typography/Title.js' +import { useNavigation } from 'lib/use-navigation.js' export type DeviceTableProps = Props & UseDevicesParams diff --git a/src/lib/ui/layout/ContentHeader.tsx b/src/lib/ui/layout/ContentHeader.tsx index 0693848d8..bfd21774f 100644 --- a/src/lib/ui/layout/ContentHeader.tsx +++ b/src/lib/ui/layout/ContentHeader.tsx @@ -1,5 +1,5 @@ import { ArrowBackIcon } from 'lib/icons/ArrowBack.js' -import { useNavigation } from 'lib/NavigationProvider.js' +import { useNavigation } from 'lib/use-navigation.js' export function ContentHeader(props: { title?: string }): JSX.Element | null { const { title } = props From a626bc28f038368b15fcf90274e802de5ee58b12 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 22:34:01 +0900 Subject: [PATCH 07/11] Revert "Update useNavigation calls" This reverts commit f2b19b9d189d4f583aad5bdd1bac570842006c37. --- src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx | 2 +- src/lib/ui/AccessCodeTable/AccessCodeTable.tsx | 2 +- src/lib/ui/DeviceDetails/DeviceDetails.tsx | 2 +- src/lib/ui/DeviceTable/DeviceTable.tsx | 2 +- src/lib/ui/layout/ContentHeader.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx b/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx index cdafdbec5..4716ad72a 100644 --- a/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx +++ b/src/lib/ui/AccessCodeDetails/AccessCodeDevice.tsx @@ -1,10 +1,10 @@ +import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { useFakeDevice } from 'lib/seam/devices/use-device.js' import { Button } from 'lib/ui/Button.js' import { DeviceImage } from 'lib/ui/device/DeviceImage.js' import { getDeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js' import { TextButton } from 'lib/ui/TextButton.js' -import { useNavigation } from 'lib/use-navigation.js' export default function AccessCodeDevice({ deviceId }: { deviceId: string }) { // TODO: Replace with `useDevice()` once ready diff --git a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx index e97d6426e..c140469f1 100644 --- a/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx +++ b/src/lib/ui/AccessCodeTable/AccessCodeTable.tsx @@ -1,4 +1,5 @@ import { AccessCodeKeyIcon } from 'lib/icons/AccessCodeKey.js' +import { useNavigation } from 'lib/NavigationProvider.js' import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js' import { CodeDetails } from 'lib/ui/AccessCodeTable/CodeDetails.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' @@ -9,7 +10,6 @@ import { TableRow } from 'lib/ui/Table/TableRow.js' import { TableTitle } from 'lib/ui/Table/TableTitle.js' import { Caption } from 'lib/ui/typography/Caption.js' import { Title } from 'lib/ui/typography/Title.js' -import { useNavigation } from 'lib/use-navigation.js' export function AccessCodeTable(props: { deviceId: string }): JSX.Element { const { accessCodes } = useAccessCodes({ diff --git a/src/lib/ui/DeviceDetails/DeviceDetails.tsx b/src/lib/ui/DeviceDetails/DeviceDetails.tsx index 2260d5771..c3bebd7d7 100644 --- a/src/lib/ui/DeviceDetails/DeviceDetails.tsx +++ b/src/lib/ui/DeviceDetails/DeviceDetails.tsx @@ -1,6 +1,7 @@ import { useAccessCodes, useFakeDevice } from 'lib/index.js' import { ChevronRightIcon } from 'lib/icons/ChevronRight.js' +import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { Button } from 'lib/ui/Button.js' import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js' @@ -8,7 +9,6 @@ import { DeviceImage } from 'lib/ui/device/DeviceImage.js' import { OnlineStatus } from 'lib/ui/device/OnlineStatus.js' import { DeviceModel } from 'lib/ui/DeviceDetails/DeviceModel.js' import { ContentHeader } from 'lib/ui/layout/ContentHeader.js' -import { useNavigation } from 'lib/use-navigation.js' export interface DeviceDetailsProps { deviceId: string diff --git a/src/lib/ui/DeviceTable/DeviceTable.tsx b/src/lib/ui/DeviceTable/DeviceTable.tsx index d8625fc38..6375cfe96 100644 --- a/src/lib/ui/DeviceTable/DeviceTable.tsx +++ b/src/lib/ui/DeviceTable/DeviceTable.tsx @@ -1,3 +1,4 @@ +import { useNavigation } from 'lib/NavigationProvider.js' import { isLockDevice } from 'lib/seam/devices/types.js' import { useDevices, @@ -17,7 +18,6 @@ import { TableRow } from 'lib/ui/Table/TableRow.js' import { TableTitle } from 'lib/ui/Table/TableTitle.js' import { Caption } from 'lib/ui/typography/Caption.js' import { Title } from 'lib/ui/typography/Title.js' -import { useNavigation } from 'lib/use-navigation.js' export type DeviceTableProps = Props & UseDevicesParams diff --git a/src/lib/ui/layout/ContentHeader.tsx b/src/lib/ui/layout/ContentHeader.tsx index bfd21774f..0693848d8 100644 --- a/src/lib/ui/layout/ContentHeader.tsx +++ b/src/lib/ui/layout/ContentHeader.tsx @@ -1,5 +1,5 @@ import { ArrowBackIcon } from 'lib/icons/ArrowBack.js' -import { useNavigation } from 'lib/use-navigation.js' +import { useNavigation } from 'lib/NavigationProvider.js' export function ContentHeader(props: { title?: string }): JSX.Element | null { const { title } = props From ae977835a32b4c21463dcc922e4c116457ea0023 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 22:34:09 +0900 Subject: [PATCH 08/11] Revert "Separate out use-navigation" This reverts commit ff1dd7a67e2cc0a5145ca9503abe847ce9f561d9. --- src/lib/NavigationProvider.tsx | 16 ++++++++++++++-- src/lib/use-navigation.ts | 18 ------------------ 2 files changed, 14 insertions(+), 20 deletions(-) delete mode 100644 src/lib/use-navigation.ts diff --git a/src/lib/NavigationProvider.tsx b/src/lib/NavigationProvider.tsx index 7473e14fe..d74b12c93 100644 --- a/src/lib/NavigationProvider.tsx +++ b/src/lib/NavigationProvider.tsx @@ -33,12 +33,12 @@ export interface NavigationStackProps { children: JSX.Element } -export interface NavigationContextProps { +interface NavigationContextProps { show: (view: View) => void goBack?: () => void } -export const NavigationContext = React.createContext< +const NavigationContext = React.createContext< NavigationContextProps | undefined >(undefined) @@ -89,6 +89,18 @@ function Content({ return rootView } +export function useNavigation(): NavigationContextProps { + const context = React.useContext(NavigationContext) + if (context === undefined) { + return { + show: () => {}, + goBack: undefined, + } + } + + return context +} + function Subview({ view }: { view: View }) { switch (view.name) { case 'device_detail': diff --git a/src/lib/use-navigation.ts b/src/lib/use-navigation.ts deleted file mode 100644 index 22f62ad66..000000000 --- a/src/lib/use-navigation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' - -import { - NavigationContext, - type NavigationContextProps, -} from 'lib/NavigationProvider.js' - -export function useNavigation(): NavigationContextProps { - const context = React.useContext(NavigationContext) - if (context === undefined) { - return { - show: () => {}, - goBack: undefined, - } - } - - return context -} From 938b68223f871964637c3ab814a421dc877246a0 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 22:38:10 +0900 Subject: [PATCH 09/11] Separate out --- src/lib/NavigationProvider.tsx | 39 +++---------------- src/lib/NavigationView.tsx | 32 +++++++++++++++ .../ui/DeviceTable/DeviceTable.stories.tsx | 5 ++- 3 files changed, 41 insertions(+), 35 deletions(-) create mode 100644 src/lib/NavigationView.tsx diff --git a/src/lib/NavigationProvider.tsx b/src/lib/NavigationProvider.tsx index d74b12c93..956bff592 100644 --- a/src/lib/NavigationProvider.tsx +++ b/src/lib/NavigationProvider.tsx @@ -1,9 +1,5 @@ import React, { useState } from 'react' -import { AccessCodeTable, DeviceDetails, DeviceTable } from 'lib/index.js' - -import AccessCodeDetails from 'lib/ui/AccessCodeDetails/AccessCodeDetails.js' - interface DeviceTableView { name: 'device_table' } @@ -23,7 +19,7 @@ interface AccessCodeDetailView { accessCodeId: string } -type View = +export type View = | DeviceTableView | DeviceDetailView | AccessCodeTableView @@ -34,6 +30,7 @@ export interface NavigationStackProps { } interface NavigationContextProps { + views: View[] show: (view: View) => void goBack?: () => void } @@ -65,51 +62,25 @@ export default function NavigationProvider({ children }: NavigationStackProps) { return ( - + {children} ) } -function Content({ - views, - rootView, -}: { - views: View[] - rootView: JSX.Element -}) { - const firstView = views[0] - if (firstView != null) { - return - } - - return rootView -} - export function useNavigation(): NavigationContextProps { const context = React.useContext(NavigationContext) if (context === undefined) { return { show: () => {}, goBack: undefined, + views: [], } } return context } - -function Subview({ view }: { view: View }) { - switch (view.name) { - case 'device_detail': - return - case 'access_code_detail': - return - case 'device_table': - return - case 'access_code_table': - return - } -} diff --git a/src/lib/NavigationView.tsx b/src/lib/NavigationView.tsx new file mode 100644 index 000000000..1bca95433 --- /dev/null +++ b/src/lib/NavigationView.tsx @@ -0,0 +1,32 @@ +import { AccessCodeTable, DeviceDetails, DeviceTable } from 'lib/index.js' + +import { useNavigation, type View } from 'lib/NavigationProvider.js' +import AccessCodeDetails from 'lib/ui/AccessCodeDetails/AccessCodeDetails.js' + +interface NavigationViewProps { + children: JSX.Element +} + +export function NavigationView({ children: rootView }: NavigationViewProps) { + const { views } = useNavigation() + + const firstView = views[0] + if (firstView != null) { + return + } + + return rootView +} + +function Subview({ view }: { view: View }) { + switch (view.name) { + case 'device_detail': + return + case 'access_code_detail': + return + case 'device_table': + return + case 'access_code_table': + return + } +} diff --git a/src/lib/ui/DeviceTable/DeviceTable.stories.tsx b/src/lib/ui/DeviceTable/DeviceTable.stories.tsx index eab2ff6ea..8aecf12da 100644 --- a/src/lib/ui/DeviceTable/DeviceTable.stories.tsx +++ b/src/lib/ui/DeviceTable/DeviceTable.stories.tsx @@ -2,6 +2,7 @@ import { Button, Dialog, DialogActions } from '@mui/material' import type { Meta, StoryObj } from '@storybook/react' import NavigationProvider from 'lib/NavigationProvider.js' +import { NavigationView } from 'lib/NavigationView.js' import { DeviceTable, type DeviceTableProps, @@ -65,7 +66,9 @@ function WithNavigationComponent(props: DeviceTableProps) {
- + + +
Date: Fri, 12 May 2023 22:39:42 +0900 Subject: [PATCH 10/11] Fix circular dep from useDevice --- src/lib/ui/DeviceDetails/DeviceDetails.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/ui/DeviceDetails/DeviceDetails.tsx b/src/lib/ui/DeviceDetails/DeviceDetails.tsx index c3bebd7d7..b885c65f4 100644 --- a/src/lib/ui/DeviceDetails/DeviceDetails.tsx +++ b/src/lib/ui/DeviceDetails/DeviceDetails.tsx @@ -1,8 +1,8 @@ -import { useAccessCodes, useFakeDevice } from 'lib/index.js' - import { ChevronRightIcon } from 'lib/icons/ChevronRight.js' import { useNavigation } from 'lib/NavigationProvider.js' +import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js' import { isLockDevice } from 'lib/seam/devices/types.js' +import { useFakeDevice } from 'lib/seam/devices/use-device.js' import { Button } from 'lib/ui/Button.js' import { BatteryStatus } from 'lib/ui/device/BatteryStatus.js' import { DeviceImage } from 'lib/ui/device/DeviceImage.js' From f9a74e07f8b58c6269bd733867b22938b55cbef8 Mon Sep 17 00:00:00 2001 From: Mike Wu Date: Fri, 12 May 2023 22:42:53 +0900 Subject: [PATCH 11/11] Add return type --- src/lib/seam/access-codes/use-access-code.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/seam/access-codes/use-access-code.ts b/src/lib/seam/access-codes/use-access-code.ts index bbce6d538..5a8b6a5d7 100644 --- a/src/lib/seam/access-codes/use-access-code.ts +++ b/src/lib/seam/access-codes/use-access-code.ts @@ -25,7 +25,10 @@ export function useAccessCode(params: UseAccessCodeParams) { }) } -export function useFakeAccessCode(_params: UseAccessCodeParams) { +export function useFakeAccessCode(_params: UseAccessCodeParams): { + isLoading: boolean + data: AccessCode +} { const accessCode: AccessCode = { access_code_id: uuid(), type: 'time_bound',