mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-17 08:31:23 -05:00
Move activity loading to studio-activity-actions, include pagination
This commit is contained in:
parent
dbec1c7a97
commit
600e5846d0
3 changed files with 61 additions and 23 deletions
|
@ -1,9 +0,0 @@
|
|||
// TODO move this to studio-activity-actions, include pagination
|
||||
const activityFetcher = studioId =>
|
||||
fetch(`${process.env.API_HOST}/studios/${studioId}/activity`)
|
||||
.then(response => response.json())
|
||||
.then(data => ({items: data, moreToLoad: false})); // No pagination on the activity feed
|
||||
|
||||
export {
|
||||
activityFetcher
|
||||
};
|
43
src/views/studio/lib/studio-activity-actions.js
Normal file
43
src/views/studio/lib/studio-activity-actions.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
import keyMirror from 'keymirror';
|
||||
|
||||
import api from '../../../lib/api';
|
||||
import {activity} from './redux-modules';
|
||||
import {selectStudioId} from '../../../redux/studio';
|
||||
|
||||
const Errors = keyMirror({
|
||||
NETWORK: null,
|
||||
SERVER: null,
|
||||
PERMISSION: null
|
||||
});
|
||||
|
||||
const normalizeError = (err, body, res) => {
|
||||
if (err) return Errors.NETWORK;
|
||||
if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION;
|
||||
if (res.statusCode !== 200) return Errors.SERVER;
|
||||
return null;
|
||||
};
|
||||
|
||||
const loadActivity = () => ((dispatch, getState) => {
|
||||
const state = getState();
|
||||
const studioId = selectStudioId(state);
|
||||
const items = activity.selector(state).items;
|
||||
const params = {limit: 20};
|
||||
if (items.length > 0) {
|
||||
// dateLimit is the newest notification you want to get back, which is
|
||||
// the date of the oldest one we've already loaded
|
||||
params.dateLimit = items[items.length - 1].datetime_created;
|
||||
}
|
||||
api({
|
||||
uri: `/studios/${studioId}/activity/`,
|
||||
params
|
||||
}, (err, body, res) => {
|
||||
const error = normalizeError(err, body, res);
|
||||
if (error) return dispatch(activity.actions.error(error));
|
||||
const ids = items.map(item => item.id);
|
||||
// Deduplication is needed because pagination based on date can contain duplicates
|
||||
const deduped = body.filter(item => ids.indexOf(item.id) === -1);
|
||||
dispatch(activity.actions.append(deduped, body.length === params.limit));
|
||||
});
|
||||
});
|
||||
|
||||
export {loadActivity};
|
|
@ -2,20 +2,15 @@ import React, {useEffect} from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import {useParams} from 'react-router';
|
||||
|
||||
import {activity} from './lib/redux-modules';
|
||||
import {activityFetcher} from './lib/fetchers';
|
||||
import {loadActivity} from './lib/studio-activity-actions';
|
||||
import Debug from './debug.jsx';
|
||||
|
||||
const StudioActivity = ({items, loading, error, onInitialLoad}) => {
|
||||
const {studioId} = useParams();
|
||||
// Fetch the data if none has been loaded yet. This would run only once,
|
||||
// since studioId doesnt change, but the component is potentially mounted
|
||||
// multiple times because of tab routing, so need to check for empty items.
|
||||
const StudioActivity = ({items, loading, error, moreToLoad, onLoadMore}) => {
|
||||
useEffect(() => {
|
||||
if (studioId && items.length === 0) onInitialLoad(studioId);
|
||||
}, [studioId]); // items.length intentionally left out
|
||||
if (items.length === 0) onLoadMore();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -33,6 +28,15 @@ const StudioActivity = ({items, loading, error, onInitialLoad}) => {
|
|||
key={index}
|
||||
/>)
|
||||
)}
|
||||
<div>
|
||||
{loading ? <small>Loading...</small> : (
|
||||
moreToLoad ?
|
||||
<button onClick={onLoadMore}>
|
||||
Load more
|
||||
</button> :
|
||||
<small>No more to load</small>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -42,13 +46,13 @@ StudioActivity.propTypes = {
|
|||
items: PropTypes.array, // eslint-disable-line react/forbid-prop-types
|
||||
loading: PropTypes.bool,
|
||||
error: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
onInitialLoad: PropTypes.func
|
||||
moreToLoad: PropTypes.bool,
|
||||
onLoadMore: PropTypes.func
|
||||
};
|
||||
|
||||
export default connect(
|
||||
state => activity.selector(state),
|
||||
dispatch => ({
|
||||
onInitialLoad: studioId => dispatch(
|
||||
activity.actions.loadMore(activityFetcher.bind(null, studioId, 0)))
|
||||
})
|
||||
{
|
||||
onLoadMore: loadActivity
|
||||
}
|
||||
)(StudioActivity);
|
||||
|
|
Loading…
Reference in a new issue