Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@
"dependencies": {
"axios": "^0.18.0",
"history": "^4.7.2",
"next": "^7.0.2",
"prop-types": "^15.6.2",
"qs": "^6.6.0",
"react": "16.7.0-alpha.2",
"react-dom": "16.7.0-alpha.2",
"react-intersection-observer": "^6.4.1",
"react-media": "^1.9.2",
"react-redux": "^6.0.0",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-sticky": "^6.0.3",
"react-toastify": "^4.5.2",
"redux": "^4.0.1",
"redux-react-hook": "^3.0.2",
"redux-thunk": "^2.3.0",
"styled-components": "^4.1.3",
"uuid": "^3.3.2",
Expand All @@ -34,6 +38,7 @@
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
Expand Down
8 changes: 8 additions & 0 deletions src/js/actions/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const setMediaSize = (mediaSize) => ({
type: "SET_MEDIA_SIZE",
mediaSize
}),
setMenuState = (menuIsOpen) => ({
type: "SET_HEADER_MENU_STATE",
menuIsOpen
});
4 changes: 1 addition & 3 deletions src/js/actions/toast.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
export const showToastMsg = (text, msgType, id, options) => {
export const showToastMsg = (text, msgType) => {
return{
type: "SHOW_TOAST_MSG",
text,
msgType,
id,
options
}
},
resetToastMsg = () => {
Expand Down
6 changes: 3 additions & 3 deletions src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import './../sass/styles.scss';

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { StoreContext } from 'redux-react-hook';
import AppRouter from './routers/AppRouter';
import configureStore from './store/configureStore';

const store = configureStore();
const jsx = (
<Provider store={store}>
<StoreContext.Provider value={store}>
<AppRouter />
</Provider>
</StoreContext.Provider>
);

ReactDOM.render(jsx, document.getElementById('app'));
103 changes: 38 additions & 65 deletions src/js/components/common/layout/ActivitiesForm.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React, { useState, useEffect, useReducer } from 'react';
import React, {useState, useEffect, useReducer, useCallback} from 'react';
import qs from "qs";
import DropdownCard from "./dropdown/DropdownCard";
import FormValidator from "../libs/FormValidator";
import activitiesFormReducer from "../../../reducers/local-reducers/activitiesForm"

import Button from "./styled-components/Button";
import { showToastMsg } from "../../../actions/toast";
import { useDispatch } from "redux-react-hook";

export default function ActivitiesForm(props){
export default function ActivitiesForm({sendRequest}){

const validator = new FormValidator([
{
Expand Down Expand Up @@ -47,66 +51,36 @@ export default function ActivitiesForm(props){
},
]);

const [state, dispatch] = useReducer(
(state, action) => {
switch (action.type) {
case "CHANGE_TYPE":
return {
...state,
type: action.value
};

case "CHANGE_PRICE":
return {
...state,
price: action.value
};

case "CHANGE_KEY":
return {
...state,
key: action.value
};

case "CHANGE_ACCESSIBILITY":
return {
...state,
accessibility: action.value
};

case "VALIDATION":
return {
...state,
validation: action.validation
};

default:
return state;
}
},
{
type: null,
price: null,
key: null,
accessibility: null,
validation: validator.valid()
}
);
const initialState = {
type: null,
price: null,
key: null,
accessibility: null,
validation: validator.valid()
};

useEffect(() => {
// console.log("create/update", state);
});
const [state, dispatch] = useReducer(activitiesFormReducer, initialState);

const _dispatch = useDispatch();
const _showToastMsg = useCallback((text, msgType) => _dispatch(showToastMsg(text, msgType)));

function onSubmitHandler(e){
e.preventDefault();
const query = {};
for(let key in state){
if(state[key] && state[key] !== "") query[key] = state[key];
if(state.hasOwnProperty(key) && state[key] ){
query[key] = state[key]
}
}
delete query["validation"];
delete query.validation;

const url = ("http://www.boredapi.com/api/activity?" + qs.stringify(query));

const url = ("http://www.boredapi.com/api/activity?" + JSON.stringify(query)).replace(",", "&").replace(/\"\:/g, "=").replace(/\{|\"|\}/g, "");
props["sendRequest"](url);
if(!qs.stringify(query).length){
_showToastMsg("You should to fix an error!", "error")
}else{
sendRequest(url);
}
}

function onChangeHandler(field, value){
Expand All @@ -125,12 +99,12 @@ export default function ActivitiesForm(props){

function onDropdownChangeHandler(value){
dispatch({
type: ("change_type").toUpperCase(),
type: "CHANGE_TYPE",
value
})
}

let types = ["education", "recreational", "social", "diy", "charity", "cooking", "relaxation", "music", "busywork"].map((key) => {
const types = ["education", "recreational", "social", "diy", "charity", "cooking", "relaxation", "music", "busywork"].map((key) => {
return {
name: key,
value: key,
Expand All @@ -141,12 +115,11 @@ export default function ActivitiesForm(props){
name: "Chose type",
value: null,
stateProp: "type"
};

const theme = {main: "#80d066"};
},
theme = {main: "#80d066"};

return (
<form className={"default-form m20-0"} onSubmit={onSubmitHandler}>
<form className="default-form m20-0" onSubmit={onSubmitHandler}>

<div className="row ">
<div className="col-6 col-sm-6 form-group">
Expand All @@ -159,23 +132,23 @@ export default function ActivitiesForm(props){

<div className="col-6 col-sm-6 form-group">
<div><label className="control-label">Price: </label></div>
<input className={"form-control " + (state.validation.price["isInvalid"] && "has-error")}
type="text" name="price" defaultValue={state.price ? state.price : ""}
<input className={"form-control " + (state.validation.price.isInvalid && "has-error")}
type="text" name="price" defaultValue={""}
onChange={(e) => onChangeHandler("price", e.target.value)}/>
<span className={"error-block"}>{state.validation.price.message}</span>
</div>

<div className="col-6 col-sm-6 form-group">
<div><label className="control-label">Accessibility: </label></div>
<input className={"form-control " + (state.validation.accessibility["isInvalid"] && "has-error")}
<input className={"form-control " + (state.validation.accessibility.isInvalid && "has-error")}
type="text" name="accessibility" defaultValue={""}
onChange={(e) => onChangeHandler("accessibility", e.target.value)}/>
<span className={"error-block"}>{state.validation.accessibility.message}</span>
</div>

<div className="col-6 col-sm-6 form-group">
<div><label className="control-label">Activity id: </label></div>
<input className={"form-control " + (state.validation.key["isInvalid"] && "has-error")}
<input className={"form-control " + (state.validation.key.isInvalid && "has-error")}
type="text" name="key" defaultValue={""}
onChange={(e) => onChangeHandler("key", e.target.value)}/>
<span className={"error-block"}>{state.validation.key.message}</span>
Expand All @@ -187,7 +160,7 @@ export default function ActivitiesForm(props){
<Button
onClick={(e) => {
e.preventDefault();
props["sendRequest"]("http://www.boredapi.com/api/activity");
sendRequest("http://www.boredapi.com/api/activity");
}}>
Random event
</Button>
Expand Down
22 changes: 10 additions & 12 deletions src/js/components/common/layout/ActivitiesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import React, { useState, useEffect} from 'react';
import { StickyContainer, Sticky } from "react-sticky";


export default function ActivitiesList(props){
export default function ActivitiesList({activities, removeHandler}){

const [activitiesArr, setActivitiesArr] = useState(props.activities.all);
const [activitiesArr, setActivitiesArr] = useState(activities.all); //it's used by handleSortActivities
const [sortActivity, setSortActivity] = useState("");

useEffect(() => {
setActivitiesArr(props.activities.all);
}, [props]);
setActivitiesArr(activities.all);
}, [activities]);

function handleSortActivities(type) {
const sortedActivityArr = activitiesArr.sort((a, b) => {
Expand Down Expand Up @@ -55,22 +55,20 @@ export default function ActivitiesList(props){
<ul className={"activities-list"}>
{
activitiesArr.map((activity, i) => {
const _activity = activity;

return (
<li key={i} className={"activity-item row"}>
<div className={"title "}>{_activity.activity}</div>
<div className={"title "}>{activity.activity}</div>

<div className={"accessibility"}>{_activity["accessibility"]}</div>
<div className={"accessibility"}>{activity.accessibility}</div>

<div className={"participants"}>{_activity["participants"]}</div>
<div className={"participants"}>{activity.participants}</div>

<div className={"price"}>{_activity["price"]}</div>
<div className={"price"}>{activity.price}</div>

<div className={"type"}>{_activity["type"]}</div>
<div className={"type"}>{activity.type}</div>

<div className={"options"}>
<span className={"remove-icon"} onClick={() => props["removeHandler"](_activity.key)}/>
<span className={"remove-icon"} onClick={() => removeHandler(activity.key)}/>
</div>
</li>
)
Expand Down
63 changes: 27 additions & 36 deletions src/js/components/common/layout/Header.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { NavLink } from "react-router-dom";
import { useDispatch, useMappedState } from "redux-react-hook";
import { setMenuState } from "../../../actions/app";

const mapState = (state) => ({
_app: state.app
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Слишком много идентификаторов с префиксом _ появилось. Лучше давать более осмысленные имена.

Copy link
Author

@m-v-novikov m-v-novikov Jan 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да это есть, префикс я начал давать для actions(и для других некоторых переменных например как toast) т.к. функция(имя переменной) одна и та же(чтоб не было конфликтов имен). Тут да - надо что то придумать и придерживаться этого правила.

});

export default function Header(props){
export default function Header(){

const { _app } = useMappedState(mapState);
const [ isOpen, setIsOpen ] = useState(false);
const small = useMedia("(max-width: 767px)");
const [ small, setSmall ] = useState(_app.mediaSize === "mobile");

useEffect(() => {
const content = document.querySelector('.page-content');

if(content){

if(isOpen && small){
setTimeout(() => {
content.style.display = "none"
}, 1000);
}else{
content.style.display = "block";
}
const dispatch = useDispatch();
const _setMenuState = useCallback((menuIsOpen) => dispatch(setMenuState(menuIsOpen)));

useEffect(() => {
const isSmall = _app.mediaSize === "mobile";
setSmall(isSmall);

if(!isSmall){
_setMenuState(false);
setIsOpen(false)
}else{
setTimeout(() => {
_setMenuState(isOpen);
}, (isOpen ? 700 : 300))
}
});
}, [_app.mediaSize, _app.menuIsOpen, isOpen]);

function onMenuClickHandler(){
setIsOpen(!isOpen);
};
}

function onLinkClickHandler(){
setIsOpen(false);
};
}

return (
<header className={"header " + (small ? "mobile" : "")}>
<header className={"header " + _app.mediaSize}>
<div className="content-container">
<div className="header-content">

Expand Down Expand Up @@ -74,20 +81,4 @@ export default function Header(props){
</div>
</header>
);
}

function useMedia(query){
const [matches, setMatches] = useState(window.matchMedia(query).matches);

useEffect(() => {
const media = window.matchMedia(query);
if (media.matches !== matches) setMatches(media.matches);

const listener = () => setMatches(media.matches);
window.addEventListener("resize", listener);
return () => window.removeEventListener("resize", listener);

}, [query]);

return matches;
}
}
Loading