feat: add audio chip

This commit is contained in:
Shivam Mishra
2024-11-28 17:11:24 +05:30
parent a565ae8cba
commit bcd18a0bb0
2 changed files with 136 additions and 0 deletions
@@ -5,6 +5,8 @@ import { useMessageContext } from '../provider.js';
import BaseBubble from 'next/message/bubbles/Base.vue';
import ImageChip from 'next/message/chips/Image.vue';
import VideoChip from 'next/message/chips/Video.vue';
import AudioChip from 'next/message/chips/Audio.vue';
import MessageFormatter from 'shared/helpers/MessageFormatter.js';
import { MESSAGE_VARIANTS, ATTACHMENT_TYPES } from '../constants';
/**
@@ -52,6 +54,12 @@ const mediaAttachments = computed(() => {
allowedTypes.indexOf(a.fileType) - allowedTypes.indexOf(b.fileType)
);
});
const recordings = computed(() => {
return props.attachments.filter(
attachment => attachment.fileType === ATTACHMENT_TYPES.AUDIO
);
});
</script>
<template>
@@ -69,6 +77,13 @@ const mediaAttachments = computed(() => {
/>
</template>
</div>
<div v-if="recordings.length" class="mt-[10px] flex gap-[10px]">
<AudioChip
v-for="attachment in recordings"
:key="attachment.id"
:attachment="attachment"
/>
</div>
</BaseBubble>
</template>
@@ -0,0 +1,121 @@
<script setup>
import { computed, useTemplateRef, ref } from 'vue';
import Icon from 'next/icon/Icon.vue';
import { timeStampAppendedURL } from 'dashboard/helper/URLHelper';
const { attachment } = defineProps({
attachment: {
type: Object,
required: true,
},
});
const timeStampURL = computed(() => {
return timeStampAppendedURL(attachment.dataUrl);
});
const audioPlayer = useTemplateRef('audioPlayer');
const isPlaying = ref(false);
const isMuted = ref(false);
const currentTime = ref(0);
const duration = ref(0);
const onLoadedMetadata = () => {
duration.value = audioPlayer.value.duration;
};
const formatTime = time => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
const toggleMute = () => {
audioPlayer.value.muted = !audioPlayer.value.muted;
isMuted.value = audioPlayer.value.muted;
};
const onTimeUpdate = () => {
currentTime.value = audioPlayer.value.currentTime;
};
const seek = event => {
const time = Number(event.target.value);
audioPlayer.value.currentTime = time;
currentTime.value = time;
};
const playOrPause = () => {
if (isPlaying.value) {
audioPlayer.value.pause();
isPlaying.value = false;
} else {
audioPlayer.value.play();
isPlaying.value = true;
}
};
const onEnd = () => {
isPlaying.value = false;
currentTime.value = 0;
};
const downloadAudio = () => {
const anchor = document.createElement('a');
anchor.href = timeStampURL.value;
anchor.download = timeStampURL.value.split('/').pop() || 'audio';
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
};
</script>
<template>
<audio
ref="audioPlayer"
controls
class="hidden"
@loadedmetadata="onLoadedMetadata"
@timeupdate="onTimeUpdate"
@ended="onEnd"
>
<source :src="timeStampURL" />
</audio>
<div class="rounded-lg w-full h-8 bg-n-alpha-3 gap-1 flex items-center">
<button class="p-0 border-0 size-8" @click="playOrPause">
<Icon
v-if="isPlaying"
class="size-8"
icon="i-teenyicons-pause-small-solid"
/>
<Icon v-else class="size-8" icon="i-teenyicons-play-small-solid" />
</button>
<div class="tabular-nums text-xs">
{{ formatTime(currentTime) }} / {{ formatTime(duration) }}
</div>
<div class="flex items-center px-2">
<input
type="range"
min="0"
:max="duration"
:value="currentTime"
class="w-full h-1 bg-n-slate-10 rounded-lg appearance-none cursor-pointer accent-n-slate-12"
@input="seek"
/>
</div>
<button
class="p-0 border-0 size-8 grid place-content-center"
@click="toggleMute"
>
<Icon v-if="isMuted" class="size-4" icon="i-teenyicons-sound-off-solid" />
<Icon v-else class="size-4" icon="i-teenyicons-sound-on-solid" />
</button>
<button
class="p-0 border-0 size-8 grid place-content-center"
@click="downloadAudio"
>
<Icon class="size-4" icon="i-lucide-download" />
</button>
</div>
</template>