Storing SUSI Settings for Anonymous Users

SUSI Web Chat is equipped with a number of settings by which it can be configured. For a user who is logged in and using the chat, he can change his settings and save them by sending it to the server. But for an anonymous user, this feature is not available while opening the chat every time, unless one stores the settings. Thus to overcome this problem we store the settings in the browser’s cookies which we do by following the steps.

The current User Settings available are :-

  1. Theme – One can change the theme to either ‘dark’ or ‘light’.
  2. To have the option to send a message on enter.
  3. Enable mic to give voice input.
  4. Enable speech output only for speech input.
  5. To have speech output always ON.
  6. To select the default language.
  7. To adjust the speech output rate.
  8. To adjust the speech output pitch.

The first step is to have a default state for all the settings which act as default settings for all users when opening the chat for the first time. We get these settings from the User preferences store available at the file UserPreferencesStore.js

let _defaults = {
                Theme: 'light',
                Server: 'http://api.susi.ai',
                StandardServer: 'http://api.susi.ai',
                EnterAsSend: true,
                MicInput: true,
                SpeechOutput: true,
               SpeechOutputAlways: false,
               SpeechRate: 1,
               SpeechPitch: 1,
           };

We assign these default settings as the default constructor state of our component so that we populate them beforehand.

2. On changing the values of the various settings we update the state through various function handlers which are as follows-

handleSelectChange – handles all the theme changes, handleEnterAsSendhandleMicInput – handles Mic Input as true, handleSpeechOutput – handles whether Speech Output should be enabled or not, handleSpeechOutputAlways – handles if user wants to listen to speech after every input, handleLanguage – handles the language settings related to speech, handleTextToSpeech – handles the Text to Speech Settings including Speech Rate, Pitch.        

  1. Once the values are changed we make use of the universal-cookie package and save the settings in the User’s browser. This is done in a function handleSubmit in the Settings.react.js file.

        

 import Cookies from 'universal-cookie';
     const cookies = new Cookies(); // Create cookie object.
     // set all values
     let vals = {
            theme: newTheme,
            server: newDefaultServer,
            enterAsSend: newEnterAsSend,
            micInput: newMicInput,
            speechOutput: newSpeechOutput,
            speechOutputAlways: newSpeechOutputAlways,
            rate: newSpeechRate,
            pitch: newSpeechPitch,
      }

    let settings = Object.assign({}, vals);
    settings.LocalStorage = true;
    // Store in cookies for anonymous user
    cookies.set('settings',settings);   
  1. Once the values are set in the browser we load the new settings every time the user opens the SUSI Web Chat by having the following checks in the file API.actions.js. We get the values from the cookies using the get function if the person is not logged in and initialise the new settings for that user.
if(cookies.get('loggedIn')===null||
    cookies.get('loggedIn')===undefined){
    // check if not logged in and get the value from the set cookie
    let settings = cookies.get('settings');
    if(settings!==undefined){
      // Check if the settings are set in the cookie
      SettingsActions.initialiseSettings(settings);
    }
  }
else{
// call the AJAX for getting Settings for loggedIn users
}

Resources

Implementing Login Functionality in SUSI Web Chat

SUSI Web Chat is fully equipped with all the accounting features which are being provided by the SUSI.AI API. This blog discloses all the API features one needs to know to embed the Login functionality in SUSI Web Chat.

  1. To embed the Login feature, first we create a form using material-ui.com components with the followng fields
    1. Email
    2. Password
    3. Note: We can also chose a Custom Server while logging in, here I have used the Standard Server ie. http://api.susi.ai to make the user Login

The form can be made with the help of the following fields

  • TextField for Email, props to be passed
    • Name – email
    • Value – this.state.email which gets the value of the current email
    • floatingLabelText is Email,
    • errorText is the message which we want to show when the email does not match the regex or its empty.

Code Snippet –

<TextField name="email" value={this.state.email} onChange={this.handleChange} errorText={this.emailErrorMessage}    floatingLabelText="Email" />
  • PasswordField for Password
    • Name – password
    • Value – this.state.password which gets the value of the current email
    • floatingLabelText is Password,
    • errorText is the message which we want to show when the password is not filled.

Code Snippet-

<PasswordField name='password' value={this.state.password} onChange={this.handleChange} errorText={this.passwordErrorMessage}   floatingLabelText='Password' />
  • The next elements are RadioButton groups taken from material-ui.com. This ensures the user signs into a standard server or even to a custom server. This is not compulsory as of now.
  • And lastly we need a submit button, which is disabled until all the fields are filled.

Code Snippet –

<RaisedButton label="Login" type="submit" labelColor="#fff" disable={!this.state.validForm} />

For the full form, check out this file at Login.react.js

  1. A Sample UI could be as shown in the image
  2. Next after creating the Login Screen, we make the onSubmit prop which is to be hooked up with another function called handleSubmit. An example code snippet from Login.react.js
 handleSubmit = (e) => {
        e.preventDefault();
        // Get the trimmed values from the fields
        var email = this.state.email.trim();
        var password = this.state.password.trim();
        // Set the default server to login
        let BASE_URL = defaults.Server;
            // handle all the details of the chosen server
        let serverUrl = this.state.serverUrl;
        if(serverUrl.slice(-1) === '/'){
            serverUrl = serverUrl.slice(0,-1);
        }
        if(serverUrl !== ''){
            BASE_URL = serverUrl;
        }
// if email and password is filled return true
        if (!email || !password) { return this.state.isFilled; }
// Check the regex of email
        let validEmail = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(email); 
// Pass the parameters to the loginEndPoint
        let loginEndPoint =
            BASE_URL+'/aaa/login.json?type=access-token&login=' +
            this.state.email + '&password=' + this.state.password;
        // If email and password is filled and valid call AJAX
        if (email && validEmail) {
            // AJAX Calls
        }
    }
    1. Then we make the Ajax Calls and store the created token from hitting the URL at http://api.susi.ai/aaa/login.json?type=access-token&login=EMAIL&password=PASSWORD. We store the cookie in browser and generate a session for the user using a package ‘universal-cookies’.
$.ajax({
    url: loginEndPoint,
    dataType: 'jsonp',
    jsonpCallback: 'p',
    jsonp: 'callback',
    crossDomain: true,
    success: function (response) {
        cookies.set('serverUrl', BASE_URL, { path: '/' });
        let accessToken = response.access_token;
        let state = this.state;// Adding the current State
        let time = response.valid_seconds; // Get the valid time of the cookie
        state.isFilled = true; // Set isFilled to true
        state.accessToken = accessToken; // Get the token
        state.success = true; // Set Success to true
        state.msg = response.message; // Get the server message
        state.time = time; // Get the time in the state
        this.setState(state); // Set the  state with the values
/* Pass the token to the binding function handleOnSubmit passing the arguments - token and the valid time */
        this.handleOnSubmit(accessToken, time);
    }.bind(this),
    error: function (errorThrown) {
        let msg = 'Login Failed. Try Again';
        let state = this.state;
        state.msg = msg;
        this.setState(state);
    }.bind(this)
});

 

    1. We then fire up the handleOnSubmit(accessToken, time) which saves the token for the given expiration time from the server.

Here’s the sample code

handleOnSubmit = (loggedIn, time) => {
        let state = this.state;
        if (state.success) {
            cookies.set('loggedIn', loggedIn, { path: '/', maxAge: time }); // set the cookie in the browser to maintain the loggedIn state
            this.props.history.push('/', { showLogin: false });
            window.location.reload();// reload after the loggedIn cookie creation
        }
        else {
            this.setState({
                error: true,
                accessToken: '',
                success: false
            });
        }
    }
  1. We then check the access token and redirect him based on his login state. This is handled in MessageSection.react.js
import Cookies from 'universal-cookie';
const cookies = new Cookies();
if (cookies.get('loggedIn')) {
    //Show all functionalities of loggedIn state
}
else {
//Redirect user to login page
}

 

To have a look at the full project, visit https://github.com/fossasia/chat.susi.ai and feel free to contribute. To test the project visit http://chat.susi.ai

Resources