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

Configure Feed

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

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