show native app unavailable modal
show this modal if the native JamKazam app is not available on user's computer. occurs on submission of new session form and on clicking join session in browse session page.
This commit is contained in:
parent
b7615c3fcd
commit
fcd9dd15f4
|
|
@ -1,5 +1,7 @@
|
|||
/// <reference types="cypress" />
|
||||
|
||||
import useNativeAppCheck from '../../../src/hooks/useNativeAppCheck';
|
||||
|
||||
describe('Create new session', () => {
|
||||
beforeEach(() => {
|
||||
cy.stubAuthenticate({ id: '6' });
|
||||
|
|
@ -58,7 +60,7 @@ describe('Create new session', () => {
|
|||
cy.get('[data-testid=modal-choose-friends]').should('not.contain', 'Seth Call')
|
||||
})
|
||||
|
||||
it("prefill form using saved data in localStorage", () => {
|
||||
it.only("prefill form using saved data in localStorage", () => {
|
||||
cy.visit('/sessions/new');
|
||||
cy.get('[data-testid=session-privacy]').select("2")
|
||||
cy.get('[data-testid=autocomplete-text]').type('Dav')
|
||||
|
|
@ -85,4 +87,6 @@ describe('Create new session', () => {
|
|||
cy.get('[data-testid=btn-create-session]').should('be.disabled')
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
|
@ -6376,6 +6376,11 @@
|
|||
"resolved": "https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz",
|
||||
"integrity": "sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w=="
|
||||
},
|
||||
"custom-protocol-check": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/custom-protocol-check/-/custom-protocol-check-1.4.0.tgz",
|
||||
"integrity": "sha512-eMTyp8AKnE5eo+mKNqG3743eb5ZND5LhBgf9F8BN2tVdhSBnOCHH7me7iTcv0BUDhUW2dBQiHWLWMy776yZW1A=="
|
||||
},
|
||||
"cyclist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -24,10 +24,12 @@
|
|||
"chance": "^1.1.8",
|
||||
"chart.js": "^2.9.3",
|
||||
"classnames": "^2.2.6",
|
||||
"custom-protocol-check": "^1.4.0",
|
||||
"echarts": "^4.9.0",
|
||||
"echarts-for-react": "^2.0.16",
|
||||
"element-resize-event": "^3.0.3",
|
||||
"emoji-mart": "^3.0.0",
|
||||
"faker": "^5.5.3",
|
||||
"fuse.js": "^6.4.3",
|
||||
"google-maps-react": "^2.0.6",
|
||||
"i18next": "^21.3.3",
|
||||
|
|
@ -78,8 +80,7 @@
|
|||
"react-typed": "^1.2.0",
|
||||
"reactstrap": "^8.6.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"uuid": "^3.4.0",
|
||||
"faker": "^5.5.3"
|
||||
"uuid": "^3.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
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 [modal, setModal] = useState(show);
|
||||
|
||||
const toggle = () => {
|
||||
setModal(!modal);
|
||||
onToggle(!modal);
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
setModal(show);
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={modal} toggle={toggle}>
|
||||
<ModalHeader toggle={toggle}>{title}</ModalHeader>
|
||||
<ModalBody>{children}</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button onClick={toggle}>{t('close', { ns: 'common' })}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
JKModalDialog.propTypes = {
|
||||
show: PropTypes.bool.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
onToggle: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
JKModalDialog.defaultProps = {
|
||||
show: false,
|
||||
title: 'Modal Dialog'
|
||||
};
|
||||
|
||||
export default JKModalDialog;
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Alert, Col, Row, Card, CardBody, Table } from 'reactstrap';
|
||||
import { Alert, Col, Row, Card, CardBody } from 'reactstrap';
|
||||
import FalconCardHeader from '../common/FalconCardHeader';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { fetchSessions } from '../../store/features/sessionsSlice';
|
||||
import { isIterableArray } from '../../helpers/utils';
|
||||
import { useResponsive } from '@farfetch/react-context-responsive';
|
||||
import JKModalDialog from '../common/JKModalDialog';
|
||||
import { useNativeApp } from '../../context/NativeAppContext';
|
||||
|
||||
import JKSessionSwiper from '../sessions/JKSessionSwiper';
|
||||
import JKSessionList from '../sessions/JKSessionList';
|
||||
|
|
@ -17,42 +19,74 @@ function JKMusicSessions() {
|
|||
const sessions = useSelector(state => state.session.sessions);
|
||||
const loadingStatus = useSelector(state => state.session.status);
|
||||
const { greaterThan } = useResponsive();
|
||||
const { nativeAppUnavailable, setNativeAppUnavailable } = useNativeApp();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchSessions());
|
||||
}, []);
|
||||
|
||||
const toggleAppUnavilableModel = () => {
|
||||
setNativeAppUnavailable(!nativeAppUnavailable);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<FalconCardHeader title={t('list.page_title', { ns: 'sessions' })} titleClass="font-weight-bold" />
|
||||
<CardBody className="pt-0">
|
||||
{loadingStatus === 'loading' && sessions.length === 0 ? (
|
||||
<Loader />
|
||||
) : isIterableArray(sessions) ? (
|
||||
<>
|
||||
{greaterThan.sm ? (
|
||||
<Row className="mb-3 justify-content-between d-none d-md-block">
|
||||
<div className="table-responsive-xl px-2">
|
||||
<JKSessionList sessions={sessions} />
|
||||
</div>
|
||||
</Row>
|
||||
) : (
|
||||
<Row className="swiper-container d-block d-md-none" data-testid="sessionsSwiper">
|
||||
<JKSessionSwiper sessions={sessions} />
|
||||
</Row>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Row className="p-card">
|
||||
<Col>
|
||||
<Alert color="info" className="mb-0">
|
||||
{t('no_records', { ns: 'common' })}
|
||||
</Alert>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
<>
|
||||
<Card>
|
||||
<FalconCardHeader title={t('list.page_title', { ns: 'sessions' })} titleClass="font-weight-bold" />
|
||||
<CardBody className="pt-0">
|
||||
{loadingStatus === 'loading' && sessions.length === 0 ? (
|
||||
<Loader />
|
||||
) : isIterableArray(sessions) ? (
|
||||
<>
|
||||
{greaterThan.sm ? (
|
||||
<Row className="mb-3 justify-content-between d-none d-md-block">
|
||||
<div className="table-responsive-xl px-2">
|
||||
<JKSessionList sessions={sessions} />
|
||||
</div>
|
||||
</Row>
|
||||
) : (
|
||||
<Row className="swiper-container d-block d-md-none" data-testid="sessionsSwiper">
|
||||
<JKSessionSwiper sessions={sessions} />
|
||||
</Row>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Row className="p-card">
|
||||
<Col>
|
||||
<Alert color="info" className="mb-0">
|
||||
{t('no_records', { ns: 'common' })}
|
||||
</Alert>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
<JKModalDialog
|
||||
show={nativeAppUnavailable}
|
||||
onToggle={toggleAppUnavilableModel}
|
||||
title={t('new.app_unavailable_modal.title', { ns: 'sessions' })}
|
||||
>
|
||||
<p>{t('new.app_unavailable_modal.body', { ns: 'sessions' })}</p>
|
||||
<div className="d-flex flex-row">
|
||||
<a
|
||||
href="https://www.jamkazam.com/downloads"
|
||||
onClick={toggleAppUnavilableModel}
|
||||
target="_blank"
|
||||
className="btn btn-primary mr-2"
|
||||
>
|
||||
Download JamKazam App
|
||||
</a>
|
||||
<a
|
||||
href="https://www.jamkazam.com/help_desk"
|
||||
onClick={toggleAppUnavilableModel}
|
||||
target="_blank"
|
||||
className="btn btn-light"
|
||||
>
|
||||
Get Help
|
||||
</a>
|
||||
</div>
|
||||
</JKModalDialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import {useHistory} from 'react-router-dom';
|
||||
//import {useHistory} from 'react-router-dom';
|
||||
import { Form, FormGroup, Input, Label, Card, CardBody, Button, Row, Col } from 'reactstrap';
|
||||
import FalconCardHeader from '../common/FalconCardHeader';
|
||||
import JKTooltip from '../common/JKTooltip';
|
||||
|
|
@ -9,53 +9,59 @@ import JKFriendsAutoComplete from '../people/JKFriendsAutoComplete';
|
|||
import JKSessionInviteesChips from '../people/JKSessionInviteesChips';
|
||||
import { getFriends } from '../../helpers/rest';
|
||||
import jkCustomUrlScheme from '../../helpers/jkCustomUrlScheme';
|
||||
import JKModalDialog from '../common/JKModalDialog';
|
||||
import useNativeAppCheck from '../../hooks/useNativeAppCheck';
|
||||
import { useNativeApp } from '../../context/NativeAppContext';
|
||||
|
||||
const privacyMap = {
|
||||
"public": 1,
|
||||
"private_invite": 2,
|
||||
"private_approve": 3
|
||||
}
|
||||
public: 1,
|
||||
private_invite: 2,
|
||||
private_approve: 3
|
||||
};
|
||||
|
||||
const JKNewMusicSession = () => {
|
||||
const { currentUser } = useAuth();
|
||||
const { t } = useTranslation();
|
||||
const [friends, setFriends] = useState([]);
|
||||
const [isFriendsFetched, setIsFriendsFetched] = useState(false)
|
||||
const [description, setDescription] = useState("")
|
||||
const [isFriendsFetched, setIsFriendsFetched] = useState(false);
|
||||
const [description, setDescription] = useState('');
|
||||
const [invitees, setInvitees] = useState([]);
|
||||
const [privacy, setPrivacy] = useState("1");
|
||||
const [privacy, setPrivacy] = useState('1');
|
||||
const [submitted, setSubmitted] = useState(false);
|
||||
const history = useHistory();
|
||||
const formRef = useRef()
|
||||
const [showAppUnavailable, setShowAppUnavailable] = useState(false);
|
||||
//const history = useHistory();
|
||||
const formRef = useRef();
|
||||
const isNativeAppAvailable = useNativeAppCheck();
|
||||
const { nativeAppUnavailable, setNativeAppUnavailable } = useNativeApp();
|
||||
|
||||
useEffect(() => {
|
||||
fetchFriends();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if(isFriendsFetched){
|
||||
populateFormDataFromLocalStorage()
|
||||
if (isFriendsFetched) {
|
||||
populateFormDataFromLocalStorage();
|
||||
}
|
||||
}, [isFriendsFetched])
|
||||
}, [isFriendsFetched]);
|
||||
|
||||
const fetchFriends = () => {
|
||||
getFriends(currentUser.id)
|
||||
.then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json();
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
console.log('friends = ', data)
|
||||
setFriends(data);
|
||||
setIsFriendsFetched(true);
|
||||
});
|
||||
}
|
||||
.then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json();
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
console.log('friends = ', data);
|
||||
setFriends(data);
|
||||
setIsFriendsFetched(true);
|
||||
});
|
||||
};
|
||||
|
||||
const populateFormDataFromLocalStorage = () => {
|
||||
try {
|
||||
const formData = localStorage.getItem('formData');
|
||||
if(formData){
|
||||
if (formData) {
|
||||
const formDataItems = JSON.parse(formData);
|
||||
setDescription(formDataItems['description']);
|
||||
setPrivacy(formDataItems['privacy']);
|
||||
|
|
@ -64,11 +70,11 @@ const JKNewMusicSession = () => {
|
|||
updateSessionInvitations(invitees);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('localStorage is not available', error)
|
||||
console.error('localStorage is not available', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = event => {
|
||||
const handleSubmit = async event => {
|
||||
event.preventDefault();
|
||||
setSubmitted(true);
|
||||
const formData = new FormData(event.target);
|
||||
|
|
@ -78,35 +84,39 @@ const JKNewMusicSession = () => {
|
|||
inviteeIds: invitees.map(i => i.id).join()
|
||||
};
|
||||
console.log(payload);
|
||||
//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 url = encodeURI(`${process.env.REACT_APP_CLIENT_BASE_URL}/client#/createSession/${q}`)
|
||||
//const urlScheme = `jamkazam://url=${url}`
|
||||
const urlScheme = jkCustomUrlScheme('createSession', q)
|
||||
window.open(urlScheme)
|
||||
try {
|
||||
//store this payload in localstorage.
|
||||
localStorage.setItem('formData', JSON.stringify(payload))
|
||||
localStorage.setItem('formData', JSON.stringify(payload));
|
||||
} catch (error) {
|
||||
console.error("localStorage is not available", error);
|
||||
console.error('localStorage is not available', error);
|
||||
}
|
||||
//check if jamkazam app is installed
|
||||
try {
|
||||
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);
|
||||
window.open(urlScheme);
|
||||
//history.push('/sessions');
|
||||
} catch (error) {
|
||||
toggleAppUnavilableModel();
|
||||
}
|
||||
//history.push('/sessions');
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleOnSelect = submittedItems => {
|
||||
updateSessionInvitations(submittedItems)
|
||||
updateSessionInvitations(submittedItems);
|
||||
};
|
||||
|
||||
const updateSessionInvitations = (submittedInvitees) => {
|
||||
const updateSessionInvitations = submittedInvitees => {
|
||||
const updatedInvitees = Array.from(new Set([...invitees, ...submittedInvitees]));
|
||||
setInvitees(updatedInvitees);
|
||||
|
||||
const friendIds = submittedInvitees.map(si => si.id)
|
||||
const friendIds = submittedInvitees.map(si => si.id);
|
||||
const updatedFriends = friends.filter(f => !friendIds.includes(f.id));
|
||||
setFriends(updatedFriends);
|
||||
}
|
||||
};
|
||||
|
||||
const removeInvitee = invitee => {
|
||||
const updatedInvitees = invitees.filter(i => i.id !== invitee.id);
|
||||
|
|
@ -115,6 +125,13 @@ const JKNewMusicSession = () => {
|
|||
setFriends(updatedFriends);
|
||||
};
|
||||
|
||||
const toggleAppUnavilableModel = () => {
|
||||
setNativeAppUnavailable(prev => !prev);
|
||||
if (!nativeAppUnavailable) {
|
||||
setSubmitted(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
|
|
@ -128,10 +145,21 @@ const JKNewMusicSession = () => {
|
|||
{t('new.privacy', { ns: 'sessions' })}{' '}
|
||||
<JKTooltip title={t('new.privacy_help', { ns: 'sessions' })} />
|
||||
</Label>
|
||||
<Input type="select" aria-label="Session Privacy" name="privacy" value={privacy} 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"]}>{t('new.privacy_opt_private_invite', { ns: 'sessions' })}</option>
|
||||
<option value={privacyMap["private_approve"]}>{t('new.privacy_opt_private_approve', { ns: 'sessions' })}</option>
|
||||
<Input
|
||||
type="select"
|
||||
aria-label="Session Privacy"
|
||||
name="privacy"
|
||||
value={privacy}
|
||||
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']}>
|
||||
{t('new.privacy_opt_private_invite', { ns: 'sessions' })}
|
||||
</option>
|
||||
<option value={privacyMap['private_approve']}>
|
||||
{t('new.privacy_opt_private_approve', { ns: 'sessions' })}
|
||||
</option>
|
||||
</Input>
|
||||
</FormGroup>
|
||||
|
||||
|
|
@ -141,9 +169,7 @@ const JKNewMusicSession = () => {
|
|||
<JKTooltip title={t('new.invitations_help', { ns: 'sessions' })} />
|
||||
</Label>
|
||||
<JKFriendsAutoComplete friends={friends} onSelect={handleOnSelect} />
|
||||
{ invitees.length > 0 &&
|
||||
<JKSessionInviteesChips invitees={invitees} removeInvitee={removeInvitee} />
|
||||
}
|
||||
{invitees.length > 0 && <JKSessionInviteesChips invitees={invitees} removeInvitee={removeInvitee} />}
|
||||
</FormGroup>
|
||||
<FormGroup className="mb-3">
|
||||
<Label>
|
||||
|
|
@ -152,7 +178,7 @@ const JKNewMusicSession = () => {
|
|||
</Label>
|
||||
<Input
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
name="description"
|
||||
type="textarea"
|
||||
data-testid="session-description"
|
||||
|
|
@ -161,7 +187,9 @@ const JKNewMusicSession = () => {
|
|||
</FormGroup>
|
||||
|
||||
<FormGroup className="mb-3">
|
||||
<Button color="primary" data-testid="btn-create-session" disabled={submitted}>{t('new.create_session', { ns: 'sessions' })}</Button >
|
||||
<Button color="primary" data-testid="btn-create-session" disabled={submitted}>
|
||||
{t('new.create_session', { ns: 'sessions' })}
|
||||
</Button>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</Col>
|
||||
|
|
@ -169,6 +197,31 @@ const JKNewMusicSession = () => {
|
|||
</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>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,25 +66,37 @@ function JKFriendsAutoComplete({ friends, onSelect }) {
|
|||
onSelect(selectedFriends);
|
||||
};
|
||||
|
||||
const toggleVisibility = (isVisible) => {
|
||||
setShowModal(isVisible)
|
||||
}
|
||||
const toggleVisibility = isVisible => {
|
||||
setShowModal(isVisible);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row className="mb-2">
|
||||
<Col md={9}>
|
||||
<InputGroup>
|
||||
<InputGroupText>
|
||||
<FontAwesomeIcon icon="search" transform="shrink-4 down-1" />
|
||||
</InputGroupText>
|
||||
|
||||
<Input onChange={handleInputChange} onKeyDown={handleOnKeyDown} value={inputValue} innerRef={inputRef} placeholder={t('new.invitations_filter_placeholder', { ns: 'sessions' })} data-testid="autocomplete-text" />
|
||||
<InputGroupText>
|
||||
<FontAwesomeIcon icon="search" transform="shrink-4 down-1" />
|
||||
</InputGroupText>
|
||||
|
||||
<Input
|
||||
onChange={handleInputChange}
|
||||
onKeyDown={handleOnKeyDown}
|
||||
value={inputValue}
|
||||
innerRef={inputRef}
|
||||
placeholder={t('new.invitations_filter_placeholder', { ns: 'sessions' })}
|
||||
data-testid="autocomplete-text"
|
||||
/>
|
||||
</InputGroup>
|
||||
</Col>
|
||||
<Col md={3}>
|
||||
<Button variant="outline-info" outline onClick={() => setShowModal(!showModal)} data-testid="btn-choose-friends">
|
||||
{t('new.choose_friends', { ns: 'sessions' })}
|
||||
<Button
|
||||
variant="outline-info"
|
||||
outline
|
||||
onClick={() => setShowModal(!showModal)}
|
||||
data-testid="btn-choose-friends"
|
||||
>
|
||||
{t('new.choose_friends', { ns: 'sessions' })}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
@ -93,14 +105,19 @@ function JKFriendsAutoComplete({ friends, onSelect }) {
|
|||
{filteredFriends.map(f => (
|
||||
<ListGroupItem key={f.id} onMouseOver={highlight} onMouseOut={unhighlight} onClick={() => handleOnClick(f)}>
|
||||
<Avatar src={f.photo_url} size="m" />
|
||||
<span className='ml-1'>
|
||||
{f.first_name} {f.last_name}
|
||||
<span className="ml-1">
|
||||
{f.first_name} {f.last_name}
|
||||
</span>
|
||||
</ListGroupItem>
|
||||
))}
|
||||
</ListGroup>
|
||||
|
||||
<JKSelectFriendsModal friends={friends} show={showModal} toggleVisibility={toggleVisibility} onSubmit={onSubmitFriendsModal} />
|
||||
<JKSelectFriendsModal
|
||||
friends={friends}
|
||||
show={showModal}
|
||||
toggleVisibility={toggleVisibility}
|
||||
onSubmit={onSubmitFriendsModal}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -110,10 +127,10 @@ JKFriendsAutoComplete.propTypes = {
|
|||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
first_name: PropTypes.string,
|
||||
last_name: PropTypes.string,
|
||||
last_name: PropTypes.string
|
||||
})
|
||||
),
|
||||
),
|
||||
onSelect: PropTypes.func.isRequired
|
||||
}
|
||||
};
|
||||
|
||||
export default JKFriendsAutoComplete;
|
||||
|
|
|
|||
|
|
@ -13,12 +13,16 @@ import jkCustomUrlScheme from '../../helpers/jkCustomUrlScheme';
|
|||
|
||||
import JKUserLatencyBadge from '../profile/JKUserLatencyBadge';
|
||||
import JKSessionUser from './JKSessionUser';
|
||||
import useNativeAppCheck from '../../hooks/useNativeAppCheck';
|
||||
import { useNativeApp } from '../../context/NativeAppContext';
|
||||
|
||||
function JKSession({ session }) {
|
||||
const { currentUser } = useAuth();
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { greaterThan } = useResponsive();
|
||||
const isNativeAppAvailable = useNativeAppCheck();
|
||||
const { setNativeAppUnavailable } = useNativeApp();
|
||||
|
||||
useEffect(() => {
|
||||
const participantIds = session.participants.map(p => p.user.id);
|
||||
|
|
@ -26,14 +30,19 @@ function JKSession({ session }) {
|
|||
dispatch(fetchUserLatencies(options));
|
||||
}, []);
|
||||
|
||||
function joinSession() {
|
||||
async 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;
|
||||
try {
|
||||
await isNativeAppAvailable()
|
||||
const q = `joinSessionId~${session.id}`;
|
||||
const urlScheme = jkCustomUrlScheme('findSession', q);
|
||||
window.open(urlScheme, '_blank');
|
||||
return;
|
||||
} catch (error) {
|
||||
setNativeAppUnavailable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +116,7 @@ clicks this button, we open an audio stream using Icecast server to let the user
|
|||
<td>
|
||||
<div>
|
||||
{session.participants.map(participant => (
|
||||
<JKUserLatencyBadge user={participant.user} />
|
||||
<JKUserLatencyBadge key={participant.id} user={participant.user} />
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import React from 'react';
|
||||
|
||||
const NativeAppContext = React.createContext(null)
|
||||
|
||||
export const NativeAppProvider = ({ children}) => {
|
||||
|
||||
const [nativeAppUnavailable, setNativeAppUnavailable] = React.useState(false)
|
||||
|
||||
return(
|
||||
<NativeAppContext.Provider value={ {nativeAppUnavailable, setNativeAppUnavailable} }>
|
||||
{children}
|
||||
</NativeAppContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useNativeApp = () => React.useContext(NativeAppContext)
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
export default (section, queryStr) => {
|
||||
const url = encodeURI(`${process.env.REACT_APP_CLIENT_BASE_URL}/client#/${section}/custom~yes|${queryStr}`)
|
||||
const urlScheme = `jamkazam://url=${url}`
|
||||
return urlScheme
|
||||
}
|
||||
const url = encodeURI(`${process.env.REACT_APP_CLIENT_BASE_URL}/client#/${section}/custom~yes|${queryStr}`);
|
||||
const urlScheme = `jamkazam://url=${url}`;
|
||||
return urlScheme;
|
||||
};
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import customProtocolCheck from 'custom-protocol-check';
|
||||
|
||||
const useNativeAppCheck = () => {
|
||||
const isAvailable = () => {
|
||||
const customUrlScheme = 'jamkazam://';
|
||||
return new Promise((resolve, reject) => {
|
||||
customProtocolCheck(
|
||||
customUrlScheme,
|
||||
() => {
|
||||
//Custom protocol NOT found
|
||||
reject('JamKazam application cannot be found.');
|
||||
},
|
||||
() => {
|
||||
//Custom protocol found and opened the file successfully
|
||||
resolve();
|
||||
},
|
||||
1000
|
||||
);
|
||||
});
|
||||
};
|
||||
return isAvailable;
|
||||
};
|
||||
|
||||
export default useNativeAppCheck;
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
"more": "More",
|
||||
"actions": "Actions",
|
||||
"no_records": "No Records!",
|
||||
"close": "Close",
|
||||
"navigation": {
|
||||
"home": "Home",
|
||||
"friends": "Friends",
|
||||
|
|
@ -15,5 +16,13 @@
|
|||
"contact": "Contact",
|
||||
"privacy": "Privacy",
|
||||
"terms": "Terms of Service"
|
||||
},
|
||||
"modals": {
|
||||
"native_app_unavailable": {
|
||||
"title": "JamKazam App Unavailable",
|
||||
"body": "We’re sorry, but we cannot find the JamKazam application installed on your computer. If you don’t have the app on this computer, download and install the JamKazam app. If you’re sure the app is already installed on your computer, get help from our help desk.",
|
||||
"download_button": "Download JamKazam App",
|
||||
"help_button": "Get Help"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import React, { useState, useEffect, createContext } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import DashboardMain from '../components/dashboard/JKDashboardMain';
|
||||
import UserAuth from '../context/UserAuth';
|
||||
import { BrowserQueryProvider } from '../context/BrowserQuery';
|
||||
|
||||
import { NativeAppProvider } from '../context/NativeAppContext';
|
||||
|
||||
const DashboardLayout = ({ location }) => {
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
|
|
@ -13,7 +15,9 @@ const DashboardLayout = ({ location }) => {
|
|||
return (
|
||||
<UserAuth path={location.pathname}>
|
||||
<BrowserQueryProvider>
|
||||
<DashboardMain />
|
||||
<NativeAppProvider>
|
||||
<DashboardMain />
|
||||
</NativeAppProvider>
|
||||
</BrowserQueryProvider>
|
||||
</UserAuth>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue