session lobby with global chat feature
This commit is contained in:
parent
9b72852a54
commit
5a8c85e765
|
|
@ -1 +1 @@
|
|||
v14.17.1
|
||||
v14.21.3
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const JKModalDialog = ({ title, children, show, onToggle }) => {
|
||||
const JKModalDialog = ({ title, children, show, onToggle, showFooter }) => {
|
||||
const [modal, setModal] = useState(show);
|
||||
|
||||
const toggle = () => {
|
||||
|
|
@ -21,9 +21,11 @@ const JKModalDialog = ({ title, children, show, onToggle }) => {
|
|||
<Modal isOpen={modal} toggle={toggle}>
|
||||
<ModalHeader toggle={toggle}>{title}</ModalHeader>
|
||||
<ModalBody>{children}</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button onClick={toggle}>{t('close', { ns: 'common' })}</Button>
|
||||
</ModalFooter>
|
||||
{showFooter && (
|
||||
<ModalFooter>
|
||||
<Button onClick={toggle}>{t('close', { ns: 'common' })}</Button>
|
||||
</ModalFooter>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
@ -32,12 +34,14 @@ JKModalDialog.propTypes = {
|
|||
show: PropTypes.bool.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
onToggle: PropTypes.func.isRequired
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
showFooter: PropTypes.bool
|
||||
};
|
||||
|
||||
JKModalDialog.defaultProps = {
|
||||
show: false,
|
||||
title: 'Modal Dialog'
|
||||
title: 'Modal Dialog',
|
||||
showFooter: true
|
||||
};
|
||||
|
||||
export default JKModalDialog;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,54 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Col, Row, Card, CardBody } from 'reactstrap';
|
||||
import FalconCardHeader from '../common/FalconCardHeader';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Col, Row, Card, CardBody, Button, CardHeader, Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useResponsive } from '@farfetch/react-context-responsive';
|
||||
import JKLobbyUserList from '../sessions/JKLobbyUserList';
|
||||
import JKLobbyUserSwiper from '../sessions/JKLobbyUserSwiper';
|
||||
import JKLobbyChat from '../sessions/JKLobbyChat';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { fetchOnlineMusicians } from '../../store/features/onlineMusiciansSlice';
|
||||
import { fetchUserLatencies } from '../../store/features/latencySlice';
|
||||
import { useAuth } from '../../context/UserAuth';
|
||||
import { sessionPrivacyMap } from '../../config';
|
||||
import jkCustomUrlScheme from '../../helpers/jkCustomUrlScheme';
|
||||
import useNativeAppCheck from '../../hooks/useNativeAppCheck';
|
||||
import { useNativeApp } from '../../context/NativeAppContext';
|
||||
import JKModalDialog from '../common/JKModalDialog';
|
||||
import JKTooltip from '../common/JKTooltip';
|
||||
|
||||
function JKMusicSessionsLobby() {
|
||||
const { t } = useTranslation();
|
||||
const { greaterThan } = useResponsive();
|
||||
const dispatch = useDispatch();
|
||||
const { currentUser } = useAuth();
|
||||
const isNativeAppAvailable = useNativeAppCheck();
|
||||
const { nativeAppUnavailable, setNativeAppUnavailable } = useNativeApp();
|
||||
|
||||
const onlineMusicians = useSelector(state => state.onlineMusician.musicians);
|
||||
const loadingStatus = useSelector(state => state.onlineMusician.status);
|
||||
|
||||
const [selectedUsers, setSelectedUsers] = useState([]);
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState('1');
|
||||
const [showNotificationsModal, setShowNotificationsModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchOnlineMusicians());
|
||||
|
||||
//check if browser notifications are enabled
|
||||
try {
|
||||
const notificationsEnabled = localStorage.getItem('showLobbyChatNotifications');
|
||||
const dontAskAgain = localStorage.getItem('dontAskLobbyChatNotificationPermission');
|
||||
if (notificationsEnabled || dontAskAgain) {
|
||||
return;
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
if (true) {
|
||||
setShowNotificationsModal(true);
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
} catch (error) {}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -31,30 +59,216 @@ function JKMusicSessionsLobby() {
|
|||
}
|
||||
}, [loadingStatus]);
|
||||
|
||||
const handleClick = async () => {
|
||||
const payload = {
|
||||
privacy: sessionPrivacyMap.public,
|
||||
description: t('list.descriptions.public_open_session', { ns: 'sessions' }),
|
||||
inviteeIds: selectedUsers
|
||||
};
|
||||
try {
|
||||
//throw new Error('test');
|
||||
await isNativeAppAvailable();
|
||||
//window.open jamkazam app url using custom URL scheme
|
||||
//an example URL would be: jamkazam://url=https://www.jamkazam.com/client#/createSession/privacy~2|description~hello|inviteeIds~1,2,3,4
|
||||
const q = `privacy~${payload.privacy}|description~${payload.description}|inviteeIds~${payload.inviteeIds}`;
|
||||
const urlScheme = jkCustomUrlScheme('createSession', q);
|
||||
setSubmitted(true);
|
||||
window.open(urlScheme);
|
||||
//history.push('/sessions');
|
||||
} catch (error) {
|
||||
toggleAppUnavilableModel();
|
||||
} finally {
|
||||
setSubmitted(false);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const toggleAppUnavilableModel = () => {
|
||||
setNativeAppUnavailable(prev => !prev);
|
||||
if (!nativeAppUnavailable) {
|
||||
setSubmitted(false);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleShowNotificationsModel = () => {
|
||||
setShowNotificationsModal(prev => !prev);
|
||||
};
|
||||
|
||||
const grantShowNotificationsPermission = () => {
|
||||
setShowNotificationsModal(false);
|
||||
try {
|
||||
if (!window.Notification) {
|
||||
console.log('Your web browser does not support notifications.');
|
||||
alert('Your web browser does not support notifications.');
|
||||
} else {
|
||||
// check if permission is already granted
|
||||
if (Notification.permission === 'granted') {
|
||||
// show notification here
|
||||
} else {
|
||||
// request permission from user
|
||||
Notification.requestPermission()
|
||||
.then(function(p) {
|
||||
if (p === 'granted') {
|
||||
new Notification('Lobby Chat Notifications', {
|
||||
body: 'Notifications will appear here.',
|
||||
});
|
||||
localStorage.setItem('showLobbyChatNotifications', true);
|
||||
} else {
|
||||
console.log('User blocked notifications.');
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const handleDontAsk = e => {
|
||||
const checked = e.target.checked;
|
||||
if (!checked) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
localStorage.setItem('dontAskLobbyChatNotificationPermission', true);
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<Card>
|
||||
<FalconCardHeader title={t('lobby.page_title', { ns: 'sessions' })} titleClass="font-weight-bold" />
|
||||
<CardBody className="pt-0">
|
||||
{greaterThan.sm ? (
|
||||
<Row className="justify-content-between">
|
||||
<Col>
|
||||
<div className="table-responsive-xl px-2">
|
||||
<JKLobbyUserList loadingStatus={loadingStatus} onlineMusicians={onlineMusicians} />
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<JKLobbyChat />
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Row className="swiper-container d-block d-md-none" data-testid="sessionsSwiper" />
|
||||
<>
|
||||
<Card>
|
||||
<CardHeader className="card-header bg-light mb-2">
|
||||
<div className="d-flex justify-content-between">
|
||||
<div className="d-flex">
|
||||
<h5 className="mb-0 mr-1">{t('lobby.page_title', { ns: 'sessions' })}</h5>
|
||||
<JKTooltip
|
||||
placement="top"
|
||||
title="
|
||||
The Lobby shows all users who: 1) are currently on this page; or 2) have this page open in a tab in their browser and have given us permission to send browser notifications; or 3) have the JamKazam app running but aren’t currently in sessions, and have given us permission to send browser notifications.” Use the Lobby to see what other users are currently online but not in sessions. You can message other users to see if they want to join a session with you, and use checkboxes plus the Create Session & Invite Selected Users button to get a session started with other Lobby users.
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
{greaterThan.sm && (
|
||||
<div className="align-self-end">
|
||||
<Button
|
||||
color="primary"
|
||||
className="ml-2"
|
||||
onClick={handleClick}
|
||||
disabled={submitted || selectedUsers.length === 0}
|
||||
>
|
||||
Create Session & Invite Selected Users
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody className="pt-0">
|
||||
{greaterThan.sm ? (
|
||||
<Row className="justify-content-between">
|
||||
<Col>
|
||||
<div className="table-responsive-xl px-2">
|
||||
<JKLobbyUserList
|
||||
loadingStatus={loadingStatus}
|
||||
onlineMusicians={onlineMusicians}
|
||||
setSelectedUsers={setSelectedUsers}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<JKLobbyChat />
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Row className="swiper-container d-block d-md-none" data-testid="sessionsSwiper">
|
||||
<Nav tabs>
|
||||
<NavItem>
|
||||
<NavLink className="active" onClick={() => setActiveTab('1')}>
|
||||
Users
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
<NavItem>
|
||||
<NavLink className="" onClick={() => setActiveTab('2')}>
|
||||
Chat
|
||||
</NavLink>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
|
||||
<TabContent activeTab={activeTab}>
|
||||
<TabPane tabId="1">
|
||||
<Row>
|
||||
<Col sm="12">
|
||||
<JKLobbyUserSwiper
|
||||
loadingStatus={loadingStatus}
|
||||
onlineMusicians={onlineMusicians}
|
||||
setSelectedUsers={setSelectedUsers}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</TabPane>
|
||||
<TabPane tabId="2">
|
||||
<Row>
|
||||
<Col sm="12">
|
||||
<JKLobbyChat />
|
||||
</Col>
|
||||
</Row>
|
||||
</TabPane>
|
||||
</TabContent>
|
||||
</Row>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
<JKModalDialog
|
||||
show={nativeAppUnavailable}
|
||||
onToggle={toggleAppUnavilableModel}
|
||||
title={t('modals.native_app_unavailable.title', { ns: 'common' })}
|
||||
>
|
||||
<p>{t('modals.native_app_unavailable.body', { ns: 'common' })}</p>
|
||||
<div className="d-flex flex-row">
|
||||
<a
|
||||
href="https://www.jamkazam.com/downloads"
|
||||
onClick={() => toggleAppUnavilableModel()}
|
||||
target="_blank"
|
||||
className="btn btn-primary mr-2"
|
||||
>
|
||||
{t('modals.native_app_unavailable.download_button', { ns: 'common' })}
|
||||
</a>
|
||||
<a
|
||||
href="https://www.jamkazam.com/help_desk"
|
||||
onClick={() => toggleAppUnavilableModel()}
|
||||
target="_blank"
|
||||
className="btn btn-light"
|
||||
>
|
||||
{t('modals.native_app_unavailable.help_button', { ns: 'common' })}
|
||||
</a>
|
||||
</div>
|
||||
</JKModalDialog>
|
||||
|
||||
<JKModalDialog
|
||||
show={showNotificationsModal}
|
||||
onToggle={toggleShowNotificationsModel}
|
||||
title={t('lobby.chat_notifications.title', { ns: 'sessions' })}
|
||||
showFooter={false}
|
||||
>
|
||||
<p>{t('lobby.chat_notifications.body', { ns: 'sessions' })}</p>
|
||||
<div className="">
|
||||
<div>
|
||||
<label htmlFor="">
|
||||
<input type="checkbox" className="mr-2" onChange={handleDontAsk} />
|
||||
{t('lobby.chat_notifications.dont_ask_again', { ns: 'sessions' })}
|
||||
</label>
|
||||
</div>
|
||||
<div className="d-flex justify-content-end">
|
||||
<Button color="primary" className="ml-2" onClick={grantShowNotificationsPermission}>
|
||||
{t('lobby.chat_notifications.grant_permission', { ns: 'sessions' })}
|
||||
</Button>
|
||||
<Button color="secondary" outline={true} className="ml-2" onClick={() => toggleShowNotificationsModel()}>
|
||||
{t('lobby.chat_notifications.no_thanks', { ns: 'sessions' })}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</JKModalDialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,13 @@ import JKModalDialog from '../common/JKModalDialog';
|
|||
import useNativeAppCheck from '../../hooks/useNativeAppCheck';
|
||||
import { useNativeApp } from '../../context/NativeAppContext';
|
||||
import { useResponsive } from '@farfetch/react-context-responsive';
|
||||
import { sessionPrivacyMap } from '../../config';
|
||||
// const privacyMap = {
|
||||
// public: 1,
|
||||
// private_invite: 2,
|
||||
// private_approve: 3
|
||||
// };
|
||||
|
||||
const privacyMap = {
|
||||
public: 1,
|
||||
private_invite: 2,
|
||||
private_approve: 3
|
||||
};
|
||||
|
||||
const JKNewMusicSession = () => {
|
||||
const { currentUser } = useAuth();
|
||||
|
|
@ -54,7 +55,6 @@ const JKNewMusicSession = () => {
|
|||
}
|
||||
})
|
||||
.then(data => {
|
||||
console.log('friends = ', data);
|
||||
setFriends(data);
|
||||
setIsFriendsFetched(true);
|
||||
});
|
||||
|
|
@ -85,7 +85,6 @@ const JKNewMusicSession = () => {
|
|||
description: formData.get('description'),
|
||||
inviteeIds: invitees.map(i => i.id).join()
|
||||
};
|
||||
console.log(payload);
|
||||
try {
|
||||
//store this payload in localstorage.
|
||||
localStorage.setItem('formData', JSON.stringify(payload));
|
||||
|
|
@ -155,11 +154,11 @@ const JKNewMusicSession = () => {
|
|||
onChange={e => setPrivacy(e.target.value)}
|
||||
data-testid="session-privacy"
|
||||
>
|
||||
<option value={privacyMap['public']}>{t('new.privacy_opt_public', { ns: 'sessions' })}</option>
|
||||
<option value={privacyMap['private_invite']}>
|
||||
<option value={sessionPrivacyMap['public']}>{t('new.privacy_opt_public', { ns: 'sessions' })}</option>
|
||||
<option value={sessionPrivacyMap['private_invite']}>
|
||||
{t('new.privacy_opt_private_invite', { ns: 'sessions' })}
|
||||
</option>
|
||||
<option value={privacyMap['private_approve']}>
|
||||
<option value={sessionPrivacyMap['private_approve']}>
|
||||
{t('new.privacy_opt_private_approve', { ns: 'sessions' })}
|
||||
</option>
|
||||
</Input>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import { useAuth } from '../../context/UserAuth';
|
|||
import JKProfileAvatar from '../profile/JKProfileAvatar';
|
||||
import TimeAgo from '../common/JKTimeAgo';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useResponsive } from '@farfetch/react-context-responsive';
|
||||
|
||||
import useOnScreen from '../../hooks/useOnScreen';
|
||||
import useKeepScrollPosition from '../../hooks/useKeepScrollPosition';
|
||||
|
|
@ -17,6 +19,7 @@ function JKLobbyChat() {
|
|||
const dispatch = useDispatch();
|
||||
const messageTextBox = useRef();
|
||||
const scrollbar = useRef();
|
||||
const { greaterThan } = useResponsive();
|
||||
const scrolledToBottom = useRef(false);
|
||||
const { currentUser } = useAuth();
|
||||
const [fetching, setFetching] = useState(false);
|
||||
|
|
@ -126,7 +129,9 @@ function JKLobbyChat() {
|
|||
|
||||
useEffect(() => {
|
||||
if (createStatus === 'succeeded') {
|
||||
fetchMessages({ start: 0, limit: 1, lastOnly: true });
|
||||
// fetchMessages({ start: 0, limit: 1, lastOnly: true });
|
||||
setMessages([])
|
||||
fetchMessages({ start: 0, limit: offset === 0 ? LIMIT : offset * LIMIT})
|
||||
messageTextBox.current.focus();
|
||||
}
|
||||
}, [createStatus]);
|
||||
|
|
@ -152,12 +157,12 @@ function JKLobbyChat() {
|
|||
<div className="border pt-1 pl-3 p-2" style={wrapperStyle}>
|
||||
<div className="lobby-chat" ref={containerRef} style={containerStyle}>
|
||||
{messages.map((message, i) => (
|
||||
<div className="d-flex mb-3 mr-1 text-message-row" key={message.id}>
|
||||
<div ref={ref => (i === 0 ? setLastMessageRef(ref) : null)}>
|
||||
<div className="avatar avatar-2xl d-inline-block">
|
||||
<div className="d-flex mb-3 mr-1 text-message-row" key={greaterThan ? `desktop_${message.id}` : `mobile_${message.id}`}>
|
||||
<div className='d-flex align-items-center' ref={ref => (i === 0 ? setLastMessageRef(ref) : null)}>
|
||||
<div className="avatar avatar-2xl">
|
||||
<JKProfileAvatar url={message.user.photo_url} />
|
||||
</div>
|
||||
<div className="d-inline-block">
|
||||
<div className="pt-2">
|
||||
<div className="d-flex flex-column">
|
||||
<div>
|
||||
<strong>{message.user.name}</strong>
|
||||
|
|
@ -171,9 +176,9 @@ function JKLobbyChat() {
|
|||
)}
|
||||
</div>
|
||||
<div>
|
||||
{ message.id }
|
||||
{message.message}
|
||||
|
||||
{/* <hr />
|
||||
{message.id} */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,29 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Row } from 'reactstrap';
|
||||
import JKProfileAvatar from '../profile/JKProfileAvatar';
|
||||
import JKConnectButton from '../profile/JKConnectButton';
|
||||
import JKMessageButton from '../profile/JKMessageButton';
|
||||
import JKMoreDetailsButton from '../profile/JKMoreDetailsButton';
|
||||
import JKLatencyBadge from '../profile/JKLatencyBadge';
|
||||
import JKProfileInstrumentsList from '../profile/JKProfileInstrumentsList';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import JKProfileSidePanel from '../profile/JKProfileSidePanel';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { fetchPerson } from '../../store/features/peopleSlice';
|
||||
import { useResponsive } from '@farfetch/react-context-responsive';
|
||||
|
||||
import { useAuth } from '../../context/UserAuth';
|
||||
|
||||
function JKLobbyUser({ user }) {
|
||||
const [showSidePanel, setShowSidePanel] = useState(false)
|
||||
function JKLobbyUser({ user, setSelectedUsers }) {
|
||||
const { t } = useTranslation();
|
||||
const [showSidePanel, setShowSidePanel] = useState(false);
|
||||
const { currentUser } = useAuth();
|
||||
const dispatch = useDispatch();
|
||||
const { greaterThan } = useResponsive();
|
||||
const latencyData = useSelector(state => state.latency.latencies.find(l => l.user_id === user.id));
|
||||
const userData = useSelector(state => state.people.people.find(p => p.id === user.id));
|
||||
|
||||
const toggleMoreDetails = async (e) => {
|
||||
const toggleMoreDetails = async e => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await dispatch(fetchPerson({ userId: user.id })).unwrap();
|
||||
|
|
@ -28,43 +33,105 @@ function JKLobbyUser({ user }) {
|
|||
setShowSidePanel(prev => !prev);
|
||||
};
|
||||
|
||||
const setSelection = e => {
|
||||
if (e.target.checked) {
|
||||
setSelectedUsers(prev => [...prev, user.id]);
|
||||
} else {
|
||||
setSelectedUsers(prev => prev.filter(u => u !== user.id));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<tr>
|
||||
<td className="align-middle">
|
||||
<Row className="d-flex flex-row justify-content-between" style={{ alignItems: 'center' }}>
|
||||
<input type="checkbox" className="align-middle" />
|
||||
<div className="avatar avatar-sm">
|
||||
<a href="/#" onClick={toggleMoreDetails}>
|
||||
<JKProfileAvatar src={user.photo_url} />
|
||||
</a>
|
||||
</div>
|
||||
<div className="ml-2 ms-2" style={{ width: '70%' }}>
|
||||
<a href="/#" onClick={toggleMoreDetails}>
|
||||
{user.name}
|
||||
</a>
|
||||
</div>
|
||||
</Row>
|
||||
</td>
|
||||
<td className="align-middle text-center">
|
||||
<JKConnectButton
|
||||
currentUser={currentUser}
|
||||
user={user}
|
||||
addContent={<FontAwesomeIcon icon="plus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
removeContent={<FontAwesomeIcon icon="minus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
cssClasses="fs--1 px-2 py-1 mr-1"
|
||||
/>
|
||||
{greaterThan.sm ? (
|
||||
<tr>
|
||||
<td className="align-middle">
|
||||
<div className="d-flex">
|
||||
<div className="mr-2 pt-2">
|
||||
<input type="checkbox" className="align-middle" onClick={setSelection} />
|
||||
</div>
|
||||
<div className="">
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="avatar avatar-sm mr-2">
|
||||
<JKProfileAvatar src={user.photo_url} />
|
||||
</div>
|
||||
<div className="">
|
||||
<a href="/#" onClick={toggleMoreDetails}>
|
||||
<strong>{user.name}</strong>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{t('person_attributes.latency_to_me', { ns: 'people' })}:</strong>{' '}
|
||||
<JKLatencyBadge latencyData={latencyData} />
|
||||
</div>
|
||||
<div>
|
||||
<strong>{t('person_attributes.instruments', { ns: 'people' })}</strong>
|
||||
{/* <JKProfileInstrumentsList instruments={user.instruments} toggleMoreDetails={toggleMoreDetails} /> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<JKMessageButton currentUser={currentUser} user={user} cssClasses="fs--1 px-2 py-1 mr-1">
|
||||
<FontAwesomeIcon icon="comments" transform="shrink-4 down-1" className="mr-1" />
|
||||
</JKMessageButton>
|
||||
<td className="align-middle text-center">
|
||||
<JKConnectButton
|
||||
currentUser={currentUser}
|
||||
user={user}
|
||||
addContent={<FontAwesomeIcon icon="plus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
removeContent={<FontAwesomeIcon icon="minus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
cssClasses="fs--1 px-2 py-1 mr-1"
|
||||
/>
|
||||
|
||||
<JKMoreDetailsButton toggleMoreDetails={toggleMoreDetails} cssClasses="btn btn-primary fs--1 px-2 py-1">
|
||||
<FontAwesomeIcon icon="user" transform="shrink-4 down-1" className="mr-1" />
|
||||
</JKMoreDetailsButton>
|
||||
</td>
|
||||
</tr>
|
||||
<JKProfileSidePanel user={userData} latencyData={latencyData} show={showSidePanel} setShow={setShowSidePanel} />
|
||||
<JKMessageButton currentUser={currentUser} user={user} cssClasses="fs--1 px-2 py-1 mr-1">
|
||||
<FontAwesomeIcon icon="comments" transform="shrink-4 down-1" className="mr-1" />
|
||||
</JKMessageButton>
|
||||
|
||||
<JKMoreDetailsButton toggleMoreDetails={toggleMoreDetails} cssClasses="btn btn-primary fs--1 px-2 py-1">
|
||||
<FontAwesomeIcon icon="user" transform="shrink-4 down-1" className="mr-1" />
|
||||
</JKMoreDetailsButton>
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<strong>{t('person_attributes.latency_to_me', { ns: 'people' })}:</strong>{' '}
|
||||
<JKLatencyBadge latencyData={latencyData} />
|
||||
</div>
|
||||
<div>
|
||||
<h5>{t('person_attributes.instruments', { ns: 'people' })}</h5>
|
||||
{/* <JKProfileInstrumentsList instruments={instruments} toggleMoreDetails={toggleMoreDetails} /> */}
|
||||
</div>
|
||||
<div>
|
||||
<h5>{t('person_attributes.genres', { ns: 'people' })}</h5>
|
||||
{/* <JKProfileGenres genres={genres} toggleMoreDetails={toggleMoreDetails} /> */}
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<JKConnectButton
|
||||
currentUser={currentUser}
|
||||
user={user}
|
||||
addContent={<FontAwesomeIcon icon="plus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
removeContent={<FontAwesomeIcon icon="minus" transform="shrink-4 down-1" className="mr-1" />}
|
||||
cssClasses="fs--1 px-2 py-1 mr-1"
|
||||
/>
|
||||
|
||||
<JKMessageButton currentUser={currentUser} user={user} cssClasses="fs--1 px-2 py-1 mr-1">
|
||||
<FontAwesomeIcon icon="comments" transform="shrink-4 down-1" className="mr-1" />
|
||||
</JKMessageButton>
|
||||
|
||||
<a href="/#" onClick={toggleMoreDetails} data-testid="btnMore">
|
||||
<span
|
||||
className="btn btn-primary fs--1 px-2 py-1"
|
||||
data-bs-toggle="tooltip"
|
||||
title={t('view_profile', { ns: 'people' })}
|
||||
>
|
||||
<FontAwesomeIcon icon="user" transform="shrink-4 down-1" className="mr-1" />
|
||||
</span>
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
|
||||
<JKProfileSidePanel user={userData} latencyData={latencyData} show={showSidePanel} setShow={setShowSidePanel} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import JKLobbyUser from './JKLobbyUser';
|
|||
import { isIterableArray } from '../../helpers/utils';
|
||||
import Loader from '../common/Loader';
|
||||
|
||||
function JKLobbyUserList({ loadingStatus, onlineMusicians}) {
|
||||
function JKLobbyUserList({ loadingStatus, onlineMusicians, selectedUsers, setSelectedUsers }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{loadingStatus === 'loading' && onlineMusicians.length === 0 ? (
|
||||
|
|
@ -16,7 +16,7 @@ function JKLobbyUserList({ loadingStatus, onlineMusicians}) {
|
|||
<Table striped bordered className="fs--1" data-testid="sessionsLobbyUsersTable">
|
||||
<thead className="bg-200 text-900">
|
||||
<tr>
|
||||
<th width="35%" scope="col">
|
||||
<th width="75%" scope="col">
|
||||
{t('lobby.header.musician', { ns: 'sessions' })}
|
||||
</th>
|
||||
<th scope="col" className="text-center">
|
||||
|
|
@ -26,7 +26,7 @@ function JKLobbyUserList({ loadingStatus, onlineMusicians}) {
|
|||
</thead>
|
||||
<tbody className="list">
|
||||
{isIterableArray(onlineMusicians) ? (
|
||||
onlineMusicians.map(musician => <JKLobbyUser key={musician.id} user={musician} />)
|
||||
onlineMusicians.map(musician => <JKLobbyUser key={musician.id} user={musician} setSelectedUsers={setSelectedUsers} />)
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={2}>No users currently online.</td>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import JKProfileAvatar from '../profile/JKProfileAvatar';
|
||||
|
||||
// import Swiper core and required modules
|
||||
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper';
|
||||
|
||||
// Import Swiper React components
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
|
||||
import JKLobbyUser from '../sessions/JKLobbyUser';
|
||||
|
||||
// Import Swiper styles
|
||||
import 'swiper/swiper.scss';
|
||||
import 'swiper/components/navigation/navigation.scss';
|
||||
import 'swiper/components/pagination/pagination.scss';
|
||||
import 'swiper/components/scrollbar/scrollbar.scss';
|
||||
|
||||
import { Card, CardBody, CardHeader } from 'reactstrap';
|
||||
|
||||
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y]);
|
||||
|
||||
const JKLobbyUserSwiper = ({ onlineMusicians, setSelectedUsers, loadingStatus }) => {
|
||||
return (
|
||||
<>
|
||||
<Swiper
|
||||
spaceBetween={0}
|
||||
slidesPerView={1}
|
||||
//onSlideChange={() => console.log('slide change')}
|
||||
onSlideNextTransitionEnd={swiper => {
|
||||
if(swiper.isEnd){
|
||||
//goNextPage()
|
||||
}
|
||||
}}
|
||||
pagination={{
|
||||
clickable: true,
|
||||
type: 'custom'
|
||||
}}
|
||||
navigation={{
|
||||
nextEl: '.swiper-button-next',
|
||||
prevEl: '.swiper-button-prev'
|
||||
}}
|
||||
>
|
||||
{onlineMusicians.map((musician, index) => (
|
||||
|
||||
<SwiperSlide key={`session-lobby-swiper-item-${musician.id}`}>
|
||||
<Card className="swiper-person-card">
|
||||
<CardHeader className="text-center bg-200">
|
||||
<div className="avatar avatar-xl d-inline-block me-2 mr-2">
|
||||
<JKProfileAvatar url={musician.photo_url} size="xl"/>
|
||||
</div>
|
||||
<h5 className="d-inline-block align-top mt-1">{musician.name}</h5>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<JKLobbyUser user={musician} setSelectedUsers={setSelectedUsers} viewMode="swipe" />
|
||||
</CardBody>
|
||||
</Card>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
<div className="py-4 px-6 bg-white border-top w-100 fixed-bottom">
|
||||
<div className="swiper-pagination" />
|
||||
<div className="swiper-button-prev" />
|
||||
<div className="swiper-button-next" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
JKLobbyUserSwiper.propTypes = {
|
||||
onlineMusicians: PropTypes.arrayOf(PropTypes.instanceOf(Object)).isRequired,
|
||||
setSelectedUsers: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default JKLobbyUserSwiper;
|
||||
|
|
@ -15,4 +15,9 @@ export const settings = {
|
|||
isNavbarVerticalCollapsed: false,
|
||||
navbarStyle: 'transparent'
|
||||
};
|
||||
export default { version, navbarBreakPoint, topNavbarBreakpoint, settings };
|
||||
export const sessionPrivacyMap = {
|
||||
public: 1,
|
||||
private_invite: 2,
|
||||
private_approve: 3
|
||||
};
|
||||
export default { version, navbarBreakPoint, topNavbarBreakpoint, settings, sessionPrivacyMap };
|
||||
|
|
|
|||
|
|
@ -44,6 +44,13 @@
|
|||
"header": {
|
||||
"musician": "Musician",
|
||||
"actions": "Actions"
|
||||
},
|
||||
"chat_notifications": {
|
||||
"title": "Lobby Chat Notifications",
|
||||
"body": "To avoid missing lobby chat messages when you’re not actively watching this page, you can give us permission to give you browser notifications when this page is open in a tab on your browser, but you’re not currently looking at this tab/page",
|
||||
"dont_ask_again": "Don’t ask me again",
|
||||
"grant_permission": "Grant Permission",
|
||||
"no_thanks": "Not Now. Thanks"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,19 +34,13 @@ const chatMessagesSlice = createSlice({
|
|||
state.status = 'loading';
|
||||
})
|
||||
.addCase(fetchLobbyChatMessages.fulfilled, (state, action) => {
|
||||
console.log('_DEBUG_1 fetchLobbyChatMessages', action.payload);
|
||||
//let chats = [...state.records.messages, ...action.payload.chats];
|
||||
const lastOnly = action.meta.arg.lastOnly;
|
||||
console.log('_DEBUG_2 fetchLobbyChatMessages', lastOnly);
|
||||
state.records = {
|
||||
next: state.records.next === null && lastOnly? null : action.payload.next,
|
||||
messages: action.payload.chats.map(m => ({...m, status: 'delivered'})).sort((a, b) => {
|
||||
return new Date(a.created_at) - new Date(b.created_at);
|
||||
})
|
||||
};
|
||||
// state.offset_messages = action.payload.chats.sort((a, b) => {
|
||||
// return new Date(a.created_at) - new Date(b.created_at);
|
||||
// });
|
||||
state.status = 'succeeded';
|
||||
})
|
||||
.addCase(fetchLobbyChatMessages.rejected, (state, action) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue