Unverified Commit bf73950c authored by Paul Kaplan's avatar Paul Kaplan Committed by GitHub

Merge pull request #5346 from paulkaplan/bugfix/studio-playground-may5

Bug fixes from studio playground test on May 5 2021
parents a9d70b2c 9904586a
......@@ -76,10 +76,11 @@ const InfiniteList = key => {
...state,
items: state.items.filter((_, i) => i !== action.index)
};
case `${key}_PREPEND`:
case `${key}_CREATE`:
return {
...state,
items: [action.item].concat(state.items)
items: action.atEnd ? state.items.concat([action.item]) :
[action.item].concat(state.items)
};
case `${key}_ERROR`:
return {
......@@ -94,7 +95,7 @@ const InfiniteList = key => {
};
const actions = {
create: item => ({type: `${key}_PREPEND`, item}),
create: (item, atEnd = false) => ({type: `${key}_CREATE`, item, atEnd}),
remove: index => ({type: `${key}_REMOVE`, index}),
replace: (index, item) => ({type: `${key}_REPLACE`, index, item}),
error: error => ({type: `${key}_ERROR`, error}),
......
......@@ -24,6 +24,8 @@ const Errors = keyMirror({
UNHANDLED: null
});
const MAX_IMAGE_BYTES = 524288;
const getInitialState = () => ({
mutationErrors: {}, // { [field]: <error>, ... }
isMutating: {} // { [field]: <boolean>, ... }
......@@ -171,10 +173,14 @@ const mutateFollowingStudio = shouldFollow => ((dispatch, getState) => {
});
const mutateStudioImage = input => ((dispatch, getState) => {
if (!input.files || !input.files[0]) return;
const state = getState();
const studioId = selectStudioId(state);
const currentImage = selectStudioImage(state);
dispatch(startMutation('image'));
if (input.files[0].size && input.files[0].size > MAX_IMAGE_BYTES) {
return dispatch(completeMutation('image', currentImage, Errors.THUMBNAIL_TOO_LARGE));
}
const formData = new FormData();
formData.append('file', input.files[0]);
api({
......
......@@ -8,13 +8,24 @@ import {selectStudioId, setRoles} from '../../../redux/studio';
const Errors = keyMirror({
NETWORK: null,
SERVER: null,
PERMISSION: null
PERMISSION: null,
DUPLICATE: null,
UNKNOWN_USERNAME: null,
RATE_LIMIT: null
});
const normalizeError = (err, body, res) => {
if (err) return Errors.NETWORK;
if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION;
if (res.statusCode === 404) return Errors.UNKNOWN_USERNAME;
if (res.statusCode === 429) return Errors.RATE_LIMIT;
if (res.statusCode !== 200) return Errors.SERVER;
if (body && body.status === 'error') {
if (body.message.indexOf('already a curator') !== -1) {
return Errors.DUPLICATE;
}
return Errors.UNHANDLED;
}
return null;
};
......@@ -132,7 +143,7 @@ const promoteCurator = username => ((dispatch, getState) => new Promise((resolve
const index = curatorList.findIndex(v => v.username === username);
const curatorItem = curatorList[index];
if (index !== -1) dispatch(curators.actions.remove(index));
dispatch(managers.actions.create(curatorItem));
dispatch(managers.actions.create(curatorItem, true));
return resolve();
});
}));
......@@ -156,7 +167,7 @@ const acceptInvitation = () => ((dispatch, getState) => new Promise((resolve, re
if (userError) return reject(userError);
// Note: this assumes that the user items from the curator endpoint
// are the same structure as the single user data returned from /users/:username
dispatch(curators.actions.create(userBody));
dispatch(curators.actions.create(userBody, true));
dispatch(setRoles({invited: false, curator: true}));
return resolve();
});
......
......@@ -9,12 +9,16 @@ import {projects} from './redux-modules';
const Errors = keyMirror({
NETWORK: null,
SERVER: null,
PERMISSION: null
PERMISSION: null,
UNKNOWN_PROJECT: null,
RATE_LIMIT: null
});
const normalizeError = (err, body, res) => {
if (err) return Errors.NETWORK;
if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION;
if (res.statusCode === 404) return Errors.UNKNOWN_PROJECT;
if (res.statusCode === 429) return Errors.RATE_LIMIT;
if (res.statusCode !== 200) return Errors.SERVER;
return null;
};
......
......@@ -11,7 +11,14 @@ const StudioCuratorInviter = ({onSubmit}) => {
const [value, setValue] = useState('');
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState(null);
const submit = () => {
setSubmitting(true);
setError(null);
onSubmit(value)
.then(() => setValue(''))
.catch(e => setError(e))
.then(() => setSubmitting(false));
};
return (
<div className="studio-adder-section">
<h3><FormattedMessage id="studio.inviteCuratorsHeader" /></h3>
......@@ -20,6 +27,7 @@ const StudioCuratorInviter = ({onSubmit}) => {
type="text"
placeholder="<username>"
value={value}
onKeyDown={e => e.key === 'Enter' && submit()}
onChange={e => setValue(e.target.value)}
/>
<button
......@@ -27,14 +35,7 @@ const StudioCuratorInviter = ({onSubmit}) => {
'mod-mutating': submitting
})}
disabled={submitting}
onClick={() => {
setSubmitting(true);
setError(null);
onSubmit(value)
.then(() => setValue(''))
.catch(e => setError(e))
.then(() => setSubmitting(false));
}}
onClick={submit}
><FormattedMessage id="studio.inviteCurator" /></button>
{error && <div>{error}</div>}
</div>
......
......@@ -11,7 +11,14 @@ const StudioProjectAdder = ({onSubmit}) => {
const [value, setValue] = useState('');
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState(null);
const submit = () => {
setSubmitting(true);
setError(null);
onSubmit(value)
.then(() => setValue(''))
.catch(e => setError(e))
.then(() => setSubmitting(false));
};
return (
<div className="studio-adder-section">
<h3><FormattedMessage id="studio.addProjectsHeader" /></h3>
......@@ -20,6 +27,7 @@ const StudioProjectAdder = ({onSubmit}) => {
type="text"
placeholder="<project id>"
value={value}
onKeyDown={e => e.key === 'Enter' && submit()}
onChange={e => setValue(e.target.value)}
/>
<button
......@@ -27,14 +35,7 @@ const StudioProjectAdder = ({onSubmit}) => {
'mod-mutating': submitting
})}
disabled={submitting}
onClick={() => {
setSubmitting(true);
setError(null);
onSubmit(value)
.then(() => setValue(''))
.catch(e => setError(e))
.then(() => setSubmitting(false));
}}
onClick={submit}
><FormattedMessage id="studio.addProject" /></button>
{error && <div>{error}</div>}
</div>
......
......@@ -4,8 +4,8 @@ import SubNavigation from '../../components/subnavigation/subnavigation.jsx';
import {FormattedMessage} from 'react-intl';
const StudioTabNav = () => {
const match = useRouteMatch();
const {params: {studioPath, studioId}} = useRouteMatch();
const base = `/${studioPath}/${studioId}`;
return (
<SubNavigation
align="left"
......@@ -13,26 +13,26 @@ const StudioTabNav = () => {
>
<NavLink
activeClassName="active"
to={`${match.url}`}
to={base}
exact
>
<li><FormattedMessage id="studio.tabNavProjects" /></li>
</NavLink>
<NavLink
activeClassName="active"
to={`${match.url}/curators`}
to={`${base}/comments`}
>
<li><FormattedMessage id="studio.tabNavCurators" /></li>
<li><FormattedMessage id="studio.tabNavComments" /></li>
</NavLink>
<NavLink
activeClassName="active"
to={`${match.url}/comments`}
to={`${base}/curators`}
>
<li><FormattedMessage id="studio.tabNavComments" /></li>
<li><FormattedMessage id="studio.tabNavCurators" /></li>
</NavLink>
<NavLink
activeClassName="active"
to={`${match.url}/activity`}
to={`${base}/activity`}
>
<li><FormattedMessage id="studio.tabNavActivity" /></li>
</NavLink>
......
......@@ -35,7 +35,7 @@ $radius: 8px;
.studio-info {
justify-self: center;
width: 300px;
height: fit-content;
height: max-content;
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 20px;
......
......@@ -93,15 +93,18 @@ describe('Infinite List redux module', () => {
});
describe('CREATE', () => {
let action;
beforeEach(() => {
action = module.actions.create(7);
});
test('prepends the given item', () => {
test('prepends the given item by default', () => {
const action = module.actions.create(7);
initialState.items = [8, 9, 10, 11];
const newState = module.reducer(initialState, action);
expect(newState.items).toEqual([7, 8, 9, 10, 11]);
});
test('appends the given item if given `atEnd` arg', () => {
const action = module.actions.create(7, true);
initialState.items = [8, 9, 10, 11];
const newState = module.reducer(initialState, action);
expect(newState.items).toEqual([8, 9, 10, 11, 7]);
});
});
describe('ERROR', () => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment