<commit_before>'use strict';
// A fix for client-side components to work via require.
global.document = window.document;
global.navigator = window.navigator;

import fs from 'fs';
import gui from 'nw.gui';

const React = require('react');
import _ from 'lodash';
import Reflux from 'reflux';
import tinder from 'tinderjs';

const CLIENT = new tinder.TinderClient();
const LOCAL = true;
const SECRETS = JSON.parse(fs.readFileSync('./secrets.json', 'utf8'));


/*
 * Flux components.
 */
const TinderActions = Reflux.createActions([
  'loadHistory',
  'loadHistorySuccess',
]);


const MatchStore = Reflux.createStore({
  listenables: TinderActions,
  init() {
    this.loading = false;
    this.matches = [];
  },

  onLoadHistory(){
    this.loading = true;
    this.trigger(this.getState());

    if (LOCAL) {
      const history = JSON.parse(fs.readFileSync('./history.json', 'utf8'));
      TinderActions.loadHistorySuccess(history);
    } else {
      CLIENT.getHistory((error, history) => {
        if (error) throw error;
        console.log('History received.');
        TinderActions.loadHistorySuccess(history);
      });
    }
  },

  onLoadHistorySuccess(history) {
    this.loading = false;
    this.matches = history.matches;
    this.trigger(this.getState());
  },

  getState() {
    return {
      loading: this.loading,
      matches: this.matches,
    };
  },

  getInitialState() {
    return this.getState();
  },
});


/*
 * React components.
 */
var Match = React.createClass({
  propTypes: {
    match: React.PropTypes.object.isRequired
  },

  render: function() {
    var messages = this.props.match.messages;
    var person = this.props.match.person;

    return (
      <div>
        <p><strong>{person.name}</strong> - {person.ping_time}</p>
        {person.photos.map(function(photo) {
          return <img src={photo.processedFiles[3].url} key={photo.id} />;
        })}
        {messages.map(function(message) {
          return <p key={message._id}>{message.sent_date} {message.message}</p>;
        })}
      </div>
    );
  }
});

var MatchList = React.createClass({
  propTypes: {
    matches: React.PropTypes.array.isRequired
  },

  render: function() {
    var matchNodes = this.props.matches
      .map(function(match) {
        return <Match match={match} key={match._id} />
      });
    return <div>{matchNodes}</div>;
  }
});

const Application = React.createClass({
  mixins: [Reflux.connect(MatchStore)],

  handleLoadHistory(e) {
    e.preventDefault();
    TinderActions.loadHistory();
  },

  render() {
    const displayMatches = _(this.state.matches)
      .filter(match => _.has(match, 'person'))
      .sortBy(match => match.person.ping_time)
      .reverse()
      .take(60)
      .value();

    return (
      <div>
        <a className='btn btn-primary' onClick={this.handleLoadHistory}>Load Matches</a>
        <MatchList matches={displayMatches} />
      </div>
    );
  },
});


// Add a nice menu bar with copy and paste.
const nativeMenuBar = new gui.Menu({type: 'menubar'});
nativeMenuBar.createMacBuiltin('OptimalTinder');
const win = gui.Window.get();
win.menu = nativeMenuBar;


// Authorize the client and start the app.
onload = function() {
  if (LOCAL) {
    React.render(<Application />, document.getElementById('application'));
  } else {
    CLIENT.authorize(SECRETS.token, SECRETS.facebook_id, () => {
      console.log('Authorized');
      React.render(<Application />, document.getElementById('application'));
    });
  }
};
<commit_msg>Add facebook token interface in.<commit_after>'use strict';
// A fix for client-side components to work via require.
global.document = window.document;
global.navigator = window.navigator;

import gui from 'nw.gui';

const React = require('react');
import _ from 'lodash';
import Reflux from 'reflux';
import request from 'superagent';
import tinder from 'tinderjs';

const LOGIN_URL = 'https://m.facebook.com/dialog/oauth?client_id=464891386855067&' +
    'redirect_uri=https://www.facebook.com/connect/login_success.html&' +
    'scope=user_birthday,user_relationship_details,user_likes,user_activities,' +
    'user_education_history,user_photos,user_friends,user_about_me,email,' +
    'public_profile&response_type=token';


class ClientFetcher {
  constructor() {
      this.client = new tinder.TinderClient();
  }

  authorize(paramString, resolve, reject) {
    const fbAuthData = _.zipObject(paramString.split('&')
                                              .map(p => p.split('=')));
    const fbUserId = this._getFBUserId(fbAuthData['access_token'], reject);

    this.client.authorize(fbAuthData['access_token'], resolve, () => {
      console.log('Authorized');
      resolve(this.client);
    });
  }

  _getFBUserId(token, reject) {
    const graphUrl = 'https://graph.facebook.com/me?access_token=' + token;
    request.get(graphUrl).end((err, res) => {
      console.log('_getFBUserId');
      if (res.ok) {
        console.log('ok', res.body);
        return res.body.id;
      } else {
        console.log('FUCK: _getFBUserId');
        reject('Error: Could not get FB User ID: ' + res.text);
      }
    });
  }

  static fetch(resolve, reject) {
    let loginPopup = gui.Window.open(LOGIN_URL, {
      title: 'Login to Facebook',
      position: 'center',
      width: 400,
      height: 480,
      focus: true
    });

    const clientFetcher = new ClientFetcher();
    const checkTokenInterval = setInterval(() => {
      if (loginPopup) {
        const loginPopupWindow = loginPopup.window;
        if (loginPopupWindow.closed) {
          clearInterval(checkTokenInterval);
        } else {
          const paramString = loginPopupWindow.document.URL.split('#')[1];
          if (!!paramString) {
            loginPopupWindow.close();
            clearInterval(checkTokenInterval);


            clientFetcher.authorize(paramString, resolve, reject);
          }
        }
      }
    }, 500);

    loginPopup.on('closed', () => {
      clearInterval(checkTokenInterval);
      loginPopup = null;
    });
  }
}


/*
 * Flux components.
 */
const TinderActions = Reflux.createActions({
  'loadClient': {children: ['completed', 'failed']},
  'loadHistory': {children: ['completed', 'failed']},
});


const MatchStore = Reflux.createStore({
  listenables: TinderActions,
  init() {
    this.client = null;
    this.loading = false;
    this.matches = [];
  },

  onLoadClient() {
    ClientFetcher.fetch(TinderActions.loadClient.completed,
                        TinderActions.loadClient.failed);
  },

  onLoadClientCompleted(client) {
    this.client = client;
  },

  onLoadHistory() {
    this.loading = true;
    this.trigger(this.getState());

    this.client.getHistory((error, history) => {
      if (error) throw error;
      console.log('History received.');
      TinderActions.loadHistory.completed(history);
    });
  },

  onLoadHistoryCompleted(history) {
    this.loading = false;
    this.matches = history.matches;
    this.trigger(this.getState());
  },

  getState() {
    return {
      loading: this.loading,
      matches: this.matches,
    };
  },

  getInitialState() {
    return this.getState();
  },
});


/*
 * React components.
 */
var Match = React.createClass({
  propTypes: {
    match: React.PropTypes.object.isRequired
  },

  render: function() {
    var messages = this.props.match.messages;
    var person = this.props.match.person;

    return (
      <div>
        <p><strong>{person.name}</strong> - {person.ping_time}</p>
        {person.photos.map(function(photo) {
          return <img src={photo.processedFiles[3].url} key={photo.id} />;
        })}
        {messages.map(function(message) {
          return <p key={message._id}>{message.sent_date} {message.message}</p>;
        })}
      </div>
    );
  }
});

var MatchList = React.createClass({
  propTypes: {
    matches: React.PropTypes.array.isRequired
  },

  render: function() {
    var matchNodes = this.props.matches
      .map(function(match) {
        return <Match match={match} key={match._id} />
      });
    return <div>{matchNodes}</div>;
  }
});

const Application = React.createClass({
  mixins: [Reflux.connect(MatchStore)],

  handleLoadHistory(e) {
    e.preventDefault();
    TinderActions.loadHistory();
  },

  render() {
    const displayMatches = _(this.state.matches)
      .filter(match => _.has(match, 'person'))
      .sortBy(match => match.person.ping_time)
      .reverse()
      .take(60)
      .value();

    return (
      <div>
        <a className='btn btn-primary' onClick={this.handleLoadHistory}>Load Matches</a>
        <MatchList matches={displayMatches} />
      </div>
    );
  },
});


// Add a nice menu bar with copy and paste.
const nativeMenuBar = new gui.Menu({type: 'menubar'});
nativeMenuBar.createMacBuiltin('OptimalTinder');
const win = gui.Window.get();
win.menu = nativeMenuBar;

// Start the app.
onload = () => {
  React.render(<Application />, document.getElementById('application'));
  TinderActions.loadClient();
};

{'fraction_non_alphanumeric': 0.15296727676095395, 'fraction_numerical': 0.003660565723793677, 'mean_word_length': 2.956121105748135, 'pattern_counts': {'":': 0, '<': 40, '<?xml version=': 0, '>': 53, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5230179', 'n_tokens_mistral': 2878, 'n_tokens_neox': 2797, 'n_words': 527}