Currently, I'm using the React version of WebChat on my site. (https://github.com/Microsoft/BotFramework-WebChat)
I'm building a feature to hide and show the WebChat element on a page. When the page first loads, the WebChat shows. When clicking on top of the WebChat (.chatbot-top-pane), the WebChat minimizes (.chat-window hides). When clicking on the div again to maximize the window (show .chat-window), the WebChat shows then tries to reconnect to the server, but fails. Why does it try to reconnect? Did the session somehow end in the background and it's trying to resume using the same token?
I tried using display: none, visibility: hidden on .chat-window.
<div className="chatbot-container">
<div className="chatbot-top-pane" onClick={handleToggleStatus.bind(this)}>
<p className="chatbot-pane-label-text">Chatbot Disco</p>
</div>
<ReactWebChat className="chat-window" directLine={createDirectLine({ token })} />
</div>
Also, I tried wrapping the .chat-window with a div and tried hiding .chat-window-container, instead:
<div className="chatbot-container">
<div className="chatbot-top-pane" onClick={handleToggleStatus.bind(this)}>
<p className="chatbot-pane-label-text">Chatbot Disco</p>
</div>
<div className="chat-window-container">
<ReactWebChat className="chat-window" directLine={createDirectLine({ token })} />
</div>
</div>
Below is a screenshot of the error message displayed on the WebChat
Error message on WebChat
I tried the same thing on the iframe version of the WebChat and had no issue at all.
When Web Chat dismounts, it triggers the DIRECT_LINE/DISCONNECT action to fire which causes DirectLine to disconnect. However, looking over your code, I'm not sure why you would have an issue with that especially since your implementation looks similar to the Minimizable WebChat Sample and you are not dismounting the Web Chat component.
I put together a simplified version of the sample that seems to work fine.
Web Chat v4
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import ReactWebChat, { createDirectLine, createStore } from 'botframework-webchat';
import classNames from 'classnames';
const WebChat = () => {
const [token, setToken] = useState('');
const [minimized, setMinimized] = useState(false);
const directLine = useMemo(() => createDirectLine({ token }), [token]);
const store = useMemo(() => createStore(), []);
const toggleChatVisivility = useCallback(() => setMinimized(minimized => !minimized), [setMinimized]);
useEffect(() => {
(async function () {
const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' });
const { token } = await res.json();
setToken(token);
})();
}, []);
return (
<React.Fragment>
<button onClick={toggleChatVisivility}>{minimized ? 'Show Chat' : 'Hide Chat'}</button>
<ReactWebChat className={classNames('webchat__chat-container', minimized ? 'webchat__hidden': 'webchat__visivle')} directLine={directLine} store={store} />
</React.Fragment>);
}
export default WebChat;
css
.webchat__chat-container {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.webchat__hidden {
visibility: hidden;
}
Screen capture
Hopefully the code snippets above help you figure out what's missing from your implementation. Sorry, I couldn't be more helpful. Let me know if you run into anymore questions. I'm happy to help answer them.
Related
I have a use case in which i have to show responses from an API to the user, as he types in the text view in the adaptive card, I have a Bot using Microsoft Bot Framework, my chat bot is currently working in a web client, but i want this functionality to also work on Microsoft Teams and other platforms (in which adaptive card is supported). Adaptive Card doesn't support AutoComplete view, its in there roadmap for now.
So here i am looking for a workaround to implement this functionality in my Bot.
If your main use case is Teams, then you have another option, but it will work only in Teams - it is to use a Task Module. Basically, Task Modules are a special kind of popup window in Teams. You have two options for what gets shown in a Task Module:
An Adaptive Card
Any custom web page, that is basically just being IFramed into the popup
So, you could look at using option 2 above, building anything you want into the web page, including "autocomplete". Later, if / when Adaptive Cards offers what you need, you could simply change out what gets shown to the user, from being your custom web page, to being the Adaptive Card.
You can read more about Task Modules, and how to get started, at What are Task Modules.
Of course, as I said, Task Modules only work in Teams, so it won't help your other scenarios, but perhaps you can use the regular Adaptive Cards there, and do the Task Module if you detect the client is using Microsoft Teams.
Although Adaptive Cards do not support autocomplete view as of now, instead of using Adaptive Cards, you can build our own component and use attachmentMiddleware to intercept those cards. You can add an input field with your autocomplete logic in your custom renderer.
Refer to this sample for the implementation of the above logic.
Code Sample:
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Web Chat: Custom attachment with GitHub Stargazers</title>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script src="https://unpkg.com/react#16.8.6/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.6/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-redux#7.1.0/dist/react-redux.min.js"></script>
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
<style>
html, body { height: 100% }
body { margin: 0 }
#webchat {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="webchat" role="main"></div>
<script type="text/babel">
const GitHubRepositoryAttachment = props =>
<div style={{ fontFamily: '\'Calibri\', \'Helvetica Neue\', Arial, sans-serif', margin: 20, textAlign: 'center' }}>
<svg height="64" viewBox="0 0 16 16" version="1.1" width="64" aria-hidden="true"><path fillRule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path></svg>
<p>
{ props.owner }/<br />{ props.repo }
</p>
</div>;
(async function () {
const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' });
const { token } = await res.json();
const { ReactWebChat } = window.WebChat;
const attachmentMiddleware = () => next => card => {
switch (card.attachment.contentType) {
case 'application/vnd.microsoft.botframework.samples.github-repository':
return <GitHubRepositoryAttachment owner={ card.attachment.content.owner } repo={ card.attachment.content.repo } />;
default:
return next(card);
}
};
window.ReactDOM.render(
<ReactWebChat
attachmentMiddleware={ attachmentMiddleware }
directLine={ window.WebChat.createDirectLine({ token }) }
/>,
document.getElementById('webchat')
);
store.dispatch({
type: 'WEB_CHAT/SET_SEND_BOX',
payload: { text: 'sample:github-repository' }
});
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));
</script>
</body>
</html>
Also, refer to this SO answer on Autocompletion with Botframework's Web Chat where you can add flexdatalist to WebChat's input field, but you also have to dispatch actions to WebChat's store to notify it of changes.
Hope this helps.
I am trying to make my own chatbot but am unsure of how to change the background image to my own jpg.
I have tried to place the code in the <body> and inside the styleOptions but none of my methods work.
Below is my html file:
<body style = "background-image: url('CorpBotOrchestrator/Images/whatsapp.jpg');">
<div id="webchat" role="main">Loading...</div>
<script>
styleOptions: {
bubbleFromUserBackground: 'LightBlue',
hideUploadButton: true,
botAvatarImage: 'https://learn.microsoft.com/en-us/azure/bot-service/v4sdk/media/logo_bot.svg?view=azure-bot-service-4.0',
//make the speech bubbles round
bubbleBorderRadius: 20,
bubbleFromUserBorderRadius: 20,
}
}, document.getElementById('webchat'));
})().catch(err => console.error(err));
</script>
</body>
Thank you in advance!
You will want to instantiate your web chat in the following way to change the background image of the activities window. Be aware that, in the BotFramework-WebChat styleSet object, there is activity which represents the individual messages, cards, etc., that are sent, and there is activities which represents the window of scrolling messages. It is the latter we will be working with.
Also, be aware, that because you are directly specifying a DOM element and property, albeit via web chat, should some aspect of web chat receive an update that changes the element or property used, this can cause a breaking change for you. Additionally, you will need to pass all the property values necessary as, generally, this strips all default values from the webchat element you are working with.
const styleSet = createStyleSet ( {
bubbleBackground: 'blue',
bubbleFromUserBackground: 'red',
bubbleBorderRadius: 18,
bubbleFromUserBorderRadius: 18,
} );
styleSet.activities = {
...styleSet.activities,
backgroundImage: "url('<someUrlLink>')";
}
window.WebChat.renderWebChat( {
directLine: [...],
styleSet
});
Hope of help!
EDIT
styleOptions can actually be passed in using the spread operator into styleSet. In this way, you can continue to use the ease of styleOptions for customizing and largely remain within best practices of Web Chat.
const styleOptions = {
bubbleBackground: 'blue',
bubbleFromUserBackground: 'red',
bubbleBorderRadius: 18,
bubbleFromUserBorderRadius: 18
}
const styleSet = createStyleSet ( {
...styleOptions,
} );
styleSet.activities = {
...styleSet.activities,
backgroundImage: "url('<someUrlLink>')";
}
window.WebChat.renderWebChat( {
directLine: [...],
styleSet
});
I have an image for my bot, but it's not showing up in the webchat.
Is there any way to make it happen like the image in this link (https://cloud.githubusercontent.com/assets/979837/19395693/cbdf6ac2-91f3-11e6-8a48-ba533bf91dca.png) ?
My script for the bot is:
<script>
//Scrip for the webchat window
(function () {
var div = document.createElement("div");
document.getElementsByTagName('body')[0].appendChild(div);
div.outerHTML = "<div id='botDiv' style='height: 38px; position: fixed; bottom: 0; right: 1%; z-index: 1000; background-color: #fff'><div id='botTitleBar' style='height: 38px; width: 350px; position:fixed; cursor: pointer;'></div><iframe width='400px' height='600px' src='https://webchat.botframework.com/embed/xxx'></iframe></div>";
document.querySelector('body').addEventListener('click', function (e) {
e.target.matches = e.target.matches || e.target.msMatchesSelector;
if (e.target.matches('#botTitleBar')) {
var botDiv = document.querySelector('#botDiv');
botDiv.style.height = botDiv.style.height == '600px' ? '38px' : '600px';
};
});
}());
</script>
I would recommend you step away from the webchat channel iFrame option and utilize the BotFramework-WebChat tool, instead. The webchat channel is fine for simple deployments, but it isn't overly robust out-of-the-bot.
If you look over sample 04.b.display-user-bot-images-styling from the BotFramework-WebChat repo, you will see precisely how you can effect the bot/user avatars.
In short, you will include the web chat CDN into you html file, assign some basic styling for the web chat div, followed by the script that connects to and generates that actual web chat experience.
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Web Chat: Avatar with images and initials</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--
For demonstration purposes, we are using the development branch of Web Chat at "/master/webchat.js".
When you are using Web Chat for production, you should use the latest stable release at "/latest/webchat.js",
or lock down on a specific version with the following format: "/4.1.0/webchat.js".
-->
<script src="https://cdn.botframework.com/botframework-webchat/master/webchat.js"></script>
<style>
html, body { height: 100% }
body { margin: 0 }
#webchat {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="webchat" role="main"></div>
<script>
(async function () {
// In this demo, we are using Direct Line token from MockBot.
// To talk to your bot, you should use the token exchanged using your Direct Line secret.
// You should never put the Direct Line secret in the browser or client app.
// https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication
const res = await fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' });
const { token } = await res.json();
const styleOptions = {
botAvatarImage: 'https://learn.microsoft.com/en-us/azure/bot-service/v4sdk/media/logo_bot.svg?view=azure-bot-service-4.0',
botAvatarInitials: 'BF',
userAvatarImage: 'https://github.com/compulim.png?size=64',
userAvatarInitials: 'WC'
};
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
styleOptions
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));
</script>
</body>
</html>
This option does require generating a direct line token. This is accomplished by passing your direct line secret to the direct line /tokens/generate api and receiving a token back. The generated token is tied to the particular session so, as such, provides a certain level of security. Please don't use your secret in this setting.
The below is a node.js example and can be run as an independent app or incorporated into your existing bot as a separate api call made while the html page / web chat is loading. It wouldn't be hard to recreate an equivalent dotnet version.
server.post('/directline/token', (req, res) => {
// userId must start with `dl_`
const userId = (req.body && req.body.id) ? req.body.id : `dl_${Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
headers: {
'Authorization': `Bearer ${process.env.directLineSecret}`
},
json: {
User: {
Id: userId
}
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
res.send({
token: body.token
});
} else {
res.status(500).send('Call to retrieve token from DirectLine failed');
}
});
});
Hope of help!
I'm building a page that show dynamically some photos in a feed like Instagram. I'm getting stuck trying to avoid everytime I load a page or I go into a photo's detail page and then go back, to do an API request to Laravel controller, so that means fetching data and images, losing the position of the page and starting on the top of the page.
My code:
Feed.vue
<template>
<div v-for="(image, index) in images" :key="index">
<v-img :src="image.path" class="image-masonry mini-cover" slot-scope="{ hover }"></v-img>
</div>
</template>
<script>
export default {
data() {
return {
images: []
}
},
mounted() {
this.getImagesHome();
},
methods: {
getImagesHome() {
this.axios.get('/api/images', {},
).then(response => {
this.images = response.data;
}).catch(error => {
console.log(error);
});
},
}
}
</script>
Edit:
I saw that keep-alive is primarily used to preserve component state or avoid re-rendering it. But i can't understand how to use it. I call my Feed.vue component in another Home.vue as below:
<template>
<v-app>
<Toolbar #toggle-drawer="$refs.drawer.drawer = !$refs.drawer.drawer"></Toolbar>
<Navbar ref="drawer"></Navbar>
<keep-alive>
<Feed></Feed>
</keep-alive>
</v-app>
</template>
<script>
import store from '../store';
export default {
components: {
'Toolbar' : () => import('./template/Toolbar.vue'),
'Navbar' : () => import('./template/Navbar.vue'),
'Feed' : () => import('./Feed.vue')
}
}
</script>
What i have to put more in keep-alive and what i have to change in my Feed.vue component?
mounted() should only be called once.
There seem to be multiple ways to go about this.
If you are using vue-router, then have a look at scrollBehaviour
https://router.vuejs.org/guide/advanced/scroll-behavior.html
From their documentation,
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return desired position
}
})
You can also try using keep-alive: https://v2.vuejs.org/v2/api/#keep-alive
It keeps the component in memory so it is not destroyed, you get activated and deactivated events to check when it comes into view.
But I don't think it saves scroll position, so you may want to use this in combination with scrollBehaviour
We are creating a live chat support system. Currently if the visitor clicks on live chat button, a new window opens up and user can talk their. What I am trying to accomplish is to open the chat window inline like this link:
http://anantgarg.com/chat/sampleb.php
Please note that we are building support chat system not peer chat system as the above link intends.
The problem we are facing here is that how we'll be able to access the database which is located on different server (our server) and not on the server where our client's website is located. The above solution can work fine if we are on the same server.
So, please suggest on how to overcome this hurdle.
Thanks.
I think, for solve your task you may use WebSockets, it support cross-domain connections.
In your case, you may write chat client side and place it on cliet's website, but request from it weill processed by your server with DB access.
Extend
Of course you can use JSON with WebSockets just as JSON with AJAX. WebSockets is transport - JSON is content passed with this transport.
I write this code when reserarch WebSockets (chat lietn side):
function connect(){
var socket;
var host = window.location.host;
var wsUrl = "ws://" + host + "/connect";
try{
var socket = new WebSocket(wsUrl);
message('<p class="event">Socket Status: '+socket.readyState);
socket.onopen = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (open)');
//Run "Ping-Pong" for support connection
setTimeout(pingPong, 5000);
}
socket.onmessage = function(msg){
//Parse server answer from string to JSON object
var answer = JSON.parse(msg.data);
if (answer.type == 'message') {
message('<p class="message">'+answer.user+': '+answer.message);
}
}
socket.onclose = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
}
} catch(exception){
message('<p>Error'+exception);
}
function send(){
var text = $('#text').val();
if(text==""){
message('<p class="warning">Please enter a message');
return ;
}
try{
//Send data via JSON
socket.send('{"type": "message", "message":'+JSON.stringify(text)+'}');
} catch(exception){
message('<p class="warning">');
}
$('#text').val("");
}
var token = 0;
function pingPong()
{
token++;
try{
var msg = {'type': 'ping', 'token': token};
socket.send(JSON.stringify(msg));
setTimeout(pingPong, 5000);
} catch(exception){
message('<p class="warning">');
}
}
function message(msg){
$('#chatLog').append(msg+'</p>');
}//End message()
$('#text').keypress(function(event) {
if (event.keyCode == '13') {
send();
}
});
$('#disconnect').click(function(){
socket.close();
});
}
$(document).ready(function() {
if(!("WebSocket" in window)){
$('#chatLog, input, button, #examples').fadeOut("fast");
$('<p>Oh no, you need a browser that supports WebSockets. How about Google Chrome?</p>').appendTo('#container');
}else{
//The user has WebSockets
connect();
}
});
</script>
<meta charset=utf-8 />
<style type="text/css">
body{font-family:Arial, Helvetica, sans-serif;}
#container{
border:5px solid grey;
width:800px;
margin:0 auto;
padding:10px;
}
#chatLog{
padding:5px;
border:1px solid black;
}
#chatLog p{margin:0;}
.event{color:#999;}
.warning{
font-weight:bold;
color:#CCC;
}
</style>
<title>WebSockets Client</title>
</head>
<body>
<div id="wrapper">
<div id="container">
<h1>WebSockets Client</h1>
<div id="chatLog">
</div>
<p id="examples">e.g. try 'hi', 'name', 'age', 'today'</p>
<input id="text" type="text" />
<button id="disconnect">Disconnect</button>
</div>
</div>
</body>
</html>
Also you can use SocketIO library for it, but I dont sure for it cross-domain work.