From f87fdd2ea203ba39eaa777b819e62caad9350c7e Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 25 Aug 2026 09:40:14 +0300 Subject: [PATCH] Enable React Native strict TypeScript API (#11459) --- jest/jestSetup.js | 10 + oxlint-suppressions.json | 177 +----------------- src/Navigation.tsx | 4 +- src/Splash.tsx | 11 +- src/Splash.web.tsx | 4 +- .../components/RedirectOverlay.tsx | 1 - src/alf/fonts.ts | 7 +- src/alf/typography.tsx | 10 +- src/alf/util/dimensions.ts | 11 +- src/alf/util/flatten.ts | 19 +- src/alf/util/useColorModeTheme.ts | 5 +- src/analytics/utils.ts | 2 +- src/components/AltBadgeWithDialog.tsx | 6 +- src/components/BetaBadge.tsx | 3 +- src/components/Button.tsx | 5 +- src/components/Composer/index.tsx | 19 +- src/components/ContextMenu/index.tsx | 7 +- src/components/Dialog/index.tsx | 147 ++++++++------- src/components/Dialog/index.web.tsx | 5 +- src/components/FeedInterstitials.tsx | 4 +- src/components/FocusScope/index.tsx | 19 +- src/components/GlassView.tsx | 2 +- src/components/InterestTabs.tsx | 2 +- src/components/Layout/Header/index.tsx | 2 +- src/components/Lightbox/Lightbox.web.tsx | 3 - src/components/Lightbox/chrome/ImageMenu.tsx | 2 +- .../pager/ImageItem/ImageItem.ios.tsx | 4 +- src/components/Lightbox/pager/ImagePager.tsx | 9 +- src/components/Link.tsx | 4 +- src/components/Menu/index.web.tsx | 4 +- src/components/Menu/types.ts | 2 +- .../VideoEmbedInner/VideoEmbedInnerWeb.tsx | 2 +- src/components/Post/Translated/index.tsx | 2 +- .../PostControls/PostControlButton.tsx | 2 +- src/components/Pressable.tsx | 37 ++++ src/components/ProfileHoverCard/index.web.tsx | 3 +- src/components/ProgressGuide/FollowDialog.tsx | 8 +- src/components/RichText.tsx | 9 +- src/components/Select/index.tsx | 11 +- src/components/Select/index.web.tsx | 18 +- .../StarterPack/Main/ProfilesList.tsx | 7 - src/components/Tooltip/index.tsx | 9 +- src/components/Tooltip/index.web.tsx | 4 +- .../contacts/components/OTPInput.tsx | 4 +- .../contacts/screens/ViewMatches.tsx | 2 +- .../dialogs/SearchablePeopleList.tsx | 4 +- .../lists/UserAddRemoveListsDialog.tsx | 4 +- src/components/dialogs/nuxs/index.tsx | 2 +- src/components/dms/AddMembersFlow.tsx | 4 +- src/components/dms/InitiateChatFlow.tsx | 2 +- src/components/dms/ReactionsDialog.tsx | 2 +- .../dms/components/UserSearchInput.tsx | 3 +- src/components/forms/AutosizedTextarea.tsx | 6 +- .../forms/DateField/index.android.tsx | 1 - src/components/forms/DateField/index.web.tsx | 2 +- src/components/forms/SearchInput.tsx | 4 +- src/components/forms/TextField.tsx | 72 +++---- src/components/hooks/useOnKeyboard.ts | 4 +- src/components/icons/TEMPLATE.tsx | 2 +- src/components/images/Gallery/index.tsx | 12 +- src/components/moderation/BlockDialog.tsx | 2 +- .../moderation/ReportDialog/index.tsx | 2 +- src/features/gifPicker/GifPickerDialog.tsx | 2 +- .../gifPicker/components/GifPickerGrid.tsx | 2 +- .../gifPicker/components/GifPickerHeader.tsx | 2 +- src/features/liveEvents/preferences.ts | 2 +- src/lib/api/feed/merge.ts | 2 +- src/lib/batchedUpdates.ts | 6 +- src/lib/hooks/useDraggableScrollView.ts | 14 +- src/lib/hooks/useOTAUpdates.test.ts | 6 +- src/lib/hooks/useOTAUpdates.ts | 2 +- src/lib/jwt.ts | 2 +- src/lib/media/picker.shared.ts | 2 + src/lib/strings/embed-player.ts | 8 +- src/locale/deviceLocales.ts | 2 +- src/logger/__tests__/logger.test.ts | 6 +- src/platform/markBundleStartTime.ts | 2 +- src/screens/Hashtag.tsx | 1 - src/screens/Login/LoginForm.tsx | 4 +- src/screens/Messages/JoinRequests.tsx | 2 +- .../Messages/components/MessageComposer.tsx | 6 +- src/screens/Onboarding/Layout.tsx | 2 +- .../StepFinished/ValuePropositionPager.tsx | 3 +- .../StepProfile/PlaceholderCanvas.tsx | 1 - .../StepSuggestedAccounts/index.tsx | 4 +- src/screens/PostThread/index.tsx | 4 +- src/screens/Profile/KnownFollowers.tsx | 1 - src/screens/Search/Explore.tsx | 3 +- src/screens/Search/Shell.tsx | 2 +- .../AdvancedSearchDialog/ClearableInput.tsx | 2 +- .../components/AdvancedSearchDialog/index.tsx | 4 +- .../SearchAutocompleteInput/index.tsx | 2 +- src/screens/Settings/AppIconSettings/types.ts | 6 +- .../ActivityNotificationSettings.tsx | 2 +- .../components/DeleteAccountDialog.tsx | 2 +- .../Signup/StepCaptcha/CaptchaWebView.web.tsx | 3 - src/screens/Signup/StepInfo/index.tsx | 4 +- src/screens/StarterPack/StarterPackScreen.tsx | 14 +- src/screens/Topic.tsx | 1 - src/screens/VideoFeed/index.tsx | 8 +- src/state/cache/thread-mutes.tsx | 1 - src/state/persisted/schema.ts | 2 +- src/state/queries/nuxs/index.ts | 2 +- src/state/queries/usePostThread/utils.ts | 6 +- src/state/queries/usePostThread/views.ts | 4 +- src/storage/__tests__/index.test.ts | 2 +- src/view/com/auth/SplashScreen.tsx | 8 +- src/view/com/auth/SplashScreen.web.tsx | 1 - src/view/com/composer/drafts/DraftItem.tsx | 3 +- src/view/com/composer/photos/Gallery.tsx | 2 +- .../com/composer/text-input/TextInput.tsx | 10 +- .../com/composer/text-input/TextInput.web.tsx | 5 +- .../composer/text-input/web/Autocomplete.tsx | 4 +- .../videos/VideoTranscodeBackdrop.web.tsx | 4 +- src/view/com/feeds/ComposerPrompt.tsx | 1 - src/view/com/pager/DraggableScrollView.tsx | 2 +- src/view/com/pager/PagerWithHeader.tsx | 8 +- src/view/com/pager/PagerWithHeader.web.tsx | 6 +- src/view/com/pager/TabBar.web.tsx | 5 +- src/view/com/post-thread/PostQuotes.tsx | 1 - src/view/com/post-thread/PostRepostedBy.tsx | 1 - src/view/com/post/Post.tsx | 1 - src/view/com/profile/ProfileFollowers.tsx | 1 - src/view/com/profile/ProfileFollows.tsx | 1 - src/view/com/util/Alert.web.tsx | 4 +- src/view/com/util/EventStopper.tsx | 1 - src/view/com/util/Link.tsx | 20 +- src/view/com/util/List.tsx | 3 +- src/view/com/util/List.web.tsx | 11 +- src/view/com/util/PressableWithHover.tsx | 25 +-- src/view/com/util/Views.tsx | 12 +- src/view/com/util/Views.web.tsx | 10 +- src/view/com/util/WebAuxClickWrapper.tsx | 2 +- src/view/com/util/fab/FABInner.tsx | 2 +- src/view/com/util/forms/Button.tsx | 15 +- src/view/com/util/layouts/LoggedOutLayout.tsx | 1 - src/view/com/util/listNativeTag.ts | 2 +- src/view/com/util/text/Text.tsx | 16 +- src/view/icons/Logo.tsx | 4 +- src/view/icons/Logomark.tsx | 2 +- src/view/icons/Logotype.tsx | 2 +- src/view/screens/DebugMod.tsx | 4 +- src/view/screens/Home.tsx | 2 +- src/view/screens/Notifications.tsx | 2 +- src/view/screens/Profile.tsx | 2 +- src/view/screens/Storybook/Forms.tsx | 2 +- src/view/shell/Composer.web.tsx | 2 - src/view/shell/desktop/LeftNav.tsx | 2 +- tsconfig.json | 3 +- 149 files changed, 506 insertions(+), 623 deletions(-) create mode 100644 src/components/Pressable.tsx diff --git a/jest/jestSetup.js b/jest/jestSetup.js index 2a1d3f3293..44a2c985f5 100644 --- a/jest/jestSetup.js +++ b/jest/jestSetup.js @@ -56,6 +56,16 @@ jest.mock('expo-media-library', () => ({ __esModule: true, // this property makes it work default: jest.fn(), usePermissions: jest.fn(() => [true]), + requestPermissionsAsync: jest.fn().mockResolvedValue({granted: true}), + saveToLibraryAsync: jest.fn().mockResolvedValue(undefined), +})) + +jest.mock('expo-media-library/legacy', () => ({ + __esModule: true, + default: jest.fn(), + usePermissions: jest.fn(() => [true]), + requestPermissionsAsync: jest.fn().mockResolvedValue({granted: true}), + saveToLibraryAsync: jest.fn().mockResolvedValue(undefined), })) jest.mock('@bsky.app/expo-guess-language', () => ({ diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index e04604a747..1ef59ea20c 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -80,14 +80,6 @@ "count": 1 } }, - "src/alf/util/flatten.ts": { - "typescript/no-explicit-any": { - "count": 1 - }, - "typescript/no-unsafe-member-access": { - "count": 9 - } - }, "src/alf/util/systemUI.ts": { "typescript/no-floating-promises": { "count": 2 @@ -167,9 +159,6 @@ "typescript/no-explicit-any": { "count": 4 }, - "typescript/no-misused-promises": { - "count": 1 - }, "typescript/no-unsafe-call": { "count": 2 }, @@ -224,9 +213,6 @@ "src/components/Lists.tsx": { "typescript/no-explicit-any": { "count": 1 - }, - "typescript/no-misused-promises": { - "count": 1 } }, "src/components/Menu/types.ts": { @@ -261,9 +247,6 @@ "src/components/PostControls/BookmarkButton.tsx": { "typescript/no-explicit-any": { "count": 2 - }, - "typescript/no-misused-promises": { - "count": 1 } }, "src/components/PostControls/DiscoverDebug.tsx": { @@ -335,9 +318,6 @@ "typescript/no-floating-promises": { "count": 5 }, - "typescript/no-misused-promises": { - "count": 2 - }, "typescript/require-await": { "count": 4 } @@ -346,9 +326,6 @@ "typescript/no-floating-promises": { "count": 1 }, - "typescript/no-misused-promises": { - "count": 2 - }, "typescript/require-await": { "count": 1 } @@ -363,16 +340,6 @@ "count": 1 } }, - "src/components/ageAssurance/AgeAssuranceErrors.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, - "src/components/ageAssurance/AgeAssuranceInitDialog.tsx": { - "typescript/no-misused-promises": { - "count": 2 - } - }, "src/components/ageAssurance/AgeAssuranceRedirectDialog.tsx": { "typescript/require-await": { "count": 1 @@ -388,18 +355,10 @@ "count": 1 } }, - "src/components/contacts/screens/ViewMatches.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, "src/components/dialogs/DeviceLocationRequestDialog.tsx": { "typescript/no-explicit-any": { "count": 1 }, - "typescript/no-misused-promises": { - "count": 1 - }, "typescript/no-unsafe-member-access": { "count": 2 } @@ -409,26 +368,6 @@ "count": 1 } }, - "src/components/dialogs/EmailDialog/screens/Manage2FA/Disable.tsx": { - "typescript/no-misused-promises": { - "count": 3 - } - }, - "src/components/dialogs/EmailDialog/screens/Manage2FA/Enable.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, - "src/components/dialogs/EmailDialog/screens/Update.tsx": { - "typescript/no-misused-promises": { - "count": 3 - } - }, - "src/components/dialogs/EmailDialog/screens/Verify.tsx": { - "typescript/no-misused-promises": { - "count": 3 - } - }, "src/components/dialogs/LanguageSelectDialog.tsx": { "typescript/no-explicit-any": { "count": 1 @@ -447,7 +386,7 @@ "count": 2 }, "typescript/no-misused-promises": { - "count": 3 + "count": 1 }, "typescript/no-unsafe-member-access": { "count": 2 @@ -490,9 +429,6 @@ "src/components/dialogs/lists/CreateOrEditListDialog.tsx": { "typescript/no-explicit-any": { "count": 2 - }, - "typescript/no-misused-promises": { - "count": 1 } }, "src/components/dialogs/nuxs/FindContactsAnnouncement.tsx": { @@ -580,16 +516,6 @@ "count": 2 } }, - "src/components/intents/VerifyEmailIntentDialog.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, - "src/components/verification/VerificationCreatePrompt.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, "src/components/verification/VerificationRemovePrompt.tsx": { "typescript/no-misused-promises": { "count": 1 @@ -696,13 +622,10 @@ "count": 1 }, "typescript/no-misused-promises": { - "count": 3 + "count": 2 } }, "src/lib/hooks/usePermissions.ts": { - "typescript/no-misused-promises": { - "count": 1 - }, "typescript/no-unsafe-enum-comparison": { "count": 5 } @@ -845,15 +768,9 @@ }, "typescript/no-floating-promises": { "count": 1 - }, - "typescript/no-misused-promises": { - "count": 1 } }, "src/screens/E2E/SharedPreferencesTesterScreen.tsx": { - "typescript/no-misused-promises": { - "count": 6 - }, "typescript/require-await": { "count": 6 } @@ -863,22 +780,9 @@ "count": 1 } }, - "src/screens/Feeds/NoSavedFeedsOfAnyType.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, - "src/screens/Home/NoFeedsPinned.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, "src/screens/List/ListHiddenScreen.tsx": { "typescript/no-floating-promises": { "count": 3 - }, - "typescript/no-misused-promises": { - "count": 1 } }, "src/screens/Log.tsx": { @@ -894,7 +798,7 @@ "count": 1 }, "typescript/no-misused-promises": { - "count": 2 + "count": 1 }, "typescript/no-unsafe-member-access": { "count": 2 @@ -922,19 +826,6 @@ "src/screens/Onboarding/StepProfile/index.tsx": { "typescript/no-floating-promises": { "count": 2 - }, - "typescript/no-misused-promises": { - "count": 1 - } - }, - "src/screens/Onboarding/StepSuggestedStarterpacks/StarterPackCard.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, - "src/screens/Onboarding/StepSuggestedStarterpacks/index.tsx": { - "typescript/no-misused-promises": { - "count": 1 } }, "src/screens/PostThread/components/ThreadItemAnchorFollowButton.tsx": { @@ -962,18 +853,12 @@ "src/screens/Profile/Header/EditProfileDialog.tsx": { "typescript/no-explicit-any": { "count": 3 - }, - "typescript/no-misused-promises": { - "count": 1 } }, "src/screens/Profile/Header/ProfileHeaderLabeler.tsx": { "typescript/no-explicit-any": { "count": 2 }, - "typescript/no-misused-promises": { - "count": 1 - }, "typescript/no-unsafe-member-access": { "count": 3 } @@ -1004,11 +889,6 @@ "count": 1 } }, - "src/screens/ProfileList/components/Header.tsx": { - "typescript/no-misused-promises": { - "count": 3 - } - }, "src/screens/ProfileList/components/MoreOptionsMenu.tsx": { "typescript/no-floating-promises": { "count": 1 @@ -1027,11 +907,6 @@ "count": 1 } }, - "src/screens/SavedFeeds.tsx": { - "typescript/no-misused-promises": { - "count": 2 - } - }, "src/screens/Search/modules/ExploreSuggestedAccounts.tsx": { "typescript/no-floating-promises": { "count": 1 @@ -1093,9 +968,6 @@ "src/screens/Settings/components/ChangePasswordDialog.tsx": { "typescript/no-explicit-any": { "count": 2 - }, - "typescript/no-misused-promises": { - "count": 2 } }, "src/screens/Settings/components/CopyButton.tsx": { @@ -1107,9 +979,6 @@ "typescript/no-explicit-any": { "count": 1 }, - "typescript/no-misused-promises": { - "count": 1 - }, "typescript/no-unsafe-member-access": { "count": 1 } @@ -1119,11 +988,6 @@ "count": 2 } }, - "src/screens/Settings/components/DisableEmail2FADialog.tsx": { - "typescript/no-misused-promises": { - "count": 4 - } - }, "src/screens/Settings/components/OTAInfo.tsx": { "typescript/no-floating-promises": { "count": 1 @@ -1147,7 +1011,7 @@ "count": 1 }, "typescript/no-misused-promises": { - "count": 2 + "count": 1 }, "typescript/no-unsafe-call": { "count": 1 @@ -1168,9 +1032,6 @@ "typescript/no-floating-promises": { "count": 1 }, - "typescript/no-misused-promises": { - "count": 2 - }, "typescript/require-await": { "count": 1 } @@ -1474,9 +1335,6 @@ "src/view/com/composer/SelectMediaButton.tsx": { "typescript/no-floating-promises": { "count": 1 - }, - "typescript/no-misused-promises": { - "count": 1 } }, "src/view/com/composer/drafts/DraftsButton.tsx": { @@ -1500,11 +1358,6 @@ "count": 2 } }, - "src/view/com/composer/photos/EditImageDialog.web.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, "src/view/com/composer/photos/Gallery.tsx": { "typescript/no-floating-promises": { "count": 1 @@ -1513,9 +1366,6 @@ "src/view/com/composer/photos/OpenCameraBtn.tsx": { "typescript/no-explicit-any": { "count": 1 - }, - "typescript/no-misused-promises": { - "count": 1 } }, "src/view/com/composer/text-input/TextInput.web.tsx": { @@ -1560,7 +1410,7 @@ "count": 2 }, "typescript/no-misused-promises": { - "count": 3 + "count": 2 }, "typescript/no-unsafe-member-access": { "count": 6 @@ -1598,12 +1448,6 @@ "src/view/com/pager/TabBar.web.tsx": { "typescript/no-explicit-any": { "count": 1 - }, - "typescript/no-unsafe-call": { - "count": 2 - }, - "typescript/no-unsafe-member-access": { - "count": 4 } }, "src/view/com/post-thread/PostLikedBy.tsx": { @@ -1627,9 +1471,6 @@ "src/view/com/posts/FeedShutdownMsg.tsx": { "typescript/no-explicit-any": { "count": 2 - }, - "typescript/no-misused-promises": { - "count": 2 } }, "src/view/com/posts/PostFeedErrorMessage.tsx": { @@ -1693,9 +1534,6 @@ "src/view/com/util/Views.tsx": { "react/display-name": { "count": 1 - }, - "typescript/no-explicit-any": { - "count": 1 } }, "src/view/com/util/Views.web.tsx": { @@ -1703,11 +1541,6 @@ "count": 1 } }, - "src/view/com/util/forms/Button.tsx": { - "typescript/no-misused-promises": { - "count": 1 - } - }, "src/view/screens/Feeds.tsx": { "typescript/no-floating-promises": { "count": 3 diff --git a/src/Navigation.tsx b/src/Navigation.tsx index ec3072f46d..daa76629d1 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -1006,7 +1006,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { ax.metric('init', { initMs: Math.round( - // @ts-ignore Emitted by Metro in the bundle prelude + // @ts-expect-error Emitted by Metro in the bundle prelude performance.now() - global.__BUNDLE_START_TIME__, ), }) @@ -1081,7 +1081,7 @@ function navigate( } navigationRef.addListener('state', handler) - // @ts-ignore I don't know what would make typescript happy but I have a life -prf + // @ts-expect-error I don't know what would make typescript happy but I have a life -prf navigationRef.navigate(name, params) }), timeout(1e3), diff --git a/src/Splash.tsx b/src/Splash.tsx index 4a4dea831a..76c9c7de82 100644 --- a/src/Splash.tsx +++ b/src/Splash.tsx @@ -20,14 +20,15 @@ import * as SplashScreen from 'expo-splash-screen' import {Logotype} from '#/view/icons/Logotype' import {atoms as a} from '#/alf' -// @ts-ignore +// @ts-expect-error import splashImagePointer from '../assets/splash/splash.png' -// @ts-ignore +// @ts-expect-error import darkSplashImagePointer from '../assets/splash/splash-dark.png' -const splashImageUri = RNImage.resolveAssetSource(splashImagePointer).uri + +const splashImageUri = RNImage.resolveAssetSource(splashImagePointer)!.uri const darkSplashImageUri = RNImage.resolveAssetSource( darkSplashImagePointer, -).uri +)!.uri export const Logo = forwardRef(function LogoImpl(props: SvgProps, ref) { const width = 1000 @@ -35,7 +36,7 @@ export const Logo = forwardRef(function LogoImpl(props: SvgProps, ref) { return ( diff --git a/src/Splash.web.tsx b/src/Splash.web.tsx index ff86c9a770..44e14baa33 100644 --- a/src/Splash.web.tsx +++ b/src/Splash.web.tsx @@ -7,7 +7,7 @@ import {useEffect, useRef, useState} from 'react' import Svg, {Path} from 'react-native-svg' -import {atoms as a, flatten} from '#/alf' +import {atoms as a, flattenToCSS} from '#/alf' const size = 100 const ratio = 57 / 64 @@ -72,7 +72,7 @@ export function Splash({ {!isAnimationComplete && (
, ) { - const s = flatten(styles) ?? {} + const s: MutableTextStyle = {...flatten(styles)} // should always be defined on these components s.fontSize = (s.fontSize || atoms.text_md.fontSize) * fontScale diff --git a/src/alf/util/dimensions.ts b/src/alf/util/dimensions.ts index 31af2ffa99..e5d60055c1 100644 --- a/src/alf/util/dimensions.ts +++ b/src/alf/util/dimensions.ts @@ -1,5 +1,5 @@ import {useEffect, useState} from 'react' -import {Dimensions} from 'react-native' +import {Dimensions, type DimensionsPayload} from 'react-native' /** * Same as `useWindowDimensions().fontScale`, but avoids rerendering @@ -9,9 +9,12 @@ export function useNativeFontScale() { const [fontScale, setFontScale] = useState(Dimensions.get('window').fontScale) useEffect(() => { - const sub = Dimensions.addEventListener('change', evt => { - setFontScale(evt.window.fontScale) - }) + const sub = Dimensions.addEventListener( + 'change', + (evt: DimensionsPayload) => { + if (evt.window) setFontScale(evt.window.fontScale) + }, + ) return () => sub.remove() }, []) diff --git a/src/alf/util/flatten.ts b/src/alf/util/flatten.ts index 6d49ce6e51..b218c8879e 100644 --- a/src/alf/util/flatten.ts +++ b/src/alf/util/flatten.ts @@ -1,6 +1,19 @@ -import {type DimensionValue, StyleSheet} from 'react-native' +import {type DimensionValue, type StyleProp, StyleSheet} from 'react-native' -export const flatten = StyleSheet.flatten +export function flatten( + style?: StyleProp, +): T extends (infer U)[] ? U : T { + return (StyleSheet.flatten( + style as unknown as Parameters[0], + ) ?? {}) as T extends (infer U)[] ? U : T +} + +/** Flatten React Native styles passed directly to a web-only DOM component. */ +export function flattenToCSS(style: unknown): React.CSSProperties { + return (StyleSheet.flatten( + style as Parameters[0], + ) ?? {}) as React.CSSProperties +} /** * Coerce a style value to a number. Padding values are typed as @@ -28,7 +41,7 @@ interface PaddingStyle { * non-numeric `DimensionValue` (e.g. percentages) is treated as 0. */ export function extractPadding(style: PaddingStyle | PaddingStyle[]) { - const s = flatten(style as any) ?? {} + const s = flatten(style) const base = num(s.padding) return { paddingTop: num(s.paddingTop) || num(s.paddingVertical) || base, diff --git a/src/alf/util/useColorModeTheme.ts b/src/alf/util/useColorModeTheme.ts index 7cb9b12723..dfcc57b53a 100644 --- a/src/alf/util/useColorModeTheme.ts +++ b/src/alf/util/useColorModeTheme.ts @@ -24,7 +24,7 @@ export function useThemeName(): ThemeName { } function getThemeName( - colorScheme: ColorSchemeName, + colorScheme: ColorSchemeName | null | undefined, colorMode: 'system' | 'light' | 'dark', darkTheme?: ThemeName, ) { @@ -39,11 +39,8 @@ function getThemeName( } function updateDocument(theme: ThemeName) { - // @ts-ignore web only if (IS_WEB && typeof window !== 'undefined') { - // @ts-ignore web only const html = window.document.documentElement - // @ts-ignore web only const meta = window.document.querySelector('meta[name="theme-color"]') // remove any other color mode classes diff --git a/src/analytics/utils.ts b/src/analytics/utils.ts index 95d2efb589..b20b3719c9 100644 --- a/src/analytics/utils.ts +++ b/src/analytics/utils.ts @@ -14,7 +14,7 @@ import { export function useMeta(metadata?: MergeableMetadata) { const m = useMemo(() => metadata, [metadata]) if (!m) return - // @ts-ignore + // @ts-expect-error m.__meta = true return m } diff --git a/src/components/AltBadgeWithDialog.tsx b/src/components/AltBadgeWithDialog.tsx index 07cf9544f0..4ae078e12b 100644 --- a/src/components/AltBadgeWithDialog.tsx +++ b/src/components/AltBadgeWithDialog.tsx @@ -1,9 +1,9 @@ -import {Pressable} from 'react-native' import {Trans, useLingui} from '@lingui/react/macro' import {HITSLOP_20} from '#/lib/constants' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {atoms as a, useTheme} from '#/alf' +import {Pressable} from '#/components/Pressable' import * as Prompt from '#/components/Prompt' import {Text} from '#/components/Typography' @@ -49,7 +49,7 @@ export function AltBadgeWithDialog({ accessibilityHint="" hitSlop={HITSLOP_20} onPress={control.open} - style={s => [ + style={({pressed, hovered}) => [ a.justify_center, a.rounded_sm, a.p_xs, @@ -62,7 +62,7 @@ export function AltBadgeWithDialog({ opacity: 0.8, }, pos, - s.hovered || s.pressed + hovered || pressed ? [ { opacity: 1, diff --git a/src/components/BetaBadge.tsx b/src/components/BetaBadge.tsx index 115f8d0046..c8c0ddd2bf 100644 --- a/src/components/BetaBadge.tsx +++ b/src/components/BetaBadge.tsx @@ -1,11 +1,12 @@ import {useState} from 'react' -import {type Insets, Pressable, View} from 'react-native' +import {type Insets, View} from 'react-native' import {Trans, useLingui} from '@lingui/react/macro' import {usePreferencesQuery} from '#/state/queries/preferences' import {useSession} from '#/state/session' import {atoms as a, useTheme} from '#/alf' import {Beaker_Stroke2_Corner2_Rounded as BeakerIcon} from '#/components/icons/Beaker' +import {Pressable} from '#/components/Pressable' import * as Tooltip from '#/components/Tooltip' import type * as bsky from '#/types/bsky' diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 54332fbc07..2c1f7ff3f8 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -101,6 +101,8 @@ export type ButtonProps = Pick< | 'onPressOut' | 'onFocus' | 'onBlur' + | 'onAccessibilityAction' + | 'onAccessibilityEscape' > & AccessibilityProps & VariantProps & { @@ -131,7 +133,7 @@ export function useButtonContext() { return useContext(Context) } -export const Button = forwardRef( +export const Button = forwardRef, ButtonProps>( ( { children, @@ -575,7 +577,6 @@ export const Button = forwardRef( role="button" accessibilityHint={undefined} // optional {...rest} - // @ts-ignore - this will always be a pressable ref={ref} aria-label={label} aria-pressed={state.pressed} diff --git a/src/components/Composer/index.tsx b/src/components/Composer/index.tsx index f4a0302ff7..b2266db5a1 100644 --- a/src/components/Composer/index.tsx +++ b/src/components/Composer/index.tsx @@ -39,6 +39,9 @@ import { import {Span, Text} from '#/components/Typography' import {IS_IOS, IS_WEB, IS_WEB_TOUCH_DEVICE} from '#/env' +type TextInputInstance = React.ComponentRef +type ViewInstance = React.ComponentRef + export type SubmitRequest = | { platform: 'web' @@ -60,7 +63,7 @@ export type ComposerInternalApi = { input?: ReturnType['input'] clear: () => void insert(text: string): void - setAutocompleteAnchor: (node: View | null) => void + setAutocompleteAnchor: (node: ViewInstance | null) => void } export function useComposerInternalApiRef() { @@ -82,7 +85,7 @@ export type ComposerProps = Omit< | 'onSubmitEditing' > & { label: string - ref?: React.RefObject + ref?: React.RefObject internalApiRef?: React.Ref outerStyle?: ViewStyleProp['style'] contentTextStyle?: TextStyleProp['style'] @@ -139,6 +142,11 @@ export function Composer({ placement: autocompletePlacement, dynamicWidth: IS_WEB, }) + const inputRef = mergeRefs([ + ref, + tapper.inputProps.ref as React.Ref, + sift.targetProps.ref as React.Ref, + ]) /* * Active facet state for controlling the visibility of the Autocomplete. @@ -306,7 +314,7 @@ export function Composer({ style={[a.absolute, a.inset_0, a.z_10, {overflow: 'hidden'}]} ref={node => { if (IS_WEB && node) { - // @ts-ignore web only a11y + // @ts-expect-error web only a11y node.setAttribute('inert', '') } }}> @@ -345,7 +353,7 @@ export function Composer({ {...rest} {...tapper.inputProps} {...sift.targetProps} - ref={mergeRefs([ref, tapper.inputProps.ref, sift.targetProps.ref])} + ref={inputRef} rawValue={tapper.state.text} onBlur={e => { rest.onBlur?.(e) @@ -359,11 +367,10 @@ export function Composer({ inputScrollSharedValue.value = e.nativeEvent.contentOffset.y } }} - // @ts-ignore web only + // @ts-expect-error web only onCompositionStart={() => { isComposing.current = true }} - // @ts-ignore web only onCompositionEnd={() => { isComposing.current = false }} diff --git a/src/components/ContextMenu/index.tsx b/src/components/ContextMenu/index.tsx index d09e0e60ec..95492a385a 100644 --- a/src/components/ContextMenu/index.tsx +++ b/src/components/ContextMenu/index.tsx @@ -244,7 +244,7 @@ export function Trigger({ const context = useContextMenuContext() const playHaptic = useHaptics() const insets = useSafeAreaInsets() - const ref = useRef(null) + const ref = useRef>(null) const isFocused = useIsFocused() const [image, setImage] = useState(null) const [pendingMeasurement, setPendingMeasurement] = useState<{ @@ -971,7 +971,10 @@ export function Divider() { ) } -function measureView(view: View | null, insets: EdgeInsets) { +function measureView( + view: React.ComponentRef | null, + insets: EdgeInsets, +) { if (!view) return Promise.resolve(null) return new Promise(resolve => { view?.measureInWindow((x, y, width, height) => diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx index 4f6984a4a0..5cfa2d1c10 100644 --- a/src/components/Dialog/index.tsx +++ b/src/components/Dialog/index.tsx @@ -8,7 +8,7 @@ import { } from 'react' import { Keyboard, - type KeyboardEventListener, + type KeyboardEvent, type LayoutChangeEvent, type NativeScrollEvent, type NativeSyntheticEvent, @@ -203,80 +203,85 @@ export function Inner(props: DialogInnerProps) { return } -export const ScrollableInner = forwardRef( - function ScrollableInner( - {children, contentContainerStyle, header, footer, style, ...props}, - ref, - ) { - const {nativeSnapPoint, disableDrag, setDisableDrag, isHeightConstrained} = - useDialogContext() - const isAtMaxSnapPoint = nativeSnapPoint === BottomSheetSnapPoint.Full - const insets = useSafeAreaInsets() - const [keyboardHeight, setKeyboardHeight] = useState(() => - IS_ANDROID ? (Keyboard.metrics()?.height ?? 0) : 0, - ) +export function ScrollableInner({ + ref, + children, + contentContainerStyle, + header, + footer, + style, + ...props +}: DialogInnerProps & { + ref?: React.Ref> +}) { + const {nativeSnapPoint, disableDrag, setDisableDrag, isHeightConstrained} = + useDialogContext() + const isAtMaxSnapPoint = nativeSnapPoint === BottomSheetSnapPoint.Full + const insets = useSafeAreaInsets() + const [keyboardHeight, setKeyboardHeight] = useState(() => + IS_ANDROID ? (Keyboard.metrics()?.height ?? 0) : 0, + ) - const keyboardEventHandler = useCallback(e => { - setKeyboardHeight(e.endCoordinates.height) - }, []) - useOnKeyboard('keyboardDidShow', keyboardEventHandler) - useOnKeyboard('keyboardDidHide', keyboardEventHandler) + const keyboardEventHandler = useCallback((e: KeyboardEvent) => { + setKeyboardHeight(e.endCoordinates.height) + }, []) + useOnKeyboard('keyboardDidShow', keyboardEventHandler) + useOnKeyboard('keyboardDidHide', keyboardEventHandler) - const onScroll = (e: NativeSyntheticEvent) => { - if (!IS_ANDROID) { - return - } - const {contentOffset} = e.nativeEvent - if (contentOffset.y > 0 && !disableDrag) { - setDisableDrag(true) - } else if (contentOffset.y <= 1 && disableDrag) { - setDisableDrag(false) - } + const onScroll = (e: NativeSyntheticEvent) => { + if (!IS_ANDROID) { + return } + const {contentOffset} = e.nativeEvent + if (contentOffset.y > 0 && !disableDrag) { + setDisableDrag(true) + } else if (contentOffset.y <= 1 && disableDrag) { + setDisableDrag(false) + } + } - return ( - <> - - {header} - {children} - - {footer} - - ) - }, -) + return ( + <> + + {header} + {children} + + {footer} + + ) +} export const InnerFlatList = forwardRef< ListMethods, diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx index c26a71825b..e867acd60a 100644 --- a/src/components/Dialog/index.web.tsx +++ b/src/components/Dialog/index.web.tsx @@ -193,7 +193,6 @@ export function Inner({ aria-label={label} aria-labelledby={accessibilityLabelledBy} aria-describedby={accessibilityDescribedBy} - // @ts-expect-error web only -prf onClick={stopPropagation} onStartShouldSetResponder={_ => true} onTouchEnd={stopPropagation} @@ -239,7 +238,9 @@ export function Inner({ export function ScrollableInner({ ref: _ref, ...props -}: DialogInnerProps & {ref?: React.Ref}) { +}: DialogInnerProps & { + ref?: React.Ref> +}) { return } diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx index aa77a7b36b..59bbe85752 100644 --- a/src/components/FeedInterstitials.tsx +++ b/src/components/FeedInterstitials.tsx @@ -217,7 +217,7 @@ export function ProfileGrid({ // Track seen profiles const seenProfilesRef = useRef>(new Set()) - const containerRef = useRef(null) + const containerRef = useRef>(null) const hasTrackedRef = useRef(false) const logContext: Metrics['suggestedUser:seen']['logContext'] = isFeedContext ? 'DiscoverInterstitial' @@ -276,7 +276,7 @@ export function ProfileGrid({ }, {threshold: 0.5}, ) - // @ts-ignore - web only + // @ts-expect-error - web only observer.observe(node) return () => observer.disconnect() } else { diff --git a/src/components/FocusScope/index.tsx b/src/components/FocusScope/index.tsx index 48e013500e..598c3cd6ca 100644 --- a/src/components/FocusScope/index.tsx +++ b/src/components/FocusScope/index.tsx @@ -41,7 +41,7 @@ export function FocusScope({children}: {children: React.ReactNode}) { */ function FocusTrap({children}: {children: React.ReactNode}) { const {_} = useLingui() - const child = useRef(null) + const child = useRef>(null) /* * Here we add a ref to the first child of this component. This currently @@ -66,13 +66,16 @@ function FocusTrap({children}: {children: React.ReactNode}) { }) }, [children]) - const focusNode = useCallback((ref: View | null) => { - if (!ref) return - const node = findNodeHandle(ref) - if (node) { - AccessibilityInfo.setAccessibilityFocus(node) - } - }, []) + const focusNode = useCallback( + (ref: React.ComponentRef | null) => { + if (!ref) return + const node = findNodeHandle(ref) + if (node) { + AccessibilityInfo.setAccessibilityFocus(node) + } + }, + [], + ) useEffect(() => { setTimeout(() => { diff --git a/src/components/GlassView.tsx b/src/components/GlassView.tsx index cac5bd268d..ddbcc07d80 100644 --- a/src/components/GlassView.tsx +++ b/src/components/GlassView.tsx @@ -20,7 +20,7 @@ export const IS_GLASS_AVAILABLE = */ export const GlassView = IS_GLASS_AVAILABLE ? InnerGlassView : FallbackView -export type GlassViewProps = ExpoGlassViewProps & { +export type GlassViewProps = Omit & { fallbackStyle?: StyleProp } diff --git a/src/components/InterestTabs.tsx b/src/components/InterestTabs.tsx index 5df1fb62e3..5a99a36992 100644 --- a/src/components/InterestTabs.tsx +++ b/src/components/InterestTabs.tsx @@ -47,7 +47,7 @@ export function InterestTabs({ }) { const t = useTheme() const {_} = useLingui() - const listRef = useRef(null) + const listRef = useRef>(null) const [totalWidth, setTotalWidth] = useState(0) const [scrollX, setScrollX] = useState(0) const [contentWidth, setContentWidth] = useState(0) diff --git a/src/components/Layout/Header/index.tsx b/src/components/Layout/Header/index.tsx index a0bd45c8fc..c70cebd703 100644 --- a/src/components/Layout/Header/index.tsx +++ b/src/components/Layout/Header/index.tsx @@ -42,7 +42,7 @@ export function Outer({ }: { children: React.ReactNode noBottomBorder?: boolean - headerRef?: React.RefObject + headerRef?: React.RefObject | null> sticky?: boolean }) { const t = useTheme() diff --git a/src/components/Lightbox/Lightbox.web.tsx b/src/components/Lightbox/Lightbox.web.tsx index 0222aa3156..cccbe4f851 100644 --- a/src/components/Lightbox/Lightbox.web.tsx +++ b/src/components/Lightbox/Lightbox.web.tsx @@ -446,9 +446,7 @@ function LightboxGalleryItem({ const styles = StyleSheet.create({ avi: { - // @ts-ignore web-only maxWidth: `calc(min(400px, 100vw))`, - // @ts-ignore web-only maxHeight: `calc(min(400px, 100vh))`, padding: 16, boxSizing: 'border-box', @@ -458,7 +456,6 @@ const styles = StyleSheet.create({ // column via ScrollView's default flexGrow. flexGrow: 0, flexShrink: 0, - // @ts-ignore web-only -sfn maxHeight: '50vh', }, menuBtn: { diff --git a/src/components/Lightbox/chrome/ImageMenu.tsx b/src/components/Lightbox/chrome/ImageMenu.tsx index 9088b9ded9..14ce100c0f 100644 --- a/src/components/Lightbox/chrome/ImageMenu.tsx +++ b/src/components/Lightbox/chrome/ImageMenu.tsx @@ -35,7 +35,7 @@ const TIMING_OUT = {duration: 150} export function ImageMenu({onPressShare, onPressSave}: Props) { const {t: l} = useLingui() - const triggerRef = useRef(null) + const triggerRef = useRef>(null) const [isMounted, setIsMounted] = useState(false) const [anchor, setAnchor] = useState(null) const progress = useSharedValue(0) diff --git a/src/components/Lightbox/pager/ImageItem/ImageItem.ios.tsx b/src/components/Lightbox/pager/ImageItem/ImageItem.ios.tsx index 9cf088f864..c4a842a74e 100644 --- a/src/components/Lightbox/pager/ImageItem/ImageItem.ios.tsx +++ b/src/components/Lightbox/pager/ImageItem/ImageItem.ios.tsx @@ -82,7 +82,7 @@ const ImageItem = ({ const scrollHandler = useAnimatedScrollHandler({ onScroll(e) { 'worklet' - const nextIsScaled = e.zoomScale > 1 + const nextIsScaled = (e.zoomScale ?? 1) > 1 if (scaled !== nextIsScaled) { scheduleOnRN(handleZoom, nextIsScaled) } @@ -109,7 +109,6 @@ const ImageItem = ({ height: number }) { const scrollResponderRef = scrollViewRef?.current?.getScrollResponder() - // @ts-ignore scrollResponderRef?.scrollResponderZoomTo({ ...nextZoomRect, // This rect is in screen coordinates animated: true, @@ -218,7 +217,6 @@ const ImageItem = ({ return ( { + onPageSelected={(e: PagerViewOnPageSelectedEvent) => { const next = e.nativeEvent.position setImageIndex(prev => { if (metricsContext && prev !== next) { @@ -401,7 +404,7 @@ function ImageView({ }) setIsScaled(false) }} - onPageScrollStateChanged={e => { + onPageScrollStateChanged={(e: PageScrollStateChangedNativeEvent) => { setIsDragging(e.nativeEvent.pageScrollState !== 'idle') }} overdrag={true} diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 6d60edb615..8e5891f3b2 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -478,7 +478,7 @@ export function InlineLinkText({ onIn: onInteract, onOut: onInteractOut, } = useInteractionState() - const flattenedStyle = flatten(style) || {} + const flattenedStyle = flatten(style) return ( void + onKeyDown: PressableProps['onKeyDown'] /** * Radix provides this, but we override on web to use `onPress` instead, * which is less sensitive while scrolling. diff --git a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx index d20cc0d90a..947d4ac3d6 100644 --- a/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx +++ b/src/components/Post/Embed/VideoEmbed/VideoEmbedInner/VideoEmbedInnerWeb.tsx @@ -149,7 +149,7 @@ function canPlayBskyVideoCodecs(): boolean { type CachedPromise = Promise & {value: undefined | T} const promiseForHls = import( - // @ts-ignore + // @ts-expect-error 'hls.js/dist/hls.min' // oxlint-disable-next-line typescript/no-unsafe-member-access ).then(mod => mod.default) as CachedPromise diff --git a/src/components/Post/Translated/index.tsx b/src/components/Post/Translated/index.tsx index 849222df03..6d44e9ce75 100644 --- a/src/components/Post/Translated/index.tsx +++ b/src/components/Post/Translated/index.tsx @@ -267,7 +267,7 @@ function TranslationResult({ ? codeToLanguageName(resultSourceLanguage, i18n.locale) : undefined - const flattenedStyle = flatten(postTextStyle) ?? {} + const flattenedStyle = flatten(postTextStyle) const fontSize = flattenedStyle.fontSize return ( diff --git a/src/components/PostControls/PostControlButton.tsx b/src/components/PostControls/PostControlButton.tsx index 3ea85e2811..e65a931f7d 100644 --- a/src/components/PostControls/PostControlButton.tsx +++ b/src/components/PostControls/PostControlButton.tsx @@ -27,7 +27,7 @@ export function PostControlButton({ activeColor, ...props }: Omit & { - ref?: React.Ref + ref?: React.Ref> active?: boolean big?: boolean color?: string diff --git a/src/components/Pressable.tsx b/src/components/Pressable.tsx new file mode 100644 index 0000000000..5fc96e3047 --- /dev/null +++ b/src/components/Pressable.tsx @@ -0,0 +1,37 @@ +import { + Pressable as NativePressable, + type PressableStateCallbackType as NativePressableStateCallbackType, + type StyleProp, + type ViewStyle, +} from 'react-native' + +export interface PressableStateCallbackType extends NativePressableStateCallbackType { + /** Provided by react-native-web. */ + readonly focused?: boolean + /** Provided by react-native-web. */ + readonly hovered?: boolean +} + +export type PressableProps = Omit< + React.ComponentProps, + 'children' | 'style' +> & { + children?: + React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode) + style?: + | StyleProp + | ((state: PressableStateCallbackType) => StyleProp) +} + +/** + * React Native Pressable with react-native-web's callback state represented in + * its types. The web-only fields are optional because native does not provide + * them at runtime. + */ +export function Pressable({children, style, ...props}: PressableProps) { + return ( + + {children} + + ) +} diff --git a/src/components/ProfileHoverCard/index.web.tsx b/src/components/ProfileHoverCard/index.web.tsx index ccc48b8951..a5f47be28b 100644 --- a/src/components/ProfileHoverCard/index.web.tsx +++ b/src/components/ProfileHoverCard/index.web.tsx @@ -318,11 +318,10 @@ export function ProfileHoverCardInner(props: ProfileHoverCardProps) { return ( {props.children} diff --git a/src/components/ProgressGuide/FollowDialog.tsx b/src/components/ProgressGuide/FollowDialog.tsx index d7fa9c1fab..1ab2668ae5 100644 --- a/src/components/ProgressGuide/FollowDialog.tsx +++ b/src/components/ProgressGuide/FollowDialog.tsx @@ -1,5 +1,5 @@ import {memo, useCallback, useEffect, useMemo, useRef, useState} from 'react' -import {TextInput, View, type ViewToken} from 'react-native' +import {type ListViewToken as ViewToken, TextInput, View} from 'react-native' import {type ModerationOpts} from '@bsky/sdk/moderation' import {Trans, useLingui} from '@lingui/react/macro' @@ -139,7 +139,7 @@ function DialogInner({guide}: {guide?: Follow10ProgressGuide}) { const [searchText, setSearchText] = useState(lastSearchText) const moderationOpts = useModerationOpts() const listRef = useRef(null) - const inputRef = useRef(null) + const inputRef = useRef>(null) const [headerHeight, setHeaderHeight] = useState(0) const {currentAccount} = useSession() @@ -374,7 +374,7 @@ let Header = ({ interestsDisplayNames, }: { guide?: Follow10ProgressGuide - inputRef: React.RefObject + inputRef: React.RefObject | null> listRef: React.RefObject onSelectTab: (v: string) => void searchText: string @@ -679,7 +679,7 @@ function SearchInput({ }: { onChangeText: (text: string) => void onEscape: () => void - inputRef: React.RefObject + inputRef: React.RefObject | null> defaultValue: string }) { const t = useTheme() diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 0266dae0ce..1fa020f2a9 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -93,7 +93,7 @@ export function RichText({ if (!facets?.length) { if (isOnlyEmoji(text)) { - const flattenedStyle = flatten(style) ?? {} + const flattenedStyle = flatten(style) const fontSize = (flattenedStyle.fontSize ?? a.text_sm.fontSize) * emojiMultiplier return ( @@ -104,7 +104,6 @@ export function RichText({ style={[plainStyles, {fontSize}, suffixStyles]} onLayout={onLayout} onTextLayout={onTextLayout} - // @ts-ignore web only -prf dataSet={WORD_WRAP}> {text} {suffix ? ' ' : null} @@ -121,7 +120,6 @@ export function RichText({ numberOfLines={numberOfLines} onLayout={onLayout} onTextLayout={onTextLayout} - // @ts-ignore web only -prf dataSet={WORD_WRAP}> {text} {suffix ? ' ' : null} @@ -150,7 +148,7 @@ export function RichText({ selectable={selectable} to={`/profile/${mention.did}`} style={interactiveStyles} - // @ts-ignore TODO + // @ts-expect-error TODO dataSet={WORD_WRAP} shouldProxy={shouldProxyLinks} onPress={onLinkPress}> @@ -169,7 +167,7 @@ export function RichText({ key={key} to={link.uri} style={interactiveStyles} - // @ts-ignore TODO + // @ts-expect-error TODO dataSet={WORD_WRAP} shareOnLongPress shouldProxy={shouldProxyLinks} @@ -209,7 +207,6 @@ export function RichText({ numberOfLines={numberOfLines} onLayout={onLayout} onTextLayout={onTextLayout} - // @ts-ignore web only -prf dataSet={WORD_WRAP}> {els} {suffix ? ' ' : null} diff --git a/src/components/Select/index.tsx b/src/components/Select/index.tsx index a7dd70d8e7..af5849346b 100644 --- a/src/components/Select/index.tsx +++ b/src/components/Select/index.tsx @@ -100,7 +100,16 @@ export function Trigger({children, hitSlop, label}: TriggerProps) { } else { return (