react-native#NativeScrollEvent TypeScript Examples
The following examples show how to use
react-native#NativeScrollEvent.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx From react-native-actions-sheet with MIT License | 7 votes |
_onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
this.offsetY = event.nativeEvent.contentOffset.y;
let correction = this.state.deviceHeight * 0.15;
let distanceFromTop = this.actionSheetHeight + correction - this.offsetY;
if (this.actionSheetHeight < this.offsetY) {
if (!this.isReachedTop) {
this.isReachedTop = true;
this.props.onPositionChanged && this.props.onPositionChanged(true);
}
} else {
if (this.isReachedTop) {
this.isReachedTop = false;
this.props.onPositionChanged && this.props.onPositionChanged(false);
}
}
if (this.actionSheetHeight >= this.state.deviceHeight - 1) {
if (distanceFromTop < this.state.paddingTop) {
if (!this.props.drawUnderStatusBar) return;
this.indicatorTranslateY.setValue(
-this.state.paddingTop + (this.state.paddingTop - distanceFromTop)
);
} else {
this.indicatorTranslateY.setValue(-this.state.paddingTop);
}
}
};
Example #2
Source File: index.d.ts From react-native-actions-sheet with MIT License | 6 votes |
_onScrollBegin: (_event: NativeSyntheticEvent<NativeScrollEvent>) => Promise<void>;
Example #3
Source File: index.d.ts From react-native-actions-sheet with MIT License | 5 votes |
_onScrollBeginDrag: (event: NativeSyntheticEvent<NativeScrollEvent>) => Promise<void>;
Example #4
Source File: index.d.ts From react-native-actions-sheet with MIT License | 5 votes |
_onScrollEnd: (event: NativeSyntheticEvent<NativeScrollEvent>) => Promise<void>;
Example #5
Source File: index.d.ts From react-native-actions-sheet with MIT License | 5 votes |
_onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
Example #6
Source File: index.tsx From react-native-actions-sheet with MIT License | 5 votes |
_onScrollBegin = async (
_event: NativeSyntheticEvent<NativeScrollEvent>
) => {};
Example #7
Source File: index.tsx From react-native-actions-sheet with MIT License | 5 votes |
_onScrollBeginDrag = async (
event: NativeSyntheticEvent<NativeScrollEvent>
) => {
let verticalOffset = event.nativeEvent.contentOffset.y;
this.prevScroll = verticalOffset;
};
Example #8
Source File: index.tsx From react-native-actions-sheet with MIT License | 5 votes |
_onScrollEnd = async (event: NativeSyntheticEvent<NativeScrollEvent>) => {
let { springOffset, extraScroll } = this.props;
let verticalOffset = event.nativeEvent.contentOffset.y;
let correction = this.state.deviceHeight * 0.15;
if (this.isRecoiling) return;
if (this.prevScroll < verticalOffset || this.initialScrolling) {
if (
verticalOffset - this.prevScroll > (springOffset ?? 100) * 0.75 ||
this.initialScrolling
) {
this.isRecoiling = true;
this._applyHeightLimiter();
this.currentOffsetFromBottom =
this.currentOffsetFromBottom <
(this.props.initialOffsetFromBottom ?? 1)
? this.props.initialOffsetFromBottom ?? 1
: 1;
let scrollOffset =
this.actionSheetHeight * this.currentOffsetFromBottom +
correction +
(extraScroll ?? 100);
if (this.initialScrolling) {
this.initialScrolling = false;
scrollOffset = this.prevScroll;
}
this._scrollTo(scrollOffset);
await waitAsync(300);
this.isRecoiling = false;
this.props.onPositionChanged && this.props.onPositionChanged(true);
} else {
this._returnToPrevScrollPosition(this.actionSheetHeight);
}
} else {
if (this.prevScroll - verticalOffset > (springOffset ?? 100)) {
this._hideModal(null);
} else {
if (this.isRecoiling) {
return;
}
this.isRecoiling = true;
this._returnToPrevScrollPosition(this.actionSheetHeight);
await waitAsync(300);
this.isRecoiling = false;
}
}
};
Example #9
Source File: MessageDetail.tsx From lexicon with MIT License | 4 votes |
export default function MessageDetail() {
const styles = useStyles();
const { colors } = useTheme();
const storage = useStorage();
const user = storage.getItem('user');
const { authorizedExtensions } = useSiteSettings();
const extensions = authorizedExtensions?.split('|');
const normalizedExtensions = formatExtensions(extensions);
const ios = Platform.OS === 'ios';
const screen = Dimensions.get('screen');
const { navigate } = useNavigation<StackNavProp<'MessageDetail'>>();
const {
params: {
id,
postPointer,
emptied,
hyperlinkUrl = '',
hyperlinkTitle = '',
},
} = useRoute<StackRouteProp<'MessageDetail'>>();
const [hasOlderMessages, setHasOlderMessages] = useState(true);
const [hasNewerMessages, setHasNewerMessages] = useState(true);
const [loadingOlderMessages, setLoadingOlderMessages] = useState(false);
const [loadingNewerMessages, setLoadingNewerMessages] = useState(false);
const [refetching, setRefetching] = useState(false);
const [isInitialRequest, setIsInitialRequest] = useState(true);
const [textInputFocused, setInputFocused] = useState(false);
const [title, setTitle] = useState('');
const [message, setMessage] = useState('');
const [startIndex, setStartIndex] = useState(0);
const [endIndex, setEndIndex] = useState(0);
const [initialHeight, setInitialHeight] = useState<number>();
const [data, setData] = useState<Message>();
const [members, setMembers] = useState<Array<User>>([]);
const [userWhoComment, setUserWhoComment] = useState<Array<User>>([]);
const [stream, setStream] = useState<Array<number>>([]);
const virtualListRef = useRef<VirtualizedList<MessageContent>>(null);
const [showUserList, setShowUserList] = useState(false);
const [mentionLoading, setMentionLoading] = useState(false);
const [mentionKeyword, setMentionKeyword] = useState('');
const [cursorPosition, setCursorPosition] = useState<CursorPosition>({
start: 0,
end: 0,
});
let contentHeight = initialHeight ? initialHeight : 0;
const messageRef = useRef<TextInputType>(null);
const { mentionMembers } = useMention(
mentionKeyword,
showUserList,
setMentionLoading,
);
const {
data: baseData,
loading: messageDetailLoading,
refetch,
fetchMore,
} = useTopicDetail({
variables: {
topicId: id,
postPointer,
},
onCompleted: ({ topicDetail: result }) => {
if (result) {
setIsInitialRequest(true);
setTitle(result.title || '');
const tempParticipants: Array<User> = [];
result.details?.allowedUsers?.forEach((allowedUser) =>
tempParticipants.push({
id: allowedUser.id,
username: allowedUser.username,
avatar: getImage(allowedUser.avatarTemplate),
}),
);
setMembers(tempParticipants);
let userWhoComment: Array<User> = [];
result.details?.participants.forEach((user) => {
userWhoComment.push({
id: user.id,
username: user.username,
avatar: getImage(user.avatar),
});
});
setUserWhoComment(userWhoComment);
}
},
onError: (error) => {
loadingOlderMessages && setLoadingOlderMessages(false);
errorHandlerAlert(error);
},
fetchPolicy: 'cache-and-network',
});
useEffect(() => {
if (emptied) {
setMessage('');
}
}, [emptied]);
useEffect(() => {
if (!baseData) {
return;
}
const {
topicDetail: { postStream, details },
} = baseData;
const {
data: tempData,
hasNewerMessage: newMessage,
hasOlderMessage: oldMessage,
baseStream,
firstPostIndex,
lastPostIndex,
} = messageDetailHandler({ postStream, details });
setData(tempData);
setStream(baseStream);
setHasNewerMessages(newMessage);
setHasOlderMessages(oldMessage);
setStartIndex(firstPostIndex);
setEndIndex(lastPostIndex);
}, [baseData]);
useEffect(() => {
if (!refetching) {
return;
}
virtualListRef.current?.scrollToEnd({ animated: false });
setRefetching(false);
}, [refetching]);
useEffect(() => {
if (!hyperlinkUrl) {
return;
}
const { newUrl, newTitle } = getHyperlink(hyperlinkUrl, hyperlinkTitle);
const result = insertHyperlink(message, newTitle, newUrl);
setMessage(result);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hyperlinkTitle, hyperlinkUrl]);
const { reply, loading: replyLoading } = useReplyPost({
onCompleted: () => {
setMessage('');
refetch({ postPointer: stream.length || 1 }).then(() => {
if (textInputFocused && data?.contents.length) {
if (ios) {
virtualListRef.current?.scrollToIndex({
index: data.contents.length,
animated: true,
});
} else {
setTimeout(() => {
virtualListRef.current?.scrollToIndex({
index: data.contents.length,
animated: true,
});
}, 500);
}
}
setRefetching(true);
});
},
});
useMessageTiming(id, startIndex, data?.contents);
const loadStartMore = async () => {
if (
loadingOlderMessages ||
!hasOlderMessages ||
!stream ||
messageDetailLoading
) {
return;
}
setLoadingOlderMessages(true);
let nextEndIndex = startIndex;
let newDataCount = Math.min(10, stream.length - nextEndIndex);
let nextStartIndex = Math.max(0, nextEndIndex - newDataCount);
let nextPosts = stream.slice(nextStartIndex, nextEndIndex);
if (!nextPosts.length) {
return;
}
await fetchMore({
variables: {
topicId: id,
posts: nextPosts,
},
}).then(() => {
setStartIndex(nextStartIndex);
setLoadingOlderMessages(false);
});
};
const loadEndMore = async () => {
if (
loadingNewerMessages ||
!hasNewerMessages ||
!stream ||
messageDetailLoading
) {
return;
}
setLoadingNewerMessages(true);
let nextStartIndex = endIndex + 1;
let newDataCount = Math.min(10, stream.length - nextStartIndex);
let nextEndIndex = nextStartIndex + newDataCount;
let nextPosts = stream.slice(nextStartIndex, nextEndIndex);
if (!nextPosts.length) {
return;
}
await fetchMore({
variables: {
topicId: id,
posts: nextPosts,
},
});
setEndIndex(nextEndIndex - 1);
setLoadingNewerMessages(false);
};
const onPressSend = (message: string) => {
setShowUserList(false);
if (message.trim() !== '') {
reply({
variables: {
replyInput: {
topicId: id,
raw: message,
},
},
});
}
};
const onPressImage = async () => {
try {
let result = await imagePickerHandler(normalizedExtensions);
if (!user || !result || !result.uri) {
return;
}
let imageUri = result.uri;
Keyboard.dismiss();
navigate('ImagePreview', {
topicId: id,
imageUri,
postPointer: stream.length,
message,
});
} catch (error) {
errorHandlerAlert(error);
}
return;
};
const compareTime = (currIndex: number) => {
if (currIndex === 0) {
return true;
}
const currContentTime = data
? data.contents[currIndex].time
: new Date().toDateString();
const prevContentTime = data
? data.contents[currIndex - 1].time
: new Date().toDateString();
const time = new Date(currContentTime);
const prevTime = new Date(prevContentTime);
return (time.getTime() - prevTime.getTime()) / (1000 * 60) > 15;
};
const isPrev = (currIndex: number) => {
if (data) {
if (currIndex === 0) {
return;
}
const currUserId = data.contents[currIndex].userId;
const prevUserId = data.contents[currIndex - 1].userId;
return currUserId === prevUserId;
}
return false;
};
const settings = (operation: Operation, currIndex: number) => {
if (currIndex === -1) {
return operation === Operation.USER;
}
const isPrevUser = isPrev(currIndex);
if (!isPrevUser) {
return operation === Operation.USER;
}
if (isPrevUser) {
return operation === Operation.USER
? compareTime(currIndex)
: !compareTime(currIndex);
}
return false;
};
const onPressLink = () => {
navigate('HyperLink', {
id,
postPointer,
prevScreen: 'MessageDetail',
});
};
const onPressAvatar = (username: string) => {
navigate('UserInformation', { username });
};
const renderItem = ({ item, index }: MessageDetailRenderItem) => {
let user;
if (item.userId === 0) {
user = members.find((member) => member.id === -1);
} else {
user = userWhoComment.find((member) => member.id === item.userId);
}
const newTimestamp = compareTime(index);
const isPrevUser = isPrev(index);
const currSettings = settings(Operation.USER, index);
const senderUsername = user?.username || '';
return (
<MessageItem
content={item}
sender={user}
newTimestamp={newTimestamp}
isPrev={isPrevUser}
settings={currSettings}
onPressAvatar={() => onPressAvatar(senderUsername)}
/>
);
};
const keyExtractor = ({ id }: MessageContent) => `message-${id}`;
const getItem = (data: Array<MessageContent>, index: number) => data[index];
const getItemCount = (data: Array<MessageContent>) => data?.length;
const renderFooter = (
<KeyboardAccessoryView
androidAdjustResize
inSafeAreaView
hideBorder
alwaysVisible
style={styles.keyboardAcc}
>
<MentionList
showUserList={showUserList}
members={mentionMembers}
mentionLoading={mentionLoading}
rawText={message}
textRef={messageRef}
setRawText={setMessage}
setShowUserList={setShowUserList}
/>
<View style={styles.footerContainer}>
<Icon
name="Photo"
style={styles.footerIcon}
onPress={onPressImage}
color={colors.textLighter}
/>
<Icon
name="Link"
style={styles.footerIcon}
onPress={onPressLink}
color={colors.textLighter}
/>
<ReplyInputField
inputRef={messageRef}
loading={replyLoading}
onPressSend={onPressSend}
style={styles.inputContainer}
message={message}
setMessage={setMessage}
onSelectedChange={(cursor) => {
setCursorPosition(cursor);
}}
onChangeValue={(message: string) => {
mentionHelper(
message,
cursorPosition,
setShowUserList,
setMentionLoading,
setMentionKeyword,
);
setMessage(message);
}}
onFocus={() => {
setInputFocused(true);
if (contentHeight) {
setTimeout(() => {
virtualListRef.current?.scrollToOffset({
offset: contentHeight + (37 * screen.height) / 100,
});
}, 50);
}
}}
onBlur={() => {
setInputFocused(false);
if (contentHeight) {
setTimeout(() => {
virtualListRef.current?.scrollToOffset({
offset: contentHeight - (37 * screen.height) / 100,
});
}, 50);
}
}}
/>
</View>
</KeyboardAccessoryView>
);
const onMessageScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
if (!initialHeight) {
setInitialHeight(
Math.round(
event.nativeEvent.contentSize.height -
event.nativeEvent.layoutMeasurement.height,
),
);
}
contentHeight = event.nativeEvent.contentOffset.y;
};
const onMessageScrollHandler = ({ index }: OnScrollInfo) => {
if (index) {
setTimeout(
() =>
virtualListRef.current?.scrollToIndex({
animated: true,
index,
}),
ios ? 50 : 150,
);
}
};
const onContentSizeChange = () => {
if (isInitialRequest) {
let pointerToIndex = postPointer - 1 - startIndex;
let index = Math.min(19, pointerToIndex);
try {
virtualListRef.current?.scrollToIndex({
animated: true,
index,
});
} catch {
virtualListRef.current?.scrollToEnd();
}
setTimeout(() => setIsInitialRequest(false), 1000);
}
};
if (messageDetailLoading && title === '') {
return <LoadingOrError loading />;
}
return (
<SafeAreaView style={styles.container}>
{ios ? (
<CustomHeader title={t('Message')} />
) : (
<Divider style={styles.divider} />
)}
<AvatarRow
title={title}
posters={members}
style={styles.participants}
extended
/>
<VirtualizedList
ref={virtualListRef}
refreshControl={
<RefreshControl
refreshing={refetching || loadingOlderMessages}
onRefresh={loadStartMore}
tintColor={colors.primary}
/>
}
data={data?.contents}
getItem={getItem}
getItemCount={getItemCount}
renderItem={renderItem}
keyExtractor={keyExtractor}
contentInset={{
bottom: textInputFocused ? (35 * screen.height) / 100 : 0,
top: contentHeight ? ((5 * screen.height) / 100) * -1 : 0,
}}
onEndReachedThreshold={0.1}
onEndReached={loadEndMore}
onContentSizeChange={onContentSizeChange}
contentContainerStyle={styles.messages}
ListFooterComponent={
<FooterLoadingIndicator isHidden={!hasNewerMessages} />
}
onScroll={onMessageScroll}
onScrollToIndexFailed={onMessageScrollHandler}
/>
{renderFooter}
</SafeAreaView>
);
}
Example #10
Source File: index.tsx From krmanga with MIT License | 4 votes |
function Brief({
navigation, dispatch, isLogin, headerHeight, bookInfo, book_id, markRoast, markChapterNum,
loading, collection_id, refreshing, chapterList
}: IProps) {
const [showTop, setShowTop] = useState<boolean>(true);
const scrollY: Animated.Value = useRef(new Animated.Value(0)).current;
const drawerX: Animated.Value = useRef(new Animated.Value(viewportWidth)).current;
const fixedHeight = headerHeight + imageHeight;
let compHeight: number;
if (Platform.OS === "android") {
compHeight = 30 - 11;
} else {
compHeight = isIphoneX() ? 30 - 22 : 30 - 11 + getStatusBarHeight();
}
const stickyHeader = fixedHeight + compHeight;
useEffect(() => {
loadData(true);
return () => {
dispatch({
type: "brief/setState",
payload: {
...initialState
}
});
};
}, []);
const getOpacity = () => {
return scrollY.interpolate({
inputRange: [
headerHeight,
fixedHeight
],
outputRange: [1, 0],
extrapolate: "clamp"
});
};
const getBlurOpacity = () => {
return scrollY.interpolate({
inputRange: [
stickyHeader - 1,
stickyHeader
],
outputRange: [0, 1],
extrapolate: "clamp"
});
};
const getLeftViewX = () => {
return scrollY.interpolate({
inputRange: [
headerHeight,
fixedHeight
],
outputRange: [0, wp(22)],
extrapolate: "clamp"
});
};
const getRightViewX = () => {
return scrollY.interpolate({
inputRange: [
headerHeight,
fixedHeight
],
outputRange: [0, wp(10)],
extrapolate: "clamp"
});
};
const getRightViewScale = () => {
return scrollY.interpolate({
inputRange: [
headerHeight,
fixedHeight
],
outputRange: [1, 0.65],
extrapolate: "clamp"
});
};
const getRightFontSize = () => {
return scrollY.interpolate({
inputRange: [
headerHeight,
fixedHeight
],
outputRange: [1, 1.5],
extrapolate: "clamp"
});
};
const getBgImageSize = () => {
return scrollY.interpolate({
inputRange: [-100, 0],
outputRange: [1.2, 1],
extrapolate: "clamp"
});
};
const onClickCollection = useCallback(() => {
if (!isLogin) {
navigation.navigate("Login");
} else {
if (collection_id > 0) {
dispatch({
type: "brief/delUserCollection",
payload: {
id: collection_id.toString()
}
});
} else {
dispatch({
type: "brief/addUserCollection",
payload: {
book_id
}
});
}
dispatch({
type: "collection/screenReload"
});
}
}, [isLogin, collection_id]);
const onClickRead = useCallback(() => {
if (markRoast > 0) {
navigation.navigate("MangaView", {
book_id,
markRoast,
chapter_num: markChapterNum
});
} else {
navigation.navigate("MangaView", {
book_id,
chapter_num: 1
});
}
}, [markRoast]);
const goMangaView = useCallback((item: IChapter) => {
navigation.navigate("MangaView", {
book_id,
chapter_num: item.chapter_num
});
}, []);
const showDrawer = () => {
Animated.timing(drawerX, {
toValue: 0,
duration: 200,
useNativeDriver: true
}).start();
};
const hideDrawer = () => {
Animated.timing(drawerX, {
toValue: viewportWidth,
duration: 200,
useNativeDriver: true
}).start();
};
const onScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
if (event.nativeEvent.contentOffset.y >= fixedHeight) {
setShowTop(false);
} else {
setShowTop(true);
}
};
const loadData = (refreshing: boolean, callback?: () => void) => {
dispatch({
type: "brief/fetchBrief",
payload: {
refreshing,
book_id
},
callback
});
};
return (
(loading && refreshing) ? <BriefPlaceholder /> :
<View style={styles.container}>
<LightDrawer
chapterList={chapterList}
bookInfo={bookInfo}
headerHeight={headerHeight}
drawerX={drawerX}
goMangaView={goMangaView}
hideDrawer={hideDrawer}
/>
<ImageBlurBackground
bookInfo={bookInfo}
imageSize={getBgImageSize()}
/>
<TopBarWrapper
book_id={book_id}
headerHeight={headerHeight}
showTop={showTop}
opacity={getOpacity()}
/>
<Animated.ScrollView
onScroll={Animated.event(
[
{
nativeEvent: { contentOffset: { y: scrollY } }
}
],
{
useNativeDriver: true,
listener: onScroll
}
)}
overScrollMode="always"
scrollEventThrottle={1}
>
<Header
stickyHeader={stickyHeader}
compHeight={compHeight}
scrollY={scrollY}
opacity={getOpacity()}
blurOpacity={getBlurOpacity()}
leftViewX={getLeftViewX()}
rightViewX={getRightViewX()}
rightViewScale={getRightViewScale()}
rightFontSize={getRightFontSize()}
showDrawer={showDrawer}
onClickRead={onClickRead}
onClickCollection={onClickCollection}
/>
<List
chapterList={chapterList}
goMangaView={goMangaView}
/>
<Footer />
</Animated.ScrollView>
</View>
);
}
Example #11
Source File: index.tsx From krmanga with MIT License | 4 votes |
function Category({ dispatch, navigation, category_id, activeStatus, activeModel, bookList, loading, hasMore, refreshing }: IProps) {
let scrollViewStartOffsetY: number = 0;
const [endReached, setEndReached] = useState<boolean>(false);
useFocusEffect(
React.useCallback(() => {
dispatch({
type: "category/setState",
payload: {
activeCategory: category_id
}
});
loadData(true);
}, [activeStatus])
);
const loadData = (refreshing: boolean, callback?: () => void) => {
dispatch({
type: `${activeModel}/fetchBookList`,
payload: {
refreshing,
category_id
},
callback
});
};
const goBrief = useCallback((data: IBook) => {
navigation.navigate("Brief", {
id: data.id
});
}, []);
const renderItem = ({ item }: ListRenderItemInfo<IBook>) => {
return (
<BookCover
key={item.id}
data={item}
goBrief={goBrief}
/>
);
};
const onRefresh = () => {
dispatch({
type: `${activeModel}/fetchBookList`,
payload: {
refreshing: true,
onRefresh: true,
category_id
}
});
};
const renderFooter = () => {
if (endReached) {
return <More />;
}
if (!hasMore) {
return <End />;
}
return null;
};
const onScrollBeginDrag = ({ nativeEvent }: NativeSyntheticEvent<NativeScrollEvent>) => {
scrollViewStartOffsetY = nativeEvent.contentOffset.y;
};
const onScrollEndDrag = ({ nativeEvent }: NativeSyntheticEvent<NativeScrollEvent>) => {
if (scrollViewStartOffsetY > nativeEvent.contentOffset.y) {
dispatch({
type: "category/setState",
payload: {
hideHeader: false
}
});
} else {
dispatch({
type: "category/setState",
payload: {
hideHeader: true
}
});
}
};
const onEndReached = () => {
if (!hasMore || loading) {
return;
}
setEndReached(true);
loadData(false, () => {
setEndReached(false);
});
};
return (
(loading && refreshing) ? <BookPlaceholder /> :
<FlatList
keyExtractor={(item, key) => `item-${item.id}-key-${key}`}
data={bookList}
extraData={endReached}
renderItem={renderItem}
refreshing={refreshing}
style={styles.container}
onRefresh={onRefresh}
ListFooterComponent={renderFooter}
scrollEventThrottle={1}
onScrollBeginDrag={onScrollBeginDrag}
onScrollEndDrag={onScrollEndDrag}
numColumns={3}
onEndReached={onEndReached}
onEndReachedThreshold={0.1}
/>
);
}
Example #12
Source File: index.tsx From krmanga with MIT License | 4 votes |
function MangaView({
navigation, dispatch, isLogin, chapterList, bookInfo,
book_id, headerHeight, markRoast, chapter_num, episodeList, hasMore, loading,
currentChapterNum, pages
}: IProps) {
const [endReached, setEndReached] = useState<boolean>(false);
let [time, setTime] = useState<NodeJS.Timeout | null>(null);
let flatListRef: FlatList<IEpisode> | null = null;
const topPanelValue = useRef(new Animated.Value(0)).current;
const bottomPanelValue = useRef(new Animated.Value(0)).current;
const drawerX = useRef(new Animated.Value(-viewportWidth)).current;
let panelEnable: boolean = true;
useEffect(() => {
loadData(true);
return () => {
StatusBar.setHidden(false);
if (isLogin) {
dispatch({
type: "mangaView/addHistory",
payload: {
book_id
}
});
dispatch({
type: "history/setScreenReload"
});
dispatch({
type: "downloadManage/setScreenReload"
});
}
dispatch({
type: "mangaView/setState",
payload: {
...initialState
}
});
};
}, []);
const loadData = (refreshing: boolean, callback?: () => void) => {
dispatch({
type: "mangaView/fetchEpisodeList",
payload: {
refreshing,
markRoast,
chapter_num,
book_id
},
callback
});
};
const onEndReached = () => {
if (!hasMore || loading) {
return;
}
setEndReached(true);
dispatch({
type: "mangaView/fetchEpisodeList",
payload: {
book_id,
chapter_num: currentChapterNum + 1
},
callback: () => {
setEndReached(false);
}
});
};
const renderItem = ({ item }: ListRenderItemInfo<IEpisode>) => {
return (
<Item panelHandle={panelHandle} data={item} />
);
};
const renderFooter = () => {
if (endReached) {
return <More />;
}
if (!hasMore) {
return <End />;
}
return null;
};
const getItemLayout = (data: any, index: number) => {
if (data[index] === undefined) {
return { length: 0, offset: 0, index };
}
let offset = 0;
const length = viewportWidth * data[index].multiple;
for (let i = 0; i < index; i++) {
offset += viewportWidth * data[i].multiple;
}
return { length: length, offset, index };
};
const scrollToIndex = (index: number) => {
dispatch({
type: "brief/setState",
payload: {
markChapterNum: episodeList[index].chapter_num,
markRoast: episodeList[index].roast
}
});
flatListRef?.scrollToIndex({ viewPosition: 0, index: index });
};
const lastChapter = () => {
if (!loading) {
dispatch({
type: "mangaView/fetchEpisodeList",
payload: {
refreshing: true,
book_id,
chapter_num: currentChapterNum - 1
}
});
}
};
const nextChapter = () => {
if (!loading) {
dispatch({
type: "mangaView/fetchEpisodeList",
payload: {
refreshing: true,
book_id,
chapter_num: currentChapterNum + 1
}
});
}
};
const showDrawer = () => {
Animated.timing(drawerX, {
toValue: 0,
duration: 200,
useNativeDriver: true
}).start();
};
const hideDrawer = () => {
Animated.timing(drawerX, {
toValue: -viewportWidth,
duration: 200,
useNativeDriver: true
}).start();
};
const hidePanel = () => {
if (panelEnable) {
Animated.parallel([
Animated.timing(
topPanelValue,
{
toValue: -headerHeight - getStatusBarHeight(),
duration: 200,
easing: Easing.linear,
useNativeDriver: true
}
),
Animated.timing(
bottomPanelValue,
{
toValue: hp(25),
duration: 200,
easing: Easing.linear,
useNativeDriver: true
}
)
]).start(() => {
StatusBar.setHidden(true);
panelEnable = !panelEnable;
});
}
};
const showPanel = () => {
if (!panelEnable) {
Animated.parallel([
Animated.timing(
topPanelValue,
{
toValue: 0,
duration: 200,
easing: Easing.linear,
useNativeDriver: true
}
),
Animated.timing(
bottomPanelValue,
{
toValue: 0,
duration: 200,
easing: Easing.linear,
useNativeDriver: true
}
)
]).start(() => {
StatusBar.setHidden(false);
panelEnable = !panelEnable;
});
}
};
const panelHandle = useCallback(() => {
if (panelEnable) {
hidePanel();
} else {
showPanel();
}
}, [panelEnable]);
const debounce = (cb: any, wait = 250) => {
if (time !== null) {
clearTimeout(time);
}
let tempTime = setTimeout(() => {
time = null;
cb && cb();
}, wait);
setTime(tempTime);
};
const onScrollEndDrag = ({ nativeEvent }: NativeSyntheticEvent<NativeScrollEvent>) => {
let offset_total = 0;
let current_episode_total = episodeList[0].episode_total;
let current_chapter_id = episodeList[0].chapter_id;
let current_chapter_num = episodeList[0].chapter_num;
let current_number = episodeList[0].number;
let current_roast = episodeList[0].roast;
let current_title = episodeList[0].title;
for (let i = 0; i < episodeList.length; i++) {
if (nativeEvent.contentOffset.y >= offset_total) {
current_episode_total = episodeList[i].episode_total;
current_chapter_id = episodeList[i].chapter_id;
current_chapter_num = episodeList[i].chapter_num;
current_number = episodeList[i].number;
current_roast = episodeList[i].roast;
current_title = episodeList[i].title;
} else {
break;
}
offset_total = offset_total + episodeList[i].multiple * viewportWidth;
}
debounce(() => {
dispatch({
type: "mangaView/setState",
payload: {
currentEpisodeTotal: current_episode_total,
currentChapterId: current_chapter_id,
currentChapterNum: current_chapter_num,
currentNumber: current_number,
showCurrentNumber: current_number,
currentRoast: current_roast,
currentTitle: current_title
}
});
dispatch({
type: "brief/setState",
payload: {
markChapterNum: current_chapter_num,
markRoast: current_roast
}
});
});
hidePanel();
};
const goMangaChapter = useCallback((item: IChapter) => {
dispatch({
type: "mangaView/fetchEpisodeList",
payload: {
refreshing: true,
book_id,
chapter_num: item.chapter_num,
callback: () => hideDrawer
}
});
}, []);
return (
episodeList.length > 0 ?
<View>
<StatusBar barStyle="light-content" />
<TopCtrPanel
book_id={book_id}
topPanelValue={topPanelValue}
navigation={navigation}
/>
<BottomCtrPanel
bottomPanelValue={bottomPanelValue}
scrollToIndex={scrollToIndex}
showDrawer={showDrawer}
lastChapter={lastChapter}
nextChapter={nextChapter}
/>
<FlatList
ref={ref => (flatListRef = ref)}
data={episodeList}
keyExtractor={(item, key) => `item-${item.id}-key-${key}`}
renderItem={renderItem}
ListFooterComponent={renderFooter}
getItemLayout={getItemLayout}
onScrollEndDrag={onScrollEndDrag}
initialScrollIndex={pages.episode_offset - 1}
onEndReached={onEndReached}
onEndReachedThreshold={0.1}
extraData={endReached}
/>
<DarkDrawer
chapterList={chapterList}
bookInfo={bookInfo}
headerHeight={headerHeight}
drawerX={drawerX}
hideDrawer={hideDrawer}
goMangaView={goMangaChapter}
/>
<BottomStatusBar />
</View> : null
);
}
Example #13
Source File: index.tsx From react-native-wheel-scrollview-picker with MIT License | 4 votes |
export default function ScrollPicker({
itemHeight = 30,
style,
scrollViewComponent,
...props
}: ScrollPickerProps): JSX.Element {
const [initialized, setInitialized] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(
props.selectedIndex && props.selectedIndex >= 0 ? props.selectedIndex : 0
);
const sView = useRef<ScrollView>(null);
const [isScrollTo, setIsScrollTo] = useState(false);
const [dragStarted, setDragStarted] = useState(false);
const [momentumStarted, setMomentumStarted] = useState(false);
const [timer, setTimer] = useState<NodeJS.Timeout | null>(null);
const wrapperHeight =
props.wrapperHeight ||
(isViewStyle(style) && isNumeric(style.height)
? Number(style.height)
: 0) ||
itemHeight * 5;
useEffect(
function initialize() {
if (initialized) return;
setInitialized(true);
setTimeout(() => {
const y = itemHeight * selectedIndex;
sView?.current?.scrollTo({ y: y });
}, 0);
return () => {
timer && clearTimeout(timer);
};
},
[initialized, itemHeight, selectedIndex, sView, timer]
);
const renderPlaceHolder = () => {
const h = (wrapperHeight - itemHeight) / 2;
const header = <View style={{ height: h, flex: 1 }} />;
const footer = <View style={{ height: h, flex: 1 }} />;
return { header, footer };
};
const renderItem = (
data: ScrollPickerProps["dataSource"][0],
index: number
) => {
const isSelected = index === selectedIndex;
const item = props.renderItem ? (
props.renderItem(data, index, isSelected)
) : (
<Text
style={
isSelected
? [styles.itemText, styles.itemTextSelected]
: styles.itemText
}
>
{data}
</Text>
);
return (
<View style={[styles.itemWrapper, { height: itemHeight }]} key={index}>
{item}
</View>
);
};
const scrollFix = useCallback(
(e: NativeSyntheticEvent<NativeScrollEvent>) => {
let y = 0;
const h = itemHeight;
if (e.nativeEvent.contentOffset) {
y = e.nativeEvent.contentOffset.y;
}
const _selectedIndex = Math.round(y / h);
const _y = _selectedIndex * h;
if (_y !== y) {
// using scrollTo in ios, onMomentumScrollEnd will be invoked
if (Platform.OS === "ios") {
setIsScrollTo(true);
}
sView?.current?.scrollTo({ y: _y });
}
if (selectedIndex === _selectedIndex) {
return;
}
// onValueChange
if (props.onValueChange) {
const selectedValue = props.dataSource[_selectedIndex];
setSelectedIndex(_selectedIndex);
props.onValueChange(selectedValue, _selectedIndex);
}
},
[itemHeight, props, selectedIndex]
);
const onScrollBeginDrag = () => {
setDragStarted(true);
if (Platform.OS === "ios") {
setIsScrollTo(false);
}
timer && clearTimeout(timer);
};
const onScrollEndDrag = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
setDragStarted(false);
// if not used, event will be garbaged
const _e: NativeSyntheticEvent<NativeScrollEvent> = { ...e };
timer && clearTimeout(timer);
setTimer(
setTimeout(() => {
if (!momentumStarted) {
scrollFix(_e);
}
}, 50)
);
};
const onMomentumScrollBegin = () => {
setMomentumStarted(true);
timer && clearTimeout(timer);
};
const onMomentumScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
setMomentumStarted(false);
if (!isScrollTo && !dragStarted) {
scrollFix(e);
}
};
const { header, footer } = renderPlaceHolder();
const highlightWidth = (isViewStyle(style) ? style.width : 0) || deviceWidth;
const highlightColor = props.highlightColor || "#333";
const highlightBorderWidth =
props.highlightBorderWidth || StyleSheet.hairlineWidth;
const wrapperStyle: ViewStyle = {
height: wrapperHeight,
flex: 1,
backgroundColor: props.wrapperColor || "#fafafa",
overflow: "hidden",
};
const highlightStyle: ViewStyle = {
position: "absolute",
top: (wrapperHeight - itemHeight) / 2,
height: itemHeight,
width: highlightWidth,
borderTopColor: highlightColor,
borderBottomColor: highlightColor,
borderTopWidth: highlightBorderWidth,
borderBottomWidth: highlightBorderWidth,
};
const CustomScrollViewComponent = scrollViewComponent || ScrollView;
return (
<View style={wrapperStyle}>
<View style={highlightStyle} />
<CustomScrollViewComponent
ref={sView}
bounces={false}
showsVerticalScrollIndicator={false}
nestedScrollEnabled
onMomentumScrollBegin={(_e: any) => onMomentumScrollBegin()}
onMomentumScrollEnd={(e: NativeSyntheticEvent<NativeScrollEvent>) =>
onMomentumScrollEnd(e)
}
onScrollBeginDrag={(_e: any) => onScrollBeginDrag()}
onScrollEndDrag={(e: NativeSyntheticEvent<NativeScrollEvent>) =>
onScrollEndDrag(e)
}
>
{header}
{props.dataSource.map(renderItem)}
{footer}
</CustomScrollViewComponent>
</View>
);
}
Example #14
Source File: Swiper.native.tsx From react-native-paper-dates with MIT License | 4 votes |
function SwiperInner({
scrollMode,
renderItem,
renderHeader,
renderFooter,
selectedYear,
initialIndex,
width,
height,
}: SwiperProps & { width: number; height: number }) {
const idx = React.useRef<number>(initialIndex)
const isHorizontal = scrollMode === 'horizontal'
const [visibleIndexes, setVisibleIndexes] = React.useState<number[]>(
getVisibleArray(initialIndex, { isHorizontal, height })
)
const parentRef = React.useRef<ScrollView | null>(null)
const scrollTo = React.useCallback(
(index: number, animated: boolean) => {
idx.current = index
setVisibleIndexes(getVisibleArray(index, { isHorizontal, height }))
if (!parentRef.current) {
return
}
const offset = isHorizontal
? getHorizontalMonthOffset(index, width)
: getVerticalMonthsOffset(index) - montHeaderHeight
if (isHorizontal) {
parentRef.current.scrollTo({
y: 0,
x: offset,
animated,
})
} else {
parentRef.current.scrollTo({
y: offset,
x: 0,
animated,
})
}
},
[parentRef, isHorizontal, width, height]
)
const onPrev = React.useCallback(() => {
scrollTo(idx.current - 1, true)
}, [scrollTo, idx])
const onNext = React.useCallback(() => {
scrollTo(idx.current + 1, true)
}, [scrollTo, idx])
const scrollToInitial = React.useCallback(() => {
scrollTo(idx.current, false)
}, [scrollTo])
const onMomentumScrollEnd = React.useCallback(
(e: NativeSyntheticEvent<NativeScrollEvent>) => {
const contentOffset = e.nativeEvent.contentOffset
const viewSize = e.nativeEvent.layoutMeasurement
const newIndex = isHorizontal
? Math.floor(contentOffset.x / viewSize.width)
: getIndexFromVerticalOffset(contentOffset.y - beginOffset)
if (newIndex === 0) {
return
}
if (idx.current !== newIndex) {
idx.current = newIndex
setVisibleIndexes(getVisibleArray(newIndex, { isHorizontal, height }))
}
},
[idx, height, isHorizontal]
)
const renderProps = {
index: 0,
onPrev,
onNext,
}
useYearChange(
(newIndex) => {
if (newIndex) {
scrollTo(newIndex, false)
}
},
{
selectedYear,
currentIndexRef: idx,
}
)
return (
<>
<ScrollView
ref={parentRef}
horizontal={isHorizontal}
pagingEnabled={isHorizontal}
style={styles.viewPager}
onMomentumScrollEnd={onMomentumScrollEnd}
onScrollEndDrag={onMomentumScrollEnd}
onLayout={scrollToInitial}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
decelerationRate="fast"
scrollEventThrottle={10}
>
<View
style={[
styles.inner,
{
height: isHorizontal
? height
: estimatedMonthHeight * totalMonths,
width: isHorizontal ? width * totalMonths : width,
},
]}
>
{visibleIndexes
? new Array(visibleIndexes.length).fill(undefined).map((_, vi) => (
<View
key={vi}
style={{
top: isHorizontal
? 0
: getVerticalMonthsOffset(visibleIndexes[vi]),
left: isHorizontal
? getHorizontalMonthOffset(visibleIndexes[vi], width)
: 0,
right: isHorizontal ? undefined : 0,
bottom: isHorizontal ? 0 : undefined,
position: 'absolute',
width: isHorizontal ? width : undefined,
height: isHorizontal
? undefined
: getMonthHeight(scrollMode, visibleIndexes[vi]),
}}
>
{renderItem({
index: visibleIndexes[vi],
onPrev: onPrev,
onNext: onNext,
})}
</View>
))
: null}
</View>
</ScrollView>
{renderHeader && renderHeader(renderProps)}
{renderFooter && renderFooter(renderProps)}
</>
)
}