diff --git a/frontend/src/components/share-link-panel/index.js b/frontend/src/components/share-link-panel/index.js
index a2313a717ba..ed8b739b392 100644
--- a/frontend/src/components/share-link-panel/index.js
+++ b/frontend/src/components/share-link-panel/index.js
@@ -18,6 +18,8 @@ const propTypes = {
itemType: PropTypes.string
};
+const PER_PAGE = 25;
+
class ShareLinkPanel extends React.Component {
constructor(props) {
@@ -28,6 +30,9 @@ class ShareLinkPanel extends React.Component {
this.state = {
isLoading: true,
+ hasMore: false,
+ isLoadingMore: false,
+ page: 1,
mode: 'listLinks',
sharedLinkInfo: null,
shareLinks: [],
@@ -37,11 +42,12 @@ class ShareLinkPanel extends React.Component {
}
componentDidMount() {
- let path = this.props.itemPath;
- let repoID = this.props.repoID;
- seafileAPI.getShareLink(repoID, path).then((res) => {
+ const { page } = this.state;
+ const { repoID, itemPath: path } = this.props;
+ seafileAPI.listShareLinks({repoID, path, page}).then((res) => {
this.setState({
isLoading: false,
+ hasMore: res.data.length == PER_PAGE,
shareLinks: res.data.map(item => new ShareLink(item))
});
}).catch(error => {
@@ -184,13 +190,46 @@ class ShareLinkPanel extends React.Component {
});
};
+ handleScroll = (event) => {
+ if (!this.state.isLoadingMore && this.state.hasMore) {
+ const clientHeight = event.target.clientHeight;
+ const scrollHeight = event.target.scrollHeight;
+ const scrollTop = event.target.scrollTop;
+ const isBottom = (clientHeight + scrollTop + 1 >= scrollHeight);
+ if (isBottom) { // scroll to the bottom
+ this.setState({isLoadingMore: true}, () => {
+ this.getMore();
+ });
+ }
+ }
+ };
+
+ getMore = () => {
+ const { page, shareLinks } = this.state;
+ const { repoID, itemPath: path } = this.props;
+ seafileAPI.listShareLinks({repoID, path, page: page + 1}).then((res) => {
+ this.setState({
+ isLoadingMore: false,
+ hasMore: res.data.length == PER_PAGE,
+ page: page + 1,
+ shareLinks: shareLinks.concat(res.data.map(item => new ShareLink(item)))
+ });
+ }).catch(error => {
+ this.setState({
+ isLoadingMore: false
+ });
+ let errMessage = Utils.getErrorMsg(error);
+ toaster.danger(errMessage);
+ });
+ };
+
render() {
if (this.state.isLoading) {
return ;
}
const { repoID, itemPath, userPerm } = this.props;
- const { mode, shareLinks, sharedLinkInfo, permissionOptions, currentPermission } = this.state;
+ const { mode, shareLinks, sharedLinkInfo, permissionOptions, currentPermission, isLoadingMore } = this.state;
switch (mode) {
case 'displayLinkDetails':
@@ -242,6 +281,8 @@ class ShareLinkPanel extends React.Component {
toggleSelectLink={this.toggleSelectLink}
deleteShareLinks={this.deleteShareLinks}
deleteLink={this.deleteLink}
+ handleScroll={this.handleScroll}
+ isLoadingMore={isLoadingMore}
/>
);
}
diff --git a/frontend/src/components/share-link-panel/link-list.js b/frontend/src/components/share-link-panel/link-list.js
index 5736a42e1f9..8c04d7eba5e 100644
--- a/frontend/src/components/share-link-panel/link-list.js
+++ b/frontend/src/components/share-link-panel/link-list.js
@@ -4,6 +4,7 @@ import { gettext, siteRoot } from '../../utils/constants';
import EmptyTip from '../empty-tip';
import LinkItem from './link-item';
import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
+import Loading from '../../components/loading';
const propTypes = {
shareLinks: PropTypes.array.isRequired,
@@ -13,7 +14,9 @@ const propTypes = {
toggleSelectAllLinks: PropTypes.func.isRequired,
toggleSelectLink: PropTypes.func.isRequired,
deleteLink: PropTypes.func.isRequired,
- deleteShareLinks: PropTypes.func.isRequired
+ deleteShareLinks: PropTypes.func.isRequired,
+ isLoadingMore: PropTypes.bool.isRequired,
+ handleScroll: PropTypes.func.isRequired
};
class LinkList extends React.Component {
@@ -46,7 +49,7 @@ class LinkList extends React.Component {
};
render() {
- const { shareLinks, permissionOptions } = this.props;
+ const { shareLinks, permissionOptions, isLoadingMore, handleScroll } = this.props;
const selectedLinks = shareLinks.filter(item => item.isSelected);
const isAllLinksSelected = shareLinks.length == selectedLinks.length;
@@ -88,7 +91,7 @@ class LinkList extends React.Component {
-
+
@@ -114,6 +117,7 @@ class LinkList extends React.Component {
})}
+ {isLoadingMore &&
}
)}
diff --git a/frontend/src/pages/markdown-editor/editor-api.js b/frontend/src/pages/markdown-editor/editor-api.js
index 7b9b9c44b87..99d4bfa90b0 100644
--- a/frontend/src/pages/markdown-editor/editor-api.js
+++ b/frontend/src/pages/markdown-editor/editor-api.js
@@ -129,10 +129,6 @@ class EditorApi {
return seafileAPI.getInternalLink(repoID, filePath);
}
- getShareLink() {
- return seafileAPI.getShareLink(repoID, filePath);
- }
-
createShareLink (repoID, filePath, userPassword, userValidDays, permissions) {
return seafileAPI.createShareLink(repoID, filePath, userPassword, userValidDays, permissions);
}
diff --git a/frontend/src/pages/share-admin/share-links.js b/frontend/src/pages/share-admin/share-links.js
index 488fb367f36..0d5050a4a7c 100644
--- a/frontend/src/pages/share-admin/share-links.js
+++ b/frontend/src/pages/share-admin/share-links.js
@@ -19,6 +19,7 @@ import Selector from '../../components/single-selector';
const contentPropTypes = {
loading: PropTypes.bool.isRequired,
+ isLoadingMore: PropTypes.bool.isRequired,
errorMsg: PropTypes.string.isRequired,
items: PropTypes.array.isRequired,
sortBy: PropTypes.string.isRequired,
@@ -114,7 +115,12 @@ class Content extends Component {
);
- return items.length ? table : emptyTip;
+ return items.length ? (
+ <>
+ {table}
+ {this.props.isLoadingMore &&
}
+ >
+ ) : emptyTip;
}
}
}
@@ -360,6 +366,8 @@ const propTypes = {
onSearchedClick: PropTypes.func.isRequired
};
+const PER_PAGE = 25;
+
class ShareAdminShareLinks extends Component {
constructor(props) {
@@ -367,6 +375,9 @@ class ShareAdminShareLinks extends Component {
this.state = {
isCleanInvalidShareLinksDialogOpen: false,
loading: true,
+ hasMore: false,
+ isLoadingMore: false,
+ page: 1,
errorMsg: '',
items: [],
sortBy: 'name', // 'name' or 'time'
@@ -437,12 +448,14 @@ class ShareAdminShareLinks extends Component {
}
listUserShareLinks() {
- seafileAPI.listUserShareLinks().then((res) => {
+ const { page } = this.state;
+ seafileAPI.listShareLinks({ page }).then((res) => {
let items = res.data.map(item => {
return new ShareLink(item);
});
this.setState({
loading: false,
+ hasMore: res.data.length == PER_PAGE,
items: this._sortItems(items, this.state.sortBy, this.state.sortOrder)
});
}).catch((error) => {
@@ -453,6 +466,40 @@ class ShareAdminShareLinks extends Component {
});
}
+ handleScroll = (event) => {
+ if (!this.state.isLoadingMore && this.state.hasMore) {
+ const clientHeight = event.target.clientHeight;
+ const scrollHeight = event.target.scrollHeight;
+ const scrollTop = event.target.scrollTop;
+ const isBottom = (clientHeight + scrollTop + 1 >= scrollHeight);
+ if (isBottom) { // scroll to the bottom
+ this.setState({isLoadingMore: true}, () => {
+ this.getMore();
+ });
+ }
+ }
+ };
+
+ getMore = () => {
+ const { page } = this.state;
+ seafileAPI.listShareLinks({ page: page + 1 }).then((res) => {
+ let moreItems = res.data.map(item => {
+ return new ShareLink(item);
+ });
+ this.setState({
+ isLoadingMore: false,
+ hasMore: res.data.length == PER_PAGE,
+ page: page + 1,
+ items: this._sortItems(this.state.items.concat(moreItems), this.state.sortBy, this.state.sortOrder)
+ });
+ }).catch((error) => {
+ this.setState({
+ isLoadingMore: false,
+ errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
+ });
+ });
+ };
+
onRemoveLink = (item) => {
seafileAPI.deleteShareLink(item.token).then(() => {
let items = this.state.items.filter(uploadItem => {
@@ -511,9 +558,10 @@ class ShareAdminShareLinks extends Component {
{(!Utils.isDesktop() && this.state.items.length > 0) && }
-