Bluesky app fork with some witchin' additions 💫

[Drafts] Some bugs (#9833)

* Fix text input not updating

* Fix autofocus

* make placeholder text fainter

* Await invalidation

* Image only drafts

* tweaks to gif presentation

---------

Co-authored-by: Eric Bailey <git@esb.lol>

authored by samuel.fm

Eric Bailey and committed by
GitHub
917f099e 6bfe758d

+61 -56
+14 -10
src/components/Post/Embed/VideoEmbed/GifPresentationControls.tsx
··· 1 - import {Pressable, StyleSheet, TouchableOpacity, View} from 'react-native' 1 + import { 2 + ActivityIndicator, 3 + StyleSheet, 4 + TouchableOpacity, 5 + View, 6 + } from 'react-native' 2 7 import {msg, Trans} from '@lingui/macro' 3 8 import {useLingui} from '@lingui/react' 4 9 5 10 import {HITSLOP_20} from '#/lib/constants' 6 11 import {atoms as a, useTheme} from '#/alf' 12 + import {Button} from '#/components/Button' 7 13 import {Fill} from '#/components/Fill' 8 - import {Loader} from '#/components/Loader' 9 14 import * as Prompt from '#/components/Prompt' 10 15 import {Text} from '#/components/Typography' 11 16 import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' ··· 26 31 27 32 return ( 28 33 <> 29 - <Pressable 30 - accessibilityRole="button" 34 + <Button 35 + label={isPlaying ? _(msg`Pause GIF`) : _(msg`Play GIF`)} 31 36 accessibilityHint={_(msg`Plays or pauses the GIF`)} 32 - accessibilityLabel={isPlaying ? _(msg`Pause`) : _(msg`Play`)} 33 37 style={[ 34 38 a.absolute, 35 39 a.align_center, 36 40 a.justify_center, 37 41 a.inset_0, 38 - a.w_full, 39 - a.h_full, 40 42 {zIndex: 2}, 41 43 ]} 42 44 onPress={onPress}> 43 45 {isLoading ? ( 44 46 <View style={[a.align_center, a.justify_center]}> 45 - <Loader size="xl" /> 47 + <ActivityIndicator size="large" color="white" /> 46 48 </View> 47 49 ) : !isPlaying ? ( 48 50 <PlayButtonIcon /> 49 - ) : undefined} 50 - </Pressable> 51 + ) : ( 52 + <></> 53 + )} 54 + </Button> 51 55 {!isPlaying && ( 52 56 <Fill 53 57 style={[
+32 -24
src/components/Post/Embed/VideoEmbed/index.tsx
··· 6 6 import {useLingui} from '@lingui/react' 7 7 8 8 import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' 9 - import {atoms as a} from '#/alf' 9 + import {atoms as a, platform} from '#/alf' 10 10 import {Button} from '#/components/Button' 11 11 import {useThrottledValue} from '#/components/hooks/useThrottledValue' 12 12 import {ConstrainedImage} from '#/components/images/AutoSizedImage' 13 13 import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' 14 + import {GifPresentationControls} from './GifPresentationControls' 14 15 import {VideoEmbedInnerNative} from './VideoEmbedInner/VideoEmbedInnerNative' 15 16 import * as VideoFallback from './VideoEmbedInner/VideoFallback' 16 17 ··· 101 102 { 102 103 backgroundColor: 'transparent', // If you don't add `backgroundColor` to the styles here, 103 104 // the play button won't show up on the first render on android 🥴😮‍💨 104 - display: showOverlay ? 'flex' : 'none', 105 105 }, 106 + platform({ 107 + android: {display: showOverlay ? 'flex' : 'none'}, 108 + ios: {zIndex: showOverlay ? 1 : -1}, 109 + }), 106 110 ]} 107 111 cachePolicy="memory-disk" // Preferring memory cache helps to avoid flicker when re-displaying on android 108 112 > 109 - {showOverlay && ( 110 - <Button 111 - style={[a.flex_1, a.align_center, a.justify_center]} 112 - onPress={() => { 113 - ref.current?.togglePlayback() 114 - }} 115 - label={_(msg`Play video`)}> 116 - {showSpinner ? ( 117 - <View 118 - style={[ 119 - a.rounded_full, 120 - a.p_xs, 121 - a.align_center, 122 - a.justify_center, 123 - ]}> 124 - <ActivityIndicator size="large" color="white" /> 125 - </View> 126 - ) : ( 127 - <PlayButtonIcon /> 128 - )} 129 - </Button> 130 - )} 113 + {showOverlay && 114 + (embed.presentation === 'gif' ? ( 115 + <GifPresentationControls 116 + isPlaying={false} 117 + isLoading={showSpinner} 118 + onPress={() => { 119 + ref.current?.togglePlayback() 120 + }} 121 + altText={embed.alt} 122 + /> 123 + ) : ( 124 + <Button 125 + style={[a.flex_1, a.align_center, a.justify_center]} 126 + onPress={() => { 127 + ref.current?.togglePlayback() 128 + }} 129 + label={_(msg`Play video`)}> 130 + {showSpinner ? ( 131 + <View style={[a.align_center, a.justify_center]}> 132 + <ActivityIndicator size="large" color="white" /> 133 + </View> 134 + ) : ( 135 + <PlayButtonIcon /> 136 + )} 137 + </Button> 138 + ))} 131 139 </ImageBackground> 132 140 </> 133 141 )
+2 -2
src/view/com/composer/Composer.tsx
··· 1115 1115 onLayout={onScrollViewLayout}> 1116 1116 {replyTo ? <ComposerReplyTo replyTo={replyTo} /> : undefined} 1117 1117 {thread.posts.map((post, index) => ( 1118 - <React.Fragment key={post.id}> 1118 + <React.Fragment key={post.id + (composerState.draftId ?? '')}> 1119 1119 <ComposerPost 1120 1120 post={post} 1121 1121 dispatch={composerDispatch} ··· 1320 1320 style={[a.pt_xs]} 1321 1321 richtext={richtext} 1322 1322 placeholder={selectTextInputPlaceholder} 1323 - autoFocus 1323 + autoFocus={isLastPost} 1324 1324 webForceMinHeight={forceMinHeight} 1325 1325 // To avoid overlap with the close button: 1326 1326 hasRightPadding={isPartOfThread}
+8 -6
src/view/com/composer/drafts/DraftItem.tsx
··· 95 95 paddingTop: 20 + a.pt_md.paddingTop, 96 96 }, 97 97 ]}> 98 - <RichText 99 - style={[a.text_md, a.leading_snug, a.pointer_events_none]} 100 - value={post.text} 101 - enableTags 102 - disableMentionFacetValidation 103 - /> 98 + {!!post.text.trim().length && ( 99 + <RichText 100 + style={[a.text_md, a.leading_snug, a.pointer_events_none]} 101 + value={post.text} 102 + enableTags 103 + disableMentionFacetValidation 104 + /> 105 + )} 104 106 105 107 {!mediaExistsOnOtherDevice && <DraftMediaPreview post={post} />} 106 108
+1 -1
src/view/com/composer/drafts/state/queries.ts
··· 184 184 } 185 185 } 186 186 187 - queryClient.invalidateQueries({queryKey: DRAFTS_QUERY_KEY}) 187 + await queryClient.invalidateQueries({queryKey: DRAFTS_QUERY_KEY}) 188 188 }, 189 189 onError: error => { 190 190 // Check for draft limit error
+2 -2
src/view/com/composer/text-input/TextInput.tsx
··· 224 224 onPaste={onPaste} 225 225 onSelectionChange={onSelectionChange} 226 226 placeholder={placeholder} 227 - placeholderTextColor={t.atoms.text_contrast_medium.color} 227 + placeholderTextColor={t.atoms.text_contrast_low.color} 228 228 keyboardAppearance={theme.colorScheme} 229 - autoFocus={true} 229 + autoFocus={props.autoFocus !== undefined ? props.autoFocus : true} 230 230 allowFontScaling 231 231 multiline 232 232 scrollEnabled={false}
+2 -11
src/view/com/composer/text-input/TextInput.web.tsx
··· 52 52 onPressPublish, 53 53 onNewLink, 54 54 onFocus, 55 + autoFocus, 55 56 }: TextInputProps) { 56 57 const {theme: t, fonts} = useAlf() 57 58 const autocomplete = useActorAutocompleteFn() ··· 244 245 content: generateJSON(richTextToHTML(richtext), extensions, { 245 246 preserveWhitespace: 'full', 246 247 }), 247 - autofocus: 'end', 248 + autofocus: autoFocus ? 'end' : null, 248 249 editable: true, 249 250 injectCSS: true, 250 251 shouldRerenderOnTransaction: false, 251 - onCreate({editor: editorProp}) { 252 - // HACK 253 - // the 'enter' animation sometimes causes autofocus to fail 254 - // (see Composer.web.tsx in shell) 255 - // so we wait 200ms (the anim is 150ms) and then focus manually 256 - // -prf 257 - setTimeout(() => { 258 - editorProp.chain().focus('end').run() 259 - }, 200) 260 - }, 261 252 onUpdate({editor: editorProp}) { 262 253 const json = editorProp.getJSON() 263 254 const newText = editorJsonToText(json)