adding subscribe-to-posts package

This commit is contained in:
Sacha Greif 2015-01-20 11:59:12 +09:00
parent 0b3cfeabed
commit cf01d01dbd
16 changed files with 590 additions and 3 deletions

View file

@ -83,5 +83,6 @@ telescope-invites
telescope-post-by-feed telescope-post-by-feed
telescope-releases telescope-releases
telescope-getting-started telescope-getting-started
telescope-subscribe-to-posts
# Custom Packages # Custom Packages

View file

@ -128,6 +128,7 @@ telescope-releases@0.1.0
telescope-rss@0.0.0 telescope-rss@0.0.0
telescope-search@0.0.0 telescope-search@0.0.0
telescope-singleday@0.1.0 telescope-singleday@0.1.0
telescope-subscribe-to-posts@0.1.0
telescope-tags@0.0.0 telescope-tags@0.0.0
telescope-theme-base@0.0.0 telescope-theme-base@0.0.0
telescope-theme-hubble@0.0.0 telescope-theme-hubble@0.0.0

View file

@ -5,6 +5,6 @@
{{/each}} {{/each}}
</ul> </ul>
{{#each threadModules}} {{#each threadModules}}
{{> UI.dynamic template=getTemplate}} {{> UI.dynamic template=getTemplate data=..}}
{{/each}} {{/each}}
</template> </template>

View file

@ -21,7 +21,7 @@ privacyOptions = { // true means exposed
'votes.downvotedComments': true, 'votes.downvotedComments': true,
'votes.downvotedPosts': true, 'votes.downvotedPosts': true,
'votes.upvotedComments': true, 'votes.upvotedComments': true,
'votes.upvotedPosts': true, 'votes.upvotedPosts': true
}; };
// minimum required properties to display avatars // minimum required properties to display avatars
@ -37,4 +37,4 @@ avatarOptions = {
'services.facebook.id': true, 'services.facebook.id': true,
'services.twitter.screenName': true, 'services.twitter.screenName': true,
'services.github.screenName': true, // Github is not really used, but there are some mentions to it in the code 'services.github.screenName': true, // Github is not really used, but there are some mentions to it in the code
} }

View file

@ -0,0 +1 @@
.build*

View file

@ -0,0 +1,5 @@
{
"subscribed_posts": "Subscribed Posts",
"subscribe_to_thread": "Subscribe to comment thread",
"unsubscribe_from_thread": "Unsubscribe from comment thread"
}

View file

@ -0,0 +1,15 @@
<template name="postSubscribe">
{{#if canSubscribe}}
<div class="post-subscribe module grid-block">
{{#if subscribed}}
<a class="unsubscribe-link" href="#">
<span>{{_ 'unsubscribe_from_thread'}}</span>
</a>
{{else}}
<a class="subscribe-link" href="#">
<span>{{_ 'subscribe_to_thread'}}</span>
</a>
{{/if}}
</div>
{{/if}}
</template>

View file

@ -0,0 +1,47 @@
Template[getTemplate('postSubscribe')].helpers({
canSubscribe: function() {
// you cannot subscribe to your own posts
return Meteor.userId() && this.userId !== Meteor.userId();
},
subscribed: function() {
var user = Meteor.user();
if (!user) return false;
return _.include(this.subscribers, user._id);
}
});
Template[getTemplate('postSubscribe')].events({
'click .subscribe-link': function(e, instance) {
e.preventDefault();
if (this.userId === Meteor.userId())
return;
var post = this;
if (!Meteor.user()) {
Router.go('atSignIn');
flashMessage(i18n.t("please_log_in_first"), "info");
}
Meteor.call('subscribePost', post._id, function(error, result) {
if (result)
trackEvent("post subscribed", {'_id': post._id});
});
},
'click .unsubscribe-link': function(e, instance) {
e.preventDefault();
var post = this;
if (!Meteor.user()) {
Router.go('atSignIn');
flashMessage(i18n.t("please_log_in_first"), "info");
}
Meteor.call('unsubscribePost', post._id, function(error, result) {
if (result)
trackEvent("post unsubscribed", {'_id': post._id});
});
}
});

View file

@ -0,0 +1,26 @@
<template name="userSubscribedPosts">
<div class="grid-small grid-block dialog admin">
<h3>{{_ "subscribed_posts"}}</h3>
<table>
<thead>
<tr>
<td>Post</td>
<td>Subscribed At</td>
</tr>
</thead>
{{#each posts}}
<tr>
<td><a href="{{pathFor route='post_page' _id=_id}}">{{title}}</a></td>
<td>{{formatDate subscribedAt "MM/DD/YYYY, HH:mm"}}</td>
</tr>
{{/each}}
{{#if hasMorePosts}}
<tr>
<td colspan="2">
<a class="subscribedposts-more more-button grid-module" href="#"><span>{{_ "load_more"}}</span></a>
</td>
</tr>
{{/if}}
</table>
</div>
</template>

View file

@ -0,0 +1,52 @@
Template[getTemplate('userSubscribedPosts')].created = function () {
var user = this.data,
instance = this;
// initialize the terms and posts local reactive variables
instance.terms = new ReactiveVar({
view: 'userSubscribedPosts',
userId: user._id,
limit: 5
});
instance.posts = new ReactiveVar({});
// will re-run when the "terms" local reactive variable changes
this.autorun(function () {
// get the new terms and generate new parameters from them
var terms = instance.terms.get();
var parameters = getPostsParameters(terms);
// subscribe to the userPosts publication
instance.subscription = Meteor.subscribe('userSubscribedPosts', terms);
// update the instance's "posts" cursor
instance.posts.set(Posts.find(parameters.find, parameters.options));
});
};
Template[getTemplate('userSubscribedPosts')].helpers({
posts: function () {
var user = this,
posts = Template.instance().posts.get().fetch();
posts = _.map(posts, function (post) {
var item = _.findWhere(user.subscribedItems.Posts, {itemId: post._id});
post.subscribedAt = item.subscribedAt;
return post;
});
return posts;
},
hasMorePosts: function () {
return Template.instance().posts.get().count() >= Template.instance().terms.get().limit;
}
});
Template[getTemplate('userSubscribedPosts')].events({
'click .subscribedposts-more': function (e) {
e.preventDefault();
var terms = Template.instance().terms.get();
terms.limit += 5;
Template.instance().terms.set(terms)
}
});

View file

@ -0,0 +1,5 @@
Meteor.publish('userSubscribedPosts', function(terms) {
var parameters = getPostsParameters(terms);
var posts = Posts.find(parameters.find, parameters.options);
return posts;
});

View file

@ -0,0 +1,134 @@
threadModules.push(
{
template: 'postSubscribe',
order: 10
}
);
addToPostSchema.push(
{
propertyName: 'subscribers',
propertySchema: {
type: [String],
optional: true,
autoform: {
omit: true
}
}
}
);
addToPostSchema.push(
{
propertyName: 'subscriberCount',
propertySchema: {
type: Number,
optional: true,
autoform: {
omit: true
}
}
}
);
userProfileEdit.push(
{
template: 'userSubscribedPosts',
order: 5
}
);
viewParameters.userSubscribedPosts = function (terms) {
var user = Meteor.users.findOne(terms.userId),
postsIds = [];
if (user.subscribedItems && user.subscribedItems.Posts)
postsIds = _.pluck(user.subscribedItems.Posts, "itemId");
return {
find: {_id: {$in: postsIds}},
options: {limit: 5, sort: {postedAt: -1}}
};
}
var hasSubscribedItem = function (item, user) {
return item.subscribers && item.subscribers.indexOf(user._id) != -1;
};
var addSubscribedItem = function (userId, item, collection) {
var field = 'subscribedItems.' + collection;
var add = {};
add[field] = item;
Meteor.users.update({_id: userId}, {
$addToSet: add
});
};
var removeSubscribedItem = function (userId, itemId, collection) {
var field = 'subscribedItems.' + collection;
var remove = {};
remove[field] = {itemId: itemId};
Meteor.users.update({_id: userId}, {
$pull: remove
});
};
var subscribeItem = function (collection, itemId) {
var user = Meteor.user(),
item = collection.findOne(itemId),
collectionName = collection._name.slice(0,1).toUpperCase() + collection._name.slice(1);
if (!user || !item || hasSubscribedItem(item, user))
return false;
// author can't subscribe item
if (item.userId && item.userId === user._id)
return false
// Subscribe
var result = collection.update({_id: itemId, subscribers: { $ne: user._id }}, {
$addToSet: {subscribers: user._id},
$inc: {subscriberCount: 1}
});
if (result > 0) {
// Add item to list of subscribed items
var obj = {
itemId: item._id,
subscribedAt: new Date()
};
addSubscribedItem(user._id, obj, collectionName);
}
return true;
};
var unsubscribeItem = function (collection, itemId) {
var user = Meteor.user(),
item = collection.findOne(itemId),
collectionName = collection._name.slice(0,1).toUpperCase()+collection._name.slice(1);
if (!user || !item || !hasSubscribedItem(item, user))
return false;
// Unsubscribe
var result = collection.update({_id: itemId, subscribers: user._id }, {
$pull: {subscribers: user._id},
$inc: {subscriberCount: -1}
});
if (result > 0) {
// Remove item from list of subscribed items
removeSubscribedItem(user._id, itemId, collectionName);
}
return true;
};
Meteor.methods({
subscribePost: function(postId) {
return subscribeItem.call(this, Posts, postId);
},
unsubscribePost: function(postId) {
return unsubscribeItem.call(this, Posts, postId);
}
});

View file

@ -0,0 +1,5 @@
{
"translation_function_name": "__",
"helper_name": "_",
"namespace": "project"
}

View file

@ -0,0 +1,68 @@
Package.describe({
summary: 'Subscribe to posts to be notified when they get new comments',
version: '0.1.0',
name: 'telescope-subscribe-to-posts'
});
Package.onUse(function (api) {
// --------------------------- 1. Meteor packages dependencies ---------------------------
// automatic (let the package specify where it's needed)
api.use([
'tap:i18n',
'iron:router',
'telescope-base',
'telescope-lib',
'telescope-i18n',
'fourseven:scss',
'telescope-notifications'
]);
// client
api.use([
'jquery', // useful for DOM interactions
'underscore', // JavaScript swiss army knife library
'templating' // required for client-side templates
], ['client']);
// ---------------------------------- 2. Files to include ----------------------------------
// i18n config (must come first)
api.add_files([
'package-tap.i18n'
], ['client', 'server']);
// both
api.add_files([
'lib/subscribe-to-posts.js',
], ['client', 'server']);
// client
api.add_files([
'lib/client/templates/post_subscribe.html',
'lib/client/templates/post_subscribe.js',
'lib/client/templates/user_subscribed_posts.html',
'lib/client/templates/user_subscribed_posts.js',
'lib/client/stylesheets/subscribe-to-posts.scss'
], ['client']);
// server
api.add_files([
'lib/server/publications.js'
], ['server']);
// i18n languages (must come last)
api.add_files([
'i18n/en.i18n.json',
], ['client', 'server']);
});

View file

@ -0,0 +1,227 @@
{
"dependencies": [
[
"application-configuration",
"1.0.3"
],
[
"base64",
"1.0.1"
],
[
"binary-heap",
"1.0.1"
],
[
"blaze",
"2.0.3"
],
[
"blaze-tools",
"1.0.1"
],
[
"boilerplate-generator",
"1.0.1"
],
[
"callback-hook",
"1.0.1"
],
[
"check",
"1.0.2"
],
[
"cmather:handlebars-server",
"2.0.0"
],
[
"coffeescript",
"1.0.4"
],
[
"ddp",
"1.0.11"
],
[
"deps",
"1.0.5"
],
[
"ejson",
"1.0.4"
],
[
"follower-livedata",
"1.0.2"
],
[
"geojson-utils",
"1.0.1"
],
[
"handlebars",
"1.0.1"
],
[
"html-tools",
"1.0.2"
],
[
"htmljs",
"1.0.2"
],
[
"id-map",
"1.0.1"
],
[
"iron:controller",
"1.0.0"
],
[
"iron:core",
"1.0.3"
],
[
"iron:dynamic-template",
"1.0.3"
],
[
"iron:layout",
"1.0.3"
],
[
"iron:location",
"1.0.3"
],
[
"iron:middleware-stack",
"1.0.0"
],
[
"iron:router",
"1.0.1"
],
[
"iron:url",
"1.0.3"
],
[
"jquery",
"1.0.1"
],
[
"json",
"1.0.1"
],
[
"logging",
"1.0.5"
],
[
"meteor",
"1.1.3"
],
[
"minifiers",
"1.1.2"
],
[
"minimongo",
"1.0.5"
],
[
"mongo",
"1.0.8"
],
[
"observe-sequence",
"1.0.3"
],
[
"ordered-dict",
"1.0.1"
],
[
"random",
"1.0.1"
],
[
"reactive-dict",
"1.0.4"
],
[
"reactive-var",
"1.0.3"
],
[
"retry",
"1.0.1"
],
[
"routepolicy",
"1.0.2"
],
[
"session",
"1.0.4"
],
[
"spacebars",
"1.0.3"
],
[
"spacebars-compiler",
"1.0.3"
],
[
"tap:http-methods",
"0.0.23"
],
[
"tap:i18n",
"1.2.1"
],
[
"telescope-base",
"0.0.0"
],
[
"telescope-i18n",
"0.0.0"
],
[
"telescope-lib",
"0.2.9"
],
[
"templating",
"1.0.9"
],
[
"tracker",
"1.0.3"
],
[
"ui",
"1.0.4"
],
[
"underscore",
"1.0.1"
],
[
"webapp",
"1.1.4"
],
[
"webapp-hashing",
"1.0.1"
]
],
"pluginDependencies": [],
"toolVersion": "meteor-tool@1.0.35",
"format": "1.0"
}