Преглед изворни кода

Implement: Search on header bar

Sotaro KARASAWA пре 10 година
родитељ
комит
0c1d392905

+ 17 - 9
resource/js/components/Header/SearchBox.js

@@ -4,6 +4,7 @@ import React from 'react';
 
 
 import SearchForm from './SearchForm';
 import SearchForm from './SearchForm';
 import SearchSuggest from './SearchSuggest';
 import SearchSuggest from './SearchSuggest';
+import axios from 'axios'
 
 
 export default class SearchBox extends React.Component {
 export default class SearchBox extends React.Component {
 
 
@@ -12,20 +13,27 @@ export default class SearchBox extends React.Component {
 
 
     this.state = {
     this.state = {
       searchingKeyword: '',
       searchingKeyword: '',
-      suggestedPages: [],
+      searchedPages: [],
+      searchError: null,
     }
     }
 
 
     this.search = this.search.bind(this);
     this.search = this.search.bind(this);
   }
   }
 
 
   search(data) {
   search(data) {
-    console.log('search doing ... ', data);
-    //this.loadCommentsFromServer();
-    this.setState({
-      suggestedPages: [
-        { path: '/hoge/fuga ' + data.keyword, author: '@sotarok'},
-        { path: '/hoge/piyo', author: '@riaf'},
-      ]
+    const keyword = data.keyword;
+
+    axios.get('/_api/search', {params: {q: keyword}})
+    .then((res) => {
+      if (res.data.ok) {
+        this.setState({
+          searchedPages: res.data.data
+        });
+      }
+      // TODO error
+    })
+    .catch((res) => {
+      // TODO error
     });
     });
   }
   }
 
 
@@ -33,7 +41,7 @@ export default class SearchBox extends React.Component {
     return (
     return (
       <div className="search-box">
       <div className="search-box">
         <SearchForm onSearchFormChanged={this.search} />
         <SearchForm onSearchFormChanged={this.search} />
-        <SearchSuggest suggestedPages={this.state.suggestedPages} />
+        <SearchSuggest searchedPages={this.state.searchedPages} />
       </div>
       </div>
     );
     );
   }
   }

+ 7 - 2
resource/js/components/Header/SearchForm.js

@@ -18,15 +18,20 @@ export default class SearchForm extends React.Component {
     setInterval(this.searchFieldTicker.bind(this), this.props.pollInterval);
     setInterval(this.searchFieldTicker.bind(this), this.props.pollInterval);
   }
   }
 
 
-  searchFieldTicker() {
+  search() {
     if (this.state.searchedKeyword != this.state.keyword) {
     if (this.state.searchedKeyword != this.state.keyword) {
       this.props.onSearchFormChanged({keyword: this.state.keyword});
       this.props.onSearchFormChanged({keyword: this.state.keyword});
       this.setState({searchedKeyword: this.state.keyword});
       this.setState({searchedKeyword: this.state.keyword});
     }
     }
   }
   }
 
 
+  searchFieldTicker() {
+    this.search();
+  }
+
   handleSubmit(event) {
   handleSubmit(event) {
     event.preventDefault();
     event.preventDefault();
+    this.search();
   }
   }
 
 
   handleChange(event) {
   handleChange(event) {
@@ -45,7 +50,7 @@ export default class SearchForm extends React.Component {
           onChange={this.handleChange}
           onChange={this.handleChange}
         />
         />
         <span className="input-group-btn">
         <span className="input-group-btn">
-          <button type="submit" className="btn btn-default" type="button">
+          <button type="submit" className="btn btn-default">
             <i className="search-top-icon fa fa-search"></i>
             <i className="search-top-icon fa fa-search"></i>
           </button>
           </button>
         </span>
         </span>

+ 4 - 6
resource/js/components/Header/SearchSuggest.js

@@ -5,15 +5,13 @@ import ListView from '../PageList/ListView';
 export default class SearchSuggest extends React.Component {
 export default class SearchSuggest extends React.Component {
 
 
   render() {
   render() {
-    console.log('suggestedPages', this.props.suggestedPages);
-
-    if (this.props.suggestedPages.length < 1) {
+    if (this.props.searchedPages.length < 1) {
       return <div></div>;
       return <div></div>;
     }
     }
 
 
     return (
     return (
       <div className="search-suggest" id="search-suggest">
       <div className="search-suggest" id="search-suggest">
-        <ListView pages={this.props.suggestedPages} />
+        <ListView pages={this.props.searchedPages} />
       </div>
       </div>
     );
     );
   }
   }
@@ -21,9 +19,9 @@ export default class SearchSuggest extends React.Component {
 }
 }
 
 
 SearchSuggest.propTypes = {
 SearchSuggest.propTypes = {
-  suggestedPages: React.PropTypes.array.isRequired,
+  searchedPages: React.PropTypes.array.isRequired,
 };
 };
 
 
 SearchSuggest.defaultProps = {
 SearchSuggest.defaultProps = {
-  suggestedPages: [],
+  searchedPages: [],
 };
 };

+ 19 - 4
resource/js/components/PageList/ListView.js

@@ -1,19 +1,34 @@
 import React from 'react';
 import React from 'react';
 
 
+import UserPicture from '../User/UserPicture';
+import PageListMeta from './PageListMeta';
+import PagePath from './PagePath';
+
 export default class ListView extends React.Component {
 export default class ListView extends React.Component {
 
 
   render() {
   render() {
-    const listView = this.props.pages.map(function(page) {
+    const listView = this.props.pages.map((page) => {
+
       return (
       return (
-        <div key={page.path}>
-          {page.path} by {page.author}
-        </div>
+        <li className="page-list-li" key={page._id}>
+          <div className="picture-outer">
+            <UserPicture user={page.revision.author} />
+          </div>
+          <div className="page-link-outer">
+            <a className="page-list-link" href={page.path}>
+              <PagePath page={page} />
+            </a>
+            <PageListMeta page={page} />
+          </div>
+        </li>
       );
       );
     });
     });
 
 
     return (
     return (
       <div className="page-list">
       <div className="page-list">
+        <ul className="page-list-ul">
         {listView}
         {listView}
+        </ul>
       </div>
       </div>
     );
     );
   }
   }

+ 51 - 0
resource/js/components/PageList/PageListMeta.js

@@ -0,0 +1,51 @@
+import React from 'react';
+
+export default class PageListMeta extends React.Component {
+
+  isPortalPath(path) {
+    if (path.match(/.*\/$/)) {
+      return true;
+    }
+
+    return false;
+  }
+
+  render() {
+    // TODO isPortal()
+    const page = this.props.page;
+
+    // portal check
+    let PortalLabel;
+    if (this.isPortalPath(page.path)) {
+      PortalLabel = <span className="label label-info">PORTAL</span>;
+    }
+
+    let CommentCount;
+    if (page.commentCount > 0) {
+      CommentCount = <span><i className="fa fa-comment" />{page.commentCount}</span>;
+    }
+
+    let LikerCount;
+    if (page.liker.length > 0) {
+      LikerCount = <span><i className="fa fa-thumbs-up" />{page.liker.length}</span>;
+    }
+
+
+    return (
+      <span className="page-list-meta">
+        {PortalLabel}
+        {CommentCount}
+        {LikerCount}
+      </span>
+    );
+  }
+}
+
+PageListMeta.propTypes = {
+  page: React.PropTypes.object.isRequired,
+};
+
+PageListMeta.defaultProps = {
+  page: {},
+};
+

+ 46 - 0
resource/js/components/PageList/PagePath.js

@@ -0,0 +1,46 @@
+import React from 'react';
+
+export default class PagePath extends React.Component {
+
+  getShortPath(path) {
+    let name = path.replace(/(\/)$/, '');
+
+    // /.../hoge/YYYY/MM/DD 形式のページ
+    if (name.match(/.+\/([^/]+\/\d{4}\/\d{2}\/\d{2})$/)) {
+      return name.replace(/.+\/([^/]+\/\d{4}\/\d{2}\/\d{2})$/, '$1');
+    }
+
+    // /.../hoge/YYYY/MM 形式のページ
+    if (name.match(/.+\/([^/]+\/\d{4}\/\d{2})$/)) {
+      return name.replace(/.+\/([^/]+\/\d{4}\/\d{2})$/, '$1');
+    }
+
+    // /.../hoge/YYYY 形式のページ
+    if (name.match(/.+\/([^/]+\/\d{4})$/)) {
+      return name.replace(/.+\/([^/]+\/\d{4})$/, '$1');
+    }
+
+    // ページの末尾を拾う
+    return name.replace(/.+\/(.+)?$/, '$1');
+  }
+
+  render() {
+    const page = this.props.page;
+    const shortPath = this.getShortPath(page.path);
+    const pathPrefix = page.path.replace(new RegExp(shortPath + '(/)?$'), '');
+
+    return (
+      <span className="page-path">
+        {pathPrefix}<strong>{shortPath}</strong>
+      </span>
+    );
+  }
+}
+
+PagePath.propTypes = {
+  page: React.PropTypes.object.isRequired,
+};
+
+PagePath.defaultProps = {
+  page: {},
+};

+ 38 - 0
resource/js/components/User/UserPicture.js

@@ -0,0 +1,38 @@
+import React from 'react';
+
+// TODO UserComponent?
+export default class UserPicture extends React.Component {
+
+  getUserPicture(user) {
+    // from swig.setFilter('picture', function(user)
+
+    user.fbId = user.userId; // migration
+    if (user.image && user.image != '/images/userpicture.png') {
+      return user.image;
+    } else if (user.fbId) {
+      return '//graph.facebook.com/' + user.fbId + '/picture?size=square';
+    } else {
+      return '/images/userpicture.png';
+    }
+  }
+
+  render() {
+    const user = this.props.user;
+
+    return (
+      <img
+        src={this.getUserPicture(user)}
+        alt={user.username}
+        className="picture picture-rounded"
+        />
+    );
+  }
+}
+
+UserPicture.propTypes = {
+  user: React.PropTypes.object.isRequired,
+};
+
+UserPicture.defaultProps = {
+  user: {},
+};