mirror of
https://github.com/wassname/talk.git
synced 2026-07-05 00:35:22 +08:00
Integrate react-virtualized
This commit is contained in:
@@ -21,10 +21,11 @@ class CommentDetails extends Component {
|
||||
this.setState((state) => ({
|
||||
showDetail: !state.showDetail
|
||||
}));
|
||||
this.props.clearHeightCache && this.props.clearHeightCache();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {data, root, comment} = this.props;
|
||||
const {data, root, comment, clearHeightCache} = this.props;
|
||||
const {showDetail} = this.state;
|
||||
const queryData = {
|
||||
root,
|
||||
@@ -44,12 +45,14 @@ class CommentDetails extends Component {
|
||||
<Slot
|
||||
fill="adminCommentDetailArea"
|
||||
data={data}
|
||||
clearHeightCache={clearHeightCache}
|
||||
queryData={queryData}
|
||||
more={showDetail}
|
||||
/>
|
||||
{showDetail && <Slot
|
||||
fill="adminCommentMoreDetails"
|
||||
data={data}
|
||||
clearHeightCache={clearHeightCache}
|
||||
queryData={queryData}
|
||||
/>}
|
||||
</div>
|
||||
@@ -61,6 +64,7 @@ CommentDetails.propTypes = {
|
||||
data: PropTypes.object.isRequired,
|
||||
root: PropTypes.object.isRequired,
|
||||
comment: PropTypes.object.isRequired,
|
||||
clearHeightCache: PropTypes.func,
|
||||
};
|
||||
|
||||
export default CommentDetails;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
position: relative;
|
||||
transition: all 200ms;
|
||||
padding: 10px 0;
|
||||
margin-top: 13px;
|
||||
min-height: 0;
|
||||
|
||||
/*
|
||||
|
||||
@@ -65,6 +65,7 @@ class Comment extends React.Component {
|
||||
root: {settings},
|
||||
currentUserId,
|
||||
currentAsset,
|
||||
clearHeightCache,
|
||||
} = this.props;
|
||||
|
||||
const selectionStateCSS = selected ? 'mdl-shadow--16dp' : 'mdl-shadow--2dp';
|
||||
@@ -114,6 +115,7 @@ class Comment extends React.Component {
|
||||
<Slot
|
||||
fill="adminCommentInfoBar"
|
||||
data={data}
|
||||
clearHeightCache={clearHeightCache}
|
||||
queryData={queryData}
|
||||
/>
|
||||
</div>
|
||||
@@ -145,6 +147,7 @@ class Comment extends React.Component {
|
||||
<Slot
|
||||
fill="adminCommentContent"
|
||||
data={data}
|
||||
clearHeightCache={clearHeightCache}
|
||||
queryData={queryData}
|
||||
/>
|
||||
<div className={styles.sideActions}>
|
||||
@@ -166,6 +169,7 @@ class Comment extends React.Component {
|
||||
<Slot
|
||||
fill="adminSideActions"
|
||||
data={data}
|
||||
clearHeightCache={clearHeightCache}
|
||||
queryData={queryData}
|
||||
/>
|
||||
</div>
|
||||
@@ -176,6 +180,7 @@ class Comment extends React.Component {
|
||||
data={data}
|
||||
root={root}
|
||||
comment={comment}
|
||||
clearHeightCache={clearHeightCache}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
@@ -191,6 +196,7 @@ Comment.propTypes = {
|
||||
showBanUserDialog: PropTypes.func.isRequired,
|
||||
showSuspendUserDialog: PropTypes.func.isRequired,
|
||||
currentUserId: PropTypes.string.isRequired,
|
||||
clearHeightCache: PropTypes.func,
|
||||
comment: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
status: PropTypes.string.isRequired,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.root {
|
||||
height: 100%;
|
||||
}
|
||||
@@ -2,7 +2,10 @@
|
||||
padding: 8px 0;
|
||||
list-style: none;
|
||||
display: block;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
:global(html) {
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.list {
|
||||
@@ -15,12 +18,12 @@
|
||||
}
|
||||
|
||||
.commentLeaveActive {
|
||||
opacity: 0;
|
||||
opacity: 0.1;
|
||||
transition: opacity 800ms;
|
||||
}
|
||||
|
||||
.commentEnter {
|
||||
opacity: 0;
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.commentEnterActive {
|
||||
|
||||
@@ -7,7 +7,8 @@ import EmptyCard from '../../../components/EmptyCard';
|
||||
import LoadMore from '../../../components/LoadMore';
|
||||
import ViewMore from './ViewMore';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
import {CSSTransitionGroup} from 'react-transition-group';
|
||||
import {WindowScroller, CellMeasurer, CellMeasurerCache, List} from 'react-virtualized';
|
||||
import throttle from 'lodash/throttle';
|
||||
|
||||
const hasComment = (nodes, id) => nodes.some((node) => node.id === id);
|
||||
|
||||
@@ -43,14 +44,37 @@ function invalidateCursor(invalidated, state, props) {
|
||||
return {idCursors};
|
||||
}
|
||||
|
||||
let keyMapper = null;
|
||||
|
||||
// In this example, average cell height is assumed to be about 50px.
|
||||
// This value will be used for the initial `Grid` layout.
|
||||
// Width is not dynamic.
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
defaultHeight: 250,
|
||||
keyMapper: (index) => keyMapper(index),
|
||||
});
|
||||
|
||||
class ModerationQueue extends React.Component {
|
||||
isLoadingMore = false;
|
||||
cache = cache;
|
||||
listRef = null;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
...resetCursors(this.state, props),
|
||||
};
|
||||
keyMapper = (index) => {
|
||||
const view = this.getVisibleComments();
|
||||
if (index < view.length) {
|
||||
return view[index].id;
|
||||
}
|
||||
else if (index === view.length) {
|
||||
return 'loadMore';
|
||||
}
|
||||
throw new Error(`unknown index ${index}`);
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate (prev) {
|
||||
@@ -64,6 +88,10 @@ class ModerationQueue extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleListRef = (list) => {
|
||||
this.listRef = list;
|
||||
};
|
||||
|
||||
componentWillReceiveProps(next) {
|
||||
const {comments: prevComments} = this.props;
|
||||
const {comments: nextComments} = next;
|
||||
@@ -94,6 +122,11 @@ class ModerationQueue extends React.Component {
|
||||
this.setState(resetCursors);
|
||||
};
|
||||
|
||||
reflowList = throttle(() => {
|
||||
this.cache.clearAll();
|
||||
this.listRef.recomputeRowHeights();
|
||||
}, 500);
|
||||
|
||||
// getVisibileComments returns a list containing comments
|
||||
// which comes after the `idCursor`.
|
||||
getVisibleComments() {
|
||||
@@ -117,14 +150,68 @@ class ModerationQueue extends React.Component {
|
||||
return view;
|
||||
}
|
||||
|
||||
rowRenderer = ({
|
||||
index, // Index of row within collection
|
||||
parent,
|
||||
style // Style object to be applied to row (to position it)
|
||||
}) => {
|
||||
if (index === parent.props.rowCount - 1) {
|
||||
return (
|
||||
<CellMeasurer
|
||||
cache={this.cache}
|
||||
columnIndex={0}
|
||||
key={'loadMore'}
|
||||
parent={parent}
|
||||
rowIndex={index}
|
||||
>
|
||||
<div
|
||||
style={style}
|
||||
>
|
||||
<LoadMore
|
||||
loadMore={this.props.loadMore}
|
||||
showLoadMore={this.props.comments.length < this.props.commentCount}
|
||||
/>
|
||||
</div>
|
||||
</CellMeasurer>
|
||||
);
|
||||
}
|
||||
const comment = this.props.comments[index];
|
||||
return (
|
||||
<CellMeasurer
|
||||
cache={this.cache}
|
||||
columnIndex={0}
|
||||
key={comment.id}
|
||||
parent={parent}
|
||||
rowIndex={index}
|
||||
>
|
||||
<div
|
||||
style={style}
|
||||
>
|
||||
<Comment
|
||||
data={this.props.data}
|
||||
root={this.props.root}
|
||||
comment={comment}
|
||||
selected={comment.id === this.props.selectedCommentId}
|
||||
viewUserDetail={this.props.viewUserDetail}
|
||||
showBanUserDialog={this.props.showBanUserDialog}
|
||||
showSuspendUserDialog={this.props.showSuspendUserDialog}
|
||||
acceptComment={this.props.acceptComment}
|
||||
rejectComment={this.props.rejectComment}
|
||||
currentAsset={this.props.currentAsset}
|
||||
currentUserId={this.props.currentUserId}
|
||||
clearHeightCache={() => {this.cache.clear(index); this.listRef.recomputeRowHeights(index); }}
|
||||
/>
|
||||
</div>
|
||||
</CellMeasurer>
|
||||
);
|
||||
};
|
||||
|
||||
render () {
|
||||
const {
|
||||
comments,
|
||||
selectedCommentId,
|
||||
commentCount,
|
||||
singleView,
|
||||
viewUserDetail,
|
||||
activeTab,
|
||||
...props
|
||||
} = this.props;
|
||||
|
||||
@@ -167,46 +254,27 @@ class ModerationQueue extends React.Component {
|
||||
viewMore={this.viewNewComments}
|
||||
count={comments.length - view.length}
|
||||
/>
|
||||
<CSSTransitionGroup
|
||||
key={activeTab}
|
||||
component={'ul'}
|
||||
className={styles.list}
|
||||
transitionName={{
|
||||
enter: styles.commentEnter,
|
||||
enterActive: styles.commentEnterActive,
|
||||
leave: styles.commentLeave,
|
||||
leaveActive: styles.commentLeaveActive,
|
||||
}}
|
||||
transitionEnter={true}
|
||||
transitionLeave={true}
|
||||
transitionEnterTimeout={1000}
|
||||
transitionLeaveTimeout={1000}
|
||||
>
|
||||
{
|
||||
view
|
||||
.map((comment) => {
|
||||
return <Comment
|
||||
data={this.props.data}
|
||||
root={this.props.root}
|
||||
key={comment.id}
|
||||
comment={comment}
|
||||
selected={comment.id === selectedCommentId}
|
||||
viewUserDetail={viewUserDetail}
|
||||
showBanUserDialog={props.showBanUserDialog}
|
||||
showSuspendUserDialog={props.showSuspendUserDialog}
|
||||
acceptComment={props.acceptComment}
|
||||
rejectComment={props.rejectComment}
|
||||
currentAsset={props.currentAsset}
|
||||
currentUserId={this.props.currentUserId}
|
||||
/>;
|
||||
})
|
||||
}
|
||||
</CSSTransitionGroup>
|
||||
|
||||
<LoadMore
|
||||
loadMore={this.props.loadMore}
|
||||
showLoadMore={comments.length < commentCount}
|
||||
/>
|
||||
<WindowScroller onResize={this.reflowList}>
|
||||
{({height, isScrolling, onChildScroll, scrollTop}) => (
|
||||
<List
|
||||
ref={this.handleListRef}
|
||||
autoHeight
|
||||
style={{
|
||||
width: '100%',
|
||||
outline: 'none',
|
||||
}}
|
||||
height={height}
|
||||
width={1280}
|
||||
scrollTop={scrollTop}
|
||||
isScrolling={isScrolling}
|
||||
onScroll={onChildScroll}
|
||||
rowCount={view.length + 1}
|
||||
deferredMeasurementCache={this.cache}
|
||||
rowRenderer={this.rowRenderer}
|
||||
rowHeight={this.cache.rowHeight}
|
||||
/>
|
||||
)}
|
||||
</WindowScroller>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
overflow: visible;
|
||||
height: 144px;
|
||||
min-height: auto;
|
||||
margin-top: 16px;
|
||||
z-index: 10;
|
||||
|
||||
@media (--tablet) {
|
||||
|
||||
@@ -178,7 +178,7 @@ class ModerationContainer extends Component {
|
||||
|
||||
loadMore = (tab) => {
|
||||
const variables = {
|
||||
limit: 10,
|
||||
limit: 50,
|
||||
cursor: this.props.root[tab].endCursor,
|
||||
sortOrder: this.props.data.variables.sortOrder,
|
||||
asset_id: this.props.data.variables.asset_id,
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
"react-test-renderer": "15.5",
|
||||
"react-toastify": "^1.5.0",
|
||||
"react-transition-group": "^1.1.3",
|
||||
"react-virtualized": "9.9.0",
|
||||
"recompose": "^0.23.1",
|
||||
"redux": "^3.6.0",
|
||||
"redux-thunk": "^2.1.0",
|
||||
|
||||
@@ -1020,7 +1020,7 @@ babel-register@^6.26.0:
|
||||
mkdirp "^0.5.1"
|
||||
source-map-support "^0.4.15"
|
||||
|
||||
babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.6.1:
|
||||
babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.6.1:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
dependencies:
|
||||
@@ -2402,7 +2402,7 @@ doctypes@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9"
|
||||
|
||||
dom-helpers@^3.2.0:
|
||||
"dom-helpers@^2.4.0 || ^3.0.0", dom-helpers@^3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
|
||||
|
||||
@@ -5451,7 +5451,7 @@ longest@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
||||
dependencies:
|
||||
@@ -7464,6 +7464,16 @@ react-transition-group@^1.1.2, react-transition-group@^1.1.3:
|
||||
prop-types "^15.5.6"
|
||||
warning "^3.0.0"
|
||||
|
||||
react-virtualized@9.9.0:
|
||||
version "9.9.0"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.9.0.tgz#799a6f23819eeb82860d59b82fad33d1d420325e"
|
||||
dependencies:
|
||||
babel-runtime "^6.11.6"
|
||||
classnames "^2.2.3"
|
||||
dom-helpers "^2.4.0 || ^3.0.0"
|
||||
loose-envify "^1.3.0"
|
||||
prop-types "^15.5.4"
|
||||
|
||||
react@^15.3.1, react@^15.4.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
|
||||
|
||||
Reference in New Issue
Block a user