fix refreshing
This commit is contained in:
@@ -34,7 +34,6 @@ static const CGFloat kMinimumVelocity = 5.0;
|
|||||||
CGFloat _currentVelocity;
|
CGFloat _currentVelocity;
|
||||||
CGFloat _accumulatedTranslation;
|
CGFloat _accumulatedTranslation;
|
||||||
|
|
||||||
bool _refreshing;
|
|
||||||
bool _didImpact;
|
bool _didImpact;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +101,12 @@ static const CGFloat kMinimumVelocity = 5.0;
|
|||||||
[self tryFindScrollView];
|
[self tryFindScrollView];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldViewProps.refreshing != newViewProps.refreshing) {
|
||||||
|
if (!newViewProps.refreshing) {
|
||||||
|
[self endRefreshing];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[super updateProps:props oldProps:oldProps];
|
[super updateProps:props oldProps:oldProps];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,11 +216,10 @@ static const CGFloat kMinimumVelocity = 5.0;
|
|||||||
if (gesture.state == UIGestureRecognizerStateEnded) {
|
if (gesture.state == UIGestureRecognizerStateEnded) {
|
||||||
CGPoint velocity = [gesture velocityInView:self];
|
CGPoint velocity = [gesture velocityInView:self];
|
||||||
|
|
||||||
// TODO: enable this again
|
if (sv.contentOffset.y <= -kPullThreshold) {
|
||||||
// if (sv.contentOffset.y <= -kPullThreshold) {
|
[self refresh];
|
||||||
// [self refresh];
|
return;
|
||||||
// return;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
if (sv.contentOffset.y < 0) {
|
if (sv.contentOffset.y < 0) {
|
||||||
CGPoint newOffset = CGPointMake(sv.contentOffset.x, 0);
|
CGPoint newOffset = CGPointMake(sv.contentOffset.x, 0);
|
||||||
@@ -354,29 +358,51 @@ static const CGFloat kMinimumVelocity = 5.0;
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (UIRefreshControl *)refreshContorl
|
||||||
|
{
|
||||||
|
if (!_svcv) return nil;
|
||||||
|
return _svcv.scrollView.refreshControl;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)refresh
|
- (void)refresh
|
||||||
{
|
{
|
||||||
if (!_svcv) return;
|
__weak ScrollForwarderView *weakSelf = self;
|
||||||
|
|
||||||
UIScrollView *sv = _svcv.scrollView;
|
[_svcv.scrollView.refreshControl beginRefreshing];
|
||||||
UIRefreshControl *rc = sv.refreshControl;
|
|
||||||
|
|
||||||
if (!rc) return;
|
|
||||||
|
|
||||||
[UIView animateWithDuration:0.3
|
[UIView animateWithDuration:0.3
|
||||||
delay:0
|
delay:0
|
||||||
options:UIViewAnimationOptionBeginFromCurrentState
|
options:UIViewAnimationOptionBeginFromCurrentState
|
||||||
animations:^(void) {
|
animations:^(void) {
|
||||||
|
if (!weakSelf) return;
|
||||||
|
|
||||||
|
__strong ScrollForwarderView *self = weakSelf;
|
||||||
|
|
||||||
// Whenever we call this method, the scrollview will always be at a position of
|
// Whenever we call this method, the scrollview will always be at a position of
|
||||||
// -130 or less. Scrolling back to -65 simulates the default behavior of RCTRefreshControl
|
// -130 or less. Scrolling back to -80 simulates the default behavior of RCTRefreshControl
|
||||||
[sv setContentOffset:CGPointMake(0, -65)];
|
[self->_svcv.scrollView setContentOffset:CGPointMake(0, -65)];
|
||||||
}
|
}
|
||||||
completion:^(__unused BOOL finished) {
|
completion:^(__unused BOOL finished) {
|
||||||
[rc beginRefreshing];
|
__strong ScrollForwarderView *self = weakSelf;
|
||||||
|
|
||||||
|
if (self->_eventEmitter != nullptr) {
|
||||||
|
std::dynamic_pointer_cast<const facebook::react::ScrollForwarderViewEventEmitter>(self->_eventEmitter)
|
||||||
|
->onRefresh(facebook::react::ScrollForwarderViewEventEmitter::OnRefresh{});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)endRefreshing
|
||||||
|
{
|
||||||
|
UIRefreshControl *rc = [self refreshContorl];
|
||||||
|
|
||||||
|
CGPoint newOffset = CGPointMake(_svcv.scrollView.contentOffset.x, 0.0);
|
||||||
|
[self scrollToOffset:newOffset animated:true];
|
||||||
|
|
||||||
|
[rc endRefreshing];
|
||||||
|
}
|
||||||
|
|
||||||
Class<RCTComponentViewProtocol> ScrollForwarderViewCls(void)
|
Class<RCTComponentViewProtocol> ScrollForwarderViewCls(void)
|
||||||
{
|
{
|
||||||
return ScrollForwarderView.class;
|
return ScrollForwarderView.class;
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati
|
|||||||
type OnRefreshEvent = {}
|
type OnRefreshEvent = {}
|
||||||
|
|
||||||
export interface NativeProps extends ViewProps {
|
export interface NativeProps extends ViewProps {
|
||||||
scrollViewTag?: Int32
|
scrollViewTag: Int32 | null
|
||||||
onRefresh?: BubblingEventHandler<OnRefreshEvent>
|
|
||||||
refreshing?: boolean
|
refreshing?: boolean
|
||||||
|
onRefresh?: BubblingEventHandler<OnRefreshEvent>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default codegenNativeComponent<NativeProps>('ScrollForwarderView')
|
export default codegenNativeComponent<NativeProps>('ScrollForwarderView')
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {isIOS, isNative} from '#/platform/detection'
|
|||||||
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
||||||
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
|
||||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||||
import {PostFeed} from '#/view/com/posts/PostFeed'
|
import {PostFeed, PostFeedRef} from '#/view/com/posts/PostFeed'
|
||||||
import {EmptyState} from '#/view/com/util/EmptyState'
|
import {EmptyState} from '#/view/com/util/EmptyState'
|
||||||
import {type ListRef} from '#/view/com/util/List'
|
import {type ListRef} from '#/view/com/util/List'
|
||||||
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
|
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
|
||||||
@@ -24,6 +24,8 @@ interface FeedSectionProps {
|
|||||||
scrollElRef: ListRef
|
scrollElRef: ListRef
|
||||||
ignoreFilterFor?: string
|
ignoreFilterFor?: string
|
||||||
setScrollViewTag: (tag: number | null) => void
|
setScrollViewTag: (tag: number | null) => void
|
||||||
|
postFeedRef?: React.RefObject<PostFeedRef | undefined>
|
||||||
|
onRefreshEnd?: () => void
|
||||||
}
|
}
|
||||||
export const ProfileFeedSection = React.forwardRef<
|
export const ProfileFeedSection = React.forwardRef<
|
||||||
SectionRef,
|
SectionRef,
|
||||||
@@ -36,6 +38,8 @@ export const ProfileFeedSection = React.forwardRef<
|
|||||||
scrollElRef,
|
scrollElRef,
|
||||||
ignoreFilterFor,
|
ignoreFilterFor,
|
||||||
setScrollViewTag,
|
setScrollViewTag,
|
||||||
|
postFeedRef,
|
||||||
|
onRefreshEnd,
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
@@ -91,6 +95,8 @@ export const ProfileFeedSection = React.forwardRef<
|
|||||||
shouldUseAdjustedNumToRender ? adjustedInitialNumToRender : undefined
|
shouldUseAdjustedNumToRender ? adjustedInitialNumToRender : undefined
|
||||||
}
|
}
|
||||||
isVideoFeed={isVideoFeed}
|
isVideoFeed={isVideoFeed}
|
||||||
|
ref={postFeedRef}
|
||||||
|
onRefreshEnd={onRefreshEnd}
|
||||||
/>
|
/>
|
||||||
{(isScrolledDown || hasNew) && (
|
{(isScrolledDown || hasNew) && (
|
||||||
<LoadLatestBtn
|
<LoadLatestBtn
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {memo, useCallback, useRef} from 'react'
|
import React, {memo, useCallback, useImperativeHandle, useRef} from 'react'
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
AppState,
|
AppState,
|
||||||
@@ -150,6 +150,10 @@ export function getItemsForFeedback(feedRow: FeedRow):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PostFeedRef = {
|
||||||
|
refreshFeed: () => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
// DISABLED need to check if this is causing random feed refreshes -prf
|
// DISABLED need to check if this is causing random feed refreshes -prf
|
||||||
// const REFRESH_AFTER = STALE.HOURS.ONE
|
// const REFRESH_AFTER = STALE.HOURS.ONE
|
||||||
const CHECK_LATEST_AFTER = STALE.SECONDS.THIRTY
|
const CHECK_LATEST_AFTER = STALE.SECONDS.THIRTY
|
||||||
@@ -176,6 +180,7 @@ let PostFeed = ({
|
|||||||
savedFeedConfig,
|
savedFeedConfig,
|
||||||
initialNumToRender: initialNumToRenderOverride,
|
initialNumToRender: initialNumToRenderOverride,
|
||||||
isVideoFeed = false,
|
isVideoFeed = false,
|
||||||
|
ref,
|
||||||
}: {
|
}: {
|
||||||
feed: FeedDescriptor
|
feed: FeedDescriptor
|
||||||
feedParams?: FeedParams
|
feedParams?: FeedParams
|
||||||
@@ -198,6 +203,7 @@ let PostFeed = ({
|
|||||||
savedFeedConfig?: AppBskyActorDefs.SavedFeed
|
savedFeedConfig?: AppBskyActorDefs.SavedFeed
|
||||||
initialNumToRender?: number
|
initialNumToRender?: number
|
||||||
isVideoFeed?: boolean
|
isVideoFeed?: boolean
|
||||||
|
ref?: React.ForwardedRef<PostFeedRef>
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
@@ -583,22 +589,31 @@ let PostFeed = ({
|
|||||||
|
|
||||||
// events
|
// events
|
||||||
// =
|
// =
|
||||||
|
//
|
||||||
|
|
||||||
const onRefresh = React.useCallback(async () => {
|
const refreshFeed = async () => {
|
||||||
logEvent('feed:refresh', {
|
logEvent('feed:refresh', {
|
||||||
feedType: feedType,
|
feedType: feedType,
|
||||||
feedUrl: feed,
|
feedUrl: feed,
|
||||||
reason: 'pull-to-refresh',
|
reason: 'pull-to-refresh',
|
||||||
})
|
})
|
||||||
setIsPTRing(true)
|
|
||||||
try {
|
try {
|
||||||
await refetch()
|
await refetch()
|
||||||
onHasNew?.(false)
|
onHasNew?.(false)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Failed to refresh posts feed', {message: err})
|
logger.error('Failed to refresh posts feed', {message: err})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onRefresh = async () => {
|
||||||
|
setIsPTRing(true)
|
||||||
|
await refreshFeed()
|
||||||
setIsPTRing(false)
|
setIsPTRing(false)
|
||||||
}, [refetch, setIsPTRing, onHasNew, feed, feedType])
|
}
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
refreshFeed,
|
||||||
|
}))
|
||||||
|
|
||||||
const onEndReached = React.useCallback(async () => {
|
const onEndReached = React.useCallback(async () => {
|
||||||
if (isFetching || !hasNextPage || isError) return
|
if (isFetching || !hasNextPage || isError) return
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, {useCallback, useMemo} from 'react'
|
import React, {useCallback, useMemo, useRef, useState} from 'react'
|
||||||
import {StyleSheet} from 'react-native'
|
import {StyleSheet} from 'react-native'
|
||||||
import {SafeAreaView} from 'react-native-safe-area-context'
|
import {SafeAreaView} from 'react-native-safe-area-context'
|
||||||
import {
|
import {
|
||||||
@@ -46,8 +46,8 @@ import * as Layout from '#/components/Layout'
|
|||||||
import {ScreenHider} from '#/components/moderation/ScreenHider'
|
import {ScreenHider} from '#/components/moderation/ScreenHider'
|
||||||
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
|
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
|
||||||
import {navigate} from '#/Navigation'
|
import {navigate} from '#/Navigation'
|
||||||
import {ExpoScrollForwarderView} from '../../../modules/expo-scroll-forwarder'
|
|
||||||
import {ScrollForwarderView} from 'modules/react-native-scroll-forwarder/src'
|
import {ScrollForwarderView} from 'modules/react-native-scroll-forwarder/src'
|
||||||
|
import {PostFeedRef} from '../com/posts/PostFeed'
|
||||||
|
|
||||||
interface SectionRef {
|
interface SectionRef {
|
||||||
scrollToTop: () => void
|
scrollToTop: () => void
|
||||||
@@ -179,6 +179,7 @@ function ProfileScreenLoaded({
|
|||||||
enabled: !!profile.associated?.labeler,
|
enabled: !!profile.associated?.labeler,
|
||||||
})
|
})
|
||||||
const [currentPage, setCurrentPage] = React.useState(0)
|
const [currentPage, setCurrentPage] = React.useState(0)
|
||||||
|
const [isRefreshing, setIsRefreshing] = useState(false)
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
|
||||||
const [scrollViewTag, setScrollViewTag] = React.useState<number | null>(null)
|
const [scrollViewTag, setScrollViewTag] = React.useState<number | null>(null)
|
||||||
@@ -335,6 +336,14 @@ function ProfileScreenLoaded({
|
|||||||
scrollSectionToTop(index)
|
scrollSectionToTop(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const postFeedRef = useRef<PostFeedRef>()
|
||||||
|
|
||||||
|
const onRefresh = async () => {
|
||||||
|
setIsRefreshing(true)
|
||||||
|
await postFeedRef.current?.refreshFeed()
|
||||||
|
setIsRefreshing(false)
|
||||||
|
}
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
// =
|
// =
|
||||||
|
|
||||||
@@ -344,7 +353,10 @@ function ProfileScreenLoaded({
|
|||||||
setMinimumHeight: (height: number) => void
|
setMinimumHeight: (height: number) => void
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<ScrollForwarderView scrollViewTag={scrollViewTag}>
|
<ScrollForwarderView
|
||||||
|
scrollViewTag={scrollViewTag}
|
||||||
|
refreshing={isRefreshing}
|
||||||
|
onRefresh={onRefresh}>
|
||||||
<ProfileHeader
|
<ProfileHeader
|
||||||
profile={profile}
|
profile={profile}
|
||||||
labeler={labelerInfo}
|
labeler={labelerInfo}
|
||||||
@@ -409,6 +421,7 @@ function ProfileScreenLoaded({
|
|||||||
scrollElRef={scrollElRef as ListRef}
|
scrollElRef={scrollElRef as ListRef}
|
||||||
ignoreFilterFor={profile.did}
|
ignoreFilterFor={profile.did}
|
||||||
setScrollViewTag={setScrollViewTag}
|
setScrollViewTag={setScrollViewTag}
|
||||||
|
postFeedRef={postFeedRef}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
|
|||||||
Reference in New Issue
Block a user