Bluesky app fork with some witchin' additions 馃挮
at linkat-integration 183 lines 4.7 kB view raw
1import {useCallback} from 'react' 2import {type GestureResponderEvent, View} from 'react-native' 3import {msg} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5import {useNavigation} from '@react-navigation/native' 6 7import {HITSLOP_30} from '#/lib/constants' 8import {type NavigationProp} from '#/lib/routes/types' 9import {sanitizeHandle} from '#/lib/strings/handles' 10import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 11import {useFeedSourceInfoQuery} from '#/state/queries/feed' 12import {UserAvatar} from '#/view/com/util/UserAvatar' 13import {type VideoFeedSourceContext} from '#/screens/VideoFeed/types' 14import {atoms as a, useBreakpoints} from '#/alf' 15import {Button, type ButtonProps} from '#/components/Button' 16import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeft} from '#/components/icons/Arrow' 17import * as Layout from '#/components/Layout' 18import {BUTTON_VISUAL_ALIGNMENT_OFFSET} from '#/components/Layout/const' 19import {Text} from '#/components/Typography' 20 21export function HeaderPlaceholder() { 22 return ( 23 <View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> 24 <View 25 style={[ 26 a.rounded_sm, 27 { 28 width: 36, 29 height: 36, 30 backgroundColor: 'white', 31 opacity: 0.8, 32 }, 33 ]} 34 /> 35 36 <View style={[a.flex_1, a.gap_xs]}> 37 <View 38 style={[ 39 a.w_full, 40 a.rounded_xs, 41 { 42 backgroundColor: 'white', 43 height: 14, 44 width: 80, 45 opacity: 0.8, 46 }, 47 ]} 48 /> 49 <View 50 style={[ 51 a.w_full, 52 a.rounded_xs, 53 { 54 backgroundColor: 'white', 55 height: 10, 56 width: 140, 57 opacity: 0.6, 58 }, 59 ]} 60 /> 61 </View> 62 </View> 63 ) 64} 65 66export function Header({ 67 sourceContext, 68}: { 69 sourceContext: VideoFeedSourceContext 70}) { 71 let content = null 72 switch (sourceContext.type) { 73 case 'feedgen': { 74 content = <FeedHeader sourceContext={sourceContext} /> 75 break 76 } 77 case 'author': 78 // TODO 79 default: { 80 break 81 } 82 } 83 84 return ( 85 <Layout.Header.Outer noBottomBorder> 86 <BackButton /> 87 <Layout.Header.Content align="left">{content}</Layout.Header.Content> 88 </Layout.Header.Outer> 89 ) 90} 91 92export function FeedHeader({ 93 sourceContext, 94}: { 95 sourceContext: Exclude<VideoFeedSourceContext, {type: 'author'}> 96}) { 97 const {gtMobile} = useBreakpoints() 98 99 const { 100 data: info, 101 isLoading, 102 error, 103 } = useFeedSourceInfoQuery({uri: sourceContext.uri}) 104 105 if (sourceContext.sourceInterstitial !== undefined) { 106 // For now, don't show the header if coming from an interstitial. 107 return null 108 } 109 110 if (isLoading) { 111 return <HeaderPlaceholder /> 112 } else if (error || !info) { 113 return null 114 } 115 116 return ( 117 <View style={[a.flex_1, a.flex_row, a.align_center, a.gap_sm]}> 118 {info.avatar && <UserAvatar size={36} type="algo" avatar={info.avatar} />} 119 120 <View style={[a.flex_1]}> 121 <Text 122 style={[ 123 a.text_md, 124 a.font_bold, 125 a.leading_tight, 126 gtMobile && a.text_lg, 127 ]} 128 numberOfLines={2}> 129 {info.displayName} 130 </Text> 131 <View style={[a.flex_row, {gap: 6}]}> 132 <Text 133 style={[a.flex_shrink, a.text_sm, a.leading_snug]} 134 numberOfLines={1}> 135 {sanitizeHandle(info.creatorHandle, '@')} 136 </Text> 137 </View> 138 </View> 139 </View> 140 ) 141} 142 143// TODO: This customization should be a part of the layout component 144export function BackButton({onPress, style, ...props}: Partial<ButtonProps>) { 145 const {_} = useLingui() 146 const navigation = useNavigation<NavigationProp>() 147 148 const enableSquareButtons = useEnableSquareButtons() 149 150 const onPressBack = useCallback( 151 (evt: GestureResponderEvent) => { 152 onPress?.(evt) 153 if (evt.defaultPrevented) return 154 if (navigation.canGoBack()) { 155 navigation.goBack() 156 } else { 157 navigation.navigate('Home') 158 } 159 }, 160 [onPress, navigation], 161 ) 162 163 return ( 164 <Layout.Header.Slot> 165 <Button 166 label={_(msg`Go back`)} 167 size="small" 168 variant="ghost" 169 color="secondary" 170 shape={enableSquareButtons ? 'square' : 'round'} 171 onPress={onPressBack} 172 hitSlop={HITSLOP_30} 173 style={[ 174 {marginLeft: -BUTTON_VISUAL_ALIGNMENT_OFFSET}, 175 a.bg_transparent, 176 style, 177 ]} 178 {...props}> 179 <ArrowLeft size="lg" fill="white" /> 180 </Button> 181 </Layout.Header.Slot> 182 ) 183}