Vuejs plays sound periodically - javascript

This code plays a sound when I refresh the browser, but I expect it to play the sound every 15 seconds. How can I fix this problem?
When the sound is played every time I refresh, it means that fetching data from the database is working and it will play the updated sound correctly. However, the problem is that it should update periodically, not just when the user clicks refresh
Parent
<template>
<div>
<CommunicateVoice v-if="filenames.value && filenames.value[0]" :files="filenames.value[0]"/>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from "vue";
import axios from "axios";
import CommunicateVoice from './CommunicateVoice.vue';
const lands = ref([]);
const filenames = ref([]);
onMounted(async () => {
const fetchData = async () => {
const res = await axios.get("https://koh-abx.com:50100/onboardlands");
lands.value = res.data;
filenames.value = computed(() => {
return lands.value.map(item => {
const digits = item.numbershow.toString().split('');
return digits.map(digit => `https://koh-abx.com/sound/${digit}.mp3`);
});
});
};
fetchData();
setInterval(fetchData, 15000);
});
</script>
Child
<template>
<div>
<audio ref="audioEl" />
</div>
</template>
<script>
import { onMounted, ref } from 'vue';
export default {
props: {
files: {
type: Array,
required: true,
},
},
setup(props) {
const audioEl = ref(null);
const currentFileIndex = ref(0);
onMounted(() => {
audioEl.value = new Audio();
audioEl.value.addEventListener("ended", playNextFile);
document.body.appendChild(audioEl.value);
audioEl.value.src = props.files[currentFileIndex.value];
audioEl.value.play();
});
function playNextFile() {
currentFileIndex.value += 1;
if (currentFileIndex.value === props.files.length) {
document.body.removeChild(audioEl.value);
return;
}
audioEl.value.src = props.files[currentFileIndex.value];
audioEl.value.play();
}
return {
audioEl,
playNextFile,
};
},
};
</script>

Try appending something like a timestamp to your endpoint, this should avoid the cache since the URL would be unique, allowing the browser to fetch updated sounds every 15s without refreshing.
https://koh-abx.com/sound/${digit}.mp3?t=${+new Date()}

Related

How to stop the rerender in videojs when the state update in react

I get the code from https://videojs.com/guides/react/.
If I update the state, my video rerender and the video start playing from first, how to solve.
Example code:
Videojs code:
import React from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';
export const VideoJS = (props) => {
const videoRef = React.useRef(null);
const playerRef = React.useRef(null);
const {options, onReady} = props;
React.useEffect(() => {
// Make sure Video.js player is only initialized once
if (!playerRef.current) {
// The Video.js player needs to be _inside_ the component el for React 18 Strict Mode.
const videoElement = document.createElement("video-js");
videoElement.classList.add('vjs-big-play-centered');
videoRef.current.appendChild(videoElement);
const player = playerRef.current = videojs(videoElement, options, () => {
videojs.log('player is ready');
onReady && onReady(player);
});
// You could update an existing player in the `else` block here
// on prop change, for example:
} else {
const player = playerRef.current;
player.autoplay(options.autoplay);
player.src(options.sources);
}
}, [options, videoRef]);
// Dispose the Video.js player when the functional component unmounts
React.useEffect(() => {
const player = playerRef.current;
return () => {
if (player && !player.isDisposed()) {
player.dispose();
playerRef.current = null;
}
};
}, [playerRef]);
return (
<div data-vjs-player>
<div ref={videoRef} />
</div>
);
}
export default VideoJS;
App.js
import React from 'react';
// This imports the functional component from the previous sample.
import VideoJS from './VideoJS'
const App = () => {
const playerRef = React.useRef(null);
const [timestamp,setTimestamp]= useState(0)
const videoJsOptions = {
autoplay: true,
controls: true,
responsive: true,
fluid: true,
sources: [{
src: '/path/to/video.mp4',
type: 'video/mp4'
}]
};
const handlePlayerReady = (player) => {
playerRef.current = player;
// You can handle player events here, for example:
player.on('waiting', () => {
videojs.log('player is waiting');
});
player.on('dispose', () => {
videojs.log('player will dispose');
});
player.on('timeupdate', function(){
setTimestamp (player.currentTime())
});
};
return (
<>
<div>Rest of app here</div>
<VideoJS options={videoJsOptions} onReady={handlePlayerReady} />
<div>Rest of app here</div>
</>
);
}
In the App.js, I update the timestamp in timeupdate listener, I get rerender and video again start playing from first.
Please help me to solve
The video is re-rendering because of the onReady(player) in the Videojs file. onReady is prop which is coming to VideoJS from the App.js file which is handlePlayerReady.
When you try to set the state of timestamp on the App.js file with player.on('timeupdate') function the function runs and the prop value goes to Video js and then it again re-renders because the Videojs is wrapped in useEffect. So instead of passing the data from App.js i did changed some codes.
VideoJS
import React from "react";
import videojs from "video.js";
import "video.js/dist/video-js.css";
export const VideoJS = (props) => {
const videoRef = React.useRef(null);
const playerRef = React.useRef(null);
const { options, setTimestamp1 } = props;
React.useEffect(() => {
// Make sure Video.js player is only initialized once
if (!playerRef.current) {
// The Video.js player needs to be _inside_ the component el for React 18 Strict Mode.
const videoElement = document.createElement("video-js");
videoElement.classList.add("vjs-big-play-centered");
videoRef.current.appendChild(videoElement);
const player = (playerRef.current = videojs(videoElement, options, () => {
player.on("waiting", () => {
videojs.log("player is waiting");
});
player.on("dispose", () => {
videojs.log("player will dispose");
});
player.on("timeupdate", () => {
setTimestamp1(player.currentTime());
});
}));
// You could update an existing player in the `else` block here
// on prop change, for example:
} else {
const player = playerRef.current;
player.autoplay(options.autoplay);
player.src(options.sources);
}
}, []);
// Dispose the Video.js player when the functional component unmounts
React.useEffect(() => {
const player = playerRef.current;
return () => {
if (player && !player.isDisposed()) {
player.dispose();
playerRef.current = null;
}
};
}, [playerRef]);
return (
<div data-vjs-player>
<div ref={videoRef} />
</div>
);
};
export default VideoJS;
App.js
import React,{ useState, useEffect} from 'react';
// This imports the functional component from the previous sample.
import VideoJS from './VideoJS'
const App = () => {
const playerRef = React.useRef(null);
const [timestamp1,setTimestamp1]= useState(null);
const videoJsOptions = {
autoplay: true,
controls: true,
responsive: true,
fluid: true,
sources: [{
src: 'dandelions.mp4',
type: 'video/mp4'
}]
};
return (
<>
<div>{timestamp1}</div>
<VideoJS options={videoJsOptions} setTimestamp1={setTimestamp1}/>
<div>{timestamp1}</div>
</>
);
}
export default App
here you can see i removed onReady function and put the data directly into VideoJs where OnReady was rendering and i am sending the setTimestamp as a prop and changing the value from VideoJs file. So re rendering problem is solved and you can use state value in App.js.

Can't delay using data in Vue for after getting value from database

I need to compare string that I get from firebase in document.check1 with some strings (written hard in function below) and show Content. I know how to call it out in button but I want to check it right after entering the page - not after clicking. When I try to do it - I get error that it has no value. How can I make it "wait" for the data to collect automaticaly?
<template>
<router-link to="/konto">Back</router-link>
<div v-if="document">
<div>
<span>1:</span>
{{ document.check1 }},
<span>2:</span>
{{ document.check2 }},
<span>3:</span>
{{ document.check3.length }}
</div>
</div>
<button v-if="itWorkOk" #click="documentCheck">Show Content after finding result</button>
<div v-if="isOther">
<p>Content</p>
</div>
</template>
<script>
import getUser from "../composables/getUser";
import getDocument from "../composables/getDocument";
import { ref } from "#vue/reactivity";
export default {
props: ["id", "document"],
setup(props) {
const { error, document } = getDocument("AllData", props.id);
const { user } = getUser();
const itWorkOk = ref(true);
const result1 = ref("");
const isOther = ref("");
const documentCheck = async () => {
const isItOk = document.value.check1
if (isItOk == "Result One") {
result1.value = true;
itWorkOk.value = false;
} else {
isOther.value = true;
itWorkOk.value = false;
}
};
return {
error, user, document, documentCheck, result1, isOther, itWorkOk,
};
},
};
</script>
The error (when I put function to call immediately):
Uncaught (in promise) TypeError: document.value is null
The getDocument code:
import { ref, watchEffect } from 'vue'
import { projectFirestore } from '../firebase/config'
const getDocument = (collection, id) => {
const document = ref(null)
const error = ref(null)
let documentRef = projectFirestore.collection(collection).doc(id)
const unsub = documentRef.onSnapshot(doc => {
if(doc.data()) {
document.value = {...doc.data(), id: doc.id}
error.value = null
} else {
error.value = "Document does not exist"
}
}, err => {
console.log(err.message)
error.value = 'Couldn't get the document'
})
watchEffect((onInvalidate) => {
onInvalidate(() => unsub());
});
return { error, document }
}
export default getDocument
If I correctly understand your question, you need to fetch the Firestore data in one of the lifecycle hooks before the component is mounted, for example created or mounted.
In the lifecycle hook, you asynchronously read the data from Firestore and compare the result to the desired values.
I used setTimeout(function () { methotToCall(); }, 1000); thanks to this thread - stackoverflow.com/questions/24849/… and it "worked" so I'm gonna close this one. I'm sure this is not the right method, but it will work for a time. Thank's for help :)

vue3 - can't render facebook comments

I am trying to add a Facebook comments plugin (check it here) to my vue app, the problem is that div is created in DOM but it sometimes shows, sometimes not(width: 0, height 0)
Note: I am calling XFBML.parse function, my host is added to fb app
This is my current code:
<template>
<div
ref="commentContainer"
class="fb-comments"
:data-href="onUrl()"
:data-width="cwidth"
:data-numposts="numposts"
></div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, watch } from "vue";
import router from "../../router";
export default defineComponent({
props: {
cwidth: {
type: String,
default: "100%",
},
numposts: {
type: String,
default: "2",
},
},
setup({ cwidth, numposts }) {
const commentContainer = ref(null);
const init = () => {
if (
window.FB &&
!commentContainer.value.hasAttribute("fb-xfbml-state")
) {
setTimeout(() => {
window.FB.XFBML.parse(commentContainer.value.parentElement);
}, 2000);
}
};
onMounted(() => {
setTimeout(() => {
init();
}, 1500);
});
const onUrl = () => {
return document.location.origin + document.location.pathname;
};
watch(
() => router.currentRoute.value,
() => {
init();
}
);
return { cwidth, numposts, commentContainer, onUrl };
},
});
</script>
Instead of doing setTimeout try using nextTick and not passing any params to the parse function.
E.g. in the mounted function
this.$nextTick(() => {
window.FB.XFBML.parse()
})
Are you waiting 1.5s before running init() for a reason?
The above works using Vue2, for Vue3 example see below:
import { createApp, nextTick } from 'vue'
const app = createApp({
setup() {
const init = async () => {
await nextTick()
window.FB.XFBML.parse()
}
}
})
https://v3.vuejs.org/api/global-api.html#nexttick
Also, make sure you have added the SDK script and provided fb-root div to your index.html. It would not work on mine unless I added these just before the closing </body> tag.
I also had to add the the nextTick code to the route watcher to force the window to parse FB again when a new page is navigated to. I'm unsure of the Vue 3 version but I'm sure you can figure it out from this example:
watch: {
$route (to, from) {
if (to.fullPath !== from.fullPath) {
this.$nextTick(() => {
window.FB.XFBML.parse()
})
}
}
}

how to use many sounds using useSound - React Js

Sorry if the title is a bit confusing. Basically I want to play a sound if a user clicks a key, and if they click again it will play another sound.
The setup could look like.
import useSound from 'use-sound';
const Home = () => {
const soundList = [assets.sounds.click1,assets.sounds.click2]
const [play] = useSound(soundList);// this seems to take one argument.
function onUserInputChange(e){
play(// random choice from soundList)
}
}
How might I be able to pass and argument into play to play audio?
You can pass from the parent url of the sound to the child and modify parent's state after click:
import useSound from 'use-sound';
import { useState } from 'react';
const soundList = [assets.sounds.click1, assets.sounds.click2];
const Home = () => {
const [soundToPlay, setSoundToPlay] = useState(soundList[0]);
const onPlay = () => {
setSoundToPlay(soundList[Math.round(Math.random() * soundList.length)]);
};
return <PlayComponent soundUrl={soundToPlay} onPlay={onPlay} />;
};
const PlayComponent = ({ soundUrl, onPlay }) => {
const [play] = useSound(soundUrl);
function onUserInputChange(e) {
play();
onPlay();
}
return <button onClick={onUserInputChange}>Play</button>;
};
*NOTE
I guess you wanted to put assets.sounds.click2 instead of assets.sounds.click1 as a second array item

Why does copies of the same API call returned undefined for one function and works for the other

While trying to use TMDB API in my project I ran into an issue that I am unable to figure out. I use copies of the same code as shown below in two different files and functions - one works, and the other one returned undefined for some reason. Can you please point out what I am not doing right, I need fresh new eyes on this. Thank you
import Head from 'next/head';
import React from 'react';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import styles from '../styles/Home.module.css';
export const getServerSideProps = async () => {
const movieApi = process.env.TMDB_API_KEY;
const res = await fetch(`https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=${movieApi}&page=1`);
const movie_data = await res.json();
return {
props: {
movies : movie_data
},
}
}
const Form = ({movies}) => {
console.log(movies); //returns "Undefined"
const [search, Setsearch] = useState("");
//Handle input value
const getLocation = async (e) => {
// console.log(e.target.value)
e.preventDefault();
}
//Handle Submit
const handleSubmit = (event) =>{
// console.log("clicked")
event.preventDefault();
}
export const getServerSideProps = async () => {
const movieApi = process.env.TMDB_API_KEY;
const res = await fetch(`https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=${movieApi}&page=1`);
const movie_data = await res.json();
return {
props: {
movies : movie_data
},
}
}
export default function Home({movies}) {
console.log(movies); //works perdectly
const [session, loading] = useSession();
const tmdbMpviesResults = movies.results
As per your comment, <Form /> is not a page. Exactly that is your problem:
getServerSideProps can only be exported from a page. You can’t export it from non-page files.

Categories