@seamapi/react 4.4.0 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import classNames from 'classnames'
2
+ import { useState } from 'react'
2
3
 
3
4
  import { ChevronRightIcon } from 'lib/icons/ChevronRight.js'
4
5
  import { useAccessCodes } from 'lib/seam/access-codes/use-access-codes.js'
@@ -16,6 +17,7 @@ import { DeviceImage } from 'lib/ui/device/DeviceImage.js'
16
17
  import { EditableDeviceName } from 'lib/ui/device/EditableDeviceName.js'
17
18
  import { OnlineStatus } from 'lib/ui/device/OnlineStatus.js'
18
19
  import { ContentHeader } from 'lib/ui/layout/ContentHeader.js'
20
+ import { Snackbar, type SnackbarVariant } from 'lib/ui/Snackbar/Snackbar.js'
19
21
  import { useToggle } from 'lib/ui/use-toggle.js'
20
22
 
21
23
  interface LockDeviceDetailsProps extends NestedSpecificDeviceDetailsProps {
@@ -38,11 +40,25 @@ export function LockDeviceDetails({
38
40
  onEditName,
39
41
  }: LockDeviceDetailsProps): JSX.Element | null {
40
42
  const [accessCodesOpen, toggleAccessCodesOpen] = useToggle()
41
- const toggleLock = useToggleLock()
42
43
  const { accessCodes } = useAccessCodes({
43
44
  device_id: device.device_id,
44
45
  })
45
46
 
47
+ const [snackbarVisible, setSnackbarVisible] = useState(false)
48
+ const [snackbarVariant, setSnackbarVariant] =
49
+ useState<SnackbarVariant>('success')
50
+
51
+ const toggleLock = useToggleLock({
52
+ onSuccess: () => {
53
+ setSnackbarVisible(true)
54
+ setSnackbarVariant('success')
55
+ },
56
+ onError: () => {
57
+ setSnackbarVisible(true)
58
+ setSnackbarVariant('error')
59
+ },
60
+ })
61
+
46
62
  const lockStatus = device.properties.locked ? t.locked : t.unlocked
47
63
  const toggleLockLabel = device.properties.locked ? t.unlock : t.lock
48
64
 
@@ -88,87 +104,103 @@ export function LockDeviceDetails({
88
104
  ]
89
105
 
90
106
  return (
91
- <div className={classNames('seam-device-details', className)}>
92
- <ContentHeader title='Device' onBack={onBack} />
93
- <div className='seam-body'>
94
- <div className='seam-summary'>
95
- <div className='seam-content'>
96
- <div className='seam-image'>
97
- <DeviceImage device={device} />
98
- </div>
99
- <div className='seam-info'>
100
- <span className='seam-label'>{t.device}</span>
101
- <EditableDeviceName
102
- tagName='h4'
103
- value={device.properties.name}
104
- className='seam-device-name'
105
- onEdit={onEditName}
106
- />
107
- <div className='seam-properties'>
108
- <span className='seam-label'>{t.status}:</span>{' '}
109
- <OnlineStatus device={device} />
110
- {device.properties.online && (
111
- <>
112
- <span className='seam-label'>{t.power}:</span>{' '}
113
- <BatteryStatusIndicator device={device} />
114
- </>
115
- )}
116
- <DeviceModel device={device} />
107
+ <>
108
+ <Snackbar
109
+ variant={snackbarVariant}
110
+ visible={snackbarVisible}
111
+ onClose={() => {
112
+ setSnackbarVisible(false)
113
+ }}
114
+ message={
115
+ snackbarVariant === 'success'
116
+ ? t.successfullyUpdated
117
+ : t.failedToUpdate
118
+ }
119
+ autoDismiss
120
+ />
121
+
122
+ <div className={classNames('seam-device-details', className)}>
123
+ <ContentHeader title='Device' onBack={onBack} />
124
+ <div className='seam-body'>
125
+ <div className='seam-summary'>
126
+ <div className='seam-content'>
127
+ <div className='seam-image'>
128
+ <DeviceImage device={device} />
129
+ </div>
130
+ <div className='seam-info'>
131
+ <span className='seam-label'>{t.device}</span>
132
+ <EditableDeviceName
133
+ tagName='h4'
134
+ value={device.properties.name}
135
+ className='seam-device-name'
136
+ onEdit={onEditName}
137
+ />
138
+ <div className='seam-properties'>
139
+ <span className='seam-label'>{t.status}:</span>{' '}
140
+ <OnlineStatus device={device} />
141
+ {device.properties.online && (
142
+ <>
143
+ <span className='seam-label'>{t.power}:</span>{' '}
144
+ <BatteryStatusIndicator device={device} />
145
+ </>
146
+ )}
147
+ <DeviceModel device={device} />
148
+ </div>
117
149
  </div>
118
150
  </div>
151
+ <Alerts alerts={alerts} className='seam-alerts-space-top' />
119
152
  </div>
120
- <Alerts alerts={alerts} className='seam-alerts-space-top' />
121
- </div>
122
- <div className='seam-box'>
123
- <div
124
- className='seam-content seam-access-codes'
125
- onClick={toggleAccessCodesOpen}
126
- >
127
- <span className='seam-value'>
128
- {accessCodeCount} {t.accessCodes}
129
- </span>
130
- <ChevronRightIcon />
153
+ <div className='seam-box'>
154
+ <div
155
+ className='seam-content seam-access-codes'
156
+ onClick={toggleAccessCodesOpen}
157
+ >
158
+ <span className='seam-value'>
159
+ {accessCodeCount} {t.accessCodes}
160
+ </span>
161
+ <ChevronRightIcon />
162
+ </div>
131
163
  </div>
132
- </div>
133
164
 
134
- <div className='seam-box'>
135
- {device.properties.locked && device.properties.online && (
136
- <div className='seam-content seam-lock-status'>
137
- <div>
138
- <span className='seam-label'>{t.lockStatus}</span>
139
- <span className='seam-value'>{lockStatus}</span>
140
- </div>
141
- <div className='seam-right'>
142
- {!disableLockUnlock &&
143
- device.capabilities_supported.includes('lock') && (
144
- <Button
145
- size='small'
146
- onClick={() => {
147
- toggleLock.mutate(device)
148
- }}
149
- >
150
- {toggleLockLabel}
151
- </Button>
152
- )}
165
+ <div className='seam-box'>
166
+ {device.properties.locked && device.properties.online && (
167
+ <div className='seam-content seam-lock-status'>
168
+ <div>
169
+ <span className='seam-label'>{t.lockStatus}</span>
170
+ <span className='seam-value'>{lockStatus}</span>
171
+ </div>
172
+ <div className='seam-right'>
173
+ {!disableLockUnlock &&
174
+ device.capabilities_supported.includes('lock') && (
175
+ <Button
176
+ size='small'
177
+ onClick={() => {
178
+ toggleLock.mutate(device)
179
+ }}
180
+ >
181
+ {toggleLockLabel}
182
+ </Button>
183
+ )}
184
+ </div>
153
185
  </div>
154
- </div>
155
- )}
186
+ )}
156
187
 
157
- <AccessCodeLength
158
- supportedCodeLengths={
159
- device.properties?.supported_code_lengths ?? []
188
+ <AccessCodeLength
189
+ supportedCodeLengths={
190
+ device.properties?.supported_code_lengths ?? []
191
+ }
192
+ />
193
+ </div>
194
+ <DeviceInfo
195
+ device={device}
196
+ disableConnectedAccountInformation={
197
+ disableConnectedAccountInformation
160
198
  }
199
+ disableResourceIds={disableResourceIds}
161
200
  />
162
201
  </div>
163
- <DeviceInfo
164
- device={device}
165
- disableConnectedAccountInformation={
166
- disableConnectedAccountInformation
167
- }
168
- disableResourceIds={disableResourceIds}
169
- />
170
202
  </div>
171
- </div>
203
+ </>
172
204
  )
173
205
  }
174
206
 
@@ -208,4 +240,6 @@ const t = {
208
240
  lockStatus: 'Lock status',
209
241
  status: 'Status',
210
242
  power: 'Power',
243
+ successfullyUpdated: 'Lock status has been successfully updated',
244
+ failedToUpdate: 'Failed to update lock status',
211
245
  }
@@ -12,8 +12,6 @@ import {
12
12
 
13
13
  import { NullSeamClientError, useSeamClient } from 'lib/seam/use-seam-client.js'
14
14
 
15
- export type UseToggleLockParams = never
16
-
17
15
  export type UseToggleLockData = undefined
18
16
 
19
17
  export type UseToggleLockMutationVariables = Pick<Device, 'device_id'> & {
@@ -29,7 +27,14 @@ type MutationError =
29
27
  | SeamActionAttemptFailedError<ToggleLockActionAttempt>
30
28
  | SeamActionAttemptTimeoutError<ToggleLockActionAttempt>
31
29
 
32
- export function useToggleLock(): UseMutationResult<
30
+ interface UseToggleLockParams {
31
+ onError?: () => void
32
+ onSuccess?: () => void
33
+ }
34
+
35
+ export function useToggleLock(
36
+ params: UseToggleLockParams = {}
37
+ ): UseMutationResult<
33
38
  UseToggleLockData,
34
39
  MutationError,
35
40
  UseToggleLockMutationVariables
@@ -92,6 +97,8 @@ export function useToggleLock(): UseMutationResult<
92
97
  )
93
98
  },
94
99
  onError: async (_error, variables) => {
100
+ params.onError?.()
101
+
95
102
  await queryClient.invalidateQueries({
96
103
  queryKey: ['devices', 'list'],
97
104
  })
@@ -99,5 +106,8 @@ export function useToggleLock(): UseMutationResult<
99
106
  queryKey: ['devices', 'get', { device_id: variables.device_id }],
100
107
  })
101
108
  },
109
+ onSuccess() {
110
+ params.onSuccess?.()
111
+ },
102
112
  })
103
113
  }
@@ -5,9 +5,9 @@ import { CheckGreenIcon } from 'lib/icons/CheckGreen.js'
5
5
  import { CloseWhiteIcon } from 'lib/icons/CloseWhite.js'
6
6
  import { ExclamationCircleIcon } from 'lib/icons/ExclamationCircle.js'
7
7
 
8
- type SnackbarVariant = 'success' | 'error'
8
+ export type SnackbarVariant = 'success' | 'error'
9
9
 
10
- interface SnackbarProps {
10
+ export interface SnackbarProps {
11
11
  message: string
12
12
  variant: SnackbarVariant
13
13
  visible: boolean
@@ -1,3 +1,3 @@
1
- const seamapiReactVersion = '4.4.0'
1
+ const seamapiReactVersion = '4.6.0'
2
2
 
3
3
  export default seamapiReactVersion