purityselect/resources/js/pages/queue.vue
2024-10-25 01:05:27 +05:00

464 lines
18 KiB
Vue

<script setup>
import videoCameraOff from '@images/svg/video-camera-off.svg';
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
const store = useStore()
const isLoadingVisible = ref(false);
const getIsTonalSnackbarVisible = ref(false);
const errorMessage = ref(null);
const videoElement = ref(null);
const isCameraEnable = ref(false);
const isMicrophoneEnable = ref(false);
const isCameraPermissionGranted = ref(false);
const isMicrophonePermissionGranted = ref(false);
const isCameraDisabled = ref(false);
const isMuted = ref(false);
const mediaStream = ref(null);
const enableCamBtn = ref(true)
const permissionModel = ref(false)
store.dispatch('updateCurrentPage', '/queue')
const camIcon = computed(() => {
return isCameraDisabled.value ? 'mdi-video-off' : 'mdi-video';
});
const camText = computed(() => {
return isCameraDisabled.value ? 'Turn on camera' : 'Turn off camera';
});
const camIconColor = computed(() => {
return isCameraDisabled.value ? 'red' : 'black';
});
const micIcon = computed(() => {
return isMuted.value ? 'mdi-microphone-off' : 'mdi-microphone';
});
const micText = computed(() => {
return isMuted.value ? 'Unmute Myself' : 'Mute Myself';
});
const micIconColor = computed(() => {
return isMuted.value ? 'red' : 'black';
});
const router = useRouter()
const route = useRoute()
const queueUsers = ref([]);
const isDialogVisible = ref(true);
const patientId = ref(localStorage.getItem('patient_id'));
const currentQueue = computed(() => {
return queueUsers.value.findIndex(user => user.id == localStorage.getItem('patient_id')) + 1
});
const currentRoute = route.path;
console.log('currentRoute', currentRoute)
onMounted(async () => {
console.log('store mic cam permissions', store.getters.getIsMicEnabled, store.getters.getIsCamEnabled)
// permissionModel.value = true
// await checkPermissions();
// try {
// const camera = await navigator.permissions.query({ name: 'camera' });
// const microphone = await navigator.permissions.query({ name: 'microphone' });
// if (camera.state === 'granted') isCameraEnable.value = true;
// if (microphone.state === 'granted') isMicrophoneEnable.value = true;
// console.log(camera.state, microphone.state); // 'granted', 'denied', or 'prompt'
// } catch (error) { }
await checkMicCamPermissions();
window.Pusher = Pusher;
const key = 'bc8bffbbbc49cfa39818';
const cluster = 'mt1';
let echo = new Echo({
broadcaster: 'pusher',
key: key,
cluster: cluster,
forceTLS: true,
auth: {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('access_token'),
},
},
});
//presence channel ((user bio show))
const presenceChannel = echo.join(`dhkjkiplqe84sdaqf17nqg`)
window.presenceChannel = presenceChannel
presenceChannel.here((users) => { //existing user/ including current user
// queueUsers.value = users.filter(user => user.type == "patient").sort((a, b) => a.time - b.time);
// queueUsers.value.forEach(user => {
// user.mic = isMicrophoneEnable.value;
// });
// console.log(queueUsers.value);
presenceChannel.whisper('DeviceCurrentStatus', {
mic: isMicrophoneEnable.value,
cam: isCameraEnable.value,
})
})
.joining((user) => {
//new user
console.log('joining', user.name);
queueUsers.value.push(user);
queueUsers.value.sort((a, b) => a.time - b.time);
console.log('queueUsers.value', queueUsers.value);
})
.leaving((user) => {
console.log('leaving', user);
queueUsers.value = queueUsers.value.filter(u => u.id !== user.id);
console.log("CurrentUser", queueUsers);
})
.listenForWhisper('getPermission-' + patientId.value, (e, metadata) => {
console.log('...', e, metadata)
presenceChannel.whisper('DeviceCurrentStatus-' + metadata.user_id, {
mic: store.getters.getIsMicEnabled,
cam: store.getters.getIsCamEnabled,
})
})
.error((error) => {
console.error(error);
});
// (private channel)
echo.private('patient-' + patientId.value)
.listen('AppointmentCreated', (e) => {
console.log("Patient AppointmentCreatedTest", e);
echo.leave('dhkjkiplqe84sdaqf17nqg')
localStorage.setItem('meeting_id', e.meeting_id);
localStorage.setItem('call_type', e.call_type);
console.log('Patient_meetingId', e.meeting_id);
if (!store.getters.getIsMicEnabled && !store.getters.getIsCamEnabled) {
permissionModel.value = true
} else {
router.push('/telehealth');
}
});
echo.private('patient-end-call-' + patientId.value)
.listen('AppointmentCallEnded', (e) => {
console.log("AppointmentCallEnded", e);
localStorage.setItem('call-end-message', 'Password Sent to Email Address')
router.push('/overview');
// echo.leave('dhkjkiplqe84sdaqf17nqg')
});
});
onUnmounted(() => {
if (mediaStream.value) {
const tracks = mediaStream.value.getTracks();
tracks.forEach((track) => track.stop());
mediaStream.value = null;
}
});
const checkPermissionStatus = async (permissionName) => {
try {
const permissionStatus = await navigator.permissions.query({ name: permissionName });
return permissionStatus.state;
} catch (error) {
console.error(`Error querying ${permissionName} permission status:`, error);
return 'unknown';
}
};
const checkMicCamPermissions = async () => {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
try {
const videoPermissionStatus = await checkPermissionStatus('camera');
const audioPermissionStatus = await checkPermissionStatus('microphone');
if (videoPermissionStatus === 'granted' && audioPermissionStatus === 'granted') {
console.log('Camera and microphone permissions already granted.');
isCameraEnable.value = true;
isMicrophoneEnable.value = true;
store.dispatch('updateIsCamEnabled', true);
store.dispatch('updateIsMicEnabled', true);
return;
}
const constraints = {
video: videoPermissionStatus !== 'granted' ? true : false,
audio: audioPermissionStatus !== 'granted' ? true : false
};
const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
if (constraints.video && mediaStream.getVideoTracks().length > 0) {
isCameraEnable.value = true;
store.dispatch('updateIsCamEnabled', true);
console.log('Camera permission granted.');
mediaStream.getVideoTracks().forEach(track => track.stop());
}
if (constraints.audio && mediaStream.getAudioTracks().length > 0) {
isMicrophoneEnable.value = true;
store.dispatch('updateIsMicEnabled', true);
console.log('Microphone permission granted.');
mediaStream.getAudioTracks().forEach(track => track.stop());
}
} catch (error) {
console.error('Error requesting camera or microphone permissions:', error);
if (error.name === 'NotAllowedError') {
console.log('Camera or microphone permissions were not granted.');
}
}
} else {
console.error('navigator.mediaDevices is not supported in this browser.');
}
};
const checkPermissions = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
console.log('stream', stream)
if (stream) {
isCameraPermissionGranted.value = true;
isMicrophonePermissionGranted.value = true;
enableCamBtn.value = false
await nextTick();
videoElement.value.srcObject = stream;
mediaStream.value = stream;
}
} catch (error) {
isCameraDisabled.value = true;
enableCamBtn.value = false
getIsTonalSnackbarVisible.value = true;
errorMessage.value = "We can't access your camera and microphone.";
console.error('Error accessing media devices:', error);
}
};
const toggleCamera = () => {
if (!isCameraPermissionGranted.value) {
getIsTonalSnackbarVisible.value = true;
errorMessage.value = "We can't access your camera and microphone.";
} else {
isCameraDisabled.value = !isCameraDisabled.value;
// Stop the stream if the camera is being turned off
if (isCameraDisabled.value && mediaStream.value) {
const tracks = mediaStream.value.getTracks();
tracks.forEach((track) => track.stop());
mediaStream.value = null;
videoElement.value.srcObject = null;
}
// Start the stream if the camera is being turned on
if (!isCameraDisabled.value) {
checkPermissions();
}
}
};
const toggleMic = () => {
if (!isMicrophonePermissionGranted.value) {
getIsTonalSnackbarVisible.value = true;
errorMessage.value = "We can't access your camera and microphone.";
} else {
isMuted.value = !isMuted.value;
}
};
const enablePermission = async () => {
await checkMicCamPermissions();
await nextTick()
if (store.getters.getIsMicEnabled && store.getters.getIsCamEnabled) {
permissionModel.value = false
router.push('/telehealth');
}
};
</script>
<template>
<VDialog v-model="isLoadingVisible" width="110" height="150" color="primary">
<VCardText class="" style="color: white !important;">
<div class="demo-space-x">
<VProgressCircular :size="40" color="primary" indeterminate />
</div>
</VCardText>
</VDialog>
<VDialog v-model="permissionModel" refs="myDialog" persistent width="500">
<!-- <template v-slot:default="{ isActive }"> -->
<v-card>
<v-card-text>
<div class="mt-2 mb-2">
<h4 class="text-center">Enable your Camera & Microphone permission from your browser</h4>
</div>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text="Grant Pesmission" @click="enablePermission()"></v-btn>
</v-card-actions>
</v-card>
<!-- </template> -->
</VDialog>
<VSnackbar v-model="getIsTonalSnackbarVisible" :timeout="5000" location="top end" variant="flat" color="red">
{{ errorMessage }}
</VSnackbar>
<VRow>
<VCol cols="12" md="9">
<VAlert border="start" color="red" variant="tonal" class="mb-2"
v-if="!store.getters.getIsMicEnabled && !store.getters.getIsCamEnabled">
<div class="">To proceed, please enable camera and microphone permissions in your browser's settings.
</div>
</VAlert>
<VCard>
<VCardText>
<p>
<b>Improved Access:</b><br>
Telehealth can provide access to healthcare for patients living in remote or underserved areas,
as well as those with mobility issues or busy schedules that make it difficult to attend
in-person appointments. This can be especially beneficial for individuals with chronic
conditions or those requiring specialized care that may not be readily available locally.
<br><br>
<b>Increased Convenience:</b><br>
Telehealth appointments allow patients to receive care from the comfort of their own homes,
eliminating the need for travel and reducing the time and costs associated with in-person
visits. This can be particularly advantageous for patients with limited transportation options
or those juggling work, family, and other commitments.
<br><br>
<b>Reduced Costs:</b><br>
Telehealth has the potential to lower healthcare costs by reducing the need for in-person
visits, transportation expenses, and time off work. This can benefit both patients and the
healthcare system as a whole.
<br><br>
<b>Expanded Care Options:</b><br>
Telehealth can enable access to specialists and healthcare providers who may not be available
locally, giving patients the opportunity to receive care from a broader range of medical
professionals.
<br><br>
<b>Increased Efficiency:</b><br>
Telehealth appointments can often be scheduled more easily and quickly than in-person visits,
potentially reducing wait times and improving the overall efficiency of the healthcare system.
</p>
</VCardText>
</VCard>
</VCol>
<VCol cols="12" md="3">
<VCard class="auth-card rounded-5 bg-black" :class="isCameraDisabled || enableCamBtn ? 'pa-2' : ''">
<div class="align-items-center justify-content-center" style="min-height:200px">
<!-- <v-menu v-if="isCameraPermissionGranted && isMicrophonePermissionGranted">
<template v-slot:activator="{ props }">
<v-btn icon="mdi-dots-horizontal" v-bind="props" class="menu-btn" size="small"
rounded="xl"></v-btn>
</template>
<v-list>
<v-list-item @click="toggleCamera()">
<v-list-item-title>
<VIcon :color="camIconColor" :icon="camIcon"></VIcon> {{ camText }}
</v-list-item-title>
</v-list-item>
<v-list-item @click="toggleMic()">
<v-list-item-title>
<VIcon :color="micIconColor" :icon="micIcon"></VIcon> {{ micText }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu> -->
<div v-if="isCameraPermissionGranted && isMicrophonePermissionGranted" class="video-icons">
<v-btn @click="toggleCamera()" :class="isCameraDisabled ? 'video-icon-red' : 'video-icon-black'"
:color="camIconColor" :icon="camIcon" size="small" rounded="xl"></v-btn>
<v-btn @click="toggleMic()" class="ml-1"
:class="isMuted ? 'video-icon-red' : 'video-icon-black'" :color="micIconColor"
:icon="micIcon" size="small" rounded="xl"></v-btn>
</div>
<div class="text-center" :class="isCameraDisabled ? 'video-single-icon mb-1 pb-0 px-2' : ''">
<VImg v-if="isCameraDisabled" :src="videoCameraOff" height="50" width="55"
class="video-camera mb-1 mt-3" />
<div v-if="isCameraPermissionGranted">
<!-- <video :ref="!isCameraDisabled ? 'videoElement' : ''" autoplay
:style="{ display: !isCameraDisabled ? 'block' : 'none' }" :muted="isMuted"></video> -->
<video ref="videoElement" autoplay
:style="{ display: !isCameraDisabled ? 'block' : 'none' }" :muted="isMuted"></video>
</div>
<div v-if="!enableCamBtn">
<small v-if="!isCameraPermissionGranted && !isMicrophonePermissionGranted">Allow access to
your cam/mic by
clicking on blocked cam icon in address bar.</small>
</div>
<div v-if="enableCamBtn" class="text-center"
:class="enableCamBtn ? 'video-single-icon mb-1 pb-0 px-2' : ''">
<VImg v-if="enableCamBtn" :src="videoCameraOff" height="50" width="55"
class="video-camera mb-1 mt-3" />
<small>Your webcam isn't enabled yet.</small>
</div>
</div>
</div>
<v-btn v-if="enableCamBtn" class=" d-grid w-100 waves-effect waves-light text-capitalize" color="error"
variant="flat" @click="checkPermissions()">
Enable Camera
</v-btn>
</VCard>
<VCard v-if="isDialogVisible" class="mt-4">
<!-- <VCardItem class="justify-center">
<VCardTitle class="font-weight-bold text-primary">
HGH
</VCardTitle>
</VCardItem> -->
<v-sheet elevation="20" max-width="" rounded="lg" width="100%" class="pa-2 px-4">
<h4> Doctor is</h4>
<v-badge color="success" content="Online" inline></v-badge>
<p class="mb-3">Your call will start soon.<br></p>
<!-- <p>You are number {{ currentQueue }} in line. </p> -->
</v-sheet>
</VCard>
</VCol>
</VRow>
</template>
<style lang="scss">
@use "@core/scss/template/pages/page-auth.scss";
</style>
<style>
.video-single-icon {
margin: 0 auto;
position: absolute;
top: calc(35% - 10px);
width: 100%;
}
.menu-btn {
position: absolute;
background-color: #0000009c !important;
left: 10px;
z-index: 999;
top: 5px;
}
.video-icons {
position: absolute;
right: 10px;
z-index: 9999;
top: 5px;
}
.video-icon-red {
background-color: red;
color: white !important;
border-radius: 12px;
}
.video-icon-black {
background-color: #0000009c !important;
color: white !important;
border-radius: 12px;
}
.video-camera {
display: block;
position: relative;
margin: 0 auto;
}
video {
width: 100%;
height: auto;
}
</style>