Buid Group video calling using WebRTC - webrtc

WebSocket
Using WebRTC easy to implement peer to peer video calling. But how to implement group video calling using WebRTC and WebSocket in Spring Boot.?
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import com.call.event.ConnectionHandler;
#Configuration
#EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ConnectionHandler(), "/call").setAllowedOrigins("*");
}
}
ConnectionHandler
Here Socket Handler.We handle all user session and socket and payoads.
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
System.out.println("Got signal - " + message.getPayload());
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
#Component
public class ConnectionHandler extends TextWebSocketHandler {
List<WebSocketSession>sessions = new CopyOnWriteArrayList<>();
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
throws InterruptedException, IOException {
System.out.println("Got signal - " + message.getPayload());
for (WebSocketSession webSocketSession : sessions) {
if (webSocketSession.isOpen() && !session.getId().equals(webSocketSession.getId())) {
webSocketSession.sendMessage(message);
}
}
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
System.out.println("-----------------------------------------------------------------------------");
System.out.println("Connection opened!");
System.out.println("-----------------------------------------------------------------------------");
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("-----------------------------------------------------------------------------");
System.out.println("Connection Closed!");
System.out.println("-----------------------------------------------------------------------------");
sessions.add(session);
}
}
WebRTC code
Here WebRTC code for Peer to Peer connection. We using webRTC for getUserMedia and DataChanells.
let peerConnection=null;
let localVideo;
let localStream;
let videoTrack;
let audioTrack;
let stream;
let signal;
var signalingWebsocket = new WebSocket("ws://localhost:8080/call");
signalingWebsocket.onmessage = function(msg) {
console.log("Got message", msg.data);
signal = JSON.parse(msg.data);
switch (signal.type) {
case "offer":
handleOffer(signal);
break;
case "answer":
handleAnswer(signal);
break;
// In local network, ICE candidates might not be generated.
case "candidate":
handleCandidate(signal);
break;
default:
break;
}
};
signalingWebsocket.onopen = init();
function sendSignal(signal) {
if (signalingWebsocket.readyState == 1) {
signalingWebsocket.send(JSON.stringify(signal));
}
};
/*
* Initialize
*/
function init() {
console.log("Connected to signaling endpoint. Now initializing.");
preparePeerConnection();
displayLocalStream(true);
};
/*
* Prepare RTCPeerConnection & setup event handlers.
*/
function preparePeerConnection() {
// Using free public google STUN server.
const configuration = {
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
};
// Prepare peer connection object
peerConnection = new webkitRTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]});
peerConnection.onnegotiationneeded = async () => {
console.log('onnegotiationneeded');
sendOfferSignal();
};
peerConnection.onicecandidate = function(event) {
if (event.candidate) {
sendSignal(event);
}
};
peerConnection.addEventListener('track', displayRemoteStream);
};
async function displayLocalStream(firstTime) {
console.log('Requesting local stream');
localVideo = document.getElementById('localVideo');
try {
stream = await navigator.mediaDevices.getUserMedia({
video: {
width: 500,
height: 300
},
audio: false
});
console.log('Received local stream');
localVideo.srcObject = stream;
localStream = stream;
videoAudioTrackInfo(localStream);
console.log(stream);
// For first time, add local stream to peer connection.
if (firstTime) {
setTimeout(
function() {
addLocalStreamToPeerConnection(localStream);
}, 2000);
}
// Send offer signal to signaling server endpoint.
sendOfferSignal();
} catch (e) {
alert(`getUserMedia() error: ${e.name}`);
throw e;
}
console.log('Start complete');
}
function videoAudioTrackInfo(localStream) {
videoTrack = localStream.getVideoTracks();
audioTrack = localStream.getAudioTracks();
if (videoTrack.length > 0) {
console.log(`Using video device: ${videoTrack[0].label}`);
}
if (audioTrack.length > 0) {
console.log(`Using audio device: ${audioTrack[0].label}`);
}
}
async function addLocalStreamToPeerConnection(localStream) {
console.log('Starting addLocalStreamToPeerConnection');
localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
console.log('localStream tracks added');
};
function sendOfferSignal() {
peerConnection.createOffer(function(offer) {
sendSignal(offer);
peerConnection.setLocalDescription(offer);
}, function(error) {
alert("Error creating an offer");
});
};
function handleOffer(offer) {
peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
peerConnection.createAnswer(function(answer) {
peerConnection.setLocalDescription(answer);
sendSignal(answer);
}, function(error) {
alert("Error creating an answer");
});
};
function handleAnswer(answer) {
peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
console.log("connection established successfully!!");
};
function handleCandidate(candidate) {
alert("handleCandidate");
peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
};
function displayRemoteStream(e) {
console.log('displayRemoteStream');
const remoteVideo = document.getElementById('remoteVideo');
if (remoteVideo.srcObject !== e.streams[0]) {
remoteVideo.srcObject = e.streams[0];
console.log('pc2 received remote stream');
}
};
function leaveMeeting() {
let cnf=confirm("Are you sure for leave meeting!")
if(cnf==true){
console.log('Ending call');
peerConnection.close();
signalingWebsocket.close();
window.location.href = '/';
}else{
console.log('you are in meeting....');
}
};

Related

Error: require() of ES Module NextJS Web3AuthConnector

I have a NextJS (Typescript) setup with Express . I'm using Moralis, Wagmi and Web3Auth for authentication. The Metamask authentication works, but when I try to setup Web3 Authentication by importing Web3AuthConnector it throws me an error.
My code is like here in the documentation but looks to be an error about CommonJS and ESM which I can't handle. This is the error:
error - Error [ERR_REQUIRE_ESM]: require() of ES Module
C:...\node_modules#web3auth\web3auth-wagmi-connector\node_modules#wagmi\core\dist\index.js
from
C:...\node_modules#web3auth\web3auth-wagmi-connector\dist\web3authWagmiConnector.cjs.js
not supported. Instead change the require of index.js in
C:...\node_modules#web3auth\web3auth-wagmi-connector\dist\web3authWagmiConnector.cjs.js
to a dynamic import() which is available in all CommonJS modules.
I tried to set in package.json "type:module", and in tsconfig: module:"ES6" or "ESNext" but got the same error.
I got the same problem than you and I resolved it by create my own Connector.
I didn't found any other way to do it.
import {
Chain,
Connector,
ConnectorData,
normalizeChainId,
UserRejectedRequestError,
} from "#wagmi/core";
import {
ADAPTER_EVENTS,
ADAPTER_STATUS,
CHAIN_NAMESPACES,
CustomChainConfig,
getChainConfig,
SafeEventEmitterProvider,
WALLET_ADAPTER_TYPE,
WALLET_ADAPTERS,
BaseAdapterConfig,
CONNECTED_EVENT_DATA,
IAdapter,
storageAvailable,
ADAPTER_CATEGORY,
} from "#web3auth/base";
import { Web3AuthCore } from "#web3auth/core";
import { MetamaskAdapter } from "#web3auth/metamask-adapter";
import { OpenloginAdapter } from "#web3auth/openlogin-adapter";
import { TorusWalletAdapter } from "#web3auth/torus-evm-adapter";
import LoginModal, {
getAdapterSocialLogins,
LOGIN_MODAL_EVENTS,
OPENLOGIN_PROVIDERS,
} from "#web3auth/ui";
import {
AdaptersModalConfig,
defaultEvmDappModalConfig,
ModalConfig,
} from "#web3auth/web3auth";
import { Options } from "#web3auth/web3auth-wagmi-connector/dist/types/lib/interfaces";
import { ethers, Signer } from "ethers";
import { getAddress } from "ethers/lib/utils";
import { WalletConnectV1Adapter } from "#web3auth/wallet-connect-v1-adapter";
import QRCodeModal from "#walletconnect/qrcode-modal";
import { ConnectorEvents, defaultChains } from "wagmi";
import EventEmitter from "events";
import { Provider } from "react";
const IS_SERVER = typeof window === "undefined";
const ADAPTER_CACHE_KEY = "Web3Auth-cachedAdapter";
export class Web3AuthConnectorLocal extends Connector {
ready = !IS_SERVER;
readonly id = "web3Auth";
readonly name = "web3Auth";
provider: SafeEventEmitterProvider;
web3AuthInstance?: Web3AuthCore;
isModalOpen = false;
web3AuthOptions: Options;
private loginModal: LoginModal;
private socialLoginAdapter: OpenloginAdapter;
private torusWalletAdapter: TorusWalletAdapter;
private metamaskAdapter: MetamaskAdapter;
private walletConnectV1Adapter: WalletConnectV1Adapter;
private adapters: Record<string, IAdapter<unknown>> = {};
private modalConfig: AdaptersModalConfig = defaultEvmDappModalConfig;
private storage: "sessionStorage" | "localStorage" = "localStorage";
constructor(config: { chains?: Chain[]; options: Options }) {
super(config);
this.web3AuthOptions = config.options;
const chainId = config.options.chainId
? parseInt(config.options.chainId, 16)
: 1;
const chainConfig = this.chains.filter((x) => x.id === chainId);
const defaultChainConfig = getChainConfig(
CHAIN_NAMESPACES.EIP155,
config.options.chainId || "0x1"
);
let finalChainConfig: CustomChainConfig = {
chainNamespace: CHAIN_NAMESPACES.EIP155,
...defaultChainConfig,
};
if (chainConfig.length > 0) {
finalChainConfig = {
...finalChainConfig,
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: config.options.chainId || "0x1",
rpcTarget: chainConfig[0].rpcUrls.default,
displayName: chainConfig[0].name,
tickerName: chainConfig[0].nativeCurrency?.name,
ticker: chainConfig[0].nativeCurrency?.symbol,
blockExplorer: chainConfig[0]?.blockExplorers.default?.url,
};
}
this.web3AuthInstance = new Web3AuthCore({
clientId: config.options.clientId,
chainConfig: {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth", // This is the public RPC we have added, please pass on your own endpoint while creating an app
},
});
this.socialLoginAdapter = new OpenloginAdapter({
adapterSettings: {
...config.options,
},
chainConfig: {
chainId: "0x1",
chainNamespace: CHAIN_NAMESPACES.EIP155,
rpcTarget: "https://rpc.ankr.com/eth",
displayName: "mainnet",
blockExplorer: "https://etherscan.io/",
ticker: "ETH",
tickerName: "Ethereum",
},
});
this.torusWalletAdapter = new TorusWalletAdapter({
adapterSettings: {
buttonPosition: "bottom-left",
},
loginSettings: {
verifier: "google",
},
initParams: {
buildEnv: "testing",
},
chainConfig: {
chainNamespace: CHAIN_NAMESPACES.EIP155,
chainId: "0x1",
rpcTarget: "https://rpc.ankr.com/eth", // This is the mainnet RPC we have added, please pass on your own endpoint while creating an app
displayName: "Ethereum Mainnet",
blockExplorer: "https://etherscan.io/",
ticker: "ETH",
tickerName: "Ethereum",
},
});
this.metamaskAdapter = new MetamaskAdapter({
clientId: config.options.clientId,
});
this.walletConnectV1Adapter = new WalletConnectV1Adapter({
adapterSettings: {
bridge: "https://bridge.walletconnect.org",
qrcodeModal: QRCodeModal,
},
clientId: config.options.clientId,
});
this.web3AuthInstance.configureAdapter(this.socialLoginAdapter);
this.web3AuthInstance.configureAdapter(this.torusWalletAdapter);
this.web3AuthInstance.configureAdapter(this.metamaskAdapter);
this.web3AuthInstance.configureAdapter(this.walletConnectV1Adapter);
this.adapters[this.socialLoginAdapter.name] = this.socialLoginAdapter;
this.adapters[this.torusWalletAdapter.name] = this.torusWalletAdapter;
this.adapters[this.metamaskAdapter.name] = this.metamaskAdapter;
this.adapters[this.walletConnectV1Adapter.name] =
this.walletConnectV1Adapter;
this.loginModal = new LoginModal({
theme: this.options.uiConfig?.theme,
appLogo: this.options.uiConfig?.appLogo || "",
version: "",
adapterListener: this.web3AuthInstance,
displayErrorsOnModal: this.options.displayErrorsOnModal,
});
// this.loginModal.initExternalWalletContainer();
this.subscribeToLoginModalEvents();
}
async connect(): Promise<Required<ConnectorData>> {
this.web3AuthInstance.init();
const adapterEventsPromise = this.subscribeToAdpaterConnectionEvents()
await this.init();
this.loginModal.open();
const elem = document.getElementById("w3a-container");
elem.style.zIndex = "10000000000";
return (await adapterEventsPromise) as Required<ConnectorData>;
}
async subscribeToAdpaterConnectionEvents(): Promise<Required<ConnectorData>> {
return new Promise((resolve, reject) => {
this.web3AuthInstance.once(ADAPTER_EVENTS.CONNECTED, async () => {
console.log(
"Received event connected: ",
this.web3AuthInstance.connectedAdapterName
);
console.log("Requesting Signer");
const signer = await this.getSigner();
const account = await signer.getAddress();
const provider = await this.getProvider();
if (provider.on) {
provider.on("accountsChanged", this.onAccountsChanged.bind(this));
provider.on("chainChanged", this.onChainChanged.bind(this));
provider.on("disconnect", this.onDisconnect.bind(this));
}
return resolve({
account,
chain: {
id: 0,
unsupported: false,
},
provider,
});
});
this.web3AuthInstance.once(ADAPTER_EVENTS.ERRORED, (err: unknown) => {
console.log("error while connecting", err);
return reject(err);
});
});
}
async init(): Promise<void> {
console.log("What is this type: ", typeof this)
console.log("What is this instance: ", this instanceof Web3AuthConnectorLocal)
try {
await this.loginModal.initModal();
const allAdapters = [
...new Set([
...Object.keys(this.modalConfig.adapters || {}),
...Object.keys(this.adapters),
]),
];
const adapterNames = [
"torus-evm",
"metamask",
"openlogin",
"wallet-connect-v1",
];
const hasInAppWallets = true;
// Now, initialize the adapters.
const initPromises = adapterNames.map(async (adapterName) => {
if (!adapterName) return;
try {
const adapter = this.adapters[adapterName];
console.log("Adapter Found: ", adapterName);
console.log("Cached Adapter: ", this.web3AuthInstance.cachedAdapter);
// only initialize a external adapter here if it is a cached adapter.
if (
this.web3AuthInstance.cachedAdapter !== adapterName &&
adapter.type === ADAPTER_CATEGORY.EXTERNAL
) {
console.log(adapterName, " Adapter is not External");
return;
}
// in-app wallets or cached wallet (being connected or already connected) are initialized first.
// if adapter is configured thn only initialize in app or cached adapter.
// external wallets are initialized on INIT_EXTERNAL_WALLET event.
this.subscribeToAdapterEvents(adapter);
if (adapter.status === ADAPTER_STATUS.NOT_READY) {
await adapter.init({
autoConnect: this.web3AuthInstance.cachedAdapter === adapterName,
});
console.log(
"Initializing In Wallet: COMPLETED",
adapter,
adapter.status
);
}
// note: not adding cachedWallet to modal if it is external wallet.
// adding it later if no in-app wallets are available.
if (adapter.type === ADAPTER_CATEGORY.IN_APP) {
this.loginModal.addSocialLogins(
WALLET_ADAPTERS.OPENLOGIN,
getAdapterSocialLogins(
WALLET_ADAPTERS.OPENLOGIN,
this.socialLoginAdapter,
this.options.uiConfig?.loginMethodConfig
),
this.options.uiConfig?.loginMethodsOrder || OPENLOGIN_PROVIDERS
);
}
} catch (error) {
console.log(error, "error while initializing adapter");
}
});
this.web3AuthInstance.status = ADAPTER_STATUS.READY;
await Promise.all(initPromises);
const hasExternalWallets = allAdapters.some((adapterName) => {
return (
this.adapters[adapterName]?.type === ADAPTER_CATEGORY.EXTERNAL &&
this.modalConfig.adapters?.[adapterName].showOnModal
);
});
console.log("Has External Wallets: ", hasExternalWallets);
if (hasExternalWallets) {
this.loginModal.initExternalWalletContainer();
}
if (!hasInAppWallets && hasExternalWallets) {
await this.initExternalWalletAdapters(false, {
showExternalWalletsOnly: true,
});
}
} catch (error) {
console.log("error while connecting", error);
throw new UserRejectedRequestError("Something went wrong");
}
}
async getAccount(): Promise<string> {
const provider = new ethers.providers.Web3Provider(
await this.getProvider()
);
const signer = provider.getSigner();
const account = await signer.getAddress();
return account;
}
async getProvider() {
if (this.provider) {
return this.provider;
}
this.provider = this.web3AuthInstance.provider;
return this.provider;
}
async getSigner(): Promise<Signer> {
console.log("Getting Signer");
const provider = new ethers.providers.Web3Provider(
await this.getProvider()
);
const signer = provider.getSigner();
return signer;
}
async isAuthorized() {
try {
const account = await this.getAccount();
return !!(account && this.provider);
} catch {
return false;
}
}
async getChainId(): Promise<number> {
try {
const networkOptions = this.socialLoginAdapter.chainConfigProxy;
if (typeof networkOptions === "object") {
const chainID = networkOptions.chainId;
if (chainID) {
return normalizeChainId(chainID);
}
}
throw new Error("Chain ID is not defined");
} catch (error) {
console.log("error", error);
throw error;
}
}
async disconnect(): Promise<void> {
await this.web3AuthInstance.logout();
this.provider = null;
}
protected onAccountsChanged(accounts: string[]): void {
if (accounts.length === 0) this.emit("disconnect");
else this.emit("change", { account: getAddress(accounts[0]) });
}
protected onChainChanged(chainId: string | number): void {
const id = normalizeChainId(chainId);
const unsupported = this.isChainUnsupported(id);
this.emit("change", { chain: { id, unsupported } });
}
protected onDisconnect(): void {
this.emit("disconnect");
}
private subscribeToLoginModalEvents(): void {
this.loginModal.on(
LOGIN_MODAL_EVENTS.LOGIN,
async (params: {
adapter: WALLET_ADAPTER_TYPE;
loginParams: unknown;
}) => {
try {
console.log("Wallet Adapters: ", +params.adapter);
await this.web3AuthInstance.connectTo<unknown>(
params.adapter,
params.loginParams
);
} catch (error) {
console.log(
`Error while connecting to adapter: ${params.adapter}`,
error
);
}
}
);
this.loginModal.on(
LOGIN_MODAL_EVENTS.INIT_EXTERNAL_WALLETS,
async (params: { externalWalletsInitialized: boolean }) => {
await this.initExternalWalletAdapters(
params.externalWalletsInitialized
);
}
);
this.loginModal.on(LOGIN_MODAL_EVENTS.DISCONNECT, async () => {
try {
await this.disconnect();
} catch (error) {
console.log(`Error while disconnecting`, error);
}
});
}
private async initExternalWalletAdapters(
externalWalletsInitialized: boolean,
options?: { showExternalWalletsOnly: boolean }
): Promise<void> {
if (externalWalletsInitialized) return;
const adaptersConfig: Record<string, BaseAdapterConfig> = {};
const adaptersData: Record<string, unknown> = {};
const adapterPromises = Object.keys(this.adapters).map(
async (adapterName) => {
try {
const adapter = this.adapters[adapterName];
if (adapter?.type === ADAPTER_CATEGORY.EXTERNAL) {
console.log("init external wallet", adapterName);
this.subscribeToAdapterEvents(adapter);
// we are not initializing cached adapter here as it is already being initialized in initModal before.
if (this.web3AuthInstance.cachedAdapter === adapterName) {
return;
}
if (adapter.status === ADAPTER_STATUS.NOT_READY) {
console.log("Adapter not Ready: " + adapterName);
return await Promise.race([
adapter
.init({
autoConnect:
this.web3AuthInstance.cachedAdapter === adapterName,
})
.then(() => {
adaptersConfig[adapterName] = (
defaultEvmDappModalConfig.adapters as Record<
WALLET_ADAPTER_TYPE,
ModalConfig
>
)[adapterName];
adaptersData[adapterName] = adapter.adapterData || {};
console.log("Adapter Init: ", adapterName);
return adapterName;
}),
new Promise((resolve) => {
setTimeout(() => {
return resolve(null);
}, 5000);
}),
]);
} else {
console.log("Adapter Ready: " + adapterName);
return adapterName;
}
}
} catch (error) {
console.log(error, "error while initializing adapter");
}
}
);
const adapterInitResults = await Promise.all(adapterPromises);
console.log("Adapter Init Results: ", adapterInitResults);
const finalAdaptersConfig: Record<WALLET_ADAPTER_TYPE, BaseAdapterConfig> =
{};
adapterInitResults.forEach((result: string | undefined) => {
if (result) {
finalAdaptersConfig[result] = adaptersConfig[result];
}
});
this.loginModal.addWalletLogins(finalAdaptersConfig, {
showExternalWalletsOnly: !!options?.showExternalWalletsOnly,
});
}
private subscribeToAdapterEvents(walletAdapter: IAdapter<unknown>): void {
console.log("Running adapter events");
walletAdapter.on(ADAPTER_EVENTS.CONNECTED, (data: CONNECTED_EVENT_DATA) => {
let status = ADAPTER_STATUS.CONNECTED;
this.web3AuthInstance.connectedAdapterName = data.adapter;
this.cacheWallet(data.adapter);
console.log(
"connected",
status,
this.web3AuthInstance.connectedAdapterName
);
this.web3AuthInstance.emit(ADAPTER_EVENTS.CONNECTED, {
...data,
} as CONNECTED_EVENT_DATA);
});
walletAdapter.on(ADAPTER_EVENTS.DISCONNECTED, async (data) => {
// get back to ready state for rehydrating.
let status = ADAPTER_STATUS.READY;
if (storageAvailable(this.storage)) {
const cachedAdapter = window[this.storage].getItem(ADAPTER_CACHE_KEY);
if (this.web3AuthInstance.connectedAdapterName === cachedAdapter) {
this.web3AuthInstance.clearCache();
}
}
console.log(
"disconnected",
status,
this.web3AuthInstance.connectedAdapterName
);
this.web3AuthInstance.connectedAdapterName = null;
this.web3AuthInstance.emit(ADAPTER_EVENTS.DISCONNECTED, data);
});
walletAdapter.on(ADAPTER_EVENTS.CONNECTING, (data) => {
let status = ADAPTER_STATUS.CONNECTING;
this.web3AuthInstance.emit(ADAPTER_EVENTS.CONNECTING, data);
console.log(
"connecting",
status,
this.web3AuthInstance.connectedAdapterName
);
});
walletAdapter.on(ADAPTER_EVENTS.ERRORED, (data) => {
let status = ADAPTER_STATUS.ERRORED;
this.web3AuthInstance.clearCache();
this.web3AuthInstance.emit(ADAPTER_EVENTS.ERRORED, data);
console.log(
"errored",
status,
this.web3AuthInstance.connectedAdapterName
);
});
walletAdapter.on(ADAPTER_EVENTS.ADAPTER_DATA_UPDATED, (data) => {
console.log("adapter data updated", data);
this.web3AuthInstance.emit(ADAPTER_EVENTS.ADAPTER_DATA_UPDATED, data);
});
}
private cacheWallet(walletName: string) {
if (!storageAvailable(this.storage)) return;
window[this.storage].setItem(ADAPTER_CACHE_KEY, walletName);
this.web3AuthInstance.cachedAdapter = walletName;
}
}
I got the code from https://forum.moralis.io/t/solved-web3auth-wagmi-connector-doesnt-show-web3-wallets-as-a-login-option/20110/4.
Let me know if it's working for you ?
( You should add all the missing packages with yarn add )

Can't send MATIC to smart contract using ethers library

I deployed my solidity smart contract in polygon testnet(mumbai network).
I tested my smart contract works well in my react frontend except one payable function.
function getLotto() payable external {
require(msg.value == 100000000000000000, "Pay 0.1MATIC");
...
}
here's my frontend code. it will be called when some button is pressed.
of course, I set the signer and abi in the contract.
const handler = () => {
const options = {
gasPrice: 800000,
value: ethers.BigNumber.from("100000000000000000"),
};
await myContract.getLotto(options);
}
I also tried this code but it also didn't work.
value: ethers.utils.parseEther("0.1")
here's the error message.
---edited------
export class MarioNft {
constructor(rpc, contractAddr, abi) {
this.rpc = rpc;
this.contractAddr = contractAddr;
this.abi = abi;
this.isSigned = false;
this.provider = new ethers.providers.JsonRpcProvider(rpc);
this.contract = new ethers.Contract(contractAddr, abi, this.provider);
this.baseUri = null;
}
...
setContractWithSigner(signer) {
this.contract = new ethers.Contract(this.contractAddr, this.abi, signer);
this.isSigned = true;
}
async lotto() {
if (this.isSigned) {
const res = await this.contract.getLotto();
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}
}
const lottoHandler = async () => {
if (metaProvider === null) {
alert("Need to connect to MetaMask");
} else {
if (!marioNft.checkIsSigned()) {
marioNft.setContractWithSigner(metaSigner);
}
const options = {
gasPrice: 800000,
value: ethers.BigNumber.from("100000000000000000"),
};
await marioNft.lotto(options);
};
-------------edited2-----------------
const updateEthers = () => {
let tempProvider = new ethers.providers.Web3Provider(window.ethereum);
setMetaProvider(tempProvider);
// this tempSigner will be used in marioNft.setContractWithSigner()
let tempSigner = tempProvider.getSigner();
setMetaSigner(tempSigner);
};
when I print tempSigner
Your async lotto function inside of the MarioNFT class does not pass on the options object:
async lotto() {
if (this.isSigned) {
const res = await this.contract.getLotto();
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}
So it should be:
async lotto(options) {
if (this.isSigned) {
const res = await this.contract.getLotto(options);
console.log("lotto res: ", res); /////
} else {
///////
console.log("from marioNft class: signer is false");
}
}

How can I keep redux saga responsive when app is in the background?

Im using React-Native and I have a saga that manages the state of a websocket. It implements re-tries and receives dispatches for things like location change to send it up to the server via websocket. I am using #mauron85/react-native-background-geolocation to grab location events in the root component of my app and I am dispatching them to redux. The route saga has a spawned generator that listens for this and will send it up. When the app is in foreground(currently in use) the socket can recover from network loss and continue receiving location events however when the app goes to the background as long as the socket is open, the saga responds to the events and can still send them up. As soon as I mark airplane mode and lose the socket, the saga stops responding to retries and other dispatch events such as the location update. When i remove airplane mode (while still in the background) it is not able to re-establish connection and receive dispatches. How can I approach this to be able to allow the app to recover without it ever transitioning back to the foreground.
Saga Code:
function* sender(socket: WebSocket, eventType, data?) {
const { token } = yield select(authState);
if (socket && socket.readyState === WebSocket.OPEN) {
let localData = {};
if (data) {
localData = data;
}
console.log(JSON.stringify({ type: eventType, data: { token, ...localData } }));
socket.send(JSON.stringify({ type: eventType, data: { token, ...localData } }));
if (eventType === ROUTE_SOCKET_MESSSAGE_TYPES.PING) {
const res = yield race({
success: take(ROUTE_SOCKET_MESSAGE_RECEIVED),
timeout: delay(SOCKET_PING_RESPONSE_EXPECTED)
});
if (res.timeout) {
console.log('ENCOUNTERED LAG IN MESSAGE RECEIPT');
yield put(markNetworkLost());
yield put({ type: ROUTE_SOCKET_RETRY_ACTION_TYPE });
} else {
yield put(markHasNetwork());
}
}
}
}
function* pinger(socket) {
while (true) {
yield call(sender, socket, ROUTE_SOCKET_MESSSAGE_TYPES.PING);
yield delay(FORTY_FIVE_SECONDS);
}
}
function* sendPosition(socket) {
while (true) {
const { latitude, longitude } = yield select(locationState);
if (latitude && longitude) {
yield call(positionDispatcher, socket, {
lat: latitude,
lng: longitude
});
}
yield take('locations/ON_LOCATION_CHANGE');
}
}
function* locationApiRequest() {
try {
const res = yield call(getLocation);
yield put(onPharmacyDetailsReceived(res.data));
} catch (error) {
console.log('Could not fetch pharmacy location details', error);
}
}
function* positionDispatcher(socket, position) {
yield call(sender, socket, ROUTE_SOCKET_MESSSAGE_TYPES.DRIVER_LOCATION, position);
}
function dispatchPhotoFetch(route) {
route.routeStops.forEach((stop) => {
if (stop.address.photos) {
console.log(stop.address.photos);
PhotoService.readLocalFileOrDownload(
stop.address.id,
stop.address.photos.map((photo) => new AddressPhoto(photo))
);
}
});
}
function* socketChannelListener(socketChannel, socket) {
let pingerProcess;
let positionProcess;
while (true) {
const payload = yield take(socketChannel); // take incoming message
const { type: baseSocketResponseType } = payload;
switch (baseSocketResponseType) {
case 'open':
yield fork(locationApiRequest);
pingerProcess = yield fork(pinger, socket);
positionProcess = yield fork(sendPosition, socket);
PendingTasks.activate(); // activate pending task upload when the socket opens
break;
case 'error':
console.log('ERROR', payload.reason, payload);
break;
case 'close':
console.log('CLOSE', payload.reason, payload);
if (payload.reason === 'Invalid creds') {
// console.log('ENCOUNTERED INVALID CREDENTIALS/STALE TOKEN');
yield put(autoLogout());
} else {
yield put(markNetworkLost());
yield put({ type: ROUTE_SOCKET_RETRY_ACTION_TYPE });
}
// console.log('CALLING CLOSE');
yield cancel(pingerProcess);
yield cancel(positionProcess);
return;
default:
break;
}
let type;
let data;
let operation;
let parsedData;
if (payload && payload.data) {
parsedData = JSON.parse(payload.data);
const { type: incomingType, data: incomingData, op } = parsedData;
type = incomingType;
data = incomingData;
operation = op;
}
// console.log('PARSED MESSAGE', parsedData);
switch (type) {
case ROUTE_SOCKET_TYPES.AUTH_STATUS:
switch (data) {
case PINGER_RESPONSE_TYPES.AUTH_SUCCESS:
yield put({ type: ROUTE_SOCKET_MESSAGE_RECEIVED });
break;
case PINGER_RESPONSE_TYPES.TOKEN_EXPIRED:
break;
default:
break;
}
break;
case ROUTE_SOCKET_TYPES.DRIVER_ROUTE:
console.log(
`received DRIVER_ROUTE with ${JSON.stringify(data.routeStops.length)} stop(s)`,
operation,
data
);
switch (operation) {
case DRIVER_ROUTE_OPERATIONS.I: {
const currentRoute = yield select(driverRouteState);
// if we do pick up a route..
if (!currentRoute || currentRoute.id !== data.id) {
yield fork(dispatchPhotoFetch, data);
yield put(onDriverRouteChange(data));
}
break;
}
case DRIVER_ROUTE_OPERATIONS.U:
// i believe we will ignore this if there are records in sqlite?
// create address photo objects first?
// yield put(onDriverRouteUpdate(data));
break;
case DRIVER_ROUTE_OPERATIONS.D:
// TODO: deletion of a route needs to be handled most likely.
break;
default:
break;
}
break;
case ROUTE_SOCKET_TYPES.DRIVER_ROUTE_LOG_REQUEST:
break;
default:
break;
}
}
}
function createSocketChannel(token) {
// TODO: we need to pull this from config....
const socket = new WebSocket(`${ROUTE_SOCKET_URL}${token}`);
// establishes a redux emitter that saga can wait for (like an action)
return {
socket,
socketChannel: eventChannel((emit) => {
socket.onmessage = (event) => {
// console.log('--MESSAGE received--', event);
emit(event);
};
socket.onopen = (evt) => {
emit(evt);
};
socket.onclose = (evt) => {
emit(evt);
};
socket.onerror = (evt) => {
emit(evt);
};
const unsubscribe = () => {
socket.onmessage = null;
};
return unsubscribe;
})
};
}
function* routeSocket(token): any {
const { socketChannel, socket } = yield call(createSocketChannel, token);
yield fork(socketChannelListener, socketChannel, socket);
}
// this method will be retried
export function* RouteSocketSaga(): any {
let task;
while (true) {
const routeSocketAction = yield take([
'DB_INITIALIZED',
PERSIST_REHYDRATE,
ON_AUTH_CHANGE,
ROUTE_SOCKET_RETRY_ACTION_TYPE // retry will attempt to start the saga again..
]);
console.log(routeSocketAction);
if (routeSocketAction.type === PERSIST_REHYDRATE) {
const currentRoute = yield select(driverRouteState);
if (currentRoute) {
yield fork(dispatchPhotoFetch, currentRoute);
}
}
// if the action is to retry, we will wait 5 seconds..
if (routeSocketAction.type === ROUTE_SOCKET_RETRY_ACTION_TYPE) {
yield delay(WAIT_BEFORE_RETRY_TIME);
}
const { token } = yield select(authState);
if (token) {
if (task) {
yield cancel(task);
}
task = yield fork(routeSocket, token);
}
}
}
Component code sending the location
BackgroundGeolocation.configure({
desiredAccuracy: BackgroundGeolocation.HIGH_ACCURACY,
stationaryRadius: 1,
distanceFilter: 1,
notificationTitle: 'Background tracking',
debug: false,
startOnBoot: false,
url: null,
stopOnTerminate: true,
locationProvider: BackgroundGeolocation.ACTIVITY_PROVIDER,
interval: 1000,
fastestInterval: 1000,
activitiesInterval: 1000,
startForeground: false,
stopOnStillActivity: false
});
BackgroundGeolocation.on('background', () => {
console.log('[INFO] App is in background');
setAppState(true);
});
BackgroundGeolocation.on('foreground', () => {
console.log('[INFO] App is in foreground');
setAppState(false);
});
BackgroundGeolocation.on('location', (location) => {
dispatch(onLocationChange({ latitude: location.latitude, longitude: location.longitude }));
console.log('LOCATION', location);
});
BackgroundGeolocation.on('stationary', (stationaryLocation) => {
console.log('STATIONARY LOCATION', stationaryLocation);
});
Ended up moving socket logic to the root component of my app and responding to location updates there which allowed me to keep the socket alive in the background and send location updates. E.g
locationSendingEventHandler = (location) => {
const {
coords: { latitude, longitude }
} = location;
console.log('LOCATION', location);
const { token } = this.props;
let socketReopenTriggered = false;
if ((!this.socket || (this.socket && this.socket.readyState >= 2)) && token) {
// 2: CLOSING 3: CLOSED ... two cases where location event should trigger socket to re-open and there is a token
socketReopenTriggered = true;
if (this.setupSocketTimeout) {
clearTimeout(this.setupSocketTimeout);
this.setupSocketTimeout = null;
}
this.setUpSocket('from location');
}
if (!socketReopenTriggered && this.socket && this.socket.readyState === 1) {
console.log('SENDING LOCATION', location, this.socket);
this.socket.send(
JSON.stringify({
type: 'DRIVER_LOCATION',
data: { token, ...{ lat: latitude, lng: longitude } }
})
);
}
};

LoginManager.logInWithReadPermissions is not calling callback in react-native-fsdk

I am trying to use react-native-fsdk for login in android. i have install and setup everything in my application. But when i click on ligon button, its open facebook and ask for permission. after giving permission its not giving any data or any error. I have tried any solution but not get success.
I am testing this in emulator in android
This is Login code :
LoginManager.logInWithReadPermissions(['public_profile', 'email', 'user_birthday', 'user_likes']).then(
function(result) {
if (result.isCancelled) {
alert('Login was cancelled');
} else {
alert('Login was successful with permissions: '
+ result.grantedPermissions.toString());
}
},
function(error) {
alert('Login failed with error: ' + error);
}
);
This is my maainApplication.java
import com.facebook.CallbackManager;
import com.facebook.reactnative.androidsdk.FBSDKPackage;
public class MainApplication extends NavigationApplication {
private static CallbackManager mCallbackManager = CallbackManager.Factory.create();
protected static CallbackManager getCallbackManager() {
return mCallbackManager;
}
#Override
public boolean isDebug() {
// Make sure you are using BuildConfig from your own application
return BuildConfig.DEBUG;
}
protected List<ReactPackage> getPackages() {
// Add additional packages you require here
// No need to add RnnPackage and MainReactPackage
return Arrays.<ReactPackage>asList(
// eg. new VectorIconsPackage()
new FBSDKPackage(mCallbackManager)
);
}
#Override
public List<ReactPackage> createAdditionalReactPackages() {
return getPackages();
}
}
MainActivity.java
import android.content.Intent;
import com.facebook.CallbackManager;
import com.facebook.FacebookSdk;
import com.facebook.reactnative.androidsdk.FBSDKPackage;
public class MainActivity extends SplashActivity {
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data);
}
}
Any suggestion would be appreciate. Thanks
Try this:
import { LoginManager, LoginButton, AccessToken, GraphRequest, GraphRequestManager } from 'react-native-fbsdk';
export function facebookLogin() {
return new Promise((resolve, reject) => {
LoginManager.logInWithReadPermissions(["public_profile, email"]).then(function (result) {
AccessToken.getCurrentAccessToken().then((data) => {
const accessToken = data.accessToken;
const responseInfoCallback = (error, result) => {
if (error) {
console.log(error);
console.log('Error fetching data=', error);
} else {
console.log("face",reslet imagePath = null;
};
const infoRequest = new GraphRequest(
'/me',
{
accessToken,
parameters: {
fields: {
string: 'email,name,first_name,middle_name,last_name, picture.type(large)',
},
},
},
responseInfoCallback,
);
new GraphRequestManager().addRequest(infoRequest).start();
});
}, function (error) {
console.log("Login fail with error: " + error);
resolve(false);
});
})
}
logInWithReadPermissions is a method from react-native-fbsdk version 0.8.0.
You should use logInWithPermissions since you have react-native-fbsdk version 0.10.0.

serverconnection.onmessage=gotMessageFromServer never called after createOffer

I'm trying to connect to a browser for video chat using WebRTC and websockets.
I'm able to createoffer, get the ice candidates but the function (where I expect a remote stream response) serverconnection.onmessage is never called.
in my index.html
function pageReady() {
localVideo = document.getElementById("localVideo");
remoteVideo = document.getElementById("remoteVideo");
serverConnection = new WebSocket('ws://localhost:3434');
serverConnection.onmessage = gotMessageFromServer;
var constraints = {
video: true,
audio: true,
};
if (navigator.getUserMedia) {
navigator.getUserMedia(constraints, getUserMediaSuccess, getUserMediaError);
} else {
alert('Your browser does not support getUserMedia API');
}
}
function start(isCaller) {
peerConnection = new RTCPeerConnection(peerConnectionConfig, optional);
console.log('the peer connection is', peerConnection);
peerConnection.onicecandidate = gotIceCandidate;
peerConnection.onaddstream = gotRemoteStream;
peerConnection.addStream(localStream);
if (isCaller)
peerConnection.createOffer(gotDescription, createOfferError);
}
function gotMessageFromServer(message) {
console.log('message is', json);
if (!peerConnection) start(false);
var signal = JSON.parse(message.data);
if (signal.sdp) {
console.log('message is signal.sdp', signal.sdp);
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp), function () {
peerConnection.createAnswer(gotDescription, createAnswerError);
});
} else if (signal.ice) {
console.log('message is signal.ice', signal.ice);
peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice));
}
}
server.js
var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({ noServer: true });
var http = require('http'),
fs = require('fs');
fs.readFile('../index.html', function (err, html) {
if (err) {
throw err;
}
var server = http.createServer(function (request, response) {
response.writeHeader(200, { "Content-Type": "text/html" });
response.write(html);
response.end();
})
server.listen(3434);
server.on('upgrade', function (req, socket, head) {
wss.handleUpgrade(req, socket, head, function (client) {
});
});
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send(message);
});
});