fix(core): add null checks for timeout refs and event listeners for React 19 compatibility (#9116)

## Description
- Add null checks before clearTimeout calls in colorful-fallback.tsx, edgeless.dialog.tsx, and local.dialog.tsx
- Fix event listener cleanup in unfolding.tsx
- Update tsconfig.jsx to use react-jsx transform

## Testing
- [x] Verified type safety improvements for React 19 compatibility
- [x] Ensured proper cleanup of event listeners and timeouts
- [x] Confirmed no unintended side effects from the changes

Link to Devin run: https://app.devin.ai/sessions/2e790f3ea0d84402837ec6c3c6f83e4c
This commit is contained in:
devin-ai-integration
2024-12-12 09:43:42 +00:00
parent dd39d049fe
commit e100d252b2
39 changed files with 496 additions and 368 deletions
@@ -51,7 +51,7 @@ export const AIOnboardingEdgeless = () => {
const generalAIOnboardingOpened = useLiveData(showAIOnboardingGeneral$);
const aiSubscription = useLiveData(subscriptionService.subscription.ai$);
const globalDialogService = useService(GlobalDialogService);
const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const mode = useLiveData(editorService.editor.mode$);
@@ -67,7 +67,9 @@ export const AIOnboardingEdgeless = () => {
if (generalAIOnboardingOpened) return;
if (notifyId) return;
if (mode !== 'edgeless') return;
clearTimeout(timeoutRef.current);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
// try to close local onboarding
notify.dismiss(localNotifyId$.value);
@@ -67,7 +67,7 @@ export const AIOnboardingLocal = () => {
const t = useI18n();
const authService = useService(AuthService);
const notifyId = useLiveData(localNotifyId$);
const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const loginStatus = useLiveData(authService.session.status$);
const notSignedIn = loginStatus !== 'authenticated';
@@ -75,7 +75,9 @@ export const AIOnboardingLocal = () => {
useEffect(() => {
if (!notSignedIn) return;
if (notifyId) return;
clearTimeout(timeoutRef.current);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
// try to close edgeless onboarding
notify.dismiss(edgelessNotifyId$.value);
@@ -5,12 +5,12 @@ import { OAuthProviderType } from '@affine/graphql';
import track from '@affine/track';
import { GithubIcon, GoogleDuotoneIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
import { type ReactElement, useCallback } from 'react';
import { type ReactElement, type SVGAttributes, useCallback } from 'react';
const OAuthProviderMap: Record<
OAuthProviderType,
{
icon: ReactElement;
icon: ReactElement<SVGAttributes<SVGElement>>;
}
> = {
[OAuthProviderType.Google]: {
@@ -43,7 +43,9 @@ export const EdgelessSwitch = ({
const prevStateRef = useRef<EdgelessSwitchState | null>(
article.initState ?? null
);
const enableScrollTimerRef = useRef<ReturnType<typeof setTimeout>>();
const enableScrollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(
null
);
const turnOffScalingRef = useRef<() => void>(() => {});
const [scrollable, setScrollable] = useState(false);
@@ -33,8 +33,8 @@ export const Unfolding = ({
const handler = () => {
onChanged?.(fold);
};
ref.current.addEventListener('transitionend', handler, { once: true });
return () => paper?.removeEventListener('transitionend', handler);
paper.addEventListener('transitionend', handler, { once: true });
return () => paper.removeEventListener('transitionend', handler);
}
return () => null;
@@ -1,4 +1,7 @@
import { InternalLottie } from '@affine/component/internal-lottie';
import {
type CustomLottieProps,
InternalLottie,
} from '@affine/component/internal-lottie';
import type { HTMLAttributes } from 'react';
import type React from 'react';
import { cloneElement, useState } from 'react';
@@ -10,7 +13,7 @@ type HoverAnimateControllerProps = {
active?: boolean;
hide?: boolean;
trash?: boolean;
children: React.ReactElement;
children: React.ReactElement<CustomLottieProps>;
} & HTMLAttributes<HTMLDivElement>;
const HoverAnimateController = ({
@@ -1,6 +1,6 @@
import type { Collection, Tag } from '@affine/env/filter';
import type { DocCollection, DocMeta } from '@blocksuite/affine/store';
import type { PropsWithChildren, ReactNode } from 'react';
import type { JSX, PropsWithChildren, ReactNode } from 'react';
import type { To } from 'react-router-dom';
export type ListItem = DocMeta | CollectionMeta | TagMeta;