Stop Current Sound when another is pressed React Native Expo - reactjs

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',
},
});

Related

React widget based api async data fetch - Invalid hook call. Hooks can only be called inside

new at React and i am trying to build a widget based dashboard. But i cant get pass this error. No matter what i try and how i try go get the api data for a widget just added to dashboard by the user.
I wanted to make the Api requests in a separate file "StatsService.js" and fetch data in "Layout.js" when the user adds a new widget to the dashboard and afterwards store the data using redux.
Heres the StatsService.js file
import {useEffect,useState} from 'react'
import { useCookies } from 'react-cookie';
async function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function WebserviceToken() {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Origin", "*");
var raw = JSON.stringify({
"applicationToken": "--------",
"customerUID": "--------",
"privateKey": "--------"
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
const [response, setResponse] = useState(null);
useEffect(() => {
(async () => {
const result = await fetch(`https://somewebsite.com/webapi/auth/webservicekey`, requestOptions)
setResponse(result);
})();
}, []);
if(response!==null){
if(response.status=== 200){
return await response.json();
}else if(response.status === 429){
console.log(response.headers["Retry-After"]);
var RetryDelay = parseInt(response.headers["Retry-After"]) * 1000;
console.log("Waitt number of miliseconds: "+RetryDelay)
await timeout(RetryDelay);
console.log("we waited")
return await WebserviceToken();
}else{
console.log(response.status)
console.log(response)
alert("Something is very wrong fetching token! - check console")
}
}
}
export async function ApiCall({path,data}){
let token ="";
const [cookies, setCookie,removeCookie] = useCookies(['auth2']);
if(typeof cookies.auth2 === "undefined"){
(async () => {
const result = await WebserviceToken();
setCookie('auth2', result.token, { path: '/' });
})();
}
token = cookies.auth2;
var myHeaders = new Headers();
myHeaders.append("Origin", "*");
myHeaders.append("Authorization", "Bearer "+token);
let querystring ="";
for (const [key, value] of Object.entries(data)) {
querystring+="&"+key+"="+value;
}
if (querystring){
querystring = "?"+querystring.substring(1);
}
var raw = JSON.stringify(data);
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
};
const [response, setResponse] = useState(null);
const [responseJson, setResponseJson] = useState("");
useEffect(() => {
(async () => {
const result = await fetch(`https://somewebsite.com/webapi/${path}${querystring}`, requestOptions)
setResponse(result);
})();
}, []);
if(response!==null){
if(response.status === 200){
return await response.json();
}else if(response.status === 429){
var RetryDelay = parseInt(response.headers.get("Retry-After")) * 1000;
console.log("Waitt number of miliseconds: "+RetryDelay)
await timeout(RetryDelay);
console.log("we waited")
ApiCall(path, data);
}else {
console.log(response.status)
console.log(response)
alert("Something is very wrong! - check console")
}
}
}
And Layout.js
import React, { useState,useEffect, useCallback } from "react";
import { Responsive as ResponsiveGrid, WidthProvider } from "react-grid-layout";
import { withSize } from "react-sizeme";
import Widget from "./Widget";
import WidgetModal from "./WidgetModal"
import CIcon from '#coreui/icons-react';
import { cilApplications} from '#coreui/icons';
import widgetJson from './WidgetJSON'
import { useSelector, useDispatch } from 'react-redux'
import {CButton} from '#coreui/react'
import {ApiCall} from '../../services/StatsService'
const initialLayouts = {lg:[]};
const ResponsiveGridLayout = WidthProvider(ResponsiveGrid);
const Layout = ({ size: { width } }) =>{
const dispatch = useDispatch()
const leftPanelShow = useSelector((state) => state.leftPanelShow)
const widgetData = useSelector((state) => state.widgetData)
const [widgetModelVisible, setWidgetModelVisible] = useState(false);
const [layouts, setLayouts] = useState(
getFromLS("layouts") || initialLayouts
);
const [cols, setCols] = useState(12);
const [breakpoints, setBreakpoints] = useState({ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 });
const [droppingItem, setDroppingItem] = useState({ i: "", h: 0, w: 0 });
const [apiData, setApiData] = useState(null);
const onLayoutChange = (_,allLayouts) => {
setLayouts(allLayouts);
onLayoutSave();
};
const onLayoutSave = () => {
saveToLS("layouts", layouts);
dispatch({ type: 'set', layouts: layouts });
};
const onRemoveItem = (itemId,event) => {
setLayouts({
lg:[...layouts.lg.filter(item=>item.i!==itemId)]
});
onLayoutSave();
return false;
};
const onAddItem = (item) => {
var tempLayouts = {...layouts};
var lg = {...item.gridLayout};
if(typeof tempLayouts.lg === "undefined"){
tempLayouts.lg = [];
}
let nextXPos = 0;
let nextYPos = 0;
let totalWH = {width:0,height:0,onlyHighestItemsInRow:0,highestItemInRow:0}
tempLayouts.lg.map(function(layout, i){
totalWH.height += layout.h;
totalWH.width += layout.w;
if((totalWH.width % cols) === 0 || (tempLayouts.lg.length-1) === i){
totalWH.onlyHighestItemsInRow += totalWH.highestItemInRow;
totalWH.highestItemsInRow = 0;
}else{
if(layout.h > totalWH.highestItemInRow){
totalWH.highestItemInRow = layout.h;
}
}
})
nextXPos = (totalWH.width) % cols;
if(nextXPos!== 0 && nextXPos > (cols/2)){
nextXPos = 0;
}
nextYPos =totalWH.onlyHighestItemsInRow;
lg.i =item.id;
lg.x = nextXPos;
lg.y = nextYPos;
tempLayouts.lg.push(lg)
setLayouts(tempLayouts);
onLayoutSave();
};
const ResetLayout = () => {
localStorage.clear()
setLayouts(initialLayouts);
}
const onWidgetModalCloseClick = () => {
setWidgetModelVisible(false)
}
const onBreakpointChange = (breakpoint, cols) =>{
setCols(cols)
setBreakpoints(breakpoints)
}
const onResize = (l, oldLayoutItem, layoutItem, placeholder) => {
const heightDiff = layoutItem.h - oldLayoutItem.h;
const widthDiff = layoutItem.w - oldLayoutItem.w;
const changeCoef = oldLayoutItem.w / oldLayoutItem.h;
if (Math.abs(heightDiff) < Math.abs(widthDiff)) {
layoutItem.h = layoutItem.w / changeCoef;
placeholder.h = layoutItem.w / changeCoef;
} else {
layoutItem.w = layoutItem.h * changeCoef;
placeholder.w = layoutItem.h * changeCoef;
}
}
const onDrop = async (layout, layoutItem, _event) => {
setTimeout(() => {
var tempLayouts = {...layouts};
setLayouts(tempLayouts);
dispatch({ type: 'set', layouts: tempLayouts })
}, 100);
}
const onDragStart = (item) => {
setDroppingItem({i:item.id,h:item.gridLayout.h,w:item.gridLayout.w})
}
useEffect(() => {
dispatch({ type: 'set', layouts: layouts })
dispatch({ type: 'set', onDragStart: onDragStart })
dispatch({ type: 'set', widgetData: widgetData })
}, [dispatch]);
useEffect(() => {
layouts.lg.map((item,i) =>{
var livedata = widgetData.filter(w =>w.id === item.i);
if(livedata.length!==0){
item.dataFromApi = livedata[0].data;
}else{
console.log(item);
const wJson = widgetJson.filter(w=>w.id === item.i)[0];
console.log(wJson);
let data = null;
(async () => {
setApiData(await ApiCall({path:wJson.liveData.path,data:wJson.liveData.defaultValues}));
})();
data = apiData;
var tempWidgetData = [...widgetData]
console.log(tempWidgetData);
tempWidgetData.push({id:item.id, data:data});
dispatch({ type: 'set', widgetData: tempWidgetData })
}
})
}, [layouts]);
return (
<>
<div className="gridlayout d-grid gap-2 d-md-flex justify-content-md-end">
<CButton color="primary" onClick={() => dispatch({ type: 'set', leftPanelShow: leftPanelShow?false:true })} className="me-md-2">
<CIcon icon={cilApplications} className="me-2" /> Widget panel
</CButton>
<CButton color="secondary" onClick={ResetLayout}>
Reset
</CButton>
</div>
<ResponsiveGridLayout
className="layout"
layouts={layouts}
autoSize={true}
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
rowHeight={60}
width={width}
onLayoutChange={onLayoutChange}
onBreakpointChange={onBreakpointChange}
onResize={onResize}
isDroppable={true}
onDrop={onDrop}
droppingItem={droppingItem}
style={{minHeight: '500px'}}
>
{layouts.lg.map((item, i) => (
<div
key={item.i}
className="card"
>
<Widget key={item.i} onRemoveItem={onRemoveItem} chart={widgetJson.filter(w=>w.id === item.i)[0]} />
</div>
))
}
</ResponsiveGridLayout>
</>
);
}
export default withSize({ refreshMode: "debounce", refreshRate: 60 })(Layout);
function getFromLS(key) {
let ls = {};
if (global.localStorage) {
try {
ls = JSON.parse(global.localStorage.getItem("rgl-8")) || {};
} catch (e) {}
}
return ls[key];
}
function saveToLS(key, value) {
if (global.localStorage) {
global.localStorage.setItem(
"rgl-8",
JSON.stringify({
[key]: value
})
);
}
}
The error is at UseEffect, where i loop trough all the items in layouts, this is where i try to use either data stored with redux of fetching the api data, if data doesn't exist.

Pausing react native expo Audio?

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',
},
});

How to update an object using axios and json server in a react hooks project

I am using a dialog box in a table to edit a row with axios.patch request. While clicking the edit button the values of that particular row are rendered into the TextFields which are in Dialog box but I'm unable to update the values. can anyone help me with this? Please see the Sandox link for better understanding. Thank you.
Sandbox link: https://codesandbox.io/s/material-demo-forked-gdxl3?file=/demo.js
code:
import React, { useState } from "react";
import {
Button,
Typography,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Table,
TableCell,
TableBody,
TableRow
} from "#material-ui/core";
import { axios } from "./Axios";
import { ValidatorForm, TextValidator } from "react-material-ui-form-validator";
import UserForm from "./UserForm";
import UserTable from "./Table";
export default function SimpleCard() {
const [users, setUsers] = useState([]);
const [postUser, setPostUser] = useState({
id: "",
name: "",
email: ""
});
// console.log(users)
const getUsers = async () => {
const response = await axios.get("/contacts");
if (response && response.data) {
setUsers(response.data);
}
};
React.useEffect(() => {
getUsers();
}, []);
// dialog
const [open, setOpen] = React.useState(false);
const [fullWidth, setFullWidth] = React.useState(true);
const [maxWidth, setMaxWidth] = React.useState("sm");
const handleClickOpen = () => {
setOpen(true);
};
const [open1, setOpen1] = React.useState(false);
const closeDialogForm = () => {
let userDetails = { ...postUser };
userDetails.id = "";
userDetails.name = "";
userDetails.email = "";
setPostUser(userDetails);
};
const handleClose = () => {
closeDialogForm();
setOpen(false);
};
const handleClose1 = () => {
closeDialogForm();
setOpen1(false);
};
const submitForm = async () => {
const response = await axios.post("/contacts", postUser).catch((error) => {
console.log(error);
});
console.log(response);
if (response) {
getUsers();
}
closeDialogForm();
};
const openEditUserDialog = (id) => {
let userDetails = { ...postUser };
for (let index = 0; index < users.length; index++) {
if (id === users[index].id) {
userDetails.id = users[index].id;
userDetails.name = users[index].name;
userDetails.email = users[index].email;
}
}
console.log(userDetails);
setPostUser(userDetails);
setOpen1(true);
};
const updateUser = async (id) => {
const response = await axios
.put(`/contacts/${id}`, postUser)
.catch((error) => {
console.log(error);
});
// console.log(response)
if (response) {
// setOpen(false);
getUsers();
}
};
const deleteUser = async (id) => {
const response = await axios.delete(`/contacts/${id}`).catch((err) => {
console.log(err);
});
if (response) {
getUsers();
}
};
return (
<div>
<Typography>Users</Typography>
<Button
size="small"
variant="contained"
onClick={handleClickOpen}
style={{ textTransform: "none" }}
>
Add
</Button>
<UserTable
users={users}
openEditUserDialog={openEditUserDialog}
deleteUser={deleteUser}
/>
{/* dialog */}
<Dialog
fullWidth={fullWidth}
maxWidth={maxWidth}
open={open}
// onClose={handleClose}
aria-labelledby="max-width-dialog-title"
>
<DialogTitle id="max-width-dialog-title">Add contact</DialogTitle>
<UserForm
submitForm={submitForm}
handleClose={handleClose}
postUser={postUser}
setPostUser={setPostUser}
/>
</Dialog>
{/* edit dialog */}
<Dialog
fullWidth={fullWidth}
maxWidth={maxWidth}
open={open1}
// onClose={handleClose}
aria-labelledby="edit-user"
>
<DialogTitle id="edit-user">Edit contact</DialogTitle>
<UserForm
submitForm={updateUser}
handleClose={handleClose1}
postUser={postUser}
setPostUser={setPostUser}
/>
</Dialog>
</div>
);
}
backend json data:
{
"contacts": [
{
"id": 1,
"name": "Gowtham",
"email": "gowtham#gmail.com"
},
{
"id": 2,
"name": "King",
"email": "gowthamKing#gmail.com"
},
{
"id": 3,
"name": "Hello",
"email": "gowthamKing123#gmail.com"
}
]
}
I have found the solution. Just replace the updateUser arrow function with below code
const updateUser = async () => {
let updateObject = {...postUser}
const response = await axios.patch(`/contacts/${updateObject.id}`, postUser).catch(error => { console.log(error) })
console.log(response)
if (response) {
// setOpen(false);
getUsers()
}
handleClose1();
}

WebRTC using React: Why the remote video is not playing?

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;

I need to send a Expo Notification once a day, but it is sending many push to same user

I am using Expo to send a daily notification at 4:30 pm, without access back end (just local).
It is working well, but not right, once it is sending many pushs to the same user.
I don't know if there is something about useEffect params, but if I do not consider anything inside of [ ], notification is not sent.
import React, { useState, createContext, useEffect, useRef } from 'react'
import { Linking, Platform } from 'react-native'
import AsyncStorage from '#react-native-community/async-storage'
import * as Notifications from 'expo-notifications'
import api from '../api'
import { userStatusStorage } from '../../utils/defaultValues'
import { useAuth } from './auth'
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
export const NotificationContext = createContext({});
export function NotificationProvider({ children }) {
const [idUser, setIdUser] = useState()
const [userObj, setUserObj] = useState({})
const [status, setStatus] = useState('')
const [notificationStatusPermission, setNotificationStatusPermission] = useState(true)
const [token, setToken] = useState('')
const [expoPushTokenDevice, setExpoPushTokenDevice] = useState('')
const [notificationSent, setNotificationSent] = useState({})
const { userId, idStorage, user, userStorage, statusStorage, userStatus } = useAuth()
const notificationListener = useRef();
const responseListener = useRef();
useEffect(() => {
if (userId) {
setIdUser(userId)
} else {
setIdUser(idStorage)
}
}, [userId, idStorage])
useEffect(() => {
if (Object.keys(userStorage).length !== 0) {
setUserObj(JSON.parse(userStorage))
} else {
setUserObj(user)
}
}, [userStorage])
useEffect(() => {
if (status !== '') {
return
} else if (userStatus) {
setStatus(userStatus)
} else {
setStatus(statusStorage)
}
}, [statusStorage, userStatus])
useEffect(() => {
try {
Notifications.getDevicePushTokenAsync().then(token => setExpoPushTokenDevice(token));
} catch (e) {
setExpoPushTokenDevice('token');
}
registerForPushNotificationsAsync().then(token => setToken(token));
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
setNotificationSent(notification);
});
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
// called at the moment that user tap on notification bar
const url = response.notification.request.content.data.url;
Linking.openURL(url);
});
return () => {
Notifications.removeNotificationSubscription(notificationListener);
Notifications.removeNotificationSubscription(responseListener);
};
}, []);
useEffect(() => {
sendNotification()
}, [idUser])
async function sendNotification() {
if (notificationStatusPermission) {
await Notifications.scheduleNotificationAsync({
content: {
title: `Olá ${userObj.name.split(' ')[0]}`,
body: `Seu status atual é ${status}!`,
data: {
data: idUser,
url: 'gogogo://'
},
},
trigger: { hour: 16, minute: 30, repeats: true },
});
}
}
async function registerForPushNotificationsAsync() {
let token;
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;
setToken(token)
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
return token;
}
async function loadStatus() {
const res = await api.get('myStatusDaily')
await AsyncStorage.setItem(userStatusStorage, res.data[0].name)
setStatus(res.data[0].name)
setNotificationStatusPermission(res.data[0].notificationPermission)
}
return (
<NotificationContext.Provider
value={{
status,
notificationStatusPermission,
loadStatus,
}}
>
{children}
</NotificationContext.Provider>
)
}
How can I garantee that will be sent just one notification per day?

Resources