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