Commit 6ad739e1 authored by MrMan's avatar MrMan

Add matches page, some test fixtures, update i18n

parent bf3912e0
......@@ -46,6 +46,7 @@ class AppPage extends React.Component {
}
viewMatches() { this.context.history.pushState(null, '/matches'); }
viewRecs() { this.context.history.pushState(null, '/recs'); }
goToPreferencesPage() { this.context.history.pushState(null, '/preferences'); }
render() {
......@@ -61,7 +62,13 @@ class AppPage extends React.Component {
<div className="pinned-to-bottom app-page-controls">
<div className="row landing-wide-button">
<button onClick={this.viewMatches.bind(this)} className="pure-u-1 pure-button button-lg squared button-success">
<button onClick={this.viewRecs.bind(this)} className="pure-u-1 pure-button button-lg squared button-success">
<i className="fa fa-binoculars"></i> {this.i18n`nav.sections.findAMatch`}
</button>
</div>
<div className="row landing-wide-button">
<button onClick={this.viewMatches.bind(this)} className="pure-u-1 pure-button button-lg squared button-error">
<i className="fa fa-heart"></i> {this.i18n`nav.sections.matches`}
</button>
</div>
......@@ -73,7 +80,7 @@ class AppPage extends React.Component {
</div>
<div className="row landing-wide-button">
<button onClick={this.doLogout.bind(this)} className="pure-u-1 pure-button button-lg squared button-error">
<button onClick={this.doLogout.bind(this)} className="pure-u-1 pure-button button-lg squared">
<i className="fa fa-sign-out"></i> {this.i18n`nav.sections.logout`}
</button>
</div>
......
import React from "react";
import TinderMatchStore from "../stores/tinder-match";
import Actions from "../actions";
import MomentFuzzyDate from "./moment-fuzzy-date";
import _ from "lodash";
import { generateI18NTemplateFn } from "../i18n";
const DEFAULT_STATE = {matchId: null, userInfo: null};
class MatchConvoLink extends React.Component {
constructor(params) {
super(params);
this.state = DEFAULT_STATE;
this.unsubFunctions = {};
this.i18n = generateI18NTemplateFn("en-US");
// Process params
this.state = _.extend(this.state, {
matchId: params.matchId || null,
user: params.user || null,
unseenMessageCount: params.unseenMessageCount || 0
});
console.log("[MATCH CONVO LINK COMPONENT] Match convo link component constructed, initial state:", this.state);
}
componentDidMount() {
this.unsubFunctions["store-tinder-match"] = TinderMatchStore.listen(store => {
console.log("[MATCH CONVO LINK COMPONENT] Detected tinder match store update:", store);
// Update the user info state if the user info is relevant to this component
if (_.has(store.matchedUserInfo, this.state.matchId)) {
console.log(`[MATCH CONVO LINK COMPONENT] Found match ID ${this.state.matchId} in update, updating state`);
this.setState({user: store.matchedUserInfo[this.state.matchId]});
}
});
// Request a user's information if not provided
if (!_.isNull(this.state.matchId) && _.isNull(this.state.user)) {
console.log(`[MATCH CONVO LINK COMPONENT] User info not provided for match with id ${this.state.matchId}, requesting...`);
Actions.retrieveMatchedUserInfo(this.state.matchId);
}
}
componentWillUnmount() {
_.each(this.unsubFunctions, f => f());
}
render() {
console.log("[MATCH CONVO LINK COMPONENT] Rendering link for match with ID:", this.state.matchId);
let user = _.get(this.state, "user", {});
// TODO: show different view (progressive?) if user isn't loaded yet
if (_.isNull(user)) {
return (
<div className="match-convo-link-container">
<div className="pure-g">
<div className="pure-u-1 center-aligned-text">{this.i18n`nav.loadingMessage`}</div>
</div>
</div>
);
}
return (
<div className="match-convo-link-container">
<div className="pure-g">
<div className="pure-u-5-6 right-aligned-text">
<h3 className="match-convo-link-name slim-text no-vertical-margins">{user.name}</h3>
<p className="match-convo-link-last-active-blurb no-vertical-margins super-slim-text">
{this.i18n`pages.matches.lastActive`} <MomentFuzzyDate date={user.ping_time}/>
</p>
</div>
<div className="pure-u-1-6 center-aligned-text">
<div className="pure-g">
<div className="message-count-container">
<div className="pure-u-1">{this.state.unseenMessageCount}</div>
<div className="pure-u-1"><i className="fa fa-comment"></i></div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default MatchConvoLink;
import React from "react";
import { Link, PropTypes } from "react-router";
import TinderRecommendationStore from "../stores/tinder-recommendation";
import LHSNav from "./lhs-nav";
import GenderIcon from "./gender-icon";
import DistanceDescription from "./distance-description";
import MomentFuzzyDate from "./moment-fuzzy-date";
import PictureStrip from "./picture-strip";
import Actions from "../actions";
import LHSNav from "./lhs-nav";
import TinderMatchStore from "../stores/tinder-match";
import TinderUserStore from "../stores/tinder-user";
import MatchConvoLink from "./match-convo-link";
import Constants from "../constants";
import _ from "lodash";
import { generateI18NTemplateFn } from "../i18n";
const DEFAULT_STATE = {recPhotoIndex: 0, rec: null, detailVisible: true};
const DEFAULT_STATE = { matches: [], matchedUserInfo: {}};
class MatchesPage extends React.Component {
constructor(props) {
super(props);
this.unsubFunctions = {};
this.state = DEFAULT_STATE;
this.unsubFunctions = {};
this.i18n = generateI18NTemplateFn("en-US");
// Set rec if available through the TinderRecommendationStore
if (_.isNull(this.state.rec) && !_.isNull(TinderRecommendationStore.state.currentRec)) {
this.state.rec = _.get(TinderRecommendationStore, "state.currentRec", null);
// Set matches if user is available through the TinderMatchStore
if (_.isNull(this.state.matches) && !_.isNull(TinderMatchStore.state.user)) {
let matches = _.clone(TinderMatchStore.state.matches, true);
this.state = _.extend(this.state, {matches});
}
console.log("[MATCHES PAGE COMPONENT] Match page constructed, initial state:", this.state);
}
componentDidMount() {
this.unsubFunctions["store-tinder-recommendations"] = TinderRecommendationStore.listen(state => {
console.log("[MATCHES PAGE COMPONENT] Detected recommendations update, updating list");
// Update list of recommendations
this.setState({rec: state.currentRec});
this.unsubFunctions["store-tinder-match"] = TinderMatchStore.listen(store => {
console.log("[MATCHES PAGE COMPONENT] Detected tinder match store update", store);
this.setState({matches: store.matches, matchedUserInfo: store.matchedUserInfo});
});
// Attempt to fetch updates from twitter whenever matches page loads,
// this will trigger the match store to have updates
Actions.retrieveTinderUpdates();
}
componentWillUnmount() {
_.each(this.unsubFunctions, f => f());
}
toggleDetail() { this.setState({detailVisible: !this.state.detailVisible}); }
pass() { Actions.pass(this.state.rec); }
like() { Actions.like(this.state.rec); }
pullRecommendations() { Actions.pullTinderRecommendations(); }
showMatchChatPage(id) {
console.log("[MATCHES PAGE] Opening chat page for match with ID ", id);
}
render() {
// Return early if there are no rec
if (_.isEmpty(this.state.rec)) {
console.log(`[MATCHES PAGE] Rendering matches page with ${this.state.matches.length} matches`);
let matchLinks = _.map(this.state.matches, m => {
let unseen = _.reduce(m.messages, (acc, m) => !m.seen ? acc+1 : acc , 0);
return (
<div>
<h3>0 Potential Matches</h3>
<h4>Maybe you&#39;d like to update your preferences?</h4>
<div className="row">
<button onClick={this.pullRecommendations} className="pure-button button-success in-stack">
<i className="fa fa-refresh"></i> Find Recommendations
</button>
</div>
<div className="row">
<button className="pure-button button-secondary in-stack">
<i className="fa fa-wrench"></i> Manage Preferences
</button>
</div>
<div className="row">
<button onClick={this.context.history.goBack} className="pure-button button-error">
<i className="fa fa-arrow-left"></i> Back
</button>
</div>
</div>
<MatchConvoLink key={m.id} unseenMessageCount={unseen} matchId={m.id} />
);
}
// Generate teaser fragment
let teaserFragment = null;
if (_.has(this.state.rec, "teaser.string")) {
let teaserFragment = <p className="no-vertical-margins picture-overlay-text">{this.state.rec.teaser.string}</p>;
}
// Generate detail fragment
let detailFragment = (
<div className={`match-detail picture-overlay-text ${this.state.detailVisible ? "visible" : "invisible"}`} >
<p className="picture-overlay-text"><DistanceDescription distance={this.state.rec.distance_mi}/></p>
<p className="picture-overlay-text"><i className="fa fa-users"></i> {this.i18n`pages.matches.friendsInCommon ${this.state.rec.common_friend_count}`}</p>
<p className="picture-overlay-text"><i className="fa fa-thumbs-up"></i> {this.i18n`pages.matches.commonInterests ${this.state.rec.common_like_count}`}</p>
<p className="picture-overlay-text"><i className="fa fa-clock-o"></i> {this.i18n`pages.matches.lastActive`} <MomentFuzzyDate date={this.state.rec.ping_time}/></p>
</div>
);
});
return (
<div className="full-height">
<div>
<LHSNav />
<div className="matches-page-container">
<div className="pure-g">
<div className="pure-u-1 right-aligned-text">
<div className="right-aligned-text">
<h1 className="no-vertical-margins slim-text">{this.i18n`nav.sections.matches`}</h1>
<h3 className="no-vertical-margins super-slim-text">{this.i18n`pages.matches.pageSubtitle`}</h3>
</div>
</div>
</div>
<div className="matches-page-header">
<div className="pure-g">
<LHSNav backEnabled={false}>
<i className="fa fa-info-circle xlarge picture-overlay-text"
onClick={this.toggleDetail.bind(this)}></i>
</LHSNav>
<div className="pure-g">
<div className="pure-u-1">
{matchLinks}
</div>
</div>
</div>
</div>
<div className="pure-g">
<div className="pure-u-1-4"></div>
<div className="pure-u-3-4 right-aligned-text">
<h1 className="picture-overlay-text no-margin">
{this.state.rec.name} <GenderIcon gender={this.state.rec.gender}/>
</h1>
<h3 className="picture-overlay-text no-margin">{this.state.rec.bio}</h3>
</div>
</div>
</div>
{teaserFragment}
<PictureStrip photos={this.state.rec.photos} index={this.state.recPhotoIndex} orientation="vertical"/>
{detailFragment}
<div className="pinned-to-bottom matches-page-controls">
<div className="pure-g">
<div className="pure-u-1-2">
<button onClick={this.pass.bind(this)} className="pure-u-1 pure-button button-lg squared button-error">
<i className="fa fa-binoculars"></i>
</button>
</div>
<div className="pure-u-1-2">
<button onClick={this.like.bind(this)} className="pure-u-1 pure-button button-lg squared button-success">
<i className="fa fa-heart"></i>
</button>
</div>
</div>
</div>
</div>
);
}
}
// Add mixins
MatchesPage.contextTypes = {
history: PropTypes.history
};
export default MatchesPage;
import React from "react";
import { Link, PropTypes } from "react-router";
import TinderRecommendationStore from "../stores/tinder-recommendation";
import LHSNav from "./lhs-nav";
import GenderIcon from "./gender-icon";
import DistanceDescription from "./distance-description";
import MomentFuzzyDate from "./moment-fuzzy-date";
import PictureStrip from "./picture-strip";
import Actions from "../actions";
import _ from "lodash";
import { generateI18NTemplateFn } from "../i18n";
const DEFAULT_STATE = {recPhotoIndex: 0, rec: null, detailVisible: true};
class RecsPage extends React.Component {
constructor(props) {
super(props);
this.unsubFunctions = {};
this.state = DEFAULT_STATE;
this.i18n = generateI18NTemplateFn("en-US");
// Set rec if available through the TinderRecommendationStore
if (_.isNull(this.state.rec) && !_.isNull(TinderRecommendationStore.state.currentRec)) {
this.state.rec = _.get(TinderRecommendationStore, "state.currentRec", null);
}
}
componentDidMount() {
this.unsubFunctions["store-tinder-recommendations"] = TinderRecommendationStore.listen(state => {
console.log("[RECS PAGE COMPONENT] Detected recommendations update, updating list");
// Update list of recommendations
this.setState({rec: state.currentRec});
});
}
componentWillUnmount() {
_.each(this.unsubFunctions, f => f());
}
toggleDetail() { this.setState({detailVisible: !this.state.detailVisible}); }
pass() { Actions.pass(this.state.rec); }
like() { Actions.like(this.state.rec); }
pullRecommendations() { Actions.pullTinderRecommendations(); }
render() {
// Return early if there are no rec
if (_.isEmpty(this.state.rec)) {
return (
<div>
<h3>0 Potential Recs</h3>
<h4>Maybe you&#39;d like to update your preferences?</h4>
<div className="row">
<button onClick={this.pullRecommendations} className="pure-button button-success in-stack">
<i className="fa fa-refresh"></i> Find Recommendations
</button>
</div>
<div className="row">
<button className="pure-button button-secondary in-stack">
<i className="fa fa-wrench"></i> Manage Preferences
</button>
</div>
<div className="row">
<button onClick={this.context.history.goBack} className="pure-button button-error">
<i className="fa fa-arrow-left"></i> Back
</button>
</div>
</div>
);
}
// Generate teaser fragment
let teaserFragment = null;
if (_.has(this.state.rec, "teaser.string")) {
let teaserFragment = <p className="no-vertical-margins picture-overlay-text">{this.state.rec.teaser.string}</p>;
}
// Generate detail fragment
let detailFragment = (
<div className={`rec-detail picture-overlay-text ${this.state.detailVisible ? "visible" : "invisible"}`} >
<p className="picture-overlay-text"><DistanceDescription distance={this.state.rec.distance_mi}/></p>
<p className="picture-overlay-text"><i className="fa fa-users"></i> {this.i18n`pages.recs.friendsInCommon ${this.state.rec.common_friend_count}`}</p>
<p className="picture-overlay-text"><i className="fa fa-thumbs-up"></i> {this.i18n`pages.recs.commonInterests ${this.state.rec.common_like_count}`}</p>
<p className="picture-overlay-text"><i className="fa fa-clock-o"></i> {this.i18n`pages.recs.lastActive`} <MomentFuzzyDate date={this.state.rec.ping_time}/></p>
</div>
);
return (
<div className="full-height">
<div className="recs-page-header">
<div className="pure-g">
<LHSNav backEnabled={false}>
<i className="fa fa-info-circle xlarge picture-overlay-text"
onClick={this.toggleDetail.bind(this)}></i>
</LHSNav>
</div>
<div className="pure-g">
<div className="pure-u-1-4"></div>
<div className="pure-u-3-4 right-aligned-text">
<h1 className="picture-overlay-text no-margin">
{this.state.rec.name} <GenderIcon gender={this.state.rec.gender}/>
</h1>
<h3 className="picture-overlay-text no-margin">{this.state.rec.bio}</h3>
</div>
</div>
</div>
{teaserFragment}
<PictureStrip photos={this.state.rec.photos} index={this.state.recPhotoIndex} orientation="vertical"/>
{detailFragment}
<div className="pinned-to-bottom recs-page-controls">
<div className="pure-g">
<div className="pure-u-1-2">
<button onClick={this.pass.bind(this)} className="pure-u-1 pure-button button-lg squared button-error">
<i className="fa fa-binoculars"></i>
</button>
</div>
<div className="pure-u-1-2">
<button onClick={this.like.bind(this)} className="pure-u-1 pure-button button-lg squared button-success">
<i className="fa fa-heart"></i>
</button>
</div>
</div>
</div>
</div>
);
}
}
// Add mixins
RecsPage.contextTypes = {
history: PropTypes.history
};
export default RecsPage;
// App-related
let LOCALSTORAGE_KEY_ERROR_STORE_STATE = "store-error-state";
let LOCALSTORAGE_KEY_FB_AUTH_STORE_STATE = "store-fb-auth-state";
let LOCALSTORAGE_KEY_FB_USER_STORE_STATE = "store-fb-user-state";
let LOCALSTORAGE_KEY_TINDER_AUTH_STORE_STATE = "store-tinder-auth-state";
let LOCALSTORAGE_KEY_TINDER_RECOMMENDATION_STORE_STATE = "store-tinder-recommendation-state";
let LOCALSTORAGE_KEY_TINDER_LOCATION_STORE_STATE = "store-tinder-location-state";
let LOCALSTORAGE_KEY_TINDER_MATCH_STORE_STATE = "store-tinder-match-state";
let LOCALSTORAGE_KEY_TINDER_RECOMMENDATION_STORE_STATE = "store-tinder-recommendation-state";
let LOCALSTORAGE_KEY_TINDER_SENTIMENT_STORE_STATE = "store-tinder-sentiment-state";
let LOCALSTORAGE_KEY_TINDER_UPDATE_STORE_STATE = "store-tinder-update-state";
let LOCALSTORAGE_KEY_TINDER_USER_STORE_STATE = "store-tinder-user-state";
let LOCALSTORAGE_KEY_ERROR_STORE_STATE = "store-error-state";
let TINDER_CLIENT_ID = "464891386855067";
// URLs
......@@ -20,6 +22,7 @@ let TINDER_RECS_URL = "https://api.gotinder.com/user/recs";
let TINDER_LOCATION_URL = "https://api.gotinder.com/user/ping";
let TINDER_AUTH_URL = "https://api.gotinder.com/auth";
let TINDER_PROFILE_URL = "https://api.gotinder.com/profile";
let TINDER_UPDATES_URL = "https://api.gotinder.com/updates";
// URL template generators
function TINDER_SENTIMENT_URL(strings, ...values) {
......@@ -53,14 +56,16 @@ export default {
APP_BACKGROUND_CLASS,
FB_AUTH_TOKEN_HREF,
FB_CURRENT_USER_URL,
LOCALSTORAGE_KEY_ERROR_STORE_STATE,
LOCALSTORAGE_KEY_FB_AUTH_STORE_STATE,
LOCALSTORAGE_KEY_FB_USER_STORE_STATE,
LOCALSTORAGE_KEY_TINDER_AUTH_STORE_STATE,
LOCALSTORAGE_KEY_TINDER_LOCATION_STORE_STATE,
LOCALSTORAGE_KEY_TINDER_MATCH_STORE_STATE,
LOCALSTORAGE_KEY_TINDER_RECOMMENDATION_STORE_STATE,
LOCALSTORAGE_KEY_TINDER_SENTIMENT_STORE_STATE,
LOCALSTORAGE_KEY_TINDER_UPDATE_STORE_STATE,
LOCALSTORAGE_KEY_TINDER_USER_STORE_STATE,
LOCALSTORAGE_KEY_ERROR_STORE_STATE,
SENTIMENT_LIKE,
SENTIMENT_PASS,
TINDER_AUTH_URL,
......@@ -73,5 +78,6 @@ export default {
TINDER_MIN_FILTER_AGE,
TINDER_PROFILE_URL,
TINDER_RECS_URL,
TINDER_SENTIMENT_URL
TINDER_SENTIMENT_URL,
TINDER_UPDATES_URL
};
......@@ -8,7 +8,9 @@ export default {
}
},
nav: {
loadingMessage: "Loading...",
sections: {
findAMatch: "Find A Match",
matches: "Matches",
preferences: "Preferences",
logout: "Logout"
......@@ -19,7 +21,7 @@ export default {
single: "mile",
multiple: "miles"
},
milesAway: "{} miles away",
milesAway: "miles away",
unitsAway: "{} {} away"
},
pages: {
......@@ -38,10 +40,13 @@ export default {
login: {
login: "Login"
},
matches: {
recs: {
friendsInCommon: "{} friends in common",
commonInterests: "{} common interests",
lastActive: "last active"
},
matches: {
pageSubtitle: "Chat it up"
}
}
};
......@@ -5,6 +5,7 @@ import App from "./components/app";
import LoginPage from "./components/login-page";
import ProcessLoginPage from "./components/process-login-page";
import AppPage from "./components/app-page";
import RecsPage from "./components/recs-page";
import MatchesPage from "./components/matches-page";
import PreferencesPage from "./components/preferences-page";
import ErrorPage from "./components/error-page";
......@@ -16,6 +17,7 @@ ReactDOM.render((
<Route name="process-login" path="/access_token*" component={ProcessLoginPage}/>
<Route name="login" path="/login" component={LoginPage}/>
<Route name="app" path="/app" component={AppPage}/>
<Route name="recs" path="/recs" component={RecsPage}/>
<Route name="matches" path="/matches" component={MatchesPage}/>
<Route name="preferences" path="/preferences" component={PreferencesPage}/>
<Route name="error" path="/error" component={ErrorPage}/>
......
......@@ -46,13 +46,13 @@ body {
border-bottom-right-radius: .25em;
}
/****************/
/* Matches page */
/****************/
/*************/
/* Recs page */
/*************/
.gender-icon.fa-male { color: blue; }
.gender-icon.fa-female { color: pink; }
.match-detail {
.rec-detail {
position: fixed;
width: 100%;
......@@ -62,16 +62,16 @@ body {
transition: bottom 1s;
}
.match-detail.visible { bottom: 44px; }
.match-detail.invisible { bottom: -100%; }
.rec-detail.visible { bottom: 44px; }
.rec-detail.invisible { bottom: -100%; }
.match-detail > p {
.rec-detail > p {
margin-top: 0;
margin-bottom: 0;
}
.match-detail > p:last-child { margin-bottom: 1em; }
.rec-detail > p:last-child { margin-bottom: 1em; }
.picture-strip-image-container {
width: 100%;
......@@ -90,17 +90,17 @@ body {
text-shadow: 0px 1px 1px black;
}
.matches-page-controls {
.recs-page-controls {
width: 100%;
height: 44px;
}
.matches-page-header {
.recs-page-header {
position: fixed;
width: 100%;
}
.matches-page-header-icon-nav > i {
.recs-page-header-icon-nav > i {
text-align: center;
display: block;
background-color: rgba(0,0,0,0.8);
......@@ -115,10 +115,6 @@ body {
/* Preference page */
/*******************/
.preferences-page-container {
padding-bottom: 48px;
}
.two-button-options > button:first-child {
border-left: none;
}
......@@ -153,6 +149,18 @@ section.collapsible-labeled-form-section > label {
color: white;
}
/******************/
/* MatchConvoLink */
/******************/
.match-convo-link-container {
padding-top: 1em;
padding-bottom: 1em;
}
.match-convo-link-container > div:first-child {
margin-bottom: 1em;
}
/*******************/
/* Utility classes */
......
export default {
distance_mi:3,
connection_count:405,
common_like_count:0,
common_friend_count:0,
common_likes:[],
common_friends:[],
_id:"54bb13d1cdc25bab190ee607",
bio:"If you're reading this I hope you like to eat at the Y",
birth_date:"1989-01-15T00:00:00.000Z",
gender:1,
name:"FAKE_USER",
ping_time:"2015-12-26T21:01:37.101Z",
photos:[
{
id: "091f0306-2be7-4520-842b-e01e49ab8ab4",
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/091f0306-2be7-4520-842b-e01e49ab8ab4.jpg",
processedFiles: [
{
width:640,
height:640,
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/640x640_091f0306-2be7-4520-842b-e01e49ab8ab4.jpg"
},
{
width:320,
height:320,
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/320x320_091f0306-2be7-4520-842b-e01e49ab8ab4.jpg"
},
{
width:172,
height:172,
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/172x172_091f0306-2be7-4520-842b-e01e49ab8ab4.jpg"
},
{
width:84,
height:84,
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/84x84_091f0306-2be7-4520-842b-e01e49ab8ab4.jpg"
}
]
},
{
id:"974b0b4e-0f08-432e-8f2d-6a9416ae9823",
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/974b0b4e-0f08-432e-8f2d-6a9416ae9823.jpg",
processedFiles: [
{
width:640,
height:640,
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/640x640_974b0b4e-0f08-432e-8f2d-6a9416ae9823.jpg"
},
{width:320,
height:320,
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/320x320_974b0b4e-0f08-432e-8f2d-6a9416ae9823.jpg"
},
{width:172,
height:172,
url:"http://images.gotinder.com/54bb13d1cdc25bab190ee607/172x172_974b0b4e-0f08-432e-8f2d-6a9416ae9823.jpg"
},
{width:84,
height:84,
url