mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
This handles the case where the selected party is deleted by another user, and also the case where the server rejects the party creation request.
278 lines
8.1 KiB
JavaScript
278 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 () {
|
|
Deps.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.party = function () {
|
|
return Parties.findOne(Session.get("selected"));
|
|
};
|
|
|
|
Template.details.anyParties = function () {
|
|
return Parties.find().count() > 0;
|
|
};
|
|
|
|
Template.details.creatorName = function () {
|
|
var owner = Meteor.users.findOne(this.owner);
|
|
if (owner._id === Meteor.userId())
|
|
return "me";
|
|
return displayName(owner);
|
|
};
|
|
|
|
Template.details.canRemove = function () {
|
|
return this.owner === Meteor.userId() && attending(this) === 0;
|
|
};
|
|
|
|
Template.details.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.rsvpName = function () {
|
|
var user = Meteor.users.findOne(this.user);
|
|
return displayName(user);
|
|
};
|
|
|
|
Template.attendance.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
|
|
]});
|
|
};
|
|
|
|
Template.attendance.invitationName = function () {
|
|
return displayName(this);
|
|
};
|
|
|
|
Template.attendance.rsvpIs = function (what) {
|
|
return this.rsvp === what;
|
|
};
|
|
|
|
Template.attendance.nobody = function () {
|
|
return ! this.public && (this.rsvps.length + this.invited.length === 0);
|
|
};
|
|
|
|
Template.attendance.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 = Deps.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.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.error = function () {
|
|
return Session.get("createError");
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Invite dialog
|
|
|
|
var openInviteDialog = function () {
|
|
Session.set("showInviteDialog", true);
|
|
};
|
|
|
|
Template.page.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.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}]});
|
|
};
|
|
|
|
Template.inviteDialog.displayName = function () {
|
|
return displayName(this);
|
|
};
|