Commit 4c502ba6 authored by MrMan's avatar MrMan

Merge branch 'improve-matches-interface' into 'master'

Improve matches interface

Able to get first match, and with that work out the bugs on the matches page

See merge request !7
parents 91e2f23f 4abbe950
......@@ -3,3 +3,4 @@
jspm_packages/
/node_modules/
dist/
/npm-debug.log
......@@ -22,7 +22,7 @@ System.config({
"core-js": "npm:core-js@1.2.6",
"fontawesome": "bower:fontawesome@4.4.0",
"lipis/flag-icon-css": "github:lipis/flag-icon-css@2.1.0",
"lodash": "npm:lodash@3.10.1",
"lodash": "bower:lodash@4.12.0",
"moment": "npm:moment@2.10.6",
"pure": "bower:pure@0.6.0",
"purecss": "npm:purecss@0.6.0",
......@@ -133,9 +133,6 @@ System.config({
"process": "github:jspm/nodelibs-process@0.1.2",
"source-map": "npm:source-map@0.1.31"
},
"npm:lodash@3.10.1": {
"process": "github:jspm/nodelibs-process@0.1.2"
},
"npm:loose-envify@1.1.0": {
"js-tokens": "npm:js-tokens@1.0.2",
"process": "github:jspm/nodelibs-process@0.1.2",
......
......@@ -9,7 +9,7 @@ const DEFAULT_STATE = {
matchId: null,
matchedUser: null, // User that was matched to logged in user
matchedUserId: null, // ID of the user that was matched to logged in user
unseenMessageCount: 0,
unseenByUserMessageCount: 0,
lastMessageSentDate: null
};
......@@ -19,12 +19,13 @@ class MatchConvoLink extends React.Component {
this.unsubFunctions = {};
// Process props
this.state = _.extend(DEFAULT_STATE, {
this.state = DEFAULT_STATE;
this.state = _.extend(this.state, {
i18n: LocaleStore.state.i18nTemplateFn,
matchId: props.matchId || this.state.matchId,
matchedUserId: props.matchedUserId || this.state.matchedUserId,
matchedUser: props.matchedUser || this.state.matchedUser,
unseenMessageCount: props.unseenMessageCount || this.state.unseenMessageCount,
unseenByUserMessageCount: props.unseenByUserMessageCount || this.state.unseenByUserMessageCount,
lastMessageSentDate: props.lastMessageSentDate || this.state.lastMessageSentDate
});
......@@ -62,6 +63,13 @@ class MatchConvoLink extends React.Component {
let matchedUser = _.get(this.state, "matchedUser", {});
let lastMessageSentFragment = "";
if (_.isNull(this.state.lastMessageSentDate) || _.isUndefined(this.state.lastMessageSentDate)) {
lastMessageSentFragment = this.state.i18n`pages.matches.noMessagesSent`;
} else {
lastMessageSentFragment = (<MomentFuzzyDate date={this.state.lastMessageSentDate}/>);
}
// TODO: show different view (progressive?) if user isn't loaded yet
if (_.isNull(matchedUser)) {
return (
......@@ -81,7 +89,7 @@ class MatchConvoLink extends React.Component {
<h3 className="match-convo-link-name slim-text no-vertical-margins">{matchedUser.name}</h3>
<p className="match-convo-link-last-active-blurb no-vertical-margins super-slim-text">
<i className="fa fa-clock-o"></i>&nbsp;
<MomentFuzzyDate date={this.state.lastMessageSentDate}/>
{lastMessageSentFragment}
</p>
</div>
</div>
......
......@@ -38,12 +38,19 @@ class MatchConvoPage extends React.Component {
console.log("[MATCH CONVO PAGE] Attempting to send message to match:", {
messageText: msg,
matchId: matchId});
matchId: matchId
});
// Clear out the message text
this.setState({messageText: ""});
// Sent the message to the match
Actions.sendMessageToMatch(matchId, msg);
}
render() {
let messages = _.get(this.props, "match.messages", []);
let pendingMessages = _.get(this.props, "match.pendingMessages", []);
// Generate list of messages
let messageListElements = messages.map((m,i) => {
......@@ -61,6 +68,20 @@ class MatchConvoPage extends React.Component {
);
});
// Generate list of pending messaegs
let pendingMessageListElements = pendingMessages.map((m,i) => {
return (
<div key={m._id + '-msg-' + i} className="pure-u-1">
<div className={`match-convo-message pending`}>
<div className="match-convo-message slim-padding">{m.message}</div>
<div className="match-convo-sent-date no-vertical-margins right-aligned-text slim-right-padding">
<i className="fa fa-circle-o-notch fa-spin"></i> {this.state.i18n`pages.matchConvo.sending`}
</div>
</div>
</div>
);
});
let closeButton = "";
if (this.props.showCloseButton) {
closeButton = (
......@@ -78,6 +99,7 @@ class MatchConvoPage extends React.Component {
<div className="match-convo-page-messages-container">
<div className="pure-g">
{messageListElements}
{pendingMessageListElements}
</div>
</div>
......
......@@ -74,7 +74,8 @@ class MatchesPage extends React.Component {
}
render() {
console.log(`[MATCHES PAGE] Rendering matches page with ${this.state.matches.length} matches`);
let matchCount = _.size(this.state.matches);
console.log(`[MATCHES PAGE] Rendering matches page with ${matchCount} matches`);
// Generate elemtn that will list or show empty message
let matchListElem;
......@@ -93,11 +94,11 @@ class MatchesPage extends React.Component {
} else {
// Generate list of matches
let matchLinks = _.map(this.state.matches, m => {
let unseen = _.reduce(m.messages, (acc, m) => !m.seen ? acc+1 : acc , 0);
let unseenByUserMessageCount = _.reduce(m.messages, (acc, m) => !m.seenByUser ? acc+1 : acc , 0);
let lastMessageSentDate = _.get(_.last(m.messages), "sent_date", null);
return (
<div key={m.id} onClick={() => this.showMatchConvoContainerWithMatch(m)} >
<MatchConvoLink unseenMessageCount={unseen}
<MatchConvoLink unseenByUserMessageCount={unseenByUserMessageCount}
matchId={m.id}
matchedUserId={m.matchedUserId}
lastMessageSentDate={lastMessageSentDate} />
......
......@@ -73,7 +73,11 @@ export default {
pageSubtitle: "Talk with your matches",
findSomeoneNew: "Find someone new",
noMatchesFound: "No matches found",
lastMessageSent: "Last message sent"
lastMessageSent: "Last message sent",
noMessagesSent: "No messages sent"
},
matchConvo: {
sending: "Sending..."
}
}
};
......@@ -82,7 +82,11 @@ export default {
pageSubtitle: "Habla con sus parejas",
findSomeoneNew: "Buscar un pareja nuevo",
noMatchesFound: "No mas parejas",
lastMessageSent: "Último mensaje enviado"
lastMessageSent: "Último mensaje enviado",
noMessagesSent: "No hay mensajes enviados"
},
matchConvo: {
sending: "Enviando..."
}
}
};
......@@ -73,7 +73,11 @@ export default {
pageSubtitle: "合わせた人と話す",
findSomeoneNew: "新しい人を探す",
noMatchesFound: "よく合わせる人わ見つけられませんでした",
lastMessageSent: "最後通信したのメッセージ"
lastMessageSent: "最後通信したのメッセージ",
noMessagesSent: "メッセージはまだ送ってありません"
},
matchConvo: {
sending: "送っています…"
}
}
};
......@@ -8,7 +8,7 @@ import LocalStorageBackupMixinGenerator from "../mixins/localstorage-backup";
import _ from "lodash";
const DEFAULT_STATE = {matches: {}, matchedUserInfo: {}, tinderAccessToken: null};
const MATCH_DEFAULT_STATE = {messages: []};
const MATCH_DEFAULT_STATE = {messages: [], pendingMessages: []};
const LocalStorageBackupMixin = LocalStorageBackupMixinGenerator(Constants.LOCALSTORAGE_KEY_TINDER_MATCH_STORE_STATE);
function generateTinderURLForMatchMessageSend(matchId) {
......@@ -49,6 +49,11 @@ let TinderMatchStore = Reflux.createStore({
this.state = _.extend(DEFAULT_STATE, this.gatherState());
this.state.tinderAccessToken = this.state.tinderAccessToken || TinderAuthStore.state.token;
// Attempt to clear out duplicate messages for all matches if present
_.forEach(this.state.matches, m => {
m.messages = _.uniqBy(m.messages, _.property('_id'));
});
console.log("[TINDER MATCH STORE] Initial state:", _.extend({}, this.state));
},
......@@ -131,14 +136,36 @@ let TinderMatchStore = Reflux.createStore({
return;
}
// Update the messages for that match preemptively
this.state.matches[matchId].pendingMessages.push({matchId, message});
this.backupState();
this.trigger(this.state);
// Attempt to send message to match
console.log("[TINDER MATCH STORE] Attempting to send message to match with id:", matchId);
TinderAPI.POSTWithToken(generateTinderURLForMatchMessageSend(matchId),
{message},
this.state.tinderAccessToken)
.then(resp => resp.json())
.then(resp => {
Actions.receivedSentMessageReceipt(matchId, resp);
.then(msg => {
// Move the proof of the pending message to
let relevantPendingMessage = _.find(this.state.matches[matchId].pendingMessages,
p => p.message === msg.message);
// Trim pending message array
let trimmedPendingMessages = _.filter(this.state.matches[matchId].pendingMessages,
p => p.message !== relevantPendingMessage.message);
// Add the message object as received from tinder to the list of messages, update pending messages
this.state.matches[matchId].messages.push(msg);
this.state.matches[matchId].pendingMessages = trimmedPendingMessages;
// Alert others to the fact that we've received a sent message receipt for this match
Actions.receivedSentMessageReceipt(matchId, msg);
// Alert to received sent message receipt
this.backupState();
this.trigger(this.state);
})
.catch(err => {
console.log(`[TINDER MATCH STORE] Failed to send message to match with ID ${matchId}:`, err);
......@@ -182,20 +209,44 @@ let TinderMatchStore = Reflux.createStore({
// Augment and add messages to state
_.forEach(m.messages, m => {
// Append augmented messages, initialized with false seen flag
this.state.matches[matchId].messages.push(_.extend(m, {seen: false}));
// TODO: this is really inefficient... maybe use a set or a map for messages instead??
let messageAlreadyExists = _.find(this.state.matches[matchId].messages,
_.matchesProperty('id', m.id));
// Append augmented messages, initialized with false seenByUser flag
if (!messageAleradyExists) {
this.state.matches[matchId].messages.push(_.extend(m, {seenByUser: false}));
}
});
// Determine ID of matched user by finding a message and using to or from user IDs
let matchedUserId = "";
if (!_.isEmpty(m.messages)) {
let first = _.first(m.messages);
matchedUserId = m.message.to === currentUser._id ? m.message.from : m.message.to;
// Save important information from the match for a brand new match
if (!_.has(this.state.matches, matchId)) {
// Handle brand new match
let matchedUserId = "";
if(!_.isUndefined(m.person)) {
// If m.person has been provided, use that to find the matched user ID, and use it to update the state
// Initial match object should contain at least
matchedUserId = m.person._id;
} else if(!_.isEmpty(m.messages)) {
// Attempt to derive matched user ID from a message
let first = _.first(m.messages);
matchedUserId = m.message.to === currentUser._id ? m.message.from : m.message.to;
}
// Save matched user ID, initial match object to internal matches
this.state.matches[matchId].matchedUserId = matchedUserId;
this.state.matches[matchId].matchObj = m;
}
// If there is pre-existing match object, but we're getting piecemeal updates, there's a problem
if (!_.has(this.state.matches, matchId) && _.isUndefined(m.person)) {
// TODO: Need to request fresh updates/specific match info in hopes of getting proper data
console.log("[TIDNER MATCH STORE] WARNING: no preexisting match object, but we're gettin piecemeal updates. Need to request a full updates object");
}
// Set match ID and matched user ID
this.state.matches[matchId].id = matchId;
this.state.matches[matchId].matchedUserId = matchedUserId;
});
// Save state and alert listeners to updates
......
......@@ -7,7 +7,7 @@ import TinderAPI from "../tinder-api";
import LocalStorageBackupMixinGenerator from "../mixins/localstorage-backup";
import _ from "lodash";
const DEFAULT_STATE = {lastUpdate: null, currentUser: null, tinderAccessToken: null};
const DEFAULT_STATE = {lastUpdate: null, currentUser: null, tinderAccessToken: null, lastUpdateTime: null};
const LocalStorageBackupMixin = LocalStorageBackupMixinGenerator(Constants.LOCALSTORAGE_KEY_TINDER_UPDATE_STORE_STATE);
let TinderUpdateStore = Reflux.createStore({
......@@ -57,15 +57,19 @@ let TinderUpdateStore = Reflux.createStore({
return;
}
// Use the last update time to restrict updates to be new ones only
let lastUpdateTime = _.isNull(this.state.lastUpdateTime) ? "" : this.state.lastUpdateTime;
console.log("[TINDER UPDATE STORE] Retrieving updates from Tinder API...");
TinderAPI.POSTWithToken(Constants.TINDER_UPDATES_URL,
{last_activity_date:""},
{last_activity_date: lastUpdateTime},
this.state.tinderAccessToken)
.then(resp => resp.json())
.then(resp => {
console.log("[TINDER UPDATE STORE] Received updates from tinder:", resp);
this.state.lastUpdate = resp;
this.state.lastUpdateTime = new Date();
// Update state/trigger actions
this.backupState();
......
......@@ -8,7 +8,7 @@
"dependencies": {
"fontawesome": "bower:fontawesome@^4.4.0",
"lipis/flag-icon-css": "github:lipis/flag-icon-css@^2.1.0",
"lodash": "npm:lodash@^3.10.1",
"lodash": "bower:lodash@^4.12.0",
"moment": "npm:moment@^2.10.6",
"pure": "bower:pure@^0.6.0",
"purecss": "npm:purecss@^0.6.0",
......
......@@ -243,6 +243,12 @@ section.collapsible-labeled-form-section > label {
padding: .25em;
}
.match-convo-message.pending {
background-color: #888;
opacity: 0.7;
color: white;
}
.match-convo-message.received {
background-color: #95a5a6;
color: white;
......
export default {
match:{
_id:"556f8b926c01ba786888dcdd5647e40a046544192cb1sk12",
closed:false,
common_friend_count:0,
common_like_count:0,
created_date: "2016-05-21T06:31:16.046Z",
dead:false,
last_activity_date:"2016-05-21T06:31:16.046Z",
message_count:0,
messages:[],
participants:["54bb13d1cdc25bab190ee607","54bb13d1cdc25bab190ee607"],
pending:false,
is_super_like:false,
following:true,
following_moments:true
},
likes_remaining:100
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment