Skip to content

Commit

Permalink
Merge pull request GoogleCloudPlatform#157 from jrereyi2000/master
Browse files Browse the repository at this point in the history
Added styled-components to repo, Added DF messenger UI Icon and Wired up DF Messenger to API passed in
  • Loading branch information
jiayuandeng authored Oct 20, 2021
2 parents df80b52 + 43446d2 commit b53f67c
Show file tree
Hide file tree
Showing 9 changed files with 372 additions and 81 deletions.
4 changes: 4 additions & 0 deletions dialogflow-messenger/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"axios": "^0.23.0",
"prettier": "^2.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"styled-components": "^5.3.1",
"typescript": "^4.4.3",
"web-vitals": "^1.0.1"
},
Expand Down Expand Up @@ -39,6 +42,7 @@
},
"devDependencies": {
"@types/react": "^17.0.20",
"@types/styled-components": "^5.1.15",
"parcel-bundler": "^1.12.5"
}
}
4 changes: 4 additions & 0 deletions dialogflow-messenger/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<df-messenger
chat-title="Development Messenger"
api-uri="https://dialogflow.cloud.google.com/v1/cx/locations/us-central1/integrations/messenger/webhook/48478c3c-e0ba-4d06-a53a-68125a972dff/sessions/dfMessenger-59269268:detectIntent"
/>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
Expand Down
172 changes: 91 additions & 81 deletions dialogflow-messenger/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,27 @@
import './App.css';
import { useEffect, useState, useRef } from 'react';

const getAttributes = (domElement: Element): { [key: string]: string } => {
let attributes: { [key: string]: string } = {}
for (let i = 0; i < domElement.attributes.length; i++) {
const attribute = domElement.attributes.item(i)
if (attribute) {
attributes[attribute.name] = attribute.value;
}
}
return attributes;
}

interface Message {
type: 'user' | 'agent';
text: string;
}

const getRandomString = () => {
return (Math.random() + 1).toString(36).substring(7);
}

const getRandomSentence = (length: number) => {
return [...Array(length)].map(i => getRandomString()).join(' ');
}

function getRandomNumber(min: number, max: number) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min)
}

const Message = ({message}: {message: Message}) => {
return (
<div className={`message ${message.type}-message ${message.type}-animation`}>
{message.text}
</div>
)
}
import { Text } from './rich-content/Text';
import {
Widget,
Messenger,
TitleBar,
TextWindow,
MessageListWrapper,
MessageList,
InputField,
TextInput,
SendIcon,
} from './Styles';
import {Message, APIResponse} from './utilities/types';
import {getAttributes} from './utilities/utils';
import {ChatIcon, CloseIcon} from './utilities/components';
import axios from "axios";

function App({ domElement }: { domElement: Element }) {
const {
"chat-title": chatTitle,
'agent-id': agentId,
'language-code': languageCode,
'api-uri': apiURI,
'chat-icon': chatIcon,
location
} = getAttributes(domElement);

Expand All @@ -55,43 +35,70 @@ function App({ domElement }: { domElement: Element }) {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })
}

const updateAgentMessage = (value: string) => {
const messagesCopy = [...messages];
const updateAgentMessage = (response: APIResponse) => {
setMessages(prevMessages => {
const messagesCopy = [...prevMessages];

let lastAgentIndex = messagesCopy.length - 1;
while (messagesCopy[lastAgentIndex].type !== 'agent') lastAgentIndex--;
const {queryResult} = response
const {text: messageSent = '', responseMessages = []} = queryResult

if (messagesCopy[lastAgentIndex].type === 'agent') {
messagesCopy[lastAgentIndex].text = value;
setMessages(messagesCopy);
scrollToBottom();
}
let lastAgentIndex = messagesCopy.length - 1;
while (lastAgentIndex > 0 && messagesCopy[lastAgentIndex].id !== messageSent) lastAgentIndex--;

if (messagesCopy[lastAgentIndex].id === messageSent) {
const responseMessage = responseMessages[0]
const responseText = responseMessage?.text?.text
messagesCopy[lastAgentIndex].text = responseText[0];
messagesCopy[lastAgentIndex].id = undefined;
}

for (let i = 1; i < responseMessages.length; i++) {
const message = responseMessages[i];
const responseText = message?.text?.text
responseText && messagesCopy.push({type: 'agent', text: responseText[0]})
}
return messagesCopy
})

scrollToBottom();
}

const addUserMessage = () => {
const messagesCopy = [...messages].concat({type: 'user', text: value});
setMessages(messagesCopy);
setValue('');
const addUserMessage = async () => {
const textVal = value
addMessage({type: 'user', text: textVal})
addMessage({type: 'agent', text: '...', id: textVal})


try {
const addUserMessageResult = await axios.post<string>(apiURI, {
queryInput: {
text: {
text: value
},
languageCode: languageCode ?? "en"
}
})

let JSONBeginningIndex = 0
while (addUserMessageResult.data[JSONBeginningIndex] !== '{') {
JSONBeginningIndex++
}
const responseJSON = JSON.parse(addUserMessageResult.data.substr(JSONBeginningIndex))

updateAgentMessage(responseJSON)
} catch (err) {
console.log(err)
}

}

const addAgentMessage = (value: string) => {
const messagesCopy = [...messages].concat({type: 'agent', text: value});
setMessages(messagesCopy);
const addMessage = ({type, text, id}: Message) => {
setMessages(prevMessages => ([...prevMessages, {type, text, id}]));
setValue('');
}

useEffect(() => {
scrollToBottom()

if (messages.length > 0) {
if (messages[messages.length - 1].type === 'agent') {
setTimeout(() => {
updateAgentMessage(getRandomSentence(getRandomNumber(2, 15)));
}, 2000);
} else {
addAgentMessage('...')
}
}
}, [messages.length]);

const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
Expand All @@ -102,30 +109,33 @@ function App({ domElement }: { domElement: Element }) {

return (
<div className="App">
<div className="messenger" data-opened={open}>
<div className="title-bar">
<Messenger opened={open}>
<TitleBar>
{chatTitle}
</div>
<div className="text-window">
<div className="message-list-wrapper">
<div className="message-list">
</TitleBar>
<TextWindow>
<MessageListWrapper>
<MessageList>
{messages.map((message, i) => (
<Message key={i} message={message} />
<Text key={i} message={message} />
))}
<div ref={messagesEndRef} />
</div>
</div>
</div>
<div className="input-field">
<input id="text-input" type='text' className="input" value={value} onKeyDown={handleKeyDown} onChange={(event) => setValue(event.target.value)} placeholder="Ask something..." />
</MessageList>
</MessageListWrapper>
</TextWindow>
<InputField>
<TextInput id="text-input" type='text' value={value} onKeyDown={handleKeyDown} onChange={(event) => setValue(event.target.value)} placeholder="Ask something..." />
<div onClick={() => addUserMessage()}>
<svg id="sendIcon" data-visible={value.length > 0}>
<SendIcon visible={value.length > 0}>
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"></path>
</svg>
</SendIcon>
</div>
</div>
</div>
<button onClick={() => setOpen(!open)} className="widget" />
</InputField>
</Messenger>
<Widget onClick={() => setOpen(!open)}>
<ChatIcon url={chatIcon} visible={!open} />
<CloseIcon visible={open} />
</Widget>
</div>
);
}
Expand Down
Loading

0 comments on commit b53f67c

Please sign in to comment.