Source

frontend/siinc-tv/src/components/userProfile/profile.js

import React, {useState, useContext, useEffect} from 'react';
import style from './profile.module.css'

import UserContext from "../../userContext";
import userActions from "../../user/userActions";
import userUtils from "../../user/userUtils";
import UserPreview from "../previews/userPreview";

import {handleFriendAction} from "../../user/friends";
import {handleFollowAction} from "../../user/follows";
import { withRouter } from 'react-router-dom';
import LiveStreamPreview from "../previews/liveStreamPreview";
import streamActions from '../../stream/streamActions';
import CreateableInputOnly from "../selectors/createableInputOnly";

import YoutubeLogo from "../../assets/YoutubeIcon.ico"
import TwitchLogo from "../../assets/TwitchGlitchPurple.svg"


/**
 * This component represents a user page
 * 
 * @prop {String} match.params.userid The id of the displayed user
 * @prop {String} initTab intial display tab
 * @prop {Boolean} editMode whether this page will start in edit mode
 * @component
 * @category Frontend
 * @subcategory Pages
 */
function Profile(props) {

    const [display, setUserInfoDisplay] = useState('friends');
    const userContext = useContext(UserContext);
    const userid = props.match.params.userid;
    const initTab = props.initTab;
    const [user, setUser] = useState(null);
    const [userOnline, setUserOnline] = useState('false');  
    const [labels, setLabels] = useState([]) 
    const [aboutInfo, setAboutInfo] = useState('') 
    const [editAboutInfo, setEditAboutInfo] = useState(false);
    const [friendsData, setFriendsData] = useState(null)
    const [profilePhoto, setProfilePhoto] = useState("")
    const [userName, setUserName] = useState("")
    const maxTagLength = 15;
    const [userFetchFail, setUserFetchFail] = useState(false);

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

  // We use this to style our selector
  const customTagStyle = {
    control: (styles, state) => ({
      ...styles,
      width: 613,
      marginTop: 10,
      backgroundColor: "#251A37",
      borderRadius: state.isFocused ? 3 : 3,
      height: 40,
      minHeight: 40,
      lineHeight: "150%",
      border: state.isFocused ? "1px solid rgb(153, 153, 153)" : "none",
      boxShadow: "0px 4px 4px rgba(0, 0, 0, 0.25)",
      ":hover": {
        borderRadius: 3,
        cursor: "text",
        border: "1px solid rgb(153, 153, 153)",
      },
    }),
    valueContainer: (styles) => ({ ...styles, height: 40, minHeight: 40 }),
    indicatorContainer: (styles) => ({
      ...styles,
      height: 40,
      minHeight: 40,
      paddingTop: 0,
      paddingBottom: 0,
    }),
    input: (styles) => ({
      ...styles,
      top: 20,
      lineHeight: 0,
      fontFamilt: "Roboto",
      textAlign: "center",
      fontWeight: "normal",
      color: "#AFAFAF",
    }),
    placeholder: (styles) => ({
      ...styles,
      top: 20,
      lineHeight: 0,
      fontFamily: "Roboto",
      textAlign: "center",
      fontWeight: "normal",
      color: "#AFAFAF",
    }),
    multiValue: (styles) => ({
      ...styles,
      textAlign: "center",
      bottom: 20,
      height: 30,
      backgroundColor: "#12343B",
      borderRadius: 5,
    }),
    multiValueLabel: (styles) => ({
      ...styles,
      height: 30,
      textAlign: "center",
      fontSize: 16,
      top: 15,
      color: "#FFFFFF",
      fontFamily: "Roboto",
    }),
    multiValueRemove: (styles) => ({
      ...styles,
      ":hover": {
        backgroundColor: "#071416",
        color: "#AFAFAF",
        cursor: "pointer",
      },
    }),
  };

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@


    useEffect(()=>{
        console.log("mounted1")
        let isMounted = true;
        userActions.getUserData( userid )
        .then(data=>{
            if( isMounted ) { 
                if( data.length === 0 ) {
                    console.log("Couldnt get user!!");
                    setUserFetchFail(true)
                    setUser(null)
                    return;
                }
                setUserFetchFail(false)
                setFriendsData(data.friendsData);
                setProfilePhoto(userUtils.assignImage(data))
                setUser(data);
                setAboutInfo(data.shortDescription)
                setLabels(data.interests)
                setUserName(data.username)
                setUserInfoDisplay('friends')
                setEditAboutInfo( (props.editMode !== undefined) ? props.editMode : false );
                if( data.currentStream && data.currentStream !== "" )
                    setUserInfoDisplay("live")
                if( initTab === "FOLLOWING")
                    setUserInfoDisplay('followers')
                if( initTab === "FRIENDS")
                    setUserInfoDisplay('friends')

                userActions.isUserOnline(data._id)
                .then((data) => {
                    if( isMounted ) {
                        setUserOnline(String(data))
                    }
                })
            }
        })
        return (() => {isMounted = false ; console.log("unmounted1")})
    }, [props.match])

    useEffect(()=>{ 
        console.log("mounted2")
        let isMounted = true;
        if( user ) { 
            setAboutInfo(user.shortDescription);
            setLabels(user.interests);

            const id = user._id;
            userActions.isUserOnline(id)
            .then((data) => {
                if( isMounted ) {
                    setUserOnline(String(data))
                }
            })
        }
        return (() => {isMounted = false ; console.log("unmounted2")})
    }, [user])

    const showDisplay = () => {
        if( !user )
            return 
        if( display === 'live' ) {
            if( !user.currentStream )
                return
            return <ProfileLiveDisplay streamId={user.currentStream.eventId}></ProfileLiveDisplay>
        }
        if( display === 'friends') {
            return <ProfileFriendsDisplay user={user} routerHistory={props.history}></ProfileFriendsDisplay>
        }
        if( display === 'followers') {
            return <ProfileFollowersDisplay user={user} routerHistory={props.history}></ProfileFollowersDisplay>
        }
        if( display === 'following') {
            return <ProfileFollowingDisplay user={user} routerHistory={props.history}></ProfileFollowingDisplay>
        }
    }

    // Sets type of form to display
    const setLiveDisplay=()=>{
        setUserInfoDisplay('live')
    }
    const setFollowersDisplay=()=>{
        setUserInfoDisplay('followers')
    }
    const setFriendsDisplay=()=>{
        setUserInfoDisplay('friends')
    }
    const setFollowingDisplay=()=>{
        setUserInfoDisplay('following')
    }


   const debugFriendRepr=()=> {
    if( userContext.user === null || user === null || friendsData === null ) {
        return "Loading";
    }
    if( !userContext.user ) {
        return "Not logged";
    }
    if( String(userContext.user._id) === String(user._id) ) {
        return "It's you!";
    }
    if(userContext.user.friendsData)
    {
        console.log(String(user._id))
        if( userContext.user.friendsData.friendsList.find(x=>String(x.memberId)===String(user._id)) !== undefined ) {     
            return "Unfriend";
        }
        if( userContext.user.friendsData.sentRequests.find(x=>String(x.userId)===String(user._id)) !== undefined ) {
            return "Pending";
        }
        if( userContext.user.friendsData.receivedRequests.find(x=>String(x.userId)===String(user._id)) !== undefined ) {
            return "Accept";
        }
    }

    return "Add Friend";
}

    const handleLive=()=>{
        props.history.push(`/stream_pages/${user.currentStream.eventId}`);
    }

    const onClickFriendAction=()=>{
        handleFriendAction(userContext.user, {username: user.username, userId: user._id});
        userContext.refreshUserData();
    }

    const onClickFollowAction=()=>{
        if( user )
            handleFollowAction(userContext.user, user);
        userContext.refreshUserData();
    }

    const debugFollowRepr=()=> {
        if( userContext.user === null || user === null )
            return "";
        if( String(userContext.user._id) === String(user._id) )
            return "you!";
        if(userContext.user.followData && 
           userContext.user.followData.followingList.find(x=>String(x.userId)===String(user._id)) !== undefined )
            return "Unfollow"
        return "Follow"
    }
    
    const isMe=()=>{
        if( !userContext.user || !user || String(userContext.user._id) !== String(user._id) )
            return false
        return true;
        
    }

    const isUserStreaming=()=>{
        if( !user || 
            !user.currentStream || 
            user.currentStream === "" )
        {
            return false;
        }
        return true;
    }

    const numOfFriends=()=>{
        if( !user )
            return 0;
        return user.friendsData.friendsList.length;
    }

    const numOfFollowers=()=>{
        if( !user )
            return 0;
        return user.followData.followersList.length;
    }

    const onClickEditDesc=()=>{
        setEditAboutInfo(true);
    }

    const onYoutubeClick=()=>{
        console.log("redirect to youtube channel : " + String(user.googleId))
    }

    const onTwitchClick=()=>{
        console.log("redirect to twitch channel : " + String(user.twitchId))
    }

    const onClickDoneEditDesc=()=>{
        /*Promise.all([userActions.updateUserShortDescription(user._id, aboutInfo),
                     userActions.updateUserInterests(user._id, labels)])*/
        userActions.updateUserShortDescription(user._id, aboutInfo)
        .then((res) => {
            console.log("res = ")
            console.log(res)
            userActions.updateUserInterests(user._id, labels)
        .then((res) => {        
            console.log("Finished doing both actions")
            userActions.getUserData( user._id )
        .then(data => {
            setUser(data)
            props.history.replace("/users/"+user._id);
        })})})
        .catch(error => {
            console.log("Error editing channel")
            console.log(error)
        })
        setEditAboutInfo(false);
        
    }

    return (
        <div>
            { (user == null && userFetchFail ) && 
            <div className={style.badUser}>
                <h1>Sorry, there appears to be a problem displaying this user</h1>
            </div>
            }
        { (user != null ) &&
         <div>
            <hr className={style.seperatorTitle}></hr>    
            <div>
                <img className={style.profilePhoto} src={profilePhoto}/>
            </div>
            <div className={style.userInfo}>
                <div className={style.userTitle}>
                    <ul className={style.testMyUl}>
                    <li className={style.testMyLi}><span className={style.name}>{userName}</span></li>
                    <li className={style.testMyLi}>{
                        (userOnline==='true' && <span className={style.online}/>) ||
                        (userOnline==='false'&& <span className={style.offline}/>)}</li>
                    <li className={style.testMyLi}>{ isUserStreaming() && <span className={style.live} onClick={handleLive}>Live</span>}</li>
                    <li className={style.testMyLi}><TwitchLogoLink  user={user} onClick={onTwitchClick}></TwitchLogoLink>
                                                   <YoutubeLogoLink user={user} onClick={onYoutubeClick}></YoutubeLogoLink></li>
                    { userContext.user && <div className={style.responsiveButtons}>
                        { !isMe() && <button className={style.addFriendsButton} onClick={onClickFriendAction}>{debugFriendRepr()}</button>}
                        { !isMe() && <button className={style.addFavoritesButton} onClick={onClickFollowAction}>{debugFollowRepr()}</button>}
                        { ( isMe() && (!editAboutInfo) ) &&
                                            <a><button className={style.editButton} onClick={onClickEditDesc} > Edit </button></a> }
                        { ( isMe() && (editAboutInfo) ) &&
                                            <a><button className={style.editButton} onClick={onClickDoneEditDesc} > Done </button></a> }
                    </div> }  
                    </ul>
                </div>
                <div className={style.userSocial}>
                    <span className={style.number}>
                                {numOfFriends()} Friends
                                <br />
                                {numOfFollowers()} Followers
                    </span>
                </div>
                <br />
                <div className={style.aboutContent}>
                    <AboutContainer desc={aboutInfo} setDescription={setAboutInfo} editShortDesc={editAboutInfo}></AboutContainer>
                </div>
                <div className={style.pointsSpan}>Interests: </div>
                        { labels && !editAboutInfo && 
                          labels.map((value, index) => {
                            return <label className={style.pointsLabel} key={index}>{value.value}</label>
                        })}
                        {!editAboutInfo && (!labels || labels.length === 0) && <label>No Interests found!</label>}
                        { editAboutInfo && 
                          <CreateableInputOnly style={customTagStyle} value={labels} updateTags={setLabels} maxLen={maxTagLength} /> }
            </div>
            
            <div className={style.userPageSelector}>
                <hr className={style.seperatorSelector}></hr>
                { isUserStreaming() && <button className={style.tabListBtn} onClick={setLiveDisplay}>Live</button>}
                            <button className={style.tabListBtn} onClick={setFriendsDisplay}>Friends</button>
                            <button className={style.tabListBtn} onClick={setFollowersDisplay}>Followers</button>
                            <button className={style.tabListBtn} onClick={setFollowingDisplay}>Following</button>
                <div className={style.displayContainer}>
                    {showDisplay()}
                </div>
            </div>
        </div>}
        </div>
    );
}

//---------------------- Live stream preview display -----------------------------

function ProfileLiveDisplay(props) {
    const streamId = props.streamId;
    const [streamData, setStreamData] = useState(null)

    useEffect(() => {
        streamActions.getStreamById(streamId)
        .then(data => {
            setStreamData(data);
        })
        .catch(error => {
            console.log("Error in ProfileLiveDisplay")
            console.log(error);
        })
    }, [props.streamId]) 


    return (
        <div className={style.relevantContent}>
            <br />
            { streamData && 
              <LiveStreamPreview key={streamData._id} 
                streamData={streamData} 
              /> }
        </div>
    )
}

//------------------------------------------------------------------------------

// ------------------------------- Friends display -----------------------------

function ProfileFriendsDisplay(props) {

    const [user, setUser] = useState(props.user);

    useEffect(() => {
        setUser(props.user)
    }, [props.user])

    function mapFriends(){
        if( !user )
            return

        if( user.friendsData.friendsList.length === 0 ) {
            return "Friends list is empty"
        }

        return((user.friendsData.friendsList).map((friend, index)=>{
            return(
                    <UserPreview key={index} user={ {_id: friend.memberId, username: friend.displayName, image: friend.userImage } } />
            )
            
        })) ;
    }

    return (
        <div className={style.friendsOuterDisplayContainer}>
            <div className={style.friendsInnerDisplayContainer}>
                {mapFriends()}
            </div>
        </div>

    )
}

// ----------------------------------------------------------------------

// ----------------------  Follower display ---------------------------

function ProfileFollowersDisplay(props) {

    const [user, setUser] = useState(props.user);

    useEffect(() => {
        setUser(props.user)
    }, [props.user])

    function mapFollowers(){
        if( !user )
            return

        if( user.followData.followersList.length === 0 ) {
            return "No followers"
        }

        return((user.followData.followersList).map((follower, index)=>{
            return(
                    <UserPreview key={index} user={ {_id: follower.userId, username: follower.userName, image: follower.userImage } } />
            )
        })) ;
    }

    return (
        <div className={style.followersOuterDisplayContainer}>
            <div className={style.followersInnerDisplayContainer}>
                {mapFollowers()}
            </div>
        </div>
    )

}
//-------------------------------------------------------------------

// ----------------------  Following display ---------------------------

function ProfileFollowingDisplay(props) {

    const [user, setUser] = useState(props.user);

    useEffect(() => {
        setUser(props.user)
    }, [props.user])

    function mapFollowing(){
        if( !user )
            return

        if( user.followData.followingList.length === 0 ) {
            return "No followings"
        }

        return((user.followData.followingList).map((following, index)=>{
            return(
                    <UserPreview key={index} user={ {_id: following.userId, username: following.userName, image: following.userImage } } />
            )
        })) ;
    }

    return (
        <div className={style.followersOuterDisplayContainer}>
            <div className={style.followersInnerDisplayContainer}>
                {mapFollowing()}
            </div>
        </div>
    )

}
//-------------------------------------------------------------------

// ---------------------   Short description  ---------------------

function AboutContainerEdit(props) {
    const setDescription = props.setDescription;

    return (
        <div>
            <textarea className={style.descriptionTextEditor}  
                onChange={e => setDescription(e.target.value)} 
                type="text" value={props.desc}/>
        </div>
    )
}

function AboutContainerView(props) {

    return (
        <p>
            {props.desc}
        </p>
    )
}

function AboutContainer(props) {
    const description = props.desc;
    const setDescription = props.setDescription;

    if(props.editShortDesc) {
        return (
            <AboutContainerEdit desc={description} setDescription={setDescription}></AboutContainerEdit>
        )
    }
    else {
        return (
            <AboutContainerView desc={description}></AboutContainerView>
        )
    }
}

// ---------------------------------------------------------

// ------------ Twitch and Youtube logos -------------------

function TwitchLogoLink(props) {
    const user = props.user;

    const isTwitchUser=()=>{
        if( !user ||
            !user.twitchId ||
            user.twitchId === "")
        {
            return false;
        }
        return true;
    }

    return(
        <div style={{display: "inline"}}>
        {isTwitchUser() && <div className={style.twitchLogoLink} >
        {
            <a href={String("https://www.twitch.tv/" + user.username)} target="_blank">
                <img Style="width:100%;height:100%;"
                   src={TwitchLogo}
                   onClick={props.onClick}
                >
                </img>
            </a>
        }
        </div>}
        </div>
    )

}

function YoutubeLogoLink(props) {
    const user = props.user;
    
    const isYoutubeUser=()=>{
        if( !user ||
            !user.googleId ||
            user.googleId === "")
        {
            return false;
        }
        return true;
    }

    return(
        <div style={{display: "inline"}}>
        {   
            isYoutubeUser() && 
            <a href={String("https://www.youtube.com/channel/" + user.googleData.youtubeId)} target="_blank">
                <img className={style.youtubeLogoLink} 
                   src={YoutubeLogo}
                   onClick={props.onClick}
                >
                </img>
            </a>
        }     
        </div>
    )
}
// ---------------------------------------------------------

export default withRouter(Profile)