aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile14
-rw-r--r--src/components/moments/Moment.tsx52
-rw-r--r--src/constants/api.ts1
-rw-r--r--src/services/MomentService.ts129
-rw-r--r--src/types/types.ts15
5 files changed, 209 insertions, 2 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..b0022598
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+build:
+ yarn
+ cd ios && pod install && cd ..
+
+clean:
+ -rm *.lock
+ -rm ios/*.lock
+
+deep_clean:
+ -rm *.lock
+ -rm ios/*.lock
+ -rm -rf /Users/ivan/Library/Developer/Xcode/DerivedData
+ -rm -rf node_modules
+ yarn cache clean
diff --git a/src/components/moments/Moment.tsx b/src/components/moments/Moment.tsx
index cde5b2e0..34b2c7ea 100644
--- a/src/components/moments/Moment.tsx
+++ b/src/components/moments/Moment.tsx
@@ -7,11 +7,15 @@ import ImagePicker from 'react-native-image-crop-picker';
import LinearGradient from 'react-native-linear-gradient';
import DeleteIcon from '../../assets/icons/delete-logo.svg';
import DownIcon from '../../assets/icons/down_icon.svg';
-import PlusIcon from '../../assets/icons/plus-icon.svg';
import BigPlusIcon from '../../assets/icons/plus-icon-white.svg';
+import PlusIcon from '../../assets/icons/plus-icon.svg';
import UpIcon from '../../assets/icons/up_icon.svg';
import {TAGG_LIGHT_BLUE} from '../../constants';
import {ERROR_UPLOAD} from '../../constants/strings';
+import {
+ handlePresignedURL,
+ handleVideoUpload,
+} from '../../services/MomentService';
import {MomentType, ScreenType} from '../../types';
import {normalize, SCREEN_WIDTH} from '../../utils';
import MomentTile from './MomentTile';
@@ -42,6 +46,45 @@ const Moment: React.FC<MomentProps> = ({
externalStyles,
}) => {
const navigation = useNavigation();
+ /**
+ * This function opens the ImagePicker, only lets you select video files,
+ * formats the file extension, then makes a call to the server to get the presigned URL,
+ * after which it makes a POST request to the returned URL to upload the file directly to S3.
+ * params: none
+ * @returns: none
+ */
+ const navigateToVideoPicker = () => {
+ ImagePicker.openPicker({
+ smartAlbums: [
+ 'Favorites',
+ 'RecentlyAdded',
+ 'SelfPortraits',
+ 'Screenshots',
+ 'UserLibrary',
+ ],
+ width: 580,
+ height: 580,
+ cropping: false,
+ cropperToolbarTitle: 'select a video',
+ mediaType: 'video',
+ })
+ .then(async (vid) => {
+ if ('path' in vid) {
+ let fileName = vid.filename || '';
+ if (fileName.endsWith('.heic') || fileName.endsWith('.HEIC')) {
+ fileName = fileName.split('.')[0] + '.jpg';
+ }
+ let presignedURL = await handlePresignedURL(fileName, title);
+ console.log('presigned' + JSON.stringify(presignedURL));
+ handleVideoUpload(vid, presignedURL);
+ }
+ })
+ .catch((err) => {
+ if (err.code && err.code !== 'E_PICKER_CANCELLED') {
+ Alert.alert(ERROR_UPLOAD);
+ }
+ });
+ };
const navigateToImagePicker = () => {
ImagePicker.openPicker({
@@ -110,6 +153,13 @@ const Moment: React.FC<MomentProps> = ({
<PlusIcon
width={23}
height={23}
+ onPress={() => navigateToVideoPicker()}
+ color={'black'}
+ style={styles.horizontalMargin}
+ />
+ <PlusIcon
+ width={23}
+ height={23}
onPress={() => navigateToImagePicker()}
color={TAGG_LIGHT_BLUE}
style={styles.horizontalMargin}
diff --git a/src/constants/api.ts b/src/constants/api.ts
index b55489d9..6dab1153 100644
--- a/src/constants/api.ts
+++ b/src/constants/api.ts
@@ -38,6 +38,7 @@ export const VERIFY_INVITATION_CODE_ENDPOUNT: string = API_URL + 'verify-code/';
export const COMMENTS_ENDPOINT: string = API_URL + 'comments/';
export const COMMENT_REACTIONS_ENDPOINT: string = API_URL + 'reaction-comment/';
export const COMMENT_REACTIONS_REPLY_ENDPOINT: string = API_URL + 'reaction-reply/';
+export const PRESIGNED_URL_ENDPOINT: string = API_URL + 'presigned-url/';
export const FRIENDS_ENDPOINT: string = API_URL + 'friends/';
export const ALL_USERS_ENDPOINT: string = API_URL + 'users/';
export const REPORT_ISSUE_ENDPOINT: string = API_URL + 'report/';
diff --git a/src/services/MomentService.ts b/src/services/MomentService.ts
index b837585a..d0ed56ab 100644
--- a/src/services/MomentService.ts
+++ b/src/services/MomentService.ts
@@ -1,12 +1,19 @@
import AsyncStorage from '@react-native-community/async-storage';
+import {Image, Video} from 'react-native-image-crop-picker';
import RNFetchBlob from 'rn-fetch-blob';
import {
MOMENTS_ENDPOINT,
MOMENTTAG_ENDPOINT,
MOMENT_TAGS_ENDPOINT,
MOMENT_THUMBNAIL_ENDPOINT,
+ PRESIGNED_URL_ENDPOINT,
+ TAGG_CUSTOMER_SUPPORT,
} from '../constants';
-import {MomentPostType, MomentTagType} from '../types';
+import {
+ ERROR_SOMETHING_WENT_WRONG,
+ ERROR_SOMETHING_WENT_WRONG_REFRESH,
+} from '../constants/strings';
+import {MomentPostType, MomentTagType, PresignedURLResponse} from '../types';
import {checkImageUploadStatus} from '../utils';
export const postMoment = async (
@@ -18,6 +25,7 @@ export const postMoment = async (
) => {
try {
const request = new FormData();
+
//Manipulating filename to end with .jpg instead of .heic
if (fileName.endsWith('.heic') || fileName.endsWith('.HEIC')) {
fileName = fileName.split('.')[0] + '.jpg';
@@ -208,3 +216,122 @@ export const deleteMomentTag = async (moment_tag_id: string) => {
return false;
}
};
+/**
+ * This function makes a request to the server in order to provide the client with a presigned URL.
+ * This is called first, in order for the client to directly upload a file to S3
+ * @param value: string | undefined
+ * @param filename: string | undefined
+ * @returns a PresignedURLResponse object
+ */
+export const handlePresignedURL = async (
+ filename: string | undefined,
+ momentCategory: string,
+) => {
+ try {
+ const token = await AsyncStorage.getItem('token');
+ const response = await fetch(PRESIGNED_URL_ENDPOINT, {
+ method: 'POST',
+ headers: {
+ Authorization: 'Token ' + token,
+ },
+ body: JSON.stringify({
+ filename,
+ category: momentCategory,
+ }),
+ });
+ const status = response.status;
+ let data: PresignedURLResponse = await response.json();
+ if (status === 200) {
+ console.log('done');
+ return data;
+ } else {
+ if (status === 404) {
+ console.log(
+ `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`,
+ );
+ } else {
+ console.log(ERROR_SOMETHING_WENT_WRONG_REFRESH);
+ }
+ console.log(response);
+ }
+ } catch (error) {
+ console.log(error);
+ console.log(ERROR_SOMETHING_WENT_WRONG);
+ }
+};
+/**
+ * This util function takes in the file object and the PresignedURLResponse object, creates form data from the latter,
+ * and makes a post request to the presigned URL, sending the file object inside of the form data.
+ * @param file: Video, Image, Undefined
+ * @param urlObj PresignedURLResponse | undefined
+ * @returns responseURL or boolean
+ */
+export const handleVideoUpload = async (
+ file: Video | Image | undefined,
+ urlObj: PresignedURLResponse | undefined,
+) => {
+ try {
+ let fileName = file?.filename;
+ if (fileName === null || '') {
+ console.log('Invalid filename');
+ return false;
+ }
+ if (urlObj === null || urlObj === undefined) {
+ console.log('Invalid urlObj');
+ return false;
+ }
+ //build formData for POST request
+ // Could not get a forEach to work and could not assign directly, will look into cleaning this series of appends up later.
+ const form = new FormData();
+ form.append('key', urlObj.response_url.fields.key);
+ form.append(
+ 'x-amz-algorithm',
+ urlObj.response_url.fields['x-amz-algorithm'],
+ );
+ form.append(
+ 'x-amz-credential',
+ urlObj.response_url.fields['x-amz-credential'],
+ );
+ form.append('x-amz-date', urlObj.response_url.fields['x-amz-date']);
+ form.append('policy', urlObj.response_url.fields.policy);
+ form.append(
+ 'x-amz-signature',
+ urlObj.response_url.fields['x-amz-signature'],
+ );
+ form.append('file', {
+ uri: file?.sourceURL,
+ // other types such as 'quicktime' 'image' etc exist, and we can programmatically type this, but for now sticking with simple 'video'
+ type: 'video',
+ name: fileName,
+ });
+ const response = await fetch(urlObj.response_url.url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ body: form,
+ });
+ const status = response.status;
+ // let data = await response.json();
+ if (status === 200 || status === 204) {
+ console.log('complete');
+ return response;
+ } else {
+ if (status === 404) {
+ console.log(
+ `Please make sure that the email / username entered is registered with us. You may contact our customer support at ${TAGG_CUSTOMER_SUPPORT}`,
+ );
+ } else {
+ console.log('FFFFFF \n');
+ console.log(response);
+ console.log(ERROR_SOMETHING_WENT_WRONG_REFRESH);
+ }
+ console.log(response);
+ return false;
+ }
+ } catch (error) {
+ console.log(error);
+ console.log(ERROR_SOMETHING_WENT_WRONG);
+ return false;
+ }
+};
diff --git a/src/types/types.ts b/src/types/types.ts
index 171a9ff3..f6f23fc8 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -365,3 +365,18 @@ export type ReactionType = {
id: string;
type: ReactionOptionsType;
};
+// used to handle direct S3 uploads by packaging presigned_url info into one object
+export type PresignedURLResponse = {
+ response_msg: string;
+ response_url: {
+ url: string;
+ fields: {
+ key: string | undefined;
+ 'x-amz-algorithm': string;
+ 'x-amz-credential': string;
+ 'x-amz-date': string;
+ policy: string;
+ 'x-amz-signature': string;
+ };
+ };
+};