Skip to content

Commit d18313c

Browse files
authored
Merge pull request #22 from oslabs-beta/denton/pubbutton
Denton/pubbutton
2 parents a4c5ecd + 5337938 commit d18313c

File tree

16 files changed

+102
-62
lines changed

16 files changed

+102
-62
lines changed

app/src/components/App.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import '../public/styles/style.css';
22

3-
import React, { useEffect } from 'react';
3+
import React, { useEffect, useState } from 'react';
44
import {
55
setInitialState,
66
toggleLoggedIn
@@ -19,16 +19,20 @@ import { saveProject } from '../helperFunctions/projectGetSaveDel';
1919
// Intermediary component to wrap main App component with higher order provider components
2020
export const App = (): JSX.Element => {
2121
const state = useSelector((store: RootState) => store.appState);
22+
const [toggleAttempt, setToggleAttempt] = useState(false);
2223
const dispatch = useDispatch();
2324
// checks if user is signed in as guest or actual user and changes loggedIn boolean accordingly
2425
useEffect(() => {
2526
if (window.localStorage.getItem('ssid') !== 'guest') {
26-
dispatch(toggleLoggedIn());
27+
dispatch(toggleLoggedIn(true));
2728
}
29+
//setToggleAttempt(!toggleAttempt);
2830
}, []);
2931

3032
// following useEffect runs on first mount
3133
useEffect(() => {
34+
console.log('state.isLoggedIn', state.isLoggedIn)
35+
// console.log('cookies.get in App', Cookies.get())
3236
// if user is a guest, see if a project exists in localforage and retrieve it
3337
if (!state.isLoggedIn) {
3438
localforage.getItem('guestProject').then((project) => {
@@ -39,6 +43,7 @@ export const App = (): JSX.Element => {
3943
});
4044
} else {
4145
// otherwise if a user is logged in, use a fetch request to load user's projects from DB
46+
4247
let userId;
4348
if (Cookies.get('ssid')) {
4449
userId = Cookies.get('ssid');

app/src/components/marketplace/MarketplaceCard.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { red } from '@mui/material/colors';
2020
import axios from 'axios';
2121
import {useDispatch, useSelector} from 'react-redux'
2222
import { RootState } from '../../redux/store';
23-
import { saveProject } from '../../helperFunctions/projectGetSaveDel';
2423

2524
interface Project {
2625
forked: String,
@@ -40,15 +39,15 @@ const MarketplaceCard = ({proj} :{proj: Project}) => {
4039
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
4140
const open = Boolean(anchorEl);
4241
const state = useSelector((store:RootState) => store.appState);
43-
console.log('HEY', state)
4442
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
4543
setAnchorEl(event.currentTarget);
4644
};
4745
const handleClone = async () => { // creates a copy of the project
4846
const updatedProject: Project = JSON.parse(JSON.stringify(proj)); // creates a deep copy
4947
updatedProject.forked = `Forked from ${updatedProject.username}`;
48+
const username = window.localStorage.getItem('username');
5049
await axios.post('/cloneProject', {
51-
updatedProject
50+
updatedProject, username: username //passing in the username from localstorage for verification, just in case someone altered local storage
5251
});
5352
alert('Project cloned!');
5453
setAnchorEl(null);

app/src/components/marketplace/Searchbar.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ const SearchBar = ({marketplaceProjects, updateDisplayProjects }):JSX.Element =>
2929
//a-zA-Z letters (lowercase and uppercase) and underscore symbol
3030
//All together: match either the start/end of a line/string or any character that is not a letter.
3131
//Looks for anything that has the searchText in between non-letter characters
32+
const patternString2 = '(?:^|.)' + searchText.toLowerCase() + '(?:$|.)';
33+
//Only difference is that (?:^|.) (?:$|.) look for anything that matches the string in between any other characters for the username search
34+
//test3 and 1test) would both match for string 'test'
35+
3236
const searchResults = marketplaceProjects.reduce((results, curr) => {
33-
if(curr.name.match(patternString) || curr.username.match(patternString))
37+
if(curr.name.match(patternString) || curr.username.match(patternString2))
3438
results.push(curr)
3539
return results;
3640
}, [])

app/src/components/right/LoginButton.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,23 @@ import { useSelector, useDispatch } from 'react-redux';
33
import { toggleLoggedIn } from '../../redux/reducers/slice/appStateSlice';
44
import config from '../../../../config.js';
55
import { RootState } from '../../redux/store';
6+
import Cookies from 'js-cookie';
67
// note that API_BASE_URL is assigned to different pages on dev mode vs prod mode
78
const { API_BASE_URL, API_BASE_URL2 } = config;
89

910
export default function LoginButton() {
1011
const state = useSelector((store:RootState) => store.appState);
1112
const dispatch = useDispatch();
12-
13+
// console.log('doc cookie 1', Cookies.get())
1314
const handleLogout = () => {
15+
// console.log('doc cookie 2', Cookies.get('ssid'))
16+
// // document.cookie = 'ssid' + '=; Max-Age=0';
17+
// console.log('doc cookie 3', Cookies.get())
18+
1419
window.localStorage.clear();
15-
document.cookie = 'ssid' + '=; Max-Age=0';
1620

1721
if (state.isLoggedIn) {
18-
dispatch(toggleLoggedIn());
22+
dispatch(toggleLoggedIn(false));
1923
}
2024

2125
window.location.href = state.isLoggedIn

app/src/components/right/OpenProjects.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export interface ProjectDialogProps {
2424
function ProjectsDialog(props: ProjectDialogProps) {
2525
const classes = useStyles();
2626
const { onClose, open, projects } = props;
27-
console.log(projects)
2827
const state = useSelector((store:RootState) => store.appState);
2928
const dispatch = useDispatch();
3029
// If no projects selected, keep the name of the current displayed

app/src/components/right/SaveProjectButton.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import DialogTitle from '@mui/material/DialogTitle';
88
import SaveOutlinedIcon from '@mui/icons-material/SaveOutlined';
99
import { saveProject } from '../../helperFunctions/projectGetSaveDel';
1010
import {useDispatch, useSelector} from 'react-redux'
11-
import {updateProjectName} from '../../redux/reducers/slice/appStateSlice';
11+
import {updateProjectName, updateProjectId} from '../../redux/reducers/slice/appStateSlice';
1212
import { RootState } from '../../redux/store';
13+
import { State } from '../../interfaces/Interfaces';
1314

1415
export default function FormDialog() {
1516
const [open, setOpen] = useState(false);
@@ -34,7 +35,7 @@ const dispatch = useDispatch();
3435
// If errors occur on the backend, the project name still gets updated
3536

3637
dispatch(updateProjectName(projectName))
37-
saveProject(projectName, state);
38+
saveProject(projectName, state).then((project: State) => dispatch(updateProjectId(project._id)))//updates the slice with new _id from mongo
3839
setOpen(false);
3940
} else {
4041
setInvalidProjectName(true);

app/src/components/top/NavBarButtons.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import List from '@mui/material/List';
88
import ListItem from '@mui/material/ListItem';
99
import ListItemText from '@mui/material/ListItemText';
1010
import LoginButton from '../right/LoginButton';
11-
import MarketplaceButton from '../right/MarketplaceButton';
1211
import Menu from '@mui/material/Menu';
1312
import MenuItem from '@mui/material/MenuItem';
1413
import ProjectsFolder from '../right/OpenProjects';
@@ -357,7 +356,7 @@ function navbarDropDown(props) {
357356
></input>
358357
<button onClick={() => joinRoom()}>Join Room</button>
359358
<p>In Room: {joinedRoom}</p>
360-
<Link to="/marketplace" style={{ textDecoration: 'none' }} target="_blank">
359+
<Link to="/marketplace" style={{ textDecoration: 'none' }}>
361360
<button>
362361
Marketplace
363362
<svg

app/src/containers/MarketplaceContainer.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import MarketplaceCardContainer from '../components/marketplace/MarketplaceCardC
22
import SearchBar from '../components/marketplace/Searchbar';
33
import React, {useEffect, useState} from 'react';
44
import axios from 'axios';
5+
import { CircularProgress } from '@mui/material';
56

67
const MarketplaceContainer = () => {
78

@@ -28,13 +29,16 @@ const MarketplaceContainer = () => {
2829

2930
}, []);
3031

32+
3133
const updateDisplayProjects = (searchResults) => {
3234

3335
setDisplayProjects(searchResults);//have to pass this down as a prop so that the setting is done outside of Rendering otherwise callstack issues
3436

3537
};
3638

3739

40+
41+
3842
return (
3943
<div style={containerStyles}>
4044
<div style={contentStyles}>
@@ -46,9 +50,17 @@ const MarketplaceContainer = () => {
4650
<SearchBar marketplaceProjects = {marketplaceProjects} updateDisplayProjects = {updateDisplayProjects}/>
4751
</div>
4852
{displayProjects.length ? <MarketplaceCardContainer displayProjects = {displayProjects} /> :
49-
<h2 style={{textAlign: 'center'}}>
50-
No Results Found!
51-
</h2>
53+
54+
(/*while marketplaceProjects is length 0 means it is not done fetching. Add loading...*/
55+
marketplaceProjects.length ?
56+
<h2 style={{textAlign: 'center'}}>
57+
No Results Found!
58+
</h2>
59+
:
60+
<h2 style={{textAlign: 'center'}}> {/*added a circular progress bar*/}
61+
Loading... <CircularProgress thickness={5.0}/>
62+
</h2>
63+
)
5264
}
5365
</div>
5466
);

app/src/helperFunctions/projectGetSaveDel.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { State } from "../interfaces/Interfaces";
2+
13
const isDev = process.env.NODE_ENV === 'development';
24
const { DEV_PORT, API_BASE_URL } = require('../../../config.js');
35
let serverURL = API_BASE_URL;
@@ -31,9 +33,11 @@ export const saveProject = (
3133
name: String,
3234
workspace: Object
3335
): Promise<Object> => {
36+
const newProject = { ...workspace}
37+
delete newProject['_id']; //deleting the _id from the current state slice. We don't actually want it in the project object in the mongo db document
3438
const body = JSON.stringify({
3539
name,
36-
project: { ...workspace, name },
40+
project: { ...newProject, name },
3741
userId: window.localStorage.getItem('ssid'),
3842
username: window.localStorage.getItem('username'),
3943
comments: []
@@ -48,10 +52,10 @@ export const saveProject = (
4852
})
4953
.then((res) => res.json())
5054
.then((data) => {
51-
return data.project;
55+
return {_id: data._id, ...data.project}; //passing up what is needed for the global appstateslice
5256
})
5357
.catch((err) => console.log(`Error saving project ${err}`));
54-
return project;
58+
return project;//returns _id in addition to the project object from the document
5559
};
5660

5761
export const deleteProject = (project: any): Promise<Object> => {

app/src/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { HTML5Backend } from 'react-dnd-html5-backend';
1212
import App from './components/App';
1313
import Cookies from 'js-cookie';
1414
import FBPassWord from './components/login/FBPassWord';
15-
import MarketplaceButton from './components/right/MarketplaceButton';
1615
import MarketplaceContainer from './containers/MarketplaceContainer';
1716
import ProjectDashboard from './Dashboard/ProjectContainer';
1817
import { Provider } from 'react-redux';

app/src/interfaces/Interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DragObjectWithType } from 'react-dnd';
22

33
export interface State {
44
name: string;
5+
_id: string;
56
forked: boolean;
67
isLoggedIn: boolean;
78
components: Component[];

app/src/redux/reducers/slice/appStateSlice.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import manageSeparators from '../../../helperFunctions/manageSeparators';
1313

1414
export const initialState: State = {
1515
name: '',
16+
_id: '',
1617
forked: false,
1718
isLoggedIn: false,
1819
// config: { saveFlag: true, saveTimer: false },
@@ -770,6 +771,10 @@ const appStateSlice = createSlice({
770771
const projectName = action.payload;
771772
state.name = projectName;
772773
},
774+
updateProjectId: (state, action) => {
775+
const projectId = action.payload; //updates the slice with new _id
776+
state._id = projectId;
777+
},
773778
deleteElement: (state, action) => {
774779
let name: string = '';
775780
const HTMLTypes: HTMLType[] = [...state.HTMLTypes].filter((el) => {
@@ -1248,8 +1253,8 @@ const appStateSlice = createSlice({
12481253
state.components = components;
12491254
},
12501255

1251-
toggleLoggedIn: (state) => {
1252-
state.isLoggedIn = !state.isLoggedIn;
1256+
toggleLoggedIn: (state, action) => {
1257+
state.isLoggedIn = action.payload;
12531258
},
12541259

12551260
snapShotAction: (state, action) => {
@@ -1292,6 +1297,7 @@ export const {
12921297
changeProjectType,
12931298
resetState,
12941299
updateProjectName,
1300+
updateProjectId,
12951301
deleteElement,
12961302
updateAttributes,
12971303
deleteChild,

server/controllers/cookieController.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,6 @@ const cookieController: CookieController = {
1212
return next();
1313
},
1414

15-
/**
16-
* Stores the current user's username in cookie (for use in cloning projects)
17-
*/
18-
setUserCookie: (req, res, next) => {
19-
res.cookie('username', res.locals.username, {
20-
httpOnly: true,
21-
sameSite: 'none',
22-
secure: true
23-
});
24-
return next();
25-
}
2615
};
2716

2817
export default cookieController;

server/controllers/marketplaceController.ts

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MarketplaceController } from '../interfaces';
2-
import { Projects } from '../models/reactypeModels';
2+
import { Projects, Users } from '../models/reactypeModels';
33

44
// array of objects, objects inside
55
type Projects = { project: {} }[];
@@ -52,7 +52,7 @@ const marketplaceController: MarketplaceController = {
5252
}
5353
});
5454
}
55-
res.locals.publishedProject = result;
55+
res.locals.publishedProject = result; //returns the entire document
5656
return next();
5757
}
5858
);
@@ -109,32 +109,52 @@ const marketplaceController: MarketplaceController = {
109109
*
110110
*/
111111
cloneProject: (req, res, next) => {
112+
const { updatedProject, username } = req.body;
113+
console.log('username in cloneProject end', username);
112114
// pulls cookies from request
113-
const userId = req.cookies.ssid;
114-
const username = req.cookies.username;
115-
const { updatedProject } = req.body;
116-
updatedProject.userId = userId;
117-
updatedProject.username = username;
118-
updatedProject.project.forked = true; // updated the forked tag
119-
delete updatedProject._id; // removes the old project id from the object
120-
updatedProject.createdAt = Date.now();
115+
const currentuserID = req.cookies.ssid
116+
//getting the username based on the cookies ssid
117+
Users.findOne({ _id: currentuserID }, (err, user) => {
118+
if (err) {
119+
return next({
120+
log: `Error in marketplaceController.cloneProjects findUser: ${err}`,
121+
message: {
122+
err: 'Error in marketplaceController.cloneProjects findUser, check server logs for details'
123+
}
124+
});
125+
}else if (user.username !== username){ //prevents users from editing their username to assign a different username to a cloned project
126+
return next({
127+
log: `Error in marketplaceController.cloneProjects. Window username did not match the corresponding db username: ${err}`,
128+
message: {
129+
err: 'Error in marketplaceController.cloneProjects. Window username did not match the corresponding db username, check server logs for details'
130+
}
131+
});
132+
}
133+
//adding the current user's username and userID since the project is now cloned
134+
updatedProject.username = user.username;
135+
updatedProject.userId = currentuserID;
136+
updatedProject.project.forked = true; // updated the forked tag
137+
delete updatedProject._id; // removes the old project id from the object
138+
updatedProject.createdAt = Date.now();
139+
updatedProject.published = false;
121140

122-
Projects.create(
123-
// creates a copy of the project to the user's library
124-
updatedProject,
125-
(err, result) => {
126-
if (err) {
127-
return next({
128-
log: `Error in marketplaceController.cloneProject: ${err}`,
129-
message: {
130-
err: 'Error in marketplaceController.cloneProject, check server logs for details'
131-
}
132-
});
141+
Projects.create(
142+
// creates a copy of the project to the user's library with a new generated _id
143+
updatedProject,
144+
(err, result) => {
145+
if (err) {
146+
return next({
147+
log: `Error in marketplaceController.cloneProject: ${err}`,
148+
message: {
149+
err: 'Error in marketplaceController.cloneProject, check server logs for details'
150+
}
151+
});
152+
}
153+
res.locals.clonedProject = result;
154+
return next();
133155
}
134-
res.locals.clonedProject = result;
135-
return next();
136-
}
137-
);
156+
);
157+
})
138158
},
139159
};
140160
export default marketplaceController;

server/interfaces.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Document, NativeError } from 'mongoose';
33

44
export interface CookieController {
55
setSSIDCookie: RequestHandler;
6-
setUserCookie: RequestHandler;
76
}
87

98
export interface ProjectController {

0 commit comments

Comments
 (0)