forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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}