Im having no problem running audio but im having trouble pausing it. If anyone can help that would be great.
async function playMusic() {
const soundObject = new Audio.Sound();
console.log("Being hit")
try {
await soundObject.loadAsync({uri:'https://firebasestorage.googleapis.com/v0/b/o/myFolder%2FMello%20C%20-%20Easy.mp3?c4e-4bf8-b8ea-dcc201efff44'});
await soundObject.playAsync();
} catch (error) {
alert("Error" + error.message)
}
}
async function stopMusic() {
console.log("Not Being hit")
const soundObject = new Audio.Sound();
try {
await soundObject.loadAsync({uri:'https://firebasestorage.googleapis.com/v03?alt=media&token=e44f7b2f-8c4e-4bf8-b8ea-dcc201efff44'});
await soundObject.stopAsync();
} catch (error) {
alert("Error" + error.message)
}
}
See we should never create Sound instance as a state variable. Because whenever there is a re-render, it re-initializes the sound instance.
You should create a ref variable and use it,
const sound = React.useRef(new Audio.Sound());
Working Example
Implementation of Pause/Play music
import * as React from 'react';
import {
Text,
View,
StyleSheet,
ActivityIndicator,
Button,
} from 'react-native';
import Constants from 'expo-constants';
import { Audio } from 'expo-av';
const SampleTrack = require('./Roses.m4a');
export default function App() {
const [Loaded, SetLoaded] = React.useState(false);
const [Loading, SetLoading] = React.useState(false);
const sound = React.useRef(new Audio.Sound());
React.useEffect(() => {
LoadAudio();
}, []);
const PlayAudio = async () => {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === false) {
sound.current.playAsync();
}
}
} catch (error) {}
};
const PauseAudio = async () => {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === true) {
sound.current.pauseAsync();
}
}
} catch (error) {}
};
const LoadAudio = async () => {
SetLoading(true);
const checkLoading = await sound.current.getStatusAsync();
if (checkLoading.isLoaded === false) {
try {
const result = await sound.current.loadAsync(SampleTrack, {}, true);
if (result.isLoaded === false) {
SetLoading(false);
console.log('Error in Loading Audio');
} else {
SetLoading(false);
SetLoaded(true);
}
} catch (error) {
console.log(error);
SetLoading(false);
}
} else {
SetLoading(false);
}
};
return (
<View style={styles.container}>
<View style={styles.AudioPLayer}>
{Loading ? (
<ActivityIndicator size={'small'} color={'red'} />
) : (
<>
{Loaded === false ? (
<>
<ActivityIndicator />
<Text>Loading Song</Text>{' '}
</>
) : (
<>
<Button title="Play Song" onPress={PlayAudio} />
<Button title="Pause Song" onPress={PauseAudio} />
</>
)}
</>
)}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
AudioPLayer: {
width: '100%',
height: 50,
alignItems: 'center',
},
});
Related
I'm working on a React Native app and scheduling a notification, the notification arrive properly, even when the app is closed/killed. But addNotificationResponseReceivedListener() function only executes the logic when the app is in the foreground or background, but when the app is closed it only opens the app.
For now I'm using this dummy app to emulate what is happening in my app.
This is the code I have in the App.js
import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';
import React, { useState, useEffect, useRef } from 'react';
import { Text, View, Button, Platform } from 'react-native';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
export default function App() {
const [expoPushToken, setExpoPushToken] = useState('');
const [notification, setNotification] = useState(false);
const notificationListener = useRef();
const responseListener = useRef();
const lastNotificationResponse = Notifications.useLastNotificationResponse();
React.useEffect(() => {
if (
lastNotificationResponse &&
lastNotificationResponse.notification.request.content.data.url &&
lastNotificationResponse.actionIdentifier === Notifications.DEFAULT_ACTION_IDENTIFIER
) {
Linking.openURL(lastNotificationResponse.notification.request.content.data.url);
}
}, [lastNotificationResponse]);
useEffect(() => {
registerForPushNotificationsAsync().then(token => setExpoPushToken(token));
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
setNotification(notification);
});
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
setNotification(response);
console.log(response, 'hemos tapped');
});
return () => {
Notifications.removeNotificationSubscription(notificationListener.current);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
console.log(lastNotificationResponse);
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'space-around',
}}>
<Text>Your expo push token: {expoPushToken}</Text>
<View style={{ alignItems: 'center', justifyContent: 'center' }}>
</View>
<Button
title="Press to schedule a notification"
onPress={async () => {
await schedulePushNotification();
}}
/>
</View>
);
}
async function schedulePushNotification() {
await Notifications.scheduleNotificationAsync({
content: {
title: "You've got mail! 📬",
body: 'Here is the notification body',
data: { data: 'goes here' },
},
trigger: { seconds: 5 },
});
}
async function registerForPushNotificationsAsync() {
let token;
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
console.log(token);
} else {
alert('Must use physical device for Push Notifications');
}
return token;
}
Any idea what's causing this?
Im having trouble trying to stop the current playing sound when another Sound is pressed without having to pause the current Sound in react Native Expo. Note: I can pause and play the Sound perfectly. So basically like a normal music player if that is overlapping when pressing another song.
const SampleTrack = {uri:'https://firebasestorage.googleapisro-b97ec.appspot.com/o/song%201.mp3?a9'}
const FirstP = () => {
const [Loaded, SetLoaded] = React.useState(false);
const [Loading, SetLoading] = React.useState(false);
const sound = React.useRef(new Audio.Sound());
React.useEffect(() => {
LoadAudio();
}, []);
const PlayAudio = async () => {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === false) {
sound.current.playAsync();
}
}
} catch (error) {}
};
const PauseAudio = async () => {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === true) {
sound.current.pauseAsync();
}
}
} catch (error) {}
};
const LoadAudio = async () => {
SetLoading(true);
const checkLoading = await sound.current.getStatusAsync();
if (checkLoading.isLoaded === false) {
try {
const result = await sound.current.loadAsync(SampleTrack, {}, true);
if (result.isLoaded === false) {
SetLoading(false);
console.log('Error in Loading Audio');
} else {
SetLoading(false);
SetLoaded(true);
}
} catch (error) {
console.log(error);
SetLoading(false);
}
} else {
SetLoading(false);
}
};
if anyone can help that would be great.
Here's a complete Audio Player with Play/Pause/Load/Next/Previous feature.
Working Example
import * as React from 'react';
import {
Text,
View,
StyleSheet,
ActivityIndicator,
Button,
} from 'react-native';
import Constants from 'expo-constants';
import { Audio } from 'expo-av';
const Tracks = [
{
id: 0,
track: require('./Roses.m4a'),
},
{
id: 1,
track: require('./Poison.m4a'),
},
{
id: 2,
track: require('./Alone.m4a'),
},
];
export default function App() {
const [Loaded, SetLoaded] = React.useState(false);
const [Loading, SetLoading] = React.useState(false);
const [CurrentSong, SetCurrentSong] = React.useState(Tracks[0]);
const sound = React.useRef(new Audio.Sound());
React.useEffect(() => {
LoadAudio();
return () => Unload();
}, [CurrentSong]);
const Unload = async () => {
await sound.current.unloadAsync();
};
const PlayAudio = async () => {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === false) {
sound.current.playAsync();
}
}
} catch (error) {
console.log(error);
}
};
const PauseAudio = async () => {
try {
const result = await sound.current.getStatusAsync();
if (result.isLoaded) {
if (result.isPlaying === true) {
sound.current.pauseAsync();
}
}
} catch (error) {
console.log(error);
}
};
const LoadAudio = async () => {
SetLoaded(false);
SetLoading(true);
const checkLoading = await sound.current.getStatusAsync();
if (checkLoading.isLoaded === false) {
try {
const result = await sound.current.loadAsync(
CurrentSong.track,
{},
true
);
if (result.isLoaded === false) {
SetLoading(false);
console.log('Error in Loading Audio');
} else {
SetLoading(false);
PlayAudio();
SetLoaded(true);
}
} catch (error) {
console.log(error);
SetLoading(false);
}
} else {
SetLoading(false);
}
};
const NextSong = () => {
if (CurrentSong.id === Tracks[Tracks.length - 1].id) {
SetCurrentSong(Tracks[0]);
} else {
SetCurrentSong(Tracks[CurrentSong.id + 1]);
}
};
const PrevSong = () => {
if (CurrentSong.id === 0) {
SetCurrentSong(Tracks[Tracks.length - 1]);
} else {
SetCurrentSong(Tracks[CurrentSong.id - 1]);
}
};
return (
<View style={styles.container}>
<View style={styles.AudioPLayer}>
{Loading === true ? (
<ActivityIndicator size={'small'} color={'red'} />
) : (
<>
<Button title="Play Song" onPress={PlayAudio} />
<Button title="Pause Song" onPress={PauseAudio} />
<Text>Currently Playing : {CurrentSong.id}</Text>
<Button title="Next Song" onPress={NextSong} />
<Button title="Previous Song" onPress={PrevSong} />
</>
)}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
AudioPLayer: {
width: '100%',
height: 50,
alignItems: 'center',
},
});
I ran into this issue of Invalid hook call. The AltIconButton is a component that I place in the export default function with redirect={GoogleLogin}
Here is my login.js snippet:
const AltIconButton = (props) => {
console.log(props.name);
return (
<TouchableOpacity activeOpacity={0.5} onPress={props.redirect}>
<MaterialCommunityIcons
style={{
marginHorizontal: 15,
}}
name={props.name}
size={48}
color="white"
/>
</TouchableOpacity>
);
};
Then this is my google_login:
function GoogleLogin() {
const navigation = useNavigation();
const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
expoClientId: Constants.manifest.extra.google.WEB_CLIENT_ID,
});
useEffect(() => {
if (response?.type === "success") {
const { id_token } = response.params;
const credential = Firebase.auth.GoogleAuthProvider.credential(id_token);
Firebase.auth()
.signInWithCredential(credential)
.then(() => {
navigation.replace("Home");
});
}
}, [response]);
return;
}
EDIT:
This is another snippet of where I implement my AltIconButton Component
<View style={styles.bottomBody}>
<AltIconButton name="facebook" redirect={FBLogin}></AltIconButton>
<AltIconButton name="google"redirect={GoogleLogin}></AltIconButton>
</View>
By changing the JS function into its own component solve the problem.
For example:
function GoogleLogin() {
const navigation = useNavigation();
const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
expoClientId: Constants.manifest.extra.google.WEB_CLIENT_ID,
});
useEffect(() => {
if (response?.type === "success") {
const { id_token } = response.params;
const credential = Firebase.auth.GoogleAuthProvider.credential(id_token);
Firebase.auth()
.signInWithCredential(credential)
.then(() => {
navigation.replace("Home");
});
}
}, [response]);
return (
<TouchableOpacity
disabled={!request}
activeOpacity={0.5}
onPress={() => promptAsync()}
/>
);
}
I have a .map() function with JSX code inside. Although, the JSX is not rendering. It is only rendering after I save the file. I am using expo (React Native).
Here is my code:
import React, { useEffect, useState } from "react";
import * as SecureStore from "expo-secure-store";
import { View, Text, ActivityIndicator } from "react-native";
import { Button } from "react-native-elements";
const Receipts = ({ navigation }) => {
const [receipts, setReceipts] = useState([]);
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
const [keys, setKeys] = useState([]);
useEffect(() => {
const getReceiptsData = async () => {
let token = await SecureStore.getItemAsync("token");
console.log(token);
fetch("https://notrealapi/api/receipts", {
method: "GET",
headers: {
Authorization: `JWT ${JSON.parse(token)}`,
},
})
.then((res) => res.json())
.then((json) => {
setReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData();
processReceipts();
}, []);
const processReceipts = () => {
const dubps = [];
const resultObj = {};
receipts.map((item) => {
if (dubps.includes(item.merchant_name)) {
resultObj[item.merchant_name] =
resultObj[item.merchant_name] + parseFloat(item.total);
} else {
resultObj[item.merchant_name] = parseFloat(item.total);
dubps.push(item.merchant_name);
}
});
setResult(resultObj);
setKeys(Object.keys(resultObj));
};
const exportReport = async () => {
let token = await SecureStore.getItemAsync("token");
fetch("https://notrealapi/api/export", {
method: "GET",
headers: {
Authorization: `JWT ${JSON.parse(token)}`,
},
})
.then((res) => res.json())
.then((json) => {
console.log(json);
})
.catch((error) => console.error(error));
};
const renderSummary = () => {
return keys.map((key) => {
return (
<View>
<Text
key={key}
style={{
fontSize: 15,
fontWeight: "normal",
paddingBottom: 50,
}}
>
{`You have spent $${result[key].toString()} at ${key}`}
</Text>
</View>
);
});
};
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
{loading ? (
<ActivityIndicator size="large" color="blue" />
) : (
<>
<Text style={{ fontSize: 30, fontWeight: "bold", paddingBottom: 50 }}>
Summary:
</Text>
{renderSummary()}
<Button
type="outline"
title="Export detailed report"
onPress={exportReport}
/>
<Text style={{ fontSize: 10, marginTop: 10 }}>
*The detailed report shall be sent by email.
</Text>
</>
)}
</View>
);
};
export default Receipts;
Note: It does work but only when I save the file and it refreshes using expo CLI. Also, error occurs in the renderSummary() function.
Update: keys can be equal to ["Costco"] and result can be equal to {Costco: 69.99}
You are running processReceipts() before the fetch within getReceiptsData() has resolved.
Notice the order of the console logs in this example.
import React, { useEffect, useState } from "react";
const Receipts = () => {
const [receipts, setReceipts] = useState([]);
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
const [keys, setKeys] = useState([]);
useEffect(() => {
const getReceiptsData = async () => {
fetch("https://rickandmortyapi.com/api/character/1", {
method: "GET"
})
.then((res) => res.json())
.then((json) => {
console.log("getReceiptsData resolves");
setReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData(); // js won't wait here
processReceipts();
}, []);
const processReceipts = (json) => {
console.log("processReceipts()");
};
return null;
};
export default Receipts;
Instead, handle the data manipulation when the fetch resolves.
import React, { useEffect, useState } from "react";
const Receipts = () => {
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
useEffect(() => {
const getReceiptsData = async () => {
fetch("https://rickandmortyapi.com/api/character/1", {
method: "GET"
})
.then((res) => res.json())
.then((json) => {
console.log("getReceiptsData resolves");
processReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData();
}, []);
const processReceipts = (json) => {
console.log("processReceipts()");
// do some work and then setResult
};
return null;
};
export default Receipts;
Also, avoid storing a state that is derived from another state where possible. You should either translate the server payload into usable data:
when you receive the payload then set it to state OR
when you are rendering
I have two services, one is sending video service and the other is receiving video service.
The server only forwards the information and forwards the information to the other party.
The service that receives the video can receive the video at some times, but most of them cannot.
I don't know where the problem occurred, the service that sends the video or the service that receives the video.
caller
import { useEffect, useRef, useState, useCallback } from "react";
import io from "socket.io-client";
function createPeerConnection() {
const myPeerConnection = new RTCPeerConnection();
return myPeerConnection;
}
async function setLocalStream(myPeerConnection, target) {
console.log("setLocalStream");
const webcamStream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
});
target.srcObject = webcamStream;
webcamStream
.getTracks()
.forEach((track) =>
myPeerConnection.addTransceiver(track, { streams: [webcamStream] })
);
}
const App = () => {
const ref = useRef();
const [socket] = useState(io());
useEffect(() => {
socket.emit("init", "hello");
socket.on("init", (res) => {
console.log(res);
});
}, []);
const handleNegotiationNeededEvent = useCallback(
(myPeerConnection) => async () => {
try {
const offer = await myPeerConnection.createOffer();
await myPeerConnection.setLocalDescription(offer);
console.log(
"send video-offer",
myPeerConnection.localDescription
);
socket.emit("video-offer", myPeerConnection.localDescription);
} catch (error) {
console.error(error);
}
},
[socket]
);
const handleICECandidateEvent = useCallback(
(event) => {
if (event.candidate) {
console.log("send new-ice-candidate", event.candidate);
socket.emit("new-ice-candidate", event.candidate);
}
},
[socket]
);
useEffect(() => {
const myPeerConnection = createPeerConnection();
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent(
myPeerConnection
);
setLocalStream(myPeerConnection, ref.current);
socket.on("video-answer", async (sdp) => {
console.log("received video-answer", sdp);
const desc = new RTCSessionDescription(sdp);
try {
await myPeerConnection.setRemoteDescription(desc);
} catch (error) {
console.error(error);
}
});
socket.on("new-ice-candidate", async (c) => {
console.log("received new-ice-candidate", c);
const candidate = new RTCIceCandidate(c);
try {
await myPeerConnection.addIceCandidate(candidate);
} catch (error) {
console.error(error);
}
});
}, []);
return (
<>
<video style={{ width: "10px" }} ref={ref} autoPlay muted />
</>
);
};
export default App;
callee
import { useEffect, useRef, useState, useCallback } from "react";
import io from "socket.io-client";
function createPeerConnection() {
const myPeerConnection = new RTCPeerConnection();
return myPeerConnection;
}
const App = () => {
const ref = useRef();
const [socket] = useState(io());
useEffect(() => {
socket.emit("init", "hello");
socket.on("init", (res) => {
console.log(res);
});
}, []);
const handleICECandidateEvent = useCallback(
(event) => {
if (event.candidate) {
console.log("send new-ice-candidate", event.candidate);
socket.emit("new-ice-candidate", event.candidate);
}
},
[socket]
);
const handleTrackEvent = useCallback((event) => {
ref.current.srcObject = event.streams[0];
}, []);
useEffect(() => {
const myPeerConnection = createPeerConnection();
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.ontrack = handleTrackEvent;
socket.on("video-offer", async (sdp) => {
console.log("received offer", sdp);
const desc = new RTCSessionDescription(sdp);
await myPeerConnection.setRemoteDescription(desc);
await myPeerConnection.setLocalDescription(
await myPeerConnection.createAnswer()
);
console.log("send answer", myPeerConnection.localDescription);
socket.emit("video-answer", myPeerConnection.localDescription);
});
socket.on("new-ice-candidate", async (c) => {
console.log("received new-ice-candidate", c);
const candidate = new RTCIceCandidate(c);
try {
await myPeerConnection.addIceCandidate(candidate);
} catch (error) {
console.error(error);
}
});
}, []);
return (
<>
<video style={{ width: "20px" }} ref={ref} autoPlay />
</>
);
};
export default App;