/themes/default/media/js/activities.js

https://github.com/ushahidi/SwiftRiver · JavaScript · 234 lines · 176 code · 40 blank · 18 comment · 44 complexity · 6a31f4dffbf08fe0bdc2742dd6e6a9f0 MD5 · raw file

  1. /**
  2. * Activities module
  3. */
  4. (function (root) {
  5. // Init the module
  6. Activities = root.Activities = {};
  7. // Activity model, collection and view
  8. var Activity = Activities.Activity = Backbone.Model.extend({
  9. defaults: {
  10. "count": 1,
  11. },
  12. initialize: function() {
  13. if (this.get("action_on") == "account") {
  14. var actionOnObj = this.get("action_on_obj")
  15. actionOnObj["name"] = actionOnObj["owner"]["name"];
  16. this.set("action_on_obj", actionOnObj);
  17. } else if (this.get("action_on") == "river") {
  18. this.set("invite_to", "river");
  19. this.set("invite_to_name", this.get("action_on_obj")["name"]);
  20. } else if (this.get("action_on") == "bucket") {
  21. this.set("invite_to", "bucket");
  22. this.set("invite_to_name", this.get("action_on_obj")["name"]);
  23. }
  24. this.set("date", this.getDateString(this.get("date_added")));
  25. },
  26. getDateString: function(dateString) {
  27. var msPerMinute = 60 * 1000;
  28. var msPerHour = msPerMinute * 60;
  29. var msPerDay = msPerHour * 24;
  30. var msPerMonth = msPerDay * 30;
  31. var msPerYear = msPerDay * 365;
  32. var elapsed = Date.now() - Date.parse(dateString);
  33. if (elapsed < msPerMinute) {
  34. return Math.round(elapsed/1000) + ' seconds ago';
  35. }
  36. else if (elapsed < msPerHour) {
  37. return Math.round(elapsed/msPerMinute) + ' minutes ago';
  38. }
  39. else if (elapsed < msPerDay ) {
  40. return Math.round(elapsed/msPerHour ) + ' hours ago';
  41. }
  42. else if (elapsed < msPerMonth) {
  43. return 'approximately ' + Math.round(elapsed/msPerDay) + ' days ago';
  44. }
  45. else if (elapsed < msPerYear) {
  46. return 'approximately ' + Math.round(elapsed/msPerMonth) + ' months ago';
  47. }
  48. else {
  49. return 'approximately ' + Math.round(elapsed/msPerYear ) + ' years ago';
  50. }
  51. },
  52. ignore: function() {
  53. this.save({ignored: 1}, {wait: true});
  54. },
  55. incCount: function() {
  56. this.set('count', this.get('count')+1)
  57. }
  58. });
  59. var ActivityList = Activities.ActivityList = Backbone.Collection.extend({
  60. model: Activity
  61. });
  62. var ActivityView = Activities.ActivityView = Backbone.View.extend({
  63. tagName: "article",
  64. className: "news-feed-item cf",
  65. events: {
  66. "click .actions .confirm a": "confirm",
  67. "click .actions .ignore a": "ignore"
  68. },
  69. initialize: function() {
  70. switch(this.model.get("action")) {
  71. case "create":
  72. this.template = _.template($("#create_activity_template").html());
  73. break;
  74. case "invite":
  75. this.template = _.template($("#invite_activity_template").html());
  76. break;
  77. case "follow":
  78. this.template = _.template($("#follow_activity_template").html());
  79. break;
  80. case "comment":
  81. this.template = _.template($("#comment_activity_template").html());
  82. break;
  83. }
  84. // Listen for confirmed state change
  85. this.model.on("change:confirmed", this.render, this);
  86. this.model.on("change:count", this.render, this);
  87. },
  88. confirm: function() {
  89. this.model.save({confirmed: true}, {
  90. wait: true,
  91. success: function(model, response) {
  92. // Show notification message of the acceptance
  93. var message = "You have accepted " + model.get("user_name") + "'s " +
  94. "invitation to collaborate on the " + model.get("action_on_name") +
  95. " " + model.get("action_on");
  96. showConfirmationMessage(message);
  97. },
  98. error: function(model, response) {
  99. // The server threw an error
  100. var message = "Oops! Something went wrong...";
  101. showConfirmationMessage(message);
  102. }
  103. });
  104. return false;
  105. },
  106. ignore: function() {
  107. this.model.ignore();
  108. this.$el.fadeOut('slow');
  109. return false;
  110. },
  111. render: function() {
  112. var action = this.model.get("action");
  113. if (action == "create" || action == "invite") {
  114. this.$el.addClass("add");
  115. } else if (action == "follow") {
  116. this.$el.addClass("follow");
  117. }
  118. this.$el.html(this.template(this.model.toJSON()));
  119. return this;
  120. }
  121. });
  122. // View of the entire activity stream
  123. var ActivityStream = Activities.ActivityStream = Backbone.View.extend({
  124. el: "#news-feed",
  125. inviteViews: {},
  126. maxId: 0,
  127. initialize: function() {
  128. this.collection.on('add', this.addActivity, this);
  129. this.collection.on('reset', this.addActivities, this);
  130. },
  131. showNewActivity: function(activity, isInvite) {
  132. var view = new ActivityView({model: activity});
  133. if (isInvite) {
  134. this.inviteViews[key] = [view];
  135. }
  136. if (activity.get("id") > this.maxId) {
  137. this.$el.prepend(view.render().el);
  138. } else {
  139. this.$el.append(view.render().el);
  140. }
  141. return view;
  142. },
  143. addActivity: function(activity) {
  144. if (activity.get("action") == "invite") {
  145. // For invites, if we have seen an invite for a specific
  146. // river/bucket id before and its timestamp is within 6 hours
  147. // of the activity of the activity then simply reuse that view
  148. // otherwise create a new one.
  149. // In effect, group nearby river/bucket invites.
  150. var actionOnObjId = activity.get("action_on_obj")["id"];
  151. key = activity.get("action_on") + actionOnObjId;
  152. if (!_.has(this.inviteViews, key)) {
  153. this.showNewActivity(activity, true);
  154. } else {
  155. var view = _.find(this.inviteViews[key], function(v){
  156. var activityTimestamp = Date.parse(activity.get("date_added"));
  157. var modelTimestamp = Date.parse(v.model.get("date_added"));
  158. return Math.abs(activityTimestamp - modelTimestamp) <= 21600000;
  159. });
  160. if (view == undefined) {
  161. view = this.showNewActivity(activity, true);
  162. } else {
  163. view.model.incCount();
  164. }
  165. if (actionOnObjId == logged_in_user) {
  166. // If the logged in user is invited, display that
  167. // view to allow them to ignore/accept the invite
  168. // Also there can be multiple invites but only one unconfirmed so
  169. // the below IF makes sure we always display the unconfirmed one
  170. if (!(actionOnObjId == view.model.get("action_on_id")["id"] && activity.get("confirmed"))) {
  171. activity.set("count", view.model.get("count"));
  172. view.model = activity;
  173. view.initialize();
  174. view.render();
  175. }
  176. }
  177. }
  178. } else {
  179. this.showNewActivity(activity, false);
  180. }
  181. if (activity.get("id") > this.maxId) {
  182. this.maxId = activity.get("id");
  183. }
  184. },
  185. addActivities: function() {
  186. if (!this.collection.size()) {
  187. $("#no-activity-message").show();
  188. } else {
  189. $("#no-activity-message").hide();
  190. this.collection.each(this.addActivity, this);
  191. }
  192. }
  193. });
  194. }(this));