dappros/ethora

View on GitHub
client-reactnative/src/Screens/Actions/MintScreen.tsx

Summary

Maintainability
F
5 days
Test Coverage
import React, {Fragment, useState} from 'react';
import {
  Text,
  View,
  StyleSheet,
  ScrollView,
  ActivityIndicator,
  Alert,
  TextInput,
  TouchableOpacity,
} from 'react-native';
import SecondaryHeader from '../../components/SecondaryHeader/SecondaryHeader';
import {
  widthPercentageToDP as wp,
  heightPercentageToDP as hp,
} from 'react-native-responsive-screen';
import {commonColors, textStyles} from '../../../docs/config';
import AntIcon from 'react-native-vector-icons/AntDesign';
import FastImage from 'react-native-fast-image';
import DocumentPicker from 'react-native-document-picker';
import {uploadFiles} from '../../helpers/uploadFiles';
import {useStores} from '../../stores/context';
import {showError} from '../../components/Toast/toast';
import {useNavigation} from '@react-navigation/native';
import {httpPost} from '../../config/apiService';
import {fileUpload, nftTransferURL} from '../../config/routesConstants';
import CheckBox from '@react-native-community/checkbox';
import Modal from 'react-native-modal';
import {isAudioMimetype} from '../../helpers/checkMimetypes';
import {HStack, VStack} from 'native-base';
import {HomeStackNavigationProp} from '../../navigation/types';

const selectOptions = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const MintScreen = () => {

  //mobx stores
  const {loginStore, walletStore} = useStores();
  //mobx stores

  //navigator
  const navigation = useNavigation<HomeStackNavigationProp>();
  //navigator

  //local states
  const [itemName, setItemName] = useState<string>('');
  const [nftFileUrl, setNftFileUrl] = useState<string>('');
  const [filePickResult, setFilePickResult] = useState<any>({});
  const [loading, setLoading] = useState<boolean>(false);
  const [selectedValue, setSelectedValue] = useState<number>(1);
  const [fileId, setFileId] = useState<any>('');
  const [isModalVisible, setModalVisible] = useState<boolean>(false);
  const [distributionRightsApproved, setDistributionRightsApproved] =
    useState<boolean>(true);
  //local states

  //handle to display or hide modal
  const toggleModal = () => {
    setModalVisible(!isModalVisible);
  };

  //handle to clear all data
  const clearData = () => {
    setLoading(false);
    setNftFileUrl('');
    setItemName('');
    setDistributionRightsApproved(false);
  };

  //handle to set the quantity of nft to mint
  const selectNftQuantity = (value: number) => {
    setSelectedValue(value);
    setModalVisible(false);
  };

  //handle to create an Nft item
  const createNftItem = async () => {
    let item = {name: itemName, rarity: selectedValue, mediaId: fileId};
    const url = nftTransferURL;
    try {
      await httpPost(url, item, loginStore.userToken);
      walletStore.fetchWalletBalance(loginStore.userToken, true);
    } catch (error) {
      showError('Error', 'Cannot create item, try again later');
      console.log(error);
    }
  };

  //handle for mint 
  const onMintClick = () => {
    if (!nftFileUrl) {
      showError('Error', 'Please load the image.');
      return;
    }
    if (!itemName.length) {
      showError('Error', 'Please fill the item name.');
      return;
    }
    if (!distributionRightsApproved) {
      showError('Error', 'Please confirm distribution rights');
      return;
    }

    createNftItem();
    walletStore.fetchOwnTransactions(
      loginStore.initialData.walletAddress,
      100,
      0,
    );
    Alert.alert(
      'Minting...',
      'Awesome! 😎 Once blockchain confirms it, you will see this Item in your wallet and Profile screen.',
      [
        {text: 'Mint another', onPress: () => clearData()},
        {
          text: 'My Profile',
          onPress: () => {
            navigation.navigate('ProfileScreen', {
              viewItems: true,
            });
            clearData();
          },
        },
      ],
    );
  };

  //dialog to choose a file
  const chooseImageOption = () => {
    Alert.alert('Choose a file', '', [
      {text: 'Open from files', onPress: () => setNftFile()},
      {text: 'Dismiss', onPress: () => console.log('dismissed')},
    ]);
  };

  //handle to upload file
  const sendFiles = async (data: any) => {
    setLoading(true);
    try {
      const url = fileUpload;
      const response = await uploadFiles(data, loginStore.userToken, url);
      console.log(JSON.stringify(response), 'sdfasdfadf');
      setFileId(response.results[0]['_id']);
      setLoading(false);
      setNftFileUrl(response.results[0].location);
    } catch (error) {
      console.log(error);
    }
  };

  //handle to set NFT file from file
  const setNftFile = async () => {
    try {
      const res = await DocumentPicker.pick({
        type: [
          DocumentPicker.types.images,
          DocumentPicker.types.audio,
          DocumentPicker.types.video,
        ],
      });
      setFilePickResult(res[0]);
      const data = new FormData();
      data.append('files', {
        name: res[0].name,
        type: res[0].type,
        uri: res[0].uri,
      });
      sendFiles(data);
    } catch (err) {
      if (DocumentPicker.isCancel(err)) {
        // User cancelled the picker, exit any dialogs or menus and move on
      } else {
        throw err;
      }
    }
  };

  return (
    <Fragment>
      <View>
        <SecondaryHeader title="Mint Item" />
      </View>
      <ScrollView style={classes.container}>
        <View style={classes.contentContainer}>
          <View style={classes.section1}>
            <TextInput
              value={itemName}
              onChangeText={text => setItemName(text)}
              placeholder="Item Name"
              placeholderTextColor={commonColors.primaryColor}
              style={classes.itemNameInput}
              maxLength={50}
            />
          </View>

          <HStack justifyContent={'space-between'} mt={'2'}>
            <TouchableOpacity
              onPress={chooseImageOption}
              style={{alignItems: 'flex-start', width: wp('50%')}}>
              <VStack
                justifyContent={'center'}
                alignItems={'center'}
                style={classes.filePreviewContainer}>
                {nftFileUrl ? (
                  <>
                    {isAudioMimetype(filePickResult.type) ? (
                      <AntIcon
                        name={'playcircleo'}
                        color={commonColors.primaryColor}
                        size={hp('5%')}
                      />
                    ) : (
                      <FastImage
                        source={{
                          uri: nftFileUrl,
                          priority: FastImage.priority.normal,
                        }}
                        resizeMode={FastImage.resizeMode.cover}
                        style={{
                          width: wp('50%'),
                          height: wp('50%'),
                          borderRadius: 10,
                        }}
                      />
                    )}
                  </>
                ) : (
                  <View>
                    <AntIcon
                      name="plus"
                      size={hp('10%')}
                      color={commonColors.primaryColor}
                    />
                    <Text style={classes.addFileText}>Add file</Text>
                  </View>
                )}
              </VStack>
            </TouchableOpacity>
            <View style={classes.selectContainer}>
              <Text style={[classes.selectTextStyle, {left: 5}]}> Rarity</Text>
              <Text style={[classes.selectTextStyle, {right: 40}]}>
                {' '}
                {selectedValue}
              </Text>

              <View />
              <>
                <TouchableOpacity onPress={toggleModal}>
                  <AntIcon
                    color={commonColors.primaryColor}
                    name="caretdown"
                    size={hp('2%')}
                    style={{marginRight: 5, marginBottom: 2}}
                  />
                </TouchableOpacity>
              </>
            </View>
          </HStack>

          <TouchableOpacity
            disabled={loading}
            onPress={onMintClick}
            style={classes.createButton}>
            <VStack justifyContent={'center'} alignItems={'center'} flex={1}>
              {loading ? (
                <ActivityIndicator
                  animating={loading}
                  size="small"
                  color={'white'}
                />
              ) : (
                <Text style={classes.createButtonText}>Mint Item</Text>
              )}
            </VStack>
          </TouchableOpacity>
          <View style={classes.checkboxContainer}>
            <CheckBox
              onCheckColor={commonColors.primaryColor}
              onTintColor={commonColors.primaryColor}
              value={distributionRightsApproved}
              onValueChange={setDistributionRightsApproved}
              style={{marginRight: 3}}
            />
            <Text style={{color: commonColors.primaryColor}}>
              By proceeding I confirm that I have the rights to distribute the
              above content.
            </Text>
          </View>
        </View>
      </ScrollView>
      <Modal
        onBackdropPress={() => setModalVisible(false)}
        isVisible={isModalVisible}>
        <VStack
          justifyContent={'center'}
          alignItems={'center'}
          bgColor={'white'}
          borderRadius={10}>
          {selectOptions.map(item => {
            return (
              <TouchableOpacity
                key={item}
                onPress={() => selectNftQuantity(item)}
                style={classes.rarityItems}>
                <Text style={classes.quantityItem}>{item}</Text>
              </TouchableOpacity>
            );
          })}
        </VStack>
      </Modal>
    </Fragment>
  );
};

export default MintScreen;

//styles
const classes = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
  },
  filePreviewContainer: {
    width: wp('50%'),
    height: wp('50%'),
    borderRadius: 10,
    borderColor: commonColors.primaryColor,
    borderWidth: 1,
    marginRight: wp('5%'),
  },
  selectContainer: {
    borderColor: commonColors.primaryColor,
    borderWidth: 1,
    borderRadius: 5,
    marginLeft: 10,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'flex-end',
    width: wp('35%'),
    height: wp('10%'),
  },
  addFileText: {
    marginTop: 'auto',
    fontFamily: textStyles.lightFont,
    fontSize: hp('2.6%'),
    color: commonColors.primaryColor,
  },
  contentContainer: {
    flex: 1,
    margin: 20,
    marginTop: 0,
  },
  section1: {
    flexDirection: 'row',
    marginTop: 20,
  },

  selectTextStyle: {
    fontFamily: textStyles.lightFont,
    color: commonColors.primaryColor,
    position: 'absolute',
  },
  itemNameInput: {
    color: 'black',
    borderWidth: 1,
    borderColor: commonColors.primaryColor,
    borderRadius: 5,
    flex: 1,
    paddingLeft: 20,
    alignItems: 'flex-start',
    height: wp('14%'),
    fontFamily: textStyles.lightFont,
    fontSize: hp('1.8%'),
  },
  checkboxContainer: {
    flexDirection: 'row',
    width: wp('80%'),
    alignItems: 'center',
    marginTop: 10,
  },
  quantityItem: {
    fontSize: hp('2.23%'),
    fontFamily: textStyles.regularFont,
    textAlign: 'left',
    paddingLeft: 5,
    color: commonColors.primaryColor,
  },
  rarityItems: {
    paddingLeft: 5,
    paddingVertical: 5,
    borderBottomColor: commonColors.primaryColor,
    borderBottomWidth: 1,
    width: '100%',
    justifyContent: 'center',
    alignItems: 'center',
  },
  createButton: {
    backgroundColor: commonColors.primaryColor,
    borderRadius: 5,
    height: hp('7%'),
    marginTop: 20,
  },
  createButtonText: {
    fontSize: hp('2%'),
    color: '#fff',
    fontFamily: textStyles.regularFont,
  },
});
//styles