diff --git a/jam-ui/src/components/profile/JKUserLatencyBadge.js b/jam-ui/src/components/profile/JKUserLatencyBadge.js
new file mode 100644
index 000000000..655743d0c
--- /dev/null
+++ b/jam-ui/src/components/profile/JKUserLatencyBadge.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import PropTypes from 'prop-types';
+
+import JKLatencyBadge from './JKLatencyBadge';
+
+const JKUserLatency = ({user}) => {
+ const latencyData = useSelector(state => state.latency.latencies.find(l => l.user_id === user.id));
+ return (
+
+ );
+};
+
+JKUserLatency.propTypes = { user: PropTypes.object.isRequired };
+
+export default JKUserLatency;
diff --git a/jam-ui/src/components/sessions/JKSession.js b/jam-ui/src/components/sessions/JKSession.js
new file mode 100644
index 000000000..959238cf4
--- /dev/null
+++ b/jam-ui/src/components/sessions/JKSession.js
@@ -0,0 +1,164 @@
+import React, { useEffect } from 'react';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { useAuth } from '../../context/UserAuth';
+import { useDispatch, useSelector } from 'react-redux';
+import { fetchUserLatencies } from '../../store/features/latencySlice';
+import { toast } from 'react-toastify';
+import { useTranslation } from 'react-i18next';
+import { useResponsive } from '@farfetch/react-context-responsive';
+import PropTypes from 'prop-types';
+import { Row, Col, Button } from 'reactstrap';
+
+import jkCustomUrlScheme from '../../helpers/jkCustomUrlScheme';
+
+import JKUserLatencyBadge from '../profile/JKUserLatencyBadge';
+import JKSessionUser from './JKSessionUser';
+
+function JKSession({ session }) {
+ const { currentUser } = useAuth();
+ const dispatch = useDispatch();
+ const { t } = useTranslation();
+ const { greaterThan } = useResponsive();
+
+ useEffect(() => {
+ const participantIds = session.participants.map(p => p.user.id);
+ const options = { currentUserId: currentUser.id, participantIds };
+ dispatch(fetchUserLatencies(options));
+ }, []);
+
+ function joinSession() {
+ if (session.musician_access && session.approval_required) {
+ toast.info(t('list.alerts.join_request_sent', { ns: 'sessions' }));
+ } else {
+ const q = `joinSessionId~${session.id}`;
+ const urlScheme = jkCustomUrlScheme('findSession', q);
+ window.open(urlScheme);
+ return;
+ }
+ }
+
+ const sessionDescription = session => {
+ if (session.description) {
+ return session.description;
+ } else if (session.musician_access && !session.approval_required) {
+ return t('list.descriptions.public_open_session', { ns: 'sessions' });
+ } else if (session.musician_access && session.approval_required) {
+ return t('list.descriptions.private_session', { ns: 'sessions' });
+ } else if (!session.musician_access && !session.approval_required) {
+ return t("list.descriptions.rsvp_session", { ns: "sessions" });
+ }
+ };
+
+ const invitedNote = session => {
+ if (session.invitations.find(i => i.receiver_id === currentUser.id)) {
+ return t('list.notes.invited', { ns: 'sessions' });
+ }
+ };
+
+ const hasFriendNote = session => {
+ if (session.participants.find(p => p.user.is_friend)) {
+ return t('list.notes.has_friend', { ns: 'sessions' })
+ }
+ };
+
+ const actionButtons = () => {
+ return (
+
+
+
+
+ );
+ };
+
+ return (
+ <>
+ {greaterThan.sm ? (
+
+ |
+
+
+
+ {invitedNote(session)}
+
+
+
+
+
+ {hasFriendNote(session)}
+
+
+ {sessionDescription(session)}
+ |
+
+ {session.participants.map(participant => (
+
+ ))}
+ |
+
+
+ {session.participants.map(participant => (
+
+ ))}
+
+ |
+
+ {session.participants.map(participant => (
+
+ {participant.tracks.map(track => (
+ {track.instrument}
+ ))}
+
+ ))}
+ |
+ {actionButtons()} |
+
+ ) : (
+
+
+
+
+
+ {invitedNote(session)}
+
+
+
+
+
+ {hasFriendNote(session)}
+
+
+ {sessionDescription(session)}
+
+
+
{t('list.header.musicians', { ns: 'sessions'})}
+
+
+ {t('list.header.latency', { ns: 'sessions'})}
+
+
+
+ {session.participants.map(participant => (
+
+ ))}
+
+ {actionButtons()}
+
+
+ )}
+ >
+ );
+}
+
+JKSession.propTypes = { session: PropTypes.object.isRequired };
+
+export default JKSession;
diff --git a/jam-ui/src/components/sessions/JKSessionList.js b/jam-ui/src/components/sessions/JKSessionList.js
new file mode 100644
index 000000000..97ed3931e
--- /dev/null
+++ b/jam-ui/src/components/sessions/JKSessionList.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Table } from 'reactstrap';
+import JKSession from './JKSession';
+import { useTranslation } from 'react-i18next';
+import PropTypes from 'prop-types';
+
+function JKSessionList({ sessions }) {
+ const { t } = useTranslation();
+ return (
+
+
+
+ | {t('list.header.session', { ns: 'sessions' })} |
+
+ {t('list.header.musicians', { ns: 'sessions' })}
+ |
+ {t('list.header.latency', { ns: 'sessions' })} |
+ {t('list.header.instruments', { ns: 'sessions' })} |
+ {t('actions', { ns: 'common' })} |
+
+
+
+ {sessions.map(session => (
+
+ ))}
+
+
+ );
+}
+
+JKSessionList.propTypes = { sessions: PropTypes.array.isRequired };
+
+export default JKSessionList;
diff --git a/jam-ui/src/components/sessions/JKSessionSwiper.js b/jam-ui/src/components/sessions/JKSessionSwiper.js
new file mode 100644
index 000000000..e912e15a8
--- /dev/null
+++ b/jam-ui/src/components/sessions/JKSessionSwiper.js
@@ -0,0 +1,72 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+// 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 JKSession from './JKSession';
+
+// 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 JKSessionSwiper = ({ sessions }) => {
+
+ return (
+ <>
+ console.log('slide change')}
+ onSlideNextTransitionEnd={swiper => {
+ if(swiper.isEnd){
+ //goNextPage()
+ }
+ }}
+ pagination={{
+ clickable: true,
+ type: 'custom'
+ }}
+ navigation={{
+ nextEl: '.swiper-button-next',
+ prevEl: '.swiper-button-prev'
+ }}
+ >
+ {sessions.map((session, index) => (
+
+
+
+
+
+ {session.name}
+
+
+
+
+
+
+ ))}
+
+
+ >
+ );
+};
+
+JKSessionSwiper.propTypes = {
+ sessions: PropTypes.arrayOf(PropTypes.instanceOf(Object)).isRequired,
+};
+
+export default JKSessionSwiper;
diff --git a/jam-ui/src/components/sessions/JKSessionUser.js b/jam-ui/src/components/sessions/JKSessionUser.js
new file mode 100644
index 000000000..f6c0446bc
--- /dev/null
+++ b/jam-ui/src/components/sessions/JKSessionUser.js
@@ -0,0 +1,60 @@
+import React, { useState } from 'react';
+import { useSelector, useDispatch } from 'react-redux';
+import { fetchPerson } from '../../store/features/peopleSlice';
+import { useResponsive } from '@farfetch/react-context-responsive';
+
+import JKLatencyBadge from '../profile/JKLatencyBadge';
+import JKProfileSidePanel from '../profile/JKProfileSidePanel';
+import JKProfileAvatar from '../profile/JKProfileAvatar';
+import PropTypes from 'prop-types';
+
+function JKSessionUser({ user }) {
+ const dispatch = useDispatch();
+ const latencyData = useSelector(state => state.latency.latencies.find(l => l.user_id === user.id));
+ const [showSidePanel, setShowSidePanel] = useState(false);
+ const { greaterThan } = useResponsive();
+
+ const toggleMoreDetails = async e => {
+ e.preventDefault();
+ try {
+ await dispatch(fetchPerson({ userId: user.id })).unwrap();
+ } catch (error) {
+ console.log(error);
+ }
+
+ setShowSidePanel(prev => !prev);
+ };
+ return (
+ <>
+ {!greaterThan.sm ? (
+
+ ) : (
+
+
+
+
+
+ {user.name}
+
+
+ )}
+
+ >
+ );
+}
+
+JKSessionUser.propTypes = { user: PropTypes.object.isRequired };
+
+export default JKSessionUser;
diff --git a/jam-ui/src/components/sessions/SessionRow.js b/jam-ui/src/components/sessions/SessionRow.js
deleted file mode 100644
index ce1a19b22..000000000
--- a/jam-ui/src/components/sessions/SessionRow.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import React, { useEffect } from 'react';
-import jkCustomUrlScheme from '../../helpers/jkCustomUrlScheme';
-import JKProfileAvatar from '../profile/JKProfileAvatar';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { useAuth } from '../../context/UserAuth';
-import SessionUserLatency from './SessionUserLatency';
-import { useDispatch, useSelector } from 'react-redux';
-import { fetchUserLatencies } from '../../store/features/latencySlice';
-import SessionUser from './SessionUser';
-
-function SessionRow({session}) {
- const { currentUser } = useAuth();
- const dispatch = useDispatch()
-
- useEffect(() => {
- const participantIds = session.participants.map(p => p.user.id)
- const options = { currentUserId: currentUser.id, participantIds }
- dispatch(fetchUserLatencies(options))
- }, [])
-
- function joinSession(){
- const q = `joinSessionId~${session.id}`
- const urlScheme = jkCustomUrlScheme('findSession', q)
- window.open(urlScheme)
- }
-
- const sessionDescription = (session) => {
- if(session.description){
- return session.description;
- }else if(session.musician_access && !session.approval_required){
- return "Public, open session. Feel free to join!"
- }else if(session.musician_access && session.approval_required){
- return "Private session. Click the enter button in the right column to request to join"
- }else if(!session.musician_access && !session.approval_required){
- return "Only RSVP musicians may join"
- }
- }
-
- const invitedNote = (session) => {
- if(session.invitations.find(i => i.receiver_id === currentUser.id)){
- return "YOU WERE INVITED TO THIS SESSION"
- }
- }
-
- const hasFriendNote = session => {
- if(session.participants.find(p => p.user.is_friend)){
- return "YOU HAVE A FRIEND IN THIS SESSION"
- }
- }
-
- return (
-
- |
-
-
-
- {invitedNote(session)}
-
-
-
-
-
- {hasFriendNote(session)}
-
-
- {sessionDescription(session)}
- |
-
- {session.participants.map(participant => (
-
- ))}
- |
-
-
- {session.participants.map(participant => (
-
- ))}
-
- |
-
-
- {session.participants.map(participant =>
- participant.tracks.map(track => {track.instrument} )
- )}
-
- |
-
-
-
-
-
-
-
- |
-
- );
-}
-
-export default SessionRow;
diff --git a/jam-ui/src/components/sessions/SessionUser.js b/jam-ui/src/components/sessions/SessionUser.js
deleted file mode 100644
index 8afc72c43..000000000
--- a/jam-ui/src/components/sessions/SessionUser.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React, { useState } from 'react';
-import { useSelector, useDispatch } from 'react-redux';
-import JKProfileSidePanel from '../profile/JKProfileSidePanel';
-import JKProfileAvatar from '../profile/JKProfileAvatar';
-import { fetchPerson } from '../../store/features/peopleSlice';
-
-function SessionUser({ user }) {
- const dispatch = useDispatch()
- const latencyData = useSelector(state => state.latency.latencies.find(l => l.user_id === user.id));
- const [showSidePanel, setShowSidePanel] = useState(false);
-
- const toggleMoreDetails = async e => {
- e.preventDefault();
- try {
- await dispatch(fetchPerson({ userId: user.id })).unwrap();
- } catch (error) {
- console.log(error);
- }
-
- setShowSidePanel(prev => !prev);
- };
- return (
- <>
-
-
- >
- );
-}
-
-export default SessionUser;
diff --git a/jam-ui/src/components/sessions/SessionUserLatency.js b/jam-ui/src/components/sessions/SessionUserLatency.js
deleted file mode 100644
index 5b9338db1..000000000
--- a/jam-ui/src/components/sessions/SessionUserLatency.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import JKLatencyBadge from '../profile/JKLatencyBadge';
-import { useSelector } from 'react-redux';
-
-function SessionUserLatency({user}) {
-
- const latencyData = useSelector(state => state.latency.latencies.find(l => l.user_id === user.id));
-
- return ;
-}
-
-export default SessionUserLatency;
diff --git a/jam-ui/src/i18n/locales/en/common.json b/jam-ui/src/i18n/locales/en/common.json
index 587a948f5..d34121ae8 100644
--- a/jam-ui/src/i18n/locales/en/common.json
+++ b/jam-ui/src/i18n/locales/en/common.json
@@ -4,6 +4,7 @@
"loading": "Loading",
"more": "More",
"actions": "Actions",
+ "no_records": "No Records!",
"navigation": {
"home": "Home",
"friends": "Friends",
diff --git a/jam-ui/src/i18n/locales/en/sessions.json b/jam-ui/src/i18n/locales/en/sessions.json
index 7aae9c31b..efc9b24a2 100644
--- a/jam-ui/src/i18n/locales/en/sessions.json
+++ b/jam-ui/src/i18n/locales/en/sessions.json
@@ -25,6 +25,18 @@
"latency": "Latency",
"instruments": "Instruments",
"actions": "Actions"
+ },
+ "alerts" : {
+ "join_request_sent": "You have requested to join this private session. You will be notified when you are approved."
+ },
+ "descriptions": {
+ "public_open_session": "Public, open session. Feel free to join!",
+ "private_session": "Private session. Click the enter button in the right column to request to join",
+ "rsvp_session": "Only RSVP musicians may join"
+ },
+ "notes": {
+ "invited": "YOU WERE INVITED TO THIS SESSION",
+ "has_friend": "YOU HAVE A FRIEND IN THIS SESSION"
}
}
}
\ No newline at end of file