diff --git a/package.json b/package.json
index 7c683b1..ea6c2fc 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"react-overlays": "^1.2.0",
"react-router-dom": "^5.0.0",
"react-scripts": "3.0.1",
+ "react-spinner-material": "^1.1.1",
"react-transition-group": "^4.0.1",
"typescript": "3.4.5"
},
diff --git a/src/components/Loading.module.css b/src/components/Loading.module.css
new file mode 100644
index 0000000..439c5f5
--- /dev/null
+++ b/src/components/Loading.module.css
@@ -0,0 +1,6 @@
+.loading {
+ display: flex;
+ justify-content: center;
+ align-content: center;
+ margin: 100px 0 0 0;
+}
diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx
new file mode 100644
index 0000000..e55881e
--- /dev/null
+++ b/src/components/Loading.tsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import Spinner from 'react-spinner-material';
+import styles from './Loading.module.css';
+
+const Loading = () => {
+ return (
+
+
+
+ );
+}
+
+export default Loading;
\ No newline at end of file
diff --git a/src/components/PageNotFound.tsx b/src/components/PageNotFound.tsx
index 010e285..38cc6ff 100644
--- a/src/components/PageNotFound.tsx
+++ b/src/components/PageNotFound.tsx
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import Helmet from 'react-helmet';
-import { RouteComponentProps } from 'react-router';
+import { RouteComponentProps, withRouter } from 'react-router';
interface Props extends RouteComponentProps { }
@@ -33,7 +33,7 @@ class PageNotFound extends Component {
Page Not Found
- The page you were trying to access doesn't exist or has been removed.
+ The page you were trying to access doesn't exist.
If the page does not automatically reload, please click here
@@ -41,4 +41,4 @@ class PageNotFound extends Component {
}
}
-export default PageNotFound;
\ No newline at end of file
+export default withRouter(PageNotFound);
\ No newline at end of file
diff --git a/src/components/database/DatabaseDetailItem.tsx b/src/components/database/DatabaseDetailItem.tsx
index 4d99d2f..f9ab4a7 100644
--- a/src/components/database/DatabaseDetailItem.tsx
+++ b/src/components/database/DatabaseDetailItem.tsx
@@ -3,63 +3,64 @@ import Helmet from 'react-helmet';
import { RouteComponentProps } from 'react-router';
import ItemUtil, { Item } from './lib/ItemUtil';
import ItemType from './item-type';
+import Loading from '../Loading';
+import PageNotFound from '../PageNotFound';
interface Props extends RouteComponentProps<{ id: string, doi: string }> { }
interface State {
- id: number;
- doi: string;
+ loading: boolean;
item: Item | null;
}
class DatabaseDetailItem extends Component {
+ public state: State = {
+ loading: true,
+ item: null,
+ };
+
+ private id: number;
+ private doi: string;
+
constructor(props: Props) {
super(props);
const { params } = this.props.match;
- this.state = {
- id: typeof params.id !== 'undefined' ? parseInt(params.id, 10) : 0,
- doi: typeof params.doi !== 'undefined' ? params.doi : '',
- item: null,
- }
+ this.id = typeof params.id !== 'undefined' ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : 0;
+ this.doi = typeof params.doi !== 'undefined' ? params.doi : '';
}
componentWillReceiveProps(nextProps: Props) {
const { params } = nextProps.match;
- const id = typeof params.id !== 'undefined' ? parseInt(params.id, 10) : 0;
- const doi = typeof params.doi !== 'undefined' ? params.doi : '';
- if (doi !== '') {
- ItemUtil.getByDoi(this.state.doi, (item) => {
- this.setState({ item });
- });
- } else if (id !== 0) {
- ItemUtil.get(this.state.id, (item) => {
- this.setState({ item });
- });
- }
- this.setState({ id, doi });
- this.updateItem(id, doi);
+ this.id = typeof params.id !== 'undefined' ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : 0) : 0;
+ this.doi = typeof params.doi !== 'undefined' ? params.doi : '';
+ this.updateItem();
}
componentDidMount() {
- this.updateItem(this.state.id, this.state.doi);
+ this.updateItem();
}
- updateItem(id: number, doi: string) {
- if (doi !== '') {
- ItemUtil.getByDoi(doi, (item) => {
- this.setState({ item });
+ updateItem() {
+ if (this.doi !== '') {
+ ItemUtil.getByDoi(this.doi, (item) => {
+ this.setState({ loading: false, item });
});
- } else if (id !== 0) {
- ItemUtil.get(id, (item) => {
- this.setState({ item });
+ } else if (this.id !== 0) {
+ ItemUtil.get(this.id, (item) => {
+ this.setState({ loading: false, item });
});
+ } else {
+ this.setState({ loading: false });
}
}
render() {
+ if (this.state.loading) {
+ return ;
+ }
if (this.state.item === null) {
- return null;
+ return ;
}
return (
<>
diff --git a/src/components/database/DatabaseSearchByAdvancedKeyword.tsx b/src/components/database/DatabaseSearchByAdvancedKeyword.tsx
index dc3ce53..2992b9d 100644
--- a/src/components/database/DatabaseSearchByAdvancedKeyword.tsx
+++ b/src/components/database/DatabaseSearchByAdvancedKeyword.tsx
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { RouteComponentProps } from 'react-router';
import AdvancedSearchQuery from './lib/AdvancedSearchQuery';
-import ItemUtil, { SortCondition, SearchCallbackFunc } from './lib/ItemUtil';
+import ItemUtil, { SortCondition } from './lib/ItemUtil';
import DatabaseListItem from './lib/DatabaseListItem';
interface Props extends RouteComponentProps { }
@@ -28,9 +28,8 @@ class DatabaseSearchByAdvancedKeyword extends Component {
return ItemUtil.getSearchByAdvancedKeywordsUrl(this.state.query);
}
- async search(condition: SortCondition, callback: SearchCallbackFunc) {
- const items = ItemUtil.getListByAdvancedSearchQuery(this.state.query, condition, callback);
- return items;
+ async search(condition: SortCondition) {
+ return ItemUtil.getListByAdvancedSearchQuery(this.state.query, condition);
}
render() {
diff --git a/src/components/database/DatabaseSearchByIndexId.tsx b/src/components/database/DatabaseSearchByIndexId.tsx
index e19bf17..593a1d2 100644
--- a/src/components/database/DatabaseSearchByIndexId.tsx
+++ b/src/components/database/DatabaseSearchByIndexId.tsx
@@ -3,9 +3,10 @@ import Helmet from 'react-helmet';
import { RouteComponentProps } from 'react-router';
import { Link } from 'react-router-dom';
import IndexUtil, { Index, INDEX_ID_PUBLIC } from './lib/IndexUtil';
-import ItemUtil, { SortCondition, SearchCallbackFunc } from './lib/ItemUtil';
+import ItemUtil, { SortCondition } from './lib/ItemUtil';
import DatabaseListIndex from './lib/DatabaseListIndex';
import DatabaseListItem from './lib/DatabaseListItem';
+import PageNotFound from '../PageNotFound';
interface Props extends RouteComponentProps<{ id: string }> { }
@@ -18,18 +19,19 @@ class DatabaseSearchByIndexId extends Component {
constructor(props: Props) {
super(props);
const { params } = this.props.match;
- const indexId = params.id ? parseInt(params.id, 10) : INDEX_ID_PUBLIC;
+ const indexId = params.id ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : null) : INDEX_ID_PUBLIC;
this.state = {
- index: IndexUtil.get(indexId),
+ index: indexId ? IndexUtil.get(indexId) : null,
}
this.search = this.search.bind(this);
}
componentWillReceiveProps(nextProps: Props) {
const { params } = nextProps.match;
- const indexId = params.id ? parseInt(params.id, 10) : INDEX_ID_PUBLIC;
- const index = IndexUtil.get(indexId);
- this.setState({ index });
+ const indexId = params.id ? (params.id.match(/^\d+$/) !== null ? parseInt(params.id, 10) : null) : INDEX_ID_PUBLIC;
+ this.setState({
+ index: indexId ? IndexUtil.get(indexId) : null,
+ })
}
getUrl() {
@@ -39,17 +41,16 @@ class DatabaseSearchByIndexId extends Component {
return IndexUtil.getUrl(this.state.index.id);
}
- async search(condition: SortCondition, callback: SearchCallbackFunc) {
+ async search(condition: SortCondition) {
if (this.state.index === null) {
return { total: 0, data: [] };
}
- const items = ItemUtil.getListByIndexId(this.state.index.id, condition, callback);
- return items;
+ return ItemUtil.getListByIndexId(this.state.index.id, condition);
}
render() {
if (this.state.index === null) {
- return null;
+ return ;
}
const baseUrl = this.getUrl();
const pIndexes = IndexUtil.getParents(this.state.index.id);
diff --git a/src/components/database/DatabaseSearchByItemType.tsx b/src/components/database/DatabaseSearchByItemType.tsx
index 3518681..a634dbc 100644
--- a/src/components/database/DatabaseSearchByItemType.tsx
+++ b/src/components/database/DatabaseSearchByItemType.tsx
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { RouteComponentProps } from 'react-router';
-import ItemUtil, { SortCondition, SearchCallbackFunc } from './lib/ItemUtil';
+import ItemUtil, { SortCondition } from './lib/ItemUtil';
import DatabaseListItem from './lib/DatabaseListItem';
interface Props extends RouteComponentProps<{ itemType: string, subItemType: string }> { }
@@ -29,12 +29,11 @@ class DatabaseSearchByItemType extends Component {
this.setState({ item_type, sub_item_type });
}
- async search(condition: SortCondition, callback: SearchCallbackFunc) {
+ async search(condition: SortCondition) {
if (this.state.item_type === '') {
return { total: 0, data: [] };
}
- const items = ItemUtil.getListByItemType(this.state.item_type, this.state.sub_item_type, condition, callback);
- return items;
+ return ItemUtil.getListByItemType(this.state.item_type, this.state.sub_item_type, condition);
}
getUrl() {
diff --git a/src/components/database/DatabaseSearchByKeyword.tsx b/src/components/database/DatabaseSearchByKeyword.tsx
index 93b415c..cc1641b 100644
--- a/src/components/database/DatabaseSearchByKeyword.tsx
+++ b/src/components/database/DatabaseSearchByKeyword.tsx
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { RouteComponentProps } from 'react-router';
-import ItemUtil, { SearchByKeywordType, SortCondition, SearchCallbackFunc } from './lib/ItemUtil';
+import ItemUtil, { SearchByKeywordType, SortCondition } from './lib/ItemUtil';
import DatabaseListItem from './lib/DatabaseListItem';
interface Props extends RouteComponentProps { }
@@ -15,7 +15,7 @@ class DatabaseSearchByKeyword extends Component {
constructor(props: Props) {
super(props);
const { type, keyword } = ItemUtil.getSearchKeywordByQuery(this.props.location.search);
- this.state = {type, keyword};
+ this.state = { type, keyword };
this.search = this.search.bind(this);
}
@@ -28,12 +28,11 @@ class DatabaseSearchByKeyword extends Component {
return ItemUtil.getSearchByKeywordUrl(this.state.type, this.state.keyword);
}
- async search(condition: SortCondition, callback: SearchCallbackFunc) {
+ async search(condition: SortCondition) {
if (this.state.keyword === '') {
return { total: 0, data: [] };
}
- const items = ItemUtil.getListByKeyword(this.state.type, this.state.keyword, condition, callback);
- return items;
+ return ItemUtil.getListByKeyword(this.state.type, this.state.keyword, condition);
}
render() {
diff --git a/src/components/database/lib/DatabaseListItem.tsx b/src/components/database/lib/DatabaseListItem.tsx
index aae16b5..15ba262 100644
--- a/src/components/database/lib/DatabaseListItem.tsx
+++ b/src/components/database/lib/DatabaseListItem.tsx
@@ -2,9 +2,10 @@ import React, { Component, ChangeEvent } from 'react';
import { RouteComponentProps } from 'react-router';
import { Link, withRouter } from 'react-router-dom';
import qs from 'query-string';
-import { Item, SortConditionLimit, SortConditionOrderBy, SortConditionOrderDir, SortCondition, SearchResult, SearchCallbackFunc } from './ItemUtil';
+import { Item, SortConditionLimit, SortConditionOrderBy, SortConditionOrderDir, SortCondition, SearchResult } from './ItemUtil';
import ItemType from '../item-type';
import styles from './DatabaseListItem.module.css';
+import Loading from '../../Loading';
const SORT_CONDITION_DEFAULT: SortCondition = {
limit: 20,
@@ -16,7 +17,7 @@ const SORT_CONDITION_RANGE_ORDER_BY = ['title', 'doi', 'last_update_date', 'crea
const SORT_CONDITION_RANGE_ORDER_DIR = ['0', '1'];
const SORT_CONDITION_RANGE_LIMIT = ['20', '50', '100'];
-export interface SearchFunc { (condition: SortCondition, callback: SearchCallbackFunc): Promise }
+export interface SearchFunc { (condition: SortCondition): Promise }
interface Props extends RouteComponentProps {
url: string;
@@ -24,40 +25,41 @@ interface Props extends RouteComponentProps {
}
interface State {
- url: string;
- search: SearchFunc;
+ loading: boolean;
condition: SortCondition;
result: SearchResult;
}
class DatabaseListItem extends Component {
+ private url: string;
+ private search: SearchFunc;
+
constructor(props: Props) {
super(props);
this.state = {
- url: props.url,
- search: props.search,
+ loading: true,
condition: this.getSortConditionByQuery(this.props.location.search),
result: { total: 0, data: [] }
};
+ this.url = props.url;
+ this.search = props.search;
this.handleSelectOrderby = this.handleSelectOrderby.bind(this);
this.handleSelectItemcount = this.handleSelectItemcount.bind(this);
}
- componentWillReceiveProps(nextProps: Props) {
- const url = nextProps.url;
- const search = nextProps.search;
+ async componentWillReceiveProps(nextProps: Props) {
+ this.url = nextProps.url;
+ this.search = nextProps.search;
const condition = this.getSortConditionByQuery(nextProps.location.search);
- this.setState({ url, search, condition });
- search(condition, (result) => {
- this.setState({ result });
- });
+ this.setState({ loading: true });
+ const result = await this.search(condition);
+ this.setState({ loading: false, result, condition });
}
- componentDidMount() {
- this.state.search(this.state.condition, (result) => {
- this.setState({ result });
- });
+ async componentDidMount() {
+ const result = await this.search(this.state.condition);
+ this.setState({ loading: false, result });
}
handleSelectOrderby(event: ChangeEvent) {
@@ -85,8 +87,8 @@ class DatabaseListItem extends Component {
params.push('order_dir=' + String(newOrderDir));
params.push('itemcount=' + String(newLimit));
params.push('page=' + String(newPage));
- const join = this.state.url.indexOf('?') < 0 ? '?' : '&'
- return (params.length > 0 ? this.state.url + join + params.join('&') : this.state.url);
+ const join = this.url.indexOf('?') < 0 ? '?' : '&'
+ return (params.length > 0 ? this.url + join + params.join('&') : this.url);
}
getDefaultSortCondition() {
@@ -183,6 +185,9 @@ class DatabaseListItem extends Component {
}
render() {
+ if (this.state.loading) {
+ return ;
+ }
const result = this.state.result;
if (result.data.length === 0) {
return (
diff --git a/src/components/database/lib/ItemUtil.ts b/src/components/database/lib/ItemUtil.ts
index 9951355..764cfe4 100644
--- a/src/components/database/lib/ItemUtil.ts
+++ b/src/components/database/lib/ItemUtil.ts
@@ -177,8 +177,6 @@ export interface SearchResult {
data: Item[];
}
-export interface SearchCallbackFunc { (result: SearchResult): void }
-
class ItemDatabase {
private db: loki;
private items: Collection | null = null;
@@ -334,7 +332,7 @@ class ItemUtil {
return item;
}
- static async getListByIndexId(indexId: number, condition: SortCondition, callback: SearchCallbackFunc) {
+ static async getListByIndexId(indexId: number, condition: SortCondition) {
const items = await database.getItems();
const filter = {
'index.index_id': indexId
@@ -346,11 +344,10 @@ class ItemUtil {
total: result.count(),
data: result.sort(itemSorter.sort).offset(offset).limit(condition.limit).data()
};
- callback(ret);
return ret;
}
- static async getListByItemType(itemType: string, subItemType: string, condition: SortCondition, callback: SearchCallbackFunc) {
+ static async getListByItemType(itemType: string, subItemType: string, condition: SortCondition) {
const items = await database.getItems();
let filter: any = {
item_type_name: 'xnp' + itemType,
@@ -377,11 +374,10 @@ class ItemUtil {
total: result.count(),
data: result.sort(itemSorter.sort).offset(offset).limit(condition.limit).data()
};
- callback(ret);
return ret;
}
- static async getListByKeyword(type: SearchByKeywordType, keyword: string, condition: SortCondition, callback: SearchCallbackFunc) {
+ static async getListByKeyword(type: SearchByKeywordType, keyword: string, condition: SortCondition) {
const items = await database.getItems();
const regex = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const num = keyword.match(/^[0-9]+$/) ? parseInt(keyword, 10) : null;
@@ -454,11 +450,10 @@ class ItemUtil {
total: result.count(),
data: result.sort(itemSorter.sort).offset(offset).limit(condition.limit).data()
};
- callback(ret);
return ret;
}
- static async getListByAdvancedSearchQuery(query: AdvancedSearchQuery, condition: SortCondition, callback: SearchCallbackFunc) {
+ static async getListByAdvancedSearchQuery(query: AdvancedSearchQuery, condition: SortCondition) {
const items = await database.getItems();
const filter: any = query.getSearchFilter();
const offset = condition.limit * (condition.page - 1);
@@ -468,7 +463,6 @@ class ItemUtil {
total: result.count(),
data: result.sort(itemSorter.sort).offset(offset).limit(condition.limit).data()
};
- callback(ret);
return ret;
}
}
diff --git a/yarn.lock b/yarn.lock
index 949106f..4286370 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8372,6 +8372,11 @@ react-side-effect@^1.1.0:
exenv "^1.2.1"
shallowequal "^1.0.1"
+react-spinner-material@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/react-spinner-material/-/react-spinner-material-1.1.1.tgz#b996d893251dd82ec12e21e03a28af100df05cab"
+ integrity sha512-zoCsu4hmL2Z6Ihm6aI40+FKUUQ6ckMK2FvAwYpHd9xdc7Q9V4OiDlbDfH5XgxZUZuB/tAjvDaC+dbRexEw2D2A==
+
react-transition-group@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.0.1.tgz#8cb8d58763e259da465385bb83b3b41b3ecba629"