Skip to content

Commit 787f445

Browse files
committed
Fixed Navbar links for react router
* project page reloads request appropriate data * project page use selectors for redux store * cards use ids to load project page easier
1 parent 132b339 commit 787f445

File tree

15 files changed

+284
-131
lines changed

15 files changed

+284
-131
lines changed

src/components/app/actions/project.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,13 @@ export const getProjects = createAction('GET_PROJECTS', async () => {
1212
const response = await db.getAll('projects');
1313
return normalize(response, [ schema.project ]);
1414
});
15+
16+
export const getProject = createAction('GET_PROJECT', async (pid) => {
17+
const response = await db.get('projects')(pid);
18+
return normalize(response, schema.project);
19+
});
20+
21+
export const updateProject = createAction('UPDATE_PROJECT', async ({ pid, values }) => {
22+
await db.update('projects')(pid)(values);
23+
return normalize({ ...values }, schema.project);
24+
});

src/components/app/actions/tag.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,25 @@ import { normalize } from 'normalizr';
22
import { createAction } from 'redux-actions';
33

44
import { schema } from '../../../utils';
5-
import { db } from '../../../firebase';
5+
import { db, Project } from '../../../firebase';
66

77
export const getTags = createAction('GET_TAGS', async () => {
88
const response = await db.getAll('tags');
99
return normalize(response, [ schema.tag ]);
1010
});
11+
12+
export const getTag = createAction('GET_TAG', async (tid) => {
13+
const response = await db.get('tags')(tid);
14+
return normalize(response, schema.tag);
15+
});
16+
17+
export const updateTags = createAction('UPDATE_TAGS', async ({ pid, tags, type }) => {
18+
await Project.dropTags(pid)(type);
19+
await Promise.all(
20+
tags.map(async (tagId) => {
21+
await Project.updateFieldArrayWithId(db.add)('tags')(pid)(tagId);
22+
}),
23+
);
24+
25+
return normalize({ id: pid, tags }, schema.project);
26+
});

src/components/app/reducers/project.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { handleActions } from 'redux-actions';
22

3-
import { createProject, getProjects } from '../actions';
3+
import { createProject, getProjects, getProject, updateProject } from '../actions';
44

55
const reducer = handleActions(
66
{
@@ -20,6 +20,26 @@ const reducer = handleActions(
2020
...payload.entities.projects,
2121
}),
2222
},
23+
[getProject]: {
24+
FULFILLED: (state, { payload }) => {
25+
const id = payload.result;
26+
const values = payload.entities.projects[id];
27+
return {
28+
...state,
29+
[id]: { ...state[id], ...values },
30+
};
31+
},
32+
},
33+
[updateProject]: {
34+
FULFILLED: (state, { payload }) => {
35+
const id = payload.result;
36+
const values = payload.entities.projects[id];
37+
return {
38+
...state,
39+
[id]: { ...state[id], ...values },
40+
};
41+
},
42+
},
2343
},
2444
{},
2545
);

src/components/app/reducers/tag.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { handleActions } from 'redux-actions';
22

3-
import { getTags } from '../actions';
3+
import { getTags, getTag, updateTags } from '../actions';
44

55
const reducer = handleActions(
66
{
@@ -10,6 +10,26 @@ const reducer = handleActions(
1010
...payload.entities.tags,
1111
}),
1212
},
13+
[getTag]: {
14+
FULFILLED: (state, { payload }) => {
15+
const id = payload.result;
16+
const values = payload.entities.tags[id];
17+
return {
18+
...state,
19+
[id]: { ...state[id], ...values },
20+
};
21+
},
22+
},
23+
// [updateTags]: {
24+
// FULFILLED: (state, { payload }) => {
25+
// const id = payload.result;
26+
// const values = payload.entities.projects[id].tags;
27+
// return {
28+
// ...state,
29+
// [id]: { ...state[id], ...values },
30+
// };
31+
// },
32+
// },
1333
},
1434
{},
1535
);

src/components/explore-projects/components/project-list/ProjectList.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const renderProjects = projects =>
99
projects.map((p, i) => (
1010
<Col sm key={i}>
1111
<Card
12+
id={p.id}
1213
name={p.name}
1314
shortname={p.shortname}
1415
description={p.description}

src/components/home/Home.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const Home = ({ history }) => {
3737
{projects.slice(0, 3).map((p, i) => (
3838
<Col sm key={i}>
3939
<Card
40+
id={p.id}
4041
name={p.name}
4142
shortname={p.shortname}
4243
description={p.description}
@@ -56,6 +57,7 @@ const Home = ({ history }) => {
5657
{projects.slice(3, 6).map((p, i) => (
5758
<Col sm key={i}>
5859
<Card
60+
id={p.id}
5961
name={p.name}
6062
shortname={p.shortname}
6163
description={p.description}

src/components/project-page/ProjectPage.jsx

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,61 @@
11
import React, { useState, useEffect } from 'react';
2-
import styled from 'styled-components';
3-
import some from 'lodash/some';
2+
import { some, get, isNil } from 'lodash';
43
import { withRouter } from 'react-router-dom';
54

65
import { Row } from 'reactstrap';
7-
import { auth, db } from '../../firebase';
6+
import { useSelector, useDispatch } from 'react-redux';
7+
import { bindActionCreators } from 'redux';
8+
import { getProject } from '../app/actions';
9+
import { auth } from '../../firebase';
810

911
import Dashboard from './components/dashboard';
1012
import BannerContent from './components/banner-content';
1113
import ProjectInfoContent from './components/project-info-content';
1214
import SocialContentSection from './components/social-content-section';
1315
import EditProjectBanner from './components/edit-project-banner';
1416

15-
const LoadingView = styled.div`
16-
display: flex;
17-
height: 70vh;
18-
flex: 1;
19-
justify-content: center;
20-
align-items: center;
21-
`;
17+
const ProjectPage = ({ location }) => {
18+
const [ showDashboard, setShowDashboard ] = useState(false);
19+
const [ editable, setEditable ] = useState(false);
2220

23-
const ProjectPage = ({ match, location }) => {
24-
const [ loadingState, setLoadingState ] = useState(false);
25-
const [ showDashboardState, setShowDashboardState ] = useState(false);
26-
const [ projectDataState, setProjectDataState ] = useState({});
27-
const [ editableState, setEditableState ] = useState(false);
28-
const [ pidState, setPidState ] = useState(null);
21+
const pid = get(location, 'state.id', null);
22+
const project = useSelector(state => state.data.projects[pid]);
23+
const dispatch = useDispatch();
24+
const actions = bindActionCreators({ getProject }, dispatch);
2925

3026
useEffect(() => {
31-
const init = async () => {
32-
setLoadingState(true);
33-
34-
const { shortname } = match.params;
35-
const projects = await db.getAllByFilter('projects')(
36-
db.where('shortname')('==')(shortname.trim()),
37-
);
38-
39-
if (projects !== undefined && projects.length === 1) {
40-
const project = projects[0];
41-
setProjectDataState(project);
42-
setPidState(project.id);
43-
// a project is editable if the user either belongs to the team or the admin
44-
if (auth.isLoggedIn()) {
45-
const { uid: currentUserId } = auth.getUserInfo();
46-
if (currentUserId) {
47-
const editable = some(project.team, id => id === currentUserId)
48-
|| some(project.admin, id => id === currentUserId)
49-
|| project.owner === currentUserId;
50-
// set whether or not editable
51-
setEditableState(editable);
52-
}
53-
}
54-
}
55-
setLoadingState(false);
56-
};
57-
58-
init();
27+
actions.getProject(pid);
5928
}, []);
6029

61-
const toggleDashboard = toggle => () => setShowDashboardState(toggle);
30+
useEffect(() => {
31+
const info = auth.getUserInfo();
32+
if (project && info) {
33+
const _editable = some(project.team, id => id === info.uid)
34+
|| some(project.admin, id => id === info.uid)
35+
|| project.owner === info.uid;
36+
setEditable(_editable);
37+
}
38+
}, [ project ]);
39+
40+
const toggleDashboard = toggle => () => setShowDashboard(toggle);
6241

63-
if (loadingState) {
64-
return <LoadingView>Loading ...</LoadingView>;
42+
if (isNil(project)) {
43+
return <div>Loading...</div>;
6544
}
66-
if (pidState && showDashboardState) {
67-
return <Dashboard pid={pidState} handleCloseDashboard={toggleDashboard(false)} />;
45+
if (showDashboard) {
46+
return <Dashboard pid={pid} handleCloseDashboard={toggleDashboard(false)} />;
6847
}
6948
return (
7049
<section>
71-
{editableState ? <EditProjectBanner onEdit={toggleDashboard(true)} /> : <div />}
50+
{editable ? <EditProjectBanner onEdit={toggleDashboard(true)} /> : <div />}
7251
<Row>
73-
<BannerContent name={projectDataState.name} images={projectDataState.images} />
74-
<ProjectInfoContent project={projectDataState} />
52+
<BannerContent name={project.name} images={project.images} />
53+
<ProjectInfoContent project={project} />
7554
</Row>
7655
<SocialContentSection
77-
isOwner={editableState}
78-
projectId={pidState}
79-
ourstory={projectDataState.about ? projectDataState.about : ''}
56+
isOwner={editable}
57+
projectId={pid}
58+
ourstory={project.about ? project.about : ''}
8059
selected={location.hash}
8160
/>
8261
</section>

src/components/project-page/components/dashboard/components/description-form/DescriptionForm.jsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,21 @@ import React from 'react';
22
import styled from 'styled-components';
33
import { Formik, Field, Form, ErrorMessage } from 'formik';
44

5+
import useProject from '../../../../../../hooks/use-project';
56
import SaveButton from '../../../../../shared/save-button';
6-
import useFirestoreProject from '../../../../../../hooks/use-firestore-project';
77
import { DescriptionSchema } from '../../validation';
88

9-
const initialValues = { name: '', description: '' };
10-
119
const DescriptionForm = ({ className, pid }) => {
12-
const { values, success, loading, handleSave } = useFirestoreProject(pid, initialValues);
10+
const { project, handleSave, success, loading } = useProject(pid);
1311
return (
1412
<section className={className}>
1513
<h2>Description</h2>
1614
<div className="wrapper">
1715
<Formik
1816
enableReinitialize
1917
validationSchema={DescriptionSchema}
20-
initialValues={{ name: values.name, description: values.description }}
21-
onSubmit={vals => handleSave(vals)}
18+
initialValues={{ name: project.name, description: project.description }}
19+
onSubmit={values => handleSave(values)}
2220
render={({ status }) => (
2321
<Form>
2422
<h5>Project name</h5>
Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
1-
import React from 'react';
1+
import React, { useEffect } from 'react';
2+
import { bindActionCreators } from 'redux';
3+
import { useDispatch } from 'react-redux';
24
import CategoryForm from './components/category-form';
35
import OrganizationForm from './components/organization-form';
46
import ReleaseForm from './components/release-form';
57

6-
const TagsForm = ({ pid }) => (
7-
<section>
8-
<h2>Tags</h2>
9-
<hr />
10-
<CategoryForm pid={pid} />
11-
<OrganizationForm pid={pid} />
12-
<ReleaseForm pid={pid} />
13-
</section>
14-
);
8+
import { getTags } from '../../../../../app/actions';
9+
10+
const TagsForm = ({ pid }) => {
11+
const dispatch = useDispatch();
12+
const actions = bindActionCreators({ getTags }, dispatch);
13+
useEffect(() => {
14+
actions.getTags();
15+
}, []);
16+
17+
return (
18+
<section>
19+
<h2>Tags</h2>
20+
<hr />
21+
<CategoryForm pid={pid} />
22+
<OrganizationForm pid={pid} />
23+
<ReleaseForm pid={pid} />
24+
</section>
25+
);
26+
};
1527

1628
export default TagsForm;

src/components/project-page/components/dashboard/components/tags-form/components/category-form/CategoryForm.jsx

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,50 @@
11
import React from 'react';
2+
import { Formik } from 'formik';
23
import styled from 'styled-components';
34
import Select from 'react-select';
45
import SaveButton from '../../../../../../../shared/save-button';
5-
import useTags from '../../../../../../../../hooks/use-tags';
6+
import useTag from '../../../../../../../../hooks/use-tag';
67

78
const CategoryForm = ({ className, pid }) => {
8-
const { handleSave, handleChange, success, loading, tags, chosenTags } = useTags(
9-
pid,
10-
'project-category',
11-
);
9+
const { tags, chosenTags, handleSave } = useTag(pid, 'project-category');
10+
const submitForm = (values, { setSubmitting, setStatus }) => {
11+
handleSave(values.chosenTags.map(tag => tag.id));
12+
setSubmitting(false);
13+
setStatus({ success: true });
14+
};
15+
1216
return (
1317
<article className={className}>
14-
<h3>Category</h3>
15-
<span className="helpText">
16-
Choose tags that best describe the category or discipline your project falls under.
17-
</span>
18-
<Select
19-
captureMenuScroll={false}
20-
value={chosenTags}
21-
isMulti
22-
name="category"
23-
options={tags}
24-
onChange={handleChange}
25-
classNamePrefix="select"
18+
<Formik
19+
enableReinitialize
20+
initialValues={{ chosenTags }}
21+
onSubmit={submitForm}
22+
render={({ values, setFieldValue, handleSubmit, isSubmitting, status }) => (
23+
<form onSubmit={handleSubmit}>
24+
<h3>Category</h3>
25+
<span className="helpText">
26+
Choose tags that best describe the category or discipline your project falls under.
27+
</span>
28+
<Select
29+
captureMenuScroll={false}
30+
value={values.chosenTags}
31+
getOptionLabel={option => option.name}
32+
getOptionValue={option => option.id}
33+
isMulti
34+
name="category"
35+
options={tags}
36+
onChange={value => setFieldValue('chosenTags', value)}
37+
classNamePrefix="select"
38+
/>
39+
<SaveButton
40+
type="submit"
41+
disabled={isSubmitting}
42+
success={status && status.success}
43+
loading={isSubmitting}
44+
/>
45+
</form>
46+
)}
2647
/>
27-
<SaveButton loading={loading} disabled={loading} success={success} onClick={handleSave} />
2848
</article>
2949
);
3050
};

0 commit comments

Comments
 (0)