aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Chen <ivan@tagg.id>2021-06-10 19:20:19 -0400
committerIvan Chen <ivan@tagg.id>2021-06-10 19:20:19 -0400
commit2f9ecfb9bc89c7c6e7ac09952b97d3f3813075bc (patch)
tree352386ad82382a506b49232473bdab820e6fe4ff
parent770dcf385fa99fbb93c4ae89a51b09fd96d23bf9 (diff)
Add logic for keyboard focus
-rw-r--r--src/App.tsx19
-rw-r--r--src/components/comments/AddComment.tsx5
-rw-r--r--src/components/moments/IndividualMomentTitleBar.tsx11
-rw-r--r--src/components/moments/MomentPost.tsx3
-rw-r--r--src/components/moments/MomentPostContent.tsx52
-rw-r--r--src/screens/profile/IndividualMoment.tsx102
-rw-r--r--src/screens/profile/MomentCommentsScreen.tsx5
7 files changed, 135 insertions, 62 deletions
diff --git a/src/App.tsx b/src/App.tsx
index 92e7abee..64f40bae 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -27,16 +27,15 @@ export const ChatContext = React.createContext({} as ChatContextType);
const App = () => {
const routeNameRef = useRef();
const [channel, setChannel] = useState<ChannelGroupedType>();
- const chatClient =
- StreamChat.getInstance<
- LocalAttachmentType,
- LocalChannelType,
- LocalCommandType,
- LocalEventType,
- LocalMessageType,
- LocalResponseType,
- LocalUserType
- >(STREAM_CHAT_API);
+ const chatClient = StreamChat.getInstance<
+ LocalAttachmentType,
+ LocalChannelType,
+ LocalCommandType,
+ LocalEventType,
+ LocalMessageType,
+ LocalResponseType,
+ LocalUserType
+ >(STREAM_CHAT_API);
return (
<Provider store={store}>
<NavigationContainer
diff --git a/src/components/comments/AddComment.tsx b/src/components/comments/AddComment.tsx
index 18b9c24e..9d9824db 100644
--- a/src/components/comments/AddComment.tsx
+++ b/src/components/comments/AddComment.tsx
@@ -25,13 +25,15 @@ export interface AddCommentProps {
momentId: string;
placeholderText: string;
callback?: (message: string) => void;
+ onFocus?: () => void;
theme?: 'dark' | 'white';
}
const AddComment: React.FC<AddCommentProps> = ({
momentId,
placeholderText,
- callback = (msg) => null,
+ callback = (_) => null,
+ onFocus = () => null,
theme = 'white',
}) => {
const {setShouldUpdateAllComments = () => null, commentTapped} =
@@ -125,6 +127,7 @@ const AddComment: React.FC<AddCommentProps> = ({
placeholderTextColor={theme === 'dark' ? '#828282' : undefined}
placeholder={placeholderText}
value={inReplyToMention + comment}
+ onFocus={onFocus}
onChange={(newText: string) => {
// skipping the `inReplyToMention` text
setComment(
diff --git a/src/components/moments/IndividualMomentTitleBar.tsx b/src/components/moments/IndividualMomentTitleBar.tsx
index 2bebafa7..4ae9471f 100644
--- a/src/components/moments/IndividualMomentTitleBar.tsx
+++ b/src/components/moments/IndividualMomentTitleBar.tsx
@@ -1,8 +1,13 @@
import React from 'react';
-import {TouchableOpacity} from 'react-native';
-import {Text, View, StyleSheet, ViewProps} from 'react-native';
-import {normalize} from '../../utils';
+import {
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+ ViewProps,
+} from 'react-native';
import CloseIcon from '../../assets/ionicons/close-outline.svg';
+import {normalize} from '../../utils';
interface IndividualMomentTitleBarProps extends ViewProps {
title: string;
diff --git a/src/components/moments/MomentPost.tsx b/src/components/moments/MomentPost.tsx
index 02dbffa3..7a588325 100644
--- a/src/components/moments/MomentPost.tsx
+++ b/src/components/moments/MomentPost.tsx
@@ -11,12 +11,14 @@ interface MomentPostProps {
moment: MomentPostType;
userXId: string | undefined;
screenType: ScreenType;
+ index: number;
}
const MomentPost: React.FC<MomentPostProps> = ({
moment,
userXId,
screenType,
+ index,
}) => {
const {userId: loggedInUserId, username: loggedInUsername} = useSelector(
(state: RootState) => state.user.user,
@@ -88,6 +90,7 @@ const MomentPost: React.FC<MomentPostProps> = ({
moment={moment}
screenType={screenType}
momentTags={tags}
+ index={index}
/>
</>
);
diff --git a/src/components/moments/MomentPostContent.tsx b/src/components/moments/MomentPostContent.tsx
index 5192fdf0..eb89fd03 100644
--- a/src/components/moments/MomentPostContent.tsx
+++ b/src/components/moments/MomentPostContent.tsx
@@ -1,9 +1,10 @@
import {useNavigation} from '@react-navigation/native';
-import React, {useEffect, useRef, useState} from 'react';
+import React, {useContext, useEffect, useRef, useState} from 'react';
import {Image, StyleSheet, Text, View, ViewProps} from 'react-native';
import {TouchableWithoutFeedback} from 'react-native-gesture-handler';
import Animated, {EasingNode} from 'react-native-reanimated';
import {useDispatch, useStore} from 'react-redux';
+import {MomentContext} from '../../screens/profile/IndividualMoment';
import {RootState} from '../../store/rootReducer';
import {
MomentCommentPreviewType,
@@ -13,11 +14,11 @@ import {
UserType,
} from '../../types';
import {
+ getLoggedInUserAsProfilePreview,
getTimePosted,
navigateToProfile,
normalize,
SCREEN_WIDTH,
- getLoggedInUserAsProfilePreview,
} from '../../utils';
import {mentionPartTypes, renderTextWithMentions} from '../../utils/comments';
import {AddComment} from '../comments';
@@ -28,12 +29,14 @@ interface MomentPostContentProps extends ViewProps {
screenType: ScreenType;
moment: MomentPostType;
momentTags: MomentTagType[];
+ index: number;
}
const MomentPostContent: React.FC<MomentPostContentProps> = ({
screenType,
moment,
style,
momentTags,
+ index,
}) => {
const state: RootState = useStore().getState();
const navigation = useNavigation();
@@ -46,6 +49,7 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
);
const [commentPreview, setCommentPreview] =
useState<MomentCommentPreviewType | null>(moment.comment_preview);
+ const {keyboardVisible, setScrollToTargetIndex} = useContext(MomentContext);
useEffect(() => {
setTags(momentTags);
@@ -92,20 +96,30 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
/>
</Animated.View>
)}
- {moment.caption !== '' &&
- renderTextWithMentions({
- value: moment.caption,
- styles: styles.captionText,
- partTypes: mentionPartTypes('momentCaption'),
- onPress: (user: UserType) =>
- navigateToProfile(state, dispatch, navigation, screenType, user),
- })}
- <MomentCommentPreview
- momentId={moment.moment_id}
- commentsCount={moment.comments_count}
- commentPreview={commentPreview}
- screenType={screenType}
- />
+ {!keyboardVisible && (
+ <>
+ {moment.caption !== '' &&
+ renderTextWithMentions({
+ value: moment.caption,
+ styles: styles.captionText,
+ partTypes: mentionPartTypes('momentCaption'),
+ onPress: (user: UserType) =>
+ navigateToProfile(
+ state,
+ dispatch,
+ navigation,
+ screenType,
+ user,
+ ),
+ })}
+ <MomentCommentPreview
+ momentId={moment.moment_id}
+ commentsCount={moment.comments_count}
+ commentPreview={commentPreview}
+ screenType={screenType}
+ />
+ </>
+ )}
<AddComment
placeholderText={'Add a comment here!'}
momentId={moment.moment_id}
@@ -115,6 +129,7 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
comment: message,
})
}
+ onFocus={() => setScrollToTargetIndex(index)}
theme={'dark'}
/>
<Text style={styles.text}>{getTimePosted(moment.date_created)}</Text>
@@ -123,9 +138,7 @@ const MomentPostContent: React.FC<MomentPostContentProps> = ({
};
const styles = StyleSheet.create({
- container: {
- borderWidth: 1,
- },
+ container: {},
image: {
width: SCREEN_WIDTH,
aspectRatio: 1,
@@ -147,7 +160,6 @@ const styles = StyleSheet.create({
lineHeight: normalize(15.51),
letterSpacing: normalize(0.6),
marginBottom: normalize(18),
- borderWidth: 1,
},
tapTag: {
position: 'absolute',
diff --git a/src/screens/profile/IndividualMoment.tsx b/src/screens/profile/IndividualMoment.tsx
index 447ba2a9..a91f1913 100644
--- a/src/screens/profile/IndividualMoment.tsx
+++ b/src/screens/profile/IndividualMoment.tsx
@@ -1,10 +1,11 @@
import {BlurView} from '@react-native-community/blur';
import {RouteProp} from '@react-navigation/native';
import {StackNavigationProp} from '@react-navigation/stack';
-import React from 'react';
-import {FlatList, StyleSheet} from 'react-native';
+import React, {useEffect, useRef, useState} from 'react';
+import {FlatList, Keyboard, StyleSheet} from 'react-native';
import {useSelector} from 'react-redux';
import {IndividualMomentTitleBar, MomentPost} from '../../components';
+import {AVATAR_DIM} from '../../constants';
import {MainStackParams} from '../../routes';
import {RootState} from '../../store/rootreducer';
import {MomentPostType} from '../../types';
@@ -13,11 +14,21 @@ import {normalize, StatusBarHeight} from '../../utils';
/**
* Individual moment view opened when user clicks on a moment tile
*/
+
+type MomentContextType = {
+ keyboardVisible: boolean;
+ setScrollToTargetIndex: (index: number) => void;
+};
+
+export const MomentContext = React.createContext({} as MomentContextType);
+
type IndividualMomentRouteProp = RouteProp<MainStackParams, 'IndividualMoment'>;
+
type IndividualMomentNavigationProp = StackNavigationProp<
MainStackParams,
'IndividualMoment'
>;
+
interface IndividualMomentProps {
route: IndividualMomentRouteProp;
navigation: IndividualMomentNavigationProp;
@@ -27,39 +38,78 @@ const IndividualMoment: React.FC<IndividualMomentProps> = ({
route,
navigation,
}) => {
- const {moment_category, moment_id} = route.params.moment;
- const {userXId, screenType} = route.params;
-
+ const {
+ userXId,
+ screenType,
+ moment: {moment_category, moment_id},
+ } = route.params;
const {moments} = useSelector((state: RootState) =>
userXId ? state.userX[screenType][userXId] : state.moments,
);
-
+ const scrollRef = useRef<FlatList<MomentPostType>>(null);
const momentData = moments.filter(
(m) => m.moment_category === moment_category,
);
const initialIndex = momentData.findIndex((m) => m.moment_id === moment_id);
+ const [scrollToTargetIndex, setScrollToTargetIndex] =
+ useState<number>(initialIndex);
+ const [keyboardVisible, setKeyboardVisible] = React.useState(false);
+
+ useEffect(() => {
+ const showKeyboard = () => setKeyboardVisible(true);
+ Keyboard.addListener('keyboardWillShow', showKeyboard);
+ return () => Keyboard.removeListener('keyboardWillShow', showKeyboard);
+ }, []);
+
+ useEffect(() => {
+ const hideKeyboard = () => setKeyboardVisible(false);
+ Keyboard.addListener('keyboardWillHide', hideKeyboard);
+ return () => Keyboard.removeListener('keyboardWillHide', hideKeyboard);
+ }, []);
+
+ useEffect(() => {
+ if (keyboardVisible) {
+ scrollRef.current?.scrollToIndex({
+ index: scrollToTargetIndex,
+ // viewOffset: -(AVATAR_DIM + normalize(120)),
+ viewOffset: -(AVATAR_DIM + normalize(90)),
+ });
+ }
+ }, [scrollToTargetIndex, keyboardVisible]);
return (
- <BlurView
- blurType="light"
- blurAmount={30}
- reducedTransparencyFallbackColor="white"
- style={styles.contentContainer}>
- <IndividualMomentTitleBar
- style={styles.header}
- close={() => navigation.goBack()}
- title={moment_category}
- />
- <FlatList
- data={momentData}
- renderItem={({item}: {item: MomentPostType}) => (
- <MomentPost moment={item} userXId={userXId} screenType={screenType} />
- )}
- keyExtractor={(item, _) => item.moment_id}
- showsVerticalScrollIndicator={false}
- initialScrollIndex={initialIndex}
- />
- </BlurView>
+ <MomentContext.Provider
+ value={{
+ keyboardVisible,
+ setScrollToTargetIndex,
+ }}>
+ <BlurView
+ blurType="light"
+ blurAmount={30}
+ reducedTransparencyFallbackColor="white"
+ style={styles.contentContainer}>
+ <IndividualMomentTitleBar
+ style={styles.header}
+ close={() => navigation.goBack()}
+ title={moment_category}
+ />
+ <FlatList
+ ref={scrollRef}
+ data={momentData}
+ renderItem={({item, index}) => (
+ <MomentPost
+ moment={item}
+ userXId={userXId}
+ screenType={screenType}
+ index={index}
+ />
+ )}
+ keyExtractor={(item, _) => item.moment_id}
+ showsVerticalScrollIndicator={false}
+ initialScrollIndex={initialIndex}
+ />
+ </BlurView>
+ </MomentContext.Provider>
);
};
const styles = StyleSheet.create({
diff --git a/src/screens/profile/MomentCommentsScreen.tsx b/src/screens/profile/MomentCommentsScreen.tsx
index 402e5f44..7dfe8ae9 100644
--- a/src/screens/profile/MomentCommentsScreen.tsx
+++ b/src/screens/profile/MomentCommentsScreen.tsx
@@ -48,8 +48,9 @@ const MomentCommentsScreen: React.FC<MomentCommentsScreenProps> = ({route}) => {
React.useState(true);
//Keeps track of the current comments object in focus so that the application knows which comment to post a reply to
- const [commentTapped, setCommentTapped] =
- useState<CommentType | CommentThreadType | undefined>();
+ const [commentTapped, setCommentTapped] = useState<
+ CommentType | CommentThreadType | undefined
+ >();
useEffect(() => {
navigation.setOptions({