I am setting up the ability to disconnect the session from my wallet app in React-Native. My code looks like this which matches the docs except that I am importing "getSdkError" from WalletConnectUtils:
import { Core } from "#walletconnect/core";
import { Web3Wallet } from "#walletconnect/web3wallet";
import "#walletconnect/react-native-compat";
import * as WalletConnectUtils from '#walletconnect/utils';
import { CONNECT_WALLET_PROJECT_ID } from '#env';
const core = new Core({
projectId: CONNECT_WALLET_PROJECT_ID
});
const metadata = {
name: '<company name>',
description: 'A Wallet Application',
url: "#",
icons: []
};
const web3wallet = await Web3Wallet.init({
core,
metadata: metadata
});
const disconnect = await web3wallet.disconnectSession({
topic: topic,
reason: WalletConnectUtils.getSdkError("USER_DISCONNECTED"),
});
To completely match the docs I have also tried this:
const disconnect = await web3wallet.disconnectSession({
topic,
reason: WalletConnectUtils.getSdkError("USER_DISCONNECTED"),
});
When I run this though I get the error:
Object {
"context": "core",
}, Object {
"context": "core/pairing",
}, Object {
"code": 10001,
"message": "Unsupported wc_ method. wc_pairingDelete",
}
I checked to see what WalletConnectUtils.getSdkError("USER_DISCONNECTED") returns and got this:
Object {
"code": 6000,
"message": "User disconnected.",
}
I've been using https://react-app.walletconnect.com/ to test and it doesn't seem to disconnect on that site when I run this so I want to make sure I am disconnecting successfully.
Related
I am trying to fetch some data from an API, do some data formatting, and then feed that data to my Chart component but I get the following error when trying to do so:
Uncaught (in promise) Error: Maximum recursive updates exceeded. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.
This is how my code looks like:
<template>
<apexchart type="rangeBar" :options="chartOptions" :series="sceneAttributes"></apexchart>
</template>
<script>
import { ref } from 'vue';
setup() {
const sceneAttributes = ref([]);
fetch(`apiUrl`)
.then(resp => resp.json())
.then(data => {
// const atts = [];
for (const key in data.sceneAttributeValues) {
data.sceneAttributeValues[key].forEach(att => {
let obj = {};
obj.data = [];
obj.data.push({
x: att.attributeValue.attributeId,
y: [att.from, att.to+1]
})
// obj.from = att.from;
// obj.to = att.to;
if (att.attributeValue.strValue) {
obj.name = att.attributeValue.strValue;
}
if (att.attributeValue.intValue) {
obj.name = att.attributeValue.intValue;
}
if (att.attributeValue.boolValue) {
obj.name = att.attributeValue.boolValue;
}
sceneAttributes.value.push(obj)
})
}
// End of first for loop
});
return { sceneAttributes }
}
}
I have to do this convoluted data formatting because the raw data I get from the fetched JSON.
{
"sceneAttributeValues": {
"Object Distance": [
{
"attributeValue": {
"attributeId": "Object Distance",
"intValue": 39
},
"from": 29,
"to": 123
}
],
"Object Detection": [
{
"attributeValue": {
"attributeId": "Object Detection",
"strValue": "Yes"
},
"from": 79,
"to": 122
}
],
}
}
Any hint on what I'm doing wrong would be greatly appreciated.
I guess using AXIOS for requesting an API might help you
Here is the document on how to use axios in Vue : Using Axios to Consume APIs
Don't forget to install and import axios before using it , To install axios you need to run this command in your project directory :
npm i axios
Here you can find more info about axios : axios-npm
I'm trying to upload an image and some data to my API with Axios on my Expo app. Here's the code:
// api.ts
import axios from 'axios'
const api = axios.create({
baseURL: 'http://999.999.999.999/api', // <-- my cloud server ip
responseType: 'json'
})
export default api
// register.ts
import FormData from 'form-data' // https://www.npmjs.com/package/form-data
import api from '../services/api'
const options = { headers: 'Content-Type': 'multipart/form-data' } }
export const register = async (formData: FormData): Promise<boolean> => {
try {
const response = api.post<{ error: boolean, data: string }>(
'/authentication/register',
formData,
options
)
return Boolean(response.data?.data)
} catch (error) {
return false
}
}
I'm also using the expo-image-picker and appending the result to FormData:
const result = await ImagePicker.launchCameraAsync({})
if (result.cancelled) {
return
}
const personalPhotoUriParts = result.uri.split('.')
const personalPhotoFileType = personalPhotoUriParts[personalPhotoUriParts.length - 1]
const formData = new FormData()
formData.append('email', 'teste#mail.com')
formData.appent('password', '123456')
formData.append('personal_photo', {
uri: result.uri,
name: `personal_photo.${personalPhotoFileType}`,
type: `image/${personalPhotoFileType}`
})
register(formData) // from register.ts
All the things work as expected on the emulator, send and print from PHP API body content I get:
// var_dump($_POST);
Array [
"email": "test#mail.com",
"password": "123456"
]
// var_dump($_FILES)
Object {
"personal_photo": Object {
"error": 0,
"name": "document_photo.jpg",
"size": 135327,
"tmp_name": "/tmp/php5o11qT",
"type": "image/jpg",
}
}
But when I try to send it from a physical device, I get an empty body on both cases:
// var_dump($_POST);
Array []
// var_dump(get_file_contents('php://input'));
// nothing
// var_dump($_FILES);
Array []
My bet is on FormData, I was using other API routes without problems (on physical device), but when I had to use multipart/form-data I've got this problem. For me, does not make sense since the same API worked with React JS (WEB), Postman, and RN w/ Expo on emulator.
Maybe you need to enable on your Android Manifest
android:usesCleartextTraffic="true"
your problem is on Android or iOS device ?
I'm creating a REST API using AWS CDK version 1.22 and I would like to document my API using CDK as well, but I do not see any documentation generated for my API after deployment.
I've dived into aws docs, cdk example, cdk reference but I could find concrete examples that help me understand how to do it.
Here is my code:
const app = new App();
const api = new APIStack(app, 'APIStack', { env }); // basic api gateway
// API Resources
const resourceProps: APIResourceProps = {
gateway: api.gateway,
}
// dummy endpoint with some HTTP methods
const siteResource = new APISiteStack(app, 'APISiteStack', {
env,
...resourceProps
});
const siteResourceDocs = new APISiteDocs(app, 'APISiteDocs', {
env,
...resourceProps,
});
// APISiteDocs is defined as follow:
class APISiteDocs extends Stack {
constructor(scope: Construct, id: string, props: APIResourceProps) {
super(scope, id, props);
new CfnDocumentationVersion(this, 'apiDocsVersion', {
restApiId: props.gateway.restApiId,
documentationVersion: config.app.name(`API-${config.gateway.api.version}`),
description: 'Spare-It API Documentation',
});
new CfnDocumentationPart(this, 'siteDocs', {
restApiId: props.gateway.restApiId,
location: {
type: 'RESOURCE',
method: '*',
path: APISiteStack.apiBasePath,
statusCode: '405',
},
properties: `
{
"status": "error",
"code": 405,
"message": "Method Not Allowed"
}
`,
});
}
}
Any help/hint is appreciated, Thanks.
I have tested with CDK 1.31 and it is possible to use the CDK's default deployment option and also add a document version to the stage. I have used the deployOptions.documentVersion in rest api definition to set the version identifier of the API documentation:
import * as cdk from '#aws-cdk/core';
import * as apigateway from "#aws-cdk/aws-apigateway";
import {CfnDocumentationPart, CfnDocumentationVersion} from "#aws-cdk/aws-apigateway";
export class CdkSftpStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const documentVersion = "v1";
// create the API
const api = new apigateway.RestApi(this, 'books-api', {
deploy: true,
deployOptions: {
documentationVersion: documentVersion
}
});
// create GET method on /books resource
const books = api.root.addResource('books');
books.addMethod('GET');
// // create documentation for GET method
new CfnDocumentationPart(this, 'doc-part1', {
location: {
type: 'METHOD',
method: 'GET',
path: books.path
},
properties: JSON.stringify({
"status": "successful",
"code": 200,
"message": "Get method was succcessful"
}),
restApiId: api.restApiId
});
new CfnDocumentationVersion(this, 'docVersion1', {
documentationVersion: documentVersion,
restApiId: api.restApiId,
description: 'this is a test of documentation'
});
}
}
From what I can gather, if you use the CDK's default deployment options which create stage and deployment on your behalf, it won't be possible to append the stage with a documentation version set.
Instead, the solution would be to set the RESTAPI's option object to deploy:false and define the stage and deployment manually.
stack.ts code
import * as cdk from '#aws-cdk/core';
import * as apigateway from '#aws-cdk/aws-apigateway';
import { Stage, Deployment, CfnDocumentationPart, CfnDocumentationVersion, CfnDeployment } from '#aws-cdk/aws-apigateway';
export class StackoverflowHowToDocumentRestApiUsingAwsCdkStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// create the API, need to not rely on CFN's automatic deployment because we need to
// make our own deployment to set the documentation we create
const api = new apigateway.RestApi(this, 'books-api',{
deploy: false
});
// create GET method on /books resource
const books = api.root.addResource('books');
books.addMethod('GET');
// // create documentation for GET method
const docpart = new CfnDocumentationPart(this, 'doc-part1', {
location: {
type: 'METHOD',
method: 'GET',
path: books.path
},
properties: JSON.stringify({
"status": "successful",
"code": 200,
"message": "Get method was succcessful"
}),
restApiId: api.restApiId
});
const doc = new CfnDocumentationVersion(this, 'docVersion1', {
documentationVersion: 'version1',
restApiId: api.restApiId,
description: 'this is a test of documentation'
});
// not sure if this is necessary but it made sense to me
doc.addDependsOn(docpart);
const deployment = api.latestDeployment ? api.latestDeployment: new Deployment(this,'newDeployment',{
api: api,
description: 'new deployment, API Gateway did not make one'
});
// create stage of api with documentation version
const stage = new Stage(this, 'books-api-stage1', {
deployment: deployment,
documentationVersion: doc.documentationVersion,
stageName: 'somethingOtherThanProd'
});
}
}
OUTPUT:
Created a feature request for this option here.
I had the same exact problem. The CfnDocumentationVersion call has to occur after you create all of your CfnDocumentationPart. Using your code as an example, it should look something like this:
class APISiteDocs extends Stack {
constructor(scope: Construct, id: string, props: APIResourceProps) {
super(scope, id, props);
new CfnDocumentationPart(this, 'siteDocs', {
restApiId: props.gateway.restApiId,
location: {
type: 'RESOURCE',
method: '*',
path: APISiteStack.apiBasePath,
statusCode: '405',
},
properties: JSON.stringify({
"status": "error",
"code": 405,
"message": "Method Not Allowed"
}),
});
new CfnDocumentationVersion(this, 'apiDocsVersion', {
restApiId: props.gateway.restApiId,
documentationVersion: config.app.name(`API-${config.gateway.api.version}`),
description: 'Spare-It API Documentation',
});
}
}
I was following this article here (which is not complete unfortunately) in attempt to learn how to friend Ionic 3 based PWA and Firebase Cloud Messaging: Push Notifications with FCM
What I did:
as advised in the article added FCM libraries into service-worker.js:
'use strict';
importScripts('./build/sw-toolbox.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': '47286327412'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler((payload) => {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the service sending messages
const notificationOptions = {
icon: '/assets/img/appicon.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.toolbox.options.cache = {
name: 'ionic-cache'
};
// pre-cache our key assets
self.toolbox.precache(
[
'./build/main.js',
'./build/vendor.js',
'./build/main.css',
'./build/polyfills.js',
'index.html',
'manifest.json'
]
);
// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);
// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;
Then created Firebase Messaging based provider here:
import { Injectable } from "#angular/core";
import * as firebase from 'firebase';
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging: firebase.messaging.Messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage
) {
this.messaging = firebase.messaging();
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
And now during app initialization I call enableNotifications() and get error that says that default service worker is not found (404):
A bad HTTP response code (404) was received when fetching the script.
:8100/firebase-messaging-sw.js Failed to load resource: net::ERR_INVALID_RESPONSE
If I move service-worker.js firebase related stuff into default service worker in WWW folder - I get general error from Firebase (Error, failed to register service worker).
QUESTIONS:
- is there a fresh guide on Ionic 3's PWA & FCM?
- at high level what is the difference in registering service workers in Ionic 3 vs Angular? I did watch the tutorial about Angular but can't figure how to do the same in Ionic 3.
UPDATE: the below is valid as of today (02/12/2018) and most likely will be less relevant once AngularFire2 supports messaging module. So take the below with that assumption...
OK I researched and finally made it work on my Ionic 3 PWA, so I am posting solution here:
Prerequisites:
I created ionic blank app (just a home page)
installed angularfire2 and firebase ("angularfire2": "5.0.0-rc.4","firebase": "4.9.1") using npm install, I used specifically 5.0.0-rc.4" cause I had stability issues with latest one;(
created config (filename environment.ts in src folder):
export const firebaseConfig = {
apiKey: "Your Stuff Here from FB",
authDomain: "YOURAPPNAME.firebaseapp.com",
databaseURL: "https://YOURAPPNAME.firebaseio.com",
projectId: "YOURAPPNAME",
storageBucket: "YOURAPPNAME.appspot.com",
messagingSenderId: "FROMFIREBASECONEOLE"
};
I modified app.module.ts to add firebase and angularfire2 this way:
...
import { AngularFireModule } from 'angularfire2';
import 'firebase/messaging'; // only import firebase messaging or as needed;
import { firebaseConfig } from '../environment';
import { FirebaseMessagingProvider } from '../providers/firebase-messaging';
...
#NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [
FirebaseMessagingProvider,
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
Here we also import our provider whose code is below:
in providers folder I created firebase-messaging.ts like this:
import { Injectable } from "#angular/core";
import { FirebaseApp } from 'angularfire2';
// I am importing simple ionic storage (local one), in prod this should be remote storage of some sort.
import { Storage } from '#ionic/storage';
#Injectable()
export class FirebaseMessagingProvider {
private messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage,
private app: FirebaseApp
) {
this.messaging = app.messaging();
navigator.serviceWorker.register('service-worker.js').then((registration) => {
this.messaging.useServiceWorker(registration);
//this.disableNotifications()
this.enableNotifications();
});
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
console.log(currentToken)
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
Please note I init the firebase app and then in constructor we register ionic's default service worker (service-worker.js) that contains the following right after whatever is there by default:
service-worker.js:
// firebase messaging part:
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging.js');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the Webtask
const notificationOptions = {
icon: '/assets/images/logo-128.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
At this point you also need to make sure you enabled your app as PWA, there is a good guide from Josh Morony and today there was a video stream on youtube that covers it. In TLDR you need to uncomment this in your index.html:
index.html in src uncomment:
<!-- un-comment this code to enable service worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>
OK almost the last thing - your manifest.json (in src) should have exact line:
"gcm_sender_id": "103953800507"
This concludes initial stuff on the client. Please note I didn't implement yet anything to handle notifications while user is in app itself, think for now it just handles when a message is sent from a server while your tab is not in focus (that is what I tested).
Now you want to go to your firebase console and obtain server key (click setting gear icon, then see cloud messaging section there). Copy server key. Also run the client (ionic serve and capture your local token (i just console.logged it). Now try sending yourself the message using a POST method. ( I did it with Postman)
// method: "POST",
//url: "https://fcm.googleapis.com/fcm/send",
// get the key from Firebase console
headers: { Authorization: `key=${fcmServerKey}` },
json: {
"notification": {
"title": "Message title",
"body": "Message body",
"click_action": "URL to your app?"
},
// userData is where your client stored the FCM token for the given user
// it should be read from the database
"to": userData.fcmRegistrationKey
}
So by doing all this I was able to reliable send myself a message WHILE the app was in background. I am yet to handle foreground but this SO question is about how to init default service worker and marry it with FCM.
I hope this will help some learners in future.
I have successfully implemented the process and got success response on API calls. But no notification popup coming on my browser. Any idea?
api: https://fcm.googleapis.com/fcm/send
response got:
{"multicast_id":6904414188195222649,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1545375125056264%e609af1cf9fd7ecd"}]}
cheth the attached url of my console:
My server responds with a 400 status if a book title already exists. The ember app catches the error and does a errors.add with the attribute name and error detail. For some reason when a receive the 400 when I change the title the form submit button is dead. I found that when I eliminate the errors.add line the problem goes away.
Here is my action:
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Route.extend({
model() {
return this.store.createRecord('book');
},
setupController: function(controller, model) {
controller.set('book', model);
controller.set('errors', DS.Errors.create());
},
actions: {
createBook: function(book) {
var _this = this;
var errors = _this.controllerFor('books.new').get('errors');
book.save().then(function(book) {
_this.transitionTo('books.book', book);
}).catch(function(response) {
response.errors.forEach(function(error) {
var attribute = error.source.pointer.split('/')[3];
//This line causes the problem---> errors.add(attribute, error.detail);
});
});
}
}
});
This is the response:
res.status(400).send({
errors: [
{
status: "400",
source: { pointer: '/data/attributes/title' },
title: "Invalid Attribute",
detail: 'must be unique'
}
]
});