Sotaro KARASAWA 10 лет назад
Родитель
Сommit
9dae68d837

+ 39 - 12
lib/models/bookmark.js

@@ -10,12 +10,21 @@ module.exports = function(crowi) {
     user: { type: ObjectId, ref: 'User', index: true },
     createdAt: { type: Date, default: Date.now() }
   });
+  bookmarkSchema.index({page: 1, user: 1}, {unique: true});
 
   // bookmark チェック用
-  bookmarkSchema.statics.findByPageIdAndUser = function(pageId, user, callback) {
+  bookmarkSchema.statics.findByPageIdAndUserId = function(pageId, userId) {
     var Bookmark = this;
 
-    Bookmark.findOne({ page: pageId, user: user._id }, callback);
+    return new Promise(function(resolve, reject) {
+      return Bookmark.findOne({ page: pageId, user: userId }, function(err, doc) {
+        if (err) {
+          return reject(err);
+        }
+
+        return resolve(doc);
+      });
+    });
   };
 
   bookmarkSchema.statics.findByUser = function(user, option, callback) {
@@ -35,21 +44,39 @@ module.exports = function(crowi) {
       });
   };
 
-  bookmarkSchema.statics.add = function(page, user, callback) {
+  bookmarkSchema.statics.add = function(page, user) {
     var Bookmark = this;
 
-    Bookmark.findOneAndUpdate(
-      { page: page._id, user: user._id },
-      { page: page._id, user: user._id, createdAt: Date.now() },
-      { upsert: true, },
-      function (err, bookmark) {
-        debug('Bookmark.findOneAndUpdate', err, bookmark);
-        callback(err, bookmark);
+    return new Promise(function(resolve, reject) {
+      var newBookmark = new Bookmark;
+
+      newBookmark.page = page;
+      newBookmark.user = user;
+      newBookmark.createdAt = Date.now();
+      newBookmark.save(function(err, bookmark) {
+        debug('Bookmark.save', err, bookmark);
+        if (err) {
+          return reject(err);
+        }
+
+        resolve(bookmark);
+      });
     });
   };
 
-  bookmarkSchema.statics.remove = function(page, user, callback) {
-    // To be implemented ...
+  bookmarkSchema.statics.remove = function(page, user) {
+    var Bookmark = this;
+
+    return new Promise(function(resolve, reject) {
+      Bookmark.findOneAndRemove({page: page, user: user}, function(err, data) {
+        if (err) {
+          debug('Bookmark.findOneAndRemove failed', err);
+          return reject(err);
+        }
+
+        return resolve(data);
+      });
+    });
   };
 
   return mongoose.model('Bookmark', bookmarkSchema);

+ 92 - 0
lib/routes/bookmark.js

@@ -0,0 +1,92 @@
+module.exports = function(crowi, app) {
+  'use strict';
+
+  var debug = require('debug')('crowi:routes:bookmark')
+    , Bookmark = crowi.model('Bookmark')
+    , Page = crowi.model('Page')
+    , User = crowi.model('User')
+    , Revision = crowi.model('Revision')
+    , Bookmark = crowi.model('Bookmark')
+    , ApiResponse = require('../util/apiResponse')
+    , actions = {}
+  ;
+  actions.api = {};
+
+  /**
+   * @api {get} /bookmarks.get Get bookmark of the page with the user
+   * @apiName GetBookmarks
+   * @apiGroup Bookmark
+   *
+   * @apiParam {String} page_id Page Id.
+   */
+  actions.api.get = function (req, res) {
+    var pageId = req.query.page_id;
+
+    Bookmark.findByPageIdAndUserId(pageId, req.user)
+    .then(function(data) {
+      debug('bookmark found', pageId, data);
+      var result = {};
+      if (data) {
+      }
+
+      result.bookmark = data;
+      return res.json(ApiResponse.success(result));
+    }).catch(function(err) {
+      return res.json(ApiResponse.error(err));
+    });
+  };
+
+  /**
+   * @api {post} /bookmarks.add Add bookmark of the page
+   * @apiName AddBookmark
+   * @apiGroup Bookmark
+   *
+   * @apiParam {String} page_id Page Id.
+   */
+  actions.api.add = function(req, res) {
+    var pageId = req.body.page_id;
+
+    Page.findPageByIdAndGrantedUser(pageId, req.user, function(err, pageData) {
+      if (err) {
+        return res.json(ApiResponse.error(err));
+      }
+      if (pageData) {
+        Bookmark.add(pageData, req.user)
+        .then(function(data) {
+          var result = {};
+          data.depopulate('page');
+          data.depopulate('user');
+
+          result.bookmark = data;
+          return res.json(ApiResponse.success(result));
+        }).catch(function(err) {
+          return res.json(ApiResponse.error(err));
+        });
+      } else {
+        return res.json(ApiResponse.success({bookmark: null}));
+      }
+    });
+  };
+
+  /**
+   * @api {post} /bookmarks.remove Remove bookmark of the page
+   * @apiName RemoveBookmark
+   * @apiGroup Bookmark
+   *
+   * @apiParam {String} page_id Page Id.
+   */
+  actions.api.remove = function(req, res){
+    var pageId = req.body.page_id;
+
+    Bookmark.remove(pageId, req.user)
+    .then(function(data) {
+      debug('Bookmark removed.', data);
+      return res.json(ApiResponse.success({}));
+    }).catch(function(err) {
+      return res.json(ApiResponse.error(err));
+    });
+  };
+
+
+  return actions;
+};

+ 2 - 2
lib/routes/comment.js

@@ -41,7 +41,7 @@ module.exports = function(crowi, app) {
   };
 
   /**
-   * @api {post} /comments.post Post comment for the page
+   * @api {post} /comments.add Post comment for the page
    * @apiName PostComment
    * @apiGroup Comment
    *
@@ -50,7 +50,7 @@ module.exports = function(crowi, app) {
    * @apiParam {String} comment Comment body
    * @apiParam {Number} comment_position=-1 Line number of the comment
    */
-  api.post = function(req, res){
+  api.add = function(req, res){
     var form = req.form.commentForm;
 
     if (!req.form.isValid) {

+ 6 - 4
lib/routes/index.js

@@ -9,7 +9,8 @@ module.exports = function(crowi, app) {
     , installer = require('./installer')(crowi, app)
     , user      = require('./user')(crowi, app)
     , attachment= require('./attachment')(crowi, app)
-    , comment= require('./comment')(crowi, app)
+    , comment   = require('./comment')(crowi, app)
+    , bookmark  = require('./bookmark')(crowi, app)
     , loginRequired = middleware.loginRequired
     , accessTokenParser = middleware.accessTokenParser
     ;
@@ -73,13 +74,14 @@ module.exports = function(crowi, app) {
   app.post('/_api/attachment/:id/remove',loginRequired(crowi, app), attachment.api.remove);
   app.post('/_api/page/:id/like'      , loginRequired(crowi, app) , page.api.like);
   app.post('/_api/page/:id/unlike'    , loginRequired(crowi, app) , page.api.unlike);
-  app.get( '/_api/page/:id/bookmark'  , loginRequired(crowi, app) , page.api.isBookmarked);
-  app.post('/_api/page/:id/bookmark'  , loginRequired(crowi, app) , page.api.bookmark);
 
   // HTTP RPC Styled API (に徐々に移行していいこうと思う)
   app.get('/_api/pages.get'           , accessTokenParser(crowi, app) , loginRequired(crowi, app) , page.api.get);
   app.get('/_api/comments.get'        , accessTokenParser(crowi, app) , loginRequired(crowi, app) , comment.api.get);
-  app.post('/_api/comments.post'      , form.comment, accessTokenParser(crowi, app) , loginRequired(crowi, app) , comment.api.post);
+  app.post('/_api/comments.add'       , form.comment, accessTokenParser(crowi, app) , loginRequired(crowi, app) , comment.api.add);
+  app.get( '/_api/bookmarks.get'      , accessTokenParser(crowi, app) , loginRequired(crowi, app) , bookmark.api.get);
+  app.post('/_api/bookmarks.add'      , accessTokenParser(crowi, app) , loginRequired(crowi, app) , bookmark.api.add);
+  app.post('/_api/bookmarks.remove'   , accessTokenParser(crowi, app) , loginRequired(crowi, app) , bookmark.api.remove);
   //app.get('/_api/revision/:id'     , user.useUserData()         , revision.api.get);
   //app.get('/_api/r/:revisionId'    , user.useUserData()         , page.api.get);
 

+ 0 - 28
lib/routes/page.js

@@ -228,34 +228,6 @@ module.exports = function(crowi, app) {
     });
   };
 
-  /**
-   * page bookmark
-   */
-  api.isBookmarked = function(req, res){
-    var id = req.params.id;
-    Bookmark.findByPageIdAndUser(id, req.user, function(err, bookmark) {
-      debug('isBookmarked', id, req.user._id, err, bookmark);
-      if (err === null && bookmark) {
-        return res.json({bookmarked: true});
-      } else {
-        return res.json({bookmarked: false});
-      }
-    });
-  };
-
-  api.bookmark = function(req, res){
-    var id = req.params.id;
-    Page.findPageByIdAndGrantedUser(id, req.user, function(err, pageData) {
-      if (pageData) {
-        Bookmark.add(pageData, req.user, function(err, data) {
-          return res.json({status: true});
-        });
-      } else {
-        return res.json({status: false});
-      }
-    });
-  };
-
   /**
    * page like
    */

+ 1 - 1
lib/util/apiResponse.js

@@ -10,7 +10,7 @@ ApiResponse.error = function (err) {
     ok: false
   };
 
-  if (typeof err == Error) {
+  if (err instanceof Error) {
     result.error = err.toString();
   } else {
     result.error = err;

+ 4 - 23
lib/views/page.html

@@ -7,6 +7,10 @@
   <header id="page-header">
     <p class="stopper"><a href="#" data-affix-disable="#page-header"><i class="fa fa-chevron-up"></i></a></p>
 
+
+    {% if page %}
+      <a href="#" title="Bookmark" class="bookmark-link" id="bookmark-button" data-bookmarked="0"><i class="fa fa-star-o"></i></a>
+    {% endif %}
     <h1 class="title" id="revision-path">{{ path }}</h1>
   </header>
 </div>
@@ -217,13 +221,6 @@
 
   <div class="like-box">
     <dl class="dl-horizontal">
-      <dt>
-        <i class="fa fa-star"></i> お気に入り
-      </dt>
-      <dd>
-        <button class="btn btn-default btn-sm btn-bookmark" id="bookmarkButton"><i class="fa fa-star-o"></i></button>
-      </dd>
-
       <dt>
         <i class="fa fa-thumbs-o-up"></i> いいね!
       </dt>
@@ -264,22 +261,6 @@
   </div>
 <script>
 $(function() {
-  $.get('/_api/page/{{ page._id.toString() }}/bookmark', function(data) {
-    if (data.bookmarked) {
-      $('#bookmarkButton')
-        .removeClass('btn-default')
-        .addClass('btn-warning active bookmarked');
-      $('#bookmarkButton i')
-        .removeClass('fa-star-o')
-        .addClass('fa-star');
-    }
-  });
-
-  $('#bookmarkButton').click(function() {
-    var pageId = {{page._id|json|safe}};
-    $.post('/_api/page/{{ page._id.toString() }}/bookmark', function(data) {
-    });
-  });
   $('#pageLikeButton').click(function() {
     var pageId = {{page._id|json|safe}};
     $.post('/_api/page/{{ page._id.toString() }}/like', function(data) {

+ 25 - 14
resource/css/_layout.scss

@@ -142,13 +142,6 @@
               text-align: right;
             }
           }
-
-          .btn-bookmark {
-            color: #e6b422;
-            &.bookmarked {
-              color: #fff;
-            }
-          }
         }
 
         .liker-list, .contributor-list, .seen-user-list {
@@ -248,15 +241,27 @@
       }
 
 
-      article header h1 {
-        margin-top: 0;
 
-        a:last-child {
-          color: #D1E2E4;
-          opacity: .7;
+      article header { // not affixed
+        .bookmark-link {
+          float: right;
+          color: #e6b422;
+          font-size: 2em;
+          &.bookmarked {
+            //color: #fff;
+          }
+        }
 
-          &:hover {
-            color: inherit;
+        h1 {
+          margin-top: 0;
+
+          a:last-child {
+            color: #D1E2E4;
+            opacity: .7;
+
+            &:hover {
+              color: inherit;
+            }
           }
         }
       }
@@ -624,6 +629,9 @@
   .crowi.main-container { // {{{
     .main {
       article header {
+        .bookmark-link {
+          font-size: 1.3em;
+        }
         h1 {
           font-size: 1.4em;
           margin-bottom: 0;
@@ -642,6 +650,9 @@
     .main {
       padding: 10px;
       article header {
+        .bookmark-link {
+          font-size: 1em;
+        }
         h1 {
           font-size: 1.1em;
         }

+ 48 - 0
resource/js/crowi.js

@@ -391,6 +391,54 @@ $(function() {
         $('.page-attachments').remove();
       }
     });
+
+    // bookmark
+    var $bookmarkButton = $('#bookmark-button');
+    $.get('/_api/bookmarks.get', {page_id: pageId}, function(res) {
+      if (res.ok) {
+        if (res.bookmark) {
+          MarkBookmarked();
+        }
+      }
+    });
+
+    $bookmarkButton.click(function() {
+      var bookmarked = $bookmarkButton.data('bookmarked');
+      console.log('isBookmarked', bookmarked);
+      if (!bookmarked) {
+        $.post('/_api/bookmarks.add', {page_id: pageId}, function(res) {
+          console.log(res);
+          if (res.ok && res.bookmark) {
+            MarkBookmarked();
+          }
+        });
+      } else {
+        $.post('/_api/bookmarks.remove', {page_id: pageId}, function(res) {
+          console.log(res);
+          if (res.ok) {
+            MarkUnBookmarked();
+          }
+        });
+      }
+
+      return false;
+    });
+
+    function MarkBookmarked()
+    {
+      $('i', $bookmarkButton)
+        .removeClass('fa-star-o')
+        .addClass('fa-star');
+      $bookmarkButton.data('bookmarked', 1);
+    }
+
+    function MarkUnBookmarked()
+    {
+      $('i', $bookmarkButton)
+        .removeClass('fa-star')
+        .addClass('fa-star-o');
+      $bookmarkButton.data('bookmarked', 0);
+    }
   }
 });