Files
meteor/examples/parties/client/client.js
Stephan Hochhaus 4462c18964 Deps.autorun becomes Tracker.autorun
Updated wordplay and parties example to use Tracker.autorun instead of
legacy Deps
2014-10-13 16:27:05 -07:00

286 lines
8.1 KiB
JavaScript

// All Tomorrow's Parties -- client
Meteor.subscribe("directory");
Meteor.subscribe("parties");
// If no party selected, or if the selected party was deleted, select one.
Meteor.startup(function () {
Tracker.autorun(function () {
var selected = Session.get("selected");
if (! selected || ! Parties.findOne(selected)) {
var party = Parties.findOne();
if (party)
Session.set("selected", party._id);
else
Session.set("selected", null);
}
});
});
///////////////////////////////////////////////////////////////////////////////
// Party details sidebar
Template.details.helpers({
party: function () {
return Parties.findOne(Session.get("selected"));
},
anyParties: function () {
return Parties.find().count() > 0;
},
creatorName: function () {
var owner = Meteor.users.findOne(this.owner);
if (owner._id === Meteor.userId())
return "me";
return displayName(owner);
},
canRemove: function () {
return this.owner === Meteor.userId() && attending(this) === 0;
},
maybeChosen: function (what) {
var myRsvp = _.find(this.rsvps, function (r) {
return r.user === Meteor.userId();
}) || {};
return what == myRsvp.rsvp ? "chosen btn-inverse" : "";
}
});
Template.details.events({
'click .rsvp_yes': function () {
Meteor.call("rsvp", Session.get("selected"), "yes");
return false;
},
'click .rsvp_maybe': function () {
Meteor.call("rsvp", Session.get("selected"), "maybe");
return false;
},
'click .rsvp_no': function () {
Meteor.call("rsvp", Session.get("selected"), "no");
return false;
},
'click .invite': function () {
openInviteDialog();
return false;
},
'click .remove': function () {
Parties.remove(this._id);
return false;
}
});
///////////////////////////////////////////////////////////////////////////////
// Party attendance widget
Template.attendance.helpers({
rsvpName: function () {
var user = Meteor.users.findOne(this.user);
return displayName(user);
},
outstandingInvitations: function () {
var party = Parties.findOne(this._id);
return Meteor.users.find({$and: [
{_id: {$in: party.invited}}, // they're invited
{_id: {$nin: _.pluck(party.rsvps, 'user')}} // but haven't RSVP'd
]});
},
invitationName: function () {
return displayName(this);
},
rsvpIs: function (what) {
return this.rsvp === what;
},
nobody: function () {
return ! this.public && (this.rsvps.length + this.invited.length === 0);
},
canInvite: function () {
return ! this.public && this.owner === Meteor.userId();
}
});
///////////////////////////////////////////////////////////////////////////////
// Map display
// Use jquery to get the position clicked relative to the map element.
var coordsRelativeToElement = function (element, event) {
var offset = $(element).offset();
var x = event.pageX - offset.left;
var y = event.pageY - offset.top;
return { x: x, y: y };
};
Template.map.events({
'mousedown circle, mousedown text': function (event, template) {
Session.set("selected", event.currentTarget.id);
},
'dblclick .map': function (event, template) {
if (! Meteor.userId()) // must be logged in to create events
return;
var coords = coordsRelativeToElement(event.currentTarget, event);
openCreateDialog(coords.x / 500, coords.y / 500);
}
});
Template.map.rendered = function () {
var self = this;
self.node = self.find("svg");
if (! self.handle) {
self.handle = Tracker.autorun(function () {
var selected = Session.get('selected');
var selectedParty = selected && Parties.findOne(selected);
var radius = function (party) {
return 10 + Math.sqrt(attending(party)) * 10;
};
// Draw a circle for each party
var updateCircles = function (group) {
group.attr("id", function (party) { return party._id; })
.attr("cx", function (party) { return party.x * 500; })
.attr("cy", function (party) { return party.y * 500; })
.attr("r", radius)
.attr("class", function (party) {
return party.public ? "public" : "private";
})
.style('opacity', function (party) {
return selected === party._id ? 1 : 0.6;
});
};
var circles = d3.select(self.node).select(".circles").selectAll("circle")
.data(Parties.find().fetch(), function (party) { return party._id; });
updateCircles(circles.enter().append("circle"));
updateCircles(circles.transition().duration(250).ease("cubic-out"));
circles.exit().transition().duration(250).attr("r", 0).remove();
// Label each with the current attendance count
var updateLabels = function (group) {
group.attr("id", function (party) { return party._id; })
.text(function (party) {return attending(party) || '';})
.attr("x", function (party) { return party.x * 500; })
.attr("y", function (party) { return party.y * 500 + radius(party)/2 })
.style('font-size', function (party) {
return radius(party) * 1.25 + "px";
});
};
var labels = d3.select(self.node).select(".labels").selectAll("text")
.data(Parties.find().fetch(), function (party) { return party._id; });
updateLabels(labels.enter().append("text"));
updateLabels(labels.transition().duration(250).ease("cubic-out"));
labels.exit().remove();
// Draw a dashed circle around the currently selected party, if any
var callout = d3.select(self.node).select("circle.callout")
.transition().duration(250).ease("cubic-out");
if (selectedParty)
callout.attr("cx", selectedParty.x * 500)
.attr("cy", selectedParty.y * 500)
.attr("r", radius(selectedParty) + 10)
.attr("class", "callout")
.attr("display", '');
else
callout.attr("display", 'none');
});
}
};
Template.map.destroyed = function () {
this.handle && this.handle.stop();
};
///////////////////////////////////////////////////////////////////////////////
// Create Party dialog
var openCreateDialog = function (x, y) {
Session.set("createCoords", {x: x, y: y});
Session.set("createError", null);
Session.set("showCreateDialog", true);
};
Template.page.helpers({
showCreateDialog: function () {
return Session.get("showCreateDialog");
}
});
Template.createDialog.events({
'click .save': function (event, template) {
var title = template.find(".title").value;
var description = template.find(".description").value;
var public = ! template.find(".private").checked;
var coords = Session.get("createCoords");
if (title.length && description.length) {
var id = createParty({
title: title,
description: description,
x: coords.x,
y: coords.y,
public: public
});
Session.set("selected", id);
if (! public && Meteor.users.find().count() > 1)
openInviteDialog();
Session.set("showCreateDialog", false);
} else {
Session.set("createError",
"It needs a title and a description, or why bother?");
}
},
'click .cancel': function () {
Session.set("showCreateDialog", false);
}
});
Template.createDialog.helpers({
error: function () {
return Session.get("createError");
}
});
///////////////////////////////////////////////////////////////////////////////
// Invite dialog
var openInviteDialog = function () {
Session.set("showInviteDialog", true);
};
Template.page.helpers({
showInviteDialog: function () {
return Session.get("showInviteDialog");
}
});
Template.inviteDialog.events({
'click .invite': function (event, template) {
Meteor.call('invite', Session.get("selected"), this._id);
},
'click .done': function (event, template) {
Session.set("showInviteDialog", false);
return false;
}
});
Template.inviteDialog.helpers({
uninvited: function () {
var party = Parties.findOne(Session.get("selected"));
if (! party)
return []; // party hasn't loaded yet
return Meteor.users.find({$nor: [{_id: {$in: party.invited}},
{_id: party.owner}]});
},
displayName: function () {
return displayName(this);
}
});