diff --git a/package.json b/package.json index 30dfa2f8db..46fdf9096c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bsky.app", - "version": "1.85.0", + "version": "1.85.1", "private": true, "engines": { "node": ">=18" diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 862e36625f..5807488393 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -17,6 +17,7 @@ import { import { KeyboardAvoidingView, KeyboardStickyView, + useKeyboardContext, } from 'react-native-keyboard-controller' import Animated, { interpolateColor, @@ -129,6 +130,17 @@ export const ComposePost = observer(function ComposePost({ const {closeAllDialogs} = useDialogStateControlContext() const t = useTheme() + // Disable this in the composer to prevent any extra keyboard height being applied. + // See https://github.com/bluesky-social/social-app/pull/4399 + const {setEnabled} = useKeyboardContext() + React.useEffect(() => { + setEnabled(false) + + return () => { + setEnabled(true) + } + }, [setEnabled]) + const [isKeyboardVisible] = useIsKeyboardVisible({iosUseWillEvents: true}) const [isProcessing, setIsProcessing] = useState(false) const [processingState, setProcessingState] = useState('') diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index 531b6668ef..3ccce5bf31 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -25,6 +25,7 @@ import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' import {CenteredView} from '#/view/com/util/Views' import {FeedItem} from './FeedItem' import hairlineWidth = StyleSheet.hairlineWidth +import {isWeb} from '#/platform/detection' const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} @@ -214,7 +215,15 @@ export function Feed({ refreshing={isPTRing} onRefresh={onRefresh} onEndReached={onEndReached} - onEndReachedThreshold={2} + onEndReachedThreshold={ + /* + NOTE: + web's intersection observer struggles with the 2x threshold + and leads to missed pagination, so we keep it <1 + -prf + */ + isWeb ? 0.6 : 2 + } onScrolledDownChange={onScrolledDownChange} contentContainerStyle={s.contentContainer} // @ts-ignore our .web version only -prf diff --git a/src/view/com/util/List.web.tsx b/src/view/com/util/List.web.tsx index 6b0c17762c..e917ab1d32 100644 --- a/src/view/com/util/List.web.tsx +++ b/src/view/com/util/List.web.tsx @@ -38,6 +38,7 @@ function ListImpl( { ListHeaderComponent, ListFooterComponent, + ListEmptyComponent, containWeb, contentContainerStyle, data, @@ -72,23 +73,35 @@ function ListImpl( ) } - let header: JSX.Element | null = null + const isEmpty = !data || data.length === 0 + + let headerComponent: JSX.Element | null = null if (ListHeaderComponent != null) { if (isValidElement(ListHeaderComponent)) { - header = ListHeaderComponent + headerComponent = ListHeaderComponent } else { // @ts-ignore Nah it's fine. - header = + headerComponent = } } - let footer: JSX.Element | null = null + let footerComponent: JSX.Element | null = null if (ListFooterComponent != null) { if (isValidElement(ListFooterComponent)) { - footer = ListFooterComponent + footerComponent = ListFooterComponent } else { // @ts-ignore Nah it's fine. - footer = + footerComponent = + } + } + + let emptyComponent: JSX.Element | null = null + if (ListEmptyComponent != null) { + if (isValidElement(ListEmptyComponent)) { + emptyComponent = ListEmptyComponent + } else { + // @ts-ignore Nah it's fine. + emptyComponent = } } @@ -323,36 +336,38 @@ function ListImpl( onVisibleChange={handleAboveTheFoldVisibleChange} style={[styles.aboveTheFoldDetector, {height: headerOffset}]} /> - {onStartReached && ( + {onStartReached && !isEmpty && ( )} - {header} - {(data as Array).map((item, index) => { - const key = keyExtractor!(item, index) - return ( - - key={key} - item={item} - index={index} - renderItem={renderItem} - extraData={extraData} - onItemSeen={onItemSeen} - disableContentVisibility={disableContentVisibility} - /> - ) - })} - {onEndReached && ( + {headerComponent} + {isEmpty + ? emptyComponent + : (data as Array)?.map((item, index) => { + const key = keyExtractor!(item, index) + return ( + + key={key} + item={item} + index={index} + renderItem={renderItem} + extraData={extraData} + onItemSeen={onItemSeen} + disableContentVisibility={disableContentVisibility} + /> + ) + })} + {onEndReached && !isEmpty && ( )} - {footer} + {footerComponent} )