Files
bsky-social-app/src/view/com/composer/photos/OpenCameraBtn.tsx
T
Claude 3652b30811 Add web typecheck pass (tsconfig.check.web.json) and fix web type drift
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
2026-07-16 21:13:17 +03:00

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>
)
}