3652b30811
The main typecheck models native resolution (.ios/.android/.native), so .web variants were never checked in their real resolution context. Add a second pass that roots the program at the web entry point and resolves with moduleSuffixes [".web", ""], wire it up as pnpm typecheck:web, and add it to the lint workflow matrix. Ambient shims (src/platform/*.web-check.d.ts, web pass only) pin react-native-svg, expo-file-system, and expo-image-manipulator to their native declarations, since their .web.d.ts files expose a different API surface than the one the app is written against (and, for expo-file-system/legacy, raw package sources that break under suffix remapping). The rest fixes the drift the new pass surfaced, notably: - List.web: ListRef now carries ListMethods (was RefObject<View>), scrollToOffset accepts optional animated, ListProps defaults ItemT - Dialog.web: ScrollableInner accepts a (never-attached) ScrollView ref, InnerFlatList label optional + ListMethods ref, Handle accepts props - Menu.web exports MenuControlProps; ContextMenu.web gets a properly typed Trigger wrapper and accepts align - Pager/TabBar/PagerWithHeader web variants align with the shared contract (testID, onTabPressed, drag shared values, setMinimumHeight) - useNavigationTabState.web now reports isAtFeeds/isAtBookmarks, fixing drawer highlighting for those routes on web - Web-only throw-stubs get real signatures; duplicated prop types move into .shared.ts files (CaptchaWebView, EditImageDialog, OpenCameraBtn, GestureActionView, ValuePropositionPager) - findListNativeTag helper replaces findNodeHandle casts on List refs - Assorted call-site type fixes (keyExtractor params, PostQuotes record narrowing, saveBytesToDisk bytes param, openCamera web signature) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCuMKXWHuyGoNhTVAHBMyk
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
import {useCallback} from 'react'
|
|
import * as MediaLibrary from 'expo-media-library'
|
|
import {msg} from '@lingui/core/macro'
|
|
import {useLingui} from '@lingui/react'
|
|
|
|
import {useCameraPermission} from '#/lib/hooks/usePermissions'
|
|
import {openCamera} from '#/lib/media/picker'
|
|
import {logger} from '#/logger'
|
|
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'
|
|
|
|
export function OpenCameraBtn({disabled, onAdd}: OpenCameraBtnProps) {
|
|
const {_} = useLingui()
|
|
const {requestCameraAccessIfNeeded} = useCameraPermission()
|
|
const [mediaPermissionRes, requestMediaPermission] =
|
|
MediaLibrary.usePermissions({granularPermissions: ['photo']})
|
|
const t = useTheme()
|
|
|
|
const onPressTakePicture = useCallback(async () => {
|
|
try {
|
|
if (!(await requestCameraAccessIfNeeded())) {
|
|
return
|
|
}
|
|
if (!mediaPermissionRes?.granted && mediaPermissionRes?.canAskAgain) {
|
|
await requestMediaPermission()
|
|
}
|
|
|
|
const img = await openCamera({
|
|
aspect: [1, 1],
|
|
})
|
|
|
|
// If we don't have permissions it's fine, we just wont save it. The post itself will still have access to
|
|
// the image even without these permissions
|
|
if (mediaPermissionRes) {
|
|
await MediaLibrary.createAssetAsync(img.path)
|
|
}
|
|
|
|
const res = await createComposerImage(img)
|
|
|
|
onAdd([res])
|
|
} catch (err: any) {
|
|
// ignore
|
|
logger.warn('Error using camera', {error: err})
|
|
}
|
|
}, [
|
|
onAdd,
|
|
requestCameraAccessIfNeeded,
|
|
mediaPermissionRes,
|
|
requestMediaPermission,
|
|
])
|
|
|
|
const shouldShowCameraButton = IS_NATIVE || IS_WEB_MOBILE
|
|
if (!shouldShowCameraButton) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<Button
|
|
testID="openCameraButton"
|
|
onPress={onPressTakePicture}
|
|
label={_(msg`Camera`)}
|
|
accessibilityHint={_(msg`Opens camera on device`)}
|
|
style={a.p_sm}
|
|
variant="ghost"
|
|
shape="round"
|
|
color="primary"
|
|
disabled={disabled}>
|
|
<Camera size="lg" style={disabled && t.atoms.text_contrast_low} />
|
|
</Button>
|
|
)
|
|
}
|