Просмотр исходного кода

Move revision-history to react

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

+ 5 - 1
lib/routes/revision.js

@@ -22,9 +22,13 @@ module.exports = function(crowi, app) {
     Revision
       .findRevision(revisionId)
       .then(function(revisionData) {
-        return res.json(ApiResponse.success(revisionData));
+        var result = {
+          revision: revisionData,
+        }
+        return res.json(ApiResponse.success(result));
       })
       .catch(function(err) {
+        debug('Error revisios.get', err);
         return res.json(ApiResponse.error(err));
       });
   };

+ 24 - 7
resource/css/_page.scss

@@ -277,22 +277,39 @@
     .revision-hisory-outer {
       margin-top: 8px;
 
-      .picture {
-        float: left;
-        width: 32px;
-        height: 32px;
-      }
-
       .revision-history-main {
-        margin-left: 40px;
+
+        .picture {
+          float: left;
+          width: 32px;
+          height: 32px;
+        }
 
         .revision-history-author {
+          margin-left: 40px;
           color: #666;
         }
         .revision-history-comment {
+          margin-left: 40px;
         }
         .revision-history-meta {
+          margin-left: 40px;
+
+          p {
+            margin-bottom: 8px;
+          }
+
+          a {
+            margin-right: 8px;
+          }
+          a:hover {
+            cursor: pointer;
+          }
         }
+
+      }
+      .revision-history-diff {
+        margin-left: 40px;
       }
     }
 

+ 6 - 1
resource/js/app.js

@@ -28,7 +28,7 @@ const componentMappings = {
   'search-top': <HeaderSearchBox />,
   'search-page': <SearchPage />,
   'page-list-search': <PageListSearch />,
-  'revision-history': <PageHistory pageId={pageId} />,
+  //'revision-history': <PageHistory pageId={pageId} />,
   //'page-comment': <PageComment />,
   'seen-user-list': <SeenUserList />,
 };
@@ -39,3 +39,8 @@ Object.keys(componentMappings).forEach((key) => {
     ReactDOM.render(componentMappings[key], elem);
   }
 });
+
+// うわーもうー
+$('a[data-toggle="tab"][href="#revision-history"]').on('show.bs.tab', function() {
+  ReactDOM.render(<PageHistory pageId={pageId} />, document.getElementById('revision-history'));
+});

+ 89 - 3
resource/js/components/PageHistory.js

@@ -12,7 +12,11 @@ export default class PageHistory extends React.Component {
 
     this.state = {
       revisions: [],
+      diffOpened: {},
     };
+
+    this.getPreviousRevision = this.getPreviousRevision.bind(this);
+    this.onDiffOpenClicked = this.onDiffOpenClicked.bind(this);
   }
 
   componentDidMount() {
@@ -26,28 +30,110 @@ export default class PageHistory extends React.Component {
     .then(res => {
 
       const rev = res.revisions;
+      let diffOpened = {};
+      const lastId = rev.length - 1;
       res.revisions.map((revision, i) => {
         const user = this.crowi.findUserById(revision.author);
         if (user) {
           rev[i].author = user;
         }
+
+        if (i === 0 || i === lastId) {
+          console.log('DEbug, diffopen', revision._id);
+          diffOpened[revision._id] = true;
+        } else {
+          diffOpened[revision._id] = false;
+        }
       });
 
-      this.setState({revisions: rev});
+      this.setState({
+        revisions: rev,
+        diffOpened: diffOpened,
+      });
+
+      // load 0, and last default
+      if (rev[0]) {
+        this.fetchPageRevisionBody(rev[0]);
+      }
+      if (rev[1]) {
+        this.fetchPageRevisionBody(rev[1]);
+      }
+      if (lastId !== 0 && lastId !== 1 && rev[lastId]) {
+        this.fetchPageRevisionBody(rev[lastId]);
+      }
     }).catch(err => {
       // do nothing
     });
   }
 
-  fetchPageRevisionBody()
+  getPreviousRevision(currentRevision) {
+    console.log('getPreviousRevision', currentRevision, this.state.revisions);
+    let cursor = null;
+    for (let revision of this.state.revisions) {
+      if (cursor && cursor._id == currentRevision._id) {
+        cursor = revision;
+        break;
+      }
+
+      cursor = revision;
+    }
+
+    return cursor;
+  }
+
+  onDiffOpenClicked(revision)
+  {
+    const diffOpened = this.state.diffOpened,
+      revisionId = revision._id;
+
+    if (diffOpened[revisionId]) {
+      return ;
+    }
+
+    diffOpened[revisionId] = true;
+    this.setState({
+      diffOpened
+    });
+
+    this.fetchPageRevisionBody(revision);
+    this.fetchPageRevisionBody(this.getPreviousRevision(revision));
+  }
+
+  fetchPageRevisionBody(revision)
   {
+    if (revision.body) {
+      return ;
+    }
+
+    this.crowi.apiGet('/revisions.get', {revision_id: revision._id})
+    .then(res => {
+      if (res.ok) {
+        this.setState({
+          revisions: this.state.revisions.map((rev) => {
+            if (rev._id == res.revision._id) {
+              return res.revision;
+            }
+
+            return rev;
+          })
+        })
+      }
+    }).catch(err => {
+
+    });
+
   }
 
   render() {
     return (
       <div>
         <h1><Icon name="history" /> History</h1>
-        <PageRevisionList revisions={this.state.revisions} />
+        <PageRevisionList
+          revisions={this.state.revisions}
+          diffOpened={this.state.diffOpened}
+          getPreviousRevision={this.getPreviousRevision}
+          onDiffOpenClicked={this.onDiffOpenClicked}
+        />
       </div>
     );
   }

+ 32 - 16
resource/js/components/PageHistory/PageRevisionList.js

@@ -1,28 +1,42 @@
 import React from 'react';
 
-import Revision from './Revision';
+import Revision     from './Revision';
+import RevisionDiff from './RevisionDiff';
 
 export default class PageRevisionList extends React.Component {
 
-  fetchPreviousRevision(currentRevision) {
+  render() {
+    const revisions = this.props.revisions,
+      revisionCount = this.props.revisions.length;
 
-    let cursor = null;
-    for (let revision of this.props.revisions) {
-      if (cursor && cursor._id == currentRevision._id) {
-        cursor = revision;
-        break;
-      }
+    const revisionList = this.props.revisions.map((revision, idx) => {
+      const revisionId = revision._id
+        , revisionDiffOpened = this.props.diffOpened[revisionId] || false
 
-      cursor = revision;
-    }
 
-    console.log('previous is', cursor);
-  }
+      let previousRevision;
+      if (idx+1 < revisionCount) {
+        previousRevision = revisions[idx + 1];
+      } else {
+        previousRevision = revision; // if it is the first revision, show full text as diff text
+      }
 
-  render() {
-    const revisionList = this.props.revisions.map((revision) =>
-      <Revision key={revision._id} revision={revision} fetchPreviousRevision={this.fetchPreviousRevision.bind(this)} />
-    );
+      return (
+        <div className="revision-hisory-outer" key={"revision-history-" + revisionId}>
+          <Revision
+            revision={revision}
+            onDiffOpenClicked={this.props.onDiffOpenClicked}
+            key={"revision-history-rev-" + revisionId}
+            />
+          <RevisionDiff
+            revisionDiffOpened={revisionDiffOpened}
+            currentRevision={revision}
+            previousRevision={previousRevision}
+            key={"revision-diff-" + revisionId}
+          />
+        </div>
+      );
+    });
 
     return (
       <div className="revision-history-list">
@@ -34,5 +48,7 @@ export default class PageRevisionList extends React.Component {
 
 PageRevisionList.propTypes = {
   revisions: React.PropTypes.array,
+  diffOpened: React.PropTypes.object,
+  onDiffOpenClicked: React.PropTypes.func.isRequired,
 }
 

+ 14 - 39
resource/js/components/PageHistory/Revision.js

@@ -3,21 +3,20 @@ import React from 'react';
 import UserDate     from '../Common/UserDate';
 import Icon         from '../Common/Icon';
 import UserPicture  from '../User/UserPicture';
-import RevisionDiff from './RevisionDiff';
 
 export default class Revision extends React.Component {
 
   constructor(props) {
     super(props);
 
-    this.openRevisionDiff = this.openRevisionDiff.bind(this);
+    this._onDiffOpenClicked = this._onDiffOpenClicked.bind(this);
   }
 
   componentDidMount() {
   }
 
-  openRevisionDiff() {
-    this.props.fetchPreviousRevision(this.props.revision);
+  _onDiffOpenClicked() {
+    this.props.onDiffOpenClicked(this.props.revision);
   }
 
   render() {
@@ -29,56 +28,32 @@ export default class Revision extends React.Component {
       pic = <UserPicture user={author} />;
     }
 
-    //<a href={"?revision=" + revision._id }><Icon name="history" /> {{ t('View this version') }}</a>
     return (
-      <div className="revision-hisory-outer">
+      <div className="revision-history-main">
         {pic}
-        <div className="revision-history-main">
-          <div className="revision-history-author">
-            <strong>{author.username}</strong>
-          </div>
-          <div className="revision-history-meta">
+        <div className="revision-history-author">
+          <strong>{author.username}</strong>
+        </div>
+        <div className="revision-history-meta">
+         <p>
             <UserDate dateTime={revision.createdAt} />
-            <br />
+          </p>
+          <p>
             <a href={"?revision=" + revision._id }>
               <Icon name="history" /> View this version
             </a>
-            <a className="diff-view" onClick={this.openRevisionDiff}>
+            <a className="diff-view" onClick={this._onDiffOpenClicked}>
               <Icon name="arrow-circle-right" /> View diff
             </a>
-
-            <RevisionDiff
-              current={revision}
-              previous={this.props.previousRevisionText}
-            />
-          </div>
+          </p>
         </div>
       </div>
     );
   }
-    /*
-        <img src="{{ tt.author|picture }}" class="picture picture-rounded">
-        <div class="revision-history-main">
-          <div class="revision-history-author">
-            <strong>{% if tt.author %}{{ tt.author.username }}{% else %}-{% endif %}</strong>
-          </div>
-          <div class="revision-history-comment">
-          </div>
-          <div class="revision-history-meta">
-            {{ tt.createdAt|datetz('Y-m-d H:i:s') }}
-            <br>
-            <a href="?revision={{ tt._id.toString() }}"><i class="fa fa-history"></i> {{ t('View this version') }}</a>
-            <a class="diff-view" data-revision-id="{{ tt._id.toString() }}">
-              <i id="diff-icon-{{ tt._id.toString() }}" class="fa fa-arrow-circle-right"></i> {{ t('View diff') }}
-            </a>
-            <div class="" id="diff-display-{{ tt._id.toString()}}" style="display: none"></div>
-          </div>
-        </div>
-        */
 }
 
 Revision.propTypes = {
   revision: React.PropTypes.object,
-  previousRevisionText: React.PropTypes.string,
+  onDiffOpenClicked: React.PropTypes.func.isRequired,
 }
 

+ 34 - 25
resource/js/components/PageHistory/RevisionDiff.js

@@ -1,33 +1,42 @@
 import React from 'react';
 
+import { createPatch } from 'diff';
+import { Diff2Html } from 'diff2html';
+
 export default class RevisionDiff extends React.Component {
 
   render() {
-    return (
-      <div className="revision-hisory-outer">
-        diff
-      </div>
-    );
+    const currentRevision = this.props.currentRevision,
+      previousRevision = this.props.previousRevision,
+      revisionDiffOpened = this.props.revisionDiffOpened;
+
+
+    let diffViewHTML = '';
+    if (currentRevision.body
+      && previousRevision.body
+      && revisionDiffOpened) {
+
+      let previousText = previousRevision.body;
+      if (currentRevision._id == previousRevision._id) {
+        previousText = '';
+      }
+
+      const patch = createPatch(
+        currentRevision.path,
+        previousText,
+        currentRevision.body
+      );
+
+      diffViewHTML = Diff2Html.getPrettyHtml(patch);
+    }
+
+    const diffView = {__html: diffViewHTML};
+    return <div className="revision-history-diff" dangerouslySetInnerHTML={diffView} />;
   }
-    /*
-        <img src="{{ tt.author|picture }}" class="picture picture-rounded">
-        <div class="revision-history-main">
-          <div class="revision-history-author">
-            <strong>{% if tt.author %}{{ tt.author.username }}{% else %}-{% endif %}</strong>
-          </div>
-          <div class="revision-history-comment">
-          </div>
-          <div class="revision-history-meta">
-            {{ tt.createdAt|datetz('Y-m-d H:i:s') }}
-            <br>
-            <a href="?revision={{ tt._id.toString() }}"><i class="fa fa-history"></i> {{ t('View this version') }}</a>
-            <a class="diff-view" data-revision-id="{{ tt._id.toString() }}">
-              <i id="diff-icon-{{ tt._id.toString() }}" class="fa fa-arrow-circle-right"></i> {{ t('View diff') }}
-            </a>
-            <div class="" id="diff-display-{{ tt._id.toString()}}" style="display: none"></div>
-          </div>
-        </div>
-        */
 }
 
-
+RevisionDiff.propTypes = {
+  currentRevision: React.PropTypes.object.isRequired,
+  previousRevision: React.PropTypes.object.isRequired,
+  revisionDiffOpened: React.PropTypes.bool.isRequired,
+}

+ 0 - 90
resource/js/crowi.js

@@ -2,8 +2,6 @@
 /* Author: Sotaro KARASAWA <sotarok@crocos.co.jp>
 */
 
-var jsdiff = require('diff');
-var diff2html = require('diff2html').Diff2Html;
 var io = require('socket.io-client');
 
 //require('bootstrap-sass');
@@ -702,94 +700,6 @@ $(function() {
       return $userHtml;
     }
 
-    // History Diff
-    var allRevisionIds = [];
-    $.each($('.diff-view'), function() {
-      allRevisionIds.push($(this).data('revisionId'));
-    });
-
-    $('.diff-view').on('click', function(e) {
-      e.preventDefault();
-
-      var getBeforeRevisionId = function(revisionId) {
-        var currentPos = $.inArray(revisionId, allRevisionIds);
-        if (currentPos < 0) {
-          return false;
-        }
-
-        var beforeRevisionId = allRevisionIds[currentPos + 1];
-        if (typeof beforeRevisionId === 'undefined') {
-          return false;
-        }
-
-        return beforeRevisionId;
-      };
-
-      var revisionId = $(this).data('revisionId');
-      var beforeRevisionId = getBeforeRevisionId(revisionId);
-      var $diffDisplay = $('#diff-display-' + revisionId);
-      var $diffIcon = $('#diff-icon-' + revisionId);
-
-      if ($diffIcon.hasClass('fa-arrow-circle-right')) {
-        $diffIcon.removeClass('fa-arrow-circle-right');
-        $diffIcon.addClass('fa-arrow-circle-down');
-      } else {
-        $diffIcon.removeClass('fa-arrow-circle-down');
-        $diffIcon.addClass('fa-arrow-circle-right');
-      }
-
-      if ($diffDisplay.data('loaded')) {
-        $diffDisplay.slideToggle();
-        return true;
-      }
-
-      $diffDisplay.text('');
-
-      if (beforeRevisionId === false) {
-        var revisionIds = revisionId;
-
-        // For differences from blank pages, make it all green
-        $.ajax({
-          type: 'GET',
-          url: '/_api/revisions.list?revision_ids=' + revisionIds,
-          dataType: 'json'
-        }).done(function(res) {
-          var currentText = res[0].body;
-
-          var patch = jsdiff.createPatch($('#revision-path').text(), '', currentText);
-          $diffDisplay.html(diff2html.getPrettyHtml(patch));
-        });
-      } else {
-        var revisionIds = revisionId + ',' + beforeRevisionId;
-
-        $.ajax({
-          type: 'GET',
-          url: '/_api/revisions.list?revision_ids=' + revisionIds,
-          dataType: 'json'
-        }).done(function(res) {
-          var currentText = res[0].body;
-          var previousText = res[1].body;
-
-          $diffDisplay.text('');
-
-          var patch = jsdiff.createPatch($('#revision-path').text(), previousText, currentText);
-          $diffDisplay.html(diff2html.getPrettyHtml(patch));
-
-        });
-      }
-      $diffDisplay.data('loaded', 1);
-      $diffDisplay.slideToggle();
-    });
-
-    // default open
-    $('a[data-toggle="tab"][href="#revision-history"]').on('show.bs.tab', function() {
-      $('.diff-view').each(function(i, diffView) {
-        if (i < 2) {
-          $(diffView).click();
-        }
-      });
-    });
-
     // presentation
     var presentaionInitialized = false
       , $b = $('body');