David Klein

JSD4 Final Project

29 November 2016

The final project for my General Assembly JavaScript class is particularly cool. I built a dashboard for people to see my latest Instagram photo, Dribbble shot, and Tweet on one page using React, Node, Firebase, and Express. The project is live on Heroku.

Thank you Jordan, Kyle, and Arthur for pushing me to improve my code.

Node.js

// SETUP
// ---------------------------------------------------
var express = require('express');
var app = express();
var router = express.Router();
app.set('port', (process.env.PORT || 5000));
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');

// routes
router.get('/', function(req, res) {
  res.render('home');
});
router.get('/final', function(req, res) {
  res.render('final');
});
router.get('/contacts', function(req, res) {
  res.render('contacts');
});
app.use('/', router);
app.use('/final', router);
app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

// TWITTER
// ---------------------------------------------------
var Twitter = require('twitter');
var client = new Twitter({
  consumer_key: 'NICE',
  consumer_secret: 'TRY',
  access_token_key: 'BRO',
  access_token_secret: 'BROAGAIN'
});
var params = {screen_name: 'diklein'};
router.get('/tweets', function (req, res) {
  client.get('statuses/user_timeline', params, function(error, tweets, response) {
     if (!error) {
        var tweetJson = JSON.stringify(tweets);
        res.send(tweetJson);
     }
     else {
        console.log(error);
     }
  });
});

// INSTAGRAM
// ---------------------------------------------------
router.get('/instagram', function (req, res) {
  client.get(
  'https://api.instagram.com/v1/users/69315/media/recent/?access_token=NICETRY&count=1',
  function(error, photos, response) {
     if (!error) {
        var instagramJson = JSON.stringify(photos);
        // console.log(instagramJson);
        res.send(instagramJson);
     }
  });
});

// DRIBBBLE
// ---------------------------------------------------
var request = require('request');
router.get('/dribbble', function (req, res) {
  request('https://api.dribbble.com/v1/users/diklein/shots?access_token=WHATUPSON',
  function (error, response, body) {
     if (!error && response.statusCode == 200) {
        res.send(body);
     }
  })
});


     
JavaScript

// RENDER PARENT COMPONENT
// --------------------------------------------------
var RenderComponent = React.createClass({
  // set up the empty state for all data
  getInitialState: function () {
     return {
        // likes first
        twitterLikes: 0,
        dribbbleLikes: 0,
        instagramLikes: 0,
        // twitter data container
        twitterSnapshot: {
           permalink: '',
           text: '',
           time: '',
           timeYear: '',
           timeDayName: '',
           timeDayOfMonth: '',
           timeMonth: ''
        },
        // dribbble data container
        dribbbleSnapshot: {
           permalink: '',
           photoURL: '',
           caption: '',
           time: '',
           timeYear: '',
           timeDayName: '',
           timeDayOfMonth: '',
           timeMonth: ''
        },
        // instagram data container
        instagramSnapshot: {
           permalink: '',
           photoURL: '',
           caption: '',
           time: '',
           timeYear: '',
           timeDayName: '',
           timeDayOfMonth: '',
           timeMonth: ''
        }
     };
  },

  // set up firebase before page is ready
  componentWillMount() {
     this.ref = new Firebase("https://dk-jsd4.firebaseio.com/");
  },

  // after page is ready go round up the data
  componentDidMount() {
     // get firebase data and set likes state
     this.ref.on('value', function (snapshot) {
        var data = snapshot.val();
        if (data !== null) {
           this.setState({
              twitterLikes: data.twitter,
              instagramLikes: data.instagram,
              dribbbleLikes: data.dribbble
           });
        }
     }.bind(this));

     // set state with twitter content with promise
     getTwitterData().then(function (data) {
        this.setState({
           twitterSnapshot: data
        });
     }.bind(this));

     // set state with dribbble content with promise
     getDribbbleData().then(function (data) {
        this.setState({
           dribbbleSnapshot: data
        });
     }.bind(this));

     // set state with instagram content with promise
     getInstagramData().then(function (data) {
        this.setState({
           instagramSnapshot: data
        });
     }.bind(this));

  },

  // if user clicks twitter like link
  increaseTwitterLike: function(e) {
     e.preventDefault();
     // make a new variable because of timing
     const newLikes = this.state.twitterLikes + 1;
     this.setState({ twitterLikes: newLikes });
     // only update twitter value in firebase
     // '.child' allows you to write to a specific part of the firebase data
     this.ref.child('twitter').set(newLikes);
  },

  // if user clicks dribbble like link
  increaseDribbbleLike: function(e) {
     e.preventDefault();
     // make a new variable because of timing
     const newLikes = this.state.dribbbleLikes + 1;
     this.setState({ dribbbleLikes: newLikes });
     // only update dribbble value in firebase
     this.ref.child('dribbble').set(newLikes);
  },

  // if user clicks instagramLike like link
  increaseInstagramLike: function(e) {
     e.preventDefault();
     // make a new variable because of timing
     const newLikes = this.state.instagramLikes + 1;
     this.setState({ instagramLikes: newLikes });
     // only update instagram value in firebase
     this.ref.child('instagram').set(newLikes);
  },

  // render individual components
  // each component receives props — likes, state, and click function}
  // '...' saves significant space by not specifying each part of the object
  render: function() {
     return(
        <div>
        <TwitterComponent likes={this.state.twitterLikes} {...this.state.twitterSnapshot} onClick={this.increaseTwitterLike} />
        <InstagramComponent likes={this.state.instagramLikes} {...this.state.instagramSnapshot} onClick={this.increaseInstagramLike} />
        <DribbbleComponent likes={this.state.dribbbleLikes} {...this.state.dribbbleSnapshot} onClick={this.increaseDribbbleLike} />
        </div>
     )
  }
});

// TWITTER
// --------------------------------------------------
function getTwitterData() {
  // set up promise for json data
  return $.getJSON('/tweets').then(function(tweets) {
     const firstTweet = tweets.filter(tweet => (tweet.text)[0] !== '@')[0];
     const firstTweetDate = new Date(firstTweet.created_at.toString());
     // return data from json
     return {
        'permalink': "https://twitter.com/diklein/status/" + firstTweet.id_str,
        'text': firstTweet.text,
        'time': firstTweetDate.created_at,
        'timeYear': firstTweetDate.getFullYear(),
        'timeDayName': days[firstTweetDate.getDay()],
        'timeDayOfMonth': firstTweetDate.getUTCDate(),
        'timeMonth': months[(firstTweetDate.getUTCMonth() + 1)]
     };
  });
}

// twitter component
function TwitterComponent(props) {
  return (
     <div className="twitter">
        {props.text}
        <span className="time">
           <a href={props.permalink}>{props.timeDayName} {props.timeMonth} {props.timeDayofMonth}, {props.timeYear}</a>
            · <a href='#' onClick={props.onClick} className="twitterLike">Like {props.likes > 0 && ' · ' + props.likes}</a>
        </span>
     </div>
  )
}

// DRIBBBLE
// --------------------------------------------------
// get data out of json
function getDribbbleData() {
  // set up promise for json data
  return $.getJSON('/dribbble').then(function(shots) {
     // setup
     const firstShot = shots[0];
     const firstShotDate = new Date(firstShot.created_at);
     const fixingCaption = firstShot.description.replace('<p>','');
     // return data from json
     return {
        'permalink': firstShot.html_url,
        'photoURL': firstShot.images.hidpi,
        'caption': fixingCaption.replace('</p>',''),
        'time': firstShotDate,
        'timeYear': firstShotDate.getFullYear(),
        'timeDayName': days[firstShotDate.getDay()],
        'timeDayOfMonth': firstShotDate.getUTCDate(),
        'timeMonth': months[(firstShotDate.getUTCMonth() + 1)]
     };
  });
}

// dribbble component
function DribbbleComponent(props) {
  return (
     <div className="dribbble">
        <div className="imgContainer">
           <img src={props.photoURL} />
           </div>
        <div className="dribbbleContent">
           <span className="caption">
              {props.caption}
           </span>
           <span className="time">
              <a href={props.permalink}>{props.timeDayName}, {props.timeMonth} {props.timeDayOfMonth}, {props.timeYear}</a>
               · <a href='#' onClick={props.onClick} className="dribbbleLike">Like {props.likes > 0 && ' · ' + props.likes}</a>
           </span>
        </div>
     </div>
  )
}

// INSTAGRAM
// --------------------------------------------------
// get data out of json
function getInstagramData() {
  // set up promise for json data
  return $.getJSON('/instagram').then(function(photos) {
     // setup
     const firstPhoto = photos.data[0];
     const firstPhotoDate = new Date(firstPhoto.created_time * 1000);
     // return data from json
     return {
        'permalink': firstPhoto.link,
        'photoURL': firstPhoto.images.standard_resolution.url,
        'caption': firstPhoto.caption.text,
        'time': firstPhotoDate,
        'timeYear': firstPhotoDate.getFullYear(),
        'timeDayName': days[firstPhotoDate.getDay()],
        'timeDayOfMonth': firstPhotoDate.getUTCDate(),
        'timeMonth': months[(firstPhotoDate.getUTCMonth() + 1)]
     };
  });
}

// instagram component
function InstagramComponent(props) {
  return (
     <div className="instagram">
        <div className="imgContainer">
           <img src={props.photoURL} />
        </div>
        <div className="instagramContent">
           <span className="caption">
              {props.caption}
           </span>
           <span className="time">
              <a href={props.permalink}>{props.timeDayName}, {props.timeMonth} {props.timeDayOfMonth}, {props.timeYear}</a>
               · 
              <a href='#' onClick={props.onClick} className="instagramLike">Like {props.likes > 0 && ' · ' + props.likes}</a>
           </span>
        </div>
     </div>
  )
}

// render the component that contains the smaller components
const parent = document.querySelector(".parent");
ReactDOM.render(<RenderComponent />, parent);


     
← back to journal