Dialog Component in SUSI.AI

Dialog Component in SUSI.AI is rendered in App.js to remove code redundancy. Redux is integrated in the Dialog component which allows us to open/close the dialog from any component by altering the modal states. This implementation allows us to get rid of the need of having dialog component in different components. Redux Code There are two actions and reducers which control the dialog component. Default state of isModalOpen is false and modalType is an empty string. To open a dialog modal the action openModal is dispatched, which sets isModalOpen to true and the modalType. To close a dialog modal the action closeModal is dispatched, which sets isModalOpen to default state i.e. false. import { handleActions } from 'redux-actions'; import actionTypes from '../actionTypes'; const defaultState = { modalProps: { isModalOpen: false, modalType: '', }, }; export default handleActions( { [actionTypes.UI_OPEN_MODAL](state, { payload }) { return { ...state, modalProps: { isModalOpen: true, ...payload, }, }; }, [actionTypes.UI_CLOSE_MODAL](state) { return { ...state, modalProps: defaultState.modalProps, }; }, } defaultState, ); Shared Dialog Component Dialog Modal can be opened from any component by dispatching an action.  To open a Dialog Modal: this.props.actions.openModal({modalType: [modal name]}); To close a Dialog Modal: this.props.actions.closeModal(); Shared Dialog Component has a DialogData object which contains objects with two main properties : Dialog component and Dialog size. Other props can also be passed along with these two properties such as fullScreen. Dialog Content of different Dialogs are present in their respective folders. Each Dialog Content has a Title, Content and Actions.Different Dialog types present are: Confirm Delete with Input: This dialog modal is used when a user deletes account, device and skill. Confirm Dialog: This dialog modal is used where confirmation is required from the user/admin such as on changing skill status, on password reset,etc.Share Dialog: This dialog modal opens up when the share icon is clicked in the chat.Standard Action Dialog: This dialog modal opens up on restore skill, delete feedback, system settings and bot.Tour Dialog: This dialog modal opens up SUSI.AI tour. To add a new Dialog to DialogSection, the steps are: Import the Dialog Content ComponentAdd the Dialog Component to DialogData object in the following manner: const DialogData = { [dialog componet name]: { Component : [imported dialog component name], size : [size of the Dialog Component]}, } Code (Reduced) const DialogData = {   login: { Component: Login, size: 'sm' }, } const DialogSection = props => {  const {    actions,    modalProps: { isModalOpen, modalType, ...otherProps },    visited,  } = props;  const getDialog = () => {    if (isModalOpen) {      return DialogData[modalType];    }    return DialogData.noComponent;  };  const { size, Component, fullScreen = false } = getDialog(); return ( <Dialog     maxWidth={size}     fullWidth={true}     open={isModalOpen || !visited}     onClose={isModalOpen ? actions.closeModal : actions.setVisited}      fullScreen={fullScreen}    >     <DialogContainer>       {Component ? <Component {...otherProps} /> : null}     </DialogContainer>  </Dialog> ) }; In conclusion, having a shared dialog component reduces redundant code and allows to have a similar Dialog UI across the…

Continue ReadingDialog Component in SUSI.AI

My Devices in SUSI.AI

In this blog I’ll be explaining how to view, edit and delete connected devices from SUSI.AI webclient. To connect a device open up the SUSI.AI android app, and fill the details accordingly. Device can also be connected by logging in to your raspberry pi. Once the devices is connected you can edit, delete and access specific features for the device from the web client. My Devices All the connected devices can be viewed in My Devices tab in the Dashboard. In this tab all the devices connected to your account are listed in a table along with their locations on the map. Each device table row has three action buttons - view, edit and delete. Clicking on the view button takes to device specific page. Clicking on the edit button makes the fields name and room editable in table row. Clicking on the delete button opens a confirm with input dialog. Device can be deleted by entering the device name and clicking on delete. To fetch all the device getUserDevices action is dispatched on component mounting which sets the reducer state devices in settings reducer. initialiseDevices function is called after all the devices are fetched from the server. This function creates an array of objects of devices with name, room, macId, latitude, longitude and location. componentDidMount() { const { accessToken, actions } = this.props; if (accessToken) { actions .getUserDevices() .then(({ payload }) => { this.initialiseDevices(); this.setState({ loading: false, emptyText: 'You do not have any devices connected yet!', }); }) .catch(error => { this.setState({ loading: false, emptyText: 'Some error occurred while fetching the devices!', }); console.log(error); }); } document.title = 'My Devices - SUSI.AI - Open Source Artificial Intelligence for Personal Assistants, Robots, Help Desks and Chatbots'; } initialiseDevices = () => { const { devices } = this.props; if (devices) { let devicesData = []; let deviceIds = Object.keys(devices); let invalidLocationDevices = 0; deviceIds.forEach(eachDevice => { const { name, room, geolocation: { latitude, longitude }, } = devices[eachDevice]; let deviceObj = { macId: eachDevice, deviceName: name, room, latitude, longitude, location: `${latitude}, ${longitude}`, }; if ( deviceObj.latitude === 'Latitude not available.' || deviceObj.longitude === 'Longitude not available.' ) { deviceObj.location = 'Not found'; invalidLocationDevices++; } else { deviceObj.latitude = parseFloat(latitude); deviceObj.longitude = parseFloat(longitude); } devicesData.push(deviceObj); }); this.setState({ devicesData, invalidLocationDevices, }); } }; Device Page Clicking on the view icon button in my devices redirects to mydevices/:macId. This page consists of device information in tabular format, local configuration settings and location of the device on the map. User can edit and delete the device from actions present in table. Local configuration settings can be accessed only if the user is logged in the local server. Edit Device To edit a device click on the edit icon button in the actions column of the table. The name and room field become editable.On changing the values handleChange function is called which updates the devicesData state. Clicking on the tick icon saves the new details by calling the onDeviceSave function. This function class the addUserDevice…

Continue ReadingMy Devices in SUSI.AI

Tax Information on Public Ticket Page

This blog post will elaborate on how Tax Information is being displayed on the public page of an event. In current implementation, the user gets to know the total tax inclusive amount only after he/she decides to place an order but no such information was given to them on the public ticket page itself. Order summary example in eventyay Example : In initial implementation, the user gets to know that the order is of only $120 and no information is given about the additional 30% being charged and taking the total to $156. To tackle this issue, I added two hybrid components to the ticket object to handle the two tax cases :  Inclusion in the price : In European and Asian Countries , the tax amount is included in the ticket price itself. For this case, I created the following parameter to store the tax amount included in gross amount. // app/models/ticket.js includedTaxAmount: computed('event.tax.isTaxIncludedInPrice', 'event.tax.rate', function() {   const taxType = this.event.get('tax.isTaxIncludedInPrice');   if (taxType) {     const taxRate = this.event.get('tax.rate');     return ((taxRate * this.price) / (100 + taxRate)).toFixed(2);   }   return 0; }) Added on the ticket price : In basic US tax policy, the tax amount is added on top of the ticket price. For such cases I have added a new attribute to ticket model which calculates the total amount payable for that particular ticket with tax inclusion // app/models/ticket.js ticketPriceWithTax: computed('event.tax.isTaxIncludedInPrice', 'event.tax.rate', function() {   let taxType = this.event.get('tax.isTaxIncludedInPrice');   if (!taxType) {     return ((1 + this.event.get('tax.rate') / 100) * this.price).toFixed(2);   }   return this.price; }) Now, the public ticket page has to be edited accordingly. The design I decided to follow is inspired by eventbrite itself :  Eventbrite specimen of the proposed implementation For this implementation, I modified the ticket list template to accommodate the changes in the following way :  // app/components/public/ticket-list.hbs<td id="{{ticket.id}}_price"> {{currency-symbol eventCurrency}} {{format-number ticket.price}} {{#if (and taxInfo (not-eq ticket.type 'free'))}}   {{#if showTaxIncludedMessage}}     <small class="ui gray-text small">       {{t 'includes'}} {{currency-symbol eventCurrency}} {{format-number ticket.includedTaxAmount}}     </small>   {{else}}     <small class="ui gray-text small">       + {{currency-symbol eventCurrency}} {{format-number (sub ticket.ticketPriceWithTax ticket.price)}}     </small>   {{/if}}   <div>     <small class="ui gray-text tiny aligned right">({{taxInfo.name}})</small>   </div> {{/if}}</td> Tax amount is included in ticket price Hence making the new public ticket list display to look like this in case of tax amount inclusion and additional charge as follows Tax amount is charged over the base price Discount Code application cases: In the cases when a user applies the discount code, the ticket price need to be updated, hence, the tax applied has to be updated accordingly. I achieved this by updating the two computed properties of the ticket model on each togglePromotionalCode and applyPromotionalCode action. When a promotional code is applied, the appropriate attribute is updated according to the discount offered. // app/components/public/ticket-list.jstickets.forEach(ticket => { let ticketPrice = ticket.get('price'); let taxRate = ticket.get('event.tax.rate'); let…

Continue ReadingTax Information on Public Ticket Page

Data Binding with Kotlin in Eventyay Attendee

Databinding is a common and powerful technique in Android Development. Eventyay Attendee has found many situations where data binding comes in as a great solution for our complex UI. Let’s take a look at this technique. Problems without data binding in Android DevelopmentImplementing Databinding with Kotlin inside FragmentImplementing Databinding with Kotlin inside RecyclerView/AdapterResults and GIFConclusions PROBLEMS WITHOUT DATABINDING IN ANDROID DEVELOPMENT Getting the data and fetching it to the UI is a basic work in any kind of application. With Android Development, the most common way to do is it to call function like .setText(), isVisible = True/False,.. in your fragment. This can create many long boilerplate codes inside Android classes. Databinding removes them and moves to the UI classes (XML). IMPLEMENTING DATABINDING IN FRAGMENT VIEW Step 1: Enabling data binding in the project build.gradle android { dataBinding { enabled = true } Step 2: Wrap the current layout with <layout></layout> tag. Inside that, put <data></data> to indicate any variables needed for data binding. For example, this code here display an event variable for our fragment about event details: <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:bind="http://schemas.android.com/tools"> <data> <variable name="event" type="org.fossasia.openevent.general.event.Event" /> </data> <androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/eventCoordinatorLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> Step 3: Bind your data in the XML file and create a Binding Adapter class for better usage With the setup above, you can start binding your data with “@{<data code here>}” <TextView android:id="@+id/eventName" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/layout_margin_large" android:layout_marginTop="@dimen/layout_margin_large" android:layout_marginRight="@dimen/layout_margin_large" android:text="@{event.name}" android:fontFamily="sans-serif-light" android:textColor="@color/dark_grey" android:textSize="@dimen/text_size_extra_large" app:layout_constraintEnd_toEndOf="@+id/eventImage" app:layout_constraintStart_toStartOf="@+id/eventImage" app:layout_constraintTop_toBottomOf="@+id/eventImage" tools:text="Open Source Meetup" /> Sometimes, to bind our data normally we need to use a complex function, then creating Binding Adapter class really helps. For example, Eventyay Attendee heavily uses Picasso function to fetch image to ImageView: @BindingAdapter("eventImage") fun setEventImage(imageView: ImageView, url: String?) { Picasso.get() .load(url) .placeholder(R.drawable.header) .into(imageView) } <ImageView android:id="@+id/eventImage" android:layout_width="@dimen/layout_margin_none" android:layout_height="@dimen/layout_margin_none" android:scaleType="centerCrop" android:transitionName="eventDetailImage" app:layout_constraintDimensionRatio="2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:eventImage="@{event.originalImageUrl}" app:layout_constraintTop_toBottomOf="@id/alreadyRegisteredLayout" /> Step 4: Finalize data binding setup in Android classes. We can create a binding variable. The binding root will serve as the root node of the layout. Whenever data is needed to be bind, set the data variable stated to that binding variable and call function executePendingBingdings() private lateinit var rootView: View private lateinit var binding: FragmentEventBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_event, container, false) rootView = binding.root binding.event = event binding.executePendingBindings() SOME NOTES In the example mentioned above, the name of the binding variable class is auto-generated based on the name of XML file + “Binding”. For example, the XML name was fragment_event so the DataBinding classes generated name is FragmentEventBinding.The data binding class is only generated only after compiling the project.Sometimes, compiling the project fails because of some problems due to data binding without any clear log messages, then that’s probably because of error when binding your data in XML class. For example, we encounter a problem when changing the value in Attendee data class from firstname to firstName but XML doesn’t follow the update. So make sure you bind your data correctly <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/layout_margin_large" android:textColor="@color/black" android:textSize="@dimen/text_size_expanded_title_large" android:text="@{attendee.firstname + ` ` + attendee.lastname}" tools:text="@string/name_preview" />…

Continue ReadingData Binding with Kotlin in Eventyay Attendee

How to fix undetected Arduino boards in Android

In the development process of the Neurolab Android app, we needed an Arduino-Android connection. This blog explains how to  establish the connection and getting the Arduino board detected in my Android device Context-connecting the board and getting it detected Arduino boards are primarily programmed from the Desktop using the Arduino IDE, but they are not limited to the former. Android devices can be used to program the circuit boards using an application named Arduinodroid. Arduino is basically a platform for building various types of electronic projects and the best part about it is that, it is open-sourced. Arduino, the company has got two products The physical programmable circuit board (often referred to as a microcontroller).  Examples of Arduino circuit boards - UNO, UNO CH340G, Mega, etc. Find more here. Connecting the board and getting it detected Arduino boards are primarily programmed from the Desktop using the Arduino IDE, but they are not limited to the former. Android devices can be used to program the circuit boards using an application named Arduinodroid. In this blog, we are going to use Arduinodroid app for establishing a connection between the Arduino board and the Android device, getting the board detected in the Android phone and uploading a sketch to it. Materials/Gadgets required:- Arduino board (UNO preferably)Arduino-USB CableOTG CableAndroid device Now, one of the most frequent issues, while establishing a connection and getting the Arduino board detected with the Android device, is the error message of: “No Arduino boards detected” in the Arduinodroid app. There can be a few core reasons for this - Your Android mobile device isn’t USB-OTG supported - Probably because it is an old model or it might be a company/brand-specific issue.Disabled OTG Mode - Be sure to enable USB-OTG mode (only if your device has one) from the Developer options in your Android device settings. Even after trying and making sure of these above points, if you still continue to get an error while uploading a sketch from the Arduinodroid app like this:                                                             Figure 1: The Error Message Follow the steps below carefully and simultaneously one after the other: Look for any external module attached to your Arduino board using jumper wires. If so, remove those connections completely and press the reset button on the Arduino circuit board. The attached modules can be one of the following: Micro SD Card module, Bluetooth module, etc.Remove pin connections, if any from the TX and RX pin-slots in the Arduino board. These pre-attached pins can cause unnecessary signal transfers which can hinder and make the actual port of Arduino board busy.Before connecting the Arduino to the Android device, go to the drop down menu in the app at the top-right corner -> Settings -> Board Type -> Arduino -> UNONow, you need to code a sketch and make it ready for compile and upload to the circuit board. We will use a basic example sketch for this case. Feel free to try out your own custom coded Arduino sketches. Go to the…

Continue ReadingHow to fix undetected Arduino boards in Android

Implementing Attendee Forms in Wizard of Open Event Frontend

This blog post illustrates on how the order form is included in the attendee information of the Open Event Frontend form  and enabling the organizer to choosing what information to collect from the attendee apart from the mandatory data i.e. First Name, Last Name and the Email Id during the creation of event itself. The addition of this feature required alteration in the existing wizard flow to accommodate this extra step. This new wizard flow contains the step : Basic Details : Where organizer fills the basic details regarding the event.Attendee Form : In this step, the organizer can choose what information he/she has to collect from the ticket buyers.Sponsors : This step enables the organizer to fill in the sponsor detailsSession and Speakers : As the name suggests, this final step enables the organizer to fill in session details to be undertaken during the event. This essentially condensed the flow to this : The updated wizard checklist To implement this, the navigation needed to be altered first in the way that Forward and Previous buttons comply to the status bar steps // app/controller/create.jsmove() {     this.saveEventDataAndRedirectTo(       'events.view.edit.attendee',       ['tickets', 'socialLinks', 'copyright', 'tax', 'stripeAuthorization']     );   } //app/controller/events/view/edit/sponsorshipmove(direction) {     this.saveEventDataAndRedirectTo(       direction === 'forwards' ? 'events.view.edit.sessions-speakers' : 'events.view.edit.attendee',       ['sponsors']     );   } Once the navigation was done, I decided to add the step in the progress bar by simply including the attendees form in the event mixin. // app/mixins/event-wizard.js    {       title     : this.l10n.t('Attendee Form'),       description : this.l10n.t('Know your audience'),       icon     : 'list icon',       route     : 'events.view.edit.attendee'     } Now a basic layout for the wizard is prepared, all what is left is setting up the route for this step and including it in the router file. I took my inspiration for setting up the route from events/view/tickets/order-from.js and implemented it like this: // app/routes/events/view/edit/attendee.jsimport Route from '@ember/routing/route';import CustomFormMixin from 'open-event-frontend/mixins/event-wizard';import { A } from '@ember/array';export default Route.extend(CustomFormMixin, { titleToken() {   return this.l10n.t('Attendee Form'); }, async model() {   let filterOptions = [{     name : 'form',     op : 'eq',     val : 'attendee'   }];   let data = {     event: this.modelFor('events.view')   };   data.customForms = await data.event.query('customForms', {     filter       : filterOptions,     sort         : 'id',     'page[size]' : 50   });   return data; }, afterModel(data) {   /**    * Create the additional custom forms if only the compulsory forms exist.    */   if (data.customForms.length === 3) {     let customForms = A();     for (const customForm of data.customForms ? data.customForms.toArray() : []) {       customForms.pushObject(customForm);     }     const createdCustomForms = this.getCustomAttendeeForm(data.event);     for (const customForm of createdCustomForms ? createdCustomForms : []) {…

Continue ReadingImplementing Attendee Forms in Wizard of Open Event Frontend

Dependency Injection with Kotlin Koin in Eventyay Attendee

Eventyay Attendee Android app contains a lot of shared components between classes that should be reused. Dependency Injection with Koin really comes in as a great problem solver. Dependency Injection is a common design pattern used in various projects, especially with Android Development. In short, dependency injection helps to create/provide instances to the dependent class, and share it among other classes. Why using Koin?Process of setting up Koin in the applicationResultsConclusionResources Let’s get into the details WHY USING KOIN? Before Koin, dependency injection in Android Development was mainly used with other support libraries like Dagger or Guice. Koin is a lightweight alternative that was developed for Kotlin developers. Here are some of the major things that Koin can do for your project: Modularizing your project by declaring modulesInjecting class instances into Android classesInjecting class instance by the constructorSupporting with Android Architecture Component and KotlinTesting easily SETTING UP KOIN IN THE ANDROID APPLICATION Adding the dependencies to build.gradle // Koin implementation "org.koin:koin-android:$koin_version" implementation "org.koin:koin-androidx-scope:$koin_version" implementation "org.koin:koin-androidx-viewmodel:$koin_version" Create a folder to manage all the dependent classes. Inside this Modules class, we define modules and create “dependency” class instances/singletons that can be reused or injected. For Eventyay Attendee, we define 5 modules: commonModule, apiModule, viewModelModule, networkModule, databaseModule. This saves a lot of time as we can make changes like adding/removing/editing the dependency in one place. Let’s take a look at what is inside some of the modules: DatabaseModule val databaseModule = module { single { Room.databaseBuilder(androidApplication(), OpenEventDatabase::class.java, "open_event_database") .fallbackToDestructiveMigration() .build() } factory { val database: OpenEventDatabase = get() database.eventDao() } factory { val database: OpenEventDatabase = get() database.sessionDao() } CommonModule val commonModule = module { single { Preference() } single { Network() } single { Resource() } factory { MutableConnectionLiveData() } factory<LocationService> { LocationServiceImpl(androidContext()) } } ApiModule val apiModule = module { single { val retrofit: Retrofit = get() retrofit.create(EventApi::class.java) } single { val retrofit: Retrofit = get() retrofit.create(AuthApi::class.java) } NetworkModule single { val connectTimeout = 15 // 15s val readTimeout = 15 // 15s val builder = OkHttpClient().newBuilder() .connectTimeout(connectTimeout.toLong(), TimeUnit.SECONDS) .readTimeout(readTimeout.toLong(), TimeUnit.SECONDS) .addInterceptor(HostSelectionInterceptor(get())) .addInterceptor(RequestAuthenticator(get())) .addNetworkInterceptor(StethoInterceptor()) if (BuildConfig.DEBUG) { val httpLoggingInterceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } builder.addInterceptor(httpLoggingInterceptor) } builder.build() } single { val baseUrl = BuildConfig.DEFAULT_BASE_URL val objectMapper: ObjectMapper = get() val onlineApiResourceConverter = ResourceConverter( objectMapper, Event::class.java, User::class.java, SignUp::class.java, Ticket::class.java, SocialLink::class.java, EventId::class.java, EventTopic::class.java, Attendee::class.java, TicketId::class.java, Order::class.java, AttendeeId::class.java, Charge::class.java, Paypal::class.java, ConfirmOrder::class.java, CustomForm::class.java, EventLocation::class.java, EventType::class.java, EventSubTopic::class.java, Feedback::class.java, Speaker::class.java, FavoriteEvent::class.java, Session::class.java, SessionType::class.java, MicroLocation::class.java, SpeakersCall::class.java, Sponsor::class.java, EventFAQ::class.java, Notification::class.java, Track::class.java, DiscountCode::class.java, Settings::class.java, Proposal::class.java) Retrofit.Builder() .client(get()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JSONAPIConverterFactory(onlineApiResourceConverter)) .addConverterFactory(JacksonConverterFactory.create(objectMapper)) .baseUrl(baseUrl) .build() } As described in the code, Koin support single for creating a singleton object, factory for creating a new instance every time an object is injected. With all the modules created, it is really simple to get Koin running in the project with the function startKoin() and a few lines of code. We use it inside the application class: startKoin { androidLogger() androidContext(this@OpenEventGeneral) modules(listOf( commonModule, apiModule, viewModelModule, networkModule, databaseModule )) } Injecting created instances defined in the modules can be used in two way, directly inside…

Continue ReadingDependency Injection with Kotlin Koin in Eventyay Attendee

Announcing the FOSSASIA Codeheat Winners 2018/19

We are very proud to announce our Grand Prize Winners and Finalist Winners of Codeheat 2018/2019. Codeheat participants not only solved a stunning number of issues in FOSSASIA's projects, reviewed pull requests, shared scrums, and wrote blog posts, but most importantly they encouraged and helped each other and collaborated across borders and cultures. More than 700 developers from 18 countries participated in the contest supported by 37 mentors. Over 2000 pull requests were merged. Thank you all for this amazing achievement! With so many outstanding developers participating it was extremely difficult to decide the Grand Prize and Finalist Winners of the contest. Our winners stand out in particular as they contributed to FOSSASIA projects on a continuously high level following our Best Practices. Each of the Grand Prize Winners is awarded a travel grant to join us at the FOSSASIA Summit in Singapore in March where they receive the official Codeheat award, and meet with mentors and FOSSASIA developers. Other Finalist Winners will receive travel support vouchers to go to a Free and Open Source Software event of their choice. Congratulations to our Grand Prize Winners, Finalist Winners, and all of the participants who spent the last few of months learning, sharing and contributing to Free and Open Source Projects. Well-done! We are truly impressed by your work, your progress and advancement. The winners are (in alphabetical order): Grand Prize Winners Aakash S. MallikHarshit KhandelwalShubham Gupta Finalist Winners Aditya SrivastavaSamagra GuptaShridhar GoelShubham KumarPranav KulshresthaRaj Vaibhav DubeyYogesh Sharma About Codeheat Codeheat is a contest that the FOSSASIA organization is honored to run every year. We saw immense growth this year in participants and the depth of contributions. Thank you Mentors and Supporters Our 40+ mentors and many project developers, the heart and soul of Codeheat, are the reason the contest thrives. Mentors volunteer their time to help participants become open source contributors. Mentors spend hundreds of hours during answering questions, reviewing submitted code, and welcoming the new developers to project. Codeheat would not be possible without their patience and tireless efforts. Learn more about this year's mentors on the Codeheat website. Certificate of Participation Participating developers, mentors and the FOSSASIA admin team learnt so much and it was an amazing and enriching experience and we believe the learnings are the main take-away of the program. We hope to see everyone continuing their contributions, sharing what they have learnt with others and to seize the opportunity to develop their code profile with FOSSASIA. We want to work together with the Open Tech community to improve people’s lives and create a better world for all. As a participating developer or mentor, you will receive your certificate over the upcoming weeks. Thank you! More Links Codeheat Website: https://codeheat.orgCodeheat Twitter: https://twitter.com/codeheat_Codeheat Facebook: https://www.facebook.com/codeheat.org/FOSSASIA Website: https://fossasia.orgFOSSASIA on GitHub: https://github.com/fossasia

Continue ReadingAnnouncing the FOSSASIA Codeheat Winners 2018/19

Integrating Redux with SUSI.AI Web Clients

In this blog post, we are going to go through the implementation of the Redux integration on the SUSI.AI web clients. The existing SUSI.AI WebChat codebase has Flux integrated into it, but integrating Redux would make it a lot easier to manage the app state in a single store. And would result in a more maintainable and performant application. Let us go through the implementation in the blog - The key steps involved the following - Restructuring the directory structure of the repository to enable better maintenance.Creating a Redux store and configuring the middlewares.Standardizing the format for writing actions and make API calls on dispatching an action.Standardizing the format for writing reducers.Hook the components to the Redux store. Restructuring the directory structure DIrectory structure for https://chat.susi.ai All the redux related files and utils are put into the redux directory, to avoid any sort of confusion, better maintenance and enhanced discoverability. The prime reason for it also because the integration was done side-by-side the existing Flux implementation.The actions and reducers directory each has a index.js, which exports all the actions and reducers respectively, so as to maintain a single import path for the components and this also helped to easily split out different types of actions/reducers. Creating Redux store and configure middlewares import { createStore as _createStore, applyMiddleware } from 'redux'; import { routerMiddleware } from 'react-router-redux'; import reduxPromise from 'redux-promise'; import reducers from './reducers'; export default function createStore(history) { // Sync dispatched route actions to the history const reduxRouterMiddleware = routerMiddleware(history); const middleware = [reduxRouterMiddleware, reduxPromise]; let finalCreateStore; finalCreateStore = applyMiddleware(...middleware)(_createStore); const store = finalCreateStore( reducers, {}, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), ); return store; } The function createStore takes in the browserHistory (provided by React Router) and returns a single store object that is passed on to the entry point component of the App. The store is passed to the application via the <Provider> component, provided by the react-redux. It is wrapped to the <App> component as follows - ReactDOM.render( <Provider store={store} key="provider"> <App /> </Provider>, document.getElementById('root'), ); The 2 middlewares used are routerMiddleware provided by the react-router-redux and the reduxPromise provided by redux-promise.The routerMiddleware enhances a history instance to allow it to synchronize any changes it receives into application state.The reduxPromise returns a promise to the caller so that it can wait for the operation to finish before continuing. This is useful to assist the application to tackle async behaviour. Standardizing the actions and making API calls on action dispatch The actions file contains the following - import { createAction } from 'redux-actions'; import actionTypes from '../actionTypes'; import * as apis from '../../apis'; const returnArgumentsFn = function(payload) { return Promise.resolve(payload); }; export default { // API call on action dispatch getApiKeys: createAction(actionTypes.APP_GET_API_KEYS, apis.fetchApiKeys), // Returns a promise for actions not requiring API calls logout: createAction(actionTypes.APP_LOGOUT, returnArgumentsFn), }; As new actions are added, it can be added to the actionTypes file and can be added in the export statement of the above snippet. This enables very standard and easy to manage…

Continue ReadingIntegrating Redux with SUSI.AI Web Clients

Make a helper for AJAX requests using axios

In this blog post, we are going to go through the implementation of the helper function that is created for making AJAX requests using axios. Currently, the AJAX calls are made in a very random manner where the calls are written in the component itself and involves rewriting of headers, etc for the API call. Let us go through the implementation in the blog, which will standardise the way to make API calls in SUSI.AI web clients. The primary changes are - Making a common helper for AJAX requests with the help of axios.Making a common file containing all the API calls across the project. Going through the implementation The API calls within the repository were not being made in an organised way, also a lot of redundant code was present. The aim of creating the helper is that, all the API calls is called via this common function. It takes care of the headers and also sending access_token with the API if the user is already logged in for API calls requiring authentication. The function for a API request now looks this simple - // API call for signup export function getSignup(payload) { const { email, password } = payload; const url = `${API_URL}/${AUTH_API_PREFIX}/signup.json`; return ajax.get(url, { signup: email, password }); } In the above snippet, the ajax is the common helper used for making API calls. ajax is an object of functions that returns a promise for  various methods of API requestsWe have primarily taken into consideration GET & POST requests type.The helper function is as follows - /* Insert imports here*/ const cookies = new Cookies(); const obj = {}; ['get', 'post', 'all'].forEach(function(method) { obj[method] = function(url, payload, settings = {}) { /* Request will be aborted after 30 seconds */ settings = { timeout: 30000, dataType: 'json', crossDomain: true, ...settings, }; // Check if logged in if (cookies.get('loggedIn')) { payload = { access_token: cookies.get('loggedIn'), ...payload, }; } return new Promise(function(resolve, reject) { let methodArgs = []; if (method === 'post') { if (payload && payload instanceof FormData !== true) { // Convert to Form Data payload = toFormData(payload); } settings.headers = { 'Content-Type': 'application/x-www-form-urlencoded', ...settings.headers, }; } else if (method === 'get') { if (payload) { // Add params to the URL url += `?${Object.keys(payload) .map(key => key + '=' + payload[key]) .join('&')}`; } } const methodsToAxiosMethodsMap = { get: 'get', post: 'post', all: 'all', }; if (method === 'all') { methodArgs = [url]; } else if (method === 'get') { methodArgs = [url, settings]; } else { methodArgs = [url, payload, settings]; } axios[methodsToAxiosMethodsMap[method]].apply({}, methodArgs).then( function(data = {}, ...restSuccessArgs) { const statusCode = _.get(data, 'status'); /* Send only api response */ let responseData = { statusCode, ..._.get(data, 'data') }; if (method === 'all') { responseData = data; responseData.statusCode = statusCode; } if (payload) { responseData.requestPayload = payload; } // Mark the promise resolved and return the payload resolve(camelizeKeys(responseData), ...restSuccessArgs); }, function(data = {}, ...restErrorArgs) { // If request is canceled by user if (axios.isCancel(data)) {…

Continue ReadingMake a helper for AJAX requests using axios