1 |
|
2 |
|
3 |
|
4 | type Settings = {
|
5 | key: string;
|
6 | rowsPerLoad: number;
|
7 | manipulateItems: (items: any) => void;
|
8 | resetView: (data : any) => void;
|
9 | changeView: (data: any) => void;
|
10 | requestPage: (page: string, token : string) => any;
|
11 | }
|
12 |
|
13 | type OwnProps = {
|
14 | token: string;
|
15 | settings: Settings;
|
16 | };
|
17 |
|
18 | type LoadPaginatorPropTypes = {
|
19 | token: string;
|
20 | children: any;
|
21 | settings: Settings;
|
22 | paginator: any;
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | changeView: (info : { token: string, view: number }) => void;
|
34 | resetView: (token : string) => void;
|
35 |
|
36 | loadData: (page : number) => void;
|
37 | changeFilter: (filter : string) => (event : { target : { value : string } }) => void;
|
38 | };
|
39 |
|
40 | import React, { Component } from "react";
|
41 | import * as Immutable from "immutable";
|
42 | import { connect } from "react-redux";
|
43 | import { bindActionCreators } from "redux";
|
44 |
|
45 | import selectors from "./selectors";
|
46 |
|
47 | import LoadingButton from "./LoadingButton";
|
48 |
|
49 | import { LargeErrorMessage, LoadingMessage } from "x25/Messages";
|
50 | import words from "./words";
|
51 |
|
52 | const mapStateToProps = (state, { token, settings } : OwnProps) => {
|
53 |
|
54 | const { list, entities } = selectors.getPaginators(state, settings.key);
|
55 |
|
56 | const
|
57 | currentPage = selectors.getCurrentView(list, token),
|
58 | isFetching = selectors.isPageFetching(list, token, currentPage),
|
59 | isFetched = selectors.isPageFetched(list, token, currentPage),
|
60 | isNextFetching = selectors.isPageFetching(list, token, currentPage + 1),
|
61 | isNextFetched = selectors.isPageFetched(list, token, currentPage + 1),
|
62 | resultsUpTo = (
|
63 | selectors.getResultsUpToPage(list, token, entities, currentPage, settings.rowsPerLoad)
|
64 | ),
|
65 | items = settings.manipulateItems(resultsUpTo),
|
66 | total = selectors.getCurrentTotalResultsCount(list, token),
|
67 | hasProblems = selectors.hasPageProblems(list, token, currentPage);
|
68 |
|
69 | const shouldLoadNext = !isNextFetched && !isNextFetching;
|
70 |
|
71 | return {
|
72 | paginator: Immutable.Map({
|
73 | currentPage,
|
74 | hasProblems,
|
75 | isFetched,
|
76 | isFetching,
|
77 | items,
|
78 | shouldLoadNext,
|
79 | total,
|
80 | }),
|
81 | };
|
82 | },
|
83 | mapDispatchToProps = (dispatch, { token, settings } : OwnProps) => ({
|
84 | ...bindActionCreators({
|
85 | resetView : settings.resetView,
|
86 | changeView : settings.changeView,
|
87 | }, dispatch),
|
88 | loadData (page) {
|
89 | dispatch(settings.requestPage(page, token));
|
90 | },
|
91 | });
|
92 |
|
93 | class LoadPaginator extends Component<LoadPaginatorPropTypes> {
|
94 |
|
95 | handleLoadMoreClick: () => void;
|
96 |
|
97 | constructor (props : LoadPaginatorPropTypes) {
|
98 | super(props);
|
99 |
|
100 | this.handleLoadMoreClick = () => {
|
101 | const { paginator, token, changeView, loadData } = this.props;
|
102 |
|
103 | const
|
104 | currentPage = paginator.get("currentPage"),
|
105 | hasProblems = paginator.get("hasProblems"),
|
106 | shouldLoadNext = paginator.get("shouldLoadNext");
|
107 |
|
108 | if (hasProblems) {
|
109 | loadData(currentPage);
|
110 | } else {
|
111 | changeView({
|
112 | token,
|
113 | view: currentPage + 1,
|
114 | });
|
115 |
|
116 | if (shouldLoadNext) {
|
117 | loadData(currentPage + 1);
|
118 | }
|
119 | }
|
120 | };
|
121 | }
|
122 |
|
123 | UNSAFE_componentWillMount () {
|
124 | const { paginator, loadData } = this.props;
|
125 |
|
126 | const
|
127 | isFetched = paginator.get("isFetched"),
|
128 | isFetching = paginator.get("isFetching"),
|
129 | currentPage = paginator.get("currentPage");
|
130 |
|
131 | const shouldFetch = !isFetched && !isFetching;
|
132 |
|
133 | if (shouldFetch) {
|
134 | loadData(currentPage);
|
135 | }
|
136 | }
|
137 |
|
138 | UNSAFE_componentWillReceiveProps (nextProps) {
|
139 | const { loadData, paginator } = nextProps;
|
140 |
|
141 | const
|
142 | isFetched = paginator.get("isFetched"),
|
143 | isFetching = paginator.get("isFetching"),
|
144 | currentPage = paginator.get("currentPage");
|
145 |
|
146 | const shouldFetch = !isFetched && !isFetching;
|
147 |
|
148 | if (shouldFetch) {
|
149 | loadData(currentPage);
|
150 | }
|
151 | }
|
152 |
|
153 | shouldComponentUpdate (nextProps : LoadPaginatorPropTypes) {
|
154 | return (
|
155 | this.props.paginator !== nextProps.paginator
|
156 | );
|
157 | }
|
158 |
|
159 | componentWillUnmount () {
|
160 | this.props.resetView(this.props.token);
|
161 | }
|
162 |
|
163 | render () {
|
164 | const { paginator } = this.props;
|
165 |
|
166 | const
|
167 | items = paginator.get("items"),
|
168 | hasProblems = paginator.get("hasProblems"),
|
169 | currentPage = paginator.get("currentPage"),
|
170 | isFetching = paginator.get("isFetching"),
|
171 | total = paginator.get("total");
|
172 |
|
173 | const
|
174 | isEmpty = items.size === 0,
|
175 | showLoading = total !== items.size;
|
176 |
|
177 | if (isEmpty && isFetching) {
|
178 | return (
|
179 | <LoadingMessage message={words.LoadingData} />
|
180 | );
|
181 | }
|
182 |
|
183 | if (hasProblems && currentPage === 1) {
|
184 | return (
|
185 | <LargeErrorMessage
|
186 | message={words.ThereWasAProblem}
|
187 | onRetry={this.handleLoadMoreClick}
|
188 | />
|
189 | );
|
190 | }
|
191 |
|
192 | return (
|
193 | <React.Fragment>
|
194 | {React.cloneElement(this.props.children, this.props)}
|
195 | {
|
196 | showLoading ? (
|
197 | <LoadingButton
|
198 | hasProblems={hasProblems}
|
199 | isFetching={isFetching}
|
200 | onLoadMoreClick={this.handleLoadMoreClick}
|
201 | />
|
202 | ) : null
|
203 | }
|
204 | </React.Fragment>
|
205 | );
|
206 | }
|
207 | }
|
208 |
|
209 | export default connect(mapStateToProps, mapDispatchToProps)(LoadPaginator);
|