chore: enable rule of hooks (#7080)

This commit is contained in:
Laurin Quast 2025-10-08 09:56:32 +02:00 committed by GitHub
parent ea73b9ab9b
commit b02b1dbf2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 135 additions and 112 deletions

View file

@ -179,7 +179,7 @@ module.exports = {
'react/jsx-no-useless-fragment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'react-hooks/rules-of-hooks': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'off',
'unicorn/filename-case': 'off',
'import/no-default-export': 'off',
@ -206,13 +206,12 @@ module.exports = {
},
},
},
// {
// files: ['packages/web/app/**'],
// excludedFiles: ['packages/web/app/src/pages/**'],
// rules: {
// 'import/no-unused-modules': ['error', { unusedExports: true }],
// },
// },
{
files: ['packages/web/app/**/*.stories.tsx', 'packages/web/docs/**'],
rules: {
'react-hooks/rules-of-hooks': 'off',
},
},
{
files: ['packages/web/docs/**'],
settings: {

View file

@ -85,6 +85,7 @@ function CollectedOperationsOverTime(props: {
const dataRef = useRef<[string, number][]>();
dataRef.current ||= operations.map(node => [node.date, node.count]);
const data = dataRef.current;
const chartStyles = useChartStyles();
return (
<AutoSizer disableHeight>
@ -93,7 +94,7 @@ function CollectedOperationsOverTime(props: {
style={{ width: size.width, height: 200 }}
theme={theme.theme}
option={{
...useChartStyles(),
...chartStyles,
grid: {
left: 50,
top: 50,

View file

@ -130,13 +130,23 @@ export function ChangesBlock(
}
),
): ReactElement | null {
const changes = props.changesWithUsage ?? props.changes;
return (
<div>
<h2 className="mb-3 font-bold text-gray-900 dark:text-white">{props.title}</h2>
<div className="list-inside list-disc space-y-2 text-sm leading-relaxed">
{changes.map((change, key) => (
{props.changesWithUsage?.map((change, key) => (
<ChangeItem
organizationSlug={props.organizationSlug}
projectSlug={props.projectSlug}
targetSlug={props.targetSlug}
schemaCheckId={props.schemaCheckId}
key={key}
change={null}
changeWithUsage={change}
conditionBreakingChangeMetadata={props.conditionBreakingChangeMetadata ?? null}
/>
))}
{props.changes?.map((change, key) => (
<ChangeItem
organizationSlug={props.organizationSlug}
projectSlug={props.projectSlug}
@ -144,6 +154,7 @@ export function ChangesBlock(
schemaCheckId={props.schemaCheckId}
key={key}
change={change}
changeWithUsage={null}
conditionBreakingChangeMetadata={props.conditionBreakingChangeMetadata ?? null}
/>
))}
@ -152,32 +163,34 @@ export function ChangesBlock(
);
}
// Obviously I'm not proud of this...
// But I didn't want to spend too much time on this
function isChangesBlock_SchemaChangeWithUsageFragment(
fragment: any,
): fragment is FragmentType<typeof ChangesBlock_SchemaChangeWithUsageFragment> {
return (
!!fragment[' $fragmentRefs'] &&
'ChangesBlock_SchemaChangeWithUsageFragment' in fragment[' $fragmentRefs']
function ChangeItem(
props: {
conditionBreakingChangeMetadata: FragmentType<
typeof ChangesBlock_SchemaCheckConditionalBreakingChangeMetadataFragment
> | null;
organizationSlug: string;
projectSlug: string;
targetSlug: string;
schemaCheckId: string;
} & (
| {
change: FragmentType<typeof ChangesBlock_SchemaChangeFragment>;
changeWithUsage: null;
}
| {
change: null;
changeWithUsage: FragmentType<typeof ChangesBlock_SchemaChangeWithUsageFragment>;
}
),
) {
const cchange = useFragment(ChangesBlock_SchemaChangeFragment, props.change);
const cchangeWithUsage = useFragment(
ChangesBlock_SchemaChangeWithUsageFragment,
props.changeWithUsage,
);
}
function ChangeItem(props: {
change:
| FragmentType<typeof ChangesBlock_SchemaChangeWithUsageFragment>
| FragmentType<typeof ChangesBlock_SchemaChangeFragment>;
conditionBreakingChangeMetadata: FragmentType<
typeof ChangesBlock_SchemaCheckConditionalBreakingChangeMetadataFragment
> | null;
organizationSlug: string;
projectSlug: string;
targetSlug: string;
schemaCheckId: string;
}) {
const change = isChangesBlock_SchemaChangeWithUsageFragment(props.change)
? useFragment(ChangesBlock_SchemaChangeWithUsageFragment, props.change)
: useFragment(ChangesBlock_SchemaChangeFragment, props.change);
// at least one prop must be provided :)
const change = (cchange ?? cchangeWithUsage)!;
const metadata = useFragment(
ChangesBlock_SchemaCheckConditionalBreakingChangeMetadataFragment,

View file

@ -49,12 +49,10 @@ function OperationsFilter({
operationStatsConnection,
);
const clientFilteredOperations = clientOperationStatsConnection
? useFragment(
OperationsFilter_OperationStatsValuesConnectionFragment,
clientOperationStatsConnection,
)
: null;
const clientFilteredOperations = useFragment(
OperationsFilter_OperationStatsValuesConnectionFragment,
clientOperationStatsConnection,
);
function getOperationHashes() {
const items: string[] = [];
@ -324,11 +322,12 @@ function OperationRow({
}): ReactElement {
const operation = useFragment(OperationRow_OperationStatsValuesFragment, operationStats);
const requests = useFormattedNumber(operation.count);
const clientsOperation = clientOperationStats
? useFragment(OperationRow_OperationStatsValuesFragment, clientOperationStats)
: undefined;
const clientsOperation = useFragment(
OperationRow_OperationStatsValuesFragment,
clientOperationStats || null,
);
const hasClientOperation = clientOperationStats !== false;
const clientsRequests = clientsOperation ? useFormattedNumber(clientsOperation.count) : null;
const clientsRequests = useFormattedNumber(clientsOperation?.count);
const hash = operation.operationHash || '';
const change = useCallback(() => {
if (hash) {
@ -340,7 +339,7 @@ function OperationRow({
if (hasClientOperation) {
return (
<div className="flex shrink-0 text-right text-gray-500">
<span>{clientsRequests ?? 0}</span>
<span>{clientsRequests === '-' ? 0 : clientsRequests}</span>
<span className="ml-1 truncate text-gray-600">/ {requests}</span>
</div>
);
@ -425,10 +424,10 @@ function ClientRow({
style: any;
}): ReactElement {
const client = useFragment(ClientRow_ClientStatsValuesFragment, props.client);
const clientOperation =
props.clientOperationStats === false
? false
: useFragment(ClientRow_ClientStatsValuesFragment, props.clientOperationStats);
const clientOperation = useFragment(
ClientRow_ClientStatsValuesFragment,
props.clientOperationStats || null,
);
const requests = useFormattedNumber(client.count);
const hash = client.name;
const change = useCallback(() => {
@ -438,7 +437,7 @@ function ClientRow({
}, [onSelect, hash, selected]);
const Totals = () => {
if (clientOperation !== false) {
if (props.clientOperationStats !== false) {
return (
<div className="flex shrink-0 text-right text-gray-500">
<span>{clientOperation?.count ?? 0}</span>
@ -544,9 +543,10 @@ function ClientsFilter({
setSelectedItems([]);
}, [setSelectedItems]);
const operationConnection = operationStatsConnection
? useFragment(ClientsFilter_ClientStatsValuesConnectionFragment, operationStatsConnection)
: null;
const operationConnection = useFragment(
ClientsFilter_ClientStatsValuesConnectionFragment,
operationStatsConnection ?? null,
);
const renderRow = useCallback<ComponentType<ListChildComponentProps>>(
({ index, style }) => {

View file

@ -11,19 +11,9 @@ import { AlertTriangleIcon, TrashIcon } from '@/components/ui/icon';
import { SubPageLayout, SubPageLayoutHeader } from '@/components/ui/page-content-layout';
import { Input, Modal, Table, Tag, TBody, Td, TimeAgo, Tr } from '@/components/v2';
import { InlineCode } from '@/components/v2/inline-code';
import { graphql, useFragment } from '@/gql';
import { FragmentType, graphql, useFragment } from '@/gql';
import { Link, useRouter } from '@tanstack/react-router';
const CDNAccessTokeRowFragment = graphql(`
fragment CDNAccessTokens_CdnAccessTokenRowFragment on CdnAccessToken {
id
firstCharacters
lastCharacters
alias
createdAt
}
`);
const CDNAccessTokenCreateMutation = graphql(`
mutation CDNAccessTokens_CDNAccessTokenCreateMutation($input: CreateCdnAccessTokenInput!) {
createCdnAccessToken(input: $input) {
@ -407,38 +397,9 @@ export function CDNAccessTokens(props: {
</div>
<Table>
<TBody>
{target?.data?.target?.cdnAccessTokens.edges?.map(edge => {
const node = useFragment(CDNAccessTokeRowFragment, edge.node);
return (
<Tr key={node.id}>
<Td>
{node.firstCharacters + new Array(10).fill('•').join('') + node.lastCharacters}
</Td>
<Td>{node.alias}</Td>
<Td align="right">
created <TimeAgo date={node.createdAt} />
</Td>
<Td align="right">
<Button
className="hover:text-red-500"
variant="ghost"
onClick={() => {
void router.navigate({
search: {
page: 'cdn',
cdn: 'delete',
id: node.id,
},
});
}}
>
<TrashIcon />
</Button>
</Td>
</Tr>
);
})}
{target?.data?.target?.cdnAccessTokens.edges?.map(edge => (
<CDNAccessTokenRow cdnAccessToken={edge.node} key={edge.node.id} />
))}
</TBody>
</Table>
@ -503,3 +464,49 @@ export function CDNAccessTokens(props: {
</SubPageLayout>
);
}
const CDNAccessTokenRowFragment = graphql(`
fragment CDNAccessTokens_CdnAccessTokenRowFragment on CdnAccessToken {
id
firstCharacters
lastCharacters
alias
createdAt
}
`);
type CDNAccessTokenRowProps = {
cdnAccessToken: FragmentType<typeof CDNAccessTokenRowFragment>;
};
function CDNAccessTokenRow(props: CDNAccessTokenRowProps): React.ReactNode {
const node = useFragment(CDNAccessTokenRowFragment, props.cdnAccessToken);
const router = useRouter();
return (
<Tr key={node.id}>
<Td>{node.firstCharacters + new Array(10).fill('•').join('') + node.lastCharacters}</Td>
<Td>{node.alias}</Td>
<Td align="right">
created <TimeAgo date={node.createdAt} />
</Td>
<Td align="right">
<Button
className="hover:text-red-500"
variant="ghost"
onClick={() => {
void router.navigate({
search: {
page: 'cdn',
cdn: 'delete',
id: node.id,
},
});
}}
>
<TrashIcon />
</Button>
</Td>
</Tr>
);
}

View file

@ -271,22 +271,13 @@ function ChecksPageContent(props: {
},
});
if (query.error) {
return (
<QueryError
organizationSlug={props.organizationSlug}
error={query.error}
showLogoutButton={false}
/>
);
}
const isLoading = query.fetching || query.stale;
const renderLoading = useDebouncedLoader(isLoading);
const [hasSchemaChecks, setHasSchemaChecks] = useState(
!!query.data?.target?.schemaChecks?.edges?.length,
);
useEffect(() => {
if (!isLoading) {
setHasSchemaChecks(!!query.data?.target?.schemaChecks?.edges?.length);
@ -298,8 +289,19 @@ function ChecksPageContent(props: {
const [paginationVariables, setPaginationVariables] = useState<Array<string | null>>(() => [
null,
]);
const onLoadMore = (cursor: string) => setPaginationVariables(cursors => [...cursors, cursor]);
if (query.error) {
return (
<QueryError
organizationSlug={props.organizationSlug}
error={query.error}
showLogoutButton={false}
/>
);
}
return (
<>
<div className={cn(!hasSchemaChecks && 'w-full')}>

View file

@ -166,6 +166,11 @@ function SchemaView(props: {
});
};
const schemas = useFragment(
SchemaView_SchemaFragment,
target.latestSchemaVersion?.schemas?.edges?.map(edge => edge.node),
);
const isDistributed =
project.type === ProjectType.Federation || project.type === ProjectType.Stitching;
@ -178,10 +183,6 @@ function SchemaView(props: {
return noSchema;
}
const schemas = useFragment(
SchemaView_SchemaFragment,
target.latestSchemaVersion?.schemas?.edges?.map(edge => edge.node),
);
const compositeSchemas = schemas?.filter(isCompositeSchema) as CompositeSchema[];
const singleSchema = schemas?.filter(schema => !isCompositeSchema(schema))[0] as
| SingleSchema