Skip to content

Commit 31d775d

Browse files
authored
Merge pull request #35 from TryShape/issue-11-add-functionality-to-create-new-shapes
Issue 11 add functionality to create new shapes
2 parents 2e94e37 + 99746cb commit 31d775d

10 files changed

Lines changed: 626 additions & 14 deletions

File tree

components/core/App.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@ const App = (props) => {
1818
const [loading, setLoading] = useState(true); // shapes loading
1919

2020
const [searchTerm, setSearchTerm] = useState(""); // search
21-
const [sort, setSort] = useState("popularity"); // sort
21+
const [sort, setSort] = useState("recent"); // sort
22+
23+
const [shapeAction, setShapeAction] = useState({
24+
'action': '',
25+
'payload': {}
26+
});
2227

2328
const { user } = props;
2429

2530
useEffect(async () => {
2631
setData([]);
2732
setLoading(true);
28-
2933
let shapes = [];
3034

3135
if(user.length === 0) {
@@ -84,18 +88,21 @@ const App = (props) => {
8488
});
8589

8690
console.log(shapes);
87-
91+
console.log({shapeAction});
8892
await setData(shapes);
93+
console.log(shapes);
8994
setLoading(false);
90-
}, [user]);
95+
}, [user, shapeAction]);
9196

9297
return (
9398
<>
9499
<Header {...props}
95100
searchTerm={searchTerm}
96101
setSearchTerm={setSearchTerm}
97102
sort={sort}
98-
setSort={setSort} />
103+
setSort={setSort}
104+
shapeAction={shapeAction}
105+
setShapeAction={setShapeAction} />
99106
{loading ? (
100107
<Loader
101108
style={{margin: '20% auto auto 42%'}}

components/core/CreateShape.js

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
import React, { useState } from "react";
2+
import Container from "react-bootstrap/Container";
3+
import Row from "react-bootstrap/Row";
4+
import Col from "react-bootstrap/Col";
5+
import Modal from "react-bootstrap/Modal";
6+
import Button from "react-bootstrap/Button";
7+
8+
// Toast
9+
import toast from "react-hot-toast";
10+
11+
// harperDb fetch call
12+
import { harperFetch } from "../../utils/HarperFetch";
13+
14+
import { ShapeForm, ShapePreview } from "..";
15+
16+
const CreateShape = (props) => {
17+
const [shapeInformation, setShapeInformation] = useState({
18+
"name": "Tilted Square",
19+
"formula": "polygon(10% 10%, 90% 10%, 90% 90%, 10% 80%)",
20+
"vertices": 4,
21+
"private": false,
22+
"edges": 4,
23+
"notes": "",
24+
"clipPathType": "polygon",
25+
"showShadow": true,
26+
"backgroundColor": "#d61284",
27+
"verticeCoordinates" : [
28+
{
29+
"x": "10%",
30+
"y": "10%",
31+
},
32+
{
33+
"x": "90%",
34+
"y": "10%",
35+
},
36+
{
37+
"x": "90%",
38+
"y": "90%",
39+
},
40+
{
41+
"x": "10%",
42+
"y": "80%",
43+
},
44+
]
45+
});
46+
47+
function handleChange(event, data, number) {
48+
const name = event.target.name || event.type;
49+
const value = event.target.type === "checkbox" ? event.target.checked : event.target.value;
50+
51+
// console.log(event, data);
52+
53+
if (name === "name") {
54+
setShapeInformation({
55+
...shapeInformation,
56+
"name": value,
57+
});
58+
} else if (name === 'private') {
59+
setShapeInformation({
60+
...shapeInformation,
61+
"private": !shapeInformation.private,
62+
63+
});
64+
} else if (name === "formula") {
65+
const edgeVerticeNumber = shapeInformation.clipPathType === "polygon" ? value.split(",").length: 0;
66+
67+
if (value === "") {
68+
handleFormulaChange(shapeInformation.clipPathType + "()", edgeVerticeNumber)
69+
} else if (value.includes("polygon")) {
70+
handleFormulaChange(value, edgeVerticeNumber, "polygon");
71+
} else if (value.includes("circle")) {
72+
handleFormulaChange(value, edgeVerticeNumber, "circle");
73+
} else if (value.includes("ellipse")) {
74+
handleFormulaChange(value, edgeVerticeNumber, "ellipse");
75+
} else {
76+
handleFormulaChange(value, edgeVerticeNumber);
77+
}
78+
} else if (name === "mousemove") {
79+
const newVerticeCoordinates = addNewVerticeCoordinates(data.x, data.y, number);
80+
const newFormula = generateNewFormula(newVerticeCoordinates);
81+
82+
setShapeInformation({
83+
...shapeInformation,
84+
"verticeCoordinates": newVerticeCoordinates,
85+
"formula": newFormula,
86+
});
87+
} else if (name === "click" && event.target.id === "shapeShadow") {
88+
const newVerticeCoordinates = addNewVerticeCoordinates(event.nativeEvent.offsetX, event.nativeEvent.offsetY, shapeInformation.verticeCoordinates.length);
89+
const newFormula = generateNewFormula(newVerticeCoordinates);
90+
91+
setShapeInformation({
92+
...shapeInformation,
93+
"vertices": shapeInformation.vertices + 1,
94+
"edges": shapeInformation.edges + 1,
95+
"verticeCoordinates": newVerticeCoordinates,
96+
"formula": newFormula,
97+
});
98+
} else if ((event.target.id.includes("deleteButton") || event.target.localName === "line") && number !== undefined) {
99+
100+
let newVerticeCoordinates = [];
101+
102+
for (let i = 0; i < shapeInformation.verticeCoordinates.length; i++) {
103+
if (i !== number) {
104+
newVerticeCoordinates.push(shapeInformation.verticeCoordinates[i]);
105+
}
106+
}
107+
108+
const newFormula = generateNewFormula(newVerticeCoordinates);
109+
110+
setShapeInformation({
111+
...shapeInformation,
112+
"vertices": shapeInformation.vertices - 1,
113+
"edges": shapeInformation.edges - 1,
114+
"verticeCoordinates": newVerticeCoordinates,
115+
"formula": newFormula,
116+
});
117+
118+
} else if (name === "clipPathType") {
119+
handleClipPathChange(value);
120+
} else {
121+
setShapeInformation({
122+
...shapeInformation,
123+
[name]: value,
124+
});
125+
}
126+
}
127+
128+
function addNewVerticeCoordinates(x ,y, number) {
129+
const xPercentage = Math.round((x / 280.0) * 100.0);
130+
const yPercentage = Math.round((y / 280.0) * 100.0);
131+
132+
let newVerticeCoordinates = shapeInformation.verticeCoordinates;
133+
newVerticeCoordinates[number] = {
134+
"x": xPercentage + "%",
135+
"y": yPercentage + "%"
136+
}
137+
138+
return newVerticeCoordinates;
139+
}
140+
141+
function generateNewFormula(newVerticeCoordinates) {
142+
let newFormula = shapeInformation.clipPathType + "(";
143+
144+
for (let i = 0; i < newVerticeCoordinates.length; i++) {
145+
let newX = newVerticeCoordinates[i].x;
146+
let newY = newVerticeCoordinates[i].y;
147+
148+
i === newVerticeCoordinates.length - 1 ? newFormula = newFormula + newX + " " + newY + ")" : newFormula = newFormula + newX + " " + newY + ", ";
149+
}
150+
151+
return newFormula;
152+
}
153+
154+
function handleFormulaChange(formula, edgeVerticeNumber, clipPathType) {
155+
let newVerticeCoordinates = [];
156+
157+
if (clipPathType === "polygon") {
158+
let formulaNumbers = formula.slice(formula.indexOf("(") + 1, formula.indexOf(")"));
159+
formulaNumbers = formulaNumbers.split(", ");
160+
newVerticeCoordinates = formulaNumbers.map(x => {
161+
let percentageArray = x.split(" ");
162+
return {
163+
"x": percentageArray[0],
164+
"y": percentageArray[1],
165+
}
166+
});
167+
}
168+
169+
setShapeInformation(prevState => {
170+
return {
171+
...prevState,
172+
"formula": formula.includes("(") && formula.includes(")") ? formula : prevState.formula,
173+
"clipPathType": clipPathType === null ? prevState.clipPathType : clipPathType,
174+
"vertices": edgeVerticeNumber,
175+
"edges": edgeVerticeNumber,
176+
"verticeCoordinates": newVerticeCoordinates,
177+
}
178+
});
179+
}
180+
181+
function handleClipPathChange(clipPathType) {
182+
if (clipPathType === "polygon") {
183+
setShapeInformation({
184+
...shapeInformation,
185+
"name": "Tilted Square",
186+
"formula": "polygon(10% 10%, 90% 10%, 90% 90%, 10% 80%)",
187+
});
188+
}
189+
190+
if (clipPathType === "circle") {
191+
setShapeInformation({
192+
...shapeInformation,
193+
"name": "Circle",
194+
"formula": "circle(50% at 50% 50%)",
195+
});
196+
}
197+
198+
if (clipPathType === "ellipse") {
199+
setShapeInformation({
200+
...shapeInformation,
201+
"name": "Ellipse",
202+
"formula": "ellipse(25% 40% at 50% 50%)",
203+
});
204+
}
205+
206+
setShapeInformation(prevState => {
207+
return {
208+
...prevState,
209+
"clipPathType": clipPathType,
210+
"edges": clipPathType === "polygon" ? 4 : 0,
211+
"vertices": clipPathType === "polygon" ? 4 : 0,
212+
"notes": "",
213+
}
214+
})
215+
}
216+
217+
const [validated, setValidated] = useState(false);
218+
219+
const handleSubmit = async(event) => {
220+
event.preventDefault();
221+
const form = event.currentTarget;
222+
if (form.checkValidity() === false) {
223+
event.preventDefault();
224+
event.stopPropagation();
225+
}
226+
setValidated(true);
227+
228+
console.log(shapeInformation);
229+
230+
// Create the shape in the DB
231+
const insertShape = await harperFetch({
232+
operation: "sql",
233+
sql: `INSERT into tryshape.shapes(backgroundColor, createdAt, createdBy, edges, email, formula, likes, name, notes, private, type, vertices)
234+
values('${shapeInformation.backgroundColor}', null, '${props.user.email}', ${shapeInformation.edges}, null, '${shapeInformation.formula}', 0, '${shapeInformation.name}', '${shapeInformation.notes}', ${shapeInformation.private}, '${shapeInformation.clipPathType}', ${shapeInformation.vertices})`,
235+
});
236+
237+
console.log(insertShape);
238+
239+
// Create the user in the db
240+
if (insertShape['inserted_hashes'].length > 0) {
241+
// First check if the user exist
242+
const result = await harperFetch({
243+
operation: "sql",
244+
sql: `SELECT count(*) from tryshape.users WHERE email='${props.user.email}'`,
245+
});
246+
const count = (result[0]['COUNT(*)']);
247+
console.log({count});
248+
// If doesn't exist, create in db
249+
if (count === 0) {
250+
const insertUser = await harperFetch({
251+
operation: "sql",
252+
sql: `INSERT into tryshape.users(email, name, photoURL)
253+
values('${props.user.email}', '${props.user.displayName}', '${props.user.photoURL}')`,
254+
});
255+
} else {
256+
console.log(`The user ${props.user.email} present in DB`);
257+
}
258+
}
259+
props.handleClose();
260+
toast.success(`Shape ${shapeInformation.name} created successfully.`);
261+
props.setShapeAction({
262+
...props.shapeAction,
263+
"action": "add",
264+
"payload": {
265+
"backgroundColor": shapeInformation.backgroundColor,
266+
"createdAt": null,
267+
"createdBy": props.user.email,
268+
"edges": shapeInformation.edges,
269+
"email": null,
270+
"email1": props.user.email,
271+
"formula": shapeInformation.formula,
272+
"likes": 0,
273+
"name": shapeInformation.name,
274+
"name1": props.user.displayName,
275+
"notes": shapeInformation.notes,
276+
"photoURL": props.user.photoURL,
277+
"private": shapeInformation.private,
278+
"type": shapeInformation.clipPathType
279+
}
280+
});
281+
}
282+
283+
return(
284+
<>
285+
<Modal
286+
show={props.show}
287+
centered
288+
size="lg"
289+
onHide={props.handleClose}
290+
backdrop="static"
291+
>
292+
<Modal.Header closeButton>
293+
<Modal.Title>Create a Shape</Modal.Title>
294+
</Modal.Header>
295+
<Modal.Body>
296+
<Container fluid>
297+
<Row lg={2} md={1} sm={1} xs={1}>
298+
<Col>
299+
<ShapeForm
300+
shapeInformation={shapeInformation}
301+
handleChange={handleChange}
302+
handleSubmit={handleSubmit}
303+
validated={validated}
304+
/>
305+
</Col>
306+
<Col>
307+
<ShapePreview
308+
shapeInformation={shapeInformation}
309+
handleChange={handleChange}
310+
/>
311+
</Col>
312+
</Row>
313+
</Container>
314+
</Modal.Body>
315+
<Modal.Footer>
316+
<Button onClick={() => props.handleClose()} variant="outline-info">
317+
Close
318+
</Button>
319+
<Button variant="secondary" type="submit" form="createShapeForm">
320+
Save
321+
</Button>
322+
</Modal.Footer>
323+
</Modal>
324+
</>
325+
);
326+
}
327+
328+
export default CreateShape;

components/index.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ export { default as App } from "./core/App";
33
export { default as Landing } from "./core/Landing";
44
export { default as SignInModal } from "./core/SignInModal";
55
export { default as ExportShape } from "./core/ExportShape";
6+
export { default as CreateShape } from "./core/CreateShape";
67
export {default as CopyShapeSource} from "./core/CopyShapeSource";
78

89
// utils
910
export { default as Header } from "./utils/Header";
10-
export { default as ShapeList } from './utils/ShapeList';
11-
export { default as Radios } from './utils/Radios';
12-
export { default as NoShapeFound } from './utils/NoShapeFound';
11+
export { default as ShapeList } from "./utils/ShapeList";
12+
export { default as Radios } from "./utils/Radios";
13+
export { default as ShapeForm } from "./utils/ShapeForm";
14+
export { default as ShapePreview } from "./utils/ShapePreview";
15+
export { default as DraggableVertice } from "./utils/DraggableVertice";
16+
export { default as NoShapeFound } from './utils/NoShapeFound';

0 commit comments

Comments
 (0)