import React from 'react'; import ReactModal from 'react-modal'; import PropTypes from 'prop-types'; import { nanoid } from 'nanoid'; import { X, AlertCircle } from 'react-feather'; import classNames from 'classnames'; import { useSelector, useDispatch } from 'react-redux'; import { createUser } from '@/reducers/user'; import Crypto from '@/utils/crypto'; import { connect as connectSocket } from '@/utils/socket'; import Nav from '@/components/Nav'; import Connecting from '@/components/Connecting'; import About from '@/components/About'; import Settings from '@/components/Settings'; import Welcome from '@/components/Welcome'; import RoomLocked from '@/components/RoomLocked'; import ActivityList from './ActivityList'; import styles from './styles.module.scss'; const crypto = new Crypto(); ReactModal.setAppElement('#root'); const Modal = ({ closeModal, modalComponent, roomId, translations, toggleSoundEnabled, togglePersistenceEnabled, soundIsEnabled, persistenceIsEnabled, toggleNotificationEnabled, toggleNotificationAllowed, notificationIsEnabled, notificationIsAllowed, setLanguage, language, }) => { const getModal = () => { switch (modalComponent) { case 'Connecting': return { component: , title: 'Connecting...', preventClose: true, }; case 'About': return { component: , title: translations.aboutHeader, }; case 'Settings': return { component: ( ), title: translations.settingsHeader, }; case 'Welcome': return { component: , title: translations.welcomeHeader, }; case 'Room Locked': return { component: , title: translations.lockedRoomHeader, preventClose: true, }; default: return { component: null, title: null, }; } }; const modalOpts = getModal(); return (
{!modalOpts.preventClose && ( )}

{modalOpts.title}

{modalOpts.component}
); }; const Home = ({ receiveEncryptedMessage, receiveUnencryptedMessage, activities, username, publicKey, members, socketId, roomId, roomLocked, modalComponent, openModal, closeModal, iAmOwner, userId, toggleWindowFocus, soundIsEnabled, persistenceIsEnabled, toggleSoundEnabled, togglePersistenceEnabled, notificationIsEnabled, notificationIsAllowed, toggleNotificationEnabled, toggleNotificationAllowed, toggleSocketConnected, socketConnected, sendUnencryptedMessage, sendEncryptedMessage, translations, setLanguage, language, }) => { const socketPayloadRef = React.useRef({ username: username, publicKey: publicKey, isOwner: iAmOwner, id: userId, }); socketPayloadRef.current = { username: username, publicKey: publicKey, isOwner: iAmOwner, id: userId, }; // Add blur et focus listeners React.useEffect(() => { const onFocus = () => { toggleWindowFocus(true); }; const onBlur = () => { toggleWindowFocus(false); }; window.addEventListener('focus', onFocus); window.addEventListener('blur', onBlur); return () => { window.removeEventListener('focus', onFocus); window.removeEventListener('blur', onBlur); }; }, [toggleWindowFocus]); React.useEffect(() => { const socket = connectSocket(socketId); socket.on('disconnect', () => { toggleSocketConnected(false); }); socket.on('connect', () => { socket.emit('USER_ENTER', { publicKey: socketPayloadRef.current.publicKey, }); toggleSocketConnected(true); }); socket.on('USER_ENTER', payload => { receiveUnencryptedMessage('USER_ENTER', payload); sendEncryptedMessage({ type: 'ADD_USER', payload: socketPayloadRef.current, }); if (payload.users.length === 1) { openModal('Welcome'); } }); socket.on('USER_EXIT', payload => { receiveUnencryptedMessage('USER_EXIT', payload); }); socket.on('ENCRYPTED_MESSAGE', payload => { receiveEncryptedMessage(payload); }); socket.on('TOGGLE_LOCK_ROOM', payload => { receiveUnencryptedMessage('TOGGLE_LOCK_ROOM', payload); }); socket.on('ROOM_LOCKED', () => { openModal('Room Locked'); }); const onUnload = () => { socket.emit('USER_DISCONNECT'); }; window.addEventListener('beforeunload', onUnload); return () => { window.removeEventListener('beforeunload', onUnload); onUnload(); socket.close(); }; }, [ openModal, receiveEncryptedMessage, receiveUnencryptedMessage, sendEncryptedMessage, socketId, toggleSocketConnected, ]); return (
{!socketConnected && (
{' '} Disconnected
)}
); }; export const WithUser = ({ ...rest }) => { const [loaded, setLoaded] = React.useState(false); const loading = React.useRef(false); const user = useSelector(state => state.user); const dispatch = useDispatch(); React.useEffect(() => { let mounted = true; const createUserLocal = async () => { const localUsername = user.username || nanoid(); const encryptDecryptKeys = await crypto.createEncryptDecryptKeys(); const exportedEncryptDecryptPrivateKey = await crypto.exportKey(encryptDecryptKeys.privateKey); const exportedEncryptDecryptPublicKey = await crypto.exportKey(encryptDecryptKeys.publicKey); if (!mounted) { loading.current = false; return; } const payload = { username: localUsername, publicKey: exportedEncryptDecryptPublicKey, privateKey: exportedEncryptDecryptPrivateKey, }; dispatch(createUser(payload)); dispatch({ type: 'CREATE_USER', payload }); loading.current = false; setLoaded(true); }; if (!loaded && !loading.current) { loading.current = true; createUserLocal(); } return () => { loading.current = false; mounted = false; }; }, [dispatch, loaded, user.username]); if (!loaded) { return null; } return ; }; WithUser.defaultProps = { modalComponent: null, }; WithUser.propTypes = { receiveEncryptedMessage: PropTypes.func.isRequired, receiveUnencryptedMessage: PropTypes.func.isRequired, activities: PropTypes.array.isRequired, members: PropTypes.array.isRequired, socketId: PropTypes.string.isRequired, roomId: PropTypes.string.isRequired, roomLocked: PropTypes.bool.isRequired, modalComponent: PropTypes.string, openModal: PropTypes.func.isRequired, closeModal: PropTypes.func.isRequired, iAmOwner: PropTypes.bool.isRequired, toggleWindowFocus: PropTypes.func.isRequired, soundIsEnabled: PropTypes.bool.isRequired, persistenceIsEnabled: PropTypes.bool.isRequired, toggleSoundEnabled: PropTypes.func.isRequired, togglePersistenceEnabled: PropTypes.func.isRequired, notificationIsEnabled: PropTypes.bool.isRequired, notificationIsAllowed: PropTypes.bool, toggleNotificationEnabled: PropTypes.func.isRequired, toggleNotificationAllowed: PropTypes.func.isRequired, toggleSocketConnected: PropTypes.func.isRequired, socketConnected: PropTypes.bool.isRequired, sendUnencryptedMessage: PropTypes.func.isRequired, sendEncryptedMessage: PropTypes.func.isRequired, setLanguage: PropTypes.func.isRequired, language: PropTypes.string.isRequired, translations: PropTypes.object.isRequired, }; export default WithUser;