Skip to content

Commit d60e72c

Browse files
Fix Lint
1 parent fc947a9 commit d60e72c

File tree

8 files changed

+44
-133
lines changed

8 files changed

+44
-133
lines changed

app/gui/src/dashboard/components/Activity.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
* This component is used to suspend the rendering of a subtree until a promise is resolved.
55
*/
66
import { unsafeWriteValue } from '#/utilities/write'
7-
import { startTransition, Suspense, useEffect, useLayoutEffect, useRef, useState } from 'react'
8-
import { useAwait } from './Await'
7+
import { startTransition, Suspense, use, useEffect, useLayoutEffect, useRef, useState } from 'react'
98

109
/**
1110
* Props for {@link Activity}
@@ -88,8 +87,10 @@ interface ActivityInnerProps {
8887
function ActivityInner(props: ActivityInnerProps) {
8988
const { promise, children } = props
9089

91-
// Suspend the subtree
92-
useAwait(promise)
90+
if (promise != null) {
91+
// Suspend the subtree
92+
use(promise)
93+
}
9394

9495
return children
9596
}

app/gui/src/dashboard/components/AriaComponents/Button/CopyButton.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ export function CopyButton<IconType extends string>(props: CopyButtonProps<IconT
3838
...buttonProps
3939
} = props
4040
const { getText } = textProvider.useText()
41-
const { copy, isCopying, isCopied } = useCopy({ onCopy })
41+
const { copy, isCopied } = useCopy({ onCopy })
42+
4243
const showIcon = copyIcon !== false
4344
const icon =
4445
showIcon ?
@@ -52,11 +53,8 @@ export function CopyButton<IconType extends string>(props: CopyButtonProps<IconT
5253
/* eslint-disable-next-line @typescript-eslint/no-explicit-any,no-restricted-syntax */
5354
{...(buttonProps as any)}
5455
variant={variant}
55-
isLoading={isCopying}
5656
aria-label={props['aria-label'] ?? getText('copyShortcut')}
57-
onPress={() => {
58-
copy(copyText)
59-
}}
57+
onPress={() => copy(copyText)}
6058
icon={icon}
6159
/>
6260
)

app/gui/src/dashboard/components/AriaComponents/Form/components/FormProvider.tsx

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Context that injects form instance into the component tree.
55
*/
6-
import { createContext, useContext } from 'react'
6+
import { createContext, use } from 'react'
77
import invariant from 'tiny-invariant'
88
import type * as types from './types'
99
import type { FormInstance, FormInstanceValidated } from './types'
@@ -36,32 +36,19 @@ export function FormProvider<Schema extends types.TSchema>(
3636
export function useFormContext<Schema extends types.TSchema>(
3737
form?: FormInstanceValidated<Schema>,
3838
): FormInstance<Schema> {
39-
if (form != null && 'control' in form) {
40-
return form
41-
} else {
42-
// eslint-disable-next-line react-compiler/react-compiler
43-
// eslint-disable-next-line react-hooks/rules-of-hooks
44-
const ctx = useContext(FormContext)
39+
const formInstance = useOptionalFormContext<Schema, FormInstanceValidated<Schema>>(form)
4540

46-
invariant(ctx, 'FormContext not found')
41+
invariant(formInstance, 'FormContext not found')
4742

48-
// This is safe, as we pass the value transparently and it is typed outside
49-
// eslint-disable-next-line no-restricted-syntax
50-
return ctx.form as unknown as types.UseFormReturn<Schema>
51-
}
43+
return formInstance
5244
}
5345

5446
/** Returns the form instance from the context, or null if the context is not available. */
5547
// eslint-disable-next-line react-refresh/only-export-components
5648
export function useOptionalFormContext<
57-
Form extends FormInstanceValidated<Schema> | undefined,
5849
Schema extends types.TSchema,
59-
>(form?: Form): Form extends undefined ? FormInstance<Schema> | null : FormInstance<Schema> {
60-
try {
61-
// eslint-disable-next-line react-compiler/react-compiler
62-
return useFormContext<Schema>(form)
63-
} catch {
64-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
65-
return null!
66-
}
50+
Form extends FormInstanceValidated<Schema> | undefined,
51+
>(form?: Form) {
52+
// eslint-disable-next-line no-restricted-syntax
53+
return (form ?? use(FormContext)?.form ?? null) as FormInstance<Schema> | null
6754
}

app/gui/src/dashboard/components/Await.tsx

Lines changed: 2 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
*
44
* Await a promise and render the children when the promise is resolved.
55
*/
6-
import { type ReactNode } from 'react'
6+
import { use, type ReactNode } from 'react'
77

8-
import invariant from 'tiny-invariant'
98
import { ErrorBoundary, type ErrorBoundaryProps } from './ErrorBoundary'
109
import { Suspense, type SuspenseProps } from './Suspense'
1110

@@ -87,8 +86,6 @@ export function Await<PromiseType>(props: AwaitProps<PromiseType>) {
8786
)
8887
}
8988

90-
const PRIVATE_AWAIT_PROMISE_STATE = Symbol('PRIVATE_AWAIT_PROMISE_STATE_REF')
91-
9289
/**
9390
* Internal implementation of the {@link Await} component.
9491
*
@@ -99,84 +96,7 @@ const PRIVATE_AWAIT_PROMISE_STATE = Symbol('PRIVATE_AWAIT_PROMISE_STATE_REF')
9996
function AwaitInternal<PromiseType>(props: AwaitProps<PromiseType>) {
10097
const { promise, children } = props
10198

102-
const data = useAwait(promise)
99+
const data = use(promise)
103100

104101
return typeof children === 'function' ? children(data) : children
105102
}
106-
107-
export function useAwait(promise?: null): void
108-
export function useAwait<PromiseType>(promise: Promise<PromiseType>): PromiseType
109-
export function useAwait<PromiseType>(
110-
promise?: Promise<PromiseType> | null,
111-
): PromiseType | undefined
112-
113-
/**
114-
* A hook that accepts a promise and triggers the Suspense boundary until the promise is resolved.
115-
* @param promise - The promise to await.
116-
* @throws {Promise} - The promise that is being awaited by Suspense
117-
* @returns The data of the promise.
118-
*/
119-
// eslint-disable-next-line react-refresh/only-export-components
120-
export function useAwait<PromiseType>(
121-
promise?: Promise<PromiseType> | null,
122-
): PromiseType | undefined {
123-
if (promise == null) {
124-
return
125-
}
126-
127-
/**
128-
* Define the promise state on the promise.
129-
*/
130-
const definePromiseState = (
131-
promiseToDefineOn: Promise<PromiseType>,
132-
promiseState: PromiseState<PromiseType>,
133-
) => {
134-
// @ts-expect-error: we know that the promise state is not defined in the type but it's fine,
135-
// because it's a private and scoped to the component.
136-
promiseToDefineOn[PRIVATE_AWAIT_PROMISE_STATE] = promiseState
137-
}
138-
139-
// We need to define the promise state, only once.
140-
// We don't want to use refs on state, because it scopes the state to the component.
141-
// But we might use multiple Await components with the same promise.
142-
if (!(PRIVATE_AWAIT_PROMISE_STATE in promise)) {
143-
definePromiseState(promise, { status: 'pending' })
144-
145-
// This breaks the chain of promises, but it's fine,
146-
// because this is suppsed to the last in the chain.
147-
// and the error will be thrown in the render phase
148-
// to trigger the error boundary.
149-
void promise.then((data) => {
150-
definePromiseState(promise, { status: 'success', data })
151-
})
152-
void promise.catch((error) => {
153-
definePromiseState(promise, { status: 'error', error })
154-
})
155-
}
156-
157-
// This should never happen, as the promise state is defined above.
158-
// But we need to check it, because the promise state is not defined in the type.
159-
// And we want to make TypeScript happy.
160-
invariant(
161-
PRIVATE_AWAIT_PROMISE_STATE in promise,
162-
'Promise state is not defined. This should never happen.',
163-
)
164-
165-
const promiseState =
166-
// This is safe, as we defined the promise state above.
167-
// and it always present in the promise object.
168-
// eslint-disable-next-line no-restricted-syntax
169-
promise[PRIVATE_AWAIT_PROMISE_STATE] as PromiseState<PromiseType>
170-
171-
if (promiseState.status === 'pending') {
172-
// Throwing a promise is the valid way to trigger Suspense
173-
// eslint-disable-next-line @typescript-eslint/only-throw-error
174-
throw promise
175-
}
176-
177-
if (promiseState.status === 'error') {
178-
throw promiseState.error
179-
}
180-
181-
return promiseState.data
182-
}

app/gui/src/dashboard/components/JSONSchemaInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { useText } from '#/providers/TextProvider'
1111
import { constantValueOfSchema, getSchemaName, lookupDef } from '#/utilities/jsonSchema'
1212
import { asObject, singletonObjectOrNull } from '#/utilities/object'
1313
import { twMerge } from '#/utilities/tailwindMerge'
14-
import React from 'react'
14+
import * as React from 'react'
1515
import { twJoin } from 'tailwind-merge'
1616

1717
/** Props for a {@link JSONSchemaInput}. */

app/gui/src/dashboard/layouts/AssetContextMenu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
171171
color="accent"
172172
action="copyId"
173173
doAction={() => {
174-
copyMutation.copy(asset.id)
174+
void copyMutation.copy(asset.id)
175175
}}
176176
/>
177177
)
@@ -415,7 +415,7 @@ export default function AssetContextMenu(props: AssetContextMenuProps) {
415415
hidden={hidden}
416416
action="copyAsPath"
417417
doAction={() => {
418-
copyMutation.copy(path)
418+
void copyMutation.copy(path)
419419
}}
420420
/>
421421
)}

app/gui/src/dashboard/layouts/AssetsTableContextMenu.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ export default function AssetsTableContextMenu(props: AssetsTableContextMenuProp
132132
action="copyId"
133133
color="accent"
134134
label={getText('copyAllIdsShortcut')}
135-
doAction={() => copyMutation.copy(selectedAssets.map((asset) => asset.id).join('\n'))}
135+
doAction={() => {
136+
void copyMutation.copy(selectedAssets.map((asset) => asset.id).join('\n'))
137+
}}
136138
/>
137139
)
138140

app/gui/src/dashboard/providers/AuthProvider.tsx

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -437,27 +437,30 @@ export function GuestLayout() {
437437
const { session } = useAuth()
438438
const { localStorage } = localStorageProvider.useLocalStorage()
439439

440-
if (session?.type === UserSessionType.partial) {
441-
return <router.Navigate to={appUtils.SETUP_PATH} />
442-
} else if (session?.type === UserSessionType.full) {
440+
if (session?.type === UserSessionType.full) {
443441
const redirectTo = localStorage.get('loginRedirect')
442+
444443
if (redirectTo != null) {
445444
localStorage.delete('loginRedirect')
446-
location.href = redirectTo
445+
unsafeWriteValue(window.location, 'href', redirectTo)
447446
return
448-
} else {
449-
return <router.Navigate to={appUtils.DASHBOARD_PATH} />
450447
}
451-
} else {
452-
return (
453-
<>
454-
{/* This div is used as a flag to indicate that the user is not logged in. */}
455-
{/* also it guarantees that the top-level suspense boundary is already resolved */}
456-
<div data-testid="before-auth-layout" aria-hidden />
457-
<router.Outlet />
458-
</>
459-
)
448+
449+
return <router.Navigate to={appUtils.DASHBOARD_PATH} />
460450
}
451+
452+
if (session?.type === UserSessionType.partial) {
453+
return <router.Navigate to={appUtils.SETUP_PATH} />
454+
}
455+
456+
return (
457+
<>
458+
{/* This div is used as a flag to indicate that the user is not logged in. */}
459+
{/* also it guarantees that the top-level suspense boundary is already resolved */}
460+
<div data-testid="before-auth-layout" aria-hidden />
461+
<router.Outlet />
462+
</>
463+
)
461464
}
462465

463466
/** A React Router layout route containing routes only accessible by users that are not deleted. */
@@ -466,9 +469,9 @@ export function NotDeletedUserLayout() {
466469

467470
if (isUserMarkedForDeletion()) {
468471
return <router.Navigate to={appUtils.RESTORE_USER_PATH} />
469-
} else {
470-
return <router.Outlet context={session} />
471472
}
473+
474+
return <router.Outlet context={session} />
472475
}
473476

474477
/** A React Router layout route containing routes only accessible by users that are deleted softly. */

0 commit comments

Comments
 (0)