Add moduleSuffixes to tsconfig (#11146)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import {type ComposerImage} from '#/state/gallery'
|
||||
import type * as Dialog from '#/components/Dialog'
|
||||
|
||||
export type EditImageDialogProps = {
|
||||
control: Dialog.DialogOuterProps['control']
|
||||
image?: ComposerImage
|
||||
onChange: (next: ComposerImage) => void
|
||||
aspectRatio?: number
|
||||
circularCrop?: boolean
|
||||
}
|
||||
@@ -1,13 +1,6 @@
|
||||
import {type ComposerImage} from '#/state/gallery'
|
||||
import type * as Dialog from '#/components/Dialog'
|
||||
import {type EditImageDialogProps} from './EditImageDialog.shared'
|
||||
|
||||
export type EditImageDialogProps = {
|
||||
control: Dialog.DialogOuterProps['control']
|
||||
image?: ComposerImage
|
||||
onChange: (next: ComposerImage) => void
|
||||
aspectRatio?: number
|
||||
circularCrop?: boolean
|
||||
}
|
||||
export type {EditImageDialogProps} from './EditImageDialog.shared'
|
||||
|
||||
export const EditImageDialog = ({}: EditImageDialogProps): React.ReactNode => {
|
||||
return null
|
||||
|
||||
@@ -16,7 +16,7 @@ import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {type EditImageDialogProps} from './EditImageDialog'
|
||||
import {type EditImageDialogProps} from './EditImageDialog.shared'
|
||||
|
||||
export function EditImageDialog(props: EditImageDialogProps) {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import {type ComposerImage} from '#/state/gallery'
|
||||
|
||||
export type OpenCameraBtnProps = {
|
||||
disabled?: boolean
|
||||
onAdd: (next: ComposerImage[]) => void
|
||||
}
|
||||
@@ -6,18 +6,14 @@ import {useLingui} from '@lingui/react'
|
||||
import {useCameraPermission} from '#/lib/hooks/usePermissions'
|
||||
import {openCamera} from '#/lib/media/picker'
|
||||
import {logger} from '#/logger'
|
||||
import {type ComposerImage, createComposerImage} from '#/state/gallery'
|
||||
import {createComposerImage} from '#/state/gallery'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Button} from '#/components/Button'
|
||||
import {Camera_Stroke2_Corner0_Rounded as Camera} from '#/components/icons/Camera'
|
||||
import {IS_NATIVE, IS_WEB_MOBILE} from '#/env'
|
||||
import {type OpenCameraBtnProps} from './OpenCameraBtn.shared'
|
||||
|
||||
type Props = {
|
||||
disabled?: boolean
|
||||
onAdd: (next: ComposerImage[]) => void
|
||||
}
|
||||
|
||||
export function OpenCameraBtn({disabled, onAdd}: Props) {
|
||||
export function OpenCameraBtn({disabled, onAdd}: OpenCameraBtnProps) {
|
||||
const {_} = useLingui()
|
||||
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
||||
const [mediaPermissionRes, requestMediaPermission] =
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export function OpenCameraBtn() {
|
||||
import {type OpenCameraBtnProps} from './OpenCameraBtn.shared'
|
||||
|
||||
export function OpenCameraBtn(_props: OpenCameraBtnProps) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export function SubtitleFilePicker() {
|
||||
import {type SubtitleFilePickerProps} from './SubtitleFilePicker.shared'
|
||||
|
||||
export function SubtitleFilePicker(_props: SubtitleFilePickerProps): never {
|
||||
throw new Error('SubtitleFilePicker is a web-only component')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export type SubtitleFilePickerProps = {
|
||||
onSelectFile: (file: File) => void
|
||||
disabled?: boolean
|
||||
}
|
||||
@@ -9,14 +9,12 @@ import {atoms as a} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {CC_Stroke2_Corner0_Rounded as CCIcon} from '#/components/icons/CC'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {type SubtitleFilePickerProps} from './SubtitleFilePicker.shared'
|
||||
|
||||
export function SubtitleFilePicker({
|
||||
onSelectFile,
|
||||
disabled,
|
||||
}: {
|
||||
onSelectFile: (file: File) => void
|
||||
disabled?: boolean
|
||||
}) {
|
||||
}: SubtitleFilePickerProps) {
|
||||
const {_} = useLingui()
|
||||
const ref = useRef<HTMLInputElement>(null)
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {atoms as a, flatten} from '#/alf'
|
||||
|
||||
export function clearThumbnailCache() {
|
||||
export function clearThumbnailCache(_queryClient: QueryClient) {
|
||||
// no-op on web
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
useState,
|
||||
} from 'react'
|
||||
import {
|
||||
findNodeHandle,
|
||||
type ListRenderItemInfo,
|
||||
type StyleProp,
|
||||
useWindowDimensions,
|
||||
@@ -26,6 +25,7 @@ import {useSession} from '#/state/session'
|
||||
import {EmptyState} from '#/view/com/util/EmptyState'
|
||||
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
||||
import {List, type ListRef} from '#/view/com/util/List'
|
||||
import {findListNativeTag} from '#/view/com/util/listNativeTag'
|
||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
||||
import {atoms as a, ios, useTheme} from '#/alf'
|
||||
@@ -219,7 +219,7 @@ export function ProfileFeedgens({
|
||||
|
||||
useEffect(() => {
|
||||
if (IS_IOS && enabled && scrollElRef.current) {
|
||||
const nativeTag = findNodeHandle(scrollElRef.current)
|
||||
const nativeTag = findListNativeTag(scrollElRef.current)
|
||||
setScrollViewTag(nativeTag)
|
||||
}
|
||||
}, [enabled, scrollElRef, setScrollViewTag])
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
useState,
|
||||
} from 'react'
|
||||
import {
|
||||
findNodeHandle,
|
||||
type ListRenderItemInfo,
|
||||
type StyleProp,
|
||||
useWindowDimensions,
|
||||
@@ -26,6 +25,7 @@ import {useSession} from '#/state/session'
|
||||
import {EmptyState} from '#/view/com/util/EmptyState'
|
||||
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
||||
import {List, type ListRef} from '#/view/com/util/List'
|
||||
import {findListNativeTag} from '#/view/com/util/listNativeTag'
|
||||
import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
||||
import {atoms as a, ios, useTheme} from '#/alf'
|
||||
@@ -218,7 +218,7 @@ export function ProfileLists({
|
||||
|
||||
useEffect(() => {
|
||||
if (IS_IOS && enabled && scrollElRef.current) {
|
||||
const nativeTag = findNodeHandle(scrollElRef.current)
|
||||
const nativeTag = findListNativeTag(scrollElRef.current)
|
||||
setScrollViewTag(nativeTag)
|
||||
}
|
||||
}, [enabled, scrollElRef, setScrollViewTag])
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
useState,
|
||||
} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type SharedValue, useSharedValue} from 'react-native-reanimated'
|
||||
import {flushSync} from 'react-dom'
|
||||
|
||||
import {s} from '#/lib/styles'
|
||||
@@ -19,7 +20,9 @@ export interface PagerRef {
|
||||
export interface RenderTabBarFnProps {
|
||||
selectedPage: number
|
||||
onSelect?: (index: number) => void
|
||||
tabBarAnchor?: JSX.Element
|
||||
tabBarAnchor?: JSX.Element | null | undefined // Ignored on native.
|
||||
dragProgress: SharedValue<number> // Ignored on web.
|
||||
dragState: SharedValue<'idle' | 'dragging' | 'settling'> // Ignored on web.
|
||||
}
|
||||
export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element
|
||||
|
||||
@@ -27,7 +30,17 @@ interface Props {
|
||||
ref?: React.Ref<PagerRef>
|
||||
initialPage?: number
|
||||
renderTabBar: RenderTabBarFn
|
||||
// tab pressed, yet to scroll to page
|
||||
onTabPressed?: (index: number) => void
|
||||
// scroll settled
|
||||
onPageSelected?: (index: number) => void
|
||||
/**
|
||||
* Never fires on web - pages switch instantly, there is no drag gesture.
|
||||
*/
|
||||
onPageScrollStateChanged?: (
|
||||
scrollState: 'idle' | 'dragging' | 'settling',
|
||||
) => void
|
||||
testID?: string
|
||||
}
|
||||
|
||||
export function Pager({
|
||||
@@ -35,12 +48,22 @@ export function Pager({
|
||||
children,
|
||||
initialPage = 0,
|
||||
renderTabBar,
|
||||
onTabPressed,
|
||||
onPageSelected,
|
||||
onPageScrollStateChanged: _onPageScrollStateChanged,
|
||||
testID,
|
||||
}: React.PropsWithChildren<Props>) {
|
||||
const [selectedPage, setSelectedPage] = useState(initialPage)
|
||||
const scrollYs = useRef<Array<number | null>>([])
|
||||
const anchorRef = useRef(null)
|
||||
|
||||
/*
|
||||
* There is no drag gesture on web; these exist to satisfy the shared
|
||||
* RenderTabBarFnProps contract and never change.
|
||||
*/
|
||||
const dragProgress = useSharedValue(selectedPage)
|
||||
const dragState = useSharedValue<'idle' | 'dragging' | 'settling'>('idle')
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
setPage: (index: number) => {
|
||||
onTabBarSelect(index)
|
||||
@@ -59,6 +82,7 @@ export function Pager({
|
||||
: -scrollY // If there's no anchor, treat the top of the page as one.
|
||||
const isSticking = anchorTop <= 5 // This would be 0 if browser scrollTo() was reliable.
|
||||
|
||||
onTabPressed?.(index)
|
||||
if (isSticking) {
|
||||
scrollYs.current[selectedPage] = window.scrollY
|
||||
} else {
|
||||
@@ -77,15 +101,17 @@ export function Pager({
|
||||
}
|
||||
}
|
||||
},
|
||||
[selectedPage, setSelectedPage, onPageSelected],
|
||||
[selectedPage, setSelectedPage, onPageSelected, onTabPressed],
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={s.hContentRegion}>
|
||||
<View testID={testID} style={s.hContentRegion}>
|
||||
{renderTabBar({
|
||||
selectedPage,
|
||||
tabBarAnchor: <View ref={anchorRef} />,
|
||||
onSelect: e => onTabBarSelect(e),
|
||||
dragProgress,
|
||||
dragState,
|
||||
})}
|
||||
{Children.map(children, (child, i) => (
|
||||
<View
|
||||
|
||||
@@ -29,11 +29,12 @@ export interface PagerWithHeaderProps {
|
||||
renderHeader?: ({
|
||||
setMinimumHeight,
|
||||
}: {
|
||||
setMinimumHeight: () => void
|
||||
setMinimumHeight: (height: number) => void
|
||||
}) => JSX.Element
|
||||
initialPage?: number
|
||||
onPageSelected?: (index: number) => void
|
||||
onCurrentPageSelected?: (index: number) => void
|
||||
allowHeaderOverScroll?: boolean // Ignored on web.
|
||||
}
|
||||
export const PagerWithHeader = forwardRef<PagerRef, PagerWithHeaderProps>(
|
||||
function PageWithHeaderImpl(
|
||||
@@ -127,7 +128,7 @@ let PagerTabBar = ({
|
||||
renderHeader?: ({
|
||||
setMinimumHeight,
|
||||
}: {
|
||||
setMinimumHeight: () => void
|
||||
setMinimumHeight: (height: number) => void
|
||||
}) => JSX.Element
|
||||
isHeaderReady: boolean
|
||||
onCurrentPageSelected?: (index: number) => void
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useCallback, useEffect, useRef} from 'react'
|
||||
import {type ScrollView, StyleSheet, View} from 'react-native'
|
||||
import {type SharedValue} from 'react-native-reanimated'
|
||||
|
||||
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
@@ -15,6 +16,14 @@ export interface TabBarProps {
|
||||
|
||||
onSelect?: (index: number) => void
|
||||
onPressSelected?: (index: number) => void
|
||||
|
||||
/**
|
||||
* The drag-following indicator and transparent background only exist in
|
||||
* the native tab bar - accepted here so shared callers typecheck.
|
||||
*/
|
||||
dragProgress?: SharedValue<number> // Ignored on web.
|
||||
dragState?: SharedValue<'idle' | 'dragging' | 'settling'> // Ignored on web.
|
||||
transparent?: boolean // Ignored on web.
|
||||
}
|
||||
|
||||
// How much of the previous/next item we're showing
|
||||
|
||||
@@ -17,6 +17,7 @@ import {usePostQuotesQuery} from '#/state/queries/post-quotes'
|
||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {Post} from '#/view/com/post/Post'
|
||||
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {List} from '../util/List'
|
||||
|
||||
function renderItem({
|
||||
@@ -70,7 +71,13 @@ export function PostQuotes({uri}: {uri: string}) {
|
||||
data?.pages
|
||||
.flatMap(page =>
|
||||
page.posts.map(post => {
|
||||
if (!AppBskyFeedPost.isRecord(post.record) || !moderationOpts) {
|
||||
if (
|
||||
!bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||
post.record,
|
||||
AppBskyFeedPost.isRecord,
|
||||
) ||
|
||||
!moderationOpts
|
||||
) {
|
||||
return null
|
||||
}
|
||||
const moderation = moderatePost(post, moderationOpts)
|
||||
|
||||
@@ -28,7 +28,7 @@ function renderItem({
|
||||
)
|
||||
}
|
||||
|
||||
function keyExtractor(item: ActorDefs.ProfileViewBasic) {
|
||||
function keyExtractor(item: ActorDefs.ProfileView) {
|
||||
return item.did
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ function renderItem({
|
||||
)
|
||||
}
|
||||
|
||||
function keyExtractor(item: ActorDefs.ProfileViewBasic) {
|
||||
function keyExtractor(item: ActorDefs.ProfileView) {
|
||||
return item.did
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ function renderItem({
|
||||
)
|
||||
}
|
||||
|
||||
function keyExtractor(item: ActorDefs.ProfileViewBasic) {
|
||||
function keyExtractor(item: ActorDefs.ProfileView) {
|
||||
return item.did
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,10 @@ import * as Layout from '#/components/Layout'
|
||||
|
||||
export type ListMethods = {
|
||||
scrollToTop: () => void
|
||||
scrollToOffset: (options: {animated: boolean; offset: number}) => void
|
||||
scrollToEnd: (options?: {animated?: boolean}) => void
|
||||
// Signature kept compatible with FlatList's scrollToOffset (the native
|
||||
// ListMethods type) so callers stay platform-agnostic.
|
||||
scrollToOffset: (options: {animated?: boolean | null; offset: number}) => void
|
||||
scrollToEnd: (options?: {animated?: boolean | null}) => void
|
||||
// Signature kept compatible with FlatList's scrollToIndex (the native
|
||||
// ListMethods type) so callers stay platform-agnostic. viewOffset is
|
||||
// accepted for parity but not currently used by the web implementation.
|
||||
@@ -40,7 +42,8 @@ export type ListMethods = {
|
||||
viewPosition?: number
|
||||
}) => void
|
||||
}
|
||||
export type ListProps<ItemT> = Omit<
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ListProps<ItemT = any> = Omit<
|
||||
FlatListProps<ItemT>,
|
||||
| 'onScroll' // Use ScrollContext instead.
|
||||
| 'refreshControl' // Pass refreshing and/or onRefresh instead.
|
||||
@@ -59,7 +62,7 @@ export type ListProps<ItemT> = Omit<
|
||||
*/
|
||||
sideBorders?: boolean
|
||||
}
|
||||
export type ListRef = React.RefObject<View>
|
||||
export type ListRef = React.RefObject<ListMethods | null>
|
||||
|
||||
const ON_ITEM_SEEN_WAIT_DURATION = 0.5e3 // when we consider post to be "seen"
|
||||
const ON_ITEM_SEEN_INTERSECTION_OPTS = {
|
||||
@@ -248,7 +251,13 @@ function ListImpl<ItemT>(
|
||||
getScrollableNode()?.scrollTo({top: 0})
|
||||
},
|
||||
|
||||
scrollToOffset({animated, offset}: {animated: boolean; offset: number}) {
|
||||
scrollToOffset({
|
||||
animated,
|
||||
offset,
|
||||
}: {
|
||||
animated?: boolean | null
|
||||
offset: number
|
||||
}) {
|
||||
getScrollableNode()?.scrollTo({
|
||||
left: 0,
|
||||
top: offset,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import {findNodeHandle} from 'react-native'
|
||||
|
||||
import {type ListMethods} from './List'
|
||||
|
||||
/**
|
||||
* Returns the native view tag backing a List, for handing to native code
|
||||
* (e.g. the pager's scrollViewTag). Always null on web.
|
||||
*/
|
||||
export function findListNativeTag(list: ListMethods | null): number | null {
|
||||
if (!list) return null
|
||||
return findNodeHandle(list)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import {type ListMethods} from './List'
|
||||
|
||||
/**
|
||||
* Returns the native view tag backing a List, for handing to native code
|
||||
* (e.g. the pager's scrollViewTag). Always null on web.
|
||||
*/
|
||||
export function findListNativeTag(_list: ListMethods | null): number | null {
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user