diff --git a/jam-ui/src/components/navbar/JKCurrentUserAvatar.js b/jam-ui/src/components/navbar/JKCurrentUserAvatar.js deleted file mode 100644 index 740676b97..000000000 --- a/jam-ui/src/components/navbar/JKCurrentUserAvatar.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, {useEffect} from "react"; -import { useAuth } from '../../context/UserAuth'; -import avatar from "../../assets/img/team/avatar.png"; - -const JKCurrentUserAvatar = () => { - const { currentUser } = useAuth(); - - if(currentUser && currentUser.photo_url) { - return ( ); - }else { - return ( ); - } - -} - -export default JKCurrentUserAvatar; \ No newline at end of file diff --git a/jam-ui/src/components/page/JKEditProfile.js b/jam-ui/src/components/page/JKEditProfile.js index 904ed0f73..d44026110 100644 --- a/jam-ui/src/components/page/JKEditProfile.js +++ b/jam-ui/src/components/page/JKEditProfile.js @@ -3,7 +3,7 @@ import { Card, CardBody, Col, Row, CardHeader, Form, FormGroup, Label, Input, Bu import Select from 'react-select'; import FalconCardHeader from '../common/FalconCardHeader'; import { useTranslation } from 'react-i18next'; -import JKCurrentUserAvatar from '../navbar/JKCurrentUserAvatar'; +import JKProfileAvatar from '../profile/JKProfileAvatar'; import { useAuth } from '../../context/UserAuth'; import { useForm, Controller } from 'react-hook-form'; import { @@ -15,7 +15,7 @@ import { getRegions, getCities } from '../../helpers/rest'; -import JKModalDialog from '../common/JKModalDialog'; +import JKProfileAvatarUpload from '../profile/JKProfileAvatarUpload'; function JKEditProfile() { const { t } = useTranslation('profile'); @@ -28,7 +28,8 @@ function JKEditProfile() { const [countries, setCountries] = useState([]); const [regions, setRegions] = useState([]); const [cities, setCities] = useState([]); - const [showPhotoModal, setShowPhotoModal] = useState(false); + const [showAvatarUpload, setShowAvatarUpload] = useState(false); + //const [userData, setUserData] = useState({}); const [_, forceUpdate] = useReducer(x => x + 1, 0); @@ -64,6 +65,7 @@ function JKEditProfile() { setCurrentUserLoaded(true); fetchCurentUser().then(data => { console.log("userData", data) + //setUserData(data); updateUserData(data); fetchInstruments(); fetchGenres(); @@ -370,8 +372,8 @@ function JKEditProfile() { .catch(error => console.log(error)); }; - const togglePhotoModel = () => { - setShowPhotoModal(!showPhotoModal); + const toggleAvatarUpload = () => { + setShowAvatarUpload(!showAvatarUpload); }; return ( @@ -427,14 +429,14 @@ function JKEditProfile() {
- +
{ e.preventDefault(); - togglePhotoModel(); + toggleAvatarUpload(); }} > {t('change_photo')} @@ -693,27 +695,7 @@ function JKEditProfile() { - -
-
- -
- -
- - -
-
-
+ ); } diff --git a/jam-ui/src/components/page/JKMusicSessionsLobby.js b/jam-ui/src/components/page/JKMusicSessionsLobby.js index b8ee6c6ae..ec7bf45f8 100644 --- a/jam-ui/src/components/page/JKMusicSessionsLobby.js +++ b/jam-ui/src/components/page/JKMusicSessionsLobby.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useLayoutEffect, 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'; @@ -18,6 +18,7 @@ import JKTooltip from '../common/JKTooltip'; import { updateUser } from '../../helpers/rest'; import { postUserAppInteraction } from '../../helpers/rest'; import { useVisbilityState } from '../../hooks/useVisibilityState'; +import { useHistory } from 'react-router-dom' function JKMusicSessionsLobby() { const INTERACTION_CLIENT = 'browser'; @@ -31,6 +32,7 @@ function JKMusicSessionsLobby() { const { currentUser } = useAuth(); const isNativeAppAvailable = useNativeAppCheck(); const { nativeAppUnavailable, setNativeAppUnavailable } = useNativeApp(); + const history = useHistory(); const onlineMusicians = useSelector(state => state.onlineMusician.musicians); const loadingStatus = useSelector(state => state.onlineMusician.status); @@ -39,7 +41,10 @@ function JKMusicSessionsLobby() { const [submitted, setSubmitted] = useState(false); const [activeTab, setActiveTab] = useState('1'); const [showNotificationsModal, setShowNotificationsModal] = useState(false); - const NOTIFICATION_INTERVAL = 1000 * 10 //10 seconds + + const NOTIFICATION_MODAL_INTERVAL = 1000 * 10 //10 seconds + const MUSICIANS_FETCH_INTERVAL = 1000 * 20 //20 seconds + const LIVENESS_UPDATE_INTERVAL = 1000 * 60 * 1 //1 minute const documentVisibility = useVisbilityState(); @@ -54,13 +59,21 @@ function JKMusicSessionsLobby() { } else { setTimeout(() => { setShowNotificationsModal(true); - }, NOTIFICATION_INTERVAL); + }, NOTIFICATION_MODAL_INTERVAL); } } catch (error) { console.log('Error reading localStorage', error); } }, []); + //periodically fetch online musicians + useEffect(() => { + const interval = setInterval(() => { + dispatch(fetchOnlineMusicians()); + }, MUSICIANS_FETCH_INTERVAL); + return () => clearInterval(interval); + }, []); + useEffect(() => { if (loadingStatus === 'succeeded' && onlineMusicians.length > 0) { const userIds = onlineMusicians.map(p => p.id); @@ -69,12 +82,39 @@ function JKMusicSessionsLobby() { } }, [loadingStatus]); + //update user liveness when user switches tabs useEffect(() => { if(currentUser){ postInteraction(documentVisibility); } }, [documentVisibility]); + //update user liveness when user leaves the page + useEffect(() => { + const unlisten = history.listen(() => { + console.log('User is leaving the page'); + // Do something before the user leaves the page + postInteraction('hidden'); + }); + + return () => { + console.log('Component is unmounting'); + unlisten(); + // Do something before the component unmounts + }; + }, [history]); + + //periodically update user liveness + useEffect(() => { + const interval = setInterval(() => { + if(documentVisibility === 'visible'){ + postInteraction('visible'); + } + }, LIVENESS_UPDATE_INTERVAL); + return () => clearInterval(interval); + }, []) + + const postInteraction = visibilityState => { const options = { @@ -208,7 +248,6 @@ function JKMusicSessionsLobby() {
diff --git a/jam-ui/src/components/profile/JKProfileAvatarUpload.js b/jam-ui/src/components/profile/JKProfileAvatarUpload.js new file mode 100644 index 000000000..650e934d9 --- /dev/null +++ b/jam-ui/src/components/profile/JKProfileAvatarUpload.js @@ -0,0 +1,58 @@ +import React, { useEffect } from 'react' +import * as filestack from 'filestack-js'; +import { Button } from 'reactstrap'; +import { useTranslation } from 'react-i18next'; +import JKModalDialog from '../common/JKModalDialog'; +import JKProfileAvatar from './JKProfileAvatar'; +import { useAuth } from '../../context/UserAuth'; +import { getUserDetails } from '../../helpers/rest'; + +const JKProfileAvatarUpload = ({show, toggle}) => { + const { t } = useTranslation('profile'); + const { currentUser } = useAuth(); + + useEffect(() => { + if(currentUser) { + console.log(currentUser.photo_url); + getUserDetails(currentUser.id).then(response => { + console.log('_userDetails', response); + }); + } + }, [currentUser]); + + const openFilePicker = () => { + const client = filestack.init(window.gon.fp_apikey); + client.picker({ + accept: 'image/*', + maxFiles: 1, + onFileUploadFinished: (response) => { + console.log(response); + } + }).open(); + } + return ( + +
+
+ +
+ +
+ + +
+
+
+ ) +} + +export default JKProfileAvatarUpload \ No newline at end of file diff --git a/jam-ui/src/components/sessions/JKLobbyUserList.js b/jam-ui/src/components/sessions/JKLobbyUserList.js index 12fe50067..6e34f4325 100644 --- a/jam-ui/src/components/sessions/JKLobbyUserList.js +++ b/jam-ui/src/components/sessions/JKLobbyUserList.js @@ -3,38 +3,35 @@ import { Table } from 'reactstrap'; import { useTranslation } from 'react-i18next'; import JKLobbyUser from './JKLobbyUser'; import { isIterableArray } from '../../helpers/utils'; -import Loader from '../common/Loader'; -function JKLobbyUserList({ loadingStatus, onlineMusicians, selectedUsers, setSelectedUsers }) { +function JKLobbyUserList({ onlineMusicians, selectedUsers, setSelectedUsers }) { const { t } = useTranslation('sessions'); return ( <> - {loadingStatus === 'loading' && onlineMusicians.length === 0 ? ( - - ) : ( - - +
+ + + + + + + + {isIterableArray(onlineMusicians) ? ( + onlineMusicians.map(musician => ( + + )) + ) : ( - - + - - - {isIterableArray(onlineMusicians) ? ( - onlineMusicians.map(musician => ) - ) : ( - - - - )} - -
+ {t('lobby.users.header.musician', { ns: 'sessions' })} + + {t('lobby.users.header.actions', { ns: 'sessions' })} +
- {t('lobby.users.header.musician', { ns: 'sessions' })} - - {t('lobby.users.header.actions', { ns: 'sessions' })} - {t('lobby.users.not_found')}
{t('lobby.users.not_found')}
- )} + )} + + ); } diff --git a/jam-ui/src/helpers/rest.js b/jam-ui/src/helpers/rest.js index 925c0bbe6..7e3536bc6 100644 --- a/jam-ui/src/helpers/rest.js +++ b/jam-ui/src/helpers/rest.js @@ -24,6 +24,15 @@ export const getPersonById = id => { ); }; +export const getUserDetails = options => { + const { id, ...rest } = options; + return new Promise((resolve, reject) => + apiFetch(`/users/${id}?${new URLSearchParams(rest)}`) + .then(response => resolve(response)) + .catch(error => reject(error)) + ); +} + export const getPeople = ({ data, offset, limit } = {}) => { return new Promise((resolve, reject) => { apiFetch(`/filter?offset=${offset}&limit=${limit}`, { diff --git a/jam-ui/src/store/features/onlineMusiciansSlice.js b/jam-ui/src/store/features/onlineMusiciansSlice.js index b7647ae3a..e242c4504 100644 --- a/jam-ui/src/store/features/onlineMusiciansSlice.js +++ b/jam-ui/src/store/features/onlineMusiciansSlice.js @@ -36,6 +36,12 @@ export const onlineMusiciansSlice = createSlice({ console.log('fetchOnlineMusicians.fulfilled', action.payload) state.status = 'succeeded' state.musicians = action.payload + // if (action.payload && action.payload.length > 0) { + // const difference = state.musicians.filter((element) => !action.payload.includes(element)); + // if (difference.length > 0) { + // state.musicians = action.payload + // } + // } }) .addCase(fetchOnlineMusicians.rejected, (state, action) => { state.status = 'failed' diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index c909df269..d003f62d2 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -317,18 +317,22 @@ module JamRuby #the users that have opened the lobby page but now in a different tab or window or #the users that have been active in the last 15 minutes query = User.joins(:app_interactions).where(" - (app_interactions.client = 'browser' AND app_interactions.action_at > ? AND app_interactions.screen = 'lobby' AND app_interactions.action = 'page:enter' AND app_interactions.user_id <> ?) - OR (app_interactions.client = 'browser' AND app_interactions.action_at > ? AND app_interactions.screen = 'lobby' AND app_interactions.action = 'page:exit' AND app_interactions.user_id = ? AND users.accept_desktop_notifications = ?) - OR (users.last_jam_updated_at IS NOT NULL AND users.last_jam_updated_at > ? AND users.id <> ?)", - 10.minutes.ago, + (app_interactions.client = 'browser' AND app_interactions.action_at > ? AND app_interactions.screen = 'session:lobby' AND app_interactions.action = 'page:enter' AND app_interactions.user_id <> ?) + OR (app_interactions.client = 'browser' AND app_interactions.action_at > ? AND app_interactions.screen = 'session:lobby' AND app_interactions.action = 'page:exit' AND app_interactions.user_id <> ? AND users.accept_desktop_notifications = ?) + OR (users.last_jam_updated_at IS NOT NULL AND users.last_jam_updated_at > ? AND users.id <> ? AND users.accept_desktop_notifications = ?) + ", + 5.minutes.ago, current_user.id, - 10.minutes.ago, + 5.minutes.ago, current_user.id, true, - 10.minutes.ago, - current_user.id + 5.minutes.ago, + current_user.id, + true ) + # + #the users that are currently not in a music session live_music_sessions = ActiveMusicSession if live_music_sessions.count > 0