session history page:

list all the sessions in decending order they were created
This commit is contained in:
Nuwan 2024-05-05 18:38:29 +05:30
parent 7176fecd7b
commit 97e0a8d36a
11 changed files with 308 additions and 30 deletions

View File

@ -8,6 +8,7 @@ import { isIterableArray } from '../../helpers/utils';
import { useResponsive } from '@farfetch/react-context-responsive';
import { fetchSessionsHistory } from '../../store/features/sessionsHistorySlice';
import JKSessionsHistoryList from '../sessions/JKSessionsHistoryList';
import JKSessionsHistorySwiper from '../sessions/JKSessionsHistorySwiper';
const JKMusicSessionsHistory = () => {
const { t } = useTranslation();
@ -29,7 +30,7 @@ const JKMusicSessionsHistory = () => {
return (
<Card>
<FalconCardHeader title={t('history.page_title', { ns: 'sessions' })} titleClass="font-weight-bold" />
<CardBody className="pt-0">
<CardBody className="pt-0 pr-0">
{loadingStatus === 'loading' && sessions.length === 0 ? (
<Loader />
) : isIterableArray(sessions) ? (
@ -42,7 +43,7 @@ const JKMusicSessionsHistory = () => {
</Row>
) : (
<Row className="swiper-container d-block d-md-none" data-testid="sessionsSwiper">
{/* <JKSessionSwiper sessions={sessions} /> */}
<JKSessionsHistorySwiper sessions={sessions} />
</Row>
)}
</>

View File

@ -155,6 +155,7 @@ const JKProfileSidePanel = props => {
{currentUser && (
<div className="p-3 bg-white border-top fixed-bottom">
<JKConnectButton
cssClasses="mb-2 mb-md-0"
currentUser={currentUser}
user={user}
addContent={

View File

@ -17,12 +17,15 @@ import { useNativeApp } from '../../context/NativeAppContext';
import EnterIcon from '../../icons/enter.svg';
import JKInstrumentIcon from '../profile/JKInstrumentIcon';
import useSessionHelper from './JKUseSessionHelper';
function JKSession({ session }) {
const { currentUser } = useAuth();
const dispatch = useDispatch();
const { t } = useTranslation();
const { greaterThan } = useResponsive();
const { setNativeAppUnavailable } = useNativeApp();
const { sessionDescription } = useSessionHelper(session);
useEffect(() => {
const otherUserIds = session.participants.map(p => p.user.id);
@ -30,17 +33,17 @@ function JKSession({ session }) {
dispatch(fetchUserLatencies(options));
}, [session.id]);
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 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)) {
@ -78,7 +81,7 @@ function JKSession({ session }) {
<small>{hasFriendNote(session)}</small>
</u>
</div>
<div>{sessionDescription(session)}</div>
<div>{sessionDescription}</div>
</td>
<td>
{session.participants.map(participant => (

View File

@ -0,0 +1,138 @@
import React, { useState, useEffect } from 'react';
import { useResponsive } from '@farfetch/react-context-responsive';
import useSessionHelper from './JKUseSessionHelper';
import { Row, Col, UncontrolledTooltip } from 'reactstrap';
import JKInstrumentIcon from '../profile/JKInstrumentIcon';
import JKSessionUser from './JKSessionUser';
import JKUserLatencyBadge from '../profile/JKUserLatencyBadge';
import { useTranslation } from 'react-i18next';
const JKSessionsHistoryItem = ({ session_id, sessionGroup }) => {
const { greaterThan } = useResponsive();
const { sessionDescription, sessionDateTime } = useSessionHelper(sessionGroup[0]);
const [participants, setParticipants] = useState([]);
const { t } = useTranslation();
useEffect(() => {
setParticipants(
sessionGroup.map(history => ({
id: history.user_id,
user: {
id: history.user_id,
photo_url: history.photo_url,
first_name: history.first_name,
last_name: history.last_name,
name: `${history.first_name} ${history.last_name}`
},
tracks: history.instruments.split('|').map((instrument, index) => ({
id: index,
instrument_id: instrument,
instrument: instrument
}))
}))
);
}, [sessionGroup]);
// const formattedDate = date => {
// const d = new Date(date);
// return d.toLocaleDateString('en-us', {
// weekday: 'long',
// year: 'numeric',
// month: 'short',
// day: 'numeric',
// timeZoneName: 'short'
// });
// };
const musicianRowStyle = {
height: '40px',
flexWrap: 'wrap',
overflow: 'hidden',
alignItems: 'center'
};
return (
<>
{greaterThan.sm ? (
<tr key={session_id}>
<td>
<div>
<u>
<small>{sessionDateTime}</small>
</u>
</div>
<div>{sessionDescription}</div>
</td>
<td>
{participants.map(participant => (
<Row style={musicianRowStyle} key={participant.id}>
<Col>
<JKSessionUser user={participant.user} />
</Col>
</Row>
))}
</td>
<td className="text-center">
{participants.map(participant => (
<Row key={participant.id} style={musicianRowStyle}>
<Col>
<JKUserLatencyBadge key={participant.id} user={participant.user} showBadgeOnly={true} />
</Col>
</Row>
))}
</td>
<td>
{participants.map(participant => (
<Row style={musicianRowStyle} key={participant.id} data-testid={`Participant${participant.id}Tracks`}>
<Col>
{participant.tracks.map(track => (
<span key={track.id} className="mr-1 mb-1" title={track.instrment}>
<a
id={`Participant${participant.id}Track${track.id}Instrument`}
data-testid={`Track${track.id}Instrument`}
>
<JKInstrumentIcon instrumentId={track.instrument_id} instrumentName={track.instrument} />
</a>
<UncontrolledTooltip
placement="top"
target={`Participant${participant.id}Track${track.id}Instrument`}
>
{track.instrument}
</UncontrolledTooltip>
</span>
))}
</Col>
</Row>
))}
</td>
</tr>
) : (
<Row>
<Col>
<div>
<u>
<small>{sessionDateTime}</small>
</u>
</div>
<div>{sessionDescription}</div>
<div className="d-flex flex-row justify-content-between mt-3">
<div className="ml-0 ms-2">
<h5>{t('list.header.musicians', { ns: 'sessions' })}</h5>
</div>
<div className="ml-2 ms-2">
<strong>{t('list.header.latency', { ns: 'sessions' })}</strong>
</div>
</div>
<div>
{participants.map(participant => (
<JKSessionUser user={participant.user} />
))}
</div>
</Col>
</Row>
)}
</>
);
};
export default JKSessionsHistoryItem;

View File

@ -1,12 +1,40 @@
import React from 'react'
import React from 'react';
import { groupByKey } from '../../helpers/utils';
import { Table } from 'reactstrap';
import { useResponsive } from '@farfetch/react-context-responsive';
import JKSessionsHistoryItem from './JKSessionsHistoryItem';
import { useTranslation } from 'react-i18next';
const JKSessionsHistoryList = ({ sessions }) => {
const { greaterThan } = useResponsive();
const sessionsById = groupByKey(sessions, 'session_id');
const { t } = useTranslation();
const JKSessionsHistoryList = ({sessions}) => {
return (
<div>
<h1>JKSessionsHistoryList</h1>
<pre>{JSON.stringify(sessions, null, 2)}</pre>
</div>
)
}
<>
<Table striped bordered className="fs--1">
<thead className="bg-200 text-900">
<tr>
<th width="35%" scope="col">
{t('list.header.session', { ns: 'sessions' })}
</th>
<th width="15%" scope="col" style={{ minWidth: 250 }}>
{t('list.header.musicians', { ns: 'sessions' })}
</th>
<th scope="col" className="text-center">
{t('list.header.latency', { ns: 'sessions' })}
</th>
<th scope="col">{t('list.header.instruments', { ns: 'sessions' })}</th>
</tr>
</thead>
<tbody>
{Object.entries(sessionsById).map(([session_id, sessionGroup]) => (
<JKSessionsHistoryItem key={session_id} session_id={session_id} sessionGroup={sessionGroup} />
))}
</tbody>
</Table>
</>
);
};
export default JKSessionsHistoryList
export default JKSessionsHistoryList;

View File

@ -0,0 +1,73 @@
import React from 'react';
import PropTypes from 'prop-types';
import { groupByKey } from '../../helpers/utils';
import JKSessionsHistoryItem from './JKSessionsHistoryItem';
import { useTranslation } from 'react-i18next';
// 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 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 JKSessionsHistorySwiper = ({ sessions }) => {
const sessionsById = groupByKey(sessions, 'session_id');
const { t } = useTranslation();
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'
}}
>
{Object.entries(sessionsById).map(([session_id, sessionGroup]) => (
<SwiperSlide key={`sessions-swiper-item-${session_id}`}>
<Card className="swiper-card">
<CardHeader className="text-center bg-200">
<h4 className="d-inline-block align-center mt-1">{t("history.header.session", {ns: 'sessions'})}</h4>
</CardHeader>
<CardBody>
<JKSessionsHistoryItem key={session_id} session_id={session_id} sessionGroup={sessionGroup} />
</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>
</>
);
};
JKSessionsHistorySwiper.propTypes = {
sessions: PropTypes.arrayOf(PropTypes.instanceOf(Object)).isRequired,
};
export default JKSessionsHistorySwiper;

View File

@ -0,0 +1,23 @@
import { useTranslation } from 'react-i18next';
const useSessionHelper = (session) => {
const { t } = useTranslation();
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' });
}
};
return {
sessionDescription: sessionDescription(session)
};
};
export default useSessionHelper;

View File

@ -246,4 +246,6 @@ const months = new Array("January", "February", "March",
export const formatDateShort = (dateString) => {
const date = dateString instanceof Date ? dateString : new Date(dateString);
return months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear();
}
}
export const groupByKey = (list, key) => list.reduce((hash, obj) => ({...hash, [obj[key]]:( hash[obj[key]] || [] ).concat(obj)}), {})

View File

@ -27,12 +27,9 @@ export const SessionsHistorySlice = createSlice({
})
.addCase(fetchSessionsHistory.fulfilled, (state, action) => {
console.log(action.payload);
// state.status = "succeeded";
// state.sessions = action.payload;
const records = new Set([...state.sessions, ...action.payload]);
const unique = [];
records.map(x => unique.filter(a => a.id === x.id).length > 0 ? null : unique.push(x))
records.map(x => unique.filter(a => a.session_history_id === x.session_history_id).length > 0 ? null : unique.push(x))
state.sessions = unique
state.status = 'succeeded'
})

View File

@ -571,7 +571,18 @@ module JamRuby
def self.history(options)
offset = options[:offset] || 0
limit = options[:limit] || 10
MusicSession.order('created_at DESC').offset(offset).limit(limit)
MusicSession.joins(
%Q{
INNER JOIN
music_sessions_user_history
ON
music_sessions.id = music_sessions_user_history.music_session_id
INNER JOIN
users
ON
music_sessions_user_history.user_id = users.id
}
).order('music_sessions.created_at DESC').select("music_sessions.id AS session_id, music_sessions_user_history.id AS session_history_id, music_sessions.created_at, music_sessions.name, music_sessions.description, music_sessions_user_history.instruments, users.first_name, users.last_name, users.photo_url, users.id AS user_id").offset(offset).limit(limit)
end
def self.scheduled user, only_public = false

View File

@ -1,3 +1,4 @@
collection @music_sessions
attributes :id, :name, :description, :created_at
attributes :session_id, :session_history_id, :name, :description, :created_at, :musician_access, :approval_required,
:user_id, :instruments, :first_name, :last_name, :photo_url