feat: added AudioPlayer component

This commit is contained in:
Shariq Ansari 2024-07-17 17:47:36 +05:30
parent b12ed81aff
commit 9c98dc82b3
5 changed files with 271 additions and 9 deletions

View File

@ -165,9 +165,7 @@
class="relative flex justify-center after:absolute after:left-[50%] after:top-0 after:-z-10 after:border-l after:border-gray-200"
:class="i != activities.length - 1 ? 'after:h-full' : 'after:h-4'"
>
<div
class="z-10 flex h-7 w-7 items-center justify-center bg-white"
>
<div class="z-10 flex h-7 w-7 items-center justify-center bg-white">
<CommentIcon class="text-gray-800" />
</div>
</div>
@ -373,6 +371,7 @@
v-if="call.show_recording && call.recording_url"
class="flex items-center justify-between rounded border"
>
<AudioPlayer :src="call.recording_url" />
<audio class="audio-control" controls :src="call.recording_url" />
</div>
<div class="flex items-center justify-between">
@ -698,13 +697,9 @@
</div>
<div
v-if="activity.show_recording && activity.recording_url"
class="flex items-center justify-between"
class="flex flex-col items-center justify-between"
>
<audio
class="audio-control"
controls
:src="activity.recording_url"
></audio>
<AudioPlayer :src="activity.recording_url" />
</div>
</div>
</div>
@ -930,6 +925,7 @@
<script setup>
import MultipleAvatar from '@/components/MultipleAvatar.vue'
import EmailContent from '@/components/Activities/EmailContent.vue'
import AudioPlayer from '@/components/Activities/AudioPlayer.vue'
import UserAvatar from '@/components/UserAvatar.vue'
import ActivityIcon from '@/components/Icons/ActivityIcon.vue'
import Email2Icon from '@/components/Icons/Email2Icon.vue'

View File

@ -0,0 +1,178 @@
<template>
<div class="player w-full text-sm text-gray-600">
<div class="player-controls flex items-center gap-2">
<Button variant="ghost" @click="playPause">
<template #icon>
<FeatherIcon class="size-4" :name="isPaused ? 'play' : 'pause'" />
</template>
</Button>
<div
class="player-timeline flex gap-2 items-center justify-between flex-1"
>
<input
class="w-full slider !h-[0.5] bg-gray-200 [&::-webkit-slider-thumb]:shadow [&::-webkit-slider-thumb:hover]:outline [&::-webkit-slider-thumb:hover]:outline-[0.5px]"
:style="{
background: `linear-gradient(to right, #171717 ${progress}%, #ededed ${progress}%)`,
}"
type="range"
id="track"
min="0"
:max="duration"
:value="currentTime"
step="0.01"
@input="(e) => (audio.currentTime = e.target.value)"
/>
<div class="shrink-0">
{{ formatTime(currentTime) }} / {{ formatTime(duration) }}
</div>
</div>
<div class="flex items-center gap-1">
<div class="flex group gap-2 items-center">
<input
class="slider opacity-0 group-hover:opacity-100 w-0 group-hover:w-20 !h-[0.5] [&::-webkit-slider-thumb]:shadow [&::-webkit-slider-thumb:hover]:outline [&::-webkit-slider-thumb:hover]:outline-[0.5px]"
:style="{
background: `linear-gradient(to right, #171717 ${volumnProgress}%, #ededed ${volumnProgress}%)`,
}"
type="range"
id="volume"
min="0"
max="1"
:value="currentVolumn"
step="0.01"
@input="(e) => updateVolumnProgress(e.target.value)"
/>
<Button variant="ghost">
<template #icon>
<MuteIcon
v-if="volumnProgress == 0"
class="size-4"
@click="updateVolumnProgress('1')"
/>
<VolumnLowIcon
v-else-if="volumnProgress <= 40"
class="size-4"
@click="updateVolumnProgress('0')"
/>
<VolumnHighIcon
v-else-if="volumnProgress > 20"
class="size-4"
@click="updateVolumnProgress('0')"
/>
</template>
</Button>
</div>
<Button variant="ghost">
<template #icon>
<FeatherIcon class="size-4" name="more-horizontal" />
</template>
</Button>
</div>
</div>
<audio
ref="audio"
:src="src"
crossorigin="anonymous"
@loadedmetadata="setupDuration"
@timeupdate="updateCurrentTime"
@ended="isPaused = true"
></audio>
</div>
</template>
<script setup>
import VolumnLowIcon from '@/components/Icons/VolumnLowIcon.vue'
import VolumnHighIcon from '@/components/Icons/VolumnHighIcon.vue'
import MuteIcon from '@/components/Icons/MuteIcon.vue'
import { computed, ref } from 'vue'
const props = defineProps({
src: String,
})
const audio = ref(null)
const isPaused = ref(true)
const duration = ref(0)
const currentTime = ref(0)
const progress = computed(() => (currentTime.value / duration.value) * 100)
const currentVolumn = ref(1)
const volumnProgress = ref(100)
function setupDuration() {
duration.value = audio.value.duration
}
function updateCurrentTime() {
currentTime.value = audio.value.currentTime
}
function playPause() {
if (audio.value.paused) {
audio.value.play()
isPaused.value = false
} else {
audio.value.pause()
isPaused.value = true
}
}
function formatTime(time) {
if (isNaN(time)) return '00:00'
const minutes = Math.floor(time / 60)
const seconds = Math.floor(time % 60)
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
}
function updateVolumnProgress(value) {
audio.value.volume = value
currentVolumn.value = value
volumnProgress.value = value * 100
}
</script>
<style scoped>
.slider {
--trackHeight: 2px;
--thumbRadius: 14px;
-webkit-appearance: none;
appearance: none;
background: transparent;
padding: 0;
margin: 0;
border-radius: 100px;
cursor: pointer;
}
.slider::-webkit-slider-runnable-track {
appearance: none;
height: var(--trackHeight);
border-radius: 100px;
}
.slider:focus-visible {
outline: none;
}
.slider::-webkit-slider-thumb {
width: var(--thumbRadius);
height: var(--thumbRadius);
margin-top: calc((var(--trackHeight) - var(--thumbRadius)) / 2);
background: #fff;
border-radius: 100px;
pointer-events: all;
appearance: none;
z-index: 1;
}
.slider::-webkit-slider-thumb {
width: var(--thumbRadius);
height: var(--thumbRadius);
margin-top: calc((var(--trackHeight) - var(--thumbRadius)) / 2);
background: #fff;
border-radius: 100px;
pointer-events: all;
appearance: none;
z-index: 1;
}
</style>

View File

@ -0,0 +1,38 @@
<template>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.05142 5.27273H2.6926C2.3798 5.27273 2.07981 5.38766 1.85863 5.59225C1.63744 5.79683 1.51318 6.07431 1.51318 6.36364V9.63636C1.51318 9.92569 1.63744 10.2032 1.85863 10.4078C2.07981 10.6123 2.3798 10.7273 2.6926 10.7273H5.05142L9.76908 14V2L5.05142 5.27273Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M11.5 5.5C11.8143 5.81281 12.066 6.1975 12.2384 6.62854C12.4108 7.05958 12.5 7.52708 12.5 8C12.5 8.47292 12.4108 8.94042 12.2384 9.37146C12.066 9.8025 11.8143 10.1872 11.5 10.5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12.5 3C13.1286 3.62561 13.632 4.39501 13.9768 5.25708C14.3217 6.11915 14.5 7.05416 14.5 8C14.5 8.94584 14.3217 9.88085 13.9768 10.7429C13.632 11.605 13.1286 12.3744 12.5 13"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5.25 3.07596L13.7286 12.2609"
stroke="white"
stroke-linecap="round"
/>
<path
d="M2.97913 2.0192L14.0209 13.9808"
stroke="currentColor"
stroke-linecap="round"
/>
</svg>
</template>

View File

@ -0,0 +1,28 @@
<template>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.05142 5.27273H2.6926C2.3798 5.27273 2.07981 5.38766 1.85863 5.59225C1.63744 5.79683 1.51318 6.07431 1.51318 6.36364V9.63636C1.51318 9.92569 1.63744 10.2032 1.85863 10.4078C2.07981 10.6123 2.3798 10.7273 2.6926 10.7273H5.05142L9.76908 14V2L5.05142 5.27273Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M11.5 5.5C11.8143 5.81281 12.066 6.1975 12.2384 6.62854C12.4108 7.05958 12.5 7.52708 12.5 8C12.5 8.47292 12.4108 8.94042 12.2384 9.37146C12.066 9.8025 11.8143 10.1872 11.5 10.5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12.5 3C13.1286 3.62561 13.632 4.39501 13.9768 5.25708C14.3217 6.11915 14.5 7.05416 14.5 8C14.5 8.94584 14.3217 9.88085 13.9768 10.7429C13.632 11.605 13.1286 12.3744 12.5 13"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>

View File

@ -0,0 +1,22 @@
<template>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.05142 5.27273H2.6926C2.3798 5.27273 2.07981 5.38766 1.85863 5.59225C1.63744 5.79683 1.51318 6.07431 1.51318 6.36364V9.63636C1.51318 9.92569 1.63744 10.2032 1.85863 10.4078C2.07981 10.6123 2.3798 10.7273 2.6926 10.7273H5.05142L9.76908 14V2L5.05142 5.27273Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M11.5 5.5C11.8143 5.81281 12.066 6.1975 12.2384 6.62854C12.4108 7.05958 12.5 7.52708 12.5 8C12.5 8.47292 12.4108 8.94042 12.2384 9.37146C12.066 9.8025 11.8143 10.1872 11.5 10.5"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>