dappros/ethora

View on GitHub
client-reactnative/src/components/Chat/MetaNavigation.tsx

Summary

Maintainability
F
5 days
Test Coverage
import {useNavigation} from '@react-navigation/native';
import {HStack, Image} from 'native-base';
import React, {useEffect, useState} from 'react';
import {
  ActivityIndicator,
  StyleSheet,
  Text,
  TouchableOpacity,
  TouchableWithoutFeedback,
  View,
} from 'react-native';
import Modal from 'react-native-modal';
import {heightPercentageToDP as hp} from 'react-native-responsive-screen';
import Ionicons from 'react-native-vector-icons/Ionicons';
import {coinImagePath, commonColors, textStyles} from '../../../docs/config';
import {asyncStorageGetItem} from '../../helpers/cache/asyncStorageGetItem';
import {useStores} from '../../stores/context';
import {CreateNewChatButton} from './CreateNewChatButton';
import {metaRooms as predefinedMeta} from '../../../docs/config';
import {underscoreManipulation} from '../../helpers/underscoreLogic';
import {sendMessageStanza} from '../../xmpp/stanzas';

import Share from 'react-native-share';
import {httpGet, httpPost} from '../../config/apiService';
import {homeStackRoutes} from '../../navigation/routes';
import {HomeStackNavigationProp} from '../../navigation/types';

type IRoom = {
  _id: string;
  contractAddress: string;
  createdAt: string;
  description: string;
  name: string;
  ownerId: string;
};

export interface IApiMetaRoom {
  _id: string;
  contractAddress: string;
  createdAt: Date;
  description: string;
  name: string;
  ownerId: string;
  ownerNavLinks: {
    east: IRoom | null;
    north: IRoom | null;
    south: IRoom | null;
    west: IRoom | null;
  };
  roomJid: string;
  updatedAt: Date;
  userNavLinks: {
    east: IRoom | null;
    north: IRoom | null;
    south: IRoom | null;
    west: IRoom | null;
  };
}

export interface IMetaNavigation {
  chatId: string;
  open: boolean;
  onClose: () => void;
}
const DIRECTIONS = {
  NORTH: 'north',
  WEST: 'west',
  SOUTH: 'south',
  EAST: 'east',
};
const SHORT_DIRECTIONS: Record<string, string> = {
  north: 'n',
  west: 'w',
  south: 's',
  east: 'e',
};

const OPOSITE_DIRECTIONS: Record<string, string> = {
  [DIRECTIONS.WEST]: DIRECTIONS.EAST,
  [DIRECTIONS.EAST]: DIRECTIONS.WEST,
  [DIRECTIONS.SOUTH]: DIRECTIONS.NORTH,
  [DIRECTIONS.NORTH]: DIRECTIONS.SOUTH,
};

const getOpositeDirection = (direction: string) => {
  return OPOSITE_DIRECTIONS[direction];
};

const CompassItem = ({
  room,
  name,
  chatId,
  setDirection,
}: {
  room: IApiMetaRoom | undefined;
  name: string;
  chatId: string;
  setDirection: () => void;
}) => {
  const navigation = useNavigation<HomeStackNavigationProp>();
  const {apiStore} = useStores();
  if (!room) {
    return (
      <HStack
        justifyContent={'center'}
        alignItems={'center'}
        style={{paddingVertical: 10}}>
        <TouchableOpacity
          disabled={!chatId}
          onPress={() => {
            setDirection();
            navigation.navigate('ChatScreen', {
              chatJid: '',
            });
          }}>
          <Text
            style={{
              color: 'black',
              textAlign: 'center',
              fontFamily: textStyles.mediumFont,
              fontSize: 16,
            }}>
            {'Empty'}
          </Text>
        </TouchableOpacity>
      </HStack>
    );
  }
  return (
    <HStack
      justifyContent={'center'}
      alignItems={'center'}
      style={{paddingVertical: 10}}>
      <TouchableOpacity
        onPress={() => {
          setDirection();

          navigation.navigate('ChatScreen', {
            chatJid: room.roomJid + apiStore.xmppDomains.CONFERENCEDOMAIN,
          });
        }}>
        <Text
          style={{
            color: 'black',
            textAlign: 'center',
            fontFamily: textStyles.mediumFont,
            fontSize: 16,
          }}>
          {name}
        </Text>
      </TouchableOpacity>
    </HStack>
  );
};

const MetaHeader = ({
  room,
  direction,
  previousRoom,
}: {
  room: IApiMetaRoom | undefined;
  direction: string;
  previousRoom: IApiMetaRoom | undefined;
}) => {
  const navigation = useNavigation<HomeStackNavigationProp>();
  if (!room?.name) {
    return (
      <View style={[styles.top, styles.innerContainer]}>
        <Text style={{fontFamily: textStyles.semiBoldFont, color: 'black'}}>
          This space is empty. You can build your own room here for 120{' '}
          <Image
            alt="Coin Image"
            source={coinImagePath}
            h={hp('3%')}
            w={hp('3%')}
          />
        </Text>
        <CreateNewChatButton
          onPress={() =>
            navigation.navigate('NewChatScreen', {
              metaDirection: direction,
              metaRoom: previousRoom,
            })
          }
        />
      </View>
    );
  }
  return (
    <View style={[styles.top, styles.innerContainer]}>
      <Text style={{fontFamily: textStyles.semiBoldFont, color: 'black'}}>
        {room.name}
      </Text>
      <Text style={{fontFamily: textStyles.semiBoldFont, color: 'black'}}>
        {room.description}
      </Text>
    </View>
  );
};
const emptyMetaRoom = {
  name: '',
  description: '',
  ownerNavLinks: {west: null, east: null, north: null, south: null},
  ownerId: '',
  contractAddress: '',
  createdAt: new Date(),
  _id: '',
  roomJid: '',
  updatedAt: new Date(),
  userNavLinks: {west: null, east: null, north: null, south: null},
};
const roomRoute = '/room';
export const MetaNavigation: React.FC<IMetaNavigation> = ({
  chatId,
  open,
  onClose,
}) => {
  const [previousDirection, setPreviousDirection] = useState('');
  const [loading, setLoading] = useState(false);

  const [metaRooms, setMetaRooms] = useState<IApiMetaRoom[]>([]);
  const [previousRoom, setPreviuosRoom] = useState<IApiMetaRoom | undefined>();
  const {loginStore, chatStore, apiStore} = useStores();
  const [currentMetaRoom, setCurrentMetaRoom] =
    useState<IApiMetaRoom>(emptyMetaRoom);
  const getMetaRooms = async () => {
    const rooms = await asyncStorageGetItem('metaRooms');
    setMetaRooms(rooms || predefinedMeta);
  };


  // getting last rooms where user was
  const getCurrentRoom = async () => {
    setLoading(true);
    try {
      const res = await httpGet(
        roomRoute + '/getRoom/' + chatId,
        loginStore.userToken,
      );
      setCurrentMetaRoom(res.data.result);
    } catch (error) {
      setCurrentMetaRoom(emptyMetaRoom);
      console.log(error);
    }
    setLoading(false);
  };
  useEffect(() => {
    getMetaRooms();
  }, []);
  useEffect(() => {
    if (!chatId) {
      setCurrentMetaRoom(emptyMetaRoom);
    }
    if (chatId) {
      getCurrentRoom();
    }
  }, [chatId]);
  const checkEmptyDirections = () => {
    return (
      !currentMetaRoom?.ownerNavLinks?.south &&
      !currentMetaRoom?.ownerNavLinks?.east &&
      !currentMetaRoom?.ownerNavLinks?.west &&
      !currentMetaRoom?.ownerNavLinks?.north &&
      !currentMetaRoom?.userNavLinks?.south &&
      !currentMetaRoom?.userNavLinks?.east &&
      !currentMetaRoom?.userNavLinks?.west &&
      !currentMetaRoom?.userNavLinks?.north
    );
  };

  const sendMessage = (chatName: string, jid: string, isPrevious: boolean) => {
    const manipulatedWalletAddress = underscoreManipulation(
      loginStore.initialData.walletAddress,
    );
    const textEnter =
      loginStore.initialData.firstName +
      ' ' +
      loginStore.initialData.lastName +
      ' ' +
      'has joined' +
      ' ' +
      '<-';
    const textLeave =
      loginStore.initialData.firstName +
      ' ' +
      loginStore.initialData.lastName +
      ' ' +
      'has left' +
      ' ' +
      '->';
    const data = {
      senderFirstName: loginStore.initialData.firstName,
      senderLastName: loginStore.initialData.lastName,
      senderWalletAddress: loginStore.initialData.walletAddress,
      isSystemMessage: true,
      tokenAmount: 0,
      receiverMessageId: '',
      mucname: chatName,
      photoURL: loginStore.userAvatar,
      roomJid: jid,
      isReply: false,
      mainMessage: undefined,
      push: false,
    };

    sendMessageStanza(
      manipulatedWalletAddress,
      jid,
      isPrevious ? textLeave : textEnter,
      data,
      chatStore.xmpp,
    );
  };
  // join request, sends every time when user entered to the room
  const sendRoomJoin = async () => {
    try {
      await httpPost(roomRoute + '/join/' + chatId, {}, loginStore.userToken);
    } catch (error) {
      console.log(error);
    }
  };
  // sends leaving message to the room
  useEffect(() => {
    if (previousRoom?.name) {
      sendMessage(
        previousRoom.name,
        previousRoom.roomJid + apiStore.xmppDomains.CONFERENCEDOMAIN,
        true,
      );
    }
  }, [previousRoom]);
    // sends joining message to the room
  useEffect(() => {
    if (currentMetaRoom.name) {
      sendMessage(
        currentMetaRoom.name,
        currentMetaRoom.roomJid + apiStore.xmppDomains.CONFERENCEDOMAIN,
        false,
      );
      sendRoomJoin();
    }
  }, [currentMetaRoom]);

  const exportRooms = async () => {
    try {
      const res = await Share.open({
        message: JSON.stringify(metaRooms),
        title: 'Cached rooms',
      });
      console.log(res);
    } catch (error) {
      console.log(error);
    }
  };

  if (!currentMetaRoom.roomJid && !previousDirection) {
    return null;
  }

  const renderDirections = (direction: string) => {
    const oppositePreviousDirection = getOpositeDirection(previousDirection);
    if (checkEmptyDirections() && direction === oppositePreviousDirection) {
      return (
        <CompassItem
          name={oppositePreviousDirection + ':' + previousRoom?.name}
          chatId={chatId}
          room={previousRoom}
          setDirection={() => {
            setPreviousDirection(oppositePreviousDirection);
            setPreviuosRoom(previousRoom);
          }}
        />
      );
    }
    return (
      <CompassItem
        name={
          SHORT_DIRECTIONS[direction] +
          ':' +
          (currentMetaRoom.ownerNavLinks[direction]?.name ||
            currentMetaRoom.userNavLinks[direction]?.name)
        }
        chatId={chatId}
        room={
          currentMetaRoom?.ownerNavLinks?.[direction] ||
          currentMetaRoom?.userNavLinks?.[direction]
        }
        setDirection={() => {
          setPreviousDirection(direction);
          setPreviuosRoom(currentMetaRoom);
        }}
      />
    );
  };
  return (
    <Modal isVisible={open} onBackdropPress={onClose}>
      {loading ? (
        <ActivityIndicator color={commonColors.primaryColor} size={50} />
      ) : (
        <View style={styles.container}>
          <MetaHeader
            room={currentMetaRoom}
            direction={previousDirection}
            previousRoom={previousRoom}
          />
          <TouchableWithoutFeedback onPress={onClose}>
            <View style={{flex: 1}} />
          </TouchableWithoutFeedback>
          <View style={[styles.bottom, styles.innerContainer]}>
            {renderDirections(DIRECTIONS.NORTH)}
            <HStack justifyContent={'space-between'} alignItems={'center'}>
              <View style={{width: '30%'}}>
                {renderDirections(DIRECTIONS.WEST)}
              </View>
              <View
                style={{
                  width: '30%',
                  justifyContent: 'center',
                  alignItems: 'center',
                }}>
                <TouchableOpacity onPress={exportRooms} activeOpacity={0.9}>
                  <Ionicons
                    name={'compass'}
                    size={70}
                    color={commonColors.primaryDarkColor}
                  />
                </TouchableOpacity>
              </View>
              <View style={{width: '30%'}}>
                {renderDirections(DIRECTIONS.EAST)}
              </View>
            </HStack>
            {renderDirections(DIRECTIONS.SOUTH)}
          </View>
        </View>
      )}
    </Modal>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    height: hp('100%'),
    justifyContent: 'space-between',
  },
  innerContainer: {
    borderRadius: 20,
    justifyContent: 'center',
    alignItems: 'center',
    borderWidth: 2,
    borderColor: commonColors.primaryColor,
  },
  top: {
    backgroundColor: 'white',
    height: '20%',
    marginTop: 30,
  },
  bottom: {
    backgroundColor: 'white',
    height: '40%',
  },
});