Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 171 lines 4.9 kB view raw
1import React from 'react' 2import {type ImageStyle, useWindowDimensions, View} from 'react-native' 3import {Image} from 'expo-image' 4import {msg, Plural, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6 7import {MAX_ALT_TEXT} from '#/lib/constants' 8import {enforceLen} from '#/lib/strings/helpers' 9import {type ComposerImage} from '#/state/gallery' 10import {AltTextCounterWrapper} from '#/view/com/composer/AltTextCounterWrapper' 11import {atoms as a, useTheme} from '#/alf' 12import {Button, ButtonText} from '#/components/Button' 13import * as Dialog from '#/components/Dialog' 14import {type DialogControlProps} from '#/components/Dialog' 15import * as TextField from '#/components/forms/TextField' 16import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 17import {Text} from '#/components/Typography' 18import {IS_ANDROID, IS_WEB} from '#/env' 19 20type Props = { 21 control: Dialog.DialogOuterProps['control'] 22 image: ComposerImage 23 onChange: (next: ComposerImage) => void 24} 25 26export const ImageAltTextDialog = ({ 27 control, 28 image, 29 onChange, 30}: Props): React.ReactNode => { 31 const [altText, setAltText] = React.useState(image.alt) 32 33 return ( 34 <Dialog.Outer 35 control={control} 36 onClose={() => { 37 onChange({ 38 ...image, 39 alt: enforceLen(altText, MAX_ALT_TEXT, true), 40 }) 41 }}> 42 <Dialog.Handle /> 43 <ImageAltTextInner 44 control={control} 45 image={image} 46 altText={altText} 47 setAltText={setAltText} 48 /> 49 </Dialog.Outer> 50 ) 51} 52 53const ImageAltTextInner = ({ 54 altText, 55 setAltText, 56 control, 57 image, 58}: { 59 altText: string 60 setAltText: (text: string) => void 61 control: DialogControlProps 62 image: Props['image'] 63}): React.ReactNode => { 64 const {_, i18n} = useLingui() 65 const t = useTheme() 66 const windim = useWindowDimensions() 67 68 const imageStyle = React.useMemo<ImageStyle>(() => { 69 const maxWidth = IS_WEB ? 450 : windim.width 70 const source = image.transformed ?? image.source 71 72 if (source.height > source.width) { 73 return { 74 resizeMode: 'contain', 75 width: '100%', 76 aspectRatio: 1, 77 borderRadius: 8, 78 } 79 } 80 return { 81 width: '100%', 82 height: (maxWidth / source.width) * source.height, 83 borderRadius: 8, 84 } 85 }, [image, windim]) 86 87 return ( 88 <Dialog.ScrollableInner label={_(msg`Add alt text`)}> 89 <Dialog.Close /> 90 91 <View> 92 <Text style={[a.text_2xl, a.font_semi_bold, a.leading_tight, a.pb_sm]}> 93 <Trans>Add alt text</Trans> 94 </Text> 95 96 <View style={[t.atoms.bg_contrast_50, a.rounded_sm, a.overflow_hidden]}> 97 <Image 98 style={imageStyle} 99 source={{uri: (image.transformed ?? image.source).path}} 100 contentFit="contain" 101 accessible={true} 102 accessibilityIgnoresInvertColors 103 enableLiveTextInteraction 104 autoplay={false} 105 /> 106 </View> 107 </View> 108 109 <View style={[a.mt_md, a.gap_md]}> 110 <View style={[a.gap_sm]}> 111 <View style={[a.relative, {width: '100%'}]}> 112 <TextField.LabelText> 113 <Trans>Descriptive alt text</Trans> 114 </TextField.LabelText> 115 <TextField.Root> 116 <Dialog.Input 117 label={_(msg`Alt text`)} 118 onChangeText={text => { 119 setAltText(text) 120 }} 121 defaultValue={altText} 122 multiline 123 numberOfLines={3} 124 autoFocus 125 /> 126 </TextField.Root> 127 </View> 128 129 {altText.length > MAX_ALT_TEXT && ( 130 <View style={[a.pb_sm, a.flex_row, a.gap_xs]}> 131 <CircleInfo fill={t.palette.negative_500} /> 132 <Text 133 style={[ 134 a.italic, 135 a.leading_snug, 136 t.atoms.text_contrast_medium, 137 ]}> 138 <Trans> 139 Alt text will be truncated.{' '} 140 <Plural 141 value={MAX_ALT_TEXT} 142 other={`Limit: ${i18n.number(MAX_ALT_TEXT)} characters.`} 143 /> 144 </Trans> 145 </Text> 146 </View> 147 )} 148 </View> 149 150 <AltTextCounterWrapper altText={altText}> 151 <Button 152 label={_(msg`Save`)} 153 disabled={altText === image.alt} 154 size="large" 155 color="primary" 156 variant="solid" 157 onPress={() => { 158 control.close() 159 }} 160 style={[a.flex_grow]}> 161 <ButtonText> 162 <Trans>Save</Trans> 163 </ButtonText> 164 </Button> 165 </AltTextCounterWrapper> 166 </View> 167 {/* Maybe fix this later -h */} 168 {IS_ANDROID ? <View style={{height: 300}} /> : null} 169 </Dialog.ScrollableInner> 170 ) 171}