Case Study: WyFy

Project Overview

As a class project at Nashville Software School, my team was given a dataset from Nashville's open data portal. We were tasked with creating an application for Nashville residents to find free municipal wifi hotspots. As Nashville residents ourselves this project gave us the opportunity to give back to the community we know and love.

My Roles

At the kickoff of our project, all 7 members of our team filled the role of UX designer. Once the discovery and definition phases were finished the team branched off into our alternate roles. I assumed responsibility for the functionality of our app's map, login modal, and alert components.

Project Timeline

One week was the designated timeline for this project. This caused the entire team to place more emphasis than other class projects on the UX process of our application to ensure as many questions were answered prior to beginning development.

MVP Goals

  • Use geolocation to find a user’s current location.
  • Display an Interactive Map showing Nashville-Davidson Co Metro Wifi Locations.
  • Results from all Datasets display in a map/list split view.
  • Expandable location details

Our Users

With all 7 members of our team lending their UX skills during the initial design phases we were able to discover insights and define a concise challenge statement. Our design process of conducting user research, affinity clustering, and journey mapping helped us develop a primary and alternate persona.

Challenge Statement

How might we...
Allow a user the ability to discover local wi-fi resources from Nashville’s Open Data Portal based on their current location.

Code & Data

I made the decision to use the Google maps API in conjunction with the npm package google-maps-react based on several factors. Google maps proved to be the most used map application by our users based on our team's user research. This meant creating an interface with familiar tech for our users. From a development standpoint, google-maps-react allowed me to efficiently integrate the Google maps API with our React app. Finally, being able to reference both Google's and google-maps-react thorough documentation allowed me to quickly solve any issues I encountered during my development.

With all dependencies in place, I began developing the app's map component. The initial component rendered a Google Map at country level zoom value and no markers. By specifying a value for the Map element's zoom attribute I was able to adjust the zoom value to display at a city level. Using the Marker element from google-maps-react I was able to render a marker to ensure functionality.

    import {GoogleApiWrapper, Map, Marker} from 'google-maps-react';
    import React, {Component} from 'react';
    var API_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXX';

    export class MapContainer extends Component {
      render() {
        return (
          <Map google={this.props.google} zoom={14}>
            <Marker name={'Current location'} />
          </Map>
        );
      }
    }

    export default GoogleApiWrapper({
    apiKey: (API_KEY)
    })(MapContainer) 
                        

Once our data team pulled in location data from our open data API, I paired up with a teammate to peer code in an attempt to buy our team some time. Two heads are better than one, and together we developed a functional map component with accurate location markers which our dev team could adjust as required. Taking an array of locations pulled from the open data API and using the .map() method ensured the locations were plotted correctly, and the data in the array remained unchanged.

The next component on my issue list was a user login displayed in a modal element. Our team decided during planning to use a component library as this would save crucial time during development. Using reactstrap, I was able to quickly render a modal for users to enter their login info.

    import React from 'react';
    import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';

    class LandingModal extends React.Component {
      constructor(props) {
        super(props);
        
        this.state = {
          modal: false
        };
        
        this.toggle = this.toggle.bind(this);
        }
        
        toggle() {
          this.setState({
            modal: !this.state.modal
          });
        }

        render() {
          return (
            <div>
              <Button color="danger" onClick={this.toggle}>{this.props.buttonLabel}</Button>
                <Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
                  <ModalHeader toggle={this.toggle}>Modal title</ModalHeader>
                  <ModalBody>
                    <!-- BODY OF MODAL HERE -->
                  </ModalBody>
                  <ModalFooter>
                    <Button color="primary" onClick={this.toggle}>Do Something</Button>{' '}
                    <Button color="secondary" onClick={this.toggle}>Cancel</Button>
                  </ModalFooter>
                </Modal>
            </div>
          );
        }
    }
    export default ModalExample; 
                            

Once our team had user geolocation functional I had to add state to the component to ensure that the modal displayed alerting users that the app requires access to their device's location services. Assigning the modal property a true value ensured that the modal render's on page load. Once a user clicks the "Got it!" button, their location is received by our application, and a map renders displaying nearby wifi hotspots.

    constructor(props) {
      super(props);       
      this.state = {      
        modal: true        // Renders modal on page load 
        geolocated:false,  /* Location properties will update once the Geolocation
        latitude: null,       function is called when user clicks "Got it!" button. */
        longitude:null     
      };      
    };                                
                            

Finally, I was required to create an alert component which prompts users to enable the wifi on their device once logged in. Users should see the alert as soon as the app renders, and the alert should disappear after a few seconds. I imported an alert component from reactstrap's awesome component library and began to tailor it to suit our user's needs.

    import React, {Component} from 'react';
    import { Alert } from 'reactstrap';
    
    class Reminder extends Component {
      render (){
        return (
          <div>
            <Alert color="success">
              Please ensure your wifi is turned on!
            </Alert>
          </div>
        )
      }
    }
    export default Reminder; 
                            

The initial component was a good starting point but it needed a few changes in order to meet our team's design intent. Knowing that the alert should render on app load I began by adding state to the alert. In addition to adding state, I built a function to handle when a user dismisses the alert.

    class Reminder extends React.Component {
      constructor(props) {
        super(props);

        this.state = {
          visible: true
        }; // Displays alert on page render

        this.onDismiss = this.onDismiss.bind(this);
      } 

      // Event listener to close wifi reminder alert.
      onDismiss () {
        this.setState({ visible: false });
      }
                            

The last requirement for the alert was ensuring it disappeared after a set time. This was to ensure that the alert was visible however not persistent. Using the setTimeout method inside of React's lifecycle hook ComponentDidMount, I was able to declare a set time that the alert should be visible.

    componentDidMount (){
      setTimeout(() => {
        this.setState({ visible: false });     
      }, 5000)
    }
                            

With the timeout function in place, it was time to set the values of the components props. Adding isOpen={this.state.visible} to the element allowed its visibility to be controlled by the local state. Finally, adding the toggle={this.onDismiss} assigned the dismiss button's functionality to the component.

    render () {
      return (
        <div>
          <Alert color="success" isOpen={this.state.visible} toggle={this.onDismiss}>
            Please ensure your wifi is on!
          </Alert>
        </div>
      )
    }
                            

With the functionality requirements of the alert complete, I turned to my teammates to help with their components. The deadline was close and every member of the team was giving it their all. After some final style adjustments to polish the app our team lead declared that we had hit our MVP!

Closing

When the project was complete I was not short on fresh knowledge. Integrating with the Google Maps API was another lesson in how to leverage powerful third-party tech in applications. Building the app with Reactjs taught me countless lessons and helped familiarize me with the framework. Finally, the importance of a deliberate and thorough UX/UI design process prior to development was reinforced in my mind. From the user interviews I conducted, to the affinity clusters and journey maps I built in hand with my team, the insights we gathered were crucial in developing an app for our city and its amazing users!