Implementation of Features in Generator UI

In the early stage of development, Meilix Generator only has wallpaper and event name customization. But today the webapp has bunch of customization and features list which enables an user to design its own customizable ISO.

Iteration in the form

Meilix Generator came across several changes in the form throughout the time.

At starting we only have an email part where the ISO get mailed, a name for the event so as to distinguish the ISO image and an image upload which will be set as the default desktop wallpaper in the ISO.

Then the user gets a link which get activated after 20 minutes. Till then user have to preserve the link to download the ISO.

Then we introduced a new field which contains event link and this link will be set as the homepage of the browser. And we change the basic UI of the webapp.

At the same we implemented SendGrid to send the user the email link in their mail. This decreases the burden of carrying the downloadable link till the ISO becomes ready.

Finally today Meilix Generator looks like this. It got some more customizable fields like providing default search engine, bookmark enabling or disabling and packages to include in the ISO.

It has a link on the footer from which the latest pre-build ISO can be downloaded instantly and another link which takes user to the releases page of Meilix.

Reference:

SendGrid Email Delivery Service

SendGrid Email API

Displaying skill rating for each skill on skill page of SUSI SKILL CMS

SUSI exhibits several skills which are managed by the SUSI Skill CMS, it essentially is a client which allows users to create/update skills conveniently since for each skill it is important to have the functionality of rating system so developers can get to know which skills are performing better than the rest and consequently improve them, thus a skill rating system which allows the users to give positive or negative feedback for each skill is implemented on the server.

Fetching skill_rating from the server

  1. Fetch skill data for which ratings are to be displayed through ajax calls
    API Endpoint –

    /cms/getSkillMetadata.json?
    

  2. Parse the received metadata object to get positive and negative ratings for that skill
  3. if(skillData.skill_rating) {
           	let positive_rating = skillData.skill_rating.positive;
            	let negative_rating = skillData.skill_rating.negative;
    }
    

    Sample API response

    {
      "skill_metadata": {
        "model": "general",
        "group": "Knowledge",
        "language": "en",
        "developer_privacy_policy": null,
        "descriptions": "Want to know about fossasia, just ask susi to tell that, Susi tells about the SUSI.AI creators",
        "image": "images/creator_info.png",
        "author": "madhav rathi",
        "author_url": "https://github.com/madhavrathi",
        "author_email": null,
        "skill_name": "Creator Info",
        "terms_of_use": null,
        "dynamic_content": false,
        "examples": [
          "Who created you?",
          "what is fossasia?"
        ],
        "skill_rating": {
          "negative": "0",
          "positive": "0",
          "stars": {
            "one_star": 0,
            "four_star": 0,
            "five_star": 0,
            "total_star": 0,
            "three_star": 0,
            "avg_star": 0,
            "two_star": 0
          },
          "feedback_count": 0
        },
        "creationTime": "2018-03-17T16:38:29Z",
        "lastAccessTime": "2018-06-15T15:51:50Z",
        "lastModifiedTime": "2018-03-17T16:38:29Z"
      },
      "accepted": true,
      "message": "Success: Fetched Skill's Metadata",
      "session": {"identity": {
        "type": "host",
        "name": "162.158.166.37_d80fb5c9",
        "anonymous": true
      }}
    }
    

  4. Set the react state of the component to store positive and negative rating.
  5. this.setState({
      positive_rating,
      negative_rating
    })
    

  6. Use react-icons to fetch like and dislike icon components from font-awesome.
  7. npm i -S react-icons
    

  8. Import the corresponding icons in the SkillPage component
  9. import { FaThumbsOUp, FaThumbsODown } from 'react-icons/lib/fa/'
    

  10. Display the rating count along with their icons
  11. <div className="rating">
        <div className="positive">
             <FaThumbsOUp />
             {this.state.positive_rating}
         </div>
           <div className="negative">
                 <FaThumbsODown />
                 {this.state.negative_rating}
             </div>
    </div>
    

Example

References

Use of Flux Architecture in SUSI.AI

SUSI Web Chat is based on Flux, a React.js Architecture. It provides a simple way for implementing many features in SUSI Web Chat. This blog post explains how Flux Architecture works, and also how it is used in SUSI Web Chat to implement the feature of switching between Light and Dark themes.

What is Flux?

Flux is a micro-architecture for software application development. It is the application architecture that Facebook uses for building client-side web applications. It complements React’s composable view components by utilizing a unidirectional data flow.

Flux Overview

As can be seen from the above diagram, Flux has four main components:

Component Description
Actions Helper methods that pass data to the Dispatcher.
Dispatcher Receives these Actions and broadcast payloads to registered callbacks.
Stores The Stores that are registered to the Dispatcher will update the Views accordingly.
Views  Views are actually React Components that grab the state from the stores and then pass it down to the child components.

How these 4 components work together?

  • Application Initialisation:
    1. Stores let the registered Dispatcher know that they want to be notified whenever an action comes in.
    2. Then the controller views ask the stores for the latest state.
    3. When the stores give the state to the controller views, they pass that state along to their child views to render.
    4. The controller views also ask the stores to keep them notified when state changes so that they can re-render accordingly.
  • The Data Flow:

Once the setup is done, the application is ready to accept user input. So let us trigger an action by having the user make a change.

    1. The view tells the action creator to prepare an action.
    2. The action creator formats the action and sends it off to the Dispatcher.
    3. The Dispatcher dispatches the action off to all of the stores in sequence. Then the store decides whether it cares about this action or not, and changes the state accordingly.
    4. Once it’s done changing state, the store lets its subscribed view controllers know.
    5. Those view controllers will then ask the store to give them the updated state.
    6. After the store gives it the state, the view controller will tell its child views to re-render based on the new state.

So this is how Flux works. Now let us see an example of Flux being used in SUSI Web Chat.

Use of Flux Architecture to Switch Between Themes in SUSI Web Chat

Different Flux components used in SUSI Web Chat:

  1. Actions – Settings.actions.js
  2. Dispatcher – ChatAppDispatcher.js
  3. Store – UserPreferencesStore.js
  4. View – MessageSection.react.js

Let us now see in detail how we are able to switch themes:
In the MessageSection.react.js file, we have the following functions to handle theme change:

handleThemeChanger = () => {
    //code
    this.applyLightTheme();
}

applyLightTheme = () =>{
    //code
    Actions.settingsChanged(settingsChanged);
}

 
Hence, the view tells the action creator to prepare an action.
Now, the settingsChanged() function in Settings.actions.js file is called as follows:

export function settingsChanged(settings) {
    ChatAppDispatcher.dispatch({
    type: ActionTypes.SETTINGS_CHANGED,
    settings
});
    //code
}

 
Hence, the process of invoking the callbacks is initiated through the dispatch() method, which takes a data payload object as its sole argument.

Now, as the UserPreferencesStore is registered with the ChatAppDispatcher, it receives the actions dispatched by the Dispatcher. It checks for the type of action, and with the help of switch cases, executes the code for the corresponding action type accordingly. In this case, the states inside the Store are updated with the new state which the User wants to switch to.

UserPreferencesStore.dispatchToken = ChatAppDispatcher.register(action => {
    switch (action.type) {
        //code
        case ActionTypes.SETTINGS_CHANGED: {
            let settings = action.settings;
            //code
            UserPreferencesStore.emitChange();
            break;
        }
        //code
    }
});

 
This is how Flux Architecture is facilitating the switching of themes in SUSI Web Chat.

Resources

 

Holding query search in Loklak

Continuously searching user’s input while the user kept typing is not good. It has certain drawbacks which result in continuous action dispatch and loading of the result page which depicts the site to be slow, but actually it is not. To prevent continuous loading and searching while user types a query in Loklak Search, it was important to keep hold on query until user completes typing and then start searching for the results. I am writing this blog post to emphasise a proper solution to constantly keep track of the query and search on an appropriate event.

Adding keypress event

First and foremost task would be to add keypress event to input tag on home and results page.

<input     
     

     (keypress)=“onEnter($event)” 
     [formControl]=“searchInputControl

/>

 

formControl will constantly keep track of the user input and will be stored in searchInputControl, while keypress event will activate onEnter() method firing an $event as an input to onEnter() method.

Adding onEnter() method

Note: Keeping track of special keypress event code 13 which is equivalent for an enter key press.

Add a new method onEnter() with event as an input which will execute its function only when the event’s keypress code is 13 (Enter key press). One point should be noted here that query to be searched should not be empty or containing leading and trailing spaces or should not contain only spaces (treated as an incomplete query to be searched).

onEnter(event: any) {  
   if (event.which === 13) {  

      if (this.searchInputControl.value.trim() !== ”) {

        // Actual work of query search will go here.

  }

}

Add query searching and migration code under onEnter()

Now comes the last part to shift query search and route migration code from setupSearchField() to the portion mentioned above in comments. We need to take one point in consideration that from now on we actually do not need to subscribe to formControl in ngOnInit() since now it depends on user query input.

Complete onEnter() in home component file

After completion, onEnter() method should be similar to:

onEnter(event: any) {
   if (event.which === 13) {
       if (this._queryControl.value.trim() !== ”) {
           this.store.dispatch(new 
               queryAction.RelocationAfterQuerySetAction());
           this.store.dispatch(new suggestAction
               .SuggestAction(this._queryControl.value.trim()));
           this.store.dispatch(new queryAction
               .InputValueChangeAction(this._queryControl.value.trim()
           ));
           this.router.navigate([`/search`],
               { queryParams: { query: this._queryControl.value.trim() }
               , skipLocationChange: true } );
           this.store.dispatch(new titleAction
           .SetTitleAction(this._queryControl.value.trim()
               +   Loklak Search’));
           this.getDataFromStore();
       }
   }
}

 

Since we are not depending on ngOnInit() therefore we also need to append getDataFromStore() to onEnter() method.

Complete onEnter() in feed-header component file

After completion, onEnter() method should be similar to:

onEnter(event: any) {
   if (event.which === 13) {
       if (this.searchInputControl.value.trim() !== '') {
           this.searchEvent.emit(this.searchInputControl
               .value.trim());
           this.setupSuggestBoxClosing();
       }
   }
}

Testing

Try typing different formats of query on home and results page of loklak.

Resources

Creating the Mockup for Wave Generator using Moqups

PSLab is a versatile device that can be used as a waveform signal generator, as it is capable of producing different types of waves. It provides a cheap and portable alternative as compared to the physical wave generators available in markets.

The current UI of the Wave Generator present in the PSLab app as shown in Figure 1 is very basic and makes use of some basic UI components which are not able to properly expose all the functionalities of the wave generator. This affects the user experience negatively.

Figure 1 shows the current layout of Wave Generator in PSLab android app

In this blog, we will discuss creating a mockup for the PSLab wave generator using modern design to improve the UI/UX of the app. I will try to keep the layout simple enough so that users can easily understand it. 

Understanding the Functionality of Waveform Generator

Figure 2 shows the schematic pin diagram of the PSLab

We can observe from the image that PSLab provide:

  • Two pins named W1 and W2 which are capable of producing two different sine or saw-tooth waves having different characteristics simultaneously.
  • Four Pins named SQ1, SQ2, SQ3, SQ4 which are capable of producing Square Wave and Pulse Width modulation signals.
  • It also allows us to change the properties of these waves such as frequency, phase, duty cycle.

Before starting with the mockup a physical commodity Waveform Signal Generator as shown in Figure 3 was inspected.

Figure 3 shows digital Wave Generator by HP

Most waveform generators in the market have a control panel with buttons and a monitor screen so that the user can view the changes while controlling the characteristics of the wave. I will try to create a layout similar to this in following steps by dividing it into two halves:

  1. Upper half containing monitor screens
  2. Lower half containing controlling panel having buttons

Steps for creating the mockup

Step 1: Choosing the Tool

The tool that we will be using here to create a mockup is moqups[1].

I have chosen this software because:-

  • It is an online tool and doesn’t require it to be set up first
  • It is very efficient and powerful tool for creating mobile or web wireframes.
  • It doesn’t require any prior knowledge of programming language.
  • It has a palette to drag & drop UI elements like Buttons, Textboxes, Checkboxes etc. on the left-hand side, allows modifying the features of each element (here on the right) and other options at the top related to prototyping, previewing etc as shown in figure 4.
Figure 4  shows Moqups prototyping tool webpage

Step 2: Creating the base of the layout

The mockup tool moqups[1] have a built-in template of Android screen which we can drag and drop from the palette and use it as our base.

Figure 5 shows the Android mobile template picked from the palette of available design elements in the ‘moqups’ application.

Step 3: Creating the Lower Half

Now as discussed above I will create controlling panel containing buttons in the lower half of the screen, for this I will drag and drop buttons from the palette on the right-hand side and design them according to the theme of the app that is making their border round shape, changing their color to match the theme color and editing text in them. As the PSLab has two groups of pins so I will create two different panels with buttons for properties related to the pins of that group.

At this point we have a layout looking like this:

Figure 6 shows the lower panel containing buttons being drawn for Wave Generator UI. At right-hand side, it shows the palette having different UI elements.

I will create the monitor screens in the upper half, for our purpose as I have two panels we will create two monitor screen as shown in Figure 6. The left panel will correspond to the left panel and the right monitor will correspond to the right panel.

Step 4: Creating the Upper Half

For creating monitor, I will use simple rectangles with rounded borders and give the background black to resemble the monitor in physical devices.

I will also create some partitions in the monitor to make compartment. I will use these compartments to position the text view that represents the characteristics of the wave so that they are clearly visible.

Figure 7 shows two monitor layout with the black background being drawn using text areas and lines

So, in this layout the user will be able to see all the properties corresponding to one type of waveform on the monitor together so he/she doesn’t have to click the buttons, again and again, to see the different properties as he/she have to do in a physical device.

For instance let’s say, if the user wants to generate sine wave he/she will have to click the Wave 1 button in the panel below and on clicking,all the values of characteristics related to waveform produced by W1 pin will be visible together to the user in one screen which makes it easier for the user to analyze and set the values.

Therefore, the final layout produced is shown in Figure 7

Figure 8 shows final Wave Generator Mockup.

As we can see this layout is quite interactive and looks very appealing to the user and this will help to improve UI/UX of the app.

Resources

  1. Moqups prototyping tool website: https://moqups.com
  2. Youtube Video – How to use moqups

Adding System Image for Event Categories

The Open Event Server is using the JSON 1.0 Specification and build on top of Flask Rest Json API (for building Rest APIs) and Marshmallow (for Schema). In this blog, we will talk about how to add feature of System Image for Event Categories on Open Event Server. The focus is on Model updation, Schema updation and migrating the Database.

Model Updation

For adding System Image, we’ll update our Model EventTopic.

In this feature, we are providing rights to the Admin to add a system image for each Event Category so that if no image is given by a organizer of event on event creation then it will use the system image of that Event Category as event image by default.

Here we are adding a Column named system_image_url which is of type String. This value cannot be nullable and having a default value.

Migrating the Database

For the migrating the Database we will use simple commands.

This command runs migrations. If it cause problems naming Multiple Migration Head, then you need to run

This problem is caused when two developers push a migration file without merging two heads to achieve one head.

The above command will give us ids of two migration heads.

This command is merging two migration heads.

This command is upgrading the migrations.

Finally, we migrate the Database using above command.

Schema Updation

For the system image, we’ll update the Schema EventTopicSchema as follows

In this feature, to provide system image for each Event Category we’ll add a field named system_image_url in the Schema.

Here we are adding a field named system_image_url which is of marshmallow field type URL. This value cannot be none.

Validating the Event Image and using System Image by default

In this step, we’ll check if a event image is provided by organizer. If that is not provided then we’ll use system image of Event Category as Event Image.

Here, we will first take the event topic of event as added by the organizer. Then we will fetch the the database row in Event Topic model which has id == event_topic_id . Then we will return the system image url of that event topic to the event image.

So we saw how we could provide a default image for any event.

Resources

Using a Git repo as a Storage & Managing skills through susi_skill_cms

In this post, I’ll be talking about SUSI’s skill management and the workflow of creating new skills

The SUSI skills are maintained in a separate github repository susi_skill_data which provides the features of version controlling and the ability to rollback to a previous version implemented in SUSI Server.

The workflow is as explained in the featured image of this blog, SUSI CMS provides the user with a GUI through which user can talk to the SUSI Server and using it’s api calls, it can manipulate the susi skills present/stored on the susi_skill_data repository.

When the user opts to create a new skill, a new createSkill component is loaded with an editor to define rules of the skill. Once the form is submitted, an AJAX POST request is made to the server which actually commits the skill data to the repository and thus it is visible in the CMS from that point on.

Grab the skill details within the editor and put them in a form which is to be sent via the POST request.

let form = new FormData();
form.append('model', 'general');
form.append('group', this.state.groupValue);
form.append('language', this.state.languageValue);
form.append('skill', this.state.expertValue.trim().replace(/\s/g,'_'));
form.append('image', this.state.file);
form.append('content', this.state.code);
form.append('image_name', this.state.imageUrl.replace('images/',''));
form.append('access_token', cookies.get('loggedIn'));


Configure POST request settings object

let settings = {
   'async': true,
   'crossDomain': true,
   'url': urls.API_URL + '/cms/createSkill.json',
   'method': 'POST',
   'processData': false,
   'contentType': false,
   'mimeType': 'multipart/form-data',
   'data': form
};


Make an AJAX request using the settings above to upload the skill to the server and send a notification when the request is successful.

$.ajax(settings)
   .done(function (response) {
   self.setState({
          loading:false
   });
notification.open({
    message: 'Accepted',
    description: 'Your Skill has been uploaded to the server',
    icon: <Icon type='check-circle' style={{ color: '#00C853' }}       />,
});


Parse the received response as JSON and if the accept key in the response is true, we push the new skill data to the history API and set relevant states.

let data = JSON.parse(response);
if(data.accepted===true){
  self.props.history.push({
	pathname: '/' + self.state.groupValue  +
  	'/' + self.state.expertValue.trim().replace(/\s/g,'_') +
  	'/' + self.state.languageValue,
	state: {
  	from_upload: true,
  	expertValue:  self.state.expertValue,
  	groupValue: self.state.groupValue ,
  	languageValue: self.state.languageValue,
}});


If the accepted key of the server response is not true, display a notification.

else{
	self.setState({
  		loading:false
	});
	notification.open({
	  	message: 'Error Processing your Request',
	  	description: String(data.message),
	  	icon: <Icon type='close-circle' style={{ color: '#f44336' }} />,
	});
}})


Handle cases when AJAX request fails and send a corresponding notification

.fail(function (jqXHR, textStatus) {
 ...
  notification.open({
    message: 'Error Processing your Request',
    description: String(textStatus),
    icon: <Icon type='close-circle' style={{ color: '#f44336' }} />,
  });
});


I hope after reading this post, the objectives of susi_skill_data are more clear and you understood how CMS handles the creation of skills.

Resources

1.AJAX Jquery – AJAX request using Jquery
2. React State – Read about React states and lifecycle hooks.

Adding Event Roles concerning a User on Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues. Event managers can create invitation forms for speakers and build schedules in a drag and drop interface. The event information is stored in a database. The system provides API endpoints to fetch the data, and to modify and update it. The Open Event Server is based on JSON 1.0 Specification and hence build on top of Flask Rest Json API (for building Rest APIs) and Marshmallow (for Schema).

In this blog, we will talk about how to add different events role concerning a user on Open Event Server. The focus is on its model and Schema updation.

Model Updation

For the User Table, we’ll update our User Model as follows:

Now, let’s try to understand these hybrid properties.

In this feature, we are providing Admin the rights to see whether a user is acting as a organizer, co-organizer, track_organizer, moderator, attendee and registrar of any of the event or not. Here, _is_role method is used to check whether an user plays a event role like organizer, co-organizer, track_organizer, moderator, attendee and registrar or not. This is done by querying the record from UserEventsRole model. If the record is present then the returned value is True otherwise False.

Schema Updation

For the User Model, we’ll update our Schema as follows

Now, let’s try to understand this Schema.

Since all the properties will return either True or false so these all properties are set to Boolean in Schema. Here dump_only means, we will return this property in the Schema.

So, we saw how User Model and Schema is updated to show events role concerning a user on Open Event Server.

Resources

Integrating Firebase Cloud Functions In Badgeyay

Badgeyay is an open source project developed by FOSSASIA Community for generating badges for conferences and events. The Project is divided into two parts frontend, which is in ember, and backend, which is in flask. Backend uses firebase admin SDK (Python) and Frontend uses firebase javascript client with emberfire wrapper for ember. Whenever an user signs up on the website, database listener that is attached to to the Model gets triggered and uses flask-mail for sending welcome mail to the user and in case of email and password signup, verification mail as well.

Problem is sending mail using libraries is a synchronous process and takes a lot of processing on the server. We can use messaging queues like RabbitMQ and Redis but that will be burden as server cost will increase. The workaround is to remove the code from the server and create a firebase cloud function for the same task.

Firebase cloud functions lets you run backend code on the cloud and can be triggered with HTTP events or listen for the events on the cloud, like user registration.

Procedure

  1. Firebase uses our Gmail ID for login, so make sure to have a Gmail ID and on the first sight we will be greeted with Firebase console, where we can see our created or imported firebase apps.

  1. Create the app by clicking on the Add Project Icon and write the name of the application (e.g. Test Application) and select the region, in my case it is India. Firebase will automatically generated an application ID for the app. Click on Create Project to complete creation of project

  2. After Completion, click on the project to enter into the project. You will be greeted with an overview saying to integrate firebase with your project. We will click on the Add Firebase to web App and save the config as JSON in a file as clientKey.json for later use.

  1. Now we need to install the firebase tools on our local machine so for that execute
npm i -g firebase-tools

 

  1. Now login from the CLI so that firebase gets token for the Gmail ID of the user and can access the firebase account of that Gmail ID.
firebase login

 

  1. After giving permissions to the firebase CLI from your Gmail account in the new tab opened in browser, create a folder named cloud_functions in the project directory and in that execute
firebase init

 

  1. Select only functions from the list of options by pressing space.

  2. After this select the project from the list where you want to use the cloud function. You can skip the step if you later want to add the cloud function to project by selecting don’t setup a default project and can later be used by command
firebase use --add

  1. Choose the language of choice

  2. If you want, you can enforce eslint on the project and after this the cloud function is set up and the directory structure looks as follows.

  3. We will write our cloud function in index.js. So let’s take a look at index.js
const functions = require('firebase-functions');

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });

 

As we can see there is a sample function already given, we don’t need that sample function so we will remove it and will write the logic for sending mail. Before that we need to acquire the key for service accounts so that admin functionality can be accessed in the cloud function. So for that go to project settings and then service accounts and click on Generate New Private Key  and save it as serviceKey.json

  1. Now the directory structure will look like this after adding the clientKey.json and serviceKey.json

  2. We will use node-mailer for sending mails in cloud functions and as there is a limitation on the gmail account to send only 500 mails in a day, we can use third party services like sendGrid and others for sending mails with firebase. Configure node-mailer for sending mails as
const nodemailer = require('nodemailer');

const gmailEmail = functions.config().gmail.email;
const gmailPassword = functions.config().gmail.password;
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: gmailEmail,
pass: gmailPassword
}
});

 

Also set the environment variables for the cloud functions like email and password:

firebase functions:config:set gmail.email="Email ID" gmail.password="Password"

 

  1. Logic for sending Greeting Mail on user registration
exports.greetingMail = functions.auth.user().onCreate((user) => {
const email = user.email;
const displayName = user.displayName;

return sendGreetingMail(email, displayName);
});

function sendGreetingMail(email, displayName) {
const mailOptions = {
from: `${APP_NAME}<noreply@firebase.com>`,
to: email,
};

mailOptions.subject = `Welcome to Badgeyay`;
mailOptions.text = `Hey ${displayName || ''}! Welcome to Badgeyay. We welcome you onboard and pleased to offer you service.`;
return mailTransport.sendMail(mailOptions).then(() => {
return console.log('Welcome mail sent to: ', email)
}).catch((err) => {
console.error(err.message);
});
}

 

Function will get triggered on creation of user in firebase and calls the greeting mail function with parameters as the email id of the registered user and the Display name. Then a default template is used to send mail to the recipient and Logged on successful submission.

  1. Currently firebase admin sdk doesn’t support the functionality to send verification mail but the client SDK does. So the approach which is followed in badgeyay is that admin SDK will create a custom token and client sdk will use that custom token to sign in and them send verification mail to the user.
exports.sendVerificationMail = functions.auth.user().onCreate((user) => {
const uid = user.uid;
if (user.emailVerified) {
console.log('User has email already verified: ', user.email);
return 0;
} else {
return admin.auth().createCustomToken(uid)
.then((customToken) => {
return firebase.auth().signInWithCustomToken(customToken)
})
.then((curUser) => {
return firebase.auth().onAuthStateChanged((user_) => {
if (!user.emailVerified) {
user_.sendEmailVerification();
return console.log('Verification mail sent: ', user_.email);
} else {
return console.log('Email is already verified: ', user_.email);
}
})
})
.catch((err) => {
console.error(err.message);
})
}
});

 

  1. Now we need to deploy the functions to firebase.
firebase deploy --only functions

 

Link to the respective PR  : Link

 

Topics Involved

  • Firebase Admin SDK
  • Configuring Gmail for third party apps
  • Token Verification and verification mail by client SDK
  • Nodemailer and Express.js

 

Resources

  • Firebase Cloud functions – Link
  • Extending authentication with cloud function – Link
  • Custom Token Verification – Link
  • Nodemailer message configuration – Link
  • Issue discussion on sending verification mail with admin SDK – Link