import {RouteProp} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import React, {FC, useEffect, useMemo, useState} from 'react'; import { Alert, Image, ImageSourcePropType, Keyboard, KeyboardAvoidingView, Platform, StyleSheet, Text, TouchableOpacity, TouchableWithoutFeedback, View, } from 'react-native'; import {Button} from 'react-native-elements'; import Video from 'react-native-video'; import {useDispatch, useSelector} from 'react-redux'; import FrontArrow from '../../assets/icons/front-arrow.svg'; import { MentionInputControlled, SearchBackground, TaggSquareButton, } from '../../components'; import {CaptionScreenHeader} from '../../components/'; import TaggLoadingIndicator from '../../components/common/TaggLoadingIndicator'; import {TAGG_LIGHT_BLUE_2} from '../../constants'; import { ERROR_NO_MOMENT_CATEGORY, ERROR_SOMETHING_WENT_WRONG_REFRESH, ERROR_UPLOAD, SUCCESS_PIC_UPLOAD, } from '../../constants/strings'; import * as RootNavigation from '../../RootNavigation'; import {MainStackParams} from '../../routes'; import {patchMoment, postMoment, postMomentTags} from '../../services'; import { handleVideoMomentUpload, loadUserMoments, updateProfileCompletionStage, } from '../../store/actions'; import {RootState} from '../../store/rootReducer'; import {MomentTagType} from '../../types'; import {isIPhoneX, normalize, SCREEN_WIDTH, StatusBarHeight} from '../../utils'; import {mentionPartTypes} from '../../utils/comments'; /** * Upload Screen to allow users to upload posts to Tagg */ type CaptionScreenRouteProp = RouteProp; type CaptionScreenNavigationProp = StackNavigationProp< MainStackParams, 'CaptionScreen' >; interface CaptionScreenProps { route: CaptionScreenRouteProp; navigation: CaptionScreenNavigationProp; } const CaptionScreen: React.FC = ({route, navigation}) => { // moment is only present when editing const {moment} = route.params; const { user: {userId}, } = useSelector((state: RootState) => state.user); const dispatch = useDispatch(); const [caption, setCaption] = useState(moment ? moment.caption : ''); const [loading, setLoading] = useState(false); const [tags, setTags] = useState([]); const [taggedUsersText, setTaggedUsersText] = useState(''); const [momentCategory, setMomentCategory] = useState(); // only used for upload purposes, undefined for editing is fine const videoDuration = moment ? undefined : route.params.media!.videoDuration; const mediaUri = moment ? moment.moment_url : route.params.media!.uri; // TODO: change this once moment refactor is done const isMediaAVideo = moment ? !( moment.moment_url.endsWith('.jpg') || moment.moment_url.endsWith('.JPG') || moment.moment_url.endsWith('.png') || moment.moment_url.endsWith('.PNG') ) : route.params.media?.isVideo ?? false; useEffect(() => { setTags(route.params.selectedTags ?? []); }, [route.params.selectedTags]); useEffect(() => { setMomentCategory(route.params.selectedCategory); }, [route.params.selectedCategory]); useEffect(() => { // if we're editing, hide tab bar if (moment) { navigation.dangerouslyGetParent()?.setOptions({ tabBarVisible: false, }); } }, [route.params.moment]); useEffect(() => { let listString = ''; // Append non-truncated usernames together and no more than 21 characters total // e.g. "@ivan.tagg" // e.g. "@ivan.tagg @foobar . . ." for (const tag of tags) { const usernameStr = `@${tag.user.username} `; if (listString.length + usernameStr.length > 21) { listString = listString.concat('. . .'); break; } else { listString = listString.concat(usernameStr); } } setTaggedUsersText(listString); }, [tags]); const handleFailed = (noCategory = false) => { setLoading(false); setTimeout(() => { if (noCategory) { Alert.alert(ERROR_NO_MOMENT_CATEGORY); } else { Alert.alert(moment ? ERROR_SOMETHING_WENT_WRONG_REFRESH : ERROR_UPLOAD); } }, 500); }; const handleSuccess = () => { setLoading(false); navigation.dangerouslyGetParent()?.setOptions({ tabBarVisible: true, }); if (!moment) { // if posting, pop all screens until at camera screen (default upload screen) // then switch to the profile tab navigation.popToTop(); RootNavigation.navigate('ProfileTab'); setTimeout(() => { if (!isMediaAVideo) { Alert.alert(SUCCESS_PIC_UPLOAD); } }, 500); } else { // if editing, simply go back to profile screen navigation.navigate('Profile', { userXId: undefined, screenType: route.params.screenType, }); } }; const formattedTags = () => { return tags.map((tag) => ({ x: isMediaAVideo ? 0 : tag.x, y: isMediaAVideo ? 0 : tag.y, z: isMediaAVideo ? 0 : tag.z, user_id: tag.user.id, })); }; const handleShare = async () => { setLoading(true); if (moment || !momentCategory) { handleFailed(true); return; } let profileCompletionStage; // separate upload logic for image/video if (isMediaAVideo) { if (videoDuration) { dispatch( handleVideoMomentUpload( mediaUri, videoDuration, momentCategory, formattedTags(), ), ); } else { handleFailed(); return; } } else { const momentResponse = await postMoment( mediaUri, caption, momentCategory, userId, ); if (!momentResponse) { handleFailed(); return; } profileCompletionStage = momentResponse.profile_completion_stage; const momentId = momentResponse.moment_id; if (momentId) { const momentTagResponse = await postMomentTags( momentId, formattedTags(), ); if (!momentTagResponse) { handleFailed(); return; } } } if (!isMediaAVideo) { dispatch(loadUserMoments(userId)); } if (profileCompletionStage) { dispatch(updateProfileCompletionStage(profileCompletionStage)); } handleSuccess(); }; const handleSubmitEditChanges = async () => { setLoading(true); if (moment?.moment_id && momentCategory) { const success = await patchMoment( moment.moment_id, caption, momentCategory, formattedTags(), ); if (success) { dispatch(loadUserMoments(userId)); handleSuccess(); } else { handleFailed(); } } }; const SelectableItem: FC<{ text: 'Tag Friends' | 'Category'; imageUri: ImageSourcePropType; onPress: () => void; }> = ({text, imageUri, onPress}) => { return ( {text === 'Category' && !momentCategory && ( * )} {text} {text === 'Tag Friends' && ( {taggedUsersText} )} {text === 'Category' && ( {momentCategory} )} ); }; return ( {loading && }