44 | {groups.map((group, i) =>
45 |
49 | )}
50 |
this.scrollTarget = el} />
53 |
54 |
55 | );
56 | }
57 | }
58 | export default Dialog;
59 |
--------------------------------------------------------------------------------
/src/components/DialogGroup.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 |
3 | const Message = (props) => (
4 |
7 | {props.text === null ?
:
{props.text}
}
8 |
9 | );
10 |
11 | const TypingAnimation = (props) => (
12 |
13 |
14 |
15 |
16 |
17 | );
18 |
19 | export const UserImage = (props) => (
20 |
![User]()
21 | );
22 |
23 | export const BotImage = (props) => (
24 |
![Bot]()
25 | );
26 |
27 | class DialogGroup extends Component {
28 | render() {
29 | const image = this.props.group.isUser ? this.props.isUserHidden ?
:
:
;
30 | const messages = this.props.group.messages.map((text, i) => (
31 |
35 | ));
36 |
37 | return (
38 |
39 | {image}
40 | {messages}
41 |
42 | );
43 | }
44 | }
45 | export default DialogGroup;
46 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 |
3 | class Header extends Component {
4 | render(){
5 | return(
6 |
8 | {this.props.title}
9 |
10 | );
11 | }
12 | }
13 | export default Header;
14 |
--------------------------------------------------------------------------------
/src/components/Input.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import {EntypoPaperPlane, EntypoMic} from 'react-entypo';
3 | //import Audio from './Audio';
4 |
5 | class Input extends Component {
6 | constructor(props) {
7 | super(props);
8 | this.state = {value: ''};
9 |
10 | //this.handleAudio = this.handleAudio.bind(this);
11 | this.handleChange = this.handleChange.bind(this);
12 | //this.handleListen = this.handleListen.bind(this);
13 | this.handleSubmit = this.handleSubmit.bind(this);
14 | }
15 |
16 | /*handleAudio(e) {
17 | const last = e.results.length - 1;
18 | const value = this.state.value + e.results[last][0].transcript;
19 | this.setState({value});
20 | }*/
21 |
22 | handleChange(e) {
23 | const value = e.target.value;
24 | if (value.length >= 256) {
25 | alert('You have reached 256 character limit!');
26 | }
27 | this.setState({value});
28 | }
29 |
30 | /*handleListen() {
31 | this.audio.listen();
32 | }*/
33 |
34 | handleSubmit(e) {
35 | e.preventDefault();
36 | this.props.onSubmit(this.state.value);
37 | this.setState({value: ''});
38 | }
39 |
40 | componentDidMount() {
41 | this._text.focus();
42 | //this.audio = new Audio(this.handleAudioStart, this.handleAudio, this.handleAudioError);
43 | }
44 |
45 | render(){
46 | return(
47 |
70 | );
71 | }
72 | }
73 | export default Input;
74 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import ApiAiClient from './lib/dialogflow';
3 | import Header from './components/Header';
4 | import Dialog from './components/Dialog';
5 | import Input from './components/Input';
6 | import '../css/main.css';
7 |
8 | const BOT_DELAY = 4000;
9 | const BOT_SPEED = 0.03;
10 | const BOT_MAX_CHARS = 150;
11 |
12 | function getBotDelay(msg, isQuick = false) {
13 | let delay = isQuick ? BOT_DELAY / 2 : BOT_DELAY;
14 | let speed = isQuick ? BOT_SPEED * 2 : BOT_SPEED;
15 | return msg.length > BOT_MAX_CHARS ? delay : Math.floor(msg.length / speed);
16 | }
17 |
18 | export default class ReactBotUI extends Component {
19 | constructor(props) {
20 | super(props);
21 | if (props.dialogflow) {
22 | this.dialogflow = new ApiAiClient(props.dialogflow);
23 | }
24 | this.botQueue = [];
25 | this.isProcessingQueue = false;
26 | this.state = {
27 | title: props.title || 'React Bot UI',
28 | messages: [],
29 | isBotTyping: false,
30 | isOpen: props.isOpen !== undefined ? props.isOpen : true,
31 | isVisible: props.isVisible !== undefined ? props.isVisible : true
32 | };
33 |
34 | this.appendMessage = this.appendMessage.bind(this);
35 | this.processBotQueue = this.processBotQueue.bind(this);
36 | this.processResponse = this.processResponse.bind(this);
37 | this.getResponse = this.getResponse.bind(this);
38 | this.handleResize = this.handleResize.bind(this);
39 | this.handleSubmitText = this.handleSubmitText.bind(this);
40 | this.handleToggle = this.handleToggle.bind(this);
41 | }
42 |
43 | appendMessage(text, isUser = false, next = () => {}) {
44 | let messages = this.state.messages.slice();
45 | messages.push({isUser, text});
46 | this.setState({messages, isBotTyping: this.botQueue.length > 0}, next);
47 | }
48 |
49 | processBotQueue(isQuick = false) {
50 | if (!this.isProcessingQueue && this.botQueue.length) {
51 | this.isProcessingQueue = true;
52 | const nextMsg = this.botQueue.shift();
53 | setTimeout(() => {
54 | this.isProcessingQueue = false;
55 | this.appendMessage(nextMsg, false, this.processBotQueue);
56 | }, getBotDelay(nextMsg, isQuick));
57 | }
58 | }
59 |
60 | processResponse(text) {
61 | const messages = text
62 | .match(/[^.!?]+[.!?]*/g)
63 | .map(str => str.trim());
64 | this.botQueue = this.botQueue.concat(messages);
65 |
66 | // start processing bot queue
67 | const isQuick = !this.state.isBotTyping;
68 | this.setState({isBotTyping: true}, () => this.processBotQueue(isQuick));
69 | }
70 |
71 | getResponse(text) {
72 | return this.dialogflow.textRequest(text)
73 | .then(data => data.result.fulfillment.speech);
74 | }
75 |
76 | handleSubmitText(text) {
77 |
78 | // append user text
79 | this.appendMessage(text, true);
80 |
81 | // fetch bot text, process as queue
82 | if (this.dialogflow) {
83 | this.getResponse(text)
84 | .then(this.processResponse);
85 | } else if (this.props.getResponse) {
86 | this.props.getResponse(text)
87 | .then(this.processResponse);
88 | } else {
89 | this.processResponse('Sorry, I\'m not configured to respond. :\'(')
90 | }
91 | }
92 |
93 | handleResize(e) {
94 | const window = e.target || e;
95 | const y = window.innerHeight;
96 | const header = document.querySelector('.container header');
97 | const input = document.querySelector('.container .text-form');
98 | let dialogHeight = y - header.offsetHeight - input.offsetHeight;
99 | if (dialogHeight < 0 || !dialogHeight) {
100 | dialogHeight = 0;
101 | } else if (this.props.dialogHeightMax && dialogHeight > this.props.dialogHeightMax) {
102 | dialogHeight = this.props.dialogHeightMax;
103 | }
104 | this.setState({dialogHeight});
105 | }
106 |
107 | handleToggle() {
108 | if (this.state.isVisible) {
109 | this.setState({isOpen: !this.state.isOpen});
110 | } else {
111 | this.setState({isVisible: true});
112 | }
113 | }
114 |
115 | componentDidMount() {
116 | window.addEventListener('resize', this.handleResize);
117 | this.handleResize(window);
118 | }
119 |
120 | componentWillUnmount() {
121 | window.removeEventListener('resize');
122 | }
123 |
124 | render() {
125 | return (
126 |
127 |
129 |
130 |
134 |
135 |
136 |
137 | );
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/ApiAiClient.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { ApiAiConstants } from "./ApiAiConstants";
18 | import { ApiAiClientConfigurationError } from "./Errors";
19 | import { EventRequest } from "./Request/EventRequest";
20 | import TextRequest from "./Request/TextRequest";
21 | export * from "./Interfaces";
22 | export { ApiAiConstants } from "./ApiAiConstants";
23 | export default class ApiAiClient {
24 | constructor(options) {
25 | if (!options || !options.accessToken) {
26 | throw new ApiAiClientConfigurationError("Access token is required for new ApiAi.Client instance");
27 | }
28 | this.accessToken = options.accessToken;
29 | this.apiLang = options.lang || ApiAiConstants.DEFAULT_CLIENT_LANG;
30 | this.apiVersion = options.version || ApiAiConstants.DEFAULT_API_VERSION;
31 | this.apiBaseUrl = options.baseUrl || ApiAiConstants.DEFAULT_BASE_URL;
32 | this.sessionId = options.sessionId || this.guid();
33 | }
34 | textRequest(query, options = {}) {
35 | if (!query) {
36 | throw new ApiAiClientConfigurationError("Query should not be empty");
37 | }
38 | options.query = query;
39 | return new TextRequest(this, options).perform();
40 | }
41 | eventRequest(eventName, eventData = {}, options = {}) {
42 | if (!eventName) {
43 | throw new ApiAiClientConfigurationError("Event name can not be empty");
44 | }
45 | options.event = { name: eventName, data: eventData };
46 | return new EventRequest(this, options).perform();
47 | }
48 | // @todo: implement local tts request
49 | /*public ttsRequest(query) {
50 | if (!query) {
51 | throw new ApiAiClientConfigurationError("Query should not be empty");
52 | }
53 | return new TTSRequest(this).makeTTSRequest(query);
54 | }*/
55 | /*public userEntitiesRequest(options: IRequestOptions = {}): UserEntitiesRequest {
56 | return new UserEntitiesRequest(this, options);
57 | }*/
58 | getAccessToken() {
59 | return this.accessToken;
60 | }
61 | getApiVersion() {
62 | return (this.apiVersion) ? this.apiVersion : ApiAiConstants.DEFAULT_API_VERSION;
63 | }
64 | getApiLang() {
65 | return (this.apiLang) ? this.apiLang : ApiAiConstants.DEFAULT_CLIENT_LANG;
66 | }
67 | getApiBaseUrl() {
68 | return (this.apiBaseUrl) ? this.apiBaseUrl : ApiAiConstants.DEFAULT_BASE_URL;
69 | }
70 | setSessionId(sessionId) {
71 | this.sessionId = sessionId;
72 | }
73 | getSessionId() {
74 | return this.sessionId;
75 | }
76 | /**
77 | * generates new random UUID
78 | * @returns {string}
79 | */
80 | guid() {
81 | const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
82 | return s4() + s4() + "-" + s4() + "-" + s4() + "-" +
83 | s4() + "-" + s4() + s4() + s4();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/ApiAiConstants.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export var ApiAiConstants;
18 | (function (ApiAiConstants) {
19 | let AVAILABLE_LANGUAGES;
20 | (function (AVAILABLE_LANGUAGES) {
21 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["EN"] = "en"] = "EN";
22 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["DE"] = "de"] = "DE";
23 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["ES"] = "es"] = "ES";
24 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["PT_BR"] = "pt-BR"] = "PT_BR";
25 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["ZH_HK"] = "zh-HK"] = "ZH_HK";
26 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["ZH_CN"] = "zh-CN"] = "ZH_CN";
27 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["ZH_TW"] = "zh-TW"] = "ZH_TW";
28 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["NL"] = "nl"] = "NL";
29 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["FR"] = "fr"] = "FR";
30 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["IT"] = "it"] = "IT";
31 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["JA"] = "ja"] = "JA";
32 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["KO"] = "ko"] = "KO";
33 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["PT"] = "pt"] = "PT";
34 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["RU"] = "ru"] = "RU";
35 | AVAILABLE_LANGUAGES[AVAILABLE_LANGUAGES["UK"] = "uk"] = "UK";
36 | })(AVAILABLE_LANGUAGES = ApiAiConstants.AVAILABLE_LANGUAGES || (ApiAiConstants.AVAILABLE_LANGUAGES = {}));
37 | ApiAiConstants.VERSION = "2.0.0-beta.20";
38 | ApiAiConstants.DEFAULT_BASE_URL = "https://api.api.ai/v1/";
39 | ApiAiConstants.DEFAULT_API_VERSION = "20150910";
40 | ApiAiConstants.DEFAULT_CLIENT_LANG = AVAILABLE_LANGUAGES.EN;
41 | })(ApiAiConstants || (ApiAiConstants = {}));
42 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/Errors.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export class ApiAiBaseError extends Error {
18 | constructor(message) {
19 | super(message);
20 | this.message = message;
21 | this.stack = new Error().stack;
22 | }
23 | }
24 | export class ApiAiClientConfigurationError extends ApiAiBaseError {
25 | constructor(message) {
26 | super(message);
27 | this.name = "ApiAiClientConfigurationError";
28 | }
29 | }
30 | export class ApiAiRequestError extends ApiAiBaseError {
31 | constructor(message, code = null) {
32 | super(message);
33 | this.message = message;
34 | this.code = code;
35 | this.name = "ApiAiRequestError";
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/Interfaces.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export var IStreamClient;
18 | (function (IStreamClient) {
19 | let ERROR;
20 | (function (ERROR) {
21 | ERROR[ERROR["ERR_NETWORK"] = 0] = "ERR_NETWORK";
22 | ERROR[ERROR["ERR_AUDIO"] = 1] = "ERR_AUDIO";
23 | ERROR[ERROR["ERR_SERVER"] = 2] = "ERR_SERVER";
24 | ERROR[ERROR["ERR_CLIENT"] = 3] = "ERR_CLIENT";
25 | })(ERROR = IStreamClient.ERROR || (IStreamClient.ERROR = {}));
26 | let EVENT;
27 | (function (EVENT) {
28 | EVENT[EVENT["MSG_WAITING_MICROPHONE"] = 0] = "MSG_WAITING_MICROPHONE";
29 | EVENT[EVENT["MSG_MEDIA_STREAM_CREATED"] = 1] = "MSG_MEDIA_STREAM_CREATED";
30 | EVENT[EVENT["MSG_INIT_RECORDER"] = 2] = "MSG_INIT_RECORDER";
31 | EVENT[EVENT["MSG_RECORDING"] = 3] = "MSG_RECORDING";
32 | EVENT[EVENT["MSG_SEND"] = 4] = "MSG_SEND";
33 | EVENT[EVENT["MSG_SEND_EMPTY"] = 5] = "MSG_SEND_EMPTY";
34 | EVENT[EVENT["MSG_SEND_EOS_OR_JSON"] = 6] = "MSG_SEND_EOS_OR_JSON";
35 | EVENT[EVENT["MSG_WEB_SOCKET"] = 7] = "MSG_WEB_SOCKET";
36 | EVENT[EVENT["MSG_WEB_SOCKET_OPEN"] = 8] = "MSG_WEB_SOCKET_OPEN";
37 | EVENT[EVENT["MSG_WEB_SOCKET_CLOSE"] = 9] = "MSG_WEB_SOCKET_CLOSE";
38 | EVENT[EVENT["MSG_STOP"] = 10] = "MSG_STOP";
39 | EVENT[EVENT["MSG_CONFIG_CHANGED"] = 11] = "MSG_CONFIG_CHANGED";
40 | })(EVENT = IStreamClient.EVENT || (IStreamClient.EVENT = {}));
41 | })(IStreamClient || (IStreamClient = {}));
42 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/Request/EventRequest.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import Request from "./Request";
18 | export class EventRequest extends Request {
19 | }
20 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/Request/Request.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import { ApiAiRequestError } from "../Errors";
18 | import XhrRequest from "../XhrRequest";
19 | class Request {
20 | constructor(apiAiClient, options) {
21 | this.apiAiClient = apiAiClient;
22 | this.options = options;
23 | this.uri = this.apiAiClient.getApiBaseUrl() + "query?v=" + this.apiAiClient.getApiVersion();
24 | this.requestMethod = XhrRequest.Method.POST;
25 | this.headers = {
26 | Authorization: "Bearer " + this.apiAiClient.getAccessToken(),
27 | };
28 | this.options.lang = this.apiAiClient.getApiLang();
29 | this.options.sessionId = this.apiAiClient.getSessionId();
30 | }
31 | static handleSuccess(xhr) {
32 | return Promise.resolve(JSON.parse(xhr.responseText));
33 | }
34 | static handleError(xhr) {
35 | let error = new ApiAiRequestError(null);
36 | try {
37 | const serverResponse = JSON.parse(xhr.responseText);
38 | if (serverResponse.status && serverResponse.status.errorDetails) {
39 | error = new ApiAiRequestError(serverResponse.status.errorDetails, serverResponse.status.code);
40 | }
41 | else {
42 | error = new ApiAiRequestError(xhr.statusText, xhr.status);
43 | }
44 | }
45 | catch (e) {
46 | error = new ApiAiRequestError(xhr.statusText, xhr.status);
47 | }
48 | return Promise.reject(error);
49 | }
50 | perform(overrideOptions = null) {
51 | const options = overrideOptions ? overrideOptions : this.options;
52 | return XhrRequest.ajax(this.requestMethod, this.uri, options, this.headers)
53 | .then(Request.handleSuccess.bind(this))
54 | .catch(Request.handleError.bind(this));
55 | }
56 | }
57 | export default Request;
58 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/Request/TextRequest.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import Request from "./Request";
18 | export default class TextRequest extends Request {
19 | }
20 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/XhrRequest.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * quick ts implementation of example from
19 | * https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
20 | * with some minor improvements
21 | * @todo: test (?)
22 | * @todo: add node.js implementation with node's http inside. Just to make SDK cross-platform
23 | */
24 | class XhrRequest {
25 | // Method that performs the ajax request
26 | static ajax(method, url, args = null, headers = null, options = {}) {
27 | // Creating a promise
28 | return new Promise((resolve, reject) => {
29 | // Instantiates the XMLHttpRequest
30 | const client = XhrRequest.createXMLHTTPObject();
31 | let uri = url;
32 | let payload = null;
33 | // Add given payload to get request
34 | if (args && (method === XhrRequest.Method.GET)) {
35 | uri += "?";
36 | let argcount = 0;
37 | for (const key in args) {
38 | if (args.hasOwnProperty(key)) {
39 | if (argcount++) {
40 | uri += "&";
41 | }
42 | uri += encodeURIComponent(key) + "=" + encodeURIComponent(args[key]);
43 | }
44 | }
45 | }
46 | else if (args) {
47 | if (!headers) {
48 | headers = {};
49 | }
50 | headers["Content-Type"] = "application/json; charset=utf-8";
51 | payload = JSON.stringify(args);
52 | }
53 | for (const key in options) {
54 | if (key in client) {
55 | client[key] = options[key];
56 | }
57 | }
58 | // hack: method[method] is somewhat like .toString for enum Method
59 | // should be made in normal way
60 | client.open(XhrRequest.Method[method], uri, true);
61 | // Add given headers
62 | if (headers) {
63 | for (const key in headers) {
64 | if (headers.hasOwnProperty(key)) {
65 | client.setRequestHeader(key, headers[key]);
66 | }
67 | }
68 | }
69 | payload ? client.send(payload) : client.send();
70 | client.onload = () => {
71 | if (client.status >= 200 && client.status < 300) {
72 | // Performs the function "resolve" when this.status is equal to 2xx
73 | resolve(client);
74 | }
75 | else {
76 | // Performs the function "reject" when this.status is different than 2xx
77 | reject(client);
78 | }
79 | };
80 | client.onerror = () => {
81 | reject(client);
82 | };
83 | });
84 | }
85 | static get(url, payload = null, headers = null, options = {}) {
86 | return XhrRequest.ajax(XhrRequest.Method.GET, url, payload, headers, options);
87 | }
88 | static post(url, payload = null, headers = null, options = {}) {
89 | return XhrRequest.ajax(XhrRequest.Method.POST, url, payload, headers, options);
90 | }
91 | static put(url, payload = null, headers = null, options = {}) {
92 | return XhrRequest.ajax(XhrRequest.Method.PUT, url, payload, headers, options);
93 | }
94 | static delete(url, payload = null, headers = null, options = {}) {
95 | return XhrRequest.ajax(XhrRequest.Method.DELETE, url, payload, headers, options);
96 | }
97 | static createXMLHTTPObject() {
98 | let xmlhttp = null;
99 | for (const i of XhrRequest.XMLHttpFactories) {
100 | try {
101 | xmlhttp = i();
102 | }
103 | catch (e) {
104 | continue;
105 | }
106 | break;
107 | }
108 | return xmlhttp;
109 | }
110 | }
111 | XhrRequest.XMLHttpFactories = [
112 | () => new XMLHttpRequest(),
113 | () => new window["ActiveXObject"]("Msxml2.XMLHTTP"),
114 | () => new window["ActiveXObject"]("Msxml3.XMLHTTP"),
115 | () => new window["ActiveXObject"]("Microsoft.XMLHTTP")
116 | ];
117 | (function (XhrRequest) {
118 | let Method;
119 | (function (Method) {
120 | Method[Method["GET"] = "GET"] = "GET";
121 | Method[Method["POST"] = "POST"] = "POST";
122 | Method[Method["PUT"] = "PUT"] = "PUT";
123 | Method[Method["DELETE"] = "DELETE"] = "DELETE";
124 | })(Method = XhrRequest.Method || (XhrRequest.Method = {}));
125 | })(XhrRequest || (XhrRequest = {}));
126 | export default XhrRequest;
127 |
--------------------------------------------------------------------------------
/src/lib/dialogflow/index.js:
--------------------------------------------------------------------------------
1 | import ApiAiClient from './ApiAiClient';
2 |
3 | export default ApiAiClient;
--------------------------------------------------------------------------------
/tests/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tests/index-test.js:
--------------------------------------------------------------------------------
1 | import expect from 'expect'
2 | import React from 'react'
3 | import {render, unmountComponentAtNode} from 'react-dom'
4 |
5 | import Component from 'src/'
6 |
7 | describe('Component', () => {
8 | let node
9 |
10 | beforeEach(() => {
11 | node = document.createElement('div')
12 | })
13 |
14 | afterEach(() => {
15 | unmountComponentAtNode(node)
16 | })
17 |
18 | it('displays a welcome message', () => {
19 | render(
, node, () => {
20 | expect(node.innerHTML).toContain('Welcome to React components')
21 | })
22 | })
23 | })
24 |
--------------------------------------------------------------------------------