diff --git a/package.json b/package.json index d53a7ad..597dfdb 100644 --- a/package.json +++ b/package.json @@ -8,18 +8,25 @@ "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", + "array-move": "^3.0.1", + "audio-react-recorder": "^1.0.4", "axios": "^0.21.1", "bootstrap": "^5.0.1", "jalali-moment": "^3.3.10", "prettier": "^2.3.1", "prop-types": "^15.7.2", "react": "^17.0.2", + "react-audio-player": "^0.17.0", "react-dom": "^17.0.2", "react-elastic-carousel": "^0.11.5", + "react-leaflet": "^3.2.0", + "react-player": "^2.9.0", "react-qr-scanner": "^1.0.0-alpha.7", "react-redux": "^7.2.4", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", + "react-sortable-hoc": "^2.0.0", + "react-swipeable-views": "^0.14.0", "react-toastify": "^7.0.4", "redux": "^4.1.0", "redux-devtools-extension": "^2.13.9", diff --git a/src/AppRouter.js b/src/AppRouter.js index cee95d1..d0cea3e 100644 --- a/src/AppRouter.js +++ b/src/AppRouter.js @@ -12,6 +12,8 @@ import QR from './views/QR/index'; import Auth from './views/Auth/index'; import QRScan from './views/QRScan/index'; import Activation from './views/Activation/index'; +import ChatList from './views/ChatList/index'; +import Chatroom from './views/ChatRoom/index'; class AppRouter extends Component { constructor(props) { @@ -64,6 +66,15 @@ class AppRouter extends Component { + + + + { + window.innerWidth < 1000 ? + + + : null + } ); diff --git a/src/Redux/actions/message.js b/src/Redux/actions/message.js index 14d7279..064898f 100644 --- a/src/Redux/actions/message.js +++ b/src/Redux/actions/message.js @@ -1,13 +1,13 @@ -// import proxy from '../proxy'; -// export const list = -// (data = {}) => -// async (dispatch) => -// (await proxy.get('message/list', data)).dispatch(dispatch); -// export const send = -// (data = {}) => -// async (dispatch) => -// (await proxy.post('message/send', data)).dispatch(dispatch); -// export const sendKeys = -// (data = {}) => -// async (dispatch) => -// (await proxy.post('message/sendKeys', data)).dispatch(dispatch); +import proxy from '../proxy'; +export const list = + (data = {}) => + async (dispatch) => + (await proxy.get('message/list', data)).dispatch(dispatch); +export const send = + (data = {}) => + async (dispatch) => + (await proxy.post('message/send', data)).dispatch(dispatch); +export const sendKeys = + (data = {}) => + async (dispatch) => + (await proxy.post('message/sendKeys', data)).dispatch(dispatch); diff --git a/src/Redux/proxy.js b/src/Redux/proxy.js index 7b3ea3c..c3ab403 100644 --- a/src/Redux/proxy.js +++ b/src/Redux/proxy.js @@ -1,6 +1,6 @@ import axios from 'axios'; import { toast } from 'react-toastify'; -const baseURL = 'https://chatstory.ir/api/v1'; //"https://chatstory.ir/api/v1"; +const baseURL = 'https://chatstory.ir/api/v1'; //'https://chatstory.ir/api/v1'; const token = ''; const loginURL = 'user/otp/login'; const logoutURL = 'user/logout'; @@ -48,7 +48,7 @@ class Proxy { localStorage.setItem('refresh', login.refreshToken); delete login.refreshToken; localStorage.setItem('userData', JSON.stringify(login)); - if (data !== {}) window.router.setState({ islogin: true }); + if (data != {}) window.router.setState({ islogin: true }); return this.result('login', login); }; logout = async () => { @@ -62,7 +62,7 @@ class Proxy { let refresh = localStorage.getItem('refresh'); let userData = localStorage.getItem('userData'); if (!refresh) return false; - if (refresh === 'undefined') { + if (refresh == 'undefined') { localStorage.removeItem('refresh'); localStorage.removeItem('userData'); return false; diff --git a/src/Redux/reducers/message.js b/src/Redux/reducers/message.js index 4f00511..2cda7d0 100644 --- a/src/Redux/reducers/message.js +++ b/src/Redux/reducers/message.js @@ -1,16 +1,16 @@ -// const initialState = { -// list: [], -// }; -// export default function chat(state = initialState, action) { -// let { type, data } = action; -// switch (type) { -// case 'message/list': -// return { ...state, list: data }; -// case 'message/send': -// return { ...state, list: [...list, ...data] }; -// case 'message/sendKeys': -// return { ...state, list: [...list, ...data] }; -// default: -// return state; -// } -// } +const initialState = { + list: [], +}; +export default function chat(state = initialState, action) { + let { type, data } = action; + switch (type) { + case 'message/list': + return { ...state, list: data }; + case 'message/send': + return { ...state, list: [...state.list, ...data] }; + case 'message/sendKeys': + return { ...state, list: [...state.list, ...data] }; + default: + return state; + } +} diff --git a/src/assets/images/whatsapp.png b/src/assets/images/whatsapp.png new file mode 100644 index 0000000..301d951 Binary files /dev/null and b/src/assets/images/whatsapp.png differ diff --git a/src/views/ChatList/Chat/index.js b/src/views/ChatList/Chat/index.js new file mode 100644 index 0000000..8697ef4 --- /dev/null +++ b/src/views/ChatList/Chat/index.js @@ -0,0 +1,22 @@ +import React, { Component } from 'react'; +import { Link } from 'react-router-dom'; +import moment from 'jalali-moment'; +import Avatar from '@material-ui/core/Avatar'; + +import './index.scss'; +export default class Chat extends Component { + render() { + const { data } = this.props; + return ( + + +
+

{data.name}

+

{data.lastMessage.slice(0, 40) + '...'}

+ {data.badge} +
{moment(data.date || new Date()).format('jD/jM hh:mm')}
+
+ + ); + } +} diff --git a/src/views/ChatList/Chat/index.scss b/src/views/ChatList/Chat/index.scss new file mode 100644 index 0000000..9e79cd4 --- /dev/null +++ b/src/views/ChatList/Chat/index.scss @@ -0,0 +1,72 @@ +@media screen and (max-width: 4500px) { + .chat { + width: 100%; + direction: rtl; + align-items: center; + margin-bottom: 10px; + position: relative; + padding: 0px 0px 0px; + text-decoration: none; + .avatar { + background-color: #b4b4b4; + width: 55px; + height: 55px; + // padding: 5px; + border-radius: 35px; + object-fit: cover; + } + &__data { + text-align: right; + flex: 1; + justify-content: space-around; + height: 100%; + padding: 10px 10px 20px; + border-bottom: rgba(209, 208, 208, 0.7) solid 1px; + + &--name { + font-size: 18px; + } + h1 { + font-family: numeralMedium; + font-size: 15px; + margin: 0; + font-weight: 600; + color: #6f7074; + } + p { + font-size: 12px; + margin: 0; + max-width: calc(100vw - 100px); + font-family: numeralLight; + color: #6f7074; + } + span { + background-color: #25d366; + min-width: 20px; + font-size: 11px; + height: 20px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 13px; + position: absolute; + padding-top: 1px; + padding: 0 5px; + left: 20px; + bottom: 10px; + color: white; + } + div { + color: rgb(99, 99, 99); + position: absolute; + left: 20px; + font-size: 12px; + top: 5px; + } + } + } +} + +a{ + text-decoration: none; +} diff --git a/src/views/ChatList/index.js b/src/views/ChatList/index.js new file mode 100644 index 0000000..a331790 --- /dev/null +++ b/src/views/ChatList/index.js @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; +import {Link} from 'react-router-dom'; +import { list } from '../../Redux/actions/chat'; +import { connect } from 'react-redux'; +import ChatIcon from '@material-ui/icons/Chat'; +import Chat from './Chat/index'; +import Navbar from '../Home/Navbar/index'; + + +import './index.scss'; + +class ChatList extends Component { + componentDidMount() { + this.props.list(); + } + render() { + return ( +
+ + {this.props.chat_list.map((item, i) => ( + + ))} + + + +
+ ); + } +} + +export default connect( + (state) => ({ + chat_list: state.chat.list || [], + }), + { list } +)(ChatList); + diff --git a/src/views/ChatList/index.scss b/src/views/ChatList/index.scss new file mode 100644 index 0000000..eb2b27d --- /dev/null +++ b/src/views/ChatList/index.scss @@ -0,0 +1,26 @@ +@media screen and (max-width: 4500px) { + .chat-list { + flex: 1; + padding: 70px 14px 10px; + position: relative; + background: white; + &__float { + position: fixed; + height: 50px; + width: 50px; + bottom: 20px; + left: 20px; + justify-content: center; + align-items: center; + background-color: #01db38; + border-radius: 100px; + + box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.4); + & svg { + color: white; + width: 25px; + height: 25px; + } + } + } +} diff --git a/src/views/ChatRoom/Body/Emoji/index.js b/src/views/ChatRoom/Body/Emoji/index.js new file mode 100644 index 0000000..0b5f1e3 --- /dev/null +++ b/src/views/ChatRoom/Body/Emoji/index.js @@ -0,0 +1,224 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { makeStyles } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; + +import './index.scss'; + +const Smileys = [ + '😃', + '😃', + '😄', + '😁', + '😆', + '😅', + '😂', + '🤣', + '🥲', + '☺️', + '😊', + '😇', + '🙂', + '🙃', + '😉', + '😌', + '😍', + '🥰', + '😘', + '😗', + '😙', + '😚', + '😋', + '😛', + '😝', + '😜', + '🤪', + '🤨', + '🧐', + '🤓', + '😎', + '🥸', + '🤩', + '🥳', + '😏', + '😒', + '😞', + '😔', + '😟', + '😕', + '🙁', + '☹️', + '😣', + '😖', + '😫', + '😩', + '🥺', + '😢', + '😭', + '😤', + '😠', + '😡', + '🤬', + '🤯', + '😳', + '🥵', + '🥶', + '😱', + '😨', + '😰', + '😥', + '😓', + '🤗', + '🤔', + '🤭', + '🤫', + '🤥', + '😶', + '😐', + '😑', + '😬', + '🙄', + '😯', + '😦', + '😧', + '😮', + '😲', + '🥱', + '😴', + '🤤', + '😪', + '😵', + '🤐', + '🥴', + '🤢', + '🤮', + '🤧', + '😷', + '🤒', + '🤕', + '🤑', + '🤠', + '😈', + '👿', + '👹', + '👺', + '🤡', + '💩', + '👻', + '💀', + '☠️', + '👽', + '👾', + '🤖', + '🎃', + '😺', + '😸', + '😹', + '😻', + '😼', + '😽', + '🙀', + '😿', + '😾', +]; + +const animals = [ + '🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐻‍❄️', '🐨', '🐯', '🦁', '🐮', '🐷', '🐽', '🐸', '🐵', '🙈', '🙉', '🙊', '🐒', '🐔', '🐧', '🐦', '🐤', '🐣', '🐥', '🦆', '🦅', '🦉', '🦇', '🐺', '🐗', '🐴', '🦄', '🐝', '🪱', '🐛', '🦋', '🐌', '🐞', '🐜', '🪰', '🪲', '🪳', '🦟', '🦗', '🕷', '🕸', '🦂', '🐢', '🐍', '🦎', '🦖', '🦕', '🐙', '🦑', '🦐', '🦞', '🦀', '🐡', '🐠', '🐟', '🐬', '🐳', '🐋', '🦈', '🐊', '🐅', '🐆', '🦓', '🦍', '🦧', '🦣', '🐘', '🦛', '🦏', '🐪', '🐫', '🦒', '🦘', '🦬', '🐃', '🐂', '🐄', '🐎', '🐖', '🐏', '🐑', '🦙', '🐐', '🦌', '🐕', '🐩', '🦮', '🐕‍🦺', '🐈', '🐈‍⬛', '🪶', '🐓', '🦃', '🦤', '🦚', '🦜', '🦢', '🦩', '🕊', '🐇', '🦝', '🦨', '🦡', '🦫', '🦦', '🦥', '🐁', '🐀', '🐿', '🦔', '🐾', '🐉', '🐲', '🌵', '🎄', '🌲', '🌳', '🌴', '🪵', '🌱', '🌿', '☘️', '🍀', '🎍', '🪴', '🎋', '🍃', '🍂', '🍁', '🍄', '🐚', '🪨', '🌾', '💐', '🌷', '🌹', '🥀', '🌺', '🌸', '🌼', '🌻', '🌞', '🌝', '🌛', '🌜', '🌚', '🌕', '🌖', '🌗', '🌘', '🌑', '🌒', '🌓', '🌔', '🌙', '🌎', '🌍', '🌏', '🪐 ', '💫', '⭐️', '🌟', '✨', '⚡️', '☄️', '💥', '🔥', '🌪', '🌈', '☀️', '🌤', '⛅️', '🌥', '☁️', '🌦', '🌧', '⛈', '🌩', '🌨', '❄️', '☃️', '⛄️', '🌬', '💨', '💧', '💦', '☔️', '☂️', '🌊', '🌫' +]; + +function TabPanel(props) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +TabPanel.propTypes = { + children: PropTypes.node, + index: PropTypes.any.isRequired, + value: PropTypes.any.isRequired, +}; + +function a11yProps(index) { + return { + id: `scrollable-force-tab-${index}`, + 'aria-controls': `scrollable-force-tabpanel-${index}`, + }; +} + +const useStyles = makeStyles((theme) => ({ + root: { + flexGrow: 1, + width: '100%', + backgroundColor: theme.palette.background.paper, + flexDirection: 'row-reverse' + }, + list: { + flex: 1, + width: '100%', + flexDirection: 'row-reverse' + }, +})); + +export default function ScrollableTabsButtonForce(props) { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + + const handleChange = (event, newValue) => { + setValue(newValue); + }; + console.log(props.emojies[0]); + return ( +
+ + + {props.emojies.map((item) => ( + + ))} + + + {props.emojies.map((item) => ( + +
+ {item.list.map((jojo,i) => ( + props.typeText('emoji',jojo)} style={{fontSize: 24, width: 30, height:30 }} className="m-1">{jojo} + ))} +
+
+ ))} +
+ ); +} + +ScrollableTabsButtonForce.defaultProps = { + emojies: [ + { type: 'Smileys', list: Smileys, id: 0 }, + { type: 'Animals', list: animals, id: 1 }, + ], +}; + diff --git a/src/views/ChatRoom/Body/Emoji/index.scss b/src/views/ChatRoom/Body/Emoji/index.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/views/ChatRoom/Body/Input/index.js b/src/views/ChatRoom/Body/Input/index.js new file mode 100644 index 0000000..4e7f62d --- /dev/null +++ b/src/views/ChatRoom/Body/Input/index.js @@ -0,0 +1,171 @@ +import React, { Component } from 'react'; +import AudioReactRecorder from 'audio-react-recorder'; +import MicIcon from '@material-ui/icons/Mic'; +import CheckIcon from '@material-ui/icons/Check'; +import CloseIcon from '@material-ui/icons/Close'; +import AttachmentIcon from '@material-ui/icons/Attachment'; +import TextField from '@material-ui/core/TextField'; +import SendIcon from '@material-ui/icons/Send'; +import EmojiEmotions from '@material-ui/icons/EmojiEmotions'; +import KeyboardIcon from '@material-ui/icons/Keyboard'; +import PhotoCamera from '@material-ui/icons/PhotoCamera'; + +import './index.scss'; +export default class ChatInputs extends Component { + state = { + emoji: false, + }; + render() { + const { + onStop, + stop, + start, + recordState, + cancel, + typeText, + message, + sendMessage, + addFile, + file, + addcounter, + } = this.props; + return ( +
+ + {/* + + { + recordState ? + : null + } */} + +
+
+ {(recordState === null && message === '' && file === null) || + (recordState && recordState.url && message === '' && file === null) ? ( +
+ +
+ ) : null} + {message !== '' || file !== null ? ( +
addcounter()}> + +
+ ) : null} + {recordState === 'start' ? ( + <> +
+ +
+
+ +
+ + ) : null} +
+
+ {recordState !== 'start' ? ( +
+ {message === '' ? ( + + ) : null} +
+ addFile(e)} /> + + +
+ + typeText('text',e.target.value)} + multiline + style={{ + marginRight: 5, + }} + /> + {this.props.parent.state.emoji ? ( + this.props.parent.setState({ emoji: false })} + /> + ) : ( + this.props.parent.setState({ emoji: true })} + /> + )} +
+ ) : null} +
+ ); + } +} diff --git a/src/views/ChatRoom/Body/Input/index.scss b/src/views/ChatRoom/Body/Input/index.scss new file mode 100644 index 0000000..309440b --- /dev/null +++ b/src/views/ChatRoom/Body/Input/index.scss @@ -0,0 +1,97 @@ +@media screen and (max-width: 4500px) { + .chat-inputs { + border-bottom-right-radius: 40px; + border-bottom-left-radius: 40px; + width: 100%; + padding: 2px 5px 0px; + align-items: flex-end; + justify-content: flex-end; + &__box { + width: 100%; + border-radius: 30px; + background-color: #fff; + box-shadow: 0 0.4px 1px 0px #aaa; + // background: rgb(18, 15, 153); + // background: linear-gradient(90deg, rgba(18, 15, 153, 1) 0%, rgba(105, 9, 121, 1) 60%); + min-height: 45px; + margin-left: 10px; + align-items: center; + padding: 8px 15px; + &--file { + width: 30px; + height: 30px; + position: relative; + align-self: flex-end; + input[type='file'] { + opacity: 0; + width: 100%; + height: 100%; + z-index: 100; + position: relative; + } + } + // input[type=text]{ + // flex : 1; + // background-color: inherit; + // border : 0px; + // text-align: right; + // direction: rtl; + // color : white; + // font-size: 14px; + // &:focus{ + // outline:none; + // } + // &::placeholder{ + // color : white; + // } + // } + .MuiTextField-root { + flex: 1; + border: 0px !important; + outline: none !important; + text-align: right; + textarea { + text-align: right; + direction: rtl; + color: black; + font-size: 14px; + font-family: IRANSansNumeralLight; + } + } + } + &__voice { + &--icon { + width: 50px; + height: 50px; + border-radius: 30px; + justify-content: center; + align-items: center; + background-color: white; + } + } + } +} + +.audio-react-recorder { + display: none; +} + +.MuiInput-underline:hover:not(.Mui-disabled):before { + border-bottom: 0px !important; +} + +.MuiInput-underline:hover:not(.Mui-disabled):before { + border-bottom: 0px !important; +} + +.MuiInput-underline:hover:not(.Mui-disabled):before { + border-bottom: 0px !important; +} + +.MuiInput-underline:after { + border-bottom: 0px !important; +} + +.MuiInput-underline:before { + border-bottom: 0px !important; +} diff --git a/src/views/ChatRoom/Body/Keyboard/index.js b/src/views/ChatRoom/Body/Keyboard/index.js new file mode 100644 index 0000000..b458f1e --- /dev/null +++ b/src/views/ChatRoom/Body/Keyboard/index.js @@ -0,0 +1,32 @@ +import React, { Component } from 'react'; +import Carousel from 'react-elastic-carousel'; +import './index.scss'; +export default function Keyboard({ options, action }) { + return ( +
+ + {options.map((row, i) => ( + + ))} + +
+ ); +} + +const Row = ({ row, action }) => { + return ( +
+ {row.map((item, i) => ( + + ))} +
+ ); +}; + +const KeyboardItem = ({ item, action }) => { + return ( +
action(item)}> + {item.text} +
+ ); +}; diff --git a/src/views/ChatRoom/Body/Keyboard/index.scss b/src/views/ChatRoom/Body/Keyboard/index.scss new file mode 100644 index 0000000..fa9c43f --- /dev/null +++ b/src/views/ChatRoom/Body/Keyboard/index.scss @@ -0,0 +1,74 @@ +@media screen and (max-width: 4500px) { + .chat-suggest { + & .rec-slider-container { + margin: 0 !important; + } + // border-bottom-right-radius: 40px; + // border-bottom-left-radius: 40px; + width: 100%; + // height : 40px; + padding: 2px 0px 0px; + align-items: flex-start; + margin-top: 5px; + &__row { + width: 100%; + &--item { + padding: 8px 6px; + background-color: white; + border-radius: 5px; + margin: 0 2px; + width: 100% !important; + justify-content: right; + color: black; + height: 90px; + text-align: right; + } + } + } +} + +.portrait-slider { + width: 100% !important; + height: 100% !important; + position: relative; + padding: 0px 6px 0px; + margin-bottom: 2px; + justify-content: center !important; +} + +.rec-dot_active { + background-color: white !important; + color: white !important; + // box-shadow: 0 0 1px rgba(255, 255, 255, 1) !important; +} + +.Carousel-button-10 { + background-color: white !important; +} +.rec-pagination { + z-index: 600; + margin-top: 2px !important; + // position: absolute; + // top: -12px; + // left: calc(50% - 20px) !important; + & button { + background-color: grey; + margin: 2px !important; + box-shadow: 0px 0px 0px rgba(255, 255, 255, 1); + } +} + +// .guuruq{ +// box-shadow: 0 1px 2px rgba(0, 0, 0, 0%); +// } + +// .cJeRRR{ +// background-color: white !important; + +// } + +// .cJeRRR:hover, .cJeRRR:focus{ +// cursor: pointer; +// box-shadow: 0px 0px 0px rgba(255,255,255,1) !important; +// background-color: white !important; +// } diff --git a/src/views/ChatRoom/Body/Message/Comment/index.js b/src/views/ChatRoom/Body/Message/Comment/index.js new file mode 100644 index 0000000..e40d64a --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Comment/index.js @@ -0,0 +1,83 @@ +import React, { Component } from 'react'; +import DoneIcon from '@material-ui/icons/Done'; +import DoneAllIcon from '@material-ui/icons/DoneAll'; +import EditIcon from '@material-ui/icons/Edit'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import './index.scss'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import ReplyIcon from '@material-ui/icons/Reply'; +import ForwardIcon from '@material-ui/icons/Forward'; +import DeleteIcon from '@material-ui/icons/Delete'; +import ScheduleIcon from '@material-ui/icons/Schedule'; +const menu = [ + { text: 'پسند', icon: FavoriteIcon, onClick: () => {} }, + { text: 'تبادل نظر', icon: TextsmsIcon, onClick: () => {} }, + { text: 'پاسخ', icon: ReplyIcon, onClick: () => {} }, + { text: 'باز ارسال', icon: ForwardIcon, onClick: () => {} }, +]; +const selfMenu = [ + { text: 'باز ارسال', icon: ForwardIcon, onClick: () => {} }, + { text: 'ویرایش', icon: EditIcon, onClick: () => {} }, + { text: 'حذف', icon: DeleteIcon, onClick: () => {} }, +]; +export default class Comment extends Component { + state = { messageMenu: null }; + handleClose = () => { + this.setState({ + messageMenu: null, + }); + }; + handleClick = (event) => { + this.setState({ + messageMenu: event.currentTarget, + }); + }; + render() { + const { message, user, document, children, addcounter } = this.props; + const { messageMenu } = this.state; + const hasavatar = prevUserId != message.userId; + + return ( +
+
+
+
addcounter('nothing')}> + +
+ this.handleClose()} + className="rtl fontLight" + > + {(isSelf ? selfMenu : menu).map((e) => ( + { + this.handleClose(); + e.onClick(); + addcounter('nothing'); + }} + > + + {e.text} + + ))} + +
+ +

{message.text}

+
+
+ ); + } +} diff --git a/src/views/ChatRoom/Body/Message/Comment/index.scss b/src/views/ChatRoom/Body/Message/Comment/index.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/views/ChatRoom/Body/Message/Inline/index.js b/src/views/ChatRoom/Body/Message/Inline/index.js new file mode 100644 index 0000000..bccb404 --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Inline/index.js @@ -0,0 +1,46 @@ +import React, { Component } from 'react'; +import Avatar from '@material-ui/core/Avatar'; + +import './index.scss'; +import { toast } from 'react-toastify'; + +export default class Message2D extends Component { + state = { value: this.props.message.value }; + click(id) { + if (this.props.isActive) { + this.setState({ value: id }); + toast.success('ثبت شد'); + this.props.action(); + } + } + render() { + const { message, user, isSelf, isActive, action } = this.props; + return ( +
+
+ {message.options.map((option, i) => ( +
+ {option.map((item, i) => ( +
this.click(item.id)} + > + {item.name} +
+ ))} +
+ ))} +
+
+ ); + } +} diff --git a/src/views/ChatRoom/Body/Message/Inline/index.scss b/src/views/ChatRoom/Body/Message/Inline/index.scss new file mode 100644 index 0000000..3b1c3ba --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Inline/index.scss @@ -0,0 +1,101 @@ +@media screen and (max-width: 4500px) { + .message-2d { + display: flex; + justify-content: flex-end; + // width: 100%; + position: relative; + img { + width: 65px; + height: 65px; + object-fit: cover; + border-radius: 30px; + border: 3px solid rgba(201, 201, 201, 0.857); + position: relative; + top: 20px; + z-index: 50; + } + &__text { + justify-content: flex-end; + width: 100%; + padding: 15px 15px 5px; + background-color: white; + border-radius: 5px; + position: relative; + margin-bottom: 5px; + text-align: right; + direction: rtl; + p { + width: 100%; + text-align: right; + font-size: 16px; + // font-family: IRANSansNumeralBold; + } + } + &__options { + width: 100%; + z-index: 200; + &--row { + width: 100%; + justify-content: space-around; + div { + background-color: rgb(255 255 255 / 40%); + border-radius: 10px; + justify-content: center; + align-items: center; + margin: 2px 3px; + padding: 6px; + flex: 1; + text-align: center; + span { + font-size: 16px; + color: black; + font-weight: 400; + } + } + } + &--radio { + width: calc(50% - 6px); + margin: 3px; + padding: 10px; + text-align: center; + font-size: 16px; + color: black; + font-weight: 600; + border-radius: 5px; + } + &--draggable { + background-color: rgb(255 255 255 / 40%); + border-radius: 10px; + justify-content: center; + align-items: center; + margin: 2px 3px; + padding: 6px; + font-size: 16px; + color: black; + font-weight: 600; + position: relative; + } + } + & button { + background: #64cd82; + // background: linear-gradient(180deg, rgb(163, 73, 186) 0%, rgb(108, 72, 191) 52%); + width: 100%; + margin: 3px auto 0px; + border: none; + padding: 5px 0px; + font-size: 18px; + border-radius: 10px; + color: white; + } + } +} + +.MuiGrid-container { + display: flex !important; + width: 100% !important; + margin: 0px !important; + background-color: rgb(255 255 255 / 40%); + justify-content: center !important; + align-items: center !important; + border-radius: 10px; +} diff --git a/src/views/ChatRoom/Body/Message/MultiSelect/index.js b/src/views/ChatRoom/Body/Message/MultiSelect/index.js new file mode 100644 index 0000000..aacd485 --- /dev/null +++ b/src/views/ChatRoom/Body/Message/MultiSelect/index.js @@ -0,0 +1,63 @@ +import React, { Component } from 'react'; +import ArrowRightAltIcon from '@material-ui/icons/ArrowRightAlt'; +import Avatar from '@material-ui/core/Avatar'; +import { toast } from 'react-toastify'; + +export default class RadioMessage extends Component { + constructor(props) { + super(props); + this.state = { + value: this.props.message.value, + }; + } + + handleRadioChange = (newValue) => { + if (this.props.isActive) + this.setState({ + value: newValue, + }); + }; + + render() { + const { message, user, isSelf, isActive } = this.props; + return ( +
+
+ {message.options.map((row) => ( + + ))} +
+ {isActive && ( + + )} +
+ ); + } +} + +const Row = (props) => { + return ( +
+ {props.list.map((item) => ( + + ))} +
+ ); +}; + +const Item = (props) => { + return ( +
props.handleRadioChange(props.item.name)} + style={{ + backgroundColor: props.item.name === props.value ? '#6f42c1' : 'rgb(181 224 252 / 47%)', + color: props.item.name === props.value ? 'white' : 'black', + }} + > + {props.item.name} +
+ ); +}; diff --git a/src/views/ChatRoom/Body/Message/Normal/index.js b/src/views/ChatRoom/Body/Message/Normal/index.js new file mode 100644 index 0000000..e9be640 --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Normal/index.js @@ -0,0 +1,245 @@ +import React, { Component } from 'react'; +import ReactAudioPlayer from 'react-audio-player'; +import ReactPlayer from 'react-player'; +import CloudDownloadIcon from '@material-ui/icons/CloudDownload'; +import Avatar from '@material-ui/core/Avatar'; +import moment from 'jalali-moment'; +import DoneIcon from '@material-ui/icons/Done'; +import DoneAllIcon from '@material-ui/icons/DoneAll'; +import EditIcon from '@material-ui/icons/Edit'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import './index.scss'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import ReplyIcon from '@material-ui/icons/Reply'; +import ForwardIcon from '@material-ui/icons/Forward'; +import DeleteIcon from '@material-ui/icons/Delete'; +import FavoriteIcon from '@material-ui/icons/Favorite'; +import TextsmsIcon from '@material-ui/icons/Textsms'; +import ScheduleIcon from '@material-ui/icons/Schedule'; +const file = (fileId) => `${window.baseURL}/file/download/${fileId}`; +const menu = [ + { text: 'پسند', icon: FavoriteIcon, onClick: () => {} }, + { text: 'تبادل نظر', icon: TextsmsIcon, onClick: () => {} }, + { text: 'پاسخ', icon: ReplyIcon, onClick: () => {} }, + { text: 'باز ارسال', icon: ForwardIcon, onClick: () => {} }, +]; +const selfMenu = [ + { text: 'باز ارسال', icon: ForwardIcon, onClick: () => {} }, + { text: 'ویرایش', icon: EditIcon, onClick: () => {} }, + { text: 'حذف', icon: DeleteIcon, onClick: () => {} }, +]; +export default class NormalMessage extends Component { + state = { messageMenu: null }; + handleClose = () => { + this.setState({ + messageMenu: null, + }); + }; + handleClick = (event) => { + this.setState({ + messageMenu: event.currentTarget, + }); + }; + render() { + const { message, user, document, isSelf, prevUserId, children,addcounter } = this.props; + const { messageMenu } = this.state; + const hasavatar = prevUserId != message.userId; + + return ( +
+ {hasavatar && false && ( + + {user.name + .split(' ') + .map((e) => e.slice(0, 1)) + .join(' ')} + + )} + +
+
+
+
addcounter('nothing')}> + +
+ this.handleClose()} + className="rtl fontLight" + > + {(isSelf ? selfMenu : menu).map((e) => ( + { + this.handleClose(); + e.onClick(); + addcounter('nothing'); + }} + > + {e.text} + + + ))} + + {hasavatar &&
{user.name}
} +
+ + {message.type == 'text' ? ( +

{message.text}

+ ) : null} + {message.type == 'voice' ? ( + + ) : null} + {message.type == 'image' ? ( +
+ + + +

{message.text}

+
+ ) : null} + {message.type == 'video' ? ( +
+ + + +

{message.text}

+
+ ) : null} + {message.type == 'audio' ? ( +
+ +

{message.text}

+
+ ) : null} + {message.type == 'document' ? ( + + +
{message.text}
+
+ ) : null} + +
+ + {message.updatedAt != message.createdAt ? ( + <> + ویرایش در + + {moment(message.updatedAt).format(' hh:mm jMM/jDD')} + + ) : ( + moment(message.createdAt).format('hh:mm') + )} + + + {isSelf && message.status == 0 && } + {isSelf && message.status == 2 && } + {isSelf && message.status == 1 && ( + + )} + +
+
+ +
addcounter('nothing')}> + {!isSelf ? ( + <> + {' '} +
+ {10} + + +
+
+ {10} + +
+ + ) : ( + <> +
+ + +5 +
+ خلاقیت +
+
+ + )} +
+
+
+ {children} +
+
+ ); + } +} diff --git a/src/views/ChatRoom/Body/Message/Normal/index.scss b/src/views/ChatRoom/Body/Message/Normal/index.scss new file mode 100644 index 0000000..86fe695 --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Normal/index.scss @@ -0,0 +1,155 @@ +@media screen and (max-width: 4500px) { + .normal-message__box--date.self { + padding-right: 20px 0 0 !important ; + } + .keys { + max-width: 465px; + min-width: 250px; + padding: 2px 0; + } + .icons { + //position: absolute; + width: 90px; + padding: 0 5px; + display: inline-block !important; + // top: 55px; + // right: -35px; + direction:rtl; + text-align: left; + & .MuiSvgIcon-root { + font-size: 18px !important; + opacity: 0.8; + } + & span { + font-size: 11px !important; + opacity: 0.8; + } + } + .icons.self { + direction: ltr; + text-align: right; + font-size: small; + // left: -35px; + // right: none; + } + .MuiMenuItem-root { + min-height: 10px !important; + font-size: 12px !important; + color: grey; + padding: 5px 10px !important; + & .MuiSvgIcon-root { + font-size: 16px !important; + padding: 2px; + color: grey; + } + } + + .normal-message { + // width: 100%; + position: relative; + margin-bottom: 2px; + img { + width: 65px; + height: 65px; + object-fit: cover; + border-radius: 15px; + border: 3px solid rgba(201, 201, 201, 0.857); + position: relative; + top: 20px; + z-index: 50; + } + &__box { + line-height: 1.3em; + max-width: 500px; + min-width: 250px; + display: flex; + justify-content: flex-end; + // width: 100%; + padding: 20px 10px 15px; + background-color: white; + border-radius: 5px; + position: relative; + p { + width: 100%; + text-align: right; + direction: rtl; + font-size: 15px; + margin: 0 0 5px !important; + // font-family: IRANSansNumeral; + font-weight: 200; + color: #afafaf; + } + &--detail { + position: absolute; + width: 100%; + top: 10px; + justify-content: space-between; + font-size: 12px; + span { + font-family: IRANSansNumeralLight; + } + } + + &--date { + position: absolute; + width: 100%; + padding: 0px 0px 0; + bottom: 0px; + justify-content: flex-end; + font-size: 11px; + span { + font-family: IRANSansNumeralLight; + } + } + &--date.self { + justify-content: flex-end; + padding: 0; + } + &--caption { + width: 100%; + margin-bottom: 20px; + text-align: right; + & img { + height: 100%; + max-height: 250px; + width: 100%; + max-width: 250px; + border-radius: 0px; + border: 0px; + position: static; + } + & video { + height: 180px !important; + width: 100%; + } + & p { + margin-top: 20px; + } + // & audio{ + // min-width: 100% !important; + // } + } + &--file { + background-color: rgb(143, 228, 143) !important; + } + &--document { + width: 100%; + max-width: 300px; + align-items: center; + justify-content: space-between; + height: 50px; + padding: 10px; + border-radius: 10px; + div { + font-size: 14px; + } + } + } + } +} + +.react-audio-player { + width: 100% !important; + color: red; + fill: red; +} diff --git a/src/views/ChatRoom/Body/Message/Select/index.js b/src/views/ChatRoom/Body/Message/Select/index.js new file mode 100644 index 0000000..067731b --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Select/index.js @@ -0,0 +1,56 @@ +import React, { Component } from 'react'; +import ArrowRightAltIcon from '@material-ui/icons/ArrowRightAlt'; + +export default class RadioMessage extends Component { + state = { + value: this.props.message.value, + }; + handleRadioChange = (value) => { + if (this.props.isActive) this.setState({ value }); + }; + + render() { + const { message, user, isSelf, isActive, action } = this.props; + const { value } = this.state; + return ( +
+
+ {message.options.map((row) => ( + + ))} +
+ {isActive && ( + + )} +
+ ); + } +} + +const Row = (props) => { + return ( +
+ {props.list.map((item) => ( + + ))} +
+ ); +}; + +const Item = (props) => { + return ( +
props.handleRadioChange(props.item.name)} + style={{ + backgroundColor: + props.item.name === props.value ? 'rgb(100, 205, 130)' : 'rgba(255, 255, 255, 0.4)', + color: props.item.name === props.value ? 'white' : 'black', + }} + > + {props.item.name} +
+ ); +}; diff --git a/src/views/ChatRoom/Body/Message/Select/index.scss b/src/views/ChatRoom/Body/Message/Select/index.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/views/ChatRoom/Body/Message/Slider/index.js b/src/views/ChatRoom/Body/Message/Slider/index.js new file mode 100644 index 0000000..5458515 --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Slider/index.js @@ -0,0 +1,62 @@ +import React, { Component } from 'react'; +import Grid from '@material-ui/core/Grid'; +import Slider from '@material-ui/core/Slider'; +import Input from '@material-ui/core/Input'; +import ArrowRightAltIcon from '@material-ui/icons/ArrowRightAlt'; +import Avatar from '@material-ui/core/Avatar'; +import { toast } from 'react-toastify'; +export default class SliderMessage extends Component { + state = { + value: this.props.message.value || 0, + }; + onChange = (value) => { + if (!this.props.isActive || !value) return; + const [min, max, step] = this.props.message.options[0] || [0, 100, 1]; + this.setState({ value: Math.min(max, Math.max(min, parseFloat(value))) }); + }; + + render() { + const { message, isActive, action } = this.props; + const [min, max, step] = message.options[0] || [0, 100, 1]; + const { value } = this.state; + return ( +
+ + + this.onChange(value)} + aria-labelledby="input-slider" + min={min} + max={max} + step={step} + /> + + + this.onChange(e.target.value)} + onBlur={() => this.onChange(value)} + inputProps={{ + step: step, + min: min, + max: max, + type: 'number', + 'aria-labelledby': 'input-slider', + }} + /> + + + {isActive && ( + + )} +
+ ); + } +} diff --git a/src/views/ChatRoom/Body/Message/Slider/index.scss b/src/views/ChatRoom/Body/Message/Slider/index.scss new file mode 100644 index 0000000..a5ed48b --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Slider/index.scss @@ -0,0 +1,13 @@ +.sliderBox { + margin-top: 2px; + .slider { + margin-top: 10px; + } + input { + width: 42px; + border-bottom: 2px solid #000099; + padding-left: 10px; + margin-bottom: 18px; + color: #000099; + } +} diff --git a/src/views/ChatRoom/Body/Message/Sort/index.js b/src/views/ChatRoom/Body/Message/Sort/index.js new file mode 100644 index 0000000..99df8c5 --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Sort/index.js @@ -0,0 +1,53 @@ +import React, { Component, useEffect } from 'react'; +import { SortableContainer, SortableElement } from 'react-sortable-hoc'; +import arrayMove from 'array-move'; +import ArrowRightAltIcon from '@material-ui/icons/ArrowRightAlt'; +import Avatar from '@material-ui/core/Avatar'; +import { toast } from 'react-toastify'; + +export default class DraggableMessage extends Component { + constructor(props) { + super(props); + this.state = this.props.message; + } + onSortEnd = ({ oldIndex, newIndex }) => { + if (this.props.isActive) + this.setState(({ options }) => ({ + options: arrayMove(options, oldIndex, newIndex), + })); + }; + + render() { + const { message, user, isSelf, isActive, action } = this.props; + + return ( +
+ + {isActive && ( + + )} +
+ ); + } +} + +const SortableList = SortableContainer(({ items, disabled }) => { + return ( +
+ {items.map((row, index) => ( + + ))} +
+ ); +}); + +const SortableItem = SortableElement(({ value }) => ( +
+ {value.map((item) => ( +
{item.name}
+ ))} +
+)); diff --git a/src/views/ChatRoom/Body/Message/Sort/index.scss b/src/views/ChatRoom/Body/Message/Sort/index.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/views/ChatRoom/Body/Message/Tap/index.js b/src/views/ChatRoom/Body/Message/Tap/index.js new file mode 100644 index 0000000..6d039a7 --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Tap/index.js @@ -0,0 +1,27 @@ +import React, { Component } from 'react'; +import arrayMove from 'array-move'; +import './index.scss'; + +export default class DraggableMessage extends Component { + constructor(props) { + super(props); + this.state = this.props.message; + } + onSortEnd = ({ oldIndex, newIndex }) => { + if (this.props.isActive) + this.setState(({ options }) => ({ + options: arrayMove(options, oldIndex, newIndex), + })); + }; + + render() { + const { message, user, isSelf, isActive } = this.props; + if (!isActive) return null; + return
; + } + a = ( + + ); +} diff --git a/src/views/ChatRoom/Body/Message/Tap/index.scss b/src/views/ChatRoom/Body/Message/Tap/index.scss new file mode 100644 index 0000000..5769f8e --- /dev/null +++ b/src/views/ChatRoom/Body/Message/Tap/index.scss @@ -0,0 +1,15 @@ +.tapbox { + z-index: 500; + position: absolute; + left: -10px; + background-color: #fff; + width: 100vw; + height: calc(100% - 90px); + opacity: 0.1; + cursor: pointer; +} +.ptapbox { + top: 0; + left: 0; + position: absolute; +} diff --git a/src/views/ChatRoom/Body/Message/index.js b/src/views/ChatRoom/Body/Message/index.js new file mode 100644 index 0000000..4b13c79 --- /dev/null +++ b/src/views/ChatRoom/Body/Message/index.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import Normal from './Normal'; +import Inline from './Inline'; +import Sort from './Sort'; +import Slider from './Slider'; +import Select from './Select'; +import MultiSelect from './MultiSelect'; +import Tap from './Tap'; +import ArrowRightAltIcon from '@material-ui/icons/ArrowRightAlt'; + +import './index.scss'; +const types = { + sort: Sort, + inline: Inline, + slider: Slider, + select: Select, + multiselect: MultiSelect, + tap: Tap, +}; +export default class Message extends Component { + render() { + const { message, user, isSelf, file, document, prevUserId, isActive, addcounter } = this.props; + const Action = types[message.action]; + return ( +
+ + {Action ? ( + + ) : null} + {/* {isActive && ( + + )} */} + +
+ ); + } +} diff --git a/src/views/ChatRoom/Body/Message/index.scss b/src/views/ChatRoom/Body/Message/index.scss new file mode 100644 index 0000000..4fc619f --- /dev/null +++ b/src/views/ChatRoom/Body/Message/index.scss @@ -0,0 +1,5 @@ +@media screen and (max-width: 4500px) { + .Message { + margin-top: 2px; + } +} diff --git a/src/views/ChatRoom/Body/index.js b/src/views/ChatRoom/Body/index.js new file mode 100644 index 0000000..2e38970 --- /dev/null +++ b/src/views/ChatRoom/Body/index.js @@ -0,0 +1,493 @@ +import React, { Component } from 'react'; +import Message from './Message'; +import ChatInputs from './Input'; +import Keyboard from './Keyboard'; +import { RecordState } from 'audio-react-recorder'; +import ReactPlayer from 'react-player'; +import ReactAudioPlayer from 'react-audio-player'; +import { ToastContainer } from 'react-toastify'; +import { list, send, sendKeys } from '../../../Redux/actions/message'; +import { info, bio } from '../../../Redux/actions/chat'; +import { connect } from 'react-redux'; +import Emoji from './Emoji/index'; + +import './index.scss'; +class List extends Component { + constructor(props) { + super(props); + this.state = { + emoji: false, + counter: 1, + id: window.location.href.split('/').pop() * 1, + data: {}, + message: '', + voice: null, + file: null, + image: null, + video: null, + music: null, + document: null, + }; + } + addcounter(type) { + if (type === 'parent') { + if (localStorage.getItem('tapType') === 'nothing') { + localStorage.removeItem('tapType'); + return; + } else { + this.setState({ counter: this.state.counter + 1 }); + } + } else { + localStorage.setItem('tapType', 'nothing'); + return; + } + } + + start = () => { + this.setState({ + voice: RecordState.START, + }); + }; + + stop = () => { + this.setState({ + voice: RecordState.STOP, + }); + }; + + //audioData contains blob and blobUrl + onStop = (audioData) => { + this.setState({ + voice: audioData, + }); + this.sendVoice(audioData); + }; + + cancel = () => { + this.setState({ + voice: null, + }); + }; + + typeText = (type,val) => { + if(type === 'text'){ + this.setState({ + message: val, + }); + } + else{ + this.setState(prevState => ({ + message: prevState.message + val , + })); + } + }; + + + + addFile = async (e) => { + if ( + e.target.files[0].type === 'image/apng' || + e.target.files[0].type === 'image/avif' || + e.target.files[0].type === 'image/gif' || + e.target.files[0].type === 'image/jpeg' || + e.target.files[0].type === 'image/png' || + e.target.files[0].type === 'image/svg+xml' || + e.target.files[0].type === 'image/webp' + ) { + this.setState({ + image: URL.createObjectURL(e.target.files[0]), + file: 'fill', + video: null, + document: null, + music: null, + }); + } else if ( + e.target.files[0].type === 'video/3gpp' || + e.target.files[0].type === 'video/mpeg' || + e.target.files[0].type === 'video/mp4' || + e.target.files[0].type === 'video/ogg' || + e.target.files[0].type === 'video/quicktime' || + e.target.files[0].type === 'video/webm' + ) { + this.setState({ + image: null, + file: 'fill', + video: URL.createObjectURL(e.target.files[0]), + document: null, + music: null, + }); + } else if ( + e.target.files[0].type === 'audio/3gpp' || + e.target.files[0].type === 'audio/aac' || + e.target.files[0].type === 'audio/flac' || + e.target.files[0].type === 'audio/mpeg' || + e.target.files[0].type === 'audio/mp3' || + e.target.files[0].type === 'audio/mp4' || + e.target.files[0].type === 'audio/ogg' || + e.target.files[0].type === 'audio/wav' || + e.target.files[0].type === 'audio/webm' + ) { + this.setState({ + image: null, + file: 'fill', + video: null, + document: null, + music: URL.createObjectURL(e.target.files[0]), + }); + } else { + this.setState({ + image: null, + file: 'fill', + video: null, + music: null, + document: { data: URL.createObjectURL(e.target.files[0]), name: e.target.files[0].name }, + }); + } + }; + + sendMessage = () => { + if (this.state.file !== null) { + this.sendFile(); + return; + } else { + let now = new Date(); + this.setState((prevState) => ({ + data: { + ...prevState.data, + messages: [ + ...prevState.data.messages, + { + id: 'm12', + type: 'normal', + text: prevState.message, + voice: null, + image: null, + file: null, + video: null, + music: null, + document: null, + createdAt: now.toString(), + user: { + id: 'u1', + name: 'علی', + }, + }, + ], + }, + })); + this.setState({ + message: '', + }); + } + }; + + sendVoice = (audioData) => { + let now = new Date(); + this.setState((prevState) => ({ + data: { + ...prevState.data, + messages: [ + ...prevState.data.messages, + { + id: 'm9', + text: null, + type: 'normal', + voice: audioData, + createdAt: now.toString(), + image: null, + video: null, + document: null, + music: null, + user: { + id: 'u1', + name: 'علی', + }, + }, + ], + }, + })); + this.setState({ + voice: null, + }); + }; + + sendFile = () => { + const { image, video, music, document } = this.state; + let now = new Date(); + if (image !== null) { + this.setState((prevState) => ({ + data: { + ...prevState.data, + messages: [ + ...prevState.data.messages, + { + id: 'm8', + type: 'normal', + text: prevState.message, + voice: null, + createdAt: now.toString(), + image: image, + video: null, + document: null, + music: null, + user: { + id: 'u1', + name: 'علی', + }, + }, + ], + }, + })); + this.setState({ + file: null, + image: null, + message: '', + }); + } else if (video !== null) { + this.setState((prevState) => ({ + data: { + ...prevState.data, + messages: [ + ...prevState.data.messages, + { + id: 'm8', + type: 'normal', + text: prevState.message, + voice: null, + createdAt: now.toString(), + image: null, + video: video, + document: null, + music: null, + user: { + id: 'u1', + name: 'علی', + }, + }, + ], + }, + })); + this.setState({ + file: null, + video: null, + message: '', + }); + } else if (music !== null) { + this.setState((prevState) => ({ + data: { + ...prevState.data, + messages: [ + ...prevState.data.messages, + { + id: 'm8', + text: prevState.message, + type: 'normal', + voice: null, + createdAt: now.toString(), + image: null, + video: null, + document: null, + music: music, + user: { + id: 'u1', + name: 'علی', + }, + }, + ], + }, + })); + this.setState({ + file: null, + music: null, + message: '', + }); + } else if (document !== null) { + this.setState((prevState) => ({ + data: { + ...prevState.data, + messages: [ + ...prevState.data.messages, + { + id: 'm8', + text: prevState.message, + type: 'normal', + voice: null, + + createdAt: now.toString(), + image: null, + video: null, + document: document, + music: null, + user: { + id: 'u1', + name: 'علی', + }, + }, + ], + }, + })); + this.setState({ + file: null, + document: null, + message: '', + }); + } + }; + + cancelSendFile = () => { + this.setState({ + file: null, + document: null, + message: '', + image: null, + video: null, + voice: null, + music: null, + }); + }; + + componentDidMount() { + this.props.list({ chatId: this.state.id }); + this.props.info({ id: this.state.id }); + // this.componentDidUpdate(); + } + + componentDidUpdate() { + let chatroom = document.getElementById('chatroom'); + chatroom.scrollTop = chatroom.scrollHeight + 40; + } + randomColor() { + let hex = Math.floor(Math.random() * 0x999999); + let color = '#' + hex.toString(16); + + return color; + } + render() { + const { setPage, chatInfo, messageList: msaster, userId } = this.props; //last + const messageList = msaster.slice(0, this.state.counter); + const last = messageList.slice(-1)[0] || {}; + const userIdx = this.userIdx || {}; + if (Object.keys(userIdx).length == 0) { + chatInfo.users.map((e) => (userIdx[e.id] = { ...e, color: this.randomColor() })); + this.userIdx = userIdx; + } + + return ( +
this.addcounter('parent')} + > +
+ {chatInfo.users && + messageList.map((message, i) => + this.state.image === null && + this.state.music === null && + this.state.video === null ? ( + this.addcounter()} + isActive={message.id == last.id} + prevUserId={i > 0 ? messageList[i - 1].userId : 0} + isSelf={new Set([101, 103, 106, 108, 110]).has(message.userId * 1)} + key={message.id} + file={this.state.image} + document={this.state.document} + message={message} + user={userIdx[message.userId] || { id: 0, fileId: 0, name: 'null' }} + /> + ) : null + )} + {this.state.image !== null ? ( +
+ +
+ ) : null} + {this.state.video !== null ? ( +
+ +
+ ) : null} + {this.state.music !== null ? ( +
+ +
+ ) : null} + {this.state.document !== null ? ( +
+
+

{`ارسال (${this.state.document.name}) به ${this.props.info.title}؟`}

+
+ + +
+
+
+ ) : null} +
+
this.addcounter('nothing')} + > + {last.action == 'text' ? ( + this.addcounter()} + onStop={this.onStop} + stop={this.stop} + start={this.start} + cancel={this.cancel} + message={this.state.message} + recordState={this.state.voice} + typeText={this.typeText} + sendMessage={this.sendMessage} + addFile={this.addFile} + file={this.state.file} + parent={this} + /> + ) : null} + {last.action === 'keyboard' ? ( + this.addcounter()} /> + ) : null} + {this.state.emoji ? ( +
+ +
+ ) : null} +
+ +
+ ); + } +} +export default connect( + (state) => { + return { + chatInfo: state.chat.info, + userId: state.user.isLogin.id, + messageList: state.message.list || [], + last: state.message.list.slice(-1)[0] || {}, + }; + }, + { list, send, sendKeys, info, bio } +)(List); diff --git a/src/views/ChatRoom/Body/index.scss b/src/views/ChatRoom/Body/index.scss new file mode 100644 index 0000000..88161cc --- /dev/null +++ b/src/views/ChatRoom/Body/index.scss @@ -0,0 +1,139 @@ +@media screen and (max-width: 4500px) { + .chat-body .MuiAvatar-circle { + box-shadow: 0px 0px 0px 2px #fff; + border: 3px solid #ffffff66; + width: 60px; + height: 60px; + // top: 20px; + z-index: 30; + // font-family: 'IRANSansNumeralBold'; + // & div { + // font-family: 'IRANSansNumeralBold'; + // } + } + + .chat-body { + background-color: rgb(210, 196, 214); + // justify-content: flex-start; + flex: 1; + height: 100%; + overflow-x: hidden; + font-family: numeralLight; + &__content { + background: url(../../../assets/images/whatsapp.png); + background-size: cover; + padding: 0px 10px 0px; + margin-bottom: 55px; + height: 100%; + min-height: 200px; + position: relative; + &--preview { + position: absolute; + bottom: 0px; + left: 0px; + width: 100%; + height: 100%; + justify-content: center; + align-items: center; + background-color: rgb(210, 196, 214); + z-index: 1000; + & img { + width: 100%; + max-height: 400px; + object-fit: scale-down; + } + } + &--confirm { + width: 100vw; + height: 100vh; + position: fixed; + top: 0px; + left: 0px; + justify-content: center; + align-items: center; + z-index: 300; + &___box { + position: relative; + bottom: 50px; + width: 100%; + max-width: 250px; + padding: 10px; + // background: rgb(139, 73, 186); + // background: linear-gradient(180deg, rgb(144, 73, 186) 0%, #64cd82 95%); + text-align: right; + direction: rtl; + color: white; + border-radius: 5px; + box-shadow: 0px 0px 20px rgba(0, 0, 0, 1); + font-size: 16px; + &---options { + justify-self: flex-end; + align-self: flex-start; + width: 110px; + height: 40px; + justify-content: space-between; + & button { + background-color: inherit; + border: none; + font-size: 14px; + &:focus { + outline: none; + } + } + } + } + } + } + &__footer { + width: 100%; + z-index: 1000; + background-color: rgb(210, 196, 214); + background: url(../../../assets/images/whatsapp.png); + // box-shadow: 0px 0px 5px 2px #9d9595ad; + background-size: cover; + overflow-y: scroll; + overflow-x: hidden; + position: fixed; + bottom: 0px; + left: 0; + padding-bottom: 5px; + &--emoji{ + margin-top: 5px; + width: 100%; + background-color: red; + height: 250px; + // justify-self: flex-end; + // position: absolute; + // bottom: 0px; + // left: 0px; + .MuiBox-root{ + max-height : 202px; + min-height: 160px; + overflow-y: scroll; + padding: 10px 10px 0px !important; + } + + .MuiTab-root{ + padding: 2px !important; + min-height: 22px !important; + min-width: 34px !important; + font-size: 24px !important; + margin-right: 10px !important; + } + + .MuiTabs-root{ + min-height: 28px !important; + } + + .PrivateTabIndicator-colorPrimary-7{ + width : 34px !important; + background-color: green !important; + } + + .MuiPaper-elevation4{ + box-shadow: none !important; + } + } + } + } +} diff --git a/src/views/ChatRoom/Header/Avatar/index.js b/src/views/ChatRoom/Header/Avatar/index.js new file mode 100644 index 0000000..1a9537b --- /dev/null +++ b/src/views/ChatRoom/Header/Avatar/index.js @@ -0,0 +1,54 @@ +import React from 'react'; +import Badge from '@material-ui/core/Badge'; +import Avatar from '@material-ui/core/Avatar'; +import { makeStyles, withStyles } from '@material-ui/core/styles'; + +const StyledBadge = withStyles((theme) => ({ + badge: { + backgroundColor: '#44b700', + color: '#44b700', + boxShadow: `0 0 0 2px ${theme.palette.background.paper}`, + '&::after': { + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '100%', + borderRadius: '50%', + animation: '$ripple 1.2s infinite ease-in-out', + border: '1px solid currentColor', + content: '""', + }, + }, + '@keyframes ripple': { + '0%': { + transform: 'scale(.8)', + opacity: 1, + }, + '100%': { + transform: 'scale(2.4)', + opacity: 0, + }, + }, +}))(Badge); + +const useStyles = makeStyles((theme) => ({ + root: { + display: 'flex', + '& > *': { + margin: 0, + }, + }, +})); + +export default function StatusAvatar(props) { + const classes = useStyles(); + + return ( +
+ + + +
+ ); +} diff --git a/src/views/ChatRoom/Header/index.js b/src/views/ChatRoom/Header/index.js new file mode 100644 index 0000000..ccae44c --- /dev/null +++ b/src/views/ChatRoom/Header/index.js @@ -0,0 +1,102 @@ +import React, { Component } from "react"; +import MoreVertIcon from "@material-ui/icons/MoreVert"; +import ArrowForwardIcon from "@material-ui/icons/ArrowForward"; +import Menu from "@material-ui/core/Menu"; +import MenuItem from "@material-ui/core/MenuItem"; +import VideocamIcon from "@material-ui/icons/Videocam"; +import StatusAvatar from "./Avatar/index.js"; +import CallIcon from "@material-ui/icons/Call"; +import { connect } from "react-redux"; + +import "./index.scss"; +class Header extends Component { + constructor(props) { + super(props); + this.state = { + anchorEl: null, + }; + } + + handleClick = (event) => { + this.setState({ + anchorEl: event.currentTarget, + }); + }; + + handleClose = () => { + this.setState({ + anchorEl: null, + }); + }; + + back = () => { + window.history.back(); + }; + render() { + const { page, setPage, chatInfo } = this.props; + const { anchorEl } = this.state; + return ( +
+
+ + page === "chatroom" ? this.back() : setPage("chatroom") + } + style={{ color: "white", width: 29, height: 29, marginTop: 4 }} + /> +
setPage("info")} + className="chat-header__info d-flex mr-1" + > + +
+

{chatInfo ? chatInfo.title : null}

+ {chatInfo ? chatInfo.subTitle : null} +
+
+
+
+
+ + {/* + */} +
+ this.handleClose()} + className="fontLight rtl" + > + this.handleClose()} + > + پروفایل + + this.handleClose()} + > + حساب کاربری + + this.handleClose()} + > + خروج + + +
+
+ ); + } +} + +export default connect((state) => ({ chatInfo: state.chat.info }), {})(Header); diff --git a/src/views/ChatRoom/Header/index.scss b/src/views/ChatRoom/Header/index.scss new file mode 100644 index 0000000..a300095 --- /dev/null +++ b/src/views/ChatRoom/Header/index.scss @@ -0,0 +1,31 @@ +@media screen and (max-width: 4500px) { + .chat-header { + height: 60px; + width: 100%; + justify-content: space-between; + align-items: flex-end; + padding: 5px 5px 5px; + position: relative; + background-color: #128c7e; + &__info { + text-align: right; + + & h1 { + font-size: 16px; + color: white; + margin: 0px; + margin-bottom: 5px; + font-weight: 600; + } + & span { + color: white; + font-size: 11px; + } + } + } +} + +.MuiMenu-paper { + background: #00cba2; //rgb(63, 71, 67) !important; + color: black !important; +} diff --git a/src/views/ChatRoom/Info/FileTabs/index.js b/src/views/ChatRoom/Info/FileTabs/index.js new file mode 100644 index 0000000..16d75aa --- /dev/null +++ b/src/views/ChatRoom/Info/FileTabs/index.js @@ -0,0 +1,90 @@ +import React, { Component } from 'react'; + +import './index.scss'; +export default class FileTabs extends Component { + constructor(props) { + super(props); + this.state = { + type: 'media', + }; + } + + setType = (val) => { + this.setState({ + type: val, + }); + }; + render() { + return ( +
+
+
  • this.setType('media')} + > + Media +
  • +
  • this.setType('file')} + > + Files +
  • +
  • this.setType('link')} + > + Links +
  • +
  • this.setType('music')} + > + Music +
  • +
  • this.setType('voice')} + > + Voice +
  • +
    + {this.state.type === 'media' ? ( +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + ) : null} +
    + ); + } +} diff --git a/src/views/ChatRoom/Info/FileTabs/index.scss b/src/views/ChatRoom/Info/FileTabs/index.scss new file mode 100644 index 0000000..73224c7 --- /dev/null +++ b/src/views/ChatRoom/Info/FileTabs/index.scss @@ -0,0 +1,36 @@ +@media screen and (max-width: 4500px) { + .file-tabs { + width: 100%; + height: 100%; + position: relative; + direction: rtl; + position: sticky; + top: 0-5px; + &__types { + width: 100%; + justify-content: space-between; + align-items: center; + padding: 3px calc((100vw - 310px) / 2) 3px calc((100vw - 310px) / 2); + direction: ltr; + border-top: 8px solid rgb(13, 84, 150); + margin-top: 20px; + background-color: #128c7e; + & li { + font-size: 16px; + list-style: none; + font-weight: 600; + } + } + &__media { + width: 100%; + overflow-y: scroll; + padding-bottom: 52px; + &--item { + width: calc(100% / 3); + height: calc(100vw / 3); + border: 2px solid rgb(200, 183, 232); + background-color: red; + } + } + } +} diff --git a/src/views/ChatRoom/Info/Gallery/index.js b/src/views/ChatRoom/Info/Gallery/index.js new file mode 100644 index 0000000..d233951 --- /dev/null +++ b/src/views/ChatRoom/Info/Gallery/index.js @@ -0,0 +1,29 @@ +import React from 'react'; +import Carousel from 'react-elastic-carousel'; +import CloseIcon from '@material-ui/icons/Close'; + +import './index.scss'; + +export default function Gallery(props) { + // const [state,setState] = useState({ + + // }); + return ( +
    +
    props.onClick(null)}> + +
    + + {props.data.map((item, i) => ( + + ))} + +
    + ); +} + +const Image = (props) => { + return ; +}; diff --git a/src/views/ChatRoom/Info/Gallery/index.scss b/src/views/ChatRoom/Info/Gallery/index.scss new file mode 100644 index 0000000..a1149d6 --- /dev/null +++ b/src/views/ChatRoom/Info/Gallery/index.scss @@ -0,0 +1,64 @@ +@media screen and (max-width: 4500px) { + .gallery-modal { + width: 100%; + height: 100%; + background-color: #383737f1; + position: absolute; + left: 0; + top: 0; + z-index: 700; + .rec-carousel-wrapper { + position: relative; + bottom: 70px; + } + .rec-arrow { + position: absolute; + background: none; + box-shadow: none; + &:hover:enabled { + background: none; + box-shadow: none; + color: black; + } + } + .rec-arrow-right { + top: calc(50% - 20px); + left: 0px; + z-index: 800; + } + .rec-arrow-left { + top: calc(50% - 20px); + right: 0px; + z-index: 800; + } + .rec-pagination { + // display:none; + z-index: 600; + position: absolute; + top: auto; + bottom: -30px !important; + // left: calc(50% - 20px) !important; + & button { + background-color: grey; + margin: 2px !important; + box-shadow: 0px 0px 0px rgba(255, 255, 255, 1); + } + } + } + .rec-dot_active { + background-color: white !important; + color: white !important; + // box-shadow: 0 0 1px rgba(255, 255, 255, 1) !important; + } + + .Carousel-button-10 { + background-color: white !important; + } + .rec-pagination { + img { + // width: 100vw; + // max-height: 200px; + // height: 100vh; + } + } +} diff --git a/src/views/ChatRoom/Info/index.js b/src/views/ChatRoom/Info/index.js new file mode 100644 index 0000000..ac07b94 --- /dev/null +++ b/src/views/ChatRoom/Info/index.js @@ -0,0 +1,60 @@ +import React, { Component } from 'react'; +import ChatIcon from '@material-ui/icons/Chat'; +import FileTabs from './FileTabs'; +import Gallery from './Gallery'; +import user2 from '../../../assets/images/user2.png'; + +import './index.scss'; +export default class ChatInfo extends Component { + constructor(props) { + super(props); + this.state = { + gallery: null, + user: { + profileImages: [ + { id: 0, src: user2 }, + { id: 1, src: user2 }, + { id: 2, src: user2 }, + { id: 3, src: user2 }, + ], + name: 'محسن محمدی', + lastAct: '2 دقیقه پیش آنلاین بوده', + bio: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد ', + }, + }; + } + + setGallery = (val) => { + this.setState({ + gallery: val, + }); + }; + + render() { + const { user, gallery } = this.state; + return ( +
    +
    + this.setGallery('profile')} alt="" /> +
    + +
    +
    +
    +
    +

    {user.name}

    +

    {user.lastAct}

    +
    +
    +

    {user.bio}

    +
    + +
    + {gallery === 'profile' ? ( + + ) : null} + {gallery === 'media' ? : null} +
    + ); + } +} diff --git a/src/views/ChatRoom/Info/index.scss b/src/views/ChatRoom/Info/index.scss new file mode 100644 index 0000000..0bb73d2 --- /dev/null +++ b/src/views/ChatRoom/Info/index.scss @@ -0,0 +1,82 @@ +@media screen and (max-width: 4500px) { + .chat-info { + width: 100%; + min-height: 100vh; + position: relative; + direction: rtl; + &__image { + width: 100%; + background-color: rgb(82, 82, 82); + justify-content: center; + align-items: center; + height: 290px; + position: relative; + & img { + width: 100%; + height: 100%; + object-fit: cover; + } + &--float { + position: absolute; + width: 65px; + height: 65px; + bottom: -30px; + right: 40px; + background : #128c7e; + padding: 5px; + border-radius: 100px; + z-index: 400; + justify-content: center; + align-items: center; + box-shadow: 0px 0px 50px rgba(36, 0, 72, 0.8); + } + } + &__box { + width: 100%; + z-index: 100; + height: calc(100% - 250px); + background-color: #0ab8a3; + direction: rtl; + padding: 20px 0px 0px 0px; + position: sticky; + // transform: translateY(-40px); + //top: -150px; + overflow-y: scroll; + overflow-x: hidden; + z-index: 200; + &::-webkit-scrollbar { + display: none; + } + &--status { + width: 100%; + align-items: flex-end; + padding: 0px 30px; + h1 { + font-size: 19px; + margin: 0; + letter-spacing: -1px; + font-family: IRANSansNumeralLight; + color: black; + font-weight: 100; + } + p { + font-size: 14px; + color: rgb(61, 61, 61); + margin: 0px; + } + } + &--description { + width: 100%; + justify-content: center; + align-items: center; + margin-top: 20px; + & p { + width: 100%; + max-width: 310px; + text-align: justify; + font-size: 12px; + } + } + } + } +} diff --git a/src/views/ChatRoom/index.js b/src/views/ChatRoom/index.js new file mode 100644 index 0000000..d702040 --- /dev/null +++ b/src/views/ChatRoom/index.js @@ -0,0 +1,38 @@ +import React, { Component } from 'react'; +import Header from './Header'; +import Body from './Body'; +import Info from './Info'; +import './index.scss'; +export default class ChatRoom extends Component { + constructor(props) { + super(props); + window.nav = this.nav.bind(this); + this.state = { + page: 'chatroom', + }; + } + nav = (url) => { + this.props.history.push(url); + }; + setPage = (val) => { + this.setState({ + page: val, + }); + }; + render() { + return ( +
    +
    + {this.state.page === 'chatroom' ? ( + <> + + + ) : ( + <> + + + )} +
    + ); + } +} diff --git a/src/views/ChatRoom/index.scss b/src/views/ChatRoom/index.scss new file mode 100644 index 0000000..e4bf0ac --- /dev/null +++ b/src/views/ChatRoom/index.scss @@ -0,0 +1,33 @@ +@media screen and (max-width: 4500px) { + .chat-room { + flex: 1; + background: rgb(139, 73, 186); + background: linear-gradient(180deg, rgb(144, 73, 186) 0%, #64cd82 22%); + height: 100vh; + overflow: hidden; + width: 100vw; + box-shadow: 0px 0px 40px rgba(145, 145, 145, 0.8); + &__status { + border-top-left-radius: 40px; + border-top-right-radius: 40px; + height: 150px; + background: rgb(106, 103, 250); + position: relative; + align-items: center; + justify-content: flex-start; + h1 { + font-size: 18px; + margin: 0; + letter-spacing: -1px; + font-family: IRANSansNumeralLight; + color: white; + font-weight: 100; + } + p { + font-size: 12px; + color: white; + margin: 0px; + } + } + } +} diff --git a/yarn.lock b/yarn.lock index dd51740..767e126 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1150,6 +1150,13 @@ core-js-pure "^3.14.0" regenerator-runtime "^0.13.4" +"@babel/runtime@7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c" + integrity sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA== + dependencies: + regenerator-runtime "^0.12.0" + "@babel/runtime@7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740" @@ -1164,6 +1171,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.2.0": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.14.5", "@babel/template@^7.3.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" @@ -1592,6 +1606,11 @@ schema-utils "^2.6.5" source-map "^0.7.3" +"@react-leaflet/core@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@react-leaflet/core/-/core-1.1.0.tgz#389b2b373f1da4caeb3c4cf6f33d84561c58d504" + integrity sha512-zFxMHgfjCi7khRVB7o7H8NoJl36NaezvfcaeEurVXx22lAGHFlTHiSuLOGA4tOiHj+Ep+Lo3uwUGJ3YM9BGkHg== + "@rollup/plugin-node-resolve@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz#80de384edfbd7bfc9101164910f86078151a3eca" @@ -2563,6 +2582,11 @@ array-includes@^3.1.1, array-includes@^3.1.2, array-includes@^3.1.3: get-intrinsic "^1.1.1" is-string "^1.0.5" +array-move@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/array-move/-/array-move-3.0.1.tgz#179645cc0987b65953a4fc06b6df9045e4ba9618" + integrity sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg== + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -2679,6 +2703,13 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +audio-react-recorder@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/audio-react-recorder/-/audio-react-recorder-1.0.4.tgz#db81f10286c2252af8d42162e1f49dbd1de0dffd" + integrity sha512-an7eX0yOGDbZOSu2LvnfWIsI41pkx9nXgtBVbI+9ByS91WKqoVGVb5pbmqHax5sZty2DjOIG/neuxcghIU/ucg== + dependencies: + prop-types "^15.7.2" + autoprefixer@^9.6.1: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" @@ -4217,7 +4248,7 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@^4.2.2: +deepmerge@^4.0.0, deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== @@ -6078,6 +6109,13 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -7141,6 +7179,11 @@ jss@10.6.0, jss@^10.5.1: array-includes "^3.1.2" object.assign "^4.1.2" +keycode@^2.1.7: + version "2.2.0" + resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04" + integrity sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ= + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -7236,6 +7279,11 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +load-script@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" + integrity sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ= + loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -7351,7 +7399,7 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== -loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -7449,6 +7497,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +memoize-one@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -9220,7 +9273,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -9391,6 +9444,13 @@ react-app-polyfill@^2.0.0: regenerator-runtime "^0.13.7" whatwg-fetch "^3.4.1" +react-audio-player@^0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/react-audio-player/-/react-audio-player-0.17.0.tgz#4be7b1952512801f36ba0865a9c98f7b108f991e" + integrity sha512-aCZgusPxA9HK7rLZcTdhTbBH9l6do9vn3NorgoDZRxRxJlOy9uZWzPaKjd7QdcuP2vXpxGA/61JMnnOEY7NXeA== + dependencies: + prop-types "^15.7.2" + react-dev-utils@^11.0.3: version "11.0.4" resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" @@ -9445,6 +9505,20 @@ react-error-overlay@^6.0.9: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== +react-event-listener@^0.6.0: + version "0.6.6" + resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.6.tgz#758f7b991cad9086dd39fd29fad72127e1d8962a" + integrity sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw== + dependencies: + "@babel/runtime" "^7.2.0" + prop-types "^15.6.0" + warning "^4.0.1" + +react-fast-compare@^3.0.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -9455,11 +9529,29 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-leaflet@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-leaflet/-/react-leaflet-3.2.0.tgz#8d5d05beab3f9e52665738ad737bb7b1da99f30b" + integrity sha512-eHVqoRGjW8T9GxLt7jyTKP3BDQ7XQ5AD+tc/zkbaABn1dbmREDy8GojNcYjZQa3QFLQoOLQMcUC1PTtzytZpUA== + dependencies: + "@react-leaflet/core" "^1.1.0" + react-only-when@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/react-only-when/-/react-only-when-1.0.2.tgz#a8a79b48dd6cfbd91ddc710674a94153e88039d3" integrity sha512-agE6l3L6bqaVuwNtjihTQ36M+VBfPS63KOzcNL4ZTmlwSxQPvhzIqmBWfiol0/wLYmKxCcBqgXkEJpvj5Kob8Q== +react-player@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-player/-/react-player-2.9.0.tgz#ef7fe7073434087565f00ff219824e1e02c4b046" + integrity sha512-jNUkTfMmUhwPPAktAdIqiBcVUKsFKrVGH6Ocutj6535CNfM91yrvWxHg6fvIX8Y/fjYUPoejddwh7qboNV9vGA== + dependencies: + deepmerge "^4.0.0" + load-script "^1.0.0" + memoize-one "^5.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.0.1" + react-qr-scanner@^1.0.0-alpha.7: version "1.0.0-alpha.7" resolved "https://registry.yarnpkg.com/react-qr-scanner/-/react-qr-scanner-1.0.0-alpha.7.tgz#f63993cffe238f8cbca4984d03973accc842500b" @@ -9580,6 +9672,46 @@ react-scripts@4.0.3: optionalDependencies: fsevents "^2.1.3" +react-sortable-hoc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7" + integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg== + dependencies: + "@babel/runtime" "^7.2.0" + invariant "^2.2.4" + prop-types "^15.5.7" + +react-swipeable-views-core@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-swipeable-views-core/-/react-swipeable-views-core-0.14.0.tgz#6ac443a7cc7bc5ea022fbd549292bb5fff361cce" + integrity sha512-0W/e9uPweNEOSPjmYtuKSC/SvKKg1sfo+WtPdnxeLF3t2L82h7jjszuOHz9C23fzkvLfdgkaOmcbAxE9w2GEjA== + dependencies: + "@babel/runtime" "7.0.0" + warning "^4.0.1" + +react-swipeable-views-utils@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-swipeable-views-utils/-/react-swipeable-views-utils-0.14.0.tgz#6b76e251906747482730c22002fe47ab1014ba32" + integrity sha512-W+fXBOsDqgFK1/g7MzRMVcDurp3LqO3ksC8UgInh2P/tKgb5DusuuB1geKHFc6o1wKl+4oyER4Zh3Lxmr8xbXA== + dependencies: + "@babel/runtime" "7.0.0" + keycode "^2.1.7" + prop-types "^15.6.0" + react-event-listener "^0.6.0" + react-swipeable-views-core "^0.14.0" + shallow-equal "^1.2.1" + +react-swipeable-views@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/react-swipeable-views/-/react-swipeable-views-0.14.0.tgz#149c0df3d92220cc89e3f6d5c04a78dfe46f9b54" + integrity sha512-wrTT6bi2nC3JbmyNAsPXffUXLn0DVT9SbbcFr36gKpbaCgEp7rX/OFxsu5hPc/NBsUhHyoSRGvwqJNNrWTwCww== + dependencies: + "@babel/runtime" "7.0.0" + prop-types "^15.5.4" + react-swipeable-views-core "^0.14.0" + react-swipeable-views-utils "^0.14.0" + warning "^4.0.1" + react-swipeable@^5.5.1: version "5.5.1" resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-5.5.1.tgz#48ae6182deaf62f21d4b87469b60281dbd7c4a76" @@ -9735,6 +9867,11 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== +regenerator-runtime@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" + integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== + regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7: version "0.13.7" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" @@ -10306,6 +10443,11 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +shallow-equal@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" + integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== + shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" @@ -11511,6 +11653,13 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +warning@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + watchpack-chokidar2@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"