Passing Username and UserID Parameter to WebChat using WebChat Hooks API - javascript

I used to be able to pass the Username and UserID parameter from the URI using the below code on Webchat:
var queryString = window.location.search;
var urlParams = new URLSearchParams(queryString);
var user = urlParams.get('userid')
var usern = urlParams.get('username')
and I pass it on to the Webchat using the below code:
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token: }),
store,
userID: user,
username: usern,
styleOptions
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
Now this works perfectly before when I was using the usual Webchat for the Botframework. Its no longer working when I started using ReachJS on the Webchat. How do I pass this parameter using the Webchat Hooks API? I tried specifying it using the below code but does not work.
ReactDOM.render(
<Composer directLine={window.WebChat.createDirectLine({ token: })}>
<BasicWebChat />
<SendMessageOnConnect />
<user />
<usern />
</Composer>,
document.getElementById('webchat')
);

This works in my React project. You're implementation is different, but this should put you in the right direction. You can see in the listed activity that the user name is updated to 'Steve'. In my case, id is not updated because I am generating an id with the token. When an id is generated via Direct Line with the token, it takes precedence over any id's passed via Web Chat.
import React from 'react';
import ReactWebChat, { createDirectLine } from 'botframework-webchat';
export default class WebChatView extends React.Component {
constructor(props) {
super(props);
this.state = {
user: '',
usern: ''
};
}
componentDidMount() {
var queryString = window.location.search;
var urlParams = new URLSearchParams(queryString);
var user = urlParams.get('userid');
var usern = urlParams.get('username');
this.setState({ user: user, usern: usern })
this.fetchToken();
}
async fetchToken() {
const res = await fetch('http://localhost:3500/directline/token', { method: 'POST' });
const { token } = await res.json();
this.setState(() => ({
directLine: createDirectLine({ token: token })
}));
}
render() {
return (
this.state.directLine ?
<ReactWebChat
directLine={this.state.directLine}
userID={this.state.user}
username={this.state.usern}
/>
:
<div>Connecting to bot…</div>
)
}
}
Activity output:
{
type: 'message',
id: '4wpfp......-f|0000002',
timestamp: 2020-09-02T21:39:01.197Z,
serviceUrl: 'https://directline.botframework.com/',
channelId: 'directline',
from: { id: 'dl_15990824712200.ruhpue7p1j', name: 'Steve', role: 'user' },
conversation: { id: '4wpfp......-f' },
recipient: { id: 'botberg#QaeuoeEamLg', name: 'Bot' },
textFormat: 'plain',
locale: 'en-US',
text: 'Hi',
entities: [
{
type: 'ClientCapabilities',
requiresBotState: true,
supportsListening: true,
supportsTts: true
}
],
channelData: {
clientActivityID: '1599082740974mexnvax1jq',
clientTimestamp: '2020-09-02T21:39:00.974Z'
},
rawTimestamp: '2020-09-02T21:39:01.1971653Z',
callerId: 'urn:botframework:azure'
}
Hope of help!

You can try with useParams() hook instead as:
let { userid, username } = useParams();
Or rename as the following:
let { userid: user, username: usern } = useParams();
Read further in the documentation here which states:
useParams returns an object of key/value pairs of URL parameters. Use it to access match.params of the current <Route>.
Or you can also use useLocation() hook as:
const { search } = useLocation();
const { userid, username } = search;
Read from the documentation:
The useLocation hook returns the location object that represents the current URL. You can think about it like a useState that returns a new location whenever the URL changes.

Related

How can I route with array of objects in Next JS?

I've been struggling to route through pages using next js route query method. I was passing raw array of objects but it became empty in the page. I've been researching and I found a solution to use JSON.stringify(result) and parsing it in the other page with JSON.parse(query.result) and this worked. but when I use this approach on page reload the page crashes and the browser displays the following error.
This page isn’t workingIf the problem continues, contact the site owner.
HTTP ERROR 431
my code in the index.js is
<Link href={{
pathname: "/TvShows",
query: {
result: JSON.stringify(result),
//result: [result],
img: img,
index: index,
}}}
//as={"/TvShows"}
>
and the target page is Tvshows.js
first I defined a constants
const [result, setResult] = useState([]);
const [index, setIndex] = useState("");
const [img, setImg] = useState("");
const router = useRouter();
const query = router.query;
then
useEffect (()=>{
if(router.isReady){
//console.log(result);
const res = JSON.parse(query.result);
setResult(res);
setIndex(query.index);
setImg(query.img);
//console.log(res);
//router.isReady= false;
}
},[router.isReady])
the problem is when I stringily and parse these JSON. why is this happening?
and NOTE: in the url section of the page it uses the datas and it is super long.(maybe if that has an effect on it somehow). what is the best way to pass an array to a page?
the local data being passed in JSON.strimgify(result)
const Shows = [
{
id: 1,
title: "title1",
img: "imgURL",
desc: "ddescription1"
},
{
id: 2,
title: "title1",
img: "imgURL",
desc: "ddescription1"
},
{
id: 3,
title: "title1",
img: "imgURL",
desc: "ddescription1"
},
{
id: 4,
title: "title1",
img: "imgURL",
desc: "ddescription1"
},
] export default Shows;
Interestingly, I couldn't use the word 'code' as a query parameter in my project, so I renamed it to codeTerm. First, I suggest you to stay away some keywords like 'index' or 'code' etc.
In your TvShows component, try to console query object like below
import { withRouter } from 'next/router';
const TvShows = (props) => {
// the rest of your code
useEffect(() => {
console.log(props.router.query);
if (props.router.query.result)
console.log(JSON.parse(props.router.query.result));
}, [props.router]);
// the rest of your code
}
export default withRouter(TvShows);
You do not need that
const router = useRouter();
const query = router.query;
UPDATE
Can you try to route user using next/router instead of next/link?
import Router from 'next/router';
Router.push(
{
pathname: '/TvShows',
query: {
result: JSON.stringify(result),
img: img,
index: index,
},
}
);
This should give you what you want, here's the result

How can I disable an button/div for a certain amount of time, while using getTime() logic from the database, using React/Node.js

I want to disable a div for a certain period of time. Im trying the whole day to make this work, but somehow I am stuck. I don't want to use some front end logic like setTimeout() and add/remove classes or disable pointer-event. The logic I am trying to implement is to share a key with the current time when the document was posted to the database timeCreateInMS: { default: new Date().getTime(), in miliseconds and when I try to press the button once more to share I'll should first get all the documents from the mongo db, filter them and find all the documents that have been posted on the DB within the last 5 minutes with this kind of logic const filteredArray = array.filter((a) => {a.timeCreateInMS > new Date().getTime() - 300000}), but for some reason it doeasn't work, either I misunderstood how getTime() works or I made a major error somewhere along the line. Here is the important code used.
Getting the Data from the DB on the backend
export const getLocations = async (req, res) => {
try {
const locations = await shareLocation.find({});
res.status(200).json(locations);
} catch (error) {
console.log(error);
res.status(409).json("Unable to fetch Locations");
}
};
The schema I am using for the MongoDB
import mongoose from "mongoose";
const locationSchema = mongoose.Schema({
timeShared: {
type: String,
required: true,
},
dateShared: {
type: String,
required: true,
},
latitude: {
type: String,
required: true,
},
longitude: {
type: String,
required: true,
},
city: {
type: String,
},
road: {
type: String,
},
created_at: {
type: Date,
},
timeCreateInMS: {
type: String,
default: new Date().getTime(),
},
});
locationSchema.index({ created_at: 1 }, { expires: 10800 });
export default mongoose.model("shareLocation", locationSchema);
and the frontend logic using react
import styles from "./ShareButton.module.css";
import { useState, useContext, useRef, useEffect, useCallback } from "react";
import ShareIcon from "./ShareIcon";
import DataContext from "../context/DataContext";
import axios from "axios";
function ShareButton() {
const [fetchedLocations, setFetchedLocations] = useState([]);
const [filterShared, setFilterShared] = useState([]);
const [dateRN, setDateRN] = useState(new Date().getTime());
const { createLocation } = useContext(DataContext);
const getLocations = useCallback(async (setFetch) => {
const { data } = await axios.get("http://localhost:5000/radar");
setFetch(data);
}, []);
const submitLocationHandler = async () => {
if (filterShared.length <= 0) {
createLocation();
setDateRN(new Date().getTime());
} else {
console.log(
"Already Shared in the last 5 mintues"
);
}
};
useEffect(() => {
getLocations(setFetchedLocations);
}, [getLocations]);
useEffect(() => {
setDateRN(new Date().getTime());
setFilterShared(
fetchedLocations.filter(
(location) => parseInt(location.timeCreateInMS) > parseInt(dateRN) - 300000
)
);
}, [fetchedLocations]);
const onClickHandler = () => {
submitLocationHandler();
getLocations(fetchedLocations);
};
return (
<div className={styles.share_button_container}>
<div onClick={onClickHandler} }>
</div>
</div>
);
}
export default ShareButton;
p.s the createLocation is taken from another document from the Context API, not important in this scenario, works as expected.
I cannot wrap my head around, why this doesn't work, from what I am trying to achieve is when posting once, there should be a timeout for 5 minutes and once the time passes the user should be able to post once more, but for some reason, the document that was posted in the last 5 min is not filtered in the logic above.
Anyone has an idea where I made the error, or has any better way to disable the component for a certain amount of time, getting the information from the database if possible, so the logic wont be interrupted when frontend or backend server is down for some sec, I thought the best way is to make the condition from the DB itself

How can I add the currently logged in User as a field in Firestore

I am submitting a form to Google Firestore Daatabase and I would like to add a field where the currently logged in user is added to the array that I am pushing.
I have tried using firebase.auth().curentUser but I cannot seem to get it to work. Sometimes it says undefined or just crashes my app. I need to get the firebase current user stored in a variable and then push that variable to Firestore.
Basically my problem is I can't figure out how to use variables when I am trying to write to the database for the current user. Thanks for any and all help, I need all I can get.
currentUser
firebase.auth().currentUser
some other user.uid code I saw etc.
<script>
import db from "#/components/fbInit";
import firebase from 'firebase'
export default {
name: "Popup",
data() {
return {
dialog: false,
name: "",
employee_id: "",
company: "",
state: "",
status: "",
userId: ""
};
},
computed: {
userId() {
return firebase.auth().currentUser
}
},
methods: {
submit() {
const users = {
employee_id: this.employee_id,
company: this.company,
state: this.state,
status: this.status,
name: this.name,
userId: this.userId
};
db.collection("users")
.add(users)
.then(() => {
alert("Succesfully added to database");
});
}
}
};
</script>
To get the user's uid you have to use your firebase.auth().currentUser like this:
userId() { return firebase.auth().currentUser.uid}
And then you can pass it to your user const.
const users = {
employee_id: this.employee_id,
company: this.company,
state: this.state,
status: this.status,
name: this.name,
userId: this.userId
};

apollo-link-state: How to write Query resolvers?

I create my state link with defaults values, something like this:
const stateLink = withClientState({
cache,
resolvers,
defaults: {
quote: {
__typename: 'Quote',
name: '',
phoneNumber: '',
email: '',
items: []
}
}
})
So my cache should not be empty. Now my resolvers map looks like this:
resolvers = {
Mutation: { ... },
Query: {
quote: (parent, args, { cache }) => {
const query = gql`query getQuote {
quote #client {
name phoneNumber email items
}
}`
const { quote } = cache.readQuery({ query, variables: {} })
return ({ ...quote })
}
}
}
The datasource of my resolvers is the cache right ? so I have to query the cache somehow. But this is not working, I guess it is because I am trying to respond to quote query, and for that I am making another quote query.
I think I should get the quote data without querying for quote, but how ?
I am getting this error:
Can't find field **quote** on object (ROOT_QUERY) undefined
Please help
Just wanted to post the same question - and fortunatly just figured it out.
readQuery-Methode only allows you to query from root. So instead you should use readFragment, because it allows you to access any normalized field in the cache, as long you got it's id (Something like this: GraphQlTypeName:0 typically constructed from the fields: id and __typename ). Your Query-Resolver should then look something like this:
protected resolvers = {
Query: {
getProdConfig: (parent, args, { cache, getCacheKey }) => {
const id = getCacheKey({ __typename: 'ProdConfig', id: args.id });
const fragment = gql`fragment prodConfig on ProdConfig {
id,
apiKey,
backupUrl,
serverUrl,
cache,
valid
}`;
const data = cache.readFragment({ fragment, id })
return ({ ...data });
}
}
and the call from apollo like:
let query = this.$apollo.query(`
query prodConfig($id: Int!) {
getProdConfig(id: $id) #client {
apiKey,
backupUrl,
serverUrl,
cache,
valid
}
}`,
{ id: 0 }
);

react redux merge state with given array

Lets say I have a reducer which is like :
const initialState = [
{
accessToken: null,
isLoggedIn: false,
}
]
export default function my_reducer(state = initialState, action) {
switch (action.type) {
case LOGIN:
return state.merge(user: action) ---> how to handle this
and the output should be like:
[
{
accessToken: null,
isLoggedIn: false,
user: {
name: 'some name',
email: 'some email'
}
]
In action I am getting a array which I am providing by doing JSON.stringify(response)
previous data should not be changed and new data should be updated
The ES6 Way
To create a new object with the state in ES6 we can use the spread operator. Like this
...
case ActionType.SUCCESS_GET_DATA : {
let newState = { ...state, [action.uniqueKey]: action.payload };
return state.merge(newState);
}
...
I did the uniqueKey part as a variable because you will want a unique name for your state.
IMO this syntax is much easier to understand than the Object.assign
You can use Object.assign() function:
var state = {
accessToken: null,
isLoggedIn: false,
};
var user = {
name: 'some name',
email: 'some email'
};
var newState = Object.assign({}, state, {user});
console.log(newState);
First I see that your state is actually an array, but I think you would need an object right?
So it would be:
const initialState = {
accessToken: null,
isLoggedIn: false,
}
(requires Babel) So with spread operator you can:
return {
...initialState,
user: {
name: '...',
surname: '...'
}
};
Or if you do not transpile via Babel alike:
return Object.assign({}, initialState, {
user: {
name: '...',
surname: '...'
}
});
Using ES6 spread syntax
...
case 'ACTION_TYPE_A': {
return { ...state, action.key: action.value };
}
...
This will return the merged state by updating the 'key' if it exists in the original state.
Everything according to new Es6 format : )
A total addToDo reducer function where the data is appended to the previous state. And you get the output of a new state data : )
export const addToDo = (state, action) => {
const { name, email, phone, image,key } = action;
var data = [...state.data];
var newData = {
name: name, email: email, phone: phone, image: image,key:key
}
data.push(newData)
return(
state.merge({
data : data
})
)};
Happy coding.

Categories