Bluesky app fork with some witchin' additions ๐Ÿ’ซ
0
fork

Configure Feed

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

[๐Ÿด] Finalize web message screen (#3868)

* add `onStartReached` to web list

* fix `rootMargin`

* Add `contain`, handle scroll events

* improve types, fix typo

* simplify

* adjust `scrollToTop` and `scrollToOffset` to support `contain`, add `scrollToEnd`

* rename `handleWindowScroll` to `handleScroll`

* support basic `maintainVisibleContentPosition`

* rename `contain` to `containWeb`

* remove unnecessary `flex: 1`

* add missing props

* add root prop to `Visibility`

* add root prop to `Visibility`

* revert adding `maintainVisibleContentPosition`

* remove unnecessary wrapper

* add style

* oops

* maintain position for web

* always apply `flex: 1` to styles when contained

* add a contained list to storybook

* make `onScroll` a worklet in storybook

* revert test code

* remove unnecessary `flex: 1`

authored by

Hailey and committed by
GitHub
ae7626ce bc070199

+47 -35
+46 -35
src/screens/Messages/Conversation/MessagesList.tsx
··· 94 94 // the bottom. 95 95 const isAtBottom = useSharedValue(true) 96 96 97 + // This will be used on web to assist in determing if we need to maintain the content offset 98 + const isAtTop = useSharedValue(true) 99 + 97 100 // Used to keep track of the current content height. We'll need this in `onScroll` so we know when to start allowing 98 101 // onStartReached to fire. 99 102 const contentHeight = useSharedValue(0) ··· 116 119 // we will not scroll whenever new items get prepended to the top. 117 120 const onContentSizeChange = useCallback( 118 121 (_: number, height: number) => { 122 + // Because web does not have `maintainVisibleContentPosition` support, we will need to manually scroll to the 123 + // previous offset whenever we add new content to the previous offset whenever we add new content to the list. 124 + if (isWeb && isAtTop.value && hasInitiallyScrolled) { 125 + flatListRef.current?.scrollToOffset({ 126 + animated: false, 127 + offset: height - contentHeight.value, 128 + }) 129 + } 130 + 119 131 contentHeight.value = height 120 132 121 133 // This number _must_ be the height of the MaybeLoader component ··· 133 145 contentHeight, 134 146 hasInitiallyScrolled, 135 147 isAtBottom.value, 148 + isAtTop.value, 136 149 isMomentumScrolling, 137 150 ], 138 151 ) ··· 164 177 // Most apps have a little bit of space the user can scroll past while still automatically scrolling ot the bottom 165 178 // when a new message is added, hence the 100 pixel offset 166 179 isAtBottom.value = e.contentSize.height - 100 < bottomOffset 180 + isAtTop.value = e.contentOffset.y <= 1 167 181 168 182 // This number _must_ be the height of the MaybeLoader component. 169 183 // We don't check for zero, because the `MaybeLoader` component is always present, even when not visible, which ··· 172 186 runOnJS(setHasInitiallyScrolled)(true) 173 187 } 174 188 }, 175 - [contentHeight.value, hasInitiallyScrolled, isAtBottom], 189 + [contentHeight.value, hasInitiallyScrolled, isAtBottom, isAtTop], 176 190 ) 177 191 178 192 const onMomentumEnd = React.useCallback(() => { ··· 211 225 keyboardVerticalOffset={isIOS ? topInset : 0} 212 226 behavior="padding" 213 227 contentContainerStyle={a.flex_1}> 214 - {/* This view keeps the scroll bar and content within the CenterView on web, otherwise the entire window would scroll */} 215 - {/* @ts-expect-error web only */} 216 - <View style={[a.flex_1, isWeb && {'overflow-y': 'scroll'}]}> 217 - {/* Custom scroll provider so that we can use the `onScroll` event in our custom List implementation */} 218 - <ScrollProvider onScroll={onScroll} onMomentumEnd={onMomentumEnd}> 219 - <List 220 - ref={flatListRef} 221 - data={chat.status === ConvoStatus.Ready ? chat.items : undefined} 222 - renderItem={renderItem} 223 - keyExtractor={keyExtractor} 224 - disableVirtualization={true} 225 - initialNumToRender={isWeb ? 50 : 25} 226 - maxToRenderPerBatch={isWeb ? 50 : 25} 227 - keyboardDismissMode="on-drag" 228 - keyboardShouldPersistTaps="handled" 229 - maintainVisibleContentPosition={{ 230 - minIndexForVisible: 1, 231 - }} 232 - contentContainerStyle={{paddingHorizontal: 10}} 233 - removeClippedSubviews={false} 234 - onContentSizeChange={onContentSizeChange} 235 - onStartReached={onStartReached} 236 - onScrollToIndexFailed={onScrollToIndexFailed} 237 - scrollEventThrottle={100} 238 - ListHeaderComponent={ 239 - <MaybeLoader 240 - isLoading={ 241 - chat.status === ConvoStatus.Ready && chat.isFetchingHistory 242 - } 243 - /> 244 - } 245 - /> 246 - </ScrollProvider> 247 - </View> 228 + {/* Custom scroll provider so that we can use the `onScroll` event in our custom List implementation */} 229 + <ScrollProvider onScroll={onScroll} onMomentumEnd={onMomentumEnd}> 230 + <List 231 + ref={flatListRef} 232 + data={chat.status === ConvoStatus.Ready ? chat.items : undefined} 233 + renderItem={renderItem} 234 + keyExtractor={keyExtractor} 235 + disableVirtualization={true} 236 + initialNumToRender={isWeb ? 50 : 25} 237 + maxToRenderPerBatch={isWeb ? 50 : 25} 238 + keyboardDismissMode="on-drag" 239 + keyboardShouldPersistTaps="handled" 240 + maintainVisibleContentPosition={{ 241 + minIndexForVisible: 1, 242 + }} 243 + containWeb={true} 244 + contentContainerStyle={{paddingHorizontal: 10}} 245 + removeClippedSubviews={false} 246 + onContentSizeChange={onContentSizeChange} 247 + onStartReached={onStartReached} 248 + onScrollToIndexFailed={onScrollToIndexFailed} 249 + scrollEventThrottle={100} 250 + ListHeaderComponent={ 251 + <MaybeLoader 252 + isLoading={ 253 + chat.status === ConvoStatus.Ready && chat.isFetchingHistory 254 + } 255 + /> 256 + } 257 + /> 258 + </ScrollProvider> 248 259 <MessageInput onSendMessage={onSendMessage} scrollToEnd={scrollToEnd} /> 249 260 </KeyboardAvoidingView> 250 261 )
+1
src/view/com/util/List.web.tsx
··· 143 143 scrollToTop() { 144 144 getScrollableNode()?.scrollTo({top: 0}) 145 145 }, 146 + 146 147 scrollToOffset({ 147 148 animated, 148 149 offset,