parent
601e0d4a6c
commit
b2a6a5d145
5 changed files with 241 additions and 11 deletions
|
|
@ -185,10 +185,8 @@ $admin-fg: white;
|
||||||
background-color: $admin-bg;
|
background-color: $admin-bg;
|
||||||
}
|
}
|
||||||
.audio-bubble {
|
.audio-bubble {
|
||||||
overflow: scroll;
|
width: 70%;
|
||||||
display: inline-block;
|
height: 50px;
|
||||||
width: fit-content;
|
|
||||||
max-width: 70%;
|
|
||||||
}
|
}
|
||||||
.bubble.image-bubble {
|
.bubble.image-bubble {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
|
@ -252,10 +250,14 @@ $admin-fg: white;
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
}
|
}
|
||||||
.audio-bubble {
|
.audio-bubble {
|
||||||
overflow: scroll;
|
background-color: #e5e5e5;
|
||||||
|
border-radius: 10px 10px 0 10px;
|
||||||
|
padding: 8px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: fit-content;
|
position: relative;
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
|
width: 70%;
|
||||||
|
height: 50px;
|
||||||
}
|
}
|
||||||
.video2-bubble {
|
.video2-bubble {
|
||||||
background-color: #e5e5e5;
|
background-color: #e5e5e5;
|
||||||
|
|
@ -384,6 +386,40 @@ $admin-fg: white;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.audio-player {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
.currentColor {
|
||||||
|
background-color: #000000 !important;
|
||||||
|
}
|
||||||
|
.v-icon {
|
||||||
|
color: black !important;
|
||||||
|
}
|
||||||
|
.from-admin & {
|
||||||
|
color: $admin-fg;
|
||||||
|
.currentColor {
|
||||||
|
background-color: #555555 !important;
|
||||||
|
}
|
||||||
|
.v-icon {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.play-time {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 13 * $chat-text-size;
|
||||||
|
}
|
||||||
|
.play-progress {
|
||||||
|
padding: 0px 10px;
|
||||||
|
.v-slider__thumb {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:hover .v-slider__thumb {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.message-operations-strut {
|
.message-operations-strut {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 0px;
|
height: 0px;
|
||||||
|
|
|
||||||
185
src/components/messages/AudioPlayer.vue
Normal file
185
src/components/messages/AudioPlayer.vue
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
<template>
|
||||||
|
<div class="audio-player d-flex flex-row" style="align-items: center">
|
||||||
|
<audio ref="player" :src="src" @durationchange="updateDuration">
|
||||||
|
<slot></slot>
|
||||||
|
</audio>
|
||||||
|
<v-btn v-if="playing" @click.stop="pause" icon
|
||||||
|
><v-icon size="20">pause</v-icon></v-btn
|
||||||
|
>
|
||||||
|
<v-btn v-else @click.stop="play" icon
|
||||||
|
><v-icon size="20">play_arrow</v-icon></v-btn
|
||||||
|
>
|
||||||
|
<div class="play-time" style="flex: 1 1 80px">
|
||||||
|
{{ currentTime }} / {{ totalTime }}
|
||||||
|
</div>
|
||||||
|
<v-slider
|
||||||
|
color="currentColor"
|
||||||
|
track-color="#cccccc"
|
||||||
|
class="play-progress"
|
||||||
|
v-model="playheadPercent"
|
||||||
|
style="flex: 1 1 100%; height: 30px"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import util from "../../plugins/utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
src: {
|
||||||
|
type: String,
|
||||||
|
default: function () {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
player: null,
|
||||||
|
duration: 0,
|
||||||
|
playPercent: 0,
|
||||||
|
playTime: 0,
|
||||||
|
playing: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.player = this.$refs.player;
|
||||||
|
this.player.addEventListener("timeupdate", this.updateProgressBar);
|
||||||
|
this.player.addEventListener("play", () => {
|
||||||
|
this.playing = true;
|
||||||
|
});
|
||||||
|
this.player.addEventListener("pause", () => {
|
||||||
|
this.playing = false;
|
||||||
|
});
|
||||||
|
this.player.addEventListener("ended", function () {
|
||||||
|
this.pause();
|
||||||
|
this.playing = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentTime() {
|
||||||
|
return util.formatDuration(this.playTime);
|
||||||
|
},
|
||||||
|
totalTime() {
|
||||||
|
return util.formatDuration(this.duration);
|
||||||
|
},
|
||||||
|
playheadPercent: {
|
||||||
|
get: function () {
|
||||||
|
return this.playPercent;
|
||||||
|
},
|
||||||
|
set: function (percent) {
|
||||||
|
if (this.player.src) {
|
||||||
|
this.playPercent = percent;
|
||||||
|
this.player.currentTime = (percent / 100) * this.player.duration;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
play() {
|
||||||
|
if (this.player.src) {
|
||||||
|
if (this.player.paused) {
|
||||||
|
this.player.play();
|
||||||
|
} else if (this.player.ended) {
|
||||||
|
// restart
|
||||||
|
this.player.currentTime = 0;
|
||||||
|
this.player.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pause() {
|
||||||
|
if (this.player.src) {
|
||||||
|
this.player.pause();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateProgressBar() {
|
||||||
|
if (this.player.duration > 0) {
|
||||||
|
this.playPercent = Math.floor(
|
||||||
|
(100 / this.player.duration) * this.player.currentTime
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.playPercent = 0;
|
||||||
|
}
|
||||||
|
this.playTime = 1000 * this.player.currentTime;
|
||||||
|
},
|
||||||
|
updateDuration() {
|
||||||
|
this.duration = 1000 * this.player.duration;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "@/assets/css/chat.scss";
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function playPauseAudio() {
|
||||||
|
if (player.src) {
|
||||||
|
if (player.paused || player.ended) {
|
||||||
|
// Change the button to a pause button
|
||||||
|
changeButtonType(btnPlayPause, 'pause');
|
||||||
|
player.play();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Change the button to a play button
|
||||||
|
changeButtonType(btnPlayPause, 'play');
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the current media from playing, and return it to the start position
|
||||||
|
function stopAudio() {
|
||||||
|
if (player.src) {
|
||||||
|
player.pause();
|
||||||
|
if (player.currentTime) player.currentTime = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggles the media player's mute and unmute status
|
||||||
|
function muteVolume() {
|
||||||
|
if (player.src) {
|
||||||
|
if (player.muted) {
|
||||||
|
// Change the button to a mute button
|
||||||
|
changeButtonType(btnMute, 'mute');
|
||||||
|
player.muted = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Change the button to an unmute button
|
||||||
|
changeButtonType(btnMute, 'unmute');
|
||||||
|
player.muted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replays the media currently loaded in the player
|
||||||
|
function replayAudio() {
|
||||||
|
if (player.src) {
|
||||||
|
resetPlayer();
|
||||||
|
player.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Updates a button's title, innerHTML and CSS class
|
||||||
|
function changeButtonType(btn, value) {
|
||||||
|
btn.title = value;
|
||||||
|
btn.innerHTML = value;
|
||||||
|
btn.className = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetPlayer() {
|
||||||
|
progressBar.value = 0;
|
||||||
|
//clear the current song
|
||||||
|
player.src = '';
|
||||||
|
// Move the media back to the start
|
||||||
|
player.currentTime = 0;
|
||||||
|
// Set the play/pause button to 'play'
|
||||||
|
changeButtonType(btnPlayPause, 'play');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<message-incoming v-bind="{...$props, ...$attrs}">
|
<message-incoming v-bind="{...$props, ...$attrs}">
|
||||||
<div class="audio-bubble">
|
<div class="bubble audio-bubble">
|
||||||
<audio controls :src="src">Audio file</audio>
|
<audio-player :src="src">Audio file</audio-player>
|
||||||
</div>
|
</div>
|
||||||
</message-incoming>
|
</message-incoming>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -9,11 +9,12 @@
|
||||||
<script>
|
<script>
|
||||||
import attachmentMixin from "./attachmentMixin";
|
import attachmentMixin from "./attachmentMixin";
|
||||||
import MessageIncoming from './MessageIncoming.vue';
|
import MessageIncoming from './MessageIncoming.vue';
|
||||||
|
import AudioPlayer from './AudioPlayer.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
extends: MessageIncoming,
|
extends: MessageIncoming,
|
||||||
mixins: [attachmentMixin],
|
mixins: [attachmentMixin],
|
||||||
components: { MessageIncoming }
|
components: { MessageIncoming, AudioPlayer }
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<message-outgoing v-bind="{ ...$props, ...$attrs }">
|
<message-outgoing v-bind="{ ...$props, ...$attrs }">
|
||||||
<div class="audio-bubble">
|
<div class="audio-bubble">
|
||||||
<audio controls :src="src">Audio file</audio>
|
<audio-player :src="src">Audio file</audio-player>
|
||||||
</div>
|
</div>
|
||||||
</message-outgoing>
|
</message-outgoing>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import attachmentMixin from "./attachmentMixin";
|
import attachmentMixin from "./attachmentMixin";
|
||||||
|
import AudioPlayer from './AudioPlayer.vue';
|
||||||
import MessageOutgoing from "./MessageOutgoing.vue";
|
import MessageOutgoing from "./MessageOutgoing.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
extends: MessageOutgoing,
|
extends: MessageOutgoing,
|
||||||
components: { MessageOutgoing },
|
components: { MessageOutgoing, AudioPlayer },
|
||||||
mixins: [attachmentMixin],
|
mixins: [attachmentMixin],
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -565,6 +565,13 @@ class Util {
|
||||||
return dayjs.duration(ms).format("HH:mm:ss");
|
return dayjs.duration(ms).format("HH:mm:ss");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatDuration(ms) {
|
||||||
|
if (ms >= (60 * 60000)) {
|
||||||
|
return dayjs.duration(ms).format("H:mm:ss");
|
||||||
|
}
|
||||||
|
return dayjs.duration(ms).format("m:ss");
|
||||||
|
}
|
||||||
|
|
||||||
formatRecordStartTime(timestamp) {
|
formatRecordStartTime(timestamp) {
|
||||||
var then = dayjs(timestamp);
|
var then = dayjs(timestamp);
|
||||||
return then.format('lll');
|
return then.format('lll');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue