refactoring and implementing refreshing items list every 2 seconds

This commit is contained in:
egradanin
2019-01-14 22:41:53 +01:00
parent a431c58763
commit ae446d5333
19 changed files with 166 additions and 154 deletions

View File

@@ -2,7 +2,7 @@ import React from "react";
import Select from "react-select"; import Select from "react-select";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { CATEGORY_SELECT, ITEMS_CHANGED } from "constants/actionTypes"; import { CATEGORY_SELECT, ITEMS_CHANGED } from "constants/actionTypes";
import { hoc } from "utils/hoc"; import { hoc, areObjectEqual } from "utils/helpers";
import { createOlxLink } from "utils/createOlxLink"; import { createOlxLink } from "utils/createOlxLink";
import axios from "axios"; import axios from "axios";
@@ -29,21 +29,43 @@ const mapDispatchToProps = dispatch => ({
onItemsChanged: items => dispatch({ type: ITEMS_CHANGED, items }) onItemsChanged: items => dispatch({ type: ITEMS_CHANGED, items })
}); });
let lastUpdateTime = null;
let interval = null;
class App extends React.Component { class App extends React.Component {
componentDidMount() {
interval = setInterval(() => {
if (lastUpdateTime && Date.now() - lastUpdateTime > 2000) {
const { category, options, subcategory, onItemsChanged } = this.props;
let url = createOlxLink(category, subcategory, options);
url = encodeURI(url);
if (url) {
axios
.get(`/api/${url}`)
.then(response => onItemsChanged(response.data))
.catch(error => console.log(error));
}
lastUpdateTime = null;
}
}, 1000);
}
componentWillUnmount() {
clearInterval(interval);
}
componentWillReceiveProps(newProps) {
const { subcategory, category, options } = this.props;
if (
newProps.subcategory !== subcategory ||
newProps.category !== category ||
!areObjectEqual(newProps.options, options)
) {
lastUpdateTime = Date.now();
}
}
handleChange = selectedOption => { handleChange = selectedOption => {
this.props.onCategoryChanged(selectedOption); this.props.onCategoryChanged(selectedOption);
}; };
getDataFromOlx = () => {
const { category, options, subcategory, onItemsChanged } = this.props;
let url = createOlxLink(category, subcategory, options);
url = encodeURI(url);
axios
.get(`/api/${url}`)
.then(response => onItemsChanged(response.data))
.catch(error => console.log(error));
};
render() { render() {
const { category } = this.props; const { category } = this.props;
@@ -58,7 +80,6 @@ class App extends React.Component {
Vozila: <Vozila />, Vozila: <Vozila />,
Nekretnine: <Nekretnine /> Nekretnine: <Nekretnine />
})} })}
<button onClick={this.getDataFromOlx}>Get Data from OLX </button>
<ItemsContainer /> <ItemsContainer />
</div> </div>
); );

View File

@@ -1,15 +1,12 @@
import React from "react"; import React from "react";
import Select from "react-select"; import Select from "react-select";
import { subcategorywrapper } from "utils/subcategorywrapper"; import { subcategorywrapper } from "utils/subcategorywrapper";
import { hoc } from "utils/hoc"; import { hoc } from "utils/helpers";
import Stanovi from "../subcategories/nekretnine/Stanovi"; import Stanovi from "../subcategories/nekretnine/Stanovi";
import Kuce from "../subcategories/nekretnine/Kuce"; import Kuce from "../subcategories/nekretnine/Kuce";
const options = [ const options = [{ value: 23, label: "Stanovi" }, { value: 24, label: "Kuce" }];
{ value: "Stanovi", label: "Stanovi" },
{ value: "Kuce", label: "Kuce" }
];
class Nekretnine extends React.Component { class Nekretnine extends React.Component {
handleChange = selectedOption => { handleChange = selectedOption => {
@@ -27,8 +24,8 @@ class Nekretnine extends React.Component {
options={options} options={options}
/> />
{hoc(subcategory && subcategory.value, { {hoc(subcategory && subcategory.value, {
Stanovi: <Stanovi />, 23: <Stanovi />,
Kuce: <Kuce /> 24: <Kuce />
})} })}
</div> </div>
); );

View File

@@ -3,35 +3,35 @@ import Select from "react-select";
import Automobili from "../subcategories/vozila/Automobili"; import Automobili from "../subcategories/vozila/Automobili";
import Motocikli from "../subcategories/vozila/Motocikli"; import Motocikli from "../subcategories/vozila/Motocikli";
import { subcategorywrapper } from "utils/subcategorywrapper"; import { subcategorywrapper } from "utils/subcategorywrapper";
import { hoc } from "utils/hoc"; import { hoc } from "utils/helpers";
const options = [ const options = [
{ value: "Automobili", label: "Automobili" }, { value: 18, label: "Automobili" },
{ value: "Motocikli", label: "Motocikli" } { value: 21, label: "Motocikli" }
]; ];
class Vozila extends React.Component { class Vozila extends React.Component {
handleChange = selectedOption => { handleChange = selectedOption => {
this.props.onSubCategoryChanged(selectedOption); this.props.onSubCategoryChanged(selectedOption);
}; };
render() { render() {
const { subcategory } = this.props; const { subcategory } = this.props;
return ( return (
<div> <div>
<Select <Select
value={subcategory} value={subcategory}
onChange={this.handleChange} onChange={this.handleChange}
options={options} options={options}
/> />
{hoc(subcategory && subcategory.value, { {hoc(subcategory && subcategory.value, {
Automobili: <Automobili />, 18: <Automobili />,
Motocikli: <Motocikli /> 21: <Motocikli />
})} })}
</div> </div>
); );
} }
} }
export default subcategorywrapper(Vozila); export default subcategorywrapper(Vozila);

View File

@@ -2,5 +2,6 @@ export const rangeOptions = {
min: 0, min: 0,
max: 100000, max: 100000,
defaultValues: [0, 100000], defaultValues: [0, 100000],
step: 100 step: 100,
optionNames: ["od", "do"]
}; };

View File

@@ -5,42 +5,50 @@ const elements = [
{ {
type: "checkbox", type: "checkbox",
name: "Uknjizeno", name: "Uknjizeno",
optionName: "uknjizeno" optionName: "uknjizeno-zk_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Namjesteno", name: "Namjesteno",
optionName: "namjesteno" optionName: "namjestena_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Nedavno adaptirano", name: "Nedavno adaptirano",
optionName: "nedavno_adaptirano" optionName: "nedavno-adaptirana_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Garaza", name: "Garaza",
optionName: "garaza" optionName: "gara-a_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Balkon", name: "Balkon",
optionName: "balkon" optionName: "balkon_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Voda", name: "Voda",
optionName: "voda" optionName: "voda_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Plin", name: "Plin",
optionName: "plin" optionName: "plin_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Bazen", name: "Bazen",
optionName: "bazen" optionName: "bazen_checkbox",
value: "on"
} }
]; ];
class KuceFilter extends React.Component { class KuceFilter extends React.Component {

View File

@@ -5,47 +5,56 @@ const elements = [
{ {
type: "checkbox", type: "checkbox",
name: "Novogradnja", name: "Novogradnja",
optionName: "novogradnja" optionName: "novogradnja_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Namjesten", name: "Namjesten",
optionName: "namjesten" optionName: "namjesten_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Nedavno adaptiran", name: "Nedavno adaptiran",
optionName: "Nedavno_adaptiran" optionName: "nedavno-adaptiran_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Uknjizeno", name: "Uknjizeno",
optionName: "uknjizeno" optionName: "uknjizeno-zk_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Lift", name: "Lift",
optionName: "lift" optionName: "lift_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Balkon", name: "Balkon",
optionName: "balkon" optionName: "balkon_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Parking", name: "Parking",
optionName: "parking" optionName: "parking_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "Plin", name: "Plin",
optionName: "plin" optionName: "plin_checkbox",
value: "on"
}, },
{ {
type: "checkbox", type: "checkbox",
name: "kablovska", name: "kablovska",
optionName: "kablovska" optionName: "kablovska-tv_checkbox",
value: "on"
} }
]; ];

View File

@@ -2,5 +2,6 @@ export const rangeOptions = {
min: 0, min: 0,
max: 1000, max: 1000,
defaultValues: [0, new Date().getFullYear()], defaultValues: [0, new Date().getFullYear()],
step: 1 step: 1,
optionNames: ["kvadrata_min", "kvadrata_max"]
}; };

View File

@@ -2,5 +2,6 @@ export const rangeOptions = {
min: 0, min: 0,
max: 100000, max: 100000,
defaultValues: [0, 100000], defaultValues: [0, 100000],
step: 100 step: 100,
optionNames: ["od", "do"]
}; };

View File

@@ -2,5 +2,6 @@ export const rangeOptions = {
min: 1960, min: 1960,
max: new Date().getFullYear(), max: new Date().getFullYear(),
defaultValues: [1960, new Date().getFullYear()], defaultValues: [1960, new Date().getFullYear()],
step: 1 step: 1,
optionNames: ["godiste_min", "godiste_max"]
}; };

View File

@@ -2,26 +2,31 @@ export const elements = [
{ {
name: "Dizel", name: "Dizel",
optionName: "gorivo_select_dizel", optionName: "gorivo_select_dizel",
type: "checkbox" type: "checkbox",
value: "Dizel"
}, },
{ {
name: "Benzin", name: "Benzin",
optionName: "gorivo_select_benzin", optionName: "gorivo_select_benzin",
type: "checkbox" type: "checkbox",
value: "Benzin"
}, },
{ {
name: "Plin", name: "Plin",
optionName: "gorivo_select_plin", optionName: "gorivo_select_plin",
type: "checkbox" type: "checkbox",
value: "Plin"
}, },
{ {
name: "Hibrid", name: "Hibrid",
optionName: "gorivo_select_hibrid", optionName: "gorivo_select_hibrid",
type: "checkbox" type: "checkbox",
value: "Hibrid"
}, },
{ {
name: "Elektro", name: "Elektro",
optionName: "gorivo_select_elektro", optionName: "gorivo_select_elektro",
type: "checkbox" type: "checkbox",
value: "Elektro"
} }
]; ];

View File

@@ -1,15 +1,15 @@
export const kilometrazaOptions = { export const kilometrazaOptions = {
kilometraMin: { kilometraMin: {
choices: [{ value: 5000, label: "5000" }, { value: 10000, label: "10000" }], choices: [{ value: 5000, label: "5000" }, { value: 10000, label: "10000" }],
value: "kilometrazaMin", value: "kilometra-a_min",
optionName: "kilometrazaMin" optionName: "kilometra-a_min"
}, },
kilometraMax: { kilometraMax: {
choices: [ choices: [
{ value: 15000, label: "15000" }, { value: 15000, label: "15000" },
{ value: 200000, label: "200000" } { value: 200000, label: "200000" }
], ],
value: "kilometrazaMax", value: "kilometra-a_max",
optionName: "kilometrazaMax" optionName: "kilometra-a_max"
} }
}; };

View File

@@ -1,5 +1,5 @@
export const proizvodacOptions = { export const proizvodacOptions = {
choices: [{ value: "1900", label: "Audi" }, { value: "9000", label: "Ford" }], choices: [{ value: "1900", label: "Audi" }, { value: "9000", label: "Ford" }],
value: "proizvodac", value: "v_b",
optionName: "proizvodac" optionName: "v_b"
}; };

View File

@@ -20,6 +20,7 @@ export const elements = [
{ {
type: "checkbox", type: "checkbox",
name: "Udarena vozila", name: "Udarena vozila",
optionName: "udaren_checkbox" optionName: "udaren_checkbox",
value: "on"
} }
]; ];

View File

@@ -19,9 +19,9 @@ class VozilaFilter extends React.Component {
elements={Stanje.elements} elements={Stanje.elements}
/> />
<SelectWrapper {...Proizvodac.proizvodacOptions} /> <SelectWrapper {...Proizvodac.proizvodacOptions} />
<RangeWrapper {...Cijena.rangeOptions} optionName="cijena" /> <RangeWrapper {...Cijena.rangeOptions} />
<SelectWrapper {...Lokacija.lokacijaOptions} /> <SelectWrapper {...Lokacija.lokacijaOptions} />
<RangeWrapper {...Godiste.rangeOptions} optionName="godiste" /> <RangeWrapper {...Godiste.rangeOptions} />
<SelectWrapper {...Kilometraza.kilometrazaOptions.kilometraMin} /> <SelectWrapper {...Kilometraza.kilometrazaOptions.kilometraMin} />
<SelectWrapper {...Kilometraza.kilometrazaOptions.kilometraMax} /> <SelectWrapper {...Kilometraza.kilometrazaOptions.kilometraMax} />
<CheckboxAndRadioWrapper <CheckboxAndRadioWrapper

View File

@@ -5,7 +5,7 @@ class CheckboxAndRadioWrapper extends React.Component {
optionChange = (option, optionName, type) => { optionChange = (option, optionName, type) => {
const optionTypePicker = { const optionTypePicker = {
radio: option.target.value, radio: option.target.value,
checkbox: option.target.checked checkbox: option.target.checked ? option.target.value : false
}; };
const { onOptionChanged } = this.props; const { onOptionChanged } = this.props;
onOptionChanged({ onOptionChanged({
@@ -15,10 +15,9 @@ class CheckboxAndRadioWrapper extends React.Component {
}; };
isChecked = (type, value, optionName) => { isChecked = (type, value, optionName) => {
const { options } = this.props; const { options } = this.props;
return type === "checkbox" return options.hasOwnProperty(optionName) && type === "checkbox"
? value ? options[optionName]
: options.hasOwnProperty(optionName) && : options[optionName] === String(value);
options[optionName] === String(value);
}; };
renderElements = (elements, componentName) => { renderElements = (elements, componentName) => {
return elements.map(({ type, value, name, optionName } = {}) => ( return elements.map(({ type, value, name, optionName } = {}) => (

View File

@@ -4,22 +4,28 @@ import { optionchangewrapper } from "utils/optionchangewrapper";
import "rc-slider/assets/index.css"; import "rc-slider/assets/index.css";
class RangeWrapper extends React.Component { class RangeWrapper extends React.Component {
sendAction = (optionName, optionValue) => {
this.props.onOptionChanged({
optionName,
optionValue
});
};
handleRangeChange = ([min, max] = this.props.defaultValues) => { handleRangeChange = ([min, max] = this.props.defaultValues) => {
this.inputMin.value = min; this.inputMin.value = min;
this.inputMax.value = max; this.inputMax.value = max;
const { onOptionChanged, optionName } = this.props; const { optionNames } = this.props;
onOptionChanged({ const optionValues = [min, max];
optionName, optionNames.forEach((optionName, index) =>
optionValue: [min, max] this.sendAction(optionName, optionValues[index])
}); );
}; };
handleInputChange = () => { handleInputChange = () => {
const { onOptionChanged, optionName } = this.props; const { optionNames } = this.props;
onOptionChanged({ const optionValues = [this.inputMin.value, this.inputMax.value];
optionName, optionNames.forEach((optionName, index) =>
optionValue: [this.inputMin.value, this.inputMax.value] this.sendAction(optionName, optionValues[index])
}); );
}; };
render() { render() {

View File

@@ -1,63 +1,15 @@
/*category: {value: "Vozila", label: "Vozila"} const isObject = obj => obj === Object(obj);
options: const mapOptionToLink = (options, option) =>
cijena: (2) [6500, 70500] options[option] !== false
godiste: (2) [2004, 2017] ? `${option}=${
gorivo_select_benzin: true isObject(options[option]) ? options[option].value : options[option]
gorivo_select_dizel: true }&`
kanton: {value: "9", label: "Sarajevo"} : "";
kilometrazaMax: {value: 20000, label: "20000"}
kilometrazaMin: {value: 5000, label: "5000"}
proizvodac: {value: "1900", label: "Audi"}
stanje: ""
subcategory: {value: "Automobili", label: "Automobili"}
export const createOlxLink = (category, subcategory, options) =>
https://www.olx.ba/pretraga? subcategory.value
kategorija=18&stanje=&v_b=1900 ? Object.keys(options).reduce(
&od=6500&do=70500 (link, option) => link + mapOptionToLink(options, option),
&kanton=9& `kategorija=${subcategory.value}&`
godiste_min=2004&godiste_max=2017 )
&kilometra-a_min=5000&kilometra-a_max=50000 : "";
&gorivo_select_dizel=Dizel&gorivo_select_benzin=Benzin
{
"Automobili": 18,
"v_b": "proizvodac",
}*/
function AutomobiliLinkCreator(options) {
const [od, do_] = options.cijena;
const [godiste_min, godiste_max] = options.godiste;
const goriva = [
"gorivo_select_benzin",
"gorivo_select_dizel",
"gorivo_select_plin",
"gorivo_select_hibrid",
"gorivo_selector_elektro"
]
.filter(gorivo => options.hasOwnProperty(gorivo))
.reduce(
(izborGoriva, gorivo) =>
izborGoriva +
"" +
options[gorivo] +
"=" +
options[gorivo] +
"&",
""
);
return `kategorija=18&stanje=${options.stanje}&v_b=${
options.proizvodac.value
}&od=${od}&do=${do_}&kanton=${
options.kanton.value
}&godiste_min=${godiste_min}&godiste_max=${godiste_max}&kilometra-a_min=${
options.kilometrazaMin.value
}&kilometra-a_max=${options.kilometrazaMax.value}&${goriva}`;
}
const mappingFunctios = {
Automobili: AutomobiliLinkCreator
};
export const createOlxLink = (category, subcategory, options) => {
return mappingFunctios[subcategory.value](options);
};

View File

@@ -0,0 +1,11 @@
export const hoc = (option, componentList) => componentList[option] || null;
export const areObjectEqual = function checkEquality(objectA, objectB) {
return (
Object.keys(objectA).length === Object.keys(objectB).length &&
Object.keys(objectA).every(property =>
objectA[property] === Object(objectA[property])
? checkEquality(objectA[property], objectB[property])
: objectA[property] === objectB[property]
)
);
};

View File

@@ -1 +0,0 @@
export const hoc = (option, componentList) => componentList[option] || null;